以太坊 TOKEN

admin
admin 2020年07月03日
  • 在其它设备中阅读本文章

EIP全称 Ethereum Imporvement Proposals(以太坊改进建议), 是以太坊开发者社区提出的改进建议, EIP 可以是Ethereum 生态中任意领域的改进, 比如新特性、ERC、协议改进、编程工具等等。EIP 是以太坊生态演进的公告牌,可以在以下地址查询到当前所有的 EIPs:https://eips.ethereum.org/all
ERC全称 Ethereum Request For Comment(以太坊意见征求稿), 用以记录以太坊上应用级的各种开发标准和协议(application-level standards and conventions)。如典型的 Token 标准(ERC20,ERC721)、名字注册(ERC26,ERC13), URI 范式(ERC67), Library/Package 格式(EIP82), 钱包格式(EIP75,EIP85)。ERC 协议标准是影响以太坊发展的重要因素, 像 ERC20, ERC223, ERC721, ERC777 等, 都是对以太坊生态产生了很大影响。
可以看出:所有的 ERC 都是 EIP。反之则未必。以太坊开发者希望利用技术社区的众包智慧来决定 Ethereum 系统的未来走向, 而不是集中在几个硏发人员手里。因此每个人都可以在 Ethereum 社区提出任何好的 EIP。

一、ERC20

ERC20是是 2015 年 11 月份推出的一种技术标准,我们经常看到的 EOS,USDT(ERC20),OMG 等就是基于 ERC20 标准开发的代币;
ERC20 的优势

  • ERC20 标准规定了各个代币的基本功能,非常方便第三方使用;
  • 基于 ERC20 标准开发的同种代币价值都是相同的,它们可以进行互换;
  • ERC20 代币能兼容 ETH 钱包,也就是说,ETH 的钱包地址可以接收所有的 ERC20 代币。因此 ERC20 代币能被交易所整合,立即进行交易。
    ERC20 的劣势
  • ERC20 标准无法通过接收方合同处理传入的交易。这是该令牌存在的最大问题,也是开发者一直希望改进的地方;
  • ERC20 令牌无法将令牌发送给一个与这些令牌不兼容的契约,也正因为这样,部分资金存在丢失的风险。
    实现
    pragma solidity ^0.5.6;
    
    contract ERC20 {
        uint256 public totalSupply;
        string public name;
        string public symbol;
        uint8 public decimals;
        
        mapping (address => uint256) private balances;
        mapping (address => mapping (address => uint256)) private allowed;
        
        event Transfer(address indexed _from, address indexed _to, uint256 _value);
        event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    
        constructor(
            uint256 _initialAmount,
            string memory _tokenName,
            uint8 _decimalUnits,
            string memory _tokenSymbol
        ) public {
            balances[msg.sender] = _initialAmount;
            totalSupply = _initialAmount;
            name = _tokenName;
            decimals = _decimalUnits;
            symbol = _tokenSymbol;
        }
    
        function transfer(address _to, uint256 _value) public returns (bool success) {
            require(balances[msg.sender] >= _value);
            balances[msg.sender] -= _value;
            balances[_to] += _value;
            emit Transfer(msg.sender, _to, _value);
            return true;
        }
    
        function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
            uint256 allowance = allowed[_from][msg.sender];
            require(balances[_from] >= _value && allowance >= _value);
            balances[_to] += _value;
            balances[_from] -= _value;
            allowed[_from][msg.sender] -= _value;
            emit Transfer(_from, _to, _value);
            return true;
        }
    
        function balanceOf(address _owner) public view returns (uint256 balance) {
            return balances[_owner];
        }
    
        function approve(address _spender, uint256 _value) public returns (bool success) {
            allowed[msg.sender][_spender] = _value;
            emit Approval(msg.sender, _spender, _value);
            return true;
        }
    
        function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
            return allowed[_owner][_spender];
        }
    }

二、ERC721

和 ERC20 一样,ERC721 同样是一个代币标准,ERC721 的官方解释是是“Non-Fungible Tokens”,英文简写为”NFT”,翻译过来即不可互换的非同质 Token。简单地说,就是每个 Token 都是独一无二的,是不能互换的;
之前大火的撸猫游戏 CryptoKitties 也是基于 ERC721 标准开发的,CryptoKitties 中的猫都是 ERC721 的 Token,不同基因的猫其实只是不同属性的 Token,正是由于 ERC721 的独一无二性(NFT)让 CryptoKitties 的猫变得极具收藏价值,单只猫的价值更是达到了 1000 个 ETH!!
ERC20 和 ERC721 的区别:

  • Token 之间是否可以互换,基于 ERC20 标准开发的 Token 没有价值的区别,是可以互换的,基于 ERC721 标准开发的 Token 则是独一无二,不可以互换的;
  • Token 是否可以分割,ERC20 的 Token 是可以分割的,而 ERC721 的 Token 最小的单位为 1,它是不能分割的

实现

    pragma solidity ^0.5.6;
    
    /* 一种非同质代币合约,doc:eips.ethereum.org/EIPS/eip-721 */
    contract ERC721 {
        using AddressUtils for address;
    
        mapping (uint256 => address) private  idToOwner;
        mapping (uint256 => address) private idToApproval;
        mapping (address => uint256) private ownerToNFTokenCount;
        mapping (address => mapping (address => bool)) private ownerToOperators;
    
        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);
    
        modifier validNFToken(uint256 _tokenId){
            require(idToOwner[_tokenId] != address(0), "NOT_VALID_TOKEN");
            _;
        }
    
        modifier canOperate(uint256 _tokenId){
            address tokenOwner = idToOwner[_tokenId];
            require(tokenOwner == msg.sender || ownerToOperators[tokenOwner][msg.sender], "NOT_OWNER_OR_OPERATOR");
            _;
        }
    
        modifier canTransfer(uint256 _tokenId){
            address tokenOwner = idToOwner[_tokenId];
            require(tokenOwner == msg.sender
            || idToApproval[_tokenId] == msg.sender
            || ownerToOperators[tokenOwner][msg.sender],
            "NOT_OWNER_APPROWED_OR_OPERATOR");
            _;
        }
    
        // 核心接口
        function balanceOf(address _owner) public view returns (uint256){
            require(_owner != address(0), "ZERO_ADDRESS");
            return ownerToNFTokenCount[_owner];
        }
    
        function ownerOf(uint256 _tokenId) public view returns (address _owner){
            _owner = idToOwner[_tokenId];
            require(_owner != address(0), "NOT_VALID_NFT");
        }
    
        function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) public payable{
            _safeTransferFrom(_from, _to, _tokenId, data);
        }
    
        function safeTransferFrom(address _from, address _to, uint256 _tokenId) public payable{
            _safeTransferFrom(_from, _to, _tokenId, "");
        }
    
        function transferFrom(address _from, address _to, uint256 _tokenId) public payable
        canTransfer(_tokenId)
        validNFToken(_tokenId)
        {
            address tokenOwner = idToOwner[_tokenId];
            require(tokenOwner == _from, "NOT_OWNER");
            require(_to != address(0), "ZERO_ADDRESS");
            _transfer(_to, _tokenId);
        }
    
        function approve(address _approved, uint256 _tokenId) public payable
        canOperate(_tokenId)
        validNFToken(_tokenId)
        {
            address tokenOwner = idToOwner[_tokenId];
            require(_approved != tokenOwner, "IS_OWNER");
    
            idToApproval[_tokenId] = _approved;
            emit Approval(tokenOwner, _approved, _tokenId);
        }
    
        function setApprovalForAll(address _operator, bool _approved) public{
            ownerToOperators[msg.sender][_operator] = _approved;
            emit ApprovalForAll(msg.sender, _operator, _approved);
        }
    
        function getApproved(uint256 _tokenId) public view validNFToken(_tokenId) returns (address){
            return idToApproval[_tokenId];
        }
    
        function isApprovedForAll(address _owner, address _operator) public view returns (bool){
            return ownerToOperators[_owner][_operator];
        }
    
        function supportsInterface(bytes4 _interfaceID) private view returns (bool);
    
    
        //元信息扩展,可选的
        string public name;
        string public symbol;
        mapping (uint256 => string) tokenURIs;
    
        function tokenURI(uint256 _tokenId) public view validNFToken(_tokenId) returns (string memory){
            return tokenURIs[_tokenId];
        }
        
        
        // 枚举信息扩展,可选的
        uint256[] internal tokens;
        mapping(uint256 => uint256) private idToIndex;
        mapping(address => uint256[]) private ownerToIds;
        mapping(uint256 => uint256) private idToOwnerIndex;
    
        function totalSupply() external view returns (uint256){
            return tokens.length;
        }
    
        function tokenByIndex(uint256 _index) public view returns (uint256){
            require(_index < tokens.length, "INVALID_INDEX");
            return tokens[_index];
        }
    
        function tokenOfOwnerByIndex(address _owner, uint256 _index) public view returns (uint256){
            require(_index < ownerToIds[_owner].length, "INVALID_INDEX");
            return ownerToIds[_owner][_index];
        }
    
    
        function _transfer(address _to,uint256 _tokenId)internal{
            address from = idToOwner[_tokenId];
            _clearApproval(_tokenId);
    
            _removeNFToken(from, _tokenId);
            _addNFToken(_to, _tokenId);
            emit Transfer(from, _to, _tokenId);
        }
        
        function _mint(address _to,uint256 _tokenId)internal{
            require(_to != address(0), "ZERO_ADDRESS");
            require(idToOwner[_tokenId] == address(0), "NFT_ALREADY_EXISTS");
            _addNFToken(_to, _tokenId);
            emit Transfer(address(0), _to, _tokenId);
            
            // for 枚举信息扩展
            tokens.push(_tokenId);
            idToIndex[_tokenId] = tokens.length - 1;
        }
        
        // for 枚举信息扩展
        function _setTokenUri(uint256 _tokenId,string memory _uri)internal validNFToken(_tokenId){
            tokenURIs[_tokenId] = _uri;
        }
        
        function _burn(uint256 _tokenId)internal validNFToken(_tokenId){
            address tokenOwner = idToOwner[_tokenId];
            _clearApproval(_tokenId);
            _removeNFToken(tokenOwner, _tokenId);
            emit Transfer(tokenOwner, address(0), _tokenId);
            
            // for 元信息扩展
            if (bytes(tokenURIs[_tokenId]).length != 0)
                delete tokenURIs[_tokenId];
            
            // for 枚举信息扩展
            uint256 tokenIndex = idToIndex[_tokenId];
            uint256 lastTokenIndex = tokens.length - 1;
            uint256 lastToken = tokens[lastTokenIndex];
            tokens[tokenIndex] = lastToken;
            tokens.pop();
            idToIndex[lastToken] = tokenIndex;
            idToIndex[_tokenId] = 0;
        }
        
        function _removeNFToken(address _from,uint256 _tokenId)internal{
            require(idToOwner[_tokenId] == _from, "NOT_OWNER");
            ownerToNFTokenCount[_from] = ownerToNFTokenCount[_from] - 1;
            delete idToOwner[_tokenId];
            
            // for 枚举信息扩展
            uint256 tokenToRemoveIndex = idToOwnerIndex[_tokenId];
            uint256 lastTokenIndex = ownerToIds[_from].length - 1;
            
            if (lastTokenIndex != tokenToRemoveIndex){
                uint256 lastToken = ownerToIds[_from][lastTokenIndex];
                ownerToIds[_from][tokenToRemoveIndex] = lastToken;
                idToOwnerIndex[lastToken] = tokenToRemoveIndex;
            }
            ownerToIds[_from].pop();
        }
        
        function _addNFToken(address _to,uint256 _tokenId)internal {
            require(idToOwner[_tokenId] == address(0), "NFT_ALREADY_EXISTS");
            idToOwner[_tokenId] = _to;
            ownerToNFTokenCount[_to] += 1;
            
            // for 枚举信息扩展
            ownerToIds[_to].push(_tokenId);
            idToOwnerIndex[_tokenId] = ownerToIds[_to].length - 1;
        }
        
         function _getOwnerNFTCount(address _owner)internal view returns (uint256){
             return ownerToIds[_owner].length;
         }
        
        function _safeTransferFrom(address _from,address _to,uint256 _tokenId,bytes memory _data) private
        canTransfer(_tokenId) validNFToken(_tokenId)
        {
            address tokenOwner = idToOwner[_tokenId];
            require(tokenOwner == _from, "NOT_OWNER");
            require(_to != address(0), "ZERO_ADDRESS");
            
            _transfer(_to, _tokenId);
            
            if (_to.isContract()){
                bytes4 retval = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
                require(retval == bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")), "NOT_ABLE_TO_RECEIVE_NFT");
            }
        }
        
        function _clearApproval(uint256 _tokenId)private{
            if (idToApproval[_tokenId] != address(0))
                delete idToApproval[_tokenId];
        }
    }
    
    
    interface ERC721TokenReceiver {
        // return  bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
        function onERC721Received(address _operator,address _from,uint256 _tokenId,bytes calldata _data) external returns(bytes4);
    }
    
    library AddressUtils {
        function isContract (address _addr) internal view returns (bool) {
            uint256 size;
            assembly { size := extcodesize(_addr) }
            return size > 0;
        }
    }