foundry_cheatcodes/
inspector.rs

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