Core Contracts

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

Missing Vote Delay Enforcement in Gauge Voting

Location

governance -> gauges -> GaugeController.sol -> Line 190

Summary

function vote(address gauge, uint256 weight) external override whenNotPaused {

The GaugeController contract defines constants for vote delay (e.g., VOTE_DELAY set to 10 days and MIN_VOTE_DELAY set to 1 day), which suggest that the system is intended to enforce a minimum delay between successive votes by the same user. Although a previous audit flagged that these constants are declared but never used, the previous auditor failed to point out the implecations of this flaw.

Without vote delay enforcement, users can repeatedly cast votes in rapid succession, potentially manipulating gauge weights and disrupting fair reward distribution.

Vulnerability Details

Intended Behavior:
The presence of VOTE_DELAY (10 days) and MIN_VOTE_DELAY (1 day) in the code and documentation indicates that the system was designed to restrict users from voting too frequently. This is crucial for maintaining stability in gauge weight allocations and ensuring that voting power is exercised judiciously.

Observed Behavior:
The vote function does not reference or enforce any delay between votes. Although the unused constants have been noted by another auditor, the lack of an enforced vote delay means that users can cast votes consecutively without waiting.

Design Flaw and Implications:
This oversight allows users to change their votes repeatedly in a short timeframe, which can be exploited to manipulate gauge weights. Such manipulation may result in an unintended skew in reward emissions and revenue distribution, undermining the protocol’s fairness and economic stability.

Impact

Economic Impact:
Unrestricted voting frequency can allow users (or automated actors) to significantly influence gauge weights, leading to distorted reward distributions.

User Trust:
The absence of the intended vote delay may cause participants to lose confidence in the fairness and predictability of the voting mechanism.

Exploitation Risk:
Attackers could exploit rapid vote changes to repeatedly adjust gauge weights, thereby gaining an unfair advantage in the reward system.

PoC

// Unused constants indicate intended vote delay functionality:
uint256 public constant VOTE_DELAY = 10 days; // Intended vote delay (unused)
uint256 public constant MIN_VOTE_DELAY = 1 days; // Intended minimum vote delay (unused)
// vote function does not enforce any delay:
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();
// Missing check for vote delay:
// require(block.timestamp >= lastVoteTime[msg.sender] + VOTE_DELAY, "Vote delay not elapsed");
uint256 oldWeight = userGaugeVotes[msg.sender][gauge];
userGaugeVotes[msg.sender][gauge] = weight;
_updateGaugeWeight(gauge, oldWeight, weight, votingPower);
emit WeightUpdated(gauge, oldWeight, weight);
}

To demonstrate the vulnerability, we deploy a dummy gauge contract (DummyGauge) so that GaugeController receives a valid gauge address

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract DummyGauge {
// Minimal dummy contract to serve as a gauge.
}
import pkg from "hardhat";
const { ethers } = pkg;
import { expect } from "chai";
import { time } from "@nomicfoundation/hardhat-network-helpers";
describe("GaugeController - Vote Delay Enforcement", function () {
let gaugeController, veToken, owner, user, dummyGauge;
beforeEach(async function () {
[owner, user] = await ethers.getSigners();
// Deploy the MockVeToken (artifact name: "MockVeToken")
const VeTokenMock = await ethers.getContractFactory("MockVeToken");
veToken = await VeTokenMock.deploy();
// In ethers v6, .deployed() is not necessary if deploy() returns an already deployed instance.
await veToken.setBalance(user.address, ethers.parseUnits("100", 18));
// Deploy the GaugeController with the veToken address
const GaugeController = await ethers.getContractFactory("GaugeController");
gaugeController = await GaugeController.deploy(veToken.target);
await gaugeController.deployed();
// Deploy a DummyGauge contract to act as a valid gauge
const DummyGauge = await ethers.getContractFactory("DummyGauge");
dummyGauge = await DummyGauge.deploy();
await dummyGauge.deployed();
// Add the dummy gauge as a supported pool in the BoostController
await gaugeController.connect(owner).addGauge(dummyGauge.target, 0, 5000);
});
it("should allow repeated votes without delay", async function () {
// User casts the first vote
await gaugeController.connect(user).vote(dummyGauge.target, 5000);
// Immediately casts a second vote without any enforced delay
await gaugeController.connect(user).vote(dummyGauge.target, 6000);
// Since there is no delay enforcement, the vote is updated
const newVote = await gaugeController.userGaugeVotes(user.address, dummyGauge.target);
expect(newVote).to.equal(6000);
});
});

Recommendations

Enforce Vote Delay:
Modify the vote function to enforce the intended delay. For example:

require(block.timestamp >= lastVoteTime[msg.sender] + VOTE_DELAY, "Vote delay not elapsed");
lastVoteTime[msg.sender] = block.timestamp;
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

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.