58362 sc low users will lose tokemak rewards earned in tokeautoethstrategy

Submitted on Nov 1st 2025 at 15:36:32 UTC by @oxrex for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58362

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

    • Permanent freezing of unclaimed yield

Description

Brief/Intro

TokeAutoEthStrategy contract will have its TokeMak reward tokens stuck inside the strategy contract and these tokens won't be sent to the VaultV2 contract as the protocol assumed.

Vulnerability Details

In the _deallocate function below, I will explain why that will be the case:

function _deallocate(uint256 amount) internal override returns (uint256) {
        uint256 sharesNeeded = autoEth.convertToShares(amount);
        uint256 actualSharesHeld = rewarder.balanceOf(address(this));
        uint256 shareDiff = actualSharesHeld - sharesNeeded;
        if (shareDiff <= 1e18) {
            // account for vault rounding up
            sharesNeeded = actualSharesHeld;
        }
        // withdraw shares, claim any rewards
        rewarder.withdraw(address(this), sharesNeeded, true);
        uint256 wethBalanceBefore = TokenUtils.safeBalanceOf(address(weth), address(this));
        autoEth.redeem(sharesNeeded, address(this), address(this));
        uint256 wethBalanceAfter = TokenUtils.safeBalanceOf(address(weth), address(this));
        uint256 wethRedeemed = wethBalanceAfter - wethBalanceBefore;
        if (wethRedeemed < amount) {
            emit StrategyDeallocationLoss("Strategy deallocation loss.", amount, wethRedeemed);
        }
        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;
    }

In the Rewarder contract for TokeAuto vaults from the Autopilot protocol, when a user calls the rewarder.withdraw() function, the rewarder contract sends autoETH tokens to the user/caller. It also sends along accumulated TokeMak (TOKE token) portion of reward tokens to the same caller/user.

So, if say the strategy deposits 100 WETH and gets minted x amount of autoETH which then gets staked into the rewarder contract, when a user then calls withdraw on the rewarder contract, the rewarder contract

  1. Sends autoETH requested by the user to the user (these autoETH will then be exchanged to WETH in the redeem() call to autoETH contract)

  2. Sends accumulated unclaimed rewards (TOKE tokens) to the same user

This goes against the assumption of Alchemix because Alchemix assumes and needs these reward tokens to go to the VaultV2 contract this strategy is tied to, not stay inside the strategy contract. As a result, these tokens sent during that withdraw() call will be stuck and cannot be retrieved out of the strategy contract rendering a loss.

For an attacker to make this the case, they can:

  1. Deposit a small amount of assets into the VaultV2 contract tied to this strategy contract

  2. Later, these assets will be allocated to the TokeAutoETHStrategy contract along with other user deposits. Let's assume the attacker deposits 0.5 WETH or 0.1 etc and then other users deposit e.g 10 WETH, 20 WETH etc

  3. After the allocation of these WETH to the strategy contract which then swaps these into autoETH and deposits them into the rewarder contract to earn rewards, the attacker can wait a couple hours and then unstake small amounts from their initial 0.5 WETH deposit.

  4. Doing this, since the VaultV2 will have had all of its WETH allocated to the strategy, the _deallocate call will be triggered for each of his withdrawals and the rewarder.withdraw() function will be triggered which will:

  5. Withdraw x amount of shares that is enough to cover x amount of user requested WETH

  6. Send all accumulated TOKE rewards to the strategy contract instead of the VaultV2 contract

Now, the attacker will be able to aggregate all the accumulated TOKE rewards thus far into the strategy contract and other users who deposited large sums of WETH that were allocated to this strategy will all lose their TOKE portion of yield which cannot be recovered from the strategy contract.

Note: My POC above demonstrates the locked TOKE tokens inside the strategy contract as you can see from the logs:

The revert is from the AutoPool Debt external contract that the Rewarder contract interacts with during forked tests and calls chainlink for prices which is not relevant to this bug but that is the reason for the revert in the end.

Also, note the added detailed log I added to the _deallocate() function in the TokeAutoEthStrategy contract to speed up understanding of the bug:

Impact Details

Complete loss of accumulated TOKE yield everytime the bug I described above in the vulnerability details section gets triggered. Since these reward tokens then get diverted to the strategy contract rather than the VaultV2 contract, they cannot be reclaimed.

From my POC and a 1 month yield gains duration, the locked TOKE tokens would be 35k TOKE which is currently valued at USD 7000 since each TOKE is worth USD 0.2.

References

https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/strategies/mainnet/TokeAutoEth.sol#L67C5-L88

Proof of Concept

Proof of Concept

Was this helpful?