foundry_evm/inspectors/
stack.rs

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