The GraphというSaaSを使うと、Ethereum上のイベントをGraphQLで取得することができます。3年ほど前に同じことをしようとしたら、せこせことノードにリクエストを投げていたのですが、今は非常に便利になって他サービスに乗っかることができるようになっています。
せこせこと投げた記事
The Graph,実際に触ったので、誰かの役に立てばと備忘しておきます。The Graph概説はHashHubリサーチでも書かれているようです。(本文有料)
The Graphとは
The GraphはEthereum上のイベントをGraphQLで取得することができるSaaSです。SaaSでありつつ、トークンを発行してエコシステムを回しています。
GraphにはSubGraphという、指定したコントラクトからイベントを抜き出すための定義マッピングを作成し、Graphのサーバーにデプロイします。クライアントサイドからGraphのサーバーにリクエストを投げることで、イベントのレスポンスを受け取れます。
The Graphでリクエストを投げるまでの流れ
The Graphでリクエストを投げるまでの流れは以下になります。
今回はリクエスト準備までの1~6までそれぞれ説明していきます。なお、ブロックチェーンはRinkebyネットワークを利用します。ローカルのganacheではうまくいきませんでした。基本的には以下のチュートリアルに基づいて実行しています。
環境 macOS Big Sur version 11.3 CPU: Apple M1 node v15.14.0 Truffle v5.3.4 yarn 1.22.10
コントラクトのデプロイ
この記事を読む人であれば、コントラクトデプロイはできると思うのでこちらは省略します。
なお僕はtruffleで、 truffle migrate
でデプロイしました。
graphノードの起動
graphを落としてきて、dockerでノードを動かします。
$ git clone https://github.com/graphprotocol/graph-node/ $ cd graph-node/docker $ ./setup.sh $ docker compose up
docker-compose up
に成功するとpostgres, ipfs, graphが起動します。
$ docker-compose up Docker Compose is now in the Docker CLI, try `docker compose up` Starting docker_postgres_1 ... done Starting docker_ipfs_1 ... done Recreating docker_graph-node_1 ... done Attaching to docker_ipfs_1, docker_postgres_1, docker_graph-node_1 ipfs_1 | Changing user to ipfs ipfs_1 | ipfs version 0.4.23 ipfs_1 | Found IPFS fs-repo at /data/ipfs ipfs_1 | Initializing daemon... ipfs_1 | go-ipfs version: 0.4.23-6ce9a35 ipfs_1 | Repo version: 7 ipfs_1 | System version: amd64/linux ipfs_1 | Golang version: go1.12.16 postgres_1 | postgres_1 | PostgreSQL Database directory appears to contain a database; Skipping initialization postgres_1 | ipfs_1 | Swarm listening on /ip4/127.0.0.1/tcp/4001
プロジェクト作成
Graphのプロジェクトを作成します。
# Yarn $ yarn global add @graphprotocol/graph-cli $ graph init --from-example <GITHUB_USERNAME>/<SUBGRAPH_NAME> <DIRECTORY>
成功すると以下のようにログが出ます。
$ graph init --from-example naomasabit/hogehoge hogehoge ✔ Cloning example subgraph ✔ Update subgraph name and commands in package.json ✔ Initialize subgraph repository ✔ Install dependencies with yarn ✔ Generate ABI and schema types with yarn codegen Subgraph naomasabit/hogehoge created in hogehoge Next steps: 1. Run `graph auth https://api.thegraph.com/deploy/ <access-token>` to authenticate with the hosted service. You can get the access token from https://thegraph.com/explorer/dashboard/. 2. Type `cd hogehoge` to enter the subgraph. 3. Run `yarn deploy` to deploy the subgraph to https://thegraph.com/explorer/subgraph/naomasabit/hogehoge. Make sure to visit the documentation on https://thegraph.com/docs/ for further information. # hogehogeを確認 $ cd hogehoge # テンプレートが色々作られている $ ls LICENSE abis contracts migrations package.json src truffle.js README.md bin generated node_modules schema.graphql subgraph.yaml yarn.lock
SubGraphの設定
yamlの中は以下のようになっています。
が必要です。
$ less subgraph.yaml specVersion: 0.0.2 description: Gravatar for Ethereum repository: https://github.com/graphprotocol/example-subgraph schema: file: ./schema.graphql dataSources: - kind: ethereum/contract name: Gravity network: mainnet # 1. rinkebyに変える source: address: '0x2E645469f354BB4F5c8a05B3b30A929361cf77eC' # 2. デプロイしたコントラクトに変える abi: Gravity # コントラクトabiの名に変える mapping: kind: ethereum/events apiVersion: 0.0.4 language: wasm/assemblyscript entities: - Gravatar # 3. GraphQLのEntityを記載する abis: - name: Gravity file: ./abis/Gravity.json # 4. abiのjsonに変える eventHandlers: # 5. コントラクトのイベント名と後述のmapping.tsで定義するhandlerメソッドをそれぞれ指定します。 - event: NewGravatar(uint256,address,string,string) handler: handleNewGravatar - event: UpdatedGravatar(uint256,address,string,string) handler: handleUpdatedGravatar file: ./src/mapping.ts
ABIからSubGraph,GraphQLのコード自動生成
ABIからSubGraphに使うGraphQLのコードを自動生成します。
% yarn codegen yarn run v1.22.10 $ graph codegen Skip migration: Bump mapping apiVersion from 0.0.1 to 0.0.2 Skip migration: Bump mapping apiVersion from 0.0.2 to 0.0.3 Skip migration: Bump mapping apiVersion from 0.0.3 to 0.0.4 Skip migration: Bump mapping specVersion from 0.0.1 to 0.0.2 ✔ Apply migrations ⚠ Warnings while loading subgraph from subgraph.yaml: Warnings in subgraph.yaml: Path: repository The repository is still set to https://github.com/graphprotocol/example-subgraph. Please replace it with a link to your subgraph source code. Path: description The description is still the one from the example subgraph. Please update it to tell users more about your subgraph. ✔ Load subgraph from subgraph.yaml Load contract ABI from abis/Gravity.json ✔ Load contract ABIs Generate types for contract ABI: Gravity (abis/Gravity.json) Write types to generated/Gravity/Gravity.ts ✔ Generate types for contract ABIs ✔ Generate types for data source templates ✔ Load data source template ABIs ✔ Generate types for data source template ABIs ✔ Load GraphQL schema from schema.graphql Write types to generated/schema.ts ✔ Generate types for GraphQL schema Types generated successfully ✨ Done in 2.42s.
そうすると、GeneratedディレクトリにGraphQLのコードができています。
generated % ls Gravity schema.ts generated % ls Gravity Gravity.ts
schema.tsは以下のようになります。内容はサンプルabiですが、実際にはコントラクトのABIからイベントを取得するためのスキーマ定義が書かれます。
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. import { TypedMap, Entity, Value, ValueKind, store, Address, Bytes, BigInt, BigDecimal } from "@graphprotocol/graph-ts"; export class Gravatar extends Entity { constructor(id: string) { super(); this.set("id", Value.fromString(id)); } save(): void { let id = this.get("id"); assert(id !== null, "Cannot save Gravatar entity without an ID"); assert( id.kind == ValueKind.STRING, "Cannot save Gravatar entity with non-string ID. " + 'Considering using .toHex() to convert the "id" to a string.' ); store.set("Gravatar", id.toString(), this); } static load(id: string): Gravatar | null { return store.get("Gravatar", id) as Gravatar | null; } get id(): string { let value = this.get("id"); return value.toString(); } set id(value: string) { this.set("id", Value.fromString(value)); }
mapping.tsの作成
mapping.tsを温かみのある手作業で作成します。前述した subgraph.yaml
のeventHandlersにここに書かれるhandlerが設定されます。
import { NewGravatar, UpdatedGravatar } from '../generated/Gravity/Gravity' import { Gravatar } from '../generated/schema' export function handleNewGravatar(event: NewGravatar): void { let gravatar = new Gravatar(event.params.id.toHex()) gravatar.owner = event.params.owner gravatar.displayName = event.params.displayName gravatar.imageUrl = event.params.imageUrl gravatar.save() } export function handleUpdatedGravatar(event: UpdatedGravatar): void { let id = event.params.id.toHex() let gravatar = Gravatar.load(id) if (gravatar == null) { gravatar = new Gravatar(id) } gravatar.owner = event.params.owner gravatar.displayName = event.params.displayName gravatar.imageUrl = event.params.imageUrl gravatar.save() }
SubGraphデプロイ
Graphエクスプローラーのアカウントを作成し、マイページから Add Subgraph
を生成すると、SubGraphの作成ができます。情報を入力して登録ます。
マイページにあるAuth Codeを用いて認証を行った後、yarn deploy
をすると、SubGraphがデプロイできます。
$ yarn deploy
これでデプロイされ、疎通ができる状態になります。エンドポイントなどはマイページから取得します。
まとめ
イベントの取得を定期的に実行するのが非常に楽になったサービスです。Dappsを作成するときにこのサービスは非常に有用になると思います。なお、The Graphを試すにあたっては「試して学ぶスマートコントラクト開発」著者の篠原さんに色々と教えていただきました。感謝!
初めてのGraphQL ―Webサービスを作って学ぶ新世代API
- 作者:Eve Porcello,Alex Banks
- 発売日: 2019/11/13
- メディア: 単行本(ソフトカバー)