Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

Period is not updated correctly for the gauge

Summary

Period is not updated correctly for the gauge even when there is update of period in the gauge controller. This causes only 1 period to be initialzed for the gauge thus rendering it for future use because once max emissions are reached for a guage for that particular period then notifying of the rewards will not be possible for the subsequent periods.

Vulnerability Details

Following is updatePeriod function in base gauge contract

function updatePeriod() external override onlyController {
uint256 currentTime = block.timestamp;
uint256 periodEnd = periodState.periodStartTime + getPeriodDuration();
if (currentTime < periodEnd) {
revert PeriodNotElapsed();
}
uint256 periodDuration = getPeriodDuration();
// Calculate average weight for the ending period
uint256 avgWeight = periodState.votingPeriod.calculateAverage(periodEnd);
// Calculate the start of the next period (ensure it's in the future)
uint256 nextPeriodStart = ((currentTime / periodDuration) + 2) * periodDuration;
// Reset period state
periodState.distributed = 0;
periodState.periodStartTime = nextPeriodStart;
// Create new voting period
TimeWeightedAverage.createPeriod(
periodState.votingPeriod,
nextPeriodStart,
periodDuration,
avgWeight,
WEIGHT_PRECISION
);
}

We can clearly see that it resets the distributed variable which is essential in rewards distribution. Once max distribution has reached for a period it needs to be reset for that gauge then only the gauge would be useful otherwise it will remain useless. Now see that update period call has access control i.e it can only be called by controller. But there is no updateperiod call from the the gauge controller contract thus period can never be updated.

Even when gauge period is updated in the gauge controller there is no update period call made to the gauge

function updatePeriod(address gauge) external override whenNotPaused {
Gauge storage g = gauges[gauge];
if (!g.isActive) revert GaugeNotActive();
TimeWeightedAverage.Period storage period = gaugePeriods[gauge];
uint256 duration = g.gaugeType == GaugeType.RWA ? 30 days : 7 days;
// If this is the first period, initialize it
if (period.startTime == 0) {
TimeWeightedAverage.createPeriod(
period,
// Add 1 second to avoid timestamp collision
block.timestamp + 1,
duration,
0,
g.weight
);
emit PeriodRolled(gauge, block.timestamp, g.weight);
return;
}
// Check if current period has elapsed
if (block.timestamp < period.startTime + period.totalDuration) {
revert PeriodNotElapsed();
}
uint256 average = TimeWeightedAverage.calculateAverage(period, block.timestamp);
TimeWeightedAverage.createPeriod(
period,
// Add 1 second to avoid timestamp collision
block.timestamp + 1,
duration,
average,
g.weight
);
emit PeriodRolled(gauge, block.timestamp, g.weight);
}

Impact

Period can never be updated for the gauge making the gauge useless after one period.

Tools Used

Manual review

Recommendations

Call update period from the gauge controller properly

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BaseGauge::updatePeriod uses ((currentTime / periodDuration) + 2) calculation causing entire reward periods to be skipped, resulting in permanent loss of user rewards

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BaseGauge::updatePeriod uses ((currentTime / periodDuration) + 2) calculation causing entire reward periods to be skipped, resulting in permanent loss of user rewards

Support

FAQs

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

Give us feedback!