Not taking into account the tokens in the vesting process will cause the distribution not to occur as intended.
Vulnerability Details
The startUnstake process starts a 10-day vesting period. Then unstaking becomes possible. Alternatively, unstaking is possible before the vesting period ends with rageQuit().
However, this reward distribution (executeRewardDistributionYeet function) does not take into account the tokens in the vesting process, and causes the tokens in the vesting process to be distributed.
This causes the "stakingToken.balanceOf(address(this))" value to be lower than totalSupply after users unstake. In other words, it causes a loss of funds.
Impact Details
Tokens in the vesting period are included in the reward distribution, which will result in a loss of funds.
In the StakeV2 contract, a variable named totalStartUnstakeToken should be defined. This variable should be incremented by the unStakeAmount during the startUnstake process, and decremented when an unstake is performed. Additionally, when calculating accumulatedDeptRewardsYeet, the totalStartUnstakeToken value should be taken into account.
Proof of Concept
Proof of Concept
Staker1 and Staker2 users stake 50 tokens.
Staker1 user calls the startUnstake function to unstake 50 tokens and the 10-day vesting period begins.
The Manager performs the distribution with the executeRewardDistributionYeet() function according to the result obtained from the accumulatedDeptRewardsYeet() function.
After 10 days, Staker1 unstakes its tokens. The StakeV2 balance is reset.
Staker2 user calls the startUnstake function to unstake 50 tokens and the 10-day period begins.
After 10 days, Staker2 user cannot unstake his tokens. Tokens are no longer available in StakeV2.
/// @notice The total supply of the staking token
uint256 public totalSupply;
+ uint256 public totalStartUnstakeToken;
function accumulatedDeptRewardsYeet() public view returns (uint256) {
- return stakingToken.balanceOf(address(this)) - totalSupply;
+ return stakingToken.balanceOf(address(this)) - totalSupply - totalStartUnstakeToken;
}
function startUnstake(uint256 unStakeAmount) external {
require(unStakeAmount > 0, "Amount must be greater than 0");
require(stakedTimes[msg.sender] < STAKING_LIMIT, "Amount must be less then the STAKING_LIMIT constant"); // DOS protection https://github.com/Enigma-Dark/Yeet/issues/12
_updateRewards(msg.sender);
uint256 amount = balanceOf[msg.sender];
require(amount >= unStakeAmount, "Insufficient balance");
balanceOf[msg.sender] -= unStakeAmount;
totalSupply -= unStakeAmount;
+ totalStartUnstakeToken += unStakeAmount;
...