56343 sc low morphoyearnogweth deallocate function always emits strategydeallocationloss due to flawed balance measurement

Submitted on Oct 14th 2025 at 18:22:27 UTC by @DashBug for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #56343

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

    • Contract fails to deliver promised returns, but doesn't lose value

Description

Brief/Intro

The _deallocate function in src/strategies/mainnet/MorphoYearnOGWETH.sol contains flawed balance measurement logic that causes it to always emit StrategyDeallocationLoss events, even when no actual loss occurs. This creates misleading performance signals and false negative reporting to users and monitoring systems.

Vulnerability Details

  • The _deallocate function measures balance before and after withdrawal incorrectly:

    function _deallocate(uint256 amount) internal override returns (uint256) {
        vault.withdraw(amount, address(this), address(this));
        uint256 wethBalanceBefore = TokenUtils.safeBalanceOf(address(weth), address(this));
        uint256 wethBalanceAfter = TokenUtils.safeBalanceOf(address(weth), address(this));
        uint256 wethRedeemed = wethBalanceAfter - wethBalanceBefore;
        if (wethRedeemed < amount) {
            emit StrategyDeallocationLoss("Strategy deallocation loss.", amount, wethRedeemed);
        }
        require(wethRedeemed + wethBalanceBefore >= amount, "Strategy balance is less than the amount needed");
        require(TokenUtils.safeBalanceOf(address(weth), address(this)) >= amount, "Strategy balance is less than the amount needed");
        TokenUtils.safeApprove(address(weth), msg.sender, amount);
        return amount;
    }
  • The balance measurement occurs after the withdrawal on both lines 51 and 52

  • Since both measurements are taken at the same point, wethBalanceBefore equals wethBalanceAfter

  • This results in wethRedeemed = 0 always

  • The condition if (wethRedeemed < amount) is always true (since 0 < amount)

  • StrategyDeallocationLoss is always emitted regardless of actual performance

Impact Details

  • Misleading events: Users see loss events even when the strategy performs correctly

  • False negative reporting: External monitoring systems receive incorrect loss signals

  • Reputation damage: The strategy appears to always underperform

  • No actual financial loss: The contract still delivers promised returns, just with incorrect reporting

  • Severity: Low (logic error affecting reporting, not financial security)

References

  • src/strategies/mainnet/MorphoYearnOGWETH.sol:

    • Flawed balance measurement (both measurements after withdrawal)

  • Always true condition leading to false loss emission

Recommendation

Fix the balance measurement by taking the "before" measurement before the withdrawal:

This ensures accurate loss reporting and eliminates false negative signals.

https://gist.github.com/dash19991226/6766247cd19c790a47bb6240e6c1b735

Proof of Concept

Proof of Concept

Runnable PoC Test

A working Proof of Concept test is available at:

To run the PoC:

Expected output:

The PoC demonstrates that the loss event is always emitted due to the flawed balance measurement logic, confirming the bug exists in the production code.

Manual Steps to Reproduce

  1. Call _deallocate function with any valid amount

  2. Observe that StrategyDeallocationLoss event is always emitted

  3. Verify that both balance measurements occur after withdrawal

Expected vs Actual:

  • Expected: Loss event only emitted when actual loss occurs

  • Actual: Loss event always emitted due to flawed measurement logic

Root Cause Analysis:

  • Both balance measurements taken after withdrawal

  • wethRedeemed always equals 0

  • Condition wethRedeemed < amount always true

  • False loss reporting to users and monitoring systems

Was this helpful?