Core Contracts

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

Missing voting power validation during proposal execution

Summary

The Governance contract allows for the creation, voting on, and execution of proposals. However, there is an oversight in the execute() function that permits proposals to be executed even if the proposer's voting power has fallen below the required threshold.

Vulnerability Details

When a proposal is created, it requires the proposer to have a certain amount of voting power, defined by proposalThreshold.

uint256 proposerVotes = _veToken.getVotingPower(msg.sender);
>> if (proposerVotes < proposalThreshold) {
revert InsufficientProposerVotes(msg.sender, proposerVotes, proposalThreshold, "Below threshold");
}

This ensures that only those with significant stakes can propose changes.

The cancel() function allows for proposals to be canceled if the proposer’s voting power drops below this threshold.

// Only proposer or if proposer's voting power dropped below threshold
>> if (msg.sender != proposal.proposer &&
_veToken.getVotingPower(proposal.proposer) >= proposalThreshold) {
revert InsufficientProposerVotes(proposal.proposer,
_veToken.getVotingPower(proposal.proposer), proposalThreshold, "Proposer lost required voting power");
}
>> proposal.canceled = true;

However, the execute() function does not perform a similar check, allowing proposals to be executed regardless of the proposer's current voting power.

function execute(uint256 proposalId) external override nonReentrant {
ProposalCore storage proposal = _proposals[proposalId];
if (proposal.executed) revert ProposalAlreadyExecuted(proposalId, block.timestamp);
>> // @audit-issue Missing check for proposer's voting power
ProposalState currentState = state(proposalId);
if (currentState == ProposalState.Succeeded) {
// Queue the proposal
_queueProposal(proposalId);
} else if (currentState == ProposalState.Queued) {
// Execute the queued proposal
_executeProposal(proposalId);
} else {
revert InvalidProposalState(proposalId, currentState, "Invalid state for execution");
}
}

This means that even if a proposer’s voting power has dropped below the threshold after the proposal was created and voted on, the proposal could still be queued and executed.

Impact

This loophole allows proposers who, after creating a proposal, could lose their voting power but still push through their proposal, potentially leading to decisions that are not in the best interest of the community.

Tools Used

Manual Review

Recommendations

Add a check in the execute() function to verify that the proposer still has voting power above the threshold before allowing the proposal to be executed.

function execute(uint256 proposalId) external override nonReentrant {
ProposalCore storage proposal = _proposals[proposalId];
if (proposal.executed) revert ProposalAlreadyExecuted(proposalId, block.timestamp);
// @audit Check if proposer's voting power is still above the threshold
+ uint256 proposerVotingPower = _veToken.getVotingPower(proposal.proposer);
+ if (proposerVotingPower < proposalThreshold) {
+ cancel(proposalId);
+ }
---SNIP---
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
5 months ago
inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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