summary
In the GaugeController.sol distributeRewards() and _distributeToGauges() function, when the rewards are distributed to a guage, the guage's lastRewardTime is not updated.
Vulnerability Details
* @notice Distributes rewards to a gauge
* @dev Calculates and transfers rewards based on gauge weight
* @param gauge Address of gauge to distribute rewards to
*/
function distributeRewards(address gauge) external override nonReentrant whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (!gauges[gauge].isActive) revert GaugeNotActive();
uint256 reward = _calculateReward(gauge);
if (reward == 0) return;
IGauge(gauge).notifyRewardAmount(reward);
emit RewardDistributed(gauge, msg.sender, reward);
}
Impact
This will result in stale data when rewards are updated to a guage.
This mapping mapping(address => Gauge) public gauges will always return a stale lastRewardTime for all gauges, and every gauge will retain the same lastRewardTime set during addGauge, regardless of when rewards are actually distributed.
Tools Used
manual review
Recommendations
function distributeRewards(address gauge) external override nonReentrant whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (!gauges[gauge].isActive) revert GaugeNotActive();
uint256 reward = _calculateReward(gauge);
if (reward == 0) return;
+ gauges[gauge].lastRewardTime = block.timestamp;
IGauge(gauge).notifyRewardAmount(reward);
emit RewardDistributed(gauge, msg.sender, reward);
}
in _distributeToGauges() function apply this change.
// Second pass: distribute rewards
for (uint256 i = 0; i < _gaugeList.length; i++) {
address gauge = _gaugeList[i];
if (gauges[gauge].isActive && gauges[gauge].gaugeType == gaugeType) {
uint256 gaugeShare = (amount * gaugeWeights[i]) / totalTypeWeight;
if (gaugeShare > 0) {
IGauge(gauge).notifyRewardAmount(gaugeShare);
+ gauges[gauge].lastRewardTime = block.timestamp;
}
}
}