Skip to main content

ERC20 Decoder

Decode USDC Transfer events. Use case: Decode ERC20 token transfers with typed event data.
import {
  evmPortalSource,
  evmDecoder,
  commonAbis,
} from "@subsquid/pipes/evm";

async function main() {
  const source = evmPortalSource({
    portal: "https://portal.sqd.dev/datasets/ethereum-mainnet",
  });

  const decoder = evmDecoder({
    range: { from: 20000000, to: 20000100 },
    contracts: ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"], // USDC
    events: {
      transfer: commonAbis.erc20.events.Transfer,
    },
  });

  for await (const { data } of source.pipe(decoder)) {
    for (const t of data.transfer) {
      console.log({
        from: t.event.from,
        to: t.event.to,
        value: t.event.value.toString(),
        block: t.block.number,
        tx: t.rawEvent.transactionHash,
      });
    }
  }
}

void main();

Data Flow

Composite Decoder

Decode multiple event types simultaneously. Use case: Index ERC20 transfers and Uniswap swaps in one pipe.
import {
  evmPortalSource,
  evmDecoder,
  commonAbis,
} from "@subsquid/pipes/evm";
import * as uniswapAbi from "./abi/uniswap-v3-pool";

async function main() {
  const source = evmPortalSource({
    portal: "https://portal.sqd.dev/datasets/ethereum-mainnet",
  });

  const pipeline = source.pipeComposite({
    usdc: evmDecoder({
      range: { from: 20000000, to: 20000100 },
      contracts: ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"],
      events: {
        transfer: commonAbis.erc20.events.Transfer,
      },
    }),
    uniswap: evmDecoder({
      range: { from: 20000000, to: 20000100 },
      contracts: ["0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640"],
      events: {
        swap: uniswapAbi.events.Swap,
      },
    }),
  });

  for await (const { data } of pipeline) {
    console.log({
      usdcTransfers: data.usdc.transfer.length,
      uniswapSwaps: data.uniswap.swap.length,
    });
  }
}

void main();

Data Flow

Multiple Event Types

Decode Transfers and Approvals from same contract. Use case: Index multiple event types from one contract.
Use @subsquid/evm-typegen to generate typed ABIs instead of manually finding raw event signatures. This approach provides type safety and eliminates the need to look up topic0 hashes. See the Custom ABI section below.
import {
  evmPortalSource,
  evmDecoder,
  commonAbis,
} from "@subsquid/pipes/evm";

async function main() {
  const source = evmPortalSource({
    portal: "https://portal.sqd.dev/datasets/ethereum-mainnet",
  });

  const decoder = evmDecoder({
    range: { from: 20000000, to: 20000100 },
    contracts: ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"],
    events: {
      transfer: commonAbis.erc20.events.Transfer,
      approval: commonAbis.erc20.events.Approval,
    },
  });

  for await (const { data } of source.pipe(decoder)) {
    console.log({
      transfers: data.transfer.length,
      approvals: data.approval.length,
    });

    for (const t of data.transfer) {
      console.log(
        `Transfer: ${t.event.from} -> ${t.event.to}: ${t.event.value}`
      );
    }

    for (const a of data.approval) {
      console.log(
        `Approval: ${a.event.owner} approved ${a.event.spender}: ${a.event.value}`
      );
    }
  }
}

void main();

Custom ABI

Generate and use custom ABIs. Use case: Index events from contracts with custom ABIs.

Generate ABI

npx @subsquid/evm-typegen src/abi abi/MyContract.json

Use Generated ABI

import { evmPortalSource, evmDecoder } from "@subsquid/pipes/evm";
import * as myContractAbi from "./abi/MyContract";

async function main() {
  const source = evmPortalSource({
    portal: "https://portal.sqd.dev/datasets/ethereum-mainnet",
  });

const decoder = evmDecoder({
  range: { from: 20000000 },
  contracts: ["0x..."],
  events: {
    myEvent: myContractAbi.events.MyCustomEvent,
    anotherEvent: myContractAbi.events.AnotherEvent,
  },
});

for await (const { data } of source.pipe(decoder)) {
  console.log(data.myEvent);
  console.log(data.anotherEvent);
}
}

void main();