Skip to main content

foundry_evm/inspectors/
stack.rs

1use super::{
2    Cheatcodes, CheatsConfig, ChiselState, CmpOperands, CustomPrintTracer, EdgeCovInspector,
3    Fuzzer, LineCoverageCollector, LogCollector, RevertDiagnostic, ScriptExecutionInspector,
4    TempoLabels, TracingInspector,
5};
6use alloy_primitives::{
7    Address, B256, Bytes, Log, TxKind, U256,
8    map::{AddressHashMap, AddressMap},
9};
10
11use foundry_cheatcodes::{CheatcodeAnalysis, CheatcodesExecutor, NestedEvmClosure, Wallets};
12use foundry_common::compile::Analysis;
13use foundry_evm_core::{
14    FoundryBlock, FoundryTransaction, InspectorExt,
15    backend::{DatabaseError, DatabaseExt, JournaledState},
16    constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH,
17    env::FoundryContextExt,
18    evm::{
19        BlockEnvFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory,
20        FoundryEvmNetwork, SpecFor, TxEnvFor, get_create2_factory_call_inputs, with_cloned_context,
21    },
22};
23use foundry_evm_coverage::HitMaps;
24use foundry_evm_networks::NetworkConfigs;
25use foundry_evm_traces::{SparsedTraceArena, TraceMode};
26use revm::{
27    Inspector,
28    context::{
29        Block, Cfg, ContextTr, JournalTr, Transaction, TransactionType,
30        result::{EVMError, ExecutionResult, Output},
31    },
32    context_interface::CreateScheme,
33    handler::FrameResult,
34    inspector::JournalExt,
35    interpreter::{
36        CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
37        InstructionResult, Interpreter, InterpreterResult, return_ok,
38    },
39    primitives::KECCAK_EMPTY,
40    state::{Account, AccountStatus},
41};
42use std::{
43    ops::{Deref, DerefMut},
44    sync::Arc,
45};
46
47#[derive(Clone, Debug)]
48#[must_use = "builders do nothing unless you call `build` on them"]
49pub struct InspectorStackBuilder<BLOCK: Clone> {
50    /// Solar compiler instance, to grant syntactic and semantic analysis capabilities.
51    pub analysis: Option<Analysis>,
52    /// The block environment.
53    ///
54    /// Used in the cheatcode handler to overwrite the block environment separately from the
55    /// execution block environment.
56    pub block: Option<BLOCK>,
57    /// The gas price.
58    ///
59    /// Used in the cheatcode handler to overwrite the gas price separately from the gas price
60    /// in the execution environment.
61    pub gas_price: Option<u128>,
62    /// The cheatcodes config.
63    pub cheatcodes: Option<Arc<CheatsConfig>>,
64    /// The fuzzer inspector and its state, if it exists.
65    pub fuzzer: Option<Fuzzer>,
66    /// Whether to enable tracing and revert diagnostics.
67    pub trace_mode: TraceMode,
68    /// Whether logs should be collected.
69    /// - None for no log collection.
70    /// - Some(true) for realtime console.log-ing.
71    /// - Some(false) for log collection.
72    pub logs: Option<bool>,
73    /// Whether line coverage info should be collected.
74    pub line_coverage: Option<bool>,
75    /// Whether to print all opcode traces into the console. Useful for debugging the EVM.
76    pub print: Option<bool>,
77    /// The chisel state inspector.
78    pub chisel_state: Option<usize>,
79    /// Whether to enable call isolation.
80    /// In isolation mode all top-level calls are executed as a separate transaction in a separate
81    /// EVM context, enabling more precise gas accounting and transaction state changes.
82    pub enable_isolation: bool,
83    /// Networks with enabled features.
84    pub networks: NetworkConfigs,
85    /// The wallets to set in the cheatcodes context.
86    pub wallets: Option<Wallets>,
87    /// The CREATE2 deployer address.
88    pub create2_deployer: Address,
89}
90
91impl<BLOCK: Clone> Default for InspectorStackBuilder<BLOCK> {
92    fn default() -> Self {
93        Self {
94            analysis: None,
95            block: None,
96            gas_price: None,
97            cheatcodes: None,
98            fuzzer: None,
99            trace_mode: TraceMode::None,
100            logs: None,
101            line_coverage: None,
102            print: None,
103            chisel_state: None,
104            enable_isolation: false,
105            networks: NetworkConfigs::default(),
106            wallets: None,
107            create2_deployer: Default::default(),
108        }
109    }
110}
111
112impl<BLOCK: Clone> InspectorStackBuilder<BLOCK> {
113    /// Create a new inspector stack builder.
114    #[inline]
115    pub fn new() -> Self {
116        Self::default()
117    }
118
119    /// Set the solar compiler instance that grants syntactic and semantic analysis capabilities
120    #[inline]
121    pub fn set_analysis(mut self, analysis: Analysis) -> Self {
122        self.analysis = Some(analysis);
123        self
124    }
125
126    /// Set the block environment.
127    #[inline]
128    pub fn block(mut self, block: BLOCK) -> Self {
129        self.block = Some(block);
130        self
131    }
132
133    /// Set the gas price.
134    #[inline]
135    pub const fn gas_price(mut self, gas_price: u128) -> Self {
136        self.gas_price = Some(gas_price);
137        self
138    }
139
140    /// Enable cheatcodes with the given config.
141    #[inline]
142    pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
143        self.cheatcodes = Some(config);
144        self
145    }
146
147    /// Set the wallets.
148    #[inline]
149    pub fn wallets(mut self, wallets: Wallets) -> Self {
150        self.wallets = Some(wallets);
151        self
152    }
153
154    /// Set the fuzzer inspector.
155    #[inline]
156    pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
157        self.fuzzer = Some(fuzzer);
158        self
159    }
160
161    /// Set the Chisel inspector.
162    #[inline]
163    pub const fn chisel_state(mut self, final_pc: usize) -> Self {
164        self.chisel_state = Some(final_pc);
165        self
166    }
167
168    /// Set the log collector, and whether to print the logs directly to stdout.
169    #[inline]
170    pub const fn logs(mut self, live_logs: bool) -> Self {
171        self.logs = Some(live_logs);
172        self
173    }
174
175    /// Set whether to collect line coverage information.
176    #[inline]
177    pub const fn line_coverage(mut self, yes: bool) -> Self {
178        self.line_coverage = Some(yes);
179        self
180    }
181
182    /// Set whether to enable the trace printer.
183    #[inline]
184    pub const fn print(mut self, yes: bool) -> Self {
185        self.print = Some(yes);
186        self
187    }
188
189    /// Set whether to enable the tracer.
190    /// Revert diagnostic inspector is activated when `mode != TraceMode::None`
191    #[inline]
192    pub fn trace_mode(mut self, mode: TraceMode) -> Self {
193        if self.trace_mode < mode {
194            self.trace_mode = mode
195        }
196        self
197    }
198
199    /// Set whether to enable the call isolation.
200    /// For description of call isolation, see [`InspectorStack::enable_isolation`].
201    #[inline]
202    pub const fn enable_isolation(mut self, yes: bool) -> Self {
203        self.enable_isolation = yes;
204        self
205    }
206
207    /// Set networks with enabled features.
208    #[inline]
209    pub const fn networks(mut self, networks: NetworkConfigs) -> Self {
210        self.networks = networks;
211        self
212    }
213
214    #[inline]
215    pub const fn create2_deployer(mut self, create2_deployer: Address) -> Self {
216        self.create2_deployer = create2_deployer;
217        self
218    }
219
220    /// Builds the stack of inspectors to use when transacting/committing on the EVM.
221    pub fn build<FEN: FoundryEvmNetwork<EvmFactory: FoundryEvmFactory<BlockEnv = BLOCK>>>(
222        self,
223    ) -> InspectorStack<FEN> {
224        let Self {
225            analysis,
226            block,
227            gas_price,
228            cheatcodes,
229            fuzzer,
230            trace_mode,
231            logs,
232            line_coverage,
233            print,
234            chisel_state,
235            enable_isolation,
236            networks,
237            wallets,
238            create2_deployer,
239        } = self;
240        let mut stack = InspectorStack::new();
241
242        // inspectors
243        if let Some(config) = cheatcodes {
244            let mut cheatcodes = Cheatcodes::new(config);
245            // Set analysis capabilities if they are provided
246            if let Some(analysis) = analysis {
247                stack.set_analysis(analysis.clone());
248                cheatcodes.set_analysis(CheatcodeAnalysis::new(analysis));
249            }
250            // Set wallets if they are provided
251            if let Some(wallets) = wallets {
252                cheatcodes.set_wallets(wallets);
253            }
254            stack.set_cheatcodes(cheatcodes);
255        }
256
257        if let Some(fuzzer) = fuzzer {
258            stack.set_fuzzer(fuzzer);
259        }
260        if let Some(chisel_state) = chisel_state {
261            stack.set_chisel(chisel_state);
262        }
263        stack.collect_line_coverage(line_coverage.unwrap_or(false));
264        stack.collect_logs(logs);
265        stack.print(print.unwrap_or(false));
266        stack.tracing(trace_mode);
267
268        stack.enable_isolation(enable_isolation);
269        stack.networks(networks);
270        stack.set_create2_deployer(create2_deployer);
271
272        if networks.is_tempo() {
273            stack.inner.tempo_labels = Some(Box::default());
274        }
275
276        // environment, must come after all of the inspectors
277        if let Some(block) = block {
278            stack.set_block(block);
279        }
280        if let Some(gas_price) = gas_price {
281            stack.set_gas_price(gas_price);
282        }
283
284        stack
285    }
286}
287
288/// Helper macro to call the same method on multiple inspectors without resorting to dynamic
289/// dispatch.
290#[macro_export]
291macro_rules! call_inspectors {
292    ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {
293        $(
294            if let Some($id) = $inspector {
295                $crate::utils::cold_path();
296                $body;
297            }
298        )+
299    };
300    (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {{
301        $(
302            if let Some($id) = $inspector {
303                $crate::utils::cold_path();
304                if let Some(result) = $body {
305                    return result;
306                }
307            }
308        )+
309    }};
310}
311
312/// The collected results of [`InspectorStack`].
313pub struct InspectorData<FEN: FoundryEvmNetwork> {
314    pub logs: Vec<Log>,
315    pub labels: AddressHashMap<String>,
316    pub traces: Option<SparsedTraceArena>,
317    pub line_coverage: Option<HitMaps>,
318    pub edge_coverage: Option<Vec<u8>>,
319    pub evm_cmp_values: Option<Vec<CmpOperands>>,
320    pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
321    pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
322    pub reverter: Option<Address>,
323}
324
325/// Contains data about the state of outer/main EVM which created and invoked the inner EVM context.
326/// Used to adjust EVM state while in inner context.
327///
328/// We need this to avoid breaking changes due to EVM behavior differences in isolated vs
329/// non-isolated mode. For descriptions and workarounds for those changes see: <https://github.com/foundry-rs/foundry/pull/7186#issuecomment-1959102195>
330#[derive(Debug, Clone)]
331pub struct InnerContextData {
332    /// Origin of the transaction in the outer EVM context.
333    original_origin: Address,
334}
335
336/// An inspector that calls multiple inspectors in sequence.
337///
338/// If a call to an inspector returns a value (indicating a stop or revert) the remaining inspectors
339/// are not called.
340///
341/// Stack is divided into [Cheatcodes] and `InspectorStackInner`. This is done to allow assembling
342/// `InspectorStackRefMut` inside [Cheatcodes] to allow usage of it as [revm::Inspector]. This gives
343/// us ability to create and execute separate EVM frames from inside cheatcodes while still having
344/// access to entire stack of inspectors and correctly handling traces, logs, debugging info
345/// collection, etc.
346#[derive(Clone, Debug)]
347pub struct InspectorStack<FEN: FoundryEvmNetwork = EthEvmNetwork> {
348    #[allow(clippy::type_complexity)]
349    pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
350    pub inner: InspectorStackInner,
351}
352
353/// All used inpectors besides [Cheatcodes].
354///
355/// See [`InspectorStack`].
356#[derive(Default, Clone, Debug)]
357pub struct InspectorStackInner {
358    /// Solar compiler instance, to grant syntactic and semantic analysis capabilities.
359    pub analysis: Option<Analysis>,
360
361    // Inspectors.
362    // These are boxed to reduce the size of the struct and slightly improve performance of the
363    // `if let Some` checks.
364    pub chisel_state: Option<Box<ChiselState>>,
365    pub edge_coverage: Option<Box<EdgeCovInspector>>,
366    pub fuzzer: Option<Box<Fuzzer>>,
367    pub line_coverage: Option<Box<LineCoverageCollector>>,
368    pub log_collector: Option<Box<LogCollector>>,
369    pub printer: Option<Box<CustomPrintTracer>>,
370    pub revert_diag: Option<Box<RevertDiagnostic>>,
371    pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
372    pub tempo_labels: Option<Box<TempoLabels>>,
373    pub tracer: Option<Box<TracingInspector>>,
374
375    // FoundryInspectorExt and other internal data.
376    /// Whether to collect sancov edge coverage from instrumented native crates.
377    pub sancov_edges: bool,
378    /// Whether to capture sancov trace-cmp operands for dictionary injection.
379    pub sancov_trace_cmp: bool,
380    pub enable_isolation: bool,
381    pub networks: NetworkConfigs,
382    pub create2_deployer: Address,
383    /// Flag marking if we are in the inner EVM context.
384    pub in_inner_context: bool,
385    pub inner_context_data: Option<InnerContextData>,
386    pub top_frame_journal: AddressMap<Account>,
387    /// Address that reverted the call, if any.
388    pub reverter: Option<Address>,
389    /// LIFO stack tracking CREATE2 frames that were redirected to the CREATE2 factory.
390    /// Each entry records the journal depth at which the redirect occurred.
391    pub pending_create2_redirects: Vec<usize>,
392    /// Pending CREATE2 deployer validation error, deferred from `frame_start` to `create` so
393    /// it goes through the normal inspector lifecycle (tracing, etc.).
394    pub pending_create2_error: Option<CreateOutcome>,
395}
396
397/// Struct keeping mutable references to both parts of [InspectorStack] and implementing
398/// [revm::Inspector]. This struct can be obtained via [InspectorStack::as_mut].
399pub struct InspectorStackRefMut<'a, FEN: FoundryEvmNetwork = EthEvmNetwork> {
400    pub cheatcodes: Option<&'a mut Cheatcodes<FEN>>,
401    pub inner: &'a mut InspectorStackInner,
402}
403
404impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for InspectorStackInner {
405    fn with_nested_evm(
406        &mut self,
407        cheats: &mut Cheatcodes<FEN>,
408        ecx: &mut FoundryContextFor<'_, FEN>,
409        f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
410    ) -> Result<(), EVMError<DatabaseError>> {
411        let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
412        with_cloned_context(ecx, |db, evm_env, journal_inner| {
413            let mut evm =
414                FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, &mut inspector);
415            *evm.journal_inner_mut() = journal_inner;
416            f(&mut *evm)?;
417            let sub_inner = evm.journal_inner_mut().clone();
418            let sub_evm_env = evm.to_evm_env();
419            Ok((sub_evm_env, sub_inner))
420        })
421    }
422
423    fn with_fresh_nested_evm(
424        &mut self,
425        cheats: &mut Cheatcodes<FEN>,
426        db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
427        evm_env: EvmEnvFor<FEN>,
428        f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
429    ) -> Result<EvmEnvFor<FEN>, EVMError<DatabaseError>> {
430        let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
431        let mut evm =
432            FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, &mut inspector);
433        f(&mut *evm)?;
434        Ok(evm.to_evm_env())
435    }
436
437    fn transact_on_db(
438        &mut self,
439        cheats: &mut Cheatcodes<FEN>,
440        ecx: &mut FoundryContextFor<'_, FEN>,
441        fork_id: Option<U256>,
442        transaction: B256,
443    ) -> eyre::Result<()> {
444        let evm_env = ecx.evm_clone();
445        let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
446        let (db, inner) = ecx.db_journal_inner_mut();
447        db.transact(fork_id, transaction, evm_env, inner, &mut inspector)
448    }
449
450    fn transact_from_tx_on_db(
451        &mut self,
452        cheats: &mut Cheatcodes<FEN>,
453        ecx: &mut FoundryContextFor<'_, FEN>,
454        tx_env: TxEnvFor<FEN>,
455    ) -> eyre::Result<()> {
456        let evm_env = ecx.evm_clone();
457        let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
458        let (db, inner) = ecx.db_journal_inner_mut();
459        db.transact_from_tx(tx_env, evm_env, inner, &mut inspector)
460    }
461
462    fn console_log(&mut self, msg: &str) {
463        if let Some(ref mut collector) = self.log_collector {
464            InspectorExt::console_log(&mut **collector, msg);
465        }
466    }
467
468    fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
469        self.tracer.as_deref_mut()
470    }
471
472    fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option<Address>) {
473        self.in_inner_context = enabled;
474        self.inner_context_data = enabled.then(|| InnerContextData {
475            original_origin: original_origin.expect("origin required when enabling inner ctx"),
476        });
477    }
478}
479
480impl<FEN: FoundryEvmNetwork> Default for InspectorStack<FEN> {
481    fn default() -> Self {
482        Self::new()
483    }
484}
485
486impl<FEN: FoundryEvmNetwork> InspectorStack<FEN> {
487    /// Creates a new inspector stack.
488    ///
489    /// Note that the stack is empty by default, and you must add inspectors to it.
490    /// This is done by calling the `set_*` methods on the stack directly, or by building the stack
491    /// with [`InspectorStack`].
492    #[inline]
493    pub fn new() -> Self {
494        Self { cheatcodes: None, inner: InspectorStackInner::default() }
495    }
496
497    /// Set the solar compiler instance.
498    #[inline]
499    pub fn set_analysis(&mut self, analysis: Analysis) {
500        self.analysis = Some(analysis);
501    }
502
503    /// Sets the block for the relevant inspectors.
504    #[inline]
505    pub fn set_block(&mut self, block: BlockEnvFor<FEN>) {
506        if let Some(cheatcodes) = &mut self.cheatcodes {
507            cheatcodes.block = Some(block);
508        }
509    }
510
511    /// Sets the gas price for the relevant inspectors.
512    #[inline]
513    pub fn set_gas_price(&mut self, gas_price: u128) {
514        if let Some(cheatcodes) = &mut self.cheatcodes {
515            cheatcodes.gas_price = Some(gas_price);
516        }
517    }
518
519    /// Set the cheatcodes inspector.
520    #[inline]
521    pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes<FEN>) {
522        self.cheatcodes = Some(cheatcodes.into());
523    }
524
525    /// Set the fuzzer inspector.
526    #[inline]
527    pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
528        self.fuzzer = Some(fuzzer.into());
529    }
530
531    /// Set the Chisel inspector.
532    #[inline]
533    pub fn set_chisel(&mut self, final_pc: usize) {
534        self.chisel_state = Some(ChiselState::new(final_pc).into());
535    }
536
537    /// Set whether to enable the line coverage collector.
538    #[inline]
539    pub fn collect_line_coverage(&mut self, yes: bool) {
540        self.line_coverage = yes.then(Default::default);
541    }
542
543    /// Set whether to enable the edge coverage collector.
544    #[inline]
545    pub fn collect_edge_coverage(&mut self, yes: bool) {
546        // TODO: configurable edge size?
547        self.edge_coverage = yes.then(EdgeCovInspector::new).map(Into::into);
548    }
549
550    /// Set whether to collect EVM comparison operands.
551    #[inline]
552    pub fn collect_evm_cmp_log(&mut self, yes: bool) {
553        if yes {
554            self.edge_coverage
555                .get_or_insert_with(|| EdgeCovInspector::new().into())
556                .enable_cmp_log(true);
557        } else if let Some(edge_coverage) = &mut self.edge_coverage {
558            edge_coverage.enable_cmp_log(false);
559        }
560    }
561
562    /// Set whether to collect sancov edge coverage from instrumented native crates.
563    #[inline]
564    pub const fn collect_sancov_edges(&mut self, yes: bool) {
565        self.inner.sancov_edges = yes;
566    }
567
568    /// Set whether to capture sancov trace-cmp operands for dictionary injection.
569    #[inline]
570    pub const fn collect_sancov_trace_cmp(&mut self, yes: bool) {
571        self.inner.sancov_trace_cmp = yes;
572    }
573
574    /// Set whether to enable call isolation.
575    #[inline]
576    pub const fn enable_isolation(&mut self, yes: bool) {
577        self.inner.enable_isolation = yes;
578    }
579
580    /// Set networks with enabled features.
581    #[inline]
582    pub const fn networks(&mut self, networks: NetworkConfigs) {
583        self.inner.networks = networks;
584    }
585
586    /// Set the CREATE2 deployer address.
587    #[inline]
588    pub fn set_create2_deployer(&mut self, deployer: Address) {
589        self.create2_deployer = deployer;
590    }
591
592    /// Set whether to enable the log collector.
593    /// - None for no log collection.
594    /// - Some(true) for realtime console.log-ing.
595    /// - Some(false) for log collection.
596    #[inline]
597    pub fn collect_logs(&mut self, live_logs: Option<bool>) {
598        self.log_collector = live_logs.map(|live_logs| {
599            Box::new(if live_logs {
600                LogCollector::LiveLogs
601            } else {
602                LogCollector::Capture { logs: Vec::new() }
603            })
604        });
605    }
606
607    /// Set whether to enable the trace printer.
608    #[inline]
609    pub fn print(&mut self, yes: bool) {
610        self.printer = yes.then(Default::default);
611    }
612
613    /// Set whether to enable the tracer.
614    /// Revert diagnostic inspector is activated when `mode != TraceMode::None`
615    #[inline]
616    pub fn tracing(&mut self, mode: TraceMode) {
617        self.revert_diag = (!mode.is_none()).then(RevertDiagnostic::default).map(Into::into);
618
619        if let Some(config) = mode.into_config() {
620            *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
621        } else {
622            self.tracer = None;
623        }
624    }
625
626    /// Set whether to enable script execution inspector.
627    #[inline]
628    pub fn script(&mut self, script_address: Address) {
629        self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
630            script_address;
631    }
632
633    #[inline(always)]
634    fn as_mut(&mut self) -> InspectorStackRefMut<'_, FEN> {
635        InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
636    }
637
638    /// Collects all the data gathered during inspection into a single struct.
639    pub fn collect(self) -> InspectorData<FEN> {
640        let Self {
641            mut cheatcodes,
642            inner:
643                InspectorStackInner {
644                    chisel_state,
645                    line_coverage,
646                    edge_coverage,
647                    log_collector,
648                    tempo_labels,
649                    tracer,
650                    reverter,
651                    ..
652                },
653        } = self;
654
655        let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
656            let ignored = cheatcodes
657                .as_mut()
658                .map(|cheatcodes| {
659                    let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
660
661                    // If the last pause call was not resumed, ignore the rest of the trace
662                    if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
663                        ignored.insert(last_pause_call, (arena.nodes().len(), 0));
664                    }
665
666                    ignored
667                })
668                .unwrap_or_default();
669
670            SparsedTraceArena { arena, ignored }
671        });
672
673        let (edge_coverage, evm_cmp_values) = edge_coverage
674            .map(|edge_coverage| {
675                let (hitcount, cmp_values) = edge_coverage.into_parts();
676                (Some(hitcount), (!cmp_values.is_empty()).then_some(cmp_values))
677            })
678            .unwrap_or_default();
679
680        InspectorData {
681            logs: log_collector.and_then(|logs| logs.into_captured_logs()).unwrap_or_default(),
682            labels: {
683                let mut labels = cheatcodes.as_ref().map(|c| c.labels.clone()).unwrap_or_default();
684                if let Some(tempo_labels) = tempo_labels {
685                    labels.extend(tempo_labels.labels);
686                }
687                labels
688            },
689            traces,
690            line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()),
691            edge_coverage,
692            evm_cmp_values,
693            cheatcodes,
694            chisel_state: chisel_state.and_then(|state| state.state),
695            reverter,
696        }
697    }
698}
699
700impl<FEN: FoundryEvmNetwork> InspectorStackRefMut<'_, FEN> {
701    /// Adjusts the EVM data for the inner EVM context.
702    /// Should be called on the top-level call of inner context (depth == 0 &&
703    /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility
704    /// Updates tx.origin to the value before entering inner context
705    fn adjust_evm_data_for_inner_context<CTX: FoundryContextExt>(&mut self, ecx: &mut CTX) {
706        let inner_context_data =
707            self.inner_context_data.as_ref().expect("should be called in inner context");
708        ecx.tx_mut().set_caller(inner_context_data.original_origin);
709    }
710
711    fn do_call_end(
712        &mut self,
713        ecx: &mut FoundryContextFor<'_, FEN>,
714        inputs: &CallInputs,
715        outcome: &mut CallOutcome,
716    ) {
717        let result = outcome.result.result;
718        call_inspectors!(
719            #[ret]
720            [
721                &mut self.fuzzer,
722                &mut self.tracer,
723                &mut self.cheatcodes,
724                &mut self.printer,
725                &mut self.revert_diag
726            ],
727            |inspector| {
728                let previous_outcome = outcome.clone();
729                inspector.call_end(ecx, inputs, outcome);
730
731                // If the inspector returns a different status or a revert with a non-empty message,
732                // we assume it wants to tell us something
733                let different = outcome.result.result != result
734                    || (outcome.result.result == InstructionResult::Revert
735                        && outcome.output() != previous_outcome.output());
736                different.then_some(())
737            },
738        );
739
740        // Record first address that reverted the call.
741        if result.is_revert() && self.reverter.is_none() {
742            self.reverter = Some(inputs.target_address);
743        }
744    }
745
746    fn do_create_end(
747        &mut self,
748        ecx: &mut FoundryContextFor<'_, FEN>,
749        call: &CreateInputs,
750        outcome: &mut CreateOutcome,
751    ) {
752        let result = outcome.result.result;
753        call_inspectors!(
754            #[ret]
755            [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
756            |inspector| {
757                let previous_outcome = outcome.clone();
758                inspector.create_end(ecx, call, outcome);
759
760                // If the inspector returns a different status or a revert with a non-empty message,
761                // we assume it wants to tell us something
762                let different = outcome.result.result != result
763                    || (outcome.result.result == InstructionResult::Revert
764                        && outcome.output() != previous_outcome.output());
765                different.then_some(())
766            },
767        );
768    }
769
770    fn transact_inner(
771        &mut self,
772        ecx: &mut FoundryContextFor<'_, FEN>,
773        kind: TxKind,
774        caller: Address,
775        input: Bytes,
776        gas_limit: u64,
777        value: U256,
778    ) -> (InterpreterResult, Option<Address>) {
779        let cached_evm_env = ecx.evm_clone();
780        let cached_tx_env = ecx.tx_clone();
781
782        ecx.block_mut().set_basefee(0);
783
784        let chain_id = ecx.cfg().chain_id();
785        ecx.tx_mut().set_chain_id(Some(chain_id));
786        ecx.tx_mut().set_caller(caller);
787        ecx.tx_mut().set_kind(kind);
788        ecx.tx_mut().set_data(input);
789        ecx.tx_mut().set_value(value);
790        // Add 21000 to the gas limit to account for the base cost of transaction.
791        ecx.tx_mut().set_gas_limit(gas_limit + 21000);
792
793        // If we haven't disabled gas limit checks, ensure that transaction gas limit will not
794        // exceed block gas limit.
795        if !ecx.cfg().is_block_gas_limit_disabled() {
796            let gas_limit = std::cmp::min(ecx.tx().gas_limit(), ecx.block().gas_limit());
797            ecx.tx_mut().set_gas_limit(gas_limit);
798        }
799        ecx.tx_mut().set_gas_price(0);
800        // If the cached tx is EIP-4844 (e.g. set by `vm.blobhashes`), downgrade
801        // it to EIP-1559 and drop the blob hashes so revm doesn't reject the
802        // synthetic inner tx because `gas_price = 0` plus blob fields fail
803        // 4844 validation. The contract-visible `BLOBHASH` opcode is restored
804        // via `EnvOverrides`. Other types (incl. EIP-2930 access lists) are
805        // intentionally left intact, and the full original tx is restored
806        // from `cached_tx_env` after the inner call.
807        if ecx.tx().tx_type() == TransactionType::Eip4844 as u8 {
808            ecx.tx_mut().set_tx_type(TransactionType::Eip1559 as u8);
809            ecx.tx_mut().set_blob_hashes(Vec::new());
810        }
811
812        self.inner_context_data =
813            Some(InnerContextData { original_origin: cached_tx_env.caller() });
814        self.in_inner_context = true;
815
816        // Tell cheatcodes we're entering the synthetic inner transaction so
817        // env-mutating cheatcodes route through `env_overrides` instead of
818        // fighting with the fee-accounting zeroing above. See `EnvOverrides`.
819        if let Some(cheats) = self.cheatcodes.as_deref_mut() {
820            cheats.in_isolation_context = true;
821        }
822
823        let evm_env = ecx.evm_clone();
824        let tx_env = ecx.tx_clone();
825
826        let res = self.with_inspector(|mut inspector| {
827            let (res, nested_env) = {
828                let (db, journal) = ecx.db_journal_inner_mut();
829                let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(
830                    db,
831                    evm_env,
832                    &mut inspector,
833                );
834
835                evm.journal_inner_mut().state = {
836                    let mut state = journal.state.clone();
837
838                    for (addr, acc_mut) in &mut state {
839                        // mark all accounts cold, besides preloaded addresses
840                        if journal.warm_addresses.is_cold(addr) {
841                            acc_mut.mark_cold();
842                        }
843
844                        // mark all slots cold
845                        for slot_mut in acc_mut.storage.values_mut() {
846                            slot_mut.is_cold = true;
847                            slot_mut.original_value = slot_mut.present_value;
848                        }
849                    }
850
851                    state
852                };
853
854                // set depth to 1 to make sure traces are collected correctly
855                evm.journal_inner_mut().depth = 1;
856
857                let res = evm.transact_raw(tx_env);
858                let nested_evm_env = evm.to_evm_env();
859                (res, nested_evm_env)
860            };
861
862            // Restore env, preserving cheatcode cfg/block changes from the nested EVM
863            // but restoring the original tx and basefee (which we zeroed for the nested call).
864            let mut restored_evm_env = nested_env;
865            restored_evm_env.block_env.set_basefee(cached_evm_env.block_env.basefee());
866            ecx.set_evm(restored_evm_env);
867            ecx.set_tx(cached_tx_env);
868
869            res
870        });
871
872        self.in_inner_context = false;
873        self.inner_context_data = None;
874
875        // Reset the cheatcodes isolation flag now that the synthetic inner
876        // transaction has finished.
877        if let Some(cheats) = self.cheatcodes.as_deref_mut() {
878            cheats.in_isolation_context = false;
879        }
880
881        let mut gas = Gas::new(gas_limit);
882
883        let Ok(res) = res else {
884            // Should we match, encode and propagate error as a revert reason?
885            let result =
886                InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
887            return (result, None);
888        };
889
890        for (addr, mut acc) in res.state {
891            let Some(acc_mut) = ecx.journal_mut().evm_state_mut().get_mut(&addr) else {
892                ecx.journal_mut().evm_state_mut().insert(addr, acc);
893                continue;
894            };
895
896            // make sure accounts that were warmed earlier do not become cold
897            if acc.status.contains(AccountStatus::Cold)
898                && !acc_mut.status.contains(AccountStatus::Cold)
899            {
900                acc.status -= AccountStatus::Cold;
901            }
902            acc_mut.info = acc.info;
903            acc_mut.status |= acc.status;
904
905            for (key, val) in acc.storage {
906                let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
907                    acc_mut.storage.insert(key, val);
908                    continue;
909                };
910                slot_mut.present_value = val.present_value;
911                slot_mut.is_cold &= val.is_cold;
912            }
913        }
914
915        let (result, address, output) = match res.result {
916            ExecutionResult::Success { reason, gas: result_gas, logs: _, output } => {
917                gas.set_refund(result_gas.final_refunded() as i64);
918                let _ = gas.record_regular_cost(result_gas.tx_gas_used());
919                let address = match output {
920                    Output::Create(_, address) => address,
921                    Output::Call(_) => None,
922                };
923                (reason.into(), address, output.into_data())
924            }
925            ExecutionResult::Halt { reason, gas: result_gas, .. } => {
926                let _ = gas.record_regular_cost(result_gas.tx_gas_used());
927                (InstructionResult::from(reason), None, Bytes::new())
928            }
929            ExecutionResult::Revert { gas: result_gas, output, .. } => {
930                let _ = gas.record_regular_cost(result_gas.tx_gas_used());
931                (InstructionResult::Revert, None, output)
932            }
933        };
934        (InterpreterResult { result, output, gas }, address)
935    }
936
937    /// Moves out of references, constructs a new [`InspectorStackRefMut`] and runs the given
938    /// closure with it.
939    fn with_inspector<O>(&mut self, f: impl FnOnce(InspectorStackRefMut<'_, FEN>) -> O) -> O {
940        let mut cheatcodes = self
941            .cheatcodes
942            .as_deref_mut()
943            .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())));
944        let mut inner = std::mem::take(self.inner);
945
946        // Save pending CREATE2 redirects so frame_end in the nested EVM doesn't consume them.
947        // These belong to the outer EVM's frame lifecycle and must be restored after.
948        let saved_create2_redirects = std::mem::take(&mut inner.pending_create2_redirects);
949
950        let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner });
951
952        if let Some(cheats) = self.cheatcodes.as_deref_mut() {
953            *cheats = cheatcodes.unwrap();
954        }
955
956        inner.pending_create2_redirects = saved_create2_redirects;
957        *self.inner = inner;
958
959        out
960    }
961
962    /// Invoked at the beginning of a new top-level (0 depth) frame.
963    fn top_level_frame_start(&mut self, ecx: &mut FoundryContextFor<'_, FEN>) {
964        if self.enable_isolation {
965            // If we're in isolation mode, we need to keep track of the state at the beginning of
966            // the frame to be able to roll back on revert
967            self.top_frame_journal.clone_from(ecx.journal().evm_state());
968        }
969    }
970
971    /// Invoked at the end of root frame.
972    fn top_level_frame_end(
973        &mut self,
974        ecx: &mut FoundryContextFor<'_, FEN>,
975        result: InstructionResult,
976    ) {
977        if !result.is_revert() {
978            return;
979        }
980        // Encountered a revert, since cheatcodes may have altered the evm state in such a way
981        // that violates some constraints, e.g. `deal`, we need to manually roll back on revert
982        // before revm reverts the state itself
983        if let Some(cheats) = self.cheatcodes.as_mut() {
984            cheats.on_revert(ecx);
985        }
986
987        // If we're in isolation mode, we need to rollback to state before the root frame was
988        // created We can't rely on revm's journal because it doesn't account for changes
989        // made by isolated calls
990        if self.enable_isolation {
991            *ecx.journal_mut().evm_state_mut() = std::mem::take(&mut self.top_frame_journal);
992        }
993    }
994
995    // We take extra care in optimizing `step` and `step_end`, as they're are likely the most
996    // hot functions in all of Foundry.
997    // We want to `#[inline(always)]` these functions so that `InspectorStack` does not
998    // delegate to `InspectorStackRefMut` in this case.
999
1000    #[inline(always)]
1001    fn step_inlined(
1002        &mut self,
1003        interpreter: &mut Interpreter,
1004        ecx: &mut FoundryContextFor<'_, FEN>,
1005    ) {
1006        call_inspectors!(
1007            [
1008                // These are sorted in definition order.
1009                &mut self.edge_coverage,
1010                &mut self.fuzzer,
1011                &mut self.line_coverage,
1012                &mut self.printer,
1013                &mut self.revert_diag,
1014                &mut self.script_execution_inspector,
1015                &mut self.tracer,
1016                // Keep `cheatcodes` last to make use of the tail call.
1017                &mut self.cheatcodes,
1018            ],
1019            |inspector| (**inspector).step(interpreter, ecx),
1020        );
1021    }
1022
1023    #[inline(always)]
1024    fn step_end_inlined(
1025        &mut self,
1026        interpreter: &mut Interpreter,
1027        ecx: &mut FoundryContextFor<'_, FEN>,
1028    ) {
1029        call_inspectors!(
1030            [
1031                // These are sorted in definition order.
1032                &mut self.chisel_state,
1033                &mut self.printer,
1034                &mut self.revert_diag,
1035                &mut self.tracer,
1036                // Keep `cheatcodes` last to make use of the tail call.
1037                &mut self.cheatcodes,
1038            ],
1039            |inspector| (**inspector).step_end(interpreter, ecx),
1040        );
1041    }
1042}
1043
1044impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>>
1045    for InspectorStackRefMut<'_, FEN>
1046{
1047    fn initialize_interp(
1048        &mut self,
1049        interpreter: &mut Interpreter,
1050        ecx: &mut FoundryContextFor<'_, FEN>,
1051    ) {
1052        call_inspectors!(
1053            [
1054                &mut self.line_coverage,
1055                &mut self.tracer,
1056                &mut self.cheatcodes,
1057                &mut self.script_execution_inspector,
1058                &mut self.printer
1059            ],
1060            |inspector| inspector.initialize_interp(interpreter, ecx),
1061        );
1062    }
1063
1064    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1065        self.step_inlined(interpreter, ecx);
1066    }
1067
1068    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1069        self.step_end_inlined(interpreter, ecx);
1070    }
1071
1072    #[allow(clippy::redundant_clone)]
1073    fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1074        call_inspectors!(
1075            [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
1076            |inspector| inspector.log(ecx, log.clone()),
1077        );
1078    }
1079
1080    #[allow(clippy::redundant_clone)]
1081    fn log_full(
1082        &mut self,
1083        interpreter: &mut Interpreter,
1084        ecx: &mut FoundryContextFor<'_, FEN>,
1085        log: Log,
1086    ) {
1087        call_inspectors!(
1088            [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
1089            |inspector| inspector.log_full(interpreter, ecx, log.clone()),
1090        );
1091    }
1092
1093    fn frame_start(
1094        &mut self,
1095        ecx: &mut FoundryContextFor<'_, FEN>,
1096        frame_input: &mut FrameInput,
1097    ) -> Option<FrameResult> {
1098        if let FrameInput::Create(inputs) = frame_input
1099            && let CreateScheme::Create2 { salt } = inputs.scheme()
1100            && self.should_use_create2_factory(ecx.journal().depth(), inputs)
1101        {
1102            let gas_limit = inputs.gas_limit();
1103            let create2_deployer = self.create2_deployer();
1104
1105            // Validate deployer before rewriting.
1106            let code_hash = ecx.journal_mut().load_account(create2_deployer).ok()?.info.code_hash;
1107            if code_hash == KECCAK_EMPTY {
1108                // Store the revert so `create` can return it inside the normal inspector
1109                // lifecycle (avoids tracing mismatch from short-circuiting in frame_start).
1110                self.inner.pending_create2_error = Some(CreateOutcome {
1111                    result: InterpreterResult {
1112                        result: InstructionResult::Revert,
1113                        output: Bytes::from(
1114                            format!("missing CREATE2 deployer: {create2_deployer}").into_bytes(),
1115                        ),
1116                        gas: Gas::new(gas_limit),
1117                    },
1118                    address: None,
1119                });
1120                return None;
1121            } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH {
1122                self.inner.pending_create2_error = Some(CreateOutcome {
1123                    result: InterpreterResult {
1124                        result: InstructionResult::Revert,
1125                        output: "invalid CREATE2 deployer bytecode".into(),
1126                        gas: Gas::new(gas_limit),
1127                    },
1128                    address: None,
1129                });
1130                return None;
1131            }
1132
1133            let call_inputs =
1134                get_create2_factory_call_inputs(salt, inputs, create2_deployer, ecx.journal_mut())
1135                    .ok()?;
1136
1137            // Record the redirect depth *after* validation succeeds.
1138            self.inner.pending_create2_redirects.push(ecx.journal().depth());
1139
1140            // Rewrite the frame input from Create to Call.
1141            *frame_input = FrameInput::Call(Box::new(call_inputs));
1142        }
1143
1144        None
1145    }
1146
1147    fn frame_end(
1148        &mut self,
1149        ecx: &mut FoundryContextFor<'_, FEN>,
1150        _frame_input: &FrameInput,
1151        frame_result: &mut FrameResult,
1152    ) {
1153        let depth = ecx.journal().depth();
1154        if self.inner.pending_create2_redirects.last().copied() != Some(depth) {
1155            return;
1156        }
1157
1158        self.inner.pending_create2_redirects.pop();
1159
1160        let FrameResult::Call(call) = frame_result else {
1161            debug_assert!(false, "pending CREATE2 redirect ended with non-call result");
1162            return;
1163        };
1164
1165        let address = match call.instruction_result() {
1166            return_ok!() => Address::try_from(call.output().as_ref())
1167                .map_err(|_| {
1168                    call.result = InterpreterResult {
1169                        result: InstructionResult::Revert,
1170                        output: "invalid CREATE2 factory output".into(),
1171                        gas: Gas::new(call.result.gas.limit()),
1172                    };
1173                })
1174                .ok(),
1175            _ => None,
1176        };
1177
1178        *frame_result = FrameResult::Create(CreateOutcome { result: call.result.clone(), address });
1179    }
1180
1181    fn call(
1182        &mut self,
1183        ecx: &mut FoundryContextFor<'_, FEN>,
1184        call: &mut CallInputs,
1185    ) -> Option<CallOutcome> {
1186        if self.in_inner_context && ecx.journal().depth() == 1 {
1187            self.adjust_evm_data_for_inner_context(ecx);
1188            return None;
1189        }
1190
1191        if ecx.journal().depth() == 0 {
1192            self.top_level_frame_start(ecx);
1193        }
1194
1195        call_inspectors!(
1196            #[ret]
1197            [
1198                &mut self.fuzzer,
1199                &mut self.tracer,
1200                &mut self.log_collector,
1201                &mut self.printer,
1202                &mut self.revert_diag,
1203                &mut self.tempo_labels
1204            ],
1205            |inspector| {
1206                let mut out = None;
1207                if let Some(output) = inspector.call(ecx, call) {
1208                    out = Some(Some(output));
1209                }
1210                out
1211            },
1212        );
1213
1214        if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1215            // Handle mocked functions, replace bytecode address with mock if matched.
1216            if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
1217                let input_bytes = call.input.bytes(ecx);
1218                // Check if any mock function set for call data or if catch-all mock function set
1219                // for selector.
1220                if let Some(target) = mocks
1221                    .get(&input_bytes)
1222                    .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector)))
1223                {
1224                    call.bytecode_address = *target;
1225
1226                    let target = ecx
1227                        .journal_mut()
1228                        .load_account_with_code(*target)
1229                        .expect("failed to load account");
1230                    call.known_bytecode =
1231                        (target.info.code_hash, target.info.code.clone().unwrap_or_default());
1232                }
1233            }
1234
1235            if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
1236                return Some(output);
1237            }
1238        }
1239
1240        if self.enable_isolation && !self.in_inner_context && ecx.journal().depth() == 1 {
1241            match call.scheme {
1242                // Isolate CALLs
1243                CallScheme::Call => {
1244                    let input = call.input.bytes(ecx);
1245                    let (result, _) = self.transact_inner(
1246                        ecx,
1247                        TxKind::Call(call.target_address),
1248                        call.caller,
1249                        input,
1250                        call.gas_limit,
1251                        call.value.get(),
1252                    );
1253                    return Some(CallOutcome {
1254                        result,
1255                        memory_offset: call.return_memory_offset.clone(),
1256                        was_precompile_called: true,
1257                        precompile_call_logs: vec![],
1258                    });
1259                }
1260                // Mark accounts and storage cold before STATICCALLs
1261                CallScheme::StaticCall => {
1262                    let (_, journal_inner) = ecx.db_journal_inner_mut();
1263                    let JournaledState { state, warm_addresses, .. } = journal_inner;
1264                    for (addr, acc_mut) in state {
1265                        // Do not mark accounts and storage cold accounts with arbitrary storage.
1266                        if let Some(cheatcodes) = &self.cheatcodes
1267                            && cheatcodes.has_arbitrary_storage(addr)
1268                        {
1269                            continue;
1270                        }
1271
1272                        if warm_addresses.is_cold(addr) {
1273                            acc_mut.mark_cold();
1274                        }
1275
1276                        for slot_mut in acc_mut.storage.values_mut() {
1277                            slot_mut.is_cold = true;
1278                        }
1279                    }
1280                }
1281                // Process other variants as usual
1282                CallScheme::CallCode | CallScheme::DelegateCall => {}
1283            }
1284        }
1285
1286        None
1287    }
1288
1289    fn call_end(
1290        &mut self,
1291        ecx: &mut FoundryContextFor<'_, FEN>,
1292        inputs: &CallInputs,
1293        outcome: &mut CallOutcome,
1294    ) {
1295        // We are processing inner context outputs in the outer context, so need to avoid processing
1296        // twice.
1297        if self.in_inner_context && ecx.journal().depth() == 1 {
1298            return;
1299        }
1300
1301        self.do_call_end(ecx, inputs, outcome);
1302
1303        if ecx.journal().depth() == 0 {
1304            self.top_level_frame_end(ecx, outcome.result.result);
1305        }
1306    }
1307
1308    fn create(
1309        &mut self,
1310        ecx: &mut FoundryContextFor<'_, FEN>,
1311        create: &mut CreateInputs,
1312    ) -> Option<CreateOutcome> {
1313        if self.in_inner_context && ecx.journal().depth() == 1 {
1314            self.adjust_evm_data_for_inner_context(ecx);
1315            return None;
1316        }
1317
1318        if ecx.journal().depth() == 0 {
1319            self.top_level_frame_start(ecx);
1320        }
1321
1322        call_inspectors!(
1323            #[ret]
1324            [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes],
1325            |inspector| inspector.create(ecx, create).map(Some),
1326        );
1327
1328        // If frame_start detected an invalid CREATE2 deployer, return the error here
1329        // (after sub-inspectors have been notified) so tracing stays balanced.
1330        if let Some(error) = self.inner.pending_create2_error.take() {
1331            return Some(error);
1332        }
1333
1334        if !matches!(create.scheme(), CreateScheme::Create2 { .. })
1335            && self.enable_isolation
1336            && !self.in_inner_context
1337            && ecx.journal().depth() == 1
1338        {
1339            // In isolation mode, transact_inner returns None for the address on revert; pre-compute
1340            // the would-be deployed address so create_end can enforce expected_revert reverter
1341            // checks.
1342            let precomputed_address = ecx
1343                .journal()
1344                .evm_state()
1345                .get(&create.caller())
1346                .map(|acc| create.caller().create(acc.info.nonce));
1347
1348            let (result, address) = self.transact_inner(
1349                ecx,
1350                TxKind::Create,
1351                create.caller(),
1352                create.init_code().clone(),
1353                create.gas_limit(),
1354                create.value(),
1355            );
1356            let address =
1357                address.or_else(|| if result.is_revert() { precomputed_address } else { None });
1358            return Some(CreateOutcome { result, address });
1359        }
1360
1361        None
1362    }
1363
1364    fn create_end(
1365        &mut self,
1366        ecx: &mut FoundryContextFor<'_, FEN>,
1367        call: &CreateInputs,
1368        outcome: &mut CreateOutcome,
1369    ) {
1370        // We are processing inner context outputs in the outer context, so need to avoid processing
1371        // twice.
1372        if self.in_inner_context && ecx.journal().depth() == 1 {
1373            return;
1374        }
1375
1376        self.do_create_end(ecx, call, outcome);
1377
1378        if ecx.journal().depth() == 0 {
1379            self.top_level_frame_end(ecx, outcome.result.result);
1380        }
1381    }
1382
1383    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1384        call_inspectors!([&mut self.printer], |inspector| {
1385            Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1386                inspector, contract, target, value,
1387            )
1388        });
1389    }
1390}
1391
1392impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStackRefMut<'_, FEN> {
1393    fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1394        call_inspectors!(
1395            #[ret]
1396            [&mut self.cheatcodes],
1397            |inspector| { inspector.should_use_create2_factory(depth, inputs).then_some(true) },
1398        );
1399
1400        false
1401    }
1402
1403    fn console_log(&mut self, msg: &str) {
1404        call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1405            inspector, msg
1406        ));
1407    }
1408
1409    fn get_networks(&self) -> NetworkConfigs {
1410        self.inner.networks
1411    }
1412
1413    fn create2_deployer(&self) -> Address {
1414        self.inner.create2_deployer
1415    }
1416}
1417
1418impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for InspectorStack<FEN> {
1419    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1420        self.as_mut().step_inlined(interpreter, ecx)
1421    }
1422
1423    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1424        self.as_mut().step_end_inlined(interpreter, ecx)
1425    }
1426
1427    fn call(
1428        &mut self,
1429        context: &mut FoundryContextFor<'_, FEN>,
1430        inputs: &mut CallInputs,
1431    ) -> Option<CallOutcome> {
1432        self.as_mut().call(context, inputs)
1433    }
1434
1435    fn call_end(
1436        &mut self,
1437        context: &mut FoundryContextFor<'_, FEN>,
1438        inputs: &CallInputs,
1439        outcome: &mut CallOutcome,
1440    ) {
1441        self.as_mut().call_end(context, inputs, outcome)
1442    }
1443
1444    fn create(
1445        &mut self,
1446        context: &mut FoundryContextFor<'_, FEN>,
1447        create: &mut CreateInputs,
1448    ) -> Option<CreateOutcome> {
1449        self.as_mut().create(context, create)
1450    }
1451
1452    fn create_end(
1453        &mut self,
1454        context: &mut FoundryContextFor<'_, FEN>,
1455        call: &CreateInputs,
1456        outcome: &mut CreateOutcome,
1457    ) {
1458        self.as_mut().create_end(context, call, outcome)
1459    }
1460
1461    fn initialize_interp(
1462        &mut self,
1463        interpreter: &mut Interpreter,
1464        ecx: &mut FoundryContextFor<'_, FEN>,
1465    ) {
1466        self.as_mut().initialize_interp(interpreter, ecx)
1467    }
1468
1469    fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1470        self.as_mut().log(ecx, log)
1471    }
1472
1473    fn log_full(
1474        &mut self,
1475        interpreter: &mut Interpreter,
1476        ecx: &mut FoundryContextFor<'_, FEN>,
1477        log: Log,
1478    ) {
1479        self.as_mut().log_full(interpreter, ecx, log)
1480    }
1481
1482    fn frame_start(
1483        &mut self,
1484        context: &mut FoundryContextFor<'_, FEN>,
1485        frame_input: &mut FrameInput,
1486    ) -> Option<FrameResult> {
1487        self.as_mut().frame_start(context, frame_input)
1488    }
1489
1490    fn frame_end(
1491        &mut self,
1492        context: &mut FoundryContextFor<'_, FEN>,
1493        frame_input: &FrameInput,
1494        frame_result: &mut FrameResult,
1495    ) {
1496        self.as_mut().frame_end(context, frame_input, frame_result)
1497    }
1498
1499    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1500        call_inspectors!([&mut self.inner.printer], |inspector| {
1501            Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1502                inspector, contract, target, value,
1503            )
1504        });
1505    }
1506}
1507
1508impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStack<FEN> {
1509    fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1510        self.as_mut().should_use_create2_factory(depth, inputs)
1511    }
1512
1513    fn get_networks(&self) -> NetworkConfigs {
1514        self.networks
1515    }
1516
1517    fn create2_deployer(&self) -> Address {
1518        self.create2_deployer
1519    }
1520}
1521
1522impl<'a, FEN: FoundryEvmNetwork> Deref for InspectorStackRefMut<'a, FEN> {
1523    type Target = &'a mut InspectorStackInner;
1524
1525    fn deref(&self) -> &Self::Target {
1526        &self.inner
1527    }
1528}
1529
1530impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStackRefMut<'_, FEN> {
1531    fn deref_mut(&mut self) -> &mut Self::Target {
1532        &mut self.inner
1533    }
1534}
1535
1536impl<FEN: FoundryEvmNetwork> Deref for InspectorStack<FEN> {
1537    type Target = InspectorStackInner;
1538
1539    fn deref(&self) -> &Self::Target {
1540        &self.inner
1541    }
1542}
1543
1544impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStack<FEN> {
1545    fn deref_mut(&mut self) -> &mut Self::Target {
1546        &mut self.inner
1547    }
1548}