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-721 或 ERC-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-721 和 ERC-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.