Core Contracts

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

Cancelled Proposals Remain Voteable in Governance Contract

Summary

In the Governance contract, when a proposal is cancelled, the contract merely updates the proposal’s state by setting a canceled flag, without removing or invalidating the proposal data. As a result, the castVote function does not check whether a proposal is cancelled, allowing users to cast votes on proposals that have been cancelled.

Vulnerability Details

  • Affected Functions:

    • cancel(uint256 proposalId):

      function cancel(uint256 proposalId) external override {
      ProposalCore storage proposal = _proposals[proposalId];
      if (proposal.startTime == 0) revert ProposalDoesNotExist(proposalId);
      ProposalState currentState = state(proposalId);
      if (currentState == ProposalState.Executed) {
      revert InvalidProposalState(proposalId, currentState, ProposalState.Active, "Cannot cancel executed proposal");
      }
      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;
      emit ProposalCanceled(proposalId, msg.sender, "Proposal canceled by proposer");
      }
    • castVote(uint256 proposalId, bool support):

      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;
      }
  • Issue:
    The cancel function only sets proposal.canceled to true but does not remove the proposal data or enforce cancellation in other functions. The castVote function does not check if a proposal has been cancelled (i.e., if proposal.canceled == true) before allowing a vote. Consequently, users can cast votes on proposals that have been cancelled.

Impact

  • Inconsistent Governance State:
    Votes may be cast on proposals that are no longer active or intended to be considered, leading to skewed or misleading vote counts.

  • Manipulation Risk:
    Malicious actors might exploit this by reviving cancelled proposals through casting votes, undermining the integrity of the governance process.

  • Loss of Trust:
    Stakeholders may lose confidence in the governance mechanism if cancelled proposals continue to affect vote tallies.

Tools Used

  • Manual Code Review

Recommendations

  1. Enforce Proposal Cancellation in Voting:

    • Update the castVote function to check if a proposal has been cancelled before allowing a vote. For example:

      if (proposal.canceled) {
      revert ProposalCancelled(proposalId);
      }
  2. Invalidate Cancelled Proposals:

    • Consider modifying the proposal storage or state management such that cancelled proposals are removed or marked in a way that prevents any further interactions, including voting.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 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 7 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.

Give us feedback!