November 6, 2025

Breaking Down the Balancer Hack

The breach underscores the importance of prioritizing security even for mature protocols with multiple audits.

On November 3, 2025, Balancer suffered an incident in which attackers manipulated internal vault logic to drain liquidity across multiple blockchains. The breach underscores the importance of prioritizing security even for mature protocols with multiple audits, and it raises urgent questions about composability, operational controls, and the evolving threat-landscape in Web3.

Here is Certora’s analysis of the incident, as well as a detailed timeline of events.

How It Happened

Balancer is a decentralized exchange protocol that enables token swaps across a wide range of liquidity pools. Each pool is defined by a specific set of tokens (e.g., WETH, wstETH) and an exchange curve governed by an invariant (e.g., constant product, weighted product, etc.). Every pool issues its own liquidity token, known as Balancer Pool Token (BPT), which represents a proportional share of the pool’s assets.

Some of Balancer’s pools include two notable mechanisms: Composability and rate upscaling. Composable pools are pools that contain their own BPT as a swappable asset. Rate upscaling adjusts token values by converting certain wrapped assets (e.g., wstETH -> stETH) into their underlying equivalents when computing the invariant.

When a user initiates a swap between two tokens in a pool and specifies the exact amount they wish to receive (EXACT_OUT), the pool computes how much of the other token must be provided to maintain the invariant (excluding fees):

function _swapGivenOut( SwapRequest memory swapRequest, uint256[] memory balances, uint256 indexIn, uint256 indexOut, uint256[] memory scalingFactors ) internal returns (uint256) { _upscaleArray(balances, scalingFactors); swapRequest.amount = _upscale(swapRequest.amount, scalingFactors[indexOut]); uint256 amountIn = _onSwapGivenOut(swapRequest, balances, indexIn, indexOut); // amountIn tokens are entering the Pool, so we round up. amountIn = _downscaleUp(amountIn, scalingFactors[indexIn]); // Fees are added after scaling happens, to reduce the complexity of the rounding direction analysis. return _addSwapFeeAmount(amountIn); }

An issue arises in the upscaling of the output amount (swapRequest.amount) used in the invariant calculation. As shown in the following snippet, this value is rounded down,

function _upscale(uint256 amount, uint256 scalingFactor) internal 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 (and there's no // rounding error unless `_scalingFactor()` is overriden). return FixedPoint.mulDown(amount, scalingFactor); }

while it should have been rounded up. Why? As the common rule of thumb dictates, rounding should always favor the protocol rather than the user. Rounding down the swapRequest.amount causes the required payment to be slightly understated: During the invariant calculation, the pool “believes” the user takes less than they actually do, so it computes a smaller amountIn. Therefore, after the swap, the invariant might be lower than it should have been. In most situations, this off-by-one discrepancy is negligible.

However, when a pool is composable, an attacker can artificially deflate the pool’s liquidity by trading a large amount of BPT tokens for the actual pool's tokens. They don’t even need prior possession of the BPT: Balancer permits an intermediary “deficit” state where the user temporarily owes BPT to the Vault. With liquidity pushed artificially low, the rounding discrepancy is amplified and no longer negligible, making the attack viable.

Once the invariant has been lowered significantly, settling the deficit of BPT becomes cheap, making this round-trip profitable for the attacker.

The Incident Timeline

UTC+02 timezone, November 3, 2025:

09:45 Attacks on Balancer pools began.

10:15 Certora noticed that Balancer’s stable pool was being hacked and immediately started to investigate.

11:00 Certora notified Balancer that we are thoroughly investigating the issue.

12:30 The root cause of the vulnerability was identified: A rounding issue in the computation of the root invariant. In addition, Certora verified that the underlying issue causing the bug did not appear in v3 (i.e., rounding was correct).

14:15 Balancer reached out to Certora, and Certora notified them of the root cause. We jointly decided that Certora should investigate whether other v2 pool types were vulnerable.

18:00 Certora shared an initial analysis of the exploit with Balancer. 

Balancer v3 Security Testing

The Balancer team collaborates closely with security partners. Over the four years that they’ve worked with Certora, they have invested heavily in rigorous security practices. Every version of the Balancer protocol has undergone multiple audits, complemented by formal verification and active bug bounty programs. Recognizing the complexity and risks of v2, the Balancer team designed v3 with a clear focus on simplicity and resilience.

Balancer v3, by design, addresses some issues that caused complications in v2. All pool operations are done with 18 decimal precision, and upscaling/downscaling is handled by the vault. Composable pools are replaced by ERC4626 buffers to simplify the functionality in the pools. This new design makes the pool operations less complex and easier to verify.

In our work with Balancer, a great deal of security focus was directed at the calculations, rounding, and precision. After identifying some calculations where rounding errors could occur, explicit rounding directions were added and enforced for all calculations in the protocol. 

In addition to manually reviewing the code, Certora has used tests and the Certora Prover to verify that all computations are done correctly. When the Prover reached cases where the calculations’ complexity prevented end-to-end formal proofs of safety, additional tests were added to ensure the safety of the protocol.

Specifically, while the v3 Vault remains computationally complex for the Certora Prover, we showed a property on simplified pool semantics that would catch bugs of this nature:


swappingBackAndForth

This rule verifies that swapping some amount from one token to another and back again does not result in a gain of funds.

While this rule does not exclude all exploit scenarios, it gives an additional assurance to users that such exploits are highly unlikely as the Prover, even in simple scenarios, considers all possible values, thus catching bugs stemming from the most minuscule liquidity amounts or uneven value distributions.

Certora aided Balancer in the initial design of v3’s pools and vault, ensuring suitable mitigations against issues such as rounding errors, low-liquidity swaps, first-depositor attacks, flash-loan manipulations, and more. 

Balancer v2 Audit Analysis

In 2022, Certora conducted a security review of Balancer v2’s Stable Pools using both the Certora Prover and manual review with the goal of proving the correctness of key economic and safety properties of the pool contracts. The work focused primarily on ensuring solvency of BPT supply, parity of assets and minted BPT, and bounded behavior of amplification factors in limited ways.

Specifically, we proved:

  • The sum of all users’ BPT balances is always less than or equal to the pool’s totalSupply.
  • It is impossible to mint new BPT tokens without a corresponding increase in the pool’s total assets.

These properties ensured that no tokens could be created from nothing and that user balances always reflected actual underlying assets held by the pool. 

Unfortunately, while the verified properties guaranteed solvency at a high level, they were not strong enough to detect the rounding error that caused the recent exploit in Balancer’s v2 Stable Pools. The verified properties did not constrain the relationship between individual swaps or rounding behavior. As a result, scenarios in which iterative operations, such as a token roundtrip swap, could systematically increase value due to rounding bias were not ruled out.

With hindsight, we have identified two additional properties that would have captured this bug class and prevented the missed rounding vulnerability:

  1. Roundtrip Swap Invariance: “Swapping a token into another and then swapping back should never yield more than the original balance.”
    This property ensures that any combination of swap operations cannot lead to an increase in net token quantity. A violation of this property reveals rounding asymmetries.
  2. BPT Share Value Invariant: “For any user operation, the share value of a single BPT must not decrease.
    This invariant guarantees that the per-BPT value (share ratio) remains constant or increases across all valid operations. It prevents subtle desynchronizations between totalAssets and totalSupply, including those that stem from rounding discrepancies or imprecise pool invariant calculations.

Key Takeaways

  • Solvency proofs are not sufficient. Aggregate conservation of balances does not imply rounding safety. Local and compositional properties such as swap reversibility and share-value monotonicity are needed, which we routinely prove on lending protocols, AMMs, and pool hooks. Specifically, we have numerous rules ensuring rounding always favors the protocol, thus protecting against exploits due to rounding errors. Notably, we perform such checks on Kamino Lending, Solana’s Stake Pool, and EulerEarn, among many others.   
  • Security testing must reason about numeric precision. Abstracting StableMath functions simplified proofs but can also mask small, compounding rounding discrepancies.
  • Roundtrip-style tests are essential for AMM verification. These tests capture subtle, emergent inconsistencies that only appear after sequential operations.

Get every blog post delivered

Certora Logo
logologo
Terms of UsePrivacy Policy