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