以太坊上那些关于NFT的协议详解 |猿创征文

杰哥的技术杂货铺 2022-06-18 13:02:16

目录

  • 一、ERC简介
  • 1.1 什么是ERC协议
  • 1.2 比较常用的ERC协议
  • 1.3 各个ERC协议在以太坊系链中是通用的吗?
  • 二、 ERC721 非同质化代币
  • 2.1 ERC721简介
  • 2.1.1 ERC-721是什么?
  • 2.2 NFT的实现
  • 2.2 ERC20721 外部接口
  • 2.2.1 IERC721 基本接口
  • 2.2.2 IERC721Metadata 元数据接口
  • 2.2.3 IERC721Enumerable 枚举接口
  • 2.3 合约内部实现接口
  • 2.3.1 ERC721 token 铸造
  • 2.3.2 ERC721 token 销毁
  • 2.3.3 其它接口
  • 2.4 安全转移
  • 2.5 补充说明
  • 三、ERC-1155 多代币标准
  • 3.1 ERC-1155 简介
  • 3.2 ERC-1155 外部接口
  • 3.2.1 IERC 1155 基本接口
  • 3.2.2 IERC1155 MetadataURI 元数据接口
  • 3.2.3 IERC1155 Receiver 元数据接口
  • 3.3 ERC 1155 token 销毁
  • 3.4 ERC-1155 的批量操作功能
  • 3.4.1 批量传输
  • 3.4.2 批量余额
  • 3.4.3 批量审批
  • 四、其它说明
  • 4.1 ERC-721与ERC-1155之间的区别
  • 4.2 ERC20、ERC721或ERC1155的具体使用案例是什么?
  • 4.3 为什么合约中都需要实现ERC-165合约

参考文档:

一、ERC简介

1.1 什么是ERC协议

1. 什么是ERC协议?

ERC全称是“Ethereum Request for Comment”,表示以太坊开发者提交的协议提案,ERC中包含技术和组织等注意事项及标准,ERC后面的数字是议案的编号。目前最常见的ERC标准是ERC-20和ERC-721。

2. ERC是谁编写的?

ERC标准是由以太坊开发人员为以太坊社区编写的。他们为了给以太坊平台创建标准,提交了以太坊改进建议( EIP )——包括协议规范和合同标准。如果EIP被委员会批准并最终确定,它就成为了一个ERC

EIP的完整列表请参阅:https://github.com/ethereum/EIPs/tree/master/EIPS

所有的ERC都是EIP。但,反之则未必。

类似的操作,还例如TRON网络中的提议:

委员会由前27名超级代表组成,用于修改TRON网络动态参数,如出块奖励、交易费用等等。每个超级代表、超级代表合伙人及超级代表候选人都具有提议权,以及对提议的投票权。当提议获得18个及以上的超级代表赞成时,该提议将通过,并在下个维护期内进行网络参数修改。

3. ERC有什么作用?

这些被委员会批准确定的EIP,为以太坊开发人员提供了一套可行的ERC标准。由于EIP的接口是开放的,所以开发人员可以基于这些标准之上构建智能合约。其中ERC-20是整个加密社区中最流行的标准,以太坊平台上的所有通证都使用这一标准。

1.2 比较常用的ERC协议

1. ERC-20: 同质化代币

  • ERC-20是现下最广为人知的标准,诞生于2015年,到2017年9月被正式标准化。在ERC-20标准里没有价值的区别,Token之间是可以互换的。也就是说,在ERC-20标准下,任何单位币的价值都是相同的。ERC-20标准里规定了Token需要有它的名字、符号、总供应量以及包含转账、汇款等其他功能。目前,以太坊上ERC-20 Token的数量超过了180000种。

  • 在ERC20通证标准中执行交易有两种方式:1.transfer方法。2.approve + transferFrom 机制通证余额只是通证合约中的一个变量。通证的交易在合同中的表现是变量的变化:转出账户的余额将减少,接收账户的余额将增多。

ERC20具体API接口可参考:https://blog.csdn.net/cljdsc/article/details/125204450?spm=1001.2014.3001.5502

2. ERC-721:非同质化代币

  • 基于ERC-721标准开发的代币合约被统称为"非同质化代币(Non-Fungible Tokens,缩写为NFT)",ERC-721代币相较于ERC-20代币最大的区别就是不可分割性和唯一性其Token的最小单位为1,且每个Token对不同的用户都有不同的价值含义。
  • ERC-721代表了对资产的所有权,为物品归属性记录提供的可能性,例如现实世界中的文学作品,收藏品等等,还有金融交易中的借贷交易记录等信息。

3. ERC-1155:更适合区块链游戏的NFT

  • ERC-1155标准规范可以看做是ERC-721标准的一个升级版本,ERC-1155与ERC-721协议对比有以下方面的升级:
  • ERC-1155可以在一个智能合约中定义多个物品(Token),ERC-1155还可以用来把多个物品(Token)合并打包成一个物品(Token包)。ERC-1155融合了ERC-20和ERC-721的一些优点,开发者可以很方便的创建海量种类的物品,每个物品可以是ERC-721那样独立的,也可以像ERC-20一样同质化

1.3 各个ERC协议在以太坊系链中是通用的吗?

更准确来说,应该是叫EVM系,以上这些ERC协议在EVM系列公链中全部适用

EVM的存在是为了能让我们用Solidity编写的合约代码,运行在以太坊的环境中。然后我们调用运行在以太坊上的合约,用于执行以太坊上的合约交易或合约信息查询

这样类比的话,以太坊就相当于计算机环境,EVM把合约代码编译成以太坊能识别的机器码运行

对于EVM的理解,可参阅此文档:https://ethereum.org/en/developers/docs/evm/

除了EVM系公链,还有非EVM系的公链。至今我们已知的以 Cosmos、Polkadot、Solana等为首的非EVM系的公链。

二、 ERC721 非同质化代币

2.1 ERC721简介

2.1.1 ERC-721是什么?

非同质代币(NFT)是一种具有唯一性识别的代币。 non-fungible这个词意味着你不能用一个NFT来代替另一个NFT。 每一个NFT都是独一无二的。 相比比特币或ERC-20等任何可互换的代币,你收到的一个币(coin)/代币(token)并不重要,它们都具有相同的价值。

以太坊上Cryptokitties将其变成了现在流行的ERC-721标准。

2.2 NFT的实现

核心是有一个从 uint256 =>address 的映射。 这与从 address =>uint256的同质化代币(如ERC-20)映射相反。

为什么会有这种反向映射?

** 在同质化代币中,用所有者映射到余额。**

而在ERC-721中,我们其实也有这个映射(见下面的balanceOf),但我们更重要的是需要一种方法唯一识别代币通过从uint256到地址的映射,我们可以给每一个被铸造的代币分配一个id,并将这个id准确地映射给所有者。 当创建一个新的NFT代币时,会创建一个新的id(最常见的是从id 1开始,然后每ci计数+1任何后面的id),并将所有者设置为 ownerMapping[id] = receiverAddress

可通过查看下面马上讲到的ERC-721接口函数理解此概念

我在rinkeby测试网中发布了一个ERC721合约,该合约已进行源码认证。大家可通过该链接进行学习:https://rinkeby.etherscan.io/address/0xf1321fc4e703a494202fe516afc91489356cb22f#code

2.2 ERC20721 外部接口

EIP由三个接口组成,ERC 721分别为IERC721、IERC721Metadata和IERC721Enumerable标准的ERC 721合约规范中要求实现IERC721即可

2.2.1 IERC721 基本接口

ERC721协议要求实现ERC721代币时必须要遵守的协议,符合 ERC721 的合约所需的接口,接口的定义如下:

interface IERC721 is IERC165 {
    //合约事件接口
    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 balanceOf(address owner) external view returns (uint256 balance);
    function ownerOf(uint256 tokenId) external view returns (address owner);
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function getApproved(uint256 tokenId) external view returns (address operator);
    function setApprovalForAll(address operator, bool _approved) external;
    function isApprovedForAll(address owner, address operator) external view returns (bool);
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}

函数

1. balanceOf(address owner) → uint256 balance

返回由_owner(所有者)持有的NFTs的数量。

2. ownerOf(uint256 tokenId) → address owner

返回tokenId代币持有者的地址,tokenId必须存在。

3. safeTransferFrom(address from, address to, uint256 tokenId)

安全地将代tokenId币从转移from到to,首先检查合约接收者是否了解 ERC721 协议,以防止代币被永久锁定,需要符合以下要求:

  • from 必须是 _tokenId的所有者。如果不是,则它必须已被允许通过approve(授权将单个token给某地址)或移动此令牌setApprovalForAll(授权将该地址所有token给某地址)。

  • tokenId 应该是当前合约的某个nft,并且属于from地址。

  • to地址不应该为 0。如果to指的是智能合约,它必须实现IERC721Receiver.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);
      }
    
  • 该方法可触发Transfer事件。

4. transferFrom(address from, address to, uint256 tokenId)

将tokenId从转移from到to。不鼓励使用此方法,尽可能使用safeTransferFrom。,需要符合以下要求:

  • from 必须是 _tokenId的所有者。如果不是,则它必须已被允许通过approve(授权将单个token给某地址)或移动此令牌setApprovalForAll(授权将该地址所有token给某地址)。

  • tokenId 应该是当前合约的某个nft,并且属于from地址。

  • to地址不应该为 0。

  • 该方法可触发Transfer事件。

注:调用者需要自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件。

5. approve(address to, uint256 tokenId)

将tokenId为参数_tokenId的NFT授权给地址_approved,或者更新授权地址为_approved,并且触发Approve事件。函数能够执行,必需满足以下条件:

  • 调用者sender有权转移_tokenId对应NFT的所有权,即:msg.sender是_tokenId对应NFT的所有者或者得到了其授权。
  • tokenId必须存在。
  • 一次只能批准一个帐户,因此批准一个空地址会清除以前的批准。
  • 该方法可触发Approval事件。

6. getApproved(uint256 tokenId) → address operator

查询单个NFT的授权地址。该_tokenId 必须是一个有效的NFT的tokenId。

7. setApprovalForAll(address operator, bool _approved)

将调用者msg.sender的所有NFT资产授权给_operator地址来管理,或者取消授权。函数的实现需要做一下几种检查:

  • operator不能是调用者。
  • 参数_approved是true代表授权;参数_approved是false代表取消授权。
  • 该方法可触发ApprovalForAll事件。

注:operator是一个地址,它可以是合约或者常规的地址

,并且触发ApprovalForAll事件。

8. isApprovedForAll(address owner, address operator) → bool

查询地址_owner的所有NFT是否授权给了另一个地址_operator来管理。若已授权,则返回true;否则,返回false。

9. safeTransferFrom(address from, address to, uint256 tokenId, bytes data)

安全地转移NFT所有权并发送数据,即将tokenId为参数_tokenId的NFT的所有权由地址_from转移给地址_to,并将额外的数据data发送给地址_to,并触发Transfer事件。参数data没有格式要求。

事件

1. Transfer(address from, address to, uint256 tokenId)

三种情况将触发Transfer事件

  • 当tokenId从from转移到to时。
  • 代币合约在铸造NFT时应该触发Transfer事件,并将_from地址设置为0。此时,也可以自定义并触发Mint事件:
  • 代币合约在销毁NFT时应该触发Transfer事件,并将_to地址设置为0x。此时,也可以自定义并触发Burn事件:

2. Approval(address owner, address approved, uint256 tokenId)

当将_owner地址的单个NFT(其tokenId为参数_tokenId)授权给_approved地址时,在授权完成后触发Approval事件。

3. ApprovalForAll(address owner, address operator, bool approved)

当将_owner地址的所有NFT授权给_operator地址或者取消该授权时,触发ApprovalForAll事件。

2.2.2 IERC721Metadata 元数据接口

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

interface IERC721Metadata is IERC721 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

1. name() → string

返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。

2. symbol() → string

返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。

3. tokenURI(uint256 tokenId) → string

返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:

{
  "title": "Asset Metadata",
  "type": "object",
  "properties": {
    "name": {  //token名称
      "type": "string",  //类型
      "description": "Identifies the asset to which this token represents"  //描述
    },
    "description": { //token描述
      "type": "string",  //类型
      "description": "Describes the asset to which this token represents" //描述
    },
    "image": { //token图片
      "type": "string", //类型
      "description": "A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."  //描述
    }
  }
}

2.2.3 IERC721Enumerable 枚举接口

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

interface IERC721Enumerable is IERC721 {
    function totalSupply() external view returns (uint256);
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
    function tokenByIndex(uint256 index) external view returns (uint256);
}

1. totalSupply() → uint256

返回该NFT合约拥有的token总量

2. tokenOfOwnerByIndex(address owner, uint256 index) → uint256 tokenId

通过索引返回对应的tokenId。

3. tokenByIndex(uint256 index) → uint256

所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。

2.3 合约内部实现接口

上面讲到的ERC721的基本接口、元数据接口、枚举接口皆是引用的外部合约包的接口,以下铸造、销毁等接口由个人开发完成,属于内部接口。

2.3.1 ERC721 token 铸造

function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }
    function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
        _mint(to, tokenId);
        require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");
        _beforeTokenTransfer(address(0), to, tokenId);
        _balances[to] += 1;
        _owners[tokenId] = to;
        emit Transfer(address(0), to, tokenId);
    }

1. _safeMint(address to, uint256 tokenId)

安全铸造tokenId并将其转移到to。需符合以下要求:

  • tokenId不得存在于该NFT合约中。
  • 如果to指的是智能合约,它必须实现IERC721Receiver.onERC721Received,这被称为安全转移。
  • 该方法可触发Transfer事件。

2. _safeMint(address to, uint256 tokenId, bytes _data)

作用与要求与上面相同,带有一个附加data参数,该参数被转发IERC721Receiver.onERC721Received给合同接收者。

3. _mint(address to, uint256 tokenId)

铸造tokenId并将其转移到to。需符合以下要求:

  • tokenId不得存在于该NFT合约中。
  • to不能是零地址。
  • 该方法可触发Transfer事件。

注:不鼓励使用此方法,尽可能使用_safeMint

2.3.2 ERC721 token 销毁

function _burn(uint256 tokenId) internal virtual {
    address owner = ERC721.ownerOf(tokenId);
    _beforeTokenTransfer(owner, address(0), tokenId);
    // Clear approvals
    _approve(address(0), tokenId);
    _balances[owner] -= 1;
    delete _owners[tokenId];
    emit Transfer(owner, address(0), tokenId);
}

_burn(uint256 tokenId)

销毁tokenId。销毁tokenId时将清除批准授权。需符合以下要求:

  • tokenId必须存在。
  • 发出Transfer事件。

2.3.3 其它接口

    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
        require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
        _tokenURIs[tokenId] = _tokenURI;
    }
    

_setTokenURI(uint256 tokenId, string _tokenURI)

设置_tokenURI为 的 tokenURI tokenId。tokenId必须存在。


function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override {
      super._beforeTokenTransfer(from, to, tokenId);

      if (from == address(0)) {
          _addTokenToAllTokensEnumeration(tokenId);
      } else if (from != to) {
          _removeTokenFromOwnerEnumeration(from, tokenId);
      }
      if (to == address(0)) {
          _removeTokenFromAllTokensEnumeration(tokenId);
      } else if (to != from) {
          _addTokenToOwnerEnumeration(to, tokenId);
      }
  }

_beforeTokenTransfer(address from, address to, uint256 tokenId)

在任何token传输之前调用的钩子。这包括铸造和燃烧。调用条件

  • 当from和to都非零时,from将tokenId转移到to。
  • 当from为零时,tokenId将被铸造为to.
  • 当to为零时,from将tokenId被烧毁。
  • from不能是零地址。
  • to不能是零地址。

详情可见使用钩子


function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
    return interfaceId == type(IERC721).interfaceId
        || interfaceId == type(IERC721Metadata).interfaceId
        || super.supportsInterface(interfaceId);
}

supportsInterface(bytes4 interfaceId) → bool

如果该合约实现了由interfaceId定义的接口,则返回true 。请参阅相应的 EIP 部分 以了解有关如何创建这些 id 的更多信息。

注:这个函数调用必须使用少于30000个gas。

2.4 安全转移

上面提到我们在进行transfer或者mint时,最好使用带有safe的接口,那么大家可能会有以下疑问:

问题一:

  • 问:选择safe的理由是什么?
  • 答:使用 safe 转账或铸造,表示这个动作是安全的,即便你转账的对象是一个合约,而不会发生代币锁定在合约的情况。

问题二:

  • 问:那么为什么接受erc721代币的合同必须实现onERC721Received才可以使用safemint等safe的函数?

  • 答:因为只有这个合约明确表明接收 NFT(实现 onERC721Received),才可以完成转账。如果你使用了普通转账,那这个合约在逻辑上是不会理会你给谁转,如果你不小心转到一个错误的合约地址上,你的 NFT 就永远无法从合约中取出。

问题三:

  • 问:为什么返回值一定要是:bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));这个值,他和IERC721Receiver.onERC721Received.selector有什么关系?
  • 答:onERC721Received 使用的这个返回值,仅仅是一个约定,约定采用这个值而已。而函数选择器 selector值,代表了这个函数,就约定用这个值。

2.5 补充说明

1. NTF IDs

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

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

2. 与ERC-20的兼容性

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

3. 交易、挖矿、销毁

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

三、ERC-1155 多代币标准

3.1 ERC-1155 简介

1. 什么是ERC-1155协议

  • ERC-1155代币标准是由Enjin项目背后的团队开发的,该项目专注于基于区块链的游戏解决方案。Enjin于2019年推出了代币标准,它是ERC-20标准和ERC-721标准之间的中间地带。
  • Enjin发现了一些与相对有限的ERC-721标准相关的挑战,特别是无法进行批量转移。

  • 与ERC-721不同,如果要传输多个NFT,每个NFT将需要单笔交易——因为每个NFT由单个智能合约表示。当铸造或交易单个NFT时,就会导致有过高的交易成本。ERC-1155允许批量转移(单个智能合约上的多个资产),这将使得所有代币可一次性转移,减少网络拥挤,降低gas成本。

例如,当一个用户想要向另一个用户出售游戏中的1000个道具时,他或她可以使用ERC-1155的批量代币转移一次性将它们全部进行发送。

2. ERC 1155 代币特性

  • 这个多代币标准的另一个主要特性是它支持可替代和不可替代代币——因为它能够在同一地址和合约上支持多种状态。实际上,这意味着用户可以在该地址上使用可替代代币进行游戏内支付,同时也可以转移独特的NFT资产。
  • ERC-1155的另一个特性是它支持创建半可替代代币。SFT作为可替代代币进行交易,但一旦赎回,它们就会转换为NFT。例如,活动之前的音乐会门票可以被视为可替代资产——任何门票都会为您提供相同的 GA 入场券。然而,音乐会结束后,门票失去了交易价值,成为了一件独特的纪念品。SFT将这种类型的功能直接引入票据本身的代码中。
  • 最后,如果出现错误,可以恢复此标准上的代币转移。在ERC-721标准中,如果资产被发送到错误的地址,则无法回收它们。然而,ERC-1155包含一个函数来解决这个问题。安全传递函数和一些其他的规则是到位的,以防止剥削。

3. ERC-1155代币的特点

  • ERC-1155是一款智能合约接口,代表同质化的、半同质化的和非同质化的代币。
  • ERC-1155可以执行ERC-20和ERC-720的功能,甚至可以同时执行一次。
  • 代币都可以根据代币的性质代表不同的价值;同质化的、半同质化的和非同质化的。
  • ERC-1155适用于创建NFT、可兑换购物券、ICO等。

3.2 ERC-1155 外部接口

这套接口和合约都与ERC1155 Multi Token Standard的这份ERC 标准相关。

EIP 由履行不同角色的三个接口组成,此处分别 IERC1155 为 IERC1155MetadataURI 和 IERC1155Receiver

3.2.1 IERC 1155 基本接口

符合 ERC1155 的合约的必需接口,如EIP中所定义。

interface IERC1155 is IERC165 {
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);
    event URI(string value, uint256 indexed id);
    function balanceOf(address account, uint256 id) external view returns (uint256);
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);
    function setApprovalForAll(address operator, bool approved) external;
    function isApprovedForAll(address account, address operator) external view returns (bool);
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

功能:

1. balanceOf(address account, uint256 id) → uint256

查询owner地址的余额,与ERC20协议中的balanceOf功能相同。

  • account不能是零地址。

2. balanceOfBatch(address[] accounts, uint256[] ids) → uint256[]

账户余额批量查询,在单次调用中获取多个余额。

  • accounts并且ids必须具有相同的长度。

3. setApprovalForAll(address operator, bool approved)

设置操作帐户_operator为 已授权账户 或 取消授权。授权过程与ERC20略有不同。这里不是授权特定金额,而是类似于ERC721协议中的授权,通过 setApprovalForAll 函数,对操作账户进行完全的授权或取消授权。

  • 该功能将发出一个ApprovalForAll事件。
  • operator不能是调用者。

4. isApprovedForAll(address account, address operator) → bool

查询当前的审批状态,即_owner是否对_operator进行了授权。结果为true代表全部授权;结果为false代表未授权。 不能定义要授权代币的数量,甚至代币类型。

5. safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data)

将tokenId以及对应的amount数量的NFT所有权由地址_from转移给地址_to,并将额外的数据data发送给地址_to,参数data没有格式要求。需要符合以下要求:

  • from 必须拥有该tokenId的代币余额(), from必须是 _tokenId的所有者。如果不是,它必须已被批准from通过setApprovalForAll。
  • tokenId 应该是当前合约的某个nft,并且属于from地址。
  • amount必须有值
  • to地址不应该为 0。如果to指的是智能合约,它必须实现IERC1155Receiver.onERC1155Received,这被称为安全转移。
  • 该方法可触发TransferSingle事件。

6. safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes data)

ERC1155代币批量转移,包括同质化代币以及非同质化代币。需要符合以下要去:

  • ids并且amounts必须具有相同的长度。
  • to地址不应该为 0。如果to指的是智能合约,它必须实现IERC1155Receiver.onERC1155Received,这被称为安全转移。
  • 该方法可触发TransferSingle事件。

与ERC20与ERC721相比,ERC1155中唯一的区别是将数值作为数组参数进行传递,此外还会传递数组 id。例如,给出ids=[3, 6, 13]和values=[100, 200, 5],传输结果如下:

  • 将id=3的100个代币从地址_from传输到地址_to;
  • 将id=6的200个代币从地址_from传输到地址_to;
  • 将id=13的5个代币从地址_from转移到地址_to。

注:ERC1155中只有transferFrom,没有transfer。要想像常规的transfer一样使用它,只需将"from"地址设为调用该函数的地址。

事件:

1. TransferSingle(address operator, address from, address to, uint256 id, uint256 value)

TransferSingle事件在代币转移成功时触发,包括转移的代币数量为0的情况。另外,代币铸造和销毁时也可以触发TransferSingle事件。与ERC20协议中的Transfer事件相同。

2. TransferBatch(address operator, address from, address to, uint256[] ids, uint256[] values)

等效于多个TransferSingle事件,TransferBatch事件在代币批量转移成功时触发,包括转移的代币数量为0的情况。另外,代币批量铸造和销毁时也需要触发TransferBatch事件。

3. ApprovalForAll(address account, address operator, bool approved)

当将account地址的所有代币全部授权给_operator地址或者取消该授权时,触发ApprovalForAll事件。

URI(string value, uint256 id)

当一个代币ID的URI更新成功时触发URI事件。

3.2.2 IERC1155 MetadataURI 元数据接口

可选 ERC1155MetadataExtension接口定义中的的接口,如EIP中所定义。

interface IERC1155MetadataURI is IERC1155 {
    function uri(uint256 id) external view returns (string memory);
}

1. uri(uint256 id) → string

返回tokenId的 URI id。

3.2.3 IERC1155 Receiver 元数据接口

该接口函数详情可参考:IERC1155Receiver

interface IERC1155Receiver is IERC165 {
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

1. onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes data) → bytes4

处理单个 ERC1155 tokenId类型的接收。safeTransferFrom 此函数在余额更新后在结束时调用。要接受传输,这必须返回 bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) (即 0xf23a6e61,或它自己的函数选择器)。

2. onERC1155BatchReceived(address operator, address from, uint256[] ids, uint256[] values, bytes data) → bytes4

处理多个 ERC1155 令牌类型的接收。safeBatchTransferFrom此函数在余额更新后在结束时调用。要接受传输,它必须返回 bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)")) (即 0xbc197c81,或它自己的函数选择器)。

3.3 ERC 1155 token 销毁

1. _mint(address account, uint256 id, uint256 amount, bytes data)

创建拥有amount的tokenId,并将它们分配给account。需要符合以下要求:

  • to地址不应该为 0。如果to指的是智能合约,它必须实现IERC1155Receiver.onERC1155Received,这被称为安全转移。
  • 该方法可触发TransferSingle事件。

2. _mintBatch(address to, uint256[] ids, uint256[] amounts, bytes data)

批处理版本的mint,需要符合以下要求:

  • ids并且amounts必须具有相同的长度。
  • to地址不应该为 0。如果to指的是智能合约,它必须实现IERC1155Receiver.onERC1155Received,这被称为安全转移。
  • 该方法可触发TransferSingle事件。

3. _burn(address account, uint256 id, uint256 amount)

销毁拥有amount的tokenId在account中的存在。需要符合以下要求:

  • account不能是零地址。
  • account必须至少有amount的tokenId。
  • 该方法可触发TransferSingle事件。

4. _burnBatch(address account, uint256[] ids, uint256[] amounts)

批处理版本的burn,需要符合以下要求:

  • ids并且amounts必须具有相同的长度。
  • 该方法可触发TransferSingle事件。

3.4 ERC-1155 的批量操作功能

  • 批量传输:通过一次合约调用传输多种资产。
  • 批量余额:在一次调用中获取多个资产的余额。
  • 批量审批:审批同一地址的所有代币。
  • Hook:接收代币的钩子函数。
  • 支持非同质化代币:如果供应量仅为 1,将其作为非同质化代币处理。
  • 安全转账规则:安全转账规则集。

3.4.1 批量传输

批量传输与常规 ERC-20 传输非常相似。 让我们看看常规的 ERC-20 与ERC-1155有什么区别:

// ERC-20
function transferFrom(address from, address to, uint256 value) external returns (bool);

// ERC-1155
function safeBatchTransferFrom(
    address _from,
    address _to,
    uint256[] calldata _ids,
    uint256[] calldata _values,
    bytes calldata _data
) external;

ERC-1155 中唯一的区别是我们将数值作为数组传递,我们也传递了数组 id。 例如,给出 ids=[3, 6, 13] 和 values=[100, 200, 5],传输结果将是

  1. 将 id 3 的 100 个代币从 _from 传输到 _to。
  2. 将 id 6 的 200 个代币从 _from 传输到 _to。
  3. 将 id 13 的 5 个代币从 _from 转移到 _to。

在 ERC-1155 中,我们只有 transferFrom,没有 transfer。 要像常规的 transfer一样使用它,只需将 "from" 地址设为调用该函数的地址。

例如以下操作:

我们可以将物品转移到玩家账户:

> NFTV2.safeTransferFrom(deployerAddress, playerAddress, 2, 1, "0x0")
> NFTV2.balanceOf(playerAddress, 2)
1
> NFTV2.balanceOf(deployerAddress, 2)
0

我们也可以批量转账到玩家账户,获取批量余额:

> NFTV2.safeBatchTransferFrom(deployerAddress, playerAddress, [0,1,3,4], [50,100,1,1], "0x0")
> NFTV2.balanceOfBatch([playerAddress,playerAddress,playerAddress,playerAddress,playerAddress], [0,1,2,3,4])
[50,100,1,1,1]

3.4.2 批量余额

相应的 ERC-20 balanceOf 调用同样具有支持批处理的相应函数。 同样,使用ERC-20 与 ERC-1155 做一个对比:

// ERC-20
function balanceOf(address owner) external view returns (uint256);

// ERC-1155
function balanceOfBatch(
    address[] calldata _owners,
    uint256[] calldata _ids
) external view returns (uint256[] memory);

调用余额查询更简单的是,我们可以在单次调用中获取多个余额。 参数中传递所有者账户数组和代币的 id 数组。

例如,对于给出的 _ids=[3, 6, 13] 和 _owners=[0xbeef..., 0x1337..., 0x1111...],返回值将为:

[
    balanceOf(0xbeef...),
    balanceOf(0x1337...),
    balanceOf(0x1111...)
]

当然,我们也可以查询单个地址及token的余额

> NFTV2.balanceOf(deployerAddress,3)
1000000000

3.4.3 批量审批

// ERC-1155
function setApprovalForAll(
    address _operator,
    bool _approved
) external;

function isApprovedForAll(
    address _owner,
    address _operator
) external view returns (bool);

审批过程与 ERC-20 略有不同。 这里不是批准特定金额,而是通过 setApprovalForall 函数设置操作帐户为已批准或未批准。

查看当前的审批状态可以通过 isApprovedForall 完成。 如你所见,要么全部批准,要么不批准。 不能定义要批准代币的数量,甚至代币类型。

这是考虑到简洁性而故意设计的。 你只能批准一个地址的所有代币。

四、其它说明

4.1 ERC-721与ERC-1155之间的区别

在不久的将来,ERC-1155代币标准将比ERC-721代币标准有更显著的应用,这要归功于它的附加功能。两者都允许用户创建新的NFT,但有一些关键的区别:

  • ERC-1155允许创建半可替代代币和不可替代代币,而ERC-721只允许后者。
  • 在ERC-1155中,智能合约链接到多个URI,并且不存储额外的元数据(如文件名)。相比之下,ERC-721仅支持为每个代币ID直接存储在智能合约上的静态元数据,这增加了部署成本并限制了灵活性。
  • ERC-1155的智能合约支持无限数量的代币,而ERC-721需要为每种类型的代币提供一个新的智能合约。
  • ERC-1155还允许代币的批量转移,这可以减少交易成本和时间。对于ERC-721,如果用户想要发送多个代币,它们会分别发生。

4.2 ERC20、ERC721或ERC1155的具体使用案例是什么?

即使是所有用户,今天都可以通过不同的标准使每一个新的ERC标准成为一个简单的过程,我们必须与现有的代币标准在相关的,以及特定的特定的相关需求。以下是ERC代币的具体使用案例。

1. ERC-20的使用案例

ERC-20替代了可资产的价值,对于组织众筹活动、发行ICO和在市场上推出更多的加密货币是非常有用的。

2. ERC-721的使用案例

ERC-721代表对数字资产的成果,主要平台游戏行业的NFT,NFT市场和Metaverse。

3. ERC-1155的使用案例

多代币标准-ERC1155,用于网络游戏。游戏经常有可替代的元素,如/能量,NFT游戏角色,和可如生命的替代品-现在来交换价值。

4.3 为什么合约中都需要实现ERC-165合约

ERC165解决的问题是,以太坊没有内置的合约机制来知道给定的合约是否是具有某些功能的预期合约。 ERC165的解决方案是为合约定义一个标准,以发布其支持的接口,以便其他合约可以遵循相同的标准来检测其是否支持某些接口,并且仅在支持该接口时才调用该接口的功能。

https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md

...全文
176 回复 打赏 收藏 举报
写回复
回复
切换为时间正序
请发表友善的回复…
发表回复
发帖
Ethereum中文社区
加入

116

社区成员

这里是由区块链、以太坊、Web3.0、元宇宙等相关技术组成的国内最大的开发者聚集地,帮助社区成员快速获得更全面、更深度的技术信息,链接更多资源,让开发者更好地入门学习、成长与应用实践。
社区管理员
  • ETHPlanet
  • 活动助手
帖子事件
编辑了帖子
2022-06-21 11:40
编辑了帖子 (查看)
2022-06-18 13:07
创建了帖子 (查看)
2022-06-18 13:02
社区公告

Hi,欢迎加入我们!

 

在这里你可以:

  • 获取最官方最新最全的区块链、以太坊、Web3.0学习内容与资源
  • 获取行业内更有价值的研讨会、公开课资源,或参与社区发起的主题活动课程
  • 获得专业的以太坊、区块链、Web3.0相关技术资深专家/讲师的回复或指导,突破学习瓶颈
  • 进行学习打卡、提问或回答问题,提高个人或在社区的影响力,将有机会与各大技术官方深度合作
  • 参与丰富的社区活动,获得更多学习资源、行业资源等
  • 结识更多行业伙伴,参与线上/线下课程、交流会,拓展行业交流圈

 

【最新活动】:

1、6月1日21:00-21:30,DappLearning Infura AMA(线上)点击查看详情