Core Contracts

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

Improper vote weight validation in GaugeController::vote allows totalWeight inflation leading to reward miscalculations

Description

The GaugeController::vote function permits users to allocate 100% voting weight to multiple gauges simultaneously. This occurs because the contract tracks individual gauge votes without validating the cumulative weight across all gauges a user has voted for. As a result, the getTotalWeight calculation becomes inflated, distorting reward distributions.

Proof of Concept

  1. User with 1000 veRAAC votes for Gauge A with 100% weight (10000 basis points)

  2. Same user votes for Gauge B with 100% weight (10000 basis points)

  3. Both gauges receive full 1000 weight contribution from the user

  4. Total system weight becomes 2000 instead of correct 1000

Relevant code snippet:

// GaugeController.sol
function vote(address gauge, uint256 weight) external {
// No check for cumulative weight across all gauges
userGaugeVotes[msg.sender][gauge] = weight;
_updateGaugeWeight(gauge, oldWeight, weight, votingPower);
}

Test case demonstrating the issue:

// Add to GaugeController.test.js
it("allows over-allocation of voting weights", async () => {
// Setup voting power
await veRAACToken.mint(user1.address, ethers.parseEther("1000"));
// Vote 100% on first gauge
await gaugeController
.connect(user1)
.vote(await rwaGauge.getAddress(), WEIGHT_PRECISION);
const weight1 = await gaugeController.getGaugeWeight(
await rwaGauge.getAddress()
);
// Vote 100% on second gauge
await gaugeController
.connect(user1)
.vote(await raacGauge.getAddress(), WEIGHT_PRECISION);
const weight2 = await gaugeController.getGaugeWeight(
await raacGauge.getAddress()
);
// Get total system weight
const totalWeight = await gaugeController.getTotalWeight();
// 1000 * 1.0 (first vote) + 1000 * 1.0 (second vote) = 2000
expect(totalWeight).to.equal(ethers.parseEther("2000"));
expect(weight1.add(weight2)).to.equal(totalWeight);
});

Impact

High Severity - Enables vote weight inflation through existing gauges, diluting legitimate rewards. While gauge creation requires admin privileges, any multi-gauge environment allows users to distort reward calculations proportionally to the number of available gauges. With just 2 active gauges:

TotalWeight = 2000 (should be 1000)
RewardPool = 1000 tokens
// Legitimate user share with 500 weight:
500/2000 = 25% (should be 50%)

Recommendation

  • Track cumulative user votes:

contracts/core/governance/gauges/GaugeController.sol
+ mapping(address => uint256) public userTotalVotes;
function vote(address gauge, uint256 weight) external {
+ uint256 newTotal = userTotalVotes[msg.sender] - oldWeight + weight;
+ if (newTotal > WEIGHT_PRECISION) revert OverVote();
+ userTotalVotes[msg.sender] = newTotal;
userGaugeVotes[msg.sender][gauge] = weight;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

GaugeController::vote lacks total weight tracking, allowing users to allocate 100% of voting power to multiple gauges simultaneously

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

GaugeController::vote lacks total weight tracking, allowing users to allocate 100% of voting power to multiple gauges simultaneously

Support

FAQs

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