ERC721标准接口
在ERC721代币合约实现时,必须实现ERC721接口以及ERC165接口,以下为接口:
pragma solidity ^ 0.4 . 20 ; interface ERC721 { // 转移事件,记录转出,转入,以及被转移代币的ID event Transfer ( address indexed _from , address indexed _to , uint256 _tokenId ); // 授权,记录授权者,被授权者,被授权代币ID event Approval ( address indexed _owner , address indexed _approved , uint256 _tokenId ); // 授权所有代币,记录授权者,被授权者 event ApprovalForAll ( address indexed _owner , address indexed _operator , bool _approved ); // 获取代币余额 // @params address _owner 代币所有者 // @return uint256 返回数量 function balanceOf ( address _owner ) external view returns ( uint256 ); // 获取nft代币所有者 // @params uint256 _tokenId 代币ID // @return address 拥有者地址 function ownerOf ( uint256 _tokenId ) external view returns ( address ); // 安全转移代币 // @params address _from 发送人地址 // @params address _to 收款人地址 // @params uint256 _tokenId 代币ID function safeTransferFrom ( address _from , address _to , uint256 _tokenId ) external payable ; // 转移代币 // @params address _from 发送人地址 // @params address _to 收款人地址 // @params uint256 _tokenId 代币ID function transferFrom ( address _from , address _to , uint256 _tokenId ) external payable ; // 授权 // @params address _approved 被授权人 // @params uint256 _tokenId 代币ID function approve ( address _approved , uint256 _tokenId ) external payable ; // 授权加入或者移除所有运营权 // @params address _approved 被授权人 // @params bool _approved 加入或删除 function setApprovalForAll ( address _operator , bool _approved ) external ; // 获取被授权人 // @params uint256 _tokenId 代币ID // @return address 被授权地址 function getApproved ( uint256 _tokenId ) external view returns ( address ); // 是否代币运营者 // @params address _owner 所有者 // @params address _operator 被授权者 // @return bool 是/否 function isApprovedForAll ( address _owner , address _operator ) external view returns ( bool ); } interface ERC165 { // 查看是否支持该接口 // @params bytes4 interfaceID 接口ID // 计算方式如:bytes4(keccak256('supportsInterface(bytes4)')) function supportsInterface ( bytes4 interfaceID ) external view returns ( bool ); }
以下为 OpenZeppelin
提供的代码示例,仅提供主要方法代码,完整版代码请通过附录获取:
pragma solidity ^ 0.4 . 18 ; /** * @title ERC721 Non-Fungible Token Standard basic implementation * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md */ contract ERC721BasicToken { // 等于 `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba ; // 定义mapping 记录token的所有者 mapping ( uint256 => address ) internal tokenOwner ; // 记录指定token被授权人的地址 mapping ( uint256 => address ) internal tokenApprovals ; // 记录指定地址拥有多少个token mapping ( address => uint256 ) internal ownedTokensCount ; // 记录代币运营者 mapping ( address => mapping ( address => bool )) internal operatorApprovals ; // 修饰符,是否是 `_tokenId` 的所有者 modifier onlyOwnerOf ( uint256 _tokenId ) { require ( ownerOf ( _tokenId ) == msg . sender ); _ ; } // 修饰符 是否可以对该代币进行转账 modifier canTransfer ( uint256 _tokenId ) { require ( isApprovedOrOwner ( msg . sender , _tokenId )); _ ; } // 获取代币余额 function balanceOf ( address _owner ) public view returns ( uint256 ) { require ( _owner != address ( 0 )); return ownedTokensCount [ _owner ]; } // 代币持有人获取 function ownerOf ( uint256 _tokenId ) public view returns ( address ) { address owner = tokenOwner [ _tokenId ]; require ( owner != address ( 0 )); return owner ; } // 某个nft代币是否真实存在 function exists ( uint256 _tokenId ) public view returns ( bool ) { address owner = tokenOwner [ _tokenId ]; return owner != address ( 0 ); } // 授权代币运营权 function approve ( address _to , uint256 _tokenId ) public { address owner = ownerOf ( _tokenId ); require ( _to != owner ); require ( msg . sender == owner || isApprovedForAll ( owner , msg . sender )); if ( getApproved ( _tokenId ) != address ( 0 ) || _to != address ( 0 )) { tokenApprovals [ _tokenId ] = _to ; Approval ( owner , _to , _tokenId ); } } // 获取运营权归属者 function getApproved ( uint256 _tokenId ) public view returns ( address ) { return tokenApprovals [ _tokenId ]; } // 从运营列表中移除或者添加进列表 function setApprovalForAll ( address _to , bool _approved ) public { require ( _to != msg . sender ); operatorApprovals [ msg . sender ][ _to ] = _approved ; ApprovalForAll ( msg . sender , _to , _approved ); } // 判断是否是运营者 function isApprovedForAll ( address _owner , address _operator ) public view returns ( bool ) { return operatorApprovals [ _owner ][ _operator ]; } // 代币转移 function transferFrom ( address _from , address _to , uint256 _tokenId ) public canTransfer ( _tokenId ) { require ( _from != address ( 0 )); require ( _to != address ( 0 )); clearApproval ( _from , _tokenId ); removeTokenFrom ( _from , _tokenId ); addTokenTo ( _to , _tokenId ); Transfer ( _from , _to , _tokenId ); } // 安全转移 function safeTransferFrom ( address _from , address _to , uint256 _tokenId ) public canTransfer ( _tokenId ) { safeTransferFrom ( _from , _to , _tokenId , "" ); } // 是否拥有代币的运营权 function isApprovedOrOwner ( address _spender , uint256 _tokenId ) internal view returns ( bool ) { address owner = ownerOf ( _tokenId ); return _spender == owner || getApproved ( _tokenId ) == _spender || isApprovedForAll ( owner , _spender ); } // 清除运营权 function clearApproval ( address _owner , uint256 _tokenId ) internal { require ( ownerOf ( _tokenId ) == _owner ); if ( tokenApprovals [ _tokenId ] != address ( 0 )) { tokenApprovals [ _tokenId ] = address ( 0 ); Approval ( _owner , address ( 0 ), _tokenId ); } } // 添加一个代币 function addTokenTo ( address _to , uint256 _tokenId ) internal { require ( tokenOwner [ _tokenId ] == address ( 0 )); tokenOwner [ _tokenId ] = _to ; ownedTokensCount [ _to ] = ownedTokensCount [ _to ]. add ( 1 ); } // 移除一个代币 function removeTokenFrom ( address _from , uint256 _tokenId ) internal { require ( ownerOf ( _tokenId ) == _from ); ownedTokensCount [ _from ] = ownedTokensCount [ _from ]. sub ( 1 ); tokenOwner [ _tokenId ] = address ( 0 ); } // 是否是安全转移 // 能够接收ERC721代币的合约必须实现`onERC721Received`方法 // 通过判断是否存在该方法查看是否安全转移 function checkAndCallSafeTransfer ( address _from , address _to , uint256 _tokenId , bytes _data ) internal returns ( bool ) { if (! _to . isContract ()) { return true ; } bytes4 retval = ERC721Receiver ( _to ). onERC721Received ( _from , _tokenId , _data ); return ( retval == ERC721_RECEIVED ); } }
附录
A. 修饰符
在 Solidity
编程中,有一个概念叫做修饰符,英文是 modify
,当为一个方法添加了某个修饰符时,也就意味着这个方法必须满足这个修饰符所要求的事情,比如 external
关键词是要求该方法仅允许外部合约访问。
下面列出一些上面提到的修饰符:
external 仅允许外部合约调用该方法
payable 仅标记了该关键词的方法能够接收转账操作
B. 引用
ERC721草案
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
OpenZeppelin对于ERC721完整实现
https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/
本文由 我爱PHP169 作者:admin 发表,其版权均为 我爱PHP169 所有,文章内容系作者个人观点,不代表 我爱PHP169 对观点赞同或支持。如需转载,请注明文章来源。