Alert Source Discuss
⚠️ Draft Standards Track: ERC

ERC-5700: 可绑定 Token 接口

用于将同质化和非同质化 token 绑定到资产的接口。

Authors Leeren (@leeren)
Created 2022-09-22
Discussion Link https://ethereum-magicians.org/t/eip-5700-bindable-token-standard/11077
Requires EIP-165, EIP-721, EIP-1155

摘要

本标准定义了 ERC-721ERC-1155 token(称为“bindables”)“绑定”到 ERC-721 NFT 的接口。

当可绑定 token “绑定”到 NFT 时,即使它们的所有权已转移到 NFT,NFT 所有者也可以“解绑”这些 token 并声明其所有权。这使得可绑定 token 可以与其绑定的 NFT 一起转移,而无需额外的成本,从而提供了一种更有效的方式来创建和转移 N:1 的 token 到 NFT 的捆绑包。在 NFT 所有者决定解绑它们之前,绑定的 token 保持锁定状态,并在解绑后恢复其基本 token 功能。

本标准支持各种用例,例如:

  • NFT 捆绑的实物资产,如带有微芯片的街头服饰、数字化汽车收藏和数字化双胞胎房地产。
  • NFT 捆绑的数字资产,如可定制的虚拟衣柜、可组合的音乐曲目和可定制的 metaverse 土地。

动机

NFT 绑定的标准接口提供了一种无缝且高效的方式来捆绑和转移带有 NFT 的 token,确保与钱包、市场和其他 NFT 应用程序的兼容性。它消除了对 token 所有权的僵化、特定于实现的策略的需求。

与其他处理账户级别的 token 所有权的标准相比,本标准旨在解决 NFT 级别的 token 所有权问题。其目标是为 token 捆绑构建一个通用接口,该接口与现有的 ERC-721ERC-1155 标准兼容。

规范

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

ERC-721 可绑定

实现 ERC-721 可绑定标准的智能合约必须实现 IERC721Bindable 接口。

如果将 0x82a34a7d 作为标识符传递给 supportsInterface 函数,则 IER721Bindable 接口的实现者必须返回 true

/// @title ERC-721 可绑定 Token 标准
/// @dev 参见 https://eips.ethereum.org/ERCS/eip-5700
///  注意:此接口的 ERC-165 标识符为 0x82a34a7d。
interface IERC721Bindable /* is IERC721 */ {

    /// @notice 当未绑定 token 绑定到 NFT 时,会发出此事件。
    /// @param operator 批准执行绑定的地址。
    /// @param from 未绑定 token 所有者的地址。
    /// @param bindAddress 要绑定到的 NFT 的合约地址。
    /// @param bindId 要绑定到的 NFT 的标识符。
    /// @param tokenId 绑定 token 的标识符。
    event Bind(
        address indexed operator,
        address indexed from,
        address indexed bindAddress,
        uint256 bindId,
        uint256 tokenId
    );

    /// @notice 当 NFT 绑定的 token 被解绑时,会发出此事件。
    /// @param operator 批准执行解绑的地址。
    /// @param from token 绑定到的 NFT 的所有者。
    /// @param to 新的未绑定 token 所有者的地址。
    /// @param bindAddress 要从中解绑的 NFT 的合约地址。
    /// @param bindId 要从中解绑的 NFT 的标识符。
    /// @param tokenId 解绑 token 的标识符。
    event Unbind(
        address indexed operator,
        address indexed from,
        address to,
        address indexed bindAddress,
        uint256 bindId,
        uint256 tokenId
    );

    /// @notice 将 token `tokenId` 绑定到地址 `bindAddress` 处的 NFT `bindId`。
    /// @dev 除非 `msg.sender` 是当前所有者、授权的操作员或批准的 token 地址,否则该函数必须抛出。
    ///  如果 token 已经绑定,或者 `from` 不是 token 所有者,它也必须抛出。最后,如果 NFT 合约不支持
    ///  ERC-721 接口或要绑定到的 NFT 不存在,则必须抛出。在
    ///  绑定之前,token 所有权必须转移到
    ///  NFT 的合约地址。在绑定完成时,函数必须发出 `Transfer` 和 `Bind`
    ///  事件,以反映隐式 token 转移和后续绑定。
    /// @param from 未绑定 token 所有者的地址。
    /// @param bindAddress 要绑定到的 NFT 的合约地址。
    /// @param bindId 要绑定到的 NFT 的标识符。
    /// @param tokenId 绑定 token 的标识符。
    function bind(
        address from,
        address bindAddress,
        uint256 bindId,
        uint256 tokenId
    ) external;

    /// @notice 从地址 `bindAddress` 处的 NFT `bindId` 解绑 token `tokenId`。
    /// @dev 除非 `msg.sender` 是当前所有者、授权的操作员或已批准的 token 绑定到的 NFT 地址,否则该函数必须抛出。
    ///  如果 token 未绑定、如果 `from` 不是绑定 NFT 的所有者,或者如果 `to` 是零地址,则该函数也必须抛出。之后
    ///  解绑,token 所有权必须转移到 `to`,在此期间
    ///  该函数必须检查 `to` 是否是有效的合约(代码大小 > 0),
    ///  如果是,则调用 `onERC721Received`,如果返回错误的标识符则抛出。在解绑完成时,该函数必须发出 `Unbind` 和
    ///  `Transfer` 事件,以反映解绑和后续转移。
    /// @param from token 绑定到的 NFT 的所有者的地址。
    /// @param to 未绑定 token 新所有者的地址。
    /// @param bindAddress 要从中解绑的 NFT 的合约地址。
    /// @param bindId 要从中解绑的 NFT 的标识符。
    /// @param tokenId 解绑 token 的标识符。
    function unbind(
        address from,
        address to,
        address bindAddress,
        uint256 bindId,
        uint256 tokenId
    ) external;

    /// @notice 获取 token `tokenId` 绑定到的 NFT 地址和标识符。
    /// @dev 当 token 未绑定时,此函数必须返回零
    ///  地址的地址部分,以指示不存在绑定。
    /// @param tokenId 要查询的 token 的标识符。
    /// @return token 绑定的 NFT 合约地址和数字标识符。
    function binderOf(uint256 tokenId) external view returns (address, uint256);

    /// @notice 获取绑定到地址 `bindAddress` 处的 NFT `bindId` 的 token 总数。
    /// @param bindAddress 要查询的 NFT 的合约地址。
    /// @param bindId 要查询的 NFT 的标识符。
    /// @return 绑定到查询的 NFT 的 token 总数。
    function boundBalanceOf(address bindAddress, uint256 bindId) external view returns (uint256);

ERC-1155 可绑定

实现 ERC-1155 可绑定标准的智能合约必须实现 IERC1155Bindable 接口。

如果将 0xd0d55c6 作为标识符传递给 supportsInterface 函数,则 IER1155Bindable 接口的实现者必须返回 true

/// @title ERC-1155 可绑定 Token 标准
/// @dev 参见 https://eips.ethereum.org/ERCS/eip-5700
///  注意:此接口的 ERC-165 标识符为 0xd0d555c6。
interface IERC1155Bindable /* is IERC1155 */ {

    /// @notice 当 token 被绑定到 NFT 时,会发出此事件。
    /// @param operator 批准执行绑定的地址。
    /// @param from 未绑定 token 的所有者地址。
    /// @param bindAddress 要绑定到的 NFT 的合约地址。
    /// @param bindId 要绑定到的 NFT 的标识符。
    /// @param tokenId 绑定 token 类型的标识符。
    /// @param amount 绑定到 NFT 的 token 数量。
    event Bind(
        address indexed operator,
        address indexed from,
        address indexed bindAddress,
        uint256 bindId,
        uint256 tokenId,
        uint256 amount
    );

    /// @notice 当不同类型的 token 被绑定到 NFT 时,会发出此事件。
    /// @param operator 批准执行批量绑定的地址。
    /// @param from 未绑定 token 的所有者地址。
    /// @param bindAddress 要绑定到的 NFT 的 contracts 地址。
    /// @param bindId 要绑定到的 NFT 的标识符。
    /// @param tokenIds 绑定 token 类型的标识符。
    /// @param amounts 每个绑定到 NFT 的 token 类型的数量。
    event BindBatch(
        address indexed operator,
        address indexed from,
        address indexed bindAddress,
        uint256 bindId,
        uint256[] tokenIds,
        uint256[] amounts
    );

    /// @notice 当 token 从 NFT 解绑时,会发出此事件。
    /// @param operator 批准执行解绑的地址。
    /// @param from token 绑定到的 NFT 的所有者地址。
    /// @param to 未绑定 token 新所有者的地址。
    /// @param bindAddress 要从中解绑的 NFT 的合约地址。
    /// @param bindId 要从中解绑的 NFT 的标识符。
    /// @param tokenId 解绑 token 类型的标识符。
    /// @param amount 从 NFT 解绑的 token 数量。
    event Unbind(
        address indexed operator,
        address indexed from,
        address to,
        address indexed bindAddress,
        uint256 bindId,
        uint256 tokenId,
        uint256 amount
    );

    /// @notice 当不同类型的 token 从 NFT 解绑时,会发出此事件。
    /// @param operator 批准执行批量绑定的地址。
    /// @param from 未绑定 token 的所有者地址。
    /// @param to 未绑定 token 新所有者的地址。
    /// @param bindAddress 要从中解绑的 NFT 的 contracts 地址。
    /// @param bindId 要从中解绑的 NFT 的标识符。
    /// @param tokenIds 解绑 token 类型的标识符。
    /// @param amounts 每个从 NFT 解绑的 token 类型的数量。
    event UnbindBatch(
        address indexed operator,
        address indexed from,
        address to,
        address indexed bindAddress,
        uint256 bindId,
        uint256[] tokenIds,
        uint256[] amounts
    );

    /// @notice 将 `tokenId` 的 `amount` 个 token 绑定到地址 `bindAddress` 处的 NFT `bindId`。
    /// @dev 除非 `msg.sender` 是 `from` 的已批准操作员,否则该函数必须抛出。
    ///  如果 `from` 拥有的 token 少于 `amount` 个,它也必须抛出。最后,如果 NFT 合约不支持
    ///  ERC-721 接口或要绑定到的 NFT 不存在,则必须抛出。在
    ///  绑定之前,token 必须转移到
    ///  NFT 的合约地址。在绑定完成时,该函数必须发出 `Transfer` 和 `Bind` 事件
    ///  以反映隐式 token 转移和后续绑定。
    /// @param from 未绑定 token 的所有者地址。
    /// @param bindAddress 要绑定到的 NFT 的合约地址。
    /// @param bindId 要绑定到的 NFT 的标识符。
    /// @param tokenId 绑定 token 类型的标识符。
    /// @param amount 绑定到 NFT 的 token 数量。
    function bind(
        address from,
        address bindAddress,
        uint256 bindId,
        uint256 tokenId,
        uint256 amount
    ) external;

    /// @notice 将 `tokenIds` 的 `amounts`  token 绑定到地址 `bindAddress` 处的 NFT `bindId`。
    /// @dev 除非 `msg.sender` 是 `from` 的已批准操作员,否则该函数必须抛出。
    ///  如果 `amounts` 的长度与 `tokenIds` 不同,或者如果 `from` 的任何 `tokenIds` 余额小于
    ///  `amounts` 的余额,它也必须抛出。最后,如果 NFT 合约不支持
    ///  ERC-721 接口或绑定的 NFT 不存在,则必须抛出。在
    ///  绑定之前,token 必须转移到
    ///  NFT 的合约地址。在绑定完成时,该函数必须发出 `TransferBatch` 和
    ///  `BindBatch` 事件,以反映批量 token 转移和绑定。
    /// @param from 未绑定 token 的所有者地址。
    /// @param bindAddress 要绑定到的 NFT 的合约地址。
    /// @param bindId 要绑定到的 NFT 的标识符。
    /// @param tokenIds 绑定 token 类型的标识符。
    /// @param amounts 每个绑定到 NFT 的 token 类型的数量。
    function batchBind(
        address from,
        address bindAddress,
        uint256 bindId,
        uint256[] calldata tokenIds,
        uint256[] calldata amounts
    ) external;

    /// @notice 从地址 `bindAddress` 处的 NFT `bindId` 解绑 `tokenId` 的 `amount` 个 token。
    /// @dev 除非 `msg.sender` 是 `from` 的已批准操作员,否则该函数必须抛出。
    ///  如果 `from` 不是绑定 NFT 的所有者,如果 NFT 的 token 余额少于 `amount`,或者如果 `to` 是
    ///  零地址,则该函数也必须抛出。在解绑之后,token 必须转移到 `to`,
    ///  在此期间,该函数必须检查 `to` 是否是有效的合约(代码
    ///  size > 0),如果是,则调用 `onERC1155Received`,如果返回错误的 \
    ///  标识符,则抛出。在解绑完成时,该函数必须发出 `Unbind` 和 `Transfer` 事件
    ///  以反映解绑和转移。
    /// @param from token 绑定到的 NFT 的所有者地址。
    /// @param to 未绑定 token 新所有者的地址。
    /// @param bindAddress 要从中解绑的 NFT 的合约地址。
    /// @param bindId 要从中解绑的 NFT 的标识符。
    /// @param tokenId 解绑 token 类型的标识符。
    /// @param amount 从 NFT 解绑的 token 数量。
    function unbind(
        address from,
        address to,
        address bindAddress,
        uint256 bindId,
        uint256 tokenId,
        uint256 amount
    ) external;

    /// @notice 从地址 `bindAddress` 处的 NFT `bindId` 解绑 `tokenId` 的 `amount` 个 token。
    /// @dev 除非 `msg.sender` 是 `from` 的已批准操作员,否则该函数必须抛出。
    ///  如果 `amounts` 的长度与 `tokenIds` 不同,如果 NFT 的任何 `tokenIds` 余额少于
    ///  `amounts` 的余额,或者如果 `to` 是零地址,则该函数也必须抛出。在解绑之后,token 必须转移到 `to`,
    ///  在此期间,该函数必须检查 `to` 是否是有效的合约(代码大小 > 0),如果是,则
    ///  调用 `onERC1155BatchReceived`,如果返回错误的
    ///  标识符,则抛出。在解绑完成时,该函数必须发出 `UnbindBatch` 和
    ///  `TransferBatch` 事件,以反映批量解绑和转移。
    /// @param from 未绑定 token 的所有者地址。
    /// @param to 未绑定 token 新所有者的地址。
    /// @param bindAddress 要从中解绑的 NFT 的合约地址。
    /// @param bindId 要从中解绑的 NFT 的标识符。
    /// @param tokenIds 解绑 token 类型的标识符。
    /// @param amounts 每个从 NFT 解绑的 token 类型的数量。
    function batchUnbind(
        address from,
        address to,
        address bindAddress,
        uint256 bindId,
        uint256[] calldata tokenIds,
        uint256[] calldata amounts
    ) external;

    /// @notice 获取地址 `bindAddress` 处的 NFT `bindId` 绑定的 `tokenId` 类型的 token 数量。
    /// @param bindAddress 绑定的 NFT 的合约地址。
    /// @param bindId 绑定的 NFT 的标识符。
    /// @param tokenId 绑定到 NFT 的 token 类型的标识符。
    /// @return 绑定到 NFT 的 `tokenId` 类型的 token 数量。
    function boundBalanceOf(
        address bindAddress,
        uint256 bindId,
        uint256 tokenId
    ) external view returns (uint256);

    /// @notice 获取地址 `bindAddress` 处的 NFT `bindIds` 绑定的 `bindIds` 类型的 token 数量。
    /// @param bindAddress 绑定的 NFT 的合约地址。
    /// @param bindIds 绑定的 NFT 的标识符。
    /// @param tokenIds 绑定到 NFT 的 token 类型的标识符。
    /// @return balances 每个 token 类型/NFT 对的绑定余额。
    function boundBalanceOfBatch(
        address bindAddress,
        uint256[] calldata bindIds,
        uint256[] calldata tokenIds
    ) external view returns (uint256[] memory balances);

}

理由

token 绑定的标准为允许钱包、应用程序和协议与捆绑的 NFT 进行交互、交易和显示解锁了新的可组合性层。一个示例用例是在 Dopamine,其中街头服装可以与数字资产(如音乐、头像或服装的数字双胞胎)捆绑在一起,方法是将这些资产表示为可绑定 token 并将它们绑定到表示为 NFT 的微芯片。

绑定机制

在绑定期间,可绑定 token 的技术所有权被授予其绑定的 NFT,同时允许 NFT 所有者随时解绑。这种轻量级设计的警告是,尚未采用此标准的应用程序不会将捆绑的 token 显示为 NFT 所有者拥有。

向后兼容性

可绑定 token 接口旨在与现有的 ERC-721 和 ERC-1155 标准兼容。

参考实现

安全考虑

在绑定期间,由于所有权被授予绑定的 NFT 合约,因此实施应注意确保解绑只能由指定的 NFT 所有者执行。

版权

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

Citation

Please cite this document as:

Leeren (@leeren), "ERC-5700: 可绑定 Token 接口 [DRAFT]," Ethereum Improvement Proposals, no. 5700, September 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5700.