Skip to main content

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