The function withdraws only totalAvailable but doesn't handle the remaining amount (_amount - totalAvailable).
When this happens:
User requests withdrawal of amount X
Available balance is Y where Y < X
Function withdraws Y
Balance reduces by Y
But contract promised to withdraw X
Safety property violation: balance reduced by Y when promised X
This is fundamental logic error in handling partial withdrawals. The function should either:
Revert if full amount cannot be withdrawn
Or implement proper partial withdrawal logic with clear user communication
The vulnerability directly impacts the core withdrawal functionality and could lead to users receiving less funds than requested without proper indication.
The function:
Checks if requested amount exceeds available balance
If true, withdraws only available balance
Does not handle the remaining amount
Does not revert or signal partial withdrawal
its impact across Implementations:
Arbitrum (StrategyArb.sol):
Affects WETH/alETH pairs on Ramses
Partial withdrawals through transmuter
Optimism (StrategyOp.sol):
Impacts Velodrome-based trading
Incomplete withdrawals possible
Mainnet (StrategyMainnet.sol):
Affects Curve-based operations
Same withdrawal vulnerability
The bug affects all three blockchain implementations and their respective DEX integrations (Ramses, Velodrome, Curve), potentially leading to:
Stuck funds
Silent partial withdrawals
Broken accounting
Compromised withdrawal guarantees
This is safety violation in the core withdrawal logic across all implementations of the Alchemix strategy system.
In the Transmuter interface
And in the strategy implementations
The root cause from:
The function promises to free a specific amount of funds (_amount)
When insufficient funds are available, it:
Withdraws only what's available (totalAvailabe)
Makes no attempt to fulfill the remaining amount
Doesn't revert or signal partial fulfillment
This directly violates the safety property that states balance reduction must not exceed requested amount
This design creates a mismatch between:
What the function promises (withdraw _amount)
What it actually does (withdraws min (totalAvailabe, _amount))
How it handles the shortfall (it doesn't)
The bug affects all three implementations (Arbitrum, Optimism, Mainnet) because they share this core withdrawal logic, despite using different DEXes (Ramses, Velodrome, Curve) for their swap operations.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.