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