Sablier

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

Time Manipulation in checkCreateLockupDynamic

Summary

The checkCreateLockupDynamic function is designed to validate the creation of dynamic lockups with multiple segments. Each segment has an amount, exponent, and duration that define the release schedule of locked funds.

function checkCreateLockupDynamic(
uint128 depositAmount,
LockupDynamic.Segment[] memory segments,
uint256 maxSegmentCount,
uint40 startTime
)
internal
view
{
// ... (omitted code for brevity)
// Check: the start time is not zero.
if (startTime == 0) {
revert Errors.SablierV2Lockup_StartTimeZero();
}
// ... (omitted code for brevity)
}

Vulnerability Details

The checkCreateLockupDynamic function is expected to enforce that the lockup schedule starts at a valid time and that the segments are released in the future. However, if the startTime is not validated against the current block timestamp or if users can set the startTime themselves without proper checks, they could manipulate the start of the lockup period to a past time. This would effectively shorten the lockup duration and grant premature access to the funds.

Impact

The function assumes that the startTime provided is either the current time or a future time. If the startTime can be manipulated to a past timestamp, it could potentially allow a user to access the locked funds earlier than intended. This could undermine the purpose of the lockup, which is to restrict access to funds until certain conditions are met over time.

Tools Used

Manual review

Recommendations

To mitigate the issue of allowing startTime to be set in the past in the checkCreateLockupDynamic function, you should add an explicit check to ensure that startTime is in the future relative to the current block timestamp.

Here’s an enhanced version of the checkCreateLockupDynamic function with the added validation:

function checkCreateLockupDynamic(
uint128 depositAmount,
LockupDynamic.Segment[] memory segments,
uint256 maxSegmentCount,
uint40 startTime
)
internal
view
{
// Check: the deposit amount is not zero.
if (depositAmount == 0) {
revert Errors.SablierV2Lockup_DepositAmountZero();
}
// Check: the start time is not zero.
if (startTime == 0) {
revert Errors.SablierV2Lockup_StartTimeZero();
}
// Check: the start time is in the future.
uint40 blockTimestamp = uint40(block.timestamp);
if (startTime <= blockTimestamp) {
revert Errors.SablierV2Lockup_StartTimeNotInTheFuture(blockTimestamp, startTime);
}
// Check: the segment count is not zero.
uint256 segmentCount = segments.length;
if (segmentCount == 0) {
revert Errors.SablierV2LockupDynamic_SegmentCountZero();
}
// Check: the segment count is not greater than the maximum allowed.
if (segmentCount > maxSegmentCount) {
revert Errors.SablierV2LockupDynamic_SegmentCountTooHigh(segmentCount);
}
// Check: requirements of segments.
_checkSegments(segments, depositAmount, startTime);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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