Configuration File (config.yaml)
The config.yaml file defines your indexer's behavior, including which blockchain events to index, contract addresses, which chains to index, and various advanced indexing options. It is a crucial step in configuring your HyperIndex setup.
Whenever you make changes in config.yaml that should affect generated types (e.g. adding events, contracts, or chains), run pnpm codegen to regenerate types and code for your event handlers.
Example
This is a basic ERC-20 config.yaml — it's enough on its own to get an indexer running across Ethereum Mainnet and Gnosis, tracking Approval and Transfer events on the UNI token.
# yaml-language-server: $schema=./node_modules/envio/evm.schema.json
name: erc20-indexer
description: ERC-20 Indexer
contracts:
- name: ERC20
events:
- event: "Approval(address indexed owner, address indexed spender, uint256 value)"
- event: "Transfer(address indexed from, address indexed to, uint256 value)"
chains:
- id: 1 # Ethereum Mainnet
start_block: 0
contracts:
- name: ERC20
address: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" # UNI
- id: 100 # Gnosis Mainnet
start_block: 0
contracts:
- name: ERC20
address: "0x4537e328Bf7e4eFA29D05CAeA260D7fE26af9D74" # UNI
Next steps:
- Define how each event updates your data →
src/handlers - Shape the entities you'll query →
schema.graphql - Need more than the basics? Keep reading for the full set of configuration options below.
Contracts Definition
The top-level contracts block defines each contract once — its events and per-event options. Each chain then references these contracts by name and supplies chain-specific values like the on-chain address (see Contracts (per chain)).
contracts:
- name: Greeter
events:
- event: "NewGreeting(address user, string greeting)"
- event: "ClearGreeting(address user)"
chains:
- id: 1
start_block: 0
contracts:
- name: Greeter
address: "0x9D02A17dE4E68545d3a58D3a20BbBE0399E05c9c"
Events Selection
The recommended way to declare events is by their human-readable signature directly under events:
contracts:
- name: Greeter
events:
- event: "NewGreeting(address user, string greeting)"
- event: "ClearGreeting(address user)"
Only the events listed here are indexed. To stop indexing an event, remove its entry.
Custom Event Names
You can assign custom names to events in config.yaml. This is handy when
two events share the same name but have different signatures, or when you want
a more descriptive name in your Envio project.
events:
- event: Assigned(address indexed recipientId, uint256 amount, address token)
- event: Assigned(address indexed recipientId, uint256 amount, address token, address sender)
name: AssignedWithSender
Using an ABI File
If you'd rather reference a JSON ABI file (e.g. when you have one already and want to pick a subset of events from it), use abi_file_path and refer to events by name:
contracts:
- name: Greeter
abi_file_path: ./abis/greeter.json
events:
- event: NewGreeting # signature comes from the ABI file
Event signatures are still the recommended default — they keep config.yaml self-contained and easier to review.
Field Selection
To improve indexing performance and reduce credits usage, the block and transaction fields on events contain only a subset of the fields available on the blockchain.
To access fields that are not provided by default, specify them using the field_selection option for your event:
events:
- event: "Assigned(address indexed user, uint256 amount)"
field_selection:
transaction_fields:
- transactionIndex
block_fields:
- timestamp
See all possible options in the Config File Reference or use IDE autocomplete for your help.
Global Field Selection
You can also specify fields globally for all events in the root of the config file:
field_selection:
transaction_fields:
- hash
- gasUsed
block_fields:
- parentHash
Try to use this option sparingly as it can cause redundant Data Source calls and increased credits usage.
Chains
Everything under the top-level chains field configures the networks your indexer connects to — data sources, start/end blocks, reorg behavior, and multichain semantics.
RPC
The rpc option configures an RPC data source per chain. It supports multiple URLs with explicit roles via the for field:
sync– use this RPC for historical sync.realtime– use this RPC for low-latency head tracking once the indexer enters realtime mode. WebSocket endpoints (wss://...) are supported here.fallback– use as a fallback when the primary source is unavailable.
For chains supported by HyperSync, rpc is not required — HyperSync is used as the primary data source out of the box. You can still add an RPC entry as a fallback for extra reliability. For chains without HyperSync, rpc is required and acts as the primary data source.
chains:
- id: 1
rpc:
- url: https://eth-mainnet.your-rpc-provider.com
for: sync
- url: wss://eth-mainnet.your-rpc-provider.com
for: realtime
- url: https://fallback.example.com
for: fallback
A short form is also supported when you only need a single RPC URL:
chains:
- id: 1
rpc: https://eth-mainnet.your-rpc-provider.com
After switching to a fallback source, HyperIndex automatically attempts to recover to the primary source 60 seconds later.
See the Rpc reference for advanced tuning options such as initial_block_interval, polling_interval, query_timeout_millis, and backoff parameters.
WebSocket Height Streaming
Pair wss:// URLs with for: realtime to improve head latency by streaming new block heights over a WebSocket connection.
chains:
- id: 1
rpc:
url: ${ENVIO_RPC_ENDPOINT}
ws: ${ENVIO_WS_ENDPOINT}
for: realtime
WebSocket support for RPC sources is experimental. Please open a GitHub issue if you hit any problems.
Start Block
Set start_block on a chain to control the block at which the indexer begins ingesting data. Setting it to 0 is a safe default for HyperSync — it will automatically skip ahead to the first block that contains data for your configured contracts.
chains:
- id: 1
start_block: 0 # HyperSync will fast-forward to the first relevant block
contracts:
- name: Greeter
address: "0x9D02A17dE4E68545d3a58D3a20BbBE0399E05c9c"
For finer-grained control, see Per-Contract Start Block Override and the per-event start block tip below it.
End Block
Set end_block on a chain to stop indexing once that block is reached. Useful for backfills with a known cutoff or one-off snapshots.
chains:
- id: 1
start_block: 18000000
end_block: 19000000
contracts:
- name: Greeter
address: "0x9D02A17dE4E68545d3a58D3a20BbBE0399E05c9c"
If you're capping the range to make a deterministic test run, prefer the built-in testing framework instead — it lets you pin block ranges or feed synthetic events without spinning up a real indexer.
Contracts (per chain)
Inside each chain you list the contracts you want to index on that chain — referenced by name from your top-level Contracts Definition. The chain-level entry is where you pin the on-chain address (or list of addresses) and, optionally, a per-contract start_block.
Addresses can be provided in checksum format or in lowercase. Envio accepts both and normalizes them internally.
Addresses
Single address:
chains:
- id: 1
start_block: 0
contracts:
- name: MyContract
address: "0xContractAddress"
Multiple addresses for the same contract:
chains:
- id: 1
start_block: 0
contracts:
- name: MyContract
address:
- "0xAddress1"
- "0xAddress2"
If using a proxy contract, always use the proxy address, not the implementation address.
Per-Contract Start Block Override
By default, contracts use the chain start_block. You can also set a per-contract start_block to override it. Handy when:
- Contracts were deployed at different blocks
- You only need data from a contract starting at a specific block
- You want to skip unnecessary historical data for some contracts
- Works nicely with Dynamic Contract Registration
chains:
- id: 1 # ethereum-mainnet
start_block: 18000000 # Default start block for all contracts on this chain
contracts:
- name: ERC20
address:
- "0x1111111111111111111111111111111111111111"
- "0x2222222222222222222222222222222222222222"
start_block: 18500000 # Override for this contract
- name: Greeter
address: "0x9D02A17dE4E68545d3a58D3a20BbBE0399E05c9c"
# Uses chain default (18000000)
For even finer control, you can also specify a start block per event from your handler using the where.block.number._gte filter on indexer.onEvent. See Event Handlers for the full API.
Indexer Without Contracts
The contracts field is optional in V3. You can run an indexer that only uses block handlers (indexer.onBlock) without declaring any contracts:
name: BlockHandlerOnly
chains:
- id: 1
start_block: 18000000
Storage
How indexed data is persisted — which backends to use and whether to keep raw event records around.
Storage Backends
By default, HyperIndex writes entities to Postgres. In V3 you can additionally enable ClickHouse as a second storage backend (experimental):
storage:
postgres: true
clickhouse: true
When both backends are enabled, you must route each entity explicitly via the @storage directive in schema.graphql:
# Stored in both Postgres and ClickHouse
type Transfer @storage(postgres: true, clickhouse: true) {
id: ID!
from: String!
to: String!
value: BigInt!
}
# Stored only in ClickHouse
type Snapshot @storage(clickhouse: true) {
id: ID!
blockNumber: BigInt!
}
envio dev automatically spins up a ClickHouse Docker container for local development. For envio start, provide the connection via the environment variables ENVIO_CLICKHOUSE_HOST, ENVIO_CLICKHOUSE_DATABASE, ENVIO_CLICKHOUSE_USERNAME, and ENVIO_CLICKHOUSE_PASSWORD.
Do not run multiple indexers writing to the same ClickHouse database at the same time.
Envio Cloud currently supports ClickHouse on the Dedicated Plan.
Address Format
Use address_format to control how every address surfaced by the indexer (event fields like event.srcAddress / event.transaction.from, chain.<Contract>.addresses, addresses embedded in entity ids, etc.) is formatted.
address_format: lowercase # default: checksum
checksum(default) – EIP-55 checksummed mixed-case addresses.lowercase– every address is lowercased globally. Useful when joining against another data source that stores addresses in lowercase, or when you want byte-for-byte deterministic ids without per-handler.toLowerCase()calls.
You can still call .toLowerCase() ad-hoc inside a handler when you only need a single value lowercased.
Ecosystem
ecosystem selects which chain family your indexer targets. EVM is the default and what every example above assumes, but the same config.yaml schema is shared with Fuel and Solana (SVM).
ecosystem: evm # default — also: fuel, svm
Most projects don't set this field explicitly. Use pnpx envio init fuel or pnpx envio init svm to scaffold non-EVM indexers — the generated config.yaml will already have ecosystem set correctly. See the Fuel and Solana guides for the ecosystem-specific options.
Environment Variables
Environment variable interpolation is supported anywhere in config.yaml, which is useful for keeping RPC URLs, addresses, or chain IDs out of source control.
chains:
- id: ${ENVIO_CHAIN_ID:-ethereum-mainnet}
contracts:
- name: Greeter
address: "${ENVIO_GREETER_ADDRESS}"
Run your indexer with custom environment variables:
ENVIO_CHAIN_ID=optimism ENVIO_GREETER_ADDRESS=0xYourContractAddress pnpm dev
Interpolation syntax:
${ENVIO_VAR}– Use the value ofENVIO_VAR${ENVIO_VAR:-default}– UseENVIO_VARif set, otherwise usedefault
For more detailed information about environment variables, see our Environment Variables Guide.
Advanced
In ~95% of cases you don't need to touch any of these — the defaults are tuned for the common path. Reach for them only when you have a specific reason.
Handler File Path
Handlers are auto-discovered from src/handlers/. Override the directory with the top-level handlers option, or set a per-contract handler path when needed:
handlers: ./src/my-handlers # optional override of src/handlers
contracts:
- name: Greeter
handler: ./src/GreeterHandler.ts # optional per-contract path
Schema File Path
You can customize the path to the schema file using the schema option:
schema: ./path/to/schema.graphql
By default, the schema.graphql is expected to be in the root directory of your project.
Block Lag
Set block_lag on a chain to keep the indexer a fixed number of blocks behind the chain head. Defaults to 0.
chains:
- id: 1
start_block: 0
block_lag: 5
Only set block_lag if you understand the trade-off — it intentionally trades head latency for additional reorg safety.
Rollback on Reorg
HyperIndex automatically handles blockchain reorganizations by default. To disable or customize this behavior, set the rollback_on_reorg flag in your config.yaml:
rollback_on_reorg: true # default is true
See detailed configuration options here.
Full Batch Size
Set full_batch_size to control how many events are processed in a single batch.
full_batch_size: 5000
Raw Events Storage
By default, HyperIndex doesn't store raw event data in the database to optimize performance and reduce storage requirements. However, you can enable this feature for debugging purposes or if you need to access the original event data.
To enable storage of raw events, add the following to your config.yaml:
raw_events: true
When enabled, all indexed events will be stored in the raw_events table in the database, which you can view through the Hasura interface. This is particularly useful for:
- Debugging event processing issues
- Verifying that events are being captured correctly
- Creating custom queries against raw blockchain data
Note that enabling this option will increase database storage requirements and may slightly impact indexing performance.
Configuration Schema Reference
Explore detailed configuration schema parameters here:
- See the full, deep-linkable reference: Config Schema Reference
Recommended: Use the Config Schema Reference for programmatic access to schema information. The interactive viewer below is optimized for human users.
📋 Hierarchical Interactive Schema Explorer (Click to expand - For human reference only)
Now your configuration file is set, you're ready to start indexing with HyperIndex!