58728 sc medium when the strategy is at a loss the assets cannot be withdrawn

Submitted on Nov 4th 2025 at 09:56:34 UTC by @aman for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58728

  • Report Type: Smart Contract

  • Report severity: Medium

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

  • Impacts:

    • Temporary freezing of funds for at least 24 hour

Description

Brief/Intro

The code indicates that when deallocating from a strategy, we should use the previewAdjustedWithdraw function to determine the amount to be passed to the deallocate function. However, if the strategy is operating at a loss and returns less than the expected amount, the deallocate function will revert.

Vulnerability Details

From the allocation contract, where the vault’s deallocate function is invoked, the comments indicate that the previewAdjustedWithdraw function should be used.

/v3-poc/src/AlchemistAllocator.sol:46
46:     /// @notice When deallocating total amounts,
47:     /// consider using IMYTStrategy(address(strategy)).previewAdjustedWithdraw(amount)
48:     /// as the amount to be passed in for `amount`
49:     /// to estimate the correct amount that can be fully withdrawn,
50:     /// accounting for losses due to slippage, protocol fees, and rounding differences
51:     function deallocate(address adapter, uint256 amount) external {
52:         require(msg.sender == admin || operators[msg.sender], "PD");
53:         bytes32 id = IMYTStrategy(adapter).adapterId();
54:         uint256 absoluteCap = vault.absoluteCap(id);
55:         uint256 relativeCap = vault.relativeCap(id);
56:         // FIXME get this from the StrategyClassificationProxy for the respective risk class
57:         uint256 daoTarget = type(uint256).max;
58:         uint256 adjusted = absoluteCap < relativeCap ? absoluteCap : relativeCap;
59:         if (msg.sender != admin) {
60:             // caller is operator
61:             adjusted = adjusted < daoTarget ? adjusted : daoTarget;
62:         }
63:         // pass the old allocation to the adapter
64:         bytes memory oldAllocation = abi.encode(vault.allocation(id));
65:         vault.deallocate(adapter, oldAllocation, amount);
66:     }

The vault’s deallocate function then calls the strategy’s _deallocate function. In this analysis, I’ll focus on the TokeAutoEth strategy, though the issue can occur in other strategies as well.

At line 82, if the strategy is at a loss, it will return fewer assets than the provided amount. Then, at line 85, there’s a requirement that the strategy’s balance must be greater than or equal to the given amount. Therefore, in the case of a loss, this deallocate call will always revert.

Impact Details

DoS can occur when the strategy is at a loss, resulting in no assets being withdrawn from the strategy.

References

TokeAutoEth.sol#L81-L84arrow-up-right

Recommendation

A potential fix could be to update the amount to match the actual assets received after withdrawing from the strategy in case of a loss.

Proof of Concept

Proof of Concept

Add the following file in test/strategies Dir with the name POC.t.sol :

Run With Command : forge test --match-test test_strategy_DoS_when_in_loss -vvv --decode-internal

Was this helpful?