Core Contracts

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

Parallel Lock state variables in veRAACToken leads to incorrect accounting such as zero amount boost update when veRAACToken locked is increased.

Summary

The veRAACToken contract maintains lock positions in two separate storage locations - a direct locks mapping and within the LockManager library's _lockState. This dual storage is not properly synchronized, leading to incorrect balance tracking and potential loss of funds.

Vulnerability Details

The veRAACToken contract suffers from a critical design flaw in how it manages locked token positions. There are two parallel storage mechanisms for tracking locks:

First, direct mapping in the contract:

mapping(address => Lock) public locks;

Secondly, storage in LockManager library:

LockManager.LockState private _lockState;
// which contains as part of `locks` mapping LockState struct:
struct LockState {
mapping(address => Lock) locks;

The key issue lies in how these storage locations are used inconsistently:

In the increase() function:

function increase(uint256 amount) external nonReentrant whenNotPaused {
// Increases lock in _lockState
_lockState.increaseLock(msg.sender, amount);
// But then reads from the empty 'locks' mapping
_updateBoostState(msg.sender, locks[msg.sender].amount); // amount will be 0

The contract primarily uses _lockState for lock management but inconsistently refers to the empty locks mapping. This leads to:

Incorrect boost calculations since _updateBoostState() always receives zero amounts when locked amount is increased.

The issue also occurs when querying the getLockedBalance() and getLockEndTime() as in both cases locks is used instead of lockState.locks which means the values returned are always zero.

This isn't just a case of redundant storage - it's an active conflict between two sources of truth that leads to incorrect system behavior.

PoC

  1. Alice locks 100 RAAC tokens using lock()

  2. Lock is correctly recorded in _lockState

  3. Alice calls increase(50) to add more tokens

  4. _updateBoostState() reads from empty locks mapping, receives 0 instead of 100

  5. Boost calculations are performed with incorrect amounts

Impact

  • Incorrect boost calculations affecting reward distribution

Tools Used

Manual code review

Recommendations

Remove the redundant locks mapping and exclusively use _lockState

- mapping(address => Lock) public locks;
// ...
function increase(uint256 amount) external nonReentrant whenNotPaused {
// Increase lock using LockManager
_lockState.increaseLock(msg.sender, amount);
+ _updateBoostState(msg.sender, _lockState.locks[msg.sender].amount);
- _updateBoostState(msg.sender, locks[msg.sender].amount);
}
// ...
function getLockedBalance(address account) external view returns (uint256) {
- return locks[account].amount;
+ return _lockState.locks[account].amount;
}
function getLockEndTime(address account) external view returns (uint256) {
- return locks[account].end;
+ return _lockState.locks[account].end;
}
Updates

Lead Judging Commences

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

veRAACToken::increase uses locks[msg.sender] instead of _lockState.locks[msg.sender] inside _updateBoostState call

Support

FAQs

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