Core Contracts

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

BaseGauge::voteDirection Lacks Minimum Direction Value Check, Leading to Inaccurate Vote Weight Calculation

Summary

The voteDirection function in the BaseGauge contract allows users to vote on the direction of gauge weight distribution. According to the protocol design, the vote direction should be specified in basis points, with a valid range from a minimum of 100 to a maximum of 10,000. However, while the function (and its internal helper processVote) verifies that the vote direction does not exceed 10,000, it does not check that the value is at least 100. As a result, users can submit vote directions lower than the minimum threshold, which may lead to inaccurate vote weight processing, misallocation of rewards, and overall governance misrepresentation.

Vulnerability Details

How It Begins

  • Function Behavior:
    The voteDirection function is defined as follows:

    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);
    }
  • Internal Check:
    The internal function processVote also checks for an upper bound:

    function processVote(VoteState storage vote, uint256 direction, uint256 votingPower, uint256 totalVotes)
    internal
    returns (uint256)
    {
    if (direction > 10000) revert InvalidWeight();
    if (votingPower == 0) revert NoVotingPower();
    uint256 newTotalVotes = totalVotes - vote.weight + votingPower;
    vote.direction = direction;
    vote.weight = votingPower;
    vote.timestamp = block.timestamp;
    return newTotalVotes;
    }
  • Omission:
    Neither function enforces a minimum vote direction of 100. This omission means that values below 100, which are outside the intended specification, can be submitted. Consequently, users might submit vote directions (e.g., 50 basis points) that lead to an inaccurate reflection of their intended influence, potentially distorting reward distribution and governance calculations.

How the Issue Affects the System

  • Incorrect Vote Weight Calculation:
    A vote direction lower than the minimum may be interpreted incorrectly by the system, causing the applied weight multiplier to be less than intended. This can lead to lower reward multipliers or skewed gauge adjustments.

  • Reward and Governance Distortion:
    The inaccurate vote direction can result in misaligned reward allocation and governance decisions. If vote directions are not enforced within the designed bounds, it can dilute the meaningful participation metrics and distort the true representation of stakeholder preferences.

Proof of Concept

Scenario Walkthrough

  1. User Vote with Sub-Minimum Direction:
    Suppose a user, Alice, has sufficient voting power and is eligible to vote on gauge direction. Instead of submitting a vote with a valid direction (e.g., 500 basis points), she submits a vote with a direction of 50 (i.e., 0.5%). Since the contract does not check for a minimum value, her vote is accepted.

  2. Internal Vote Processing:
    The processVote function accepts the 50 basis point vote because it only checks that the direction is not greater than 10,000. This results in the system processing a vote that is below the expected minimum threshold of 100 basis points, leading to an inaccurate calculation of total vote weight.

Foundry Test Suite (Simplified Example)

Below is a simplified Foundry test case to demonstrate the issue:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Test, console} from "forge-std/Test.sol";
import {BaseGauge} from "../src/core/governance/gauges/BaseGauge.sol";
import {IGaugeController} from "../src/interfaces/core/governance/gauges/IGaugeController.sol";
contract BaseGaugeVoteDirectionTest is Test {
BaseGauge baseGauge;
address controller = makeAddr("CONTROLLER");
address ALICE = makeAddr("ALICE");
// Dummy initialization for testing
function setUp() public {
// Assume BaseGauge is properly deployed with required parameters.
baseGauge = new BaseGauge(/* appropriate constructor parameters, including controller */);
// For testing, assume ALICE has voting power. Role setups are omitted for brevity.
}
function testVoteDirectionNoMinimumDirection() public {
raacTokenAllotmentAndAcquireVeRaac();
raacTokenLock();
vm.warp(block.timestamp + 1 days);
uint256 initialWeight = 100;
vm.startPrank(GAUGE_CONTROLLER_OWNER);
gaugeController.addGauge(address(rwaGauge), IGaugeController.GaugeType.RWA, initialWeight);
vm.stopPrank();
vm.startPrank(ALICE);
rwaGauge.voteDirection(99);
vm.stopPrank();
}
}

How to Run the Test

  1. Initialize a Foundry project:

    forge init my-foundry-project
  2. Place your contract files in the src directory.

  3. Create a test directory adjacent to src and add the test file (e.g., BaseGaugeVoteDirectionTest.t.sol).

  4. Run the test:

    forge test --mt testVoteDirectionNoMinimumDirection -vv
  5. Expected Outcome:
    The test will succeed, demonstrating that a vote with a direction below the intended minimum is accepted. In a corrected implementation, the transaction should revert with an error (e.g., InvalidWeight()).

Impact

  • Inaccurate Vote Weighting:
    Allowing vote directions below 100 basis points can result in an underrepresentation of a user’s intended voting strength, leading to miscalculated gauge adjustments.

  • Reward Misallocation:
    Since vote direction influences reward multipliers, an erroneously low vote direction can lead to the distribution of fewer rewards than the user is entitled to, distorting incentive mechanisms.

  • Governance and Decision-Making Distortion:
    Over time, the cumulative effect of such sub-minimum votes can skew overall gauge metrics, impacting governance decisions that rely on accurate and fair vote weight calculations.

  • Reduced System Integrity:
    The absence of enforced bounds undermines the protocol’s ability to maintain consistent and predictable state, potentially leading to exploitation or unintentional misconfigurations by users.

Tools Used

  • Manual Review

  • Foundry

Recommendations

To address this vulnerability, the voteDirection function (and by extension, the processVote function) should enforce a minimum vote direction of 100 basis points. This can be done by adding a check that reverts the transaction if the direction is less than 100.

Proposed Diff for voteDirection

function voteDirection(uint256 direction) public whenNotPaused updateReward(msg.sender) {
- if (direction > 10000) revert InvalidWeight();
+ if (direction > 10000 || direction < 100) 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);
}

Proposed Diff for processVote

function processVote(VoteState storage vote, uint256 direction, uint256 votingPower, uint256 totalVotes)
internal
returns (uint256)
{
- if (direction > 10000) revert InvalidWeight();
+ if (direction > 10000 || direction < 100) revert InvalidWeight();
if (votingPower == 0) revert NoVotingPower();
uint256 newTotalVotes = totalVotes - vote.weight + votingPower;
vote.direction = direction;
vote.weight = votingPower;
vote.timestamp = block.timestamp;
return newTotalVotes;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!