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