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