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