Core Contracts

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

Voting power is incorrectly recorded when user increases lock

Summary

The function veRAACToken::increase() allows an user to add more tokens to an existing lock without changing the unlock time. However, the function is incorrectly calculates user's new voting power, which results higher voting power than expected

Vulnerability Details

The function veRAACToken::increase() is incorrect that it adds twice amount to user's lock state for calculating voting power. As shown below, _lockState.increaseLock(msg.sender, amount) handles adding the amount into user lock state. But when calling _votingState.calculateAndUpdatePower(), the function still uses userLock.amount + amount, which means that amount is counted twice.

As a result, users voting power is calculated inaccurately.

// veRAACToken
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, // here, `userLock.amount` has already included `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);
}
// LockManager
function increaseLock(
LockState storage state,
address user,
uint256 additionalAmount
) internal {
Lock storage lock = state.locks[user];
if (!lock.exists) revert LockNotFound();
if (lock.end <= block.timestamp) revert LockExpired();
// Maximum lock amount
if (lock.amount + additionalAmount > state.maxLockAmount) revert AmountExceedsLimit();
// Maximum total locked amount
// if (state.totalLocked + additionalAmount > state.maxTotalLocked) revert AmountExceedsLimit();
@> lock.amount += additionalAmount; // lock amount is increased here. This is storage update
state.totalLocked += additionalAmount;
emit LockIncreased(user, additionalAmount);
}

PoC

Add the test to test/unit/core/tokens/veRAACToken.test.js

describe("Lock Mechanism", () => {
...
it.only("increase lock amount will increase wrong power", async () => {
// @audit increase lock amount will increase wrong power
const amount1 = ethers.parseEther("1000");
const amount2 = ethers.parseEther("500");
const amount3 = ethers.parseEther("1500");
const duration = 365 * 24 * 3600; // 1 year
// user0 locks 1000
await veRAACToken.connect(users[0]).lock(amount1, duration);
// user1 locks 500
await veRAACToken.connect(users[1]).lock(amount2, duration);
// then increase 500
await veRAACToken.connect(users[1]).increase(amount2)
// user2 locks 1500
await veRAACToken.connect(users[2]).lock(amount3, duration);
let user0Power = await veRAACToken.getVotingPower(users[0].address)
let user1Power = await veRAACToken.getVotingPower(users[1].address)
let user2Power = await veRAACToken.getVotingPower(users[2].address)
console.log(`user0 locks 1000 tokens, receives ${user0Power} voting power`)
console.log(`user1 locks (500 + 500) tokens, receives ${user1Power} voting power`)
console.log(`user2 locks 1500 tokens, receives ${user2Power} voting power`)
});

Run the test and it show

veRAACToken
Lock Mechanism
user0 locks 1000 tokens, receives 250000000000000000000 voting power
user1 locks (500 + 500) tokens, receives 375000000000000000000 voting power
user2 locks 1500 tokens, receives 375000000000000000000 voting power
✔ increase lock amount will increase wrong power
1 passing (2s)

It means that user1 locks total 1000 tokens but voting power is not equal to user0 but it equals to user2

Impact

  • Incorrect voting power for users, such that users can get more voting power than expected

  • Incorrect checkpoints

Tools Used

Manual

Recommendations

(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
- userLock.amount + amount,
+ userLock.amount,
userLock.end
);
Updates

Lead Judging Commences

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

Give us feedback!