Skip to main content
Parse and decode ERC-20 Transfer and Approval events from raw log data.
import { Abi } from '@tevm/voltaire/Abi';
import { Hex } from '@tevm/voltaire/Hex';
import { EventLog } from '@tevm/voltaire/EventLog';

// Define ERC-20 event ABI
const erc20Abi = Abi([
  {
    type: "event",
    name: "Transfer",
    inputs: [
      { type: "address", name: "from", indexed: true },
      { type: "address", name: "to", indexed: true },
      { type: "uint256", name: "value", indexed: false }
    ]
  },
  {
    type: "event",
    name: "Approval",
    inputs: [
      { type: "address", name: "owner", indexed: true },
      { type: "address", name: "spender", indexed: true },
      { type: "uint256", name: "value", indexed: false }
    ]
  }
]);

// Raw log from eth_getLogs
// Transfer event: 100 tokens from 0x742d... to 0xa1b2...
const rawLog = {
  data: "0x0000000000000000000000000000000000000000000000000000000000000064",
  topics: [
    // topic0: keccak256("Transfer(address,address,uint256)")
    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    // topic1: indexed 'from' address (padded to 32 bytes)
    "0x000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f251e3",
    // topic2: indexed 'to' address (padded to 32 bytes)
    "0x000000000000000000000000a1b2c3d4e5f6789012345678901234567890abcd"
  ]
};

// Decode the log using the ABI
const decoded = Abi.parseLogs(erc20Abi, [rawLog]);

// Result: [{ event: "Transfer", params: { from, to, value: 100n } }]
const { event, params } = decoded[0];
const { from, to, value } = params;

How Event Logs Work

  • topic0: Event signature hash (keccak256 of event signature)
  • topic1-3: Indexed parameters (max 3, each 32 bytes)
  • data: Non-indexed parameters (ABI encoded)
// Get event selector (topic0)
const transferEvent = erc20Abi.getEvent("Transfer");
const selector = Abi.Event.getSelector(transferEvent);
// 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

// Match logs by comparing topic0
function isTransferLog(log) {
  const topic0 = Hex.toBytes(log.topics[0]);
  for (let i = 0; i < 32; i++) {
    if (topic0[i] !== selector[i]) return false;
  }
  return true;
}
This is a fully executable example. View the complete source with test assertions at examples/contracts/decode-logs.ts.