Ethereum - ERC20 Token Metadata

ERC20 Token Metadata Foundational Store

A specialized foundational store for tracking ERC20 token metadata on Ethereum and EVM-compatible chains. This store focuses specifically on metadata extraction and serving, working in conjunction with separate modules for transfer tracking.

Overview

The ERC20 Token Metadata foundational store provides efficient storage and retrieval of:

  • Token Metadata: Name, symbol, and decimals for ERC20 tokens

  • Metadata Events: Initialization and change events for token metadata

  • RPC-Enhanced Data: Complete metadata fetched via batch RPC calls for accuracy

Note: This foundational store is currently deployed on Ethereum Mainnet only for testing purposes. For deployments on other networks, please reach out on Discord.

Consuming Foundational Store Data

use substreams::store::FoundationalStore;
...
use substreams_ethereum::pb::eth::v2::Block;

#[substreams::handlers::map]
fn map_tokens_transfers(
    block: Block,
    token_metadata_store: FoundationalStore,
) -> Result<TokenTransfers, Error> {
    // ... extract transfers from block

    let response = token_metadata_store.get(&token_address);
    if response.response == ResponseCode::Found as i32 {
        let metadata = TokenMetadata::decode(response.value.unwrap().value.as_slice())?;
        // Use metadata.name, metadata.symbol, metadata.decimals to enrich transfer
    }

    // For multiple tokens: 
    let response = token_metadata_store.get_all(&token_addresses)
    // ...
}

Consumer Module (uses foundational store as input):

specVersion: v0.1.0
package:
  name: erc20_token_transfers_with_metadata
  version: v0.1.0

imports:
  token_metadata_store: [email protected]

modules:
  - name: map_tokens_transfers
    kind: map
    inputs:
      - source: sf.ethereum.type.v2.Block
      - foundational-store: [email protected]
    output:
      type: proto:erc20.metadata.v1.TokenTransfers

Complete Example: See the map_tokens_transfers implementation.

Data Model

Key Structure

The store uses token contract addresses as keys:

{token_contract_address} (bytes)

Value Schema

TokenMetadata (type.googleapis.com/evm.token.metadata.v1.TokenMetadata)

syntax = "proto3";

package evm.token.metadata.v1;

message TokenMetadata {
  bytes  address  = 1;
  string name     = 2;
  string symbol   = 3;
  int32  decimals = 4;
}

Implementation details

The foundational store processes ERC20 metadata through two mechanisms:

  1. MetadataInitialize Events: Direct extraction from event data

  2. MetadataChanges Events: Batch RPC calls to ensure accuracy

Each token address becomes a key, with the corresponding TokenMetadata protobuf message as the value.

Implementation

Creating Foundational Store Entries

#[substreams::handlers::map]
fn metadata_to_foundational_store(
    metadata_events: erc20_metadata::Events,
) -> Result<Entries, Error> {
    let mut entries: Vec<Entry> = Vec::new();

    for event in metadata_events.events {
        let token_metadata = TokenMetadata {
            address: event.token_address.clone(),
            name: event.name.clone(),
            symbol: event.symbol.clone(),
            decimals: event.decimals,
        };

        let mut buf = Vec::new();
        Message::encode(&token_metadata, &mut buf).unwrap();

        let any = Any {
            type_url: "type.googleapis.com/evm.token.metadata.v1.TokenMetadata".to_string(),
            value: buf,
        };

        let entry = Entry {
            key: init.address,
            value: Some(any),
        };

        entries.push(entry);

    Ok(Entries { entries })
}

Substreams Manifest Configuration

Producer Module (creates foundational store entries):

specVersion: v0.1.0
package:
  name: evm_token_metadata_foundational_store
  version: v0.1.0

imports:
  erc20_metadata: https://github.com/pinax-network/substreams-evm-tokens/releases/download/erc20-metadata-v0.2.1/evm-erc20-metadata-v0.2.1.spkg

modules:
  - name: metadata_to_foundational_store
    kind: map
    inputs:
      - map: erc20_metadata:map_events
    output:
      type: proto:sf.substreams.foundational_store.v1.Entries

Complete Example: See the metadata_to_foundational_store implementation.

Last updated

Was this helpful?