Core Contracts

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

Missing Vote Delay Enforcement in GaugeController Allows Vote Manipulation

Link to Affected Code:

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/GaugeController.sol#L190-L203

function vote(address gauge, uint256 weight) external override whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (weight > WEIGHT_PRECISION) revert InvalidWeight();
// @audit there should be a Vote Delay (10 Days)
uint256 votingPower = veRAACToken.balanceOf(msg.sender);
if (votingPower == 0) revert NoVotingPower();
uint256 oldWeight = userGaugeVotes[msg.sender][gauge];
userGaugeVotes[msg.sender][gauge] = weight;
_updateGaugeWeight(gauge, oldWeight, weight, votingPower);
emit WeightUpdated(gauge, oldWeight, weight);
}

Description:
The GaugeController contract defines a VOTE_DELAY constant of 10 days and the doc state and maintains a lastVoteTime mapping to track user votes, but fails to enforce this delay in the vote() function. This allows users to vote multiple times in rapid succession, bypassing the intended 10-day cooldown period.

The docs stated Minimum Vote Delay is 10 used for manipulation preventions

Impact:

  • Users can vote multiple times within the 10-day period

  • Gauge weights can be manipulated through rapid voting

  • Protocol's vote delay mechanism is completely bypassed

  • Breaks core voting mechanics and fairness guarantees

Proof of Concept:

// Attacker can vote multiple times in succession
function attackVoting() external {
// First vote
gaugeController.vote(targetGauge, 5000);
// Can immediately vote again
gaugeController.vote(targetGauge, 7500);
// And again with no delay
gaugeController.vote(targetGauge, 10000);
}

Recommended Mitigation:


Add vote delay enforcement to the vote() function:

function vote(address gauge, uint256 weight) external override whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (weight > WEIGHT_PRECISION) revert InvalidWeight();
// Add vote delay check
if (block.timestamp < lastVoteTime[msg.sender] + VOTE_DELAY) {
revert VotingDelayNotElapsed();
}
uint256 votingPower = veRAACToken.balanceOf(msg.sender);
if (votingPower == 0) revert NoVotingPower();
uint256 oldWeight = userGaugeVotes[msg.sender][gauge];
userGaugeVotes[msg.sender][gauge] = weight;
lastVoteTime[msg.sender] = block.timestamp; // Update last vote time
_updateGaugeWeight(gauge, oldWeight, weight, votingPower);
emit WeightUpdated(gauge, oldWeight, weight);
}
error VotingDelayNotElapsed();
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

GaugeController::vote never enforces VOTE_DELAY or updates lastVoteTime, allowing users to spam votes and manipulate gauge weights without waiting

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

GaugeController::vote never enforces VOTE_DELAY or updates lastVoteTime, allowing users to spam votes and manipulate gauge weights without waiting

Support

FAQs

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

Give us feedback!