🧊

【SubGraph作成からフロント表示まで】TheGraphの始め方

2022/09/26に公開

The Graph について

スマートコントラクトのEventに合わせて情報を整形し、保存しAPIとして返してくれるサービスです。The Graphを用いることで、スマコンの情報を直接getter関数で読みにいったり、フロントで無駄に整形せずに済みます。

記事の内容について

The Graphの基本的な使い方が分かるハンズオンになっています。実際のコントラクトや内容に関しては、こちらの記事を引用させて頂いております。その上で動かない部分やわかりづらい部分に説明を加えて紹介する形にしております。
https://qiita.com/toshiaki_takase/items/761435120d7ca9c7ff6c

Step1 -The Graphの登録&プロジェクト作成-

まずはThe Graphの公式ページ右上「product」から「SubGraph Studio」を選択しMetamaskを接続していきます。
https://thegraph.com/en/

SubGraph Studio右上から「Create Project」を選択することで、SubGraphの元を作成することができます。そうするとAPI作成に必要なsubgraph slagdeploy keyなどを取得することができます。

Step2 -contractをデプロイ&ABI取得-

  1. Remixを使ってcontractをデプロイ
  2. HardhatでcontractをコンパイルしABIを取得

RemixとHardhatの両方を使う理由は、Remixで出力したABIをTheGraphが読み込んでくれなかったことと、Hardhatでデプロイする場合INFURAなどの登録が必要な外部サービスが必要になるため、ハンズオンとして適切ではないと思い、両方使うことにしました。

Remixにコードを貼りつけてデプロイ

Remixはブラウザで簡単にSolidityを書けるツールです。今回はGoerliでデプロイしていきます。デプロイする際にコインの名前とシンボルを入力する必要があるので、名前を「TestCoin」、シンボルを「TC」にしてデプロイしました。(命名は自由です)

https://remix.ethereum.org/

coin.sol
pragma solidity ^0.8.9;

import "@OpenZeppelin/contracts/token/ERC20/ERC20.sol";

contract AdventCalendarToken is ERC20 {
    uint8 private _decimals = 18;

    address account = msg.sender;
    uint value = 10000000000000000000000;

    constructor(string memory _name, string memory _symbol) ERC20( _name, _symbol) public {
        _mint(account, value);
    }
}

Remixでデプロイしたcontractアドレスとブロックナンバーを確認してメモ

コントラクトアドレスとデプロイしたブロックナンバーを後で使うのでメモしておきます。ブロックナンバーはRemixのコンソールログに書いてあります。

Hardhatの初期設定

とりあえずRemixは横に置きつつ、HardhatでABIを取得していきます。自分はVScodeを使っています。

#メインディレクトリの作成
mkdir the-graph-test
cd the-graph-test

#package.jsonの作成
npm init --yes

#hardhatのインストール
npm install --save-dev hardhat

#hardhatの初期化
#※Create an empty hardhat.config.jsを選択
npx hardhat

スマコンファイルの作成

#solファイルの作成
touch coin.sol

#@OpenZeppelin/contractsのインストール
npm install @openzeppelin/contracts

スマコンの記述

coin.sol
pragma solidity ^0.8.9;

import "@OpenZeppelin/contracts/token/ERC20/ERC20.sol";

contract AdventCalendarToken is ERC20 {
    uint8 private _decimals = 18;

    address account = msg.sender;
    uint value = 10000000000000000000000;

    constructor(string memory _name, string memory _symbol) ERC20( _name, _symbol) public {
        _mint(account, value);
    }
}

スマコンのコンパイル&デプロイ

#hardhatでスマコンをコンパイル
npx hardhat compile

ファイルからABIを確認

artifacts/[コントラクトファイル名]/の中に[コントラクト名].jsonが生成されていることを確認してください。これがTheGraphに読み込ませるABIファイルです。

Step3 -TheGraphの準備-

TheGraphの初期設定

#必要なライブラリをインストール
npm install -g @graphprotocol/graph-cli

#初期設定
graph init --studio coin

初期設定では利用するチェーンやコントラクトアドレス、ABIのパスなど色々聞かれます。特に自分が苦戦したのはABIで、上記の[コントラクト名].jsonを選択して「Copy Path」で貼り付けると上手くいきます。

返ってきてほしいデータへの整形ロジック

schema.graphql
type AccountTokenBalance @entity {
  id: ID! 
  token: String!
  balance: BigInt!
}
#schema.graphqlから自動で生成させる
graph codegen
coin.ts
import { BigInt } from "@graphprotocol/graph-ts";
import { Transfer } from "../generated/AdventCalendarToken/AdventCalendarToken";
import { AccountTokenBalance } from "../generated/schema";

// Transferのイベントが発火された時に実行されるfunction
export function handleTransfer(event: Transfer): void {
  let tokenId = event.address.toHex();
  let toAddress = event.params.to.toHex();
  let toAccount = AccountTokenBalance.load(toAddress);
  if (toAccount == null) {
    toAccount = new AccountTokenBalance(toAddress);
    toAccount.balance = BigInt.fromI32(0);
  }
  toAccount.balance = toAccount.balance.plus(event.params.value);
  toAccount.token = tokenId;
  toAccount.save();

  let fromAddress = event.params.from.toHex();
  if (fromAddress == "0x0000000000000000000000000000000000000000") {
    return;
  }

  let fromAccount = AccountTokenBalance.load(fromAddress);
  if (fromAccount == null) {
    fromAccount = new AccountTokenBalance(fromAddress);
    fromAccount.balance = BigInt.fromI32(0);
  }
  fromAccount.balance = fromAccount.balance.minus(event.params.value);
  fromAccount.token = tokenId;
  fromAccount.save();
}

注意:

  • httpリクエスト系は送れません。(他のAPIは呼べない)
  • typescriptみたいな感じですが微妙に違うためライブラリが使えないことがある。
  • キャストの仕方が微妙に違う。

キャストや整形に関する参考記事
https://thegraph.com/docs/en/developing/assemblyscript-api/

デプロイしてAPIを作成

#デプロイキーで認証
graph auth --studio 363563505......5f7cd1179b7

#coinファイルに移動
cd coin

#ビルド
graph codegen && graph build

#デプロイ
graph deploy --studio yomiswap

APIを確認してメモ

https://api.studio.thegraph.com/query/34004/[プロジェクト名]/[バージョン]

Step4 -フロントでAPIを呼び出す-

ReactかNextのプロジェクトを作成

#Reactの場合
npx create-react-app coin

#Nextの場合
npx create-next-app coin

_app.jsxにAPIを登録

#The Graphに必要なライブラリを追加
npm install @apollo/client
_app.jsx
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'

const client = new ApolloClient({
  cache: new InMemoryCache(),
  uri: 'https://api.studio.thegraph.com/query/34004/[プロジェクト名]/[バージョン名]',
})

function App({pageProps}){
  return(
    <ApolloProvider client={client}>
      <Component {...pageProps} >
    </ApolloProvider>
  )
}

graphqlファイルを準備

graphql/subgraph.js
import { gql } from "apollo-boost";

const GET_TRANSFERS = gql`
  {
    accountTokenBalances(orderBy: balance) {
      id
      token
      balance
    }
  }

export default GET_TRANSFERS;

表示したいコンポーネントに実装

Test.jsx
import { useQuery } from '@apollo/client'
import GET_TRANSFERS from '../../../graphql/subgraph'

const Test = () => {
  const { loading, error, data } = useQuery(GET_TRANSFERS)
  const [eventList, setEventList] = useState<History[]>([])
  
  useEffect(() => {
    if (!loading && !error && data) {
      setTokenHolders(data.accountTokenBalances);
      console.log(data);
    }
  }, [loading, error, data]);
}

終わりに

よく友人のエンジニアから「TheGraphに興味があるけど始められない...」というお話をよく聞くので、そこの敷居が少しでも下がらないかと思い、書かせて頂きました。これで少しでもTheGraphを触れるエンジニアが増えることを祈ります。

シンシズモ株式会社

Discussion