#42382 [SC-Critical] Calling `StakeV2::executeRewardDistributionYeet` by manager during an ongoing unstaking period for stakers can result in them being unable to unstake permanently

Submitted on Mar 23rd 2025 at 13:15:22 UTC by @hustling0x for Audit Comp | Yeet

  • Report ID: #42382

  • Report Type: Smart Contract

  • Report severity: Critical

  • Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol

  • Impacts:

    • Permanent freezing of funds

Description

Brief/Intro

The StakeV2::executeRewardDistributionYeet method uses StakeV2::accumulatedDeptRewardsYeet to determine the amount of $YEET to zap in and distribute as excess rewards. However, the StakeV2::accumulatedDeptRewardsYeet method falsely returns accumulated rewards in $YEET tokens when there are users who have requested to unstake via StakeV2::startUnstake. This means that every time StakeV2::executeRewardDistributionYeet is called by a manager, the tokens intended to be unstaked by the users will be sent to the Zapper and distributed as excess rewards, resulting in a permanent freeze and permanent inability for users to withdraw their staked $YEET positions.

Vulnerability Details

When a user calls StakeV2::startUnstake, the totalSupply of staked tokens ($YEET) in the contract is reduced by the amount requested for unstake. This way, the calculation in StakeV2::accumulatedDeptRewardsYeet will return a false result, not taking into account the amounts pending to be unstaked.

For example:

  1. A yeetard stakes 1000 $YEET -> totalSupply = 1000 $YEET, accumulatedDeptRewardsYeet returns 0;

  2. He calls StakeV2::startUnstake to request unstake -> totalSupply = 0 $YEET, but now accumulatedDeptRewardsYeet returns 1000 $YEET, because of the calculation stakingToken.balanceOf(address(this)) - totalSupply;

This means at any time there are pending unstaking periods running for users and the manager calls StakeV2::executeRewardDistributionYeet, users' tokens will be zapped and distributed as excess rewards, and they will not be able to unstake them via StakeV2::unstake or StakeV2::rageQuit.

Impact Details

Although the method StakeV2::executeRewardDistributionYeet is only callable by a manager, calling it while there are active unstaking periods ongoing will result in DoS and permanent freezing of users' staked funds.

The proposed solution is to keep track of the total pending unstakes for the contract and deduct them in the calculation in StakeV2::accumulatedDeptRewardsYeet.

The totalPendingUnstakeAmount state should be updated when StakeV2::startUnstake is called and when the user actually unstakes either via StakeV2::unstake or StakeV2::rageQuit.

References

  • https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol#L148

  • https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol#L158

  • https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol#L255

Proof of Concept

Proof of Concept

Was this helpful?