foundry_cheatcodes/
inspector.rs

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