The reward per token variable is the heart of the staking mechanism, calculating how much rewards people are eligible for, calculated based on the current supply and time passed. During regular auctions, it's value always starts from 0 and keeps an accurate representation of rewards earned. However the TempleGoldStaking
runs all of the auctions inside itself, without resetting the reward per token from previous auctions, leading to overinflation of the earned rewards for further auctions, which would unintetionally brick the rewards for stakers.
The easiest way to showcase this is with an example scenario:
User A stakes for the first time. We do the staking first since the contract MUST have staked tokens before beginning the first auction. The user stakes 1000 tokens, so the totalSupply and his balance are 1000, rewardPerToken and claimable are 0. The lastUpdateTime will be 0 for simplicity
We begin an auction with 10000 TGLD and 10000 duration, so the rewardRate = 1. The lastUpdate = 1000 for the example
Nobody else stakes for the entire 10000 duration. User A can either claim or hold for new auctions. This is intended and he should be able to claim all of his rewards at all times.
A new auction begins with the same parameters: 10000 TGLD and 10000 duration, so the rewardRate = 1, lastUpdate = 10000. This time the rewardPerToken will get updated via it's formula (10000 - 1000) / 1000 * 1e18 = 9e18
. This value holds the rewards User A can claim from the first auction.
User B stakes into the second auction. The user stakes 1000 tokens, so the totalSupply is 2000, his balance is 1000, lastUpdate = 11000, which leads to rewardPerToken getting updated to 10e18
(due to 1000 seconds passing)
The second auction finishes as well. User A has had stakes for both auctions and wishes to claim. He initiates a claim at the finish timestamp 20000. This means his rewardPerToken is calculated as (20000 - 11000)e18 / 2000 + 10e18 = 14.5e18
. Now he should receive his claimable tokens based on this value, which are calculated as 1000(his stake) * 14.5(rewardPerToken) - claimable(0 until now) = 14500 TGLD
. For User A this seems fair, since he is eligible for all rewards from auction 1 and a part of auction 2 rewards. However rewardPerToken for User B has the same value of 14.5e18, leading to the same calculation as above.
User B is eligible for the same amount of rewards as User A even though he had been staking for only 1 auction instead of 2. This, and also considering the fact that the total TGLD of both auctions is 20000 but the earned for the users is 29000, leads to users being unable to claim their rewards after the first claimer.
User are unable to fully claim their funds after the first claimer. This happens for every auction after the first one, since the rewardPerToken does not get reset to 0 for the new stakers.
Manual Review
The rewardPerToken should get reset every new auction. It's value should only be perceived for users who are keeping their stake between auctions, to allow them to claim past AND current 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.