Skip to main content

foundry_evm/inspectors/
tempo_labels.rs

1use alloy_primitives::map::AddressMap;
2use foundry_evm_core::backend::DatabaseError;
3use revm::{
4    Database, Inspector,
5    context::ContextTr,
6    inspector::JournalExt,
7    interpreter::{CallInputs, CallOutcome, interpreter::EthInterpreter},
8};
9use tempo_precompiles::tip20::is_tip20_prefix;
10
11/// Inspector that labels TIP20 token precompile addresses with their on-chain names.
12///
13/// During execution, when a call targets a TIP20 address, this inspector reads the token's
14/// name from storage and records the `address -> name` mapping. These labels are later merged
15/// into trace output for better readability.
16#[derive(Default, Clone, Debug)]
17pub struct TempoLabels {
18    pub(crate) labels: AddressMap<String>,
19}
20
21impl<CTX, D> Inspector<CTX, EthInterpreter> for TempoLabels
22where
23    D: Database<Error = DatabaseError>,
24    CTX: ContextTr<Db = D>,
25    CTX::Journal: JournalExt,
26{
27    fn call(&mut self, ctx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
28        if is_tip20_prefix(inputs.target_address)
29            && !self.labels.contains_key(&inputs.target_address)
30        {
31            let bytes = ctx
32                .db_mut()
33                .storage(inputs.target_address, tempo_precompiles::tip20::slots::NAME)
34                .unwrap_or_default()
35                .to_be_bytes::<32>();
36            let len = bytes[31] as usize / 2; // Last byte stores length * 2 for short strings
37            let name = if len == 0 {
38                "TIP20".to_string()
39            } else {
40                String::from_utf8_lossy(&bytes[..len]).to_string()
41            };
42            self.labels.insert(inputs.target_address, name);
43        }
44
45        None
46    }
47}