In StrategyArb.sol profitability check mechanism of the claimAndSwap function.
The vulnerability is from an incorrect accounting model in the profitability verification. The function only checks the delta of the asset balance (alETH) but fails to account for the reduction in unexchanged balance from the transmuter.claim()
operation.
Technical Flow:
Initial State:
Unexchanged Balance: X
Asset Balance: Y
Total Value: X + Y
After transmuter.claim():
Unexchanged Balance: (X - _amountClaim)
Underlying Balance: _amountClaim
Asset Balance: Y
After swap:
Unexchanged Balance: (X - _amountClaim)
Asset Balance: Y + minOut
Total Value: (X - _amountClaim) + (Y + minOut)
The check require((balAfter - balBefore) >= _minOut) only verifies that asset balance increased by minOut, but doesn't ensure that: (X - _amountClaim) + (Y + minOut) >= X + Y
This allows a keeper to extract value by claiming large amounts and swapping them for marginally more than the minimum required increase in asset balance, while actually reducing the total value of the strategy.
The bug exists across all three strategy implementations (StrategyArb.sol, StrategyOp.sol, and StrategyMainnet.sol) in their claimAndSwap functions.
The same issue exists in StrategyMainnet.sol and StrategyOp.sol with their respective router implementations.
The bug pipe from an incorrect economic assumption in the profitability check. The strategies track three balances:
Unexchanged balance (in transmuter)
Underlying balance (WETH)
Asset balance (alETH)
The total value is calculated in balanceDeployed()
A keeper can execute a value-extracting trade by:
Claiming a large amount (_amountClaim
) from transmuter
Swapping for slightly more than _amountClaim
While the asset balance increases by minOut, the total strategy value decreases
Example:
Initial State: 100 unexchanged + 0 underlying + 0 asset = 100 total
Claim 100: 0 unexchanged + 100 underlying + 0 asset = 100 total
Swap 100 for 101: 0 unexchanged + 0 underlying + 101 asset = 101 total
While asset increased by 1, if the true market rate was 1:1.05, this represents a loss of value
Because the transmuter.claim()
operation's impact on unexchanged balance is not factored into the profitability verification, allowing economically unfavorable trades that appear profitable when only checking asset balance changes.
For StrategyArb.sol, StrategyOp.sol, and StrategyMainnet.sol
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.