Indexing function API
Indexing functions are user-defined functions that process blockchain data. They can be registered within any .ts
file inside the src/
directory. There are two kinds of events: log events and the "setup"
event.
Log events
Log events correspond to a smart contract event log that has occurred. The Ponder engine fetches event logs for all contracts defined in ponder.config.ts
, then passes that data to the corresponding indexing function.
import { ponder } from "@/generated";
ponder.on("ContractName:EventName", async ({ event, context }) => {
const { params, log, block, transaction } = event;
const { entities, contracts } = context;
// ...
});
Options
name | description |
---|---|
event | Event-specific data (log arguments, log, block, and transaction) |
context | Global resources (entity objects, read-only contract objects) |
Event
The event
object contains the log arguments, the transaction that produced the log, and the block containing that transaction. Log
, Block
, and Transaction
are similar to the corresponding types from viem (opens in a new tab), but are adapted to represent only the finalized blockchain state.
type Event = {
name: string;
params: {
/* Event-specific ABI parameters typed using AbiParameterToPrimitiveType */
};
log: Log;
block: Block;
transaction: Transaction;
};
Log
The raw log object.
type Log = {
/** Globally unique identifier for this log (`${blockHash}-${logIndex}`). */
id: string;
/** The address from which this log originated */
address: Address;
/** Hash of block containing this log */
blockHash: Hash;
/** Number of block containing this log */
blockNumber: bigint;
/** Contains the non-indexed arguments of the log */
data: Hex;
/** Index of this log within its block */
logIndex: number;
/** `true` if this log has been removed in a chain reorganization */
removed: boolean;
/** List of order-dependent topics */
topics: [Hex, ...Hex[]] | [];
/** Hash of the transaction that created this log */
transactionHash: Hash;
/** Index of the transaction that created this log */
transactionIndex: number;
};
Block
The block containing the transaction that emitted this log.
type Block = {
/** Base fee per gas */
baseFeePerGas: bigint | null;
/** "Extra data" field of this block */
extraData: Hex;
/** Maximum gas allowed in this block */
gasLimit: bigint;
/** Total used gas by all transactions in this block */
gasUsed: bigint;
/** Block hash */
hash: Hash;
/** Logs bloom filter */
logsBloom: Hex;
/** Address that received this block’s mining rewards */
miner: Address;
/** Block number */
number: bigint;
/** Parent block hash */
parentHash: Hash;
/** Root of the this block’s receipts trie */
receiptsRoot: Hex;
/** Size of this block in bytes */
size: bigint;
/** Root of this block’s final state trie */
stateRoot: Hash;
/** Unix timestamp of when this block was collated */
timestamp: bigint;
/** Total difficulty of the chain until this block */
totalDifficulty: bigint | null;
/** Root of this block’s transaction trie */
transactionsRoot: Hash;
};
Transaction
The transaction that emitted this log.
type Transaction = {
/** Hash of block containing this transaction */
blockHash: Hash;
/** Number of block containing this transaction */
blockNumber: bigint;
/** Chain ID. */
chainId: number;
/** Transaction sender */
from: Address;
/** Gas provided for transaction execution */
gas: bigint;
/** Base fee per gas. */
gasPrice?: bigint | undefined;
/** Hash of this transaction */
hash: Hash;
/** Contract code or a hashed method call */
input: Hex;
/** Total fee per gas in wei (gasPrice/baseFeePerGas + maxPriorityFeePerGas). */
maxFeePerGas?: bigint | undefined;
/** Max priority fee per gas (in wei). */
maxPriorityFeePerGas?: bigint | undefined;
/** Unique number identifying this transaction */
nonce: number;
/** Transaction recipient or `null` if deploying a contract */
to: Address | null;
/** Index of this transaction in the block */
transactionIndex: number;
/** Value in wei sent with this transaction */
value: bigint;
};
Context
This object contains a CRUD interface for the entities defined in schema.graphql
, and a read-only contract object for each contract specified in ponder.config.ts
.
type Context = {
// Keyed by entity type names from schema.graphql
entities: Record<string, Entity>;
// Keyed by contract names from ponder.config.ts
contracts: Record<string, ReadOnlyContract>;
};
Entity
These objects are used to create, read, update, and delete entity instances. context.entities
contains an Entity
object for each entity type defined in schema.graphql
.
See Create & update entities for a complete API reference.
ReadOnlyContract
See the read contract data guide for more details.
ReadOnlyContract
objects are used to read data directly from a contract. These objects have a method for each read-only function present in the contract's ABI (functions with state mutability of "pure"
or "view"
). The context.contracts
object has a ReadOnlyContract
for each contract defined in ponder.config.ts.
A ReadOnlyContract
is a viem Contract Instance (opens in a new tab) that has been modified to cache contract read results. By default, contract reads use the eth_call
RPC method with blockNumber
set to the block number of the event being processed (event.block.number
). You can read the contract at a different block number (e.g. the contract deployment block number or "latest"
) by passing the blockNumber
or blockTag
option, but this will disable caching.
import { ponder } from "@/generated";
ponder.on("MyERC20:Transfer", async ({ event, context }) => {
const { MyERC20 } = context.contracts;
// This read will occur at the block number of the event being
// processed (event.block.number) and will be served from the cache
// on hot reloads / reployments.
const totalSupply = await MyERC20.read.totalSupply();
// This read will occur at the latest block number when this function
// runs, and will not be cached. Avoid this pattern.
const currentBalance = await MyERC20.read.balanceOf("0xFa3...", {
blockTag: "latest",
});
});
The "setup"
event
You can also define an indexing function for a special event called "setup"
that runs before all other events.
import { ponder } from "@/generated";
ponder.on("setup", async ({ context }) => {
const { entities, contracts } = context;
// ...
});
Options
name | description |
---|---|
context | Global resources (entity objects, read-only contract objects) |