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
5// TODO: The individual executors in this module should be moved into the respective crates, and the
6// `Executor` struct should be accessed using a trait defined in `foundry-evm-core` instead of
7// the concrete `Executor` type.
8
9use crate::{
10    Env,
11    inspectors::{
12        Cheatcodes, InspectorData, InspectorStack, cheatcodes::BroadcastableTransactions,
13    },
14};
15use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
16use alloy_json_abi::Function;
17use alloy_primitives::{
18    Address, Bytes, Log, TxKind, U256, keccak256,
19    map::{AddressHashMap, HashMap},
20};
21use alloy_sol_types::{SolCall, sol};
22use foundry_evm_core::{
23    EvmEnv,
24    backend::{Backend, BackendError, BackendResult, CowBackend, DatabaseExt, GLOBAL_FAIL_SLOT},
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    utils::StateChangeset,
31};
32use foundry_evm_coverage::HitMaps;
33use foundry_evm_traces::{SparsedTraceArena, TraceMode};
34use revm::{
35    bytecode::Bytecode,
36    context::{BlockEnv, TxEnv},
37    context_interface::{
38        result::{ExecutionResult, Output, ResultAndState},
39        transaction::SignedAuthorization,
40    },
41    database::{DatabaseCommit, DatabaseRef},
42    interpreter::{InstructionResult, return_ok},
43    primitives::hardfork::SpecId,
44};
45use std::{
46    borrow::Cow,
47    sync::{
48        Arc,
49        atomic::{AtomicBool, Ordering},
50    },
51    time::{Duration, Instant},
52};
53
54mod builder;
55pub use builder::ExecutorBuilder;
56
57pub mod fuzz;
58pub use fuzz::FuzzedExecutor;
59
60pub mod invariant;
61pub use invariant::InvariantExecutor;
62
63mod corpus;
64mod trace;
65
66pub use trace::TracingExecutor;
67
68const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
69
70sol! {
71    interface ITest {
72        function setUp() external;
73        function failed() external view returns (bool failed);
74
75        #[derive(Default)]
76        function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
77    }
78}
79
80/// EVM executor.
81///
82/// The executor can be configured with various `revm::Inspector`s, like `Cheatcodes`.
83///
84/// There are multiple ways of interacting the EVM:
85/// - `call`: executes a transaction, but does not persist any state changes; similar to `eth_call`,
86///   where the EVM state is unchanged after the call.
87/// - `transact`: executes a transaction and persists the state changes
88/// - `deploy`: a special case of `transact`, specialized for persisting the state of a contract
89///   deployment
90/// - `setup`: a special case of `transact`, used to set up the environment for a test
91#[derive(Clone, Debug)]
92pub struct Executor {
93    /// The underlying `revm::Database` that contains the EVM storage.
94    // Note: We do not store an EVM here, since we are really
95    // only interested in the database. REVM's `EVM` is a thin
96    // wrapper around spawning a new EVM on every call anyway,
97    // so the performance difference should be negligible.
98    backend: Backend,
99    /// The EVM environment.
100    env: Env,
101    /// The Revm inspector stack.
102    inspector: InspectorStack,
103    /// The gas limit for calls and deployments.
104    gas_limit: u64,
105    /// Whether `failed()` should be called on the test contract to determine if the test failed.
106    legacy_assertions: bool,
107}
108
109impl Executor {
110    /// Creates a new `ExecutorBuilder`.
111    #[inline]
112    pub fn builder() -> ExecutorBuilder {
113        ExecutorBuilder::new()
114    }
115
116    /// Creates a new `Executor` with the given arguments.
117    #[inline]
118    pub fn new(
119        mut backend: Backend,
120        env: Env,
121        inspector: InspectorStack,
122        gas_limit: u64,
123        legacy_assertions: bool,
124    ) -> Self {
125        // Need to create a non-empty contract on the cheatcodes address so `extcodesize` checks
126        // do not fail.
127        backend.insert_account_info(
128            CHEATCODE_ADDRESS,
129            revm::state::AccountInfo {
130                code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
131                // Also set the code hash manually so that it's not computed later.
132                // The code hash value does not matter, as long as it's not zero or `KECCAK_EMPTY`.
133                code_hash: CHEATCODE_CONTRACT_HASH,
134                ..Default::default()
135            },
136        );
137
138        Self { backend, env, inspector, gas_limit, legacy_assertions }
139    }
140
141    fn clone_with_backend(&self, backend: Backend) -> Self {
142        let env = Env::new_with_spec_id(
143            self.env.evm_env.cfg_env.clone(),
144            self.env.evm_env.block_env.clone(),
145            self.env.tx.clone(),
146            self.spec_id(),
147        );
148        Self::new(backend, env, self.inspector().clone(), self.gas_limit, self.legacy_assertions)
149    }
150
151    /// Returns a reference to the EVM backend.
152    pub fn backend(&self) -> &Backend {
153        &self.backend
154    }
155
156    /// Returns a mutable reference to the EVM backend.
157    pub fn backend_mut(&mut self) -> &mut Backend {
158        &mut self.backend
159    }
160
161    /// Returns a reference to the EVM environment.
162    pub fn env(&self) -> &Env {
163        &self.env
164    }
165
166    /// Returns a mutable reference to the EVM environment.
167    pub fn env_mut(&mut self) -> &mut Env {
168        &mut self.env
169    }
170
171    /// Returns a reference to the EVM inspector.
172    pub fn inspector(&self) -> &InspectorStack {
173        &self.inspector
174    }
175
176    /// Returns a mutable reference to the EVM inspector.
177    pub fn inspector_mut(&mut self) -> &mut InspectorStack {
178        &mut self.inspector
179    }
180
181    /// Returns the EVM spec ID.
182    pub fn spec_id(&self) -> SpecId {
183        self.env.evm_env.cfg_env.spec
184    }
185
186    /// Sets the EVM spec ID.
187    pub fn set_spec_id(&mut self, spec_id: SpecId) {
188        self.env.evm_env.cfg_env.spec = spec_id;
189    }
190
191    /// Returns the gas limit for calls and deployments.
192    ///
193    /// This is different from the gas limit imposed by the passed in environment, as those limits
194    /// are used by the EVM for certain opcodes like `gaslimit`.
195    pub fn gas_limit(&self) -> u64 {
196        self.gas_limit
197    }
198
199    /// Sets the gas limit for calls and deployments.
200    pub fn set_gas_limit(&mut self, gas_limit: u64) {
201        self.gas_limit = gas_limit;
202    }
203
204    /// Returns whether `failed()` should be called on the test contract to determine if the test
205    /// failed.
206    pub fn legacy_assertions(&self) -> bool {
207        self.legacy_assertions
208    }
209
210    /// Sets whether `failed()` should be called on the test contract to determine if the test
211    /// failed.
212    pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
213        self.legacy_assertions = legacy_assertions;
214    }
215
216    /// Creates the default CREATE2 Contract Deployer for local tests and scripts.
217    pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
218        trace!("deploying local create2 deployer");
219        let create2_deployer_account = self
220            .backend()
221            .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
222            .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
223
224        // If the deployer is not currently deployed, deploy the default one.
225        if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
226            let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
227
228            // Probably 0, but just in case.
229            let initial_balance = self.get_balance(creator)?;
230            self.set_balance(creator, U256::MAX)?;
231
232            let res =
233                self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
234            trace!(create2=?res.address, "deployed local create2 deployer");
235
236            self.set_balance(creator, initial_balance)?;
237        }
238        Ok(())
239    }
240
241    /// Set the balance of an account.
242    pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
243        trace!(?address, ?amount, "setting account balance");
244        let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
245        account.balance = amount;
246        self.backend_mut().insert_account_info(address, account);
247        Ok(())
248    }
249
250    /// Gets the balance of an account
251    pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
252        Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
253    }
254
255    /// Set the nonce of an account.
256    pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
257        let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
258        account.nonce = nonce;
259        self.backend_mut().insert_account_info(address, account);
260        self.env_mut().tx.nonce = nonce;
261        Ok(())
262    }
263
264    /// Returns the nonce of an account.
265    pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
266        Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
267    }
268
269    /// Set the code of an account.
270    pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
271        let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
272        account.code_hash = keccak256(code.original_byte_slice());
273        account.code = Some(code);
274        self.backend_mut().insert_account_info(address, account);
275        Ok(())
276    }
277
278    /// Set the storage of an account.
279    pub fn set_storage(
280        &mut self,
281        address: Address,
282        storage: HashMap<U256, U256>,
283    ) -> BackendResult<()> {
284        self.backend_mut().replace_account_storage(address, storage)?;
285        Ok(())
286    }
287
288    /// Set a storage slot of an account.
289    pub fn set_storage_slot(
290        &mut self,
291        address: Address,
292        slot: U256,
293        value: U256,
294    ) -> BackendResult<()> {
295        self.backend_mut().insert_account_storage(address, slot, value)?;
296        Ok(())
297    }
298
299    /// Returns `true` if the account has no code.
300    pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
301        Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
302    }
303
304    #[inline]
305    pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
306        self.inspector_mut().tracing(mode);
307        self
308    }
309
310    #[inline]
311    pub fn set_script_execution(&mut self, script_address: Address) {
312        self.inspector_mut().script(script_address);
313    }
314
315    #[inline]
316    pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
317        self.inspector_mut().print(trace_printer);
318        self
319    }
320
321    #[inline]
322    pub fn create2_deployer(&self) -> Address {
323        self.inspector().create2_deployer
324    }
325
326    /// Deploys a contract and commits the new state to the underlying database.
327    ///
328    /// Executes a CREATE transaction with the contract `code` and persistent database state
329    /// modifications.
330    pub fn deploy(
331        &mut self,
332        from: Address,
333        code: Bytes,
334        value: U256,
335        rd: Option<&RevertDecoder>,
336    ) -> Result<DeployResult, EvmError> {
337        let env = self.build_test_env(from, TxKind::Create, code, value);
338        self.deploy_with_env(env, rd)
339    }
340
341    /// Deploys a contract using the given `env` and commits the new state to the underlying
342    /// database.
343    ///
344    /// # Panics
345    ///
346    /// Panics if `env.tx.kind` is not `TxKind::Create(_)`.
347    #[instrument(name = "deploy", level = "debug", skip_all)]
348    pub fn deploy_with_env(
349        &mut self,
350        env: Env,
351        rd: Option<&RevertDecoder>,
352    ) -> Result<DeployResult, EvmError> {
353        assert!(
354            matches!(env.tx.kind, TxKind::Create),
355            "Expected create transaction, got {:?}",
356            env.tx.kind
357        );
358        trace!(sender=%env.tx.caller, "deploying contract");
359
360        let mut result = self.transact_with_env(env)?;
361        result = result.into_result(rd)?;
362        let Some(Output::Create(_, Some(address))) = result.out else {
363            panic!("Deployment succeeded, but no address was returned: {result:#?}");
364        };
365
366        // also mark this library as persistent, this will ensure that the state of the library is
367        // persistent across fork swaps in forking mode
368        self.backend_mut().add_persistent_account(address);
369
370        debug!(%address, "deployed contract");
371
372        Ok(DeployResult { raw: result, address })
373    }
374
375    /// Calls the `setUp()` function on a contract.
376    ///
377    /// This will commit any state changes to the underlying database.
378    ///
379    /// Ayn changes made during the setup call to env's block environment are persistent, for
380    /// example `vm.chainId()` will change the `block.chainId` for all subsequent test calls.
381    #[instrument(name = "setup", level = "debug", skip_all)]
382    pub fn setup(
383        &mut self,
384        from: Option<Address>,
385        to: Address,
386        rd: Option<&RevertDecoder>,
387    ) -> Result<RawCallResult, EvmError> {
388        trace!(?from, ?to, "setting up contract");
389
390        let from = from.unwrap_or(CALLER);
391        self.backend_mut().set_test_contract(to).set_caller(from);
392        let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
393        let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
394        res = res.into_result(rd)?;
395
396        // record any changes made to the block's environment during setup
397        self.env_mut().evm_env.block_env = res.env.evm_env.block_env.clone();
398        // and also the chainid, which can be set manually
399        self.env_mut().evm_env.cfg_env.chain_id = res.env.evm_env.cfg_env.chain_id;
400
401        let success =
402            self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
403        if !success {
404            return Err(res.into_execution_error("execution error".to_string()).into());
405        }
406
407        Ok(res)
408    }
409
410    /// Performs a call to an account on the current state of the VM.
411    pub fn call(
412        &self,
413        from: Address,
414        to: Address,
415        func: &Function,
416        args: &[DynSolValue],
417        value: U256,
418        rd: Option<&RevertDecoder>,
419    ) -> Result<CallResult, EvmError> {
420        let calldata = Bytes::from(func.abi_encode_input(args)?);
421        let result = self.call_raw(from, to, calldata, value)?;
422        result.into_decoded_result(func, rd)
423    }
424
425    /// Performs a call to an account on the current state of the VM.
426    pub fn call_sol<C: SolCall>(
427        &self,
428        from: Address,
429        to: Address,
430        args: &C,
431        value: U256,
432        rd: Option<&RevertDecoder>,
433    ) -> Result<CallResult<C::Return>, EvmError> {
434        let calldata = Bytes::from(args.abi_encode());
435        let mut raw = self.call_raw(from, to, calldata, value)?;
436        raw = raw.into_result(rd)?;
437        Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
438    }
439
440    /// Performs a call to an account on the current state of the VM.
441    pub fn transact(
442        &mut self,
443        from: Address,
444        to: Address,
445        func: &Function,
446        args: &[DynSolValue],
447        value: U256,
448        rd: Option<&RevertDecoder>,
449    ) -> Result<CallResult, EvmError> {
450        let calldata = Bytes::from(func.abi_encode_input(args)?);
451        let result = self.transact_raw(from, to, calldata, value)?;
452        result.into_decoded_result(func, rd)
453    }
454
455    /// Performs a raw call to an account on the current state of the VM.
456    pub fn call_raw(
457        &self,
458        from: Address,
459        to: Address,
460        calldata: Bytes,
461        value: U256,
462    ) -> eyre::Result<RawCallResult> {
463        let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
464        self.call_with_env(env)
465    }
466
467    /// Performs a raw call to an account on the current state of the VM with an EIP-7702
468    /// authorization list.
469    pub fn call_raw_with_authorization(
470        &mut self,
471        from: Address,
472        to: Address,
473        calldata: Bytes,
474        value: U256,
475        authorization_list: Vec<SignedAuthorization>,
476    ) -> eyre::Result<RawCallResult> {
477        let mut env = self.build_test_env(from, to.into(), calldata, value);
478        env.tx.set_signed_authorization(authorization_list);
479        env.tx.tx_type = 4;
480        self.call_with_env(env)
481    }
482
483    /// Performs a raw call to an account on the current state of the VM.
484    pub fn transact_raw(
485        &mut self,
486        from: Address,
487        to: Address,
488        calldata: Bytes,
489        value: U256,
490    ) -> eyre::Result<RawCallResult> {
491        let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
492        self.transact_with_env(env)
493    }
494
495    /// Performs a raw call to an account on the current state of the VM with an EIP-7702
496    /// authorization last.
497    pub fn transact_raw_with_authorization(
498        &mut self,
499        from: Address,
500        to: Address,
501        calldata: Bytes,
502        value: U256,
503        authorization_list: Vec<SignedAuthorization>,
504    ) -> eyre::Result<RawCallResult> {
505        let mut env = self.build_test_env(from, TxKind::Call(to), calldata, value);
506        env.tx.set_signed_authorization(authorization_list);
507        env.tx.tx_type = 4;
508        self.transact_with_env(env)
509    }
510
511    /// Execute the transaction configured in `env.tx`.
512    ///
513    /// The state after the call is **not** persisted.
514    #[instrument(name = "call", level = "debug", skip_all)]
515    pub fn call_with_env(&self, mut env: Env) -> eyre::Result<RawCallResult> {
516        let mut stack = self.inspector().clone();
517        let mut backend = CowBackend::new_borrowed(self.backend());
518        let result = backend.inspect(&mut env, stack.as_inspector())?;
519        convert_executed_result(env, stack, result, backend.has_state_snapshot_failure())
520    }
521
522    /// Execute the transaction configured in `env.tx`.
523    #[instrument(name = "transact", level = "debug", skip_all)]
524    pub fn transact_with_env(&mut self, mut env: Env) -> eyre::Result<RawCallResult> {
525        let mut stack = self.inspector().clone();
526        let backend = self.backend_mut();
527        let result = backend.inspect(&mut env, stack.as_inspector())?;
528        let mut result =
529            convert_executed_result(env, stack, result, backend.has_state_snapshot_failure())?;
530        self.commit(&mut result);
531        Ok(result)
532    }
533
534    /// Commit the changeset to the database and adjust `self.inspector_config` values according to
535    /// the executed call result.
536    ///
537    /// This should not be exposed to the user, as it should be called only by `transact*`.
538    #[instrument(name = "commit", level = "debug", skip_all)]
539    fn commit(&mut self, result: &mut RawCallResult) {
540        // Persist changes to db.
541        self.backend_mut().commit(result.state_changeset.clone());
542
543        // Persist cheatcode state.
544        self.inspector_mut().cheatcodes = result.cheatcodes.take();
545        if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
546            // Clear broadcastable transactions
547            cheats.broadcastable_transactions.clear();
548            cheats.ignored_traces.ignored.clear();
549
550            // if tracing was paused but never unpaused, we should begin next frame with tracing
551            // still paused
552            if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
553                *last_pause_call = (0, 0);
554            }
555        }
556
557        // Persist the changed environment.
558        self.inspector_mut().set_env(&result.env);
559    }
560
561    /// Returns `true` if a test can be considered successful.
562    ///
563    /// This is the same as [`Self::is_success`], but will consume the `state_changeset` map to use
564    /// internally when calling `failed()`.
565    pub fn is_raw_call_mut_success(
566        &self,
567        address: Address,
568        call_result: &mut RawCallResult,
569        should_fail: bool,
570    ) -> bool {
571        self.is_raw_call_success(
572            address,
573            Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
574            call_result,
575            should_fail,
576        )
577    }
578
579    /// Returns `true` if a test can be considered successful.
580    ///
581    /// This is the same as [`Self::is_success`], but intended for outcomes of [`Self::call_raw`].
582    pub fn is_raw_call_success(
583        &self,
584        address: Address,
585        state_changeset: Cow<'_, StateChangeset>,
586        call_result: &RawCallResult,
587        should_fail: bool,
588    ) -> bool {
589        if call_result.has_state_snapshot_failure {
590            // a failure occurred in a reverted snapshot, which is considered a failed test
591            return should_fail;
592        }
593        self.is_success(address, call_result.reverted, state_changeset, should_fail)
594    }
595
596    /// Returns `true` if a test can be considered successful.
597    ///
598    /// If the call succeeded, we also have to check the global and local failure flags.
599    ///
600    /// These are set by the test contract itself when an assertion fails, using the internal `fail`
601    /// function. The global flag is located in [`CHEATCODE_ADDRESS`] at slot [`GLOBAL_FAIL_SLOT`],
602    /// and the local flag is located in the test contract at an unspecified slot.
603    ///
604    /// This behavior is inherited from Dapptools, where initially only a public
605    /// `failed` variable was used to track test failures, and later, a global failure flag was
606    /// introduced to track failures across multiple contracts in
607    /// [ds-test#30](https://github.com/dapphub/ds-test/pull/30).
608    ///
609    /// The assumption is that the test runner calls `failed` on the test contract to determine if
610    /// it failed. However, we want to avoid this as much as possible, as it is relatively
611    /// expensive to set up an EVM call just for checking a single boolean flag.
612    ///
613    /// See:
614    /// - Newer DSTest: <https://github.com/dapphub/ds-test/blob/e282159d5170298eb2455a6c05280ab5a73a4ef0/src/test.sol#L47-L63>
615    /// - Older DSTest: <https://github.com/dapphub/ds-test/blob/9ca4ecd48862b40d7b0197b600713f64d337af12/src/test.sol#L38-L49>
616    /// - forge-std: <https://github.com/foundry-rs/forge-std/blob/19891e6a0b5474b9ea6827ddb90bb9388f7acfc0/src/StdAssertions.sol#L38-L44>
617    pub fn is_success(
618        &self,
619        address: Address,
620        reverted: bool,
621        state_changeset: Cow<'_, StateChangeset>,
622        should_fail: bool,
623    ) -> bool {
624        let success = self.is_success_raw(address, reverted, state_changeset);
625        should_fail ^ success
626    }
627
628    #[instrument(name = "is_success", level = "debug", skip_all)]
629    fn is_success_raw(
630        &self,
631        address: Address,
632        reverted: bool,
633        state_changeset: Cow<'_, StateChangeset>,
634    ) -> bool {
635        // The call reverted.
636        if reverted {
637            return false;
638        }
639
640        // A failure occurred in a reverted snapshot, which is considered a failed test.
641        if self.backend().has_state_snapshot_failure() {
642            return false;
643        }
644
645        // Check the global failure slot.
646        if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
647            && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
648            && !failed_slot.present_value().is_zero()
649        {
650            return false;
651        }
652        if let Ok(failed_slot) = self.backend().storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
653            && !failed_slot.is_zero()
654        {
655            return false;
656        }
657
658        if !self.legacy_assertions {
659            return true;
660        }
661
662        // Finally, resort to calling `DSTest::failed`.
663        {
664            // Construct a new bare-bones backend to evaluate success.
665            let mut backend = self.backend().clone_empty();
666
667            // We only clone the test contract and cheatcode accounts,
668            // that's all we need to evaluate success.
669            for address in [address, CHEATCODE_ADDRESS] {
670                let Ok(acc) = self.backend().basic_ref(address) else { return false };
671                backend.insert_account_info(address, acc.unwrap_or_default());
672            }
673
674            // If this test failed any asserts, then this changeset will contain changes
675            // `false -> true` for the contract's `failed` variable and the `globalFailure` flag
676            // in the state of the cheatcode address,
677            // which are both read when we call `"failed()(bool)"` in the next step.
678            backend.commit(state_changeset.into_owned());
679
680            // Check if a DSTest assertion failed
681            let executor = self.clone_with_backend(backend);
682            let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
683            match call {
684                Ok(CallResult { raw: _, decoded_result: failed }) => {
685                    trace!(failed, "DSTest::failed()");
686                    !failed
687                }
688                Err(err) => {
689                    trace!(%err, "failed to call DSTest::failed()");
690                    true
691                }
692            }
693        }
694    }
695
696    /// Creates the environment to use when executing a transaction in a test context
697    ///
698    /// If using a backend with cheatcodes, `tx.gas_price` and `block.number` will be overwritten by
699    /// the cheatcode state in between calls.
700    fn build_test_env(&self, caller: Address, kind: TxKind, data: Bytes, value: U256) -> Env {
701        Env {
702            evm_env: EvmEnv {
703                cfg_env: {
704                    let mut cfg = self.env().evm_env.cfg_env.clone();
705                    cfg.spec = self.spec_id();
706                    cfg
707                },
708                // We always set the gas price to 0 so we can execute the transaction regardless of
709                // network conditions - the actual gas price is kept in `self.block` and is applied
710                // by the cheatcode handler if it is enabled
711                block_env: BlockEnv {
712                    basefee: 0,
713                    gas_limit: self.gas_limit,
714                    ..self.env().evm_env.block_env.clone()
715                },
716            },
717            tx: TxEnv {
718                caller,
719                kind,
720                data,
721                value,
722                // As above, we set the gas price to 0.
723                gas_price: 0,
724                gas_priority_fee: None,
725                gas_limit: self.gas_limit,
726                chain_id: Some(self.env().evm_env.cfg_env.chain_id),
727                ..self.env().tx.clone()
728            },
729        }
730    }
731
732    pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
733    where
734        C::Return: Default,
735    {
736        self.call_sol(CALLER, to, args, U256::ZERO, None)
737            .map(|c| c.decoded_result)
738            .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
739            .unwrap_or_default()
740    }
741}
742
743/// Represents the context after an execution error occurred.
744#[derive(Debug, thiserror::Error)]
745#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
746pub struct ExecutionErr {
747    /// The raw result of the call.
748    pub raw: RawCallResult,
749    /// The revert reason.
750    pub reason: String,
751}
752
753impl std::ops::Deref for ExecutionErr {
754    type Target = RawCallResult;
755
756    #[inline]
757    fn deref(&self) -> &Self::Target {
758        &self.raw
759    }
760}
761
762impl std::ops::DerefMut for ExecutionErr {
763    #[inline]
764    fn deref_mut(&mut self) -> &mut Self::Target {
765        &mut self.raw
766    }
767}
768
769#[derive(Debug, thiserror::Error)]
770pub enum EvmError {
771    /// Error which occurred during execution of a transaction.
772    #[error(transparent)]
773    Execution(#[from] Box<ExecutionErr>),
774    /// Error which occurred during ABI encoding/decoding.
775    #[error(transparent)]
776    Abi(#[from] alloy_dyn_abi::Error),
777    /// Error caused which occurred due to calling the `skip` cheatcode.
778    #[error("{0}")]
779    Skip(SkipReason),
780    /// Any other error.
781    #[error("{0}")]
782    Eyre(
783        #[from]
784        #[source]
785        eyre::Report,
786    ),
787}
788
789impl From<ExecutionErr> for EvmError {
790    fn from(err: ExecutionErr) -> Self {
791        Self::Execution(Box::new(err))
792    }
793}
794
795impl From<alloy_sol_types::Error> for EvmError {
796    fn from(err: alloy_sol_types::Error) -> Self {
797        Self::Abi(err.into())
798    }
799}
800
801/// The result of a deployment.
802#[derive(Debug)]
803pub struct DeployResult {
804    /// The raw result of the deployment.
805    pub raw: RawCallResult,
806    /// The address of the deployed contract
807    pub address: Address,
808}
809
810impl std::ops::Deref for DeployResult {
811    type Target = RawCallResult;
812
813    #[inline]
814    fn deref(&self) -> &Self::Target {
815        &self.raw
816    }
817}
818
819impl std::ops::DerefMut for DeployResult {
820    #[inline]
821    fn deref_mut(&mut self) -> &mut Self::Target {
822        &mut self.raw
823    }
824}
825
826impl From<DeployResult> for RawCallResult {
827    fn from(d: DeployResult) -> Self {
828        d.raw
829    }
830}
831
832/// The result of a raw call.
833#[derive(Debug)]
834pub struct RawCallResult {
835    /// The status of the call
836    pub exit_reason: Option<InstructionResult>,
837    /// Whether the call reverted or not
838    pub reverted: bool,
839    /// Whether the call includes a snapshot failure
840    ///
841    /// This is tracked separately from revert because a snapshot failure can occur without a
842    /// revert, since assert failures are stored in a global variable (ds-test legacy)
843    pub has_state_snapshot_failure: bool,
844    /// The raw result of the call.
845    pub result: Bytes,
846    /// The gas used for the call
847    pub gas_used: u64,
848    /// Refunded gas
849    pub gas_refunded: u64,
850    /// The initial gas stipend for the transaction
851    pub stipend: u64,
852    /// The logs emitted during the call
853    pub logs: Vec<Log>,
854    /// The labels assigned to addresses during the call
855    pub labels: AddressHashMap<String>,
856    /// The traces of the call
857    pub traces: Option<SparsedTraceArena>,
858    /// The line coverage info collected during the call
859    pub line_coverage: Option<HitMaps>,
860    /// The edge coverage info collected during the call
861    pub edge_coverage: Option<Vec<u8>>,
862    /// Scripted transactions generated from this call
863    pub transactions: Option<BroadcastableTransactions>,
864    /// The changeset of the state.
865    pub state_changeset: StateChangeset,
866    /// The `revm::Env` after the call
867    pub env: Env,
868    /// The cheatcode states after execution
869    pub cheatcodes: Option<Box<Cheatcodes>>,
870    /// The raw output of the execution
871    pub out: Option<Output>,
872    /// The chisel state
873    pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
874    pub reverter: Option<Address>,
875}
876
877impl Default for RawCallResult {
878    fn default() -> Self {
879        Self {
880            exit_reason: None,
881            reverted: false,
882            has_state_snapshot_failure: false,
883            result: Bytes::new(),
884            gas_used: 0,
885            gas_refunded: 0,
886            stipend: 0,
887            logs: Vec::new(),
888            labels: HashMap::default(),
889            traces: None,
890            line_coverage: None,
891            edge_coverage: None,
892            transactions: None,
893            state_changeset: HashMap::default(),
894            env: Env::default(),
895            cheatcodes: Default::default(),
896            out: None,
897            chisel_state: None,
898            reverter: None,
899        }
900    }
901}
902
903impl RawCallResult {
904    /// Unpacks an EVM result.
905    pub fn from_evm_result(r: Result<Self, EvmError>) -> eyre::Result<(Self, Option<String>)> {
906        match r {
907            Ok(r) => Ok((r, None)),
908            Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
909            Err(e) => Err(e.into()),
910        }
911    }
912
913    /// Unpacks an execution result.
914    pub fn from_execution_result(r: Result<Self, ExecutionErr>) -> (Self, Option<String>) {
915        match r {
916            Ok(r) => (r, None),
917            Err(e) => (e.raw, Some(e.reason)),
918        }
919    }
920
921    /// Converts the result of the call into an `EvmError`.
922    pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError {
923        if let Some(reason) = SkipReason::decode(&self.result) {
924            return EvmError::Skip(reason);
925        }
926        let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
927        EvmError::Execution(Box::new(self.into_execution_error(reason)))
928    }
929
930    /// Converts the result of the call into an `ExecutionErr`.
931    pub fn into_execution_error(self, reason: String) -> ExecutionErr {
932        ExecutionErr { raw: self, reason }
933    }
934
935    /// Returns an `EvmError` if the call failed, otherwise returns `self`.
936    pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError> {
937        if let Some(reason) = self.exit_reason
938            && reason.is_ok()
939        {
940            Ok(self)
941        } else {
942            Err(self.into_evm_error(rd))
943        }
944    }
945
946    /// Decodes the result of the call with the given function.
947    pub fn into_decoded_result(
948        mut self,
949        func: &Function,
950        rd: Option<&RevertDecoder>,
951    ) -> Result<CallResult, EvmError> {
952        self = self.into_result(rd)?;
953        let mut result = func.abi_decode_output(&self.result)?;
954        let decoded_result = if result.len() == 1 {
955            result.pop().unwrap()
956        } else {
957            // combine results into a tuple
958            DynSolValue::Tuple(result)
959        };
960        Ok(CallResult { raw: self, decoded_result })
961    }
962
963    /// Returns the transactions generated from this call.
964    pub fn transactions(&self) -> Option<&BroadcastableTransactions> {
965        self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
966    }
967
968    /// Update provided history map with edge coverage info collected during this call.
969    /// Uses AFL binning algo <https://github.com/h0mbre/Lucid/blob/3026e7323c52b30b3cf12563954ac1eaa9c6981e/src/coverage.rs#L57-L85>
970    pub fn merge_edge_coverage(&mut self, history_map: &mut [u8]) -> (bool, bool) {
971        let mut new_coverage = false;
972        let mut is_edge = false;
973        if let Some(x) = &mut self.edge_coverage {
974            // Iterate over the current map and the history map together and update
975            // the history map, if we discover some new coverage, report true
976            for (curr, hist) in std::iter::zip(x, history_map) {
977                // If we got a hitcount of at least 1
978                if *curr > 0 {
979                    // Convert hitcount into bucket count
980                    let bucket = match *curr {
981                        0 => 0,
982                        1 => 1,
983                        2 => 2,
984                        3 => 4,
985                        4..=7 => 8,
986                        8..=15 => 16,
987                        16..=31 => 32,
988                        32..=127 => 64,
989                        128..=255 => 128,
990                    };
991
992                    // If the old record for this edge pair is lower, update
993                    if *hist < bucket {
994                        if *hist == 0 {
995                            // Counts as an edge the first time we see it, otherwise it's a feature.
996                            is_edge = true;
997                        }
998                        *hist = bucket;
999                        new_coverage = true;
1000                    }
1001
1002                    // Zero out the current map for next iteration.
1003                    *curr = 0;
1004                }
1005            }
1006        }
1007        (new_coverage, is_edge)
1008    }
1009}
1010
1011/// The result of a call.
1012pub struct CallResult<T = DynSolValue> {
1013    /// The raw result of the call.
1014    pub raw: RawCallResult,
1015    /// The decoded result of the call.
1016    pub decoded_result: T,
1017}
1018
1019impl std::ops::Deref for CallResult {
1020    type Target = RawCallResult;
1021
1022    #[inline]
1023    fn deref(&self) -> &Self::Target {
1024        &self.raw
1025    }
1026}
1027
1028impl std::ops::DerefMut for CallResult {
1029    #[inline]
1030    fn deref_mut(&mut self) -> &mut Self::Target {
1031        &mut self.raw
1032    }
1033}
1034
1035/// Converts the data aggregated in the `inspector` and `call` to a `RawCallResult`
1036fn convert_executed_result(
1037    env: Env,
1038    inspector: InspectorStack,
1039    ResultAndState { result, state: state_changeset }: ResultAndState,
1040    has_state_snapshot_failure: bool,
1041) -> eyre::Result<RawCallResult> {
1042    let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1043        ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => {
1044            (reason.into(), gas_refunded, gas_used, Some(output), logs)
1045        }
1046        ExecutionResult::Revert { gas_used, output } => {
1047            // Need to fetch the unused gas
1048            (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output)), vec![])
1049        }
1050        ExecutionResult::Halt { reason, gas_used } => {
1051            (reason.into(), 0_u64, gas_used, None, vec![])
1052        }
1053    };
1054    let gas = revm::interpreter::gas::calculate_initial_tx_gas(
1055        env.evm_env.cfg_env.spec,
1056        &env.tx.data,
1057        env.tx.kind.is_create(),
1058        env.tx.access_list.len().try_into()?,
1059        0,
1060        0,
1061    );
1062
1063    let result = match &out {
1064        Some(Output::Call(data)) => data.clone(),
1065        _ => Bytes::new(),
1066    };
1067
1068    let InspectorData {
1069        mut logs,
1070        labels,
1071        traces,
1072        line_coverage,
1073        edge_coverage,
1074        cheatcodes,
1075        chisel_state,
1076        reverter,
1077    } = inspector.collect();
1078
1079    if logs.is_empty() {
1080        logs = exec_logs;
1081    }
1082
1083    let transactions = cheatcodes
1084        .as_ref()
1085        .map(|c| c.broadcastable_transactions.clone())
1086        .filter(|txs| !txs.is_empty());
1087
1088    Ok(RawCallResult {
1089        exit_reason: Some(exit_reason),
1090        reverted: !matches!(exit_reason, return_ok!()),
1091        has_state_snapshot_failure,
1092        result,
1093        gas_used,
1094        gas_refunded,
1095        stipend: gas.initial_gas,
1096        logs,
1097        labels,
1098        traces,
1099        line_coverage,
1100        edge_coverage,
1101        transactions,
1102        state_changeset,
1103        env,
1104        cheatcodes,
1105        out,
1106        chisel_state,
1107        reverter,
1108    })
1109}
1110
1111/// Timer for a fuzz test.
1112pub struct FuzzTestTimer {
1113    /// Inner fuzz test timer - (test start time, test duration).
1114    inner: Option<(Instant, Duration)>,
1115}
1116
1117impl FuzzTestTimer {
1118    pub fn new(timeout: Option<u32>) -> Self {
1119        Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1120    }
1121
1122    /// Whether the fuzz test timer is enabled.
1123    pub fn is_enabled(&self) -> bool {
1124        self.inner.is_some()
1125    }
1126
1127    /// Whether the current fuzz test timed out and should be stopped.
1128    pub fn is_timed_out(&self) -> bool {
1129        self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1130    }
1131}
1132
1133/// Helper struct to enable fail fast behavior: when one test fails, all other tests stop early.
1134#[derive(Clone)]
1135pub struct FailFast {
1136    /// Shared atomic flag set to `true` when a failure occurs.
1137    /// None if fail-fast is disabled.
1138    inner: Option<Arc<AtomicBool>>,
1139}
1140
1141impl FailFast {
1142    pub fn new(fail_fast: bool) -> Self {
1143        Self { inner: fail_fast.then_some(Arc::new(AtomicBool::new(false))) }
1144    }
1145
1146    /// Returns `true` if fail-fast is enabled.
1147    pub fn is_enabled(&self) -> bool {
1148        self.inner.is_some()
1149    }
1150
1151    /// Sets the failure flag. Used by other tests to stop early.
1152    pub fn record_fail(&self) {
1153        if let Some(fail_fast) = &self.inner {
1154            fail_fast.store(true, Ordering::Relaxed);
1155        }
1156    }
1157
1158    /// Whether a failure has been recorded and test should stop.
1159    pub fn should_stop(&self) -> bool {
1160        self.inner.as_ref().map(|flag| flag.load(Ordering::Relaxed)).unwrap_or(false)
1161    }
1162}