anvil/eth/backend/mem/
inspector.rs

1//! Anvil specific [`revm::Inspector`] implementation
2
3use crate::eth::macros::node_info;
4use alloy_primitives::{Address, Log, U256};
5use foundry_evm::{
6    call_inspectors,
7    decode::decode_console_logs,
8    inspectors::{LogCollector, TracingInspector},
9    traces::{
10        CallTraceDecoder, SparsedTraceArena, TracingInspectorConfig, render_trace_arena_inner,
11    },
12};
13use revm::{
14    Inspector,
15    context::ContextTr,
16    inspector::JournalExt,
17    interpreter::{
18        CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter,
19        interpreter::EthInterpreter,
20    },
21};
22use revm_inspectors::transfer::TransferInspector;
23use std::sync::Arc;
24
25/// The [`revm::Inspector`] used when transacting in the evm
26#[derive(Clone, Debug, Default)]
27pub struct AnvilInspector {
28    /// Collects all traces
29    pub tracer: Option<TracingInspector>,
30    /// Collects all `console.sol` logs
31    pub log_collector: Option<LogCollector>,
32    /// Collects all internal ETH transfers as ERC20 transfer events.
33    pub transfer: Option<TransferInspector>,
34}
35
36impl AnvilInspector {
37    /// Called after the inspecting the evm
38    ///
39    /// This will log all `console.sol` logs
40    pub fn print_logs(&self) {
41        if let Some(collector) = &self.log_collector {
42            print_logs(&collector.logs);
43        }
44    }
45
46    /// Consumes the type and prints the traces.
47    pub fn into_print_traces(mut self, decoder: Arc<CallTraceDecoder>) {
48        if let Some(a) = self.tracer.take() {
49            print_traces(a, decoder);
50        }
51    }
52
53    /// Called after the inspecting the evm
54    /// This will log all traces
55    pub fn print_traces(&self, decoder: Arc<CallTraceDecoder>) {
56        if let Some(a) = self.tracer.clone() {
57            print_traces(a, decoder);
58        }
59    }
60
61    /// Configures the `Tracer` [`revm::Inspector`]
62    pub fn with_tracing(mut self) -> Self {
63        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().set_steps(false)));
64        self
65    }
66
67    /// Configures the `TracingInspector` [`revm::Inspector`]
68    pub fn with_tracing_config(mut self, config: TracingInspectorConfig) -> Self {
69        self.tracer = Some(TracingInspector::new(config));
70        self
71    }
72
73    /// Enables steps recording for `Tracer`.
74    pub fn with_steps_tracing(mut self) -> Self {
75        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
76        self
77    }
78
79    /// Configures the `Tracer` [`revm::Inspector`] with a log collector
80    pub fn with_log_collector(mut self) -> Self {
81        self.log_collector = Some(Default::default());
82        self
83    }
84
85    /// Configures the `Tracer` [`revm::Inspector`] with a transfer event collector
86    pub fn with_transfers(mut self) -> Self {
87        self.transfer = Some(TransferInspector::new(false).with_logs(true));
88        self
89    }
90
91    /// Configures the `Tracer` [`revm::Inspector`] with a trace printer
92    pub fn with_trace_printer(mut self) -> Self {
93        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
94        self
95    }
96}
97
98/// Prints the traces for the inspector
99///
100/// Caution: This blocks on call trace decoding
101///
102/// # Panics
103///
104/// If called outside tokio runtime
105fn print_traces(tracer: TracingInspector, decoder: Arc<CallTraceDecoder>) {
106    let arena = tokio::task::block_in_place(move || {
107        tokio::runtime::Handle::current().block_on(async move {
108            let mut arena = tracer.into_traces();
109            decoder.populate_traces(arena.nodes_mut()).await;
110            arena
111        })
112    });
113
114    let traces = SparsedTraceArena { arena, ignored: Default::default() };
115    node_info!("Traces:");
116    node_info!("{}", render_trace_arena_inner(&traces, false, true));
117}
118
119impl<CTX> Inspector<CTX, EthInterpreter> for AnvilInspector
120where
121    CTX: ContextTr<Journal: JournalExt>,
122{
123    fn initialize_interp(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
124        call_inspectors!([&mut self.tracer], |inspector| {
125            inspector.initialize_interp(interp, ecx);
126        });
127    }
128
129    fn step(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
130        call_inspectors!([&mut self.tracer], |inspector| {
131            inspector.step(interp, ecx);
132        });
133    }
134
135    fn step_end(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
136        call_inspectors!([&mut self.tracer], |inspector| {
137            inspector.step_end(interp, ecx);
138        });
139    }
140
141    #[allow(clippy::redundant_clone)]
142    fn log(&mut self, interp: &mut Interpreter, ecx: &mut CTX, log: Log) {
143        call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
144            inspector.log(interp, ecx, log.clone());
145        });
146    }
147
148    fn call(&mut self, ecx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
149        call_inspectors!(
150            #[ret]
151            [&mut self.tracer, &mut self.log_collector, &mut self.transfer],
152            |inspector| inspector.call(ecx, inputs).map(Some),
153        );
154        None
155    }
156
157    fn call_end(&mut self, ecx: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
158        if let Some(tracer) = &mut self.tracer {
159            tracer.call_end(ecx, inputs, outcome);
160        }
161    }
162
163    fn create(&mut self, ecx: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
164        call_inspectors!(
165            #[ret]
166            [&mut self.tracer, &mut self.transfer],
167            |inspector| inspector.create(ecx, inputs).map(Some),
168        );
169        None
170    }
171
172    fn create_end(&mut self, ecx: &mut CTX, inputs: &CreateInputs, outcome: &mut CreateOutcome) {
173        if let Some(tracer) = &mut self.tracer {
174            tracer.create_end(ecx, inputs, outcome);
175        }
176    }
177
178    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
179        call_inspectors!([&mut self.tracer, &mut self.transfer], |inspector| {
180            Inspector::<CTX, EthInterpreter>::selfdestruct(inspector, contract, target, value)
181        });
182    }
183}
184
185/// Prints all the logs
186pub fn print_logs(logs: &[Log]) {
187    for log in decode_console_logs(logs) {
188        tracing::info!(target: crate::logging::EVM_CONSOLE_LOG_TARGET, "{}", log);
189    }
190}