Summary
The TimeWeightedAverage::calculateAverage function erroneously uses self.weightedSum as the totalWeightedSum despite temporal misalignment between these values. The current implementation only handles scenarios where endTime exceeds lastUpdateTime, while ignoring cases where endTime predates lastUpdateTime.
Vulnerability Details
function calculateAverage(
Period storage self,
uint256 timestamp
) internal view returns (uint256) {
if (timestamp <= self.startTime) return self.value;
uint256 endTime = timestamp > self.endTime ? self.endTime : timestamp;
uint256 totalWeightedSum = self.weightedSum; <==@found
if (endTime > self.lastUpdateTime) {
uint256 duration = endTime - self.lastUpdateTime;
uint256 timeWeightedValue = self.value * duration;
if (duration > 0 && timeWeightedValue / duration != self.value) revert ValueOverflow();
totalWeightedSum += timeWeightedValue;
}
return totalWeightedSum / (endTime - self.startTime);
}
Impact
The calculated average is inflated.
Tools Used
Recommendations
1.Record the previous self.value as lastValue, and use it when endtime <= lastUpdateTime.
+ uint256 lastValue;
function updateValue(
Period storage self,
uint256 newValue,
uint256 timestamp
) internal {
if (timestamp < self.startTime || timestamp > self.endTime) {
revert InvalidTime();
}
unchecked {
uint256 duration = timestamp - self.lastUpdateTime;
if (duration > 0) {
uint256 timeWeightedValue = self.value * duration;
if (timeWeightedValue / duration != self.value) revert ValueOverflow();
self.weightedSum += timeWeightedValue;
self.totalDuration += duration;
}
}
+ lastValue = self.value;
self.value = newValue;
self.lastUpdateTime = timestamp;
}
2 To address scenarios where endtime ≤ lastUpdateTime, the logic should be refined as follows:
function calculateAverage(
Period storage self,
uint256 timestamp
) internal view returns (uint256) {
if (timestamp <= self.startTime) return self.value;
uint256 endTime = timestamp > self.endTime ? self.endTime : timestamp;
uint256 totalWeightedSum = self.weightedSum; <==@found
if (endTime > self.lastUpdateTime) {
uint256 duration = endTime - self.lastUpdateTime;
uint256 timeWeightedValue = self.value * duration;
if (duration > 0 && timeWeightedValue / duration != self.value) revert ValueOverflow();
totalWeightedSum += timeWeightedValue;
+ }else{
+ uint256 duration = self.lastUpdateTime - endTime;
+ uint256 timeWeightedValue = lastValue * duration;
+ totalWeightedSum -= timeWeightedValue;
+ }
return totalWeightedSum / (endTime - self.startTime);
}