By using this site, you agree to the Privacy Policy and Terms of Use.
Accept
World of SoftwareWorld of SoftwareWorld of Software
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Search
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
Reading: Balancer V2 Exploit Explained: Inside the Smart Contract Rounding Error That Cost $120M | HackerNoon
Share
Sign In
Notification Show More
Font ResizerAa
World of SoftwareWorld of Software
Font ResizerAa
  • Software
  • Mobile
  • Computing
  • Gadget
  • Gaming
  • Videos
Search
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Have an existing account? Sign In
Follow US
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
World of Software > Computing > Balancer V2 Exploit Explained: Inside the Smart Contract Rounding Error That Cost $120M | HackerNoon
Computing

Balancer V2 Exploit Explained: Inside the Smart Contract Rounding Error That Cost $120M | HackerNoon

News Room
Last updated: 2025/11/13 at 2:54 PM
News Room Published 13 November 2025
Share
Balancer V2 Exploit Explained: Inside the Smart Contract Rounding Error That Cost 0M | HackerNoon
SHARE

Technical Background of Balancer V2 Composable Stable Pools

Pool Structure and How It Works :

Composable Stable Pools are built on math inspired by Curve’s StableSwap model.

What is a Composable Stable Pool?

These pools are designed to let assets with nearly identical values, like USDC and DAI, or stETH and ETH, trade with minimal slippage.

**What makes them “composable”?
The pool’s LP token (BPT) is a standard ERC-20 token that can be reused across the ecosystem, for example, in other pools or as collateral. This allows liquidity to be seamlessly composed throughout the system.

**How it works
A Composable Stable Pool can hold multiple tokens, and its invariant DDD is calculated using the following polynomial equation:

In Balancer’s stable pools (inspired by Curve), swaps are computed by keeping an invariant (D) as constant as possible. When a swap happens, token balances move to a new state that preserves this invariant. The equation above is the quadratic you solve to get the new balance (x) of the token you’re solving for, given that (D) stays fixed.

Terms

  • (x): the new (post-swap) balance of the token you’re solving for.
  • (S): the sum of the other tokens’ balances (excluding (x)).
  • (P): the product of the other tokens’ balances (excluding (x)).
  • (D): the pool invariant (targeted to remain constant across the swap).
  • (A): the amplification parameter (flattens the curve → lower slippage for like-valued assets).
  • (n): the number of tokens in the pool.

Intuition: we compress “all the other tokens” into two aggregates, (S) and (P). Enforcing “(D) stays constant” yields a quadratic in (x).

Why quadratic?

After isolating the unknown token’s new balance and folding the rest into (S) and (P), the invariant condition reduces to a second-degree polynomial in (x). Hence, you solve a quadratic.

Solving it

Which root? Choose the root that is positive and economically valid (within feasible balance ranges). The other root is typically negative or nonsensical.

A Simple Example

Setup (2 tokens, large A → very low slippage)

  • Internal (upscaled) starting balances: token0 = 1,000,000; token1 = 1,000,000
  • Number of tokens: n = 2
  • Amplification: A = 1000
  • With large A, D ≈ 2,000,000

Action

  • You add 10,000 units of token0 (EXACT_IN).

Quadratic to solve

x^2 + (S - D/(A*n^n) - D)*x - D^(n+1)/(A*n^(2n)*P) = 0

Where:

S = 1,010,000, P = 1,010,000, and x is token1’s post-swap balance.

Solution

x ≈ 990,999.546

Amount out

amountOut = 1,000,000 - 990,999.546 ≈ 9,000.454

Interpretation

  • You put in 10,000 of token0 and receive ~9,000.45 of token1 → low slippage with large A.
  • All math uses 18-decimal internal units; implementations round down for safety.

Where it’s used (high level)

  • EXACT_IN: you increase the input token; solve for the counter-token’s new balance (x); the difference from its old balance is the amount out.
  • EXACT_OUT: you target a specific received amount; solve for the needed (x) (and thus the input required). This path is more numerically sensitive (root-finding + rounding).

Practical notes

  • All balances are handled internally at 18 decimals (scaled); calculations typically round down to be pool-safe.
  • A larger (A) makes the curve more constant-sum-like (tiny slippage for stable pairs); a smaller (A) is closer to constant-product behavior.

:::tip
TL;DR: This equation is the workhorse quadratic that, under a fixed invariant (D), gives you the new token balance (x) after a swap. The coefficients encode “the rest of the pool” via (S) and (P), while (A) and (n) set the shape/flatness of the stable curve.

:::

The pool enforces a balance relationship among token balances, captured by an invariant D.

When you swap, one balance goes up, another goes down, and the pool finds a new balance that keeps D as constant as possible. That’s how price emerges.

The pool uses scaling factors to normalize tokens to 18 decimals. Upscale operation adjusts balances to internal precision and uses rounding down.

Swap Types

  • EXACTIN (GIVENIN): Fixed input amount, calculate output.
  • EXACTOUT (GIVENOUT): Fixed output amount, calculate input. Exploit focuses here.

Vault and BatchSwapBalancer Vault manages all operations. BatchSwap combines multiple swaps in one transaction and uses “deferred settlement” like flashloans. BPTs act like normal tokens, bypassing min pool supply limit to drop liquidity very low.

1. Exploit Analysis

1.1 The Attack

On November 3, 2025, at around 7:40 AM UTC, specific Balancer v2 stable pools were targeted with a sophisticated attack that resulted in a cumulative loss of $120+ million across Balancer and its forks.

The attack leveraged several circumstances:

  • The majority of the targeted pools were instances of the ComposableStablePool contract. A special pool designed for assets that are either expected to consistently swap at near parity or at a known exchange rate.
  • The batchSwap functionality of the Balancer Vault contract allows for transient swaps to happen before the need to settle the outstanding deltas.
  • The attack was profitable in low liquidity scenarios. The attacker had to prepare large swaps so that the pools’ balances could be brought to low liquidity of one or the other token. The most used way of achieving this was to swap LP tokens for pool tokens, leaving the pool in a low liquidity state.
  • All of the above are just necessary but not conclusive circumstances, with the only necessary circumstance for this to happen being the root cause of the issue, a rounding behaviour in the _upscale function.

The attack vector was introduced first on July 16, 2021, when MetaStablePool overrode the _scalingFactors function at commit 059284e. The same change was later applied on September 1, 2021, for linear pools at commit 4e9e70a and on September 20, 2021, at commit f450760 for StablePhantomPool, later renamed ComposableStablePool. OpenZeppelin’s prior audits did not cover any of those pools presenting this attack vector.

In the issue description below, we explain why the introduction of this override was at the core of the attack.

1.2 The Issue

The batchSwap function of the Vault contract is the entry point for this attack. The attacker crafted a call to it, targeting instances of ComposableStablePool. The relevant logic in the Vault contract that brings to the ComposableStablePool interaction is defined by this flow:

batchSwaps → internally calls _swapWithPools → which internally calls a pool for each swap at _swapWithPool → _processGeneralPoolSwapRequest→ ultimately calls onSwap on the ComposableStablePool.

Once landed in ComposableStablePool, the execution continues within the onSwap hook, where two things happen:

  • The call to _scalingFactors function.
  • The handover to _swapGivenOut as defined in the example transaction provided above. Notice that the attacker can have decided to take the _swapGivenIn pattern, but the provided inputs in the attack transaction clearly show the choice. This function will call the _upscale function.

Let’s take a look at these two functions.

_scalingFactors

function _scalingFactors() internal view virtual override returns(uint256[] memory) {
    uint256 totalTokens = _getTotalTokens();
    uint256[] memory scalingFactors = new uint256[](totalTokens);

    for (uint256 i = 0; i < totalTokens; ++i) {
        scalingFactors[i] = _getScalingFactor(i).mulDown(_getTokenRate(i));
    }

    return scalingFactors;
}

This function does two things:

  • It scales any amount to a fixed number of decimals as suggested by the _getScalingFactor function and what it returns.
  • It factors in the token exchange rate by multiplying it by the scaling factor and dividing by 1e18.

_upscale

function _upscale(uint256 amount, uint256 scalingFactor) pure returns (uint256) {
    /* Upscale rounding wouldn't necessarily always go in the same direction: 
in a   swap for example the balance of token in should be 
rounded up, and that of token out rounded down. This
is the only place where we round in the same direction for all amounts, 
as the impact of this rounding is expected to be minimal. */

    return FixedPoint.mulDown(amount, scalingFactor);
}

This just performs a multiplication of the amount by its scaling factor, dividing by 1e18 at the end.

The issue lies in two facts:

  • These functions always round down (mulDown) independently from the direction of the swap.
  • If amounts are orders of magnitude less than scalingFactors ones, the precision loss becomes non-negligible. In the attacker transaction, a typical value of amount and scaling factor is: amount: “17”scalingFactor: “1058132408689971699”

If we calculate amount * scalingFactor / 1e18, we get 17.98, but since Solidity truncates decimals, this becomes 17, resulting in a 0% net change in the _upscale function.

Now, consider an amount of 17,000000000000000000 (a token with 18 decimals). The result is 17.988250950000000000, representing a 5.8% increase—which correctly reflects the rate.

Likewise, for a token with 6 decimals (as most stablecoins have), using an amount of 17,000000 yields 17.988250, which still corresponds to roughly the same 5.8% positive change.

Truncation is maximized by making amount*scalingFactor mod 1e18 as large as possible, so the product loses the maximum precision when divided by 1e18.

For example, in the table below, amount=17 and amount=50 both produce an absolute rounding loss of about 0.90. However, the error as a percentage of the amount differs significantly, and the percentage of increase lost diverges even more, since the same absolute loss is much larger relative to 17 than to 50.

Examples

Table 1. Examples of floor rounding error when scaling by a factor of 1.058132408689971699

| amount | upscale | error | %error | %increase lost |
|—-|—-|—-|—-|—-|
| 17 | 17 | 0.98 | 5.76% | 100% |
| 50 | 52 | 0.90 | 1.80% | 17% |
| … | … | … | … | … |

The _swapGivenOut function will later use these rounded values to calculate the amount owed by the attacker to the pool after the swap. This value is artificially deflated, making the swap cheaper. The inner mechanics of the _swapGivenOut are complex, and they also justify why this can be achieved in pools where low liquidity states can be obtained at will. This is due to an invariant check that ensures convergence to some degree. In high liquidity situations, big shifts in the balances would have failed due to the same invariant protection.

Ultimately, with a high number of iterations, the deltas after the batchSwap are inflated, crediting the attacker with the majority of the funds in the pool.

Additionally, there’s a pertinent observation to be made about the _upscale in-line docstrings:

/* Upscale rounding wouldn't necessarily always go in the same direction: in a swap for example the balance of token in should be rounded up, and that of token out rounded down. This is the only place where we round in the same direction for all amounts, as the impact of this rounding is expected to be minimal. */

A prior version of this comment had an additional note:

/* …as the impact of this rounding is expected to be minimal (and there's no rounding error unless `_scalingFactor()` is overriden). */

This is important because the StablePhantomPool, introduced on September 20, 2021 and later renamed to ComposableStablePool, implements exactly the _scalingFactor override discussed above.

In earlier versions of StablePool, the _scalingFactor function only accounted for differences in token decimals. In the newer implementation, however, it also incorporates the exchange rate, marking a fundamental change.

As noted in the _upscale function’s comments, the code evolved from using unitary scaling factors (e.g., 1e12) to non-unitary exchange rates. This shift, while functionally necessary, introduces potential rounding errors—and with them, a new attack vector.

While enabling the rounding error was the root cause, exploiting it required leveraging additional protocol mechanics and specific attack steps.

Balancer’s batchSwap allows transient internal balances that are only net-settled at the end of the batch. Thanks to this, the attacker effectively “borrowed” BPT within a batch to manipulate the pool without needing to end the transaction holding BPT.

A BPT (Balancer Pool Token) is an ERC-20 that represents a pro-rata share of a Balancer pool. In some pool designs, including the targeted ComposableStablePools, the BPT can also appear as a pool asset and be tradable/swappable.

The first phase pushed pool token balances (e.g., WETH / osETH) to very low levels (≲ 100k) by repeatedly swapping BPT to token₁ and BPT to token₂.

With balances small, fixed-point rounding becomes dominant. The objective is to maximize the discarded fractional part in floor arithmetic, i.e., maximize amount*scalingFactor mod 1e18 so that the product loses as much precision as possible when divided by 1e18. At sufficiently low balances, the entire intended increment can be truncated away, this is where the maximum is attained and what the hacker looks for.

The exploit runs in repeating triplets of swaps:

  • Prime: move the pool to a state where truncation will occur on the next operation. In the screenshot below, a swap of WETH to osETH
  • Exploit: perform the swap that realizes the rounding loss. In the screenshot, a swap of 17 WETH for osETH.
  • Reset: restore balances so the triplet can be replayed. In the screenshot is the swap of osETH to WETH.

As shown in the trace below, the critical swap often uses an amount=17 against a vault balance of 18. You can see the repeated 17 amounts in the second swap of each triplet.

2. Advancing Security Best Practices: Lessons for the Industry

This incident has sparked debate about the effectiveness of smart contract security audits, with some commentators questioning whether they provide value. While this sentiment may be understandable in the moment of frustration, it fails to acknowledge why security audits have become an industry best practice and the tremendous impact the security audit practice has had on mitigating risk and protecting users over the past decade.

Security firms collectively prevent hundreds of potential exploits each year. OpenZeppelin alone has identified over 700 critical and high-severity vulnerabilities before they reached production across all our audits. These prevented disasters don’t make headlines, but they represent billions in protected value and countless users saved from losses. High-quality security audits also help development teams improve their security posture across the development lifecycle, resulting in an exponential reduction in the likelihood of further errors being introduced into blockchain applications.

The real lesson isn’t that audits are ineffective — it’s that the industry’s audit practices haven’t yet caught up with how fast complex protocols that secure significant value evolve. Because audits are often scoped as isolated reviews rather than continuous engagements, context can be lost as codebases evolve. Changing this requires a collective shift: protocol teams investing in ongoing security, and auditors building frameworks that support it.

2.1 Establishing Continuous Security as the Industry Standard

The Balancer v2 exploit illustrates why we’ve been advocating for a fundamental shift in how the industry approaches security. Since pioneering the practice of smart contract auditing in 2016, we’ve seen how the most successful security outcomes come from ongoing security partnerships that cover entire protocol codebases and all of their changes over time, rather than ad hoc code reviews of major upgrades, which are often limited in scope to only those changes.

When security researchers work with protocols continuously, they develop deep familiarity with the protocol’s architecture, engineering processes, and why specific design decisions were made. This deep understanding significantly lowers risks when compared to ad hoc audits.

Although the Balancer v2 codebase was reviewed by four independent audit firms, each firm was engaged to focus on different scopes of the protocol. Engaging multiple auditors helps reduce the chance of missing vulnerabilities, but maintaining at least one long-term security partner provides a deeper, continuous understanding of how the codebase evolves and how new changes interact with existing logic.

2.2 Building Better Security Frameworks

Through our decade of experience, OpenZeppelin has consistently worked to establish and elevate security standards across the industry. OpenZeppelin Contracts have become the de facto standard for building secure smart contracts, currently powering over $30 trillion in total value transferred across the ecosystem. Just as these contracts set the foundation for secure development, we have been actively working on efforts to establish more robust standards for security assessment and continuous protocol protection.

In addition to long-term relationships with our clients, we’re actively working to shape these standards at both the industry and regulatory levels. For example, OpenZeppelin contributes to industry standard-setting bodies, like the Blockchain Security Standards Council, the Enterprise Ethereum Alliance, and the International Standards Organization (ISO), to ensure that the security best practices our team and community have helped pioneer are available to the whole industry.

We also engage with regulators and policymakers globally to ensure blockchain security best practices are promoted in relevant regulatory regimes. In particular, OpenZeppelin has engaged with the U.S. Department of Treasury, the U.S. Securities and Exchange Commission, the UK Financial Conduct Authority, and the French ACPR and AMF regarding the benefits of conducting comprehensive security audits on a regular basis. In addition, we have explored the potential to create a dedicated self-regulatory organization similar to auditors in other fields that we believe could help formalize security auditing standards and methodologies for blockchain technology, set quality and ethics requirements for auditors, and manage accreditation to certify qualified auditors, which could improve security outcomes and increase consumer confidence.

2.3 Strengthening the Blockchain Security Ecosystem Together

Every security incident provides valuable insights that drive improvement across our industry. The Balancer v2 exploit reinforces that as protocols become more valuable and sophisticated, it becomes critical for protocol teams to invest in ongoing security, as well as for auditing firms to build frameworks that support it. Together, we can create security practices that are as innovative and robust as the technology they protect.

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Twitter Email Print
Share
What do you think?
Love0
Sad0
Happy0
Sleepy0
Angry0
Dead0
Wink0
Previous Article Believe It Or Not, Tesla Now Wants To Add CarPlay To Its Vehicles – BGR Believe It Or Not, Tesla Now Wants To Add CarPlay To Its Vehicles – BGR
Next Article Best iPad Pro deal: Save  on the M5 iPad Pro at Amazon Best iPad Pro deal: Save $64 on the M5 iPad Pro at Amazon
Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Stay Connected

248.1k Like
69.1k Follow
134k Pin
54.3k Follow

Latest News

Linux Has Another Maintainer Now For Its DEC Alpha Port
Linux Has Another Maintainer Now For Its DEC Alpha Port
Computing
Early Black Friday Kindle deals 2025: Books, e-readers, accessories
Early Black Friday Kindle deals 2025: Books, e-readers, accessories
News
Apple to restore TikTok on US App Store after Attorney General letter · TechNode
Apple to restore TikTok on US App Store after Attorney General letter · TechNode
Computing
New Report Claims iPhone 16e Isn’t Selling Well, But Does It Matter? – BGR
New Report Claims iPhone 16e Isn’t Selling Well, But Does It Matter? – BGR
News

You Might also Like

Linux Has Another Maintainer Now For Its DEC Alpha Port
Computing

Linux Has Another Maintainer Now For Its DEC Alpha Port

1 Min Read
Apple to restore TikTok on US App Store after Attorney General letter · TechNode
Computing

Apple to restore TikTok on US App Store after Attorney General letter · TechNode

1 Min Read
What’s It Like to Work With the  Influence Team?
Computing

What’s It Like to Work With the Influence Team?

4 Min Read
Foundation Models Are Reshaping How Developers Code Together | HackerNoon
Computing

Foundation Models Are Reshaping How Developers Code Together | HackerNoon

5 Min Read
//

World of Software is your one-stop website for the latest tech news and updates, follow us now to get the news that matters to you.

Quick Link

  • Privacy Policy
  • Terms of use
  • Advertise
  • Contact

Topics

  • Computing
  • Software
  • Press Release
  • Trending

Sign Up for Our Newsletter

Subscribe to our newsletter to get our newest articles instantly!

World of SoftwareWorld of Software
Follow US
Copyright © All Rights Reserved. World of Software.
Welcome Back!

Sign in to your account

Lost your password?