stake.link

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

SDLPoolPrimary : User would lose their boost amount value even if even they initiate unlock after completing the lock duration.

Summary

During initiateUnlock, though the total lock duration is passed, user's boost among is still nullified.

Vulnerability Details

To initiate the unlock, user can call the function initiateUnlock and wait till the expiry to withdraw their locked amount.
User can call this function once half of the lock duration is passed.

Lets see the function initiateUnlock,
https://github.com/Cyfrin/2023-12-stake-link/blob/549b2b8c4a5b841686fceb9c311dca9ac58225df/contracts/core/sdlPool/SDLPoolPrimary.sol#L107-L121

function initiateUnlock(uint256 _lockId) external onlyLockOwner(_lockId, msg.sender) updateRewards(msg.sender) {
if (locks[_lockId].expiry != 0) revert UnlockAlreadyInitiated();
uint64 halfDuration = locks[_lockId].duration / 2;
if (locks[_lockId].startTime + halfDuration > block.timestamp) revert HalfDurationNotElapsed();
uint64 expiry = uint64(block.timestamp) + halfDuration;
locks[_lockId].expiry = expiry;
uint256 boostAmount = locks[_lockId].boostAmount;
locks[_lockId].boostAmount = 0;
effectiveBalances[msg.sender] -= boostAmount;
totalEffectiveBalance -= boostAmount;
emit InitiateUnlock(msg.sender, _lockId, expiry);
}

above functions check if half of the lock duration is passed and updates the expiry time. Later user would call withdraw function and withdraw their sdl tokens.
Here, it is not checked if the lock duration is already passed when updating the boost amount. But the boost amount is reset.

This would impact a valid user, who initiate the unlock after their total lock duration and waiting for some time. But they will be forgiven the rewards because non availability of boost amount.

Impact

User would be forgiven the rewards despite they locked for full duration.

Tools Used

Manual review.

Recommendations

Update the initiateUnlock function as show below.

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

function initiateUnlock(uint256 _lockId) external onlyLockOwner(_lockId, msg.sender) updateRewards(msg.sender) {
if (locks[_lockId].expiry != 0) revert UnlockAlreadyInitiated();
uint64 halfDuration = locks[_lockId].duration / 2;
if (locks[_lockId].startTime + halfDuration > block.timestamp) revert HalfDurationNotElapsed();
uint64 expiry = uint64(block.timestamp) + halfDuration;
locks[_lockId].expiry = expiry;
uint256 boostAmount = locks[_lockId].boostAmount;
if(locks[_lockId].duration > block.timestamp) ----------->> updated here.
{
locks[_lockId].boostAmount = 0;
effectiveBalances[msg.sender] -= boostAmount;
totalEffectiveBalance -= boostAmount;
}
emit InitiateUnlock(msg.sender, _lockId, expiry);
}

Update this boost amount value in withdraw call if partial withdraw is done.

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.