Sablier

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

Potential Bypass of Time-Release Mechanism Due to Past Timestamps in Tranches

Summary

The createWithTimestamps function allows for the creation of a tranched stream with user-specified timestamps. However, there is no mechanism in place to prevent users from inputting timestamps from the past. This could lead to immediate vesting of tranches that are intended to be vested in the future, allowing recipients to withdraw all the vested funds at once, contrary to the sender's intentions.

function createWithTimestamps(LockupTranched.CreateWithTimestamps calldata params)
external
override
noDelegateCall
returns (uint256 streamId)
{
// ... (existing code)
streamId = _create(params);
// ... (existing code)
}

Proof of Concept

A sender creates a stream with multiple tranches, each with a timestamp meant to represent future vesting dates. However, by setting one or more tranche timestamps to a date in the past, the recipient can immediately access the funds associated with those tranches upon stream creation.

Impact

This issue could result in the sender's funds being released earlier than intended, potentially leading to financial loss or disputes. It also undermines the protocol's purpose of providing a secure time-release payment mechanism.

Tools Used

Manual review

Recommendations

To prevent users from inputting past timestamps, we can add validation checks in the createWithTimestamps function. These checks ensure that all provided timestamps are in the future relative to the current block timestamp.
Here is a modified createWithTimestamps function

function createWithTimestamps(LockupTranched.CreateWithTimestamps calldata params)
external
override
noDelegateCall
returns (uint256 streamId)
{
// Validate that the total amount is greater than zero
require(params.totalAmount > 0, "Total amount must be greater than zero");
// Validate that the start time is not in the past
require(params.startTime >= block.timestamp, "Start time must be in the future");
// Validate that tranches are non-empty
require(params.tranches.length > 0, "Tranches cannot be empty");
// Validate tranche ordering and future timestamps
for (uint256 i = 0; i < params.tranches.length; i++) {
// Ensure each tranche timestamp is in the future
require(params.tranches[i].timestamp > block.timestamp, "Tranche timestamp must be in the future");
// Ensure tranches are in chronological order
if (i > 0) {
require(params.tranches[i].timestamp > params.tranches[i - 1].timestamp, "Tranches are not ordered");
}
}
// Checks, Effects and Interactions: create the stream.
streamId = _create(params);
}

The changes are:

  1. The line require(params.tranches[i].timestamp > block.timestamp, "Tranche timestamp must be in the future"); ensures that each tranche's timestamp is in the future relative to the current block timestamp.

  2. This check is inside a loop that iterates over all tranches to ensure that every single tranche timestamp is validated.

  3. The line if (i > 0) { require(params.tranches[i].timestamp > params.tranches[i - 1].timestamp, "Tranches are not ordered"); } ensures that the tranches are in chronological order, preventing any past timestamps relative to each other.

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.