Core Contracts

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

Incorrect Total Supply Check in veRAACToken Lock Function Breaking the Invariant

Summary

The lock function in the veRAACToken contract incorrectly checks the total supply of veRAAC tokens against the amount of RAAC tokens being locked to enforce the MAX_TOTAL_SUPPLY limit. This is invalid because veRAAC tokens are minted based on voting power (newPower), not the amount of RAAC tokens locked. This low-severity issue could lead to incorrect enforcement of the total supply limit.


Vulnerability Details

Explanation

The lock function enforces a maximum total supply limit (MAX_TOTAL_SUPPLY) by comparing the current total supply of veRAAC tokens with the amount of RAAC tokens being locked. However, veRAAC tokens are minted based on the voting power (newPower) calculated from the lock duration and amount, not directly on the amount of RAAC tokens. This means the current check is invalid and could lead to incorrect enforcement of the total supply limit.

Root Cause in the Contract Function

The issue lies in the following line of the lock function:

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
if (amount == 0) revert InvalidAmount();
if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();
@> if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded(); //@audit-issue checking totalSupply of VeRAAC against amount of RAAC is invalid
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();
// Do the transfer first - this will revert with ERC20InsufficientBalance if user doesn't have enough tokens
raacToken.safeTransferFrom(msg.sender, address(this), amount);
// Calculate unlock time
uint256 unlockTime = block.timestamp + duration;
// Create lock position
_lockState.createLock(msg.sender, amount, duration);
_updateBoostState(msg.sender, amount);
// Calculate initial voting power
(int128 bias, int128 slope) = _votingState.calculateAndUpdatePower(
msg.sender,
amount,
unlockTime
);
// Update checkpoints
uint256 newPower = uint256(uint128(bias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
// Mint veTokens
_mint(msg.sender, newPower);
emit LockCreated(msg.sender, amount, unlockTime);
}

Here, amount refers to the RAAC tokens being locked, but veRAAC tokens are minted based on the voting power (newPower), which is calculated later in the function:

// Calculate initial voting power
(int128 bias, int128 slope) = _votingState.calculateAndUpdatePower(
msg.sender,
amount,
unlockTime
);
// Update checkpoints
@> uint256 newPower = uint256(uint128(bias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
// Mint veTokens
_mint(msg.sender, newPower);

The correct check should compare the current total supply of veRAAC tokens with the newPower that is about to be minted, not the amount of RAAC tokens being locked.


Impact

  • Incorrect Enforcement of Total Supply Limit: The total supply limit may be enforced incorrectly, leading to unnecessary reverts or failure to prevent exceeding the limit.

  • Broken Invariant: The total supply can be broken if newPower + totalSupply() is larger than Max supply since it was not checked.


Tools Used

  • Manual Code Review: The vulnerability was identified through a manual review of the veRAACToken contract.


Recommendations

  1. Check Total Supply Against New Power:

    • Update the total supply check to compare the current total supply of veRAAC tokens with the newPower that is about to be minted.

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
if (amount == 0) revert InvalidAmount();
if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();
- if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded();
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();
// Do the transfer first - this will revert with ERC20InsufficientBalance if user doesn't have enough tokens
raacToken.safeTransferFrom(msg.sender, address(this), amount);
// Calculate unlock time
uint256 unlockTime = block.timestamp + duration;
// Create lock position
_lockState.createLock(msg.sender, amount, duration);
_updateBoostState(msg.sender, amount);
// Calculate initial voting power
(int128 bias, int128 slope) = _votingState.calculateAndUpdatePower(
msg.sender,
amount,
unlockTime
);
// Update checkpoints
uint256 newPower = uint256(uint128(bias));
+ if (totalSupply() + newPower > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded(); // Fix: Check against newPower
_checkpointState.writeCheckpoint(msg.sender, newPower);
// Mint veTokens
_mint(msg.sender, newPower);
emit LockCreated(msg.sender, amount, unlockTime);
}
Updates

Lead Judging Commences

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

Incorrect `MAX_TOTAL_SUPPLY` check in the `veRAACToken::lock/extend` function of `veRAACToken` could harm locking functionality

Support

FAQs

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

Give us feedback!