Skip to main content

Technical Details

This page covers technical specifications for working with ERC token data on Hedera, including account identifiers, event signatures, and data formats.

Data Pipeline Overview​

The Hedera ERC Indexer processes token data through a six-stage pipeline:

Key Pipeline Stages​

  1. Discovery: Scans Hedera mirror node for contracts emitting Transfer events
  2. Extraction: Retrieves token metadata (name, symbol, decimals) via JSON-RPC
  3. Balance Fetching: Fetches current balances via RPC balanceOf() calls
  4. Transfer History: Extracts and indexes Transfer events to tables
  5. NFT Tracking: For ERC-721 tokens, tracks individual NFT ownership and metadata
  6. Storage: Persists data to erc schema tables exposed via Hasura GraphQL

Account Identifiers and Aliases​

Understanding Hedera Account Systems​

Every entity on Hedera has a unique account ID (e.g., 0.0.12345), but accounts can be referenced through multiple identifiers:

1. Account Number (Native Hedera Format)​

  • Format: <shard>.<realm>.<account_number> (e.g., 0.0.924713)
  • Usage: Native Hedera transactions and queries
  • In Topics: Stored as 1-8 bytes depending on size

2. Account-num Alias (Long-zero Address)​

  • Format: 20-byte EVM address with 12 leading zeros
  • Example: 0x00000000000000000000000000000e1c29 (Account 924713)
  • Purpose: Makes every Hedera account EVM-compatible
  • Resolution: Direct calculation from bytes (no lookup needed)

3. ECDSA EVM Alias​

  • Format: Standard 20-byte Ethereum address
  • Example: 0x6d9f1a927cbcb5e2c28d13ca735bc6d6131406da
  • Purpose: Allows MetaMask and EVM wallets to interact with Hedera
  • Resolution: Requires lookup to map to Hedera account ID

Hollow Accounts and Auto-Creation​

When an EVM address receives tokens or HBAR before having a Hedera account:

  1. Hollow Account Creation: System automatically creates an incomplete account
  2. Properties: Has account ID and alias, but no key (can receive but not send)
  3. Completion: Becomes full account when used as fee payer with proper signature
  4. Important: Every hollow account still has a unique Hedera ID

Event Detection & Signatures​

Transfer Event​

All ERC-20 and ERC-721 tokens emit a standardized Transfer event:

event Transfer(address indexed from, address indexed to, uint256 value_or_tokenId)

Event Signature Hash:

0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

This signature is the keccak256 hash of the event declaration and is always the same for all ERC Transfer events.

Approval Event​

event Approval(address indexed owner, address indexed spender, uint256 value)

Event Signature Hash:

0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925

ERC-1400 Events​

Security tokens use partition-based events for transfers:

EventSignature Hash
IssuedByPartition0x5af1c8f424b104b6ba4e3c0885f2ed9fef04a9b1ea39cd9ed362432105c0791a
TransferByPartition0xff4e9a26af4eb73b8bacfaa4abd4fea03d9448e7b912dc5ff4019048875aa2d4
RedeemedByPartition0xa4f62471c9bdf88115b97203943c74c59b655913ee5ee592706d84ef53fb6be2

Token Type Detection​

The indexer distinguishes token types by analyzing event structure:

ERC-20 Detection​

  • Characteristic: topic3 IS NULL
  • Reason: Transfer value is stored in the event's data section
  • Structure: Transfer(from, to, amount) - amount goes in data section

ERC-721 Detection​

  • Characteristic: topic3 IS NOT NULL
  • Reason: Unique tokenId is indexed and gets its own topic slot
  • Structure: Transfer(from, to, tokenId) - tokenId becomes topic3

ERC-1400 Detection​

  • Method: RPC probing via isIssuable() and isControllable()
  • Selectors: 0x2f1cae85 (isIssuable), 0x4c783bf5 (isControllable)
  • Characteristic: Both methods must return true for positive detection
  • Note: ERC-165 supportsInterface is NOT used (returns false for valid ERC-1400 tokens)

Standard ERC Methods​

ERC-20, ERC-721, and ERC-1400 tokens implement standard methods that the indexer queries:

Common Methods (All Standards)​

function name() external view returns (string memory);
function symbol() external view returns (string memory);

ERC-20 Specific Methods​

function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);

ERC-721 Specific Methods​

function totalSupply() external view returns (uint256); // Optional
function ownerOf(uint256 tokenId) external view returns (address);
function tokenURI(uint256 tokenId) external view returns (string memory);

Reliability Scoring​

The indexer calculates quality scores based on metadata completeness to help identify well-formed contracts.

Scoring Methodology​

The metadata_reliability_score field indicates how many standard methods returned valid data:

ERC-20 Tokens (4 fields)​

Fields checked: name, symbol, decimals, totalSupply

ScoreFields ExtractedMeaning
1.004 of 4Fully compliant ERC-20
0.753 of 4Mostly compliant
0.502 of 4Partial implementation
0.251 of 4Minimal implementation
0.000 of 4No metadata available

ERC-721 NFTs (3 fields)​

Fields checked: name, symbol, totalSupply (decimals not applicable)

ScoreFields ExtractedMeaning
1.003 of 3Fully compliant ERC-721
0.672 of 3Mostly compliant
0.331 of 3Minimal implementation
0.000 of 3No metadata available

ERC-1400 Security Tokens (4 fields)​

Fields checked: name, symbol, decimals, totalSupply (same as ERC-20)

ScoreFields ExtractedMeaning
1.004 of 4Fully compliant ERC-1400
0.753 of 4Mostly compliant
0.502 of 4Partial implementation
0.251 of 4Minimal implementation
0.000 of 4No metadata available

Unknown/Failed Deployments​

For contracts that couldn't be identified as ERC-20 or ERC-721:

ScoreFields ExtractedMeaning
1.00name AND symbolBasic token interface
0.50name OR symbolPartial interface
0.00NeitherNo standard interface

Using Reliability Scores​

Filter for high-quality tokens in your queries:

query ReliableTokens {
erc_token(
where: {
metadata_reliability_score: { _gte: 0.75 }
contract_type: { _in: ["ERC_20", "ERC_721"] }
}
) {
name
symbol
metadata_reliability_score
}
}

Data Encoding Formats​

Address Encoding​

Addresses in the GraphQL API are always returned as strings in hex format with 0x prefix:

  • EVM Addresses: 0x + 40 hex characters
  • Example: 0x00000000000000000000000000000000002cc823

Numeric Values​

Large numbers (balances, token supplies) are returned as strings to preserve precision:

  • Format: Decimal string representation
  • Example: "1000000000000000000" (1 token with 18 decimals)
  • Max Value: Full uint256 range (2^256 - 1)

Timestamps​

All timestamps use nanoseconds since epoch (Hedera format):

Field TypeExampleDescription
created_timestamp1698765432100000000Entity creation time
balance_timestamp1698765432100000000Last balance update
processing_timestamp1698765432100000000Record processing time
transfers_indexed_timestamp1698765432100000000Last transfer indexed
consensus_timestamp1698765432100000000Transaction time

Working with Token Balances​

ERC-20 Balances​

Balances are stored in the smallest unit (wei):

// Convert balance to human-readable format
function formatBalance(balance, decimals) {
return (BigInt(balance) / BigInt(10 ** decimals)).toString();
}

// Example: 1000000000000000000 with 18 decimals = 1 token

ERC-721 Balances​

For NFTs, the balance represents the count of NFTs owned:

query NFTCount($accountId: bigint!, $tokenId: bigint!) {
erc_token_account(where: { account_id: { _eq: $accountId }, token_id: { _eq: $tokenId } }) {
balance # This is the NFT count, not wei
}
}

Balance Fetching​

Balances are fetched via RPC balanceOf() calls rather than calculated from Transfer events. This ensures accuracy for:

  • Standard ERC-20: Exact match with on-chain state
  • Rebasing tokens (aTokens): Accurate despite continuous interest accrual without Transfer events
  • Debt tokens: Accurate despite interest changes
  • ERC-1400: Standard balanceOf() works for most implementations
note

Some ERC-1400 security tokens use partition-only balance storage, where balanceOf() returns 0 and balances are tracked per-partition via balanceOfByPartition(). These tokens will show zero balances in the erc_token_account table even when holders have positions. This is a limitation of the standard balanceOf() approach.

Process​

  1. Account Discovery: Extract unique addresses from Transfer event topics
  2. Address Resolution: Resolve EVM addresses to Hedera account IDs (two-pass strategy)
  3. RPC Batch Calls: Fetch current balances via balanceOf(address) method
  4. Storage: Persist balances to erc_token_account table

Why RPC Instead of Event Aggregation?​

Event-based balance calculation (summing Transfer amounts) fails for:

  • Rebasing tokens: Balance changes without Transfer events (e.g., Aave aTokens accrue interest continuously)
  • Debt tokens: Interest accrues without events
  • Fee-on-transfer tokens: Actual received amount differs from event amount

RPC balanceOf() queries the contract's current state directly, providing exact balances for all token types.