Skip to main content

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