Summary
A user can call lock()
more than once which will override their existing position, leading to lost funds.
Vulnerability Details
In veRAACToken.sol, a user calls lock()
to create a new lock position with their RAAC Tokens.
The function will call _lockState.createLock()
to create a lock and mint veRAAC tokens for the user.
function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
if (amount == 0) revert InvalidAmount();
if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();
if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded();
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();
raacToken.safeTransferFrom(msg.sender, address(this), amount);
uint256 unlockTime = block.timestamp + duration;
> _lockState.createLock(msg.sender, amount, duration);
_updateBoostState(msg.sender, amount);
(int128 bias, int128 slope) = _votingState.calculateAndUpdatePower(
msg.sender,
amount,
unlockTime
);
uint256 newPower = uint256(uint128(bias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
> _mint(msg.sender, newPower);
This is the createLock()
function. Note that there is no checks to ensure that the lock exist or not, which means that users can call lock()
again by accident which will override their existing lock.
function createLock(
LockState storage state,
address user,
uint256 amount,
uint256 duration
) internal returns (uint256 end) {
if (state.minLockDuration != 0 && state.maxLockDuration != 0) {
if (duration < state.minLockDuration || duration > state.maxLockDuration)
revert InvalidLockDuration();
}
if (amount == 0) revert InvalidLockAmount();
end = block.timestamp + duration;
state.locks[user] = Lock({
amount: amount,
end: end,
exists: true
});
state.totalLocked += amount;
emit LockCreated(user, amount, end);
return end;
}
Impact
Losing funds
Tools Used
Recommendations
Ensure that users can only have one lock position at a time. A way is to check whether the lock exist in the createLock()
function, or simply check the unlock time in veRAACToken.sol. If the unlock time is more than 0, that means that there is a lock.