Balancer scales every token balance up to 18 decimals before doing pool math, and that scaling rounds down. Normally the lost wei is noise. The attacker drove pool balances down to the 8–9 wei range, where a single wei is a huge fraction of the value, and then chained dozens of swaps inside one batchSwap so the error compounded in his favour on every hop — suppressing the pool token's price, then cycling the arbitrage. Ethereum, Base, Arbitrum, Optimism, Polygon, Avalanche, Gnosis, Berachain and Sonic, all inside half an hour. Balancer's TVL fell 58% in two days.
function _upscale(uint256 amount, uint256 scalingFactor) internal pure returns (uint256) { return FixedPoint.mulDown(amount, scalingFactor); // ❌ always rounds DOWN } // At normal balances the discarded wei is invisible. // At a balance of 9 wei it is 11% of the value. batchSwap([ swap, swap, swap, ... ]) // one tx, many hops hop 1 balance 9 → rounds to 8 // 1 wei to the attacker hop 2 ... // and again hop n ... // and again → BPT price suppressed → buy cheap, redeem at true value, repeat // ✅ the invariant: rounding must ALWAYS favour the pool, never the caller. // Direction has to be chosen per call site, not globally.
Entries in the SAFE database that describe this failure. The first ones name this incident directly.
Every figure on this page comes from the post-mortems above, not from us. Losses are US dollars at the time of the incident.
Arithmetic is one of the 203 classes the SaferICO scanner checks for. It will not review your signing process — but it will read your Solidity.