🎄
thegraphでcontract のinterfaceを変更する
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
- subgraphに読み込ませるabiに、新旧で同じ名前のfunctionを入れる
- 同じ名前のfunctionで定義違いのものがある場合 build時に function function1と二種類生成される
- 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
- 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