Core Contracts

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

Timestamp Manipulation in GaugeController's Period Updates Enables Reward Exploitation

Summary

In the GaugeController.sol file, the updatePeriod function contains a critical flaw in its timestamp handling that allows manipulation of rewards through precise transaction timing.

Vulnerability Details

function updatePeriod(address gauge) external override whenNotPaused {
Gauge storage g = gauges[gauge];
if (!g.isActive) revert GaugeNotActive();
TimeWeightedAverage.Period storage period = gaugePeriods[gauge];
uint256 duration = g.gaugeType == GaugeType.RWA ? 30 days : 7 days;
// @audit Critical: Predictable and manipulatable timestamps
TimeWeightedAverage.createPeriod(
period,
block.timestamp + 1,
duration,
average,
g.weight
);
}

In TimeWeightedAverage.sol: The TimeWeightedAverage.sol library does not implement any protection against timestamp manipulation, making it vulnerable to predictable period starts and reward exploitation:

function createPeriod(
Period storage self,
uint256 startTime,
uint256 duration,
uint256 initialValue,
uint256 weight
) internal {
if (self.startTime != 0 && startTime < self.startTime + self.totalDuration) {
revert PeriodNotElapsed();
}
// @audit No timestamp manipulation protection
self.startTime = startTime;
self.endTime = startTime + duration;
self.lastUpdateTime = startTime;
self.value = initialValue;
self.weightedSum = 0;
self.totalDuration = duration;
self.weight = weight;
}

The vulnerability allows:

  1. Prediction of exact period start times

  2. Manipulation of weight calculations

  3. Gaming of reward distributions

Impact

  1. Distorted reward calculations

  2. Unfair reward distributions

  3. Manipulation of voting power

  4. Compromised gauge weight system

Tools Used

Manual code review and Hardhat testing framework were utilized to identify and confirm this vulnerability. The analysis focused on detecting how predictable timestamps could be exploited to manipulate reward distribution.

Proof of Concept (PoC)

The following steps outline how an attacker can exploit this vulnerability:

  1. A malicious actor precisely times transactions to take advantage of predictable period boundaries.

  2. The attacker ensures their transaction executes at an optimal moment to manipulate weight calculations.

  3. By exploiting this flaw, they unfairly increase their gauge weight influence.

  4. As a result, the protocol’s reward distribution mechanism becomes compromised.

const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("GaugeController Timestamp Manipulation", function() {
let controller, rwaGauge, owner, attacker;
const WEEK = 7 * 24 * 60 * 60;
beforeEach(async function() {
[owner, attacker] = await ethers.getSigners();
const GaugeController = await ethers.getContractFactory("GaugeController");
controller = await GaugeController.deploy();
const RWAGauge = await ethers.getContractFactory("RWAGauge");
rwaGauge = await RWAGauge.deploy();
await controller.addGauge(rwaGauge.address, 0, 1000); // Type 0 = RWA
});
it("Should allow period manipulation through timestamp prediction", async function() {
// Get current block
const block = await ethers.provider.getBlock('latest');
// First period initialization
await controller.updatePeriod(rwaGauge.address);
// Fast forward to just before period end
await ethers.provider.send("evm_increaseTime", [WEEK - 2]);
await ethers.provider.send("evm_mine");
// Attacker can predict exact timestamp
const predictedStart = block.timestamp + WEEK + 1;
// Update period with predicted timestamp
await controller.connect(attacker).updatePeriod(rwaGauge.address);
// Verify period starts at predicted time
const period = await controller.gaugePeriods(rwaGauge.address);
expect(period.startTime).to.equal(predictedStart);
});
});

Recommendation

function updatePeriod(address gauge) external whenNotPaused {
Gauge storage g = gauges[gauge];
if (!g.isActive) revert GaugeNotActive();
TimeWeightedAverage.Period storage period = gaugePeriods[gauge];
uint256 duration = g.gaugeType == GaugeType.RWA ? 30 days : 7 days;
// Use block.number based period starts
uint256 currentPeriod = block.number / duration;
uint256 nextPeriodStart = (currentPeriod + 1) * duration;
TimeWeightedAverage.createPeriod(
period,
nextPeriodStart,
duration,
average,
g.weight
);
}

Final Assessment

Severity: High

  • Predictable timestamps enable reward manipulation

  • No protections against precise timing

  • Affects core reward mechanism

Likelihood: High

  • Easy to execute

  • Predictable outcomes

  • High incentive

Impact: Protocol reward manipulation

Recommendation Status: Critical to implement before mainnet

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 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.