58357 sc low permanent freezing of tokeautoeth strategy rewards in myt vault

Submitted on Nov 1st 2025 at 15:21:56 UTC by @call_me_rp for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58357

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

    • Permanent freezing of unclaimed royalties

    • Permanent freezing of unclaimed yield

Description

Summary

Rewards generated by the TokeAutoEth strategy are claimed into the MYT vault (address(MYT)). Once received or staked under the MYT address, these rewards (TOKE token) cannot be withdrawn, transferred, or reallocated. As a result, all rewards are permanently stuck in the MYT contract.

Description

When claimRewards() is called, the strategy executes:

MYTStrategy.sol::
    function claimRewards() public virtual returns (uint256) {
        require(!killSwitch, "emergency");
        _claimRewards();
    }

It calls into TokeAutoEth.sol::
    function _claimRewards() internal override returns (uint256 rewardsClaimed) {
        rewardsClaimed = rewarder.earned(address(this));
>>      rewarder.getReward(address(this), address(MYT), false); // transfer all TOKE token rewards to morpho vault or get staked on myt address
    }
  • The Tokemak rewarder then either transfers the TOKE tokens or stakes them with recipient = address(MYT):

[Link to Tokemak rewarder code]](https://github.com/Tokemak/v2-core-pub/blob/de163d5a1edf99281d7d000783b4dc8ade03591e/src/rewarders/AbstractRewarder.sol#L306C4-L331C6)

Since the MYT vault has no functionality to transfer, withdraw, or re-allocate these TOKE rewards, all claimed rewards becomes permanently locked.

Additionally, anyone can call claimRewards(), meaning the freeze can occur at any time, even if rewards are small and unintended.

Impact

All TOKE rewards generated by the strategy become locked into myt.

Recommendation

Claim rewards to the strategy instead of the MYT vault:

rewarder.getReward(address(this), address(this), false);

Then implement appropriate handling in the strategy:

Optionally swap to underlying asset, OR sends to owner

Introduce unstake() too to get staked toke.

Proof of Concept

Proof of Concept

PoC Summary

Since the real rewarder contract on mainnet distributes rewards based on live protocol state, the test environment cannot naturally reproduce accrued rewards. To demonstrate the issue in isolation, we replaced the rewarder contract implementation at its address using vm.etch and pointed its rewardToken storage slot to a mock ERC20 token. The mock rewarder is intentionally minimal and always returns a fixed reward amount (5e18) for earned() and sends that amount in getReward().

By doing this, we simulate the presence of claimable rewards without altering the whole rewarder logic. When claimRewards() is executed, the adapter puts recipient as myt, and then those reward token(TOKE token) will always stuck in vault/MYT.

  1. Add below test case and interfaces into TokeAutoEthStrategy.t.sol:

  1. run by forge test --mt test_rewards_stuck_into_myt -vvvv

  2. output:

Was this helpful?