シンプルなフルオンチェーンNFTを作りました
Solidityの勉強として、シンプルなフルオンチェーンNFT (HelloNFT) を作ってみました。
- フルオンチェーンのNFT (サーバーを使っていません)
- 見た目も中身もシンプル
- 数字(#)が変わるだけです
- ランダム要素はありません
- #0から順番に発行されます
- 発行数の制限はありません
- mint関数でガス代だけで発行できます
- Polygonのテストネット(Mumbai)にデプロイしました (MumbaiのMATICが必要です)
という、なんともシンプルなNFTです。
コード解説
最低限の機能しかないため、コードもシンプルです。
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "./Base64.sol";
- OpenZeppelinのERC721を継承して作っています。
- Strings.solはuintからstringへの変換に使います。
- Base64.solはSVGやJSONをBase64エンコードするときに使っています。(tokenURI関数で利用)
contract HelloNft is ERC721("HelloNFT", "HELLO") { uint256 public nextTokenId = 0; constructor() {}
↑このコンストラクタは消し忘れです (^^;
function mint() external { uint256 tokenId = nextTokenId; nextTokenId++; // <- Effects _safeMint(_msgSender(), tokenId); // <- Interactions }
mint関数で、新しいNFTを発行します。 LootのようにtokenIdを指定するのではなく、0番から順に発行していきます。
リエントランシー攻撃対策のCheck-Effects-Interactionsパターンを意識して、 nextTokenId++を_safeMint()より先にしています。
function tokenURI(uint256 tokenId) public view override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); string memory svg = getSVG(tokenId); bytes memory json = abi.encodePacked( '{"name": "HelloNFT #', Strings.toString(tokenId), '", "description": "HelloNFT is a full on-chain text NFT.", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(svg)), '"}' ); return string(abi.encodePacked("data:application/json;base64,", Base64.encode(json))); }
一般的なNFTでは tokenURI関数でサーバーのURLを返すだけですが、 今回は tokenURI関数でSVGを含んだJSONデータを返すことで、 サーバーを必要としないNFT(フルオンチェーンNFT)を実現しています。
最初に requireで引数のtokenIdが発行済みかどうかチェックしています。 (Lootだと未発行のtokenURIも見えてしまうという問題がありました)
JSONデータには name, description, image の項目があります。 imageにはBase64エンコードしたSVGデータを設定しています。
tokenURI関数の最後で、JSON全体をBase64エンコードして返します。
function getSVG(uint256 tokenId) private pure returns (string memory) { return string( abi.encodePacked( '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350">\ <style>text{fill:black;30px;font-family:serif;}</style>\ <rect width="100%" height="100%" fill="#ccddcc" />\ <text x="10%" y="30%" font-size="50px">HelloNFT</text>\ <text x="10%" y="60%" font-size="30px">#', Strings.toString(tokenId), "</text></svg>" ) ); } }
ここではSVGを作成します。rectタグが背景の塗りつぶし、textタグで名前(HelloNFT)とID(#数字)を表示します。
コードはこれで終わりです。わずか46行でNFTができてしまいました。
おわりに
まだSolidity初心者なので、間違い、バグ、改善点などありましたら、どうぞご指摘ください。
さて、今回はとても簡単なものでしたが、次はもう少し楽しめるランダム要素のあるNFTを作りたいと思います。