📖

ThirdwebのNFT Dropコントラクトで作られたNFTのメタデータを更新する

に公開

はじめに

ダッシュボードのメタデータ更新画面から変更できるかと思いきやエラーが発生。
めんどくさい手順を取らざるを得ず、後から同じことやる時に絶対迷うので手順を残しておく。

手順概要

  1. 更新したいNFTのメタデータファイルをIPFSからダウンロード
  2. メタデータファイルを修正
  3. 修正したメタデータをThirdweb CLIでアップロード
  4. コントラクトのメタデータURIを更新
  5. (オプション)Alchemy APIのキャッシュ更新

1. 更新したいNFTのメタデータファイルをIPFSからダウンロード

まずはメタデータファイルをダウンロードするためにCIDを確認する。
確認方法は2パターン。
まず一つ目。
Top > Usage > Storage を開くと過去にPinしたディレクトリのCIDが表示される。
Pin日時を元に該当のCIDを探す。

該当のディレクトリのCIDが見つけられなければ二つ目のtokenURIから調べる方法を取る。
Top > Overview > 該当のプロジェクト > Contract > 該当のNFT Dropコントラクト > Explorer > Read > tokenURI
を選択し、更新したいNFTのトークンIDを入力することでtokenURIからCIDを確認。

ちなみにthirdwebは画面からBatch Uploadを行う単位でメタデータファイルをディレクトリにまとめてIPFSに登録している
なので10個のNFTを一度Batch Uploadで登録し、その後追加で10個登録した場合は各NFTのtokenURIは以下のようになる

tokenId tokenURI
0:  ipfs://AAAAAA/0
1:  ipfs://AAAAAA/1
...
8:  ipfs://AAAAAA/8
9:  ipfs://AAAAAA/9
10: ipfs://BBBBBB/10
11: ipfs://BBBBBB/11
...
18: ipfs://BBBBBB/18
19: ipfs://BBBBBB/19

今回の例では15-19の修正が必要である前提で話を進めていく。

CIDがBBBBBBのメタデータファイルを更新したいため、IPFSからディレクトリごとデータをダウンロードする。

方法は何でもいいが、自分は以下のコマンドでファイルを1個ずつwgetコマンドで取得した。

wget -r -np -nH --cut-dirs=1 "https://ipfs.io/ipfs/<CID>?limit=500"

ipfsコマンドでディレクトリを一括でダウンロードを試みたのだが、一向に終わる気配が無かったので1件ずつ落とす方向性に切り替え。
ちなみにwget方式でも時間は相当かかる。
パブリックゲートウェイを使ってるのが原因なので、途中からゲートウェイのURLをPinataで発行した自分用のモノに差し替えてスピードアップを図った。

ちなみにデフォルトだと1ページあたり100件しか取得できず、limitパラメータを付与して1ページで対象のファイルが全件取れるようにする必要がある。
ただしlimitパラメータも上限が500のため、501件以上ある場合は&page=2のURLパラメータを付与してページングしながら取得しなければならない。

wget が遅い問題の対策としてthirdwebが提供しているIPFSも試したが、いざアクセスしてみると以下のエラーが発生。ドキュメントに従ってClient IDも指定したが上手くいかなかった。

The keys are invalid. Please check the secret-key/clientId and try again.

てかCLIからじゃないとディレクトリ単位でのダウンロードが出来ないの本当に不便。
ブラウザからササっとダウンロードできるようになって欲しい。

2. メタデータファイルの修正

前の手順でダウンロードしたディレクトリ内に、メタデータファイルが存在するため、
これらのファイルをテキストエディタ等で開いて必要な箇所を修正する。

複数のファイルにわたって特定の文字列を一括で置換したい場合はsed コマンドが便利。
ダウンロードしたディレクトリ内で以下コマンドを実行すると、全ファイルに含まれる"abc" を"efg" に一括置換可能。

for file in [0-9]*; do if [ -f "$file" ]; then sed -i '' 's/"abc"/"efg"/g' "$file"; fi; done

3. 修正したメタデータのアップロード

正直Pinataでやるのが圧倒的に楽なのだが、Thirdwebで発行したNFTのメタデータはThirdwebで管理したいので、CLIを使って修正したファイルをアップロードする。
昔はダッシュボードからアップロードできた気もしたのだが、あまり手軽に使われるとthirdweb側で負担するコストがでかくなるから敢えて使いづらくしたのかも。

npm i -g thirdweb

でthirdwebをグローバルインストール。
環境汚したくない人はどこかのプロジェクト内でインストールするでも良い。

今度はthirdwebのダッシュボードに移り、
Top > Overview > 該当のプロジェクト > Project Settings > Secret Key
からシークレットキーを取得する。

取得したら以下のコマンドでシークレットキーを指定してアップロード。

thirdweb upload <修正したメタデータが入っているディレクトリ> -k <シークレットキー>

4. メタデータ更新用の関数を実行

BaseURIとインデックスの対応

前述の通りメタデータのCIDはバッチアップロードした単位で管理されており、今回トークンID 15~19 を修正したい場合でも、それらが含まれるバッチ全体(例: CIDがBBBBBB、トークンID 10~19)をまとめて新しいメタデータ(CID: CCCCCCC)で更新する必要がある。

ThirdwebのコントラクトではメタデータのインデックスとCID(のベースURI)の対応が管理されており、

インデックス 0: ipfs://AAAAAA/
インデックス 1: ipfs://BBBBBB/
...
のような形で保存されている。
そのため、トークンID 15~19 を更新したい場合は、インデックス 1 に紐づくベースURI ipfs://BBBBBB/ を、新しくアップロードしたメタデータのベースURI ipfs://CCCCCCC/ に置き換える必要があるのだ。

更新対象インデックスの特定

まず、更新したいトークンIDが含まれるバッチの インデックス を特定する必要がある。
NFTコントラクトの Explorer タブ > Read セクションにある getBatchIdAtIndex (※) 関数を使う。
この関数は、引数にインデックス(0から始まる番号)を入れると、そのバッチに含まれる 最後のトークンID を返してくれることが多い。
今までの例で言うと、

getBatchIdAtIndex に 0 を入れて実行 → 9 が返ってくる
getBatchIdAtIndex に 1 を入れて実行 → 19 が返ってくる
この挙動を利用して、更新対象のインデックスを探す。例えばトークンID 15 を含むバッチのインデックスを知りたいなら、インデックス 0 から順番にこの関数を実行し、返り値が 15 以上になる最初のインデックスを見つける。今回の例ではインデックス 1 で 19 が返ってきたので、更新対象はインデックス 1 だと判断できる。

BaseURIの更新

更新対象のインデックスがわかったら、Write タブに移動し、updateBatchBaseURI 関数を実行する。

  1. Explorer タブWrite セクション → updateBatchBaseURI(または同等の関数)を開く
  2. 引数を入力してトランザクションを送信
引数 値 (例)
_index 1 (先ほど特定したインデックス)
_uri ipfs://CCCCCCC/(手順3 で得た新CID)※末尾の /は必須

実行がブロックチェーンで確定すると、該当バッチ(トークン ID 10 – 19)の tokenURI が新しいメタデータに切り替わる。

5.Alchemy APIのキャッシュ更新 (オプション)

今回の更新はAlchemyでメタデータを取得しているサイトにも反映させたかったので、以下のコマンドでリフレッシュAPIを実行。
これを叩くことでメタデータを表示しているWebサービス上でも変更が反映された。

for i in {<対象のNFTの開始トークンID>..<対象のNFTの終了トークンID>}; do echo "Executing for tokenId: $i"; curl --request GET --url "https://<ネットワーク>.g.alchemy.com/nft/v3/APIキー/getNFTMetadata?contractAddress=<コントラクトアドレス>&tokenId=${i}&refreshCache=true" --header 'accept: application/json'; echo ""; done

Discussion