Full Report
$125M was stolen from Balancer's V2 Composable Stable Pools, alongside several forked projects of it. This article is a breakdown of the incident. Composable Stable Pools are assets that are expected to have a nearly 1 to 1 parity, allowing large swaps with minimal price impact. For instance, USDC to USDT. The pool uses stable math with Price(BPT) = (D / total supply) where D represents the pool's virtual value and BPT is Balancer Pool Token. If D becomes smaller, then the BPT price will appear cheaper. In Balancer, batchSwap() is used to perform multi-hop swaps. The user can specify the exact amount in or the actual amount out to receive. To normalize calculations across different token balances, Balancer has to perform scaling. Sometimes, this involves upscaling, while other times it is downscaling. When performing a batch swap and specifying the amount out, the function _swapGivenOut() must calculate the input amount that is required for the transfer to succeed. Upon doing this, the function upscale() rounds down to benefit the user. It is standard practice always to have rounding benefit the protocol; otherwise, incidents like this can occur. For example, let's say the pool uses 89 to calculate a fair amount in for a trade going from wstETH to cbETH. To perform a trade to get 8 cbETH, the math is (amount * 100) / 9 = ?. This means (8 * 100) / 9 = 800 / 9 = 88.888 in practice. Now, the upscale function will round to 88 instead of 89. In practice, this is catastrophic. It allows a user to exchange a smaller amount of one underlying asset (wstETH) for another (cbETH)! Over time, this decreases the invariant D, since there is now less liquidity. As a result, the corresponding BPT becomes deflated. The process for manipulating the pool works as follows: Swap BPT for the underlying asset to make one of the assets be on the edge of a rounding boundary, such as being at 9. This sets up the precision loss. Perform a swap using the crafted amount to trigger the rounding error. The delta can be set up as 8.918 to 8. Hence, this underestimates the number of tokens required for the trade from the user's perspective. This leads to the BPT price becoming deflated. Reverse swap the assets back into BPT. By restoring the assets at the deflated BPT price, the attacker gains a profit. Repeat this over and over again to get large amounts of BPT at a discounted rate. Rounding errors that have massive impacts have always been weird to me. How can a token with nine decimals that loses a single point of precision lead to catastrophic losses? In this case, it's because of the D value. The price manipulation of D is how the attackers profit. In a separate transaction, they purchase the BPT at a significant discount and sell it for a substantial profit. This could have been done in the same transaction, but they likely did it in a separate one to prevent frontrunning. The most challenging aspect of the attack was determining the optimal parameters to maximize the effect of the precision loss. The attacker performed off-chain calculations and then on-chain simulations for the hop parameters to manipulate the pool precisely for the exploit to succeed. The code received several audits by Certora and Trail of Bits. The game is hard and we can't blame anyone, though: find all bugs or else it's a challenging game to play. The vulnerability was particularly complex, as indicated by much of Twitter, which initially thought this was an access control issue. Great write-up on the nuances of this issue! Another article, written by one of the auditors Certora, that did formal verification. The primary purpose was to ensure the solvency of BPT supply, parity of assets, and minting of BPT. The properties they tested ensured that no token could be created from nothing and that user balances always reflected the actual underlying value. So, what happened? Solvency was verified at a high level, but not strong enough to detect the rounding errors. The verified properties did not constrain the relationship between individual swaps or rounding behaviour. Iterative operations, such as token round-trip swaps, could then increase in value due to the rounding bias. Two additional properties for formal verification would have caught this: round-trip swap invariant and BPT share value invariant. Security is hard! I'm not the biggest fan in formal verification - I kind of wish it had a different name. Another post provided additional insights into the aftermath of the exploit. The author claims that the attacker wasn't very experienced. First, they didn't use flashbots, which led to some frontrunners. The hacker took 20 minutes to finish the attack on the various chains as well. They even left forks on the table to be exploited. The first copy-cat took the contract code of the attacker, replaced pool information and profited heavily without my effort. They also pointed out that Balancer didn't pause any of their pools. Since they aim to be decentralized, pausing is prevented after a specific deployment window. Decentralization and speed are often enemies. Another interesting aspect was that Polymarket had a bet on "Crypto Hack Over $100M in 2025" that lagged by about 10 minutes after the attack. What a crazy world we live in.
Analysis Summary
# Incident Report: Balancer V2 Precision Loss & Price Manipulation
## Executive Summary
On November 3, 2025, Balancer V2’s Composable Stable Pools and their ecosystem forks were exploited for approximately $125 million across multiple chains. The attacker leveraged a rounding inconsistency in the protocol’s scaling logic to artificially deflate the pool's invariant ($D$), allowing them to acquire Balancer Pool Tokens (BPT) at a deep discount. The incident was exacerbated by the protocol's decentralized architecture, which prevented certain pools from being paused after their initial deployment window.
## Incident Details
- **Discovery Date:** November 3, 2025
- **Incident Date:** November 3, 2025
- **Affected Organization:** Balancer Labs & various forked projects (e.g., Beethoven X)
- **Sector:** Decentralized Finance (DeFi)
- **Geography:** Global / Distributed (Ethereum and multiple EVM chains)
## Timeline of Events
### Initial Access
- **Date/Time:** November 3, 2025
- **Vector:** Smart Contract Vulnerability (Rounding Error)
- **Details:** The attacker utilized off-chain calculations to identify optimal parameters for a `batchSwap()` that would trigger a precision loss in the `_swapGivenOut()` function.
### Lateral Movement
- **Details:** The attack was not a network breach but a cross-chain smart contract exploit. The attacker moved between multiple chains (Ethereum, Optimism, Polyton, etc.), deploying the same logic to various Balancer forks over a 20-minute window.
### Data Exfiltration/Impact
- **Details:** The attacker drained liquidity by performing a cycle of swaps: manipulating the rounding boundary of an asset, triggering the error to deflate the BPT price, and then purchasing the deflated BPT for a profit. Approximately $125M in various stablecoins and liquid staking tokens were compromised.
### Detection & Response
- **Discovery:** BlockSec issued an alert via Phalcon monitoring services shortly after the first transaction.
- **Response Actions:** Balancer acknowledged the issue via social media; however, many pools could not be paused due to decentralization safeguards (deployment window expiration).
## Attack Methodology
- **Initial Access:** Exploitation of logic flaw in `upscale()` rounding (rounding down to benefit the user instead of the protocol).
- **Persistence:** Not applicable (Atomic/Transaction-based exploit).
- **Discovery:** Off-chain simulations and on-chain testing of hop parameters to find rounding boundaries.
- **Defense Evasion:** Executed the core exploit and profit realization in separate transactions to prevent frontrunning by MEV bots.
- **Impact:** Intentional deflation of the pool invariant ($D$), making the protocol "believe" it had less value than it actually did, allowing for discounted asset acquisition.
## Impact Assessment
- **Financial:** Estimated $125M total loss across Balancer and its forks.
- **Data Breach:** None (Public blockchain data).
- **Operational:** Inability to pause decentralized pools led to "copycat" attacks following the initial exploit.
- **Reputational:** High; the vulnerability persisted despite audits by top-tier firms (Certora, Trail of Bits) and formal verification efforts.
## Indicators of Compromise
- **Behavioral indicators:**
- High-frequency `batchSwap` calls involving Composable Stable Pools.
- Swaps using unusual, highly specific decimal amounts (e.g., 8.918).
- Discrepancy between BPT supply and the underlying pool invariant $D$.
## Response Actions
- **Containment:** Emergency sub-DAOs attempted to mitigate where possible; however, "unpauseable" pools remained vulnerable.
- **Recovery:** Migrating liquidity to "v3" or patched versions of the Composable Stable Pools.
- **Eradication:** Protocol-wide analysis to identify all forks using the same vulnerable scaling library.
## Lessons Learned
- **Rounding Direction Matters:** In DeFi, rounding must *always* favor the protocol to prevent "drain by a thousand cuts."
- **Formal Verification Gaps:** Standard solvency checks were insufficient; invariants must specifically test "round-trip" swaps (swapping A to B and back to A should never result in a profit from rounding).
- **The Cost of Decentralization:** The inability to pause contracts (immutability) is a security risk during "zero-day" events, even if it is a decentralization virtue.
## Recommendations
- **Logic Correction:** Update the `upscale()` function to round up for input calculations, ensuring the protocol is never underpaid.
- **Invariant Testing:** Implement formal verification properties that constrain the relationship between individual swaps and rounding behavior.
- **Monitoring:** Utilize real-time threat detection (like BlockSec Phalcon) to identify and potentially frontrun/block malicious transactions before they are finalized.