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