Reproduced Exploit

Tapioca DAO — BalancerStrategy _withdraw scales WETH as BPT

1. _withdraw converts desired WETH → BPT-like figure via getRate. 2. _vaultWithdraw encodes type-2 exact-tokens-out with that figure as minAmountsOut. 3. Vault pays only the scaled amount → require balance fails → withdraw DoS.

Jul 2023Otheruntagged2 min read

Chain

Other

Category

untagged

Date

Jul 2023

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 27530-h-40-balancerstrategysol-withdraw-withdraws-insufficient-to. Standalone Foundry PoC and full write-up: 27530-h-40-balancerstrategysol-withdraw-withdraws-insufficient-to_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/wrong-condition · vuln/direct-drain · vuln/fot-slippage

Reproduction: self-contained Foundry PoC with only forge-std — no fork, no RPC. Full trace: output.txt. PoC: test/27530-h-40-balancerstrategysol-withdraw-withdraws-insufficient-to.sol.


Key info#

ImpactHIGH — full WETH withdraw reverts after under-exiting the Balancer pool (BalancerStrategy: not enough)
ProtocolTapioca DAO
Vulnerable codeBalancerStrategy._withdraw scales amount by pricePerShare then uses it as type-2 exact tokens out
Bug classUnit mismatch / wrong exit kind argument
FindingCode4rena — Tapioca, 2023-07 · #27530 · reporter carrotsmuggler
Reportcode4rena.com/reports/2023-07-tapioca
SourceAuditVault
StatusConfirmed (dup #51)
Compiler^0.8.24 (PoC)

TL;DR#

  1. _withdraw converts desired WETH → BPT-like figure via getRate.
  2. _vaultWithdraw encodes type-2 exact-tokens-out with that figure as minAmountsOut.
  3. Vault pays only the scaled amount → require balance fails → withdraw DoS.

The vulnerable code#

SOLIDITY
uint256 toWithdraw = (((amount - queued) * (10 ** decimals)) / pricePerShare);
// @> VULN: scaled value used as exact WETH out
_vaultWithdraw(toWithdraw);

Fix: pass unscaled WETH amount for exact-tokens-out, or convert correctly for exact-BPT-in.

Root cause#

Type-2 Balancer exits withdraw exactly minAmountsOut, not “at least this much after BPT math.” Scaling by pricePerShare underpays WETH.

Attack walkthrough#

  1. Strategy holds ample BPT, rate = 2e18.
  2. User requests 1000 WETH withdraw.
  3. Vault exits only 500 WETH → not enough revert; BPT remains stuck for full exit.

Diagrams#

flowchart TD A["User wants amount WETH"] --> B["toWithdraw = amount * 1e18 / rate"] B --> C["type-2 exit minAmountsOut = toWithdraw"] C --> D["Vault pays only toWithdraw WETH"] D --> E["require amount <= balance reverts"]

Impact#

Withdrawal liveness failure for Balancer strategy deposits — funds effectively frozen for full-size exits when idle buffer is insufficient.

Taxonomy#

  • genome: wrong-condition, direct-drain, access-roles, fot-slippage
  • sector: dex, governance, liquid-staking, vault
  • severity: high
  • platform: code4rena

Sources#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.