Core Contracts

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

No Snapshot-Based Voting Allows Last-Minute Locking/Boosting of Votes

Overview

In castVote, the contract calls:

uint256 weight = _veToken.getVotingPower(msg.sender);

This fetches the current veRAAC balance. However, typical governance models (e.g., Compound, Uniswap, Aave) utilize snapshot‐based voting to prevent users from acquiring or transferring voting power after a proposal is created. The present implementation:

  1. Never records a user’s voting power at proposal creation or the start of voting.

  2. Allows them to lock or increase tokens in veRAAC at any point before casting a vote.

Thus a user can drastically raise their veRAAC immediately before castVote, granting them more voting power than they held at proposal creation.

Attack/Exploit Path

  1. Proposal Creation

    • At time T0, a user with minimal veRAAC triggers or sees a proposal. No snapshot is stored.

  2. Mid-Vote

    • The user (or a whale) decides near the end of the voting period to lock/increase tokens in the veRAAC contract, boosting their live voting power.

  3. CastVote

    • Because castVote calls _veToken.getVotingPower(msg.sender) at the time of voting, they now have a large weight despite holding few or no tokens at T0.

  4. Votes with Inflated Power

    • The system has no reference to a “frozen” or “past” block to confirm the user’s voting power at proposal creation. The inflated vote stands.

Impact

  • Vote Manipulation
    A user or group can easily accumulate veRAAC tokens after a proposal is posted (or near its end), influencing the result far more than if their power were locked at creation.

  • Contradicts Typical Governance Assumptions
    The doc states: “Users can lock RAAC tokens for voting power,” but standard governance often expects your power is fixed at the proposal’s start. Without a block-based snapshot, the system invites strategic last-minute locking that disrupts fair representation of users who held tokens early on.

  • Volatile & Over-Optimizable Governance
    Because power is not snapshotted, the protocol is more vulnerable to immediate capital swings from outside sources who can buy or borrow RAAC, lock them for a short period, and cast votes with disproportionate influence.

Proof / Demonstration

A minimal demonstration in Foundry:

// Assume user starts with 0 veRAAC. A proposal is created at T0.
veToken.lock(0, 0); // user has no voting power initially
// ...
// Time passes to near the end of the voting period, user sees the proposal is close:
veToken.lock(1_000_000e18, 365 days); // Acquire or borrow RAAC & lock them
governance.castVote(proposalId, true);
// => The user’s call to getVotingPower(...) now sees 1,000,000 tokens locked,
// even though they had none at T0. No revert occurs.

Because the contract calls getVotingPower without referencing a past block or snapshot, the user has full new power.

Recommendation

Implement Snapshot Voting:
A typical approach is to store each proposal’s start block in propose(...):

proposal.snapshotBlock = block.number;

Then in castVote, use:

uint256 weight = _veToken.getPastVotes(msg.sender, proposal.snapshotBlock);
Updates

Lead Judging Commences

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

Governance.castVote uses current voting power instead of proposal creation snapshot, enabling vote manipulation through token transfers and potential double-voting

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

Governance.castVote uses current voting power instead of proposal creation snapshot, enabling vote manipulation through token transfers and potential double-voting

Support

FAQs

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

Give us feedback!