Appearance
L1-6: Rebasing Token(弹性供应代币)⭐
1. 问题
创建类似 AMPL (Ampleforth) 的弹性供应 ERC20 代币。总供应量可以通过 rebase 调整,所有持有者余额按比例变化。
例如:总供应 10M → rebase(-9M) → 总供应 1M,每个持有者余额变为原来的 1/10。
2. 原因
AMPL 的目标是创建「购买力稳定」的货币。通过调整供应量实现价格目标:
- 价格 > $1 → 正 rebase(增加供应,持有者余额增加)
- 价格 < $1 → 负 rebase(减少供应,持有者余额减少)
技术上不可能遍历所有持有者修改余额(Gas ∞),需要间接方案。
3. 方案
核心机制:GONs + 缩放因子
内部表示 (GONs): _gonBalances[addr] // rebase 不改变
缩放因子: _gonsPerFragment // rebase 时改变
外部表示 (显示值): balanceOf = _gonBalances / _gonsPerFragment
Rebase 操作
solidity
function rebase(int256 amount) external onlyOwner {
uint256 currentTotal = totalSupply(); // GONs / _gonsPerFragment
uint256 newTotal = currentTotal + amount; // (正rebase) 或 currentTotal - decreaseBy (负rebase)
_gonsPerFragment = _gonTotalSupply / newTotal;
// 无需修改任何 _gonBalances!
}
为什么有效
- 正 rebase: 总供应 10M → 11M,
_gonsPerFragment从 1e18 变为_gonTotalSupply / 11M(变小) balanceOf=_gonBalances / _gonsPerFragment(分子不变,分母变小 = 结果变大)
4. 遇到的陷阱 ⭐
4.1 transfer/transferFrom 金额需转换
用户传入的 amount 是显示值,内部必须转换为 GONs:
solidity
function transfer(address to, uint256 amount) public override returns (bool) {
uint256 gonAmount = amount * _gonsPerFragment;
_transferGon(msg.sender, to, gonAmount);
emit Transfer(msg.sender, to, amount);
return true;
}
4.2 allowance 不受 rebase 影响
OZ ERC20 的 allowance 存储的是显示值,不是 GONs。transferFrom 必须正确处理:
solidity
_spendAllowance(from, msg.sender, amount); // 按显示值扣 allowance
_transferGon(from, to, gonAmount); // 按 GONs 转账
4.3 整数除法精度损失
多次 rebase 后 _gonsPerFragment 可能累积误差。_gonTotalSupply / newTotal 有舍入误差。
5. 陷阱的原因
5.1
如果 transfer 不转换,用户转出 100 tokens,实际只转出 100 GONs。但 _gonsPerFragment 可能 > 1 或 < 1,导致实际金额和预期严重不符。
5.2
allowance 语义:用户授权的是「显示值」,不是 GONs。如果 allowance 也按 GONs 缩放,授权量会随 rebase 变化(不符合预期)。
5.3
Solidity 整数除法截断。每次 rebase 累积舍入误差可能导致 balanceOf 之和 ≠ totalSupply。
6. 如何解决陷阱
- 所有外部接口(transfer/transferFrom/approve)的参数和 events 使用显示值
- 内部存储和计算使用 GONs
- allowance 存储和消费使用显示值(调用
_spendAllowance时传显示值) - 对于精度损失:使用高精度(1e18 作为初始 _gonsPerFragment)减小舍入影响
7. 技术要点
| 要点 | 说明 |
|---|---|
| GONs 模式 | 内部固定单位 + 外部可变比例 |
| AMPL 原理 | 弹性供应实现购买力稳定 |
| ERC20 覆盖 | balanceOf / totalSupply / transfer / transferFrom 全部重写 |
| Allowance 隔离 | allowance 不受 rebase 影响 |
| 精度处理 | 使用 18 位小数精度 = 1e18 基准 |