lesson-4_NFTを発行するスマートコントラクトを作ろう
🪄 NFT を作成しよう
これから、いくつかのNFTを作成します。
下記のように、MyEpicNFT.sol
を更新しましょう。
// MyEpicNFT.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
// いくつかの OpenZeppelin のコントラクトをインポートします。
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "hardhat/console.sol";
// インポートした OpenZeppelin のコントラクトを継承しています。
// 継承したコントラクトのメソッドにアクセスできるようになります。
contract MyEpicNFT is ERC721URIStorage {
// OpenZeppelin が tokenIds を簡単に追跡するために提供するライブラリを呼び出しています
using Counters for Counters.Counter;
// _tokenIdsを初期化(_tokenIds = 0)
Counters.Counter private _tokenIds;
// NFT トークンの名前とそのシンボルを渡します。
constructor() ERC721 ("TanyaNFT", "TANYA") {
console.log("This is my NFT contract.");
}
// ユーザーが NFT を取得するために実行する関数です。
function makeAnEpicNFT() public {
// NFT が Mint されるときのカウンターをインクリメントします。
_tokenIds.increment();
// 現在のtokenIdを取得します。tokenIdは1から始まります。
uint256 newItemId = _tokenIds.current();
// msg.sender を使って NFT を送信者に Mint します。
_safeMint(msg.sender, newItemId);
// NFT データを設定します。
_setTokenURI(newItemId, "Valuable data!");
// NFTがいつ誰に作成されたかを確認します。
console.log("An NFT w/ ID %s has been minted to %s", newItemId, msg.sender);
}
}
1行ずつコードを見ていきましょう。
// MyEpicNFT.sol
contract MyEpicNFT is ERC721URIStorage {
:
ここでは、コントラクトを宣言する際に、is ERC721URIStorage
を使用してOpenZeppelinのコントラクトを「継承」しています。
継承とは、OpenZepplin等のイーサリアムに関する開発を便利にするフレームワークを提供するライブラリから、スマートコントラクトに必要なモジュールを呼び出すことを意味します。
これは関数をインポートするようなイメージで理解してください。
NFTのモジュールはERC721
として知られています。
このモジュールには、NFT発行に必要な標準機能が含まれているため、開発者は独自のロジックを記述してカスタマイズするところに集中できます。
次に、下記のコードを見ていきましょう。
// MyEpicNFT.sol
using Counters for Counters.Counter;
using Counters for Counters.Counter
はOpenZeppelinが_tokenIds
を追跡するために提供するライブラリを呼び出しています。
これにより、トラッキングの際に起こりうるオーバーフローを防ぎます。
💡 オーバーフローとは?
例えば、8 ビットの情報量しか持てない
uint8
があるとします。つまり、格納できる最大の数値は 2 進数の 11111111(10 進数では、2^8 - 1 = 255
)です。 次のコードを見てください。最後の number は何に等しいでしょうか?uint8 number = 255;
number++;この場合、オーバーフローを起こしたので、
number
は増加したにもかかわらず、実は 0 になっています。( 2 進数の 11111111 に 1 を加えると、時計が 23 時 59 分 から 00 時 00 分 に戻るように、00000000 にリセットされます)。アンダーフローも同様で、0 に等しい
uint8
から 1 を引くと、255 になります(uint
は符号なしなので、負にすることはできないからです)。ここでは
uint8
を使用していませんし、uint256
が毎回 1 ずつ増加するときにオーバーフローする可能性は低いと思われますが(2^256
は本当に大きな数です)、将来的に DApp が予期せぬ動作をすることがないように、コントラクトに保護規定を設けることはグッドプラクティスです!👍
次に、下記のコードを見ていきましょう。
// MyEpicNFT.sol
Counters.Counter private _tokenIds;
ここでは、private _tokenIds
を宣言して、_tokenIds
を初期化しています。
_tokenIds
の初期値は0です。
tokenIdはNFTの一意な識別子で、0, 1, 2, .. Nのように付与されます。
次に、下記のコードを見ていきましょう。
// MyEpicNFT.sol
constructor() ERC721 ("TanyaNFT", "TANYA") {
console.log("This is my NFT contract.");
}
ERC721
モジュールを使用したconstructor
を定義しています。
ここでは任意のNFTトークンの名前とシンボルを引数として渡しています。
TanyaNFT
: NFTトークンの名前TANYA
: NFTトークンのそのシンボル
次に、下記のmakeAnEpicNFT
関数を段階的に見ていきましょう。
// MyEpicNFT.sol
// ユーザーが NFT を取得するために実行する関数です。
function makeAnEpicNFT() public {
// NFT が Mint されるときのカウンターをインクリメントします。
_tokenIds.increment();
// 現在のtokenIdを取得します。tokenIdは1から始まります。
uint256 newItemId = _tokenIds.current();
// msg.sender を使って NFT を送信者に Mint します。
_safeMint(msg.sender, newItemId);
// NFT データを設定します。
_setTokenURI(newItemId, "Valuable data!");
// NFTがいつ誰に作成されたかを確認します。
console.log("An NFT w/ ID %s has been minted to %s", newItemId, msg.sender);
}
まず、下記のコードを見ていきます。
// MyEpicNFT.sol
_tokenIds.increment();