🎄

thegraphでcontract のinterfaceを変更する

2024/12/26に公開

contractのinterface変更

  • thegraphでは、abiをもとにtsファイルをbuildして
    event発生ごとにhandlerを実行する
  • hardforkや、upgradeなどによってcontractのinterfaceが変わった場合は新しいABIでsubgraphをbuildし直す必要がある
  • 変更をしない場合、thegraph実行時にエラーとなる

新abiで、旧abiの存在したblockを処理できない問題

  • 例えば、以下の図で300万blockの時に旧ABIから、新ABIに変更になったとする
  • その場合、300万block以降は、subgraph側で新ABIでbuildしなおさないとinterfaceが合わないerrorによってfeedが止まってしまう。
  • 逆に新abiでbuildした場合は、0-300万blockをthegraphで処理するとエラーになってしまう
  • thegraphのsubgraph.yamlにて、startBlockを300万にすると,300万block以前がdropされる仕様
  • 300万block以前のデータにはgraphqlなどでアクセスするとエラーになる

解決策1

  1. subgraphに読み込ませるabiに、新旧で同じ名前のfunctionを入れる
  2. 同じ名前のfunctionで定義違いのものがある場合 build時に function function1と二種類生成される
  3. ABIの変更が発生した blockで呼び出す処理を分岐する
    const network = getNetwork(overview.network);
    // ハードフォークかの判定
    const hardforkFlg = network.isHardforkReleased(block.number);

    // hardfordした場合 新ABIを利用
    if (hardforkFlg) {
      const result = MyContract.myfunction1(
        xxx,
        yyy,
      );
    ...
    } else {
      // 古ABIを利用
      const result = MyContract.myfunction(
        xxx,
      );
    ...
    }
  • 課題は、abiをcontractから生成したものをそのままコピペできない
  • 新旧contractのabiを組み合わせたものをsubgraphに読み込ませる必要があり

解決策2

https://thegraph.com/docs/en/subgraphs/cookbook/grafting/

  • grafting (接ぎ木)という機能を使う
  • Graftingはサブグラフのインデックスを分割して、既存のサブグラフを基に新しいサブグラフを作成する機能
  • 特定のブロック番号から新しいサブグラフを開始し、以前のデータを保持しつつ新しいデータをインデックスできる
  • old ABI でabi変更があったblockまでfeed、そこでエラーで止まる
  • grapftingの設定をして、再度deploy
  • 新ABIでfeedが先に進める
  • 新しいサブグラフでは、新しいABIを使用し、イベントハンドラーも新しいものに更新します。
  • 新しいサブグラフをデプロイすると、指定したブロックから新しいABIに基づいてインデックスが開始されます。
  ---
features:
  - grafting # feature name
graft:
  base: Qm... # subgraph ID of base subgraph
  block: 5956000 # block number

subgraph IDは、ipfsにdeployされた時のhash値のこと
デプロイメントIDと呼ばれIPFSのコンテンツアドレスでこれらは一意であり、デプロイごとに異なります。

postgres=> SELECT * FROM info.subgraph_info; 
 schema_id | schema_name |                    subgraph                    | version |     name      | status  | failed | synced 
-----------+-------------+------------------------------------------------+---------+---------------+---------+--------+--------
         4 | sgd4        | QmTtEWQJ4SK9yAREVuAvfLd4PAFJc8DNBwsHwQMJqFgGRy |       1 | oasys/staking | current | t      | f
(1 row)

subgraphをdeployするたびにsgd(num)が変更されてipfsにdeployされ直す

その他

  • 複数ABIの取り扱い: The Graphでは、単一のサブグラフ内で複数のABIを使用する場合、それぞれのABIに対応する別々のデータソース(dataSources)を定義し、それぞれに適用するブロック範囲を指定する必要があります。これにより、異なるABIを持つスマートコントラクトのバージョンを適切に処理できます。
  • データソースの分割: ABIが変更されたポイントで新しいデータソースを定義し、それぞれに異なるABIを適用する必要があります。具体的には、subgraph.yaml内で以下のようにデータソースを複数定義します。
dataSources:
  - kind: ethereum/contract
    name: MyContractOld
    network: mainnet
    source:
      address: "0xYourContractAddress"
      abi: MyContractOldABI
      startBlock: 0
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.5
      language: wasm/assemblyscript
      entities:
        - YourEntity
      abis:
        - name: MyContractOldABI
          file: ./abis/MyContractOld.json
      eventHandlers:
        - event: OldEvent(indexed uint256, ...)
          handler: handleOldEvent
      file: ./src/mappingsOld.ts

  - kind: ethereum/contract
    name: MyContractNew
    network: mainnet
    source:
      address: "0xYourContractAddress"
      abi: MyContractNewABI
      startBlock: 3000000
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.5
      language: wasm/assemblyscript
      entities:
        - YourEntity
      abis:
        - name: MyContractNewABI
          file: ./abis/MyContractNew.json
      eventHandlers:
        - event: NewEvent(indexed uint256, ...)
          handler: handleNewEvent
      file: ./src/mappingsNew.ts

Discussion