How to Index Sei Smart Contract Data in Minutes using Envio
- Scaffold the ERC20 template with
pnpx envio init template -t erc20 -d ./sei-indexer - Point
config.yamlat Sei mainnet (chain ID1329, start block79123881) and USDC at0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392 - Define an
AccountandApprovalschema, writeTransferandApprovalhandlers insrc/handlers/MyContract.ts - Run
pnpm codegen, thenpnpm dev, and query live Sei USDC data athttp://localhost:8080/v1/graphql
This guide walks you through building a HyperIndex indexer for a smart contract on Sei. By the end, you will have a local indexer that streams Sei blockchain data into a Postgres database and serves it through a GraphQL API.
What you will build
An indexer for USDC on Sei mainnet (Circle's native USDC). It tracks Transfer and Approval events, keeps a running token balance per account, and records every approval.
Target contract:
- Chain: Sei mainnet, chain ID
1329 - Contract address:
0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392 - Explorer: seitrace.com
Indexing testnet instead? See the Indexing on Sei testnet section at the bottom of this guide for the testnet config changes.
Steps at a glance
- Scaffold the indexer
- Add your API token
- Configure the indexer for Sei
- Define the data schema
- Write the event handler
- Generate types
- Run the indexer
- Query your data
- Stop the indexer
Before you begin
Make sure you have the following prerequisites installed. Run each check in a terminal:
- Node.js v22 or later. Check with
node -v. - pnpm v8 or later. Check with
pnpm -v. - Docker installed and running. Check with
docker ps. - A free Envio API token. HyperIndex requires an API token to use HyperSync as a data source. Create one at envio.dev/app/api-tokens.
Step 1: Scaffold the indexer
Create a new HyperIndex project from the ERC20 template, then move into the project folder:
pnpx envio init template -t erc20 -d ./sei-indexer
cd sei-indexer
A new sei-indexer folder is created with config.yaml, schema.graphql, a .env file, and a src/handlers/ directory. Every file in src/handlers/ is registered automatically, so there is no central EventHandlers.ts file. Generated types are written to the .envio/ directory.
Step 2: Add your API token
The scaffolded .env file ships with three commented lines:
# To create or update a token visit https://envio.dev/app/api-tokens
# Uncomment the line below and set a valid token
# ENVIO_API_TOKEN="<YOUR-API-TOKEN>"
Uncomment the last line and replace <YOUR-API-TOKEN> with your token so it reads:
ENVIO_API_TOKEN="your-token-here"
Note: A token is required. Without a valid token, the indexer stops immediately with the error "An API token is required for using HyperSync as a data-source."
Step 3: Configure the indexer for Sei
Replace the contents of config.yaml with the configuration below. This points the indexer at Sei mainnet and the USDC contract, and selects the two events to index:
# yaml-language-server: $schema=./node_modules/envio/evm.schema.json
name: sei-indexer
description: Sei ERC20 indexer
contracts:
- name: MyContract
events:
- event: Transfer(address indexed from, address indexed to, uint256 value)
- event: Approval(address indexed owner, address indexed spender, uint256 value)
chains:
- id: 1329 # Sei mainnet (pacific-1)
start_block: 79123881
contracts:
- name: MyContract
address:
- "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392"
Important: start_block: 79123881 is the first EVM block on Sei mainnet. Earlier blocks are non-EVM and have no Solidity events to index. Setting it to 0 will waste time scanning blocks that can never match. Sei is a supported HyperSync network, so the endpoint resolves automatically from the chain ID. No hypersync_config block is needed.
Step 4: Define the data schema
Replace the contents of schema.graphql with the schema below. Each type becomes a database table and a GraphQL query:
type Account {
id: ID!
balance: BigInt!
approvals: [Approval!]! @derivedFrom(field: "owner")
}
type Approval {
id: ID!
owner: Account!
spender: String!
value: BigInt!
blockNumber: BigInt!
timestamp: BigInt!
}
The @derivedFrom directive creates a virtual reverse lookup, so each Account exposes its list of approvals without you storing that list explicitly.
Step 5: Write the event handler
The ERC20 template ships a handler and a test file that both reference the template contract. Remove them first:
rm -f src/handlers/ERC20.ts src/indexer.test.ts
Note: Removing src/indexer.test.ts matters. It imports the deleted ERC20 handler, so leaving it in place breaks pnpm test.
Now create a new handler file at src/handlers/MyContract.ts with the following content:
import { indexer } from "envio";
indexer.onEvent(
{ contract: "MyContract", event: "Transfer" },
async ({ event, context }) => {
const { from, to, value } = event.params;
const sender = await context.Account.get(from);
context.Account.set({
id: from,
balance: (sender?.balance ?? 0n) - value,
});
const receiver = await context.Account.get(to);
context.Account.set({
id: to,
balance: (receiver?.balance ?? 0n) + value,
});
},
);
indexer.onEvent(
{ contract: "MyContract", event: "Approval" },
async ({ event, context }) => {
const { owner, spender, value } = event.params;
const existing = await context.Account.get(owner);
context.Account.set({
id: owner,
balance: existing?.balance ?? 0n,
});
context.Approval.set({
id: `${event.block.hash}-${event.logIndex}`,
owner_id: owner,
spender,
value,
blockNumber: BigInt(event.block.number),
timestamp: BigInt(event.block.timestamp),
});
},
);
Key points about the handler API:
- Handlers are registered with
indexer.onEvent, imported from theenviopackage. - Linked entities are set with the
<field>_idconvention, so theownerrelation is written asowner_id. - Event metadata is available on the
eventobject, includingevent.block(number,timestamp,hash),event.logIndex,event.srcAddress, and more. See the Event Handlers docs for the full list.
Step 6: Generate types
Generate the typed code from your config and schema:
pnpm codegen
The command reads config.yaml and schema.graphql, writes typed code into .envio/, and exits with no errors. Run this again any time you change the config or schema.
Step 7: Run the indexer
Start the indexer in development mode:
pnpm dev
Envio starts Postgres and Hasura in Docker, then begins streaming Sei blocks through HyperSync. Indexed data appears within seconds. Leave this terminal running.
You can explore the data in the Envio Console.
Step 8: Query your data
With the indexer still running, open a new terminal and query the GraphQL API. This asks for the top 3 accounts by USDC balance and the 3 most recent approvals:
curl -s -X POST http://localhost:8080/v1/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ Account(limit: 3, order_by: { balance: desc }) { id balance } Approval(limit: 3, order_by: { blockNumber: desc }) { id spender value blockNumber } }"}'
You will get back JSON with Account and Approval rows from live Sei USDC activity. If the response is empty, wait a few seconds for the indexer to sync further and run the query again. USDC has six decimals on Sei, so a balance of 1000000 represents 1 USDC.
Step 9: Stop the indexer
Stop pnpm dev with Ctrl+C in its terminal, then clean up the local environment:
pnpm envio stop
Important: This stops the Docker containers and removes the local database. Do not use docker compose down. HyperIndex manages its containers directly, so docker compose down fails.
Indexing on Sei testnet
To target Sei testnet (atlantic-2) instead of mainnet, swap three values in the chains block of config.yaml:
chains:
- id: 1328 # Sei testnet (atlantic-2)
start_block: 186100000
contracts:
- name: MyContract
address:
- "0x4fCF1784B31630811181f670Aea7A7bEF803eaED" # Testnet USDC
Notes for testnet:
- Chain ID is
1328(vs.1329on mainnet). - Testnet USDC address:
0x4fCF1784B31630811181f670Aea7A7bEF803eaED(verified on Seitrace). start_block: 186100000is the first EVM block on Sei testnet. Earlier blocks are non-EVM.- The HyperSync endpoint (
https://sei-testnet.hypersync.xyz) auto-resolves from the chain ID. - To get testnet USDC, use the Circle Faucet so the contract has activity to index.
Docs: Sei Testnet on Envio | USDC on Sei (Sei docs)
Troubleshooting
-
"An API token is required for using HyperSync as a data-source": set
ENVIO_API_TOKENin.env(see Step 2: Add your API token above). -
The indexer resumes but makes no progress (progress shows -1): the database holds stale state from an earlier aborted run. Run
pnpm envio dev -rto wipe the database and re-index from scratch. -
No events showing up on mainnet: confirm
start_blockis at least79123881. Blocks before that are non-EVM on Sei mainnet and contain no Solidity events. On testnet, the equivalent floor is186100000. -
"Failed to automatically find HyperSync endpoint for the chain 1329": if the endpoint doesn't auto-resolve for your CLI version, set it explicitly under the chain entry in
config.yaml:chains:
- id: 1329
start_block: 79123881
hypersync_config:
url: https://sei.hypersync.xyz
contracts:
# ...Use
https://sei-testnet.hypersync.xyzfor testnet (chain1328).
Resources
- HyperIndex overview
- Sei on Envio
- HyperSync supported networks
- Configuration file
- Schema reference
- Event handlers
- Envio CLI reference
- Running locally
- USDC on Sei (Sei docs)
Build With Envio
Envio is a real-time multichain blockchain indexer that turns onchain events into a queryable GraphQL API. Supports any EVM chain, plus Solana and Fuel. Use Envio Cloud or self-host. If you're building onchain, come talk to us about your data needs.
Website | X | Discord | Telegram | GitHub | YouTube | Reddit
Jordyn Laurier