Indexing
An indexing function is a TypeScript function that's triggered by onchain activity (an event log, call trace, transaction, transfer, or block).
The purpose of indexing functions is to transform onchain data and insert it into the tables you've defined in ponder.schema.ts
.
Register an indexing function
To register an indexing function, use ponder.on(...)
. The first argument is the event name, and the second argument is the function / callback that the indexing engine runs to process the event.
import { ponder } from "ponder:registry";
ponder.on("Blitmap:MetadataChanged", async ({ event, context }) => {
await context.db
.insert(tokens)
.values({
id: event.args.tokenId,
metadata: event.args.newMetadata,
})
.onConflictDoUpdate({
metadata: event.args.newMetadata,
});
});
Read more about how to write to the database and read contract data within indexing functions.
Frequently asked questions
Ordering
For each chain, the indexing engine calls indexing functions according to EVM execution order (block number, transaction index, log index). Read more about the ordering guarantee across multiple chains, which is more complex.
Reorgs
The indexing engine handles reorgs automatically. You do not need to adjust your indexing function logic to handle them. Here's how it works.
- The indexing engine records all changes (insert, update, delete) to each table defined in
ponder.schema.ts
using a trigger-based transaction log. - When the indexing engine detects a reorg, it follows these steps to reconcile the database state with the new canonical chain.
- Evict all non-canonical data from the RPC cache.
- Roll back the database to the common ancestor block height using the transaction log.
- Fetch the new canonical data from the RPC / remote chain.
- Run indexing functions to process the new canonical data.
- The indexing engine periodically drops finalized data from the transaction log to avoid database bloat.
Crash recovery
If the process crashes and starts back up again using the same exact configuration, the indexing engine attempts a crash recovery. This process is similar to reorg reconciliation, except all unfinalized changes (the entire transaction log) are rolled back. Then, indexing resumes from the finalized block.
Historical vs. realtime
Internally, the indexing engine has two distinct modes – historical and realtime – which use different approaches for fetching data from the RPC.
However, indexing function logic works the same way in both modes. As long as the logic is compatible with the expected onchain activity, indexing will work fine in both modes.