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 pub analysis: Option<Analysis>,
52 pub block: Option<BLOCK>,
57 pub gas_price: Option<u128>,
62 pub cheatcodes: Option<Arc<CheatsConfig>>,
64 pub fuzzer: Option<Fuzzer>,
66 pub trace_mode: TraceMode,
68 pub logs: Option<bool>,
73 pub line_coverage: Option<bool>,
75 pub print: Option<bool>,
77 pub chisel_state: Option<usize>,
79 pub enable_isolation: bool,
83 pub networks: NetworkConfigs,
85 pub wallets: Option<Wallets>,
87 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 #[inline]
115 pub fn new() -> Self {
116 Self::default()
117 }
118
119 #[inline]
121 pub fn set_analysis(mut self, analysis: Analysis) -> Self {
122 self.analysis = Some(analysis);
123 self
124 }
125
126 #[inline]
128 pub fn block(mut self, block: BLOCK) -> Self {
129 self.block = Some(block);
130 self
131 }
132
133 #[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 #[inline]
142 pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
143 self.cheatcodes = Some(config);
144 self
145 }
146
147 #[inline]
149 pub fn wallets(mut self, wallets: Wallets) -> Self {
150 self.wallets = Some(wallets);
151 self
152 }
153
154 #[inline]
156 pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
157 self.fuzzer = Some(fuzzer);
158 self
159 }
160
161 #[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 #[inline]
170 pub const fn logs(mut self, live_logs: bool) -> Self {
171 self.logs = Some(live_logs);
172 self
173 }
174
175 #[inline]
177 pub const fn line_coverage(mut self, yes: bool) -> Self {
178 self.line_coverage = Some(yes);
179 self
180 }
181
182 #[inline]
184 pub const fn print(mut self, yes: bool) -> Self {
185 self.print = Some(yes);
186 self
187 }
188
189 #[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 #[inline]
202 pub const fn enable_isolation(mut self, yes: bool) -> Self {
203 self.enable_isolation = yes;
204 self
205 }
206
207 #[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 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 if let Some(config) = cheatcodes {
244 let mut cheatcodes = Cheatcodes::new(config);
245 if let Some(analysis) = analysis {
247 stack.set_analysis(analysis.clone());
248 cheatcodes.set_analysis(CheatcodeAnalysis::new(analysis));
249 }
250 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 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#[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
312pub 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#[derive(Debug, Clone)]
331pub struct InnerContextData {
332 original_origin: Address,
334}
335
336#[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#[derive(Default, Clone, Debug)]
357pub struct InspectorStackInner {
358 pub analysis: Option<Analysis>,
360
361 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 pub sancov_edges: bool,
378 pub sancov_trace_cmp: bool,
380 pub enable_isolation: bool,
381 pub networks: NetworkConfigs,
382 pub create2_deployer: Address,
383 pub in_inner_context: bool,
385 pub inner_context_data: Option<InnerContextData>,
386 pub top_frame_journal: AddressMap<Account>,
387 pub reverter: Option<Address>,
389 pub pending_create2_redirects: Vec<usize>,
392 pub pending_create2_error: Option<CreateOutcome>,
395}
396
397pub 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 #[inline]
493 pub fn new() -> Self {
494 Self { cheatcodes: None, inner: InspectorStackInner::default() }
495 }
496
497 #[inline]
499 pub fn set_analysis(&mut self, analysis: Analysis) {
500 self.analysis = Some(analysis);
501 }
502
503 #[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 #[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 #[inline]
521 pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes<FEN>) {
522 self.cheatcodes = Some(cheatcodes.into());
523 }
524
525 #[inline]
527 pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
528 self.fuzzer = Some(fuzzer.into());
529 }
530
531 #[inline]
533 pub fn set_chisel(&mut self, final_pc: usize) {
534 self.chisel_state = Some(ChiselState::new(final_pc).into());
535 }
536
537 #[inline]
539 pub fn collect_line_coverage(&mut self, yes: bool) {
540 self.line_coverage = yes.then(Default::default);
541 }
542
543 #[inline]
545 pub fn collect_edge_coverage(&mut self, yes: bool) {
546 self.edge_coverage = yes.then(EdgeCovInspector::new).map(Into::into);
548 }
549
550 #[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 #[inline]
564 pub const fn collect_sancov_edges(&mut self, yes: bool) {
565 self.inner.sancov_edges = yes;
566 }
567
568 #[inline]
570 pub const fn collect_sancov_trace_cmp(&mut self, yes: bool) {
571 self.inner.sancov_trace_cmp = yes;
572 }
573
574 #[inline]
576 pub const fn enable_isolation(&mut self, yes: bool) {
577 self.inner.enable_isolation = yes;
578 }
579
580 #[inline]
582 pub const fn networks(&mut self, networks: NetworkConfigs) {
583 self.inner.networks = networks;
584 }
585
586 #[inline]
588 pub fn set_create2_deployer(&mut self, deployer: Address) {
589 self.create2_deployer = deployer;
590 }
591
592 #[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 #[inline]
609 pub fn print(&mut self, yes: bool) {
610 self.printer = yes.then(Default::default);
611 }
612
613 #[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 #[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 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 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 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 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 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 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 ecx.tx_mut().set_gas_limit(gas_limit + 21000);
792
793 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 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 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 if journal.warm_addresses.is_cold(addr) {
841 acc_mut.mark_cold();
842 }
843
844 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 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 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 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 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 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 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 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 fn top_level_frame_start(&mut self, ecx: &mut FoundryContextFor<'_, FEN>) {
964 if self.enable_isolation {
965 self.top_frame_journal.clone_from(ecx.journal().evm_state());
968 }
969 }
970
971 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 if let Some(cheats) = self.cheatcodes.as_mut() {
984 cheats.on_revert(ecx);
985 }
986
987 if self.enable_isolation {
991 *ecx.journal_mut().evm_state_mut() = std::mem::take(&mut self.top_frame_journal);
992 }
993 }
994
995 #[inline(always)]
1001 fn step_inlined(
1002 &mut self,
1003 interpreter: &mut Interpreter,
1004 ecx: &mut FoundryContextFor<'_, FEN>,
1005 ) {
1006 call_inspectors!(
1007 [
1008 &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 &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 &mut self.chisel_state,
1033 &mut self.printer,
1034 &mut self.revert_diag,
1035 &mut self.tracer,
1036 &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 let code_hash = ecx.journal_mut().load_account(create2_deployer).ok()?.info.code_hash;
1107 if code_hash == KECCAK_EMPTY {
1108 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 self.inner.pending_create2_redirects.push(ecx.journal().depth());
1139
1140 *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 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
1217 let input_bytes = call.input.bytes(ecx);
1218 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 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 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 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 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 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 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 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 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}