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;
uint256 endTime;
uint256 lastUpdateTime;
uint256 value;
uint256 weightedSum;
uint256 totalDuration;
uint256 weight;
}
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) {
uint256 nextPeriodStart = ((currentTime / duration) + 1) * duration;
TimeWeightedAverage.createPeriod(
weightPeriod,
nextPeriodStart,
duration,
newWeight,
WEIGHT_PRECISION
);
} else {
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();
uint256 avgWeight = periodState.votingPeriod.calculateAverage(periodEnd);
uint256 nextPeriodStart = ((currentTime / periodDuration) + 2) * periodDuration;
periodState.distributed = 0;
periodState.periodStartTime = nextPeriodStart;
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
);
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).