TempleGold

TempleDAO
Foundry
25,000 USDC
View results
Submission Details
Severity: low
Valid

Continuous Reward Period Extension in `TempleGoldStaking::_notifyReward` Leads to Reward Rate Dilution

Summary

The _notifyReward function in the TempleGoldStaking contract incorrectly extends the reward period every time new rewards are added. Due to frequent calls to distributeRewards, this causes continuous period extension and progressive dilution of the reward rate, severely impacting the staking mechanism's effectiveness.

Vulnerability Details

In the _notifyReward function:

function _notifyReward(uint256 amount) private {
if (block.timestamp >= rewardData.periodFinish) {
rewardData.rewardRate = uint216(amount / rewardDuration);
// collect dust
nextRewardAmount = amount - (rewardData.rewardRate * rewardDuration);
} else {
uint256 remaining = uint256(rewardData.periodFinish) - block.timestamp;
uint256 leftover = remaining * rewardData.rewardRate;
rewardData.rewardRate = uint216((amount + leftover) / rewardDuration);
// collect dust
nextRewardAmount = (amount + leftover) - (rewardData.rewardRate * rewardDuration);
}
rewardData.lastUpdateTime = uint40(block.timestamp);
rewardData.periodFinish = uint40(block.timestamp + rewardDuration); //@audit
}

The function always extends periodFinish, and for ongoing periods, it recalculates the reward rate based on the extended duration. With distributeRewards callable every 10 seconds to 1 minute (as per test configurations), this leads to:

  1. Continuous extension of the reward period.

  2. Constant recalculation and dilution of the reward rate.

  3. Discrepancy between intended and actual reward distribution timeframes.

Note: The current implementation also blocks calls to setVestingPeriod and setRewardDuration functions due to their checks if (rewardData.periodFinish >= block.timestamp) { revert InvalidOperation(); }. As the period finish time is constantly extended, these critical configuration functions become unusable, further compromising the contract's adaptability and management.

Impact

Severe Reward Rate Dilution: As the period is continuously extended and the reward rate recalculated, the effective reward rate for stakers dramatically decreases over time. This results in stakers receiving significantly fewer rewards than intended, potentially approaching zero as the period extends indefinitely.

Recommendation

Modify the _notifyReward function to maintain fixed-duration reward periods:

function _notifyReward(uint256 amount) private {
if (block.timestamp >= rewardData.periodFinish) {
rewardData.rewardRate = uint216(amount / rewardDuration);
// collect dust
nextRewardAmount = amount - (rewardData.rewardRate * rewardDuration);
+ rewardData.periodFinish = uint40(block.timestamp + rewardDuration);
} else {
uint256 remaining = uint256(rewardData.periodFinish) - block.timestamp;
uint256 leftover = remaining * rewardData.rewardRate;
rewardData.rewardRate = uint216((amount + leftover) / rewardDuration);
// collect dust
nextRewardAmount = (amount + leftover) - (rewardData.rewardRate * rewardDuration);
}
rewardData.lastUpdateTime = uint40(block.timestamp);
- rewardData.periodFinish = uint40(block.timestamp + rewardDuration);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

nisedo Submitter
11 months ago
inallhonesty Lead Judge
11 months ago
inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

setRewardDuration and setVestingPeriod can be griefed from anyone when distributionStarter is unset

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.