Description
The calculateTimeWeightedAverage function lacks validation on the period parameters' start and end times. The function assumes timestamps are provided in a valid order but doesn't enforce this requirement, which could lead to underflows in duration calculations and incorrect weighted averages.
Affected code
function calculateTimeWeightedAverage(
PeriodParams[] memory periods,
uint256 timestamp
) public pure returns (uint256 weightedAverage) {
uint256 totalWeightedSum;
uint256 totalDuration;
for (uint256 i = 0; i < periods.length; i++) {
if (timestamp <= periods[i].startTime) continue;
uint256 endTime = timestamp > periods[i].endTime ? periods[i].endTime : timestamp;
uint256 duration = endTime - periods[i].startTime;
unchecked {
uint256 timeWeightedValue = periods[i].value * duration;
if (timeWeightedValue / duration != periods[i].value) revert ValueOverflow();
totalWeightedSum += timeWeightedValue * periods[i].weight;
totalDuration += duration;
}
}
return totalDuration == 0 ? 0 : totalWeightedSum / (totalDuration * 1e18);
}
Vulnerability details
The function calculates duration as endTime - periods[i].startTime without first validating that endTime is greater than startTime. If periods[i].startTime is greater than periods[i].endTime, the duration calculation will underflow. Since this calculation occurs before the unchecked block, it would revert, but this represents improper validation of inputs that could lead to function failures and denial of service.
The impact is increased by the fact that this is a public function that can be called by other contracts or users who might rely on its calculations. Incorrect period parameters could cause the function to revert unexpectedly or produce incorrect results, potentially affecting any system components that depend on these time-weighted averages.
Tools Used
Manual Review
Recommended Mitigation Steps
Add explicit validation of period parameters to ensure startTime is less than endTime:
function calculateTimeWeightedAverage(
PeriodParams[] memory periods,
uint256 timestamp
) public pure returns (uint256 weightedAverage) {
uint256 totalWeightedSum;
uint256 totalDuration;
for (uint256 i = 0; i < periods.length; i++) {
if (periods[i].startTime >= periods[i].endTime) revert InvalidPeriod();
if (timestamp <= periods[i].startTime) continue;
uint256 endTime = timestamp > periods[i].endTime ? periods[i].endTime : timestamp;
uint256 duration = endTime - periods[i].startTime;
unchecked {
uint256 timeWeightedValue = periods[i].value * duration;
if (timeWeightedValue / duration != periods[i].value) revert ValueOverflow();
totalWeightedSum += timeWeightedValue * periods[i].weight;
totalDuration += duration;
}
}
return totalDuration == 0 ? 0 : totalWeightedSum / (totalDuration * 1e18);
}