The _updateReward function in BaseGauge.sol sets state.lastUpdateTime to block.timestamp without ensuring that block.timestamp is less than or equal to periodFinish(). This oversight could lead to incorrect reward calculations, particularly if block.timestamp exceeds periodFinish(). As a result, users may receive more rewards than they are entitled to, or rewards may be calculated based on an incorrect time frame.
The issue is located in the _updateReward function in BaseGauge.sol:
The vulnerability arises because state.lastUpdateTime is set directly to block.timestamp without using the lastTimeRewardApplicable() function, which ensures that the timestamp used for reward calculations does not exceed periodFinish(). This inconsistency could lead to the following issues:
Reward Calculation Errors: If state.lastUpdateTime is set to a time after periodFinish(), reward calculations could be incorrect, potentially allowing users to claim more rewards than they are entitled to.
Exploitation: An attacker could manipulate the timing of transactions to maximize their rewards by exploiting this inconsistency.
The lastTimeRewardApplicable() function is defined as follows:
This function ensures that the timestamp used for reward calculations does not exceed periodFinish(). However, it is not used when setting state.lastUpdateTime in _updateReward.
The incorrect setting of state.lastUpdateTime in _updateReward has a significant impact on the contract's functionality. Since the updateReward modifier is used in 6 critical functions, the mistake propagates across the entire reward distribution mechanism. Below is an analysis of how each function is affected, along with the corresponding code:
stake FunctionPurpose: Allows users to stake tokens and start earning rewards.
Impact: If state.lastUpdateTime is set incorrectly, the user's rewards will be calculated based on an incorrect time frame, potentially overpaying or underpaying rewards.
Code:
withdraw FunctionPurpose: Allows users to withdraw staked tokens and claim rewards.
Impact: Incorrect state.lastUpdateTime could lead to overpayment of rewards during withdrawal, as the time elapsed calculation would include periods beyond periodFinish().
Code:
getReward FunctionPurpose: Allows users to claim their accumulated rewards.
Impact: If state.lastUpdateTime is set incorrectly, users could claim rewards for a period when no rewards should be distributed, leading to overpayment.
Code:
notifyRewardAmountFunctionPurpose: Updates the reward rate and distribution period.
Impact: If state.lastUpdateTime is set incorrectly, it could affect the reward calculations for all users, leading to systemic overpayment or underpayment of rewards.
Code:
voteDirection FunctionPurpose: Allows users to vote on the direction of rewards.
Impact: If state.lastUpdateTime is set incorrectly, it could affect the reward calculations for the voter, leading to unfair distribution of rewards.
Code:
checkpoint FunctionPurpose: Creates a checkpoint for reward calculations.
Impact: If state.lastUpdateTime is set incorrectly, the checkpoint will be based on an incorrect time frame, leading to inaccurate reward calculations.
Code:
Scenario: Suppose periodFinish() is set to 1000, and block.timestamp is 1200.
Current Behavior: state.lastUpdateTime is set to 1200 (which is after periodFinish()).
Expected Behavior: state.lastUpdateTime should be set to 1000 (the value returned by lastTimeRewardApplicable()).
Consequence: When rewards are calculated, the time elapsed will be based on 1200 instead of 1000, leading to incorrect rewards.
Manual Review
To mitigate this issue, state.lastUpdateTime should be set using lastTimeRewardApplicable() instead of block.timestamp. This ensures that state.lastUpdateTime does not exceed periodFinish().
_updateReward Function: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.