58313 sc medium incorrect allocation accounting and dust handling in stargateethpoolstrategy causes systematic loss cap mis accounting and deallocation reverts

Submitted on Nov 1st 2025 at 07:25:28 UTC by @IronsideSec for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58313

  • Report Type: Smart Contract

  • Report severity: Medium

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

  • Impacts:

    • Contract fails to deliver promised returns, but doesn't lose value

Description

Brief/Intro

StargateEthPoolStrategy._allocate returns the requested amount instead of the actually deposited amount (floored to 1e12), creating an accounting mismatch with the vault. This over-reports allocation, can prematurely trip caps, and leaves ETH “dust” stranded on the strategy that is not reflected in realAssets(). Over time, this leads to systematic realized loss when interest/loss is accrued, and can also make deallocation revert due to insufficient wrapped WETH. Sub-1e12 allocations deposit 0 by design, causing a 100% loss of those amounts.

Vulnerability Details

  • Root cause: returning amount instead of amountToDeposit from _allocate after rounding down to the nearest 1e12.

47:58:src/strategies/optimism/StargateEthPoolStrategy.sol
function _allocate(uint256 amount) internal override returns (uint256) {
    require(TokenUtils.safeBalanceOf(address(weth), address(this)) >= amount, "not enough WETH");
    // unwrap to native ETH for Pool Native
    weth.withdraw(amount);
    uint256 amountToDeposit = (amount / 1e12) * 1e12;
    uint256 dust = amount - amountToDeposit;
    if (dust > 0) {
        emit StrategyAllocationLoss("Strategy allocation loss due to rounding.", amount, amountToDeposit);
    }
    pool.deposit{value: amountToDeposit}(address(this), amountToDeposit);
    return amount;
}
  • Why this is harmful:

    • The vault uses the strategy’s returned “change” to update the allocation and enforce caps; returning the wrong value overstates allocation.

  • The wrong value propagates from _allocate through the adapter interface:

  • Dust is stranded as native ETH on the strategy and not counted in realAssets() (which uses Stargate pool redeemable()).

  • On accrual, the vault recomputes total assets using adapters’ realAssets(); dust is excluded and becomes a realized loss.

  • Deallocation reverts are amplified because _deallocate requires WETH balance ≥ amount, while dust remains as ETH unless explicitly wrapped.

  • Proven by PoC tests:

    • Over-reported allocation and floored realAssets():

  • Sub-1e12 allocations deposit 0 (100% loss of the requested amount; all stranded as ETH dust):

  • Caps can be tripped due to over-reported “change”:

Impact Details

  • Systematic realized loss from dust:

    • Each allocation can leave up to < 1e12 wei (~1e-6 ETH) stranded. At ~$4,000/ETH, that is ~$0.004 per allocation.

    • 250 allocations ≈ $1 of loss; over many operations, losses accumulate.

  • Sub-1e12 allocations:

    • Entire requested amount becomes stranded as ETH dust (0 deposited to the pool), causing a 100% loss of that request in accounting terms.

  • Cap/Accounting integrity:

    • Allocation is overstated by the dust amount, potentially tripping absolute/relative caps spuriously and misrepresenting exposure.

  • Deallocation failures (DoS of allocator actions):

    • _deallocate may revert because the strategy checks WETH balance ≥ requested amount while dust is left as ETH; this can block deallocation in certain edge cases.

Vulnerability classification: Accounting error leading to loss of funds (rounding/dust), cap mis-accounting, and potential DoS of deallocation.

References

  • Affected code:

    • Wrong return in _allocate:

  • realAssets() excludes ETH dust:

  • Deallocation require on WETH:

  • Change propagation to caps:

  • Accrual based on adapters’ realAssets():

  • Adapter return used as “change”:

  • Proof-of-concept tests:

    • Over-reported allocation and floored realAssets():

  • Sub-1e12 allocation deposits 0:

  • Cap exceeded due to over-reported change:

  • Recommendation (concise):

    • Return amountToDeposit from _allocate.

    • Immediately re-wrap dust ETH back to WETH to keep balances consistent and avoid realized loss.

    • In _deallocate, wrap existing ETH dust before computing remaining redemption, then wrap all redeemed ETH to meet the requested WETH amount.

https://gist.github.com/IronsideSec/e0e6e14f6f6bd6e8700a654450a309c0

Proof of Concept

Proof of Concept

  1. replace src/test/strategies/StargateEthPoolStrategy.t.sol with whole file in https://gist.github.com/IronsideSec/e0e6e14f6f6bd6e8700a654450a309c0

  2. run forge t --mt test_allocate -vvvv

Was this helpful?