Back to Blog

#3 Balancer V2 事件:舍入不一致破坏了不变量并跨链传播

Code Auditing
February 10, 2026
5 min read

#3 Balancer V2 事件:舍入不一致破坏了不变式并跨链传播

2025 年 11 月 3 日,Balancer V2 的可组合稳定池以及多个链上的多个分叉项目遭受了协调性攻击,导致总损失超过 1.25 亿美元,据报道约有 4500 万美元被追回。根本原因是由于在放大和缩小操作之间的舍入不一致,导致不变式计算中的精度损失,从而使 BPT(Balancer 池代币)定价逻辑扭曲。

此次事件之所以成为 2025 年十大安全事件之一,不仅因为损失的规模,还因为底层 bug 的微妙性。此外,此次攻击迅速蔓延到多个链,并影响了 Balancer 及其分叉,凸显了共享代码库和可组合 DeFi 基础设施如何显著放大系统性风险。

我们已发布一份题为“深入分析:Balancer V2 攻击” [1] 的详细报告,其中提供了详细的技术 breakdown。下面,我们对此次事件进行简要说明。

背景

Balancer V2 的可组合稳定池

此次攻击中受影响的组件是 Balancer V2 协议的可组合稳定池 [2]。这些池专为预期保持近 1:1 比例(或以已知汇率交易)的资产而设计,并允许进行大额掉期,价格影响极小,从而显著提高了同类或相关资产之间的资本效率。每个池都有自己的 Balancer 池代币(BPT),代表流动性提供者在池中的份额,以及相应的底层资产。

  • 此池采用稳定币数学(基于 Curve 的 StableSwap 模型),其中不变式 D 代表池的虚拟总价值。

  • BPT 价格可近似为:

Price(BPT)DtotalSupplyPrice(BPT) \approx \frac{D}{totalSupply}

从上述公式可以看出,如果 D 在纸面上可以减小(即使没有实际的资金损失),BPT 的价格就会显得更便宜。

batchSwap()onSwap()

Balancer V2 提供了 batchSwap() 函数,该函数支持 Vault [3] 内的多跳掉期。根据传递给该函数的参数,有两种掉期类型:

  • GIVEN_IN(“给定输入”):调用者指定输入的精确数量,池计算相应的输出数量。
  • GIVEN_OUT(“给定输出”):调用者指定所需的输出数量,池计算所需的输入数量。

通常,batchSwap() 由通过 onSwap() 函数执行的多个代币到代币掉期组成。此过程不可避免地涉及与不变式 D [1] 相关的金额计算。

缩放和舍入

为了标准化不同代币余额之间的计算,Balancer 执行以下两个操作:

  • 放大:在执行计算之前,将余额和金额放大到统一的内部精度。
  • 缩小:将结果转换回其原始精度,并应用定向舍入(例如,输入金额通常向上舍入以确保池不会少收费用,而输出金额通常向下舍入)。

放大和缩小在理论上是成对的操作:分别对应乘法和除法。然而,在这些操作的实现中存在不一致性。 具体来说,缩小操作有两种变体或方向:“divUp”和“divDown”。相比之下,放大操作只有一个方向,即“mulDown”。

漏洞分析

根本问题源于 BaseGeneralPool._swapGivenOut() 函数中放大期间执行的向下舍入操作。特别地,_swapGivenOut() 通过 _upscale() 函数错误地向下舍除了 swapRequest.amount。由此产生的舍入值随后在通过 _onSwapGivenOut() 计算 amountIn 时用作 amountOut此行为与协议有利的舍入标准做法相悖。

因此,对于给定的池(wstETH/rETH/cbETH),计算出的 amountIn 低估了实际所需的输入。这使得用户可以用较少数量的一种底层资产(例如 wstETH)换取另一种资产(例如 cbETH),从而由于有效流动性降低而导致不变式 D 降低。因此,相应 BPT(wstETH/rETH/cbETH)的价格被人为压低,因为 BPT 价格 = D / totalSupply。

攻击分析

攻击者执行了分两个阶段的攻击,可能是为了最大限度地降低被发现的风险:

  • 在第一阶段,核心攻击在单个交易内完成,没有立即获利。
  • 在第二阶段,攻击者在单独的交易中提取资产以实现利润。

第一阶段可进一步细分为两个阶段:参数计算批量掉期。下面,我们将使用 Arbitrum 上的示例攻击交易(TX)来说明这些阶段。

参数计算阶段

在此阶段,攻击者结合了链下计算和链上模拟,根据可组合稳定池的当前状态(包括缩放因子、放大系数、BPT 费率、交易费用和其他参数),精确调整下一个(批量掉期)阶段的每个跳跃参数。攻击者还部署了一个辅助合约来协助这些计算,这可能旨在减少被抢跑的风险。有关更多详细信息,请参阅 [1]。

批量掉期阶段

然后,batchSwap() 操作可分解为三个步骤:

步骤 1:攻击者将 BPT(wstETH/rETH/cbETH)兑换成底层资产,以精确地将一种代币(cbETH)的余额调整到舍入边界的边缘(金额 = 9)。这为下一步的精度损失创造了条件。

步骤 2:攻击者随后使用一个精心设计的金额(= 8)在另一种底层资产(wstETH)和 cbETH 之间进行掉期。由于在缩放代币金额时向下舍入,计算出的 Δx 变得稍小(从 8.918 变为 8),导致 Δy 被低估,从而导致不变式(Curve 的 StableSwap 模型中的 D)减小。由于 BPT 价格 = D / totalSupply,BPT 价格被人为压低。

步骤 3:攻击者将底层资产反向兑换回 BPT,恢复了平衡,同时从被压低的 BPT 价格中获利。

总结

此次事件涉及一系列协调攻击交易,目标是 Balancer V2 的可组合稳定池以及跨不同链的多个分叉部署,造成了巨额损失。在第一次攻击后,模仿攻击交易迅速涌现,表明一旦攻击模式暴露,其传播速度有多快。

关键经验教训:

  • 舍入和精度处理:所有代币金额的缩放和精度操作都应朝着有利于协议的方向舍入。_upscale()(仅向下舍入)和缩小操作(定向舍入)之间的单一不一致足以造成可利用的定价扭曲。
  • 安全军备竞赛:攻击者将操纵和利润提取分拆到单独的交易中以逃避检测。检测系统应关联相关交易,而不仅仅是标记单个交易。
  • 运营安全:一旦攻击模式公开,模仿者在几分钟内就跨链复制了它。共享代码库的协议需要协调的监控和快速的跨链暂停功能。

参考

  1. https://blocksec.com/blog/in-depth-analysis-the-balancer-v2-exploit

  2. https://docs-v2.balancer.fi/concepts/pools/composable-stable.html

  3. https://docs-v2.balancer.fi/reference/swaps/batch-swaps.html


关于 BlockSec

BlockSec 是一家全栈式区块链安全和加密合规提供商。我们构建产品和服务,帮助客户在协议和平台的整个生命周期中进行代码审计(包括智能合约、区块链和钱包)、实时拦截攻击、分析事件、追踪非法资金,并满足 AML/CFT 义务。

BlockSec 在知名会议上发表了多篇区块链安全论文,报告了多个 DeFi 应用的零日攻击,阻止了多次黑客攻击,挽救了超过 2000 万美元,并确保了数十亿美元的加密货币安全。

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit