「NFT」という語からは、美術品やコレクションを思い浮かべる方が多いと思います。この本で読んでいる取引でも NFT が 1 つ作られていますが、そこにあるのは古典籍の IIIF マニフェストです。
同じ規格の上に、まったく違うものが載っています。この章では、規格が何を決めていて、何を決めていないのかを分けます。
規格が要求していること
ERC-721 が定めているのは、突き詰めると次の 3 つです。
- 1 つずつ区別できること(トークン ID)
- 持ち主を答えられ、譲渡できること(
ownerOf/transferFrom) - 自分が ERC-721 だと名乗れること(
supportsInterface)
厳密には、これに加えて譲渡の代理(approve / setApprovalForAll)と、受け取り先が契約の場合の安全確認(safeTransferFrom)も必須です。
名前・記号・tokenURI は付随的な拡張(ERC721Metadata)です。しかも tokenURI が何を返すべきかは、「URI であること」以上には決まっていません。
規格には適合している
この本で読んでいる NFT に、直接問い合わせてみます。3 章で使った eth_getCode と同じ要領で、eth_call を使うと契約の関数を呼べます。
curl -s -X POST https://ethereum-sepolia-rpc.publicnode.com \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"0x04ECc497632847529FC431bA780Ffc573Fe4aC47","data":"0x01ffc9a780ac58cd00000000000000000000000000000000000000000000000000000000"},"latest"]}'
data は「呼びたい関数」と「引数」を並べたものです。ここでは supportsInterface(0x01ffc9a7)に 0x80ac58cd(ERC-721 の識別子)を渡しています。末尾が …01 なら true、…00 なら false です。以降は同じ形で関数を差し替えれば読めます。
supportsInterface(0x80ac58cd) ERC-721 → true
supportsInterface(0x5b5e139f) ERC721Metadata → true
supportsInterface(0x780e9d63) ERC721Enumerable → true
ownerOf(1) = 0xa60eF4E6E8F821F3bd5D42f8067BD5d4a96e0CEf(公開した本人)
totalSupply = 1
name/symbol = PX Data NFT / PX-NFT
ふつうの NFT です。譲渡もできますし、ウォレットに入れれば NFT として表示されます。「データスペース専用の特別なトークン」ではありません。
ERC-165 だけ結果が異なりました
ついでに調べたところ、1 つだけ想定と違う応答がありました。
supportsInterface(0x01ffc9a7) ERC-165 自身 → false
supportsInterface(0xffffffff) → false
ERC-721 は ERC-165(対応規格を問い合わせる仕組み)の上に成り立っており、ERC-165 は「自分自身の識別子には true を返すこと」を求めています。ここが false なので、仕様の求めるところとは異なる挙動のようです。2 行目の 0xffffffff に false を返すのは仕様どおりです。
実務上の影響は限定的だと思われます。ウォレットも流通市場も 0x80ac58cd(ERC-721 の識別子)を見るので、0x01ffc9a7 を単独で確認する実装はまれです。ただし規格適合を機械的に検査する場面では、引っかかる可能性があります。
譲渡できない場合がある
こちらのほうが重い逸脱です。ERC-721 が必須としている safeTransferFrom には引数が 3 つの版と 4 つの版があり、この契約には 4 つの版が実装されていませんでした。必須の 16 関数のうち 15 しかない、ということです。
問題は、呼んだときに何が起きるかです。この契約には fallback が定義されているので、実装されていない関数を呼んでも失敗しません。何もせずに成功します。
ガスの見積もりを取ると、その差がはっきり出ます。
存在しない関数 0xdeadbeef → 24,271 gas (fallback)
safeTransferFrom(4引数) → 25,275 gas (fallback。譲渡は起きない)
safeTransferFrom(3引数) → 260,242 gas (実際に譲渡する)
transferFrom → 261,499 gas (実際に譲渡する)
4 引数版だけ、譲渡する版の 10 分の 1 以下です。中身が動いていません。
4 引数の safeTransferFrom を使うウォレットや流通市場は少なくありません。それらから見ると、取引は成功したのに資産が移っていないという状態になります。エラーも出ません。
前節の「ふつうの NFT です。譲渡もできます」は、3 引数版を使う限りは正しいのですが、経路によっては成り立たないということになります。実物を読むと、こういうものが出てきます。
関数の 7 割は規格の外側にある
契約が持つ関数を数えると、こうなりました。テンプレート(0x9C9eE07b…)の ABI(どんな関数があるかの一覧)から、外部から呼べる関数を数えています。supportsInterface は ERC-721 系の 15 個に含めました。
| 数 | |
|---|---|
| 関数の総数 | 51 |
| うち ERC-721 系の標準 | 15 |
| それ以外 | 36 |
独自の関数には、たとえば次のようなものがあります。
createERC20— この NFT から別のトークンを発行するaddManager/getPermissions/cleanPermissions— 役割の管理setMetaData/getMetaData— メタデータの登録と参照executeCall— 任意の呼び出しの実行
2 つ目に注目してください。標準の NFT にも所有者以外の権限はありますが、approve / setApprovalForAll による「譲渡の代理」に限られます。ここにあるのは manager、deployer、メタデータ更新者といった、譲渡以外の操作まで含む役割です。個人の所有物ではなく、組織で運用することを想定した作りです。
1 つ目も標準にはない構造です。NFT が子トークンを産む、という関係は ERC-721 の想定外で、Ocean が資産(NFT)とアクセス権(トークン)を分けるために足したものです。
中身が 45 バイトしかない
3 章で触れた点をもう一度確認します。この NFT にコードは 45 バイトしか置かれていません。51 個の関数を持つプログラムとしては、明らかに足りない大きさです。
0x04ECc497…(今回の NFT・転送役) 45 バイト
0x9C9eE07b…(テンプレート・実体) 21,504 バイト
0xEF62FB49…(工場) 16,900 バイト
中身が「本体はあちらです」と転送するだけだからです。ERC-1167 という規格で、最小プロキシと呼ばれます。実体は別の契約(テンプレート、0x9C9eE07b… で 21,504 バイト)にあり、そこを共用しています。工場はテンプレートとは別で、転送役を新しく置く係です。3 章で中身を分解しています。
これは費用の工夫です。契約を新しく置く操作はチェーン上で高い部類なので、毎回本体を複製していれば、1 件登録するたびに大きなガスがかかります。45 バイトの転送役を置いて中身を借りることで、前章までに見た「契約 2 つを新設して約 1,300,000 ガス」に収まっています。
「NFT はこういうもの」という像
日本語の NFT 解説では、次のような説明をよく見かけます。
実際の作品データは大きすぎるため別の場所に保存し、そのリンク情報を NFT が持つのが一般的です。
一般的な運用としては、そのとおりです。ただしこれは規格の要求ではありません。ERC-721 は「何を載せるか」を一切決めていないので、リンクを持たせるのも、データそのものを埋め込むのも、何も持たせないのも、すべて規格の範囲内です。
「JSON に name と image を入れる」という書式は、実は規格本文にも「ERC721 Metadata JSON Schema」として載っています。ただし tokenURI の説明は「その URI は、この書式の JSON を指してもよい」という書き方で、従う義務はありません。次章で開ける JSON にある external_url や background_color に至っては規格本文になく、流通市場が事実上の標準として広めたものです。いずれも強制力はありません。
つまり「NFT は共通の規格か」という問いには、器は共通、中身は共通ではない、と答えるのが正確だと思います。規格が保証するのは「誰のものか」までで、そこから先は実装ごとに違います。
考えてみれば妥当な線引きです。所有権の移転はチェーンが保証すべきことで、中身の書式は用途ごとに違って当然です。NFT を「デジタル所有権の器」と捉え直すと、美術品も資料も同じ器に収まります。
この章で見たこと
- ERC-721 が決めているのは、区別・所有・名乗りの 3 つ
- この NFT は規格に適合している。ウォレットから見ればふつうの NFT
- ただし規格からの逸脱が 2 つあった。
supportsInterfaceの ERC-165 自身への応答と、4 引数のsafeTransferFromの未実装 - 関数 51 個のうち 36 個は規格の外側。役割の管理や子トークンの発行はここに入る
- コードが 45 バイトなのは、本体を共用する仕組み(ERC-1167)のため
- 「NFT=画像へのリンク」は規格ではなく慣習
次章では、その慣習の中心にある tokenURI の中身を実際に開けてみます。