Core Contracts

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

Calculation of required quorum for proposal execution is not checked based on the voting power during the voting period

Summary

In Governance.sol, execute()function can be called by any user. To determine a proposal's state and whether it is successful, it internally calls the state()function. However, in state() , the calculation of the required quorum is based on the current total voting power, instead of limiting to only capturing the voting power at the start of the voting period. This can lead to vote manipulation due to the dynamic value of the required quorum.

Vulnerability Details

Assume this scenario whereby a proposal has ended and no votes can be cast. The snippet below is from the state()function, showing the logic in determining the state of the proposal.

// After voting period ends, check quorum and votes
ProposalVote storage proposalVote = _proposalVotes[proposalId];
uint256 currentQuorum = proposalVote.forVotes + proposalVote.againstVotes;
uint256 requiredQuorum = quorum();
// Check if quorum is met and votes are in favor
if (currentQuorum < requiredQuorum || proposalVote.forVotes <= proposalVote.againstVotes) {
return ProposalState.Defeated;
}
  1. Malicious user calls getVotes()to get the proposal's forVotesand againstVotes. This would be the currentQuorumof the proposal.

  2. Malicious user calls quorum()to get the current total voting power and the requiredQuorum to allow the proposal to succeed. The user realises with the current total voting power, the proposal will indeed succeed.

    1. As seen below, the quorum()function retrieves the current voting power, which is based on the total supply of veRAAC tokens

      function quorum() public view override returns (uint256) {
      return (_veToken.getTotalVotingPower() * quorumNumerator) / QUORUM_DENOMINATOR;
      }
  3. The malicious user sees that the execute()function is in the mempool for this proposal. As the user wishes for this proposal to be defeated, the user now calls veToken.lock() before execute() function is executed, in order to lock RAAC tokens and get minted veRAAC tokens, hence increasing the total supply

    1. [veToken.lock()](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/veRAACToken.sol#L240-L241)

  4. Since total supply of veRAAC tokens has now increased, the current total voting power now increased, leading to requiredQuorumto increase as well.

  5. Now, when execute()function is executed, the currentQuorum will not hit the requiredQuorum. The state()function called internally will return ProposalState.Defeated. The execute()function will revert.

Impact

As seen above, the current total voting power is dynamic and compared against the current quorum of the proposal, which will not be changed once voting period ends. Hence, the outcome of proposals can be manipulated easily.

Additionally, the execute()can be called as many times as long as it has not been executed. Since the determining of the proposal state is dependent on the current total voting power at the time the function is executed, it is possible for voters to manipulate the proposal outcome and eventually result in the proposal to succeed and execute.

This results in a largely flawed voting system.

Tools Used

Manual

Recommendations

  1. Capture a snapshot of the total voting power at the start of the proposal voting period to determine the required quorum that is not dynamic.

  2. If proposal outcome == defeated, execute()function should not be able to be called for that proposal

Updates

Lead Judging Commences

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

Governance allows execution of previously-defeated proposals if quorum requirements are later lowered, enabling unexpected resurrection of old proposals

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

Governance allows execution of previously-defeated proposals if quorum requirements are later lowered, enabling unexpected resurrection of old proposals

Support

FAQs

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