Ponder: EVM Data Indexing Framework
Ponder is an open-source TypeScript framework designed for indexing data from EVM-compatible blockchains. It provides tools to define a database schema, write indexing functions to process on-chain events, and then query the indexed data via automatically generated GraphQL or SQL APIs. The current stable version is 0.16.6, with frequent patch releases indicating active development. Ponder differentiates itself by offering a powerful local development server with hot reloading, end-to-end type safety, support for Postgres, and the ability to index data from multiple chains within a single application. It aims to provide a robust, performant alternative to traditional subgraph indexing solutions, optimized for application developers who require custom backend logic and greater control over their data infrastructure.
Common errors
-
Error: Ponder requires Node.js version >=18.14.
cause The installed Node.js version is older than Ponder's minimum requirement.fixUpgrade Node.js to version 18.14 or newer using a version manager like `nvm` or `volta`. -
TS2307: Cannot find module 'ponder:registry' or its corresponding type declarations.
cause TypeScript compiler or IDE cannot resolve Ponder's virtual modules, often due to a missing or outdated `ponder-env.d.ts` file or incorrect `tsconfig.json` configuration.fixEnsure `ponder-env.d.ts` is present in your project root and accept any changes Ponder's dev server suggests. Verify `tsconfig.json` is correctly configured to include this file. -
Error: Start block number (XXXX) cannot be greater than latest block number (YYYY). Are you sure the RPC endpoint is for the correct network?
cause The RPC URL configured for a chain is either incorrect, points to a different network than expected, or the RPC provider is returning stale or invalid data.fixDouble-check the RPC URL and `chain.id` in `ponder.config.ts`. Confirm the RPC endpoint is active and serves the correct network. Consider using a more robust RPC provider or a Viem fallback transport. -
Error: Invalid ABI: Contract ABI must be asserted as const.
cause Ponder's type system (which leverages Viem and ABIType) requires ABIs to be explicitly asserted as `const` for proper type inference.fixWhen importing or defining your ABI, ensure it is `as const`, for example: `import { BaseRegistrarAbi } from './abis/BaseRegistrar' as const;` or `const MyAbi = [...] as const;`.
Warnings
- breaking Ponder has specific Node.js and TypeScript version requirements. For Node.js, version >=18.14 is required. For TypeScript, >=5.0.4 is mandatory to leverage its advanced type system. Older versions of these tools can lead to build failures or unexpected runtime errors.
- gotcha Ponder relies on several peer dependencies, including `hono`, `typescript`, and `viem`. These packages are critical for the framework's operation, and failing to install them or installing incompatible versions can lead to runtime errors or incomplete functionality.
- gotcha Ponder uses 'virtual modules' like `ponder:registry` and `ponder:schema` for its zero-codegen type system. These modules are not actual files on disk and require specific TypeScript configuration (e.g., `ponder-env.d.ts`) to be recognized by your IDE and build tools. Problems with `tsconfig.json` or `ponder-env.d.ts` can cause 'Cannot find module' errors.
- gotcha Incorrect RPC endpoint configuration or unreliable RPC providers can lead to Ponder service shutdowns or stalled indexing. An `Error: Start block number (...) cannot be greater than latest block number (...)` indicates an RPC issue, often with an incorrect network or a down provider.
Install
-
npm install ponder -
yarn add ponder -
pnpm add ponder
Imports
- createConfig
import createConfig from 'ponder'
import { createConfig } from 'ponder' - onchainTable
import { onchainTable } from 'ponder' - ponder
import { ponder } from 'ponder'import { ponder } from 'ponder:registry' - schema
import { schema } from 'ponder:schema'import schema from 'ponder:schema'
Quickstart
import { createConfig } from "ponder";
import { BaseRegistrarAbi } from "./abis/BaseRegistrar"; // Assuming ABI is local
import { ponder } from "ponder:registry";
import schema from "ponder:schema";
// 1. Initialize a new Ponder project (run in terminal):
// npm init ponder@latest
// cd my-ponder-project
// 2. Start the development server (run in project directory):
// npm run dev
// 3. Define Ponder configuration in ponder.config.ts
export default createConfig({
chains: {
mainnet: {
id: 1,
rpc: process.env.MAINNET_RPC_URL ?? "https://eth-mainnet.g.alchemy.com/v2/YOUR_ALCHEMY_KEY", // Replace with your actual RPC URL
},
},
contracts: {
BaseRegistrar: {
abi: BaseRegistrarAbi,
chain: "mainnet",
address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
startBlock: 9380410,
},
},
});
// 4. Define your schema in ponder.schema.ts
export const ensName = schema.onchainTable("ens_name", (t) => ({
name: t.string().primaryKey(),
owner: t.string().notNull(),
registeredAt: t.int().notNull(),
}));
// 5. Write indexing functions in src/BaseRegistrar.ts
ponder.on("BaseRegistrar:NameRegistered", async ({ event, context }) => {
const { name, owner } = event.params;
await context.db.insert(schema.ensName).values({
name: name,
owner: owner,
registeredAt: Number(event.block.timestamp),
});
});