代币标准--ERC1155协议源码解析

Posted 宁N分析

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代币标准--ERC1155协议源码解析相关的知识,希望对你有一定的参考价值。

ERC1155多代币标准

ERC1155结合了ERC20和ERC721的能力,这是一个标准接口,支持开发同质化的、半同质化的、非同质化的代币和其他配置的通用智能合约。

IERC1155接口

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

// IERC1155接口同样继承了IERC165接口
interface IERC1155 is IERC165 

TransferSingle事件

// 转移代币后触发事件,记录转移信息
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

TransferBatch事件

// 批量转移代币后触发事件,记录转移信息
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

ApprovalForAll事件

// 授权approve后触发事件,记录授权信息
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

URI事件

// URI的值改变时触发该事件,记录信息
    event URI(string value, uint256 indexed id);

balanceOf函数

// 获取account账户对应代币id拥有的数量
    function balanceOf(address account, uint256 id) external view returns (uint256);

balanceOfBatch函数

// 获取账户列表对用的token余额,相当于多次balanceOf
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

setApprovalForAll函数

// 设置给operate的授权
    function setApprovalForAll(address operator, bool approved) external;

isApprovedForAll函数

// 判断operate是否有account账号的授权
    function isApprovedForAll(address account, address operator) external view returns (bool);

safeTransferFrom函数

// 从from账户转移amount数量的代币id到to地址
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

safeBatchTransferFrom函数

// 批量转移token,相当于调用多次safeTransfer
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;

ERC1155

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/ERC1155.sol)

pragma solidity ^0.8.0;

import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./extensions/IERC1155MetadataURI.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/introspection/ERC165.sol";


contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI 
    using Address for address;
    // 代币id与拥有者地址及其该代币余额的映射
    mapping(uint256 => mapping(address => uint256)) private _balances;
    // 地址之间的授权状态的映射
    mapping(address => mapping(address => bool)) private _operatorApprovals;
    // 相同类型的代币 _uri是一致的
    string private _uri;
   // 构造函数 初始化uri
   constructor(string memory uri_) 
       _setURI(uri_);
   

supportsInterface函数

// 检查合约是否实现该接口
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) 
        return
            interfaceId == type(IERC1155).interfaceId ||
            interfaceId == type(IERC1155MetadataURI).interfaceId ||
            super.supportsInterface(interfaceId);
    

uri函数

// 获取代币的uri,uri指向是代币的元数据,例如图片信息
    function uri(uint256) public view virtual override returns (string memory) 
        return _uri;
    

balanceOf函数

// 获取account地址的id代币的数量
    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) 
    // 检查account不为空地址
        require(account != address(0), "ERC1155: address zero is not a valid owner");
        return _balances[id][account];
    

balanceOfBatch函数

// 返回account账号列表对应的id代币的数量
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public
        view
        virtual
        override
        returns (uint256[] memory)
    
        // 检查账号列表长度与代币id列表长度一致
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
        
        // 创建一个列表长度与account相同
        uint256[] memory batchBalances = new uint256[](accounts.length);
       
       // 获取账号对应的代币id的数量
        for (uint256 i = 0; i < accounts.length; ++i) 
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        

        return batchBalances;
    

setApprovalForAll函数

// 设置对operate的授权approve状态
    function setApprovalForAll(address operator, bool approved) public virtual override 
    // 调用_setApprovalForAll函数
        _setApprovalForAll(_msgSender(), operator, approved);
    

isApprovedForAll函数

    // 获取account地址对operate地址的授权状态
    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) 
        return _operatorApprovals[account][operator];
    

safeTransferFrom函数

// 转移mount数量的id代币 从from地址到to地址
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override 
    // 检查from是否是合约调用者地址,或者from账号有approve授权给合约调用者
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );
        // 调用_safeTransferFrom函数,转移代币
        _safeTransferFrom(from, to, id, amount, data);
    

safeBatchTransferFrom函数

// 批量转移代币
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override 
        // 检查from是否是合约调用者地址,或者from账号有approve授权给合约调用者
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );
        // 调用_safeBatchTransferFrom函数转移代币
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    

_safeTransferFrom函数

// 转移mount数量的id代币 从from地址到to地址
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual 
    // 检查to地址是否为空地址
        require(to != address(0), "ERC1155: transfer to the zero address");
// 获取当前合约调用者地址
        address operator = _msgSender();
        // 获取id列表,该列表只有一个元素 ids[0] = id
        uint256[] memory ids = _asSingletonArray(id);
        // 获取amount列表该列表只有一个元素 amounts[0] = amount
        uint256[] memory amounts = _asSingletonArray(amount);
        // 转移代币前执行的函数
        _beforeTokenTransfer(operator, from, to, ids, amounts, data);
        // 获取from地址的id代币数量
        uint256 fromBalance = _balances[id][from];
        // 检查 from地址的id代币数量fromBalance 是否大于等于要转移的数量amount
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        // 减去转移走的数量 重新写入代币数量
        unchecked 
            _balances[id][from] = fromBalance - amount;
        
        // 加上得到的代币数量,重新写入值
        _balances[id][to] += amount;
        // 触发转移单个代币时间,记录信息
        emit TransferSingle(operator, from, to, id, amount);
        // 代币转移后执行函数
        _afterTokenTransfer(operator, from, to, ids, amounts, data);
        // 检查接收的合约地址是否实现IERC1155Receiver接口
        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    

_safeBatchTransferFrom函数

    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual 
    // 检查账号列表长度与代币id列表长度一致
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
    // 检查to地址是否为空地址
        require(to != address(0), "ERC1155: transfer to the zero address");
   // 获取当前合约调用者地址
        address operator = _msgSender();
  // 代币转移前执行函数
        _beforeTokenTransfer(operator, from, to, ids, amounts, data);
 
        for (uint256 i = 0; i < ids.length; ++i) 
        // 获取代币id
            uint256 id = ids[i];
        // 获取转移数量值
            uint256 amount = amounts[i];
        // 获取当前账号与代币对应数量
            uint256 fromBalance = _balances[id][from];
        // 检查余额是否大于转移数量
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            // 减去转移数量,重新写入值
            unchecked 
                _balances[id][from] = fromBalance - amount;
            
            // 加上转移数量,重新写入值
            _balances[id][to] += amount;
        
    // 触发转移单个代币事件,记录信息
        emit TransferBatch(operator, from, to, ids, amounts);
    // 代币转移后执行函数
        _afterTokenTransfer(operator, from, to, ids, amounts, data);
   // 检查接收的合约地址是否实现IERC1155Receiver接口
        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    

_setURI函数

    // 设置新uri地址
    function _setURI(string memory newuri) internal virtual 
        _uri = newuri;
    

_mint函数

// 铸造amount数量id代币给to地址
    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual 
    // 检查to是否是空地址
        require(to != address(0), "ERC1155: mint to the zero address");
   // 获取合约调用者地址
        address operator = _msgSender();
        // 获取id列表,该列表只有一个元素 ids[0] = id
        uint256[] memory ids = _asSingletonArray(id);
        // 获取amount列表该列表只有一个元素 amounts[0] = amount
        uint256[] memory amounts = _asSingletonArray(amount);
        // 代币转移前执行函数
        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
        // 增加代币余额
        _balances[id][to] += amount;
        // 触发转移单个代币事件,记录信息
        emit TransferSingle(operator, address(0), to, id, amount);
        // 代币转移后执行函数
        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
        // 检查接收的合约地址是否实现IERC1155Receiver接口
        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    

_mintBatch函数

// 铸造amounts列表对应ids列表代币给to地址
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual 
         // 检查to是否是空地址
        require(to != address(0), "ERC1155: mint to the zero address");
        // 检查账号列表长度与代币id列表长度一致
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        // 获取当前合约调用者地址
        address operator = _msgSender();
        // 代币转移前执行函数
        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
        // 增加代币余额
        for (uint256 i = 0; i < ids.length; i++) 
            _balances[ids[i]][to] += amounts[i];
        
        // 触发转移单个代币事件,记录信息
        emit TransferBatch(operator, address(0), to, ids, amounts);
        // 代币转移后执行函数
        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
        // 检查接收的合约地址是否实现IERC1155Receiver接口
        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    

_burn函数

//销毁from地址的id代币
    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal virtual 
        // 检查from地址 不为空地址
        require(from != address(0), "ERC1155: burn from the zero address");
        // 获取当前合约调用者地址
        address operator = _msgSender();

        // 获取id列表,该列表只有一个元素 ids[0] = id
        uint256[] memory ids = _asSingletonArray(id);

         // 获取amount列表该列表只有一个元素 amounts[0] = amount
        uint256[] memory amounts = _asSingletonArray(amount);

        // 代币转移前执行函数
        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        // 获取from账号id代币余额
        uint256 fromBalance = _balances[id][from];

        // 检查余额是否大于等于销毁数量
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");

        // 减去销毁数量,重新写入值
        unchecked 
            _balances[id][from] = fromBalance - amount;
        
        // 触发转移单个代币事件,记录信息
        emit TransferSingle(operator, from, address(0), id, amount);
        // 代币转移后执行函数
        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    

_burnBatch函数

// 批量销毁地址拥有的各种id代币
    function _burnBatch(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual 
        // 检查from地址 不为空地址
        require(from != address(0), "ERC1155: burn from the zero address");
        // 检查账号列表长度与代币id列表长度一致
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
         // 获取当前合约调用者地址
        address operator = _msgSender();
        // 代币转移前执行函数
        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
        // 销毁代币
        for (uint256 i = 0; i < ids.length; i++) 
            // 获取代币id
            uint256 id = ids[i];
            // 获取销毁数量
            uint256 amount = amounts[i];
            // 获取代币余额
            uint256 fromBalance = _balances[id][from];
            // 检查余额是否大于等于销毁数量
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            // 减去转销毁量,重新写入值
            unchecked 
                _balances[id][from] = fromBalance - amount;
            
        
        // 触发批量转移代币事件,记录信息
        emit TransferBatch(operator, from, address(0), ids, amounts);
        // 代币转移后执行函数
        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    

_setApprovalForAll函数

// 设置owner对operate的授权状态
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual 
        // 检查 owner地址和operate地址是否相同
        require(owner != operator, "ERC1155: setting approval status for self");
        // 设置对operate的授权approve状态
        _operatorApprovals[owner][operator] = approved;
        // 触发授权事件
        emit ApprovalForAll(owner, operator, approved);
    

_beforeTokenTransfer函数

// 代币转移前函数
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual 

_afterTokenTransfer函数

// 代币转移后执行函数
    function _afterTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual 

_doSafeTransferAcceptanceCheck函数

// 如果to是普通地址则返回ture,如果to是合约地址则检查该合约是否实现onERC1155Received接口
    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private 
        if (to.isContract()) 
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) 
                if (response != IERC1155Receiver.onERC1155Received.selector) 
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                
             catch Error(string memory reason) 
                revert(reason);
             catch 
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            
        
    

_doSafeBatchTransferAcceptanceCheck函数

// 如果to是普通地址则返回ture,如果to是合约地址则检查该合约是否实现onERC1155Received接口
    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private 
        if (to.isContract()) 
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) 
                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) 
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                
             catch Error(string memory reason) 
                revert(reason);
             catch 
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            
        
    

_asSingletonArray函数

// 返回包含单个元素的列表
    function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) 
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    

剖析非同质化代币ERC721-全面解析ERC721标准

什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC-721创造出来的,每只都是一个独一无二的ERC-721代币,不过ERC-721在区块链世界远不止猫猫狗狗,它更大的想象空间在于将物理世界的资产映射到区块链上。本文就来剖析下什么是ERC721.

ERC721是什么

创建代币一篇,我们讲到过ERC20代币,
和ERC20一样,ERC721同样是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFTs,多翻译为非同质代币。

ERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(2018/03/23官方提议。

那怎么理解非同质代币呢?

非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能置换的。这种独特性使得某些稀有猫具有收藏价值,也因此受到追捧。

ERC20代币是可置换的,且可细分为N份(1 = 10 * 0.1), 而ERC721的Token最小的单位为1,无法再分割。

如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的,而同质是某个部分或数量可以被另一个同等部分或数量所代替。

非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。

ERC721标准

ERC721最为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:

pragma solidity ^0.4.20;

interface ERC721 /* is ERC165 */ {

    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    function balanceOf(address _owner) external view returns (uint256);
    function ownerOf(uint256 _tokenId) external view returns (address);

    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    function approve(address _approved, uint256 _tokenId) external payable;
    function setApprovalForAll(address _operator, bool _approved) external;
    function getApproved(uint256 _tokenId) external view returns (address);
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

接口说明:

  • balanceOf(): 返回由_owner 持有的NFTs的数量。
  • ownerOf(): 返回tokenId代币持有者的地址。
  • approve(): 授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。
  • setApprovalForAll(): 授予地址_operator具有所有NFTs的控制权,成功后需触发ApprovalForAll事件。
  • getApproved()、isApprovedForAll(): 用来查询授权。

  • safeTransferFrom(): 转移NFT所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:
    1. 调用者msg.sender应该是当前tokenId的所有者或被授权的地址
    2. _from 必须是 _tokenId的所有者
    3. _tokenId 应该是当前合约正在监测的NFTs 中的任何一个
    4. _to 地址不应该为 0
    5. 如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。
      一个可接收NFT的合约必须实现ERC721TokenReceiver接口:
      interface ERC721TokenReceiver {
      /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
      function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);
      }
  • transferFrom(): 用来转移NFTs, 方法成功后需触发Transfer事件。调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。

ERC165 标准

ERC721标准同时要求必须符合ERC165标准 ,其接口如下:

interface ERC165 {
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

ERC165同样是一个合约标准,这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。
interfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256(‘supportsInterface(bytes4)‘));ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。
关于ERC165,这里不深入介绍,有兴趣的同学可以阅读官方提案

可选实现接口:ERC721Metadata

ERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。
其接口定义如下:

interface ERC721Metadata /* is ERC721 */ {
    function name() external pure returns (string _name);
    function symbol() external pure returns (string _symbol);
    function tokenURI(uint256 _tokenId) external view returns (string);
}

接口说明:

  • name(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。
  • symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。
  • tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:
    {
    "title": "Asset Metadata",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the asset to which this NFT represents",
        },
        "description": {
            "type": "string",
            "description": "Describes the asset to which this NFT represents",
        },
        "image": {
            "type": "string",
            "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.",
        }
    }
    }

    tokenURI通常是被web3调用,以便在应用层做相应的查询和展示。

可选实现接口:ERC721Enumerable

ERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:

interface ERC721Enumerable /* is ERC721 */ {
    function totalSupply() external view returns (uint256);
    function tokenByIndex(uint256 _index) external view returns (uint256);
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}

接口说明:

  • totalSupply(): 返回NFT总量
  • tokenByIndex(): 通过索引返回对应的tokenId。
  • tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。

补充说明

NTF IDs

NTF ID,即tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:

  1. 从0开始,每新加一个NFT,NTF ID加1
  2. 使用sha3后uuid 转换为 NTF ID

与ERC-20的兼容性

ERC721标准尽可能遵循 ERC-20 的语义,但由于同质代币与非同质代币之间的根本差异,并不能完全兼容ERC-20。

交易、挖矿、销毁

在实现transter相关接口时除了满足上面的的条件外,我们可以根据需要添加自己的逻辑,如加入黑名单等。
同时挖矿、销毁尽管不是标准的一部分,我们可以根据需要实现。

参考实现

参考实现为订阅用户专有福利,请订阅我的小专栏:区块链技术查看。

## 参考文献 1. [EIPS-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) 2. [EIPS-721](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md) 3. [深入浅出区块链](https://learnblockchain.cn/) - 系统学习区块链,打造最好的区块链技术博客。

以上是关于代币标准--ERC1155协议源码解析的主要内容,如果未能解决你的问题,请参考以下文章

,让你了解ERC-1155 多代币标准协议

剖析非同质化代币ERC721-全面解析ERC721标准

初探区块链数字加密资产标准ERC721

solidity实现智能合约教程-ERC1155合约

ERC20代币标准初识

ERC1155实践欧阳询书法复制品从确权设计到买卖测试