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 10 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 10 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.

Give us feedback!