Open code423n4 opened 2 years ago
Duplicate of #141
The warden has shown how a desynch between periodFinish
and DURATION
can cause rewards to be distributed faster than a reward period, leaving the last claimers unable to claim.
While the loss in this scenario, to those claimers can be viewed as total, in reality those people will be able to claim on the next rewards round (unless no more tokens will be minted to that gauge, which is highly unlikely but possible).
For that reason, I believe Medium Severity to be more appropriate, as this will be a temporary loss of yield which for some people may be permanent
Lines of code
https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L597-L612
Vulnerability details
In
Gauge.sol#notifyRewardAmount()
, the updated rewardRate for the token:rewardRate[token]
is calculated based on the newly added amount of tokens (amount
), the remaining amount of existing rewards (_left
), and theDURATION
.While the DURATION is
5 days
, the period from the current time toperiodFinish[token]
is much longer.rewardPerToken()
is calculated based on the current time,lastUpdateTime[token]
, andrewardRate[token]
.https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L375-L380
lastUpdateTime[token]
will frequently be updated to the current timestamp by_updateRewardForAllTokens()
.See: https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L460-L469
As a result,
rewardPerToken()
can be much higher than expected, which makes the total amount of reward tokens less than the total amount of rewards accumulated by all the users.This makes the users who claim the rewards later unable to retrieve their rewards as the balance can be insufficient.
PoC
Alice and Bob both deposited
1,000
stake token to Gauge at1653091200
(May 21 2022 00:00:00 GMT+0000)Admin called
notifyRewardAmount()
add 1,000 DAI at1653100000
(May 21 2022 02:26:40 GMT+0000)bribeStart
= 1653004800 (May 20 2022 00:00:00 GMT+0000)adjustedTstamp
= 1653609600 (May 27 2022 00:00:00 GMT+0000)periodFinish[DAI]
= 1654041600 (Jun 01 2022 00:00:00 GMT+0000)lastUpdateTime[DAI]
= 1653100000rewardRate[DAI]
= 1,000 * 1e18 / 432000 = 2314814814814815getReward()
at1654041800
Jun 01 2022 00:03:20 GMT+0000, get ~1,000 DAIrewardPerTokenStored[DAI]
= ~1e18getReward()
, the transaction will revert due to insufficient balance.Recommendation
Consider calculating
rewardRate
base on timeUntilNextPeriodFinish to next period finish: