Files
gh-enviodev-envio-plugins-p…/skills/hyperindex-development/references/block-handlers.md
2025-11-29 18:26:05 +08:00

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.number and block.chainId available