Core Contracts

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

BaseGauge.sol Uses Raw Balance Instead of Voting Power for Direction Voting

Summary

the BaseGauge.sol::voteDirection() function incorrectly uses raw veToken balance instead of actual voting power for weighting user votes, undermining the vote-escrow tokenomics for directional voting. Additionally, users whose locks have expired can continue to keep voting on key protocol decisions despite having zero voting power.

Vulnerability Details

In BaseGauge.sol::voteDirection() function determines voting influence using:

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(); Should revert if the user has no voting power
totalVotes = processVote(
userVotes[msg.sender],
direction,
votingPower,
totalVotes
);
emit DirectionVoted(msg.sender, direction, votingPower);
}

The function uses the raw balanceOf() value from the veRAACToken.sol contract instead of getting the proper time-weighted voting power.

Additionally, if a user's lock has expired (therefore they should have 0 voting power), but they do not withdraw their veRaacTokens, they can still vote in the protocol.

uint256 votingPower = IERC20(IGaugeController(controller).veRAACToken()).balanceOf(msg.sender);
-> if (votingPower == 0) revert NoVotingPower(); Should revert if the user has no voting power

Although this is identical to the bug found in GaugeController.sol, This is a separate issue as it affects directional voting rather than gauge weight voting.

Impact

High-

  • Direction voting weights ignore lock duration, breaking vote-escrow incentives

  • Users with short-term locks have disproportionate influence over directional votes

  • Users can continue to impact the protocol after their voting power has ended

Likelihood

High - Affects every directional vote cast through BaseGauge implementations.

Recommendations

Use the getVotingPower() function instead:

function voteDirection(uint256 direction) public whenNotPaused updateReward(msg.sender) {
if (direction > 10000) revert InvalidWeight();
- uint256 votingPower = IERC20(IGaugeController(controller).veRAACToken()).balanceOf(msg.sender);
+ uint256 votingPower = IERC20(IGaugeController(controller).veRAACToken()).getVotingPower(msg.sender);
if (votingPower == 0) revert NoVotingPower(); Should revert if the user has no voting power
totalVotes = processVote(
userVotes[msg.sender],
direction,
votingPower,
totalVotes
);
emit DirectionVoted(msg.sender, direction, votingPower);
}
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!