Vulnerability Details
function increase(uint256 amount) external nonReentrant whenNotPaused {
_lockState.increaseLock(msg.sender, amount);
_updateBoostState(msg.sender, locks[msg.sender].amount);
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount + amount,
userLock.end
);
....
}
We can see that lockState.increaseLock(msg.sender, amount);
is being called in _increase
which updates the _ _lockState of msg.sender
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();
if (lock.amount + additionalAmount > state.maxLockAmount) revert AmountExceedsLimit();
lock.amount += additionalAmount;
state.totalLocked += additionalAmount;
emit LockIncreased(user, additionalAmount);
}
We can see that lock.amount
has been updated with the additionalAmount
.
function increase(uint256 amount) external nonReentrant whenNotPaused {
_lockState.increaseLock(msg.sender, amount);
_updateBoostState(msg.sender, locks[msg.sender].amount);
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount + amount,
userLock.end
);
However, when calling _votingState.calculateAndUpdatePower
, the second argument adds the new amount
to userLock.amount
even though the userLock.amount
has already been updated with amount
. This creates a situation where the old userLock
amount
has been updated twice with the increase amount
instead of once.
Impact
An inflated amount is being passed to _votingState.calculateAndUpdatePower
Tools Used
Manual
Recommendations
Use userLock.amount
without adding amount
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount
userLock.end
);