In the BaseGauge contract, the _updateReward
function updates the lastUpdateTime
using the lastTimeRewardApplicable
function. However, due to an incorrect implementation of the periodFinish
function, the reward period never properly ends. As a result, rewards continue to accrue indefinitely, allowing users to earn rewards beyond the intended period.
What Went Wrong:
The lastTimeRewardApplicable
function is defined as:
and the periodFinish function is implemented as:
Here, getPeriodDuration() returns a fixed duration (e.g., 7 days for RAAC or 30 days for RWA). However, since lastUpdateTime is updated in the _updateReward function:
each interaction updates lastUpdateTime to the current block.timestamp. This means that periodFinish() becomes block.timestamp + getPeriodDuration() on every call, and the condition in lastTimeRewardApplicable almost always returns block.timestamp.
Consequently, the reward calculation never recognizes that the reward period has ended, and users continue to accumulate rewards even after the intended period expiration.
Why It Matters:
The intended design is that once the reward period expires, no additional rewards should accrue. However, due to the shifting lastUpdateTime, the system incorrectly treats every call as if it were still within an active reward period. This leads to an unintended and continuous accumulation of rewards.
Infinite Reward Accrual:
Users can earn rewards indefinitely because the reward period never properly closes.
Economic Imbalance:
This bug undermines the reward distribution mechanism, potentially leading to excessive token minting and diluting the value of rewards.
Exploitation Risk:
Malicious users could take advantage of the perpetual reward accrual to manipulate the system's economics, affecting both current and future stakeholders.
Manual Code Review: We analyzed the implementation of _updateReward, lastTimeRewardApplicable, and periodFinish functions, and identified that updating lastUpdateTime on every call prevents the reward period from ever ending.
Separate Reward Period Tracking:
Redesign the reward period logic so that periodFinish is calculated based on a fixed start time for the reward period rather than on the mutable lastUpdateTime. For example:
This change ensures that the reward period is fixed and does not shift with each user interaction.
Adjust Update Mechanism:
Modify _updateReward to update lastUpdateTime appropriately without affecting the fixed reward period. Ensure that new stakes or withdrawals do not inadvertently extend the reward period.
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.