目次
はじめに
BlockchainBizではブロックチェーンのニュースやサービスをビジネスマンによりわかりやすく解説するメディアとして活動してきました。
ブロックチェーンのことをより多くの方に知ってもらうことと同様に、やはりブロックチェーンを使って何かしらのサービスや事業を立ち上げる人が出てくることも同時に重要だと考えています。
そこで、ブロックチェーン開発実務で役に立つ知識や具体的な実装方法などをまとめたブロックチェーン開発者向けの記事を定期的に書いていくことにしました。
第一弾は「フルオンチェーンNFTの作り方」になります。
本記事の筆者が「スマートコントラクトの仕組みと法律」という本を出版し、その本の購入特典としてNFTを配布しました。そのNFTはフルオンチェーンNFTになっており、開発の過程で培った知見を本記事で共有できたらと思います。
フルオンチェーンNFTとは、NFTに関する全ての情報は単一のブロックチェーンに保存されているNFTのことを指します。
NFTの中にはNFTに関する情報の一部がブロックチェーン外に保存されているものがあり、フルオンチェーンNFTという名前はそれらのNFTとの違いを明確にするために利用されています。
ERC721とtokenURI
多くのNFTはERC721と呼ばれる、スマートコントラクトを開発する際の規格に沿って開発されています。
ERC721の詳しい解説についてはこちらを参考にしてください。
ERC721ではtokenURI()
という関数を使って、特定のNFTに関する情報を取得します。この関数は、NFTに関連付けられた画像や動画などのメタデータへのリンクを提供する役割があります。このリンクは、Uniform Resource Identifier(URI)として表現されます。URIは、インターネット上のリソースを一意に識別するための文字列で、URL(リソースの具体的な場所を示す)を含む広いカテゴリです。tokenURI
が返すURIは、RFC3986というインターネット標準に基づいています。
提案時のEIP721では、tokenURI()
はJSON形式でデータを返却し、そのJSONのフォーマットやどのような情報が含まれるかのサンプルも提示されていました。
https://eips.ethereum.org/EIPS/eip-721#specification
しかしながら、現在の多くのNFTプロジェクトでは大手NFTマーケットプレイスのOpenseaが提示しているJSONフォーマットが利用されています。
OpenSeaのフォーマットはこのようになっています。
name | NFTの名前 |
description | NFTの詳細説明 |
image | NFTに紐ずく画像のURL |
attributes | NFTの属性を追加できるもの |
image_data | imageが指定されてない時に指定するraw SVGデータ |
external_url | OpenseaではNFTの画像の下に表示される外部に移動するためのURL |
animation_url | 動画のリンクやHTMLなどより多くのメディアに対応できるURL |
youtube_url | YouTubeのURL |
また、各データは下記画像のように表示されます。
実際によく使われるのは「name」「description」「image」「attributes」の4つになり、他のものはオプショナルでつけるケースが一般的です。
NFTの論点とtokenURIの取り扱いの歴史
ここでNFTの論点として挙げられるのが、tokenURIの情報がブロックチェーン外に保存されているNFTがあることです。
特に画像はデータサイズが大きく直接ブロックチェーンに保存するのは困難です。従って、NFTが流行り始めた当初は画像データに関してはブロックチェーンの外で管理するプロジェクトが多くありました。
しかし、ある特定の企業が管理するデータベースに保存されている画像のURLが指定した場合、もし画像を管理している企業がなくなってしまうとtokenURIから画像データにアクセスできなくなってしまいます。
つまり、ブロックチェーンが動き続ける限りブロックチェーン上にはそのURL存在しているが、そのURLの中身を開けず今まで持っていたNFTの価値がなくなってしまう可能性があります。
CloneXはtokenURIのJSONデータ、画像情報ともに RTFKTが管理するサーバーに保存されています。
そのため、特定の企業が管理するサーバーに画像を保存するのではなく、IPFSなどの分散型ストレージに画像を保存する動きが出てきました。
実際に、NFTプロジェクトとして一世を風靡したBored Ape のコントラクトを見てみると、IPFSで管理されていることがわかります。
Board Apeの場合はtokenURIのJSONデータをIPFSに保存し、さらに画像のデータもIPFSに保存し2重にIPFSを利用しています。
IPFSを利用するパターンは他のプロジェクトでも多く使われ、AzukiやDoodlesなどで利用されています
ただ、IPFSに画像が保存されたとしても、時と場合によってはIPFSの仕組みとして画像にアクセスできなくなる可能性があります。
*IPFSの詳しい説明はこちらの記事をご覧ください。
そこで、なるべくNFTに関する全ての情報をブロックチェーンに保存するフルオンチェーンの流れが起こりました。
ただ、前述したように画像を直接ブロックチェーンに保存することは困難なので、HTMLやSVGなどプログラミングで表現できる範囲のものであればそのコードを直接ブロックチェーンに書き込んで実現しようとするものです。
フルオンチェーンNFTの作り方
フルオンチェーンNFTの作り方は非常に簡単です。
まず、SVGのコードを返却する関数を作成します。今回は六角形と”Full onchain NFT”という文字列を返すシンプルなSVGにしています。非常にシンプルでSVGのコードをSolidityにハードコードしているだけです。
このSVGはコードのデータ量の許す範囲であればいくらでも複雑にすることは可能です。拡張性に関しては後ほど説明します。
function generateSVG() internal pure returns (string memory svg) { return string( abi.encodePacked( '<svg viewBox="0 0 100 100" width="100" height="100" fill="none" xmlns="http://www.w3.org/2000/svg">', '<defs>', '<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">', '<stop offset="0%" style="stop-color:#29b6f6;stop-opacity:1" />', '<stop offset="100%" style="stop-color:#ab47bc;stop-opacity:1" />', '</linearGradient>', '</defs>', '<circle cx="50" cy="50" r="40" fill="url(#gradient)" />', '<text x="50" y="53" text-anchor="middle" fill="#ffffff" font-weight="bold" font-family="Arial" font-size="8">Full on-chain NFT</text>', '</svg>' ) ); }
次に、このSVGデータを含むJSONデータをtokenURIで返却できるようにします。
function tokenURI(uint256 _tokenId) public view override(ERC721) returns (string memory) { string memory image = Base64.encode(bytes(generateSVG())); return string( abi.encodePacked( 'data:application/json;base64,', Base64.encode( bytes( abi.encodePacked( '{"name":"', "Full-onchain-nft-sample", '", "description":"', "Description is just discription.", '", "image":"', 'data:image/svg+xml;base64,', image, '", "attributes": [{"trait_type": "Type", "value": "', "Full-onchain", '"}]}' ) ) ) ) ); }
SolidityではJSONデータを直接返却することができないので、JSONの型のstring値をbase64でencodeした値を返却しています。また、dataURIの形にするために、文頭にdata:application/json;base64
をつけています。
また、generateSVG()で生成されたSVGのコードもdataURIの形にするために、imageを指定する際に文頭に”data:image/svg+xml;base64: “を記載することでSVGがencodeされたデータであることを明示しています。
フルオンチェーンと聞くと難しく感じるかもしれませんが、HTMLやSVGをsolidityにハードコードしているだけで仕組みさえわかれば非常に簡単です。
フルオンチェーンNFTの実際の現れ方
今回はこちらのGithubにあるNFTをデプロイしてみました。
https://github.com/0xywzx/event/blob/main/20230905_full_onchain_nft/FullOnchainNFT.sol
コントラクトのデプロイはRemixを使うと非常に簡単に行えます。実際にデプロイしてみてください。
コントラクトがデプロイされてNFTが作成されると自動的にOpenSeaで表示されます。こちらが実際に作成したNFTになります。
「OpenSeaを介さずに独自でNFTを作成した場合、OpenSeaなどのNFTマーケットプレイスに表示されないのではないか」との質問をよく頂きます。
ですが、この例を見てわかるように独自にNFTを作成したとしてもOpenSeaでは表示されます。OpenSeaを介さずに作成されたNFTでもOpenSeaで見ることができる理由は、ブロックチェーンのデータは誰もが確認することができるためOpenSeaがブロックチェーンを常に監視しNFTが作成されたのを確認したらOpenSeaのサイト上に反映させているためです。
従って、OpenSea以外のマーケットプレイスであっても独自のNFTを作成すると自動でサイトに表示されます。(*特定の人しかNFTを販売できないNFTマーケットプレイスも存在しており、そのようなNFTマーケットプレイスでは表示されません。)
tokenURIを見てみると、このようなデータが返却されます。このデータはJSONをbase64でencodeしたものです。
実際にdecodeしてみるとこのようにJSONの値が返ってきて、NFTの情報をみることができます。
フルオンチェーンNFTの拡張性
今回は簡単なSVGを利用してNFTを作成しましたが、プログラミングで表現できる範囲であれば様々な拡張を行うことができます。
例えば、SVGを生成する際に独自の関数を追加することで、NFT保有者のアドレスを表示させたり、特定のtokenIdのNFTのみ画像を変更したりすることができます。
実際に、本の特典として配布したNFTはtokenIDによって色を変えたり、NFT保有者のアドレスを表示させたり、1/50の確率でレアのNFTを表示させたりしています。
詳しくはこちらのコードを参考にしてください。
また、簡単に3Dグラフィックを生成できるThree.jsと呼ばれるモジュールを分割してブロックチェーンに保存し、ブロックチェーンに保存されたモジュールを使って3DのNFTを作成するというプロジェクトもあります。
Three.jsを使った3DフルオンチェーンNFTを作ってみよう【開発】
おわりに
tokenURIを中身をURLではなくコードで表現することでフルオンチェーンNFTは作成されており、非常にシンプルな構成になっています。
また、プログラミングで表現ができる範囲であればなんでもできるので、想像力次第では様々NFTを作成することができます。
ぜひ一度フルオンチェーンNFTを作成してみてください!