agentleFS
hefgihefgi/skills/ponder/SKILL.md

ponder

Skill7 starsChanged 4 months agoMITView on GitHub

Build EVM blockchain data indexers using Ponder (ponder.sh) - an open-source TypeScript framework for indexing smart contract events, transactions, and traces into custom database schemas with type-safe APIs. Use when the user mentions ponder, blockchain/EVM indexing, onchain data pipelines, subgraph replacement, or wants to index smart contract events into a queryable database.

  • Reads credentials

4 other projects use a version of this.

Adopted by

Most recently taken up by

  • Other versions4 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 4

    1. github/awesome-copilot★ 41ksame name
    2. scaffold-eth/scaffold-eth-2★ 2.1ksame name
    3. Quinnod345/ponder★ 6same name
    4. 0xvikram/backboard-skills★ 1same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repository2 other files it gives its agents.

The file

Show its rules and text

We split this skill into the rules it gives an agent

22 rules. 0 are common, 3 are distinctive, and 19 are very specific to this project.

  • 0

    Common

    Most of the 4 edited versions we read keep these.

  • 3

    Distinctive

    This author's own choices. Worth a look.

  • 19

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Must exist at project root.

    line 63
  2. Distinctive:

    Events: Default choice for most indexing use cases.

    line 175
  3. Very specific:

    Only enable when you need gasUsed, logs from other contracts, or tx status.

    line 180
  4. Distinctive:

    All addresses are lowercase since v0.12.

    line 209
  5. Very specific:

    Use t.hex() for addresses and byte data, t.bigint() for uint256/int256.

    line 210
  6. Distinctive:

    Always add indexes on columns used in GraphQL filters or API WHERE clauses.

    line 211
  7. Very specific:

    Run pnpm create ponder and select template

    line 248
  8. Very specific:

    Add RPC URL to .env.local as PONDER_RPC_URL_<CHAIN_ID>

    line 249
  9. Very specific:

    Update ponder.config.ts with contract address, ABI, and startBlock

    line 250
  10. Very specific:

    Write indexing functions in src/index.ts, then run ponder dev

    line 252
  11. Very specific:

    Add ABI file with as const assertion

    line 256
  12. Very specific:

    Add contract entry to ponder.config.ts (abi, chain, address, startBlock)

    line 257
  13. Very specific:

    Add tables to ponder.schema.ts for the data you want to store

    line 258
  14. Very specific:

    Write handler in src/ using ponder.on("ContractName:EventName", ...)

    line 259
  15. Very specific:

    Import parseAbiItem from viem to define the creation event

    line 263
  16. Very specific:

    Use factory() in config: address: factory({ address, event, parameter })

    line 264
  17. Very specific:

    Write handler using event.log.address to identify the child contract

    line 265
  18. Very specific:

    Import db from ponder:api and schema from ponder:schema

    line 271
  19. Very specific:

    Add Hono route (app.get("/path", ...)) with Drizzle queries on db.sql

    line 272
  20. Very specific:

    Add chain configs for each chain in ponder.config.ts

    line 277
  21. Very specific:

    Use composite PKs with chainId if using experimental_isolated

    line 278
  22. Very specific:

    Use context.chain.id in handlers when chain-specific logic is needed

    line 279

“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Ponder Skill

What is Ponder

Ponder is an open-source TypeScript framework that indexes EVM blockchain data into Postgres with type-safe APIs. It replaces subgraphs with 10-15x faster indexing, hot reloading, and zero-codegen type safety.

Architecture flow: ponder.config.ts (what to index) -> ponder.schema.ts (where to store) -> src/*.ts (how to transform) -> src/api/index.ts (how to query)

Stack: v0.16.x, Drizzle ORM, Hono for HTTP, viem for Ethereum.

Quick Start

Scaffold

pnpm create ponder  # also: npm, yarn, bun

Requirements

  • Node >= 18.18, TypeScript >= 5.0.4, viem >= 2, hono >= 4.5

tsconfig.json (exact)

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "target": "ES2022",
    "module": "ES2022",
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

ponder-env.d.ts

Must exist at project root. Run ponder codegen to generate it.

Environment Variables

  • PONDER_RPC_URL_<CHAIN_ID> - RPC endpoint per chain (e.g., PONDER_RPC_URL_1 for mainnet)
  • DATABASE_URL - Postgres connection string (production)
  • DATABASE_SCHEMA - Schema isolation name (production)

Minimal Working Example

ponder.config.ts:

import { createConfig } from "ponder";
import { http } from "viem";
import { ERC20Abi } from "./abis/ERC20Abi";

export default createConfig({
  chains: {
    mainnet: { id: 1, rpc: process.env.PONDER_RPC_URL_1 },
  },
  contracts: {
    USDC: {
      abi: ERC20Abi,
      chain: "mainnet",
      address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
      startBlock: 6082465,
    },
  },
});

ponder.schema.ts:

import { onchainTable } from "ponder";

export const transfers = onchainTable("transfers", (t) => ({
  id: t.text().primaryKey(),
  from: t.hex().notNull(),
  to: t.hex().notNull(),
  amount: t.bigint().notNull(),
  timestamp: t.integer().notNull(),
}));

src/index.ts:

import { ponder } from "ponder:registry";
import { transfers } from "ponder:schema";

ponder.on("USDC:Transfer", async ({ event, context }) => {
  await context.db.insert(transfers).values({
    id: event.id,
    from: event.args.from,
    to: event.args.to,
    amount: event.args.value,
    timestamp: Number(event.block.timestamp),
  });
});

src/api/index.ts:

import { Hono } from "hono";
import { db } from "ponder:api";
import { graphql } from "ponder";
import * as schema from "ponder:schema";

const app = new Hono();
app.use("/graphql", graphql({ db, schema }));
export default app;

CLI Commands

CommandPurpose
ponder devDevelopment with hot reload, PGlite, port 42069
ponder start --schema <name>Production with Postgres
ponder serve --schema <name>API only (no indexing), for horizontal scaling
ponder codegenRegenerate types and ponder-env.d.ts
ponder db listList schemas in database
ponder db pruneRemove unused schemas

Exit codes: 75 = retryable (safe to auto-restart), 1 = fatal (check logs).

Decision Heuristics

Ordering Mode

ModeWhen to Use
multichain (default)Per-chain ordering, low latency. Most projects.
omnichainCross-chain consistency needed (bridges, aggregators, cross-chain protocols).
experimental_isolatedMax performance. Requires chainId in ALL table primary keys.

Table vs View

  • Table (onchainTable): Data written by indexing functions. Source of truth.
  • View (onchainView): Aggregations/rollups computed at query time. No writes, no store API.

Store API vs Raw SQL

  • Store API (context.db): Always prefer in indexing functions. 100-1000x faster (batched writes).
  • Raw SQL (db.sql): Only for complex multi-table updates that Store API cannot express.

Factory vs Explicit Addresses

  • factory(): Contract instances created dynamically at runtime (Uniswap pools, token clones).
  • Explicit address: Known addresses at config time. Use array for multiple fixed addresses.

Call Traces vs Events

  • Events: Default choice for most indexing use cases.
  • Call traces (includeCallTraces: true): Only when you need function inputs/outputs not emitted as events. Requires debug_traceBlockByNumber RPC support.

includeTransactionReceipts

Only enable when you need gasUsed, logs from other contracts, or tx status. Adds significant RPC overhead.

PGlite vs Postgres

  • PGlite: Dev only (ponder dev uses it automatically).
  • Postgres: Any shared or production environment.

GraphQL vs Custom Routes

  • GraphQL middleware: Standard CRUD with built-in filtering, sorting, pagination.
  • Custom Hono routes: Complex joins, aggregations, non-standard responses, or non-GraphQL APIs.

Gotchas and Anti-Patterns

Setup

  • ABIs MUST use as const assertion for type inference:
    export const MyAbi = [...] as const; // REQUIRED
  • ponder-env.d.ts must exist and be current. Run ponder codegen after config changes.
  • Set startBlock to the contract deployment block, NOT 0. Setting 0 scans entire chain history (hours vs seconds).
  • Local dev nodes (Anvil/Hardhat): set disableCache: true in chain config.
  • Table/schema names: snake_case, max 45 characters.

Schema

  • ANTI-PATTERN: String concatenation for composite keys (${owner}-${spender}). USE: primaryKey({ columns: [table.owner, table.spender] }).
  • All addresses are lowercase since v0.12. Never use checksummed addresses.
  • Use t.hex() for addresses and byte data, t.bigint() for uint256/int256.
  • Always add indexes on columns used in GraphQL filters or API WHERE clauses.

Indexing

  • Virtual module imports: ponder:registry, ponder:schema, ponder:api. NOT regular file paths.
  • context.db (indexing functions, read-write) vs db from ponder:api (API routes, read-only).
  • ANTI-PATTERN: Writing to database in API routes. db from ponder:api is read-only.
  • onConflictDoUpdate callback receives the EXISTING row, not the values you passed to .values().
  • context.client.readContract automatically scopes reads to the current event's block number.
  • Factory contracts: use event.log.address to get which child contract emitted the event.
  • Multi-chain shared handlers: use context.chain.id to distinguish chains.
  • Execution order per chain: block number -> tx index -> log index (deterministic).
  • event.id is a 75-digit string, globally unique across chains.

Deployment

  • ponder start requires --schema flag (or DATABASE_SCHEMA env var).
  • experimental_isolated ordering requires chainId in ALL table primary keys.

Reference Navigation

Load the appropriate reference based on the task:

TaskReference
Configure chains, contracts, factory, accounts, blocksreferences/config.md
Define tables, columns, relations, views, enumsreferences/schema.md
Write event handlers, DB operations, contract readsreferences/indexing.md
Set up GraphQL, SQL over HTTP, custom API routesreferences/api.md
Build frontend with @ponder/client, React, Next.js, tRPCreferences/frontend.md
Deploy, scale, debug, monitor, configure RPCreferences/production.md
Need a complete working example to start fromreferences/recipes.md

Common Task Checklists

Scaffold a New Project

  1. Run pnpm create ponder and select template
  2. Add RPC URL to .env.local as PONDER_RPC_URL_<CHAIN_ID>
  3. Update ponder.config.ts with contract address, ABI, and startBlock
  4. Define schema in ponder.schema.ts
  5. Write indexing functions in src/index.ts, then run ponder dev

Add a New Contract to Index

  1. Add ABI file with as const assertion
  2. Add contract entry to ponder.config.ts (abi, chain, address, startBlock)
  3. Add tables to ponder.schema.ts for the data you want to store
  4. Write handler in src/ using ponder.on("ContractName:EventName", ...)

Add a Factory Contract

  1. Import parseAbiItem from viem to define the creation event
  2. Use factory() in config: address: factory({ address, event, parameter })
  3. Write handler using event.log.address to identify the child contract
  4. Schema tables may need composite PKs including the child contract address

Add a Custom API Endpoint

  1. Open src/api/index.ts
  2. Import db from ponder:api and schema from ponder:schema
  3. Add Hono route (app.get("/path", ...)) with Drizzle queries on db.sql

Set Up Multi-Chain Indexing

  1. Choose ordering mode (see Decision Heuristics above)
  2. Add chain configs for each chain in ponder.config.ts
  3. Use composite PKs with chainId if using experimental_isolated
  4. Use context.chain.id in handlers when chain-specific logic is needed

Deploy to Production

  1. Provision Postgres v14-17 with <50ms latency to app server
  2. Set DATABASE_URL and choose a DATABASE_SCHEMA name
  3. Run ponder start --schema <name>
  4. Health checks: /health (always 200), /ready (503 during backfill, 200 when caught up)
  5. For zero-downtime redeploys, use --views-schema pattern

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.