Skip to main content

foundry_evm/executors/
mod.rs

1//! EVM executor abstractions, which can execute calls.
2//!
3//! Used for running tests, scripts, and interacting with the inner backend which holds the state.
4
5use crate::inspectors::{
6    Cheatcodes, CmpOperands, EdgeCoverage, EdgeIndexMap, InspectorData, InspectorStack,
7    cheatcodes::BroadcastableTransactions,
8};
9use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
10use alloy_eips::eip4788::{BEACON_ROOTS_ADDRESS, SYSTEM_ADDRESS};
11use alloy_evm::Evm;
12use alloy_json_abi::Function;
13use alloy_primitives::{
14    Address, B256, Bytes, Log, TxKind, U256, keccak256,
15    map::{AddressHashMap, HashMap},
16};
17use alloy_sol_types::{SolCall, sol};
18use eyre::WrapErr;
19use foundry_evm_core::{
20    EvmEnv, FoundryBlock, FoundryChain, FoundryTransaction,
21    backend::{
22        Backend, BackendError, BackendResult, CowBackend, DatabaseError, DatabaseExt,
23        GLOBAL_FAIL_SLOT,
24    },
25    constants::{
26        CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
27        DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
28    },
29    decode::{RevertDecoder, SkipReason},
30    eip2935::{
31        HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE, history_storage_slot, history_storage_value,
32        history_window_start,
33    },
34    evm::{
35        ChainFor, EthEvmNetwork, EvmEnvFor, FoundryEvmFactory, FoundryEvmNetwork,
36        IntoInstructionResult, SpecFor, TxEnvFor,
37    },
38    utils::StateChangeset,
39};
40use foundry_evm_coverage::HitMaps;
41use foundry_evm_fuzz::ObservedCall;
42use foundry_evm_networks::NetworkConfigs;
43use foundry_evm_traces::{SparsedTraceArena, TraceRequirements};
44use revm::{
45    bytecode::Bytecode,
46    context::{Block, Cfg, ContextTr, Transaction},
47    context_interface::{
48        cfg::gas_params::Eip2780TxInfo,
49        result::{ExecutionResult, Output, ResultAndState},
50        transaction::SignedAuthorization,
51    },
52    database::{Database, DatabaseCommit, DatabaseRef},
53    interpreter::{InstructionResult, gas::InitialAndFloorGas},
54    primitives::hardfork::SpecId,
55};
56use sancov::SancovGuard;
57use std::{
58    borrow::Cow,
59    sync::{
60        Arc,
61        atomic::{AtomicBool, Ordering},
62    },
63    time::{Duration, Instant},
64};
65
66#[cfg(feature = "monad")]
67use alloy_monad_evm::MonadEvmFactory;
68#[cfg(feature = "monad")]
69use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
70#[cfg(feature = "monad")]
71use foundry_evm_core::evm::{
72    MonadEvmNetwork, refresh_chain_journal, try_transact_monad_system_replay,
73};
74#[cfg(feature = "monad")]
75use monad_revm::{MonadChainContext, MonadHardfork};
76#[cfg(feature = "monad")]
77use revm::context::TxEnv;
78
79mod builder;
80pub use builder::ExecutorBuilder;
81
82mod campaign;
83
84pub mod fuzz;
85pub use fuzz::FuzzedExecutor;
86
87pub mod invariant;
88pub use invariant::InvariantExecutor;
89
90mod corpus;
91mod corpus_io;
92mod sancov;
93mod showmap;
94mod trace;
95
96pub use corpus::{DynamicTargetCtx, StatelessReplayTarget, persist_corpus_seed};
97pub use corpus_io::{
98    CorpusDirEntry, canonical_replay_dirs, parse_corpus_filename, read_corpus_dir, read_corpus_tree,
99};
100pub use showmap::{
101    InvariantReplayOptions, MinimizationReplayInput, ReplayFailure, ReplayObservation,
102    ShowmapDomain, ShowmapOpts, ShowmapReplayTarget, ShowmapStats, replay_corpus_to_showmap,
103    replay_sequence_for_minimization,
104};
105pub use trace::{TracingExecutor, TracingFork};
106
107const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
108
109sol! {
110    interface ITest {
111        function setUp() external;
112        function failed() external view returns (bool failed);
113
114        #[derive(Default)]
115        function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
116    }
117}
118
119/// EVM executor.
120///
121/// The executor can be configured with various `revm::Inspector`s, like `Cheatcodes`.
122///
123/// There are multiple ways of interacting the EVM:
124/// - `call`: executes a transaction, but does not persist any state changes; similar to `eth_call`,
125///   where the EVM state is unchanged after the call.
126/// - `transact`: executes a transaction and persists the state changes
127/// - `deploy`: a special case of `transact`, specialized for persisting the state of a contract
128///   deployment
129/// - `setup`: a special case of `transact`, used to set up the environment for a test
130#[derive(Clone, Debug)]
131pub struct Executor<FEN: FoundryEvmNetwork> {
132    /// The underlying `revm::Database` that contains the EVM storage.
133    ///
134    /// Wrapped in `Arc` for efficient cloning during parallel fuzzing. Use [`Arc::make_mut`]
135    /// for copy-on-write semantics when mutation is needed.
136    // Note: We do not store an EVM here, since we are really
137    // only interested in the database. REVM's `EVM` is a thin
138    // wrapper around spawning a new EVM on every call anyway,
139    // so the performance difference should be negligible.
140    backend: Arc<Backend<FEN>>,
141    /// The EVM environment (block and cfg).
142    evm_env: EvmEnvFor<FEN>,
143    /// The transaction environment.
144    tx_env: TxEnvFor<FEN>,
145    /// The Revm inspector stack.
146    inspector: InspectorStack<FEN>,
147    /// The gas limit for calls and deployments.
148    gas_limit: u64,
149    /// Whether `failed()` should be called on the test contract to determine if the test failed.
150    legacy_assertions: bool,
151}
152
153#[cfg(feature = "monad")]
154impl Executor<MonadEvmNetwork> {
155    /// Tries to execute and commit a canonical system transaction during replay.
156    #[instrument(name = "transact_system_replay", level = "debug", skip_all)]
157    pub fn try_transact_system_replay_with_env_and_context(
158        &mut self,
159        mut evm_env: EvmEnv<MonadHardfork>,
160        mut tx_env: TxEnv,
161        chain_context: MonadChainContext,
162    ) -> eyre::Result<Option<RawCallResult<MonadEvmNetwork>>> {
163        let mut stack = self.inspector().clone();
164        let mut backend = CowBackend::new_borrowed(self.backend());
165        let Some(result) = backend.try_inspect_system_replay_with_context(
166            &mut evm_env,
167            &mut tx_env,
168            chain_context,
169            &mut stack,
170        )?
171        else {
172            return Ok(None);
173        };
174        let has_state_snapshot_failure = backend.has_state_snapshot_failure();
175        let fork_block_number = backend.active_fork_block_number();
176        let mut result = convert_executed_result(
177            evm_env,
178            tx_env,
179            stack,
180            result,
181            &backend,
182            has_state_snapshot_failure,
183            fork_block_number,
184        )?;
185        self.commit(&mut result);
186        Ok(Some(result))
187    }
188
189    /// Replays Monad transactions and executes the target against one EVM instance.
190    #[instrument(name = "transact_monad_block_replay", level = "debug", skip_all)]
191    pub fn transact_with_monad_block_replay(
192        &mut self,
193        evm_env: EvmEnv<MonadHardfork>,
194        target_tx_env: TxEnv,
195        target_chain_context: MonadChainContext,
196        replay: Vec<(B256, TxEnv, MonadChainContext)>,
197        replay_system_txes: bool,
198    ) -> eyre::Result<Option<(RawCallResult<MonadEvmNetwork>, bool)>> {
199        let block_number = evm_env.block_env.number();
200        let mut stack = self.inspector().clone();
201        let sancov_edges = stack.inner.sancov_edges;
202        let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
203        let sancov_active = sancov_edges || sancov_trace_cmp;
204        let backend = self.backend_mut();
205
206        let (result, evm_env, tx_env, used_system_replay) = {
207            let caller = target_tx_env.caller();
208            backend.set_caller(caller).set_spec_id(evm_env.cfg_env.spec);
209            let target_contract = match target_tx_env.kind() {
210                TxKind::Call(to) => to,
211                TxKind::Create => caller.create(target_tx_env.nonce()),
212            };
213            backend.set_test_contract(target_contract);
214            let mut evm = MonadEvmFactory::default()
215                .create_foundry_evm_with_inspector(backend, evm_env, &mut stack);
216            *evm.chain_mut() = target_chain_context.clone();
217            evm.disable_inspector();
218            for (tx_hash, tx_env, chain_context) in replay {
219                evm.ctx_mut().chain = chain_context;
220                refresh_chain_journal(evm.ctx_mut());
221                evm.ctx_mut().cfg.disable_balance_check = true;
222                let is_system = is_known_system_sender(tx_env.caller())
223                    || tx_env.tx_type() == SYSTEM_TRANSACTION_TYPE;
224                let result = if is_system {
225                    try_transact_monad_system_replay(&mut evm, &tx_env).wrap_err_with(|| {
226                        format!(
227                            "Failed to replay system transaction: {tx_hash:?} in block {block_number}"
228                        )
229                    })?
230                } else {
231                    None
232                };
233                if let Some(result) = result {
234                    evm.db_mut().commit(result.state);
235                } else if !is_system || replay_system_txes {
236                    let created = match tx_env.kind() {
237                        TxKind::Create => Some(tx_env.caller().create(tx_env.nonce())),
238                        TxKind::Call(_) => None,
239                    };
240                    let result = evm.transact(tx_env).wrap_err_with(|| {
241                        format!(
242                            "Failed to execute transaction: {tx_hash:?} in block {block_number}"
243                        )
244                    })?;
245                    if result.result.is_success()
246                        && let Some(address) = created
247                    {
248                        evm.db_mut().add_persistent_account(address);
249                    }
250                    evm.db_mut().commit(result.state);
251                }
252            }
253
254            evm.enable_inspector();
255            evm.ctx_mut().chain = target_chain_context;
256            refresh_chain_journal(evm.ctx_mut());
257            let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
258            let target_is_system = is_known_system_sender(target_tx_env.caller())
259                || target_tx_env.tx_type() == SYSTEM_TRANSACTION_TYPE;
260            let system_result = if target_is_system {
261                try_transact_monad_system_replay(&mut evm, &target_tx_env)?
262            } else {
263                None
264            };
265            let (result, used_system_replay) = if let Some(result) = system_result {
266                (result, true)
267            } else if target_is_system && !replay_system_txes {
268                return Ok(None);
269            } else {
270                (evm.transact(target_tx_env.clone()).wrap_err("EVM error")?, false)
271            };
272            let tx_env = if used_system_replay { target_tx_env } else { evm.tx().clone() };
273            let evm_env = evm.finish().1;
274            (result, evm_env, tx_env, used_system_replay)
275        };
276
277        let has_state_snapshot_failure = backend.has_state_snapshot_failure();
278        let fork_block_number = backend.active_fork_block_number();
279        let mut result = convert_executed_result(
280            evm_env,
281            tx_env,
282            stack,
283            result,
284            &*backend,
285            has_state_snapshot_failure,
286            fork_block_number,
287        )?;
288        if sancov_edges {
289            SancovGuard::append_edges_into(&mut result);
290        }
291        if sancov_trace_cmp {
292            SancovGuard::drain_cmp_into(&mut result);
293        }
294        self.commit(&mut result);
295        Ok(Some((result, used_system_replay)))
296    }
297}
298
299impl<FEN: FoundryEvmNetwork> Executor<FEN> {
300    /// Creates a new `Executor` with the given arguments.
301    #[inline]
302    pub fn new(
303        mut backend: Backend<FEN>,
304        evm_env: EvmEnvFor<FEN>,
305        tx_env: TxEnvFor<FEN>,
306        mut inspector: InspectorStack<FEN>,
307        networks: NetworkConfigs,
308        gas_limit: u64,
309        legacy_assertions: bool,
310    ) -> Self {
311        inspector.networks(networks);
312        backend.set_networks(networks);
313        let extra_cheatcode_addresses = inspector.extra_cheatcode_addresses();
314        backend.extend_persistent_accounts(extra_cheatcode_addresses.iter().copied());
315
316        // Need to create a non-empty contract on the cheatcodes address so `extcodesize` checks
317        // do not fail.
318        backend.insert_account_info(
319            CHEATCODE_ADDRESS,
320            revm::state::AccountInfo {
321                code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
322                // Also set the code hash manually so that it's not computed later.
323                // The code hash value does not matter, as long as it's not zero or `KECCAK_EMPTY`.
324                code_hash: CHEATCODE_CONTRACT_HASH,
325                ..Default::default()
326            },
327        );
328
329        for &address in extra_cheatcode_addresses {
330            backend.insert_account_info(
331                address,
332                revm::state::AccountInfo {
333                    code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
334                    code_hash: keccak256(address),
335                    ..Default::default()
336                },
337            );
338        }
339
340        if !backend.is_in_forking_mode() && evm_env.cfg_env.spec.into() >= SpecId::PRAGUE {
341            let mut account =
342                backend.basic_ref(HISTORY_STORAGE_ADDRESS).unwrap_or_default().unwrap_or_default();
343            account.code_hash = keccak256(&HISTORY_STORAGE_CODE);
344            account.code = Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone()));
345            backend.insert_account_info(HISTORY_STORAGE_ADDRESS, account);
346
347            let current_block = evm_env.block_env.number();
348            let mut block_number = history_window_start(current_block);
349            while block_number < current_block {
350                let block_hash =
351                    backend.block_hash(block_number.saturating_to()).unwrap_or_default();
352                let slot = history_storage_slot(block_number);
353                let value = history_storage_value(block_hash);
354                let _ = backend.insert_account_storage(HISTORY_STORAGE_ADDRESS, slot, value);
355                block_number += U256::ONE;
356            }
357        }
358
359        Self {
360            backend: Arc::new(backend),
361            evm_env,
362            tx_env,
363            inspector,
364            gas_limit,
365            legacy_assertions,
366        }
367    }
368
369    fn clone_with_backend(&self, backend: Backend<FEN>) -> Self {
370        let evm_env = self.evm_env.clone();
371        Self {
372            backend: Arc::new(backend),
373            evm_env,
374            tx_env: self.tx_env.clone(),
375            inspector: self.inspector().clone(),
376            gas_limit: self.gas_limit,
377            legacy_assertions: self.legacy_assertions,
378        }
379    }
380
381    /// Returns a reference to the EVM backend.
382    pub fn backend(&self) -> &Backend<FEN> {
383        &self.backend
384    }
385
386    /// Returns a mutable reference to the EVM backend.
387    ///
388    /// Uses copy-on-write semantics: if other clones of this executor share the backend,
389    /// this will clone the backend first.
390    pub fn backend_mut(&mut self) -> &mut Backend<FEN> {
391        Arc::make_mut(&mut self.backend)
392    }
393
394    fn chain_context_for_synthetic_transaction(
395        &self,
396        tx: &TxEnvFor<FEN>,
397    ) -> eyre::Result<ChainFor<FEN>> {
398        self.backend().chain_context_for_synthetic_transaction(tx)
399    }
400
401    /// Returns a reference to the EVM environment (block and cfg).
402    pub const fn evm_env(&self) -> &EvmEnvFor<FEN> {
403        &self.evm_env
404    }
405
406    /// Returns a mutable reference to the EVM environment (block and cfg).
407    pub const fn evm_env_mut(&mut self) -> &mut EvmEnvFor<FEN> {
408        &mut self.evm_env
409    }
410
411    /// Returns a reference to the transaction environment.
412    pub const fn tx_env(&self) -> &TxEnvFor<FEN> {
413        &self.tx_env
414    }
415
416    /// Returns a mutable reference to the transaction environment.
417    pub const fn tx_env_mut(&mut self) -> &mut TxEnvFor<FEN> {
418        &mut self.tx_env
419    }
420
421    /// Returns a reference to the EVM inspector.
422    pub const fn inspector(&self) -> &InspectorStack<FEN> {
423        &self.inspector
424    }
425
426    /// Returns a mutable reference to the EVM inspector.
427    pub const fn inspector_mut(&mut self) -> &mut InspectorStack<FEN> {
428        &mut self.inspector
429    }
430
431    /// Returns the EVM spec.
432    pub const fn spec_id(&self) -> SpecFor<FEN> {
433        self.evm_env.cfg_env.spec
434    }
435
436    /// Sets the EVM spec and updates spec-dependent gas parameters.
437    pub fn set_spec_id(&mut self, spec_id: SpecFor<FEN>) {
438        self.evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id);
439    }
440
441    /// Returns the gas limit for calls and deployments.
442    ///
443    /// This is different from the gas limit imposed by the passed in environment, as those limits
444    /// are used by the EVM for certain opcodes like `gaslimit`.
445    pub const fn gas_limit(&self) -> u64 {
446        self.gas_limit
447    }
448
449    /// Sets the gas limit for calls and deployments.
450    pub const fn set_gas_limit(&mut self, gas_limit: u64) {
451        self.gas_limit = gas_limit;
452    }
453
454    /// Returns whether `failed()` should be called on the test contract to determine if the test
455    /// failed.
456    pub const fn legacy_assertions(&self) -> bool {
457        self.legacy_assertions
458    }
459
460    /// Sets whether `failed()` should be called on the test contract to determine if the test
461    /// failed.
462    pub const fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
463        self.legacy_assertions = legacy_assertions;
464    }
465
466    /// Creates the default CREATE2 Contract Deployer for local tests and scripts.
467    pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
468        trace!("deploying local create2 deployer");
469        let create2_deployer_account = self
470            .backend()
471            .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
472            .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
473
474        // If the deployer is not currently deployed, deploy the default one.
475        if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
476            let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
477
478            // Probably 0, but just in case.
479            let initial_balance = self.get_balance(creator)?;
480            self.set_balance(creator, U256::MAX)?;
481
482            // The deployer is test harness state, so the debugger does not step through it.
483            let recorder = self.inspector_mut().debug_recorder.take();
484            let res = self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None);
485            self.inspector_mut().debug_recorder = recorder;
486            let res = res?;
487            trace!(create2=?res.address, "deployed local create2 deployer");
488
489            self.set_balance(creator, initial_balance)?;
490        }
491        Ok(())
492    }
493
494    /// Set the balance of an account.
495    pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
496        trace!(?address, ?amount, "setting account balance");
497        let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
498        account.balance = amount;
499        self.backend_mut().insert_account_info(address, account);
500        Ok(())
501    }
502
503    /// Gets the balance of an account
504    pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
505        Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
506    }
507
508    /// Sets the nonce of an account without modifying the transaction environment.
509    pub fn set_account_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
510        let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
511        account.nonce = nonce;
512        self.backend_mut().insert_account_info(address, account);
513        Ok(())
514    }
515
516    /// Sets the nonce of an account and the transaction environment.
517    pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
518        self.set_account_nonce(address, nonce)?;
519        self.tx_env_mut().set_nonce(nonce);
520        Ok(())
521    }
522
523    /// Returns the nonce of an account.
524    pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
525        Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
526    }
527
528    /// Set the code of an account.
529    pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
530        let account = self.backend().basic_ref(address)?.unwrap_or_default().with_code(code);
531        self.backend_mut().insert_account_info(address, account);
532        Ok(())
533    }
534
535    /// Set the storage of an account.
536    pub fn set_storage(
537        &mut self,
538        address: Address,
539        storage: HashMap<U256, U256>,
540    ) -> BackendResult<()> {
541        self.backend_mut().replace_account_storage(address, storage)?;
542        Ok(())
543    }
544
545    /// Set a storage slot of an account.
546    pub fn set_storage_slot(
547        &mut self,
548        address: Address,
549        slot: U256,
550        value: U256,
551    ) -> BackendResult<()> {
552        self.backend_mut().insert_account_storage(address, slot, value)?;
553        Ok(())
554    }
555
556    /// Apply prestate trace data to the executor's backend.
557    ///
558    /// This is used to set up the EVM state based on the prestate trace from
559    /// `debug_traceTransaction`, which provides all accounts and storage slots
560    /// that will be accessed during transaction execution.
561    pub fn apply_prestate_trace(
562        &mut self,
563        prestate: std::collections::BTreeMap<Address, alloy_rpc_types::trace::geth::AccountState>,
564    ) -> eyre::Result<()> {
565        let backend = self.backend_mut();
566        for (address, account_state) in prestate {
567            let code = account_state.code.map(Bytecode::new_raw).unwrap_or_default();
568            let info = revm::state::AccountInfo::default()
569                .with_balance(account_state.balance.unwrap_or_default())
570                .with_nonce(account_state.nonce.unwrap_or_default())
571                .with_code(code);
572            backend.insert_account_info(address, info);
573
574            for (slot, value) in account_state.storage {
575                let slot = slot.into();
576                let value = value.into();
577                backend.insert_account_storage(address, slot, value)?;
578            }
579        }
580        Ok(())
581    }
582
583    /// Returns `true` if the account has no code.
584    pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
585        Ok(self.backend().basic_ref(address)?.is_none_or(|acc| acc.is_empty_code_hash()))
586    }
587
588    #[inline]
589    pub fn set_trace_requirements(&mut self, requirements: TraceRequirements) -> &mut Self {
590        self.inspector_mut().tracing_requirements(requirements);
591        self
592    }
593
594    #[inline]
595    pub fn set_script_execution(&mut self, script_address: Address) {
596        self.inspector_mut().script(script_address);
597    }
598
599    #[inline]
600    pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
601        self.inspector_mut().print(trace_printer);
602        self
603    }
604
605    #[inline]
606    pub fn create2_deployer(&self) -> Address {
607        self.inspector().create2_deployer
608    }
609
610    /// Deploys a contract and commits the new state to the underlying database.
611    ///
612    /// Executes a CREATE transaction with the contract `code` and persistent database state
613    /// modifications.
614    pub fn deploy(
615        &mut self,
616        from: Address,
617        code: Bytes,
618        value: U256,
619        rd: Option<&RevertDecoder>,
620    ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
621        let (evm_env, tx_env) = self.prepare_call_env(from, TxKind::Create, code, value);
622        self.deploy_with_env(evm_env, tx_env, rd)
623    }
624
625    /// Deploys a contract using the given `env` and commits the new state to the underlying
626    /// database.
627    ///
628    /// # Panics
629    ///
630    /// Panics if `tx_env.kind` is not `TxKind::Create(_)`.
631    #[instrument(name = "deploy", level = "debug", skip_all)]
632    pub fn deploy_with_env(
633        &mut self,
634        evm_env: EvmEnvFor<FEN>,
635        tx_env: TxEnvFor<FEN>,
636        rd: Option<&RevertDecoder>,
637    ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
638        let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
639        self.deploy_with_env_and_context(evm_env, tx_env, chain_context, rd)
640    }
641
642    /// Deploys a contract with explicit network-specific context and commits its state changes.
643    ///
644    /// # Panics
645    ///
646    /// Panics if `tx_env.kind` is not `TxKind::Create(_)`.
647    #[instrument(name = "deploy", level = "debug", skip_all)]
648    pub fn deploy_with_env_and_context(
649        &mut self,
650        evm_env: EvmEnvFor<FEN>,
651        tx_env: TxEnvFor<FEN>,
652        chain_context: ChainFor<FEN>,
653        rd: Option<&RevertDecoder>,
654    ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
655        assert!(tx_env.kind().is_create(), "Expected create transaction, got {:?}", tx_env.kind());
656        trace!(sender=%tx_env.caller(), "deploying contract");
657
658        let mut result = self.transact_with_env_and_context(evm_env, tx_env, chain_context)?;
659        result = result.into_result(rd)?;
660        let Some(Output::Create(_, Some(address))) = result.out else {
661            panic!("Deployment succeeded, but no address was returned: {result:#?}");
662        };
663
664        // also mark this library as persistent, this will ensure that the state of the library is
665        // persistent across fork swaps in forking mode
666        self.backend_mut().add_persistent_account(address);
667
668        trace!(%address, "deployed contract");
669
670        Ok(DeployResult { raw: result, address })
671    }
672
673    /// Calls the `setUp()` function on a contract.
674    ///
675    /// This will commit any state changes to the underlying database.
676    ///
677    /// Ayn changes made during the setup call to env's block environment are persistent, for
678    /// example `vm.chainId()` will change the `block.chainId` for all subsequent test calls.
679    #[instrument(name = "setup", level = "debug", skip_all)]
680    pub fn setup(
681        &mut self,
682        from: Option<Address>,
683        to: Address,
684        rd: Option<&RevertDecoder>,
685    ) -> Result<RawCallResult<FEN>, EvmError<FEN>> {
686        trace!(?from, ?to, "setting up contract");
687
688        let from = from.unwrap_or(CALLER);
689        self.backend_mut().set_test_contract(to).set_caller(from);
690        let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
691        let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
692        res = res.into_result(rd)?;
693
694        // record any changes made to the block's environment during setup
695        self.evm_env_mut().block_env = res.evm_env.block_env.clone();
696        // and also the chainid, which can be set manually
697        self.evm_env_mut().cfg_env.chain_id = res.evm_env.cfg_env.chain_id;
698
699        let success = self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res);
700        if !success {
701            return Err(res.into_execution_error("execution error".to_string()).into());
702        }
703
704        Ok(res)
705    }
706
707    /// Performs a call to an account on the current state of the VM.
708    pub fn call(
709        &self,
710        from: Address,
711        to: Address,
712        func: &Function,
713        args: &[DynSolValue],
714        value: U256,
715        rd: Option<&RevertDecoder>,
716    ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
717        let calldata = Bytes::from(func.abi_encode_input(args)?);
718        let result = self.call_raw(from, to, calldata, value)?;
719        result.into_decoded_result(func, rd)
720    }
721
722    /// Performs a call to an account on the current state of the VM.
723    pub fn call_sol<C: SolCall>(
724        &self,
725        from: Address,
726        to: Address,
727        args: &C,
728        value: U256,
729        rd: Option<&RevertDecoder>,
730    ) -> Result<CallResult<C::Return, FEN>, EvmError<FEN>> {
731        let calldata = Bytes::from(args.abi_encode());
732        let mut raw = self.call_raw(from, to, calldata, value)?;
733        raw = raw.into_result(rd)?;
734        Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
735    }
736
737    /// Performs a call to an account on the current state of the VM.
738    pub fn transact(
739        &mut self,
740        from: Address,
741        to: Address,
742        func: &Function,
743        args: &[DynSolValue],
744        value: U256,
745        rd: Option<&RevertDecoder>,
746    ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
747        let calldata = Bytes::from(func.abi_encode_input(args)?);
748        let result = self.transact_raw(from, to, calldata, value)?;
749        result.into_decoded_result(func, rd)
750    }
751
752    /// Performs a raw call to an account on the current state of the VM.
753    pub fn call_raw(
754        &self,
755        from: Address,
756        to: Address,
757        calldata: Bytes,
758        value: U256,
759    ) -> eyre::Result<RawCallResult<FEN>> {
760        let (evm_env, tx_env) = self.prepare_call_env(from, TxKind::Call(to), calldata, value);
761        self.call_with_env(evm_env, tx_env)
762    }
763
764    /// Performs a raw call to an account on the current state of the VM with an EIP-7702
765    /// authorization list.
766    pub fn call_raw_with_authorization(
767        &mut self,
768        from: Address,
769        to: Address,
770        calldata: Bytes,
771        value: U256,
772        authorization_list: Vec<SignedAuthorization>,
773    ) -> eyre::Result<RawCallResult<FEN>> {
774        let (evm_env, mut tx_env) = self.prepare_call_env(from, to.into(), calldata, value);
775        tx_env.set_signed_authorization(authorization_list);
776        tx_env.set_tx_type(4);
777        self.call_with_env(evm_env, tx_env)
778    }
779
780    /// Performs a raw call to an account on the current state of the VM.
781    pub fn transact_raw(
782        &mut self,
783        from: Address,
784        to: Address,
785        calldata: Bytes,
786        value: U256,
787    ) -> eyre::Result<RawCallResult<FEN>> {
788        let (evm_env, tx_env) = self.prepare_call_env(from, TxKind::Call(to), calldata, value);
789        self.transact_with_env(evm_env, tx_env)
790    }
791
792    /// Performs a raw call to an account on the current state of the VM with an EIP-7702
793    /// authorization last.
794    pub fn transact_raw_with_authorization(
795        &mut self,
796        from: Address,
797        to: Address,
798        calldata: Bytes,
799        value: U256,
800        authorization_list: Vec<SignedAuthorization>,
801    ) -> eyre::Result<RawCallResult<FEN>> {
802        let (evm_env, mut tx_env) = self.prepare_call_env(from, TxKind::Call(to), calldata, value);
803        tx_env.set_signed_authorization(authorization_list);
804        tx_env.set_tx_type(4);
805        self.transact_with_env(evm_env, tx_env)
806    }
807
808    /// Applies the EIP-4788 beacon roots system call (Cancun+).
809    /// <https://eips.ethereum.org/EIPS/eip-4788>
810    pub fn apply_beacon_root(
811        &mut self,
812        parent_beacon_block_root: alloy_primitives::B256,
813    ) -> eyre::Result<()> {
814        let calldata = Bytes::from(parent_beacon_block_root);
815        let mut evm_env = self.evm_env.clone();
816        let inspector = self.inspector().clone();
817        let mut state = {
818            let mut backend = CowBackend::new_borrowed(self.backend());
819            let mut evm = FEN::EvmFactory::default().create_foundry_evm_with_inspector(
820                &mut backend,
821                evm_env.clone(),
822                inspector,
823            );
824            *evm.chain_mut() = ChainFor::<FEN>::for_transaction(&TxEnvFor::<FEN>::default());
825            let result =
826                evm.transact_system_call(SYSTEM_ADDRESS, BEACON_ROOTS_ADDRESS, calldata)?;
827            evm_env = evm.finish().1;
828            result.state
829        };
830        state.retain(|address, _| *address == BEACON_ROOTS_ADDRESS);
831
832        self.backend_mut().commit(state);
833        self.inspector_mut().set_block(evm_env.block_env);
834
835        Ok(())
836    }
837
838    /// Execute the transaction configured in `tx_env`.
839    ///
840    /// The state after the call is **not** persisted.
841    #[instrument(name = "call", level = "debug", skip_all)]
842    pub fn call_with_env(
843        &self,
844        evm_env: EvmEnvFor<FEN>,
845        tx_env: TxEnvFor<FEN>,
846    ) -> eyre::Result<RawCallResult<FEN>> {
847        let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
848        self.call_with_env_and_context(evm_env, tx_env, chain_context)
849    }
850
851    /// Executes the transaction with explicit network-specific context without committing state.
852    #[instrument(name = "call", level = "debug", skip_all)]
853    pub fn call_with_env_and_context(
854        &self,
855        mut evm_env: EvmEnvFor<FEN>,
856        mut tx_env: TxEnvFor<FEN>,
857        chain_context: ChainFor<FEN>,
858    ) -> eyre::Result<RawCallResult<FEN>> {
859        let mut stack = self.inspector().clone();
860        let sancov_edges = stack.inner.sancov_edges;
861        let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
862        let sancov_active = sancov_edges || sancov_trace_cmp;
863        let mut backend = CowBackend::new_borrowed(self.backend());
864        let result = {
865            let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
866            backend.inspect_with_context(&mut evm_env, &mut tx_env, chain_context, &mut stack)?
867        };
868        let has_state_snapshot_failure = backend.has_state_snapshot_failure();
869        let fork_block_number = backend.active_fork_block_number();
870        let mut result = convert_executed_result(
871            evm_env,
872            tx_env,
873            stack,
874            result,
875            &backend,
876            has_state_snapshot_failure,
877            fork_block_number,
878        )?;
879        if sancov_edges {
880            SancovGuard::append_edges_into(&mut result);
881        }
882        if sancov_trace_cmp {
883            SancovGuard::drain_cmp_into(&mut result);
884        }
885        Ok(result)
886    }
887
888    /// Execute the transaction configured in `tx_env`.
889    #[instrument(name = "transact", level = "debug", skip_all)]
890    pub fn transact_with_env(
891        &mut self,
892        evm_env: EvmEnvFor<FEN>,
893        tx_env: TxEnvFor<FEN>,
894    ) -> eyre::Result<RawCallResult<FEN>> {
895        let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
896        self.transact_with_env_and_context(evm_env, tx_env, chain_context)
897    }
898
899    /// Executes and commits the transaction with explicit network-specific context.
900    #[instrument(name = "transact", level = "debug", skip_all)]
901    pub fn transact_with_env_and_context(
902        &mut self,
903        mut evm_env: EvmEnvFor<FEN>,
904        mut tx_env: TxEnvFor<FEN>,
905        chain_context: ChainFor<FEN>,
906    ) -> eyre::Result<RawCallResult<FEN>> {
907        let mut stack = self.inspector().clone();
908        let sancov_edges = stack.inner.sancov_edges;
909        let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
910        let sancov_active = sancov_edges || sancov_trace_cmp;
911        let backend = self.backend_mut();
912        let result = {
913            let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
914            backend.inspect_with_context(&mut evm_env, &mut tx_env, chain_context, &mut stack)?
915        };
916        let has_state_snapshot_failure = backend.has_state_snapshot_failure();
917        let fork_block_number = backend.active_fork_block_number();
918        let mut result = convert_executed_result(
919            evm_env,
920            tx_env,
921            stack,
922            result,
923            &*backend,
924            has_state_snapshot_failure,
925            fork_block_number,
926        )?;
927        if sancov_edges {
928            SancovGuard::append_edges_into(&mut result);
929        }
930        if sancov_trace_cmp {
931            SancovGuard::drain_cmp_into(&mut result);
932        }
933        self.commit(&mut result);
934        Ok(result)
935    }
936
937    /// Replays ordinary transactions and executes the target against one EVM instance.
938    #[instrument(name = "transact_block_replay", level = "debug", skip_all)]
939    pub fn transact_with_ordinary_block_replay(
940        &mut self,
941        mut evm_env: EvmEnvFor<FEN>,
942        target_tx_env: TxEnvFor<FEN>,
943        replay: Vec<(B256, TxEnvFor<FEN>)>,
944    ) -> eyre::Result<RawCallResult<FEN>> {
945        // EIP-8130 requires phase-aware execution and has no ordinary call/create kind.
946        // Check the entire prefix before executing anything or accessing ordinary tx fields.
947        eyre::ensure!(
948            target_tx_env.tx_type() != 0x79,
949            "EIP-8130 target transactions are not supported by tooling replay"
950        );
951        for (hash, tx) in &replay {
952            eyre::ensure!(
953                tx.tx_type() != 0x79,
954                "EIP-8130 prefix transaction {hash} is not supported by tooling replay"
955            );
956        }
957        let block_number = evm_env.block_env.number();
958        let mut stack = self.inspector().clone();
959        let sancov_edges = stack.inner.sancov_edges;
960        let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
961        let sancov_active = sancov_edges || sancov_trace_cmp;
962        let backend = self.backend_mut();
963
964        let (result, evm_env, tx_env) = {
965            let caller = target_tx_env.caller();
966            backend.set_caller(caller).set_spec_id(evm_env.cfg_env.spec);
967            let target_contract = match target_tx_env.kind() {
968                TxKind::Call(to) => to,
969                // The prefix has not run yet, so use the canonical target nonce rather than the
970                // current database nonce.
971                TxKind::Create => caller.create(target_tx_env.nonce()),
972            };
973            backend.set_test_contract(target_contract);
974            let target_chain_context = ChainFor::<FEN>::for_transaction(&target_tx_env);
975            if !replay.is_empty() {
976                evm_env.cfg_env.disable_balance_check = true;
977            }
978            let mut evm = FEN::EvmFactory::default()
979                .create_foundry_evm_with_inspector(backend, evm_env, &mut stack);
980            *evm.chain_mut() = target_chain_context;
981            evm.disable_inspector();
982            for (tx_hash, tx_env) in replay {
983                let created = match tx_env.kind() {
984                    TxKind::Create => Some(tx_env.caller().create(tx_env.nonce())),
985                    TxKind::Call(_) => None,
986                };
987                let result = evm.transact(tx_env).wrap_err_with(|| {
988                    format!("Failed to execute transaction: {tx_hash:?} in block {block_number}")
989                })?;
990                if result.result.is_success()
991                    && let Some(address) = created
992                {
993                    evm.db_mut().add_persistent_account(address);
994                }
995                evm.db_mut().commit(result.state);
996            }
997
998            evm.enable_inspector();
999            let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
1000            let result = evm.transact(target_tx_env).wrap_err("EVM error")?;
1001            let tx_env = evm.tx().clone();
1002            let evm_env = evm.finish().1;
1003            (result, evm_env, tx_env)
1004        };
1005
1006        let has_state_snapshot_failure = backend.has_state_snapshot_failure();
1007        let fork_block_number = backend.active_fork_block_number();
1008        let mut result = convert_executed_result(
1009            evm_env,
1010            tx_env,
1011            stack,
1012            result,
1013            &*backend,
1014            has_state_snapshot_failure,
1015            fork_block_number,
1016        )?;
1017        if sancov_edges {
1018            SancovGuard::append_edges_into(&mut result);
1019        }
1020        if sancov_trace_cmp {
1021            SancovGuard::drain_cmp_into(&mut result);
1022        }
1023        self.commit(&mut result);
1024        Ok(result)
1025    }
1026
1027    /// Commit the changeset to the database and adjust `self.inspector_config` values according to
1028    /// the executed call result.
1029    ///
1030    /// This should not be exposed to the user, as it should be called only by `transact*`.
1031    #[instrument(name = "commit", level = "debug", skip_all)]
1032    fn commit(&mut self, result: &mut RawCallResult<FEN>) {
1033        // Persist changes to db.
1034        self.backend_mut().commit(result.state_changeset.clone());
1035
1036        // Persist cheatcode state.
1037        self.inspector_mut().cheatcodes = result.cheatcodes.take();
1038        if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
1039            // Clear broadcastable transactions
1040            cheats.broadcastable_transactions.clear();
1041            cheats.ignored_traces.ignored.clear();
1042            // if tracing was paused but never unpaused, we should begin next frame with tracing
1043            // still paused
1044            if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
1045                *last_pause_call = (0, 0);
1046            }
1047        }
1048
1049        // Persist the changed environment.
1050        self.inspector_mut().set_block(result.evm_env.block_env.clone());
1051        self.inspector_mut().set_gas_price(result.tx_env.gas_price());
1052    }
1053
1054    /// Returns `true` if a test can be considered successful.
1055    ///
1056    /// This is the same as [`Self::is_success`], but will consume the `state_changeset` map to use
1057    /// internally when calling `failed()`.
1058    pub fn is_raw_call_mut_success(
1059        &self,
1060        address: Address,
1061        call_result: &mut RawCallResult<FEN>,
1062    ) -> bool {
1063        self.is_raw_call_success(
1064            address,
1065            Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
1066            call_result,
1067        )
1068    }
1069
1070    /// Returns `true` if a test can be considered successful.
1071    ///
1072    /// This is the same as [`Self::is_success`], but intended for outcomes of [`Self::call_raw`].
1073    pub fn is_raw_call_success(
1074        &self,
1075        address: Address,
1076        state_changeset: Cow<'_, StateChangeset>,
1077        call_result: &RawCallResult<FEN>,
1078    ) -> bool {
1079        if call_result.has_state_snapshot_failure {
1080            // a failure occurred in a reverted snapshot, which is considered a failed test
1081            return false;
1082        }
1083        self.is_success(address, call_result.reverted, state_changeset)
1084    }
1085
1086    /// Like [`Self::is_raw_call_mut_success`] but uses [`Self::is_success_handler_gate`] under
1087    /// the hood. Intended for invariant view-call success checks during a campaign where the
1088    /// committed `GLOBAL_FAIL_SLOT` may be stale poison from a previously-recorded handler bug.
1089    pub fn is_raw_call_mut_success_handler_gate(
1090        &self,
1091        address: Address,
1092        call_result: &mut RawCallResult<FEN>,
1093    ) -> bool {
1094        if call_result.has_state_snapshot_failure {
1095            return false;
1096        }
1097        let state_changeset = std::mem::take(&mut call_result.state_changeset);
1098        self.is_success_handler_gate(address, call_result.reverted, Cow::Owned(state_changeset))
1099    }
1100
1101    /// Returns `true` if a test can be considered successful.
1102    ///
1103    /// If the call succeeded, we also have to check the global and local failure flags.
1104    ///
1105    /// These are set by the test contract itself when an assertion fails, using the internal `fail`
1106    /// function. The global flag is located in [`CHEATCODE_ADDRESS`] at slot [`GLOBAL_FAIL_SLOT`],
1107    /// and the local flag is located in the test contract at an unspecified slot.
1108    ///
1109    /// This behavior is inherited from Dapptools, where initially only a public
1110    /// `failed` variable was used to track test failures, and later, a global failure flag was
1111    /// introduced to track failures across multiple contracts in
1112    /// [ds-test#30](https://github.com/dapphub/ds-test/pull/30).
1113    ///
1114    /// The assumption is that the test runner calls `failed` on the test contract to determine if
1115    /// it failed. However, we want to avoid this as much as possible, as it is relatively
1116    /// expensive to set up an EVM call just for checking a single boolean flag.
1117    ///
1118    /// See:
1119    /// - Newer DSTest: <https://github.com/dapphub/ds-test/blob/e282159d5170298eb2455a6c05280ab5a73a4ef0/src/test.sol#L47-L63>
1120    /// - Older DSTest: <https://github.com/dapphub/ds-test/blob/9ca4ecd48862b40d7b0197b600713f64d337af12/src/test.sol#L38-L49>
1121    /// - forge-std: <https://github.com/foundry-rs/forge-std/blob/19891e6a0b5474b9ea6827ddb90bb9388f7acfc0/src/StdAssertions.sol#L38-L44>
1122    pub fn is_success(
1123        &self,
1124        address: Address,
1125        reverted: bool,
1126        state_changeset: Cow<'_, StateChangeset>,
1127    ) -> bool {
1128        self.is_success_raw(address, reverted, state_changeset, false)
1129    }
1130
1131    /// Like [`Self::is_success`] but ignores the *committed* `GLOBAL_FAIL_SLOT` and only treats
1132    /// the slot as failed when this call's in-flight changeset writes it. Used by the invariant
1133    /// runner's per-call handler-success gate, where a `1` already in committed storage is just
1134    /// stale poison from a previously-recorded handler bug (separately tracked) and must not
1135    /// suppress later `assert_invariants` / `afterInvariant` evaluations.
1136    pub fn is_success_handler_gate(
1137        &self,
1138        address: Address,
1139        reverted: bool,
1140        state_changeset: Cow<'_, StateChangeset>,
1141    ) -> bool {
1142        self.is_success_raw(address, reverted, state_changeset, true)
1143    }
1144
1145    #[instrument(name = "is_success", level = "debug", skip_all)]
1146    fn is_success_raw(
1147        &self,
1148        address: Address,
1149        reverted: bool,
1150        state_changeset: Cow<'_, StateChangeset>,
1151        pending_global_failure_only: bool,
1152    ) -> bool {
1153        // The call reverted.
1154        if reverted {
1155            return false;
1156        }
1157
1158        // A failure occurred in a reverted snapshot, which is considered a failed test.
1159        if self.backend().has_state_snapshot_failure() {
1160            return false;
1161        }
1162
1163        // Check the global failure slot. Callers that already track recorded handler bugs
1164        // out-of-band can pass `pending_global_failure_only = true` to ignore the committed
1165        // slot (which would otherwise stay `1` for the rest of the run after a non-reverting
1166        // `vm.assert*` under `assertions_revert = false`).
1167        let global_failed = if pending_global_failure_only {
1168            Self::has_pending_global_failure(&state_changeset)
1169        } else {
1170            self.has_global_failure(&state_changeset)
1171        };
1172        if global_failed {
1173            return false;
1174        }
1175
1176        if !self.legacy_assertions {
1177            return true;
1178        }
1179
1180        // Finally, resort to calling `DSTest::failed`.
1181        {
1182            // Construct a new bare-bones backend to evaluate success.
1183            let mut backend = self.backend().clone_empty();
1184
1185            // We only clone the test contract and cheatcode accounts,
1186            // that's all we need to evaluate success.
1187            for address in [address, CHEATCODE_ADDRESS] {
1188                let Ok(acc) = self.backend().basic_ref(address) else { return false };
1189                backend.insert_account_info(address, acc.unwrap_or_default());
1190            }
1191
1192            // If this test failed any asserts, then this changeset will contain changes
1193            // `false -> true` for the contract's `failed` variable and the `globalFailure` flag
1194            // in the state of the cheatcode address,
1195            // which are both read when we call `"failed()(bool)"` in the next step.
1196            backend.commit(state_changeset.into_owned());
1197
1198            // Check if a DSTest assertion failed
1199            let executor = self.clone_with_backend(backend);
1200            let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
1201            match call {
1202                Ok(CallResult { raw: _, decoded_result: failed }) => {
1203                    trace!(failed, "DSTest::failed()");
1204                    !failed
1205                }
1206                Err(err) => {
1207                    trace!(%err, "failed to call DSTest::failed()");
1208                    true
1209                }
1210            }
1211        }
1212    }
1213
1214    /// Returns whether the in-flight state changeset for the current call sets the global
1215    /// assertion failure flag.
1216    pub fn has_pending_global_failure(state_changeset: &StateChangeset) -> bool {
1217        if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
1218            && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
1219            && !failed_slot.present_value().is_zero()
1220        {
1221            return true;
1222        }
1223
1224        false
1225    }
1226
1227    /// Returns whether the global assertion failure flag is set either in the in-flight state
1228    /// changeset or in the committed backend state.
1229    pub fn has_global_failure(&self, state_changeset: &StateChangeset) -> bool {
1230        if Self::has_pending_global_failure(state_changeset) {
1231            return true;
1232        }
1233
1234        self.backend()
1235            .storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
1236            .is_ok_and(|failed_slot| !failed_slot.is_zero())
1237    }
1238
1239    /// Creates the environment to use when executing a transaction in a test context
1240    ///
1241    /// If using a backend with cheatcodes, `tx.gas_price` and `block.number` will be overwritten by
1242    /// the cheatcode state in between calls.
1243    pub fn prepare_call_env(
1244        &self,
1245        caller: Address,
1246        kind: TxKind,
1247        data: Bytes,
1248        value: U256,
1249    ) -> (EvmEnvFor<FEN>, TxEnvFor<FEN>) {
1250        let mut cfg_env = self.evm_env.cfg_env.clone();
1251        cfg_env.spec = self.spec_id();
1252
1253        // We always set the gas price to 0 so we can execute the transaction regardless of
1254        // network conditions - the actual gas price is kept in `self.block` and is applied
1255        // by the cheatcode handler if it is enabled
1256        let mut block_env = self.evm_env.block_env.clone();
1257        block_env.set_basefee(0);
1258        block_env.set_gas_limit(self.gas_limit);
1259
1260        let mut tx_env = self.tx_env.clone();
1261        tx_env.set_caller(caller);
1262        tx_env.set_kind(kind);
1263        tx_env.set_data(data);
1264        tx_env.set_value(value);
1265        // As above, we set the gas price to 0.
1266        tx_env.set_gas_price(0);
1267        tx_env.set_gas_priority_fee(None);
1268        tx_env.set_gas_limit(self.gas_limit);
1269        tx_env.set_chain_id(Some(self.evm_env.cfg_env.chain_id));
1270        // The EIP-8037 create charge checks the address for the transaction nonce, and the nonce
1271        // check is disabled, so a create uses the caller's current nonce.
1272        if kind.is_create()
1273            && cfg_env.is_amsterdam_eip2780_enabled()
1274            && let Ok(nonce) = self.get_nonce(caller)
1275        {
1276            tx_env.set_nonce(nonce);
1277        }
1278
1279        (EvmEnv { cfg_env, block_env }, tx_env)
1280    }
1281
1282    pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
1283    where
1284        C::Return: Default,
1285    {
1286        self.call_sol(CALLER, to, args, U256::ZERO, None)
1287            .map(|c| c.decoded_result)
1288            .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
1289            .unwrap_or_default()
1290    }
1291}
1292
1293/// Represents the context after an execution error occurred.
1294#[derive(Debug, thiserror::Error)]
1295#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
1296pub struct ExecutionErr<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1297    /// The raw result of the call.
1298    pub raw: RawCallResult<FEN>,
1299    /// The revert reason.
1300    pub reason: String,
1301}
1302
1303impl<FEN: FoundryEvmNetwork> std::ops::Deref for ExecutionErr<FEN> {
1304    type Target = RawCallResult<FEN>;
1305
1306    #[inline]
1307    fn deref(&self) -> &Self::Target {
1308        &self.raw
1309    }
1310}
1311
1312impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for ExecutionErr<FEN> {
1313    #[inline]
1314    fn deref_mut(&mut self) -> &mut Self::Target {
1315        &mut self.raw
1316    }
1317}
1318
1319#[derive(Debug, thiserror::Error)]
1320pub enum EvmError<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1321    /// Error which occurred during execution of a transaction.
1322    #[error(transparent)]
1323    Execution(Box<ExecutionErr<FEN>>),
1324    /// Error which occurred during ABI encoding/decoding.
1325    #[error(transparent)]
1326    Abi(#[from] alloy_dyn_abi::Error),
1327    /// Error caused which occurred due to calling the `skip` cheatcode.
1328    #[error("{0}")]
1329    Skip(SkipReason),
1330    /// Any other error.
1331    #[error("{0}")]
1332    Eyre(
1333        #[from]
1334        #[source]
1335        eyre::Report,
1336    ),
1337}
1338
1339impl<FEN: FoundryEvmNetwork> From<ExecutionErr<FEN>> for EvmError<FEN> {
1340    fn from(err: ExecutionErr<FEN>) -> Self {
1341        Self::Execution(Box::new(err))
1342    }
1343}
1344
1345impl<FEN: FoundryEvmNetwork> From<alloy_sol_types::Error> for EvmError<FEN> {
1346    fn from(err: alloy_sol_types::Error) -> Self {
1347        Self::Abi(err.into())
1348    }
1349}
1350
1351/// The result of a deployment.
1352#[derive(Debug)]
1353pub struct DeployResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1354    /// The raw result of the deployment.
1355    pub raw: RawCallResult<FEN>,
1356    /// The address of the deployed contract
1357    pub address: Address,
1358}
1359
1360impl<FEN: FoundryEvmNetwork> std::ops::Deref for DeployResult<FEN> {
1361    type Target = RawCallResult<FEN>;
1362
1363    #[inline]
1364    fn deref(&self) -> &Self::Target {
1365        &self.raw
1366    }
1367}
1368
1369impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for DeployResult<FEN> {
1370    #[inline]
1371    fn deref_mut(&mut self) -> &mut Self::Target {
1372        &mut self.raw
1373    }
1374}
1375
1376impl<FEN: FoundryEvmNetwork> From<DeployResult<FEN>> for RawCallResult<FEN> {
1377    fn from(d: DeployResult<FEN>) -> Self {
1378        d.raw
1379    }
1380}
1381
1382/// The result of a raw call.
1383#[derive(Debug)]
1384pub struct RawCallResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1385    /// The status of the call
1386    pub exit_reason: Option<InstructionResult>,
1387    /// Whether the call was halted by the execution cancellation inspector.
1388    pub execution_cancelled: bool,
1389    /// Whether the call reverted or not
1390    pub reverted: bool,
1391    /// Whether the call includes a snapshot failure
1392    ///
1393    /// This is tracked separately from revert because a snapshot failure can occur without a
1394    /// revert, since assert failures are stored in a global variable (ds-test legacy)
1395    pub has_state_snapshot_failure: bool,
1396    /// The raw result of the call.
1397    pub result: Bytes,
1398    /// The gas used for the call
1399    pub gas_used: u64,
1400    /// Refunded gas
1401    pub gas_refunded: u64,
1402    /// The initial gas stipend for the transaction
1403    pub stipend: u64,
1404    /// The logs emitted during the call
1405    pub logs: Vec<Log>,
1406    /// The labels assigned to addresses during the call
1407    pub labels: AddressHashMap<String>,
1408    /// The traces of the call
1409    pub traces: Option<SparsedTraceArena>,
1410    /// Runtime bytecodes for contracts seen in the trace, used by debug source mapping.
1411    pub debug_bytecodes: AddressHashMap<Bytes>,
1412    /// The line coverage info collected during the call
1413    pub line_coverage: Option<HitMaps>,
1414    /// The edge coverage info collected during the call
1415    pub edge_coverage: Option<EdgeCoverage>,
1416    /// EVM comparison operands collected during the call.
1417    pub evm_cmp_values: Option<Vec<CmpOperands>>,
1418    /// Observed sub-calls collected during the call.
1419    pub observed_calls: Vec<ObservedCall>,
1420    /// Sancov edge coverage from instrumented native Rust crates (e.g. precompiles).
1421    /// Tracked separately from EVM edge coverage to avoid ID-space collisions.
1422    pub sancov_coverage: Option<Vec<u8>>,
1423    /// Comparison operands captured via sancov trace-cmp callbacks.
1424    pub sancov_cmp_values: Option<Vec<foundry_evm_sancov::CmpSample>>,
1425    /// Scripted transactions generated from this call
1426    pub transactions: Option<BroadcastableTransactions<FEN::Network>>,
1427    /// The changeset of the state.
1428    pub state_changeset: StateChangeset,
1429    /// The `EvmEnv` after the call
1430    pub evm_env: EvmEnvFor<FEN>,
1431    /// The `TxEnv` after the call
1432    pub tx_env: TxEnvFor<FEN>,
1433    /// The cheatcode states after execution
1434    pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
1435    /// The raw output of the execution
1436    pub out: Option<Output>,
1437    /// The active fork's block number after execution, if any.
1438    pub fork_block_number: Option<u64>,
1439    /// The chisel state
1440    pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
1441    pub reverter: Option<Address>,
1442    /// Revert payloads minted by the `skip` cheatcode during this call.
1443    ///
1444    /// Moved out of the cheatcode state on conversion since `commit` moves that state back into
1445    /// the executor before results are classified.
1446    pub skip_payloads: Vec<Bytes>,
1447}
1448
1449impl<FEN: FoundryEvmNetwork> Default for RawCallResult<FEN> {
1450    fn default() -> Self {
1451        Self {
1452            exit_reason: None,
1453            execution_cancelled: false,
1454            reverted: false,
1455            has_state_snapshot_failure: false,
1456            result: Bytes::new(),
1457            gas_used: 0,
1458            gas_refunded: 0,
1459            stipend: 0,
1460            logs: Vec::new(),
1461            labels: HashMap::default(),
1462            traces: None,
1463            debug_bytecodes: HashMap::default(),
1464            line_coverage: None,
1465            edge_coverage: None,
1466            evm_cmp_values: None,
1467            observed_calls: Vec::new(),
1468            sancov_coverage: None,
1469            sancov_cmp_values: None,
1470            transactions: None,
1471            state_changeset: HashMap::default(),
1472            evm_env: EvmEnv::default(),
1473            tx_env: TxEnvFor::<FEN>::default(),
1474            cheatcodes: Default::default(),
1475            out: None,
1476            fork_block_number: None,
1477            chisel_state: None,
1478            reverter: None,
1479            skip_payloads: Vec::new(),
1480        }
1481    }
1482}
1483
1484impl<FEN: FoundryEvmNetwork> RawCallResult<FEN> {
1485    /// Unpacks an EVM result.
1486    pub fn from_evm_result(r: Result<Self, EvmError<FEN>>) -> eyre::Result<(Self, Option<String>)> {
1487        match r {
1488            Ok(r) => Ok((r, None)),
1489            Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
1490            Err(e) => Err(e.into()),
1491        }
1492    }
1493
1494    /// Returns the skip reason if this call reverted with a genuine `vm.skip` payload.
1495    ///
1496    /// The revert data must byte-equal a payload recorded by the skip cheatcode during this call;
1497    /// a matching `FOUNDRY::SKIP` prefix alone (user-crafted revert data) does not count.
1498    pub fn skip_reason(&self) -> Option<SkipReason> {
1499        if !self.reverted || !self.skip_payloads.contains(&self.result) {
1500            return None;
1501        }
1502        SkipReason::decode(&self.result)
1503    }
1504
1505    /// Converts the result of the call into an `EvmError`.
1506    pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError<FEN> {
1507        if let Some(reason) = self.skip_reason() {
1508            return EvmError::Skip(reason);
1509        }
1510        let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
1511        EvmError::Execution(Box::new(self.into_execution_error(reason)))
1512    }
1513
1514    /// Converts the result of the call into an `ExecutionErr`.
1515    pub const fn into_execution_error(self, reason: String) -> ExecutionErr<FEN> {
1516        ExecutionErr { raw: self, reason }
1517    }
1518
1519    /// Returns an `EvmError` if the call failed, otherwise returns `self`.
1520    pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError<FEN>> {
1521        if let Some(reason) = self.exit_reason
1522            && reason.is_ok()
1523        {
1524            Ok(self)
1525        } else {
1526            Err(self.into_evm_error(rd))
1527        }
1528    }
1529
1530    /// Decodes the result of the call with the given function.
1531    pub fn into_decoded_result(
1532        mut self,
1533        func: &Function,
1534        rd: Option<&RevertDecoder>,
1535    ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
1536        self = self.into_result(rd)?;
1537        let mut result = func.abi_decode_output(&self.result)?;
1538        let decoded_result =
1539            if result.len() == 1 { result.pop().unwrap() } else { DynSolValue::Tuple(result) };
1540        Ok(CallResult { raw: self, decoded_result })
1541    }
1542
1543    /// Returns the transactions generated from this call.
1544    pub fn transactions(&self) -> Option<&BroadcastableTransactions<FEN::Network>> {
1545        self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
1546    }
1547
1548    /// Update provided history map with edge coverage info collected during this call.
1549    pub fn merge_edge_coverage(
1550        &mut self,
1551        history_map: &mut Vec<u8>,
1552        edge_indices: &mut EdgeIndexMap,
1553    ) -> (bool, bool) {
1554        let mut new_coverage = false;
1555        let mut is_edge = false;
1556        if let Some(x) = &mut self.edge_coverage {
1557            match x {
1558                EdgeCoverage::Hash(x) => {
1559                    if history_map.len() < x.len() {
1560                        history_map.resize(x.len(), 0);
1561                    }
1562                    // Iterate over the current map and the history map together and update
1563                    // the history map, if we discover some new coverage, report true
1564                    for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1565                        Self::merge_edge_count(*curr, hist, &mut new_coverage, &mut is_edge);
1566
1567                        // Hash reuses its map; collision-free drains hits.
1568                        *curr = 0;
1569                    }
1570                }
1571                EdgeCoverage::CollisionFree(hits) => {
1572                    for hit in hits.drain(..) {
1573                        let edge_index = edge_indices.edge_index(hit.edge);
1574                        if history_map.len() <= edge_index {
1575                            history_map.resize(edge_index + 1, 0);
1576                        }
1577                        Self::merge_edge_count(
1578                            hit.count,
1579                            &mut history_map[edge_index],
1580                            &mut new_coverage,
1581                            &mut is_edge,
1582                        );
1583                    }
1584                }
1585            }
1586        }
1587        (new_coverage, is_edge)
1588    }
1589
1590    const fn merge_edge_count(
1591        curr: u8,
1592        hist: &mut u8,
1593        new_coverage: &mut bool,
1594        is_edge: &mut bool,
1595    ) {
1596        let Some(bucket) = Self::bin_count(curr) else {
1597            return;
1598        };
1599
1600        // If the old record for this edge pair is lower, update
1601        if *hist < bucket {
1602            if *hist == 0 {
1603                // Counts as an edge the first time we see it, otherwise it's a feature.
1604                *is_edge = true;
1605            }
1606            *hist = bucket;
1607            *new_coverage = true;
1608        }
1609    }
1610
1611    /// Convert a hitcount into an AFL-style bucket.
1612    /// <https://github.com/h0mbre/Lucid/blob/3026e7323c52b30b3cf12563954ac1eaa9c6981e/src/coverage.rs#L57-L85>
1613    const fn bin_count(count: u8) -> Option<u8> {
1614        match count {
1615            0 => None,
1616            1 => Some(1),
1617            2 => Some(2),
1618            3 => Some(4),
1619            4..=7 => Some(8),
1620            8..=15 => Some(16),
1621            16..=31 => Some(32),
1622            32..=127 => Some(64),
1623            128..=255 => Some(128),
1624        }
1625    }
1626
1627    /// Update provided history map with sancov coverage info collected during this call.
1628    /// Uses AFL-style hitcount binning.
1629    pub fn merge_sancov_coverage(&mut self, history_map: &mut Vec<u8>) -> (bool, bool) {
1630        let mut new_coverage = false;
1631        let mut is_edge = false;
1632        if let Some(x) = &mut self.sancov_coverage {
1633            if history_map.len() < x.len() {
1634                history_map.resize(x.len(), 0);
1635            }
1636            for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1637                if *curr > 0 {
1638                    if let Some(bucket) = Self::bin_count(*curr)
1639                        && *hist < bucket
1640                    {
1641                        if *hist == 0 {
1642                            is_edge = true;
1643                        }
1644                        *hist = bucket;
1645                        new_coverage = true;
1646                    }
1647                    *curr = 0;
1648                }
1649            }
1650        }
1651        (new_coverage, is_edge)
1652    }
1653
1654    /// Merge both EVM and sancov coverage into their respective history maps.
1655    /// Returns `(new_coverage, is_edge)` — true if either domain produced new coverage.
1656    pub fn merge_all_coverage(
1657        &mut self,
1658        evm_history: &mut Vec<u8>,
1659        evm_edge_indices: &mut EdgeIndexMap,
1660        sancov_history: &mut Vec<u8>,
1661    ) -> (bool, bool) {
1662        let (new_evm, edge_evm) = self.merge_edge_coverage(evm_history, evm_edge_indices);
1663        let (new_san, edge_san) = self.merge_sancov_coverage(sancov_history);
1664        (new_evm || new_san, edge_evm || edge_san)
1665    }
1666}
1667
1668/// The result of a call.
1669pub struct CallResult<T = DynSolValue, FEN: FoundryEvmNetwork = EthEvmNetwork> {
1670    /// The raw result of the call.
1671    pub raw: RawCallResult<FEN>,
1672    /// The decoded result of the call.
1673    pub decoded_result: T,
1674}
1675
1676impl<T, FEN: FoundryEvmNetwork> std::ops::Deref for CallResult<T, FEN> {
1677    type Target = RawCallResult<FEN>;
1678
1679    #[inline]
1680    fn deref(&self) -> &Self::Target {
1681        &self.raw
1682    }
1683}
1684
1685impl<T, FEN: FoundryEvmNetwork> std::ops::DerefMut for CallResult<T, FEN> {
1686    #[inline]
1687    fn deref_mut(&mut self) -> &mut Self::Target {
1688        &mut self.raw
1689    }
1690}
1691
1692pub(crate) fn calculate_stipend(tx_env: &impl Transaction, cfg: &impl Cfg) -> u64 {
1693    calculate_initial_gas(tx_env, cfg).initial_total_gas()
1694}
1695
1696/// Returns the intrinsic gas and EIP-7623 calldata floor of a transaction.
1697pub(crate) fn calculate_initial_gas(
1698    tx_env: &impl Transaction,
1699    cfg: &impl Cfg,
1700) -> InitialAndFloorGas {
1701    let eip2780 = cfg.is_amsterdam_eip2780_enabled().then(|| Eip2780TxInfo {
1702        value: tx_env.value(),
1703        is_self_transfer: matches!(tx_env.kind(), TxKind::Call(to) if to == tx_env.caller()),
1704    });
1705    revm::interpreter::gas::calculate_initial_tx_gas_for_tx(tx_env, cfg.spec().into(), eip2780)
1706}
1707
1708/// Converts the data aggregated in the `inspector` and `call` to a `RawCallResult`.
1709fn convert_executed_result<FEN: FoundryEvmNetwork, H: IntoInstructionResult>(
1710    evm_env: EvmEnvFor<FEN>,
1711    tx_env: TxEnvFor<FEN>,
1712    mut inspector: InspectorStack<FEN>,
1713    ResultAndState { result, state: state_changeset }: ResultAndState<H>,
1714    db: &dyn DatabaseRef<Error = DatabaseError>,
1715    has_state_snapshot_failure: bool,
1716    fork_block_number: Option<u64>,
1717) -> eyre::Result<RawCallResult<FEN>> {
1718    let execution_cancelled = inspector.execution_cancelled();
1719    let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1720        ExecutionResult::Success { reason, gas, output, logs } => {
1721            (reason.into(), gas.final_refunded(), gas.tx_gas_used(), Some(output), logs)
1722        }
1723        ExecutionResult::Revert { gas, output, logs } => {
1724            (InstructionResult::Revert, 0_u64, gas.tx_gas_used(), Some(Output::Call(output)), logs)
1725        }
1726        ExecutionResult::Halt { reason, gas, logs } => {
1727            (reason.into_instruction_result(), 0_u64, gas.tx_gas_used(), None, logs)
1728        }
1729    };
1730    let stipend = calculate_stipend(&tx_env, evm_env.cfg_env());
1731
1732    let result = match &out {
1733        Some(Output::Call(data)) => data.clone(),
1734        _ => Bytes::new(),
1735    };
1736    let observed_calls = inspector
1737        .inner
1738        .fuzzer
1739        .as_mut()
1740        .map(|fuzzer| fuzzer.take_observed_calls())
1741        .unwrap_or_default();
1742
1743    let InspectorData {
1744        mut logs,
1745        labels,
1746        traces,
1747        line_coverage,
1748        edge_coverage,
1749        evm_cmp_values,
1750        mut cheatcodes,
1751        chisel_state,
1752        reverter,
1753    } = inspector.collect();
1754    let fork_block_number = cheatcodes
1755        .as_ref()
1756        .and_then(|cheats| cheats.fork_block_number_override)
1757        .or(fork_block_number);
1758    let debug_bytecodes = collect_debug_bytecodes(traces.as_ref(), db);
1759
1760    if logs.is_empty() {
1761        logs = exec_logs;
1762    }
1763
1764    let transactions = cheatcodes
1765        .as_ref()
1766        .map(|c| c.broadcastable_transactions.clone())
1767        .filter(|txs| !txs.is_empty());
1768    let skip_payloads =
1769        cheatcodes.as_mut().map(|c| std::mem::take(&mut c.skip_payloads)).unwrap_or_default();
1770
1771    Ok(RawCallResult {
1772        exit_reason: Some(exit_reason),
1773        execution_cancelled,
1774        reverted: !exit_reason.is_ok(),
1775        has_state_snapshot_failure,
1776        result,
1777        gas_used,
1778        gas_refunded,
1779        stipend,
1780        logs,
1781        labels,
1782        traces,
1783        debug_bytecodes,
1784        line_coverage,
1785        edge_coverage,
1786        evm_cmp_values,
1787        observed_calls,
1788        sancov_coverage: None,
1789        sancov_cmp_values: None,
1790        transactions,
1791        state_changeset,
1792        evm_env,
1793        tx_env,
1794        cheatcodes,
1795        out,
1796        fork_block_number,
1797        chisel_state,
1798        reverter,
1799        skip_payloads,
1800    })
1801}
1802
1803fn collect_debug_bytecodes(
1804    traces: Option<&SparsedTraceArena>,
1805    db: &dyn DatabaseRef<Error = DatabaseError>,
1806) -> AddressHashMap<Bytes> {
1807    let mut bytecodes = HashMap::default();
1808    let Some(traces) = traces else { return bytecodes };
1809
1810    for node in traces.arena.nodes() {
1811        let address = node.trace.address;
1812        if bytecodes.contains_key(&address) {
1813            continue;
1814        }
1815
1816        let Ok(Some(account)) = db.basic_ref(address) else { continue };
1817        let code: Option<Bytecode> =
1818            account.code.or_else(|| db.code_by_hash_ref(account.code_hash).ok());
1819        let code: Bytes = code.map(|code| code.original_bytes()).unwrap_or_default();
1820
1821        if !code.is_empty() {
1822            bytecodes.insert(address, code);
1823        }
1824    }
1825
1826    bytecodes
1827}
1828
1829/// Timer for a fuzz test.
1830pub struct FuzzTestTimer {
1831    /// Inner fuzz test timer - (test start time, test duration).
1832    inner: Option<(Instant, Duration)>,
1833}
1834
1835impl FuzzTestTimer {
1836    pub fn new(timeout: Option<u32>) -> Self {
1837        Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1838    }
1839
1840    /// Whether the fuzz test timer is enabled.
1841    pub const fn is_enabled(&self) -> bool {
1842        self.inner.is_some()
1843    }
1844
1845    /// Whether the current fuzz test timed out and should be stopped.
1846    pub fn is_timed_out(&self) -> bool {
1847        self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1848    }
1849}
1850
1851/// Helper struct to enable early exit behavior: when one test fails or run is interrupted,
1852/// all other tests stop early.
1853#[derive(Clone, Debug)]
1854pub struct EarlyExit {
1855    /// Shared atomic flag set to `true` when a failure occurs or ctrl-c received.
1856    inner: Arc<AtomicBool>,
1857    /// Whether to exit early on test failure (fail-fast mode).
1858    fail_fast: bool,
1859}
1860
1861impl EarlyExit {
1862    pub fn new(fail_fast: bool) -> Self {
1863        Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1864    }
1865
1866    /// Records a test failure. Only triggers early exit if fail-fast mode is enabled.
1867    pub fn record_failure(&self) {
1868        if self.fail_fast {
1869            self.inner.store(true, Ordering::Relaxed);
1870        }
1871    }
1872
1873    /// Records a Ctrl-C interrupt. Always triggers early exit.
1874    pub fn record_ctrl_c(&self) {
1875        self.inner.store(true, Ordering::Relaxed);
1876    }
1877
1878    /// Whether tests should stop and exit early.
1879    pub fn should_stop(&self) -> bool {
1880        self.inner.load(Ordering::Relaxed)
1881    }
1882}
1883
1884/// Shared cancellation state for an active EVM execution.
1885#[derive(Clone, Debug)]
1886pub(crate) enum EvmExecutionCancellation {
1887    /// Cancellation driven only by the process-wide early-exit signal.
1888    EarlyExit(EarlyExit),
1889    /// Cancellation driven by the complete invariant campaign stop condition.
1890    Campaign { early_exit: EarlyExit, stop: Arc<AtomicBool>, deadline: Option<Instant> },
1891}
1892
1893impl EvmExecutionCancellation {
1894    pub(crate) const fn early_exit(early_exit: EarlyExit) -> Self {
1895        Self::EarlyExit(early_exit)
1896    }
1897
1898    pub(crate) const fn campaign(
1899        early_exit: EarlyExit,
1900        stop: Arc<AtomicBool>,
1901        deadline: Option<Instant>,
1902    ) -> Self {
1903        Self::Campaign { early_exit, stop, deadline }
1904    }
1905
1906    /// Returns whether execution should stop, optionally polling a campaign deadline.
1907    pub(crate) fn should_stop(&self, poll_deadline: bool) -> bool {
1908        match self {
1909            Self::EarlyExit(early_exit) => early_exit.should_stop(),
1910            Self::Campaign { early_exit, stop, deadline } => {
1911                if early_exit.should_stop() || stop.load(Ordering::Relaxed) {
1912                    return true;
1913                }
1914                if poll_deadline && deadline.is_some_and(|deadline| Instant::now() > deadline) {
1915                    stop.store(true, Ordering::Relaxed);
1916                    return true;
1917                }
1918                false
1919            }
1920        }
1921    }
1922
1923    pub(crate) fn request_stop(&self) {
1924        if let Self::Campaign { stop, .. } = self {
1925            stop.store(true, Ordering::Relaxed);
1926        }
1927    }
1928
1929    /// Returns whether a campaign stop was requested or its deadline was observed.
1930    pub(crate) fn stop_requested(&self) -> bool {
1931        matches!(self, Self::Campaign { stop, .. } if stop.load(Ordering::Relaxed))
1932    }
1933
1934    pub(crate) const fn early_exit_ref(&self) -> &EarlyExit {
1935        match self {
1936            Self::EarlyExit(early_exit) | Self::Campaign { early_exit, .. } => early_exit,
1937        }
1938    }
1939}
1940
1941/// Returns whether a nested revert can be ignored when fail-on-revert is disabled.
1942#[inline]
1943pub fn should_ignore_revert(
1944    fail_on_revert: bool,
1945    target: Address,
1946    reverter: Option<Address>,
1947    extra_cheatcode_addresses: &[Address],
1948) -> bool {
1949    !fail_on_revert
1950        && reverter.is_some_and(|reverter| {
1951            reverter != target
1952                && reverter != CHEATCODE_ADDRESS
1953                && !extra_cheatcode_addresses.contains(&reverter)
1954        })
1955}
1956
1957/// Derives the deterministic RNG seed for a fuzz or invariant worker.
1958///
1959/// Worker 0 keeps the configured seed, so a single worker replays the same sequence.
1960pub(crate) fn worker_seed(seed: U256, worker_id: u32) -> U256 {
1961    if worker_id == 0 {
1962        seed
1963    } else {
1964        let seed_data = [&seed.to_be_bytes::<32>()[..], &worker_id.to_be_bytes()[..]].concat();
1965        keccak256(seed_data).into()
1966    }
1967}
1968
1969#[cfg(test)]
1970mod tests {
1971    use super::*;
1972    use crate::inspectors::{EdgeCovHit, EdgeKey};
1973    use foundry_cheatcodes::{
1974        CheatsConfig,
1975        Vm::{blobhashesCall, mockCallRevert_1Call, revertToStateCall, snapshotStateCall},
1976    };
1977    use foundry_config::Config;
1978    use foundry_evm_core::{constants::MAGIC_SKIP, evm::TempoEvmNetwork, opts::EvmOpts};
1979    use foundry_evm_hardforks::TempoHardfork;
1980    use foundry_evm_traces::InternalTraceMode;
1981    use revm::context::CfgEnv;
1982    use std::{sync::mpsc, thread};
1983    use tempo_revm::{TempoBlockEnv, TempoTxEnv};
1984
1985    #[cfg(not(feature = "monad"))]
1986    use revm::context::TxEnv;
1987
1988    #[cfg(feature = "base")]
1989    use foundry_evm_core::evm::BaseEvmNetwork;
1990    #[cfg(feature = "base")]
1991    use foundry_evm_hardforks::BaseSpecId;
1992
1993    #[cfg(feature = "monad")]
1994    use foundry_evm_core::constants::MONAD_CHEATCODE_ADDRESS;
1995
1996    fn dense_call(edge: EdgeKey) -> RawCallResult {
1997        RawCallResult {
1998            edge_coverage: Some(EdgeCoverage::CollisionFree(vec![EdgeCovHit { edge, count: 1 }])),
1999            ..Default::default()
2000        }
2001    }
2002
2003    #[test]
2004    fn nested_revert_is_ignored_only_when_allowed() {
2005        let target = Address::repeat_byte(0x11);
2006        let nested = Address::repeat_byte(0x22);
2007
2008        assert!(should_ignore_revert(false, target, Some(nested), &[]));
2009        assert!(!should_ignore_revert(true, target, Some(nested), &[]));
2010        assert!(!should_ignore_revert(false, target, Some(target), &[]));
2011        assert!(!should_ignore_revert(false, target, Some(CHEATCODE_ADDRESS), &[]));
2012        assert!(!should_ignore_revert(false, target, None, &[]));
2013    }
2014
2015    #[cfg(feature = "monad")]
2016    #[test]
2017    fn network_cheatcode_revert_handling_is_monad_specific() {
2018        let target = Address::repeat_byte(0x11);
2019
2020        assert!(should_ignore_revert(false, target, Some(MONAD_CHEATCODE_ADDRESS), &[]));
2021        assert!(!should_ignore_revert(
2022            false,
2023            target,
2024            Some(MONAD_CHEATCODE_ADDRESS),
2025            &[MONAD_CHEATCODE_ADDRESS],
2026        ));
2027    }
2028
2029    #[cfg(feature = "monad")]
2030    #[test]
2031    fn executor_tooling_follows_concrete_builder() {
2032        let ethereum = ExecutorBuilder::<EthEvmNetwork>::new().build(
2033            EvmEnv::default(),
2034            TxEnv::default(),
2035            Backend::spawn(None).unwrap(),
2036            NetworkConfigs::with_monad(),
2037        );
2038        assert!(ethereum.backend().networks().is_monad());
2039        assert!(!ethereum.backend().is_persistent(&MONAD_CHEATCODE_ADDRESS));
2040
2041        let monad = ExecutorBuilder::<MonadEvmNetwork>::new().build(
2042            EvmEnv::<MonadHardfork>::default(),
2043            TxEnv::default(),
2044            Backend::spawn(None).unwrap(),
2045            NetworkConfigs::with_monad(),
2046        );
2047        assert!(monad.inspector().networks.is_monad());
2048        assert!(monad.backend().networks().is_monad());
2049        assert!(monad.backend().is_persistent(&MONAD_CHEATCODE_ADDRESS));
2050    }
2051
2052    #[test]
2053    fn tempo_labels_follow_concrete_builder() {
2054        let ethereum = ExecutorBuilder::<EthEvmNetwork>::new().build(
2055            EvmEnv::default(),
2056            TxEnv::default(),
2057            Backend::spawn(None).unwrap(),
2058            NetworkConfigs::with_tempo(),
2059        );
2060        assert!(ethereum.inspector().tempo_labels.is_none());
2061
2062        let tempo = ExecutorBuilder::<TempoEvmNetwork>::new().build(
2063            EvmEnv::<TempoHardfork, TempoBlockEnv>::default(),
2064            TempoTxEnv::default(),
2065            Backend::spawn(None).unwrap(),
2066            NetworkConfigs::default(),
2067        );
2068        assert!(tempo.inspector().tempo_labels.is_some());
2069    }
2070
2071    #[test]
2072    fn collision_free_edge_merge_uses_stable_indices() {
2073        let first =
2074            EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(10) };
2075        let second =
2076            EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(20) };
2077        let mut history = Vec::new();
2078        let mut edge_indices = EdgeIndexMap::default();
2079
2080        assert_eq!(
2081            dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
2082            (true, true)
2083        );
2084        assert_eq!(history, [1]);
2085
2086        assert_eq!(
2087            dense_call(second).merge_edge_coverage(&mut history, &mut edge_indices),
2088            (true, true)
2089        );
2090        assert_eq!(history, [1, 1]);
2091
2092        assert_eq!(
2093            dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
2094            (false, false)
2095        );
2096        assert_eq!(history, [1, 1]);
2097    }
2098
2099    #[test]
2100    fn collision_free_edge_merge_handles_sparse_observation_indices() {
2101        let first =
2102            EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(10) };
2103        let second =
2104            EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(20) };
2105        let mut edge_indices = EdgeIndexMap::default();
2106        edge_indices.edge_index(first);
2107        edge_indices.edge_index(second);
2108        let mut history = Vec::new();
2109
2110        assert_eq!(
2111            dense_call(second).merge_edge_coverage(&mut history, &mut edge_indices),
2112            (true, true)
2113        );
2114        assert_eq!(history, [0, 1]);
2115    }
2116
2117    #[test]
2118    fn cheatcode_skip_payload_is_classified_as_skip() {
2119        let raw = RawCallResult::<EthEvmNetwork> {
2120            reverted: true,
2121            result: Bytes::from_static(b"FOUNDRY::SKIPwith reason"),
2122            skip_payloads: vec![Bytes::from_static(b"FOUNDRY::SKIPwith reason")],
2123            ..Default::default()
2124        };
2125
2126        let err = raw.into_evm_error(None);
2127        assert!(matches!(err, EvmError::Skip(_)));
2128    }
2129
2130    #[test]
2131    fn forged_skip_payload_is_execution_error() {
2132        let raw = RawCallResult::<EthEvmNetwork> {
2133            reverted: true,
2134            result: Bytes::from_static(MAGIC_SKIP),
2135            reverter: Some(CHEATCODE_ADDRESS),
2136            ..Default::default()
2137        };
2138
2139        let err = raw.into_evm_error(None);
2140        assert!(matches!(err, EvmError::Execution(_)));
2141    }
2142
2143    #[cfg(feature = "base")]
2144    #[test]
2145    fn base_block_replay_rejects_eip8130_before_execution() {
2146        for target_is_eip8130 in [true, false] {
2147            let mut executor = ExecutorBuilder::<BaseEvmNetwork>::new().build(
2148                EvmEnv::<BaseSpecId>::default(),
2149                TxEnvFor::<BaseEvmNetwork>::default(),
2150                Backend::spawn(None).unwrap(),
2151                NetworkConfigs::with_base(),
2152            );
2153            let unsupported = TxEnvFor::<BaseEvmNetwork> {
2154                eip8130: Some(
2155                    serde_json::from_value(serde_json::json!({
2156                        "signed": {
2157                            "tx": {
2158                                "chainId": 8453, "sender": null, "nonceKey": "0x0",
2159                                "nonceSequence": 0, "validAfter": 0, "validBefore": 0,
2160                                "maxPriorityFeePerGas": "0x0", "maxFeePerGas": "0x0",
2161                                "gasLimit": 100000, "accountChanges": [], "calls": [],
2162                                "metadata": "0x", "payer": null
2163                            },
2164                            "senderAuth": "0x", "payerAuth": "0x"
2165                        },
2166                        "mode": "Verified", "simulation_sender_actor_id": null
2167                    }))
2168                    .unwrap(),
2169                ),
2170                ..Default::default()
2171            };
2172            let ordinary = TxEnvFor::<BaseEvmNetwork>::default();
2173            let hash = B256::repeat_byte(1);
2174            let (target, prefix, expected) = if target_is_eip8130 {
2175                (
2176                    unsupported,
2177                    vec![(hash, ordinary)],
2178                    "EIP-8130 target transactions are not supported by tooling replay".to_string(),
2179                )
2180            } else {
2181                (
2182                    ordinary.clone(),
2183                    vec![(B256::ZERO, ordinary), (hash, unsupported)],
2184                    format!(
2185                        "EIP-8130 prefix transaction {hash} is not supported by tooling replay"
2186                    ),
2187                )
2188            };
2189            let error = executor
2190                .transact_with_ordinary_block_replay(
2191                    EvmEnv::<BaseSpecId>::default(),
2192                    target,
2193                    prefix,
2194                )
2195                .unwrap_err();
2196            assert_eq!(error.to_string(), expected);
2197            assert_eq!(executor.get_nonce(Address::ZERO).unwrap(), 0);
2198        }
2199    }
2200
2201    #[test]
2202    fn block_replay_commits_prefix_and_traces_only_target() {
2203        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2204        let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
2205            EvmEnv::default(),
2206            TxEnv::default(),
2207            backend,
2208            NetworkConfigs::default(),
2209        );
2210        executor.set_balance(CALLER, U256::MAX).unwrap();
2211        executor.set_trace_requirements(TraceRequirements::none().with_calls(true));
2212
2213        let address = Address::repeat_byte(0x11);
2214        // Increment slot zero and return its new value.
2215        executor
2216            .set_code(
2217                address,
2218                Bytecode::new_raw(Bytes::from_static(&[
2219                    0x60, 0x00, 0x54, 0x60, 0x01, 0x01, 0x80, 0x60, 0x00, 0x55, 0x60, 0x00, 0x52,
2220                    0x60, 0x20, 0x60, 0x00, 0xf3,
2221                ])),
2222            )
2223            .unwrap();
2224        let prefix = TxEnv {
2225            caller: CALLER,
2226            gas_limit: 100_000,
2227            kind: TxKind::Call(address),
2228            ..Default::default()
2229        };
2230        let reverted_create = TxEnv {
2231            nonce: 1,
2232            kind: TxKind::Create,
2233            data: Bytes::from_static(&[0x5f, 0x5f, 0xfd]),
2234            ..prefix.clone()
2235        };
2236        let target = TxEnv { nonce: 2, ..prefix.clone() };
2237
2238        let result = executor
2239            .transact_with_ordinary_block_replay(
2240                EvmEnv::default(),
2241                target,
2242                vec![(B256::repeat_byte(1), prefix), (B256::repeat_byte(2), reverted_create)],
2243            )
2244            .unwrap();
2245
2246        assert_eq!(result.result, Bytes::from(B256::with_last_byte(2).0));
2247        assert_eq!(result.tx_env.nonce, 2);
2248        assert_eq!(executor.get_nonce(CALLER).unwrap(), 3);
2249        assert_eq!(executor.backend().storage_ref(address, U256::ZERO).unwrap(), U256::from(2));
2250        assert_eq!(result.traces.unwrap().arena.nodes().len(), 1);
2251    }
2252
2253    #[test]
2254    fn block_replay_initializes_create_target_from_canonical_nonce() {
2255        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2256        let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
2257            EvmEnv::default(),
2258            TxEnv::default(),
2259            backend,
2260            NetworkConfigs::default(),
2261        );
2262        executor.set_balance(CALLER, U256::MAX).unwrap();
2263
2264        let prefix = TxEnv {
2265            caller: CALLER,
2266            gas_limit: 100_000,
2267            kind: TxKind::Call(Address::repeat_byte(0x11)),
2268            ..Default::default()
2269        };
2270        let target = TxEnv {
2271            nonce: 1,
2272            kind: TxKind::Create,
2273            data: Bytes::from_static(&[0x00]),
2274            ..prefix.clone()
2275        };
2276        let expected = CALLER.create(1);
2277
2278        let result = executor
2279            .transact_with_ordinary_block_replay(
2280                EvmEnv::default(),
2281                target,
2282                vec![(B256::repeat_byte(1), prefix)],
2283            )
2284            .unwrap();
2285
2286        assert!(
2287            matches!(result.out, Some(Output::Create(_, Some(address))) if address == expected)
2288        );
2289        assert!(executor.backend().is_persistent(&expected));
2290        assert!(executor.backend().has_cheatcode_access(&expected));
2291    }
2292
2293    #[test]
2294    fn block_replay_preserves_successful_prefix_deployment() {
2295        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2296        let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
2297            EvmEnv::default(),
2298            TxEnv::default(),
2299            backend,
2300            NetworkConfigs::default(),
2301        );
2302        executor.set_balance(CALLER, U256::MAX).unwrap();
2303
2304        let deployed = CALLER.create(0);
2305        let prefix = TxEnv {
2306            caller: CALLER,
2307            gas_limit: 100_000,
2308            kind: TxKind::Create,
2309            data: Bytes::from_static(&[0x00]),
2310            ..Default::default()
2311        };
2312        let target = TxEnv { nonce: 1, kind: TxKind::Call(deployed), ..prefix.clone() };
2313
2314        executor
2315            .transact_with_ordinary_block_replay(
2316                EvmEnv::default(),
2317                target,
2318                vec![(B256::repeat_byte(1), prefix)],
2319            )
2320            .unwrap();
2321
2322        assert!(executor.backend().is_persistent(&deployed));
2323    }
2324
2325    #[cfg(feature = "monad")]
2326    #[test]
2327    fn block_replay_executes_monad_system_prefix() {
2328        use foundry_evm_core::evm::MonadEvmNetwork;
2329
2330        let backend = Backend::<MonadEvmNetwork>::spawn(None).unwrap();
2331        let mut executor = ExecutorBuilder::<MonadEvmNetwork>::default().gas_limit(1 << 20).build(
2332            EvmEnv::<MonadHardfork>::default(),
2333            TxEnv::default(),
2334            backend,
2335            NetworkConfigs::with_monad(),
2336        );
2337        executor.set_balance(CALLER, U256::MAX).unwrap();
2338
2339        let system_address = alloy_primitives::address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e");
2340        let staking_address =
2341            alloy_primitives::address!("0000000000000000000000000000000000001000");
2342        let selector = keccak256("syscallSnapshot()");
2343        let system = TxEnv {
2344            caller: system_address,
2345            gas_limit: 0,
2346            kind: TxKind::Call(staking_address),
2347            data: Bytes::copy_from_slice(&selector[..4]),
2348            chain_id: None,
2349            ..Default::default()
2350        };
2351        let target = TxEnv {
2352            caller: CALLER,
2353            gas_limit: 100_000,
2354            kind: TxKind::Call(Address::repeat_byte(0x11)),
2355            ..Default::default()
2356        };
2357        let system_chain = MonadChainContext::for_transaction(&system);
2358        let target_chain = MonadChainContext::for_transaction(&target);
2359
2360        let (result, used_system_replay) = executor
2361            .transact_with_monad_block_replay(
2362                EvmEnv::<MonadHardfork>::default(),
2363                target,
2364                target_chain,
2365                vec![(B256::repeat_byte(1), system, system_chain)],
2366                false,
2367            )
2368            .unwrap()
2369            .unwrap();
2370
2371        assert!(!used_system_replay);
2372        assert!(!result.reverted);
2373        assert_eq!(executor.get_nonce(system_address).unwrap(), 1);
2374    }
2375
2376    #[cfg(feature = "monad")]
2377    #[test]
2378    fn block_replay_executes_monad_system_target() {
2379        use foundry_evm_core::evm::MonadEvmNetwork;
2380
2381        let backend = Backend::<MonadEvmNetwork>::spawn(None).unwrap();
2382        let mut executor = ExecutorBuilder::<MonadEvmNetwork>::default().gas_limit(1 << 20).build(
2383            EvmEnv::<MonadHardfork>::default(),
2384            TxEnv::default(),
2385            backend,
2386            NetworkConfigs::with_monad(),
2387        );
2388
2389        let system_address = alloy_primitives::address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e");
2390        let staking_address =
2391            alloy_primitives::address!("0000000000000000000000000000000000001000");
2392        let selector = keccak256("syscallSnapshot()");
2393        let target = TxEnv {
2394            caller: system_address,
2395            gas_limit: 0,
2396            kind: TxKind::Call(staking_address),
2397            data: Bytes::copy_from_slice(&selector[..4]),
2398            chain_id: None,
2399            ..Default::default()
2400        };
2401        let target_chain = MonadChainContext::for_transaction(&target);
2402
2403        let (result, used_system_replay) = executor
2404            .transact_with_monad_block_replay(
2405                EvmEnv::<MonadHardfork>::default(),
2406                target,
2407                target_chain,
2408                Vec::new(),
2409                false,
2410            )
2411            .unwrap()
2412            .unwrap();
2413
2414        assert!(used_system_replay);
2415        assert!(!result.reverted);
2416        assert_eq!(executor.get_nonce(system_address).unwrap(), 1);
2417    }
2418
2419    #[test]
2420    fn mismatched_skip_payload_is_execution_error() {
2421        let raw = RawCallResult::<EthEvmNetwork> {
2422            reverted: true,
2423            result: Bytes::from_static(b"FOUNDRY::SKIPforged"),
2424            skip_payloads: vec![Bytes::from_static(b"FOUNDRY::SKIPgenuine")],
2425            ..Default::default()
2426        };
2427
2428        let err = raw.into_evm_error(None);
2429        assert!(matches!(err, EvmError::Execution(_)));
2430    }
2431
2432    #[test]
2433    fn set_spec_id_updates_spec_dependent_cfg_state() {
2434        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2435        let mut executor = ExecutorBuilder::default().build(
2436            EvmEnv::default(),
2437            TxEnv::default(),
2438            backend,
2439            NetworkConfigs::default(),
2440        );
2441
2442        executor.evm_env_mut().cfg_env.set_spec_and_mainnet_gas_params(SpecId::HOMESTEAD);
2443        assert_eq!(
2444            executor.evm_env().cfg_env.gas_params(),
2445            &revm::context_interface::cfg::GasParams::new_spec(SpecId::HOMESTEAD),
2446        );
2447        assert!(!executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
2448
2449        executor.set_spec_id(SpecId::AMSTERDAM);
2450
2451        assert_eq!(executor.spec_id(), SpecId::AMSTERDAM);
2452        assert_eq!(
2453            executor.evm_env().cfg_env.gas_params(),
2454            &revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM),
2455        );
2456        assert!(executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
2457    }
2458
2459    #[test]
2460    fn calculate_stipend_uses_eip2780_transaction_context() {
2461        let caller = Address::repeat_byte(0x11);
2462        let recipient = Address::repeat_byte(0x22);
2463        let mut tx = TxEnv { caller, kind: TxKind::Call(recipient), ..Default::default() };
2464        let cfg = CfgEnv::new_with_spec(SpecId::AMSTERDAM);
2465
2466        assert_eq!(
2467            calculate_stipend(&tx, &cfg),
2468            revm::primitives::eip2780::TX_BASE_COST
2469                + revm::primitives::eip8038::COLD_ACCOUNT_ACCESS
2470        );
2471        let cfg = cfg.with_enable_amsterdam_eip2780(false);
2472        assert_eq!(
2473            calculate_stipend(&tx, &cfg),
2474            revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM).tx_base_stipend()
2475        );
2476
2477        tx.kind = TxKind::Call(caller);
2478        let cfg = cfg.with_enable_amsterdam_eip2780(true);
2479        assert_eq!(calculate_stipend(&tx, &cfg), revm::primitives::eip2780::TX_BASE_COST);
2480    }
2481
2482    #[test]
2483    fn amsterdam_intercepted_create_refunds_state_gas() {
2484        let cheats_config =
2485            Arc::new(CheatsConfig::new(&Config::default(), EvmOpts::default(), None, None, false));
2486        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2487        let mut executor = ExecutorBuilder::default()
2488            .inspectors(|stack| stack.cheatcodes(cheats_config))
2489            .spec_id(SpecId::AMSTERDAM)
2490            .gas_limit(1_000_000)
2491            .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2492
2493        let target = Address::repeat_byte(0x11);
2494        // PUSH0; PUSH0; PUSH0; CREATE; POP; STOP.
2495        executor
2496            .set_code(
2497                target,
2498                Bytecode::new_raw(Bytes::from_static(&[0x5f, 0x5f, 0x5f, 0xf0, 0x50, 0x00])),
2499            )
2500            .unwrap();
2501        executor.inspector_mut().cheatcodes.as_mut().unwrap().core.intercept_next_create_call =
2502            true;
2503
2504        let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2505
2506        assert!(!result.reverted);
2507        assert!(
2508            result.gas_used
2509                < revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM)
2510                    .create_state_gas(),
2511            "failed CREATE retained its conditional state-gas charge"
2512        );
2513    }
2514
2515    #[test]
2516    fn amsterdam_mocked_call_revert_refunds_state_gas() {
2517        let cheats_config =
2518            Arc::new(CheatsConfig::new(&Config::default(), EvmOpts::default(), None, None, false));
2519        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2520        let mut executor = ExecutorBuilder::default()
2521            .inspectors(|stack| stack.cheatcodes(cheats_config))
2522            .spec_id(SpecId::AMSTERDAM)
2523            .gas_limit(1_000_000)
2524            .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2525
2526        let target = Address::repeat_byte(0x11);
2527        let mocked = Address::repeat_byte(0x22);
2528        executor
2529            .transact_raw(
2530                CALLER,
2531                CHEATCODE_ADDRESS,
2532                mockCallRevert_1Call {
2533                    callee: mocked,
2534                    msgValue: U256::ONE,
2535                    data: Bytes::new(),
2536                    revertData: Bytes::new(),
2537                }
2538                .abi_encode()
2539                .into(),
2540                U256::ZERO,
2541            )
2542            .unwrap();
2543        executor.set_code(mocked, Bytecode::default()).unwrap();
2544        executor.set_balance(target, U256::ONE).unwrap();
2545
2546        // PUSH0 x4; PUSH1 1; PUSH20 <mocked>; GAS; CALL; POP; STOP.
2547        let mut code = vec![0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x01, 0x73];
2548        code.extend_from_slice(mocked.as_slice());
2549        code.extend_from_slice(&[0x5a, 0xf1, 0x50, 0x00]);
2550        executor.set_code(target, Bytecode::new_raw(code.into())).unwrap();
2551
2552        let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2553
2554        assert!(!result.reverted);
2555        assert!(
2556            result.gas_used
2557                < revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM)
2558                    .new_account_state_gas(),
2559            "reverted mocked CALL retained its conditional state-gas charge"
2560        );
2561    }
2562
2563    #[test]
2564    fn amsterdam_deploy_charges_create_state_gas_at_caller_nonce() {
2565        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2566        let mut executor = ExecutorBuilder::default()
2567            .spec_id(SpecId::AMSTERDAM)
2568            .gas_limit(1_000_000)
2569            .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2570        // As in `EvmOpts`.
2571        executor.evm_env_mut().cfg_env.disable_nonce_check = true;
2572        let sender = Address::repeat_byte(0x33);
2573        executor.set_account_nonce(sender, 5).unwrap();
2574        // A non-empty address for nonce 0 must not hide the charge for the address for nonce 5.
2575        executor.set_balance(sender.create(0), U256::ONE).unwrap();
2576
2577        let result = executor.deploy(sender, Bytes::new(), U256::ZERO, None).unwrap();
2578
2579        assert_eq!(result.address, sender.create(5));
2580        // An empty creation pays 24000 regular gas plus 183600 EIP-8037 account-creation gas.
2581        assert_eq!(result.raw.gas_used, 207_600);
2582    }
2583
2584    #[test]
2585    fn set_trace_requirements_replaces_trace_mode_between_transactions() {
2586        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2587        let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
2588            EvmEnv::default(),
2589            TxEnv::default(),
2590            backend,
2591            NetworkConfigs::default(),
2592        );
2593        executor.evm_env_mut().cfg_env.disable_nonce_check = true;
2594        let target = Address::repeat_byte(0x11);
2595        // PUSH1 4; JUMP; STOP; JUMPDEST; PUSH1 1; PUSH1 0; SSTORE; STOP.
2596        executor
2597            .set_code(
2598                target,
2599                Bytecode::new_raw(Bytes::from_static(&[
2600                    0x60, 0x04, 0x56, 0x00, 0x5b, 0x60, 0x01, 0x60, 0x00, 0x55, 0x00,
2601                ])),
2602            )
2603            .unwrap();
2604
2605        let untraced = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2606        assert!(untraced.traces.is_none());
2607
2608        executor.set_trace_requirements(TraceRequirements::none().with_debug(true));
2609        let debug = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2610        let debug_steps = &debug.traces.as_ref().unwrap().nodes()[0].trace.steps;
2611        assert_eq!(debug_steps.len(), 7);
2612        assert!(debug_steps.iter().all(|step| step.stack.is_some() && step.memory.is_some()));
2613
2614        executor.set_trace_requirements(
2615            TraceRequirements::none().with_decode_internal(InternalTraceMode::Full),
2616        );
2617        let internal = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2618        let internal_steps = &internal.traces.as_ref().unwrap().nodes()[0].trace.steps;
2619        assert_eq!(internal_steps.len(), 2);
2620        assert!(internal_steps.iter().all(|step| step.stack.is_some() && step.memory.is_some()));
2621
2622        executor.set_trace_requirements(TraceRequirements::none());
2623        let untraced = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2624        assert!(untraced.traces.is_none());
2625    }
2626
2627    #[test]
2628    fn early_exit_interrupts_active_evm_execution() {
2629        const GAS_LIMIT: u64 = 1 << 24;
2630        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2631        let mut executor = ExecutorBuilder::default().gas_limit(GAS_LIMIT).build(
2632            EvmEnv::default(),
2633            TxEnv::default(),
2634            backend,
2635            NetworkConfigs::default(),
2636        );
2637        let early_exit = EarlyExit::new(false);
2638        executor.inspector_mut().set_early_exit(early_exit.clone());
2639
2640        let target = Address::repeat_byte(0x11);
2641        // JUMPDEST; PUSH1 0; JUMP loops until the inspector observes the interrupt.
2642        executor
2643            .set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x5b, 0x60, 0x00, 0x56])))
2644            .unwrap();
2645
2646        let (started_tx, started_rx) = mpsc::channel();
2647        let (result_tx, result_rx) = mpsc::channel();
2648        let handle = thread::spawn(move || {
2649            started_tx.send(()).unwrap();
2650            let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO);
2651            let _ = result_tx.send(result);
2652        });
2653
2654        started_rx.recv().unwrap();
2655        thread::sleep(Duration::from_millis(1));
2656        early_exit.record_ctrl_c();
2657
2658        let result = result_rx.recv_timeout(Duration::from_secs(1));
2659        handle.join().unwrap();
2660        let result = result.expect("active EVM execution did not observe early exit").unwrap();
2661        assert!(result.execution_cancelled);
2662        assert!(!result.reverted);
2663        assert_eq!(result.exit_reason, Some(InstructionResult::Stop));
2664        assert!(result.gas_used > 21_000, "interrupt fired before EVM execution started");
2665        assert!(result.gas_used < GAS_LIMIT, "execution ran out of gas instead of exiting");
2666    }
2667
2668    #[test]
2669    fn completed_execution_is_not_retroactively_cancelled() {
2670        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2671        let mut executor = ExecutorBuilder::default().gas_limit(1 << 24).build(
2672            EvmEnv::default(),
2673            TxEnv::default(),
2674            backend,
2675            NetworkConfigs::default(),
2676        );
2677        let early_exit = EarlyExit::new(false);
2678        executor.inspector_mut().set_early_exit(early_exit.clone());
2679
2680        let target = Address::repeat_byte(0x11);
2681        executor.set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x00]))).unwrap();
2682        let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2683        early_exit.record_ctrl_c();
2684
2685        assert!(!result.execution_cancelled);
2686        assert!(!result.reverted);
2687    }
2688
2689    #[test]
2690    fn campaign_deadline_interrupts_active_evm_execution() {
2691        const GAS_LIMIT: u64 = 1 << 24;
2692        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2693        let mut executor = ExecutorBuilder::default().gas_limit(GAS_LIMIT).build(
2694            EvmEnv::default(),
2695            TxEnv::default(),
2696            backend,
2697            NetworkConfigs::default(),
2698        );
2699        let cancellation = EvmExecutionCancellation::campaign(
2700            EarlyExit::new(false),
2701            Arc::new(AtomicBool::new(false)),
2702            Some(Instant::now()),
2703        );
2704        executor.inspector_mut().set_execution_cancellation(cancellation);
2705
2706        let target = Address::repeat_byte(0x11);
2707        executor
2708            .set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x5b, 0x60, 0x00, 0x56])))
2709            .unwrap();
2710
2711        let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2712        assert!(result.execution_cancelled);
2713        assert!(!result.reverted);
2714        assert_eq!(result.exit_reason, Some(InstructionResult::Stop));
2715        assert!(result.gas_used < GAS_LIMIT, "execution ran out of gas instead of timing out");
2716    }
2717
2718    #[test]
2719    fn beacon_root_system_call_does_not_persist_system_address() {
2720        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2721        let mut executor = ExecutorBuilder::default().spec_id(SpecId::CANCUN).build(
2722            EvmEnv::default(),
2723            TxEnv::default(),
2724            backend,
2725            NetworkConfigs::default(),
2726        );
2727        let before = executor.backend().basic_ref(SYSTEM_ADDRESS).unwrap();
2728
2729        executor.apply_beacon_root(B256::repeat_byte(0x11)).unwrap();
2730
2731        assert_eq!(
2732            executor.backend().basic_ref(SYSTEM_ADDRESS).unwrap(),
2733            before,
2734            "EIP-4788 system calls must not persist the system caller account",
2735        );
2736    }
2737
2738    /// Regression test for `pre_override_blob_hashes` restoration.
2739    ///
2740    /// Exercises the `None` arm of `sync_tx_after_env_override_restore` with
2741    /// *non-empty* native blob hashes, the case that cannot be reached from
2742    /// Solidity because no cheatcode sets `tx.blob_hashes` without also setting
2743    /// `env_overrides.blob_hashes`.
2744    ///
2745    /// Steps:
2746    /// 1. Seed `tx.blob_hashes = original` directly (no cheatcode -> override stays `None`).
2747    /// 2. `vm.snapshotState()` -> `inner_snapshot_state` captures `pre_override_blob_hashes =
2748    ///    Some(original)`.
2749    /// 3. `vm.blobhashes(new)` -> sets override (`Some`) AND real tx hashes.
2750    /// 4. `vm.revertToState(id)` -> restores override to `None`,
2751    ///    `sync_tx_after_env_override_restore` must restore `tx.blob_hashes = original`.
2752    #[test]
2753    fn pre_override_blob_hashes_restored_on_revert_to_state() {
2754        let cheats_config =
2755            Arc::new(CheatsConfig::new(&Config::default(), EvmOpts::default(), None, None, false));
2756
2757        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2758        let mut executor = ExecutorBuilder::default()
2759            .inspectors(|stack| stack.cheatcodes(cheats_config))
2760            .spec_id(SpecId::CANCUN)
2761            .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2762
2763        let original: Vec<B256> = vec![B256::repeat_byte(0x11), B256::repeat_byte(0x22)];
2764        executor.tx_env_mut().set_blob_hashes(original.clone());
2765
2766        let snap_result = executor
2767            .transact_raw(
2768                CALLER,
2769                CHEATCODE_ADDRESS,
2770                snapshotStateCall {}.abi_encode().into(),
2771                U256::ZERO,
2772            )
2773            .expect("snapshotState failed");
2774        assert!(!snap_result.reverted, "snapshotState reverted unexpectedly");
2775        let snapshot_id = U256::from_be_slice(&snap_result.result[..32]);
2776
2777        let new_hashes = vec![B256::repeat_byte(0x33)];
2778        let blob_result = executor
2779            .transact_raw(
2780                CALLER,
2781                CHEATCODE_ADDRESS,
2782                blobhashesCall { hashes: new_hashes }.abi_encode().into(),
2783                U256::ZERO,
2784            )
2785            .expect("blobhashes failed");
2786        assert!(!blob_result.reverted, "blobhashes reverted unexpectedly");
2787
2788        let revert_result = executor
2789            .transact_raw(
2790                CALLER,
2791                CHEATCODE_ADDRESS,
2792                revertToStateCall { snapshotId: snapshot_id }.abi_encode().into(),
2793                U256::ZERO,
2794            )
2795            .expect("revertToState failed");
2796        assert!(!revert_result.reverted, "revertToState reverted unexpectedly");
2797
2798        assert_eq!(
2799            revert_result.tx_env.blob_hashes, original,
2800            "pre_override_blob_hashes must be restored to original non-empty hashes, not []",
2801        );
2802        assert!(
2803            executor.inspector().cheatcodes.as_ref().unwrap().env_overrides.is_empty(),
2804            "inactive env overrides must be removed after restoring their metadata",
2805        );
2806    }
2807    #[cfg(feature = "monad")]
2808    #[test]
2809    fn concrete_system_replay_preserves_envelope_and_rejects_without_commit() {
2810        let mut executor = ExecutorBuilder::<MonadEvmNetwork>::new().gas_limit(1 << 20).build(
2811            EvmEnv::<MonadHardfork>::default(),
2812            TxEnv::default(),
2813            Backend::spawn(None).unwrap(),
2814            NetworkConfigs::with_monad(),
2815        );
2816        let caller = alloy_primitives::address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e");
2817        let selector = keccak256("syscallSnapshot()");
2818        let system = TxEnv {
2819            caller,
2820            gas_limit: 0,
2821            kind: TxKind::Call(alloy_primitives::address!(
2822                "0000000000000000000000000000000000001000"
2823            )),
2824            data: Bytes::copy_from_slice(&selector[..4]),
2825            chain_id: None,
2826            ..Default::default()
2827        };
2828        let result = executor
2829            .try_transact_system_replay_with_env_and_context(
2830                EvmEnv::<MonadHardfork>::default(),
2831                system.clone(),
2832                MonadChainContext::for_transaction(&system),
2833            )
2834            .unwrap()
2835            .unwrap();
2836        assert!(!result.reverted);
2837        assert_eq!(result.tx_env, system);
2838        assert_eq!(executor.get_nonce(caller).unwrap(), 1);
2839
2840        // Replaying the same canonical nonce must fail without committing another increment.
2841        assert!(
2842            executor
2843                .try_transact_system_replay_with_env_and_context(
2844                    EvmEnv::<MonadHardfork>::default(),
2845                    system.clone(),
2846                    MonadChainContext::for_transaction(&system),
2847                )
2848                .is_err()
2849        );
2850        assert_eq!(executor.get_nonce(caller).unwrap(), 1);
2851
2852        let ordinary = TxEnv { caller: CALLER, ..Default::default() };
2853        let nonce = executor.get_nonce(CALLER).unwrap();
2854        assert!(
2855            executor
2856                .try_transact_system_replay_with_env_and_context(
2857                    EvmEnv::<MonadHardfork>::default(),
2858                    ordinary.clone(),
2859                    MonadChainContext::for_transaction(&ordinary),
2860                )
2861                .unwrap()
2862                .is_none()
2863        );
2864        assert_eq!(executor.get_nonce(CALLER).unwrap(), nonce);
2865    }
2866
2867    #[test]
2868    fn worker_seed_preserves_master_seed_and_derives_workers() {
2869        let seed = U256::from(0x1234);
2870
2871        assert_eq!(worker_seed(seed, 0), seed);
2872        assert_ne!(worker_seed(seed, 1), seed);
2873        assert_ne!(worker_seed(seed, 1), worker_seed(seed, 2));
2874        assert_ne!(worker_seed(seed, 1), worker_seed(U256::from(0x5678), 1));
2875    }
2876}