[ 英語版はこちら / Click here for the English version of the article ]
Flow ブロックチェーン上に、あるアドレスが所有している NFT 一覧のスナップショットを作成できるスマートコントラクトをつくりました。
一度スナップショットを作成しておくと、所有していた事実をあとからでも簡単に証明できます。この正しさはスマートコントラクトのコードで保証されます。
スナップショットの表示機能もつくりました。これは誰でも拡張可能です。
所有履歴のアクセシビリティの重要性について考えてみましょう。
あらゆる NFT 標準規格に足りていないことは、過去の所有履歴にコントラクト内からアクセスできないことです。この機能が提供されない理由は、主にストレージ容量によるものでしょう。過去に、全ての送付履歴を記録する Ethereum の ERC-20 コントラクトがつくられましたが、このストレージ容量は非常に膨大になり、Ethereum ネットワーク内で問題になっています。
一方、過去のトランザクション情報をノードから直接取得するのは、ブロックチェーンの利用者が増えるにつれて難しくなります。例えば Ethereum では、1 年以上前のトランザクションデータはノードに保持されない仕様になりました(EIP-4444)。Flow においても、スポークと呼ばれる定期アップデートが起こると、過去のトランザクションデータはアーカイブ用ノードにしか保持されません。
スマートコントラクトのコンポーザビリティを考えると、過去の所有履歴をオンチェーンで取得できることは重要です。NFT の所有履歴をコントラクト内で確認できれば、NFT 取引の信頼性を高めたり、過去の履歴に基づいてサービスを提供したりすることが容易になります。副次的なメリットもあります。例えば、手放してしまった思い出の NFT を別の形としてずっと残しておくことができます。また、ある時点で同じアドレスに何の NFT がいっしょに入っていたかがわかるので、その NFT のアイデンティティの同定が容易になります。所有履歴のスナップショットは、家族写真のようなものでしょう。
私のアイデアは、実行時にどのアドレスが何の NFT を所有していたかというスナップショットを記録できる、標準仕様またはリファレンス仕様となるようなスマートコントラクトを作成するというものです。そして、そのようなオープンなスナップショット情報を活用した例をひとつ示したいと思いました。
このアイデアの背景には、スマートコントラクトのコンポーザビリティに関する仮説があります。コンポーザビリティは重要なものだと何年も前から言われていますが、オンチェーンで完結する素晴らしいアイデアを私はあまり多くみたことがありません。ほとんどがオフチェーンを経由するものであり、それは Web3 サービスとしてのメリットを小さくしてしまいます。オンチェーンで完結できないのは、スマートコントラクトからアクセスできる NFT のアイデンティティ情報が不足していることが原因ではないでしょうか。
NFT プロジェクトのコミュニティは、その NFT が様々なユースケースに活用されることを望んでいます。私は最近 Doodles の日本のミートアップに参加し、ホルダーの方々と話をしました。彼らは NFT のイラストやグッズが多くの場所でみられることを願っていました。しかし、それ以上の具体的な活用アイデアはあまり聞くことができませんでした。それは、オンチェーンにはまだまだ情報が不足しているからではないかと思うのです。これはあくまで私の推測です。
私がこのスナップショットのアイデアを Flow 上につくろうと思った理由として、Flow のスマートコントラクトが不特定多数の NFT にアクセスしやすい設計になっていることが挙げられます。Flow には共通の NFT インターフェースという仕組みがあり、標準仕様に準拠しているかどうか簡潔に調べられます。また、NFT の保存場所が各アカウントのストレージ内であるため、漏れなく一覧を取得することができます。
NFT の所有情報をどのようなデータ構造で保持するべきかに頭を悩ませました。
Flow の NFT 標準はこれまで何度かアップデートされており、いくつかの古い NFT コントラクトは最新の仕様に追従できていませんでした。そのため、すべての NFT 情報を共通の方法で取得することができませんでした。例えば、NBA Top Shot の初期からのアカウントでは、Collection に対して NonFungibleToken.CollectionPublic
インターフェースを公開していないため、TopShot.MomentCollectionPublic
インターフェースを参照する必要があります。これはコードを複雑にします。
また、特定の型に対して動的に処理を追加できないかを模索しましたが、fun getCapability<T>(_ at: CapabilityPath)
などの型指定 <T>
の箇所に変数を使えなかったため、コードの簡略化が困難でした。構造体インターフェースを使うことでロジックを追加できるようにしましたが、最善の策ではないかもしれません。
リソース指向による直感的な所有表現はとても気に入っています。これはいまだにほとんどのスマートコントラクト言語で実現されていないことです。
そして、Cadence 言語の開発者たちの、あるべき姿を追い求めてドラスティックな変化を恐れない姿勢はとても好きです。いままで幾度の破壊的変更を経験しましたが、それはいずれも良い判断だったと思います。シンプルでわかりやすい文法、UTF-8 サポートなどの実用的な機能の実装により、Cadence 言語は Move 言語よりも非常に親しみやすいです。
加えて、これは Cadence というより、もしかすると Flow プロトコルの話かもしれませんが、ソースコードの文字列がそのままトランザクションに含まれることがとても気に入っています。私はプログラムのコードはアートだと思っているので、このような仕様はすべてのコード・アーティストにとって非常に重要なことだと感じます。
Snapshot
コントラクトの中で最も重要なリソースは Snap
です。Snap
リソースはひとつのスナップショットの単位であり、そのスナップショットを作成した時刻、対象アドレス、取得に用いたロジック構造体の型、そしてその時点でアドレスが所有する複数の NFT 情報(NFTInfo
構造体)を持っています。NFTInfo
には、各 NFT のコレクション情報、型、ID、メタデータが含まれます。作成された Snap
リソースは、Album
というリソースに複数格納されます。Album
リソースは以下 3 つの機能を提供します。
スナップショットを作成する
所有証明を取得する
スナップショットを表示する
スナップショットを作成する際、引数にロジックを渡す必要があります。ロジックは、以下の構造体インターフェースを実装した型となります。
pub struct interface ILogic {
pub fun getOwnedNFTs(address: Address): {String: {UInt64: NFTInfo}}
}
基本的な処理を行うロジック型を SnapshotLogic
コントラクトに実装しました。処理をカスタマイズしたい場合、誰でも自由に実装できます。ただし、ロジック型を実際に使うには、Snapshot
コントラクトの管理者によって許可リストに追加されている必要があります。これは NFT 情報の偽造など悪意のある処理を防ぐためです。
スナップショット取得後のアカウント・ストレージの状態は以下のようになります。
ここでは、自分のアドレスを指定して、所有している TopShot や Doodles などの NFT 情報を Album
の中に Snap
リソースとして保存していますが、自分のアドレス以外の任意のアドレスを指定することもできます。
作成した Snap
リソースの情報は、以下のビューアー型を渡すことで、SVG 画像や HTML など任意の形式で出力できます。これは誰でも実装して、使用できます。
pub struct interface IViewer {
pub fun getView(snap: &Snap): AnyStruct
}
今回、HTML 形式で描画するビューアを SnapshotViewer
コントラクトに実装しました。この実装は、スナップショットに含まれる NFT のメタデータを HTML の簡単なウェブアプリに埋め込んだものです。試しに私が持っている NFT 情報のスナップショットを描画すると以下のような HTML が表示されます。
今回のスマートコントラクト、トランザクション、スクリプトのコードは以下のリポジトリに格納してあります。
スマートコントラクトは既にメインネットにデプロイされています。
持っている NFT 一覧をビジュアルにみえる形で表示できるのは、思った以上に楽しく、思い出深いです。NFT は、もしそれを手放したとしても履歴を追える技術のはずですが、実際のところ、昔のトランザクション情報をオンチェーンで確認するのは難しいです。今回のコントラクトを使って所有履歴を構築することは、コンポーザビリティの時代において重要なことだと感じています。皆さんの所有履歴も、ぜひ記録してみてください。所有履歴のアクセシビリティをよりよくするために、このコントラクトが何かしらのきっかけになれば幸いです。
Zeroichi Arakawa