Core Contracts

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

veRAACToken: Lock Extension Resets Voting Power, Enabling Exploits

Summary

The extend function does not consider user's current voting power (decayed in time):

The problem lies in how _votingState.calculateAndUpdatePower is being used within the extend function. It's using userLock.amount as the locked amount, which is the original locked amount. It's not taking into account the fact that the user's voting power has already decayed over time.

Therefore, when the lock is extended, the power is being recalculated as if the user had just locked userLock.amount for the entire new duration, leading to the power boost.

Vulnerability Details

Bob also locks 10,000 RAACTokens for a year. He notices that there's no minimum extension duration check in the contract. Every 10 seconds, he calls the extend function, extending his lock by just 10 seconds each time. Because the extend function calculates voting power based on the total extended duration from the current moment (due to Bug 1), and there's no minimum extension duration, Bob effectively "resets" his voting power calculation every 10 seconds. His voting power never decreases, even though it's supposed to decay over time. He maintains a disproportionately high voting power indefinitely by repeatedly extending his lock by tiny increments, circumventing the intended power decay mechanism. He's essentially getting the benefit of a full lock duration repeatedly.

Here's the Bob scenario broken down into steps, incorporating code snippets where relevant:

Initial State:

  1. Bob locks 10,000 RAACTokens for one year (365 days). Let's assume his initial voting power is calculated based on this lock.

  2. Bob notices the extend function lacks a minimum extension duration check:

    function extend(uint256 extensionDuration) external { // No minimum duration check!
    // ...
    }
  3. Every 10 seconds, Bob calls extend(10).

  4. Inside extend, the _lockState.extendLock function correctly updates the lock's end time. However, the crucial problem is in the subsequent voting power calculation:

    uint256 newUnlockTime = _lockState.extendLock(msg.sender, newDuration); // Correctly extends lock time
    LockManager.Lock memory userLock = _lockState.locks[msg.sender];
    (int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
    msg.sender,
    userLock.amount, // BUG: Using ORIGINAL amount, NOT current power
    newUnlockTime
    );
  5. Because calculateAndUpdatePower uses userLock.amount (the original 10,000) instead of Bob's current, decayed voting power, it recalculates his power as if he had just locked 10,000 RAACTokens for the entire duration leading up to newUnlockTime. This effectively resets his voting power to the initial high value.

  6. Since there's no minimum extensionDuration, Bob can repeat steps 3-5 indefinitely. Each 10-second extension triggers a power recalculation based on the original amount, constantly "refreshing" his voting power and preventing it from decaying.

Consequence:

Bob maintains a disproportionately high voting power, effectively circumventing the intended power decay mechanism. He gains an unfair advantage in the veRAACToken system.

Impact

By calling extend in short time periods, user can get max voting power, avoiding the power decay that's supposed to happen over time . This gives him an unfair advantage in the reward system.

Tools Used

vscode

Recommendations

The extend function must be modified to calculate voting power based on the current, decayed voting power of the user, not the original locked amount.

Updates

Lead Judging Commences

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

Support

FAQs

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