Core Contracts

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

Lock Duration Escape in veRAACToken Breaks Voting Power Economics

Summary

The lock extension validation in veRAACToken fails to properly enforce duration limits. Users can extend locks beyond the MAX_LOCK_DURATION, violating the protocol economics and potentially disrupting voting power calculations.

The extend() function's validation. While the contract defines MAX_LOCK_DURATION as 1460 days, the duration check in _lockState.extendLock() fails to properly validate the total lock duration after extension.

Vulnerability Details

The RAAC Protocol introduces an innovative way to bring real estate on-chain through its veRAACToken mechanism. However, I've discovered a significant issue in how lock extensions are handled.

Notice how the protocol uses a vote-escrow system similar to Curve's veCRV, where users lock RAAC tokens to gain voting power. The longer the lock duration, the more voting power they receive. This mechanism is meant to align long-term incentives, with a maximum lock duration of 1460 days.

When a user calls extend(), the contract fails to properly validate the total lock duration after extension. This means a user could:

  1. Create an initial lock within the allowed duration

  2. Repeatedly extend their lock beyond the 1460-day limit

  3. Gain disproportionate voting power that exceeds protocol design

Looking at the code, we can see why this happens

function extend(uint256 newDuration) external nonReentrant whenNotPaused {
// VULNERABILITY: No validation of newDuration against MAX_LOCK_DURATION (1460 days)
// This allows users to extend beyond the intended maximum lock period
// Step 1: Extend Lock Duration
uint256 newUnlockTime = _lockState.extendLock(msg.sender, newDuration);
// → Blindly trusts the input duration without checking protocol limits
// → newUnlockTime could be > (block.timestamp + MAX_LOCK_DURATION)
// Step 2: Calculate New Voting Power
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount,
newUnlockTime // → Using potentially invalid unlock time
);
// → Voting power increases with lock duration
// → Excessive duration = excessive voting power
// Step 3: Update User's Power
uint256 oldPower = balanceOf(msg.sender);
uint256 newPower = uint256(uint128(newBias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
// → Records potentially inflated voting power
// Step 4: Adjust Token Balance
if (newPower > oldPower) {
_mint(msg.sender, newPower - oldPower);
// → Mints tokens based on inflated voting power
} else if (newPower < oldPower) {
_burn(msg.sender, oldPower - newPower);
}
emit LockExtended(msg.sender, newUnlockTime);
}

The function trusts _lockState.extendLock() to handle validation, but the check is insufficient. This breaks a core assumption of the ve-tokenomics, that voting power is properly bounded by the maximum lock duration.

This vulnerability directly impacts the dual-gauge system that governs both RAAC and RWA emissions. A malicious user could accumulate excessive voting power and significantly influence protocol direction.

Impact

The protocol uses vote-escrowed tokens (veRAACToken) to align long-term incentives, where users lock RAAC tokens to gain voting power over both real estate yields and protocol emissions.

The core mistake lies in how the protocol handles lock extensions. the veRAACToken contract allows users to extend their lock durations through the extend() function. This seemingly simple mechanism contains a powerful flaw in its validation logic.

When a user creates their initial lock, they must stay within the MAX_LOCK_DURATION of 1460 days. However, the extend() function fails to enforce this same limit on extensions. This means that a strategic user can first create a valid lock, then repeatedly extend it beyond the protocol's intended maximum duration. With each extension, they gain disproportionate voting power over both the RWA and RAAC gauges.

Recommendations

function extend(uint256 newDuration) external nonReentrant whenNotPaused {
// Step 1: Validate new duration against protocol limits
require(newDuration <= MAX_LOCK_DURATION, "Duration exceeds maximum");
require(block.timestamp + newDuration <= _lockState.locks[msg.sender].end + MAX_LOCK_DURATION,
"Total lock duration exceeds maximum");
// Step 2: Extend lock with validated duration
uint256 newUnlockTime = _lockState.extendLock(msg.sender, newDuration);
// → newUnlockTime is now guaranteed to be within MAX_LOCK_DURATION
// Step 3: Calculate voting power with validated unlock time
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount,
newUnlockTime // → Using validated unlock time
);
// → Voting power calculation now respects protocol limits
// Step 4: Update checkpoints with bounded voting power
uint256 oldPower = balanceOf(msg.sender);
uint256 newPower = uint256(uint128(newBias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
// → Records properly bounded voting power
// Step 5: Adjust token balance within protocol limits
if (newPower > oldPower) {
_mint(msg.sender, newPower - oldPower);
// → Mints tokens based on validated voting power
} else if (newPower < oldPower) {
_burn(msg.sender, oldPower - newPower);
}
emit LockExtended(msg.sender, newUnlockTime);
}

Consider adding two key validations:

  1. Checks the new duration against MAX_LOCK_DURATION

  2. Validates that the total lock duration (existing + extension) stays within protocol limits

This ensures the entire voting power and token minting flow remains within the protocol's intended boundaries.

Updates

Lead Judging Commences

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

Support

FAQs

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

Give us feedback!