Skip to main content

foundry_cheatcodes/
inspector.rs

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