Sablier

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

division before multiplication in _calculateStreamedAmount

Summary

for calculating the streamedAmount, we are doing first division and then multiplication. This will cause a rounding error.

Vulnerability Details

function _calculateStreamedAmount(uint256 streamId) internal view override returns (uint128) {
// If the cliff time is in the future, return zero.
uint256 cliffTime = uint256(_cliffs[streamId]);
uint256 blockTimestamp = block.timestamp;
if (cliffTime > blockTimestamp) {
return 0;
}

    // If the end time is not in the future, return the deposited amount.
    uint256 endTime = uint256(_streams[streamId].endTime);
    if (blockTimestamp >= endTime) {
        return _streams[streamId].amounts.deposited;
    }

    // In all other cases, calculate the amount streamed so far. Normalization to 18 decimals is not needed
    // because there is no mix of amounts with different decimals.
    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());
    }
}

Impact

wrong calculation of streamedAmount ,as we are dividng first by totalDuration and then multiplication by depositedAmount.

Tools Used

Recommendations

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);
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);

        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());
Updates

Lead Judging Commences

inallhonesty Lead Judge over 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.