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