AgentSkillsCN

indexing-external-calls

在进行RPC调用、Fetch请求,或从处理器中发起任何外部I/O操作时使用。Effect API配合createEffect,S Schema校验,context.effect(),预加载优化(处理器运行两次),以及缓存与限流选项。

SKILL.md
--- frontmatter
name: indexing-external-calls
description: >-
  Use when making RPC calls, fetch requests, or any external I/O from handlers.
  Effect API with createEffect, S schema validation, context.effect(), preload
  optimization (handlers run twice), cache and rateLimit options.

External Calls (Effect API)

Why Effects?

HyperIndex uses Preload Optimization — handlers run TWICE:

  1. Preload pass: all handlers in the batch run in parallel (to warm caches)
  2. Sequential pass: handlers run in event order (actual state changes)

All external calls (fetch, RPC, APIs) MUST use the Effect API to prevent double execution and enable parallelization.

Defining an Effect

ts
import { S, createEffect } from "envio";

export const getSomething = createEffect(
  {
    name: "getSomething",
    input: {
      address: S.string,
      blockNumber: S.number,
    },
    output: S.union([S.string, null]),
    cache: true,
    rateLimit: false,
  },
  async ({ input, context }) => {
    const something = await fetch(
      `https://api.example.com/something?address=${input.address}&blockNumber=${input.blockNumber}`
    );
    return something.json();
  }
);

Consuming in Handlers

ts
import { getSomething } from "./utils";

Contract.Event.handler(async ({ event, context }) => {
  const something = await context.effect(getSomething, {
    address: event.srcAddress,
    blockNumber: event.block.number,
  });
  // Use the result...
});

context.isPreload Guard

For non-effect side effects that should only run once:

ts
Contract.Event.handler(async ({ event, context }) => {
  const data = await context.effect(myEffect, input);

  if (!context.isPreload) {
    console.log("Processing event", event.block.number);
  }
});

S Schema Module

The S module exposes a schema creation API for input/output validation: https://raw.githubusercontent.com/DZakh/sury/refs/tags/v9.3.0/docs/js-usage.md

Common schemas:

  • S.string, S.number, S.boolean
  • S.schema({ field: S.string })
  • S.array(S.string)
  • S.union([S.string, null])
  • S.optional(S.string)

RPC Call Pattern (viem)

ts
import { createEffect, S } from "envio";
import { createPublicClient, http, parseAbi } from "viem";

const ERC20_ABI = parseAbi([
  "function name() view returns (string)",
  "function symbol() view returns (string)",
  "function decimals() view returns (uint8)",
]);

const client = createPublicClient({
  transport: http(process.env.RPC_URL),
});

export const getTokenMetadata = createEffect(
  {
    name: "getTokenMetadata",
    input: S.string,
    output: S.schema({
      name: S.string,
      symbol: S.string,
      decimals: S.number,
    }),
    cache: true,
  },
  async ({ input: address }) => {
    const [name, symbol, decimals] = await Promise.all([
      client.readContract({ address: address as `0x${string}`, abi: ERC20_ABI, functionName: "name" }),
      client.readContract({ address: address as `0x${string}`, abi: ERC20_ABI, functionName: "symbol" }),
      client.readContract({ address: address as `0x${string}`, abi: ERC20_ABI, functionName: "decimals" }),
    ]);
    return { name, symbol, decimals: Number(decimals) };
  }
);

Effect Options

OptionTypeDescription
namestringName for debugging/logging
inputS.SchemaInput validation schema
outputS.SchemaOutput validation schema
cachebooleanCache results for identical inputs (default: false)
rateLimitboolean | { calls, per }Rate limit calls (default: false)

Deep Documentation

Full reference: https://docs.envio.dev/docs/HyperIndex-LLM/hyperindex-complete