ERC-4675: 多重分数化非同质化代币
使用单个合约分数化多个 NFT
Authors | David Kim (@powerstream3604) |
---|---|
Created | 2022-01-13 |
Discussion Link | https://ethereum-magicians.org/t/eip-4675-multi-fractional-non-fungible-token-standard/8008 |
Requires | EIP-165, EIP-721 |
摘要
本标准概述了一个智能合约接口,该接口有资格表示任意数量的分数化非同质化代币。使用诸如 EIP-1633 等标准的现有项目通常部署单独的 EIP-20 兼容代币合约,以将非同质化代币分数化为 EIP-20 代币。相比之下,此 ERC 允许每个代币 ID 表示一种代币类型,该类型表示(分数化)非同质化代币。
本标准在使用 _id
来区分代币类型方面是近似的。但是,此 ERC 与 EIP-1155 有明显的区别,因为每个 _id
代表一个不同的 NFT。
动机
将 NFT 分数化为 FT 的传统分数化过程需要部署一个 FT 代币合约来表示 NFT 的所有权。这导致以太坊区块链上的字节码使用效率低下,并限制了功能,因为每个代币合约都分开到其自己的许可地址中。 随着多个 NFT 项目需要将 NFT 分数化为 FT 的兴起,需要一种新型的代币标准来支持它们。
规范
/**
@title Multi-Fractional Non-Fungible Token Standard
@dev Note : The ERC-165 identifier for this interface is 0x83f5d35f.
*/
interface IMFNFT {
/**
@dev 当任何代币的所有权通过任何机制更改时,都会发出此事件。
`_from` 参数必须是发送代币的帐户/合约的地址。
`_to` 参数必须是接收代币的帐户/合约的地址。
`_id` 参数必须是正在转移的代币类型。(代表 NFT)
`_value` 参数必须是持有者余额减少的代币数量,并且与接收者余额增加的数量相匹配。
*/
event Transfer(address indexed _from, address indexed _to, uint256 indexed _id, uint256 _value);
/**
@dev 当代币的批准地址被更改或重申时,会发出此事件。
`_owner` 参数必须是批准提款的帐户/合约的地址。
`_spender` 参数必须是被批准从 `_owner` 余额中提款的帐户/合约的地址。
`_id` 参数必须是正在转移的代币类型。(代表 NFT)
`_value` 参数必须是 `_approved` 能够从 `_owner` 余额中提款的代币数量。
*/
event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _value);
/**
@dev 当添加新的代币类型时,该类型代表非同质化代币的份额,会发出此事件。
`_parentToken` 参数必须是非同质化代币合约的地址。
`_parentTokenId` 参数必须是非同质化代币的代币 ID。
`_id` 参数必须是正在添加的代币类型。(代表 NFT)
`_totalSupply` 参数必须是代币类型的总代币供应量。
*/
event TokenAddition(address indexed _parentToken, uint256 indexed _parentTokenId, uint256 _id, uint256 _totalSupply);
/**
@notice 将 `_value` 数量的 `_id` 从 msg.sender 地址转移到指定的 `_to` 地址
@dev msg.sender 必须具有足够的余额来处理从帐户中转移出去的代币。
如果 `_to` 是零地址,则必须恢复。
如果 msg.sender 的 `_id` 代币余额低于正在转移的 `_value`,则必须恢复。
必须在任何其他错误时恢复。
必须发出 `Transfer` 事件以反映余额更改。
@param _to 源地址
@param _id 代币类型的 ID
@param _value 转移数量
@return 如果转移成功,则为 True,否则为 False
*/
function transfer(address _to, uint256 _id, uint256 _value) external returns (bool);
/**
@notice 批准将 `_value` 数量的 `_id` 从 msg.sender 转移到指定的 `_spender` 地址。
@dev 当 `_spender` 想要代表转移代币时,msg.sender 必须具有足够的余额来处理代币。
如果 `_spender` 是零地址,则必须恢复。
必须在任何其他错误时恢复。
必须发出 `Approval` 事件。
@param _spender Spender 地址(可以代表 msg.sender 提取代币的帐户/合约)
@param _id 代币类型的 ID
@param _value 批准数量
@return 如果批准成功,则为 True,否则为 False
*/
function approve(address _spender, uint256 _id, uint256 _value) external returns (bool);
/**
@notice 将 `_value` 数量的 `_id` 从 `_from` 地址转移到指定的 `_to` 地址。
@dev 调用者必须被批准管理从 `_from` 帐户中转移出去的代币。
如果 `_to` 是零地址,则必须恢复。
如果持有者的 `_id` 代币余额低于发送的 `_value`,则必须恢复。
必须在任何其他错误时恢复。
必须发出 `Transfer` 事件以反映余额更改。
@param _from 源地址
@param _to 目标地址
@param _id 代币类型的 ID
@param _value 转移数量
@return 如果转移成功,则为 True,否则为 False
*/
function transferFrom(address _from, address _to, uint256 _id, uint256 _value) external returns (bool);
/**
@notice 将 NFT 设置为新的类型代币
@dev 合约本身应在使用 `_parentNFTContractAddress` 和 `_parentNFTTokenId` 添加代币之前,验证 NFT 的所有权是否属于该合约本身。
如果已经注册了相同的 NFT,则必须恢复。
如果 `_parentNFTContractAddress` 是地址零,则必须恢复。
如果 `_parentNFTContractAddress` 与 ERC-721 不兼容,则必须恢复。
如果此合约本身不是 NFT 的所有者,则必须恢复。
必须在任何其他错误时恢复。
必须发出 `TokenAddition` 事件以反映代币类型添加。
@param _parentNFTContractAddress NFT 合约地址
@param _parentNFTTokenId NFT 代币 ID
@param _totalSupply 总代币供应量
*/
function setParentNFT(address _parentNFTContractAddress, uint256 _parentNFTTokenId, uint256 _totalSupply) external;
/**
@notice 获取代币 ID 的总代币供应量。
@param _id 代币的 ID
@return 指定代币类型的总代币供应量
*/
function totalSupply(uint256 _id) external view returns (uint256);
/**
@notice 获取帐户的代币余额。
@param _owner 代币持有者的地址
@param _id 代币的 ID
@return 所请求的代币类型的 _owner 的余额
*/
function balanceOf(address _owner, uint256 _id) external view returns (uint256);
/**
@notice 获取 `_spender` 仍然可以从 `_owner` 提取的数量
@param _owner 代币持有者的地址
@param _spender 被批准代表 `_owner` 提取代币的地址
@param _id 代币的 ID
@return `_spender` 仍然可以从 `_owner` 提取的数量
*/
function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);
/**
@notice 获取一个布尔值,该值表示该 NFT 是否已由此合约注册和分数化。
@param _parentNFTContractAddress NFT 合约地址
@param _parentNFTTokenId NFT 代币 ID
@return 表示 NFT 是否已注册的布尔值。
*/
function isRegistered(address _parentNFTContractAddress, uint256 _parentNFTTokenId) external view returns (bool);
}
interface ERC165 {
/**
@notice 查询合约是否实现了接口
@param interfaceID 接口标识符,如 ERC-165 中所指定
@dev 接口标识在 ERC-165 中指定。此函数
使用的 gas 小于 30,000。
@return 如果合约实现了 `interfaceID` 且
`interfaceID` 不是 0xffffffff,则返回 `true`,否则返回 `false`
*/
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
要在 安全转移
上接收非同质化代币,合约应包含 onERC721Received()
。
需要包含 onERC721Received()
才能与安全转移规则兼容。
/**
@notice 处理 NFT 的接收
@param _operator 调用 `safeTransferFrom` 函数的地址
@param _from 先前拥有代币的地址
@param _tokenId 正在转移的 NFT 标识符
@param _data 没有指定格式的附加数据
@return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external pure returns (bytes4);
理由
元数据
未包含 symbol()
和 name()
函数,因为大多数用户可以直接从原始 NFT 合约中获取它。此外,每次添加代币时都复制名称和符号可能会在以太坊区块链上放置大量冗余字节码。
但是,根据项目的需求和设计,也可以通过从 NFT 合约中获取元数据将其添加到每种代币类型。
设计
围绕此 ERC 设计做出的大多数决策都是为了使其尽可能灵活地适应不同的代币设计和架构。 此标准的这些最低要求允许每个项目确定其自己的系统来铸造、管理和销毁其 MFNFT 代币,具体取决于其可编程架构。
向后兼容性
为了使此标准与现有标准兼容,此标准的 event
和 function
名称与 ERC-20 代币标准相同,但添加了更多 events
和 functions
以动态添加代币类型。
此外,在 functions
和 events
中使用 _id
来区分代币类型的参数顺序与 ERC-1155 多重代币标准非常相似。
由于此标准旨在与 EIP-721 非同质化代币标准交互,因此它有目的地与超出标准范围的扩展无关,以便允许特定项目设计自己的代币使用和场景。
测试用例
MFNFT 代币的参考实现包括使用 hardhat 编写的测试用例。(测试覆盖率:100%)
参考实现
安全考虑
要分数化已经铸造的 NFT,很明显,在分数化之前,应将 NFT 的所有权授予代幣合约。 在分数化 NFT 的情况下,代幣合约应彻底验证 NFT 的所有权,然后再将其分数化,以防止代币成为与 NFT 分开的单独代币。
如果任意帐户有权调用 setParentNFT()
,则可能存在抢先交易问题。setParentNFT()
的调用者可能与真正的 NFT 发送者不同。
为了防止此问题,实施者应只允许 admin 调用,或者以类似于闪电贷(swap)的原子交易分数化并接收 NFT。
版权
在 CC0 下放弃版权及相关权利。
Citation
Please cite this document as:
David Kim (@powerstream3604), "ERC-4675: 多重分数化非同质化代币 [DRAFT]," Ethereum Improvement Proposals, no. 4675, January 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-4675.