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.

ソース情報

リポジトリ
ccashwell/evm-cortex
ソースの最終更新活動
2026年4月10日 16:31
検出された SKILL.md の言語
英語
スター
131
フォーク
18

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
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
GitHubで見る