The BaseGauge::notifyReward function calculates new reward rates without accounting for remaining rewards from previous periods. This results in permanent loss of any undistributed rewards when new rewards are added, as the contract overrides the previous rate without carrying forward residual amounts.
Morever, in GaugeController::distributeRewards, anyone can call the function to distribute rewards to the gauges, making this scenario even more likely to happen.
Admin notifies 1000 tokens for 7-day distribution (142.85 tokens/day rate)
After 3 days: 428.55 tokens distributed, 571.45 remain in contract
Admin notifies another 1000 tokens
New rate becomes (1000 / 7) = 142.85 tokens/day, ignoring 571.45 remaining tokens
Total distributable becomes 1000 new + 571.45 old = 1571.45, but actual distribution will only be 1000 tokens
Add this test to BaseGauge.test.js:
High severity - Direct loss of protocol-controlled value. Undistributed rewards become permanently stuck in the contract, violating core protocol functionality and reward distribution guarantees.
Carry Forward Residual Rewards:
Sweep Functionality:
Implement emergency function to recover stuck rewards after sufficient time:
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.