Sablier

Sablier
DeFiFoundry
53,440 USDC
View results
Submission Details
Severity: high
Invalid

Calculation error of withdrawable streams

Summary

Error in the calculation of available streams for withdrawal.

Vulnerability Details

Whenever a recipient calls withdrawal, their withdrawable amount for their accumulated stream is checked and the value is what they will be able to withdraw

// Check: the withdraw amount is not greater than the withdrawable amount.
uint128 withdrawableAmount = _withdrawableAmountOf(streamId);

This _withdrawableAmountOf(streamId); calls other internal functions that ultimately calls the _calculateStreamedAmount() and this is where the issue lies.
Taking a look at the _calculateStreamedAmount() in the Lockuplinear.sol contract.sol

unchecked {
// Calculate how much time has passed since the stream started, and the stream's total duration.
uint256 startTime = uint256(_streams[streamId].startTime);
UD60x18 elapsedTime = ud(blockTimestamp - startTime);
UD60x18 totalDuration = ud(endTime - startTime);
// Divide the elapsed time by the stream's total duration.
UD60x18 elapsedTimePercentage = elapsedTime.div(totalDuration);
// Cast the deposited amount to UD60x18.
UD60x18 depositedAmount = ud(_streams[streamId].amounts.deposited);
// Calculate the streamed amount by multiplying the elapsed time percentage by the deposited amount.
UD60x18 streamedAmount = elapsedTimePercentage.mul(depositedAmount);
// Although the streamed amount should never exceed the deposited amount, this condition is checked
// without asserting to avoid locking funds in case of a bug. If this situation occurs, the withdrawn
// amount is considered to be the streamed amount, and the stream is effectively frozen.
if (streamedAmount.gt(depositedAmount)) {
return _streams[streamId].amounts.withdrawn;
}
// Cast the streamed amount to uint128. This is safe due to the check above.
return uint128(streamedAmount.intoUint256());
}
}

In a given scenario where the
start time is (May 12) given as 1683991800

endtime = (May 30) 1717121400

Current timestamp = (May 23) 1716516600

A user intends on withdrawing his streamed amount on the 23rd of May after the stream started on the 12th of May.

The elapsed time will be computed as current timestamp - start time

elapsed time = 1716516600 - 1683991800 = 32524800

while total duration will be computed as endTime - startTime

totalduration = 1717121400 - 1683991800 = 33129600

Elapsed percentage will then be computed as elapsedtime/ totalduration

32524800/ 33129600 = 0.981744422

As can be seen above, elapsed time will always be a zero amount and it is used to multiply the deposited amount of the stream as seen in the code below:

// Calculate the streamed amount by multiplying the elapsed time percentage by the deposited amount.
UD60x18 streamedAmount = elapsedTimePercentage.mul(depositedAmount);

Impact

Users won't be able to place withdrawals due to a zero amount that will be computed for them as their withdrawable amount despite their accumulated streams.

Tools Used

Manual review

Recommendations

Perform the calculations properly in a manner it won't round down to a zero amount.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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