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;
}
}
}