#42020 [SC-Critical] Inaccurate calculation in `accumulatedDeptRewardsYeet()` causes double counting of vesting tokens as excess, leading to permanent loss of user funds
Submitted on Mar 20th 2025 at 04:47:18 UTC by @nnez for Audit Comp | Yeet
Report ID: #42020
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
Description
The StakeV2
contract contains a vulnerability in the calculation of excess staking tokens available for reward distribution. The function accumulatedDeptRewardsYeet()
incorrectly calculates the amount of excess staking tokens by simply subtracting totalSupply
from the contract's token balance:
The core issue is that when users initiate unstaking via the startUnstake()
function, the totalSupply
is immediately reduced, but the tokens remain in the contract pending vesting:
This creates a discrepancy where tokens that are already allocated for vesting (and thus belong to specific users) are incorrectly considered as "excess" by the accumulatedDeptRewardsYeet()
function. When a manager calls executeRewardDistributionYeet()
using this value, these vesting tokens may be swapped to vault tokens, effectively taking more tokens that belongs to staking balance not excess.
Example scenario
The contract has 10,000 YEET tokens with
totalSupply
also at 10,000 YEETA user initiates unstaking of 5,000 YEET, reducing
totalSupply
to 5,000 while the contract still holds 10,000 YEETaccumulatedDeptRewardsYeet()
now returns 5,000 YEET (10,000 - 5,000)A manager calls
executeRewardDistributionYeet()
and swaps these 5,000 tokens, the contract now has 5,000 YEET tokensThe vesting period ends, a user withdraw their unlocked tokens, now the contract has 5,000-5,000 = 0 YEET tokens
The remaining stakers won't be able to unstake their position because YEET tokens are depleted.
Impact
This vulnerability creates contract insolvency as the system incorrectly treats vesting tokens as excess rewards. Over time, as tokens in vesting are double-counted and distributed as rewards, the contract will have insufficient funds to honor all unstaking requests. Early unstakers may receive their tokens, but later unstakers will find the contract depleted leaving remaining stakers with permanent loss of their funds.
Recommended Mitigations
Keep track of the total unvesting amount
Use total unvesting amount in excess calculation
Proof of Concept
Proof-of-Concept
The following test demonstrate the described scenario in which remaining stakers cannot unstake due to insufficient balance of staking tokens in the contract.
Steps
Add the following test in
StakeV2.test.sol
Run
forge t --mc StakeV2_IncorrectExcess --mt test_incorrectExcessCalculation -vvvv
Observe that the last unstake fails due to insufficent balance despite a positive totalSupply (indicating that there is remaining stakers in the contract).
Was this helpful?