以前は、SolanaでNFTを構築するということは、Metaplexと格闘することを意味すると思っていました。すべてのチュートリアルが示唆していたのは、Candy Machineを立ち上げ、メタデータアカウントを管理し、トークンに名前と画像をつけるためだけに別々のプログラムをやりくりするという道でした。しかし、その想定は時代遅れだったことがわかりました。Token-2022としても知られるToken Extensionsプログラムは、その複雑さをミント自体へと集約しました。メタデータプログラムに触れたり、追加のアカウントに資金を投入したりすることなく、完全に機能するNFTを作成できるようになりました。いくつかのフラグを切り替え、ミントアカウントに直接データを書き込むだけで完了です。
これは、開発者がSolana上のデジタル資産をどのように考えるべきかを変えるものです。従来のWeb開発では、NFTは独自のテーブルやスキーマを必要とする、別個のデータ構造のように感じられます。Solanaにおける現実は、よりフラットでエレガントです。NFTは、外部プロトコルによって管理される特別なオブジェクトではありません。それは単に、供給量がちょうど1で、小数点以下が0に設定されたミントアカウントに過ぎません。標準的なトークンは、供給量が多く小数点以下も複数あるため、単位を分割できます。NFTは、供給量を単一の分割不可能な単位に固定します。それをユニークにするすべての要素は、そのコアとなるミントアカウントに付随する拡張機能(extensions)の中に存在します。
旧来の方法と新しい方法
Token Extensionsが登場する前、標準的なスタックは、ミント自体にはSPL Tokenプログラムを、メタデータ、コレクション、そして時にはオフチェーンのインデックス作成にはMetaplexを使用するというものでした。メタデータは、追跡が必要なアドレスによってリンクされた、別の(別個の)アカウントに存在していました。それでも機能はしましたが、管理すべき接点(surface area)が増えてしまいました。アカウントが増えるということは、レント(rent)が増え、署名パスが増え、トークンの全体像を解決するためのクライアント側のロジックが増えることを意味しました。
Token Extensionsは、機能をミントに直接組み込むことで、その拡散を解消します。名前、シンボル、オフチェーンメディアへのポインタが必要ですか?それならメタデータ拡張(metadata extension)を有効にしてください。トークンをコレクションにグループ化する必要がありますか?それならGroupおよびMember拡張を使用してください。ミントが「信頼できる唯一の情報源(single source of truth)」となります。リレーショナルデータベースに慣れている開発者にとって、この変化は、分散マイクロサービスアーキテクチャから、適切に設計された外部キーを持つ正規化されたテーブルへと戻るような感覚です。
拡張機能ベースのNFTの構造
Token Extensionsを使用してNFTを作成するには、このチェーン上で何がトークンを非代替性(non-fungible)にしているのかを正確に理解する必要があります。供給量は1でなければならず、小数点以下は0でなければなりません。これら2つの制約が、分割(fractionalization)を防ぎます。これらのパラメータを設定したら、ミントアカウントに直接追加フィールドを保存する拡張機能を有効にします。
メタデータ拡張は、名前、シンボル、およびURIを保持します。そのURIは、通常、分散型ストレージまたは標準的なWebサーバーにホストされている、画像、属性、特性を記述したJSONファイルを指しています。発見してデシリアライズすべき、別個のメタデータアカウントは存在しません。データはミント自体に存在するため、エクスプローラー、ウォレット、クライアントソフトウェアは、1つのアカウントを検査するだけでトークンのコアとなるアイデンティティを読み取ることができます。
私はこれをdevnetで直接テストしました。メタデータ拡張を有効にした新しいミントを作成し、名前とシンボルをミントの状態(mint state)に直接書き込みました。トランザクションは成功し、結果はすぐにSolana Explorerに表示されました。資金を投入したり、場所を特定したりする必要がある2つ目のアカウントはありませんでした。数週間にわたってマルチアカウントのMetaplexメタデータを扱ってきた後では、そのシンプルさに拍子抜けするほどでした。
データベースの行のようにコレクションを構築する
コレクションは、論理的な次のステップでした。レガシーモデルでは、NFTをグループ化することは、通常、Metaplex Certified Collectionsやオフチェーンのレジストリに依存することを意味していました。Token Extensionsは、2つの特定のプリミティブ、すなわちGroup拡張とMember拡張を導入します。
ロジックの流れは以下の通りです。まず、コレクションのヘッダーとして機能する単一のミントを作成し、その上でGroup拡張を有効にします。次に、コレクション内の個々のNFTごとに、Member拡張を有効にしたミントを作成します。各メンバーミントは、コレクションのミントアドレスへのポインタを保持します。この関係は、リレーショナルデータベースの外部キーと全く同じように動作します。コレクションの行は一度だけ存在し、各メンバーの行はコレクションのアイデンティティを複製することなく、それを参照します。
私はdevnetで、この方法を使って小さなテストコレクションを構築しました。メインのコレクションミントにはgroupフラグが付与されていました。個々のトークンにはmemberフラグが付与され、親のアドレスを参照していました。チェーンをクエリすると、クリーンで辿りやすい構造が得られました。トークンが一緒に属しているかどうかをサードパーティのインデクサーが推測する必要はありませんでした。関係性は明示的であり、オンチェーンで完結しています。
オープンスキーマとオンチェーンでの実験
特筆すべき詳細の一つは、メタデータ拡張のスキーマがオープンであることです。従来の規格では、多くの場合、固定されたフィールドリストが強制されていました。非標準的なデータをオンチェーンに保存したい場合、オフチェーンのJSONに格納するか、硬直したアカウントレイアウトを無理やり書き換えるしかありませんでした。
Token Extensionsは異なるアプローチを採用しています。メタデータ拡張がカスタムフィールドを受け入れるため、私はrarity属性をミントアカウントに直接追加することができました。フィールドを書き込み、トランザクションを送信し、Solana Explorerをリフレッシュしたところ、rarityの値が名前やシンボルと並んで即座に表示されました。ゲーム開発者やダイナミックなアセットを構築する人々にとって、この柔軟性は極めて重要です。外部の検証者がJSONを解析することなく、重要な特性をオンチェーン上で公開できるのです。
オフチェーンのギャップ:URIとキャッシュ
オンチェーンストレージがいかに優れていても、一つの教訓が明確に浮かび上がりました。それは、アイデンティティは依然としてオフチェーンに存在するということです。ミントは画像を保存するのではなく、URIを保存します。そのURIを更新してdevnetに反映させた際、チェーンは即座に新しいポインタを反映しました。ブロックエクスプローラーも遅延なく更新されたリンクを表示しました。
しかし、私のウォレットは遅延していました。オンチェーンの基盤データはすでに変更されていたにもかかわらず、ウォレットは何分間も古い画像を頑なに表示し続け、キャッシュされたバージョンを出し続けていました。これは、開発者が考慮に入れておくべき現実的な問題です。Solanaのレジャーは高速で、確定時間も短いです。しかし、ユーザーが操作するビジュアルレイヤーは、HTTPキャッシュ、CDNの伝播、およびウォレット固有のリフレッシュ間隔に依存しています。現実世界のイベントに基づいて変化するダイナミックNFTを構築する場合、トランザクションが完了した瞬間にユーザーがその変化を確認できるとは限りません。キャッシュバスター戦略、URIパスのバージョニング、あるいはフロントエンドでの明示的なリフレッシュトリガーが必要になります。
次のステップ
私のdevnetでの実験は、よりダイナミックなプロジェクトのための土台を築きました。次のステップは、コレクション
