web3探索之旅---ERC721

302 阅读7分钟

ERC721是非同质化代币(Non-Fungible Token)标准,也经常被称为NFT。

非同质化意味着即使是由同一个合约发布的每个NFT都是独特的,因此比起ERC20,NFT拥有更多应用场景,比如与实物财产绑定,增加实物财产的可流动性,与门票,优惠券等线下业务结合,与游戏中的装备等虚拟资产绑定等等。非同质化的属性让NFT具有收藏和炒作的价值,而这一点也正是其被人们追捧和厌恶的最大原因。

规范

ERC721标准要求合约实现ERC165接口和ERC721接口。ERC165在上篇文章中有详细介绍,这里不再赘述。ERC721接口包含3个事件和9个函数

interface IERC721 is IERC165 {
    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 balance);
    function ownerOf(uint256 tokenId) external view returns (address owner);
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function setApprovalForAll(address operator, bool approved) external;
    function getApproved(uint256 tokenId) external view returns (address operator);
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

事件Transfer与ERC20类似,但由于ERC721是非同质化的,所以该事件的第三个参数不再是代币的数量而是代币的唯一id;

事件Approval 也与ERC20的同名事件类似,只是第三个参数从代币数量变为代币的唯一id;

事件ApprovalForAll 则表示owner将所拥有的的所有代币操作权投妥给operator,第三个参数approved表示是委托还是取消委托。

只读函数balanceOf(address owner)用于读取owner拥有的NFT数量

只读函数ownerOf(uint256 tokenId)用于读取id为tokenId的NFT的拥有者的地址

函数safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data)和函数safeTransferFrom(address from, address to, uint256 tokenId)都用于将id为tokenId的NFT从from转移到to上,该函数在成功执行后需要触发事件Transfer。这两个函数都要求from和to不为空,from是id为tokenId的拥有者切函数的调用者是from或被from委托可以操作该NFT。

这两个函数都将检测to地址是不是合约地址,如果是合约地址,则将调用合约的onERC721Received函数,该函数被要求实现为:

function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
        return this.onERC721Received.selector;
}

如果该合约未按要求实现该方法那么本次交易直接失败。这个设计的目的是防止用户错误的将NFT转移到无法操作NFT的合约账户上从而导致NFT被锁死在合约上。因此实现了onERC721Received(address, address, uint256, bytes memory)函数的合约也应当实现操作NFT相关的其他函数。

值得注意的是,这一步操作导致NFT合约可能调用不受信任的外部合约,交出了执行流程的控制权,可能发生重入攻击,在实现这两个函数时需要注意相关风险。

函数transferFrom(address from, address to, uint256 tokenId)用于将id为tokenId的NFT从from转移到to上,该函数在成功执行后需要触发事件Transfer。该函数不检测to是否为合约地址,因此调用者需要确定to为外部账户地址或者拥有操作NFT的能力。

函数approve(address to, uint256 tokenId)将调用者拥有或有操作权限的id为tokenId的NFT委托给地址to。一个NFT只能委托给一个地址,因此如果to的零地址,则相当于取消委托。该函数触发事件Approve

只读函数getApproved(uint256 tokenId)用于获取tokenId的NFT委托给了谁。

函数setApprovalForAll(address operator, bool approved)将调用者的所有NFT委托给operator,当入参approved为false时则取消调用者对operator的委托。该函数要求operator非零。该函数触发事件

ApprovalForAll。

只读函数isApprovedForAll(address owner, address operator)用于获取owner是否将所有NFT委托给了operator。

通过对ERC721接口的了解,可以看出ERC721中的每个代币都拥有唯一id,而每次转账的对象都是一个代币。和ERC20一样,ERC721也考虑到了委托代理的场景。而且ERC721进一步的考虑到了如果代币的接受者是无法操作代币的合约的情况,设计了safeTransferFrom函数用于避免该情况。

扩展

与ERC20一样,ERC721也拥有许多扩展。

ERC721Metadata

该扩展用于定义和展示NFT的元数据

interface IERC721Metadata is IERC721 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

值得注意的是,函数tokenURI(uint256 tokenId)返回了描述id为tokenId的NFT的链接,该链接可能指向某个网站,一个文件或者一张图片,一部电影等等。一个NFT的大部分价值其实在这个链接上,因此如何保证该链接的可用性和不可被篡改是非常重要的。

不幸的是如果该链接指向的是中心化服务器上的资源,那么指向的内容可以被轻易的篡改或拒绝服务。

如果该链接是去中心化网络上的内容寻址的链接(比如IPFS的cid)那么可以确信的是链接内容不会被篡改(仍然有概率丢失,但很难被恶意的删除)。

ERC721Enumerable

该扩展使得合约中的token是可枚举的

interface IERC721Enumerable is IERC721 {
    function totalSupply() external view returns (uint256);
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
    function tokenByIndex(uint256 index) external view returns (uint256);
}

ERC721Consecutive

该扩展使得合约在部署过程中可以批量铸造NFT

ERC721Royalty

该扩展是实现了ERC2981的NFT。ERC2981是通用版税的标准,该标准只包含一个函数:

function royaltyInfo(uint256 tokenId,uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount);

实现了ERC2981的NFT在token每次被转卖时都将调用该方法查询后向receiver转移royaltyAmount的版税。该扩展有效的保证了NFT的创造者的利益。

总结

ERC721标准下的每一个代币都是独特的,这使得其具有一定的收藏和炒作价值,这也是去年国内NFT在各种区块链平台爆火的原因,但是这种炒作注定使得NFT偏离其本身的价值,再加上许多区块链只是徒有区块链之名的中心化平台,甚至即使是正规的区块链设施,由于国内特色的私钥管理模式也使其与中心化平台无异,因此NFT也迅速的失去了关注。

但我认为NFT仍然是web3未来发展不可或缺的一环。与实物财产进行绑定,与门票和优惠券等线下业务进行结合可以使区块链技术从一个非常实用的角度进入大众的视野。

更进一步的,我认为NFT是数据资产化和数据价值交换的雏形,即使现在的主流NFT仍停留在创造和交易图片的阶段,但仍有很多人在尝试将具有更广泛价值的数据通过NFT资产化,再借用诸如隐私计算之类的技术来实现数据的价值交易。