ブロックチェーン方面の The Graph を試してみる (for developers)

Running through [Camilia's workshop](https://camiinthisthang.hashnode.dev/the-complete-guide-to-getting-started-with-the-graph) with (sometimes) translation into Japanese.

Camila さんによるチュートリアル記事

https://camiinthisthang.hashnode.dev/the-complete-guide-to-getting-started-with-the-graph

を やってみました。たまに翻訳してここに載せます。
あと、今この記事はサンフランシスコのホテルの中で書いているのですが、もしかしたら英語圏の知人が読んでくれるかもしれないので、たまに英語も併記します（最初だけ）

# Note：この記事は開発者向けです

Note: This article is for developers who builds a subgraph using Graph Hosted Service. I don't mention about GRT token in this article. I know it's very important to ensure economic security of The Graph Network and the integrity of data being queried, but I'm not familiar with it, so I'll talk about only developing a subgraph.

> グラフネットワークの経済的な安全性と、クエリデータの完全性を確保するために、

The Graph は ERC-20 の独自トークン (グラフトークン(GRT)) を持っていますが、
この私の記事にはその **独自トークンのことは一切出てきません** 。
Graph のサステナブルなエコシステムを保つために重要な存在ですが、私はその辺は詳しくないので、**あくまで開発の話だけ** します。

詳細）　

https://thegraph.com/docs/ja/about/network/

# What is The Graph

You can see what The Graph is in the official document [here](https://thegraph.com/docs/en/about/introduction/).

The Graph とは、公式ドキュメントより

> The Graph は、Ethereum をはじめとするブロックチェーンのデータをインデックス化してクエリするための分散型プロトコルです。 これにより、直接クエリすることが困難のデータのクエリが容易に可能になります。

https://thegraph.com/docs/ja/about/introduction/

要するに、Developer にとっての The Graph は、

* GraphQL を使用してさまざまなブロックチェーンからのデータにインデックスを付けてクエリを実行するためのプロトコル
* ブロックチェーンにデプロイされたスマートコントラクトから情報をクエリする手段を可能にする、web3 スタックの API レイヤー
* それらの API は「サブグラフ」と呼ばれ、みんなからデプロイされたサブグラフは [subgraph explorer](https://thegraph.com/explorer) から見ることができる

という感じです。(本当はもっと色々あるんだけど今回は省略)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/76a8c1ef-ecef-f03d-0a42-b31af602ffdb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/d625b58e-20fe-0ff3-f616-000a7b0cd8c2.png)

pict：Reprinted from  [公式ドキュメント](https://thegraph.com/docs/ja/about/introduction/)より

## サブグラフ

サブグラフ（Developer が作る API 的なやつ）には、以下が定義されています：

* the data source (smart contracts)
  * データソース（スマートコントラクト）
* the data that should be indexed
    * インデックスされる必要のあるデータ
* the events you want to listen for
    * listen したいイベント
* resolvers to execute when specified events are emitted which transform the raw data from the blockchain and save it to The Graph Node.
    * 指定したイベントが発火したときに実行されるリゾルバー。ブロックチェーンからの生データを良い感じに変換して The Graph ノードに保存してくれる人。

## 補足）名前空間の衝突

(As I'm familiar with Microsoft tech stack, I thought The Graph has something to do with [Microsoft Graph](https://docs.microsoft.com/en-us/graph/overview?WT.mc_id=spatial-8948-machiy), which has nothing to do with it. I guess it's came from GraphQL.)

とくに私みたいなマイクロソフト系技術スタックに慣れていると「グラフ? [Microsoft Graph](https://docs.microsoft.com/ja-jp/graph/overview?WT.mc_id=spatial-8948-machiy) のことか？」ってなるかもしれませんが、（私はなった）、 **まｔｔｔｔｔｔｔったく別のもの** です。ほぼ同じ名前ですが全く別のサービスです。マイクロソフト全然関係ないです。GraphQL から来た名前です。

# How to get started

クイックスタートは公式サイトに書いてあります

https://thegraph.com/docs/ja/developer/quick-start/

# Workshop チュートリアル

Camila さんの公開している、サブグラフ作成のチュートリアルを進めてみました

https://github.com/camiinthisthang/thegraph-workshop

このチュートリアルで作成されるサブグラフを実際に叩いてみるには：

https://thegraph.com/hosted-service/subgraph/chomado/fame-lady-tutorial?query=Example%20query

## 手順 1. Hosted Service

The Graph の Hosted Service にサインアップ

## 2. Add subgraph

“My Dashboard” から "Add subgraph" 。
作成時、私はここで「Graph Explorer で隠す (検索結果に表示しない)」トグルを on にしました。

![イラスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/84571b50-07b8-2348-5e76-4532bd89b1ba.png)


## 3. Install The Graph CLI 

The Graph CLI をインストールします

```sh
npm install -g @graphprotocol/graph-cli
```

## 4. Initialize your subgraph via the CLI command

次は既存のコントラクトからサブグラフを初期化します。

今回は、このチュートリアルの作者さんのお気に入りの NFT プロジェクト FameLadySquad (FLS) のアドレス `0xf3E6DbBE461C6fa492CeA7Cb1f5C5eA660EB1B47` を指定しています

Etherscan で見る：
https://etherscan.io/address/0xf3e6dbbe461c6fa492cea7cb1f5c5ea660eb1b47

```sh
graph init --from-contract 0xf3E6DbBE461C6fa492CeA7Cb1f5C5eA660EB1B47 \ --contract-name Token --index-events
```

実行時の様子（VSCode のターミナル）
![Animation2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/4312116e-366d-74a4-b3a8-c237e07b5c5c.gif)

## 5. Define your schema using the GraphQL interface definition language

作成されたプロジェクトの中身を見てみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/7c428cc1-efab-9231-b0d5-7b2ac4601694.png)

(↑ `.gitignore` は git 管理用に私が後から追加したものです。)

サブグラフのスキーマは、`schema.graphql` というファイルに書いてあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/18173b2a-2efd-3220-e911-a65ec195bf9c.png)

今回は (このチュートリアル作者の Camila さんのお気に入り NFT プロジェクトである) [Fame Lady Squad](https://etherscan.io/token/0xf3e6dbbe461c6fa492cea7cb1f5c5ea660eb1b47) のサブグラフを作るので、それらのプロパティに合わせるため、
この `schema.graphql` ファイルを以下のものに置き換えます。（既存の Approval とか ApprovalForAll とかもう要らないので、全部上書きで）

```graphql
type Lady @entity {
  id: ID!
  tokenID: BigInt!
  tokenURI: String! 
  owner: User
  name: String 
  faceExpression: String
  hairStyle: String
  skinColor: String
  eyeColor: String
}

type User @entity {
  id: ID! # wallet address 
  Ladies: [Lady!] @derivedFrom(field: "owner")
}
```

### もし既存のを消さなかったらどうなるの？

もし既存の（いらないプロパティ）を消さなかったらどうなるのか

消さなかったらデプロイ後のスキーマがこうなります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/a5775eef-4c6c-7cde-6686-dde390dff609.png)

でも欲しいのは Lady と User だけなので、それ以外を消します。
すると（[デプロイ後の画面](https://thegraph.com/hosted-service/subgraph/chomado/fame-lady-tutorial)で）こうなります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/42932c58-067d-c666-e62f-e95a9bcad493.png)

全体
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/4fdffb3f-6b03-6115-938c-a8acf851c062.png)

https://thegraph.com/hosted-service/subgraph/chomado/fame-lady-tutorial

### 各プロパティについて

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1646085059410/jTdvpXsjx.png)

↑ FameLadySquad には いくつかの種類の trait (特性) がある。この trait の組み合わせによって作られているコレクション

(The pict above: Reprinted from "[The Complete Guide to Getting Started With The Graph](https://camiinthisthang.hashnode.dev/the-complete-guide-to-getting-started-with-the-graph)" written by Camila)

### メタデータ

メタデータはこちらから見れます（生 json）
https://ipfs.io/ipfs/QmTngWTnURuyiz1gtoY33FKghCiU2uQusXpnUc36QJNKsY/1002

```json
{"name": "Fame Lady #1002", "description": "Fame Lady Squad is the first ever generative all-female avatar collection on the Ethereum blockchain. Yes, we are THE community who took over a project to write our own story. This is NFT history. This is HERstory. FLS are 8888 distinctive Ladies made up of millions of fierce trait combinations. Community = Everything. Commercial IP rights of each Lady NFT belong to its owner.", "image": "ipfs://QmRRRcbfE3fTqBLTmmYMxENaNmAffv7ihJnwFkAimBP4Ac/1002.png", "attributes": [{"trait_type": "Skin", "value": "Olive"}, {"trait_type": "Background", "value": "White"}, {"trait_type": "Face Expression", "value": "Pokerface"}, {"trait_type": "Eyebrows", "value": "Stylish"}, {"trait_type": "Eyes", "value": "Pink"}, {"trait_type": "Nose Piercing Lower", "value": "Silver Ring"}, {"trait_type": "Body", "value": "Small Chain"}, {"trait_type": "Clothes", "value": "Green Dress"}, {"trait_type": "Hair", "value": "Straight Black"}, {"trait_type": "Elf Ear", "value": "Elf"}]}
```

## 6. `graph codegen`

```sh
cd .\fame-lady-tutorial\
graph codegen
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/78e95847-a6e5-a683-aab9-707d467eb463.png)

これを実行すると、`mappings.ts` の中に AssemblyScript を書いてくれます

## 7. `.yaml` ファイルを編集

`subgraph.yaml` ファイルで、以下のものを定義します：

* listen (監視) したい event。
  - イベントリスナー的な。
  - 逆に、(`subgraph.yaml` 内の)  `dataSources.mapping.eventHandlers` からは listen したくないイベントはすべて削除します。
  - それぞれのイベントでは、`mappings.ts` で定義した `handler` 関数がそれぞれ紐づいています
* インデクサーがインデックス付けを開始する必要のある最初のブロック。
  - 省略した場合、サブグラフはいちばん最初のブロックからイベントの検索を開始します
  - 書く場合、Etherscan でトランザクションの最初のページまで行って、`transaction ID` をクリックしてブロックナンバーを探しましょう
* 情報をクエリするエンティティの名前。
    - `dataSources.mapping.entities` で定義したスキーマにあわせて、あなたのエンティティの名前をアップデートします

```yaml:subgraph.yaml
specVersion: 0.0.2
schema:
  file: ./schema.graphql
features:
  - ipfsOnEthereumContracts
dataSources:
  - kind: ethereum
    name: Token
    network: mainnet
    source:
      address: "0xf3E6DbBE461C6fa492CeA7Cb1f5C5eA660EB1B47"
      abi: Token
      startBlock: 12813031
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.5
      language: wasm/assemblyscript
      entities:
        - Lady
        - User
      abis:
        - name: Token
          file: ./abis/Token.json
      eventHandlers:
        - event: Transfer(indexed address,indexed address,indexed uint256)
          handler: handleTransfer
      file: ./src/mapping.ts
```

## 8. Write mappings in AssemblyScript 

最後に、AssemblyScript でマッピングを記述します。
これにより、ブロックチェーン（イーサリアム）から読み取られた生データが、スキーマで定義されたエンティティに変換されます。

### この Fame Lady の例）

ブロックチェーンから取得する生データは、ERC-721 実装に標準で付属している getter メソッドで取ってきます。
つまり、このブロックチェーンから直接取ってこれるデータは `tokenID` と `owner` だけです。

でも私たちの定義したスキーマでは以下も欲しいです：
* `hairStyle`
* `skinClolor`
* `eyeColor`
* `faceExpression`

`tokenID` を使い、メタデータがどこにストアされているか見るために、IPFS ハッシュが必要となります。これにアクセスしたら、このデータを変数に保存してから、この変数を The Graph ノードに保存して、クエリできるようにする必要があります。

src 下に mappning.ts を作ります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/e2a365b7-a51c-0fb6-316d-cc63d1d69b49.png)


```ts:src/mapping.ts
import {
  Transfer as TransferEvent
} from "../generated/Token/Token";
import { Lady, User } from "../generated/schema";
import { log, ipfs, json, JSONValue } from "@graphprotocol/graph-ts";

const ipfsHash = "QmTngWTnURuyiz1gtoY33FKghCiU2uQusXpnUc36QJNKsY";

export function handleTransfer(event: TransferEvent): void {
  let lady = Lady.load(event.params.tokenId.toString());

  if (lady == null) {
    lady = new Lady(event.params.tokenId.toString());
    lady.tokenID = event.params.tokenId;
    lady.tokenURI = "/" + event.params.tokenId.toString();

    let metadata = ipfs.cat(ipfsHash + lady.tokenURI);
    if (metadata) {
      const value = json.fromBytes(metadata).toObject();
      if (value) {
        const name = value.get("name");
        if (name) {
          lady.name = name.toString();
        }
      }
      let attributes: JSONValue[];
      let ladyAttributes = value.get("attributes");
      if (ladyAttributes) {
        attributes = ladyAttributes.toArray();

        for (let i = 0; i < attributes.length; i++) {
          let item = attributes[i].toObject();
          let trait: string;
          let traitName = item.get("trait_type");
          if (traitName) {
            trait = traitName.toString();
            let value: string;
            let traitValue = item.get("value");
            if (traitValue) {
              value = traitValue.toString();
              if (trait == "Hair") {
                lady.hairStyle = value;
              }
              if (trait == "Skin") {
                lady.skinColor = value;
              }
              if (trait == "Eyes") {
                lady.eyeColor = value;
              }
              if (trait == "Face Expression") {
                lady.faceExpression = value;
              }
            }
          }
        }
      }
    }
  }
  lady.owner = event.params.to.toHexString();
  lady.save();

  let user = User.load(event.params.to.toHexString());
  if (!user) {
    user = new User(event.params.to.toHexString());
    user.save();
  }
}
```

## 9. Authenticate with your deploy key

デプロイの準備をします。

↓ "My Dashboard" にある、アクセストークンの場所（オレンジのバーでで隠してあるところ）

![](https://camo.qiitausercontent.com/07ab0bf650283359e5c6ef51cede5f8728a85933/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f32343630392f38343537316235302d303762382d323334382d356537362d3435333262643839623162612e706e67)

"My Dashboard" からアクセストークンを持ってきて、これをターミナルで叩きます

```sh
graph auth https://api.thegraph.com/deploy/ <アクセストークン>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/8ca089f0-da42-7bb4-a815-c960f8a37680.png)

## 10: Deploy your subgraph

デプロイします

```sh
npm run deploy
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/83a585ba-7c3d-ff1d-ffbc-229bf59da93d.png)

### エラー?

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/400c7a28-2a3e-8177-b54b-5c355bfb7f35.png)

もし
`subgraph validation error: [The feature 'ipfsOnEthereumContracts' is used by the subgraph but it is not declared in the manifest.]` 
というエラーがでたら、

[英語の原文](https://camiinthisthang.hashnode.dev/the-complete-guide-to-getting-started-with-the-graph) の `subgraph.yaml` では
```yaml
features:
  - ipfsOnEthereumContracts
```
が書かれていませんが、書いてください。（この私の記事に書いたコードは追記済みです）



# すべてのソースコード

https://github.com/camiinthisthang/fameladysquad-subgraph