Alert Source Discuss
Standards Track: ERC

ERC-2135: 可消费接口 (票据等)

扩展 ERC-721 和 ERC-1155 的可消费性接口,支持诸如活动门票之类的用例。

Authors Zainan Victor Zhou (@xinbenlv)
Created 2019-06-23
Requires EIP-165, EIP-721, EIP-1155

摘要

本 EIP 定义了一个接口,用于将数字资产标记为“可消费的”并对其“消费”做出反应。

动机

数字资产有时需要被消费。最常见的例子之一是音乐会门票。 当持票人进入音乐厅时,它就被“消费”了。

拥有一个标准接口可以为基于此用例的服务、客户端、UI 和合约间功能实现互操作性。

规范

本文档中的关键词“必须”,“禁止”,“需要”,“应该”,“不应该”,“推荐”,“可以”和“可选”应按照 RFC 2119 中的描述进行解释。

  1. 任何符合规范的合约必须实现以下接口:
pragma solidity >=0.7.0 <0.9.0;

/// 此接口的 ERC-165 标识符为 0xdd691946
interface IERC2135 {
    /// @notice `consume` 函数每次成功都会消费一个 token。
    /// @param _consumer 此 token 的消费者的地址。它不必是启动 TX 的
    ///                  EOA 或合约账户。
    /// @param _assetId  要消费的 NFT 资产
    /// @param _amount   要消费的数量
    /// @param _data     传递给消费的额外数据,用于额外消息
    ///                  或未来扩展。
    function consume(
        address _consumer,
        uint256 _assetId,
        uint256 _amount,
        bytes calldata _data
    ) external returns (bool _success);

    /// @notice 用于检查资产是否可消费的接口。
    /// @param _consumer 此 token 的消费者的地址。它不必是启动 TX 的
    ///                  EOA 或合约账户。
    /// @param _assetId  要消费的 NFT 资产。
    /// @param _amount   要消费的资产数量。
    function isConsumableBy(
        address _consumer,
        uint256 _assetId,
        uint256 _amount
    ) external view returns (bool _consumable);

    /// @notice 成功消费时发出的事件。
    /// @param consumer 此 token 的消费者的地址。它不必是启动 TX 的
    ///                  EOA 或合约账户。
    /// @param assetId  要消费的 NFT 资产
    /// @param amount   要消费的资产数量。
    /// @param data     传递给消费的额外数据,用于额外消息
    ///                  或未来扩展。
    event OnConsumption(
        address indexed consumer,
        uint256 indexed assetId,
        uint256 amount,
        bytes data
    );
}
  1. 如果符合规范的合约是 ERC-721ERC-1155 token,除了 OnConsumption 之外,如果对 consume 方法的调用成功,它必须还发出 Transfer / TransferSingle 事件(如果适用),就像 token 已从当前持有者转移到零地址一样。

  2. 根据 ERC-165,任何符合规范的合约 必须 返回 supportsInterface(0xdd691946)true

理由

  1. 函数 consume 执行消费操作。本 EIP 不做以下假设:
  • 谁有权执行消费
  • 在什么条件下可以发生消费

但是,它确实假设资产可以在参数中以 uint256 资产 ID 标识。实施了一项设计约定和兼容性考虑,以遵循 ERC-721 模式。

  1. 该事件通知有兴趣了解资产正在被消费的订阅者。

  2. 为了保持简单,本标准有意不包含与可消费资产创建相关的函数或事件。这是因为创建可消费资产需要对实际用例的性质做出假设。如果存在常见的创建用例,则可以创建另一个后续标准。

  3. 与耗材相关的元数据不包含在该标准中。如有必要,可以使用单独的元数据扩展接口(如来自 ERC-721ERC721Metadata)创建相关的元数据。

  4. 我们选择为 consume 函数和 isConsumableBy 包含一个 address consumer,以便可以为交易发起者以外的其他人消费 NFT。

  5. 我们选择包含一个额外的 _data 字段,以供未来扩展,例如 添加加密签名。

  6. 我们明确地对是否应要求 ERC-721 或 ERC-1155 保持无意见,因为 虽然我们在设计此 EIP 时主要考虑了 ERC-721 和 ERC-1155,但我们不想排除 未来有人使用不同的 token 标准或在不同的用例中使用它的潜在情况。

  7. isConsumableBy 的布尔视图函数可用于检查资产是否可被 _consumer 消费。

向后兼容性

此接口旨在与 ERC-721 和 ERC-1155 的 NFT 兼容。可以对其进行调整以用于 ERC-20ERC-777 和 ERC-1155 的 Fungible Token。

测试用例


  describe("Consumption", function () {
    it("Should consume when minted", async function () {
      const fakeTokenId = "0x1234";
      const { contract, addr1 } = await loadFixture(deployFixture);
      await contract.safeMint(addr1.address, fakeTokenId);
      expect(await contract.balanceOf(addr1.address)).to.equal(1);
      expect(await contract.ownerOf(fakeTokenId)).to.equal(addr1.address);
      expect(await contract.isConsumableBy(addr1.address, fakeTokenId, 1)).to.be.true;
      const tx = await contract.consume(addr1.address, fakeTokenId, 1, []);
      const receipt = await tx.wait();
      const events = receipt.events.filter((x: any) => { return x.event == "OnConsumption" });
      expect(events.length).to.equal(1);
      expect(events[0].args.consumer).to.equal(addr1.address);
      expect(events[0].args.assetId).to.equal(fakeTokenId);
      expect(events[0].args.amount).to.equal(1);
      expect(await contract.balanceOf(addr1.address)).to.equal(0);
      await expect(contract.ownerOf(fakeTokenId))
        .to.be.rejectedWith('ERC721: invalid token ID');
      await expect(contract.isConsumableBy(addr1.address, fakeTokenId, 1))
        .to.be.rejectedWith('ERC721: invalid token ID');
    });
  });

  describe("EIP-165 Identifier", function () {
    it("Should match", async function () {
      const { contract } = await loadFixture(deployFixture);
      expect(await contract.get165()).to.equal("0xdd691946");
      expect(await contract.supportsInterface("0xdd691946")).to.be.true;
    });
  });

参考实现

版本 0x1002 的部署已部署到 goerli 测试网,地址为 0x3682bcD67b8A5c0257Ab163a226fBe07BF46379B

在 Etherscan 的 goerli 站点上找到上述地址的参考合约验证源代码。

安全注意事项

符合规范的合约应注意在消费 token 时余额的变化。 当合约暂停或用户被限制转移 token 时, 可消费性应与转移限制保持一致。

符合规范的合约还应仔细定义访问控制,特别是任何 EOA 或合约帐户是否可以在其自己的用例中启动 consume 方法。

应使用安全审计和测试来验证对 consume 函数的访问控制是否按预期运行。

版权

版权及相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Zainan Victor Zhou (@xinbenlv), "ERC-2135: 可消费接口 (票据等)," Ethereum Improvement Proposals, no. 2135, June 2019. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2135.