3.2 KiB
3.2 KiB
Block Handlers
Run logic on every block or at intervals using the onBlock API (v2.29+).
Basic Usage
import { onBlock } from "generated";
onBlock(
{
name: "MyBlockHandler",
chain: 1,
},
async ({ block, context }) => {
context.log.info(`Processing block ${block.number}`);
}
);
Note: Block handlers don't require config changes or codegen runs.
Options
| Option | Required | Description |
|---|---|---|
name |
Yes | Handler name for logging/metrics |
chain |
Yes | Chain ID to run on |
interval |
No | Block interval (default: 1 = every block) |
startBlock |
No | Block to start from |
endBlock |
No | Block to end at |
Handler Function
Important: Block handlers require preload_handlers: true in config.yaml.
onBlock(
{ name: "HourlyStats", chain: 1, interval: 300 },
async ({ block, context }) => {
// block.number - The block number
// block.chainId - The chain ID
// context - Same as event handlers
}
);
Time-Based Intervals
Convert time to blocks:
// Every 60 minutes on Ethereum (12s blocks)
const interval = (60 * 60) / 12; // 300 blocks
// Every 60 minutes on Optimism (2s blocks)
const interval = (60 * 60) / 2; // 1800 blocks
Multichain Block Handlers
Use forEach for multiple chains:
import { onBlock } from "generated";
[
{ chain: 1 as const, startBlock: 19783636, interval: 300 },
{ chain: 10 as const, startBlock: 119534316, interval: 1800 },
].forEach(({ chain, startBlock, interval }) => {
onBlock(
{ name: "HourlyPrice", chain, startBlock, interval },
async ({ block, context }) => {
// Handle block...
}
);
});
Different Historical vs Realtime Intervals
Speed up historical sync with larger intervals:
const realtimeBlock = 19783636;
// Historical: every 1000 blocks
onBlock(
{
name: "HistoricalHandler",
chain: 1,
endBlock: realtimeBlock - 1,
interval: 1000,
},
async ({ block, context }) => { /* ... */ }
);
// Realtime: every block
onBlock(
{
name: "RealtimeHandler",
chain: 1,
startBlock: realtimeBlock,
interval: 1,
},
async ({ block, context }) => { /* ... */ }
);
Preset/Initial Data Handler
Load initial data on block 0:
onBlock(
{
name: "Preset",
chain: 1,
startBlock: 0,
endBlock: 0,
},
async ({ block, context }) => {
// Skip preload phase for initial data
if (context.isPreload) return;
const users = await fetch("https://api.example.com/users");
for (const user of users) {
context.User.set({
id: user.id,
address: user.address,
name: user.name,
});
}
}
);
Use Cases
- Hourly/Daily aggregations - Price snapshots, volume totals
- Time-series data - Create periodic data points
- Initial state loading - Populate entities on startup
- State snapshots - Capture state at intervals
Limitations
- Requires
preload_handlers: true - Ordered multichain mode not supported
- Only EVM chains (no Fuel)
- No test framework support yet
- Only
block.numberandblock.chainIdavailable