Core Contracts

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

Incorrect validation check in the TimeWeightedAverage library

Summary

The createPeriod function in the TimeWeightedAverage library includes a validation check to ensure that a new period cannot be created before the previous period has elapsed. However, this check relies on self.startTime + self.totalDuration, where totalDuration is incremented in the updateValue function. This design introduces a vulnerability where totalDuration can be extended beyond the intended endTime of the period, causing the validation check to fail even when the current time exceeds the endTime.

Vulnerability Details

createPeriod Validation Check:

if (self.startTime != 0 && startTime < self.startTime + self.totalDuration) {
revert PeriodNotElapsed();
}

This check ensures that a new period cannot start before the previous period has elapsed. It compares the startTime of the new period with self.startTime + self.totalDuration.

updateValue Duration Extension:

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; // Extends totalDuration
}
}

The totalDuration is incremented by the elapsed time (duration) since the last update. This means totalDuration can grow beyond the intended endTime of the period. If updateValue is called with a timestamp beyond the endTime, totalDuration will be extended. As a result, self.startTime + self.totalDuration will exceed the endTime, causing the validation check in createPeriod to fail even when the current time is beyond the endTime.

POC

add the following test case into TimeWeightedAverage.test.js:

it("createPeriod will revert even after the end time", async () => {
const currentTime = BigInt(await time.latest());
const nextPeriod = ((currentTime / WEEK) + 1n) * WEEK;
await time.setNextBlockTimestamp(Number(currentTime));
await timeWeightedAverage.createPeriod(
nextPeriod,
WEEK,
ethers.parseEther("100"),
ethers.parseEther("1")
);
const updateTime = nextPeriod + WEEK;
await time.setNextBlockTimestamp(Number(updateTime));
await timeWeightedAverage.updateValue(
ethers.parseEther("200"),
updateTime
);
const timeAfterEndtime = nextPeriod + WEEK + DAY;
const period = await timeWeightedAverage.period();
const endTime = period.endTime;
console.log("timeAfterEndtime > endTime:",timeAfterEndtime>endTime);
await time.setNextBlockTimestamp(Number(timeAfterEndtime));
await timeWeightedAverage.createPeriod(
timeAfterEndtime,
WEEK,
ethers.parseEther("100"),
ethers.parseEther("1")
);
});

run npx hardhat test --grep "createPeriod will revert"

imeWeightedAverage
Basic Functionality
timeAfterEndtime > endTime: true
1) createPeriod will revert even after the end time
Error: VM Exception while processing transaction: reverted with custom error 'PeriodNotElapsed()'
at TimeWeightedAverageMock.createPeriod (contracts/libraries/math/TimeWeightedAverage.sol:110)
at TimeWeightedAverageMock.createPeriod (contracts/mocks/libraries/math/TimeWeightedAverageMock.sol:17)

Impact

The impact is Low because the updateValue is only used in test code. The likelihood is High, so the severity is Low.

Tools Used

Manual Review

Recommendations

Consider following fix in createPeriod:

function createPeriod(
Period storage self,
uint256 startTime,
uint256 duration,
uint256 initialValue,
uint256 weight
) internal {
if (self.startTime != 0 && startTime < self.endTime) {
revert PeriodNotElapsed();
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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