Understanding Generated Indexing Files
Overview
The /generated
directory contains files automatically created by Envio's code generation system. These files form the backbone of your indexer's runtime operations, translating your configuration, schema, and event handlers into executable code that processes blockchain data.
Important: Generated files should never be manually edited. Any changes will be overwritten the next time code generation runs.
Purpose of Generated Files
Generated files serve several critical functions:
- Type-Safe Data Access - They provide strongly-typed interfaces to interact with your defined entities
- Event Processing - They contain the logic to decode and process contract events
- Database Interactions - They manage database operations for storing and retrieving indexed data
- Runtime Orchestration - They coordinate the indexing workflow
Real-World Example: Uniswap V4 Indexer
Let's examine how specific elements from a real Uniswap V4 indexer translate into generated files:
From Schema to Generated Types
For a schema entity like this:
type Pool {
id: ID!
chainId: BigInt!
currency0: String!
currency1: String!
fee: BigInt!
tickSpacing: BigInt!
hooks: String!
numberOfSwaps: BigInt! @index
createdAtTimestamp: BigInt!
createdAtBlockNumber: BigInt!
}
The codegen process generates:
-
Type Definition in
EntityModels.res
:type pool = {
id: string,
chainId: BigInt.t,
currency0: string,
currency1: string,
fee: BigInt.t,
tickSpacing: BigInt.t,
hooks: string,
numberOfSwaps: BigInt.t,
createdAtTimestamp: BigInt.t,
createdAtBlockNumber: BigInt.t,
} -
Constructor Function:
let makePool = (
~id: string,
~chainId: BigInt.t,
~currency0: string,
~currency1: string,
~fee: BigInt.t,
~tickSpacing: BigInt.t,
~hooks: string,
~numberOfSwaps: BigInt.t,
~createdAtTimestamp: BigInt.t,
~createdAtBlockNumber: BigInt.t,
) => {
{
id,
chainId,
currency0,
currency1,
fee,
tickSpacing,
hooks,
numberOfSwaps,
createdAtTimestamp,
createdAtBlockNumber,
}
} -
Database Functions in
Queries.res
:let getPoolById = (id: string): option<EntityModels.pool> => {
// Database retrieval logic
}
let savePool = (entity: EntityModels.pool): unit => {
// Database save logic
}
From Config to Generated Event Handlers
Given a contract event in config.yaml
:
contracts:
- name: PoolManager
handler: src/EventHandlers.ts
events:
- event: Swap(bytes32 indexed id, address indexed sender, int128 amount0, int128 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick, uint24 fee)
The codegen process generates handler wrappers like:
// In Handlers.res
let handlePoolManager_Swap = (
~blockHeader: Types.blockHeader,
~txHash: string,
~logIndex: int,
~id: string,
~sender: string,
~amount0: BigInt.t,
~amount1: BigInt.t,
~sqrtPriceX96: BigInt.t,
~liquidity: BigInt.t,
~tick: BigInt.t,
~fee: BigInt.t,
): unit => {
// Call the user-defined handler
let context = makeEventContext(
~blockHeader,
~txHash,
~logIndex,
~eventIdx,
~contractName="PoolManager",
~eventName="Swap"
)
UserHandlers.handlePoolManager_Swap(
~context,
~id,
~sender,
~amount0,
~amount1,
~sqrtPriceX96,
~liquidity,
~tick,
~fee,
)
}
From Multi-Network Config to Generated Network Handlers
Your config has multiple networks:
networks:
- id: 1 # Ethereum Mainnet
# ...
- id: 10 # Optimism
# ...
- id: 42161 # Arbitrum
# ...
The generated code will include configuration parsing that handles all three networks:
// In Config.res
let networks = [
{
id: 1,
name: "ethereum-mainnet",
startBlock: 0,
contracts: [
{
name: "PositionManager",
addresses: ["0xbD216513d74C8cf14cf4747E6AaA6420FF64ee9e"],
// ...
},
{
name: "PoolManager",
addresses: ["0x000000000004444c5dc75cB358380D2e3dE08A90"],
// ...
},
],
},
{
id: 10,
name: "optimism",
startBlock: 0,
// Similar contract configuration for Optimism
},
{
id: 42161,
name: "arbitrum-one",
startBlock: 0,
// Similar contract configuration for Arbitrum
},
]
When to Run Code Generation
You should run code generation using the Envio CLI whenever you:
pnpm envio codegen
Codegen should be run after:
- Modifying your
config.yaml
file - Changing your GraphQL schema
- Adding or updating event handlers
- Switching to a new contract or ABI
- After pulling changes from version control
Troubleshooting Generation Errors
When code generation fails, the errors typically point to issues in your setup files. Here are common error patterns and their solutions:
Configuration Errors
Error messages containing Config validation failed
typically mean there's an issue in your config.yaml
file:
- Check for syntax errors in YAML formatting
- Verify that all required fields are present
- Ensure contract addresses are in the correct format
- Confirm that referenced networks are valid
For example, if you see an error about invalid network IDs, check that all network IDs in your config are valid:
networks:
- id: 1 # Valid Ethereum mainnet
- id: 10 # Valid Optimism
- id: 999 # Might be invalid if this network ID isn't recognized
Schema Errors
Errors mentioning Schema parsing error
point to issues in your GraphQL schema:
- Check for invalid GraphQL syntax
- Ensure entity names match those referenced in handlers
- Verify that relationships between entities are properly defined
- Check for unsupported types or directives
For example, if you're using the @index
directive as in your Pool
entity's numberOfSwaps
field, make sure it's correctly placed:
type Pool {
id: ID!
numberOfSwaps: BigInt! @index # Correct placement of @index directive
}
Handler Errors
If you see Handler validation failed
errors:
- Check that handler function signatures match expected patterns
- Ensure all referenced entities exist in your schema
- Verify proper import syntax for entities and contract events
Relationship with Setup Files
The generated files directly reflect the structure defined in your setup files:
- config.yaml → Determines which networks, contracts, and events are indexed
- schema.graphql → Defines the entities and relationships that are generated
- EventHandlers → Provides the business logic that the generated code wraps
Best Practices
- Never modify generated files directly - Always change the source files
- Run codegen before starting your indexer - Ensure all files are up to date
- Check error messages carefully - They often pinpoint issues in your setup files
Summary
Generated files form the critical bridge between your indexing specifications and the actual runtime execution. While you shouldn't modify them directly, understanding their structure and purpose can help you debug issues and optimize your indexing process.
If you encounter persistent errors in generated files, ensure your configuration, schema, and handlers follow Envio's best practices, or contact support for assistance.