8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ERC721トークンについて

Last updated at Posted at 2019-10-28

#はじめに
イーサリアムブロックチェーンにおける、スマートコントラクトの初めての規格として2015年11月にERC20が誕生したあと、ERC20規格の欠点である誤送金問題などを解決するために誕生したのがERC223規格でした。その後、ERC20やERC223といった規格とは別方向に発展した全く新しいERC721トークンが誕生しました。
今回はそのERC721の特徴や仕様をまとめてみました

#ERC721の特徴
ERC20やERC223トークンは、通貨としての役割を持つため代替可能トークンとして使われていました。
通貨は「価値の保存」、「価値の尺度」、「交換の手段」を持っています。なので、例えば、アリスが持っている100円とボブが持っている100円は同等のものなので、代替可能であると言えます。一方、ERC721は、ERC20やERC233トークンのように、どのトークンに対しても同じ価値を持つのではなく、固有の希少性や独自性を持つことができる代替不可能トークンです
この代替不可能トークンのことを**NFT(Non-Fungible Token)**といいます。各トークンがその所有者の名前などのメタデータを含むことで、トークンごとに違う価値を生み出せます。
例えば、あるゲーム内でモンスターを交換する場合、それぞれモンスターによってレベルやレア度は違うので、ERC721トークンによって固有の価値を表し、取引が行えます。

#ERC721の関数とイベント

ERC721トークンは、提案されたERC165インターフェイスを実装する必要があります。
この標準により、コントラクトによって実装されたインターフェイスの検出が可能になります。これは、トークンが実装するインターフェイスを検出し、その結果、メソッド/コードを適応させてそれとやり取りできるため、非常に便利です。

  • ERC721のコントラクト
function return
balanceOf(address owner) uint256
ownerOf(uint256 tokenId) address
approve(address to, uint256 tokenId)
_exists(uint256 tokenId) bool
getApproved(uint256 tokenId) address
setApprovalForAll(address to, bool approved)
isApprovedForAll(address owner, address operator) bool
_isApprovedOrOwner(address spender, uint256 tokenId) bool
safeTransferFrom(address from, address to, uint256 tokenId)
safeTransferFrom(address from, address to, uint256 tokenId, bytes data)
_safeTransferFrom(address from, address to, uint256 tokenId, bytes _data)
transferFrom(address from, address to, uint256 tokenId)
_transferFrom(address from, address to, uint256 tokenId)
_mint(address to, uint256 tokenId)
_safeMint(address to, uint256 tokenId)
_safeMint(address to, uint256 tokenId, bytes memory _data)
_burn(uint256 tokenId)
_burn(address owner, uint256 tokenId)
_checkOnERC721Received(address from, address to, uint256 tokenId, bytes _data) bool
_clearApproval(uint256 tokenId)
  • ERC165のインターファイス
function return
supportsInterface(bytes4 interfaceID) bool

上記のリストを見てみると、同じ名前の関数がいくつか定義されているのですが、引数によって実装が違うみたいです。

####・balanceOf
ownerが持つNFTの総量を返します
####・ownerOf
tokenIdからそのownerのアドレスを返す
####・approve
指定されたトークンIDを転送するためにtoを承認します

function approve(address to, uint256 tokenId) public { 
    address owner = ownerOf(tokenId); 
    require(to != owner, "ERC721: approval to current owner"); 

    require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not owner nor approved for all"); 

    _tokenApprovals[tokenId] = to; 
    emit Approval(owner, to, tokenId); 
     } 

_msgSender()がトークンのオーナーであることを確認し、トークンIDに対して承認されるアドレスtoを指定します。

####・_exists
指定されたトークンが存在するかどうかを返す
####・getApproved
tokenIdからapproveしているアドレスを返す
####・setApprovalForAll
toは承認を設定するオペレーター、approvedは設定する承認のステータスです。
オペレーターは、送信者のすべてのトークンを代理で転送できます。
####・isApprovedForAll
setApprovalForAllで_operatorが_ownerによって承認されているかどうかを示します
####・_isApprovedOrOwner
spenderがtokenIdを転送できるかどうかを返す

return(spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender))

spenderがオーナーか、トークンがspenderによって承認されてるか、isApprovedForAllでspenderがtokenIdを転送できるかどうかを返します

####・safeTransferFrom(引数にdataを持たない)

function safeTransferFrom(address from, address to, uint256 tokenId) public {
    safeTransferFrom(from, to, tokenId, ""); 
} 

第四引数のdataを""としてsafeTransferFrom()呼び出しています。

####・safeTransferFrom(引数にdataを持つ)

function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public { 
    require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 
    _safeTransferFrom(from, to, tokenId, _data); 
} 

_isApprovedOrOwnerでsenderがtokenIdを転送できるかどうかを確認し、_safeTransferFromを呼びます

####・_safeTransferFrom

function _safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) internal { 
    _transferFrom(from, to, tokenId); 
    require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); 
} 

_transferFormでNFTのownerをfromからtoへ移します.
requireで_checkOnERC721Receivedを呼びます。

_checkOnERC721Receivedでは、
toがコントラクトアドレスではないとき(ユーザーアドレスのとき)、この関数はtrueを返します。
もし、toがコントラクトアドレスのとき、ERC721Receiverに従ってonERC721Received()を呼びます。これはbyte4で返し、_ERC721_RECEIVEDと等しいかどうか確認する。

_ERC721_RECEIVEDbytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
と定義されています。

また、これはbytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))と等しいようです。

_checkOnERC721Receivedの実装は以下のようになっています。


function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) internal returns (bool) { 
    if (!to.isContract()) { 
        return true; 
    } 
  
    bytes4 retval = IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data); 
    return (retval == _ERC721_RECEIVED); 
} 

####transferFrom

function transferFrom(address from, address to, uint256 tokenId) public { 
    require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 
    _transferFrom(from, to, tokenId); 
     } 

_isApprovedOrOwner_msgSender()がそのトークンを転送できるかどうか確認し、
_transferFromを呼びます
msg.senderが所有者、承認者、またはオペレーターである必要があります。

####・_transferFrom

function _transferFrom(address from, address to, uint256 tokenId) internal {
    require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
    require(to != address(0), "ERC721: transfer to the zero address");

    _clearApproval(tokenId);

    _ownedTokensCount[from].decrement();
    _ownedTokensCount[to].increment();

    _tokenOwner[tokenId] = to;

    emit Transfer(from, to, tokenId);
}

transferFrom()とは対照的に、msg.senderに制限を課しません。
ここでNFTのownerをfromからtoへ移す処理を実装する。
####・_mint
mintとは、「鋳る」という意味であり、トークンやコインを作成します。

function _mint(address to, uint256 tokenId) internal {
    require(to != address(0), "ERC721: mint to the zero address"); 
    require(!_exists(tokenId), "ERC721: token already minted"); 
 
    _tokenOwner[tokenId] = to; 
    _ownedTokensCount[to].increment(); 
    emit Transfer(address(0), to, tokenId); 
} 

toは作成されたトークンを所有するアドレスを示すため、ゼロアドレスではないか確認する必要があります。また、トークンがすでに存在しているかも確認します。

requireが通ったらトークンに所有オーナーを設定し、トークン数をインクリメントします。

####・_safeMint(引数にdata)を持たない

function _safeMint(address to, uint256 tokenId) internal { 
    _safeMint(to, tokenId, "");
} 

第三引数のdataを""として\_safeMint()を呼び出します

####・_safeMint(引数にdataを持つ)

function _safeMint(address to, uint256 tokenId, bytes memory _data) internal {
    _mint(to, tokenId);
    require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

_mint()にto, tokenIdを与え、呼び出します。
_safeTransferFrom()の時と同様に_checkOnERC721Received()をrequireします

####・_burn(引数にaddressを持たない)

_mintとは反対にトークンを削除する関数です

function _burn(uint256 tokenId) internal { 
    _burn(ownerOf(tokenId), tokenId); 
} 

tokenIdの所有アドレスとtokenIdを与え、_burnを呼びます

####・_burn(引数にaddressを持つ)

function _burn(address owner, uint256 tokenId) internal { 
    require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own"); 

    _clearApproval(tokenId); 
    _ownedTokensCount[owner].decrement(); 
    _tokenOwner[tokenId] = address(0); 
    emit Transfer(owner, address(0), tokenId); 
} 

_clearApprovalでtokenIdの承認をクリアします。
オーナーのトークン数をデクリメントし、トークンの所有者をゼロアドレスにします。


ERC165は、スマートコントラクトがどんなインターフェイスを実装しているかを公開するインターファイスです。

####・supportsInterface

supportsInterface(interfaceID)で、使用可能なインターフェースを実装しているかどうかを判断します。

#ERC721が使用されているCryptoKittieについて

CryptoKittieとはイーサリアムのブロックチェーン上で、猫を集めたり、売買したり、交換するゲームです。
このようなブロックチェーンの技術を使ったゲームをDapps(Decentralized Applications)と呼び、非中央集権の分散型アプリケーションのことを指します。
他にもイーサエモンやBitPetなどといったゲームも、育成し、トレードや売買が可能となっています。
これらのゲームではERC721規格を用いており、トークンそれぞれに独自性や希少性を持たせることができます。
分散型アプリケーションの利点である非中央集権で取引データが管理されるため、取引のに透明性があり、仮想通貨を使って安全に取引が行えます。

#まとめ
ERC20やERC223のような代替可能トークンとは反対に、代替不可能なERC721を簡単にまとめました。ERC721規格を用いたゲームは今後もっと増えてくると思うし、新たなビジネスでもっとNFTが活用されると思いました。

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?