The BaseGauge contract operates on a period-based distribution where rewards accrue linearly. However, there is a mismanagement issue with how rewards are calculated and distributed within a given period, which can lead to inconsistent reward emissions.
The contract defines a period with a start timestamp and an end time, during which emissions occur linearly. When rewards are sent using notifyRewardAmount, they are subject to an emission cap:
The function notifyReward is used to calculate the reward rate:
The periodDuration function
The contract assumes that every reward distribution spans the entire periodDuration (typically 7 days). This creates an issue when additional rewards are added mid-period:
Assume the period has an emission cap of 20,000.
The controller calls notifyRewardAmount(10,000), setting the rewardRate as:
rewardRate = 10,000 / 7 days
After 3 days, the controller adds another reward of 10,000, assuming it will be distributed over the remaining 4 days.
However, the rewardRate is recalculated as:
rewardRate = 10,000 / 7 days, instead of 10,000 / 4 days.
This leads to a misalignment between the intended and actual reward distributions.
The period is set to end in 4 days, but the newly added reward is incorrectly scheduled to be distributed over 7 days. As a result, the portion of the reward intended for the last 3 days (10,000 / 3) will not be properly allocated.
run in BaseGauge.test.js
In the second case where reward are split user get less than the first case
To clearly see the issue we should fix another issue in the BaseGauge contract
In the second test, the user receives less reward than in the first test because the rewardRate is incorrectly calculated and split over 7 days in both calls to notifyReward. Here's a more explicit explanation:
First call to notifyReward with 500:
The rewardRate is calculated as 500 / 7, which means the reward is distributed evenly over 7 days.
So, rewardRate = 500 / 7 ≈ 71.428 per day.
Second call to notifyReward with 500:
The rewardRate is incorrectly updated by adding 500 / 7 again, instead of considering the remaining duration (which is now 3 days).
So, rewardRate += 500 / 7 ≈ 71.428, resulting in a total rewardRate ≈ 142.856 per day.
Accumulated reward calculation:
For the first 4 days, the user earns 4 * (500 / 7) = 284.
For the next 3 days, the user earns 3 * (500 / 7) + 3 * (500 / 7)≈ 426.
The total reward becomes 500 + 426 ≈ 710, which is less than the expected amount.
The rewardRate should be adjusted based on the remaining duration when the second reward is added. Here's how it should work:
First call to notifyReward with 500:
rewardRate = 500 / 7 ≈ 71.428 per day.
Second call to notifyReward with 500:
The remaining duration is 3 days, so the rewardRate for the new reward should be 500 / 3 ≈ 166.666 per day.
The total rewardRate should now be 71.428 + 166.666 ≈ 238.094 per day.
Accumulated reward calculation:
For the first 7 days, the user earns 4 * (500 / 7) = 284.
For the next 3 days, the user earns 3 * (500 / 3) + 3 * (500 / 7)≈ 713.
The total reward becomes 284 + 713 ≈ 997, which is the correct amount.
Unfair Reward Distribution: claimers may receive less rewards.
Modify the notifyReward function to correctly account for the remaining period duration instead of assuming a fixed periodDuration.
Instead of using getPeriodDuration(), compute the actual remaining period dynamically:
This ensures that rewards added mid-period are distributed over the correct duration, avoiding misalignment.
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.