use alloc::format;
use alloy_primitives::{hex, map::HashMap, Selector};
use alloy_rpc_types_trace::geth::FourByteFrame;
use revm::{
context::{ContextTr, LocalContextTr},
interpreter::{CallInput, CallInputs, CallOutcome},
Inspector,
};
#[derive(Clone, Debug, Default)]
pub struct FourByteInspector {
inner: HashMap<(Selector, usize), u64>,
}
impl FourByteInspector {
pub const fn inner(&self) -> &HashMap<(Selector, usize), u64> {
&self.inner
}
}
impl<CTX: ContextTr> Inspector<CTX> for FourByteInspector {
fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
if inputs.input.len() >= 4 {
let r;
let input_bytes = match &inputs.input {
CallInput::SharedBuffer(range) => {
match context.local().shared_memory_buffer_slice(range.clone()) {
Some(slice) => {
r = slice;
&*r
}
None => &[],
}
}
CallInput::Bytes(bytes) => bytes.as_ref(),
};
let selector =
Selector::try_from(&input_bytes[..4]).expect("input is at least 4 bytes");
let calldata_size = input_bytes[4..].len();
*self.inner.entry((selector, calldata_size)).or_default() += 1;
}
None
}
}
impl From<FourByteInspector> for FourByteFrame {
fn from(value: FourByteInspector) -> Self {
Self::from(&value)
}
}
impl From<&FourByteInspector> for FourByteFrame {
fn from(value: &FourByteInspector) -> Self {
Self(
value
.inner
.iter()
.map(|((selector, calldata_size), count)| {
let key = format!("0x{}-{}", hex::encode(selector), *calldata_size);
(key, *count)
})
.collect(),
)
}
}