Function Staking::claimRewards
does not work as expected
The function Staking::claimRewards
is expected to give rewards to users that have staked tokens in the contract for weeks
However, it does not check when some tokens were deposited, only how much time passed since the user called Staking::claimRewards
, so it is possible to claim rewards for tokens deposited seconds ago as if they were staked for weeks!
Breaks the whole purpose of the contract. There is no need to deposit the token in the contract weeks before claiming them.
Foundry
Proof of Concept:
1- User has LoveToken and has not called the function Staking::claimRewards
in a long time (or never).
2- User deposits all the tokens he has
3- Just after, user calls Staking::claimRewards
4- The rewards are returned as if the tokens were staked there since the last time the user called Staking::claimRewards
Recommended Mitigation:
Adding a call to Staking::claimRewards
in Staking::deposit
, therefore every time user deposits, it is given the rewards from the tokens staked since last time he deposited.
For that mitigation, Staking::claimRewards
has to be slightly reworked as well.
Justification for the Mitigation given:
Maybe it is a bit difficult to understand why that mitigation would solve the problem. This example will serve as a justification and an explanation:
Case 1:
User1 minted the NFT, was given 3 tokens and deposited them 5 weeks ago, then deposited other 4 tokens 3 weeks ago and lastly 2 tokens just now
User1 never called the function Staking::claimRewards
. Therefore the function sets the time passed as 5 weeks, when he minted the NFT. In the current version, then the function would return (3+4+2) * 5 = 45 tokens
Case 2:
User2 minted the NFT, was given 3 tokens 5 weeks ago, other 4 tokens 3 weeks ago and lastly 2 tokens just now. But he never deposited them in the contract. He just got them outside
User2 never called the function Staking::claimRewards
. Therefore the function sets the time passed as 5 weeks, when he minted the NFT. So, he deposits his (3+4+2) tokens and calls the function just after. The result of the function Staking::claimRewards
? (3+4+2) * 5 = 45 tokens
Wow, it did not matter that User1 staked some tokens 5 weeks ago! it only mattered that the function Staking::claimRewards
was not called in that time! so both cases got same tokens in return, even though User2 staked all of them just seconds before the call to Staking::claimRewards
!
In fact, not even Case 1 was managed properly, some tokens should have been weighted as being staked for 5 weeks, other for 3 and others for seconds. But all of them were taken into account as if the 9 tokens were staked all 5 weeks ago!
In case 1, Staking::claimRewards
should return 35 (tokens * weeks staked) + 43 + 2*0 = 27
In case 2, Staking::claimRewards
should return 9*0 = 0
What would happen if every time somebody deposited, it called Staking::claimRewards
inside the Staking::deposit
function before adding the tokens deposited to the userStakes
:
Using Case 1:
1º: Deposits 3 tokens just after minting the nft -> calls Staking::claimRewards
, first time after minting seconds ago, does not enter in the if clause because did not pass a week since minting, 0 rewards claimed
2: Deposits 4 tokens 2 weeks later -> calls Staking::claimRewards
, 2 weeks since last call to Staking::claimRewards
, 3*2 (tokens staked prior to current deposit * weeks since last call to Staking::claimRewards
) = 6 rewards claimed in this call
3: Deposits 2 tokens 3 weeks later -> calls Staking::claimRewards
, 3 weeks since last call to Staking::claimRewards
, 7*3 (tokens staked prior to current deposit * weeks since last call to Staking::claimRewards
) = 21 rewards claimed in this call
Total Rewards = 21 + 6 = 27 ! The amount that should be given in this case!
High severity, this allows users to claim additional rewards without committing to intended weekly staking period via multi-deposit/deposit right before claiming rewards.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.