Core Contracts

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

Missing time control in `GaugeController::vote` can lead to vote manipulation

Summary

The GaugeController contract defines time control constants and state variables for vote delays but fails to implement these controls in the actual voting mechanism (GaugeController::vote). This allows users to vote repeatedly without any time restrictions, leading to vote manipulation and reward distribution exploitation.

Vulnerability Details

@> mapping(address => uint256) public lastVoteTime;
@> uint256 public constant VOTE_DELAY = 10 days;
@> uint256 public constant MIN_VOTE_DELAY = 1 days;
@> uint256 public constant MAX_VOTE_DELAY = 10 days;
function vote(address gauge, uint256 weight) external override whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (weight > WEIGHT_PRECISION) revert InvalidWeight();
@>
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);
}

Impact

The GaugeController::vote fails to implement constant and variable controls (lastVoteTime, VOTE_DELAY, MIN_VOTE_DELAY, MAX_VOTE_DELAY) allowing users to vote repeatedly without any time restriction. This can lead users to vote multiple times in rapid succession (no cooldown period between votes). Allowing rapid weight changes to manipulate reward distribution and maximize rewards through rapid voting.

Tools Used

Manual review

Recommendations

Implement vote delay modifier and add it to the GaugeController::vote function.

+ modifier enforceVoteDelay() {
+ require(
+ lastVoteTime[msg.sender] == 0 ||
+ block.timestamp >= lastVoteTime[msg.sender] + VOTE_DELAY,
+ "Vote delay not elapsed"
+ );
+ _;
+ lastVoteTime[msg.sender] = block.timestamp;
+}
- function vote(address gauge, uint256 weight) external override whenNotPaused {
+ function vote(address gauge, uint256 weight) external override whenNotPaused enforceVoteDelay{
if (!isGauge(gauge)) revert GaugeNotFound();
if (weight > WEIGHT_PRECISION) revert InvalidWeight();
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);
}
Updates

Lead Judging Commences

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