Skip to main content

foundry_evm/inspectors/
custom_printer.rs

1//! Custom print inspector, it has step level information of execution.
2//! It is a great tool if some debugging is needed.
3
4use foundry_common::sh_println;
5use revm::{
6    Inspector,
7    bytecode::opcode::OpCode,
8    context::{ContextTr, JournalTr},
9    inspector::inspectors::GasInspector,
10    interpreter::{
11        CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter,
12        interpreter_types::{Jumps, MemoryTr},
13    },
14    primitives::{Address, U256},
15};
16
17/// Custom print [Inspector], it has step level information of execution.
18///
19/// It is a great tool if some debugging is needed.
20#[derive(Clone, Debug, Default)]
21pub struct CustomPrintTracer {
22    gas_inspector: GasInspector,
23}
24
25impl<CTX: ContextTr> Inspector<CTX> for CustomPrintTracer {
26    fn initialize_interp(&mut self, interp: &mut Interpreter, _context: &mut CTX) {
27        self.gas_inspector.initialize_interp(&interp.gas);
28    }
29
30    // get opcode by calling `interp.contract.opcode(interp.program_counter())`.
31    // all other information can be obtained from interp.
32    fn step(&mut self, interp: &mut Interpreter, context: &mut CTX) {
33        let opcode = interp.bytecode.opcode();
34        let name = OpCode::name_by_op(opcode);
35
36        let gas_remaining = self.gas_inspector.gas_remaining();
37
38        let memory_size = interp.memory.size();
39
40        let _ = sh_println!(
41            "depth:{}, PC:{}, gas:{:#x}({}), OPCODE: {:?}({:?})  refund:{:#x}({}) Stack:{:?}, Data size:{}",
42            context.journal().depth(),
43            interp.bytecode.pc(),
44            gas_remaining,
45            gas_remaining,
46            name,
47            opcode,
48            interp.gas.refunded(),
49            interp.gas.refunded(),
50            interp.stack.data(),
51            memory_size,
52        );
53
54        self.gas_inspector.step(&interp.gas);
55    }
56
57    fn step_end(&mut self, interpreter: &mut Interpreter, _context: &mut CTX) {
58        self.gas_inspector.step_end(&interpreter.gas);
59    }
60
61    fn call_end(&mut self, _context: &mut CTX, _inputs: &CallInputs, outcome: &mut CallOutcome) {
62        self.gas_inspector.call_end(outcome)
63    }
64
65    fn create_end(
66        &mut self,
67        _context: &mut CTX,
68        _inputs: &CreateInputs,
69        outcome: &mut CreateOutcome,
70    ) {
71        self.gas_inspector.create_end(outcome)
72    }
73
74    fn call(&mut self, _context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
75        let _ = sh_println!(
76            "SM Address: {:?}, caller:{:?},target:{:?} is_static:{:?}, transfer:{:?}, input_size:{:?}",
77            inputs.bytecode_address,
78            inputs.caller,
79            inputs.target_address,
80            inputs.is_static,
81            inputs.value,
82            inputs.input.len(),
83        );
84        None
85    }
86
87    fn create(&mut self, _context: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
88        let _ = sh_println!(
89            "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}",
90            inputs.caller(),
91            inputs.scheme(),
92            inputs.value(),
93            inputs.init_code(),
94            inputs.gas_limit()
95        );
96        None
97    }
98
99    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
100        let _ = sh_println!(
101            "SELFDESTRUCT: contract: {:?}, refund target: {:?}, value {:?}",
102            contract,
103            target,
104            value
105        );
106    }
107}