foundry_evm/inspectors/
logs.rs1use alloy_primitives::Log;
2use alloy_sol_types::{SolEvent, SolInterface, SolValue};
3use foundry_common::{ErrorExt, fmt::ConsoleFmt, sh_println};
4use foundry_evm_core::{
5 InspectorExt, abi::console, constants::HARDHAT_CONSOLE_ADDRESS, decode::decode_console_log,
6};
7use revm::{
8 Inspector,
9 context::ContextTr,
10 interpreter::{
11 CallInputs, CallOutcome, Gas, InstructionResult, InterpreterResult,
12 interpreter::EthInterpreter,
13 },
14};
15
16#[derive(Clone, Debug)]
20pub enum LogCollector {
21 Capture { logs: Vec<Log> },
23 LiveLogs,
25}
26
27impl LogCollector {
28 pub fn into_captured_logs(self) -> Option<Vec<Log>> {
29 match self {
30 Self::Capture { logs } => Some(logs),
31 Self::LiveLogs => None,
32 }
33 }
34
35 #[cold]
36 fn do_hardhat_log<CTX>(&mut self, context: &mut CTX, inputs: &CallInputs) -> Option<CallOutcome>
37 where
38 CTX: ContextTr,
39 {
40 if let Err(err) = self.hardhat_log(&inputs.input.bytes(context)) {
41 let result = InstructionResult::Revert;
42 let output = err.abi_encode_revert();
43 return Some(CallOutcome {
44 result: InterpreterResult { result, output, gas: Gas::new(inputs.gas_limit) },
45 memory_offset: inputs.return_memory_offset.clone(),
46 was_precompile_called: true,
47 precompile_call_logs: vec![],
48 });
49 }
50 None
51 }
52
53 fn hardhat_log(&mut self, data: &[u8]) -> alloy_sol_types::Result<()> {
54 let decoded = console::hh::ConsoleCalls::abi_decode(data)?;
55 self.push_msg(&decoded.fmt(Default::default()));
56 Ok(())
57 }
58
59 fn push_raw_log(&mut self, log: Log) {
60 match self {
61 Self::Capture { logs } => logs.push(log),
62 Self::LiveLogs => {
63 if let Some(msg) = decode_console_log(&log) {
64 sh_println!("{msg}").expect("fail printing to stdout");
65 } else {
66 sh_println!("console.log({:?}, {})", log.data.topics(), log.data.data)
69 .expect("fail printing to stdout");
70 }
71 }
72 }
73 }
74
75 fn push_msg(&mut self, msg: &str) {
76 match self {
77 Self::Capture { logs } => logs.push(new_console_log(msg)),
78 Self::LiveLogs => sh_println!("{msg}").expect("fail printing to stdout"),
79 }
80 }
81}
82
83impl<CTX> Inspector<CTX, EthInterpreter> for LogCollector
84where
85 CTX: ContextTr,
86{
87 fn log(&mut self, _context: &mut CTX, log: Log) {
88 self.push_raw_log(log);
89 }
90
91 fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
92 if inputs.target_address == HARDHAT_CONSOLE_ADDRESS {
93 return self.do_hardhat_log(context, inputs);
94 }
95 None
96 }
97}
98
99impl InspectorExt for LogCollector {
100 fn console_log(&mut self, msg: &str) {
101 self.push_msg(msg);
102 }
103}
104
105fn new_console_log(msg: &str) -> Log {
107 Log::new_unchecked(
108 HARDHAT_CONSOLE_ADDRESS,
109 vec![console::ds::log::SIGNATURE_HASH],
110 msg.abi_encode().into(),
111 )
112}