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

Staking Duration not properly accounted for, leading to the staker earning more reward

Summary

A user can earn more rewards, longer than the time he has staked his LoveTokens, this can happen for first stake, and also happen when the user withdraw their stake and restakes it.

Vulnerability Details

In the claim function below, lastClaim[msg.sender] is based on the time the Soulmate NFT was minted for the first claim, this calculation is wrong and would lead to the user getting more reward greater than the duration, in which his LoveToken has been in the protocol.

function claimRewards() public {
uint256 soulmateId = soulmateContract.ownerToId(msg.sender);
//@audit lastClaim is set to NFT creation time instead of staking time
// 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);
}

##POC

function test_Poc_ClaimRewards_Initial() public {
//Alice and Bob are soulmates
//They have collected 28days worth of love token
//Meaning their soulmate NFT was minted 28 days ago
//Soulmate1 decided to stake his token for one week
//By this he should be getting just 28 LoveTokens,
//But he will get more than that, he get 5 weeks worth of LoveTokens,
//Instead of 1 week he has staked for
uint balancePerSoulmates = 28 ether;
_giveLoveTokenToSoulmates(balancePerSoulmates);
vm.startPrank(soulmate1);
loveToken.approve(address(stakingContract), balancePerSoulmates);
stakingContract.deposit(balancePerSoulmates);
vm.warp(block.timestamp + 1 weeks + 60 seconds);
stakingContract.claimRewards();
assertTrue(loveToken.balanceOf(soulmate1) == balancePerSoulmates * 5);
}

Impact

A user will game the protocol to earn more reward

Tools Used

Manual Analysis

Recommendations

Set create a variable to track the first time the user is depositing to the protocol, so this variable would be set as lastClaim for the first claim.

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.