Core Contracts

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

Users cannot increase lock position after certain days

Summary

Underflow in veRAACToken.increase function will prevent users from increasing their position certain days after initial lock position.

Vulnerability Details

Root Cause Analysis

The vulnerabilty origniates from veRAACToken.increase function:

function increase(uint256 amount) external nonReentrant whenNotPaused {
// Increase lock using LockManager
_lockState.increaseLock(msg.sender, amount);
_updateBoostState(msg.sender, locks[msg.sender].amount);
// Update voting power
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount + amount,
userLock.end
);
// Update checkpoints
uint256 newPower = uint256(uint128(newBias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
// Transfer additional tokens and mint veTokens
raacToken.safeTransferFrom(msg.sender, address(this), amount);
@> _mint(msg.sender, newPower - balanceOf(msg.sender)); // @audit underflow when new power is less than balance
emit LockIncreased(msg.sender, amount);
}

When a new lock position is created, the user gets granted the following amount of veToken and voting power ref:

veTokenBalance = depositedRAACTokenAmount * duration / MAX_LOCK_DURATION
votingPower = veTokenBalance

And votingPower diminishes as days pass by ref:

votingPower = initialVotingPower - slope * elapsedTime

However, veTokenBalance is not decreased unless user extends duration or withdraws from the position.

With this knowledge, consider the following scenario:

  • User creates a position with 100 RAACToken

    • User's voting power is 100

    • User's veToken balance is 100

  • Half of the lock duration passes by

    • User's voting power is 50

    • User's veToken balance is 100

  • User tries to increase the position with 10 RAACToken

    • New voting power = (100 + 10) * 0.5 = 55

    • User's veToken balance is still 100

Thus, newPower - balanceOf(msg.sender) will revert with underflow.

POC

Scenario

Scenario is exactly the same with the aforementioned one.

How to run POC

pragma solidity ^0.8.19;
import "../lib/forge-std/src/Test.sol";
import {veRAACToken} from "../contracts/core/tokens/veRAACToken.sol";
import {RAACToken} from "../contracts/core/tokens/RAACToken.sol";
contract veRAACTokenTest is Test {
RAACToken raacToken;
veRAACToken veToken;
address user = makeAddr("user");
uint256 raacTokenAmount = 100e18;
function setUp() external {
raacToken = new RAACToken(address(this), 0, 0);
raacToken.setMinter(address(this));
veToken = new veRAACToken(address(raacToken));
veToken.setMinter(address(this));
}
function testLockAndIncrease() external {
uint256 duration = veToken.MAX_LOCK_DURATION();
// user locks 100 raacToken
raacToken.mint(user, raacTokenAmount);
vm.startPrank(user);
raacToken.approve(address(veToken), raacTokenAmount);
veToken.lock(raacTokenAmount, duration);
vm.stopPrank();
// half of the duration passes by
skip(duration / 2);
// user tries to increase the position with 10 raacToken
uint256 increaseAmount = raacTokenAmount / 10;
raacToken.mint(user, increaseAmount);
vm.startPrank(user);
raacToken.approve(address(veToken), increaseAmount);
// underflow prevents increasing
vm.expectRevert(stdError.arithmeticError);
veToken.increase(increaseAmount);
vm.stopPrank();
}
}

Impact

Users won't be able to increase their position after certain days.

However, if they provide enough raacToken so that newPower is greater than current balance, they can increase their position.

Tools Used

Manual Review, Foundry

Recommendations

Like in extend function, if newPower is smaller than oldPower, burn veToken:

diff --git a/contracts/core/tokens/veRAACToken.sol b/contracts/core/tokens/veRAACToken.sol
index 2dbeefc..aa04942 100644
--- a/contracts/core/tokens/veRAACToken.sol
+++ b/contracts/core/tokens/veRAACToken.sol
@@ -267,7 +267,12 @@ contract veRAACToken is ERC20, Ownable, ReentrancyGuard, IveRAACToken {
// Transfer additional tokens and mint veTokens
raacToken.safeTransferFrom(msg.sender, address(this), amount);
- _mint(msg.sender, newPower - balanceOf(msg.sender));
+ uint256 oldPower = balanceOf(msg.sender);
+ if (newPower > oldPower) {
+ _mint(msg.sender, newPower - oldPower);
+ } else if (newPower < oldPower) {
+ _burn(msg.sender, oldPower - newPower);
+ }
emit LockIncreased(msg.sender, amount);
}
Updates

Lead Judging Commences

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

veRAACToken::increase underflows on newPower - balanceOf(msg.sender)

Support

FAQs

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