見出し画像

【保存版】The Graphでサブグラフを作ってみよう。

こんにちは、CryptoGamesの高橋です。

クリスペの会社です。

今回、サブグラフ作成のYouTubeがありましたので、それに沿って記事を作りました。

また、自分の以前の記事でもサブグラフの作り方をみておりますので、よかったらそちらもご参照ください。

では、やっていきます。

1 The Graph上でサブグラフを作ってみよう

まずは、下のようにサブグラフを作ってみます。

スクリーンショット 2022-01-19 5.46.05

スクリーンショット 2022-01-19 5.46.15

スクリーンショット 2022-01-19 5.46.32

スクリーンショット 2022-01-19 5.48.51

こんな感じでサブグラフが作成できました。

スクリーンショット 2022-01-19 5.49.03

2 graph-cliのインストール

次のようにインストールを行います。

npm install -g @graphprotocol/graph-cli

スクリーンショット 2022-01-19 6.02.14

エラーは特に発生していなさそうです。

念の為、「graph」と打ってみて、「welcome to ~ 」と表示されれば大丈夫です。

スクリーンショット 2022-01-19 6.03.51

3 新しいサブグラフの作成

「graph init」で新しいサブグラフを作成していきます。

ytakahashi@yuki subgraph % graph init --contract-name Token --index-events --product subgraph-studio \
> --from -contract 0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7

オプション・引数として次を設定します。

① コントラクト名
② 何をindexするか(events)
③ プロダクト(subgraph-studio)
④ 使用するコントラクト
 (今回は、zoraのコントラクトを使用しています。)

スクリーンショット 2022-01-19 6.11.19

その後、いくつか質問が出るので答えていきます。

スクリーンショット 2022-01-19 6.20.15

左のようなディレクトリができたら完成です。

4 schema.graphqlの作成

次に、schema.graphqlでイベントから何を取得するのかを書いていきます。

最初にあったものは消して、次のように作成しました。

スクリーンショット 2022-01-19 6.34.34

今回は

①Token
②User

という2つのタイプで値を取得していきます。

ちなみに、この2つは関連しています。

例えば、7行目の「creator」は「User」タイプであり、13行目のtokensは「Token」タイプです。(1対多の関係)

type Token @entity {
 id: ID!
 tokenId: BigInt!
 contentURI: String!
 metadataURI: String!
 createdAtTimestamp: BigInt!
 creator: User!
 owner: User!
}
type User @entity {
 id: ID!
 tokens: [Token!]! @derivedFrom(field: "owner")
 created: [Token!]! @derivedFrom(field: "creator")
}

5 subgraph.yaml

では次に、構成が書かれる「subgraph.yaml」を修正していきます。

まずは下のように「startBlock」を追加します。

スクリーンショット 2022-01-19 6.53.30

これにより、どのブロック以降の情報をとってくれば良いのかを指定できるので、無駄な処理がなくなります。

次は「entities」「eventHandlers」です。

スクリーンショット 2022-01-19 6.56.43

「entities」は上の章の「schema.graphql」で設定した際のタイプを選択し、「eventHandlers」では、不要なイベントを削除します。

6 graph codegenでtsファイルを作成

第4・5章で構成を設定できたので、これをtsファイルにします。

「subgraph.yaml」がある階層に移動し、

graph codegen

を入力すると、下のように2つのtsファイルができます。

スクリーンショット 2022-01-19 7.08.22

7 mapping.tsを作成

おそらく一番の肝である、mapping.tsをやっていきます。

ここでは、イベントを元にして、どのように必要なデータを取得していくのかを書いていきます。

こちらはコード量が多かったので、コードは記事の末尾に載せております。

7−1 import

まずはimportからです。

スクリーンショット 2022-01-19 7.20.17

このように、上の章で作成した2つのtsファイルからインポートを行います。

7ー2 functionの作成(token)

では、まずはTransferイベントによって発火する関数を書いていきましょう。

やっている処理しては、10行目の「token」「Token」から呼び込んだオブジェクトを格納しています。

スクリーンショット 2022-01-19 8.05.05

event.params.~で取得しているのはこのイベント自体(Transfer)がとってくるパラメータです。

下のToken.tsを見ると、

スクリーンショット 2022-01-19 8.13.16

このイベントによって

① from
② to
③ tokenId

を取ってくるのがわかり、このパラメータを利用しています。


一方、Tokenの内容は「schema.ts」にあり、これは第4章の「schema.graphql」で設定した内容です。

スクリーンショット 2022-01-19 8.11.00

Tokenには「id」を除いて

① tokenId
② contentURI
③ metadataURI
④ createdAtTimestamp
⑤ creator
⑥ owner

があり、mapping.tsの11行目以降で、tokenが取得できなかった時に、これらの値を入れています。

7ー3 functionの作成(user)

同様の処理ではありますが、userの方も見てみましょう。

スクリーンショット 2022-01-19 8.43.10

schema.tsUserを見てみましょう。

スクリーンショット 2022-01-19 8.46.21

あれ?3つ設定したような。。と思ったので、「schema.ts」の元となった「schema.graphql」を見てみましょう。

スクリーンショット 2022-01-19 8.58.25

これを見ると、確かに「ID」を含めて、3つ設定されておりますが、「Token」に紐づいているようです。

「handleTokenURIUpdated」関数については、ここでは省略します。(下にコードはあります。)

8 デプロイをしよう

mapping.tsもできたので、デプロイをしていきます。

デプロイの仕方はサブグラフに書いてあります。

スクリーンショット 2022-01-19 9.11.52

スクリーンショット 2022-01-19 9.12.02

こちらに書いてありますので、このまま実行すれば作成ができます。

下のように実行してみました。

スクリーンショット 2022-01-19 9.17.41

スクリーンショット 2022-01-19 9.17.56

少し待つと、このように同期も完了しました。

スクリーンショット 2022-01-19 9.46.32

9 実際にデータを取ってみよう

「playground」の紫のボタンを押してみましょう。

スクリーンショット 2022-01-19 9.49.06

このようにデータが取ってこれましたね。

また、取ってきたいデータや順番の入れ替えなどをしたい場合には、下のようにクエリを変更します。

スクリーンショット 2022-01-19 9.51.28

これも大丈夫そうです。

以上でサブグラフの作成について終了します。

最後までお読みいただき、ありがとうございました。

以下は、参考に「mapping.ts」のコードです。

import {
 TokenURIUpdated as TokenURIUpdatedEvent,
 Transfer as TransferEvent,
 Token as TokenContract,
} from "../generated/Token/Token";
import { Token, User } from "../generated/schema";
export function handleTransfer(event: TransferEvent): void {
 let token = Token.load(event.params.tokenId.toString());
 if (!token) {
   token = new Token(event.params.to.toString());
   token.creator = event.params.to.toHexString();
   token.tokenId = event.params.tokenId;
   token.createdAtTimestamp = event.block.timestamp;
   let tokenContract = TokenContract.bind(event.address);
   token.contentURI = tokenContract.tokenURI(event.params.tokenId);
   token.metadataURI = tokenContract.tokenMetadataURI(event.params.tokenId);
 }
 token.owner = event.params.to.toHexString();
 token.save();
 let user = User.load(event.params.to.toHexString());
 if (!user) {
   user = new User(event.params.to.toHexString());
   user.save();
 }
}
export function handleTokenURIUpdated(event: TokenURIUpdatedEvent): void {
 let token = Token.load(event.params._tokenId.toString());
 if (!token) {
   token = new Token(event.params._tokenId.toString());
   token.save();
 }
 token.contentURI = event.params._uri;
 token.save();
}

サポートをしていただけたらすごく嬉しいです😄 いただけたサポートを励みに、これからもコツコツ頑張っていきます😊