56859 sc medium lp underlying mismatch in stargateethpoolstrategy deallocate causes withdrawal dos

Submitted on Oct 21st 2025 at 10:16:27 UTC by @yesofcourse for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #56859

  • 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 1 hour

Description

Brief/Intro

The Optimism StargateEthPoolStrategy treats a caller-supplied underlying amount (WETH) as if it were LP shares during deallocation and also over-reports what it allocates after rounding down deposits.

In practice this causes under-redemption and a hard require revert when exiting, freezing vault withdrawals/rebalances that depend on this strategy. When the LP rate is favorable, excess ETH/WETH becomes stranded on the strategy, creating accounting drift (reduced realized returns).

Vulnerability Details

A) Unit mismatch on deallocation (LP != underlying). The strategy sizes the LP redemption as lpNeeded = amount, implicitly assuming 1:1 LP→underlying, but later enforces a WETH balance check against that same amount:

// inside _deallocate(uint256 amount)
uint256 lpNeeded = amount;                // !! treats underlying as LP shares
pool.redeem(lpNeeded, address(this));     // returns ETH based on current exchange rate
// ...wrap some ETH to WETH...
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;

If 1 LP < 1 ETH, redeeming lpNeeded = amount returns less than amount ETH; wrapping only the newly redeemed ETH leaves WETH < amount and the require reverts (withdrawal DoS). If 1 LP > 1 ETH, the function still approves/returns only amount, stranding surplus WETH on the strategy.

B) Over-reporting on allocation + unwrapped dust. On deposit, the adapter unwraps WETH to ETH and rounds down to a 1e12 multiple, but returns the original amount to the vault and leaves ETH dust on the contract:

During exit, _deallocate wraps only the new ETH redeemed, ignoring any historical ETH dust that would otherwise bridge the shortfall. The subsequent WETH ≥ amount check then fails.

  • The strategy API semantically treats amount as underlying (WETH): it checks WETH balance, approves WETH, and returns amount. Sizing the LP redemption as lpNeeded = amount is therefore inconsistent and unsafe for share-based LP tokens.

  • Returning the unrounded amount in _allocate misleads the vault about what was actually put to work and increases the likelihood that _deallocate(amount) will fail later.

Scenario PoC - coded test in next section

  1. Allocate an amount not divisible by 1e12 (e.g., 1e18 - 1), which forces rounding down and creates ETH dust. _allocate returns the unrounded amount => vault thinks the full amount was deployed.

  2. With LP rate = 1.0 (to isolate the effect), call _deallocate for the same amount. The adapter wraps only the newly redeemed ETH (ignoring the dust), then reverts on require(WETH >= amount).

  3. Changing the rate > 1.0 shows the complementary issue: the call succeeds but leaves surplus WETH stranded on the strategy (accounting drift).

Impact Details

Temporary freezing of funds for at least 1 hour.**

  • Any vault withdrawal/rebalance that requires deallocation from this strategy can revert, temporarily freezing the portion of user funds routed through this adapter until conditions change or the contract is patched.

This is deterministic when the recorded allocation exceeds what can be redeemed or when rounding dust is left unwrapped.

Secondary operational impacts

  • Contract fails to deliver promised returns, but doesn’t lose value: when the exchange rate > 1, over-redemption strands surplus WETH on the strategy, reducing realized yield for depositors and skewing accounting.

  • Cumulative dust growth: repeated allocations build up raw ETH dust, increasing the chance of future deallocation reverts.

This is not theft; it is a correctness issue that leads to service disruption and value marooned on the adapter. An adversary (or just organic conditions) can push the system into repeated failures by timing exits under unfavorable LP rates or after many rounded allocations.

References

  • Affected file: src/strategies/optimism/StargateEthPoolStrategy.sol

    • _allocate(uint256 amount) — rounds down deposit but returns the unrounded amount (over-reporting). https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/strategies/optimism/StargateEthPoolStrategy.sol#L52

    • _deallocate(uint256 amount) — sizes redemption as lpNeeded = amount (unit mismatch), wraps only newly redeemed ETH, then requires WETH ≥ amount. https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/strategies/optimism/StargateEthPoolStrategy.sol#L60

Proof of Concept

Proof of Concept

Insrc/test/strategies/StargateEthPoolStrategy.t.sol:

  1. Add these imports right below the existing two imports at the top:

  1. Append the following three blocks at the very end of the file:

  • Harness that exposes the internal functions:

  • Tiny local mocks:

  • The PoC test:

  1. Run with:

Was this helpful?