Sablier

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

SablierV2LockupDynamic : _calculateStreamedAmountForMultipleSegments did not make sure whether the first segment time is fully passed.

Summary

The function _calculateStreamedAmountForMultipleSegments is not checking whether the first segment time is fully passed or not.
This lead to case where, even if the first tranche is not fully passed, the function return full amount of that tranche.

Vulnerability Details

_calculateStreamedAmountForMultipleSegments function is called inside the _calculateStreamedAmount if the number of segements are more than 1.

SablierV2LockupDynamic.sol#L204-L207

if (_segments[streamId].length > 1) {
// If there is more than one segment, it may be required to iterate over all of them.
return _calculateStreamedAmountForMultipleSegments(streamId);
}

Inside the _calculateStreamedAmountForMultipleSegments each segment is passed on and the corresponding amount vested is computed using the exponent factor.

SablierV2LockupDynamic.sol#L228-L263

For first segment, the index is zero.

uint128 previousSegmentAmounts;
uint40 currentSegmentTimestamp = segments[0].timestamp;
uint256 index = 0;
while (currentSegmentTimestamp < blockTimestamp) {
previousSegmentAmounts += segments[index].amount;
index += 1;
currentSegmentTimestamp = segments[index].timestamp;
}
// After exiting the loop, the current segment is at `index`.
SD59x18 currentSegmentAmount = segments[index].amount.intoSD59x18();
SD59x18 currentSegmentExponent = segments[index].exponent.intoSD59x18();
currentSegmentTimestamp = segments[index].timestamp;
uint40 previousTimestamp;
if (index == 0) {
// When the current segment's index is equal to 0, the current segment is the first, so use the start
// time as the previous timestamp.
previousTimestamp = stream.startTime;
} else {
// Otherwise, when the current segment's index is greater than zero, it means that the segment is not
// the first. In this case, use the previous segment's timestamp.
previousTimestamp = segments[index - 1].timestamp;
}
// Calculate how much time has passed since the segment started, and the total duration of the segment.
SD59x18 elapsedTime = (blockTimestamp - previousTimestamp).intoSD59x18();
SD59x18 segmentDuration = (currentSegmentTimestamp - previousTimestamp).intoSD59x18(); ---->>> @@ audit find - full duration as segment duration.
// Divide the elapsed time by the total duration of the segment.
SD59x18 elapsedTimePercentage = elapsedTime.div(segmentDuration);
// Calculate the streamed amount using the special formula.
SD59x18 multiplier = elapsedTimePercentage.pow(currentSegmentExponent);
SD59x18 segmentStreamedAmount = multiplier.mul(currentSegmentAmount);

For the first segment, thought the time for that segment is not fully passed, the function use the full time to calculate the transche amount.

Impact

Even if the full time is passed for the first tranche, the _calculateStreamedAmountForOneSegment returns the full amount for that particular tranche.

It would be something, like, an employee earn their first month salary at the first day itself.

Tools Used

Manual review

Recommendations

This one need some code changes, we would suggest to update the code to handle the total time elapsed exactly.

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.