packages/cli/templates/static/shared/.claude/skills/indexing-external-calls/SKILL.md
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.
npx skillsauth add enviodev/hyperindex indexing-external-callsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
HyperIndex uses Preload Optimization — handlers run TWICE:
All external calls (fetch, RPC, APIs) MUST use the Effect API to prevent double execution and enable parallelization.
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();
}
);
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...
});
For non-effect side effects that should only run once:
Contract.Event.handler(async ({ event, context }) => {
const data = await context.effect(myEffect, input);
if (!context.isPreload) {
console.log("Processing event", event.block.number);
}
});
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.booleanS.schema({ field: S.string })S.array(S.string)S.union([S.string, null])S.optional(S.string)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.ENVIO_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) };
}
);
| Option | Type | Description |
|--------|------|-------------|
| name | string | Name for debugging/logging |
| input | S.Schema | Input validation schema |
| output | S.Schema | Output validation schema |
| cache | boolean | Cache results for identical inputs (default: false) |
| rateLimit | boolean \| { calls, per } | Rate limit calls (default: false) |
Full reference: https://docs.envio.dev/docs/HyperIndex-LLM/hyperindex-complete
development
Write and run tests for HyperIndex indexers using Vitest and createTestIndexer(). Covers test setup, processing block ranges, asserting entity changes with toMatchInlineSnapshot, and TDD workflow. Use when writing tests, debugging handler output, or verifying indexer behavior.
data-ai
Migrate a TheGraph subgraph to Envio HyperIndex using TDD. Covers schema conversion (remove @entity, Bytes->String, @derivedFrom), handler translation (save->set, store.get->context.get, templates->contractRegister), and verification against subgraph data.
data-ai
Use when indexing all instances of a contract across all addresses (e.g., all ERC-20 transfers on a chain). Config setup (no address), wildcard handler option, and event.srcAddress.
data-ai
Use when needing transaction-level data in handlers. Configure field_selection to include transaction fields on events, and access via event.transaction. No native transaction handler — access through event handlers.