Description
The veRAACToken contract contains a critical vulnerability where the total locked token counter (_lockState.totalLocked) is not decreased when users withdraw their tokens. While the contract correctly tracks token deposits by increasing totalLocked, it fails to decrease this during withdrawals, leading to a permanent discrepancy between the tracked total and actual locked tokens.
function withdraw() external nonReentrant {
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
if (userLock.amount == 0) revert LockNotFound();
if (block.timestamp < userLock.end) revert LockNotExpired();
uint256 amount = userLock.amount;
uint256 currentPower = balanceOf(msg.sender);
delete _lockState.locks[msg.sender];
delete _votingState.points[msg.sender];
_checkpointState.writeCheckpoint(msg.sender, 0);
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
Impact
Incorrect total locked token accounting
Boost calculations become increasingly inaccurate as more withdrawals occur
New deposits might be incorrectly rejected due to inflated totalLocked value
Governance calculations relying on total locked amounts become unreliable
Protocol economics affected due to incorrect boost multipliers
Fix Recommendation
Add totalLocked reduction in both withdraw functions:
function withdraw() external nonReentrant {
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
if (userLock.amount == 0) revert LockNotFound();
if (block.timestamp < userLock.end) revert LockNotExpired();
uint256 amount = userLock.amount;
uint256 currentPower = balanceOf(msg.sender);
_lockState.totalLocked -= amount;
delete _lockState.locks[msg.sender];
delete _votingState.points[msg.sender];
_checkpointState.writeCheckpoint(msg.sender, 0);
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
function emergencyWithdraw() external nonReentrant {
uint256 amount = userLock.amount;
uint256 currentPower = balanceOf(msg.sender);
_lockState.totalLocked -= amount;
}
Tools Used