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

```Staking::claimRewards``` Incorrect Calculation of Rewards Based on Soulmate Creation Time

Summary

The Staking::claimRewards function incorrectly uses the Soulmate creation timestamp (Soulmate::idToCreationTimestamp) as the basis for calculating rewards for first-time claims. This approach does not accurately reflect the user's staking period and result in incorrect reward calculations.

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

function testWrongClaimRewards() public {
//Mint an NFT - 2 soulmates are reuinited
vm.prank(soulmate1);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate2);
soulmateContract.mintSoulmateToken();
console2.log("The soulmates reunion block.timestamp is:", block.timestamp);
console2.log("idToCreationTimestamp", soulmateContract.idToCreationTimestamp(0));
vm.startPrank(soulmate1);
//Give love token to soulmate1
vm.warp(block.timestamp + 14 days);
console2.log("Claim period after soulmates reunion:", block.timestamp / 86400);
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);
//The soulmate1 claim the token and deposits them
console2.log("The soulmate1 deposit block.timestamps:", block.timestamp / 86400);
//Claim
//Solamte1 tries to claim the rewards
stakingContract.claimRewards();
console2.log("soulmate1 loveToken amount after staking claim", loveToken.balanceOf(soulmate1));
assertEq(loveToken.balanceOf(soulmate1), 0);
vm.stopPrank();
}
[FAIL. Reason: assertion failed] testWrongClaimRewards() (gas: 408381)
Logs:
The soulmates reunion block.timestamp is: 1
idToCreationTimestamp 1
Claim perdiod after soulmates reunion: 14
soulmate1 loveToken amount after airdrop claim 14000000000000000000
The soulmate1 deposit block.timestamps: 14
soulmate1 loveToken amount after staking claim 28000000000000000000
Error: a == b not satisfied [uint]
Left: 28000000000000000000
Right: 0

The soulmate1 claims and deposits their loveToken at block.timestamp + 14 days. At the same time he/she calls the claimRewards rewards function. The solmate1 should receive 0 token because 0 days are passed from the deposit time (the staking reward is fixed at 1 token/week). But the solmate1 receives 28 loveToken, because the basis for calculating rewards for first-time claims is incorrectly done on the Soulmate::idToCreationTimestamp and not on deposit Token time.

Tools Used

Manual review

Recommendations

Modify the claimRewards function to use the deposit timestamp as the starting point for calculating rewards. Implement a mechanism to track the first deposit timestamp for each user and ensure that the lastClaim mapping is updated accordingly. Consider adding checks to prevent users from claiming rewards before making a deposit.

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.