在看 ERC721
之前,请先看 ERC165
。
例子
我们沿用 BAYC
的方式来弄 ERC721
例子。
1 | pragma solidity ^0.8.0; |
这里说一下细节 BAYC
一共有 10000
个,序号是 0 - 9999
。
其中,一个例子 https://ipfs.io/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/0
的内容如下
1 | {"image":"ipfs://QmRRPWG96cmgTn2qSzjwr2qvfNEuhunv6FNeMFGa9bx6mQ","attributes":[{"trait_type":"Earring","value":"Silver Hoop"},{"trait_type":"Background","value":"Orange"},{"trait_type":"Fur","value":"Robot"},{"trait_type":"Clothes","value":"Striped Tee"},{"trait_type":"Mouth","value":"Discomfort"},{"trait_type":"Eyes","value":"X Eyes"}]} |
里面有图片的内容还有图片的描述信息。
解析
观看 @openzeppelin/contracts/token/ERC721/ERC721.sol
会发现其继承了
Context
ERC165
IERC721
IERC721Metadata
IERC721Metadata
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
合约内容如下
1 | pragma solidity ^0.8.0; |
IERC721
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
合约内容如下
1 | pragma solidity ^0.8.0; |
IERC721事件
IERC721
包含 3
个事件,其中 Transfer
和 Approval
事件在 ERC20
中也有。
Transfer
事件:在转账时被释放,记录代币的发出地址from
,接收地址to
和tokenid
。Approval
事件:在授权时释放,记录授权地址owner
,被授权地址approved
和tokenid
。ApprovalForAll
事件:在批量授权时释放,记录批量授权的发出地址owner
,被授权地址operator
和授权与否的approved
。
IERC721函数
balanceOf
:返回某地址的NFT持有量balance
。ownerOf
:返回某tokenId
的主人owner
。transferFrom
:普通转账,参数为转出地址from
,接收地址to
和tokenId
。safeTransferFrom
:安全转账(如果接收方是合约地址,会要求实现ERC721Receiver
接口)。参数为转出地址from
,接收地址to
和tokenId
。approve
:授权另一个地址使用你的NFT
。参数为被授权地址approve
和tokenId
。getApproved
:查询tokenId
被批准给了哪个地址。setApprovalForAll
:将自己持有的该系列NFT
批量授权给某个地址operator
。isApprovedForAll
:查询某地址的NFT
是否批量授权给了另一个operator
地址。safeTransferFrom
:安全转账的重载函数,参数里面包含了data
。
表面上 ERC721
需要实现 IERC721
的接口,但是背地里有更多更细节的实现。
ERC721 实现
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
IERC721Receiver
如果一个合约没有实现 ERC721
的相关函数,转入的 NFT
就进了黑洞,永远转不出来了。
为了防止误转账,ERC721
实现了 safeTransferFrom()
安全转账函数,目标合约必须实现了 IERC721Receiver
接口才能接收 ERC721
代币,不然会revert
。「实现了该接口相当于告诉外界,你有收取 ERC721
的能力,可以参考 ERC165
」IERC721Receiver
接口只包含一个 onERC721Received()
函数。
1 | // ERC721接收者接口:合约必须实现这个接口来通过安全转账接收ERC721 |
我们看下 ERC721
利用 _checkOnERC721Received
来确保目标合约实现了 onERC721Received()
函数(返回 onERC721Received
的 selector
):
1 | function _checkOnERC721Received( |
有了这些之后,再看 ERC721.sol
主文件就会发现一切都很简单了。