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