Core Contracts

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

No Check for Quorum Before Execution on Governance

Summary

The contract allows proposals to be executed even if they do not meet the required quorum. While quorum checks exist in state(), they are not enforced before execution, allowing low-vote proposals to be passed.

Vulnerability Details

function _executeProposal(uint256 proposalId) internal {
ProposalCore storage proposal = _proposals[proposalId];
bytes32 salt = proposal.descriptionHash;
bytes32 id = _timelock.hashOperationBatch(
proposal.targets,
proposal.values,
proposal.calldatas,
bytes32(0),
salt
);
if (!_timelock.isOperationReady(id)) {
revert ProposalNotQueued(proposalId, id);
}
_timelock.executeBatch(
proposal.targets,
proposal.values,
proposal.calldatas,
bytes32(0),
salt
);
proposal.executed = true;
emit ProposalExecuted(proposalId, msg.sender, block.timestamp);
}

Problems:

  1. No explicit check that the proposal met quorum before execution.

  2. Attackers can execute proposals with minimal votes, bypassing proper governance participation.

  3. State inconsistencies may arise if an unqualified proposal is executed but later queried as "Defeated" in state().

Poc

  1. Attacker submits a proposal with very few votes.

  2. Attacker waits for the voting period to end.

  3. Despite not meeting quorum, attacker calls execute(), which does not verify quorum.

  4. The proposal executes successfully, bypassing governance legitimacy.

Impact

Proposals can be executed without proper participation, leading to governance takeovers.

Malicious proposals can execute with minimal votes

Governance legitimacy compromised

Tools Used

Manual Review

Recommendations

Modify _executeProposal() to ensure that the proposal met quorum before execution.

function _executeProposal(uint256 proposalId) internal {
ProposalCore storage proposal = _proposals[proposalId];
// New Check: Ensure the proposal met quorum before execution
if (!_isProposalSuccessful(proposalId)) {
revert ProposalDidNotMeetQuorum(proposalId);
}
bytes32 salt = proposal.descriptionHash;
bytes32 id = _timelock.hashOperationBatch(
proposal.targets,
proposal.values,
proposal.calldatas,
bytes32(0),
salt
);
if (!_timelock.isOperationReady(id)) {
revert ProposalNotQueued(proposalId, id);
}
_timelock.executeBatch(
proposal.targets,
proposal.values,
proposal.calldatas,
bytes32(0),
salt
);
proposal.executed = true;
emit ProposalExecuted(proposalId, msg.sender, block.timestamp);
}
Updates

Lead Judging Commences

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

Support

FAQs

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