Balancer V2 Composable Stable Pools exploited across multiple chains through rounding direction inconsistency in rate scaling logic. Attacker manipulated pool invariant to deflate BPT prices.
On November 3, 2025, Balancer V2's Composable Stable Pools suffered a coordinated exploit across nine blockchain networks, resulting in losses exceeding $128 million. The attacker exploited a rounding direction inconsistency in the pool's rate scaling logic that had existed in the codebase since 2021. By manipulating token balances to specific rounding boundaries and executing a carefully crafted batch swap sequence, the attacker deflated the pool invariant D, artificially reducing BPT prices and profiting through arbitrage. This incident demonstrates the evolution of DeFi exploits toward sophisticated arithmetic edge cases.
Balancer V2's Composable Stable Pools are based on Curve's StableSwap model, designed for assets expected to trade at known exchange rates. The pools use a centralized Vault architecture that manages all token balances, with the pool invariant D representing the pool's virtual total value. The BPT price directly depends on this invariant according to the formula BPT Price ≈ D / totalSupply.
The root cause stemmed from a rounding inconsistency between scaling operations in the BaseGeneralPool contract. When processing rate-based tokens like wstETH and osETH, the upscaling operation used unidirectional rounding down via FixedPoint.mulDown, while the downscaling operation used bidirectional rounding depending on context. This violated the fundamental principle that rounding should always favor the protocol.
In the _swapGivenOut() function, when a user specifies the exact amount they wish to receive, the pool incorrectly rounds down the output amount during upscaling:
function _swapGivenOut(
SwapRequest memory swapRequest,
uint256[] memory balances,
uint256 indexIn,
uint256 indexOut,
uint256[] memory scalingFactors
) internal virtual returns (uint256) {
_upscaleArray(balances, scalingFactors);
swapRequest.amount = _upscale(swapRequest.amount, scalingFactors[indexOut]); // rounds down
uint256 amountIn = _onSwapGivenOut(swapRequest, balances, indexIn, indexOut);
amountIn = _downscaleUp(amountIn, scalingFactors[indexIn]);
return _addSwapFeeAmount(amountIn);
}
function _upscale(uint256 amount, uint256 scalingFactor) pure returns (uint256) {
return FixedPoint.mulDown(amount, scalingFactor); // should use mulUp
}This rounding down causes the computed amountIn to underestimate the actual required input. During the invariant calculation via Newton's method, when working with small balances near rounding boundaries, the pool believes the user takes less than they actually do, computing a smaller required input. Each iteration accumulates precision loss, systematically lowering the invariant D and deflating the BPT value.
The attacker executed a sophisticated two-stage approach. First, they deployed auxiliary contracts for off-chain parameter calculation and on-chain simulation. The coordinator contract collected pool state including scaling factors, amplification parameter, BPT rate, and swap fees, then computed a critical value trickAmt based on the target token's scaling factor. Through binary search using revert signals from the math helper contract, the attacker identified optimal parameters that maximize rounding bias without triggering transaction failures.
The batch swap phase consisted of three steps. In step one, the attacker swapped BPT for underlying assets to position one token at a rounding boundary. The wstETH/rETH/cbETH pool showed this pattern clearly, with cbETH balance reduced to just 9 wei. In step two, the attacker executed calculated swaps between underlying assets using crafted amounts that triggered precision loss. For example, swapping 8 wei of cbETH caused the upscaled value to round down from 8.918 to 8, leading to an underestimated required input and thus a smaller invariant. Through multiple iterations, the attacker accumulated this loss systematically. In step three, the attacker reverse-swapped underlying assets back to BPT at the manipulated rate, profiting from the deflated BPT price.
The osETH/WETH pool demonstrated the severity of this manipulation. The pool invariant decreased from approximately 12.17 million to just 240,115, a 98% reduction, while the virtual price dropped from 1.027e18 to 20.189e18. The attacker withdrew 4,623 WETH and 6,851 osETH from pools that should have prevented such extraction.
The vulnerability was particularly exploitable due to Balancer's batch swap feature with deferred settlement, which allowed maintaining manipulated balances within a single transaction and bypassing minimum pool supply limits. The attacker accumulated proceeds as internal Vault balances in the manipulation transaction, then withdrew them in a separate transaction to minimize detection risk.
The exploit affected Balancer V2 Composable Stable Pools across nine blockchain networks including Ethereum, Arbitrum, Base, Optimism, Polygon, Avalanche, Gnosis, Fantom, and others. Total losses exceeded $128 million, with major pools drained including osETH/WETH, wstETH/WETH, and wstETH/rETH/cbETH pools. The protocol was unable to pause operations due to architectural constraints, which exacerbated the impact and enabled numerous copycat attacks following the initial exploit.
On Ethereum mainnet alone, two major pools showed dramatic rate manipulations. The osETH/WETH-BPT pool rate shifted from approximately 1.027e18 to 20.189e18, while the wstETH/WETH-BPT pool rate moved from 1.051e18 to 3.887e18. These changes aligned with the underestimation of the invariant during the manipulation window.
Balancer V3 and other Balancer V2 pool types including Weighted Pools remained unaffected. The vulnerability was specific to Composable Stable Pools due to their rate scaling mechanism for yield-bearing tokens. Balancer V3's redesigned architecture with 18-decimal precision for all pool operations and explicit rounding direction enforcement prevented this class of vulnerability.
Balancer issued alerts immediately after the attack began on November 3, 2025. By November 6, Balancer published an official preliminary report confirming the root cause as a rounding direction issue in the stable pool math.
Certora, which had performed formal verification work on Balancer V2 in 2022, independently confirmed that Balancer V3 was not vulnerable to this attack. The team verified that V3's explicit rounding direction enforcement and simplified pool architecture with ERC4626 buffers replacing composable pools addressed the underlying issue.
The incident revealed that this vulnerability had been present in the codebase since 2021. Trail of Bits had identified similar rounding issues during their April 2021 audit and specifically flagged concerns about Linear Pools consuming the Stable Math library in their October 2021 review. However, the finding was marked as undetermined severity at the time, as the threat landscape for arithmetic exploits was significantly different then.
In DeFi, a small delay costs millions.
Get the threat intelligence to rely on.