• ERC721解读


    NFT(非同质化代币):类似于明朝、宋朝的青花瓷。虽然都是青花瓷。但是都具有唯一的典藏价值。而且价值可能不同。 NFT就是具有唯一价值的代币。

    ERC721: 是以太坊规定实现NFT的一种标准了。实现ERC21标准的智能合约就是NFT代币了。

    1.接口

    1.ERC721

      定义接口参考:ERC 721 - OpenZeppelin 文档

    下面是以太坊官方定义的标准,由于就是我写的代码运行环境不支持payable关键字,因此我打算围绕官方接口定义,按照自己要求稍微增删一下。

    1. pragma solidity ^0.4.25;
    2. interface ERC721 {
    3. ///
    4. Event
    5. ///
    6. event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
    7. event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
    8. event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
    9. ///
    10. Function
    11. ///
    12. function balanceOf(address _owner) external view returns (uint256); // 返回所有者代币的总个数
    13. function ownerOf(uint256 _tokenId) external view returns (address); // 返回代币id对应所有者的账户地址
    14. // 安全的转账
    15. // _to:是已经被指定 id 代币的所有者授予的账户 and (接受者不是智能合约 or 接受者实现ERC721Receiver接口的智能合约
    16. // 将给定id的代币转移到接受者账户
    17. // data是元数据,可有可不有(我觉得)
    18. function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external;
    19. function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
    20. // 这个转账对比上述安全转账(少了一个接受者地址实现是否是ERC721Receiver接口的智能合约地址的判断)
    21. function transferFrom(address _from, address _to, uint256 _tokenId) external;
    22. // 授权将代币转移到另一个账户的权限
    23. function approve(address _approved, uint256 _tokenId) external;
    24. // 授权接受者使用所有代币
    25. function setApprovalForAll(address _operator, bool _approved) external;
    26. // 返回授权指定id 代币的接受者账户
    27. function getApproved(uint256 _tokenId) external view returns (address);
    28. // 判断某账户代币的拥有者是否能被某账户全部使用
    29. function isApprovedForAll(address _owner, address _operator) external view returns (bool);
    30. }

    2.ERC721Metadata

    以下就是ERC21的元数据接口,这是可选地。名称、标识符、每一个token对应的tokenURI。

    1. pragma solidity ^0.4.25;
    2. interface ERC721Metadata {
    3. function name() external view returns (string);
    4. function symbol() external view returns (string);
    5. function tokenURI(uint256 tokenId) external view returns (string); // 返回指定id的代币所对对应的uri
    6. }

    3.ERC721Enumerable

    另一个额外的可选接口是枚举, 它包含了按索引获取到对应的代币。

    1. pragma solidity ^0.4.25;
    2. interface ERC721Enumerable {
    3. // 确定合约当前全部的nft数量(出去销毁)
    4. function totalSupply() external view returns (uint256);
    5. // 从代币列表返回第n个代币
    6. function tokenByIndex(uint256 _index) external view returns (uint256);
    7. // 返回所有者代币列表的第n个代币
    8. function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
    9. }

    4.ERC721Receiver

    1. pragma solidity ^0.4.25;
    2. // 资产合约
    3. interface ERC721Receiver {
    4. function onERC721Received(address operator, address from, uint256 tokenId, bytes data) external
    5. returns (bytes);
    6. }

    2.实现

    1.Jzm721

    这是我针对ERC721接口的合约实现。基本满足官方接口标准。

    1. pragma solidity ^0.4.25;
    2. import "./ERC721.sol";
    3. import "./ERC721Metadata.sol";
    4. contract Jzm721 is ERC721,ERC721Metadata {
    5. ///
    6. Filed
    7. ///
    8. string public name;
    9. string public symbol;
    10. uint256 nftCount;
    11. mapping (address => uint[]) balanceMap; // owner => tokenId[]
    12. mapping (uint256=>string) tokenURIMap; // tokenId => tokenURI
    13. mapping (uint256=>address) tokenIdMap; // tokenId => owner
    14. mapping (uint256 => address) approveMap; // tokenId => operator(经营方)
    15. mapping (address=>mapping (address=>bool)) approveAllMap; // operator =>(owner => true/false)
    16. ///
    17. Function
    18. ///
    19. constructor(string memory _name,string memory _symbol)
    20. public {
    21. name = _name;
    22. symbol = _symbol;
    23. }
    24. function name() external view returns (string) {
    25. return name;
    26. }
    27. function symbol() external view returns (string) {
    28. return symbol;
    29. }
    30. function tokenURI(uint256 tokenId) external view returns (string) {
    31. return tokenURIMap[tokenId];
    32. }
    33. // 创建代币
    34. function mint(address _owner,string _tokenURI) external returns (uint256) {
    35. require(_owner != address(0),"owner is not empty address!");
    36. uint256 tokenId = _mint(_owner);
    37. _setTokenURI(tokenId, _tokenURI);
    38. return tokenId;
    39. }
    40. function balanceOf(address _owner) external view returns (uint256) {
    41. return balanceMap[_owner].length;
    42. }
    43. function ownerOf(uint256 _tokenId) external view returns (address) {
    44. return tokenIdMap[_tokenId];
    45. }
    46. // 这里我忽略了data这个元数据的作用
    47. function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external {
    48. _transferFrom(_from, _to, _tokenId);
    49. if(_isContractAdd(_to)) {
    50. if(_checkIfFunctionExists(_to)) {
    51. _externalTransfer(_from, _to, _tokenId);
    52. emit Transfer(_from, _to, _tokenId);
    53. }
    54. }
    55. }
    56. function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
    57. _transferFrom(_from, _to, _tokenId);
    58. if(_isContractAdd(_to)) {
    59. if(_checkIfFunctionExists(_to)) {
    60. _externalTransfer(_from, _to, _tokenId);
    61. emit Transfer(_from, _to, _tokenId);
    62. }
    63. }
    64. }
    65. function transferFrom(address _from, address _to, uint256 _tokenId) external {
    66. _transferFrom(_from, _to, _tokenId);
    67. }
    68. function approve(address _approved, uint256 _tokenId) external {
    69. require(_approved != address(0),"approved is not empty address!");
    70. address owner = msg.sender;
    71. approveMap[_tokenId] = _approved;
    72. emit Approval(owner,_approved,_tokenId);
    73. }
    74. function setApprovalForAll(address _operator, bool _approved) external {
    75. require(_operator != address(0),"operator is not empty address!");
    76. address owner = msg.sender;
    77. approveAllMap[_operator][owner] = _approved;
    78. emit ApprovalForAll(owner,_operator,_approved);
    79. }
    80. function getApproved(uint256 _tokenId) external view returns (address) {
    81. return _getApproved(_tokenId);
    82. }
    83. function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
    84. return _isApprovedForAll(_owner,_operator);
    85. }
    86. function _checkIfFunctionExists(address _add)public returns (bool) {
    87. bytes4 functionSelector = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); // 函数选择器,基础原型前4个字节
    88. bytes memory data = abi.encodeWithSelector(functionSelector, address(0),address(0),0,"");
    89. bool success = _add.call(data);
    90. return success;
    91. }
    92. function _transferFrom(address _from, address _to, uint256 _tokenId) private {
    93. // from不能是零地址。
    94. require(_from != address(0),"from is not empty address!");
    95. // to不能是零地址。
    96. require(_to != address(0),"from is not empty address!");
    97. // tokenId令牌必须存在并由from拥有。
    98. require(tokenIdMap[_tokenId] == _from,"The tokenId must exist and be owned by from!");
    99. // 接受者一方不是合约地址
    100. if (!_isContractAdd(_to)) {
    101. if (_isApproved(_from, _to, _tokenId)) {
    102. // TODO
    103. _externalTransfer(_from, _to, _tokenId);
    104. emit Transfer(_from, _to, _tokenId);
    105. }
    106. }
    107. }
    108. function _externalTransfer(address _from,address _to,uint256 _tokenId) private {
    109. // 删除代币批准
    110. if(_getApproved(_tokenId) == _to) {
    111. approveMap[_tokenId] = address(0);
    112. }
    113. // 转账
    114. _deleteAccountToken(_from,_tokenId);
    115. tokenIdMap[_tokenId] = _to;
    116. balanceMap[_to].push(_tokenId);
    117. }
    118. function _deleteAccountToken(address _owner,uint256 _tokenId) private {
    119. uint256[] storage tokenIds = balanceMap[_owner];
    120. uint len = tokenIds.length;
    121. for (uint i = 0; i < len; i++) {
    122. if(tokenIds[i] == _tokenId) {
    123. // 交换
    124. uint swap;
    125. swap = tokenIds[i];
    126. tokenIds[i] = tokenIds[len - 1];
    127. tokenIds[len - 1] = swap;
    128. }
    129. }
    130. tokenIds.length--;
    131. }
    132. function _mint(address _owner) private returns (uint256) {
    133. nftCount += 1;
    134. uint256 tokenId = nftCount + block.timestamp;
    135. balanceMap[_owner].push(tokenId);
    136. tokenIdMap[tokenId] = _owner;
    137. emit Transfer(address(0),_owner,tokenId);
    138. return tokenId;
    139. }
    140. function _setTokenURI(uint256 _tokenId,string _tokenURI) private{
    141. tokenURIMap[_tokenId] = _tokenURI;
    142. }
    143. // 判断该地址是否合约地址
    144. function _isContractAdd(address _addr) private view returns (bool) {
    145. uint size;
    146. assembly {
    147. size := extcodesize(_addr) // 返回地址关联代码的长度
    148. }
    149. return size > 0;
    150. }
    151. function _getApproved(uint256 _tokenId) private view returns(address) {
    152. return approveMap[_tokenId];
    153. }
    154. function _isApproved(address _owner, address _operator,uint256 _tokenId) private view returns (bool) {
    155. bool approved = _getApproved(_tokenId) == _operator;
    156. return approved || _isApprovedForAll(_owner, _operator);
    157. }
    158. function _isApprovedForAll(address _owner, address _operator) private view returns(bool) {
    159. return approveAllMap[_operator][_owner];
    160. }
    161. }

    2.Jzm721Receiver

    在这里我就是想要满足合约地址的合约实现ERC721Receiver接口的标准。原合约,这里该函数涉及代币的转账,由于环境的原因,不支持payable关键字,我这里是无法满足的。

    1. pragma solidity ^0.4.25;
    2. import "./ERC721Receiver.sol";
    3. contract Jzm721Receiver is ERC721Receiver {
    4. function onERC721Received(address operator, address from, uint256 tokenId, bytes data) external
    5. returns (bytes) {
    6. return data;
    7. }
    8. }

  • 相关阅读:
    使用 Aspose.Words 处理word文件,然后转存pdf预览
    Javac多模块化编译
    Redis一主一从Docker方式部署通过keepalived和 sentinel哨兵模式实现高可用
    【CSS】笔记1-基础选择器、样式引入
    从第三方数据集成工具迁移到Apache SeaTunnel的实操经验分享
    面试题:思考Tomcat 类加载器为什么要违背双亲委派模型?
    pdf 插件和node报错问题
    Android 网络配置
    ASP.Net Core异步编程
    如果不知道这4种缓存模式,敢说懂缓存吗?
  • 原文地址:https://blog.csdn.net/Qhx20040819/article/details/136220508