Core Contracts

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

In `BaseGauge` the weight precission is 1e4 but the `TimeWeightedAverage` expects 1e18

Summary

TimeWeightedAverage library expects that weight applied to period will be scaled by 1e18, however the BaseGauge passes 1e4 which leads to incorrect calculation.

Vulnerability Details

From TimeWeightedAverage we can see that weight is expected to be scaled by 1e18 when passing it to different library functions.

struct Period {
uint256 startTime; // Beginning timestamp of the period
uint256 endTime; // End timestamp of the period
uint256 lastUpdateTime; // Last timestamp the value was updated
uint256 value; // Current value being tracked
uint256 weightedSum; // Running sum of time-weighted values
uint256 totalDuration; // Total duration of accumulated values
uint256 weight; // Weight applied to period (scaled by 1e18)
}

However BaseGauge passes 1e4 (WEIGHT_PRECISSION) as weight parameter which leads to incorrect boost calculations for users.

First when creating period in _updateWeights function.

function _updateWeights(uint256 newWeight) internal {
uint256 currentTime = block.timestamp;
uint256 duration = getPeriodDuration();
if (weightPeriod.startTime == 0) {
// For initial period, start from next period boundary
uint256 nextPeriodStart = ((currentTime / duration) + 1) * duration;
TimeWeightedAverage.createPeriod(
weightPeriod,
nextPeriodStart,
duration,
newWeight,
WEIGHT_PRECISION
);
} else {
// For subsequent periods, ensure we're creating a future period
uint256 nextPeriodStart = ((currentTime / duration) + 1) * duration;
TimeWeightedAverage.createPeriod(
weightPeriod,
nextPeriodStart,
duration,
newWeight,
WEIGHT_PRECISION
);
}
}

In updatePeriod function.

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

And in setInitialWeight.

function setInitialWeight(uint256 weight) external onlyController {
uint256 periodDuration = getPeriodDuration();
uint256 currentTime = block.timestamp;
uint256 nextPeriodStart = ((currentTime / periodDuration) + 2) * periodDuration;
TimeWeightedAverage.createPeriod(
periodState.votingPeriod,
nextPeriodStart,
periodDuration,
weight,
10000 // WEIGHT_PRECISION
);
periodState.periodStartTime = nextPeriodStart;
}

Impact

TimeWeightedAverage expects weight scaled by 1e18 but receives value in BPS, this leads to incorrect calculations

Tools Used

Manual Review, Hardhat

Recommendations

To fix the calculations pass the argument scaled by expected value (1e18).

Updates

Lead Judging Commences

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

BaseGauge reward calculations divide by 1e18 despite using 1e4 precision weights, causing all user weights to round down to zero and preventing reward distribution

Support

FAQs

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