ERC-6864: 可升级的同质化代币
可升级的同质化代币,是对 ERC-20 的一个简单扩展
Authors | Jeff Huang (@jeffishjeff) |
---|---|
Created | 2023-04-05 |
Discussion Link | https://ethereum-magicians.org/t/eip-6864-upgradable-fungible-token-a-simple-extension-to-erc-20/13781 |
Requires | EIP-20 |
摘要
本提案概述了一个智能合约接口,用于升级/降级现有的 ERC-20 智能合约,同时保持用户的余额。该接口本身是对 ERC-20 标准的扩展,以便其他智能合约可以继续与升级后的智能合约交互,而无需更改地址之外的任何内容。
动机
根据设计,智能合约是不可变的,像 ERC-20 这样的代币标准是极简的。虽然这些设计原则在去中心化应用中是根本性的,但在某些合理且实际的情况下,升级 ERC-20 代币的能力是可取的,例如:
- 解决错误和消除限制
- 采用新的特性和标准
- 遵守不断变化的法规
使用 delegatecall
操作码的代理模式提供了一个合理的、通用的解决方案来协调不可变性和可升级性特性,但它也有其自身的缺点:
- 智能合约必须从一开始就支持代理模式,即它不能用于没有通过代理部署的合约
- 升级是静默的且不可逆的,即用户没有选择退出的选项
相比之下,通过将范围缩小到专门的 ERC-20 代币,本提案标准化了一个 ERC-20 扩展,它可以与任何现有或未来的 ERC-20 智能合约一起工作,实现和维护起来更简单,可以反转或嵌套,并为任何和所有用户提供双重确认机会以显式选择加入升级。
ERC-4931 试图通过引入第三个“桥”合约来解决相同的问题,以帮助促进升级/降级操作。虽然这种设计将升级/降级逻辑与代币逻辑分离,但 ERC-4931 将要求在目标智能合约上预先铸造代币,并由桥合约拥有,而不是在调用升级时按需进行。它也无法支持如下所述的升级时转移和穿透函数。
规范
本文档中的关键词 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, 和 “OPTIONAL” 按照 RFC 2119 和 RFC 8174 中的描述进行解释。
pragma solidity ^0.8.0;
/**
@title 可升级的同质化代币
@dev 参见 https://eips.ethereum.org/EIPS/eip-6864
*/
interface IERC6864 is IERC20 {
/**
@dev 当代币被升级时,必须发出
@param from 基础 ERC-20 代币的先前所有者
@param to ERC-6864 代币的新所有者
@param amount 被升级的数量
*/
event Upgrade(address indexed from, address indexed to, uint256 amount);
/**
@dev 当代币被降级时,必须发出
@param from ERC-6864 代币的先前所有者
@param to 基础 ERC-20 代币的新所有者
@param amount 被降级的数量
*/
event Downgrade(address indexed from, address indexed to, uint256 amount);
/**
@notice 将 `msg.sender` 拥有的 `amount` 个基础 ERC-20 代币升级为 `to` 名下的 ERC-6864 代币
@dev `msg.sender` 必须直接拥有足够的基础 ERC-20 代币
如果 `to` 是零地址,则必须回退
如果 `msg.sender` 没有直接拥有 `amount` 个或更多的基础 ERC-20 代币,则必须回退
@param to 接收 ERC-6864 代币的地址
@param amount 要升级的基础 ERC-20 代币的数量
*/
function upgrade(address to, uint256 amount) external;
/**
@notice 将 `from` 拥有的 `amount` 个 ERC-6864 代币降级为 `to` 名下的基础 ERC-20 代币
@dev `msg.sender` 必须直接拥有或被批准花费 `from` 的足够 ERC-6864 代币
如果 `to` 是零地址,则必须回退
如果 `from` 没有直接拥有 `amount` 个或更多的 ERC-6864 代币,则必须回退
如果 `msg.sender` 不是 `from` 且未被批准为 `from` 花费 `amount` 或更多的 ERC-6864 代币,则必须回退
@param from 释放 ERC-6864 代币的地址
@param to 接收基础 ERC-20 代币的地址
@param amount 要降级的 ERC-6864 代币的数量
*/
function downgrade(address from, address to, uint256 amount) external;
/**
@notice 获取基础 ERC-20 智能合约地址
@return 基础 ERC-20 智能合约的地址
*/
function baseToken() external view returns (address);
}
透视扩展
透视扩展是 OPTIONAL 的。它允许轻松查看此 ERC-6864 和基础 ERC-20 智能合约之间的组合状态。
pragma solidity ^0.8.0;
interface IERC6864SeeThrough is IERC6864 {
/**
@notice 获取此 ERC-6864 和基础 ERC-20 智能合约之间的组合代币总供应量
@return 组合代币总供应量
*/
function combinedTotalSupply() external view returns (uint256);
/**
@notice 获取 `account` 在此 ERC-6864 和基础 ERC-20 智能合约之间的组合代币余额
@param account 拥有代币的地址
@return 组合代币余额
*/
function combinedBalanceOf(address account) external view returns (uint256);
/**
@notice 获取 `spender` 可以为 `owner` 花费的、在此 ERC-6864 和基础 ERC-20 智能合约之间的组合授权额度
@param owner 拥有代币的地址
@param spender 被批准花费代币的地址
@return 组合花费授权额度
*/
function combinedAllowance(address owner, address spender) external view returns (uint256);
}
理由
扩展 ERC-20 标准
本提案的目标是在不影响用户余额的情况下进行升级,因此利用现有的数据结构和方法是工程工作量最小的方式,也是互操作性最强的方式。
支持降级
降级能力使得在同一个基础 ERC-20 智能合约上的多个 IERC-6864 实现之间移动成为可能。如果发现 ERC-6864 智能合约存在错误或限制,或者用户只是改变了主意,它也提供了一种退出方式。
可选透视扩展
虽然这些函数在许多情况下都很有用,但它们实现起来很简单,并且可以通过其他公共函数计算结果,因此决定将它们包含在一个可选扩展中,而不是核心接口中。
向后兼容性
ERC-6864 通常与 ERC-20 标准兼容。唯一的注意事项是,一些智能合约可能会选择实现 transfer
以处理整个组合余额(这减少了用户摩擦,请参阅参考实现),而不是标准的 balanceOf
金额。在这种情况下,建议此类合约实现 totalSupply
和 balanceOf
以返回此 ERC-6864 和基础 ERC-20 智能合约之间的组合金额
参考实现
import {IERC20, ERC20} from "@openzeppelin-contracts/token/ERC20/ERC20.sol";
contract ERC6864 is IERC6864, ERC20 {
IERC20 private immutable s_baseToken;
constructor(string memory name, string memory symbol, address baseToken_) ERC20(name, symbol) {
s_baseToken = IERC20(baseToken_);
}
function baseToken() public view virtual override returns (address) {
return address(s_baseToken);
}
function upgrade(address to, uint256 amount) public virtual override {
address from = _msgSender();
s_baseToken.transferFrom(from, address(this), amount);
_mint(to, amount);
emit Upgrade(from, to, amount);
}
function downgrade(address from, address to, uint256 amount) public virtual override {
address spender = _msgSender();
if (from != spender) {
_spendAllowance(from, spender, amount);
}
_burn(from, amount);
s_baseToken.transfer(to, amount);
emit Downgrade(from, to, amount);
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address from = _msgSender();
uint256 balance = balanceOf(from);
if (balance < amount) {
upgrade(from, amount - balance);
}
_transfer(from, to, amount);
return true;
}
function totalSupply() public view virtual override returns (uint256) {
return super.totalSupply() + s_baseToken.totalSupply() - s_baseToken.balanceOf(address(this));
}
function balanceOf(address account) public view virtual override returns (uint256) {
return super.balanceOf(account) + s_baseToken.balanceOf(account);
}
}
安全考虑
- 选择升级基础 ERC-20 代币的用户必须首先
approve
ERC-6864 智能合约来花费它们。因此,用户有责任验证 ERC-6864 智能合约是健全和安全的,并且他或她批准的金额是适当的。这代表了与任何approve
操作相同的安全考虑。 - ERC-6864 智能合约可以为升级/降级实现任何适当的转换函数:1 对 1、线性、非线性。在非线性转换函数的情况下,
upgrade
和downgrade
可能容易受到抢先交易或三明治攻击(无论是否对攻击者有利)。这代表了与任何使用类似非线性曲线进行转换的自动做市商 (AMM) 相同的安全考虑。 - ERC-6864 智能合约可能会要求用户批准无限额度,和/或尝试在
transfer
期间自动升级(请参阅参考实现)。这消除了用户三重确认其升级意图的机会(approve
是双重确认)。 - 多个 IERC-6864 实现可以应用于同一个基础 ERC-20 代币,并且 ERC-6864 智能合约可以嵌套。这将增加代币的复杂性,并可能导致现有仪表板报告不正确或不一致的结果。
版权
版权及相关权利通过 CC0 放弃。
Citation
Please cite this document as:
Jeff Huang (@jeffishjeff), "ERC-6864: 可升级的同质化代币 [DRAFT]," Ethereum Improvement Proposals, no. 6864, April 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6864.