Subgraph Development Guide

SkillCloud & infra

Guide to building blockchain data indexes with The Graph — subgraph architecture, schema design, mapping handlers, deployment, and querying. Covers GraphQL APIs for DEX data, token transfers, and DeFi protocol events. Use when explaining blockchain indexing or building custom data pipelines.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Subgraph Development Guide skill

What this skill tells your AI

The instructions your AI receives, as published by nirholas/three.ws in data/skills/development/subgraph-development-guide/SKILL.md and read by ahel’s review.

The Graph is the indexing protocol for blockchain data. Subgraphs turn raw blockchain events into queryable GraphQL APIs. This guide covers how they work and how to build them.

Why Subgraphs?

The Problem

Reading blockchain data directly is painful:

  • RPC calls are slow and sequential
  • No aggregation (can't query "total volume last 7 days")
  • No relationships (can't join swaps with token metadata)
  • Rate limits on public RPCs

The Solution

Subgraphs listen to blockchain events, process them, and store structured data:

Blockchain Events → Subgraph Indexer → GraphQL API
(Swap, Transfer,    (Processes,        (Fast queries,
 Mint, Burn)         aggregates)        relationships)

Architecture

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│  1. Schema       │    │  2. Mappings     │    │  3. Manifest     │
│  (GraphQL types) │    │  (Event handlers)│    │  (subgraph.yaml) │
│                  │    │                  │    │                  │
│  type Token {    │    │  handleSwap(e) { │    │  dataSources:    │
│    id: ID!       │    │    // process    │    │    - name: Pool  │
│    symbol: String│    │    // and save   │    │      source:     │
│    volume: BigInt│    │  }              │    │        abi: Pool  │
│  }              │    │                  │    │        address:   │
└─────────────────┘    └─────────────────┘    └─────────────────┘

Three Core Files

FilePurpose
schema.graphqlDefine your data model (entities)
src/mapping.tsEvent handler functions (AssemblyScript)
subgraph.yamlManifest — which contracts, events, and chains to index

Building a Token Transfer Tracker

Step 1: Schema (schema.graphql)

type Token @entity {
  id: ID!                       # Contract address
  symbol: String!
  name: String!
  decimals: Int!
  totalSupply: BigInt!
  transferCount: BigInt!
  holderCount: BigInt!
}

type Transfer @entity {
  id: ID!                       # tx hash + log index
  token: Token!
  from: Bytes!
  to: Bytes!
  value: BigDecimal!
  timestamp: BigInt!
  blockNumber: BigInt!
}

type Account @entity {
  id: ID!                       # Wallet address
  balances: [AccountBalance!]! @derivedFrom(field: "account")
}

type AccountBalance @entity {
  id: ID!                       # account-token
  account: Account!
  token: Token!
  balance: BigDecimal!
}

Step 2: Manifest (subgraph.yaml)

specVersion: 0.0.5
schema:
  file: ./schema.graphql

dataSources:
  - kind: ethereum
    name: USDC
    network: arbitrum-one
    source:
      address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
      abi: ERC20
      startBlock: 100000000
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.7
      language: wasm/assemblyscript
      entities:
        - Token
        - Transfer
        - Account
      abis:
        - name: ERC20
          file: ./abis/ERC20.json
      eventHandlers:
        - event: Transfer(indexed address,indexed address,uint256)
          handler: handleTransfer
      file: ./src/mapping.ts

Step 3: Mapping (src/mapping.ts)

import { Transfer as TransferEvent } from "../generated/USDC/ERC20"
import { Token, Transfer, Account, AccountBalance } from "../generated/schema"
import { BigInt, BigDecimal } from "@graphprotocol/graph-ts"

export function handleTransfer(event: TransferEvent): void {
  // Load or create token
  let token = Token.load(event.address.toHexString())
  if (token == null) {
    token = new Token(event.address.toHexString())
    token.symbol = "USDC"
    token.name = "USD Coin"
    token.decimals = 6
    token.totalSupply = BigInt.fromI32(0)
    token.transferCount = BigInt.fromI32(0)
    token.holderCount = BigInt.fromI32(0)
  }
  token.transferCount = token.transferCount.plus(BigInt.fromI32(1))
  token.save()

  // Create transfer entity
  let transfer = new Transfer(
    event.transaction.hash.toHexString() + "-" + event.logIndex.toString()
  )
  transfer.token = token.id
  transfer.from = event.params.from
  transfer.to = event.params.to
  transfer.value = event.params.value.toBigDecimal().div(
    BigDecimal.fromString("1000000") // 6 decimals
  )
  transfer.timestamp = event.block.timestamp
  transfer.blockNumber = event.block.number
  transfer.save()
}

Querying Subgraphs

GraphQL Query Examples

Top tokens by transfer volume:

{
  tokens(first: 10, orderBy: transferCount, orderDirection: desc) {
    id
    symbol
    transferCount
  }
}

Recent large transfers:

{
  transfers(
    first: 20
    orderBy: timestamp
    orderDirection: desc
    where: { value_gt: "100000" }
  ) {
    from
    to
    value
    timestamp
    token { symbol }
  }
}

Account balances:

{
  account(id: "0x1234...") {
    balances {
      token { symbol }
      balance
    }
  }
}

Query Endpoints

NetworkHosted ServiceDecentralized
Ethereumapi.thegraph.com/subgraphs/name/...gateway.thegraph.com/api/...
ArbitrumSame patternSame pattern
Base/OptimismSame patternSame pattern

Popular Existing Subgraphs

SubgraphWhat It IndexesUseful For
Uniswap V3Pools, swaps, liquidity positionsDEX price data, volume
Aave V3Deposits, borrows, liquidationsLending market data
Balancer V2Pools, swaps, BPT balancesMulti-asset pool data
ENSDomain registrations, transfersName resolution
SperaxUSDs transfers, SPA stakingSperax ecosystem data

Deployment

The Graph Studio (Decentralized)

# Install CLI
npm install -g @graphprotocol/graph-cli

# Initialize
graph init --studio my-subgraph

# Authenticate
graph auth --studio YOUR_DEPLOY_KEY

# Build + Deploy
graph codegen
graph build
graph deploy --studio my-subgraph

Self-Hosted Graph Node

For full control:

# Docker Compose
docker-compose up graph-node postgres ipfs

# Deploy to local
graph create --node http://localhost:8020/ my-subgraph
graph deploy --node http://localhost:8020/ my-subgraph

Performance Tips

TipWhy
Use startBlock wiselyDon't index from block 0 — start from contract deployment
Avoid call handlersThey're 10x slower than event handlers
Use BigDecimal for pricesAvoid precision loss with BigInt division
Batch entity loadingUse store.get() sparingly in hot paths
Index only what you needMore entities = slower indexing

Alternative Indexing Solutions

ToolApproachBest For
The GraphDecentralized, GraphQLProduction DeFi data
GoldskyManaged subgraph hosting + streamingHigh-performance queries
EnvioHyperIndex — fast parallel indexingSpeed-critical applications
PonderTypeScript framework for indexingDeveloper-friendly, type-safe
Dune AnalyticsSQL on decoded blockchain dataAnalytics and dashboards

Agent Tips

  1. Use existing subgraphs first — don't build one if Uniswap/Aave already has what you need
  2. Decentralized network is production — hosted service is being sunset
  3. Start with events, not calls — event handlers are much faster
  4. Schema design matters — think about what queries you need before designing entities
  5. Multi-chain — deploy the same subgraph to multiple networks for cross-chain data
  6. Sperax data: USDs transfer volumes, SPA staking events, and veSPA locking can all be indexed with subgraphs on Arbitrum

Links

Signals

GitHub stars
114
Forks
29
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
subgraph-development-guide
Source
github.com/nirholas/three.ws