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

```Staking::claimRewards``` incorrect reward calculation on multiple deposits due to timestamp mismanagement

Summary

The Staking::claimRewards function icalculates rewards based on the number of weeks since the last claim. However, when a soulmate deposits multiple times within the weeks, the rewards are calculated incorrectly because the deposit timestamp is not tracked.

Vulnerability Details

function claimRewards() public {
uint256 soulmateId = soulmateContract.ownerToId(msg.sender);
// first claim
if (lastClaim[msg.sender] == 0) {
lastClaim[msg.sender] = soulmateContract.idToCreationTimestamp(
soulmateId
);
}
// How many weeks passed since the last claim.
// Thanks to round-down division, it will be the lower amount possible until a week has completly pass.
uint256 timeInWeeksSinceLastClaim = ((block.timestamp -
lastClaim[msg.sender]) / 1 weeks);
if (timeInWeeksSinceLastClaim < 1)
revert Staking__StakingPeriodTooShort();
lastClaim[msg.sender] = block.timestamp;
// Send the same amount of LoveToken as the week waited times the number of token staked
@> uint256 amountToClaim = userStakes[msg.sender] *
timeInWeeksSinceLastClaim;
loveToken.transferFrom(
address(stakingVault),
msg.sender,
amountToClaim
);
emit RewardsClaimed(msg.sender, amountToClaim);
}

Impact

Run the first test:

function test_WrongClaimRewardsOnMultipleDeposit() public {
uint256 balancePerSoulmates = 5e18;
uint256 weekOfStaking = 5;
_depositTokenToStake(balancePerSoulmates);
console2.log("loveToken balance of soulmate1", loveToken.balanceOf(soulmate1));
vm.expectRevert();
stakingContract.claimRewards();
vm.warp(block.timestamp + weekOfStaking * 1 weeks + 1 seconds);
vm.startPrank(soulmate1);
// First claim
stakingContract.claimRewards();
assertTrue(loveToken.balanceOf(soulmate1) == weekOfStaking * balancePerSoulmates);
console2.log("loveToken balance of soulmate1", loveToken.balanceOf(soulmate1));
vm.warp(block.timestamp + weekOfStaking * 1 weeks + 1 days);
//Second claim
stakingContract.claimRewards();
console2.log("loveToken balance of soulmate1", loveToken.balanceOf(soulmate1));
}
[PASS] test_WrongClaimRewardsOnMultipleDeposit() (gas: 505355)
Logs:
loveToken balance of soulmate1 0
loveToken balance of soulmate1 25000000000000000000
loveToken balance of soulmate1 50000000000000000000

The soulmate1 rewards after 5 weeks for 5 deposited loveToken is 25e18 and after other 5 weeks + 1 day is again 25e18. Rewards Total amount = 50e18.

Run the second test, where the solmates1 deposit other 5 token after the first claim.

function test_WrongClaimRewardsOnMultipleDeposit() public {
uint256 balancePerSoulmates = 5e18;
uint256 weekOfStaking = 5;
_depositTokenToStake(balancePerSoulmates);
console2.log("loveToken balance of soulmate1", loveToken.balanceOf(soulmate1));
vm.expectRevert();
stakingContract.claimRewards();
vm.warp(block.timestamp + weekOfStaking * 1 weeks + 1 seconds);
vm.startPrank(soulmate1);
//First claim
stakingContract.claimRewards();
assertTrue(loveToken.balanceOf(soulmate1) == weekOfStaking * balancePerSoulmates);
console2.log("loveToken balance of soulmate1", loveToken.balanceOf(soulmate1));
vm.warp(block.timestamp + weekOfStaking * 1 weeks + 1 days);
//Deposit again
loveToken.approve(address(stakingContract), 5e18);
stakingContract.deposit(5e18);
//Second claim
stakingContract.claimRewards();
console2.log("loveToken balance of soulmate1", loveToken.balanceOf(soulmate1));
}
[PASS] test_WrongClaimRewardsOnMultipleDeposit() (gas: 516234)
Logs:
loveToken balance of soulmate1 0
loveToken balance of soulmate1 25000000000000000000
loveToken balance of soulmate1 70000000000000000000

The soulmate1 rewards after 5 weeks for 5 deposited loveToken is 25e18 and after other 5 weeks + 1 day now is 50e18. This is wrong because the second claim is done subsquently to the deposit. No time is passed so the second deposit amount hasn't accrued rewards. He/she should have received only 25e18 also on the second claim.

Tools Used

Manual review

Recommendations

The rewards calculation logic should be adjusted considering also if a new deposit has occurred and on what timestamp.

Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-claimRewards-multi-deposits-time

High severity, this allows users to claim additional rewards without committing to intended weekly staking period via multi-deposit/deposit right before claiming rewards.

Support

FAQs

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