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