The Heart of Balancer V2: Composable Stable Pools

In the fast-paced, ever-evolving world of decentralized finance (DeFi), innovation moves at light speed. Yet, as the industry matures, so does the sophistication of the challenges it faces. Every now and then, an incident shakes the foundation, reminding us of the intricate balance between pioneering technology and robust security. Such was the case with the Balancer V2 exploit in late 2025, an incident that saw over $120 million drained from various liquidity pools. It wasn’t a flaw in the underlying blockchain, nor a simple phishing scam. Instead, it was a subtle smart contract rounding error, exacerbated by specific protocol mechanics, that led to a monumental loss.
This wasn’t just another hack; it was a masterclass in exploiting granular precision, a stark reminder that in smart contracts, every decimal place counts. Let’s peel back the layers and understand how a seemingly minor mathematical quirk spiraled into a nine-figure vulnerability.
The Heart of Balancer V2: Composable Stable Pools
To truly grasp the exploit, we first need to understand the environment it thrived in: Balancer V2’s Composable Stable Pools. Inspired by Curve’s StableSwap model, these pools are ingenious designs for trading assets that typically maintain near-identical values – think USDC and DAI, or staked Ethereum (stETH) and ETH. Their “composability” comes from their LP token (BPT), a standard ERC-20 that can be reused across the ecosystem, creating seamless liquidity flows.
At the core of these pools is an invariant, represented as ‘D’, which strives to remain constant during swaps. When you swap tokens, the pool’s internal math adjusts balances to preserve this ‘D’. This process involves solving a quadratic equation to determine the new balance of the token you’re solving for, given the fixed ‘D’ and the other tokens’ aggregated balances. Balancer’s sophisticated math is designed to minimize slippage, especially when dealing with closely pegged assets.
The Mechanics of Swaps: EXACT_IN vs. EXACT_OUT
Swaps in Balancer pools come in two main flavors: `EXACT_IN` (or `GIVEN_IN`), where you specify how much of a token you’re putting in and the system calculates how much you get out, and `EXACT_OUT` (or `GIVEN_OUT`), where you specify how much you want to receive, and the system calculates how much input is required. The exploit we’re discussing primarily leveraged the `EXACT_OUT` path, a path that, as we’ll see, proved numerically sensitive.
Crucially, all internal calculations within Balancer use 18-decimal fixed-point arithmetic, with results typically rounded down for safety. This rounding down, or “floor rounding,” is a common practice in smart contracts to prevent draining pools by always giving the pool a slight advantage. However, as the Balancer incident showed, “safety” can sometimes be a double-edged sword if not perfectly implemented across all scenarios.
The $120 Million Question: Unpacking the Rounding Error
The core of the Balancer V2 exploit lay within a seemingly innocuous function called `_upscale`, particularly when combined with an overridden `_scalingFactors` function in Composable Stable Pools. Let’s break it down:
Historically, the `_scalingFactors` function primarily normalized token balances to 18 decimals. For example, if you had a 6-decimal stablecoin like USDC, it would be “upscaled” internally to an 18-decimal representation. The `_upscale` function, in turn, would multiply an amount by its scaling factor and then divide by `1e18` (effectively handling the decimal conversion).
The critical change, introduced in 2021 for Composable Stable Pools, was that `_scalingFactors` was overridden to *also* incorporate the token exchange rate. This meant the scaling factor wasn’t just a decimal normalizer; it now reflected the value ratio between tokens. This was a functional necessity, but it brought with it a significant, albeit initially overlooked, side effect.
The `_upscale` function always used `FixedPoint.mulDown`, which means it consistently rounded down any fractional results. While harmless with unitary scaling factors (like `1e12` for a 6-decimal token), it became problematic when the scaling factor itself represented a non-unitary exchange rate (e.g., 1.058…).
When Small Errors Lead to Massive Losses
Imagine this: you’re trying to upscale an amount, say 17, using a scaling factor of 1.058132408689971699. Mathematically, 17 * 1.058… = 17.988…. But because `_upscale` uses `mulDown` (floor rounding) and Solidity truncates decimals, the result becomes 17. The intended increase of almost 0.98 is completely lost, resulting in a 0% net change! If the amount was larger, say 50, the result might be 52, losing only 0.90 but representing a much smaller percentage error.
The exploit’s ingenuity lay in maximizing this truncation. The attacker meticulously crafted transactions to ensure `amount * scalingFactor % 1e18` was as large as possible, making the floor rounding error disproportionately significant. This “artificially deflated” the amount owed by the attacker to the pool, making each swap cheaper. The impact of this error was most pronounced in low-liquidity scenarios, where small nominal amounts represented a larger portion of the pool’s total value.
The attacker leveraged Balancer’s `batchSwap` functionality, which allows multiple swaps within a single transaction with “deferred settlement.” This meant they could effectively “borrow” BPT tokens transiently to manipulate pool balances to critically low levels (e.g., under 100k units of a token), where the rounding error would be most impactful. They then executed a series of “triplet” swaps:
- **Prime:** Manipulate the pool into a state where the next swap would maximize truncation.
- **Exploit:** Perform the `EXACT_OUT` swap that realizes the rounding loss, effectively paying less than required.
- **Reset:** Rebalance the pool to repeat the cycle, accumulating value with each iteration.
Over hundreds of iterations, these minute, accumulated “discounts” on their swaps drained over $120 million. It’s a classic example of how a tiny, overlooked detail can become a gaping hole when combined with specific attack vectors and protocol mechanics.
Beyond the Code: Lessons for Web3 Security
The Balancer V2 exploit wasn’t just a technical lesson; it sparked a broader conversation about smart contract security audits. While some might question their value, the reality is that security firms prevent hundreds of exploits annually, protecting billions in value. The true takeaway isn’t that audits are ineffective, but rather that the industry’s audit practices need to evolve alongside increasingly complex protocols.
The Case for Continuous Security Partnerships
The Balancer incident highlights a crucial point: many audits are often scoped as isolated, ad-hoc reviews. Codebases, however, are living entities, constantly evolving. When audit firms engage in continuous security partnerships, they develop deep familiarity with a protocol’s architecture, engineering processes, and design philosophies. This longitudinal understanding significantly mitigates risks compared to one-off, limited-scope reviews.
Even though Balancer V2 was reviewed by multiple audit firms, each focused on different scopes. While diverse eyes are beneficial, maintaining at least one long-term security partner provides the vital continuous understanding necessary to catch nuanced issues like the rounding error that emerged from successive code changes and overrides.
Elevating Industry Standards Together
Leading security firms, like OpenZeppelin, are actively working to establish and elevate security standards across the industry. This includes contributing to bodies like the Blockchain Security Standards Council and collaborating with regulators to promote best practices. The goal is to move towards a future where continuous security is the standard, where auditors are deeply integrated into the development lifecycle, and where formal accreditation ensures the highest quality of security assessments.
The Path Forward: A Resilient Web3 Ecosystem
Every security incident, no matter how devastating, offers invaluable insights. The Balancer V2 exploit underscores that as DeFi protocols grow in value and complexity, the investment in security must scale proportionally. It’s not enough to audit once; it requires an ongoing commitment from protocol teams and a shift towards more comprehensive, continuous engagement from security firms.
This journey towards a more secure Web3 ecosystem is a collaborative one. By learning from these challenges, fostering deeper partnerships, and collectively raising the bar for security standards, we can build a decentralized future that is not only innovative but also robust and trustworthy for all.




