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

From `Staking::claimRewards()` Users Can Claim Rewards Without Waiting for 1 Week

Users Can Claim Rewards Without Waiting for 1 Week from Staking::claimRewards(), Violating the Staking Rule and Rapidly Decreasing LoveTokens in StakingVault.

Description: Users can claim rewards before the required 1-week duration from the Staking contract. Consider a scenario where two users mint a Soulmate NFT token. As per the protocol rule, each user can claim 1 LoveToken per day from the Airdrop. If they or one of them refrains from claiming LoveTokens from the Airdrop for 7 days and then attempts to claim their LoveTokens from the Airdrop, they will receive 7 tokens each from the Airdrop Vault, which is acceptable. However, if they deposit their LoveTokens for staking at the 'Staking' contract using Staking::deposit(uint256 amount), the protocol breaks when they call Staking::claimRewards without waiting for the mandated 1-week duration. The issue lies in the following section of the Staking::claimRewards() function:

if (lastClaim[msg.sender] == 0) {
@> lastClaim[msg.sender] = soulmateContract.idToCreationTimestamp(
soulmateId
);
}

The problem is that lastClaim[msg.sender] calculates the claiming time based on the creation of the Soulmate NFT using soulmateContract.idToCreationTimestamp(soulmateId). This timestamp represents the time of NFT creation, not the time of depositing the token into the staking contract. Consequently, users do not need to wait 7 days after depositing their LoveTokens for staking before claiming rewards, which violates the staking and rewards claiming rule.

Impact: Viloates Staking And Rewards Claimig Rule.

Proof of Concept: The provided proof of code demonstrates how users can claim rewards without waiting for 1 week.

POC for Users Claiming Rewards Without Waiting For 1 Week
function test_withoutwaiting1WeekClaiMreward() public {
_withdraw7TokenAfterDays();
uint256 amountToDeposit = loveToken.balanceOf(soulmate1);
console.log(amountToDeposit);
uint256 balanceOfStakinVaultBefore = loveToken.balanceOf(address(stakingVault));
vm.startPrank(soulmate1);
loveToken.approve(address(stakingContract), amountToDeposit);
stakingContract.deposit(amountToDeposit);
vm.stopPrank();
vm.startPrank(soulmate1);
stakingContract.claimRewards();
vm.stopPrank();
uint256 balanceOfStakinVaultAfter = loveToken.balanceOf(address(stakingVault));
assert(balanceOfStakinVaultAfter == balanceOfStakinVaultBefore - amountToDeposit);
}
// helper function
function _withdraw7TokenAfterDays() internal{
_mintOneTokenForBothSoulmates();
vm.warp(block.timestamp + 7 days +1 seconds);
vm.startPrank(soulmate1);
airdropContract.claim();
vm.stopPrank();
vm.startPrank(soulmate2);
airdropContract.claim();
vm.stopPrank();
}

Recommended Mitigation: This issue can be mitigted by updating the both Staking::deposit(uin256 amount) and Staking::claimrewards() function

if (loveToken.balanceOf(address(stakingVault)) == 0)
revert Staking__NoMoreRewards();
// No require needed because of overflow protection
+ lastClaim[msg.sender] = block.timestamp; solution to claimrewad before weeks and overfunding of rewads
userStakes[msg.sender] += amount;
loveToken.transferFrom(msg.sender, address(this), amount);
emit Deposited(msg.sender, amount);
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);
}
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.