Core Contracts

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

Historical Power Manipulation in veRAACToken

Summary

The historical voting power preservation mechanism in veRAACToken has aan issue. When users lock tokens and record votes, their historical voting power can be manipulated through specific withdrawal patterns. This breaks a core invariant of the vote-escrow system where historical voting records should remain immutable.

The issue stems from how voting power checkpoints are handled in veRAACToken.sol. When tokens are withdrawn, the contract updates the user's voting power checkpoint but doesn't properly preserve the historical record at the previous block number.

The PowerCheckpoint library's implementation in veRAACToken doesn't maintain separate mappings for historical and current voting power. When withdraw() is called, it overwrites the checkpoint data without considering the block number context.

Vulnerability Details

The protocol assumes checkpoints are immutable historical records, but the implementation allows retroactive changes. This breaks a fundamental invariant of the governance system where past votes should be set in stone.

The assumption that the PowerCheckpoint library would handle historical preservation automatically, similar to how Compound's checkpointing works. However, the interaction between withdrawal mechanics and the gauge voting system (see GaugeController.sol) creates an unexpected state mutation path.

When you see how the veRAACToken contract interacts with the broader RAAC ecosystem. The protocol connects real estate to DeFi through a system of tokens, gauges, and voting mechanisms. The voting power drives both governance decisions and economic incentives through the dual-gauge system (RAACGauge and RWAGauge).

The core mistake's in the checkpoint mechanism. When users withdraw their tokens, the contract updates their voting power without preserving the historical record. This means that past governance decisions which could affect real estate tokenization parameters, lending rates, or gauge weights - become mutable. See the withdraw() function

function withdraw() external nonReentrant {
// STEP 1: Load user's lock position
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
// STEP 2: Validation checks
if (userLock.amount == 0) revert LockNotFound();
if (block.timestamp < userLock.end) revert LockNotExpired();
uint256 amount = userLock.amount;
uint256 currentPower = balanceOf(msg.sender);
// STEP 3: VULNERABILITY - Historical data deletion
// These deletions affect historical voting power records
delete _lockState.locks[msg.sender]; // Erases lock history
delete _votingState.points[msg.sender]; // Erases voting points
// STEP 4: CRITICAL POINT
// writeCheckpoint overwrites historical data instead of preserving it
_checkpointState.writeCheckpoint(msg.sender, 0);
// STEP 5: Token operations
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}

Looking at the technical implementation, the GaugeController relies on these historical voting records to calculate proper weight distributions between real estate assets (RWA) and protocol tokens (RAAC). By manipulating historical voting power, an attacker could retroactively alter gauge weights, affecting reward distributions across the entire protocol.

This vulnerability is particularly concerning because RAAC's dual-gauge system forms the backbone of directing protocol incentives between real estate activities and DeFi operations. The ability to change historical votes undermines the time-weighted nature of the ve-token mechanism, which is designed to encourage long-term alignment with protocol goals.

Impact

Governance Impact:

  • Affects proposal validation in Governance.sol

  • Compromises gauge weight calculations in GaugeController.sol

  • Could manipulate boost calculations in BoostController.sol

Economic Impact: The ability to manipulate historical voting power undermines the entire vote-escrow tokenomics model that incentivizes long-term protocol alignment.

Recommendations

Consider separating the historical and current state updates, because this maintains the integrity of past voting power for governance calculations while properly handling the withdrawal process, to align with IveRAACToken.sol's requirement that historical voting power must remain immutable, as specified in the getPastVotes() function interface.

function withdraw() external nonReentrant {
// STEP 1: Preserve historical voting power first
uint256 currentBlock = block.number;
uint256 historicalPower = balanceOf(msg.sender);
_checkpointState.writeHistoricalCheckpoint(
msg.sender,
currentBlock,
historicalPower
);
// STEP 2: Load and validate lock position
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
if (userLock.amount == 0) revert LockNotFound();
if (block.timestamp < userLock.end) revert LockNotExpired();
uint256 amount = userLock.amount;
uint256 currentPower = balanceOf(msg.sender);
// STEP 3: Clear current state
delete _lockState.locks[msg.sender];
delete _votingState.points[msg.sender];
// STEP 4: Update current checkpoint (separate from historical)
_checkpointState.writeCheckpoint(msg.sender, 0);
// STEP 5: Token operations
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
  1. Historical Data → Preserved (new step)

  2. User Lock Data → Validated

  3. Current State → Cleared

  4. Current Checkpoint → Updated

  5. Tokens → Burned and Transferred

Updates

Lead Judging Commences

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

veRAACToken.withdraw() overwrites historical voting power records by setting zero checkpoint

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

veRAACToken.withdraw() overwrites historical voting power records by setting zero checkpoint

Support

FAQs

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

Give us feedback!