Core Contracts

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

GaugeController uses user's token balance as voting power

Summary

GaugeController.sol incorrectly uses the veToken balance of the users as their voting power.

Vulnerability Details

In GaugeController.sol, there is a function called vote that allows VE holders to vote on gauges:

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);
}

The issue here is that this function uses the veToken balance of the user as the voting power. In VE tokens, voting power and user balance are related but distinct concepts, as voting power is also influenced by the passage of time.

Currently, if a user's lock period has ended, they can still vote.

They can call vote and vote based on their balance as voting power, withdraw their RAAC tokens from veRAAC, transfer those RAAC tokens to another account, lock them again with the new address, call vote again, and essentially vote twice.
This happens because the lock period doesn't affect voting power, as it's based solely on the balance.

Impact

Users can double vote.

Tools Used

Manual Review

Recommendations

Use getVotingPower to get the users voting power:

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);
+ IveRAACToken veToken = IveRAACToken(address(veRAACToken));
+ uint256 votingPower = veToken.getVotingPower(msg.sender, block.timestamp);
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 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BaseGauge::_applyBoost, GaugeController::vote, BoostController::calculateBoost use balanceOf() instead of getVotingPower() for vote-escrow tokens, negating time-decay mechanism

Support

FAQs

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

Give us feedback!