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