Core Contracts

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

`GaugeController::vote` Doesnt Update `lastVoteTime` Or Check Vote Delay Allowing Voting Again Right Away

Summary

GaugeController::vote function is missing implementation of the intended vote delay mechanism, allowing users to vote multiple times in rapid succession and potentially manipulate gauge weights.

Vulnerability Details

[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/GaugeController.sol#L190-L200)

[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/GaugeController.sol#L84-L88)

[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/GaugeController.sol#L82)

In GaugeController, there are constants defined for vote delay restrictions:

/// @notice Required delay between votes
uint256 public constant VOTE_DELAY = 10 days;
/// @notice Minimum allowed vote delay
uint256 public constant MIN_VOTE_DELAY = 1 days;
/// @notice Maximum allowed vote delay
uint256 public constant MAX_VOTE_DELAY = 10 days;

However, GaugeController::vote function neither checks nor updates the lastVoteTime mapping:

function vote(address gauge, uint256 weight) external override whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (weight > WEIGHT_PRECISION) revert InvalidWeight();
// MISSING: if(lastVoteTime[msg.sender] < MIN_VOTE_DELAY) revert VotingDelayNotMet();
uint256 votingPower = veRAACToken.balanceOf(msg.sender);
if (votingPower == 0) revert NoVotingPower();
uint256 oldWeight = userGaugeVotes[msg.sender][gauge];
userGaugeVotes[msg.sender][gauge] = weight;
// MISSING: lastVoteTime[msg.sender] = block.timestamp
_updateGaugeWeight(gauge, oldWeight, weight, votingPower);
....

Impact

  1. Users can rapidly change their votes to manipulate gauge weights in a short timeframe with no cooldown, despite the protocol clearly intending a delay between votes (as shown by the VOTE_DELAY and MIN_VOTE_DELAY constants)

  2. This breaks core governance assumptions where users should be locked into their voting decisions for a minimum time period

  3. The system's intended vote delay mechanism (MIN_VOTE_DELAY = 1 days, VOTE_DELAY = 10 days) is completely unused, allowing vote changes that should be rate-limited

Tools Used

Foundry

Recommendations

In GaugeController::vote add the following with whatever vote delay you want:

function vote(address gauge, uint256 weight) external override whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (weight > WEIGHT_PRECISION) revert InvalidWeight();
+ // Ensure minimum delay between votes has passed
+ if(block.timestamp < lastVoteTime[msg.sender] + MIN_VOTE_DELAY) revert VotingDelayNotMet();
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;
_updateGaugeWeight(gauge, oldWeight, weight, votingPower);
....
Updates

Lead Judging Commences

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