サイオステクノロジーの佐々木寛太です。
今回はNFTの規格の一つであるERC721について解説します。
NFTとはという方はまずこちらの記事から読んでいただけるとわかりやすいと思います。
ERCとは
まずERC721の解説の前にERCについて解説します。
ERCとは簡単に言うとEthereumのブロックチェーン上にトークンを導入する際の規格です。
Ethereumの全体の規格はEIPと呼ばれており、その中のカテゴリーの一つにERCというものが存在しています。
ERCの番号はGithubで提案された順番を示しています。
ERC721とは
ERC721とは「非代替トークン」すなわちNFTに関する規格の一つです。
この規格によりNFTの複製や偽造が不可能になっています。
ERC721で定義されているインターフェース
interface ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
balanceOf
は引数に指定したアドレスが所有しているNFTの数を取得することができますownerOf
引数に指定したNFTのtokenIdの所有者のアドレスを取得することができます。safeTransferFrom
第1引数にNFTの所有者のアドレス、第2引数にNFTの譲渡先のアドレス、第3引数にNFTのtokenIdを渡すことで第2引数に指定したアドレスへNFTの所有権を移動させることができます。approve
引数に渡したアドレスに対して指定したNFTのtokenIdの転送許可を与えることができます。setApprovalForAll
引数で渡したownerのアドレスが持つすべてのNFTの転送権限をoperatorに与えることができます。getApproved
指定したtokenIdに転送許可が与えられているアドレスを取得することができます。isApprovedForAll
ownerがoperatorに転送許可を与えているかどうかを判定します。
この機能を見ているとNFTを発行する機能がありません。 NFTを発行する関数はERC721のサブクラスであるERC721Mintableで定義されています。 MintableとはNFTを発行することを「Mint」というためこのような名前になっています
ERC721Mintableで定義されている機能は以下の通りです。
function _mint(address _to, uint256 _tokenId) returns (bool);
function _safeMint(address _to, uint256 _tokenId) returns (bool);
どちらも所有者のアドレスとtokenIdを渡すことでNFTと所有者を紐づけています。
しかしこのままだとIPFSにあるメタデータのURIとtokenIdがマッピングされていないのでERC721URIStorageというサブクラスを使用します。(こちらは使用しなくても問題ないですがあったほうがわかりやすいと思います。)
function tokenURI(uint256 tokenId) public view virtual override returns (string memory)
function _setTokenURI(uint256 tokenId, string memory _tokenURI)
function _burn(uint256 tokenId)
tokenURI
では引数に渡したNFTのtokenIdのメタデータのURLを返します。
setTokenURI
では引数に渡したtokenIdとNFTのメタデータが保存されているURIをマッピングします。
burn
は引数に渡したtokenIdのNFTを使用不可にします。
safeMint
と_setTokenURI
は同じタイミングで実行するのが良いのでSolidityでは以下のようにまとめて一つの関数にすることが多いと思います。
function safeMint(address to, string memory uri, uint256 tokenId) public {
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
ここまでで紹介してきた関数を図にすると以下のようになります。(今回はNFTマーケットプレイスを例に紹介します)
NFT作成
出品
NFT購入
ざっくりと流れを説明します。
まずsafeMint
関数を呼び出し画像をNFTにします。
次にApprove
関数を利用して所有しているNFTの所有権移動権限をマーケットに渡します。
これを行うことにより設定している金額でNFTを購入したいというユーザーが現れETHが支払われた場合にマーケットが自動的にNFTの所有権を購入者に移行することができます。
最後に購入されたらsafeTransferFrom
関数を呼び出しNFTの所有権を移動します。
もしApprove
の処理が行われていなかった場合にはエラーが返ってきて所有権を移動させることができません。
余談
関数の頭にsafeがついているものとついていないものがありますがこれは送り先がコントラクトだった場合に`onERC721Received` が実装されているかを確認し、未実装の場合は処理が完了しません。(NFTが取り出せなくなってしまうため)
なので基本的にはsafeがついたものを使用するようにした方が安全だと思います。
終わりに
今回はNFTの規格であるERC721について説明しました。
ERCにはほかにもFT(暗号通貨)の規格であるERC20やロイヤリティの規格を定義しているERC2981といったものがあります。
こちらも順を追って解説の記事を執筆予定です。
誤植等ありましたらコメント等いただければ修正します。
参考
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol
https://github.com/ethereum/EIPs/tree/master/EIPS
https://docs.openzeppelin.com/contracts/2.x/api/token/erc721