Core Contracts

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

Manipulation of Voting Power via Real-Time Balance Changes in `Governance::castVote` Function

Summary

The Governance contract's castVote function relies on the voter's current voting power at the time of vote casting rather than a fixed snapshot. This allows users to manipulate their voting power during the voting period, enabling them to temporarily inflate their influence on a proposal and then reduce it afterward, thereby undermining governance integrity.

Vulnerability Details

In the castVote function, the voting weight is determined by calling _veToken.getVotingPower(msg.sender) at the moment the vote is cast. This approach uses the current state of the voter's balance rather than a snapshot of their balance at the proposal's start time. As a result, a user can perform actions such as locking additional tokens or unlocking previously locked tokens during the voting period to artificially boost their voting power for a short period, cast their vote, and then revert to a lower balance.

function castVote(uint256 proposalId, bool support) external override returns (uint256) {
ProposalCore storage proposal = _proposals[proposalId];
if (proposal.startTime == 0) revert ProposalDoesNotExist(proposalId);
if (block.timestamp < proposal.startTime) {
revert VotingNotStarted(proposalId, proposal.startTime, block.timestamp);
}
if (block.timestamp > proposal.endTime) {
revert VotingEnded(proposalId, proposal.endTime, block.timestamp);
}
ProposalVote storage proposalVote = _proposalVotes[proposalId];
if (proposalVote.hasVoted[msg.sender]) {
revert AlreadyVoted(proposalId, msg.sender, block.timestamp);
}
@>> uint256 weight = _veToken.getVotingPower(msg.sender);
if (weight == 0) {
revert NoVotingPower(msg.sender, block.number);
}
proposalVote.hasVoted[msg.sender] = true;
if (support) {
proposalVote.forVotes += weight;
} else {
proposalVote.againstVotes += weight;
}
emit VoteCast(msg.sender, proposalId, support, weight, "");
return weight;
}

Proof of Concept (POC):

  1. Initial State: User A holds 50,000 veRAAC tokens locked, which would normally be insufficient to influence a proposal significantly.

  2. Manipulation: Before casting a vote, User A locks an additional 100,000 veRAAC tokens to temporarily raise their voting power to 150,000 tokens.

  3. Vote Casting: User A casts a vote on a proposal with the artificially inflated voting power of 150,000 tokens.

  4. Outcome: The proposal's vote tally reflects the 150,000 voting power, even though User A's normal balance is only 50,000 before the proposal, thus skewing the governance decision unfairly.

Impact

  • The use of dynamic voting power allows temporary manipulation, potentially leading to proposals being approved or rejected based on transient, inflated voting weights.

  • Attackers can exploit this vulnerability to gain disproportionate influence over governance decisions, undermining the fairness and stability of the protocol.

Tools Used

Manual Review

Recommendation

Implement a snapshot mechanism that records each voter's voting power at the start of the voting period, ensuring that all votes are based on a fixed balance. This will prevent temporary manipulation of voting power and preserve the integrity of governance outcomes.

Updates

Lead Judging Commences

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

Governance.castVote uses current voting power instead of proposal creation snapshot, enabling vote manipulation through token transfers and potential double-voting

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

Governance.castVote uses current voting power instead of proposal creation snapshot, enabling vote manipulation through token transfers and potential double-voting

Support

FAQs

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