# 58796 sc low incorrect balance snapshot in deallocate causes wethredeemed always 0

**Submitted on Nov 4th 2025 at 13:57:21 UTC by @dldLambda for** [**Audit Comp | Alchemix V3**](https://immunefi.com/audit-competition/alchemix-v3-audit-competition)

* **Report ID:** #58796
* **Report Type:** Smart Contract
* **Report severity:** Low
* **Target:** <https://github.com/alchemix-finance/v3-poc/blob/immunefi\\_audit/src/strategies/mainnet/MorphoYearnOGWETH.sol>
* **Impacts:**
  * Permanent freezing of funds

## Description

## Brief/Intro

In the strategy implementation (MorphoYearnOGWETHStrategy.\_deallocate), the WETH balance is enabled after calling vault.withdraw(...), so the wethRedeemed variable is distributed as the difference between two results taken at the same time—as a result, wethRedeemed always occurs. This strategy's control constantly emits the StrategyDeallocationLoss event, requiring invalid data for verification.

## Vulnerability Details

Problematic code:

```
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;
}
```

wethBalanceBefore and wethBalanceAfter are both read after the withdraw call.

Their difference (wethRedeemed) is always 0, regardless of the actual amount withdrawn.

Consequently, StrategyDeallocationLoss is falsely emitted, and require checks may revert incorrectly.

In production, this can lead to freezing of funds in the strategy and incorrect reporting.

In another strategies, correct (EulerWETHStrategy):

```
    function _deallocate(uint256 amount) internal override returns (uint256) {
        uint256 wethBalanceBefore = TokenUtils.safeBalanceOf(address(weth), address(this));
        vault.withdraw(amount, address(this), address(this));
        require(TokenUtils.safeBalanceOf(address(weth), address(this)) >= amount, "Strategy balance is less than the amount needed");
```

## Impact Details

freezing of funds, false alerts are emitted (StrategyDeallocationLoss)

## References

<https://github.com/alchemix-finance/v3-poc/blob/immunefi\\_audit/src/strategies/mainnet/MorphoYearnOGWETH.sol>

## Proof of Concept

## Proof of Concept

Add and run and see logs:

```
function testBug_DeallocateIncorrectBalanceTracking() public {
    uint256 amountToAllocate = 10e18; // 10 WETH

    // === 1. Allocate ===
    vm.startPrank(vault);
    // Transfer tokens so the strategy actually receives WETH
    deal(address(WETH), strategy, amountToAllocate);

    // Log strategy balance before allocation
    uint256 strategyWethBefore = IERC20(address(WETH)).balanceOf(strategy);
    console.log("=== BEFORE ALLOCATION ===");
    console.log("Strategy WETH balance: %e", strategyWethBefore);

    bytes memory prevAllocation = abi.encode(0);
    IMYTStrategy(strategy).allocate(prevAllocation, amountToAllocate, "", address(vault));
    vm.stopPrank();

    // Check real assets after allocation
    uint256 realAssetsAfterAllocation = IMYTStrategy(strategy).realAssets();
    console.log("");
    console.log("=== AFTER ALLOCATION ===");
    console.log("Real assets in strategy: %e", realAssetsAfterAllocation);
    assertGt(realAssetsAfterAllocation, 0, "Should have allocated assets");

    // === 2. Deallocate ===
    vm.startPrank(vault);

    // Log strategy balance before deallocation
    uint256 wethBalanceBefore = IERC20(address(WETH)).balanceOf(strategy);
    console.log("");
    console.log("=== BEFORE DEALLOCATION ===");
    console.log("Strategy WETH balance before: %e", wethBalanceBefore);

    bytes memory prevAllocation2 = abi.encode(amountToAllocate);

    // Call deallocate
    (, int256 change) = IMYTStrategy(strategy).deallocate(prevAllocation2, amountToAllocate, "", address(vault));
    vm.stopPrank();

    // === 3. Verify actual WETH received by the vault ===
    uint256 vaultWethAfter = IERC20(address(WETH)).balanceOf(vault);
    uint256 actualWethReceived = vaultWethAfter; // for this test, just check vault balance
    console.log("");
    console.log("=== AFTER DEALLOCATION ===");
    console.log("Vault WETH after:  %e", vaultWethAfter);
    console.log("Change (return value): %d", change);

    // Demonstrate the bug
    console.log("");
    console.log("==========================================================");
    console.log("BUG DEMONSTRATED:");
    console.log(" - StrategyDeallocationLoss event emitted with wethRedeemed=0");
    console.log(" - Actual WETH received: %e", actualWethReceived);
    console.log(" - Root cause: wethBalanceBefore taken AFTER vault.withdraw()");
    console.log(" - Location: MorphoYearnOGWETH.sol:50-53");
    console.log("==========================================================");

    // Assertions
    assertGt(actualWethReceived, 0, "Vault should have received WETH");
    assertApproxEqAbs(actualWethReceived, amountToAllocate, 1e17, "Should receive approximately allocated amount");
    assertLt(change, 0, "Change should be negative");
}
```

```
Logs:
  === BEFORE ALLOCATION ===
  Strategy WETH balance: 1e19
  
  === AFTER ALLOCATION ===
  Real assets in strategy: 9.999999999999999999e18
  
  === BEFORE DEALLOCATION ===
  Strategy WETH balance before: 0e0
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reports.immunefi.com/alchemix-v3/58796-sc-low-incorrect-balance-snapshot-in-deallocate-causes-wethredeemed-always-0.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
