Author: Kong & Lisa

Editor: 77

Background

On November 3, 2025, the established decentralized automated market maker protocol Balancer v2 was attacked, resulting in losses of approximately $120 million across multiple projects, including its forked protocols, on several chains, exacerbating the already struggling DeFi ecosystem. Below is a detailed analysis of this attack event by the Slow Mist security team:

Root Cause

In the implementation of Composable Stable Pool of Balancer v2 (based on Stable Math of Curve StableSwap), there is a precision loss issue in the integer fixed-point operations of scaling factors, leading to small but compounding price differentials/errors during token exchanges. Attackers exploit this error by making small exchanges under low liquidity to amplify the discrepancies for significant cumulative profits.

Prerequisite knowledge

Before starting the analysis, we need to understand some key knowledge about Balancer to facilitate understanding of this analysis content.

Composable Stable Pools

Composable stable asset pools are designed for token swap pools of assets expected to approach parity or known exchange rates. For example, USDC/USDT, which swaps close to 1:1, or WETH/stETH, which swaps at known rates.

Balancer Pool Token (BPT)

BPT represents shares in the Balancer pool, and users can receive BPT when adding liquidity to represent their liquidity share in the pool.

Composable

Liquidity pools allow swaps between LP and liquidity assets to enhance capital efficiency. For example, allow swaps between BPT tokens and liquidity tokens like WETH/stETH.

Scaling Factor

The underlying Balancer pools are designed with a method for managing decimal precision and rounding direction, ensuring that all funds enter the pool for calculation through scaling factors to ensure precision and rounding direction favorable to the pool.

Batch Swaps

Batch swap functionality allows users to swap different tokens within the same/multiple pools in a single transaction, with pools conducting internal accounting through virtual values and settling after the batch swap ends.

Attack analysis

Here, we take the example of two transactions involved in the attacker's attack on Ethereum:

Attack transaction: 0x6ed07db1a9fe5c0794d44cd36081d6a6df103fab868cdd75d581e3bd23bc9742

Withdrawal transaction: 0xd155207261712c35fa3d472ed1e51bfcd816e616dd4f517fa5959836f5b48569

1. The attacker first initiates an attack transaction through the batch swap function, using BPT to significantly exchange out liquidity tokens from the pool, which makes the pool's liquidity token reserves very low.

2. After the pool's liquidity token reserves decrease, the attacker begins to perform swap operations between liquidity tokens (osETH/WETH).

3. Finally, exchange liquidity tokens back to BPT tokens and continuously repeat this operation in multiple pools.

4. After completing the attack transaction, the attacker calls the Vault's manageUserBalance function through an independent withdrawal transaction to extract the profits obtained. This transaction was earlier mistakenly analyzed by multiple analysts as an attack transaction, but in fact, it was just a normal withdrawal transaction using standard withdrawal logic.

5. So why can the attacker seemingly easily withdraw from the Vault without paying any tokens through the above operations? By simple observation, we can find that the attacker's internal accounting balance in the Vault increased after completing the batch swap, which allows the attacker to directly withdraw these balances from the Vault.

Next, we specifically analyze why the attacker can realize balance growth out of nothing without paying any tokens.

Principle analysis

Through the above attack analysis, we can see that the attacker primarily profits through three steps:

  1. Use BPT to exchange liquidity tokens;

  2. Conduct liquidity token swaps;

  3. Exchange liquidity tokens back to BPT.

First, we analyze the process of BPT exchanging liquidity tokens. Since the exchange type kind passed by the attacker is 1, the pool will calculate how much amountIn the attacker should provide for the exchange based on the amountOut passed by the attacker. We will not elaborate on the complicated call chain here; we will directly analyze the underlying _swapWithBpt function for BPT exchanges.

Through the swapWithBpt function, we can see that it first scales the pool's token reserves using the scaling factor through the upscaleArray function to unify price precision and rounding direction. The _upscaleArray function performs specific scaling operations through FixedPoint.mulDown. We can quickly know that mulDown will cause the calculation result to round down, truncating decimals.

This will lead to pool reserves being directly truncated if there are decimals during the scaling process. From prerequisite knowledge, we know that composable pools are designed for expected token swap pools of assets that approach parity or have known exchange rates, using WETH and osETH as examples, there is a known exchange rate between them. Therefore, when calculating the actual reserves of osETH, it needs to be multiplied by the exchange rate and then scaled to unify prices.

Unfortunately, due to the existence of exchange rates, calculating the reserves of osETH will inevitably lead to decimal truncation.

This causes the calculated pool reserve balance to be less than expected, and the price of BPT is calculated by dividing the total price of pool liquidity tokens by the total supply of BPT tokens, meaning the above errors will result in BPT prices being lower than expected.

By utilizing the price difference caused by this error, the attacker will gain a small profit when exchanging liquidity tokens back to BPT tokens in the third step of the batch swap. However, this is obviously far from the huge profits of the attacker, so the key lies in the second step of the batch swap.

The type of exchange kind used by the attacker during liquidity swaps is still 1. We can quickly find that the swapGivenOut function first scales through the upscale function.

Similar to the previous _upscaleArray function, it will use FixedPoint.mulDown to truncate the decimal of the scaled value of the user's exchange amount, which causes the final required input token amountIn to be less than expected. If the exchanged amount is huge, this error can be ignored, but if the exchanged amount is small, it will lead to significant deviation.

This is exactly how the attacker operated. In the second step of the batch swap, they first made the balance reserves of osETH in the pool smaller and then precisely executed a small exchange of 17wei of osETH.

The actual balance during scaling calculations should be 17.98825094772952.

However, due to decimal truncation, the final balance remains 17wei, and 0.98825094772952 relative to 17 has already caused significant precision loss. This will also cause a significant deviation in the input token amount amountIn calculated from this.

To ensure that this deviation can be accumulated in cycles, in the second step of the batch swap, the attacker will exchange osETH back to WETH to ensure that there is still sufficient liquidity for small exchanges next time.

Since WETH itself is a rate-pegged token, the scaling during the osETH -> WETH exchange process is done at 1e18, so it does not accumulate the previous errors.

Thus, we can clearly understand the entire attack process:

  1. The attacker reduces the pool's liquidity token reserves by exchanging liquidity tokens through BPT to prepare for small exchanges.

  2. Prepare for precise control of small exchange precision errors through swaps between liquidity tokens WETH -> osETH.

  3. Accumulate precision errors through precise WETH -> osETH swaps.

  4. Restore liquidity through swaps between liquidity tokens osETH -> WETH.

  5. Repeat steps 2-4 to continuously amplify errors.

  6. Exchange liquidity tokens back to BPT tokens to restore liquidity balance.

  7. Utilizing the magnified errors from small token exchanges makes the number of Out tokens at settlement far greater than the required number of In tokens, thus obtaining huge profits.

MistTrack Analysis

Due to the involvement of multiple projects, this section only analyzes the main addresses and their fund flows in this incident.

1. Address 0xaa760d53541d8390074c61defeaba314675b8e3f

  • Profit situation: Profits on Ethereum, asset types include osETH, USDC, and various tokens.

  • Transfer situation: Various tokens, including ezETH, weETH, ankrETH, and some osETH, were exchanged for ETH and then concentratedly transferred to address 0xf19fd5c683a958ce9210948858b80d433f6bfae2.

  • Balance situation: Mainly includes 7,838.3569 WETH and 7,000 ETH.

  • Initial funding source: Address 0x506d1f9efe24f0d47853adca907eb8d89ae03207, its funds come from gas.zip.

Analyzing address 0x506d1f9efe24f0d47853adca907eb8d89ae03207, its transaction situation in gas.zip is as follows:


This address continuously dispersed ETH from Arbitrum to multiple chains and then launched an attack. Tracing back, the funds of this address 0x506d1f9efe24f0d47853adca907eb8d89ae03207 can originally be traced back to Tornado Cash:

  1. Tornado Cash → 0x86fedad11c4765700934639f1efe1fc01355c982: 100 ETH + 0.1 ETH withdrawn from Tornado Cash is transferred to this address.

  2. 0x86fedad11c4765700934639f1efe1fc01355c982 → 0x766a892f8ba102556c8537d02fca0ff4cacfc492: Transfer 15 ETH.

  3. 0x766a892f8ba102556c8537d02fca0ff4cacfc492 → Cross-chain and perform gas.zip operation: transfer 5 ETH from Ethereum to Arbitrum and send to address 0x506de24d01e6c8623307c9ff5e3c8a945b553207.

2. Address 0xf19fd5c683a958ce9210948858b80d433f6bfae2

  • Profit situation: This address is a core transit node, receiving funds from multiple chains and upstream addresses.

  • Upstream source: Incoming from Arbitrum address 0x872757006b6F2Fd65244C0a2A5fdd1f70A7780f4 to USDX and sUSDX;

From Sonic address 0x045371528A01071D6E5C934d42D641FD3cBE941c's stS;

Incoming from the above Ethereum addresses 0xaa760d53541d8390074c61defeaba314675b8e3f and 0x506d1f9efe24f0d47853adca907eb8d89ae03207.

  • Transfer situation: Assets were exchanged for ETH and WETH through platforms like Velora, CowSwap, KyberSwap, ODOS, and some assets were cross-chain to Arbitrum and Sonic through LI.FI;

Exchange stS for S on Sonic via KyberSwap.

  • Balance situation: Apart from transferring 7,000 ETH to the above address 0xaa760d53541d8390074c61defeaba314675b8e3f, other funds have not been transferred out.

Related addresses have been added to the SlowMist AML malicious address database, and we will continue to monitor related fund movements.

On November 6, Balancer officials released the latest report, stating that although this attack had a wide range of impacts, the rapid response from multiple parties significantly reduced losses in a short time.

(https://x.com/Balancer/status/1986104426667401241)

Current funding progress is as follows:

  1. SEAL Whitehat Safe Harbor (BIP-726, Oct 2024): Thanks to the SEAL Whitehat Safe Harbor (BIP-726) legal framework activated in October 2024, the white hat team was able to intervene rapidly and coordinate disposal after the incident.

  2. HyperNative automated emergency pause mechanism triggered: At 08:06 UTC, Hypernative's emergency pause system was activated; by 08:07 UTC, all CSPv6 liquidity pools on the affected networks had been paused to prevent further spread.

  3. All pausable CSPv6 liquidity pools have entered recovery mode, including previously unaccounted low TVL pools.

  4. CSPv6 factory function disabled: No new vulnerable pools can be created until repairs are completed.

  5. Incentives and emissions for affected pools have been completely terminated to preserve BAL and partner reward assets.

  6. Major LPs have safely exited: including Crypto.com (approximately $800,000, cdcETH/wstETH) and Ether.fi (approximately $1.06 million, eBTC/wBTC).

  7. Stakewise recovered assets: 5,041 osETH (approximately $19 million) and 13,495 osGNO (approximately $1.7–2 million) have been recovered, accounting for 73.5% of the stolen osETH, and will be proportionally returned to affected users.

  8. Berachain validator network paused: To control the risk exposure of Balancer v2 on BEX, on-chain operations have been paused and an emergency fork initiated.

  9. Sonic Labs froze related addresses: Suspicious attacker addresses associated with Beets (a Balancer v2 fork on Sonic) have been frozen to prevent further movement or exchange of funds.

  10. Base MEV bot assists in recovery: Approximately $150,000 has been recovered.

  11. BitFinding white hat team: Successfully intercepted and recovered approximately $600,000 in assets on the mainnet.

  12. Monerium froze EURe assets: Approximately 1.3 million EURe has been locked to prevent further fund flow.

  13. Gnosis bridge restrictions: Coordinated with the Monerium team to temporarily restrict cross-chain transfers on Gnosis Chain to reduce cross-chain transmission risks.

  14. SEAL team communicates with the attacker: Continuing to advance fund return negotiations according to the SEAL framework.

  15. More white hat teams participate in support: Several security teams, including SNP, are assisting in analysis, rescue, and fund return.

Currently, Balancer is actively collaborating with security partners, researchers, exchanges, and white hat teams to advance fund recovery. After verification and settlement, the official will release a more detailed post-analysis report.

Summary

The core of this attack lies in the attacker's use of the precision loss defect occurring during integer fixed-point operations on the scaling factor in the implementation of the Balancer v2 protocol Composable Stable Pool. By carefully constructing small exchanges, the errors caused by this defect are magnified, allowing the attacker to create huge profits in the batch swap. The SlowMist security team recommends that project parties/auditors enhance testing coverage for extreme scenarios and boundary conditions when faced with similar situations, particularly considering precision handling strategies in low liquidity scenarios.

Reference

[1] https://docs-v2.balancer.fi/concepts/pools/composable-stable.html

[2] https://docs.balancer.fi/concepts/core-concepts/balancer-pool-tokens.html

[3] https://docs.balancer.fi/concepts/vault/token-scaling.html

[4] https://docs-v2.balancer.fi/reference/swaps/batch-swaps.html

[5] https://docs-v2.balancer.fi/concepts/advanced/valuing-bpt/valuing-bpt.html#informational-price-evaluation