my-indexer

enviodev's avatarfrom enviodev

This skill should be used when the user asks to "migrate from subgraph", "convert subgraph to hyperindex", "migrate from thegraph", "port subgraph", "convert subgraph handlers", "migrate assemblyscript to typescript", or mentions TheGraph, subgraph migration, subgraph.yaml conversion, or converting from TheGraph to Envio. For core HyperIndex development patterns, refer to the hyperindex-development skill.

⭐0stars🔀0forks📁View on GitHub🕐Updated Nov 27, 2025

When & Why to Use This Skill

This Claude skill provides a comprehensive framework for migrating blockchain indexers from TheGraph to Envio HyperIndex. It automates the transformation of subgraph.yaml configurations, GraphQL schemas, and AssemblyScript mapping logic into high-performance TypeScript-based HyperIndex implementations, enabling developers to achieve up to 100x faster indexing speeds with modern developer tooling.

Use Cases

  • Case 1: Converting legacy TheGraph subgraphs to Envio HyperIndex to significantly improve indexing performance and reduce data latency.
  • Case 2: Refactoring AssemblyScript event handlers into type-safe TypeScript code, including the implementation of async/await patterns and the Envio Effect API.
  • Case 3: Migrating GraphQL schemas by adjusting entity decorators, converting Bytes to String, and ensuring correct @derivedFrom relationship structures.
  • Case 4: Porting dynamic contract templates from TheGraph to Envio's contract registration pattern for multichain indexing support.
nameSubgraph Migration
descriptionThis skill should be used when the user asks to "migrate from subgraph", "convert subgraph to hyperindex", "migrate from thegraph", "port subgraph", "convert subgraph handlers", "migrate assemblyscript to typescript", or mentions TheGraph, subgraph migration, subgraph.yaml conversion, or converting from TheGraph to Envio. For core HyperIndex development patterns, refer to the hyperindex-development skill.
version1.0.0

Subgraph to HyperIndex Migration

Migrate from TheGraph subgraphs to Envio HyperIndex. HyperIndex delivers up to 100x faster indexing with a developer-friendly TypeScript API.

Migration Overview

Three major steps:

  1. subgraph.yaml → config.yaml
  2. Schema migration (near copy-paste)
  3. Event handler migration (AssemblyScript → TypeScript)

Step 1: Config Migration

subgraph.yaml → config.yaml

TheGraph:

specVersion: 0.0.4
dataSources:
  - kind: ethereum/contract
    name: Factory
    network: mainnet
    source:
      address: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"
      startBlock: 10000835
      abi: Factory
    mapping:
      eventHandlers:
        - event: PairCreated(indexed address,indexed address,address,uint256)
          handler: handlePairCreated
templates:
  - name: Pair
    source:
      abi: Pair

HyperIndex:

name: my-indexer
networks:
  - id: 1
    start_block: 10000835
    contracts:
      - name: Factory
        address: 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f
        handler: src/factory.ts
        events:
          - event: PairCreated(address indexed token0, address indexed token1, address pair, uint256)
      - name: Pair
        handler: src/pair.ts  # No address - dynamic
        events:
          - event: Swap(...)

Key differences:

  • Remove dataSources, templates, mapping nesting
  • Use networks → contracts structure
  • Event signatures include parameter names
  • Dynamic contracts have no address field

Step 2: Schema Migration

Remove @entity decorator

# TheGraph
type Token @entity {
  id: ID!
  symbol: String!
}

# HyperIndex
type Token {
  id: ID!
  symbol: String!
}

Convert Bytes to String

# TheGraph
address: Bytes!

# HyperIndex
address: String!

Entity Relationships

# TheGraph
type Transfer @entity {
  token: Token!
}

# HyperIndex - use _id suffix
type Transfer {
  token_id: String!
}

Entity Arrays MUST have @derivedFrom

# TheGraph (sometimes implicit)
type Token @entity {
  transfers: [Transfer!]!
}

# HyperIndex - REQUIRED explicit @derivedFrom
type Token {
  transfers: [Transfer!]! @derivedFrom(field: "token")
}

type Transfer {
  token_id: String!  # The field referenced by @derivedFrom
}

Critical: Arrays without @derivedFrom cause codegen error EE211.

Step 3: Handler Migration

Basic Pattern

TheGraph (AssemblyScript):

export function handleTransfer(event: TransferEvent): void {
  let entity = new Transfer(event.transaction.hash.toHexString());
  entity.from = event.params.from;
  entity.to = event.params.to;
  entity.amount = event.params.value;
  entity.save();
}

HyperIndex (TypeScript):

import { MyContract } from "generated";

MyContract.Transfer.handler(async ({ event, context }) => {
  const entity = {
    id: `${event.chainId}-${event.transaction.hash}-${event.logIndex}`,
    from: event.params.from,
    to: event.params.to,
    amount: event.params.value,
    blockNumber: BigInt(event.block.number),
  };

  context.Transfer.set(entity);
});

Entity Loading

TheGraph:

let token = Token.load(id);
if (token == null) {
  token = new Token(id);
}

HyperIndex:

let token = await context.Token.get(id);
if (!token) {
  token = { id, name: "Unknown", /* ... */ };
}

Entity Updates

TheGraph:

token.totalSupply = newSupply;
token.save();

HyperIndex (use spread - entities are immutable):

context.Token.set({
  ...token,
  totalSupply: newSupply,
});

Dynamic Contract Registration

TheGraph:

import { Pair as PairTemplate } from "../generated/templates";
PairTemplate.create(event.params.pair);

HyperIndex:

// Register BEFORE handler
Factory.PairCreated.contractRegister(({ event, context }) => {
  context.addPair(event.params.pair);
});

Factory.PairCreated.handler(async ({ event, context }) => {
  // Handle event...
});

Contract State (RPC Calls)

TheGraph:

let contract = ERC20.bind(address);
let name = contract.name();

HyperIndex (use Effect API):

import { createEffect, S } from "envio";

export const getTokenName = createEffect({
  name: "getTokenName",
  input: S.string,
  output: S.string,
  cache: true,
}, async ({ input: address }) => {
  // Use viem for RPC calls
  const name = await client.readContract({
    address: address as `0x${string}`,
    abi: ERC20_ABI,
    functionName: "name",
  });
  return name;
});

// In handler
const name = await context.effect(getTokenName, address);

Common Migration Issues

Missing async/await

// WRONG - returns {} instead of entity
const token = context.Token.get(id);

// CORRECT
const token = await context.Token.get(id);

Field Selection for Transaction Data

# Add to config.yaml for event.transaction.hash access
events:
  - event: Transfer(...)
    field_selection:
      transaction_fields:
        - hash

Multichain ID Prefixes

// Always prefix with chainId for multichain
const id = `${event.chainId}-${originalId}`;

BigDecimal Precision

Maintain precision from original subgraph:

import { BigDecimal } from "generated";

const ZERO_BD = new BigDecimal(0);
const ONE_BD = new BigDecimal(1);

// Don't simplify to JavaScript numbers

Migration Checklist

  • Convert subgraph.yaml to config.yaml
  • Remove @entity decorators from schema
  • Change Bytes! to String!
  • Use _id suffix for relationships
  • Add @derivedFrom to all entity arrays
  • Add async to all handlers
  • Add await to all context.Entity.get() calls
  • Use spread operator for entity updates
  • Replace Template.create() with contractRegister
  • Add field_selection for transaction data
  • Prefix IDs with chainId for multichain
  • Convert contract bindings to Effect API

Additional Resources

Reference Files

For detailed migration patterns:

  • references/migration-patterns.md - Complete pattern reference
  • references/common-mistakes.md - Pitfalls and solutions

External Resources