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