58424 sc low morphoyearnogweth strategy balance check order bug

Submitted on Nov 2nd 2025 at 08:51:34 UTC by @Diavol0 for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58424

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

    • Monitoring and Loss Detection Failure - Strategic visibility broken but funds safe

Description

Summary

The _deallocate() function in MorphoYearnOGWETHStrategy contains a critical logic error where wethBalanceBefore is captured after the withdraw() call instead of before it. This causes the deallocation loss detection to always fail and emit incorrect events.

Vulnerable Code

function _deallocate(uint256 amount) internal override returns (uint256) {
    vault.withdraw(amount, address(this), address(this));  // Line 50 ← WITHDRAW FIRST

    uint256 wethBalanceBefore = TokenUtils.safeBalanceOf(address(weth), address(this));  // Line 51 ← AFTER withdraw!
    uint256 wethBalanceAfter = TokenUtils.safeBalanceOf(address(weth), address(this));   // Line 52 ← SAME!

    uint256 wethRedeemed = wethBalanceAfter - wethBalanceBefore;  // Line 53 ← = 0!

    if (wethRedeemed < amount) {  // Line 54 ← Always true
        emit StrategyDeallocationLoss("Strategy deallocation loss.", amount, wethRedeemed);
    }

    require(wethRedeemed + wethBalanceBefore >= amount, "Strategy balance is less than the amount needed");
    // ...
}

The Problem

  1. Line 50: vault.withdraw(amount) is executed, increasing WETH balance

  2. Line 51-52: wethBalanceBefore and wethBalanceAfter are read AFTER the withdrawal

  3. Result: Both snapshots have the same value, so wethRedeemed = 0

  4. Effect: Loss detection always triggers incorrectly

Comparison with Correct Implementation

Correct pattern (used in Euler, Fluid, Peapods strategies):


2. Impact Analysis

Direct Consequences

  1. Loss Detection Always Fails

    • wethRedeemed is always 0

    • Loss event is ALWAYS emitted regardless of actual loss

    • True losses are hidden among false positives

  2. Misleading Monitoring

  3. Secondary Impact: Broken Require Check (Not a separate bug, part of main issue)

    This check becomes meaningless due to the bug:

    • wethRedeemed = 0 (always, due to balance reading bug)

    • wethBalanceBefore = (previous balance)

    • Check effectively becomes: require(wethBalanceBefore >= amount, ...)

    • This may pass or fail based on unrelated prior state, not actual withdrawal success

    Mitigation: A second require statement (line 58) performs the correct check:

    This prevents any actual security issue, but the first require is rendered useless by the bug.

Secondary Impact

  • VaultV2 Monitoring: The VaultV2 contract relies on realAssets() for accounting, which in this case is:

    This is unaffected by the bug, but loss events are misleading.

  • Strategy Dashboard: Any monitoring dashboard reading the StrategyDeallocationLoss event will show false positives.

Proof of Concept

3. Proof of Concept

Test Case

Observed Behavior


Was this helpful?