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