Core Contracts

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

`BaseGauge::updatePeriod` Skips Periods Due to Incorrect `nextPeriodStart` Calculation

Summary

The BaseGauge::updatePeriod function miscalculates the next period start time, leading to skipped periods. This issue impacts gauges that rely on accurate period tracking, potentially causing incorrect emissions and reward distributions.

Vulnerability Details

The updatePeriod function behaves as follows:

  • When called within the current period, it reverts because the period has not yet elapsed.

  • When called in a new period, it calculates the next period start time using the following formula:

    uint256 nextPeriodStart = ((currentTime / periodDuration) + 2) * periodDuration;
  • This calculation advances the period by two additional periods, instead of just moving to the next period.

  • As a result, the function skips multiple periods (e.g., moving 3 weeks ahead instead of 1 week).

Impact

This vulnerability disrupts the continuous progression of rewards and voting periods, leading to:

  • Missed reward distributions due to skipped periods.

  • Inaccurate time-weighted averages, affecting calculations relying on period tracking.

POC

The following test demonstrates how the incorrect nextPeriodStart calculation causes period skipping.

//Paste into test/unit/core/governance/gauges/RAACGauge.test.js
it("should demonstrate period skipping in updatePeriod due to incorrect nextPeriodStart calculation", async () => {
// Get initial period state
const initialWeeklyState = await raacGauge.periodState();
const firstPeriodStart = BigInt(initialWeeklyState.periodStartTime);
const periodDuration = BigInt(WEEK);
const firstPeriodEnd = firstPeriodStart + periodDuration;
console.log("Initial period start:", firstPeriodStart.toString());
console.log("Period duration:", periodDuration.toString());
console.log("First period end:", firstPeriodEnd.toString());
// Move time to just after the first period ends
const timeAfterPeriodEnd = firstPeriodEnd + 1n;
await time.setNextBlockTimestamp(Number(timeAfterPeriodEnd));
await network.provider.send("evm_mine");
console.log("Time moved to:", timeAfterPeriodEnd.toString());
// Call updatePeriod
await raacGauge.connect(owner).updatePeriod();
// Get new period state
const newWeeklyState = await raacGauge.periodState();
const newPeriodStart = BigInt(newWeeklyState.periodStartTime);
console.log("New period start after update:", newPeriodStart.toString());
// Calculate expected buggy result based on currentTime
const currentTime = BigInt(timeAfterPeriodEnd);
const expectedNewPeriodStartWithBug = (currentTime / periodDuration + 2n) * periodDuration;
const correctNewPeriodStart = (currentTime / periodDuration) * periodDuration + 1n;
console.log("Expected new period start with bug:", expectedNewPeriodStartWithBug.toString());
console.log("Correct new period start:", correctNewPeriodStart.toString());
// Assert actual matches buggy calculation
expect(newPeriodStart).to.equal(
expectedNewPeriodStartWithBug,
"New period start should match the buggy calculation based on currentTime"
);
// Assert it skips more than one period
const periodGapWithBug = (newPeriodStart - firstPeriodStart) / periodDuration;
const periodGapCorrect = (correctNewPeriodStart - firstPeriodStart) / periodDuration;
console.log("Number of periods skipped with bug:", periodGapWithBug.toString());
console.log("Number of periods skipped correctly:", periodGapCorrect.toString());
expect(periodGapWithBug).to.be.gt(periodGapCorrect, "Should skip more periods with the bug");
// Confirm it’s not the correct next period
expect(newPeriodStart).to.not.equal(
correctNewPeriodStart,
"New period start should not be the correct next period (no skip)"
);
});

and the output:

Initial period start: 1743638400
Period duration: 604800
First period end: 1744243200
Time moved to: 1744243201
New period start after update: 1745452800
Expected new period start with bug: 1745452800
Correct new period start: 1744243201
Number of periods skipped with bug: 3
Number of periods skipped correctly: 1

Tools Used

Manual Review

Recommendations

Replace the incorrect nextPeriodStart calculation with the following formula:

uint256 nextPeriodStart = ((currentTime / periodDuration) * periodDuration) + 1;

This ensures that the gauge progresses one period at a time instead of skipping multiple periods.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 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 4 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.