ERC-5507: 可退款代币
为 ERC-20、ERC-721 和 ERC-1155 代币添加退款功能
Authors | elie222 (@elie222), Gavin John (@Pandapip1) |
---|---|
Created | 2022-08-19 |
Requires | EIP-20, EIP-165, EIP-721, EIP-1155 |
Table of Contents
摘要
本 ERC 为 ERC-20、ERC-721 和 ERC-1155 的初始代币发行添加了退款功能。资金被托管,直到预定的时间后才能被领取。在该预定时间过去之前,用户可以收到他们购买的代币的退款。
动机
NFT 和代币领域缺乏责任感。为了整个生态系统的健康,需要更好的机制来防止 rugpull 发生。提供退款为买家提供了更大的保护,并增加了创作者的合法性。
这种特定用例的标准接口具有以下优势:
- 更符合欧盟的“远程销售法规”,该法规要求对在线购买的商品(如代币)提供 14 天的退款期
- 与各种 NFT 相关的应用程序的互操作性,例如投资组合浏览器和市场
- NFT 市场可以放置一个徽章,表明 NFT 仍然可以在列表上退款,并提供退款 NFT 而不是在市场上列出它们
- 如果这样做可以产生更高的收益,DExes 可以提供退款代币
- 更好的钱包确认对话框
- 钱包可以更好地告知用户正在采取的行动(代币被退款),类似于转账通常有其自己独特的对话框
- DAO 可以更好地显示包含退款代币的智能提案的功能
规范
本文档中的关键词“必须”,“禁止”,“需要”,“应该”,“不应该”,“推荐”,“可以”和“可选”应按照 RFC 2119 中的描述进行解释。
所有实现必须使用并遵循 ERC-165 的指示。
ERC-20 退款扩展
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.17;
import "ERC20.sol";
import "ERC165.sol";
/// @notice 可退款的 ERC-20 代币
/// @dev 该接口的 ERC-165 标识符为 `0xf0ca2917`
interface ERC20Refund is ERC20, ERC165 {
/// @notice 当一个代币被退款时发出
/// @dev 由 `refund` 发出
/// @param _from 资产被退款的帐户
/// @param _amount 被退款的代币数量(以不可分割的单位计算)
event Refund(
address indexed _from,
uint256 indexed _amount
);
/// @notice 当一个代币被退款时发出
/// @dev 由 `refundFrom` 发出
/// @param _sender 发送退款的帐户
/// @param _from 资产被退款的帐户
/// @param _amount 被退款的代币数量(以不可分割的单位计算)
event RefundFrom(
address indexed _sender,
address indexed _from,
uint256 indexed _amount
);
/// @notice 只要退款有效,就退款给用户
/// @dev 确保检查用户是否拥有代币,并注意潜在的重入向量
/// @param amount 要退款的 `amount`
function refund(uint256 amount) external;
/// @notice 只要退款有效并且发送者有足够的批准,就退款代币并将以太币发送给发送者
/// @dev 确保检查用户是否拥有代币,并注意潜在的重入向量
/// 以太币会发送给 msg.sender。
/// @param from 要从中退款资产的用户
/// @param amount 要退款的 `amount`
function refundFrom(address from, uint256 amount) external;
/// @notice 获取退款价格
/// @return _wei 对于一个代币单位(10**decimals 不可分割的单位)将退还的以太币数量(以 wei 为单位)
function refundOf() external view returns (uint256 _wei);
/// @notice 获取退款无效的第一个区块
/// @return block 代币无法退款的第一个区块
function refundDeadlineOf() external view returns (uint256 block);
}
ERC-721 退款扩展
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.17;
import "ERC721.sol";
import "ERC165.sol";
/// @notice 可退款的 ERC-721 代币
/// @dev 该接口的 ERC-165 标识符为 `0xe97f3c83`
interface ERC721Refund is ERC721 /* , ERC165 */ {
/// @notice 当一个代币被退款时发出
/// @dev 由 `refund` 发出
/// @param _from 资产被退款的帐户
/// @param _tokenId 被退款的 `tokenId`
event Refund(
address indexed _from,
uint256 indexed _tokenId
);
/// @notice 当一个代币被退款时发出
/// @dev 由 `refundFrom` 发出
/// @param _sender 发送退款的帐户
/// @param _from 资产被退款的帐户
/// @param _tokenId 被退款的 `tokenId`
event RefundFrom(
address indexed _sender,
address indexed _from,
uint256 indexed _tokenId
);
/// @notice 只要给定 `tokenId` 的退款有效,就退款给用户
/// @dev 确保检查用户是否拥有代币,并注意潜在的重入向量
/// @param tokenId 要退款的 `tokenId`
function refund(uint256 tokenId) external;
/// @notice 只要退款有效并且发送者有足够的批准,就退款代币并将以太币发送给发送者
/// @dev 确保检查用户是否拥有代币,并注意潜在的重入向量
/// 以太币会发送给 msg.sender。
/// @param from 要从中退款代币的用户
/// @param tokenId 要退款的 `tokenId`
function refundFrom(address from, uint256 tokenId) external;
/// @notice 获取特定 `tokenId` 的退款价格
/// @param tokenId 要查询的 `tokenId`
/// @return _wei 将退还的以太币数量(以 wei 为单位)
function refundOf(uint256 tokenId) external view returns (uint256 _wei);
/// @notice 获取退款无效的给定 `tokenId` 的第一个区块
/// @param tokenId 要查询的 `tokenId`
/// @return block 代币无法退款的第一个区块
function refundDeadlineOf(uint256 tokenId) external view returns (uint256 block);
}
可选的 ERC-721 批量退款扩展
// SPDX-License-Identifier: CC0-1.0;
import "ERC721Refund.sol";
/// @notice 批量可退款的 ERC-721 代币
/// @dev 该接口的 ERC-165 标识符为 ``
contract ERC721BatchRefund is ERC721Refund {
/// @notice 当一个或多个代币被批量退款时发出
/// @dev 由 `refundBatch` 发出
/// @param _from 资产被退款的帐户
/// @param _tokenId 被退款的 `tokenIds`
event RefundBatch(
address indexed _from,
uint256[] _tokenIds // 可能是索引的,也可能不是
);
/// @notice 当一个或多个代币被批量退款时发出
/// @dev 由 `refundFromBatch` 发出
/// @param _sender 发送退款的帐户
/// @param _from 资产被退款的帐户
/// @param _tokenId 被退款的 `tokenId`
event RefundFromBatch(
address indexed _sender,
address indexed _from,
uint256 indexed _tokenId
);
/// @notice 只要给定 `tokenIds` 的退款有效,就退款给用户
/// @dev 确保检查用户是否拥有代币,并注意潜在的重入向量
/// 这些必须一起成功或失败;没有部分退款。
/// @param tokenIds 要退款的 `tokenId`s
function refundBatch(uint256[] tokenIds) external;
/// @notice 只要给定 `tokenIds` 的退款有效并且发送者有足够的批准,就退款代币并将以太币发送给发送者
/// @dev 确保检查用户是否拥有代币,并注意潜在的重入向量
/// 以太币会发送给 msg.sender。
/// 这些必须一起成功或失败;没有部分退款。
/// @param from 要从中退款代币的用户
/// @param tokenIds 要退款的 `tokenId`s
function refundFromBatch(address from, uint256[] tokenIds) external;
}
ERC-1155 退款扩展
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.17;
import "ERC1155.sol";
import "ERC165.sol";
/// @notice 可退款的 ERC-1155 代币
/// @dev 该接口的 ERC-165 标识符为 `0x94029f5c`
interface ERC1155Refund is ERC1155 /* , ERC165 */ {
/// @notice 当一个代币被退款时发出
/// @dev 由 `refund` 发出
/// @param _from 请求退款的帐户
/// @param _tokenId 被退款的 `tokenId`
/// @param _amount 被退款的 `tokenId` 的数量
event Refund(
address indexed _from,
uint256 indexed _tokenId,
uint256 _amount
);
/// @notice 当一个代币被退款时发出
/// @dev 由 `refundFrom` 发出
/// @param _sender 发送退款的帐户
/// @param _from 资产被退款的帐户
/// @param _tokenId 被退款的 `tokenId`
/// @param _amount 被退款的 `tokenId` 的数量
event RefundFrom(
address indexed _sender,
address indexed _from,
uint256 indexed _tokenId
);
/// @notice 只要给定 `tokenId` 的退款有效,就退款给用户
/// @dev 确保检查用户是否有足够的代币,并注意潜在的重入向量
/// @param tokenId 要退款的 `tokenId`
/// @param amount 要退款的 `tokenId` 的数量
function refund(uint256 tokenId, uint256 amount) external;
/// @notice 只要退款有效并且发送者有足够的批准,就退款代币并将以太币发送给发送者
/// @dev 确保检查用户是否有足够的代币,并注意潜在的重入向量
/// 以太币会发送给 msg.sender。
/// @param from 要从中退款代币的用户
/// @param tokenId 要退款的 `tokenId`
/// @param amount 要退款的 `tokenId` 的数量
function refundFrom(address from, uint256 tokenId, uint256 amount) external;
/// @notice 获取特定 `tokenId` 的退款价格
/// @param tokenId 要查询的 `tokenId`
/// @return _wei 对于单个代币将退还的以太币数量(以 wei 为单位)
function refundOf(uint256 tokenId) external view returns (uint256 _wei);
/// @notice 获取退款无效的给定 `tokenId` 的第一个区块
/// @param tokenId 要查询的 `tokenId`
/// @return block 代币无法退款的第一个区块
function refundDeadlineOf(uint256 tokenId) external view returns (uint256 block);
}
可选的 ERC-1155 批量退款扩展
// SPDX-License-Identifier: CC0-1.0;
import "ERC1155Refund.sol";
/// @notice 批量可退款的 ERC-1155 代币
/// @dev 该接口的 ERC-165 标识符为 ``
contract ERC1155BatchRefund is ERC1155Refund {
/// @notice 当一个或多个代币被批量退款时发出
/// @dev 由 `refundBatch` 发出
/// @param _from 请求退款的帐户
/// @param _tokenIds 被退款的 `tokenIds`
/// @param _amounts 每个被退款的 `tokenId` 的数量
event RefundBatch(
address indexed _from,
uint256[] _tokenIds, // 可能是索引的,也可能不是
uint256[] _amounts
);
/// @notice 当一个或多个代币被批量退款时发出
/// @dev 由 `refundFromBatch` 发出
/// @param _sender 发送退款的帐户
/// @param _from 资产被退款的帐户
/// @param _tokenIds 被退款的 `tokenId`
/// @param _amounts 每个被退款的 `tokenId` 的数量
event RefundFromBatch(
address indexed _sender,
address indexed _from,
uint256[] _tokenId, // 可能是索引的,也可能不是
uint256[] _amounts
);
/// @notice 只要给定 `tokenIds` 的退款有效,就退款给用户
/// @dev 确保检查用户是否有足够的代币,并注意潜在的重入向量
/// 这些必须一起成功或失败;没有部分退款。
/// @param tokenIds 要退款的 `tokenId`s
/// @param amounts 要退款的每个 `tokenId` 的数量
function refundBatch(uint256[] tokenIds, uint256[] amounts) external;
/// @notice 只要给定 `tokenIds` 的退款有效并且发送者有足够的批准,就退款代币并将以太币发送给发送者
/// @dev 确保检查用户是否拥有代币,并注意潜在的重入向量
/// 以太币会发送给 msg.sender。
/// 这些必须一起成功或失败;没有部分退款。
/// @param from 要从中退款代币的用户
/// @param tokenIds 要退款的 `tokenId`s
/// @param amounts 要退款的每个 `tokenId` 的数量
function refundFromBatch(address from, uint256[] tokenIds, uint256[] amounts external;
}
理由
refundDeadlineOf
使用区块而不是时间戳,因为时间戳不如区块号可靠。
选择 refund
、refundOf
和 refundDeadlineOf
的函数名称是为了适应 ERC-20、ERC-721 和 ERC-1155 的命名风格。
需要 ERC-165,因为如果不这样做,DApp 的自省会变得更加困难。
不支持自定义 ERC-20 代币,因为它不必要地增加了复杂性,并且 refundFrom
函数在与 DEx 结合使用时允许此功能。
批量退款是可选的,因为帐户抽象会使像这样的原子操作变得更加容易。但是,如果正确实施,它们仍然可以降低 gas 成本。
向后兼容性
未发现向后兼容性问题。
安全考虑
refund
函数存在潜在的重入风险。确保在销毁代币之后执行以太币转移(即遵守检查、效果、交互模式)。
版权
版权和相关权利通过 CC0 放弃。
Citation
Please cite this document as:
elie222 (@elie222), Gavin John (@Pandapip1), "ERC-5507: 可退款代币," Ethereum Improvement Proposals, no. 5507, August 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5507.