1use super::{
2 Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, CustomPrintTracer, Fuzzer,
3 LogCollector, RevertDiagnostic, ScriptExecutionInspector, TracingInspector,
4};
5use alloy_evm::{eth::EthEvmContext, Evm};
6use alloy_primitives::{
7 map::{AddressHashMap, HashMap},
8 Address, Bytes, Log, TxKind, U256,
9};
10use foundry_cheatcodes::{CheatcodesExecutor, Wallets};
11use foundry_evm_core::{
12 backend::{DatabaseExt, JournaledState},
13 evm::new_evm_with_inspector,
14 ContextExt, Env, InspectorExt,
15};
16use foundry_evm_coverage::HitMaps;
17use foundry_evm_traces::{SparsedTraceArena, TraceMode};
18use revm::{
19 context::{
20 result::{ExecutionResult, Output},
21 BlockEnv,
22 },
23 context_interface::CreateScheme,
24 interpreter::{
25 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, EOFCreateInputs,
26 EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterResult,
27 },
28 state::{Account, AccountStatus},
29 Inspector,
30};
31use std::{
32 ops::{Deref, DerefMut},
33 sync::Arc,
34};
35
36#[derive(Clone, Debug, Default)]
37#[must_use = "builders do nothing unless you call `build` on them"]
38pub struct InspectorStackBuilder {
39 pub block: Option<BlockEnv>,
44 pub gas_price: Option<u128>,
49 pub cheatcodes: Option<Arc<CheatsConfig>>,
51 pub fuzzer: Option<Fuzzer>,
53 pub trace_mode: TraceMode,
55 pub logs: Option<bool>,
57 pub coverage: Option<bool>,
59 pub print: Option<bool>,
61 pub chisel_state: Option<usize>,
63 pub enable_isolation: bool,
67 pub odyssey: bool,
69 pub wallets: Option<Wallets>,
71 pub create2_deployer: Address,
73}
74
75impl InspectorStackBuilder {
76 #[inline]
78 pub fn new() -> Self {
79 Self::default()
80 }
81
82 #[inline]
84 pub fn block(mut self, block: BlockEnv) -> Self {
85 self.block = Some(block);
86 self
87 }
88
89 #[inline]
91 pub fn gas_price(mut self, gas_price: u128) -> Self {
92 self.gas_price = Some(gas_price);
93 self
94 }
95
96 #[inline]
98 pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
99 self.cheatcodes = Some(config);
100 self
101 }
102
103 #[inline]
105 pub fn wallets(mut self, wallets: Wallets) -> Self {
106 self.wallets = Some(wallets);
107 self
108 }
109
110 #[inline]
112 pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
113 self.fuzzer = Some(fuzzer);
114 self
115 }
116
117 #[inline]
119 pub fn chisel_state(mut self, final_pc: usize) -> Self {
120 self.chisel_state = Some(final_pc);
121 self
122 }
123
124 #[inline]
126 pub fn logs(mut self, yes: bool) -> Self {
127 self.logs = Some(yes);
128 self
129 }
130
131 #[inline]
133 pub fn coverage(mut self, yes: bool) -> Self {
134 self.coverage = Some(yes);
135 self
136 }
137
138 #[inline]
140 pub fn print(mut self, yes: bool) -> Self {
141 self.print = Some(yes);
142 self
143 }
144
145 #[inline]
148 pub fn trace_mode(mut self, mode: TraceMode) -> Self {
149 if self.trace_mode < mode {
150 self.trace_mode = mode
151 }
152 self
153 }
154
155 #[inline]
158 pub fn enable_isolation(mut self, yes: bool) -> Self {
159 self.enable_isolation = yes;
160 self
161 }
162
163 #[inline]
166 pub fn odyssey(mut self, yes: bool) -> Self {
167 self.odyssey = yes;
168 self
169 }
170
171 #[inline]
172 pub fn create2_deployer(mut self, create2_deployer: Address) -> Self {
173 self.create2_deployer = create2_deployer;
174 self
175 }
176
177 pub fn build(self) -> InspectorStack {
179 let Self {
180 block,
181 gas_price,
182 cheatcodes,
183 fuzzer,
184 trace_mode,
185 logs,
186 coverage,
187 print,
188 chisel_state,
189 enable_isolation,
190 odyssey,
191 wallets,
192 create2_deployer,
193 } = self;
194 let mut stack = InspectorStack::new();
195
196 if let Some(config) = cheatcodes {
198 let mut cheatcodes = Cheatcodes::new(config);
199 if let Some(wallets) = wallets {
201 cheatcodes.set_wallets(wallets);
202 }
203 stack.set_cheatcodes(cheatcodes);
204 }
205
206 if let Some(fuzzer) = fuzzer {
207 stack.set_fuzzer(fuzzer);
208 }
209 if let Some(chisel_state) = chisel_state {
210 stack.set_chisel(chisel_state);
211 }
212
213 stack.collect_coverage(coverage.unwrap_or(false));
214 stack.collect_logs(logs.unwrap_or(true));
215 stack.print(print.unwrap_or(false));
216 stack.tracing(trace_mode);
217
218 stack.enable_isolation(enable_isolation);
219 stack.odyssey(odyssey);
220 stack.set_create2_deployer(create2_deployer);
221
222 if let Some(block) = block {
224 stack.set_block(&block);
225 }
226 if let Some(gas_price) = gas_price {
227 stack.set_gas_price(gas_price);
228 }
229
230 stack
231 }
232}
233
234#[macro_export]
237macro_rules! call_inspectors {
238 ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {
239 $(
240 if let Some($id) = $inspector {
241 ({ #[inline(always)] #[cold] || $call })();
242 }
243 )+
244 };
245 (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {{
246 $(
247 if let Some($id) = $inspector {
248 if let Some(result) = ({ #[inline(always)] #[cold] || $call })() {
249 return result;
250 }
251 }
252 )+
253 }};
254}
255
256pub struct InspectorData {
258 pub logs: Vec<Log>,
259 pub labels: AddressHashMap<String>,
260 pub traces: Option<SparsedTraceArena>,
261 pub coverage: Option<HitMaps>,
262 pub cheatcodes: Option<Cheatcodes>,
263 pub chisel_state: Option<(Vec<U256>, Vec<u8>, InstructionResult)>,
264}
265
266#[derive(Debug, Clone)]
272pub struct InnerContextData {
273 original_origin: Address,
275}
276
277#[derive(Clone, Debug, Default)]
288pub struct InspectorStack {
289 pub cheatcodes: Option<Cheatcodes>,
290 pub inner: InspectorStackInner,
291}
292
293#[derive(Default, Clone, Debug)]
297pub struct InspectorStackInner {
298 pub chisel_state: Option<ChiselState>,
299 pub coverage: Option<CoverageCollector>,
300 pub fuzzer: Option<Fuzzer>,
301 pub log_collector: Option<LogCollector>,
302 pub printer: Option<CustomPrintTracer>,
303 pub tracer: Option<TracingInspector>,
304 pub script_execution_inspector: Option<ScriptExecutionInspector>,
305 pub enable_isolation: bool,
306 pub odyssey: bool,
307 pub create2_deployer: Address,
308 pub revert_diag: Option<RevertDiagnostic>,
309
310 pub in_inner_context: bool,
312 pub inner_context_data: Option<InnerContextData>,
313 pub top_frame_journal: HashMap<Address, Account>,
314}
315
316pub struct InspectorStackRefMut<'a> {
320 pub cheatcodes: Option<&'a mut Cheatcodes>,
321 pub inner: &'a mut InspectorStackInner,
322}
323
324impl CheatcodesExecutor for InspectorStackInner {
325 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
326 Box::new(InspectorStackRefMut { cheatcodes: Some(cheats), inner: self })
327 }
328
329 fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
330 Some(&mut self.tracer)
331 }
332}
333
334impl InspectorStack {
335 #[inline]
341 pub fn new() -> Self {
342 Self::default()
343 }
344
345 pub fn log_status(&self) {
347 trace!(enabled=%{
348 let mut enabled = Vec::with_capacity(16);
349 macro_rules! push {
350 ($($id:ident),* $(,)?) => {
351 $(
352 if self.$id.is_some() {
353 enabled.push(stringify!($id));
354 }
355 )*
356 };
357 }
358 push!(cheatcodes, chisel_state, coverage, fuzzer, log_collector, printer, tracer);
359 if self.enable_isolation {
360 enabled.push("isolation");
361 }
362 format!("[{}]", enabled.join(", "))
363 });
364 }
365
366 #[inline]
368 pub fn set_env(&mut self, env: &Env) {
369 self.set_block(&env.evm_env.block_env);
370 self.set_gas_price(env.tx.gas_price);
371 }
372
373 #[inline]
375 pub fn set_block(&mut self, block: &BlockEnv) {
376 if let Some(cheatcodes) = &mut self.cheatcodes {
377 cheatcodes.block = Some(block.clone());
378 }
379 }
380
381 #[inline]
383 pub fn set_gas_price(&mut self, gas_price: u128) {
384 if let Some(cheatcodes) = &mut self.cheatcodes {
385 cheatcodes.gas_price = Some(gas_price);
386 }
387 }
388
389 #[inline]
391 pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes) {
392 self.cheatcodes = Some(cheatcodes);
393 }
394
395 #[inline]
397 pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
398 self.fuzzer = Some(fuzzer);
399 }
400
401 #[inline]
403 pub fn set_chisel(&mut self, final_pc: usize) {
404 self.chisel_state = Some(ChiselState::new(final_pc));
405 }
406
407 #[inline]
409 pub fn collect_coverage(&mut self, yes: bool) {
410 self.coverage = yes.then(Default::default);
411 }
412
413 #[inline]
415 pub fn enable_isolation(&mut self, yes: bool) {
416 self.enable_isolation = yes;
417 }
418
419 #[inline]
421 pub fn odyssey(&mut self, yes: bool) {
422 self.odyssey = yes;
423 }
424
425 #[inline]
427 pub fn set_create2_deployer(&mut self, deployer: Address) {
428 self.create2_deployer = deployer;
429 }
430
431 #[inline]
433 pub fn collect_logs(&mut self, yes: bool) {
434 self.log_collector = yes.then(Default::default);
435 }
436
437 #[inline]
439 pub fn print(&mut self, yes: bool) {
440 self.printer = yes.then(Default::default);
441 }
442
443 #[inline]
446 pub fn tracing(&mut self, mode: TraceMode) {
447 if mode.is_none() {
448 self.revert_diag = None;
449 } else {
450 self.revert_diag = Some(RevertDiagnostic::default());
451 }
452
453 if let Some(config) = mode.into_config() {
454 *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
455 } else {
456 self.tracer = None;
457 }
458 }
459
460 #[inline]
462 pub fn script(&mut self, script_address: Address) {
463 self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
464 script_address;
465 }
466
467 #[inline]
469 pub fn collect(self) -> InspectorData {
470 let Self {
471 mut cheatcodes,
472 inner: InspectorStackInner { chisel_state, coverage, log_collector, tracer, .. },
473 } = self;
474
475 let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
476 let ignored = cheatcodes
477 .as_mut()
478 .map(|cheatcodes| {
479 let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
480
481 if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
483 ignored.insert(last_pause_call, (arena.nodes().len(), 0));
484 }
485
486 ignored
487 })
488 .unwrap_or_default();
489
490 SparsedTraceArena { arena, ignored }
491 });
492
493 InspectorData {
494 logs: log_collector.map(|logs| logs.logs).unwrap_or_default(),
495 labels: cheatcodes
496 .as_ref()
497 .map(|cheatcodes| cheatcodes.labels.clone())
498 .unwrap_or_default(),
499 traces,
500 coverage: coverage.map(|coverage| coverage.finish()),
501 cheatcodes,
502 chisel_state: chisel_state.and_then(|state| state.state),
503 }
504 }
505
506 #[inline(always)]
507 fn as_mut(&mut self) -> InspectorStackRefMut<'_> {
508 InspectorStackRefMut { cheatcodes: self.cheatcodes.as_mut(), inner: &mut self.inner }
509 }
510}
511
512impl InspectorStackRefMut<'_> {
513 fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>) {
518 let inner_context_data =
519 self.inner_context_data.as_ref().expect("should be called in inner context");
520 ecx.tx.caller = inner_context_data.original_origin;
521 }
522
523 fn do_call_end(
524 &mut self,
525 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
526 inputs: &CallInputs,
527 outcome: &mut CallOutcome,
528 ) -> CallOutcome {
529 let result = outcome.result.result;
530 call_inspectors!(
531 #[ret]
532 [
533 &mut self.fuzzer,
534 &mut self.tracer,
535 &mut self.cheatcodes,
536 &mut self.printer,
537 &mut self.revert_diag
538 ],
539 |inspector| {
540 let previous_outcome = outcome.clone();
541 inspector.call_end(ecx, inputs, outcome);
542
543 let different = outcome.result.result != result ||
546 (outcome.result.result == InstructionResult::Revert &&
547 outcome.output() != previous_outcome.output());
548 different.then_some(outcome.clone())
549 },
550 );
551
552 outcome.clone()
553 }
554
555 fn do_create_end(
556 &mut self,
557 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
558 call: &CreateInputs,
559 outcome: &mut CreateOutcome,
560 ) -> CreateOutcome {
561 let result = outcome.result.result;
562 call_inspectors!(
563 #[ret]
564 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
565 |inspector| {
566 let previous_outcome = outcome.clone();
567 inspector.create_end(ecx, call, outcome);
568
569 let different = outcome.result.result != result ||
572 (outcome.result.result == InstructionResult::Revert &&
573 outcome.output() != previous_outcome.output());
574 different.then_some(outcome.clone())
575 },
576 );
577
578 outcome.clone()
579 }
580
581 fn do_eofcreate_end(
582 &mut self,
583 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
584 call: &EOFCreateInputs,
585 outcome: &mut CreateOutcome,
586 ) -> CreateOutcome {
587 let result = outcome.result.result;
588 call_inspectors!(
589 #[ret]
590 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
591 |inspector| {
592 let previous_outcome = outcome.clone();
593 inspector.eofcreate_end(ecx, call, outcome);
594
595 let different = outcome.result.result != result ||
598 (outcome.result.result == InstructionResult::Revert &&
599 outcome.output() != previous_outcome.output());
600 different.then_some(outcome.clone())
601 },
602 );
603
604 outcome.clone()
605 }
606
607 fn transact_inner(
608 &mut self,
609 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
610 kind: TxKind,
611 caller: Address,
612 input: Bytes,
613 gas_limit: u64,
614 value: U256,
615 ) -> (InterpreterResult, Option<Address>) {
616 let cached_env = Env::from(ecx.cfg.clone(), ecx.block.clone(), ecx.tx.clone());
617
618 ecx.block.basefee = 0;
619 ecx.tx.chain_id = Some(ecx.cfg.chain_id);
620 ecx.tx.caller = caller;
621 ecx.tx.kind = kind;
622 ecx.tx.data = input;
623 ecx.tx.value = value;
624 ecx.tx.gas_limit = gas_limit + 21000;
626
627 if !ecx.cfg.disable_block_gas_limit {
630 ecx.tx.gas_limit = std::cmp::min(ecx.tx.gas_limit, ecx.block.gas_limit);
631 }
632 ecx.tx.gas_price = 0;
633
634 self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller });
635 self.in_inner_context = true;
636
637 let res = self.with_stack(|inspector| {
638 let (db, journal, env) = ecx.as_db_env_and_journal();
639 let mut evm = new_evm_with_inspector(db, env.to_owned(), inspector);
640
641 evm.journaled_state.state = {
642 let mut state = journal.state.clone();
643
644 for (addr, acc_mut) in &mut state {
645 if !journal.warm_preloaded_addresses.contains(addr) {
647 acc_mut.mark_cold();
648 }
649
650 for slot_mut in acc_mut.storage.values_mut() {
652 slot_mut.is_cold = true;
653 slot_mut.original_value = slot_mut.present_value;
654 }
655 }
656
657 state
658 };
659
660 evm.journaled_state.depth = 1;
662
663 let res = evm.transact(env.tx.clone());
664
665 *env.cfg = evm.cfg.clone();
667 *env.block = evm.block.clone();
668
669 *env.tx = cached_env.tx;
670 env.block.basefee = cached_env.evm_env.block_env.basefee;
671
672 res
673 });
674
675 self.in_inner_context = false;
676 self.inner_context_data = None;
677
678 let mut gas = Gas::new(gas_limit);
679
680 let Ok(res) = res else {
681 let result =
683 InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
684 return (result, None);
685 };
686
687 for (addr, mut acc) in res.state {
688 let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) else {
689 ecx.journaled_state.state.insert(addr, acc);
690 continue
691 };
692
693 if acc.status.contains(AccountStatus::Cold) &&
695 !acc_mut.status.contains(AccountStatus::Cold)
696 {
697 acc.status -= AccountStatus::Cold;
698 }
699 acc_mut.info = acc.info;
700 acc_mut.status |= acc.status;
701
702 for (key, val) in acc.storage {
703 let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
704 acc_mut.storage.insert(key, val);
705 continue
706 };
707 slot_mut.present_value = val.present_value;
708 slot_mut.is_cold &= val.is_cold;
709 }
710 }
711
712 let (result, address, output) = match res.result {
713 ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => {
714 gas.set_refund(gas_refunded as i64);
715 let _ = gas.record_cost(gas_used);
716 let address = match output {
717 Output::Create(_, address) => address,
718 Output::Call(_) => None,
719 };
720 (reason.into(), address, output.into_data())
721 }
722 ExecutionResult::Halt { reason, gas_used } => {
723 let _ = gas.record_cost(gas_used);
724 (reason.into(), None, Bytes::new())
725 }
726 ExecutionResult::Revert { gas_used, output } => {
727 let _ = gas.record_cost(gas_used);
728 (InstructionResult::Revert, None, output)
729 }
730 };
731 (InterpreterResult { result, output, gas }, address)
732 }
733
734 fn with_stack<O>(&mut self, f: impl FnOnce(&mut InspectorStack) -> O) -> O {
737 let mut stack = InspectorStack {
738 cheatcodes: self
739 .cheatcodes
740 .as_deref_mut()
741 .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone()))),
742 inner: std::mem::take(self.inner),
743 };
744
745 let out = f(&mut stack);
746
747 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
748 *cheats = stack.cheatcodes.take().unwrap();
749 }
750
751 *self.inner = stack.inner;
752
753 out
754 }
755
756 fn top_level_frame_start(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>) {
758 if self.enable_isolation {
759 self.top_frame_journal = ecx.journaled_state.state.clone();
762 }
763 }
764
765 fn top_level_frame_end(
767 &mut self,
768 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
769 result: InstructionResult,
770 ) {
771 if !result.is_revert() {
772 return;
773 }
774 if let Some(cheats) = self.cheatcodes.as_mut() {
778 cheats.on_revert(ecx);
779 }
780
781 if self.enable_isolation {
785 ecx.journaled_state.state = std::mem::take(&mut self.top_frame_journal);
786 }
787 }
788}
789
790impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for InspectorStackRefMut<'_> {
791 fn initialize_interp(
792 &mut self,
793 interpreter: &mut Interpreter,
794 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
795 ) {
796 call_inspectors!(
797 [
798 &mut self.coverage,
799 &mut self.tracer,
800 &mut self.cheatcodes,
801 &mut self.script_execution_inspector,
802 &mut self.printer
803 ],
804 |inspector| inspector.initialize_interp(interpreter, ecx),
805 );
806 }
807
808 fn step(
809 &mut self,
810 interpreter: &mut Interpreter,
811 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
812 ) {
813 call_inspectors!(
814 [
815 &mut self.fuzzer,
816 &mut self.tracer,
817 &mut self.coverage,
818 &mut self.cheatcodes,
819 &mut self.script_execution_inspector,
820 &mut self.printer,
821 &mut self.revert_diag
822 ],
823 |inspector| inspector.step(interpreter, ecx),
824 );
825 }
826
827 fn step_end(
828 &mut self,
829 interpreter: &mut Interpreter,
830 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
831 ) {
832 call_inspectors!(
833 [
834 &mut self.tracer,
835 &mut self.cheatcodes,
836 &mut self.chisel_state,
837 &mut self.printer,
838 &mut self.revert_diag
839 ],
840 |inspector| inspector.step_end(interpreter, ecx),
841 );
842 }
843
844 fn log(
845 &mut self,
846 interpreter: &mut Interpreter,
847 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
848 log: Log,
849 ) {
850 call_inspectors!(
851 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
852 |inspector| inspector.log(interpreter, ecx, log.clone()),
853 );
854 }
855
856 fn call(
857 &mut self,
858 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
859 call: &mut CallInputs,
860 ) -> Option<CallOutcome> {
861 if self.in_inner_context && ecx.journaled_state.depth == 1 {
862 self.adjust_evm_data_for_inner_context(ecx);
863 return None;
864 }
865
866 if ecx.journaled_state.depth == 0 {
867 self.top_level_frame_start(ecx);
868 }
869
870 call_inspectors!(
871 #[ret]
872 [
873 &mut self.fuzzer,
874 &mut self.tracer,
875 &mut self.log_collector,
876 &mut self.printer,
877 &mut self.revert_diag
878 ],
879 |inspector| {
880 let mut out = None;
881 if let Some(output) = inspector.call(ecx, call) {
882 if output.result.result != InstructionResult::Continue {
883 out = Some(Some(output));
884 }
885 }
886 out
887 },
888 );
889
890 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
891 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.target_address) {
893 if let Some(target) = mocks.get(&call.input.bytes(ecx)).or_else(|| {
896 call.input.bytes(ecx).get(..4).and_then(|selector| mocks.get(selector))
897 }) {
898 call.bytecode_address = *target;
899 }
900 }
901
902 if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
903 if output.result.result != InstructionResult::Continue {
904 return Some(output);
905 }
906 }
907 }
908
909 if self.enable_isolation && !self.in_inner_context && ecx.journaled_state.depth == 1 {
910 match call.scheme {
911 CallScheme::Call | CallScheme::ExtCall => {
913 let input = call.input.bytes(ecx);
914 let (result, _) = self.transact_inner(
915 ecx,
916 TxKind::Call(call.target_address),
917 call.caller,
918 input,
919 call.gas_limit,
920 call.value.get(),
921 );
922 return Some(CallOutcome {
923 result,
924 memory_offset: call.return_memory_offset.clone(),
925 });
926 }
927 CallScheme::StaticCall | CallScheme::ExtStaticCall => {
929 let JournaledState { state, warm_preloaded_addresses, .. } =
930 &mut ecx.journaled_state.inner;
931 for (addr, acc_mut) in state {
932 if let Some(cheatcodes) = &self.cheatcodes {
934 if cheatcodes.has_arbitrary_storage(addr) {
935 continue;
936 }
937 }
938
939 if !warm_preloaded_addresses.contains(addr) {
940 acc_mut.mark_cold();
941 }
942
943 for slot_mut in acc_mut.storage.values_mut() {
944 slot_mut.is_cold = true;
945 }
946 }
947 }
948 CallScheme::CallCode | CallScheme::DelegateCall | CallScheme::ExtDelegateCall => {}
950 }
951 }
952
953 None
954 }
955
956 fn call_end(
957 &mut self,
958 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
959 inputs: &CallInputs,
960 outcome: &mut CallOutcome,
961 ) {
962 if self.in_inner_context && ecx.journaled_state.depth == 1 {
965 return;
966 }
967
968 self.do_call_end(ecx, inputs, outcome);
969
970 if ecx.journaled_state.depth == 0 {
971 self.top_level_frame_end(ecx, outcome.result.result);
972 }
973 }
974
975 fn create(
976 &mut self,
977 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
978 create: &mut CreateInputs,
979 ) -> Option<CreateOutcome> {
980 if self.in_inner_context && ecx.journaled_state.depth == 1 {
981 self.adjust_evm_data_for_inner_context(ecx);
982 return None;
983 }
984
985 if ecx.journaled_state.depth == 0 {
986 self.top_level_frame_start(ecx);
987 }
988
989 call_inspectors!(
990 #[ret]
991 [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes],
992 |inspector| inspector.create(ecx, create).map(Some),
993 );
994
995 if !matches!(create.scheme, CreateScheme::Create2 { .. }) &&
996 self.enable_isolation &&
997 !self.in_inner_context &&
998 ecx.journaled_state.depth == 1
999 {
1000 let (result, address) = self.transact_inner(
1001 ecx,
1002 TxKind::Create,
1003 create.caller,
1004 create.init_code.clone(),
1005 create.gas_limit,
1006 create.value,
1007 );
1008 return Some(CreateOutcome { result, address });
1009 }
1010
1011 None
1012 }
1013
1014 fn create_end(
1015 &mut self,
1016 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1017 call: &CreateInputs,
1018 outcome: &mut CreateOutcome,
1019 ) {
1020 if self.in_inner_context && ecx.journaled_state.depth == 1 {
1023 return;
1024 }
1025
1026 self.do_create_end(ecx, call, outcome);
1027
1028 if ecx.journaled_state.depth == 0 {
1029 self.top_level_frame_end(ecx, outcome.result.result);
1030 }
1031 }
1032
1033 fn eofcreate(
1034 &mut self,
1035 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1036 create: &mut EOFCreateInputs,
1037 ) -> Option<CreateOutcome> {
1038 if self.in_inner_context && ecx.journaled_state.depth == 1 {
1039 self.adjust_evm_data_for_inner_context(ecx);
1040 return None;
1041 }
1042
1043 if ecx.journaled_state.depth == 0 {
1044 self.top_level_frame_start(ecx);
1045 }
1046
1047 call_inspectors!(
1048 #[ret]
1049 [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes],
1050 |inspector| inspector.eofcreate(ecx, create).map(Some),
1051 );
1052
1053 if matches!(create.kind, EOFCreateKind::Tx { .. }) &&
1054 self.enable_isolation &&
1055 !self.in_inner_context &&
1056 ecx.journaled_state.depth == 1
1057 {
1058 let init_code = match &mut create.kind {
1059 EOFCreateKind::Tx { initdata } => initdata.clone(),
1060 EOFCreateKind::Opcode { .. } => unreachable!(),
1061 };
1062
1063 let (result, address) = self.transact_inner(
1064 ecx,
1065 TxKind::Create,
1066 create.caller,
1067 init_code,
1068 create.gas_limit,
1069 create.value,
1070 );
1071 return Some(CreateOutcome { result, address });
1072 }
1073
1074 None
1075 }
1076
1077 fn eofcreate_end(
1078 &mut self,
1079 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1080 call: &EOFCreateInputs,
1081 outcome: &mut CreateOutcome,
1082 ) {
1083 if self.in_inner_context && ecx.journaled_state.depth == 1 {
1086 return;
1087 }
1088
1089 self.do_eofcreate_end(ecx, call, outcome);
1090
1091 if ecx.journaled_state.depth == 0 {
1092 self.top_level_frame_end(ecx, outcome.result.result);
1093 }
1094 }
1095
1096 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1097 call_inspectors!([&mut self.tracer, &mut self.printer], |inspector| {
1098 Inspector::<EthEvmContext<&mut dyn DatabaseExt>>::selfdestruct(
1099 inspector, contract, target, value,
1100 )
1101 });
1102 }
1103}
1104
1105impl InspectorExt for InspectorStackRefMut<'_> {
1106 fn should_use_create2_factory(
1107 &mut self,
1108 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1109 inputs: &CreateInputs,
1110 ) -> bool {
1111 call_inspectors!(
1112 #[ret]
1113 [&mut self.cheatcodes],
1114 |inspector| { inspector.should_use_create2_factory(ecx, inputs).then_some(true) },
1115 );
1116
1117 false
1118 }
1119
1120 fn console_log(&mut self, msg: &str) {
1121 call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1122 inspector, msg
1123 ));
1124 }
1125
1126 fn is_odyssey(&self) -> bool {
1127 self.inner.odyssey
1128 }
1129
1130 fn create2_deployer(&self) -> Address {
1131 self.inner.create2_deployer
1132 }
1133}
1134
1135impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for InspectorStack {
1136 #[inline]
1137 fn step(
1138 &mut self,
1139 interpreter: &mut Interpreter,
1140 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1141 ) {
1142 self.as_mut().step(interpreter, ecx)
1143 }
1144
1145 #[inline]
1146 fn step_end(
1147 &mut self,
1148 interpreter: &mut Interpreter,
1149 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1150 ) {
1151 self.as_mut().step_end(interpreter, ecx)
1152 }
1153
1154 fn call(
1155 &mut self,
1156 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1157 inputs: &mut CallInputs,
1158 ) -> Option<CallOutcome> {
1159 self.as_mut().call(context, inputs)
1160 }
1161
1162 fn call_end(
1163 &mut self,
1164 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1165 inputs: &CallInputs,
1166 outcome: &mut CallOutcome,
1167 ) {
1168 self.as_mut().call_end(context, inputs, outcome)
1169 }
1170
1171 fn create(
1172 &mut self,
1173 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1174 create: &mut CreateInputs,
1175 ) -> Option<CreateOutcome> {
1176 self.as_mut().create(context, create)
1177 }
1178
1179 fn create_end(
1180 &mut self,
1181 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1182 call: &CreateInputs,
1183 outcome: &mut CreateOutcome,
1184 ) {
1185 self.as_mut().create_end(context, call, outcome)
1186 }
1187
1188 fn eofcreate(
1189 &mut self,
1190 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1191 create: &mut EOFCreateInputs,
1192 ) -> Option<CreateOutcome> {
1193 self.as_mut().eofcreate(context, create)
1194 }
1195
1196 fn eofcreate_end(
1197 &mut self,
1198 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1199 call: &EOFCreateInputs,
1200 outcome: &mut CreateOutcome,
1201 ) {
1202 self.as_mut().eofcreate_end(context, call, outcome)
1203 }
1204
1205 fn initialize_interp(
1206 &mut self,
1207 interpreter: &mut Interpreter,
1208 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1209 ) {
1210 self.as_mut().initialize_interp(interpreter, ecx)
1211 }
1212
1213 fn log(
1214 &mut self,
1215 interpreter: &mut Interpreter,
1216 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1217 log: Log,
1218 ) {
1219 self.as_mut().log(interpreter, ecx, log)
1220 }
1221
1222 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1223 self.as_mut().selfdestruct(contract, target, value);
1224 }
1225}
1226
1227impl InspectorExt for InspectorStack {
1228 fn should_use_create2_factory(
1229 &mut self,
1230 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1231 inputs: &CreateInputs,
1232 ) -> bool {
1233 self.as_mut().should_use_create2_factory(ecx, inputs)
1234 }
1235
1236 fn is_odyssey(&self) -> bool {
1237 self.odyssey
1238 }
1239
1240 fn create2_deployer(&self) -> Address {
1241 self.create2_deployer
1242 }
1243}
1244
1245impl<'a> Deref for InspectorStackRefMut<'a> {
1246 type Target = &'a mut InspectorStackInner;
1247
1248 fn deref(&self) -> &Self::Target {
1249 &self.inner
1250 }
1251}
1252
1253impl DerefMut for InspectorStackRefMut<'_> {
1254 fn deref_mut(&mut self) -> &mut Self::Target {
1255 &mut self.inner
1256 }
1257}
1258
1259impl Deref for InspectorStack {
1260 type Target = InspectorStackInner;
1261
1262 fn deref(&self) -> &Self::Target {
1263 &self.inner
1264 }
1265}
1266
1267impl DerefMut for InspectorStack {
1268 fn deref_mut(&mut self) -> &mut Self::Target {
1269 &mut self.inner
1270 }
1271}