58022 sc medium accounting mismatch and fund stuck due to dust eth on stargateethpoolstrategy

Submitted on Oct 30th 2025 at 02:50:32 UTC by @Jugger63 for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58022

  • Report Type: Smart Contract

  • Report severity: Medium

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

  • Impacts:

    • Temporary freezing of funds for at least 24 hour

    • Permanent freezing of funds

Description

Finding description

In StargateEthPoolStrategy the _allocation() function:

uint256 amountToDeposit = (amount / 1e12) * 1e12;   // rounded down
uint256 dust           = amount - amountToDeposit;  // remaining dust

pool.deposit{value: amountToDeposit}(address(this), amountToDeposit);
return amount;                                      // ← Refund full amount!

The contract unwraps WETH to ETH, then only the amountToDeposit (a multiple of 1e12) is actually sent to the pool, leaving the dust ETH (≤ 1e12-1 wei) in the contract. The function still returns amount, so the Morpho Vault (caller) records an allocation of amount, not amountToDeposit. The realAssets() function simply returns pool.redeemable(address(this)), so the dust ETH is never accounted for in the strategy's real asset reporting. This results in an accounting discrepancy between the allocation held by the vault and the real value of the assets redeemable by the strategy.

Impact

Allocation > realAssets() causes the vault to believe the strategy holds more assets than it actually does. When the vault wants to withdraw (deallocate) the allocation amount, the strategy must redeem LP equal to amountToDeposit and make up the difference by wrapping dust ETH into WET. If the dust is insufficient, the following check is performed:

will fail and the transaction-deallocation revert and vault funds may get stuck until the admin performs manual intervention.

Recommendation

Return amountToDeposit (the real value deposited) from _allocate and immediately rewrap dust to WETH or at least include dust in the realAssets() calculation so that accounting is always consistent.

Reference

  • https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/strategies/optimism/StargateEthPoolStrategy.sol#L46-L47

  • https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/strategies/optimism/StargateEthPoolStrategy.sol#L51-L52

  • https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/strategies/optimism/StargateEthPoolStrategy.sol#L79

Proof of Concept

Scenario Considerations

  1. User/vault allocates 1.000000001234567 ETH in WETH.

  2. _allocate:

  • amountToDeposit = 1.000000001000000 ETH (rounded)

  • dust = 0.000000000234567 ETH is deposited in the contract.

  • The function returns 1.000000001234567 ETH. The vault increases the allocation by this amount.

  1. realAssets() reports only 1.000000001000000 ETH. The difference of 234,567 wei is not recorded.

  2. In the future, the vault calls deallocate(1.000000001234567 ETH).

  3. The strategy can only redeem 1.000000001000000 ETH from the pool and attempts to cover 234567 wei with dust.

  4. If the dust is insufficient → require fails → revert → vault cannot withdraw funds → funds stuck.

POC

Add to StargateEthPoolStrategy.t.sol.

Was this helpful?