foundry_cheatcodes/
inspector.rs

1//! Cheatcode EVM inspector.
2
3use crate::{
4    CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result,
5    Vm::{self, AccountAccess},
6    evm::{
7        DealRecord, GasRecord, RecordAccess, journaled_account,
8        mock::{MockCallDataContext, MockCallReturnData},
9        prank::Prank,
10    },
11    inspector::utils::CommonCreateInput,
12    script::{Broadcast, Wallets},
13    test::{
14        assume::AssumeNoRevert,
15        expect::{
16            self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
17            ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
18        },
19        revert_handlers,
20    },
21    utils::IgnoredTraces,
22};
23use alloy_consensus::BlobTransactionSidecar;
24use alloy_evm::eth::EthEvmContext;
25use alloy_network::TransactionBuilder4844;
26use alloy_primitives::{
27    Address, B256, Bytes, Log, TxKind, U256, hex,
28    map::{AddressHashMap, HashMap, HashSet},
29};
30use alloy_rpc_types::{
31    AccessList,
32    request::{TransactionInput, TransactionRequest},
33};
34use alloy_sol_types::{SolCall, SolInterface, SolValue};
35use foundry_common::{
36    SELECTOR_LEN, TransactionMaybeSigned,
37    mapping_slots::{MappingSlots, step as mapping_step},
38};
39use foundry_evm_core::{
40    Breakpoints, InspectorExt,
41    abi::Vm::stopExpectSafeMemoryCall,
42    backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
43    constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
44    evm::{FoundryEvm, new_evm_with_existing_context},
45};
46use foundry_evm_traces::{
47    TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
48};
49use foundry_wallets::multi_wallet::MultiWallet;
50use itertools::Itertools;
51use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
52use rand::Rng;
53use revm::{
54    Inspector, Journal,
55    bytecode::opcode as op,
56    context::{BlockEnv, JournalTr, LocalContext, TransactionType, result::EVMError},
57    context_interface::{CreateScheme, transaction::SignedAuthorization},
58    handler::FrameResult,
59    interpreter::{
60        CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host,
61        InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
62        interpreter_types::{Jumps, LoopControl, MemoryTr},
63    },
64    primitives::hardfork::SpecId,
65    state::EvmStorageSlot,
66};
67use serde_json::Value;
68use std::{
69    cmp::max,
70    collections::{BTreeMap, VecDeque},
71    fs::File,
72    io::BufReader,
73    ops::Range,
74    path::PathBuf,
75    sync::{Arc, OnceLock},
76};
77
78mod utils;
79
80pub mod analysis;
81pub use analysis::CheatcodeAnalysis;
82
83pub type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
84
85/// Helper trait for obtaining complete [revm::Inspector] instance from mutable reference to
86/// [Cheatcodes].
87///
88/// This is needed for cases when inspector itself needs mutable access to [Cheatcodes] state and
89/// allows us to correctly execute arbitrary EVM frames from inside cheatcode implementations.
90pub trait CheatcodesExecutor {
91    /// Core trait method accepting mutable reference to [Cheatcodes] and returning
92    /// [revm::Inspector].
93    fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
94
95    /// Obtains [FoundryEvm] instance and executes the given CREATE frame.
96    fn exec_create(
97        &mut self,
98        inputs: CreateInputs,
99        ccx: &mut CheatsCtxt,
100    ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
101        with_evm(self, ccx, |evm| {
102            evm.journaled_state.depth += 1;
103
104            let frame = FrameInput::Create(Box::new(inputs));
105
106            let outcome = match evm.run_execution(frame)? {
107                FrameResult::Call(_) => unreachable!(),
108                FrameResult::Create(create) => create,
109            };
110
111            evm.journaled_state.depth -= 1;
112
113            Ok(outcome)
114        })
115    }
116
117    fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) {
118        self.get_inspector(ccx.state).console_log(msg);
119    }
120
121    /// Returns a mutable reference to the tracing inspector if it is available.
122    fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
123        None
124    }
125}
126
127/// Constructs [FoundryEvm] and runs a given closure with it.
128fn with_evm<E, F, O>(
129    executor: &mut E,
130    ccx: &mut CheatsCtxt,
131    f: F,
132) -> Result<O, EVMError<DatabaseError>>
133where
134    E: CheatcodesExecutor + ?Sized,
135    F: for<'a, 'b> FnOnce(
136        &mut FoundryEvm<'a, &'b mut dyn InspectorExt>,
137    ) -> Result<O, EVMError<DatabaseError>>,
138{
139    let mut inspector = executor.get_inspector(ccx.state);
140    let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
141
142    let ctx = EthEvmContext {
143        block: ccx.ecx.block.clone(),
144        cfg: ccx.ecx.cfg.clone(),
145        tx: ccx.ecx.tx.clone(),
146        journaled_state: Journal {
147            inner: ccx.ecx.journaled_state.inner.clone(),
148            database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt,
149        },
150        local: LocalContext::default(),
151        chain: (),
152        error,
153    };
154
155    let mut evm = new_evm_with_existing_context(ctx, &mut *inspector);
156
157    let res = f(&mut evm)?;
158
159    let ctx = evm.into_context();
160    ccx.ecx.journaled_state.inner = ctx.journaled_state.inner;
161    ccx.ecx.block = ctx.block;
162    ccx.ecx.tx = ctx.tx;
163    ccx.ecx.cfg = ctx.cfg;
164    ccx.ecx.error = ctx.error;
165
166    Ok(res)
167}
168
169/// Basic implementation of [CheatcodesExecutor] that simply returns the [Cheatcodes] instance as an
170/// inspector.
171#[derive(Debug, Default, Clone, Copy)]
172struct TransparentCheatcodesExecutor;
173
174impl CheatcodesExecutor for TransparentCheatcodesExecutor {
175    fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
176        Box::new(cheats)
177    }
178}
179
180macro_rules! try_or_return {
181    ($e:expr) => {
182        match $e {
183            Ok(v) => v,
184            Err(_) => return,
185        }
186    };
187}
188
189/// Contains additional, test specific resources that should be kept for the duration of the test
190#[derive(Debug, Default)]
191pub struct TestContext {
192    /// Buffered readers for files opened for reading (path => BufReader mapping)
193    pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
194}
195
196/// Every time we clone `Context`, we want it to be empty
197impl Clone for TestContext {
198    fn clone(&self) -> Self {
199        Default::default()
200    }
201}
202
203impl TestContext {
204    /// Clears the context.
205    pub fn clear(&mut self) {
206        self.opened_read_files.clear();
207    }
208}
209
210/// Helps collecting transactions from different forks.
211#[derive(Clone, Debug)]
212pub struct BroadcastableTransaction {
213    /// The optional RPC URL.
214    pub rpc: Option<String>,
215    /// The transaction to broadcast.
216    pub transaction: TransactionMaybeSigned,
217}
218
219#[derive(Clone, Debug, Copy)]
220pub struct RecordDebugStepInfo {
221    /// The debug trace node index when the recording starts.
222    pub start_node_idx: usize,
223    /// The original tracer config when the recording starts.
224    pub original_tracer_config: TracingInspectorConfig,
225}
226
227/// Holds gas metering state.
228#[derive(Clone, Debug, Default)]
229pub struct GasMetering {
230    /// True if gas metering is paused.
231    pub paused: bool,
232    /// True if gas metering was resumed or reset during the test.
233    /// Used to reconcile gas when frame ends (if spent less than refunded).
234    pub touched: bool,
235    /// True if gas metering should be reset to frame limit.
236    pub reset: bool,
237    /// Stores paused gas frames.
238    pub paused_frames: Vec<Gas>,
239
240    /// The group and name of the active snapshot.
241    pub active_gas_snapshot: Option<(String, String)>,
242
243    /// Cache of the amount of gas used in previous call.
244    /// This is used by the `lastCallGas` cheatcode.
245    pub last_call_gas: Option<crate::Vm::Gas>,
246
247    /// True if gas recording is enabled.
248    pub recording: bool,
249    /// The gas used in the last frame.
250    pub last_gas_used: u64,
251    /// Gas records for the active snapshots.
252    pub gas_records: Vec<GasRecord>,
253}
254
255impl GasMetering {
256    /// Start the gas recording.
257    pub fn start(&mut self) {
258        self.recording = true;
259    }
260
261    /// Stop the gas recording.
262    pub fn stop(&mut self) {
263        self.recording = false;
264    }
265
266    /// Resume paused gas metering.
267    pub fn resume(&mut self) {
268        if self.paused {
269            self.paused = false;
270            self.touched = true;
271        }
272        self.paused_frames.clear();
273    }
274
275    /// Reset gas to limit.
276    pub fn reset(&mut self) {
277        self.paused = false;
278        self.touched = true;
279        self.reset = true;
280        self.paused_frames.clear();
281    }
282}
283
284/// Holds data about arbitrary storage.
285#[derive(Clone, Debug, Default)]
286pub struct ArbitraryStorage {
287    /// Mapping of arbitrary storage addresses to generated values (slot, arbitrary value).
288    /// (SLOADs return random value if storage slot wasn't accessed).
289    /// Changed values are recorded and used to copy storage to different addresses.
290    pub values: HashMap<Address, HashMap<U256, U256>>,
291    /// Mapping of address with storage copied to arbitrary storage address source.
292    pub copies: HashMap<Address, Address>,
293    /// Address with storage slots that should be overwritten even if previously set.
294    pub overwrites: HashSet<Address>,
295}
296
297impl ArbitraryStorage {
298    /// Marks an address with arbitrary storage.
299    pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
300        self.values.insert(*address, HashMap::default());
301        if overwrite {
302            self.overwrites.insert(*address);
303        } else {
304            self.overwrites.remove(address);
305        }
306    }
307
308    /// Maps an address that copies storage with the arbitrary storage address.
309    pub fn mark_copy(&mut self, from: &Address, to: &Address) {
310        if self.values.contains_key(from) {
311            self.copies.insert(*to, *from);
312        }
313    }
314
315    /// Saves arbitrary storage value for a given address:
316    /// - store value in changed values cache.
317    /// - update account's storage with given value.
318    pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) {
319        self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
320        if let Ok(mut account) = ecx.journaled_state.load_account(address) {
321            account.storage.insert(slot, EvmStorageSlot::new(data, 0));
322        }
323    }
324
325    /// Copies arbitrary storage value from source address to the given target address:
326    /// - if a value is present in arbitrary values cache, then update target storage and return
327    ///   existing value.
328    /// - if no value was yet generated for given slot, then save new value in cache and update both
329    ///   source and target storages.
330    pub fn copy(&mut self, ecx: Ecx, target: Address, slot: U256, new_value: U256) -> U256 {
331        let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
332        let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
333        let value = match storage_cache.get(&slot) {
334            Some(value) => *value,
335            None => {
336                storage_cache.insert(slot, new_value);
337                // Update source storage with new value.
338                if let Ok(mut source_account) = ecx.journaled_state.load_account(*source) {
339                    source_account.storage.insert(slot, EvmStorageSlot::new(new_value, 0));
340                }
341                new_value
342            }
343        };
344        // Update target storage with new value.
345        if let Ok(mut target_account) = ecx.journaled_state.load_account(target) {
346            target_account.storage.insert(slot, EvmStorageSlot::new(value, 0));
347        }
348        value
349    }
350}
351
352/// List of transactions that can be broadcasted.
353pub type BroadcastableTransactions = VecDeque<BroadcastableTransaction>;
354
355/// An EVM inspector that handles calls to various cheatcodes, each with their own behavior.
356///
357/// Cheatcodes can be called by contracts during execution to modify the VM environment, such as
358/// mocking addresses, signatures and altering call reverts.
359///
360/// Executing cheatcodes can be very powerful. Most cheatcodes are limited to evm internals, but
361/// there are also cheatcodes like `ffi` which can execute arbitrary commands or `writeFile` and
362/// `readFile` which can manipulate files of the filesystem. Therefore, several restrictions are
363/// implemented for these cheatcodes:
364/// - `ffi`, and file cheatcodes are _always_ opt-in (via foundry config) and never enabled by
365///   default: all respective cheatcode handlers implement the appropriate checks
366/// - File cheatcodes require explicit permissions which paths are allowed for which operation, see
367///   `Config.fs_permission`
368/// - Only permitted accounts are allowed to execute cheatcodes in forking mode, this ensures no
369///   contract deployed on the live network is able to execute cheatcodes by simply calling the
370///   cheatcode address: by default, the caller, test contract and newly deployed contracts are
371///   allowed to execute cheatcodes
372#[derive(Clone, Debug)]
373pub struct Cheatcodes {
374    /// Solar compiler instance, to grant syntactic and semantic analysis capabilities
375    pub analysis: Option<CheatcodeAnalysis>,
376
377    /// The block environment
378    ///
379    /// Used in the cheatcode handler to overwrite the block environment separately from the
380    /// execution block environment.
381    pub block: Option<BlockEnv>,
382
383    /// Currently active EIP-7702 delegations that will be consumed when building the next
384    /// transaction. Set by `vm.attachDelegation()` and consumed via `.take()` during
385    /// transaction construction.
386    pub active_delegations: Vec<SignedAuthorization>,
387
388    /// The active EIP-4844 blob that will be attached to the next call.
389    pub active_blob_sidecar: Option<BlobTransactionSidecar>,
390
391    /// The gas price.
392    ///
393    /// Used in the cheatcode handler to overwrite the gas price separately from the gas price
394    /// in the execution environment.
395    pub gas_price: Option<u128>,
396
397    /// Address labels
398    pub labels: AddressHashMap<String>,
399
400    /// Prank information, mapped to the call depth where pranks were added.
401    pub pranks: BTreeMap<usize, Prank>,
402
403    /// Expected revert information
404    pub expected_revert: Option<ExpectedRevert>,
405
406    /// Assume next call can revert and discard fuzz run if it does.
407    pub assume_no_revert: Option<AssumeNoRevert>,
408
409    /// Additional diagnostic for reverts
410    pub fork_revert_diagnostic: Option<RevertDiagnostic>,
411
412    /// Recorded storage reads and writes
413    pub accesses: RecordAccess,
414
415    /// Whether storage access recording is currently active
416    pub recording_accesses: bool,
417
418    /// Recorded account accesses (calls, creates) organized by relative call depth, where the
419    /// topmost vector corresponds to accesses at the depth at which account access recording
420    /// began. Each vector in the matrix represents a list of accesses at a specific call
421    /// depth. Once that call context has ended, the last vector is removed from the matrix and
422    /// merged into the previous vector.
423    pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
424
425    /// The information of the debug step recording.
426    pub record_debug_steps_info: Option<RecordDebugStepInfo>,
427
428    /// Recorded logs
429    pub recorded_logs: Option<Vec<crate::Vm::Log>>,
430
431    /// Mocked calls
432    // **Note**: inner must a BTreeMap because of special `Ord` impl for `MockCallDataContext`
433    pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
434
435    /// Mocked functions. Maps target address to be mocked to pair of (calldata, mock address).
436    pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
437
438    /// Expected calls
439    pub expected_calls: ExpectedCallTracker,
440    /// Expected emits
441    pub expected_emits: ExpectedEmitTracker,
442    /// Expected creates
443    pub expected_creates: Vec<ExpectedCreate>,
444
445    /// Map of context depths to memory offset ranges that may be written to within the call depth.
446    pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
447
448    /// Current broadcasting information
449    pub broadcast: Option<Broadcast>,
450
451    /// Scripting based transactions
452    pub broadcastable_transactions: BroadcastableTransactions,
453
454    /// Current EIP-2930 access lists.
455    pub access_list: Option<AccessList>,
456
457    /// Additional, user configurable context this Inspector has access to when inspecting a call.
458    pub config: Arc<CheatsConfig>,
459
460    /// Test-scoped context holding data that needs to be reset every test run
461    pub test_context: TestContext,
462
463    /// Whether to commit FS changes such as file creations, writes and deletes.
464    /// Used to prevent duplicate changes file executing non-committing calls.
465    pub fs_commit: bool,
466
467    /// Serialized JSON values.
468    // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic.
469    pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
470
471    /// All recorded ETH `deal`s.
472    pub eth_deals: Vec<DealRecord>,
473
474    /// Gas metering state.
475    pub gas_metering: GasMetering,
476
477    /// Contains gas snapshots made over the course of a test suite.
478    // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic.
479    pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
480
481    /// Mapping slots.
482    pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
483
484    /// The current program counter.
485    pub pc: usize,
486    /// Breakpoints supplied by the `breakpoint` cheatcode.
487    /// `char -> (address, pc)`
488    pub breakpoints: Breakpoints,
489
490    /// Whether the next contract creation should be intercepted to return its initcode.
491    pub intercept_next_create_call: bool,
492
493    /// Optional cheatcodes `TestRunner`. Used for generating random values from uint and int
494    /// strategies.
495    test_runner: Option<TestRunner>,
496
497    /// Ignored traces.
498    pub ignored_traces: IgnoredTraces,
499
500    /// Addresses with arbitrary storage.
501    pub arbitrary_storage: Option<ArbitraryStorage>,
502
503    /// Deprecated cheatcodes mapped to the reason. Used to report warnings on test results.
504    pub deprecated: HashMap<&'static str, Option<&'static str>>,
505    /// Unlocked wallets used in scripts and testing of scripts.
506    pub wallets: Option<Wallets>,
507    /// Signatures identifier for decoding events and functions
508    signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
509    /// Used to determine whether the broadcasted call has dynamic gas limit.
510    pub dynamic_gas_limit: bool,
511    // Custom execution evm version.
512    pub execution_evm_version: Option<SpecId>,
513}
514
515// This is not derived because calling this in `fn new` with `..Default::default()` creates a second
516// `CheatsConfig` which is unused, and inside it `ProjectPathsConfig` is relatively expensive to
517// create.
518impl Default for Cheatcodes {
519    fn default() -> Self {
520        Self::new(Arc::default())
521    }
522}
523
524impl Cheatcodes {
525    /// Creates a new `Cheatcodes` with the given settings.
526    pub fn new(config: Arc<CheatsConfig>) -> Self {
527        Self {
528            analysis: None,
529            fs_commit: true,
530            labels: config.labels.clone(),
531            config,
532            block: Default::default(),
533            active_delegations: Default::default(),
534            active_blob_sidecar: Default::default(),
535            gas_price: Default::default(),
536            pranks: Default::default(),
537            expected_revert: Default::default(),
538            assume_no_revert: Default::default(),
539            fork_revert_diagnostic: Default::default(),
540            accesses: Default::default(),
541            recording_accesses: Default::default(),
542            recorded_account_diffs_stack: Default::default(),
543            recorded_logs: Default::default(),
544            record_debug_steps_info: Default::default(),
545            mocked_calls: Default::default(),
546            mocked_functions: Default::default(),
547            expected_calls: Default::default(),
548            expected_emits: Default::default(),
549            expected_creates: Default::default(),
550            allowed_mem_writes: Default::default(),
551            broadcast: Default::default(),
552            broadcastable_transactions: Default::default(),
553            access_list: Default::default(),
554            test_context: Default::default(),
555            serialized_jsons: Default::default(),
556            eth_deals: Default::default(),
557            gas_metering: Default::default(),
558            gas_snapshots: Default::default(),
559            mapping_slots: Default::default(),
560            pc: Default::default(),
561            breakpoints: Default::default(),
562            intercept_next_create_call: Default::default(),
563            test_runner: Default::default(),
564            ignored_traces: Default::default(),
565            arbitrary_storage: Default::default(),
566            deprecated: Default::default(),
567            wallets: Default::default(),
568            signatures_identifier: Default::default(),
569            dynamic_gas_limit: Default::default(),
570            execution_evm_version: None,
571        }
572    }
573
574    /// Enables cheatcode analysis capabilities by providing a solar compiler instance.
575    pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
576        self.analysis = Some(analysis);
577    }
578
579    /// Returns the configured prank at given depth or the first prank configured at a lower depth.
580    /// For example, if pranks configured for depth 1, 3 and 5, the prank for depth 4 is the one
581    /// configured at depth 3.
582    pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
583        self.pranks.range(..=depth).last().map(|(_, prank)| prank)
584    }
585
586    /// Returns the configured wallets if available, else creates a new instance.
587    pub fn wallets(&mut self) -> &Wallets {
588        self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
589    }
590
591    /// Sets the unlocked wallets.
592    pub fn set_wallets(&mut self, wallets: Wallets) {
593        self.wallets = Some(wallets);
594    }
595
596    /// Adds a delegation to the active delegations list.
597    pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
598        self.active_delegations.push(authorization);
599    }
600
601    /// Returns the signatures identifier.
602    pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
603        self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
604    }
605
606    /// Decodes the input data and applies the cheatcode.
607    fn apply_cheatcode(
608        &mut self,
609        ecx: Ecx,
610        call: &CallInputs,
611        executor: &mut dyn CheatcodesExecutor,
612    ) -> Result {
613        // decode the cheatcode call
614        let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
615            if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
616                let msg = format!(
617                    "unknown cheatcode with selector {selector}; \
618                     you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
619                     and the `forge` version"
620                );
621                return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
622            }
623            e
624        })?;
625
626        let caller = call.caller;
627
628        // ensure the caller is allowed to execute cheatcodes,
629        // but only if the backend is in forking mode
630        ecx.journaled_state.database.ensure_cheatcode_access_forking_mode(&caller)?;
631
632        apply_dispatch(
633            &decoded,
634            &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
635            executor,
636        )
637    }
638
639    /// Grants cheat code access for new contracts if the caller also has
640    /// cheatcode access or the new contract is created in top most call.
641    ///
642    /// There may be cheatcodes in the constructor of the new contract, in order to allow them
643    /// automatically we need to determine the new address.
644    fn allow_cheatcodes_on_create(&self, ecx: Ecx, caller: Address, created_address: Address) {
645        if ecx.journaled_state.depth <= 1
646            || ecx.journaled_state.database.has_cheatcode_access(&caller)
647        {
648            ecx.journaled_state.database.allow_cheatcode_access(created_address);
649        }
650    }
651
652    /// Apply EIP-2930 access list.
653    ///
654    /// If the transaction type is [TransactionType::Legacy] we need to upgrade it to
655    /// [TransactionType::Eip2930] in order to use access lists. Other transaction types support
656    /// access lists themselves.
657    fn apply_accesslist(&mut self, ecx: Ecx) {
658        if let Some(access_list) = &self.access_list {
659            ecx.tx.access_list = access_list.clone();
660
661            if ecx.tx.tx_type == TransactionType::Legacy as u8 {
662                ecx.tx.tx_type = TransactionType::Eip2930 as u8;
663            }
664        }
665    }
666
667    /// Called when there was a revert.
668    ///
669    /// Cleanup any previously applied cheatcodes that altered the state in such a way that revm's
670    /// revert would run into issues.
671    pub fn on_revert(&mut self, ecx: Ecx) {
672        trace!(deals=?self.eth_deals.len(), "rolling back deals");
673
674        // Delay revert clean up until expected revert is handled, if set.
675        if self.expected_revert.is_some() {
676            return;
677        }
678
679        // we only want to apply cleanup top level
680        if ecx.journaled_state.depth() > 0 {
681            return;
682        }
683
684        // Roll back all previously applied deals
685        // This will prevent overflow issues in revm's [`JournaledState::journal_revert`] routine
686        // which rolls back any transfers.
687        while let Some(record) = self.eth_deals.pop() {
688            if let Some(acc) = ecx.journaled_state.state.get_mut(&record.address) {
689                acc.info.balance = record.old_balance;
690            }
691        }
692    }
693
694    pub fn call_with_executor(
695        &mut self,
696        ecx: Ecx,
697        call: &mut CallInputs,
698        executor: &mut dyn CheatcodesExecutor,
699    ) -> Option<CallOutcome> {
700        // Apply custom execution evm version.
701        if let Some(spec_id) = self.execution_evm_version {
702            ecx.cfg.spec = spec_id;
703        }
704
705        let gas = Gas::new(call.gas_limit);
706        let curr_depth = ecx.journaled_state.depth();
707
708        // At the root call to test function or script `run()`/`setUp()` functions, we are
709        // decreasing sender nonce to ensure that it matches on-chain nonce once we start
710        // broadcasting.
711        if curr_depth == 0 {
712            let sender = ecx.tx.caller;
713            let account = match super::evm::journaled_account(ecx, sender) {
714                Ok(account) => account,
715                Err(err) => {
716                    return Some(CallOutcome {
717                        result: InterpreterResult {
718                            result: InstructionResult::Revert,
719                            output: err.abi_encode().into(),
720                            gas,
721                        },
722                        memory_offset: call.return_memory_offset.clone(),
723                    });
724                }
725            };
726            let prev = account.info.nonce;
727            account.info.nonce = prev.saturating_sub(1);
728
729            trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
730        }
731
732        if call.target_address == CHEATCODE_ADDRESS {
733            return match self.apply_cheatcode(ecx, call, executor) {
734                Ok(retdata) => Some(CallOutcome {
735                    result: InterpreterResult {
736                        result: InstructionResult::Return,
737                        output: retdata.into(),
738                        gas,
739                    },
740                    memory_offset: call.return_memory_offset.clone(),
741                }),
742                Err(err) => Some(CallOutcome {
743                    result: InterpreterResult {
744                        result: InstructionResult::Revert,
745                        output: err.abi_encode().into(),
746                        gas,
747                    },
748                    memory_offset: call.return_memory_offset.clone(),
749                }),
750            };
751        }
752
753        if call.target_address == HARDHAT_CONSOLE_ADDRESS {
754            return None;
755        }
756
757        // Handle expected calls
758
759        // Grab the different calldatas expected.
760        if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
761        {
762            // Match every partial/full calldata
763            for (calldata, (expected, actual_count)) in expected_calls_for_target {
764                // Increment actual times seen if...
765                // The calldata is at most, as big as this call's input, and
766                if calldata.len() <= call.input.len() &&
767                    // Both calldata match, taking the length of the assumed smaller one (which will have at least the selector), and
768                    *calldata == call.input.bytes(ecx)[..calldata.len()] &&
769                    // The value matches, if provided
770                    expected
771                        .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
772                    // The gas matches, if provided
773                    expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
774                    // The minimum gas matches, if provided
775                    expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
776                {
777                    *actual_count += 1;
778                }
779            }
780        }
781
782        // Handle mocked calls
783        if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
784            let ctx = MockCallDataContext {
785                calldata: call.input.bytes(ecx),
786                value: call.transfer_value(),
787            };
788
789            if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
790                Some(queue) => Some(queue),
791                None => mocks
792                    .iter_mut()
793                    .find(|(mock, _)| {
794                        call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
795                            && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
796                    })
797                    .map(|(_, v)| v),
798            } && let Some(return_data) = if return_data_queue.len() == 1 {
799                // If the mocked calls stack has a single element in it, don't empty it
800                return_data_queue.front().map(|x| x.to_owned())
801            } else {
802                // Else, we pop the front element
803                return_data_queue.pop_front()
804            } {
805                return Some(CallOutcome {
806                    result: InterpreterResult {
807                        result: return_data.ret_type,
808                        output: return_data.data,
809                        gas,
810                    },
811                    memory_offset: call.return_memory_offset.clone(),
812                });
813            }
814        }
815
816        // Apply our prank
817        if let Some(prank) = &self.get_prank(curr_depth) {
818            // Apply delegate call, `call.caller`` will not equal `prank.prank_caller`
819            if prank.delegate_call
820                && curr_depth == prank.depth
821                && let CallScheme::DelegateCall = call.scheme
822            {
823                call.target_address = prank.new_caller;
824                call.caller = prank.new_caller;
825                if let Some(new_origin) = prank.new_origin {
826                    ecx.tx.caller = new_origin;
827                }
828            }
829
830            if curr_depth >= prank.depth && call.caller == prank.prank_caller {
831                let mut prank_applied = false;
832
833                // At the target depth we set `msg.sender`
834                if curr_depth == prank.depth {
835                    // Ensure new caller is loaded and touched
836                    let _ = journaled_account(ecx, prank.new_caller);
837                    call.caller = prank.new_caller;
838                    prank_applied = true;
839                }
840
841                // At the target depth, or deeper, we set `tx.origin`
842                if let Some(new_origin) = prank.new_origin {
843                    ecx.tx.caller = new_origin;
844                    prank_applied = true;
845                }
846
847                // If prank applied for first time, then update
848                if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
849                    self.pranks.insert(curr_depth, applied_prank);
850                }
851            }
852        }
853
854        // Apply EIP-2930 access list
855        self.apply_accesslist(ecx);
856
857        // Apply our broadcast
858        if let Some(broadcast) = &self.broadcast {
859            // Additional check as transfers in forge scripts seem to be estimated at 2300
860            // by revm leading to "Intrinsic gas too low" failure when simulated on chain.
861            let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
862            self.dynamic_gas_limit = false;
863
864            // We only apply a broadcast *to a specific depth*.
865            //
866            // We do this because any subsequent contract calls *must* exist on chain and
867            // we only want to grab *this* call, not internal ones
868            if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
869                // At the target depth we set `msg.sender` & tx.origin.
870                // We are simulating the caller as being an EOA, so *both* must be set to the
871                // broadcast.origin.
872                ecx.tx.caller = broadcast.new_origin;
873
874                call.caller = broadcast.new_origin;
875                // Add a `legacy` transaction to the VecDeque. We use a legacy transaction here
876                // because we only need the from, to, value, and data. We can later change this
877                // into 1559, in the cli package, relatively easily once we
878                // know the target chain supports EIP-1559.
879                if !call.is_static {
880                    if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
881                        return Some(CallOutcome {
882                            result: InterpreterResult {
883                                result: InstructionResult::Revert,
884                                output: Error::encode(err),
885                                gas,
886                            },
887                            memory_offset: call.return_memory_offset.clone(),
888                        });
889                    }
890
891                    let input = TransactionInput::new(call.input.bytes(ecx));
892
893                    let account =
894                        ecx.journaled_state.inner.state().get_mut(&broadcast.new_origin).unwrap();
895
896                    let mut tx_req = TransactionRequest {
897                        from: Some(broadcast.new_origin),
898                        to: Some(TxKind::from(Some(call.target_address))),
899                        value: call.transfer_value(),
900                        input,
901                        nonce: Some(account.info.nonce),
902                        chain_id: Some(ecx.cfg.chain_id),
903                        gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None },
904                        ..Default::default()
905                    };
906
907                    let active_delegations = std::mem::take(&mut self.active_delegations);
908                    // Set active blob sidecar, if any.
909                    if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
910                        // Ensure blob and delegation are not set for the same tx.
911                        if !active_delegations.is_empty() {
912                            let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
913                            return Some(CallOutcome {
914                                result: InterpreterResult {
915                                    result: InstructionResult::Revert,
916                                    output: Error::encode(msg),
917                                    gas,
918                                },
919                                memory_offset: call.return_memory_offset.clone(),
920                            });
921                        }
922                        tx_req.set_blob_sidecar(blob_sidecar);
923                    }
924
925                    // Apply active EIP-7702 delegations, if any.
926                    if !active_delegations.is_empty() {
927                        for auth in &active_delegations {
928                            let Ok(authority) = auth.recover_authority() else {
929                                continue;
930                            };
931                            if authority == broadcast.new_origin {
932                                // Increment nonce of broadcasting account to reflect signed
933                                // authorization.
934                                account.info.nonce += 1;
935                            }
936                        }
937                        tx_req.authorization_list = Some(active_delegations);
938                    }
939
940                    self.broadcastable_transactions.push_back(BroadcastableTransaction {
941                        rpc: ecx.journaled_state.database.active_fork_url(),
942                        transaction: tx_req.into(),
943                    });
944                    debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
945
946                    // Explicitly increment nonce if calls are not isolated.
947                    if !self.config.evm_opts.isolate {
948                        let prev = account.info.nonce;
949                        account.info.nonce += 1;
950                        debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
951                    }
952                } else if broadcast.single_call {
953                    let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
954                    return Some(CallOutcome {
955                        result: InterpreterResult {
956                            result: InstructionResult::Revert,
957                            output: Error::encode(msg),
958                            gas,
959                        },
960                        memory_offset: call.return_memory_offset.clone(),
961                    });
962                }
963            }
964        }
965
966        // Record called accounts if `startStateDiffRecording` has been called
967        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
968            // Determine if account is "initialized," ie, it has a non-zero balance, a non-zero
969            // nonce, a non-zero KECCAK_EMPTY codehash, or non-empty code
970            let initialized;
971            let old_balance;
972            let old_nonce;
973            if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
974                initialized = acc.info.exists();
975                old_balance = acc.info.balance;
976                old_nonce = acc.info.nonce;
977            } else {
978                initialized = false;
979                old_balance = U256::ZERO;
980                old_nonce = 0;
981            }
982            let kind = match call.scheme {
983                CallScheme::Call => crate::Vm::AccountAccessKind::Call,
984                CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
985                CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
986                CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
987            };
988            // Record this call by pushing it to a new pending vector; all subsequent calls at
989            // that depth will be pushed to the same vector. When the call ends, the
990            // RecordedAccountAccess (and all subsequent RecordedAccountAccesses) will be
991            // updated with the revert status of this call, since the EVM does not mark accounts
992            // as "warm" if the call from which they were accessed is reverted
993            recorded_account_diffs_stack.push(vec![AccountAccess {
994                chainInfo: crate::Vm::ChainInfo {
995                    forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
996                    chainId: U256::from(ecx.cfg.chain_id),
997                },
998                accessor: call.caller,
999                account: call.bytecode_address,
1000                kind,
1001                initialized,
1002                oldBalance: old_balance,
1003                newBalance: U256::ZERO, // updated on call_end
1004                oldNonce: old_nonce,
1005                newNonce: 0, // updated on call_end
1006                value: call.call_value(),
1007                data: call.input.bytes(ecx),
1008                reverted: false,
1009                deployedCode: Bytes::new(),
1010                storageAccesses: vec![], // updated on step
1011                depth: ecx
1012                    .journaled_state
1013                    .depth()
1014                    .try_into()
1015                    .expect("journaled state depth exceeds u64"),
1016            }]);
1017        }
1018
1019        None
1020    }
1021
1022    pub fn rng(&mut self) -> &mut impl Rng {
1023        self.test_runner().rng()
1024    }
1025
1026    pub fn test_runner(&mut self) -> &mut TestRunner {
1027        self.test_runner.get_or_insert_with(|| match self.config.seed {
1028            Some(seed) => TestRunner::new_with_rng(
1029                proptest::test_runner::Config::default(),
1030                TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1031            ),
1032            None => TestRunner::new(proptest::test_runner::Config::default()),
1033        })
1034    }
1035
1036    pub fn set_seed(&mut self, seed: U256) {
1037        self.test_runner = Some(TestRunner::new_with_rng(
1038            proptest::test_runner::Config::default(),
1039            TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1040        ));
1041    }
1042
1043    /// Returns existing or set a default `ArbitraryStorage` option.
1044    /// Used by `setArbitraryStorage` cheatcode to track addresses with arbitrary storage.
1045    pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1046        self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1047    }
1048
1049    /// Whether the given address has arbitrary storage.
1050    pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1051        match &self.arbitrary_storage {
1052            Some(storage) => storage.values.contains_key(address),
1053            None => false,
1054        }
1055    }
1056
1057    /// Whether the given slot of address with arbitrary storage should be overwritten.
1058    /// True if address is marked as and overwrite and if no value was previously generated for
1059    /// given slot.
1060    pub fn should_overwrite_arbitrary_storage(
1061        &self,
1062        address: &Address,
1063        storage_slot: U256,
1064    ) -> bool {
1065        match &self.arbitrary_storage {
1066            Some(storage) => {
1067                storage.overwrites.contains(address)
1068                    && storage
1069                        .values
1070                        .get(address)
1071                        .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1072                        .is_none()
1073            }
1074            None => false,
1075        }
1076    }
1077
1078    /// Whether the given address is a copy of an address with arbitrary storage.
1079    pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1080        match &self.arbitrary_storage {
1081            Some(storage) => storage.copies.contains_key(address),
1082            None => false,
1083        }
1084    }
1085
1086    /// Returns struct definitions from the analysis, if available.
1087    pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1088        self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1089    }
1090}
1091
1092impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1093    fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1094        // When the first interpreter is initialized we've circumvented the balance and gas checks,
1095        // so we apply our actual block data with the correct fees and all.
1096        if let Some(block) = self.block.take() {
1097            ecx.block = block;
1098        }
1099        if let Some(gas_price) = self.gas_price.take() {
1100            ecx.tx.gas_price = gas_price;
1101        }
1102
1103        // Record gas for current frame.
1104        if self.gas_metering.paused {
1105            self.gas_metering.paused_frames.push(interpreter.gas);
1106        }
1107
1108        // `expectRevert`: track the max call depth during `expectRevert`
1109        if let Some(expected) = &mut self.expected_revert {
1110            expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth);
1111        }
1112    }
1113
1114    fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1115        self.pc = interpreter.bytecode.pc();
1116
1117        if self.broadcast.is_some() {
1118            self.set_gas_limit_type(interpreter);
1119        }
1120
1121        // `pauseGasMetering`: pause / resume interpreter gas.
1122        if self.gas_metering.paused {
1123            self.meter_gas(interpreter);
1124        }
1125
1126        // `resetGasMetering`: reset interpreter gas.
1127        if self.gas_metering.reset {
1128            self.meter_gas_reset(interpreter);
1129        }
1130
1131        // `record`: record storage reads and writes.
1132        if self.recording_accesses {
1133            self.record_accesses(interpreter);
1134        }
1135
1136        // `startStateDiffRecording`: record granular ordered storage accesses.
1137        if self.recorded_account_diffs_stack.is_some() {
1138            self.record_state_diffs(interpreter, ecx);
1139        }
1140
1141        // `expectSafeMemory`: check if the current opcode is allowed to interact with memory.
1142        if !self.allowed_mem_writes.is_empty() {
1143            self.check_mem_opcodes(
1144                interpreter,
1145                ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"),
1146            );
1147        }
1148
1149        // `startMappingRecording`: record SSTORE and KECCAK256.
1150        if let Some(mapping_slots) = &mut self.mapping_slots {
1151            mapping_step(mapping_slots, interpreter);
1152        }
1153
1154        // `snapshotGas*`: take a snapshot of the current gas.
1155        if self.gas_metering.recording {
1156            self.meter_gas_record(interpreter, ecx);
1157        }
1158    }
1159
1160    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1161        if self.gas_metering.paused {
1162            self.meter_gas_end(interpreter);
1163        }
1164
1165        if self.gas_metering.touched {
1166            self.meter_gas_check(interpreter);
1167        }
1168
1169        // `setArbitraryStorage` and `copyStorage`: add arbitrary values to storage.
1170        if self.arbitrary_storage.is_some() {
1171            self.arbitrary_storage_end(interpreter, ecx);
1172        }
1173    }
1174
1175    fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) {
1176        if !self.expected_emits.is_empty() {
1177            expect::handle_expect_emit(self, &log, interpreter);
1178        }
1179
1180        // `recordLogs`
1181        if let Some(storage_recorded_logs) = &mut self.recorded_logs {
1182            storage_recorded_logs.push(Vm::Log {
1183                topics: log.data.topics().to_vec(),
1184                data: log.data.data.clone(),
1185                emitter: log.address,
1186            });
1187        }
1188    }
1189
1190    fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
1191        Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1192    }
1193
1194    fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) {
1195        let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1196            || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1197
1198        // Clean up pranks/broadcasts if it's not a cheatcode call end. We shouldn't do
1199        // it for cheatcode calls because they are not applied for cheatcodes in the `call` hook.
1200        // This should be placed before the revert handling, because we might exit early there
1201        if !cheatcode_call {
1202            // Clean up pranks
1203            let curr_depth = ecx.journaled_state.depth();
1204            if let Some(prank) = &self.get_prank(curr_depth)
1205                && curr_depth == prank.depth
1206            {
1207                ecx.tx.caller = prank.prank_origin;
1208
1209                // Clean single-call prank once we have returned to the original depth
1210                if prank.single_call {
1211                    self.pranks.remove(&curr_depth);
1212                }
1213            }
1214
1215            // Clean up broadcast
1216            if let Some(broadcast) = &self.broadcast
1217                && curr_depth == broadcast.depth
1218            {
1219                ecx.tx.caller = broadcast.original_origin;
1220
1221                // Clean single-call broadcast once we have returned to the original depth
1222                if broadcast.single_call {
1223                    let _ = self.broadcast.take();
1224                }
1225            }
1226        }
1227
1228        // Handle assume no revert cheatcode.
1229        if let Some(assume_no_revert) = &mut self.assume_no_revert {
1230            // Record current reverter address before processing the expect revert if call reverted,
1231            // expect revert is set with expected reverter address and no actual reverter set yet.
1232            if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1233                assume_no_revert.reverted_by = Some(call.target_address);
1234            }
1235
1236            // allow multiple cheatcode calls at the same depth
1237            let curr_depth = ecx.journaled_state.depth();
1238            if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1239                // Discard run if we're at the same depth as cheatcode, call reverted, and no
1240                // specific reason was supplied
1241                if outcome.result.is_revert() {
1242                    let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1243                    return match revert_handlers::handle_assume_no_revert(
1244                        &assume_no_revert,
1245                        outcome.result.result,
1246                        &outcome.result.output,
1247                        &self.config.available_artifacts,
1248                    ) {
1249                        // if result is Ok, it was an anticipated revert; return an "assume" error
1250                        // to reject this run
1251                        Ok(_) => {
1252                            outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1253                        }
1254                        // if result is Error, it was an unanticipated revert; should revert
1255                        // normally
1256                        Err(error) => {
1257                            trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1258                            outcome.result.result = InstructionResult::Revert;
1259                            outcome.result.output = error.abi_encode().into();
1260                        }
1261                    };
1262                } else {
1263                    // Call didn't revert, reset `assume_no_revert` state.
1264                    self.assume_no_revert = None;
1265                }
1266            }
1267        }
1268
1269        // Handle expected reverts.
1270        if let Some(expected_revert) = &mut self.expected_revert {
1271            // Record current reverter address and call scheme before processing the expect revert
1272            // if call reverted.
1273            if outcome.result.is_revert() {
1274                // Record current reverter address if expect revert is set with expected reverter
1275                // address and no actual reverter was set yet or if we're expecting more than one
1276                // revert.
1277                if expected_revert.reverter.is_some()
1278                    && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1279                {
1280                    expected_revert.reverted_by = Some(call.target_address);
1281                }
1282            }
1283
1284            let curr_depth = ecx.journaled_state.depth();
1285            if curr_depth <= expected_revert.depth {
1286                let needs_processing = match expected_revert.kind {
1287                    ExpectedRevertKind::Default => !cheatcode_call,
1288                    // `pending_processing` == true means that we're in the `call_end` hook for
1289                    // `vm.expectCheatcodeRevert` and shouldn't expect revert here
1290                    ExpectedRevertKind::Cheatcode { pending_processing } => {
1291                        cheatcode_call && !pending_processing
1292                    }
1293                };
1294
1295                if needs_processing {
1296                    let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1297                    return match revert_handlers::handle_expect_revert(
1298                        cheatcode_call,
1299                        false,
1300                        self.config.internal_expect_revert,
1301                        &expected_revert,
1302                        outcome.result.result,
1303                        outcome.result.output.clone(),
1304                        &self.config.available_artifacts,
1305                    ) {
1306                        Err(error) => {
1307                            trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1308                            outcome.result.result = InstructionResult::Revert;
1309                            outcome.result.output = error.abi_encode().into();
1310                        }
1311                        Ok((_, retdata)) => {
1312                            expected_revert.actual_count += 1;
1313                            if expected_revert.actual_count < expected_revert.count {
1314                                self.expected_revert = Some(expected_revert);
1315                            }
1316                            outcome.result.result = InstructionResult::Return;
1317                            outcome.result.output = retdata;
1318                        }
1319                    };
1320                }
1321
1322                // Flip `pending_processing` flag for cheatcode revert expectations, marking that
1323                // we've exited the `expectCheatcodeRevert` call scope
1324                if let ExpectedRevertKind::Cheatcode { pending_processing } =
1325                    &mut self.expected_revert.as_mut().unwrap().kind
1326                {
1327                    *pending_processing = false;
1328                }
1329            }
1330        }
1331
1332        // Exit early for calls to cheatcodes as other logic is not relevant for cheatcode
1333        // invocations
1334        if cheatcode_call {
1335            return;
1336        }
1337
1338        // Record the gas usage of the call, this allows the `lastCallGas` cheatcode to
1339        // retrieve the gas usage of the last call.
1340        let gas = outcome.result.gas;
1341        self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1342            gasLimit: gas.limit(),
1343            gasTotalUsed: gas.spent(),
1344            gasMemoryUsed: 0,
1345            gasRefunded: gas.refunded(),
1346            gasRemaining: gas.remaining(),
1347        });
1348
1349        // If `startStateDiffRecording` has been called, update the `reverted` status of the
1350        // previous call depth's recorded accesses, if any
1351        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1352            // The root call cannot be recorded.
1353            if ecx.journaled_state.depth() > 0
1354                && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1355            {
1356                // Update the reverted status of all deeper calls if this call reverted, in
1357                // accordance with EVM behavior
1358                if outcome.result.is_revert() {
1359                    last_recorded_depth.iter_mut().for_each(|element| {
1360                        element.reverted = true;
1361                        element
1362                            .storageAccesses
1363                            .iter_mut()
1364                            .for_each(|storage_access| storage_access.reverted = true);
1365                    })
1366                }
1367
1368                if let Some(call_access) = last_recorded_depth.first_mut() {
1369                    // Assert that we're at the correct depth before recording post-call state
1370                    // changes. Depending on the depth the cheat was
1371                    // called at, there may not be any pending
1372                    // calls to update if execution has percolated up to a higher depth.
1373                    let curr_depth = ecx.journaled_state.depth();
1374                    if call_access.depth == curr_depth as u64
1375                        && let Ok(acc) = ecx.journaled_state.load_account(call.target_address)
1376                    {
1377                        debug_assert!(access_is_call(call_access.kind));
1378                        call_access.newBalance = acc.info.balance;
1379                        call_access.newNonce = acc.info.nonce;
1380                    }
1381                    // Merge the last depth's AccountAccesses into the AccountAccesses at the
1382                    // current depth, or push them back onto the pending
1383                    // vector if higher depths were not recorded. This
1384                    // preserves ordering of accesses.
1385                    if let Some(last) = recorded_account_diffs_stack.last_mut() {
1386                        last.extend(last_recorded_depth);
1387                    } else {
1388                        recorded_account_diffs_stack.push(last_recorded_depth);
1389                    }
1390                }
1391            }
1392        }
1393
1394        // At the end of the call,
1395        // we need to check if we've found all the emits.
1396        // We know we've found all the expected emits in the right order
1397        // if the queue is fully matched.
1398        // If it's not fully matched, then either:
1399        // 1. Not enough events were emitted (we'll know this because the amount of times we
1400        // inspected events will be less than the size of the queue) 2. The wrong events
1401        // were emitted (The inspected events should match the size of the queue, but still some
1402        // events will not be matched)
1403
1404        // First, check that we're at the call depth where the emits were declared from.
1405        let should_check_emits = self
1406            .expected_emits
1407            .iter()
1408            .any(|(expected, _)| {
1409                let curr_depth = ecx.journaled_state.depth();
1410                expected.depth == curr_depth
1411            }) &&
1412            // Ignore staticcalls
1413            !call.is_static;
1414        if should_check_emits {
1415            let expected_counts = self
1416                .expected_emits
1417                .iter()
1418                .filter_map(|(expected, count_map)| {
1419                    let count = match expected.address {
1420                        Some(emitter) => match count_map.get(&emitter) {
1421                            Some(log_count) => expected
1422                                .log
1423                                .as_ref()
1424                                .map(|l| log_count.count(l))
1425                                .unwrap_or_else(|| log_count.count_unchecked()),
1426                            None => 0,
1427                        },
1428                        None => match &expected.log {
1429                            Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1430                            None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1431                        },
1432                    };
1433
1434                    if count != expected.count { Some((expected, count)) } else { None }
1435                })
1436                .collect::<Vec<_>>();
1437
1438            // Revert if not all emits expected were matched.
1439            if let Some((expected, _)) = self
1440                .expected_emits
1441                .iter()
1442                .find(|(expected, _)| !expected.found && expected.count > 0)
1443            {
1444                outcome.result.result = InstructionResult::Revert;
1445                let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1446                outcome.result.output = error_msg.abi_encode().into();
1447                return;
1448            }
1449
1450            if !expected_counts.is_empty() {
1451                let msg = if outcome.result.is_ok() {
1452                    let (expected, count) = expected_counts.first().unwrap();
1453                    format!("log emitted {count} times, expected {}", expected.count)
1454                } else {
1455                    "expected an emit, but the call reverted instead. \
1456                     ensure you're testing the happy path when using `expectEmit`"
1457                        .to_string()
1458                };
1459
1460                outcome.result.result = InstructionResult::Revert;
1461                outcome.result.output = Error::encode(msg);
1462                return;
1463            }
1464
1465            // All emits were found, we're good.
1466            // Clear the queue, as we expect the user to declare more events for the next call
1467            // if they wanna match further events.
1468            self.expected_emits.clear()
1469        }
1470
1471        // this will ensure we don't have false positives when trying to diagnose reverts in fork
1472        // mode
1473        let diag = self.fork_revert_diagnostic.take();
1474
1475        // if there's a revert and a previous call was diagnosed as fork related revert then we can
1476        // return a better error here
1477        if outcome.result.is_revert()
1478            && let Some(err) = diag
1479        {
1480            outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1481            return;
1482        }
1483
1484        // try to diagnose reverts in multi-fork mode where a call is made to an address that does
1485        // not exist
1486        if let TxKind::Call(test_contract) = ecx.tx.kind {
1487            // if a call to a different contract than the original test contract returned with
1488            // `Stop` we check if the contract actually exists on the active fork
1489            if ecx.journaled_state.db().is_forked_mode()
1490                && outcome.result.result == InstructionResult::Stop
1491                && call.target_address != test_contract
1492            {
1493                let journaled_state = ecx.journaled_state.clone();
1494                self.fork_revert_diagnostic =
1495                    ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state);
1496            }
1497        }
1498
1499        // If the depth is 0, then this is the root call terminating
1500        if ecx.journaled_state.depth() == 0 {
1501            // If we already have a revert, we shouldn't run the below logic as it can obfuscate an
1502            // earlier error that happened first with unrelated information about
1503            // another error when using cheatcodes.
1504            if outcome.result.is_revert() {
1505                return;
1506            }
1507
1508            // If there's not a revert, we can continue on to run the last logic for expect*
1509            // cheatcodes.
1510
1511            // Match expected calls
1512            for (address, calldatas) in &self.expected_calls {
1513                // Loop over each address, and for each address, loop over each calldata it expects.
1514                for (calldata, (expected, actual_count)) in calldatas {
1515                    // Grab the values we expect to see
1516                    let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1517
1518                    let failed = match call_type {
1519                        // If the cheatcode was called with a `count` argument,
1520                        // we must check that the EVM performed a CALL with this calldata exactly
1521                        // `count` times.
1522                        ExpectedCallType::Count => *count != *actual_count,
1523                        // If the cheatcode was called without a `count` argument,
1524                        // we must check that the EVM performed a CALL with this calldata at least
1525                        // `count` times. The amount of times to check was
1526                        // the amount of time the cheatcode was called.
1527                        ExpectedCallType::NonCount => *count > *actual_count,
1528                    };
1529                    if failed {
1530                        let expected_values = [
1531                            Some(format!("data {}", hex::encode_prefixed(calldata))),
1532                            value.as_ref().map(|v| format!("value {v}")),
1533                            gas.map(|g| format!("gas {g}")),
1534                            min_gas.map(|g| format!("minimum gas {g}")),
1535                        ]
1536                        .into_iter()
1537                        .flatten()
1538                        .join(", ");
1539                        let but = if outcome.result.is_ok() {
1540                            let s = if *actual_count == 1 { "" } else { "s" };
1541                            format!("was called {actual_count} time{s}")
1542                        } else {
1543                            "the call reverted instead; \
1544                             ensure you're testing the happy path when using `expectCall`"
1545                                .to_string()
1546                        };
1547                        let s = if *count == 1 { "" } else { "s" };
1548                        let msg = format!(
1549                            "expected call to {address} with {expected_values} \
1550                             to be called {count} time{s}, but {but}"
1551                        );
1552                        outcome.result.result = InstructionResult::Revert;
1553                        outcome.result.output = Error::encode(msg);
1554
1555                        return;
1556                    }
1557                }
1558            }
1559
1560            // Check if we have any leftover expected emits
1561            // First, if any emits were found at the root call, then we its ok and we remove them.
1562            // For count=0 expectations, NOT being found is success, so mark them as found
1563            for (expected, _) in &mut self.expected_emits {
1564                if expected.count == 0 && !expected.found {
1565                    expected.found = true;
1566                }
1567            }
1568            self.expected_emits.retain(|(expected, _)| !expected.found);
1569            // If not empty, we got mismatched emits
1570            if !self.expected_emits.is_empty() {
1571                let msg = if outcome.result.is_ok() {
1572                    "expected an emit, but no logs were emitted afterwards. \
1573                     you might have mismatched events or not enough events were emitted"
1574                } else {
1575                    "expected an emit, but the call reverted instead. \
1576                     ensure you're testing the happy path when using `expectEmit`"
1577                };
1578                outcome.result.result = InstructionResult::Revert;
1579                outcome.result.output = Error::encode(msg);
1580                return;
1581            }
1582
1583            // Check for leftover expected creates
1584            if let Some(expected_create) = self.expected_creates.first() {
1585                let msg = format!(
1586                    "expected {} call by address {} for bytecode {} but not found",
1587                    expected_create.create_scheme,
1588                    hex::encode_prefixed(expected_create.deployer),
1589                    hex::encode_prefixed(&expected_create.bytecode),
1590                );
1591                outcome.result.result = InstructionResult::Revert;
1592                outcome.result.output = Error::encode(msg);
1593            }
1594        }
1595    }
1596
1597    fn create(&mut self, ecx: Ecx, mut input: &mut CreateInputs) -> Option<CreateOutcome> {
1598        // Apply custom execution evm version.
1599        if let Some(spec_id) = self.execution_evm_version {
1600            ecx.cfg.spec = spec_id;
1601        }
1602
1603        let gas = Gas::new(input.gas_limit());
1604        // Check if we should intercept this create
1605        if self.intercept_next_create_call {
1606            // Reset the flag
1607            self.intercept_next_create_call = false;
1608
1609            // Get initcode from the input
1610            let output = input.init_code();
1611
1612            // Return a revert with the initcode as error data
1613            return Some(CreateOutcome {
1614                result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1615                address: None,
1616            });
1617        }
1618
1619        let curr_depth = ecx.journaled_state.depth();
1620
1621        // Apply our prank
1622        if let Some(prank) = &self.get_prank(curr_depth)
1623            && curr_depth >= prank.depth
1624            && input.caller() == prank.prank_caller
1625        {
1626            let mut prank_applied = false;
1627
1628            // At the target depth we set `msg.sender`
1629            if curr_depth == prank.depth {
1630                // Ensure new caller is loaded and touched
1631                let _ = journaled_account(ecx, prank.new_caller);
1632                input.set_caller(prank.new_caller);
1633                prank_applied = true;
1634            }
1635
1636            // At the target depth, or deeper, we set `tx.origin`
1637            if let Some(new_origin) = prank.new_origin {
1638                ecx.tx.caller = new_origin;
1639                prank_applied = true;
1640            }
1641
1642            // If prank applied for first time, then update
1643            if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1644                self.pranks.insert(curr_depth, applied_prank);
1645            }
1646        }
1647
1648        // Apply EIP-2930 access list
1649        self.apply_accesslist(ecx);
1650
1651        // Apply our broadcast
1652        if let Some(broadcast) = &mut self.broadcast
1653            && curr_depth >= broadcast.depth
1654            && input.caller() == broadcast.original_caller
1655        {
1656            if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1657                return Some(CreateOutcome {
1658                    result: InterpreterResult {
1659                        result: InstructionResult::Revert,
1660                        output: Error::encode(err),
1661                        gas,
1662                    },
1663                    address: None,
1664                });
1665            }
1666
1667            ecx.tx.caller = broadcast.new_origin;
1668
1669            if curr_depth == broadcast.depth || broadcast.deploy_from_code {
1670                // Reset deploy from code flag for upcoming calls;
1671                broadcast.deploy_from_code = false;
1672
1673                input.set_caller(broadcast.new_origin);
1674
1675                let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
1676                self.broadcastable_transactions.push_back(BroadcastableTransaction {
1677                    rpc: ecx.journaled_state.database.active_fork_url(),
1678                    transaction: TransactionRequest {
1679                        from: Some(broadcast.new_origin),
1680                        to: None,
1681                        value: Some(input.value()),
1682                        input: TransactionInput::new(input.init_code()),
1683                        nonce: Some(account.info.nonce),
1684                        ..Default::default()
1685                    }
1686                    .into(),
1687                });
1688
1689                input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1690            }
1691        }
1692
1693        // Allow cheatcodes from the address of the new contract
1694        let address = input.allow_cheatcodes(self, ecx);
1695
1696        // If `recordAccountAccesses` has been called, record the create
1697        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1698            recorded_account_diffs_stack.push(vec![AccountAccess {
1699                chainInfo: crate::Vm::ChainInfo {
1700                    forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1701                    chainId: U256::from(ecx.cfg.chain_id),
1702                },
1703                accessor: input.caller(),
1704                account: address,
1705                kind: crate::Vm::AccountAccessKind::Create,
1706                initialized: true,
1707                oldBalance: U256::ZERO, // updated on create_end
1708                newBalance: U256::ZERO, // updated on create_end
1709                oldNonce: 0,            // new contract starts with nonce 0
1710                newNonce: 1,            // updated on create_end (contracts start with nonce 1)
1711                value: input.value(),
1712                data: input.init_code(),
1713                reverted: false,
1714                deployedCode: Bytes::new(), // updated on create_end
1715                storageAccesses: vec![],    // updated on create_end
1716                depth: curr_depth as u64,
1717            }]);
1718        }
1719
1720        None
1721    }
1722
1723    fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1724        let call = Some(call);
1725        let curr_depth = ecx.journaled_state.depth();
1726
1727        // Clean up pranks
1728        if let Some(prank) = &self.get_prank(curr_depth)
1729            && curr_depth == prank.depth
1730        {
1731            ecx.tx.caller = prank.prank_origin;
1732
1733            // Clean single-call prank once we have returned to the original depth
1734            if prank.single_call {
1735                std::mem::take(&mut self.pranks);
1736            }
1737        }
1738
1739        // Clean up broadcasts
1740        if let Some(broadcast) = &self.broadcast
1741            && curr_depth == broadcast.depth
1742        {
1743            ecx.tx.caller = broadcast.original_origin;
1744
1745            // Clean single-call broadcast once we have returned to the original depth
1746            if broadcast.single_call {
1747                std::mem::take(&mut self.broadcast);
1748            }
1749        }
1750
1751        // Handle expected reverts
1752        if let Some(expected_revert) = &self.expected_revert
1753            && curr_depth <= expected_revert.depth
1754            && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1755        {
1756            let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1757            return match revert_handlers::handle_expect_revert(
1758                false,
1759                true,
1760                self.config.internal_expect_revert,
1761                &expected_revert,
1762                outcome.result.result,
1763                outcome.result.output.clone(),
1764                &self.config.available_artifacts,
1765            ) {
1766                Ok((address, retdata)) => {
1767                    expected_revert.actual_count += 1;
1768                    if expected_revert.actual_count < expected_revert.count {
1769                        self.expected_revert = Some(expected_revert.clone());
1770                    }
1771
1772                    outcome.result.result = InstructionResult::Return;
1773                    outcome.result.output = retdata;
1774                    outcome.address = address;
1775                }
1776                Err(err) => {
1777                    outcome.result.result = InstructionResult::Revert;
1778                    outcome.result.output = err.abi_encode().into();
1779                }
1780            };
1781        }
1782
1783        // If `startStateDiffRecording` has been called, update the `reverted` status of the
1784        // previous call depth's recorded accesses, if any
1785        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1786            // The root call cannot be recorded.
1787            if curr_depth > 0
1788                && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1789            {
1790                // Update the reverted status of all deeper calls if this call reverted, in
1791                // accordance with EVM behavior
1792                if outcome.result.is_revert() {
1793                    last_depth.iter_mut().for_each(|element| {
1794                        element.reverted = true;
1795                        element
1796                            .storageAccesses
1797                            .iter_mut()
1798                            .for_each(|storage_access| storage_access.reverted = true);
1799                    })
1800                }
1801
1802                if let Some(create_access) = last_depth.first_mut() {
1803                    // Assert that we're at the correct depth before recording post-create state
1804                    // changes. Depending on what depth the cheat was called at, there
1805                    // may not be any pending calls to update if execution has
1806                    // percolated up to a higher depth.
1807                    let depth = ecx.journaled_state.depth();
1808                    if create_access.depth == depth as u64 {
1809                        debug_assert_eq!(
1810                            create_access.kind as u8,
1811                            crate::Vm::AccountAccessKind::Create as u8
1812                        );
1813                        if let Some(address) = outcome.address
1814                            && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1815                        {
1816                            create_access.newBalance = created_acc.info.balance;
1817                            create_access.newNonce = created_acc.info.nonce;
1818                            create_access.deployedCode =
1819                                created_acc.info.code.clone().unwrap_or_default().original_bytes();
1820                        }
1821                    }
1822                    // Merge the last depth's AccountAccesses into the AccountAccesses at the
1823                    // current depth, or push them back onto the pending
1824                    // vector if higher depths were not recorded. This
1825                    // preserves ordering of accesses.
1826                    if let Some(last) = recorded_account_diffs_stack.last_mut() {
1827                        last.append(last_depth);
1828                    } else {
1829                        recorded_account_diffs_stack.push(last_depth.clone());
1830                    }
1831                }
1832            }
1833        }
1834
1835        // Match the create against expected_creates
1836        if !self.expected_creates.is_empty()
1837            && let (Some(address), Some(call)) = (outcome.address, call)
1838            && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1839        {
1840            let bytecode = created_acc.info.code.clone().unwrap_or_default().original_bytes();
1841            if let Some((index, _)) =
1842                self.expected_creates.iter().find_position(|expected_create| {
1843                    expected_create.deployer == call.caller
1844                        && expected_create.create_scheme.eq(call.scheme.into())
1845                        && expected_create.bytecode == bytecode
1846                })
1847            {
1848                self.expected_creates.swap_remove(index);
1849            }
1850        }
1851    }
1852}
1853
1854impl InspectorExt for Cheatcodes {
1855    fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1856        if let CreateScheme::Create2 { .. } = inputs.scheme {
1857            let depth = ecx.journaled_state.depth();
1858            let target_depth = if let Some(prank) = &self.get_prank(depth) {
1859                prank.depth
1860            } else if let Some(broadcast) = &self.broadcast {
1861                broadcast.depth
1862            } else {
1863                1
1864            };
1865
1866            depth == target_depth
1867                && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1868        } else {
1869            false
1870        }
1871    }
1872
1873    fn create2_deployer(&self) -> Address {
1874        self.config.evm_opts.create2_deployer
1875    }
1876}
1877
1878impl Cheatcodes {
1879    #[cold]
1880    fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1881        if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1882            // Keep gas constant if paused.
1883            // Make sure we record the memory changes so that memory expansion is not paused.
1884            let memory = *interpreter.gas.memory();
1885            interpreter.gas = *paused_gas;
1886            interpreter.gas.memory_mut().words_num = memory.words_num;
1887            interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
1888        } else {
1889            // Record frame paused gas.
1890            self.gas_metering.paused_frames.push(interpreter.gas);
1891        }
1892    }
1893
1894    #[cold]
1895    fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1896        if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
1897            self.gas_metering.gas_records.iter_mut().for_each(|record| {
1898                let curr_depth = ecx.journaled_state.depth();
1899                if curr_depth == record.depth {
1900                    // Skip the first opcode of the first call frame as it includes the gas cost of
1901                    // creating the snapshot.
1902                    if self.gas_metering.last_gas_used != 0 {
1903                        let gas_diff =
1904                            interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
1905                        record.gas_used = record.gas_used.saturating_add(gas_diff);
1906                    }
1907
1908                    // Update `last_gas_used` to the current spent gas for the next iteration to
1909                    // compare against.
1910                    self.gas_metering.last_gas_used = interpreter.gas.spent();
1911                }
1912            });
1913        }
1914    }
1915
1916    #[cold]
1917    fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
1918        // Remove recorded gas if we exit frame.
1919        if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1920            && will_exit(interpreter_action)
1921        {
1922            self.gas_metering.paused_frames.pop();
1923        }
1924    }
1925
1926    #[cold]
1927    fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
1928        interpreter.gas = Gas::new(interpreter.gas.limit());
1929        self.gas_metering.reset = false;
1930    }
1931
1932    #[cold]
1933    fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
1934        if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1935            && will_exit(interpreter_action)
1936        {
1937            // Reset gas if spent is less than refunded.
1938            // This can happen if gas was paused / resumed or reset.
1939            // https://github.com/foundry-rs/foundry/issues/4370
1940            if interpreter.gas.spent()
1941                < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
1942            {
1943                interpreter.gas = Gas::new(interpreter.gas.limit());
1944            }
1945        }
1946    }
1947
1948    /// Generates or copies arbitrary values for storage slots.
1949    /// Invoked in inspector `step_end` (when the current opcode is not executed), if current opcode
1950    /// to execute is `SLOAD` and storage slot is cold.
1951    /// Ensures that in next step (when `SLOAD` opcode is executed) an arbitrary value is returned:
1952    /// - copies the existing arbitrary storage value (or the new generated one if no value in
1953    ///   cache) from mapped source address to the target address.
1954    /// - generates arbitrary value and saves it in target address storage.
1955    #[cold]
1956    fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1957        let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
1958            (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
1959        } else {
1960            return;
1961        };
1962
1963        let Some(value) = ecx.sload(target_address, key) else {
1964            return;
1965        };
1966
1967        if (value.is_cold && value.data.is_zero())
1968            || self.should_overwrite_arbitrary_storage(&target_address, key)
1969        {
1970            if self.has_arbitrary_storage(&target_address) {
1971                let arbitrary_value = self.rng().random();
1972                self.arbitrary_storage.as_mut().unwrap().save(
1973                    ecx,
1974                    target_address,
1975                    key,
1976                    arbitrary_value,
1977                );
1978            } else if self.is_arbitrary_storage_copy(&target_address) {
1979                let arbitrary_value = self.rng().random();
1980                self.arbitrary_storage.as_mut().unwrap().copy(
1981                    ecx,
1982                    target_address,
1983                    key,
1984                    arbitrary_value,
1985                );
1986            }
1987        }
1988    }
1989
1990    /// Records storage slots reads and writes.
1991    #[cold]
1992    fn record_accesses(&mut self, interpreter: &mut Interpreter) {
1993        let access = &mut self.accesses;
1994        match interpreter.bytecode.opcode() {
1995            op::SLOAD => {
1996                let key = try_or_return!(interpreter.stack.peek(0));
1997                access.record_read(interpreter.input.target_address, key);
1998            }
1999            op::SSTORE => {
2000                let key = try_or_return!(interpreter.stack.peek(0));
2001                access.record_write(interpreter.input.target_address, key);
2002            }
2003            _ => {}
2004        }
2005    }
2006
2007    #[cold]
2008    fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
2009        let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2010        match interpreter.bytecode.opcode() {
2011            op::SELFDESTRUCT => {
2012                // Ensure that we're not selfdestructing a context recording was initiated on
2013                let Some(last) = account_accesses.last_mut() else { return };
2014
2015                // get previous balance, nonce and initialized status of the target account
2016                let target = try_or_return!(interpreter.stack.peek(0));
2017                let target = Address::from_word(B256::from(target));
2018                let (initialized, old_balance, old_nonce) = ecx
2019                    .journaled_state
2020                    .load_account(target)
2021                    .map(|account| {
2022                        (account.info.exists(), account.info.balance, account.info.nonce)
2023                    })
2024                    .unwrap_or_default();
2025
2026                // load balance of this account
2027                let value = ecx
2028                    .balance(interpreter.input.target_address)
2029                    .map(|b| b.data)
2030                    .unwrap_or(U256::ZERO);
2031
2032                // register access for the target account
2033                last.push(crate::Vm::AccountAccess {
2034                    chainInfo: crate::Vm::ChainInfo {
2035                        forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2036                        chainId: U256::from(ecx.cfg.chain_id),
2037                    },
2038                    accessor: interpreter.input.target_address,
2039                    account: target,
2040                    kind: crate::Vm::AccountAccessKind::SelfDestruct,
2041                    initialized,
2042                    oldBalance: old_balance,
2043                    newBalance: old_balance + value,
2044                    oldNonce: old_nonce,
2045                    newNonce: old_nonce, // nonce doesn't change on selfdestruct
2046                    value,
2047                    data: Bytes::new(),
2048                    reverted: false,
2049                    deployedCode: Bytes::new(),
2050                    storageAccesses: vec![],
2051                    depth: ecx
2052                        .journaled_state
2053                        .depth()
2054                        .try_into()
2055                        .expect("journaled state depth exceeds u64"),
2056                });
2057            }
2058
2059            op::SLOAD => {
2060                let Some(last) = account_accesses.last_mut() else { return };
2061
2062                let key = try_or_return!(interpreter.stack.peek(0));
2063                let address = interpreter.input.target_address;
2064
2065                // Try to include present value for informational purposes, otherwise assume
2066                // it's not set (zero value)
2067                let mut present_value = U256::ZERO;
2068                // Try to load the account and the slot's present value
2069                if ecx.journaled_state.load_account(address).is_ok()
2070                    && let Some(previous) = ecx.sload(address, key)
2071                {
2072                    present_value = previous.data;
2073                }
2074                let access = crate::Vm::StorageAccess {
2075                    account: interpreter.input.target_address,
2076                    slot: key.into(),
2077                    isWrite: false,
2078                    previousValue: present_value.into(),
2079                    newValue: present_value.into(),
2080                    reverted: false,
2081                };
2082                let curr_depth = ecx
2083                    .journaled_state
2084                    .depth()
2085                    .try_into()
2086                    .expect("journaled state depth exceeds u64");
2087                append_storage_access(last, access, curr_depth);
2088            }
2089            op::SSTORE => {
2090                let Some(last) = account_accesses.last_mut() else { return };
2091
2092                let key = try_or_return!(interpreter.stack.peek(0));
2093                let value = try_or_return!(interpreter.stack.peek(1));
2094                let address = interpreter.input.target_address;
2095                // Try to load the account and the slot's previous value, otherwise, assume it's
2096                // not set (zero value)
2097                let mut previous_value = U256::ZERO;
2098                if ecx.journaled_state.load_account(address).is_ok()
2099                    && let Some(previous) = ecx.sload(address, key)
2100                {
2101                    previous_value = previous.data;
2102                }
2103
2104                let access = crate::Vm::StorageAccess {
2105                    account: address,
2106                    slot: key.into(),
2107                    isWrite: true,
2108                    previousValue: previous_value.into(),
2109                    newValue: value.into(),
2110                    reverted: false,
2111                };
2112                let curr_depth = ecx
2113                    .journaled_state
2114                    .depth()
2115                    .try_into()
2116                    .expect("journaled state depth exceeds u64");
2117                append_storage_access(last, access, curr_depth);
2118            }
2119
2120            // Record account accesses via the EXT family of opcodes
2121            op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2122                let kind = match interpreter.bytecode.opcode() {
2123                    op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2124                    op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2125                    op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2126                    op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2127                    _ => unreachable!(),
2128                };
2129                let address =
2130                    Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2131                let initialized;
2132                let balance;
2133                let nonce;
2134                if let Ok(acc) = ecx.journaled_state.load_account(address) {
2135                    initialized = acc.info.exists();
2136                    balance = acc.info.balance;
2137                    nonce = acc.info.nonce;
2138                } else {
2139                    initialized = false;
2140                    balance = U256::ZERO;
2141                    nonce = 0;
2142                }
2143                let curr_depth = ecx
2144                    .journaled_state
2145                    .depth()
2146                    .try_into()
2147                    .expect("journaled state depth exceeds u64");
2148                let account_access = crate::Vm::AccountAccess {
2149                    chainInfo: crate::Vm::ChainInfo {
2150                        forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2151                        chainId: U256::from(ecx.cfg.chain_id),
2152                    },
2153                    accessor: interpreter.input.target_address,
2154                    account: address,
2155                    kind,
2156                    initialized,
2157                    oldBalance: balance,
2158                    newBalance: balance,
2159                    oldNonce: nonce,
2160                    newNonce: nonce, // EXT* operations don't change nonce
2161                    value: U256::ZERO,
2162                    data: Bytes::new(),
2163                    reverted: false,
2164                    deployedCode: Bytes::new(),
2165                    storageAccesses: vec![],
2166                    depth: curr_depth,
2167                };
2168                // Record the EXT* call as an account access at the current depth
2169                // (future storage accesses will be recorded in a new "Resume" context)
2170                if let Some(last) = account_accesses.last_mut() {
2171                    last.push(account_access);
2172                } else {
2173                    account_accesses.push(vec![account_access]);
2174                }
2175            }
2176            _ => {}
2177        }
2178    }
2179
2180    /// Checks to see if the current opcode can either mutate directly or expand memory.
2181    ///
2182    /// If the opcode at the current program counter is a match, check if the modified memory lies
2183    /// within the allowed ranges. If not, revert and fail the test.
2184    #[cold]
2185    fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2186        let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2187            return;
2188        };
2189
2190        // The `mem_opcode_match` macro is used to match the current opcode against a list of
2191        // opcodes that can mutate memory (either directly or expansion via reading). If the
2192        // opcode is a match, the memory offsets that are being written to are checked to be
2193        // within the allowed ranges. If not, the test is failed and the transaction is
2194        // reverted. For all opcodes that can mutate memory aside from MSTORE,
2195        // MSTORE8, and MLOAD, the size and destination offset are on the stack, and
2196        // the macro expands all of these cases. For MSTORE, MSTORE8, and MLOAD, the
2197        // size of the memory write is implicit, so these cases are hard-coded.
2198        macro_rules! mem_opcode_match {
2199            ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2200                match interpreter.bytecode.opcode() {
2201                    ////////////////////////////////////////////////////////////////
2202                    //    OPERATIONS THAT CAN EXPAND/MUTATE MEMORY BY WRITING     //
2203                    ////////////////////////////////////////////////////////////////
2204
2205                    op::MSTORE => {
2206                        // The offset of the mstore operation is at the top of the stack.
2207                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2208
2209                        // If none of the allowed ranges contain [offset, offset + 32), memory has been
2210                        // unexpectedly mutated.
2211                        if !ranges.iter().any(|range| {
2212                            range.contains(&offset) && range.contains(&(offset + 31))
2213                        }) {
2214                            // SPECIAL CASE: When the compiler attempts to store the selector for
2215                            // `stopExpectSafeMemory`, this is allowed. It will do so at the current free memory
2216                            // pointer, which could have been updated to the exclusive upper bound during
2217                            // execution.
2218                            let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2219                            if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2220                                return
2221                            }
2222
2223                            disallowed_mem_write(offset, 32, interpreter, ranges);
2224                            return
2225                        }
2226                    }
2227                    op::MSTORE8 => {
2228                        // The offset of the mstore8 operation is at the top of the stack.
2229                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2230
2231                        // If none of the allowed ranges contain the offset, memory has been
2232                        // unexpectedly mutated.
2233                        if !ranges.iter().any(|range| range.contains(&offset)) {
2234                            disallowed_mem_write(offset, 1, interpreter, ranges);
2235                            return
2236                        }
2237                    }
2238
2239                    ////////////////////////////////////////////////////////////////
2240                    //        OPERATIONS THAT CAN EXPAND MEMORY BY READING        //
2241                    ////////////////////////////////////////////////////////////////
2242
2243                    op::MLOAD => {
2244                        // The offset of the mload operation is at the top of the stack
2245                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2246
2247                        // If the offset being loaded is >= than the memory size, the
2248                        // memory is being expanded. If none of the allowed ranges contain
2249                        // [offset, offset + 32), memory has been unexpectedly mutated.
2250                        if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2251                            range.contains(&offset) && range.contains(&(offset + 31))
2252                        }) {
2253                            disallowed_mem_write(offset, 32, interpreter, ranges);
2254                            return
2255                        }
2256                    }
2257
2258                    ////////////////////////////////////////////////////////////////
2259                    //          OPERATIONS WITH OFFSET AND SIZE ON STACK          //
2260                    ////////////////////////////////////////////////////////////////
2261
2262                    op::CALL => {
2263                        // The destination offset of the operation is the fifth element on the stack.
2264                        let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2265
2266                        // The size of the data that will be copied is the sixth element on the stack.
2267                        let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2268
2269                        // If none of the allowed ranges contain [dest_offset, dest_offset + size),
2270                        // memory outside of the expected ranges has been touched. If the opcode
2271                        // only reads from memory, this is okay as long as the memory is not expanded.
2272                        let fail_cond = !ranges.iter().any(|range| {
2273                            range.contains(&dest_offset) &&
2274                                range.contains(&(dest_offset + size.saturating_sub(1)))
2275                        });
2276
2277                        // If the failure condition is met, set the output buffer to a revert string
2278                        // that gives information about the allowed ranges and revert.
2279                        if fail_cond {
2280                            // SPECIAL CASE: When a call to `stopExpectSafeMemory` is performed, this is allowed.
2281                            // It allocated calldata at the current free memory pointer, and will attempt to read
2282                            // from this memory region to perform the call.
2283                            let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2284                            if to == CHEATCODE_ADDRESS {
2285                                let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2286                                let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2287                                let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2288                                if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2289                                    return
2290                                }
2291                            }
2292
2293                            disallowed_mem_write(dest_offset, size, interpreter, ranges);
2294                            return
2295                        }
2296                    }
2297
2298                    $(op::$opcode => {
2299                        // The destination offset of the operation.
2300                        let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2301
2302                        // The size of the data that will be copied.
2303                        let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2304
2305                        // If none of the allowed ranges contain [dest_offset, dest_offset + size),
2306                        // memory outside of the expected ranges has been touched. If the opcode
2307                        // only reads from memory, this is okay as long as the memory is not expanded.
2308                        let fail_cond = !ranges.iter().any(|range| {
2309                                range.contains(&dest_offset) &&
2310                                    range.contains(&(dest_offset + size.saturating_sub(1)))
2311                            }) && ($writes ||
2312                                [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2313                                    offset >= interpreter.memory.size() as u64
2314                                })
2315                            );
2316
2317                        // If the failure condition is met, set the output buffer to a revert string
2318                        // that gives information about the allowed ranges and revert.
2319                        if fail_cond {
2320                            disallowed_mem_write(dest_offset, size, interpreter, ranges);
2321                            return
2322                        }
2323                    })*
2324
2325                    _ => {}
2326                }
2327            }
2328        }
2329
2330        // Check if the current opcode can write to memory, and if so, check if the memory
2331        // being written to is registered as safe to modify.
2332        mem_opcode_match!(
2333            (CALLDATACOPY, 0, 2, true),
2334            (CODECOPY, 0, 2, true),
2335            (RETURNDATACOPY, 0, 2, true),
2336            (EXTCODECOPY, 1, 3, true),
2337            (CALLCODE, 5, 6, true),
2338            (STATICCALL, 4, 5, true),
2339            (DELEGATECALL, 4, 5, true),
2340            (KECCAK256, 0, 1, false),
2341            (LOG0, 0, 1, false),
2342            (LOG1, 0, 1, false),
2343            (LOG2, 0, 1, false),
2344            (LOG3, 0, 1, false),
2345            (LOG4, 0, 1, false),
2346            (CREATE, 1, 2, false),
2347            (CREATE2, 1, 2, false),
2348            (RETURN, 0, 1, false),
2349            (REVERT, 0, 1, false),
2350        );
2351    }
2352
2353    #[cold]
2354    fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2355        match interpreter.bytecode.opcode() {
2356            op::CREATE2 => self.dynamic_gas_limit = true,
2357            op::CALL => {
2358                // If first element of the stack is close to current remaining gas then assume
2359                // dynamic gas limit.
2360                self.dynamic_gas_limit =
2361                    try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
2362            }
2363            _ => self.dynamic_gas_limit = false,
2364        }
2365    }
2366}
2367
2368/// Helper that expands memory, stores a revert string pertaining to a disallowed memory write,
2369/// and sets the return range to the revert string's location in memory.
2370///
2371/// This will set the interpreter's next action to a return with the revert string as the output.
2372/// And trigger a revert.
2373fn disallowed_mem_write(
2374    dest_offset: u64,
2375    size: u64,
2376    interpreter: &mut Interpreter,
2377    ranges: &[Range<u64>],
2378) {
2379    let revert_string = format!(
2380        "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2381        dest_offset,
2382        size,
2383        ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2384    );
2385
2386    interpreter.bytecode.set_action(InterpreterAction::new_return(
2387        InstructionResult::Revert,
2388        Bytes::from(revert_string.into_bytes()),
2389        interpreter.gas,
2390    ));
2391}
2392
2393/// Returns true if the kind of account access is a call.
2394fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2395    matches!(
2396        kind,
2397        crate::Vm::AccountAccessKind::Call
2398            | crate::Vm::AccountAccessKind::StaticCall
2399            | crate::Vm::AccountAccessKind::CallCode
2400            | crate::Vm::AccountAccessKind::DelegateCall
2401    )
2402}
2403
2404/// Appends an AccountAccess that resumes the recording of the current context.
2405fn append_storage_access(
2406    last: &mut Vec<AccountAccess>,
2407    storage_access: crate::Vm::StorageAccess,
2408    storage_depth: u64,
2409) {
2410    // Assert that there's an existing record for the current context.
2411    if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2412        // Three cases to consider:
2413        // 1. If there hasn't been a context switch since the start of this context, then add the
2414        //    storage access to the current context record.
2415        // 2. If there's an existing Resume record, then add the storage access to it.
2416        // 3. Otherwise, create a new Resume record based on the current context.
2417        if last.len() == 1 {
2418            last.first_mut().unwrap().storageAccesses.push(storage_access);
2419        } else {
2420            let last_record = last.last_mut().unwrap();
2421            if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2422                last_record.storageAccesses.push(storage_access);
2423            } else {
2424                let entry = last.first().unwrap();
2425                let resume_record = crate::Vm::AccountAccess {
2426                    chainInfo: crate::Vm::ChainInfo {
2427                        forkId: entry.chainInfo.forkId,
2428                        chainId: entry.chainInfo.chainId,
2429                    },
2430                    accessor: entry.accessor,
2431                    account: entry.account,
2432                    kind: crate::Vm::AccountAccessKind::Resume,
2433                    initialized: entry.initialized,
2434                    storageAccesses: vec![storage_access],
2435                    reverted: entry.reverted,
2436                    // The remaining fields are defaults
2437                    oldBalance: U256::ZERO,
2438                    newBalance: U256::ZERO,
2439                    oldNonce: 0,
2440                    newNonce: 0,
2441                    value: U256::ZERO,
2442                    data: Bytes::new(),
2443                    deployedCode: Bytes::new(),
2444                    depth: entry.depth,
2445                };
2446                last.push(resume_record);
2447            }
2448        }
2449    }
2450}
2451
2452/// Dispatches the cheatcode call to the appropriate function.
2453fn apply_dispatch(
2454    calls: &Vm::VmCalls,
2455    ccx: &mut CheatsCtxt,
2456    executor: &mut dyn CheatcodesExecutor,
2457) -> Result {
2458    let cheat = calls_as_dyn_cheatcode(calls);
2459
2460    let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2461    trace!(target: "cheatcodes", ?cheat, "applying");
2462
2463    if let spec::Status::Deprecated(replacement) = *cheat.status() {
2464        ccx.state.deprecated.insert(cheat.signature(), replacement);
2465    }
2466
2467    // Apply the cheatcode.
2468    let mut result = cheat.dyn_apply(ccx, executor);
2469
2470    // Format the error message to include the cheatcode name.
2471    if let Err(e) = &mut result
2472        && e.is_str()
2473    {
2474        let name = cheat.name();
2475        // Skip showing the cheatcode name for:
2476        // - assertions: too verbose, and can already be inferred from the error message
2477        // - `rpcUrl`: forge-std relies on it in `getChainWithUpdatedRpcUrl`
2478        if !name.contains("assert") && name != "rpcUrl" {
2479            *e = fmt_err!("vm.{name}: {e}");
2480        }
2481    }
2482
2483    trace!(
2484        target: "cheatcodes",
2485        return = %match &result {
2486            Ok(b) => hex::encode(b),
2487            Err(e) => e.to_string(),
2488        }
2489    );
2490
2491    result
2492}
2493
2494fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2495    macro_rules! as_dyn {
2496        ($($variant:ident),*) => {
2497            match calls {
2498                $(Vm::VmCalls::$variant(cheat) => cheat,)*
2499            }
2500        };
2501    }
2502    vm_calls!(as_dyn)
2503}
2504
2505/// Helper function to check if frame execution will exit.
2506fn will_exit(action: &InterpreterAction) -> bool {
2507    match action {
2508        InterpreterAction::Return(result) => {
2509            result.result.is_ok_or_revert() || result.result.is_error()
2510        }
2511        _ => false,
2512    }
2513}