Core Contracts

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

Double-Counting in `veRAACToken::increase` Leads to Incorrect Voting Power Calculation

Summary

A critical vulnerability exists in the veRAACToken::increase function of the veRAACToken contract, leading to an incorrect calculation of voting power when users increase their locked token amount. This results in double-counting the increased amount, granting users excess voting power.

Vulnerability Details

The root cause lies in the veRAACToken::increase function's handling of the lock amount update and subsequent voting power calculation. After increasing the lock via _lockState.increaseLock, the code retrieves the updated userLock.amount and erroneously adds the amount parameter again when invoking _votingState.calculateAndUpdatePower. This double-addition leads to inflated voting power.

Here is the code of veRAACToken::increase:

LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount + amount, // Double-counts the increased amount
userLock.end
);

Impact

Users receive 2x the intended voting power for their increased lock amount (e.g., +1000 tokens yields +2000 power instead of +1000)

Proof of Concept

it.only("H02 - Double-Counting in `veRAACToken::increase` Leads to Incorrect Voting Power Calculation", async () => {
const alice = users[0];
const amount = ethers.parseEther("1000");
const duration = 365 * 24 * 3600 * 4;
// Create first lock for alice
console.log("Creating lock for alice...");
const alice1Tx = await veRAACToken.connect(alice).lock(amount, duration);
await alice1Tx.wait();
const positionFirstLock = await veRAACToken.getLockPosition(alice.address);
console.log("Alice Position after lock creation: ");
console.log(" - amount: ", positionFirstLock.amount);
console.log(" - power: ", positionFirstLock.power);
expect(positionFirstLock.amount).to.equal(amount);
expect(positionFirstLock.power).to.equal(amount);
// Increase lock amount for alice
console.log("Increasing lock amount for alice...");
const alice2Tx = await veRAACToken.connect(alice).increase(amount);
await alice2Tx.wait();
const positionSecondLock = await veRAACToken.getLockPosition(alice.address);
console.log("Alice Position after increase: ");
console.log(" - amount: ", positionSecondLock.amount);
console.log(" - power: ", positionSecondLock.power);
expect(positionSecondLock.amount).to.equal(amount + amount);
expect(positionSecondLock.power).to.equal(amount + amount);
});

Here is the output of the test:

Creating lock for alice...
Alice Position after lock creation:
- amount: 1000000000000000000000n
- power: 1000000000000000000000n
Increasing lock amount for alice...
Alice Position after increase:
- amount: 2000000000000000000000n
- power: 2999999833523592085235n

Tools Used

Manual Review

Recommendations

Use the already-updated userLock.amount without adding the amount parameter again

LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount,
userLock.end
);
Updates

Lead Judging Commences

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