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_primitives::TempoAddressExt;
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 inputs.target_address.is_tip20() && !self.labels.contains_key(&inputs.target_address) {
29            let bytes = ctx
30                .db_mut()
31                .storage(inputs.target_address, tempo_precompiles::tip20::slots::NAME)
32                .unwrap_or_default()
33                .to_be_bytes::<32>();
34            let len = bytes[31] as usize / 2; // Last byte stores length * 2 for short strings
35            let name = if len == 0 {
36                "TIP20".to_string()
37            } else {
38                String::from_utf8_lossy(&bytes[..len]).to_string()
39            };
40            self.labels.insert(inputs.target_address, name);
41        }
42
43        None
44    }
45}