Core Contracts

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

Missing Snapshot Block Updates for Proposal Voting Power in veRAACToken

Summary

The veRAACToken contract's getVotingPowerForProposal function relies on a storage mapping proposalPowerSnapshots to determine voting power at proposal creation time, but this mapping is never updated. This breaks the snapshot mechanism intended to prevent vote manipulation through token transfers after proposal creation.

Vulnerability Details

In voting systems, it's crucial to capture voting power at the time a proposal is created to prevent manipulation through token transfers after the fact. The veRAACToken contract implements this through proposalPowerSnapshots mapping:

// In veRAACToken.sol
mapping(uint256 => uint256) public proposalPowerSnapshots;
function getVotingPowerForProposal(address account, uint256 proposalId) external view returns (uint256) {
uint256 snapshotBlock = proposalPowerSnapshots[proposalId];
if (snapshotBlock == 0) revert InvalidProposal();
return getPastVotes(account, snapshotBlock);
}

The issue stems from the fact that while this functionality exists for querying voting power at proposal creation, the proposalPowerSnapshots mapping is never populated. Looking at the governance flow:

  1. Governance.propose() creates new proposals

  2. Governance.castVote() records votes

  3. veRAACToken.getVotingPowerForProposal() is meant to get historic voting power

The connection between these pieces is broken because:

  • The Governance contract doesn't call any function to record the block number when proposals are created

  • The veRAACToken contract doesn't expose any function to set snapshot blocks

  • The proposalPowerSnapshots mapping remains empty, causing getVotingPowerForProposal to always revert

This means the system is missing a critical security feature - the ability to get voting power from when the proposal was created rather than current voting power.

Looking at the PowerCheckpoint library used by veRAACToken:

function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) {
return _checkpointState.getPastVotes(account, blockNumber);
}

This infrastructure for historical lookups exists but cannot be properly utilized without the snapshot mapping being maintained.

PoC

  1. Alice creates a proposal via Governance.propose()

  2. The proposal is created but no snapshot block is recorded

  3. Bob tries to query voting power via veRAACToken.getVotingPowerForProposal()

  4. The call reverts because proposalPowerSnapshots[proposalId] is 0

Impact

The missing snapshot mechanism means:

  • Voting power cannot be properly determined at proposal creation time

  • Users can manipulate their voting power after proposals are created

  • A core security feature of the governance system is non-functional

Tools Used

Manual review

Recommendations

  1. Add a function in veRAACToken to set proposal snapshots:

function setProposalSnapshot(uint256 proposalId, uint256 blockNumber) external {
require(msg.sender == address(governance), "Only governance");
proposalPowerSnapshots[proposalId] = blockNumber;
}
  1. Modify Governance.propose() to record the snapshot:

function propose(...) external returns (uint256) {
uint256 proposalId = _proposalCount++;
// ... existing logic ...
// Add snapshot recording
IveRAACToken(veToken).setProposalSnapshot(proposalId, block.number - 1);
return proposalId;
}
Updates

Lead Judging Commences

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