In the increase() function of veRAACToken.sol, there is a critical bug where the lock amount is effectively doubled when calculating the new voting power. This occurs because the function incorrectly adds the new amount twice when calling calculateAndUpdatePower().
function increase(uint256 amount) external nonReentrant whenNotPaused {
_lockState.increaseLock(msg.sender, amount);
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount + amount,
userLock.end
);
}
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("veRAACToken increase() vulnerability", function () {
let raacToken, veRAACToken;
let deployer, user;
const parseEth = ethers.utils.parseEther;
before(async function () {
[deployer, user] = await ethers.getSigners();
const RAACMock = await ethers.getContractFactory("RAACMock", deployer);
raacToken = await RAACMock.deploy("RAAC", "RAAC", parseEth("1000000"));
await raacToken.deployed();
const VeRAACToken = await ethers.getContractFactory("veRAACToken", deployer);
veRAACToken = await VeRAACToken.deploy(raacToken.address);
await veRAACToken.deployed();
await raacToken.transfer(user.address, parseEth("1000"));
});
it("should inflate voting power incorrectly on increase", async function () {
await raacToken.connect(user).approve(veRAACToken.address, parseEth("1000"));
const oneYear = 365 * 24 * 3600;
await veRAACToken.connect(user).lock(parseEth("500"), oneYear);
const initialVotingPower = await veRAACToken.getVotingPower(user.address);
await raacToken.connect(user).approve(veRAACToken.address, parseEth("100"));
await veRAACToken.connect(user).increase(parseEth("100"));
const newVotingPower = await veRAACToken.getVotingPower(user.address);
const expectedMin = initialVotingPower.mul(600).div(500);
expect(newVotingPower).to.be.gt(expectedMin);
});
});
function increase(uint256 amount) external nonReentrant whenNotPaused {
_lockState.increaseLock(msg.sender, amount);
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount,
userLock.end
);
}