Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: low
Valid

A soulmate lose the staking rewards if he/she Staking::withdraw before to Staking::claimRewards

Summary

A soulmate who withdraws their staked tokens before claiming rewards will not receive any rewards for the period during which they had their tokens staked even if is passed at least 1 week (minimum staking reward period).

Vulnerability Details

The withdraw function doesn't have any logic that checks if the soulmate has unclaimed rewards.

function withdraw(uint256 amount) public {
// No require needed because of overflow protection
userStakes[msg.sender] -= amount;
loveToken.transfer(msg.sender, amount);
emit Withdrew(msg.sender, amount);
}

Impact

First scenario: The soulmate claims the rewards before to withdraw the staked amount.

function testWithdrawAmountBeforeClaim() public {
//Mint an NFT - 2 soulmates are reuinited
_mintOneTokenForBothSoulmates();
vm.startPrank(soulmate1);
//Give love token to soulmate1
vm.warp(block.timestamp + 14 days);
airdropContract.claim();
uint256 solmate1Amount = loveToken.balanceOf(soulmate1);
console2.log("soulmate1 loveToken amount after airdrop claim", solmate1Amount);
//Deposit
loveToken.approve(address(stakingContract), solmate1Amount);
stakingContract.deposit(solmate1Amount);
console2.log("soulmate1 loveToken after deposit", loveToken.balanceOf(soulmate1));
//Claim
vm.warp(block.timestamp + 7 days);
stakingContract.claimRewards();
console2.log("soulmate1 loveToken amount after 7 days staking claim", loveToken.balanceOf(soulmate1));
//Claim again
vm.warp(block.timestamp + 14 days);
stakingContract.claimRewards();
console2.log("soulmate1 loveToken after withdraw", loveToken.balanceOf(soulmate1));
//Withdraw
stakingContract.withdraw(solmate1Amount);
console2.log("soulmate1 loveToken after withdraw", loveToken.balanceOf(soulmate1));
vm.stopPrank();
}
[PASS] testWithdrawAmountBeforeClaim() (gas: 372034)
Logs:
soulmate1 loveToken amount after airdrop claim 14000000000000000000
soulmate1 loveToken after deposit 0
soulmate1 loveToken amount after 7 days staking claim 42000000000000000000
soulmate1 loveToken amount after 14 days staking claim 70000000000000000000
soulmate1 loveToken after withdraw 84000000000000000000

The soulmate has a final balance of loveToken of 84000000000000000000.

Second scenario: The soulmate claims the rewards after withdrawing the staked amount.

function testWithdrawAmountBeforeClaim() public {
//Mint an NFT - 2 soulmates are reuinited
_mintOneTokenForBothSoulmates();
vm.startPrank(soulmate1);
//Give love token to soulmate1
vm.warp(block.timestamp + 14 days);
airdropContract.claim();
uint256 solmate1Amount = loveToken.balanceOf(soulmate1);
console2.log("soulmate1 loveToken amount after airdrop claim", solmate1Amount);
//Deposit
loveToken.approve(address(stakingContract), solmate1Amount);
stakingContract.deposit(solmate1Amount);
console2.log("soulmate1 loveToken after deposit", loveToken.balanceOf(soulmate1));
//Claim
vm.warp(block.timestamp + 7 days);
stakingContract.claimRewards();
console2.log("soulmate1 loveToken amount after 7 days staking claim", loveToken.balanceOf(soulmate1));
//Withdraw
vm.warp(block.timestamp + 14 days);
stakingContract.withdraw(solmate1Amount);
console2.log("soulmate1 loveToken after withdraw", loveToken.balanceOf(soulmate1));
//Claim after
stakingContract.claimRewards();
console2.log("soulmate1 loveToken amount after 14 days staking claim", loveToken.balanceOf(soulmate1));
vm.stopPrank();
}
[PASS] testWithdrawAmountBeforeClaim() (gas: 372033)
Logs:
soulmate1 loveToken amount after airdrop claim 14000000000000000000
soulmate1 loveToken after deposit 0
soulmate1 loveToken amount after 7 days staking claim 42000000000000000000
soulmate1 loveToken after withdraw 56000000000000000000
soulmate1 loveToken amount after 14 days staking claim 56000000000000000000

The soulmate has a final balance of loveToken of 56000000000000000000. He/she loses 28000000000000000000 loveToken rewards.

Tools Used

Manual review

Recommendations

Implementing a mechanism that either automatically claims rewards before allowing withdrawals or informs users that they must claim rewards before withdrawing.

Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Other
kiteweb3 Submitter
over 1 year ago
0xnevi Lead Judge
over 1 year ago
kiteweb3 Submitter
over 1 year ago
0xnevi Lead Judge
over 1 year ago
kiteweb3 Submitter
over 1 year ago
0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-withdraw-before-claimReward

If we we implement a correct claimRewards function with its intended logic, this would indeed be an issue. I believe low severity for this findings and its duplicates to be appropriate given it is dependent on users lack of understanding of claiming rewards first before a withdrawal.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.