57957 sc medium loss of eulereth vault yields for euler weth strategy

Submitted on Oct 29th 2025 at 15:29:47 UTC by @oxrex for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #57957

  • Report Type: Smart Contract

  • Report severity: Medium

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

  • Impacts:

    • Permanent freezing of unclaimed yield

Description

Brief/Intro

When we allocate assets to third party protocols such as the EulerWETH vault on Ethereum mainnet, we get minted shares by Euler in return and during redemption of shares, we will receive more assets than we supplied into Euler. However, in the current implementation of the EulerWETHStrategy contract, these yields will not be claimed and lost.

Vulnerability Details

The problem arises because when we interact with the Euler WETH vault, we mainly work with asset and not shares and thus are constrained to the caps[id].allocation of the strategy leading us to lose the yield gained and only being able to withdraw back the initial supplied assets.

function allocateInternal(address adapter, bytes memory data, uint256 assets) internal {
        require(isAdapter[adapter], ErrorsLib.NotAdapter());

        accrueInterest();

        SafeERC20Lib.safeTransfer(asset, adapter, assets);

            // @note this function will ultimately deposit x amount of assets into Euler WETH through EulerWETHStrategy.allocate()
@>        (bytes32[] memory ids, int256 change) = IAdapter(adapter).allocate(data, assets, msg.sig, msg.sender);

        for (uint256 i; i < ids.length; i++) {
            Caps storage _caps = caps[ids[i]];
            _caps.allocation = (int256(_caps.allocation) + change).toUint256();

            require(_caps.absoluteCap > 0, ErrorsLib.ZeroAbsoluteCap());

            require(_caps.allocation <= _caps.absoluteCap, ErrorsLib.AbsoluteCapExceeded());

            require(
                _caps.relativeCap == WAD || _caps.allocation <= firstTotalAssets.mulDivDown(_caps.relativeCap, WAD),
                ErrorsLib.RelativeCapExceeded()
            );
        }
        emit EventsLib.Allocate(msg.sender, adapter, assets, ids, change);
    }
  1. The VaultV2 contract holds 100 WETH which Bob deposited.

  2. Later, admin allocates 100 WETH to EulerWETHStrategy. This will call the Euler WETH vault deposit function on ETH mainnet. The _caps.allocation = (int256(_caps.allocation) + change).toUint256() will then be 100 WETH because _caps.allocation of 0 plus change of 100 equals 100 WETH allocation

  3. A year goes by, users request withdraw or we just want to deallocate from the EulerWETHStrategy. We will only be able to withdraw 100 WETH whereas our asset balance inside Euler vault on Mainnet is now approximately 100.9 WETH because we have earned 0.9 WETH yield for the year. The function sequence will be as below:

  1. As we can already see above from the numbers and my POC attached with this report, only 100 WETH will be withdrawn from Euler whereas the 0.9 WETH yield will be lost in this case.

Impact Details

Yield gains earned from Euler WETH vault will not be withdrawable and only our initial assets supplied can be withdrawn. Also, even if we try to somehow withdraw those yield in any other way, the calls will revert since the allocation subtraction will underflow. This would result in the yield gains ultimately not being withdrawable.

For the EulerWETHStrategy vault, we should work with shares rather than assets when withdrawing from Euler. Or rather, we should expose a claim function inside the EulerWETHStrategy contract that overrides the _claimRewards() function declared in MYTStrategy.sol as this will allow us claim all rewards based on our available shares balance.

References

https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/strategies/mainnet/EulerWETHStrategy.sol?utm_source=immunefi

Proof of Concept

Proof of Concept

Was this helpful?