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 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!