Get started
What is Ponder?
Ponder is an open-source framework for custom Ethereum indexing.
You write TypeScript code to transform onchain data into your application's schema. Then, Ponder fetches data from the chain, runs your indexing logic, and writes the result to Postgres.
Once indexed, you can query the data through GraphQL, SQL over HTTP, or directly in Postgres.
Quickstart
Run create-ponder
The quickest way to create a new Ponder project is create-ponder
, which sets up everything automatically for you.
pnpm create ponder
On installation, you'll see a few prompts.
✔ What's the name of your project? › new-project
✔ Which template would you like to use? › Default
✔ Installed packages with pnpm.
✔ Initialized git repository.
This guide follows the ERC-20 example, which indexes a token contract on Ethereum mainnet.
Start the dev server
After insallation, start the local development server.
pnpm dev
Ponder will connect to the database, start the HTTP server, and begin indexing.
11:34:39 AM INFO build Using PGlite database at .ponder/pglite (default)
11:34:39 AM INFO database Using database schema 'public'
11:34:40 AM INFO database Created tables [account, transfer_event, allowance, approval_event]
11:34:40 AM INFO server Started listening on port 42069
11:34:40 AM INFO server Started returning 200 responses from /health endpoint
11:34:56 AM INFO app Indexed 12 events with 4.1% complete
Query the database
Visit localhost:42069/graphql in your browser to explore the auto-generated GraphQL API. Here's a query for the top accounts by balance, along with the total number of accounts.
query {
accounts(orderBy: "balance", orderDirection: "desc", limit: 2) {
items {
address
balance
}
totalCount
}
}
Customize the schema
Let's add a new column to a table in ponder.schema.ts
. We want to track which accounts are an owner of the token contract.
import { index, onchainTable, primaryKey, relations } from "ponder";
export const account = onchainTable("account", (t) => ({
address: t.hex().primaryKey(),
balance: t.bigint().notNull(),
isOwner: t.boolean().notNull(),
}));
// ...
Immediately, there's a type error in src/index.ts
and a runtime error in the terminal. We added a required column, but our indexing logic doesn't include it.
12:16:16 PM ERROR indexing Error while processing 'ERC20:Transfer' event
NotNullConstraintError: Column 'account.isOwner' violates not-null constraint.
at /workspace/new-project/src/index.ts:10:3
8 |
9 | ponder.on("ERC20:Transfer", async ({ event, context }) => {
> 10 | await context.db
| ^
11 | .insert(account)
12 | .values({ address: event.args.from, balance: 0n })
13 | .onConflictDoUpdate((row) => ({
Update indexing logic
Update the indexing logic to include isOwner
when inserting new rows into the account
table.
import { ponder } from "ponder:registry";
import { account } from "ponder:schema";
const OWNER_ADDRESS = "0x3bf93770f2d4a794c3d9ebefbaebae2a8f09a5e5";
ponder.on("ERC20:Transfer", async ({ event, context }) => {
await context.db
.insert(account)
.values({
address: event.args.from,
balance: 0n,
isOwner: event.args.from === OWNER_ADDRESS,
})
.onConflictDoUpdate((row) => ({
// ...
})
As soon as we save the file, the dev server hot reloads and finishes indexing successfully.
12:27:26 PM INFO build Hot reload 'src/index.ts'
12:27:29 PM INFO app Indexed 4999 events with 54.2% complete and 471ms remaining
12:27:29 PM INFO app Indexed 4999 events with 75.5% complete and 276ms remaining
12:27:29 PM INFO app Indexed 4998 events with 99.4% complete and 6ms remaining
12:27:29 PM INFO app Indexed 108 events with 100% complete and 0ms remaining
12:27:29 PM INFO indexing Completed historical indexing
Next steps
This quickstart only scratches the surface of what Ponder can do. Take a look at the examples directory for more complex projects, or the GitHub dependents for a list of real-world repositories using Ponder.
Or, continue reading the guides and API reference here on the documentation site.