Indexing functions

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.

src/index.ts
import { ponder } from "@/generated";
 
ponder.on("ContractName:EventName", async ({ event, context }) => {
  const { params, log, block, transaction } = event;
  const { entities, contracts } = context;
 
  // ...
});

Options

namedescription
eventEvent-specific data (log arguments, log, block, and transaction)
contextGlobal resources (entity objects, read-only contract objects)
Value returned by indexing functions are ignored.

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.

src/index.ts
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.

src/index.ts
import { ponder } from "@/generated";
 
ponder.on("setup", async ({ context }) => {
  const { entities, contracts } = context;
 
  // ...
});

Options

namedescription
contextGlobal resources (entity objects, read-only contract objects)