Block intervals – Ponder
Skip to content

Block intervals

Run logic on a regular schedule

To run indexing logic on a regular schedule, use the blocks field in ponder.config.ts. Block intervals are useful for aggregations, time-series logic, and bulk updates using raw SQL.

This guide describes each configuration option and suggests patterns for common use cases. Visit the config API reference for more information.

Example

This config instructs the indexing engine to run an indexing function every 10 blocks starting at the start block – 1000, 1010, 1020, and so on.

ponder.config.ts
import { createConfig } from "ponder";
 
export default createConfig({
  chains: {
    mainnet: { id: 1, rpc: process.env.PONDER_RPC_URL_1 },
  },
  blocks: {
    ChainlinkOracleUpdate: {
      chain: "mainnet",
      interval: 10, // Every 10 blocks
      startBlock: 1000,
    },
  },
});

Now, we can register an indexing function for the ChainlinkOracleUpdate:block event. This example reads the latest price from the Chainlink oracle contract and inserts a row into the priceTimeline table.

src/index.ts
import { ponder } from "ponder:registry";
import { priceTimeline } from "ponder:schema";
import { ChainlinkOracleAbi } from "../abis/ChainlinkOracle.ts";
 
ponder.on("ChainlinkOracleUpdate:block", async ({ event, context }) => {
  // Fetch the price at the current block height (1000, 1010, 1020, etc.)
  const latestPrice = await context.client.readContract({
    abi: ChainlinkOracleAbi,
    address: "0xD10aBbC76679a20055E167BB80A24ac851b37056",
    functionName: "latestAnswer",
  });
 
  // Insert a row into the price timeline table
  await context.db.insert(priceTimeline).values({
    id: event.id,
    timestamp: event.block.timestamp,
    price: latestPrice,
  });
});

Name

Every block interval must have a name, provided as a key to the blocks object. The name must be unique across blocks, contracts, and accounts.

Use a descriptive name to indicate the purpose of the block interval.

ponder.config.ts
import { createConfig } from "ponder";
 
export default createConfig({
  chains: { /* ... */ },
  blocks: {
    ChainlinkOracleUpdate: { 
      chain: "mainnet",
      interval: 10,
      startBlock: 19783636,
    },
  },
});

Interval

Use the interval option to specify how often the indexing function should run. A block interval with a start block of 100 and a interval of 10 will index blocks 100, 110, 120, 130, and so on.

Block time

It's often easier to think about a time interval instead of a block interval. To convert between the two, divide the time interval by the chain's average block time.

For example, if the block time is 3 seconds and you want to run an indexing function once per day:

// 24 hours per day, 60 minutes per hour, 60 seconds per minute
const secondsInterval = 24 * 60 * 60;
// 3 seconds per block
const blockTime = 3;
// 28800 blocks per day
const blockInterval = secondsInterval / blockTime;

To find the block time of a specific chain, check the chain's documentation website or block explorer. Most Etherscan deployments have a /chart/blocktime page.

Chain

The chain option for block intervals works the same way as it does for contracts. You can specify a different interval, startBlock and endBlock for each chain.

ponder.config.ts
import { createConfig } from "ponder";
 
export default createConfig({
  chains: { /* ... */ },
  blocks: {
    PointsAggregation: {
      chain: {
        mainnet: {
          startBlock: 19783636, 
          interval: (60 * 60) / 12, // Every 60 minutes (12s block time) 
        }, 
        optimism: { 
          startBlock: 119534316, 
          interval: (60 * 60) / 2, // Every 60 minutes (2s block time) 
        },
      },
    },
  },
});

Read more in the contracts guide.

Block range

The startBlock and endBlock options for block intervals work the same way as it does for contracts.

Read more in the contracts guide.