链上 Vault - ERC4626 与通胀攻击

本文主要分析了 ERC4626 标准下的 On-chain Vault 可能遭受的通胀攻击,攻击者通过操纵 Vault 中的资产总量来稀释其他用户的份额,并探讨了多种防御方法,包括初始化 Vault 资产、内部控制资产总量、使用 decimals offset 以及 OpenZeppelin 提出的虚拟份额和资产方案。

On-chain Vault — ERC4626 和通货膨胀攻击

背景

什么是 ERC4626?

ERC4626 是 ERC20 的扩展,它为 token 金库提出了一种标准接口,提供存款、取款 token 和读取余额的功能。

contract ERC4626 is ERC20, IERC4626 {}

实现的主要功能有

  • 存入资产(例如 USDC)以获得份额 token
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
function mint(uint256 shares, address receiver) external returns (uint256 assets);nt256 shares, address receiver, address owner) external returns (uint256 assets);
  • 赎回份额 token,获得资产
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
  • 份额和金额转换比率
function convertToShares(uint256 assets) external view returns (uint256 shares);
function convertToAssets(uint256 shares) external view returns (uint256 assets);

份额和金额之间存在一个公式:

这里的 share 是用户应该获得的份额数量,asset 是用户存入或提取到金库的资产数量。确定资产和份额之间的比率

Uniswap V2 流动性池本质上就是一个金库。

通货膨胀攻击

让我们考虑一个场景

  1. 最初,totalAssets 和 totalShares 均为 0
  2. 攻击者存入 1 个 token,获得 shareReceived = 1(totalShares = 1)
  3. 用户计划存入 1000 个 token
  4. 攻击者在用户之前转移 1000 个 token,将 totalAssets 更改为 1001
  5. 用户存入 1000 个 token,获得 shareReceived = 1 * 1000 / 1001 = 0.999001 = 0,金库状态:totalAssets = 1001 + 1000 = 2001,totalShares = 1
  6. 攻击者赎回 1 个份额以获得 2001 个 token

这是如何发生的?攻击者可以通过增加分母来操纵分母,导致用户由于向下舍入而获得比预期更少的份额。

让我们根据 通货膨胀攻击 对其进行分析,假设

在攻击者捐赠后,金库的比率为

为了将存款稀释为 0 份额,只需要确保

使用 a0 为 1 且 a1 为 u 就足够了。因此,攻击者只需要 u+1 即可执行攻击

防御方法

确保初始状态由所有者设置

如果金库中的初始资产不为零,则攻击者的成本很高。即使没有其他防御方法,假设所有者将 1000 个资产存入金库。

userShare = totalyShare(1000) * assetAmount(1000) / totalAsset(1000 + x)

攻击者需要使 (x + 1000) 大于 10⁶,因此 x = 10^-1000

控制 totalAsset 内部状态,而不是 balanceof()

攻击成功的另一个原因是,攻击者可以将资产(捐赠)转移到合约中,从而稀释份额。

跟踪金库持有的资产的内部状态,而不是依赖于 balanceOf 函数。这样,捐赠的资产就无法影响金库的内部会计。

YieldBox 中使用的十进制偏移方法

YieldBox 在 totalShare 中添加了一个十进制偏移量,这意味着 1 个资产不对应 1 个份额。

function _toShares(
    uint256 amount,
    uint256 totalShares_,
    uint256 totalAmount,
    bool roundUp
) internal pure returns (uint256 share) {
    // To prevent reseting the ratio due to withdrawal of all shares, we start with
    // 1 amount/1e8 shares already burned. 这也从 1 : 1e8 的比率开始
    // functions like 8 decimal fixed point math. This prevents ratio attacks or inaccuracy
    // due to 'gifting' or rebasing tokens. (Up to a certain degree)
    totalAmount++;
    totalShares_ += 1e8;
    // Calculte the shares using te current amount to share ratio
    // 使用当前金额与份额的比率计算份额
    share = (amount * totalShares_) / totalAmount;
    // Default is to round down (Solidity), round up if required
    // 默认是向下舍入 (Solidity),如果需要则向上舍入
    if (roundUp && (share * totalAmount) / totalShares_ < amount) {
        share++;
    }
}

Openzeppelin

Oz 提出了基于 YieldBox 的防御措施,它由两部分组成:虚拟份额和十进制偏移量

上面的公式变为:

  • Decimal 意味着我们可以使用更多的小数位来表示份额。例如 Decimal 为 3,1 个资产对应 1000 个份额
  • totalAsset + 1 而不是 totalAsset 意味着我们给金库存入一个虚拟资产

这是 openzeppelin 在 v4.9 之后根据上述公式的实现

function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
    return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
}

让我们看看如果没有小数位,上述攻击是否会成功。现在我们有:

  1. 最初,totalAssets 和 totalShares 均为 0
  2. 攻击者存入 1 个 token,获得 shareReceived = 1 * 1 / 1 = 1(totalShares = 1)
  3. 用户计划存入 1000 个资产
  4. 为了让用户收到 0 份额,攻击者需要在用户之前转移 x 个资产。我们有:

因此,最小的 x 应该是 1999。金库的总资产为 2000

  1. 用户存入 1000 个资产,获得 shareReceived = 1000 * (1 + 1) / 2001 = 0,金库状态:totalAssets = 3000,totalShares = 1

  2. 攻击者赎回 1 个份额,收到 token 数量 = 1 * (3001) / (1 + 1 ) = 1500

我们可以看到,即使没有小数位,攻击者仍然损失了 500 个资产。如果用户没有存款,则损失至少等于用户的存款 (1000)。因此,虚拟份额和资产使此攻击对攻击者来说无利可图。

实际上,攻击的捐赠必须遭受更多的损失,以确保如果没有小数位,用户无法获得份额。

这是我编写的一个用于测试的程序:

参考

https://x.com/bloom_cry/status/1852965843988365736

ERC-4626 — OpenZeppelin Docs

docs.openzeppelin.com

针对 ERC4626 通货膨胀攻击的新型防御

ERC4626,熟悉 ERC20 的扩展,为 token 金库提供了一个标准化接口,使开发人员能够构建…

blog.openzeppelin.com

  • 原文链接: blog.blockmagnates.com/o...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
blockmagnates
blockmagnates
江湖只有他的大名,没有他的介绍。