Open13

The Graph 入門

ピン留めされたアイテム
odanodan

Developer 視点の The Graph まとめ

  • Indexer を自作しなくて済むので嬉しい
  • 「コントラクトのイベント」=>「GraphQL のスキーマ」の変換をする関数を書けばよしなに永続化される
    • ドキュメントを読むとコントラクトのイベントの他にも発火点はありそうだった
  • GraphQL のスキーマからある程度のボイラープレートは生成してくれるので開発者体験としても良い
odanodan
yarn graph auth
cd test
yarn codegen
yarn build
yarn deploy

デプロイした

odanodan

同期中
普通に考えると結構時間かかりそう

odanodan

ちゃんとログ出ててすごい
2000ブロックずつ sync しているっぽい

odanodan

進捗グラフは GraphQL の Subscription で更新されている

odanodan
subgraph.yaml
specVersion: 0.0.2
schema:
  file: ./schema.graphql
dataSources:
  - kind: ethereum/contract
    name: Loot
    network: mainnet
    source:
      address: "0xff9c1b15b16263c61d017ee9f65c50e4ae0113d7"
      abi: Loot
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.4
      language: wasm/assemblyscript
      entities:
        - Approval
        - ApprovalForAll
        - OwnershipTransferred
        - Transfer
      abis:
        - name: Loot
          file: ./abis/Loot.json
      eventHandlers:
        - event: Approval(indexed address,indexed address,indexed uint256)
          handler: handleApproval
        - event: ApprovalForAll(indexed address,indexed address,bool)
          handler: handleApprovalForAll
        - event: OwnershipTransferred(indexed address,indexed address)
          handler: handleOwnershipTransferred
        - event: Transfer(indexed address,indexed address,indexed uint256)
          handler: handleTransfer
      file: ./src/mapping.ts

subgraph.yaml に設定を書くらしい
この設定だと ./src/mapping.ts で export している関数 (e.g. handleTransfer) とコントラクトの Event をマッピングしている

kind 使ってたりして、k8s の マニフェストぽさを感じる

odanodan

サンプルの ./src/mapping.ts はこんな感じ

import { BigInt } from "@graphprotocol/graph-ts";
import {
  Loot,
  Approval,
  ApprovalForAll,
  OwnershipTransferred,
  Transfer,
} from "../generated/Loot/Loot";
import { ExampleEntity } from "../generated/schema";

export function handleApproval(event: Approval): void {
  // Entities can be loaded from the store using a string ID; this ID
  // needs to be unique across all entities of the same type
  let entity = ExampleEntity.load(event.transaction.from.toHex());

  // Entities only exist after they have been saved to the store;
  // `null` checks allow to create entities on demand
  if (entity == null) {
    entity = new ExampleEntity(event.transaction.from.toHex());

    // Entity fields can be set using simple assignments
    entity.count = BigInt.fromI32(0);
  }

  // BigInt and BigDecimal math are supported
  entity.count = entity.count + BigInt.fromI32(1);

  // Entity fields can be set based on event parameters
  entity.owner = event.params.owner;
  entity.approved = event.params.approved;

  // Entities can be written to the store with `.save()`
  entity.save();

  // Note: If a handler doesn't require existing field values, it is faster
  // _not_ to load the entity from the store. Instead, create it fresh with
  // `new Entity(...)`, set the fields that should be updated and save the
  // entity back to the store. Fields that were not set or unset remain
  // unchanged, allowing for partial updates to be applied.

  // It is also possible to access smart contracts from mappings. For
  // example, the contract that has emitted the event can be connected to
  // with:
  //
  // let contract = Contract.bind(event.address)
  //
  // The following functions can then be called on this contract to access
  // state variables and other data:
  //
  // - contract.balanceOf(...)
  // - contract.getApproved(...)
  // - contract.getChest(...)
  // - contract.getFoot(...)
  // - contract.getHand(...)
  // - contract.getHead(...)
  // - contract.getNeck(...)
  // - contract.getRing(...)
  // - contract.getWaist(...)
  // - contract.getWeapon(...)
  // - contract.isApprovedForAll(...)
  // - contract.name(...)
  // - contract.owner(...)
  // - contract.ownerOf(...)
  // - contract.supportsInterface(...)
  // - contract.symbol(...)
  // - contract.tokenByIndex(...)
  // - contract.tokenOfOwnerByIndex(...)
  // - contract.tokenURI(...)
  // - contract.totalSupply(...)
}

export function handleApprovalForAll(event: ApprovalForAll): void {}

export function handleOwnershipTransferred(event: OwnershipTransferred): void {}

export function handleTransfer(event: Transfer): void {}
odanodan

コメントにある通り、コントラクトのメソッドを呼び出すことができる

const loot = Loot.bind(event.address)
loot.balanceOf(...)
odanodan

entity.save() を呼び出すと The Graph 上にデータが永続されて、その内容を GraphQL で query できる

サンプルだと次のような形

odanodan

ExampleEntity はスキーマから自動生成されたクラス

GraphQL の mutation の抽象化(具体化?)と言えそう