1use 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, CallTraceNode, SparsedTraceArena, TracingInspectorConfig,
11 render_trace_arena_inner,
12 },
13};
14use revm::{
15 Inspector,
16 context::ContextTr,
17 inspector::JournalExt,
18 interpreter::{
19 CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter,
20 interpreter::EthInterpreter,
21 },
22};
23use revm_inspectors::transfer::TransferInspector;
24use std::sync::Arc;
25
26#[derive(Clone, Debug, Default)]
28pub struct AnvilInspector {
29 pub tracer: Option<TracingInspector>,
31 pub log_collector: Option<LogCollector>,
33 pub transfer: Option<TransferInspector>,
35}
36
37#[derive(Clone, Debug)]
39pub struct InspectorTxConfig {
40 pub print_traces: bool,
42 pub print_logs: bool,
44 pub enable_steps_tracing: bool,
46 pub call_trace_decoder: Arc<CallTraceDecoder>,
48}
49
50impl AnvilInspector {
51 pub fn finish_transaction(&mut self, config: &InspectorTxConfig) -> Vec<CallTraceNode> {
55 if config.print_traces {
57 self.print_traces(config.call_trace_decoder.clone());
58 }
59 self.print_logs();
60
61 let traces = self.tracer.take().map(|t| t.into_traces().into_nodes()).unwrap_or_default();
62
63 let tracing_config = if config.enable_steps_tracing {
65 TracingInspectorConfig::all().with_state_diffs()
66 } else {
67 TracingInspectorConfig::all().set_steps(false)
68 };
69 self.tracer = Some(TracingInspector::new(tracing_config));
70
71 if config.print_logs {
73 self.log_collector = Some(LogCollector::Capture { logs: Vec::new() });
74 }
75
76 traces
77 }
78
79 pub fn print_logs(&self) {
83 if let Some(LogCollector::Capture { logs }) = &self.log_collector {
84 print_logs(logs);
85 }
86 }
87
88 pub fn into_print_traces(mut self, decoder: Arc<CallTraceDecoder>) {
90 if let Some(a) = self.tracer.take() {
91 print_traces(a, decoder);
92 }
93 }
94
95 pub fn print_traces(&self, decoder: Arc<CallTraceDecoder>) {
98 if let Some(a) = self.tracer.clone() {
99 print_traces(a, decoder);
100 }
101 }
102
103 pub fn with_tracing(mut self) -> Self {
105 self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().set_steps(false)));
106 self
107 }
108
109 pub fn with_tracing_config(mut self, config: TracingInspectorConfig) -> Self {
111 self.tracer = Some(TracingInspector::new(config));
112 self
113 }
114
115 pub fn with_steps_tracing(mut self) -> Self {
117 self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
118 self
119 }
120
121 pub fn with_log_collector(mut self) -> Self {
123 self.log_collector = Some(LogCollector::Capture { logs: Vec::new() });
124 self
125 }
126
127 pub fn with_transfers(mut self) -> Self {
129 self.transfer = Some(TransferInspector::new(false).with_logs(true));
130 self
131 }
132
133 pub fn with_trace_printer(mut self) -> Self {
135 self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
136 self
137 }
138}
139
140fn print_traces(tracer: TracingInspector, decoder: Arc<CallTraceDecoder>) {
148 let arena = tokio::task::block_in_place(move || {
149 tokio::runtime::Handle::current().block_on(async move {
150 let mut arena = tracer.into_traces();
151 decoder.populate_traces(arena.nodes_mut()).await;
152 arena
153 })
154 });
155
156 let traces = SparsedTraceArena { arena, ignored: Default::default() };
157 let trace = render_trace_arena_inner(&traces, false, true);
158 node_info!(Traces = %format!("\n{}", trace));
159}
160
161impl<CTX> Inspector<CTX, EthInterpreter> for AnvilInspector
162where
163 CTX: ContextTr<Journal: JournalExt>,
164{
165 fn initialize_interp(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
166 call_inspectors!([&mut self.tracer], |inspector| {
167 inspector.initialize_interp(interp, ecx);
168 });
169 }
170
171 fn step(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
172 call_inspectors!([&mut self.tracer], |inspector| {
173 inspector.step(interp, ecx);
174 });
175 }
176
177 fn step_end(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
178 call_inspectors!([&mut self.tracer], |inspector| {
179 inspector.step_end(interp, ecx);
180 });
181 }
182
183 #[allow(clippy::redundant_clone)]
184 fn log(&mut self, ecx: &mut CTX, log: Log) {
185 call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
186 inspector.log(ecx, log.clone());
187 });
188 }
189
190 #[allow(clippy::redundant_clone)]
191 fn log_full(&mut self, interp: &mut Interpreter, ecx: &mut CTX, log: Log) {
192 call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
193 inspector.log_full(interp, ecx, log.clone());
194 });
195 }
196
197 fn call(&mut self, ecx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
198 call_inspectors!(
199 #[ret]
200 [&mut self.tracer, &mut self.log_collector, &mut self.transfer],
201 |inspector| inspector.call(ecx, inputs).map(Some),
202 );
203 None
204 }
205
206 fn call_end(&mut self, ecx: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
207 if let Some(tracer) = &mut self.tracer {
208 tracer.call_end(ecx, inputs, outcome);
209 }
210 }
211
212 fn create(&mut self, ecx: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
213 call_inspectors!(
214 #[ret]
215 [&mut self.tracer, &mut self.transfer],
216 |inspector| inspector.create(ecx, inputs).map(Some),
217 );
218 None
219 }
220
221 fn create_end(&mut self, ecx: &mut CTX, inputs: &CreateInputs, outcome: &mut CreateOutcome) {
222 if let Some(tracer) = &mut self.tracer {
223 tracer.create_end(ecx, inputs, outcome);
224 }
225 }
226
227 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
228 call_inspectors!([&mut self.tracer, &mut self.transfer], |inspector| {
229 Inspector::<CTX, EthInterpreter>::selfdestruct(inspector, contract, target, value)
230 });
231 }
232}
233
234pub fn print_logs(logs: &[Log]) {
236 for log in decode_console_logs(logs) {
237 tracing::info!(target: crate::logging::EVM_CONSOLE_LOG_TARGET, "{}", log);
238 }
239}