In TempleGoldStaking
contract, vestingPeriod
can be updated by calling setVestingPeriod
. However, updating vestingPeriod
affects the vestingRate
of stakers
whose rewards are still not vested fully. This can also result claiming of more rewards by the users or revert
of withdraw
, withdrawAll
and getReward
function.
vestingRate
of staker
for an epoch
is calculated using function _getVestingRate
. The function uses _stakeInfo.fullyVestedAt
to determine if rewards are fully vested. If rewards are not fully vested, the vestingRate
is calculated using timeDifference
and vestingPeriod
. However, vestingPeriod
used may be different from when user
did the staking if vestingPeriod
is updated later.
There can be following 2 vulnerabilities due to this:
If the new vestingPeriod
is less than old vestingPeriod
, rewards will be fully vested before _stakeInfo.fullyVestedAt
. The issue with this is vestingRate
of users in this case can exceed1e18
which can result in more amounts of rewards to be claimed by the user than their earned amount.
Steps to reporduce:
Initial vestingPeriod
= 5 days = 5 * 24 * 3600 seconds
reward.periodFinish
= 24 hours = 24 * 3600 seconds
rewardRate
= 5
1) User calls stake
at time = 0
seconds with amount
= 1 ether
. _stakeInfos
will be set as following:
rewardData
and other params will be set as following:
After 24 hours, at time = 24 * 3600
. reward period is finished
2) User calls getReward
with index = 1
at time = 24 hours = 24 * 3600
.
rewardData
and other params will be updated as following:
3) owner
updates vestingPeriod
to 1 days
.
3) At time = 4 days and 23 hours = 119 * 3600 seconds
. User calls getReward
with index = 1
.
Thus, claimableRewards
of user is more than expected due to increased vestingPeriod
.
If new vestingPeriod
is more than old vestingPeriod
, the vestingRate
will be less than expected while rewards will again be fully vested at _stakeInfo.fullyVestedAt
. This type of vesting doesn't make sense for users because it won't be linear vesting. In this case, functions like withdraw
, withdrawAll
and getReward
may revert for some stakers due to underflow
in _earned
function.
Let's see how can underflow happen in _earned
function in this case.
Some points to note:
rewardData.rewardPerTokenStored
will keep on increasing as time passes because rewards
will be accumulated. It will increase in linear proportion till reward duration ends if there are no additional staking or withdrawals.
userRewardPerTokenPaid[_account][_index]
stores vestingRate * rewardData.rewardPerTokenStored
when any user action is done(eg. staking
, withdraw
or claim of rewards
.)
Steps to reporduce:
Initial vestingPeriod
= 1 days = 24 * 3600 seconds
reward.periodFinish
= 18 hours = 18 * 3600 seconds
rewardRate
= 5
1) User calls stake
at time = 15
seconds with amount
= 1 ether
. _stakeInfos
will be set as following:
rewardData
and other params will be set as following:
2) User calls getReward
with index = 1
at time = 17 hours and 15 seconds = 17 * 3600 + 15 seconds
.
rewardData
and other params will be updated as following:
3) after 1 hour at time = 18 hours and 15 seconds = 18 * 3600 + 15 seconds
, reward's period finishes. owner
updates vestingPeriod
to 2 days
.
New vesting Period = 2 days = 2 * 24 * 3600 seconds
after 15 seconds
at time = 18 * 3600 + 15 + 15 seconds
, user calls withdraw
which will revert due to underflow
.
3) User calls withdrawAll
with index = 1
at time = 18 * 3600 + 30 seconds
.
calling updateReward
for the user and index
will revert. rewardData
and other params will be updated as following:
NOTE: vestingRate
of user decreased due to new vesting period
update in vestingPeriod
will affect previously opened positions. Users can claim more funds than their expected rewards if new vestingPeriod
is less.
If new vestingPeriod
is more than previous vestingPeriod
, functions like withdraw
, withdrawAll
and getReward
may revert due to underflow
.
Manual review and calculation on pen-paper.
Add the vestingPeriod
in stakeInfo
struct and do not use the updated vestingPeriod
for the previously opened positions.
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.