DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: medium
Invalid

Incorrect `availableWithdrawLimit` due to missing transmuter state validation

Vulnerability Details

The strategy's availableWithdrawLimit() function fails to validate the operational state of the Alchemix transmuter before including unexchanged balances in the withdrawable amount calculation.

Current implementation: https://github.com/Cyfrin/2024-12-alchemix/blob/82798f4891e41959eef866bd1d4cb44fc1e26439/src/StrategyArb.sol#L211

function availableWithdrawLimit(
address /*_owner*/
) public view override returns (uint256) {
return asset.balanceOf(address(this)) + transmuter.getUnexchangedBalance(address(this));
}

If the transmuter is paused:

  • Strategy reports unexchanged balances as withdrawable.

  • Withdrawal transactions will revert due to inability to access transmuter.

In a nutshell, the strategy blindly assumes the transmuter is operational and includes unexchanged balances without validating the transmuter's state.

When looking at Yearn's docs for strategy we see this check is implement for both: deposit/withdraw limits: https://docs.yearn.fi/developers/v3/strategy_writing_guide#availabledepositlimitaddress-_owner

function availableWithdrawLimit(
address _owner
) public view override returns (uint256) {
@> if(positionIsLocked || yieldSource.isPaused()) {
@> return asset.balanceOf(address(this));
}
// Return both the loose balance and the current liqudity of the yield source.
return asset.balanceOf(address(this)) + asset.balanceOf(address(yieldSource));
}

If that's the case, the contract should only asset.balanceOf(address(this));.

Impact

  • Incorrect funds for preview functions

  • DoS when withdrawing

Tools Used

Manual Review

Recommendations

Only return the contract's balance when the transmuter is paused.(Must be done in all the Strategies contracts)

function availableWithdrawLimit(
address /*_owner*/
) public view override returns (uint256) {
// Only include idle balance if transmuter is non-operational
if (transmuter.isPaused()) {
return asset.balanceOf(address(this));
}
return asset.balanceOf(address(this)) + transmuter.getUnexchangedBalance(address(this));
}

Also consider adding the availableDepositLimitimplementing the same logic.

Updates

Appeal created

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
holydevoti0n Submitter
5 months ago
inallhonesty Lead Judge
5 months ago
inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.