Core Contracts

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

Incorrect Voting Power Being Assigned in `BaseGauge.voteDirection()` and `GaugeController.vote()`

Summary

The functions voteDirection() in BaseGauge.sol and vote() in GaugeController.sol incorrectly use balanceOf() instead of getVotingPower(). Since balanceOf() does not account for decaying voting power, this results in outdated and inflated governance influence, allowing users to vote with more weight than they actually should have. This affects governance fairness in generality.

Vulnerability Details

In BaseGauge.voteDirection(),

BaseGauge.sol#L403-L420

/**
* @notice Allows users to vote on direction
* @param direction Direction in basis points (0-10000)
*/
function voteDirection(uint256 direction) public whenNotPaused updateReward(msg.sender) {
if (direction > 10000) revert InvalidWeight();
uint256 votingPower = IERC20(IGaugeController(controller).veRAACToken()).balanceOf(msg.sender);
if (votingPower == 0) revert NoVotingPower();
totalVotes = processVote(
userVotes[msg.sender],
direction,
votingPower,
totalVotes
);
emit DirectionVoted(msg.sender, direction, votingPower);
}

votingPower is assigned user's veRAACToken balanceOf(), which is point.bias (i.e. the minted veTokens) typically assigned on the first lock() unless otherwise adjusted later via increase() or extend().

Understandably, a user's initial voting power will decay linearly to 0 at unlock time. The above flaw would mean, a user whose lock period was already over could still vote on direction skipping the if (votingPower == 0) check.

Evidently, the same issue also surfaces in GaugeController.vote():

GaugeController.sol#L194-L195

uint256 votingPower = veRAACToken.balanceOf(msg.sender);
if (votingPower == 0) revert NoVotingPower();

Impact

  • Users can exert more influence than they should, unfairly skewing votes.

  • The fundamental mechanism of decaying voting power becomes useless.

  • Long-term inactive token holders retain full power, reducing governance fairness.

  • Attackers could stockpile tokens, never extend the lock, and maintain full voting influence forever as long as they do not call withdraw() to burn their veToken balance.

Tools Used

Manual

Recommendations

Consider making the following fix:

BaseGauge.sol#L410

- uint256 votingPower = IERC20(IGaugeController(controller).veRAACToken()).balanceOf(msg.sender);
+ uint256 votingPower = IERC20(IGaugeController(controller).veRAACToken()).getVotingPower(msg.sender);

GaugeController.sol#L194

- uint256 votingPower = veRAACToken.balanceOf(msg.sender);
+ uint256 votingPower = veRAACToken.getVotingPower(msg.sender);
Updates

Lead Judging Commences

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