foundry_evm/inspectors/
logs.rsuse alloy_primitives::{Bytes, Log};
use alloy_sol_types::{SolEvent, SolInterface, SolValue};
use foundry_common::{fmt::ConsoleFmt, ErrorExt};
use foundry_evm_core::{
abi::{patch_hh_console_selector, Console, HardhatConsole},
constants::HARDHAT_CONSOLE_ADDRESS,
InspectorExt,
};
use revm::{
interpreter::{
CallInputs, CallOutcome, Gas, InstructionResult, Interpreter, InterpreterResult,
},
Database, EvmContext, Inspector,
};
#[derive(Clone, Debug, Default)]
pub struct LogCollector {
pub logs: Vec<Log>,
}
impl LogCollector {
fn hardhat_log(&mut self, mut input: Vec<u8>) -> (InstructionResult, Bytes) {
patch_hh_console_selector(&mut input);
let decoded = match HardhatConsole::HardhatConsoleCalls::abi_decode(&input, false) {
Ok(inner) => inner,
Err(err) => return (InstructionResult::Revert, err.abi_encode_revert()),
};
self.logs.push(convert_hh_log_to_event(decoded));
(InstructionResult::Continue, Bytes::new())
}
}
impl<DB: Database> Inspector<DB> for LogCollector {
fn log(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext<DB>, log: &Log) {
self.logs.push(log.clone());
}
fn call(
&mut self,
_context: &mut EvmContext<DB>,
inputs: &mut CallInputs,
) -> Option<CallOutcome> {
if inputs.target_address == HARDHAT_CONSOLE_ADDRESS {
let (res, out) = self.hardhat_log(inputs.input.to_vec());
if res != InstructionResult::Continue {
return Some(CallOutcome {
result: InterpreterResult {
result: res,
output: out,
gas: Gas::new(inputs.gas_limit),
},
memory_offset: inputs.return_memory_offset.clone(),
});
}
}
None
}
}
impl InspectorExt for LogCollector {
fn console_log(&mut self, input: String) {
self.logs.push(Log::new_unchecked(
HARDHAT_CONSOLE_ADDRESS,
vec![Console::log::SIGNATURE_HASH],
input.abi_encode().into(),
));
}
}
fn convert_hh_log_to_event(call: HardhatConsole::HardhatConsoleCalls) -> Log {
let fmt = call.fmt(Default::default());
Log::new_unchecked(
HARDHAT_CONSOLE_ADDRESS,
vec![Console::log::SIGNATURE_HASH],
fmt.abi_encode().into(),
)
}