Core Contracts

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

Users Can Still Vote on Canceled Proposals

Summary

When a proposal has been canceled, the Governance.sol contract sets the proposal.canceled = true. However, the castVote function does not check this flag, allowing votes to be cast on proposals after they’ve been canceled. Though these votes do not resurrect or otherwise affect the proposal’s final “canceled” state, it leads to inaccurate or misleading vote tallies.

Vulnerability Details

The castVote function only checks:
That the proposal exists (proposal.startTime != 0),
Voting window (startTime <= block.timestamp <= endTime),
Whether the voter already voted, and
Whether the voter has nonzero voting power.
It does not check whether proposal.canceled is true, so canceled proposals remain eligible for voting.

Code Snippet :

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;
}

Impact

Misleading Vote Counts:
The on-chain record may show continuing “for” and “against” votes even though the proposal is canceled and will never proceed to execution.

User Confusion and UI Issues:
Front-end interfaces might display ongoing votes on canceled proposals, confusing token holders or making them believe the proposal might still pass.

No Direct Security Compromise:
Since a canceled proposal cannot be executed, the extra votes do not reinstate it or grant the proposer any additional power. Therefore, the severity is relatively low.

Tools Used

Manual review

Recommendations

Add a require(!proposal.canceled, "Proposal is canceled") Check:
In the castVote function, verify that the proposal is not canceled before allowing votes.

Updates

Lead Judging Commences

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

Governance::castVote lacks canceled/executed proposal check, allowing users to waste gas voting on proposals that can never be executed

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

Governance::castVote lacks canceled/executed proposal check, allowing users to waste gas voting on proposals that can never be executed

Support

FAQs

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