#3 Balancer V2 Incident: A Rounding Inconsistency Breaks the Invariant and Propagates Across Chains
On November 3, 2025, Balancer V2's Composable Stable Pools, along with several forked projects across multiple chains, suffered a coordinated exploit that resulted in total losses of over $125 million, with approximately $45 million reportedly recovered. The root cause was price manipulation enabled by precision loss in the invariant calculation, stemming from inconsistent rounding between the upscaling and downscaling operations, which ultimately distorted the BPT (Balancer Pool Token) pricing logic.
This incident stands out as one of the top ten security incidents of 2025 not only because of the scale of losses, but also due to the subtlety of the underlying bug. Moreover, the exploit quickly propagated across multiple chains and affected both Balancer and its forks, highlighting how shared codebases and composable DeFi infrastructure can significantly amplify systemic risk.
We have published an extensive report, "In-Depth Analysis: The Balancer V2 Exploit" [1], which provides a detailed technical breakdown. Below, we present a concise illustration of the incident.
Background
Balancer V2's Composable Stable Pool
The affected component in this attack was the Composable Stable Pool [2] of the Balancer V2 protocol. These pools are designed for assets that are expected to maintain near 1:1 parity (or trade at a known exchange rate) and allow large swaps with minimal price impact, thereby significantly improving capital efficiency between like-kind or correlated assets. Each pool has its own Balancer Pool Token (BPT), which represents the liquidity provider's share of the pool, along with the corresponding underlying assets.
-
This pool adopts Stable Math (based on Curve's StableSwap model), where the invariant D represents the pool's virtual total value.
-
The BPT price can be approximated as:
From the above formula, if D can be made smaller on paper (even without any actual loss of funds), the BPT price will appear cheaper.
`batchSwap()` and `onSwap()`
Balancer V2 provides the batchSwap() function, which enables multi-hop swaps within the Vault [3]. There are two swap types determined by a parameter passed to this function:
GIVEN_IN("Given In"): the caller specifies the exact amount of the input token, and the pool calculates the corresponding output amount.GIVEN_OUT("Given Out"): the caller specifies the desired output amount, and the pool computes the required input amount.
Typically, a batchSwap() consists of multiple token-to-token swaps executed via the onSwap() function. This process inevitably involves amount calculations tied to the invariant D [1].
Scaling and Rounding
To normalize the calculations across different token balances, Balancer performs the following two operations:
- Upscaling: Scale balances and amounts up to a unified internal precision before performing calculations.
- Downscaling: Convert the results back to their native precision, applying directional rounding (for example, input amounts are usually rounded up to ensure the pool does not undercharge, while output amounts are often rounded down).
Upscaling and downscaling are theoretically paired operations: multiplication and division, respectively. However, an inconsistency exists in the implementation of these two operations. Specifically, the downscaling operation has two variants or directions: divUp and divDown. In contrast, the upscaling operation has only one direction, namely mulDown.
Vulnerability Analysis
The underlying issue arises from the rounding-down operation performed during upscaling in the BaseGeneralPool._swapGivenOut() function. In particular, _swapGivenOut() incorrectly rounds down swapRequest.amount through the _upscale() function. The resulting rounded value is subsequently used as amountOut when calculating amountIn via _onSwapGivenOut(). This behavior contradicts the standard practice that rounding should be applied in a manner that benefits the protocol.
Therefore, for a given pool (wstETH/rETH/cbETH), the computed amountIn underestimates the actual required input. This allows a user to exchange a smaller quantity of one underlying asset (e.g., wstETH) for another (e.g., cbETH), thereby decreasing the invariant D as a result of reduced effective liquidity. Consequently, the price of the corresponding BPT (wstETH/rETH/cbETH) becomes deflated, since BPT price = D / totalSupply.
Attack Analysis
The attacker executed a two-stage attack, likely to minimize detection risk:
- In the first stage, the core exploit was performed within a single transaction, yielding no immediate profit.
- In the second stage, the attacker realized profits by withdrawing assets in a separate transaction.
The first stage can be further divided into two phases: parameter calculation and batch swap. Below, we illustrate these phases using an example attack transaction (TX) on Arbitrum.
The Parameter Calculation Phase
In this phase, the attacker combined off-chain calculations with on-chain simulations to precisely tune each hop's parameters in the next (batch swap) phase, based on the current state of the Composable Stable Pool (including scaling factors, amplification coefficient, BPT rate, swap fees, and other parameters). The attacker also deployed an auxiliary contract to assist with these calculations, which may have been intended to reduce exposure to front-running. Please refer to [1] for more details.
The Batch Swap Phase
Then, the batchSwap() operation can be broken down into three steps:
Step 1: The attacker swaps BPT (wstETH/rETH/cbETH) for underlying assets to precisely adjust the balance of one token (cbETH) to the edge of a rounding boundary (amount = 9). This sets up the conditions for precision loss in the next step.
Step 2: The attacker then swaps between another underlying (wstETH) and cbETH using a crafted amount (= 8). Due to rounding down when scaling token amounts, the computed Δx becomes slightly smaller (8.918 to 8), leading to an underestimated Δy and thus a smaller invariant (D from Curve’s StableSwap model). Since BPT price = D / totalSupply, the BPT price becomes artificially deflated.
Step 3: The attacker reverse-swaps the underlying assets back into BPT, restoring balance while profiting from the deflated BPT price.
Summary
This incident involved a coordinated series of exploit transactions targeting Balancer V2's Composable Stable Pools and multiple forked deployments across different chains, resulting in substantial losses. After the first exploit, copycat transactions rapidly emerged, showing how quickly an attack pattern can propagate once exposed.
Key lessons:
- Rounding & Precision Handling: Every scaling and precision operation on token amounts should round in the direction that benefits the protocol. A single inconsistency between
_upscale()(round-down only) and the downscaling operations (directional rounding) was sufficient to create an exploitable pricing distortion. - Arms Race in Security: The attacker split manipulation and profit extraction across separate transactions to evade detection. Detection systems should correlate related transactions, not just flag individual ones.
- Operational Security: Once the exploit pattern was public, copycats replicated it across chains within minutes. Protocols sharing a codebase need coordinated monitoring and rapid cross-chain pause capabilities.
Reference
-
https://blocksec.com/blog/in-depth-analysis-the-balancer-v2-exploit
-
https://docs-v2.balancer.fi/concepts/pools/composable-stable.html
-
https://docs-v2.balancer.fi/reference/swaps/batch-swaps.html
About BlockSec
BlockSec is a full-stack blockchain security and crypto compliance provider. We build products and services that help customers to perform code audit (including smart contracts, blockchain and wallets), intercept attacks in real time, analyze incidents, trace illicit funds, and meet AML/CFT obligations, across the full lifecycle of protocols and platforms.
BlockSec has published multiple blockchain security papers in prestigious conferences, reported several zero-day attacks of DeFi applications, blocked multiple hacks to rescue more than 20 million dollars, and secured billions of cryptocurrencies.
-
Official website: https://blocksec.com/
-
Official Twitter account: https://twitter.com/BlockSecTeam



