Discrepency b/w the lastRewadTime
and the lastAllPoolUpdate
can allow for incorrect reward distribution to pools if registerRewardDeposit
deposits less assets
#108
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
M-31
primary issue
Highest quality submission among a set of duplicates
🤖_69_group
AI based duplicate group recommendation
satisfactory
satisfies C4 submission criteria; eligible for awards
selected for report
This submission will be included/highlighted in the audit report
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
sufficient quality report
This report is of sufficient quality
Lines of code
https://github.com/code-423n4/2024-07-loopfi/blob/57871f64bdea450c1f04c9a53dc1a78223719164/src/reward/ChefIncentivesController.sol#L920-L922
Vulnerability details
Impact
Incorrect reward distribution causing some pools to gain more while others to gain less
Proof of Concept
The
_massUpdatePools
function always setslastAllPoolUpdate = block.timestamp
But the individualPool update timestamp can become lower than block.timestamp if it surpasses the
endRewardTime
ie. the time in which the entire assets deposited is supposed to be depletedFurther more, the
endRewardTime
is calculated asnewEndTime = (unclaimedRewards + extra) / rewardsPerSecond + lastAllPoolUpdate
whenever therewardsPerSecond
is non-zeroHence if a
_massUpdatePools
call occurs whenblock.timestamp
is greater than the endRewardTime it will set thelastRewardTime
of pools to be less thanlastAllPoolUpdate
, following which even if there are no more rewards, the newendRewardTime
would be the timestamp oflastAllPoolUpdate
. This will allow a pool to claim rewards worth(lastAllPoolUpdate - initialEndTime) * rewardPerSecond
which is not an expected behaviour and not handled with the endRewardTime constaintThe above scenario can occur if the
registerRewardDeposit
function is invoked with a low amount of deposits (ie. the deposited amount shouldn't cause the new end time to be >= block.timestamp) when theendRewardTime
has been surpassedEg:
pool A and B, 1:1 rewardRatio
rewardsPerSecond = 1
endRewardTime = 100
block.timestamp = 110
registerRewardDeposit is invoked such that the new endRewardTime (ie. even after including the newly deposited assets) is 105
now the first massUpdatePools call will result in:
lastRewardTime = 105 (endRewardTime)
lastAllPoolUpdate = 110
now the entire rewards of the contract are used up
but when
massUpdatePools
gets invoked again (eg. viaclaim
->_updateEmissions
->_massUpdatePools
), the new endTime will be 110 (ie. lastPoolUpdate + 0)claim is called for pool A in the same block,
A's lastRewardTime gets updated to 110 while B's remain 105
at 120 registerRewardDeposit is invoked with a lot of assets,
B will accrue a reward of (120 - 105)/2 while A will only accrue (120 - 110)/2
Tools Used
Manual review
Recommended Mitigation Steps
In case
endTime > block.timestamp
, can set thelastPoolUpdate
toendTime
or always ensure that theregisterRewardDeposit
function will only be called with amounts such that the above issue doesn't occurAssessed type
Context
The text was updated successfully, but these errors were encountered: