57378 sc high impossible to withdraw yield from strategies

Submitted on Oct 25th 2025 at 16:31:30 UTC by @OxPhantom for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #57378

  • Report Type: Smart Contract

  • Report severity: High

  • Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/strategies/arbitrum/AaveV3ARBUSDCStrategy.sol

  • Impacts:

    • Permanent freezing of unclaimed yield

Description

Brief/Intro

Deallocation currently withdraws only principal and reduces the tracked allocation accordingly. If an admin brings an adapter’s allocation down to zero, any accrued interest (e.g., Aave aToken yield) remaining on the startegy cannot be pulled by subsequent deallocations because the vault blocks deallocation when allocation is zero and the call revert if the deallocated amount is higher than the allocation. Although interest still contributes to the vault’s total assets via realAssets(), it will not be able to be withdrawn by the strategy.

Vulnerability Details

We will take the exemple of the Aave strategy’s deallocation path withdraws exactly the requested amount and returns exactly that amount to the vault, never more:

function _deallocate(uint256 amount) internal override returns (uint256) {
    uint256 usdcBalanceBefore = TokenUtils.safeBalanceOf(address(usdc), address(this));
    // withdraw exact underlying amount back to this adapter
    pool.withdraw(address(usdc), amount, address(this));
    uint256 usdcBalanceAfter = TokenUtils.safeBalanceOf(address(usdc), address(this));
    uint256 usdcRedeemed = usdcBalanceAfter - usdcBalanceBefore;
    if (usdcRedeemed < amount) {
        emit StrategyDeallocationLoss("Strategy deallocation loss.", amount, usdcRedeemed);
    }
    require(TokenUtils.safeBalanceOf(address(usdc), address(this)) >= amount, "Strategy balance is less than the amount needed");
    TokenUtils.safeApprove(address(usdc), msg.sender, amount);
    return amount;
}

The vault updates allocation based on the change returned by the strategy and rejects deallocations when allocation is zero:

Meanwhile, the vault’s share price accounting includes adapter-level interest via realAssets():

On Aave, realAssets() is the aToken balance (principal + interest):

Result: if the admin deallocates exactly the principal until allocation hits zero, any residual interest remains at the adapter, and further deallocations are blocked by ZeroAllocation. There is no standard “harvest/skim” method to pull only interest without modifying allocation. Moreover after the deallocation the strategy compute the new allocation in the MYTStrategy contract:

Because in the majority of the strategies, the deallocation function will return the amount used as parameter if the admin try to deallocate the principal + interest, the call will revert because of an underflow.

Impact Details

  • Interest may remain stuck on the strategy after principal is fully deallocated, making it unavailable.

  • Although accounting includes the interest in total assets, the inability to repatriate can break assumptions about the value of MYT shares.

References

https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/MYTStrategy.sol#L128-L130

Proof of Concept

Proof of Concept

This is a coded POC that demonstrate the issue, you can run it by copy pasting this code into a test file and run forge test --mt test_StrandedInterest_WhenDeallocateToZero -vv

In this POC:

  1. user deposit 100 usdc in the vault

  2. The owner allocate 100 usdc into aave

  3. the vault earn 10 usdc as yield

  4. if the admin deallocates 110 USDCs the call revert because of an underflow

  5. if he deallocates 100 USDC, 10 aToken remain stuck in the strategy.

Was this helpful?