foundry_evm/inspectors/
stack.rs

1use super::{
2    Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, Fuzzer, LogCollector,
3    TracingInspector,
4};
5use alloy_primitives::{map::AddressHashMap, Address, Bytes, Log, TxKind, U256};
6use foundry_cheatcodes::{CheatcodesExecutor, Wallets};
7use foundry_evm_core::{backend::DatabaseExt, InspectorExt};
8use foundry_evm_coverage::HitMaps;
9use foundry_evm_traces::{SparsedTraceArena, TraceMode};
10use revm::{
11    inspectors::CustomPrintTracer,
12    interpreter::{
13        CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, EOFCreateInputs,
14        EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterResult,
15    },
16    primitives::{
17        Account, AccountStatus, BlockEnv, CreateScheme, Env, EnvWithHandlerCfg, ExecutionResult,
18        HashMap, Output, TransactTo,
19    },
20    EvmContext, Inspector, JournaledState,
21};
22use std::{
23    ops::{Deref, DerefMut},
24    sync::Arc,
25};
26
27#[derive(Clone, Debug, Default)]
28#[must_use = "builders do nothing unless you call `build` on them"]
29pub struct InspectorStackBuilder {
30    /// The block environment.
31    ///
32    /// Used in the cheatcode handler to overwrite the block environment separately from the
33    /// execution block environment.
34    pub block: Option<BlockEnv>,
35    /// The gas price.
36    ///
37    /// Used in the cheatcode handler to overwrite the gas price separately from the gas price
38    /// in the execution environment.
39    pub gas_price: Option<U256>,
40    /// The cheatcodes config.
41    pub cheatcodes: Option<Arc<CheatsConfig>>,
42    /// The fuzzer inspector and its state, if it exists.
43    pub fuzzer: Option<Fuzzer>,
44    /// Whether to enable tracing.
45    pub trace_mode: TraceMode,
46    /// Whether logs should be collected.
47    pub logs: Option<bool>,
48    /// Whether coverage info should be collected.
49    pub coverage: Option<bool>,
50    /// Whether to print all opcode traces into the console. Useful for debugging the EVM.
51    pub print: Option<bool>,
52    /// The chisel state inspector.
53    pub chisel_state: Option<usize>,
54    /// Whether to enable call isolation.
55    /// In isolation mode all top-level calls are executed as a separate transaction in a separate
56    /// EVM context, enabling more precise gas accounting and transaction state changes.
57    pub enable_isolation: bool,
58    /// Whether to enable Odyssey features.
59    pub odyssey: bool,
60    /// The wallets to set in the cheatcodes context.
61    pub wallets: Option<Wallets>,
62    /// The CREATE2 deployer address.
63    pub create2_deployer: Address,
64}
65
66impl InspectorStackBuilder {
67    /// Create a new inspector stack builder.
68    #[inline]
69    pub fn new() -> Self {
70        Self::default()
71    }
72
73    /// Set the block environment.
74    #[inline]
75    pub fn block(mut self, block: BlockEnv) -> Self {
76        self.block = Some(block);
77        self
78    }
79
80    /// Set the gas price.
81    #[inline]
82    pub fn gas_price(mut self, gas_price: U256) -> Self {
83        self.gas_price = Some(gas_price);
84        self
85    }
86
87    /// Enable cheatcodes with the given config.
88    #[inline]
89    pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
90        self.cheatcodes = Some(config);
91        self
92    }
93
94    /// Set the wallets.
95    #[inline]
96    pub fn wallets(mut self, wallets: Wallets) -> Self {
97        self.wallets = Some(wallets);
98        self
99    }
100
101    /// Set the fuzzer inspector.
102    #[inline]
103    pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
104        self.fuzzer = Some(fuzzer);
105        self
106    }
107
108    /// Set the Chisel inspector.
109    #[inline]
110    pub fn chisel_state(mut self, final_pc: usize) -> Self {
111        self.chisel_state = Some(final_pc);
112        self
113    }
114
115    /// Set whether to collect logs.
116    #[inline]
117    pub fn logs(mut self, yes: bool) -> Self {
118        self.logs = Some(yes);
119        self
120    }
121
122    /// Set whether to collect coverage information.
123    #[inline]
124    pub fn coverage(mut self, yes: bool) -> Self {
125        self.coverage = Some(yes);
126        self
127    }
128
129    /// Set whether to enable the trace printer.
130    #[inline]
131    pub fn print(mut self, yes: bool) -> Self {
132        self.print = Some(yes);
133        self
134    }
135
136    /// Set whether to enable the tracer.
137    #[inline]
138    pub fn trace_mode(mut self, mode: TraceMode) -> Self {
139        if self.trace_mode < mode {
140            self.trace_mode = mode
141        }
142        self
143    }
144
145    /// Set whether to enable the call isolation.
146    /// For description of call isolation, see [`InspectorStack::enable_isolation`].
147    #[inline]
148    pub fn enable_isolation(mut self, yes: bool) -> Self {
149        self.enable_isolation = yes;
150        self
151    }
152
153    /// Set whether to enable Odyssey features.
154    /// For description of call isolation, see [`InspectorStack::enable_isolation`].
155    #[inline]
156    pub fn odyssey(mut self, yes: bool) -> Self {
157        self.odyssey = yes;
158        self
159    }
160
161    #[inline]
162    pub fn create2_deployer(mut self, create2_deployer: Address) -> Self {
163        self.create2_deployer = create2_deployer;
164        self
165    }
166
167    /// Builds the stack of inspectors to use when transacting/committing on the EVM.
168    pub fn build(self) -> InspectorStack {
169        let Self {
170            block,
171            gas_price,
172            cheatcodes,
173            fuzzer,
174            trace_mode,
175            logs,
176            coverage,
177            print,
178            chisel_state,
179            enable_isolation,
180            odyssey,
181            wallets,
182            create2_deployer,
183        } = self;
184        let mut stack = InspectorStack::new();
185
186        // inspectors
187        if let Some(config) = cheatcodes {
188            let mut cheatcodes = Cheatcodes::new(config);
189            // Set wallets if they are provided
190            if let Some(wallets) = wallets {
191                cheatcodes.set_wallets(wallets);
192            }
193            stack.set_cheatcodes(cheatcodes);
194        }
195
196        if let Some(fuzzer) = fuzzer {
197            stack.set_fuzzer(fuzzer);
198        }
199        if let Some(chisel_state) = chisel_state {
200            stack.set_chisel(chisel_state);
201        }
202        stack.collect_coverage(coverage.unwrap_or(false));
203        stack.collect_logs(logs.unwrap_or(true));
204        stack.print(print.unwrap_or(false));
205        stack.tracing(trace_mode);
206
207        stack.enable_isolation(enable_isolation);
208        stack.odyssey(odyssey);
209        stack.set_create2_deployer(create2_deployer);
210
211        // environment, must come after all of the inspectors
212        if let Some(block) = block {
213            stack.set_block(&block);
214        }
215        if let Some(gas_price) = gas_price {
216            stack.set_gas_price(gas_price);
217        }
218
219        stack
220    }
221}
222
223/// Helper macro to call the same method on multiple inspectors without resorting to dynamic
224/// dispatch.
225#[macro_export]
226macro_rules! call_inspectors {
227    ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {
228        $(
229            if let Some($id) = $inspector {
230                ({ #[inline(always)] #[cold] || $call })();
231            }
232        )+
233    };
234    (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {
235        $(
236            if let Some($id) = $inspector {
237                if let Some(result) = ({ #[inline(always)] #[cold] || $call })() {
238                    return result;
239                }
240            }
241        )+
242    };
243}
244
245/// The collected results of [`InspectorStack`].
246pub struct InspectorData {
247    pub logs: Vec<Log>,
248    pub labels: AddressHashMap<String>,
249    pub traces: Option<SparsedTraceArena>,
250    pub coverage: Option<HitMaps>,
251    pub cheatcodes: Option<Cheatcodes>,
252    pub chisel_state: Option<(Vec<U256>, Vec<u8>, InstructionResult)>,
253}
254
255/// Contains data about the state of outer/main EVM which created and invoked the inner EVM context.
256/// Used to adjust EVM state while in inner context.
257///
258/// We need this to avoid breaking changes due to EVM behavior differences in isolated vs
259/// non-isolated mode. For descriptions and workarounds for those changes see: <https://github.com/foundry-rs/foundry/pull/7186#issuecomment-1959102195>
260#[derive(Debug, Clone)]
261pub struct InnerContextData {
262    /// Origin of the transaction in the outer EVM context.
263    original_origin: Address,
264}
265
266/// An inspector that calls multiple inspectors in sequence.
267///
268/// If a call to an inspector returns a value other than [InstructionResult::Continue] (or
269/// equivalent) the remaining inspectors are not called.
270///
271/// Stack is divided into [Cheatcodes] and `InspectorStackInner`. This is done to allow assembling
272/// `InspectorStackRefMut` inside [Cheatcodes] to allow usage of it as [revm::Inspector]. This gives
273/// us ability to create and execute separate EVM frames from inside cheatcodes while still having
274/// access to entire stack of inspectors and correctly handling traces, logs, debugging info
275/// collection, etc.
276#[derive(Clone, Debug, Default)]
277pub struct InspectorStack {
278    pub cheatcodes: Option<Cheatcodes>,
279    pub inner: InspectorStackInner,
280}
281
282/// All used inpectors besides [Cheatcodes].
283///
284/// See [`InspectorStack`].
285#[derive(Default, Clone, Debug)]
286pub struct InspectorStackInner {
287    pub chisel_state: Option<ChiselState>,
288    pub coverage: Option<CoverageCollector>,
289    pub fuzzer: Option<Fuzzer>,
290    pub log_collector: Option<LogCollector>,
291    pub printer: Option<CustomPrintTracer>,
292    pub tracer: Option<TracingInspector>,
293    pub enable_isolation: bool,
294    pub odyssey: bool,
295    pub create2_deployer: Address,
296
297    /// Flag marking if we are in the inner EVM context.
298    pub in_inner_context: bool,
299    pub inner_context_data: Option<InnerContextData>,
300    pub top_frame_journal: HashMap<Address, Account>,
301}
302
303/// Struct keeping mutable references to both parts of [InspectorStack] and implementing
304/// [revm::Inspector]. This struct can be obtained via [InspectorStack::as_mut] or via
305/// [CheatcodesExecutor::get_inspector] method implemented for [InspectorStackInner].
306pub struct InspectorStackRefMut<'a> {
307    pub cheatcodes: Option<&'a mut Cheatcodes>,
308    pub inner: &'a mut InspectorStackInner,
309}
310
311impl CheatcodesExecutor for InspectorStackInner {
312    fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
313        Box::new(InspectorStackRefMut { cheatcodes: Some(cheats), inner: self })
314    }
315
316    fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
317        Some(&mut self.tracer)
318    }
319}
320
321impl InspectorStack {
322    /// Creates a new inspector stack.
323    ///
324    /// Note that the stack is empty by default, and you must add inspectors to it.
325    /// This is done by calling the `set_*` methods on the stack directly, or by building the stack
326    /// with [`InspectorStack`].
327    #[inline]
328    pub fn new() -> Self {
329        Self::default()
330    }
331
332    /// Logs the status of the inspectors.
333    pub fn log_status(&self) {
334        trace!(enabled=%{
335            let mut enabled = Vec::with_capacity(16);
336            macro_rules! push {
337                ($($id:ident),* $(,)?) => {
338                    $(
339                        if self.$id.is_some() {
340                            enabled.push(stringify!($id));
341                        }
342                    )*
343                };
344            }
345            push!(cheatcodes, chisel_state, coverage, fuzzer, log_collector, printer, tracer);
346            if self.enable_isolation {
347                enabled.push("isolation");
348            }
349            format!("[{}]", enabled.join(", "))
350        });
351    }
352
353    /// Set variables from an environment for the relevant inspectors.
354    #[inline]
355    pub fn set_env(&mut self, env: &Env) {
356        self.set_block(&env.block);
357        self.set_gas_price(env.tx.gas_price);
358    }
359
360    /// Sets the block for the relevant inspectors.
361    #[inline]
362    pub fn set_block(&mut self, block: &BlockEnv) {
363        if let Some(cheatcodes) = &mut self.cheatcodes {
364            cheatcodes.block = Some(block.clone());
365        }
366    }
367
368    /// Sets the gas price for the relevant inspectors.
369    #[inline]
370    pub fn set_gas_price(&mut self, gas_price: U256) {
371        if let Some(cheatcodes) = &mut self.cheatcodes {
372            cheatcodes.gas_price = Some(gas_price);
373        }
374    }
375
376    /// Set the cheatcodes inspector.
377    #[inline]
378    pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes) {
379        self.cheatcodes = Some(cheatcodes);
380    }
381
382    /// Set the fuzzer inspector.
383    #[inline]
384    pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
385        self.fuzzer = Some(fuzzer);
386    }
387
388    /// Set the Chisel inspector.
389    #[inline]
390    pub fn set_chisel(&mut self, final_pc: usize) {
391        self.chisel_state = Some(ChiselState::new(final_pc));
392    }
393
394    /// Set whether to enable the coverage collector.
395    #[inline]
396    pub fn collect_coverage(&mut self, yes: bool) {
397        self.coverage = yes.then(Default::default);
398    }
399
400    /// Set whether to enable call isolation.
401    #[inline]
402    pub fn enable_isolation(&mut self, yes: bool) {
403        self.enable_isolation = yes;
404    }
405
406    /// Set whether to enable call isolation.
407    #[inline]
408    pub fn odyssey(&mut self, yes: bool) {
409        self.odyssey = yes;
410    }
411
412    /// Set the CREATE2 deployer address.
413    #[inline]
414    pub fn set_create2_deployer(&mut self, deployer: Address) {
415        self.create2_deployer = deployer;
416    }
417
418    /// Set whether to enable the log collector.
419    #[inline]
420    pub fn collect_logs(&mut self, yes: bool) {
421        self.log_collector = yes.then(Default::default);
422    }
423
424    /// Set whether to enable the trace printer.
425    #[inline]
426    pub fn print(&mut self, yes: bool) {
427        self.printer = yes.then(Default::default);
428    }
429
430    /// Set whether to enable the tracer.
431    #[inline]
432    pub fn tracing(&mut self, mode: TraceMode) {
433        if let Some(config) = mode.into_config() {
434            *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
435        } else {
436            self.tracer = None;
437        }
438    }
439
440    /// Collects all the data gathered during inspection into a single struct.
441    #[inline]
442    pub fn collect(self) -> InspectorData {
443        let Self {
444            mut cheatcodes,
445            inner: InspectorStackInner { chisel_state, coverage, log_collector, tracer, .. },
446        } = self;
447
448        let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
449            let ignored = cheatcodes
450                .as_mut()
451                .map(|cheatcodes| {
452                    let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
453
454                    // If the last pause call was not resumed, ignore the rest of the trace
455                    if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
456                        ignored.insert(last_pause_call, (arena.nodes().len(), 0));
457                    }
458
459                    ignored
460                })
461                .unwrap_or_default();
462
463            SparsedTraceArena { arena, ignored }
464        });
465
466        InspectorData {
467            logs: log_collector.map(|logs| logs.logs).unwrap_or_default(),
468            labels: cheatcodes
469                .as_ref()
470                .map(|cheatcodes| cheatcodes.labels.clone())
471                .unwrap_or_default(),
472            traces,
473            coverage: coverage.map(|coverage| coverage.finish()),
474            cheatcodes,
475            chisel_state: chisel_state.and_then(|state| state.state),
476        }
477    }
478
479    #[inline(always)]
480    fn as_mut(&mut self) -> InspectorStackRefMut<'_> {
481        InspectorStackRefMut { cheatcodes: self.cheatcodes.as_mut(), inner: &mut self.inner }
482    }
483}
484
485impl InspectorStackRefMut<'_> {
486    /// Adjusts the EVM data for the inner EVM context.
487    /// Should be called on the top-level call of inner context (depth == 0 &&
488    /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility
489    /// Updates tx.origin to the value before entering inner context
490    fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
491        let inner_context_data =
492            self.inner_context_data.as_ref().expect("should be called in inner context");
493        ecx.env.tx.caller = inner_context_data.original_origin;
494    }
495
496    fn do_call_end(
497        &mut self,
498        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
499        inputs: &CallInputs,
500        outcome: CallOutcome,
501    ) -> CallOutcome {
502        let result = outcome.result.result;
503        call_inspectors!(
504            #[ret]
505            [&mut self.fuzzer, &mut self.tracer, &mut self.cheatcodes, &mut self.printer],
506            |inspector| {
507                let new_outcome = inspector.call_end(ecx, inputs, outcome.clone());
508
509                // If the inspector returns a different status or a revert with a non-empty message,
510                // we assume it wants to tell us something
511                let different = new_outcome.result.result != result ||
512                    (new_outcome.result.result == InstructionResult::Revert &&
513                        new_outcome.output() != outcome.output());
514                different.then_some(new_outcome)
515            },
516        );
517
518        outcome
519    }
520
521    fn do_create_end(
522        &mut self,
523        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
524        call: &CreateInputs,
525        outcome: CreateOutcome,
526    ) -> CreateOutcome {
527        let result = outcome.result.result;
528        call_inspectors!(
529            #[ret]
530            [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
531            |inspector| {
532                let new_outcome = inspector.create_end(ecx, call, outcome.clone());
533
534                // If the inspector returns a different status or a revert with a non-empty message,
535                // we assume it wants to tell us something
536                let different = new_outcome.result.result != result ||
537                    (new_outcome.result.result == InstructionResult::Revert &&
538                        new_outcome.output() != outcome.output());
539                different.then_some(new_outcome)
540            },
541        );
542
543        outcome
544    }
545
546    fn do_eofcreate_end(
547        &mut self,
548        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
549        call: &EOFCreateInputs,
550        outcome: CreateOutcome,
551    ) -> CreateOutcome {
552        let result = outcome.result.result;
553        call_inspectors!(
554            #[ret]
555            [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
556            |inspector| {
557                let new_outcome = inspector.eofcreate_end(ecx, call, outcome.clone());
558
559                // If the inspector returns a different status or a revert with a non-empty message,
560                // we assume it wants to tell us something
561                let different = new_outcome.result.result != result ||
562                    (new_outcome.result.result == InstructionResult::Revert &&
563                        new_outcome.output() != outcome.output());
564                different.then_some(new_outcome)
565            },
566        );
567
568        outcome
569    }
570
571    fn transact_inner(
572        &mut self,
573        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
574        transact_to: TransactTo,
575        caller: Address,
576        input: Bytes,
577        gas_limit: u64,
578        value: U256,
579    ) -> (InterpreterResult, Option<Address>) {
580        let ecx = &mut ecx.inner;
581
582        let cached_env = ecx.env.clone();
583
584        ecx.env.block.basefee = U256::ZERO;
585        ecx.env.tx.caller = caller;
586        ecx.env.tx.transact_to = transact_to;
587        ecx.env.tx.data = input;
588        ecx.env.tx.value = value;
589        // Add 21000 to the gas limit to account for the base cost of transaction.
590        ecx.env.tx.gas_limit = gas_limit + 21000;
591        // If we haven't disabled gas limit checks, ensure that transaction gas limit will not
592        // exceed block gas limit.
593        if !ecx.env.cfg.disable_block_gas_limit {
594            ecx.env.tx.gas_limit =
595                std::cmp::min(ecx.env.tx.gas_limit, ecx.env.block.gas_limit.to());
596        }
597        ecx.env.tx.gas_price = U256::ZERO;
598
599        self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller });
600        self.in_inner_context = true;
601
602        let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id());
603        let res = self.with_stack(|inspector| {
604            let mut evm = crate::utils::new_evm_with_inspector(&mut ecx.db, env, inspector);
605
606            evm.context.evm.inner.journaled_state.state = {
607                let mut state = ecx.journaled_state.state.clone();
608
609                for (addr, acc_mut) in &mut state {
610                    // mark all accounts cold, besides preloaded addresses
611                    if !ecx.journaled_state.warm_preloaded_addresses.contains(addr) {
612                        acc_mut.mark_cold();
613                    }
614
615                    // mark all slots cold
616                    for slot_mut in acc_mut.storage.values_mut() {
617                        slot_mut.is_cold = true;
618                        slot_mut.original_value = slot_mut.present_value;
619                    }
620                }
621
622                state
623            };
624
625            // set depth to 1 to make sure traces are collected correctly
626            evm.context.evm.inner.journaled_state.depth = 1;
627
628            let res = evm.transact();
629
630            // need to reset the env in case it was modified via cheatcodes during execution
631            ecx.env = evm.context.evm.inner.env;
632            res
633        });
634
635        self.in_inner_context = false;
636        self.inner_context_data = None;
637
638        ecx.env.tx = cached_env.tx;
639        ecx.env.block.basefee = cached_env.block.basefee;
640
641        let mut gas = Gas::new(gas_limit);
642
643        let Ok(res) = res else {
644            // Should we match, encode and propagate error as a revert reason?
645            let result =
646                InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
647            return (result, None);
648        };
649
650        for (addr, mut acc) in res.state {
651            let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) else {
652                ecx.journaled_state.state.insert(addr, acc);
653                continue
654            };
655
656            // make sure accounts that were warmed earlier do not become cold
657            if acc.status.contains(AccountStatus::Cold) &&
658                !acc_mut.status.contains(AccountStatus::Cold)
659            {
660                acc.status -= AccountStatus::Cold;
661            }
662            acc_mut.info = acc.info;
663            acc_mut.status |= acc.status;
664
665            for (key, val) in acc.storage {
666                let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
667                    acc_mut.storage.insert(key, val);
668                    continue
669                };
670                slot_mut.present_value = val.present_value;
671                slot_mut.is_cold &= val.is_cold;
672            }
673        }
674
675        let (result, address, output) = match res.result {
676            ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => {
677                gas.set_refund(gas_refunded as i64);
678                let _ = gas.record_cost(gas_used);
679                let address = match output {
680                    Output::Create(_, address) => address,
681                    Output::Call(_) => None,
682                };
683                (reason.into(), address, output.into_data())
684            }
685            ExecutionResult::Halt { reason, gas_used } => {
686                let _ = gas.record_cost(gas_used);
687                (reason.into(), None, Bytes::new())
688            }
689            ExecutionResult::Revert { gas_used, output } => {
690                let _ = gas.record_cost(gas_used);
691                (InstructionResult::Revert, None, output)
692            }
693        };
694        (InterpreterResult { result, output, gas }, address)
695    }
696
697    /// Moves out of references, constructs an [`InspectorStack`] and runs the given closure with
698    /// it.
699    fn with_stack<O>(&mut self, f: impl FnOnce(&mut InspectorStack) -> O) -> O {
700        let mut stack = InspectorStack {
701            cheatcodes: self
702                .cheatcodes
703                .as_deref_mut()
704                .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone()))),
705            inner: std::mem::take(self.inner),
706        };
707
708        let out = f(&mut stack);
709
710        if let Some(cheats) = self.cheatcodes.as_deref_mut() {
711            *cheats = stack.cheatcodes.take().unwrap();
712        }
713
714        *self.inner = stack.inner;
715
716        out
717    }
718
719    /// Invoked at the beginning of a new top-level (0 depth) frame.
720    fn top_level_frame_start(&mut self, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
721        if self.enable_isolation {
722            // If we're in isolation mode, we need to keep track of the state at the beginning of
723            // the frame to be able to roll back on revert
724            self.top_frame_journal = ecx.journaled_state.state.clone();
725        }
726    }
727
728    /// Invoked at the end of root frame.
729    fn top_level_frame_end(
730        &mut self,
731        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
732        result: InstructionResult,
733    ) {
734        if !result.is_revert() {
735            return;
736        }
737        // Encountered a revert, since cheatcodes may have altered the evm state in such a way
738        // that violates some constraints, e.g. `deal`, we need to manually roll back on revert
739        // before revm reverts the state itself
740        if let Some(cheats) = self.cheatcodes.as_mut() {
741            cheats.on_revert(ecx);
742        }
743
744        // If we're in isolation mode, we need to rollback to state before the root frame was
745        // created We can't rely on revm's journal because it doesn't account for changes
746        // made by isolated calls
747        if self.enable_isolation {
748            ecx.journaled_state.state = std::mem::take(&mut self.top_frame_journal);
749        }
750    }
751}
752
753impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> {
754    fn initialize_interp(
755        &mut self,
756        interpreter: &mut Interpreter,
757        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
758    ) {
759        call_inspectors!(
760            [&mut self.coverage, &mut self.tracer, &mut self.cheatcodes, &mut self.printer],
761            |inspector| inspector.initialize_interp(interpreter, ecx),
762        );
763    }
764
765    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
766        call_inspectors!(
767            [
768                &mut self.fuzzer,
769                &mut self.tracer,
770                &mut self.coverage,
771                &mut self.cheatcodes,
772                &mut self.printer,
773            ],
774            |inspector| inspector.step(interpreter, ecx),
775        );
776    }
777
778    fn step_end(
779        &mut self,
780        interpreter: &mut Interpreter,
781        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
782    ) {
783        call_inspectors!(
784            [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state, &mut self.printer],
785            |inspector| inspector.step_end(interpreter, ecx),
786        );
787    }
788
789    fn log(
790        &mut self,
791        interpreter: &mut Interpreter,
792        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
793        log: &Log,
794    ) {
795        call_inspectors!(
796            [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
797            |inspector| inspector.log(interpreter, ecx, log),
798        );
799    }
800
801    fn call(
802        &mut self,
803        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
804        call: &mut CallInputs,
805    ) -> Option<CallOutcome> {
806        if self.in_inner_context && ecx.journaled_state.depth == 1 {
807            self.adjust_evm_data_for_inner_context(ecx);
808            return None;
809        }
810
811        if ecx.journaled_state.depth == 0 {
812            self.top_level_frame_start(ecx);
813        }
814
815        call_inspectors!(
816            #[ret]
817            [&mut self.fuzzer, &mut self.tracer, &mut self.log_collector, &mut self.printer],
818            |inspector| {
819                let mut out = None;
820                if let Some(output) = inspector.call(ecx, call) {
821                    if output.result.result != InstructionResult::Continue {
822                        out = Some(Some(output));
823                    }
824                }
825                out
826            },
827        );
828
829        if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
830            // Handle mocked functions, replace bytecode address with mock if matched.
831            if let Some(mocks) = cheatcodes.mocked_functions.get(&call.target_address) {
832                // Check if any mock function set for call data or if catch-all mock function set
833                // for selector.
834                if let Some(target) = mocks
835                    .get(&call.input)
836                    .or_else(|| call.input.get(..4).and_then(|selector| mocks.get(selector)))
837                {
838                    call.bytecode_address = *target;
839                }
840            }
841
842            if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
843                if output.result.result != InstructionResult::Continue {
844                    return Some(output);
845                }
846            }
847        }
848
849        if self.enable_isolation && !self.in_inner_context && ecx.journaled_state.depth == 1 {
850            match call.scheme {
851                // Isolate CALLs
852                CallScheme::Call | CallScheme::ExtCall => {
853                    let (result, _) = self.transact_inner(
854                        ecx,
855                        TxKind::Call(call.target_address),
856                        call.caller,
857                        call.input.clone(),
858                        call.gas_limit,
859                        call.value.get(),
860                    );
861                    return Some(CallOutcome {
862                        result,
863                        memory_offset: call.return_memory_offset.clone(),
864                    });
865                }
866                // Mark accounts and storage cold before STATICCALLs
867                CallScheme::StaticCall | CallScheme::ExtStaticCall => {
868                    let JournaledState { state, warm_preloaded_addresses, .. } =
869                        &mut ecx.journaled_state;
870                    for (addr, acc_mut) in state {
871                        // Do not mark accounts and storage cold accounts with arbitrary storage.
872                        if let Some(cheatcodes) = &self.cheatcodes {
873                            if cheatcodes.has_arbitrary_storage(addr) {
874                                continue;
875                            }
876                        }
877
878                        if !warm_preloaded_addresses.contains(addr) {
879                            acc_mut.mark_cold();
880                        }
881
882                        for slot_mut in acc_mut.storage.values_mut() {
883                            slot_mut.is_cold = true;
884                        }
885                    }
886                }
887                // Process other variants as usual
888                CallScheme::CallCode | CallScheme::DelegateCall | CallScheme::ExtDelegateCall => {}
889            }
890        }
891
892        None
893    }
894
895    fn call_end(
896        &mut self,
897        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
898        inputs: &CallInputs,
899        outcome: CallOutcome,
900    ) -> CallOutcome {
901        // We are processing inner context outputs in the outer context, so need to avoid processing
902        // twice.
903        if self.in_inner_context && ecx.journaled_state.depth == 1 {
904            return outcome;
905        }
906
907        let outcome = self.do_call_end(ecx, inputs, outcome);
908
909        if ecx.journaled_state.depth == 0 {
910            self.top_level_frame_end(ecx, outcome.result.result);
911        }
912
913        outcome
914    }
915
916    fn create(
917        &mut self,
918        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
919        create: &mut CreateInputs,
920    ) -> Option<CreateOutcome> {
921        if self.in_inner_context && ecx.journaled_state.depth == 1 {
922            self.adjust_evm_data_for_inner_context(ecx);
923            return None;
924        }
925
926        if ecx.journaled_state.depth == 0 {
927            self.top_level_frame_start(ecx);
928        }
929
930        call_inspectors!(
931            #[ret]
932            [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes],
933            |inspector| inspector.create(ecx, create).map(Some),
934        );
935
936        if !matches!(create.scheme, CreateScheme::Create2 { .. }) &&
937            self.enable_isolation &&
938            !self.in_inner_context &&
939            ecx.journaled_state.depth == 1
940        {
941            let (result, address) = self.transact_inner(
942                ecx,
943                TxKind::Create,
944                create.caller,
945                create.init_code.clone(),
946                create.gas_limit,
947                create.value,
948            );
949            return Some(CreateOutcome { result, address });
950        }
951
952        None
953    }
954
955    fn create_end(
956        &mut self,
957        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
958        call: &CreateInputs,
959        outcome: CreateOutcome,
960    ) -> CreateOutcome {
961        // We are processing inner context outputs in the outer context, so need to avoid processing
962        // twice.
963        if self.in_inner_context && ecx.journaled_state.depth == 1 {
964            return outcome;
965        }
966
967        let outcome = self.do_create_end(ecx, call, outcome);
968
969        if ecx.journaled_state.depth == 0 {
970            self.top_level_frame_end(ecx, outcome.result.result);
971        }
972
973        outcome
974    }
975
976    fn eofcreate(
977        &mut self,
978        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
979        create: &mut EOFCreateInputs,
980    ) -> Option<CreateOutcome> {
981        if self.in_inner_context && ecx.journaled_state.depth == 1 {
982            self.adjust_evm_data_for_inner_context(ecx);
983            return None;
984        }
985
986        if ecx.journaled_state.depth == 0 {
987            self.top_level_frame_start(ecx);
988        }
989
990        call_inspectors!(
991            #[ret]
992            [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes],
993            |inspector| inspector.eofcreate(ecx, create).map(Some),
994        );
995
996        if matches!(create.kind, EOFCreateKind::Tx { .. }) &&
997            self.enable_isolation &&
998            !self.in_inner_context &&
999            ecx.journaled_state.depth == 1
1000        {
1001            let init_code = match &mut create.kind {
1002                EOFCreateKind::Tx { initdata } => initdata.clone(),
1003                EOFCreateKind::Opcode { .. } => unreachable!(),
1004            };
1005
1006            let (result, address) = self.transact_inner(
1007                ecx,
1008                TxKind::Create,
1009                create.caller,
1010                init_code,
1011                create.gas_limit,
1012                create.value,
1013            );
1014            return Some(CreateOutcome { result, address });
1015        }
1016
1017        None
1018    }
1019
1020    fn eofcreate_end(
1021        &mut self,
1022        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
1023        call: &EOFCreateInputs,
1024        outcome: CreateOutcome,
1025    ) -> CreateOutcome {
1026        // We are processing inner context outputs in the outer context, so need to avoid processing
1027        // twice.
1028        if self.in_inner_context && ecx.journaled_state.depth == 1 {
1029            return outcome;
1030        }
1031
1032        let outcome = self.do_eofcreate_end(ecx, call, outcome);
1033
1034        if ecx.journaled_state.depth == 0 {
1035            self.top_level_frame_end(ecx, outcome.result.result);
1036        }
1037
1038        outcome
1039    }
1040
1041    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1042        call_inspectors!([&mut self.tracer, &mut self.printer], |inspector| {
1043            Inspector::<&mut dyn DatabaseExt>::selfdestruct(inspector, contract, target, value)
1044        });
1045    }
1046}
1047
1048impl InspectorExt for InspectorStackRefMut<'_> {
1049    fn should_use_create2_factory(
1050        &mut self,
1051        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
1052        inputs: &mut CreateInputs,
1053    ) -> bool {
1054        call_inspectors!(
1055            #[ret]
1056            [&mut self.cheatcodes],
1057            |inspector| { inspector.should_use_create2_factory(ecx, inputs).then_some(true) },
1058        );
1059
1060        false
1061    }
1062
1063    fn console_log(&mut self, msg: &str) {
1064        call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1065            inspector, msg
1066        ));
1067    }
1068
1069    fn is_odyssey(&self) -> bool {
1070        self.inner.odyssey
1071    }
1072
1073    fn create2_deployer(&self) -> Address {
1074        self.inner.create2_deployer
1075    }
1076}
1077
1078impl Inspector<&mut dyn DatabaseExt> for InspectorStack {
1079    #[inline]
1080    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
1081        self.as_mut().step(interpreter, ecx)
1082    }
1083
1084    #[inline]
1085    fn step_end(
1086        &mut self,
1087        interpreter: &mut Interpreter,
1088        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
1089    ) {
1090        self.as_mut().step_end(interpreter, ecx)
1091    }
1092
1093    fn call(
1094        &mut self,
1095        context: &mut EvmContext<&mut dyn DatabaseExt>,
1096        inputs: &mut CallInputs,
1097    ) -> Option<CallOutcome> {
1098        self.as_mut().call(context, inputs)
1099    }
1100
1101    fn call_end(
1102        &mut self,
1103        context: &mut EvmContext<&mut dyn DatabaseExt>,
1104        inputs: &CallInputs,
1105        outcome: CallOutcome,
1106    ) -> CallOutcome {
1107        self.as_mut().call_end(context, inputs, outcome)
1108    }
1109
1110    fn create(
1111        &mut self,
1112        context: &mut EvmContext<&mut dyn DatabaseExt>,
1113        create: &mut CreateInputs,
1114    ) -> Option<CreateOutcome> {
1115        self.as_mut().create(context, create)
1116    }
1117
1118    fn create_end(
1119        &mut self,
1120        context: &mut EvmContext<&mut dyn DatabaseExt>,
1121        call: &CreateInputs,
1122        outcome: CreateOutcome,
1123    ) -> CreateOutcome {
1124        self.as_mut().create_end(context, call, outcome)
1125    }
1126
1127    fn eofcreate(
1128        &mut self,
1129        context: &mut EvmContext<&mut dyn DatabaseExt>,
1130        create: &mut EOFCreateInputs,
1131    ) -> Option<CreateOutcome> {
1132        self.as_mut().eofcreate(context, create)
1133    }
1134
1135    fn eofcreate_end(
1136        &mut self,
1137        context: &mut EvmContext<&mut dyn DatabaseExt>,
1138        call: &EOFCreateInputs,
1139        outcome: CreateOutcome,
1140    ) -> CreateOutcome {
1141        self.as_mut().eofcreate_end(context, call, outcome)
1142    }
1143
1144    fn initialize_interp(
1145        &mut self,
1146        interpreter: &mut Interpreter,
1147        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
1148    ) {
1149        self.as_mut().initialize_interp(interpreter, ecx)
1150    }
1151
1152    fn log(
1153        &mut self,
1154        interpreter: &mut Interpreter,
1155        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
1156        log: &Log,
1157    ) {
1158        self.as_mut().log(interpreter, ecx, log)
1159    }
1160
1161    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1162        Inspector::<&mut dyn DatabaseExt>::selfdestruct(&mut self.as_mut(), contract, target, value)
1163    }
1164}
1165
1166impl InspectorExt for InspectorStack {
1167    fn should_use_create2_factory(
1168        &mut self,
1169        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
1170        inputs: &mut CreateInputs,
1171    ) -> bool {
1172        self.as_mut().should_use_create2_factory(ecx, inputs)
1173    }
1174
1175    fn is_odyssey(&self) -> bool {
1176        self.odyssey
1177    }
1178
1179    fn create2_deployer(&self) -> Address {
1180        self.create2_deployer
1181    }
1182}
1183
1184impl<'a> Deref for InspectorStackRefMut<'a> {
1185    type Target = &'a mut InspectorStackInner;
1186
1187    fn deref(&self) -> &Self::Target {
1188        &self.inner
1189    }
1190}
1191
1192impl DerefMut for InspectorStackRefMut<'_> {
1193    fn deref_mut(&mut self) -> &mut Self::Target {
1194        &mut self.inner
1195    }
1196}
1197
1198impl Deref for InspectorStack {
1199    type Target = InspectorStackInner;
1200
1201    fn deref(&self) -> &Self::Target {
1202        &self.inner
1203    }
1204}
1205
1206impl DerefMut for InspectorStack {
1207    fn deref_mut(&mut self) -> &mut Self::Target {
1208        &mut self.inner
1209    }
1210}