#41894 [SC-Critical] Incorrect calculation of deposited rewards yeet leads to Staker's not being able to get their staked amount back
Was this helpful?
Was this helpful?
Submitted on Mar 19th 2025 at 08:41:12 UTC by @Oxgritty for
Report ID: #41894
Report Type: Smart Contract
Report severity: Critical
Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol
Impacts:
Protocol insolvency
StakingV2::accumulatedDeptRewardsYeet
is used to calculate the amount of accumulated rewards in yeet that are not distributed yet.
The problem here is that, this function doesn't account for the fact that when a staker calls StakingV2::startUnstake
to start unstaking, their balance is still in the contract but subtracted from the totalSupply
, so StakingV2::accumulatedDeptRewardsYeet
mistakes the unstake amount as undistributed rewards.
When the manager would want to distribute rewards through StakingV2::executeRewardDistributionYeet
, he would first call the StakingV2::accumulatedDeptRewardsYeet
to know the amount available for distribution. This function would return an amount which includes the unStakeAmount, which staker unstaked by calling StakingV2::startUnstake
.
When the manager will call StakingV2::executeRewardDistributionYeet
, it will convert (undistributed rewards + unStakeAmount) into vaultShares
, meaning when the staker would call StakingV2::unstake
to get his unStakeAmount amount back, this contract won't have enough unStakeAmount of stakingToken and function will revert.
Stakers not being able to withdraw their staked amount as the contract will not have enough funds.
totalSupply = 0
stakingToken.balanceOf(address(this)) = 0
Alice(a staker) calls StakingV2::stake
to stake 100e18 Yeet.
Bob(another staker) calls StakingV2::stake
to stake 50e18 Yeet.
Current Balances:
totalSupply = 150e18
stakingToken.balanceOf(address(this)) = 150e18
Alice initiates unstaking process by calling StakingV2::startUnstake
where unStakeAmount=100e18
Current Balances:
totalSupply = 50e18
stakingToken.balanceOf(address(this)) = 150e18
StakingV2::accumulatedDeptRewardsYeet
. This function will return 100e18(150e18 - 50e18).StakingV2::executeRewardDistributionYeet
to distribute yeet rewards.Here the value of (stakingParams.amount0Max + swapData.inputAmount would be equal to 100e18). After the execution of this function, the contract would have 100e18 yeet worth of vault Shares and 50e18 Yeet.
Current Balance:
totalSupply = 50e18
stakingToken.balanceOf(address(this)) = 50e18
Alice calls StakingV2::unstake
to get her unStakeAmount(100e18) back.
This function will fail because StakingV2.sol has only 50e18 Yeet.
When a staker calls StakingV2::startUnstake
his unstake amount will be from the totalSupply
.
This unstake amount is not in StakingV2::accumulatedDeptRewardsYeet
.