#42382 [SC-Critical] Calling `StakeV2::executeRewardDistributionYeet` by manager during an ongoing unstaking period for stakers can result in them being unable to unstake permanently
Was this helpful?
Was this helpful?
Submitted on Mar 23rd 2025 at 13:15:22 UTC by @hustling0x for
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
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.
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:
A yeetard stakes 1000 $YEET -> totalSupply
= 1000 $YEET, accumulatedDeptRewardsYeet
returns 0;
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
.
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
.
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