58329 sc low incorrect balance measurement in morphoyearnogweth deallocate leads to temporary freezing of funds via spurious loss events
Submitted on Nov 1st 2025 at 10:05:44 UTC by @rshackin for Audit Comp | Alchemix V3
Report ID: #58329
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/strategies/mainnet/MorphoYearnOGWETH.sol
Impacts:
Temporary freezing of funds for at least 1 hour
Temporary freezing of funds for at least 24 hour
Description
Summary:
MorphoYearnOGWETH strategy contains a logic error in _deallocate() that causes it to emit StrategyDeallocationLoss events on every successful deallocation despite funds arriving correctly. This occurs because balance measurements are taken after the vault withdrawal completes, making the computed delta always zero. The false loss signals can trigger automated circuit breakers that pause the strategy, blocking further deallocations and creating temporary freezing of funds for users relying on this route to redeem collateral.
Component:
MorphoYearnOGWETH._deallocate()Impact: Deterministic false loss events on normal operations and realistic automation-driven redemption freezing.
Vulnerability Details:
Root Cause: In
MorphoYearnOGWETH._deallocate(), the balance delta calculation reads both snapshots after callingvault.withdraw():
The
vault.withdraw()call on line 1 successfully transfersamountWETH to the strategy (as confirmed by test balance assertions), but because bothbalanceOf()calls on lines 4-5 happen after this transfer completes, they read the same post-transfer balance, making their difference(wethRedeemed)zero regardless of actual funds received. This deterministically forces the loss event to emit on every deallocation.Since both reads occur after the external vault call transfers WETH to the strategy,
balanceBefore == balanceAfter, forcingwethRedeemed = 0. The subsequent checkif (wethRedeemed < amount)always triggers, emittingStrategyDeallocationLoss(message: "Strategy deallocation loss.", amountRequested: amount, actualAmountSent: 0)even though the strategy's balance increased by exactly amount.Intended correct Behavior (from other strategies):
EulerWETH,EulerUSDC,Peapods ETH/USDCall implement the correct pattern:
This confirms MorphoYearnOGWETH's ordering is an unintended implementation and this is flawed.
Impact Details:
Direct Impact: Deterministic False Loss Signals.
Every deallocation through
MorphoYearnOGWETHemitsStrategyDeallocationLoss(actualAmountSent: 0)despite funds arriving, creating constant operational error and misleading monitoring systems about the data of this strategy.
Primary Impact: Temporary Freezing of Funds (High):
Mechanism: Production teams commonly deploy automated keepers that monitor strategy events and execute circuit-breaker pauses when losses are detected to prevent cascading failures. The spurious
StrategyDeallocationLosscan trigger such automation immediately after any deallocation.Effect: Once paused, the strategy blocks further
_deallocate()calls, preventing users from unwinding positions or redeeming collateral through this route until manual intervention/un-pause occurs.
Duration Justification for ≥24h Threshold:
Investigation phase (2-8 hours): On-call engineer reviews logs, identifies StrategyDeallocationLoss events, must distinguish false positives from genuine losses by comparing event history, vault state, and balance proofs
Coordination phase (4-12 hours): Gathering multisig/admin quorum or governance vote to execute unpause, especially during weekends/holidays when key holders are unavailable
Repeated cycles: Since the bug fires on every deallocation, even if first pause resolves quickly (best-case 6-8h), subsequent user attempts re-trigger false events and additional pauses, accumulating total freeze time beyond 24 hours
Realistic minimum: 24-48+ hours is standard operational response time for non-emergency investigations (distinguishing false vs. real threats), satisfying the "at least 24 hour" criterion.
References:
(https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/strategies/mainnet/MorphoYearnOGWETH.sol#L49-L56)
Recommended Mitigation:
Move balanceBefore read to line before vault.withdraw() call, aligning with pattern used in all other MYT strategies.
This fix aligns the logic with the pattern used in all other MYT strategies (EulerWETH, EulerUSDC, PeapodsETH, PeapodsUSDC), eliminating false loss events while preserving intended safety monitoring when genuine shortfalls occur..
Proof of Concept
Proof of Concept:
Both PoCs run on a mainnet fork using real Morpho-Yearn OG WETH vault and WETH addresses, satisfying the competition's local-fork testing requirement.
PoC A: False Loss Event on Successful Deallocation:
Place test file in
test/MorphoYearnOGWETH_FalseLoss.t.solExport RPC:
export MAINNET_RPC_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"Execute:
forge test --match-contract MorphoYearnOGWETHLossEvent -vvv
Results: Test passes; trace shows strategy balance increases by 0.5 ETH (from 1.0 to 1.5) while StrategyDeallocationLoss(actualAmountSent: 0) is emitted in the same transaction — proving the contradiction.
PoC B: Automated Pause Leading to Temporary Freezing.
Place test file in
test/MorphoYearnOGWETH_AutoFreeze.t.solUse same RPC setup as PoC A
Execute:
forge test --match-contract MorphoYearnOGWETH_AutoPause -vvv
Results: Test passes; second deallocation reverts with Paused(), proving the automation-driven freeze path.
Was this helpful?