💥 Gate 广场活动:#发帖赢代币TRUST 💥
在 Gate 广场发布与 TRUST 或 CandyDrop 活动 相关的原创内容,即有机会瓜分 13,333 枚 TRUST 奖励!
📅 活动时间: 2025年11月6日 – 11月16日 24:00(UTC+8)
📌 相关详情:
CandyDrop 👉 https://www.gate.com/zh/announcements/article/47990
📌 参与方式:
1️⃣ 在 Gate 广场发布原创内容,主题需与 TRUST 或 CandyDrop 活动相关;
2️⃣ 内容不少于 80 字;
3️⃣ 帖子添加话题: #发帖赢代币TRUST
4️⃣ 附上任意 CandyDrop 活动参与截图。
🏆 奖励设置(总奖池:13,333 TRUST)
🥇 一等奖(1名):3,833 TRUST / 人
🥈 二等奖(3名):1,500 TRUST / 人
🥉 三等奖(10名):500 TRUST / 人
📄 注意事项:
内容必须原创,禁止抄袭或灌水;
获奖者需完成 Gate 广场身份认证;
活动最终解释权归 Gate 所有。
价格操纵的黑魔法:Balancer V2 不变量计算漏洞剖析
编译:白话区块链
2025 年 11 月 3 日,Balancer V2 的组合型稳定池(Composable Stable Pools)以及多个链上的几个分叉项目遭受了一次协同攻击,导致总损失超过 1.25 亿美元。BlockSec 在第一时间发出了警报,并随后发布了初步分析。
这是一次高度复杂的攻击。我们的调查显示,根本原因是不变量(invariant)计算中的精度损失导致的价格操纵,进而扭曲了 BPT(Balancer 池Token)的价格计算。这种不变量操纵允许攻击者通过单次批量交换(batch swap)从一个特定的稳定池中获利。尽管一些研究人员提供了富有洞察力的分析,但某些解读存在误导性,其根本原因和攻击过程尚未完全阐明。本博客旨在对该事件进行全面而准确的技术分析。
关键要点 (TL;DR)
根本原因:舍入不一致和精度损失
攻击执行
运营影响与放大
在以下部分中,我们将首先提供有关 Balancer V2 的关键背景信息,然后深入分析已发现的问题和相关攻击。
0x1 背景
1. Balancer V2 的组合型稳定池
本次攻击中受影响的组件是 Balancer V2 协议的组合型稳定池。这些池专为预期保持接近 1:1 锚定(或以已知汇率交易)的资产而设计,并允许以最小的价格影响进行大额交换,从而显著提高同类或相关资产之间的资本效率。每个池都有自己的 Balancer 池Token (BPT),代表流动性提供者在池中的份额,以及相应的底层资产。
该池采用了 Stable Math(基于 Curve 的 StableSwap 模型),其中不变量 D 代表了池的虚拟总价值。
BPT 价格可以近似为:
2. batchSwap() 和 onSwap()
Balancer V2 提供了 batchSwap() 函数,该函数支持在 Vault内进行多跳交换(multi-hop swaps)。根据传递给此函数的参数,有两种交换类型:
通常,batchSwap() 由多个通过 onSwap() 函数执行的Token间交换组成。以下概述了当一个 SwapRequest被分配为 GIVEN_OUT 交换类型时的执行路径(请注意 ComposableStablePool 继承自 BaseGeneralPool):
以下显示了 GIVEN_OUT 交换类型中 amount_in 的计算,这涉及不变量 D。
// inGivenOut token x for y - polynomial equation to solve // ax = amount in to calculate
// bx = balance token in
// x = bx + ax (finalBalanceIn)
// D = invariant // A = amplification coefficient // n = number of tokens // S = sum of final balances but x
// P = product of final balances but x
x^2 + ( S - ---------- - D) * x - ------------- = 0
(A * n^n) A * n^2n * P
3. 缩放与舍入
为了在不同Token余额之间规范化计算,Balancer 执行以下两个操作:
显然,放大和缩小在理论上是成对的操作——分别是乘法和除法。然而,这两种操作的实现中存在不一致。具体来说,缩小操作有两个变体或方向:divUp 和 divDown。相比之下,放大操作只有一个方向,即 mulDown。
这种不一致的原因尚不清楚。根据 _upscale() 函数中的注释,开发人员认为单向舍入的影响是微乎其微的。
0x2 漏洞分析
根本问题源于在 BaseGeneralPool._swapGivenOut() 函数中执行放大(upscaling)操作时所执行的向下舍入操作。特别是,_swapGivenOut() 通过 _upscale() 函数错误地将 swapRequest.amount 向下舍入。这个舍入后的值随后被用作 amountOut,在通过 _onSwapGivenOut() 计算 amountIn 时使用。这种行为与“舍入应以有利于协议的方式应用”的标准实践相矛盾。
因此,对于给定的池(wstETH/rETH/cbETH),计算出的 amountIn 低估了实际所需的输入。这允许用户以较少数量的一种底层资产(例如 wstETH)交换另一种资产(例如 cbETH),从而由于有效流动性减少而导致不变量 D 减少。因此,相应的 BPT(wstETH/rETH/cbETH)的价格变得被人为压低(deflated),因为 $\text{BPT price} = \frac{D}{\text{totalSupply}}$。
0x3 攻击分析
攻击者执行了
两阶段攻击,可能是为了最小化被检测的风险:
第一阶段可以进一步分为两个阶段:参数计算和批量交换。下面,我们使用 Arbitrum 上的一个攻击交易(TX)示例(来说明这些阶段。
参数计算阶段
在此阶段,攻击者将链下计算与链上模拟相结合,根据组合型稳定池的当前状态(包括缩放因子、放大系数、BPT 汇率、交换费用和其他参数),精确调整下一阶段(批量交换)中每一跳的参数。有趣的是,攻击者还部署了一个辅助合约来协助这些计算,这可能是为了减少被“抢跑”(front-running)的风险。
首先,攻击者收集目标池的基本信息,包括每个Token的缩放因子、放大参数、BPT 汇率和交换费百分比。然后他们计算一个关键值 trickAmt,这是用于引发精度损失的被操纵的目标Token数量。
将目标Token的缩放因子(scaling factor)记为 sF,计算如下:
uint256[] balances; // 池Token的余额(不包括 BPT) uint256[] scalingFactors; // 每个池Token的缩放因子 uint tokenIn; // 此跳模拟的输入Token索引 uint tokenOut; // 此跳模拟的输出Token索引 uint256 amountOut; // 期望的输出Token数量 uint256 amp; // 池的放大参数 uint256 fee; // 池交换费百分比
返回数据为:
uint256[] balances; // 交换后池Token的余额(不包括 BPT)
具体来说,初始余额和迭代循环次数是链下计算的,并作为参数传递给攻击者的合约(报告分别为 100,000,000,000 和 25)。每次迭代执行三次交换:
请注意,由于 StableMath 计算中使用了牛顿-拉弗森(Newton–Raphson)方法,此步骤可能偶尔会失败。为了缓解这种情况,攻击者实现了两次重试尝试,每次使用原始值的 9/10 作为备用值。
攻击者的辅助合约派生自 Balancer V2 的 StableMath 库,这一点可以通过其包含“BAL”风格的自定义错误消息来证明。
批量交换阶段
然后,batchSwap() 操作可以分解为三个步骤:
0x4: 攻击与损失
我们在下表中总结了这些攻击及其相应的损失,总损失超过 1.25 亿美元。
0x5 结论
该事件涉及一系列针对 Balancer V2 协议及其分叉项目的攻击交易,导致了重大的财务损失。在最初的攻击之后,在多个链上观察到了大量后续和模仿交易。这次事件凸显了 DeFi 协议设计和安全的几个关键教训:
在保持运营和业务连续性的同时,行业参与者可以利用 BlockSec Phalcon 作为最后一道防线来保护他们的资产。BlockSec 专家团队随时准备为您的项目进行全面的安全评估。
本文链接:
来源: