Skip to main content
Here’s an example of how SDK packages can be combined into a working indexer (called squid). This page goes through all the technical details to make the squid architecture easier to understand. If you would like to get to a working indexer ASAP, bootstrap from a template.

USDT transfers API

Pre-requisites: NodeJS 20.x or newer, Docker.
Suppose the task is to track transfers of USDT on Ethereum, then save the resulting data to PostgreSQL and serve it as a GraphQL API. From this description we can immediately put together a list of packages:
  • @subsquid/evm-processor - for retrieving Ethereum data
  • the triad of @subsquid/typeorm-store, @subsquid/typeorm-codegen and @subsquid/typeorm-migration - for saving data to PostgreSQL
We also assume the following choice of optional packages:
  • @subsquid/evm-typegen - for decoding Ethereum data and useful constants such as event topic0 values
  • @subsquid/evm-abi - as a peer dependency for the code generated by @subsquid/evm-typegen
  • @subsquid/graphql-server / OpenReader
To make the indexer, follow these steps:
1

Create a new folder and initialise a new project

  • create package.json
    npm init
    
  • add .gitignore
    .gitignore
    node_modules
    lib
    
2

Install the packages

npm i dotenv typeorm @subsquid/evm-processor @subsquid/typeorm-store @subsquid/typeorm-migration @subsquid/graphql-server @subsquid/evm-abi
npm i typescript @subsquid/typeorm-codegen @subsquid/evm-typegen --save-dev
3

Add a minimal `tsconfig.json`

tsconfig.json
{
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "lib",
    "module": "commonjs",
    "target": "es2020",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
4

Define the schema for both the database and the core GraphQL API

Define the schema for both the database and the core GraphQL API in schema.graphql:
schema.graphql
type Transfer @entity {
  id: ID!
  from: String! @index
  to: String! @index
  value: BigInt!
}
5

Generate TypeORM classes based on the schema

npx squid-typeorm-codegen
The TypeORM classes are now available at src/model/index.ts.
6

Prepare the database

  • create .env and docker-compose.yaml files
    .env
    DB_NAME=squid
    DB_PORT=23798
    RPC_ETH_HTTP=https://rpc.ankr.com/eth
    
    docker-compose.yaml
    services:
      db:
        image: postgres:15
        environment:
          POSTGRES_DB: "${DB_NAME}"
          POSTGRES_PASSWORD: postgres
        ports:
          - "${DB_PORT}:5432"
    
  • start the database container
    docker compose up -d
    
  • compile the TypeORM classes
    npx tsc
    
  • generate the migration file
    npx squid-typeorm-migration generate
    
  • apply the migration with
    npx squid-typeorm-migration apply
    
7

Generate utility classes for decoding USDT contract data

Generate utility classes for decoding USDT contract data based on its ABI.
  • Create an ./abi folder:
    mkdir abi
    
  • Find the ABI at the “Contract” tab of the contract page on Etherscan. Scroll down a bit:
  • Copy the ABI, then paste to a new file at ./abi/usdt.json.
  • Run the utility classes generator:
    npx squid-evm-typegen src/abi ./abi/*
    
The utility classes are now available at src/abi/usdt.ts
8

Tie all the generated code together with `src/main.ts`

Tie all the generated code together with a src/main.ts executable with the following code blocks:
  • Imports
    import { EvmBatchProcessor } from '@subsquid/evm-processor'
    import { TypeormDatabase } from '@subsquid/typeorm-store'
    import * as usdtAbi from './abi/usdt'
    import { Transfer } from './model'
    
  • EvmBatchProcessor object definition
    const processor = new EvmBatchProcessor()
      .setGateway('https://v2.archive.subsquid.io/network/ethereum-mainnet')
      .setRpcEndpoint({
        url: process.env.RPC_ETH_HTTP,
        rateLimit: 10
      })
      .setFinalityConfirmation(75) // 15 mins to finality
      .addLog({
        address: [ '0xdAC17F958D2ee523a2206206994597C13D831ec7' ],
        topic0: [ usdtAbi.events.Transfer.topic ]
      })
    
  • TypeormDatabase object definition
    const db = new TypeormDatabase()
    
  • A call to processor.run() with an inline definition of the batch handler
    processor.run(db, async ctx => {
      const transfers: Transfer[] = []
      for (let block of ctx.blocks) {
        for (let log of block.logs) {
          let {from, to, value} = usdtAbi.events.Transfer.decode(log)
          transfers.push(new Transfer({
            id: log.id,
            from, to, value
          }))
        }
      }
      await ctx.store.insert(transfers)
    })
    
    Note how supplying a TypeormDatabase to the function caused ctx.store to be a PostgreSQL-compatible Store object.
9

Compile the project and start the processor process

Compile the project and start the processor process
npx tsc
node -r dotenv/config lib/main.js
10

Start the GraphQL server

In a separate terminal, configure the GraphQL port and start the GraphQL server:
.env
 DB_NAME=squid
 DB_PORT=23798
 RPC_ETH_HTTP=https://rpc.ankr.com/eth
+GRAPHQL_SERVER_PORT=4350
npx squid-graphql-server
The finished GraphQL API with GraphiQL is available at localhost:4350/graphql. Final code for this mini-tutorial is available in this repo.
The commands listed here are often abbreviated as custom sqd commands in squids. If you’d like to do that too you can use the commands.json file of the EVM template as a starter.