stake.link

stake.link
DeFiHardhatBridge
27,500 USDC
View results
Submission Details
Severity: medium
Invalid

SDLPoolPrimary, SDLPoolSecondary :_storeUpdatedLock can not be done in certain situation

Summary

Once the lock is created, it has option to update by calling the function _storeUpdatedLock.
inside the function, the lock values are updated. during this time, the subtraction would cause overflow and revert.

Vulnerability Details

Lets see the function extendLockDuration.

https://github.com/Cyfrin/2023-12-stake-link/blob/549b2b8c4a5b841686fceb9c311dca9ac58225df/contracts/core/sdlPool/SDLPoolPrimary.sol#L91-L94

function extendLockDuration(uint256 _lockId, uint64 _lockingDuration) external {
if (_lockingDuration == 0) revert InvalidLockingDuration();
_storeUpdatedLock(msg.sender, _lockId, 0, _lockingDuration);
}

It updates the lock's duration.

below, _storeUpdatedLock function is shown.

https://github.com/Cyfrin/2023-12-stake-link/blob/549b2b8c4a5b841686fceb9c311dca9ac58225df/contracts/core/sdlPool/SDLPoolPrimary.sol#L307-L329

function _storeUpdatedLock(
address _owner,
uint256 _lockId,
uint256 _amount,
uint64 _lockingDuration
) internal onlyLockOwner(_lockId, _owner) updateRewards(_owner) {
Lock memory lock = _updateLock(locks[_lockId], _amount, _lockingDuration);
int256 diffTotalAmount = int256(lock.amount + lock.boostAmount) -
int256(locks[_lockId].amount + locks[_lockId].boostAmount);
if (diffTotalAmount > 0) {
effectiveBalances[_owner] += uint256(diffTotalAmount);
totalEffectiveBalance += uint256(diffTotalAmount);
} else if (diffTotalAmount < 0) {
effectiveBalances[_owner] -= uint256(-1 * diffTotalAmount); ---------->> revert would happen here.
totalEffectiveBalance -= uint256(-1 * diffTotalAmount);
}
locks[_lockId] = lock;
emit UpdateLock(_owner, _lockId, lock.amount, lock.boostAmount, lock.duration);
}

whenever a lock wanted to be updated using the above function, _updateLock function is called.
Here, the locked amount is updated again with updated boost value.
https://github.com/Cyfrin/2023-12-stake-link/blob/549b2b8c4a5b841686fceb9c311dca9ac58225df/contracts/core/sdlPool/base/SDLPool.sol#L417-L421

Lock memory lock = Lock(_lock.amount, _lock.boostAmount, _lock.startTime, _lock.duration, _lock.expiry);
uint256 baseAmount = _lock.amount + _amount;
uint256 boostAmount = boostController.getBoostAmount(baseAmount, _lockingDuration);

The point we need to note here is, the Boost amount can vary based on the boost value that is set by owner here, https://github.com/Cyfrin/2023-12-stake-link/blob/549b2b8c4a5b841686fceb9c311dca9ac58225df/contracts/core/sdlPool/LinearBoostController.sol#L56-L59

Lets think about below case.

A user creates a lock when the boost value is 10%. After time passes, the boost value is updated as 100% ( for understanding) due to market condition or inflation adjustments, the boost value is increased.

User wanted to update the lock, let says, increase the lock. But it would revert in following place.
https://github.com/Cyfrin/2023-12-stake-link/blob/549b2b8c4a5b841686fceb9c311dca9ac58225df/contracts/core/sdlPool/SDLPoolPrimary.sol#L313-L324

Lock memory lock = _updateLock(locks[_lockId], _amount, _lockingDuration);
int256 diffTotalAmount = int256(lock.amount + lock.boostAmount) -
int256(locks[_lockId].amount + locks[_lockId].boostAmount); ----------------->> higher then old lock.
if (diffTotalAmount > 0) {
effectiveBalances[_owner] += uint256(diffTotalAmount);
totalEffectiveBalance += uint256(diffTotalAmount);
} else if (diffTotalAmount < 0) {
effectiveBalances[_owner] -= uint256(-1 * diffTotalAmount); //// diffTotalAmount > effectiveBalances[_owner] --->> revert happen here
totalEffectiveBalance -= uint256(-1 * diffTotalAmount);
}

Impact

User can not able to update the lock. Updating lock is one of the core feature. But this can not be done due to the flaw.

Tools Used

Manual review

Recommendations

Update the _storeUpdatedLock as shown below with unchecked bock to prevent this revertion.
https://github.com/Cyfrin/2023-12-stake-link/blob/549b2b8c4a5b841686fceb9c311dca9ac58225df/contracts/core/sdlPool/SDLPoolPrimary.sol#L307-L324

function _storeUpdatedLock(
address _owner,
uint256 _lockId,
uint256 _amount,
uint64 _lockingDuration
) internal onlyLockOwner(_lockId, _owner) updateRewards(_owner) {
Lock memory lock = _updateLock(locks[_lockId], _amount, _lockingDuration);
int256 diffTotalAmount = int256(lock.amount + lock.boostAmount) -
int256(locks[_lockId].amount + locks[_lockId].boostAmount);
unchecked {
if (diffTotalAmount > 0) {
effectiveBalances[_owner] += uint256(diffTotalAmount);
totalEffectiveBalance += uint256(diffTotalAmount);
} else if (diffTotalAmount < 0) {
effectiveBalances[_owner] -= uint256(-1 * diffTotalAmount);
totalEffectiveBalance -= uint256(-1 * diffTotalAmount);
}
}
Updates

Lead Judging Commences

0kage Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.