Core Contracts

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

Local `locks[...]` Remains Zero and Is Used in Critical Calculations

****

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/veRAACToken.sol#L251

Overview

The contract code references two separate mappings for lock positions:

  1. _lockState.locks[msg.sender] — Maintained by the external LockManager library (the real “source of truth”).

  2. locks[msg.sender] (a public mapping inside veRAACToken) — Never updated in any function, yet still used in _updateBoostState.

veRAACToken.sol::increase(...) Function

function increase(uint256 amount) external nonReentrant whenNotPaused {
_lockState.increaseLock(msg.sender, amount); // <— updates library-based locks
_updateBoostState(msg.sender, locks[msg.sender].amount); // <— uses local locks[]
// ...
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
// userLock has correct new amount, but locks[msg.sender] is never updated
// ...
}

Because locks[msg.sender] is never assigned any value, locks[msg.sender].amount is presumably 0 or an uninitialized default. Consequently, _updateBoostState(msg.sender, 0) is called instead of _updateBoostState(msg.sender, actualUserLockedAmount).

Impact

  1. Incorrect Boost Calculations:
    The _updateBoostState call incorrectly passes 0 for the user’s locked amount, so the internal _boostState might drastically under‐calculate a user’s voting power ratio or skip awarding the user’s rightful boost.

  2. Misleading Public locks[...]:
    Observers or integrators calling veRAACToken.locks(user) see an empty or stale lock record, potentially believing the user has no lock or a lesser amount than reality.

  3. Potential Governance & Reward Distortions:
    Since _updateBoostState is used for reward/boost logic, the user could receive less rewards or effect on governance than intended. In some edge cases, a mismatch might also reduce how a user’s lock is recognized in other calculations.

PoC / Demonstration

A minimal Foundry test can demonstrate how calling increase(...) fails to update locks[msg.sender]:

function testLockDataMismatch() public {
// 1) User locks some tokens
veToken.lock(100e18, 365 days);
// 2) The user calls increase(50e18).
// Under the hood, only _lockState.locks[msg.sender] is updated,
// but the local mapping locks[msg.sender] is never changed.
veToken.increase(50e18);
// 3) Check the local mapping vs. the library-based lock.
// locks[user].amount => Should be 150e18, but is 0 or remains 100e18 if never set.
(uint256 localAmt, ) = veToken.locks(user).amount; // presumably 0 or stale
LockManager.Lock memory actualLock = lockState.locks(user); // 150e18
assertNotEq(localAmt, actualLock.amount); // mismatch
}

This discrepancy reveals that _updateBoostState(msg.sender, locks[msg.sender].amount) calls are passing the wrong values, leading to incorrect boost or voting power updates.

Recommended Fix

  • Remove or Maintain the Local Mapping:

    • Option A: Remove mapping(address => Lock) public locks; entirely if _lockState.locks[...] is the real storage. Then calls like _updateBoostState(msg.sender, ???) should get the amount from _lockState.locks[msg.sender].amount.

    • Option B: Keep locks[...] but update it whenever _lockState.locks[...] is modified (e.g., in lock(...), increase(...), extend(...), etc.) so that locks[user] consistently matches _lockState.locks[user].

  • Update _updateBoostState(...):
    Inside increase(...), do:

    LockManager.Lock memory userLock = _lockState.locks[msg.sender];
    _updateBoostState(msg.sender, userLock.amount);

    rather than referencing locks[msg.sender].amount.

Updates

Lead Judging Commences

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

Give us feedback!