Sablier

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

Missing Maximum and Minimum Duration Checks in checkCreateLockupTranched Function

Summary

The checkCreateLockupTranched function lacks explicit checks for maximum and minimum durations of the lockup. This omission can lead to potential misuse and unexpected behavior of the protocol, as users can create lockups with durations that are either impractically short or excessively long.

Proof of Concept (PoC)

To demonstrate the impact, consider the following scenarios where users create lockups with impractical durations:

Excessively Long Duration:

uint128 depositAmount = 1000;
LockupTranched.Tranche[] memory tranches = new LockupTranched.Tranche[](1);
tranches[0] = LockupTranched.Tranche({amount: 1000, timestamp: block.timestamp + 100 * 365 * 24 * 60 * 60}); // 100 years in the future
uint256 maxTrancheCount = 10;
uint40 startTime = uint40(block.timestamp);
checkCreateLockupTranched(depositAmount, tranches, maxTrancheCount, startTime);

Impractically Short Duration:

uint128 depositAmount = 1000;
LockupTranched.Tranche[] memory tranches = new LockupTranched.Tranche[](1);
tranches[0] = LockupTranched.Tranche({amount: 1000, timestamp: block.timestamp + 1}); // 1 second in the future
uint256 maxTrancheCount = 10;
uint40 startTime = uint40(block.timestamp);
checkCreateLockupTranched(depositAmount, tranches, maxTrancheCount, startTime);

Impact

  1. Users can create lockups with very long durations, which might lock assets for an impractical amount of time. This could lead to liquidity issues and limit the usability of the protocol.

  2. Users can create lockups with very short durations, which might not serve the intended purpose of the lockup. This could undermine the security and functionality of the protocol.

  3. Malicious actors can exploit the absence of these checks to create lockups with durations that could disrupt the protocol’s operations or lead to unexpected edge cases.

Tools Used

manual review

Recommendations

To mitigate this issue, add explicit checks for maximum and minimum durations within the checkCreateLockupTranched function and _checkTranches function. Here are the enhanced versions of these functions:

Updated checkCreateLockupTranched Function

function checkCreateLockupTranched(
uint128 depositAmount,
LockupTranched.Tranche[] memory tranches,
uint256 maxTrancheCount,
uint40 startTime,
uint40 maxDuration,
uint40 minDuration
)
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 tranche count is not zero.
uint256 trancheCount = tranches.length;
if (trancheCount == 0) {
revert Errors.SablierV2LockupTranched_TrancheCountZero();
}
// Check: the tranche count is not greater than the maximum allowed.
if (trancheCount > maxTrancheCount) {
revert Errors.SablierV2LockupTranched_TrancheCountTooHigh(trancheCount);
}
// Check: requirements of tranches.
_checkTranches(tranches, depositAmount, startTime, maxDuration, minDuration);
}

Updated _checkTranches Function

function _checkTranches(
LockupTranched.Tranche[] memory tranches,
uint128 depositAmount,
uint40 startTime,
uint40 maxDuration,
uint40 minDuration
)
private
view
{
// Check: the start time is strictly less than the first tranche timestamp.
if (startTime >= tranches[0].timestamp) {
revert Errors.SablierV2LockupTranched_StartTimeNotLessThanFirstTrancheTimestamp(
startTime, tranches[0].timestamp
);
}
// Pre-declare the variables needed in the for loop.
uint128 trancheAmountsSum;
uint40 currentTrancheTimestamp;
uint40 previousTrancheTimestamp;
// Iterate over the tranches to:
// 1. Calculate the sum of all tranche amounts.
// 2. Check that the timestamps are ordered.
uint256 count = tranches.length;
for (uint256 index = 0; index < count; ++index) {
// Add the current tranche amount to the sum.
trancheAmountsSum += tranches[index].amount;
// Check: the current timestamp is strictly greater than the previous timestamp.
currentTrancheTimestamp = tranches[index].timestamp;
if (currentTrancheTimestamp <= previousTrancheTimestamp) {
revert Errors.SablierV2LockupTranched_TrancheTimestampsNotOrdered(
index, previousTrancheTimestamp, currentTrancheTimestamp
);
}
// Make the current timestamp the previous timestamp of the next loop iteration.
previousTrancheTimestamp = currentTrancheTimestamp;
}
// Check: the last timestamp is in the future.
uint40 blockTimestamp = uint40(block.timestamp);
if (blockTimestamp >= currentTrancheTimestamp) {
revert Errors.SablierV2Lockup_EndTimeNotInTheFuture(blockTimestamp, currentTrancheTimestamp);
}
// Check: the deposit amount is equal to the tranche amounts sum.
if (depositAmount != trancheAmountsSum) {
revert Errors.SablierV2LockupTranched_DepositAmountNotEqualToTrancheAmountsSum(
depositAmount, trancheAmountsSum
);
}
// Check: the duration is within allowed limits.
uint40 endTime = currentTrancheTimestamp; // last tranche's timestamp
uint40 duration = endTime - startTime;
if (duration > maxDuration) {
revert Errors.SablierV2LockupTranched_DurationTooLong(duration, maxDuration);
}
if (duration < minDuration) {
revert Errors.SablierV2LockupTranched_DurationTooShort(duration, minDuration);
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Info/Gas/Invalid as per Docs

https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity

Support

FAQs

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