Babysitter subgraph-indexing
Subgraph development for The Graph protocol. Includes manifest configuration, GraphQL schema design, AssemblyScript handlers, entity relationships, and deployment to hosted and decentralized networks.
install
source · Clone the upstream repo
git clone https://github.com/a5c-ai/babysitter
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/a5c-ai/babysitter "$T" && mkdir -p ~/.claude/skills && cp -r "$T/library/specializations/cryptography-blockchain/skills/subgraph-indexing" ~/.claude/skills/a5c-ai-babysitter-subgraph-indexing && rm -rf "$T"
manifest:
library/specializations/cryptography-blockchain/skills/subgraph-indexing/SKILL.mdsource content
Subgraph Indexing Skill
Subgraph development for The Graph protocol, enabling efficient blockchain data indexing and querying.
Capabilities
- Manifest Configuration: Write subgraph.yaml files
- Schema Design: Define GraphQL schemas for entities
- Event Handlers: Implement AssemblyScript handlers
- Entity Relationships: Handle derived fields and relations
- Local Testing: Test with Graph Node locally
- Deployment: Deploy to hosted and decentralized networks
- Performance: Optimize indexing performance
- Reorg Handling: Handle chain reorganizations
Installation
# Install Graph CLI npm install -g @graphprotocol/graph-cli # Verify graph --version
Project Setup
Initialize Subgraph
# Initialize from existing contract graph init --product subgraph-studio \ --from-contract 0x... \ --network mainnet \ --abi ./abi.json \ my-subgraph cd my-subgraph
Project Structure
my-subgraph/ ├── subgraph.yaml # Manifest ├── schema.graphql # GraphQL schema ├── src/ │ └── mapping.ts # Event handlers ├── abis/ │ └── Contract.json # Contract ABIs ├── tests/ │ └── contract.test.ts # Unit tests └── package.json
Manifest (subgraph.yaml)
specVersion: 0.0.5 schema: file: ./schema.graphql dataSources: - kind: ethereum name: Token network: mainnet source: address: "0x..." abi: Token startBlock: 18000000 mapping: kind: ethereum/events apiVersion: 0.0.7 language: wasm/assemblyscript entities: - Transfer - Account abis: - name: Token file: ./abis/Token.json eventHandlers: - event: Transfer(indexed address,indexed address,uint256) handler: handleTransfer - event: Approval(indexed address,indexed address,uint256) handler: handleApproval callHandlers: - function: transfer(address,uint256) handler: handleTransferCall blockHandlers: - handler: handleBlock file: ./src/mapping.ts
GraphQL Schema
# schema.graphql type Token @entity { id: ID! name: String! symbol: String! decimals: Int! totalSupply: BigInt! holders: [Account!]! @derivedFrom(field: "token") } type Account @entity { id: ID! token: Token! balance: BigInt! transfersFrom: [Transfer!]! @derivedFrom(field: "from") transfersTo: [Transfer!]! @derivedFrom(field: "to") } type Transfer @entity { id: ID! from: Account! to: Account! value: BigInt! timestamp: BigInt! blockNumber: BigInt! transactionHash: Bytes! } # For aggregations type DailyVolume @entity { id: ID! # date string date: BigInt! volume: BigInt! txCount: Int! }
AssemblyScript Handlers
// src/mapping.ts import { Transfer as TransferEvent } from "../generated/Token/Token"; import { Token, Account, Transfer, DailyVolume } from "../generated/schema"; import { BigInt, Bytes, Address } from "@graphprotocol/graph-ts"; export function handleTransfer(event: TransferEvent): void { // Create Transfer entity let transfer = new Transfer( event.transaction.hash.toHex() + "-" + event.logIndex.toString() ); transfer.from = getOrCreateAccount(event.params.from).id; transfer.to = getOrCreateAccount(event.params.to).id; transfer.value = event.params.value; transfer.timestamp = event.block.timestamp; transfer.blockNumber = event.block.number; transfer.transactionHash = event.transaction.hash; transfer.save(); // Update account balances updateAccountBalance(event.params.from, event.params.value.neg()); updateAccountBalance(event.params.to, event.params.value); // Update daily volume updateDailyVolume(event.block.timestamp, event.params.value); } function getOrCreateAccount(address: Address): Account { let id = address.toHex(); let account = Account.load(id); if (account == null) { account = new Account(id); account.token = "token-id"; account.balance = BigInt.fromI32(0); account.save(); } return account; } function updateAccountBalance(address: Address, delta: BigInt): void { let account = getOrCreateAccount(address); account.balance = account.balance.plus(delta); account.save(); } function updateDailyVolume(timestamp: BigInt, value: BigInt): void { let dayId = timestamp.toI32() / 86400; let id = dayId.toString(); let volume = DailyVolume.load(id); if (volume == null) { volume = new DailyVolume(id); volume.date = BigInt.fromI32(dayId * 86400); volume.volume = BigInt.fromI32(0); volume.txCount = 0; } volume.volume = volume.volume.plus(value); volume.txCount = volume.txCount + 1; volume.save(); }
Data Source Templates
# subgraph.yaml - for factory patterns templates: - kind: ethereum name: Pool network: mainnet source: abi: Pool mapping: kind: ethereum/events apiVersion: 0.0.7 language: wasm/assemblyscript entities: - Pool - Swap abis: - name: Pool file: ./abis/Pool.json eventHandlers: - event: Swap(indexed address,uint256,uint256) handler: handleSwap file: ./src/pool.ts
// src/factory.ts import { Pool as PoolTemplate } from "../generated/templates"; export function handlePoolCreated(event: PoolCreated): void { // Create data source for new pool PoolTemplate.create(event.params.pool); // Create Pool entity let pool = new Pool(event.params.pool.toHex()); pool.save(); }
Build and Deploy
# Generate code from schema graph codegen # Build subgraph graph build # Authenticate graph auth --studio <deploy-key> # Deploy to Subgraph Studio graph deploy --studio my-subgraph # Deploy to hosted service (deprecated) graph deploy --node https://api.thegraph.com/deploy/ \ --ipfs https://api.thegraph.com/ipfs/ \ username/my-subgraph
Querying
# Example queries query RecentTransfers { transfers(first: 10, orderBy: timestamp, orderDirection: desc) { id from { id balance } to { id balance } value timestamp } } query TopHolders { accounts(first: 10, orderBy: balance, orderDirection: desc) { id balance } } query DailyVolumes { dailyVolumes(first: 30, orderBy: date, orderDirection: desc) { date volume txCount } }
Testing
// tests/token.test.ts import { assert, test, clearStore } from "matchstick-as"; import { handleTransfer } from "../src/mapping"; import { createTransferEvent } from "./utils"; test("Creates Transfer entity", () => { let event = createTransferEvent( "0x1234...", "0x5678...", "1000000000000000000" ); handleTransfer(event); assert.entityCount("Transfer", 1); clearStore(); });
Process Integration
| Process | Purpose |
|---|---|
| Subgraph development |
| Indexing pipelines |
| Data integration |
Best Practices
- Use appropriate startBlock to avoid reindexing
- Implement efficient entity updates
- Handle reorgs with immutable entities
- Use derived fields for relations
- Test with Matchstick
- Monitor indexing performance
See Also
- dApp integrationskills/wallet-integration/SKILL.md
- Frontend expertagents/web3-frontend/AGENT.md- The Graph Documentation