ERC-721最初是由Dete编写的一份EIP草案(<https://eips.ethereum.org/EIPS/eip-721>),最初是在Axiom Zen的CryptoKitties(加密猫)项目中实现。目前ERC-721已经脱离了beta版,进入了社区的正式标准,并得到了来自整个加密生态系统的大量项目的支持。简单来说,基于这种标准所生产的货币,就是我们常说的NFT。 首先来看一下ERC-721的官方定义接口有哪些:
interface ERC721 /* is ERC165 */ {
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);
可以看到接口中定义的事件只有三个,分别是Transfer、Approval以及ApprovalForAll,其中ApprovalAll是允许操作者使用全部NFT。定义的函数有八个,分别是balanceOf、ownerOf、safeTransferFrom、transgerFrom、approve、setApprovalForAll、getApproved。其中safeTransferFrom是相对ERC-20中的新增方法。官方文档解释编写这个函数的目的,见图1:
SafeTransferFrom大意是说当transfer完成之后,这个函数会检查目标地址是否为合约地址,如果是的话会调用该合约上的onERC721Ercevied 方法,并返回值。这样来防止转移到一个不支持NFT的合约上,避免NFT掉入黑洞中。
首先开门见山的看一下Openzeppelin的目录结构,如图2:
- IERC721.sol拷贝了一份官方对ERC721的借口定义;
- ERC721实现了接口中的方法;
- IERC721Recevier.sol定义了接受NFT的合约需要实现的接口;
- extensions里面定义了基于ERC721的扩展方法的接口以及实现,例如:销毁、枚举、暂缓交易、元数据;
- presets中的ERC721PresetMinterPauserAutoId.sol继承了extensions中的合约,实现了角色管理,定义minter和Pauser的功能;
- utils中的ERC721Holder.sol是IERC721Recevier.sol的实现;
在这里可以发现,Openzeppelin为NFT开发者提供了相当全面的工具类,想了解的读者可以去https://docs.openzeppelin.com/contracts/api/token/erc721中查看更多细节。
接下来我们学习一下ERC721.sol的设计与实现。合约第一部分定义了地址、名称、标识、所有者映射关系等变量,以及初始化方法constructor。
合约第二部分依次实现了ERC-721的方法,下图是balanceOf方法的实现。逻辑是先检查owner地址是否为空,非空在balcances的Map映射中查询地址(key)对应的value并返回。
合约第三部分是实现一些ERC-721标准之外的,但是NFT在运行中约定俗成必须有的功能,例如下图中的mint方法。其逻辑是先检查地址&tokenID是否为空,非空执行mint。注意到这里,具体的mint方法需要项目开发人员按照项目的本身开发,也就是并未给到公开通用的mint方法。
还有一个比较重要的功能是枚举,目的是给用户提供一个快速查询 NFT 的方法。接口设计上是让用户可以根据用户自己的索引查询所拥有的 NFT 对应的 tokenId,另一个是根据索引查询合约中的 NFT 的 tokenId, 然后是总的供给量查询,很多的 NFT 合约的总供给量反应的是现在所有的 NFT 的数量。简单来讲就是维护两个索引,一个索引用来索引整个合约中的 NFT,另一个索引是用来索引用户所拥有的 NFT。这个功能被Openzeppelin单独拿出来开发,没有放在ERC-721.sol的主体文件里。
参考文档:
[1] EIP-721: Non-Fungible Token Standard (ethereum.org): https://eips.ethereum.org/EIPS/eip-721
[2] ERC-721: http://erc721.org/
[3] EIP-721&Openzeppelin: https://www.mytokencap.com/news/337928.html
[4] Openzeppelin-API:https://docs.openzeppelin.com/contracts/api/token/erc721