stake.link

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

SDLPoolPrimary : Users are forced to wait more than the lock duration to withdraw their amount

Summary

Users amount is locked for a period of time against their choice.

Vulnerability Details

User can initiate unlock by calling the function initiateUnlock.

here,

  1. check half of the lock time is passed.

  2. updates the expiry time.

  3. boost amount is set to zero.

Later, user can call the withdraw function and withdraw their shares.

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; ------------------->> this set more time.
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);
}

lets see some cases,

  1. user locked their sdl tokens.

  2. 90% of lock duration is passed.

  3. they call initiate unlock function.

  4. Now, their expiry time is set from current time + half duration of lock.

  5. This is sets the expire more than total lock duration.

refer below place for wait time check
https://github.com/Cyfrin/2023-12-stake-link/blob/549b2b8c4a5b841686fceb9c311dca9ac58225df/contracts/core/sdlPool/SDLPoolPrimary.sol#L134-L143

function withdraw(uint256 _lockId, uint256 _amount)
external
onlyLockOwner(_lockId, msg.sender)
updateRewards(msg.sender)
{
if (locks[_lockId].startTime != 0) {
uint64 expiry = locks[_lockId].expiry;
if (expiry == 0) revert UnlockNotInitiated();
if (expiry > block.timestamp) revert TotalDurationNotElapsed(); --------->> here.
}

Impact

users are forced to wait more than the lock duration to withdraw their amount.

Tools Used

Manual review.

Recommendations

If the expiry time exceeds the total lock duration, set the expiry as total lock duration.

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;
if(expiry > locks[_lockId].duration) --------------->>>updated
expiry = locks[_lockId].duration;------------->> updated
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);
}
Updates

Lead Judging Commences

0kage Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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