Overview
In November 2025, an attacker systematically manipulated exchange rates in Balancer V2 stable pools by exploiting accumulated rounding errors in the stable pool invariant calculation. Through a crafted sequence of 84 alternating batch swaps, the attacker biased the pool’s invariant (D) downward by maintaining balances near rounding boundaries. This caused the BPT (Balancer Pool Token) price to change significantly. One pool changed from ~1.027e18 to ~20.189e18 (approximately 20x change). The attacker then extracted value through internal balance withdrawals in a subsequent transaction. Key Insight: While the root cause was complex (involving scaled balance calculations, down-rounding operations, and specific balance configurations), the invariant violation was simple: pool rates should not change drastically within a single transaction. This demonstrates how assertions protect protocols by focusing on what should never happen rather than how it might happen.This assertion was developed by analyzing the observable effects of the exploit (drastic rate changes) rather than the root cause (rounding error accumulation). While this approach provides broad protection against rate manipulation attacks, it may not cover all edge cases or could potentially trigger false positives on legitimate operations that we have not yet discovered.Use with caution. This assertion should undergo extended testing and monitoring in non-production environments before deployment to production systems. The 3x threshold may need adjustment based on real-world transaction patterns.
The Invariant
In Balancer V2 stable pools, the rate represents the price of BPT relative to underlying assets:Dis the stable pool invariant (computed from token balances)totalSupplyis the circulating supply of BPT
- Trading fees
- Slight imbalances from large trades
- Natural price discovery
batchSwap call, which should never happen for any legitimate operation.
The Exploit
The exploit consisted of two transactions:Transaction 1: Rate Manipulation
The attacker executed a singlebatchSwap with 84 swap steps in alternating patterns. These swaps manipulated balances to specific boundaries where StableMath._calculateInvariant() accumulated maximum down-rounding bias, causing the invariant D to be underestimated and reducing BPT price.
Observable Effect:
- osETH/WETH-BPT pool rate: approximately 1.027e18 to 20.189e18 (~19.7x change)
- wstETH/WETH-BPT pool rate: approximately 1.051e18 to 3.887e18 (~3.7x change)
Transaction 2: Value Extraction
The attacker withdrew accumulated internal balances viamanageUserBalance(WITHDRAW_INTERNAL).
Root Cause (Technical Detail)
While not necessary for the assertion, understanding the root cause provides context: Balancer V2’sStableMath._calculateInvariant() uses repeated down-rounding operations on scaled balances:
- Mixed token decimals amplify precision loss
- Balances maintained near rounding boundaries maximize bias
- Repeated operations in a long
batchSwapsequence compound the error - The computed invariant D becomes progressively underestimated
The Assertion Solution
Rather than attempting to detect the specific exploit mechanism, we assert a simple invariant: Pool rates must not change drastically within a singlebatchSwap call.
Implementation
Why This Works
The assertion is root-cause agnostic:- Detects the known exploit (20x rate change far exceeds 3x threshold)
- Would detect any future rate manipulation attack, regardless of technique
- Doesn’t care about:
- How the manipulation was achieved (rounding, overflow, logic bugs)
- Whether it’s 84 swaps or 10 swaps
- The specific math used in the exploit
- Helper contracts or preprocessing techniques
Threshold Selection
We chose 3x (300%) as the threshold based on the following analysis: Normal rate change behavior:- Single swaps: Cause < 10% rate changes due to swap fees (1.05x-1.10x maximum)
- Proportional joins/exits: Maintain rate constant (1.0x - no change)
- Imbalanced joins/exits: Similar to swaps, < 10% rate change maximum
- Multiple operations in a batch: Could accumulate to ~1.5-2.5x in extreme cases with many maximum-sized operations
- The actual exploit showed 20x rate change (far exceeding any legitimate scenario)
- 3x provides a 1.2x-2x buffer above the most extreme legitimate batch swap scenarios
- Significantly below the 20x change observed in the exploit
- Rate provider updates: If a pool uses yield-bearing tokens with rate providers, legitimate rates should change gradually over time. A 3x change in a single transaction would indicate either oracle manipulation or a severe underlying issue (slashing event, de-pegging) that warrants investigation.
- Amplification changes: Limited to 2x per day by protocol, causing minimal per-transaction rate impact
Backtesting Results
We have built a custom backtesting framework that allows for running assertions against real-world historical transactions. This tool is excellent for testing assertions we develop against known exploits and it’s also what we used to verify the assertion’s accuracy. We backtested the assertion against the actual exploit transaction on Ethereum mainnet:Test Configuration
Results
Key Takeaways
- Invariants > Root Causes: By focusing on what should never happen (drastic rate changes) rather than how it might happen (rounding errors, helper contracts, specific exploit techniques), the assertion protects against both known and unknown attack vectors.
-
Simplicity is powerful: A simple rate comparison is more effective than trying to detect complex exploit patterns like:
- Multi-step swap sequences
- Rounding boundary manipulation
- Helper contracts
- Specific balance configurations
- Economic invariants are robust: The assertion enforces an economic property (pool rates should be stable) that holds true regardless of the underlying implementation details or future protocol changes.
-
One assertion, multiple protections: This same assertion would catch:
- Oracle manipulation attacks on pool rates
- Future rounding error exploits
- Integer overflow/underflow affecting rates
- Any logic bug causing rate distortion
References
- Post-Mortem Analysis: Coinspect - Balancer Rate Manipulation Exploit
- Exploit Transaction (Mainnet): 0x3e173ab0ba9183efa8a42caa783bdb5ec75daffcc8505cc1302009d11daf1ccf
- Extraction Transaction: 0xd155207261712c35fa3d472ed1e51bfcd816e616dd4f517fa5959836f5b48569

