56887 sc low incorrect balance tracking in morphoyearnogwethstrategy deallocate function leads to wrong loss event emission resend

Submitted on Oct 21st 2025 at 14:36:10 UTC by @Snuggle for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #56887

  • 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 a logic bug where the balance before withdrawal is captured AFTER the withdrawal has already occurred. This leads to incorrect loss event emissions where wethRedeemed will always be 0, making the loss detection mechanism ineffective for tracking actual losses.

Vulnerability Details

  • The _deallocate function captures the balance before withdrawal AFTER the withdrawal has already happened:

    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;
    }
  • Line 50: vault.withdraw() is called first

  • Line 51: wethBalanceBefore is captured AFTER withdrawal (should be before)

  • Line 52: wethBalanceAfter is captured immediately after, so it equals wethBalanceBefore

  • Line 53: wethRedeemed will always be 0 because both balances are identical

  • Lines 57-58: Redundant require statements similar to Euler strategies

Impact Details

  • Incorrect loss event emission: wethRedeemed will always be 0, so loss events will never trigger when they should

  • Production confusion: Wrong events can mislead operators, monitoring systems, and users about actual strategy performance

  • Broken loss tracking: The loss detection mechanism cannot properly track actual losses during deallocation

  • Monitoring issues: Dashboards and alerts may show incorrect data, leading to wrong operational decisions

  • Gas inefficiency: Redundant require statements (lines 57-58) perform identical checks

  • Code maintainability: Logic is confusing and error-prone

  • Severity: Low (wrong events can cause significant production confusion and operational issues)

References

  • src/strategies/mainnet/MorphoYearnOGWETH.sol:

    • Incorrect balance capture after withdrawal

  • Redundant require statements

Recommendation

Fix the balance tracking logic and remove redundant require statements:

This fixes the balance tracking logic and eliminates redundant gas consumption while maintaining security guarantees.

https://gist.github.com/Snuggle20040629/4fba5a5c59dc463581707549233f4d4d

Proof of Concept

Proof of Concept

Runnable PoC Test

A complete runnable PoC has been implemented in src/test/strategies/MorphoYearnOGWETHStrategy.t.sol:

Running the PoC

Execute the test with mainnet fork to demonstrate the bug:

Expected Output

Was this helpful?