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