Skip to main content

subgraph-patterns

Use when building or maintaining The Graph subgraphs. Covers subgraph.yaml manifest, schema.graphql design, AssemblyScript mappings, event handlers, data source templates, testing with matchstick, deployment, and indexing optimization.

Quellinformationen

Repository
ccashwell/evm-cortex
Letzte Quellaktivität
10. April 2026 um 16:31
Erkannte Sprache von SKILL.md
Englisch
Sterne
131
Forks
18

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
subgraph-patterns
description
Use when building or maintaining The Graph subgraphs. Covers subgraph.yaml manifest, schema.graphql design, AssemblyScript mappings, event handlers, data source templates, testing with matchstick, deployment, and indexing optimization.
# The Graph Subgraph Patterns ## Project Structure ``` my-subgraph/ ├── subgraph.yaml # Manifest: data sources, event handlers ├── schema.graphql # Entity definitions (GraphQL) ├── src/ │ └── mappings.ts # Event handler logic (AssemblyScript) ├── tests/ │ └── mappings.test.ts # Matchstick unit tests ├── abis/ │ └── MyContract.json # Contract ABIs └── networks.json # Multi-network config ``` ## Subgraph Manifest (subgraph.yaml) ```yaml specVersion: 1.0.0 indexerHints: prune: auto schema: file: ./schema.graphql dataSources: - kind: ethereum name: MyProtocol network: base source: address: "0xContractAddress" abi: MyProtocol startBlock: 12345678 # Block contract was deployed mapping: kind: ethereum/events apiVersion: 0.0.9 language: wasm/assemblyscript entities: - Deposit - Withdrawal - User abis: - name: MyProtocol file: ./abis/MyProtocol.json - name: ERC20 file: ./abis/ERC20.json eventHandlers: - event: Deposit(indexed address,uint256,uint256) handler: handleDeposit - event: Withdrawal(indexed address,uint256) handler: handleWithdrawal file: ./src/mappings.ts templates: - kind: ethereum name: Vault network: base source: abi: Vault mapping: kind: ethereum/events apiVersion: 0.0.9 language: wasm/assemblyscript entities: - VaultDeposit abis: - name: Vault file: ./abis/Vault.json eventHandlers: - event: VaultDeposit(indexed address,uint256) handler: handleVaultDeposit file: ./src/vault-mappings.ts ``` ## Schema Design (schema.graphql) ```graphql type Protocol @entity { id: Bytes! # Use Bytes! for addresses totalDeposited: BigDecimal! totalUsers: BigInt! vaults: [Vault!]! @derivedFrom(field: "protocol") } type User @entity { id: Bytes! # user address deposits: [Deposit!]! @derivedFrom(field: "user") totalDeposited: BigDecimal! firstDepositAt: BigInt! lastActiveAt: BigInt! } type Vault @entity { id: Bytes! # vault address protocol: Protocol! asset: Bytes! totalAssets: BigDecimal! deposits: [Deposit!]! @derivedFrom(field: "vault") createdAt: BigInt! createdAtBlock: BigInt! } type Deposit @entity(immutable: true) { id: Bytes! # tx hash + log index user: User! vault: Vault! amount: BigDecimal! shares: BigInt! timestamp: BigInt! blockNumber: BigInt! transactionHash: Bytes! } type DailySnapshot @entity { id: String! # "YYYY-MM-DD" date: BigInt! totalDeposited: BigDecimal! depositCount: BigInt! uniqueUsers: BigInt! } ``` Key schema patterns: - Use `@entity(immutable: true)` for event logs (faster indexing) - Use `@derivedFrom` for reverse lookups (no storage cost) - Use `Bytes!` for addresses, `BigDecimal!` for token amounts - Composite IDs for uniqueness: `txHash + logIndex` ## AssemblyScript Mappings ```typescript import { BigDecimal, BigInt, Bytes, Address } from "@graphprotocol/graph-ts"; import { Deposit as DepositEvent, Withdrawal } from "../generated/MyProtocol/MyProtocol"; import { Deposit, User, Protocol, Vault } from "../generated/schema"; import { Vault as VaultTemplate } from "../generated/templates"; let ZERO_BD = BigDecimal.zero(); let ZERO_BI = BigInt.zero(); let ONE_BI = BigInt.fromI32(1); function getOrCreateProtocol(): Protocol { let protocol = Protocol.load(Bytes.fromHexString("0x01")); if (protocol == null) { protocol = new Protocol(Bytes.fromHexString("0x01")); protocol.totalDeposited = ZERO_BD; protocol.totalUsers = ZERO_BI; } return protocol; } function getOrCreateUser(address: Address, timestamp: BigInt): User { let user = User.load(address); if (user == null) { user = new User(address); user.totalDeposited = ZERO_BD; user.firstDepositAt = timestamp; user.lastActiveAt = timestamp; let protocol = getOrCreateProtocol(); protocol.totalUsers = protocol.totalUsers.plus(ONE_BI); protocol.save(); } return user; } export function handleDeposit(event: DepositEvent): void { let user = getOrCreateUser(event.params.user, event.block.timestamp); let amount = event.params.amount.toBigDecimal().div(BigDecimal.fromString("1e18")); let deposit = new Deposit( event.transaction.hash.concatI32(event.logIndex.toI32()) ); deposit.user = user.id; deposit.vault = event.address; deposit.amount = amount; deposit.shares = event.params.shares; deposit.timestamp = event.block.timestamp; deposit.blockNumber = event.block.number; deposit.transactionHash = event.transaction.hash; deposit.save(); user.totalDeposited = user.totalDeposited.plus(amount); user.lastActiveAt = event.block.timestamp; user.save(); let protocol = getOrCreateProtocol(); protocol.totalDeposited = protocol.totalDeposited.plus(amount); protocol.save(); } ``` ## Data Source Templates For dynamically created contracts (e.g., factory-deployed vaults): ```typescript import { VaultCreated } from "../generated/Factory/Factory"; import { Vault as VaultTemplate } from "../generated/templates"; import { Vault } from "../generated/schema"; export function handleVaultCreated(event: VaultCreated): void { // Start indexing the new vault contract VaultTemplate.create(event.params.vault); let vault = new Vault(event.params.vault); vault.protocol = Bytes.fromHexString("0x01"); vault.asset = event.params.asset; vault.totalAssets = BigDecimal.zero(); vault.createdAt = event.block.timestamp; vault.createdAtBlock = event.block.number; vault.save(); } ``` ## Testing with Matchstick ```typescript import { assert, test, clearStore, beforeEach } from "matchstick-as/assembly/index"; import { Address, BigInt, Bytes, ethereum } from "@graphprotocol/graph-ts"; import { handleDeposit } from "../src/mappings"; import { Deposit } from "../generated/MyProtocol/MyProtocol"; function createDepositEvent(user: string, amount: BigInt, shares: BigInt): Deposit { let event = changetype<Deposit>(newMockEvent()); event.parameters = new Array(); event.parameters.push(new ethereum.EventParam("user", ethereum.Value.fromAddress(Address.fromString(user)))); event.parameters.push(new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount))); event.parameters.push(new ethereum.EventParam("shares", ethereum.Value.fromUnsignedBigInt(shares))); return event; } beforeEach(() => { clearStore(); }); test("handleDeposit creates entities", () => { let event = createDepositEvent("0x0000000000000000000000000000000000000001", BigInt.fromString("1000000000000000000"), BigInt.fromString("1000000000000000000")); handleDeposit(event); assert.entityCount("Deposit", 1); assert.entityCount("User", 1); }); ``` ## Deployment ```bash # Build graph codegen && graph build # Deploy to Subgraph Studio graph auth --studio $DEPLOY_KEY graph deploy --studio my-subgraph # Deploy to hosted service (deprecated, use Studio) graph deploy --node https://api.thegraph.com/deploy/ my-org/my-subgraph ``` ## Querying ```graphql { deposits(first: 10, orderBy: timestamp, orderDirection: desc) { id user { id totalDeposited } amount timestamp } protocol(id: "0x01") { totalDeposited totalUsers } } ``` Pagination for large datasets: ```graphql { deposits(first: 1000, where: { id_gt: "0xlastId" }, orderBy: id) { id amount } } ``` ## Performance Tips - Mark event entities as `@entity(immutable: true)` — 30-50% faster indexing - Use `Bytes` over `String` for addresses and hashes - Minimize `store.get` calls — batch reads where possible - Set `startBlock` to contract deployment block (skip empty blocks) - Use `indexerHints.prune: auto` to reduce storage - Avoid expensive BigDecimal math in hot paths
Auf GitHub ansehen