foundry_evm/inspectors/
logs.rs
1use alloy_primitives::Log;
2use alloy_sol_types::{SolEvent, SolInterface, SolValue};
3use foundry_common::{fmt::ConsoleFmt, ErrorExt};
4use foundry_evm_core::{abi::console, constants::HARDHAT_CONSOLE_ADDRESS, InspectorExt};
5use revm::{
6 interpreter::{
7 CallInputs, CallOutcome, Gas, InstructionResult, Interpreter, InterpreterResult,
8 },
9 Database, EvmContext, Inspector,
10};
11
12#[derive(Clone, Debug, Default)]
16pub struct LogCollector {
17 pub logs: Vec<Log>,
19}
20
21impl LogCollector {
22 #[cold]
23 fn do_hardhat_log(&mut self, inputs: &CallInputs) -> Option<CallOutcome> {
24 if let Err(err) = self.hardhat_log(&inputs.input) {
25 let result = InstructionResult::Revert;
26 let output = err.abi_encode_revert();
27 return Some(CallOutcome {
28 result: InterpreterResult { result, output, gas: Gas::new(inputs.gas_limit) },
29 memory_offset: inputs.return_memory_offset.clone(),
30 })
31 }
32 None
33 }
34
35 fn hardhat_log(&mut self, data: &[u8]) -> alloy_sol_types::Result<()> {
36 let decoded = console::hh::ConsoleCalls::abi_decode(data, false)?;
37 self.logs.push(hh_to_ds(&decoded));
38 Ok(())
39 }
40}
41
42impl<DB: Database> Inspector<DB> for LogCollector {
43 fn log(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext<DB>, log: &Log) {
44 self.logs.push(log.clone());
45 }
46
47 fn call(
48 &mut self,
49 _context: &mut EvmContext<DB>,
50 inputs: &mut CallInputs,
51 ) -> Option<CallOutcome> {
52 if inputs.target_address == HARDHAT_CONSOLE_ADDRESS {
53 return self.do_hardhat_log(inputs);
54 }
55 None
56 }
57}
58
59impl InspectorExt for LogCollector {
60 fn console_log(&mut self, msg: &str) {
61 self.logs.push(new_console_log(msg));
62 }
63}
64
65fn hh_to_ds(call: &console::hh::ConsoleCalls) -> Log {
67 let msg = call.fmt(Default::default());
69 new_console_log(&msg)
70}
71
72fn new_console_log(msg: &str) -> Log {
74 Log::new_unchecked(
75 HARDHAT_CONSOLE_ADDRESS,
76 vec![console::ds::log::SIGNATURE_HASH],
77 msg.abi_encode().into(),
78 )
79}