1use super::{
2 Cheatcodes, CheatsConfig, ChiselState, CustomPrintTracer, Fuzzer, LineCoverageCollector,
3 LogCollector, RevertDiagnostic, ScriptExecutionInspector, TracingInspector,
4};
5use alloy_evm::EvmEnv;
6use alloy_primitives::{
7 Address, B256, Bytes, Log, TxKind, U256,
8 map::{AddressHashMap, HashMap},
9};
10use foundry_cheatcodes::{
11 CheatcodeAnalysis, CheatcodesExecutor, EthCheatCtx, NestedEvmClosure, Wallets,
12};
13use foundry_common::compile::Analysis;
14use foundry_compilers::ProjectPathsConfig;
15use foundry_evm_core::{
16 FoundryBlock, FoundryInspectorExt, FoundryTransaction,
17 backend::{DatabaseError, DatabaseExt, JournaledState},
18 env::FoundryContextExt,
19 evm::{NestedEvm, new_eth_evm_with_inspector, with_cloned_context},
20};
21use foundry_evm_coverage::HitMaps;
22use foundry_evm_networks::NetworkConfigs;
23use foundry_evm_traces::{SparsedTraceArena, TraceMode};
24use revm::{
25 Inspector,
26 context::{
27 Block, BlockEnv, Cfg, ContextTr, JournalTr, Transaction, TxEnv,
28 result::{EVMError, ExecutionResult, Output},
29 },
30 context_interface::CreateScheme,
31 inspector::JournalExt,
32 interpreter::{
33 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult,
34 Interpreter, InterpreterResult,
35 },
36 state::{Account, AccountStatus},
37};
38use revm_inspectors::edge_cov::EdgeCovInspector;
39use std::{
40 ops::{Deref, DerefMut},
41 sync::Arc,
42};
43
44#[derive(Clone, Debug, Default)]
45#[must_use = "builders do nothing unless you call `build` on them"]
46pub struct InspectorStackBuilder {
47 pub analysis: Option<Analysis>,
49 pub block: Option<BlockEnv>,
54 pub gas_price: Option<u128>,
59 pub cheatcodes: Option<Arc<CheatsConfig>>,
61 pub fuzzer: Option<Fuzzer>,
63 pub trace_mode: TraceMode,
65 pub logs: Option<bool>,
70 pub line_coverage: Option<bool>,
72 pub print: Option<bool>,
74 pub chisel_state: Option<usize>,
76 pub enable_isolation: bool,
80 pub networks: NetworkConfigs,
82 pub wallets: Option<Wallets>,
84 pub create2_deployer: Address,
86}
87
88impl InspectorStackBuilder {
89 #[inline]
91 pub fn new() -> Self {
92 Self::default()
93 }
94
95 #[inline]
97 pub fn set_analysis(mut self, analysis: Analysis) -> Self {
98 self.analysis = Some(analysis);
99 self
100 }
101
102 #[inline]
104 pub fn block(mut self, block: BlockEnv) -> Self {
105 self.block = Some(block);
106 self
107 }
108
109 #[inline]
111 pub fn gas_price(mut self, gas_price: u128) -> Self {
112 self.gas_price = Some(gas_price);
113 self
114 }
115
116 #[inline]
118 pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
119 self.cheatcodes = Some(config);
120 self
121 }
122
123 #[inline]
125 pub fn wallets(mut self, wallets: Wallets) -> Self {
126 self.wallets = Some(wallets);
127 self
128 }
129
130 #[inline]
132 pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
133 self.fuzzer = Some(fuzzer);
134 self
135 }
136
137 #[inline]
139 pub fn chisel_state(mut self, final_pc: usize) -> Self {
140 self.chisel_state = Some(final_pc);
141 self
142 }
143
144 #[inline]
146 pub fn logs(mut self, live_logs: bool) -> Self {
147 self.logs = Some(live_logs);
148 self
149 }
150
151 #[inline]
153 pub fn line_coverage(mut self, yes: bool) -> Self {
154 self.line_coverage = Some(yes);
155 self
156 }
157
158 #[inline]
160 pub fn print(mut self, yes: bool) -> Self {
161 self.print = Some(yes);
162 self
163 }
164
165 #[inline]
168 pub fn trace_mode(mut self, mode: TraceMode) -> Self {
169 if self.trace_mode < mode {
170 self.trace_mode = mode
171 }
172 self
173 }
174
175 #[inline]
178 pub fn enable_isolation(mut self, yes: bool) -> Self {
179 self.enable_isolation = yes;
180 self
181 }
182
183 #[inline]
185 pub fn networks(mut self, networks: NetworkConfigs) -> Self {
186 self.networks = networks;
187 self
188 }
189
190 #[inline]
191 pub fn create2_deployer(mut self, create2_deployer: Address) -> Self {
192 self.create2_deployer = create2_deployer;
193 self
194 }
195
196 pub fn build(self) -> InspectorStack {
198 let Self {
199 analysis,
200 block,
201 gas_price,
202 cheatcodes,
203 fuzzer,
204 trace_mode,
205 logs,
206 line_coverage,
207 print,
208 chisel_state,
209 enable_isolation,
210 networks,
211 wallets,
212 create2_deployer,
213 } = self;
214 let mut stack = InspectorStack::new();
215
216 if let Some(config) = cheatcodes {
218 let mut cheatcodes = Cheatcodes::new(config);
219 if let Some(analysis) = analysis {
221 stack.set_analysis(analysis.clone());
222 cheatcodes.set_analysis(CheatcodeAnalysis::new(analysis));
223 }
224 if let Some(wallets) = wallets {
226 cheatcodes.set_wallets(wallets);
227 }
228 stack.set_cheatcodes(cheatcodes);
229 }
230
231 if let Some(fuzzer) = fuzzer {
232 stack.set_fuzzer(fuzzer);
233 }
234 if let Some(chisel_state) = chisel_state {
235 stack.set_chisel(chisel_state);
236 }
237 stack.collect_line_coverage(line_coverage.unwrap_or(false));
238 stack.collect_logs(logs);
239 stack.print(print.unwrap_or(false));
240 stack.tracing(trace_mode);
241
242 stack.enable_isolation(enable_isolation);
243 stack.networks(networks);
244 stack.set_create2_deployer(create2_deployer);
245
246 if let Some(block) = block {
248 stack.set_block(&block);
249 }
250 if let Some(gas_price) = gas_price {
251 stack.set_gas_price(gas_price);
252 }
253
254 stack
255 }
256}
257
258#[macro_export]
261macro_rules! call_inspectors {
262 ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {
263 $(
264 if let Some($id) = $inspector {
265 $crate::utils::cold_path();
266 $body;
267 }
268 )+
269 };
270 (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {{
271 $(
272 if let Some($id) = $inspector {
273 $crate::utils::cold_path();
274 if let Some(result) = $body {
275 return result;
276 }
277 }
278 )+
279 }};
280}
281
282pub struct InspectorData {
284 pub logs: Vec<Log>,
285 pub labels: AddressHashMap<String>,
286 pub traces: Option<SparsedTraceArena>,
287 pub line_coverage: Option<HitMaps>,
288 pub edge_coverage: Option<Vec<u8>>,
289 pub cheatcodes: Option<Box<Cheatcodes>>,
290 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
291 pub reverter: Option<Address>,
292}
293
294#[derive(Debug, Clone)]
300pub struct InnerContextData {
301 original_origin: Address,
303}
304
305#[derive(Clone, Debug, Default)]
316pub struct InspectorStack {
317 pub cheatcodes: Option<Box<Cheatcodes>>,
318 pub inner: InspectorStackInner,
319}
320
321impl InspectorStack {
322 pub fn paths_config(&self) -> Option<&ProjectPathsConfig> {
323 self.cheatcodes.as_ref().map(|c| &c.config.paths)
324 }
325}
326
327#[derive(Default, Clone, Debug)]
331pub struct InspectorStackInner {
332 pub analysis: Option<Analysis>,
334
335 pub chisel_state: Option<Box<ChiselState>>,
339 pub edge_coverage: Option<Box<EdgeCovInspector>>,
340 pub fuzzer: Option<Box<Fuzzer>>,
341 pub line_coverage: Option<Box<LineCoverageCollector>>,
342 pub log_collector: Option<Box<LogCollector>>,
343 pub printer: Option<Box<CustomPrintTracer>>,
344 pub revert_diag: Option<Box<RevertDiagnostic>>,
345 pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
346 pub tracer: Option<Box<TracingInspector>>,
347
348 pub enable_isolation: bool,
350 pub networks: NetworkConfigs,
351 pub create2_deployer: Address,
352 pub in_inner_context: bool,
354 pub inner_context_data: Option<InnerContextData>,
355 pub top_frame_journal: HashMap<Address, Account>,
356 pub reverter: Option<Address>,
358}
359
360pub struct InspectorStackRefMut<'a> {
363 pub cheatcodes: Option<&'a mut Cheatcodes>,
364 pub inner: &'a mut InspectorStackInner,
365}
366
367impl<CTX: EthCheatCtx> CheatcodesExecutor<CTX> for InspectorStackInner {
368 fn with_nested_evm(
369 &mut self,
370 cheats: &mut Cheatcodes,
371 ecx: &mut CTX,
372 f: NestedEvmClosure<'_, CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
373 ) -> Result<(), EVMError<DatabaseError>> {
374 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
375 with_cloned_context(ecx, |db, evm_env, tx_env, journal_inner| {
376 let mut evm = new_eth_evm_with_inspector(db, evm_env, tx_env, &mut inspector);
377 *evm.journal_inner_mut() = journal_inner;
378 f(&mut evm)?;
379 let sub_evm_env = evm.to_evm_env();
380 let sub_inner = evm.journaled_state.inner.clone();
381 Ok((sub_evm_env, sub_inner))
382 })
383 }
384
385 fn with_fresh_nested_evm(
386 &mut self,
387 cheats: &mut Cheatcodes,
388 db: &mut dyn DatabaseExt<CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
389 evm_env: EvmEnv<<CTX::Cfg as Cfg>::Spec, CTX::Block>,
390 tx_env: CTX::Tx,
391 f: NestedEvmClosure<'_, CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
392 ) -> Result<(), EVMError<DatabaseError>> {
393 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
394 let mut evm = new_eth_evm_with_inspector(db, evm_env, tx_env, &mut inspector);
395 f(&mut evm)
396 }
397
398 fn transact_on_db(
399 &mut self,
400 cheats: &mut Cheatcodes,
401 ecx: &mut CTX,
402 fork_id: Option<U256>,
403 transaction: B256,
404 ) -> eyre::Result<()> {
405 let evm_env = ecx.evm_clone();
406 let tx_env = ecx.tx_clone();
407 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
408 let (db, inner) = ecx.db_journal_inner_mut();
409 db.transact(fork_id, transaction, evm_env, tx_env, inner, &mut inspector)
410 }
411
412 fn transact_from_tx_on_db(
413 &mut self,
414 cheats: &mut Cheatcodes,
415 ecx: &mut CTX,
416 tx_env: &CTX::Tx,
417 ) -> eyre::Result<()> {
418 let evm_env = ecx.evm_clone();
419 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
420 let (db, inner) = ecx.db_journal_inner_mut();
421 db.transact_from_tx(tx_env, evm_env, inner, &mut inspector)
422 }
423
424 fn console_log(&mut self, _cheats: &mut Cheatcodes, msg: &str) {
425 if let Some(ref mut collector) = self.log_collector {
426 FoundryInspectorExt::console_log(&mut **collector, msg);
427 }
428 }
429
430 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
431 self.tracer.as_deref_mut()
432 }
433
434 fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option<Address>) {
435 self.in_inner_context = enabled;
436 self.inner_context_data = if enabled {
437 Some(InnerContextData {
438 original_origin: original_origin.expect("origin required when enabling inner ctx"),
439 })
440 } else {
441 None
442 };
443 }
444}
445
446impl InspectorStack {
447 #[inline]
453 pub fn new() -> Self {
454 Self::default()
455 }
456
457 pub fn log_status(&self) {
459 trace!(enabled=%{
460 let mut enabled = Vec::with_capacity(16);
461 macro_rules! push {
462 ($($id:ident),* $(,)?) => {
463 $(
464 if self.$id.is_some() {
465 enabled.push(stringify!($id));
466 }
467 )*
468 };
469 }
470 push!(cheatcodes, chisel_state, line_coverage, fuzzer, log_collector, printer, tracer);
471 if self.enable_isolation {
472 enabled.push("isolation");
473 }
474 format!("[{}]", enabled.join(", "))
475 });
476 }
477
478 #[inline]
480 pub fn set_analysis(&mut self, analysis: Analysis) {
481 self.analysis = Some(analysis);
482 }
483
484 #[inline]
486 pub fn set_env(&mut self, evm_env: &EvmEnv, tx_env: &TxEnv) {
487 self.set_block(&evm_env.block_env);
488 self.set_gas_price(tx_env.gas_price);
489 }
490
491 #[inline]
493 pub fn set_block(&mut self, block: &BlockEnv) {
494 if let Some(cheatcodes) = &mut self.cheatcodes {
495 cheatcodes.block = Some(block.clone());
496 }
497 }
498
499 #[inline]
501 pub fn set_gas_price(&mut self, gas_price: u128) {
502 if let Some(cheatcodes) = &mut self.cheatcodes {
503 cheatcodes.gas_price = Some(gas_price);
504 }
505 }
506
507 #[inline]
509 pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes) {
510 self.cheatcodes = Some(cheatcodes.into());
511 }
512
513 #[inline]
515 pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
516 self.fuzzer = Some(fuzzer.into());
517 }
518
519 #[inline]
521 pub fn set_chisel(&mut self, final_pc: usize) {
522 self.chisel_state = Some(ChiselState::new(final_pc).into());
523 }
524
525 #[inline]
527 pub fn collect_line_coverage(&mut self, yes: bool) {
528 self.line_coverage = yes.then(Default::default);
529 }
530
531 #[inline]
533 pub fn collect_edge_coverage(&mut self, yes: bool) {
534 self.edge_coverage = yes.then(EdgeCovInspector::new).map(Into::into);
536 }
537
538 #[inline]
540 pub fn enable_isolation(&mut self, yes: bool) {
541 self.enable_isolation = yes;
542 }
543
544 #[inline]
546 pub fn networks(&mut self, networks: NetworkConfigs) {
547 self.networks = networks;
548 }
549
550 #[inline]
552 pub fn set_create2_deployer(&mut self, deployer: Address) {
553 self.create2_deployer = deployer;
554 }
555
556 #[inline]
561 pub fn collect_logs(&mut self, live_logs: Option<bool>) {
562 self.log_collector = live_logs.map(|live_logs| {
563 Box::new(if live_logs {
564 LogCollector::LiveLogs
565 } else {
566 LogCollector::Capture { logs: Vec::new() }
567 })
568 });
569 }
570
571 #[inline]
573 pub fn print(&mut self, yes: bool) {
574 self.printer = yes.then(Default::default);
575 }
576
577 #[inline]
580 pub fn tracing(&mut self, mode: TraceMode) {
581 self.revert_diag = (!mode.is_none()).then(RevertDiagnostic::default).map(Into::into);
582
583 if let Some(config) = mode.into_config() {
584 *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
585 } else {
586 self.tracer = None;
587 }
588 }
589
590 #[inline]
592 pub fn script(&mut self, script_address: Address) {
593 self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
594 script_address;
595 }
596
597 #[inline(always)]
598 fn as_mut(&mut self) -> InspectorStackRefMut<'_> {
599 InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
600 }
601
602 pub fn collect(self) -> InspectorData {
604 let Self {
605 mut cheatcodes,
606 inner:
607 InspectorStackInner {
608 chisel_state,
609 line_coverage,
610 edge_coverage,
611 log_collector,
612 tracer,
613 reverter,
614 ..
615 },
616 } = self;
617
618 let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
619 let ignored = cheatcodes
620 .as_mut()
621 .map(|cheatcodes| {
622 let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
623
624 if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
626 ignored.insert(last_pause_call, (arena.nodes().len(), 0));
627 }
628
629 ignored
630 })
631 .unwrap_or_default();
632
633 SparsedTraceArena { arena, ignored }
634 });
635
636 InspectorData {
637 logs: log_collector.and_then(|logs| logs.into_captured_logs()).unwrap_or_default(),
638 labels: cheatcodes
639 .as_ref()
640 .map(|cheatcodes| cheatcodes.labels.clone())
641 .unwrap_or_default(),
642 traces,
643 line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()),
644 edge_coverage: edge_coverage.map(|edge_coverage| edge_coverage.into_hitcount()),
645 cheatcodes,
646 chisel_state: chisel_state.and_then(|state| state.state),
647 reverter,
648 }
649 }
650}
651
652impl InspectorStackRefMut<'_> {
653 fn adjust_evm_data_for_inner_context<CTX: FoundryContextExt>(&mut self, ecx: &mut CTX) {
658 let inner_context_data =
659 self.inner_context_data.as_ref().expect("should be called in inner context");
660 ecx.tx_mut().set_caller(inner_context_data.original_origin);
661 }
662
663 fn do_call_end<CTX: EthCheatCtx>(
664 &mut self,
665 ecx: &mut CTX,
666 inputs: &CallInputs,
667 outcome: &mut CallOutcome,
668 ) -> CallOutcome {
669 let result = outcome.result.result;
670 call_inspectors!(
671 #[ret]
672 [
673 &mut self.fuzzer,
674 &mut self.tracer,
675 &mut self.cheatcodes,
676 &mut self.printer,
677 &mut self.revert_diag
678 ],
679 |inspector| {
680 let previous_outcome = outcome.clone();
681 inspector.call_end(ecx, inputs, outcome);
682
683 let different = outcome.result.result != result
686 || (outcome.result.result == InstructionResult::Revert
687 && outcome.output() != previous_outcome.output());
688 different.then_some(outcome.clone())
689 },
690 );
691
692 if result.is_revert() && self.reverter.is_none() {
694 self.reverter = Some(inputs.target_address);
695 }
696
697 outcome.clone()
698 }
699
700 fn do_create_end<CTX: EthCheatCtx>(
701 &mut self,
702 ecx: &mut CTX,
703 call: &CreateInputs,
704 outcome: &mut CreateOutcome,
705 ) -> CreateOutcome {
706 let result = outcome.result.result;
707 call_inspectors!(
708 #[ret]
709 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
710 |inspector| {
711 let previous_outcome = outcome.clone();
712 inspector.create_end(ecx, call, outcome);
713
714 let different = outcome.result.result != result
717 || (outcome.result.result == InstructionResult::Revert
718 && outcome.output() != previous_outcome.output());
719 different.then_some(outcome.clone())
720 },
721 );
722
723 outcome.clone()
724 }
725
726 fn transact_inner<CTX: EthCheatCtx>(
727 &mut self,
728 ecx: &mut CTX,
729 kind: TxKind,
730 caller: Address,
731 input: Bytes,
732 gas_limit: u64,
733 value: U256,
734 ) -> (InterpreterResult, Option<Address>) {
735 let cached_evm_env = ecx.evm_clone();
736 let cached_tx_env = ecx.tx_clone();
737
738 ecx.block_mut().set_basefee(0);
739
740 let chain_id = ecx.cfg().chain_id();
741 ecx.tx_mut().set_chain_id(Some(chain_id));
742 ecx.tx_mut().set_caller(caller);
743 ecx.tx_mut().set_kind(kind);
744 ecx.tx_mut().set_data(input);
745 ecx.tx_mut().set_value(value);
746 ecx.tx_mut().set_gas_limit(gas_limit + 21000);
748
749 if !ecx.cfg().is_block_gas_limit_disabled() {
752 let gas_limit = std::cmp::min(ecx.tx().gas_limit(), ecx.block().gas_limit());
753 ecx.tx_mut().set_gas_limit(gas_limit);
754 }
755 ecx.tx_mut().set_gas_price(0);
756
757 self.inner_context_data = Some(InnerContextData { original_origin: cached_tx_env.caller });
758 self.in_inner_context = true;
759
760 let evm_env = ecx.evm_clone();
761 let tx_env = ecx.tx_clone();
762
763 let res = self.with_inspector(|mut inspector| {
764 let (res, nested_env) = {
765 let (db, journal) = ecx.db_journal_inner_mut();
766 let mut evm =
767 new_eth_evm_with_inspector(db, evm_env, tx_env.clone(), &mut inspector);
768
769 evm.journal_inner_mut().state = {
770 let mut state = journal.state.clone();
771
772 for (addr, acc_mut) in &mut state {
773 if journal.warm_addresses.is_cold(addr) {
775 acc_mut.mark_cold();
776 }
777
778 for slot_mut in acc_mut.storage.values_mut() {
780 slot_mut.is_cold = true;
781 slot_mut.original_value = slot_mut.present_value;
782 }
783 }
784
785 state
786 };
787
788 evm.journal_inner_mut().depth = 1;
790
791 let res = evm.transact(tx_env);
792 let nested_evm_env = evm.to_evm_env();
793 (res, nested_evm_env)
794 };
795
796 let mut restored_evm_env = nested_env;
799 restored_evm_env.block_env.basefee = cached_evm_env.block_env.basefee;
800 ecx.set_evm(restored_evm_env);
801 ecx.set_tx(cached_tx_env);
802
803 res
804 });
805
806 self.in_inner_context = false;
807 self.inner_context_data = None;
808
809 let mut gas = Gas::new(gas_limit);
810
811 let Ok(res) = res else {
812 let result =
814 InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
815 return (result, None);
816 };
817
818 for (addr, mut acc) in res.state {
819 let Some(acc_mut) = ecx.journal_mut().evm_state_mut().get_mut(&addr) else {
820 ecx.journal_mut().evm_state_mut().insert(addr, acc);
821 continue;
822 };
823
824 if acc.status.contains(AccountStatus::Cold)
826 && !acc_mut.status.contains(AccountStatus::Cold)
827 {
828 acc.status -= AccountStatus::Cold;
829 }
830 acc_mut.info = acc.info;
831 acc_mut.status |= acc.status;
832
833 for (key, val) in acc.storage {
834 let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
835 acc_mut.storage.insert(key, val);
836 continue;
837 };
838 slot_mut.present_value = val.present_value;
839 slot_mut.is_cold &= val.is_cold;
840 }
841 }
842
843 let (result, address, output) = match res.result {
844 ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => {
845 gas.set_refund(gas_refunded as i64);
846 let _ = gas.record_cost(gas_used);
847 let address = match output {
848 Output::Create(_, address) => address,
849 Output::Call(_) => None,
850 };
851 (reason.into(), address, output.into_data())
852 }
853 ExecutionResult::Halt { reason, gas_used } => {
854 let _ = gas.record_cost(gas_used);
855 (reason.into(), None, Bytes::new())
856 }
857 ExecutionResult::Revert { gas_used, output } => {
858 let _ = gas.record_cost(gas_used);
859 (InstructionResult::Revert, None, output)
860 }
861 };
862 (InterpreterResult { result, output, gas }, address)
863 }
864
865 fn with_inspector<O>(&mut self, f: impl FnOnce(InspectorStackRefMut<'_>) -> O) -> O {
868 let mut cheatcodes = self
869 .cheatcodes
870 .as_deref_mut()
871 .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())));
872 let mut inner = std::mem::take(self.inner);
873
874 let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner });
875
876 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
877 *cheats = cheatcodes.unwrap();
878 }
879
880 *self.inner = inner;
881
882 out
883 }
884
885 fn top_level_frame_start<CTX: ContextTr<Journal: JournalExt>>(&mut self, ecx: &mut CTX) {
887 if self.enable_isolation {
888 self.top_frame_journal.clone_from(ecx.journal().evm_state());
891 }
892 }
893
894 fn top_level_frame_end<CTX: ContextTr<Journal: JournalExt>>(
896 &mut self,
897 ecx: &mut CTX,
898 result: InstructionResult,
899 ) {
900 if !result.is_revert() {
901 return;
902 }
903 if let Some(cheats) = self.cheatcodes.as_mut() {
907 cheats.on_revert(ecx);
908 }
909
910 if self.enable_isolation {
914 *ecx.journal_mut().evm_state_mut() = std::mem::take(&mut self.top_frame_journal);
915 }
916 }
917
918 #[inline(always)]
924 fn step_inlined<CTX: EthCheatCtx>(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
925 call_inspectors!(
926 [
927 &mut self.edge_coverage,
929 &mut self.fuzzer,
930 &mut self.line_coverage,
931 &mut self.printer,
932 &mut self.revert_diag,
933 &mut self.script_execution_inspector,
934 &mut self.tracer,
935 &mut self.cheatcodes,
937 ],
938 |inspector| (**inspector).step(interpreter, ecx),
939 );
940 }
941
942 #[inline(always)]
943 fn step_end_inlined<CTX: EthCheatCtx>(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
944 call_inspectors!(
945 [
946 &mut self.chisel_state,
948 &mut self.printer,
949 &mut self.revert_diag,
950 &mut self.tracer,
951 &mut self.cheatcodes,
953 ],
954 |inspector| (**inspector).step_end(interpreter, ecx),
955 );
956 }
957}
958
959impl<CTX: EthCheatCtx> Inspector<CTX> for InspectorStackRefMut<'_> {
960 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
961 call_inspectors!(
962 [
963 &mut self.line_coverage,
964 &mut self.tracer,
965 &mut self.cheatcodes,
966 &mut self.script_execution_inspector,
967 &mut self.printer
968 ],
969 |inspector| inspector.initialize_interp(interpreter, ecx),
970 );
971 }
972
973 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
974 self.step_inlined(interpreter, ecx);
975 }
976
977 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
978 self.step_end_inlined(interpreter, ecx);
979 }
980
981 #[allow(clippy::redundant_clone)]
982 fn log(&mut self, ecx: &mut CTX, log: Log) {
983 call_inspectors!(
984 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
985 |inspector| inspector.log(ecx, log.clone()),
986 );
987 }
988
989 #[allow(clippy::redundant_clone)]
990 fn log_full(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX, log: Log) {
991 call_inspectors!(
992 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
993 |inspector| inspector.log_full(interpreter, ecx, log.clone()),
994 );
995 }
996
997 fn call(&mut self, ecx: &mut CTX, call: &mut CallInputs) -> Option<CallOutcome> {
998 if self.in_inner_context && ecx.journal().depth() == 1 {
999 self.adjust_evm_data_for_inner_context(ecx);
1000 return None;
1001 }
1002
1003 if ecx.journal().depth() == 0 {
1004 self.top_level_frame_start(ecx);
1005 }
1006
1007 call_inspectors!(
1008 #[ret]
1009 [
1010 &mut self.fuzzer,
1011 &mut self.tracer,
1012 &mut self.log_collector,
1013 &mut self.printer,
1014 &mut self.revert_diag
1015 ],
1016 |inspector| {
1017 let mut out = None;
1018 if let Some(output) = inspector.call(ecx, call) {
1019 out = Some(Some(output));
1020 }
1021 out
1022 },
1023 );
1024
1025 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1026 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
1028 let input_bytes = call.input.bytes(ecx);
1029 if let Some(target) = mocks
1032 .get(&input_bytes)
1033 .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector)))
1034 {
1035 call.bytecode_address = *target;
1036 call.known_bytecode = None;
1037 }
1038 }
1039
1040 if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
1041 return Some(output);
1042 }
1043 }
1044
1045 if self.enable_isolation && !self.in_inner_context && ecx.journal().depth() == 1 {
1046 match call.scheme {
1047 CallScheme::Call => {
1049 let input = call.input.bytes(ecx);
1050 let (result, _) = self.transact_inner(
1051 ecx,
1052 TxKind::Call(call.target_address),
1053 call.caller,
1054 input,
1055 call.gas_limit,
1056 call.value.get(),
1057 );
1058 return Some(CallOutcome {
1059 result,
1060 memory_offset: call.return_memory_offset.clone(),
1061 was_precompile_called: true,
1062 precompile_call_logs: vec![],
1063 });
1064 }
1065 CallScheme::StaticCall => {
1067 let (_, journal_inner) = ecx.db_journal_inner_mut();
1068 let JournaledState { state, warm_addresses, .. } = journal_inner;
1069 for (addr, acc_mut) in state {
1070 if let Some(cheatcodes) = &self.cheatcodes
1072 && cheatcodes.has_arbitrary_storage(addr)
1073 {
1074 continue;
1075 }
1076
1077 if warm_addresses.is_cold(addr) {
1078 acc_mut.mark_cold();
1079 }
1080
1081 for slot_mut in acc_mut.storage.values_mut() {
1082 slot_mut.is_cold = true;
1083 }
1084 }
1085 }
1086 CallScheme::CallCode | CallScheme::DelegateCall => {}
1088 }
1089 }
1090
1091 None
1092 }
1093
1094 fn call_end(&mut self, ecx: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
1095 if self.in_inner_context && ecx.journal().depth() == 1 {
1098 return;
1099 }
1100
1101 self.do_call_end(ecx, inputs, outcome);
1102
1103 if ecx.journal().depth() == 0 {
1104 self.top_level_frame_end(ecx, outcome.result.result);
1105 }
1106 }
1107
1108 fn create(&mut self, ecx: &mut CTX, create: &mut CreateInputs) -> Option<CreateOutcome> {
1109 if self.in_inner_context && ecx.journal().depth() == 1 {
1110 self.adjust_evm_data_for_inner_context(ecx);
1111 return None;
1112 }
1113
1114 if ecx.journal().depth() == 0 {
1115 self.top_level_frame_start(ecx);
1116 }
1117
1118 call_inspectors!(
1119 #[ret]
1120 [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes],
1121 |inspector| inspector.create(ecx, create).map(Some),
1122 );
1123
1124 if !matches!(create.scheme(), CreateScheme::Create2 { .. })
1125 && self.enable_isolation
1126 && !self.in_inner_context
1127 && ecx.journal().depth() == 1
1128 {
1129 let (result, address) = self.transact_inner(
1130 ecx,
1131 TxKind::Create,
1132 create.caller(),
1133 create.init_code().clone(),
1134 create.gas_limit(),
1135 create.value(),
1136 );
1137 return Some(CreateOutcome { result, address });
1138 }
1139
1140 None
1141 }
1142
1143 fn create_end(&mut self, ecx: &mut CTX, call: &CreateInputs, outcome: &mut CreateOutcome) {
1144 if self.in_inner_context && ecx.journal().depth() == 1 {
1147 return;
1148 }
1149
1150 self.do_create_end(ecx, call, outcome);
1151
1152 if ecx.journal().depth() == 0 {
1153 self.top_level_frame_end(ecx, outcome.result.result);
1154 }
1155 }
1156
1157 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1158 call_inspectors!([&mut self.printer], |inspector| Inspector::<CTX>::selfdestruct(
1159 inspector, contract, target, value,
1160 ));
1161 }
1162}
1163
1164impl FoundryInspectorExt for InspectorStackRefMut<'_> {
1165 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1166 call_inspectors!(
1167 #[ret]
1168 [&mut self.cheatcodes],
1169 |inspector| { inspector.should_use_create2_factory(depth, inputs).then_some(true) },
1170 );
1171
1172 false
1173 }
1174
1175 fn console_log(&mut self, msg: &str) {
1176 call_inspectors!([&mut self.log_collector], |inspector| FoundryInspectorExt::console_log(
1177 inspector, msg
1178 ));
1179 }
1180
1181 fn get_networks(&self) -> NetworkConfigs {
1182 self.inner.networks
1183 }
1184
1185 fn create2_deployer(&self) -> Address {
1186 self.inner.create2_deployer
1187 }
1188}
1189
1190impl<CTX: EthCheatCtx> Inspector<CTX> for InspectorStack {
1191 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
1192 self.as_mut().step_inlined(interpreter, ecx)
1193 }
1194
1195 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
1196 self.as_mut().step_end_inlined(interpreter, ecx)
1197 }
1198
1199 fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
1200 self.as_mut().call(context, inputs)
1201 }
1202
1203 fn call_end(&mut self, context: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
1204 self.as_mut().call_end(context, inputs, outcome)
1205 }
1206
1207 fn create(&mut self, context: &mut CTX, create: &mut CreateInputs) -> Option<CreateOutcome> {
1208 self.as_mut().create(context, create)
1209 }
1210
1211 fn create_end(&mut self, context: &mut CTX, call: &CreateInputs, outcome: &mut CreateOutcome) {
1212 self.as_mut().create_end(context, call, outcome)
1213 }
1214
1215 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
1216 self.as_mut().initialize_interp(interpreter, ecx)
1217 }
1218
1219 fn log(&mut self, ecx: &mut CTX, log: Log) {
1220 self.as_mut().log(ecx, log)
1221 }
1222
1223 fn log_full(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX, log: Log) {
1224 self.as_mut().log_full(interpreter, ecx, log)
1225 }
1226
1227 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1228 call_inspectors!([&mut self.inner.printer], |inspector| Inspector::<CTX>::selfdestruct(
1229 inspector, contract, target, value,
1230 ));
1231 }
1232}
1233
1234impl FoundryInspectorExt for InspectorStack {
1235 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1236 self.as_mut().should_use_create2_factory(depth, inputs)
1237 }
1238
1239 fn get_networks(&self) -> NetworkConfigs {
1240 self.networks
1241 }
1242
1243 fn create2_deployer(&self) -> Address {
1244 self.create2_deployer
1245 }
1246}
1247
1248impl<'a> Deref for InspectorStackRefMut<'a> {
1249 type Target = &'a mut InspectorStackInner;
1250
1251 fn deref(&self) -> &Self::Target {
1252 &self.inner
1253 }
1254}
1255
1256impl DerefMut for InspectorStackRefMut<'_> {
1257 fn deref_mut(&mut self) -> &mut Self::Target {
1258 &mut self.inner
1259 }
1260}
1261
1262impl Deref for InspectorStack {
1263 type Target = InspectorStackInner;
1264
1265 fn deref(&self) -> &Self::Target {
1266 &self.inner
1267 }
1268}
1269
1270impl DerefMut for InspectorStack {
1271 fn deref_mut(&mut self) -> &mut Self::Target {
1272 &mut self.inner
1273 }
1274}