赞
踩
NFT(非同质化代币):类似于明朝、宋朝的青花瓷。虽然都是青花瓷。但是都具有唯一的典藏价值。而且价值可能不同。 NFT就是具有唯一价值的代币。
ERC721: 是以太坊规定实现NFT的一种标准了。实现ERC21标准的智能合约就是NFT代币了。
定义接口参考:ERC 721 - OpenZeppelin 文档
下面是以太坊官方定义的标准,由于就是我写的代码运行环境不支持payable关键字,因此我打算围绕官方接口定义,按照自己要求稍微增删一下。
- pragma solidity ^0.4.25;
-
- interface ERC721 {
-
- ///
- Event
- ///
- 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
- ///
- function balanceOf(address _owner) external view returns (uint256); // 返回所有者代币的总个数
- function ownerOf(uint256 _tokenId) external view returns (address); // 返回代币id对应所有者的账户地址
- // 安全的转账
- // _to:是已经被指定 id 代币的所有者授予的账户 and (接受者不是智能合约 or 接受者实现ERC721Receiver接口的智能合约
- // 将给定id的代币转移到接受者账户
- // data是元数据,可有可不有(我觉得)
- function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external;
- function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
- // 这个转账对比上述安全转账(少了一个接受者地址实现是否是ERC721Receiver接口的智能合约地址的判断)
- function transferFrom(address _from, address _to, uint256 _tokenId) external;
- // 授权将代币转移到另一个账户的权限
- function approve(address _approved, uint256 _tokenId) external;
- // 授权接受者使用所有代币
- function setApprovalForAll(address _operator, bool _approved) external;
- // 返回授权指定id 代币的接受者账户
- function getApproved(uint256 _tokenId) external view returns (address);
- // 判断某账户代币的拥有者是否能被某账户全部使用
- function isApprovedForAll(address _owner, address _operator) external view returns (bool);
- }
-
以下就是ERC21的元数据接口,这是可选地。名称、标识符、每一个token对应的tokenURI。
- pragma solidity ^0.4.25;
-
- interface ERC721Metadata {
- function name() external view returns (string);
- function symbol() external view returns (string);
- function tokenURI(uint256 tokenId) external view returns (string); // 返回指定id的代币所对对应的uri
- }
另一个额外的可选接口是枚举, 它包含了按索引获取到对应的代币。
- pragma solidity ^0.4.25;
-
- interface ERC721Enumerable {
- // 确定合约当前全部的nft数量(出去销毁)
- function totalSupply() external view returns (uint256);
- // 从代币列表返回第n个代币
- function tokenByIndex(uint256 _index) external view returns (uint256);
- // 返回所有者代币列表的第n个代币
- function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
- }
- pragma solidity ^0.4.25;
-
- // 资产合约
- interface ERC721Receiver {
- function onERC721Received(address operator, address from, uint256 tokenId, bytes data) external
- returns (bytes);
- }
这是我针对ERC721接口的合约实现。基本满足官方接口标准。
- pragma solidity ^0.4.25;
- import "./ERC721.sol";
- import "./ERC721Metadata.sol";
- contract Jzm721 is ERC721,ERC721Metadata {
-
- ///
- Filed
- ///
- string public name;
- string public symbol;
- uint256 nftCount;
- mapping (address => uint[]) balanceMap; // owner => tokenId[]
- mapping (uint256=>string) tokenURIMap; // tokenId => tokenURI
- mapping (uint256=>address) tokenIdMap; // tokenId => owner
- mapping (uint256 => address) approveMap; // tokenId => operator(经营方)
- mapping (address=>mapping (address=>bool)) approveAllMap; // operator =>(owner => true/false)
-
- ///
- Function
- ///
- constructor(string memory _name,string memory _symbol)
- public {
- name = _name;
- symbol = _symbol;
- }
-
- function name() external view returns (string) {
- return name;
- }
-
- function symbol() external view returns (string) {
- return symbol;
- }
-
- function tokenURI(uint256 tokenId) external view returns (string) {
- return tokenURIMap[tokenId];
- }
-
- // 创建代币
- function mint(address _owner,string _tokenURI) external returns (uint256) {
- require(_owner != address(0),"owner is not empty address!");
- uint256 tokenId = _mint(_owner);
- _setTokenURI(tokenId, _tokenURI);
- return tokenId;
- }
-
- function balanceOf(address _owner) external view returns (uint256) {
- return balanceMap[_owner].length;
- }
-
- function ownerOf(uint256 _tokenId) external view returns (address) {
- return tokenIdMap[_tokenId];
- }
-
- // 这里我忽略了data这个元数据的作用
- function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external {
- _transferFrom(_from, _to, _tokenId);
- if(_isContractAdd(_to)) {
- if(_checkIfFunctionExists(_to)) {
- _externalTransfer(_from, _to, _tokenId);
- emit Transfer(_from, _to, _tokenId);
- }
- }
- }
-
- function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
- _transferFrom(_from, _to, _tokenId);
- if(_isContractAdd(_to)) {
- if(_checkIfFunctionExists(_to)) {
- _externalTransfer(_from, _to, _tokenId);
- emit Transfer(_from, _to, _tokenId);
- }
- }
- }
-
- function transferFrom(address _from, address _to, uint256 _tokenId) external {
- _transferFrom(_from, _to, _tokenId);
- }
-
-
- function approve(address _approved, uint256 _tokenId) external {
- require(_approved != address(0),"approved is not empty address!");
- address owner = msg.sender;
- approveMap[_tokenId] = _approved;
- emit Approval(owner,_approved,_tokenId);
- }
-
- function setApprovalForAll(address _operator, bool _approved) external {
- require(_operator != address(0),"operator is not empty address!");
- address owner = msg.sender;
- approveAllMap[_operator][owner] = _approved;
- emit ApprovalForAll(owner,_operator,_approved);
- }
-
- function getApproved(uint256 _tokenId) external view returns (address) {
- return _getApproved(_tokenId);
- }
-
- function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
- return _isApprovedForAll(_owner,_operator);
- }
-
- function _checkIfFunctionExists(address _add)public returns (bool) {
- bytes4 functionSelector = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); // 函数选择器,基础原型前4个字节
- bytes memory data = abi.encodeWithSelector(functionSelector, address(0),address(0),0,"");
- bool success = _add.call(data);
- return success;
- }
-
- function _transferFrom(address _from, address _to, uint256 _tokenId) private {
- // from不能是零地址。
- require(_from != address(0),"from is not empty address!");
- // to不能是零地址。
- require(_to != address(0),"from is not empty address!");
- // tokenId令牌必须存在并由from拥有。
- require(tokenIdMap[_tokenId] == _from,"The tokenId must exist and be owned by from!");
- // 接受者一方不是合约地址
- if (!_isContractAdd(_to)) {
- if (_isApproved(_from, _to, _tokenId)) {
- // TODO
- _externalTransfer(_from, _to, _tokenId);
- emit Transfer(_from, _to, _tokenId);
- }
- }
- }
-
- function _externalTransfer(address _from,address _to,uint256 _tokenId) private {
- // 删除代币批准
- if(_getApproved(_tokenId) == _to) {
- approveMap[_tokenId] = address(0);
- }
- // 转账
- _deleteAccountToken(_from,_tokenId);
- tokenIdMap[_tokenId] = _to;
- balanceMap[_to].push(_tokenId);
- }
-
-
- function _deleteAccountToken(address _owner,uint256 _tokenId) private {
- uint256[] storage tokenIds = balanceMap[_owner];
- uint len = tokenIds.length;
- for (uint i = 0; i < len; i++) {
- if(tokenIds[i] == _tokenId) {
- // 交换
- uint swap;
- swap = tokenIds[i];
- tokenIds[i] = tokenIds[len - 1];
- tokenIds[len - 1] = swap;
- }
- }
- tokenIds.length--;
- }
-
- function _mint(address _owner) private returns (uint256) {
- nftCount += 1;
- uint256 tokenId = nftCount + block.timestamp;
- balanceMap[_owner].push(tokenId);
- tokenIdMap[tokenId] = _owner;
- emit Transfer(address(0),_owner,tokenId);
- return tokenId;
- }
-
- function _setTokenURI(uint256 _tokenId,string _tokenURI) private{
- tokenURIMap[_tokenId] = _tokenURI;
- }
-
- // 判断该地址是否合约地址
- function _isContractAdd(address _addr) private view returns (bool) {
- uint size;
- assembly {
- size := extcodesize(_addr) // 返回地址关联代码的长度
- }
- return size > 0;
- }
-
- function _getApproved(uint256 _tokenId) private view returns(address) {
- return approveMap[_tokenId];
- }
-
- function _isApproved(address _owner, address _operator,uint256 _tokenId) private view returns (bool) {
- bool approved = _getApproved(_tokenId) == _operator;
- return approved || _isApprovedForAll(_owner, _operator);
- }
-
- function _isApprovedForAll(address _owner, address _operator) private view returns(bool) {
- return approveAllMap[_operator][_owner];
- }
-
-
-
-
- }
-
在这里我就是想要满足合约地址的合约实现ERC721Receiver接口的标准。原合约,这里该函数涉及代币的转账,由于环境的原因,不支持payable关键字,我这里是无法满足的。
- pragma solidity ^0.4.25;
- import "./ERC721Receiver.sol";
-
- contract Jzm721Receiver is ERC721Receiver {
- function onERC721Received(address operator, address from, uint256 tokenId, bytes data) external
- returns (bytes) {
- return data;
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。