Skip to content

Commit

Permalink
refactor: rename milestone to timestamp
Browse files Browse the repository at this point in the history
refactor: rename delta to duration
  • Loading branch information
andreivladbrg committed Jan 24, 2024
1 parent 0cdccf9 commit bd64353
Show file tree
Hide file tree
Showing 27 changed files with 302 additions and 296 deletions.
8 changes: 5 additions & 3 deletions script/Init.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ contract Init is BaseScript {
//////////////////////////////////////////////////////////////////////////*/

// Create the default lockupDynamic stream.
LockupDynamic.SegmentWithDelta[] memory segments = new LockupDynamic.SegmentWithDelta[](2);
segments[0] = LockupDynamic.SegmentWithDelta({ amount: 2500e18, exponent: ud2x18(3.14e18), delta: 1 hours });
segments[1] = LockupDynamic.SegmentWithDelta({ amount: 7500e18, exponent: ud2x18(0.5e18), delta: 1 weeks });
LockupDynamic.SegmentWithDuration[] memory segments = new LockupDynamic.SegmentWithDuration[](2);
segments[0] =
LockupDynamic.SegmentWithDuration({ amount: 2500e18, exponent: ud2x18(3.14e18), duration: 1 hours });
segments[1] =
LockupDynamic.SegmentWithDuration({ amount: 7500e18, exponent: ud2x18(0.5e18), duration: 1 weeks });
lockupDynamic.createWithDurations(
LockupDynamic.CreateWithDurations({
sender: sender,
Expand Down
28 changes: 14 additions & 14 deletions src/SablierV2LockupDynamic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ contract SablierV2LockupDynamic is
noDelegateCall
returns (uint256 streamId)
{
// Checks: check the deltas and generate the canonical segments.
LockupDynamic.Segment[] memory segments = Helpers.checkDeltasAndCalculateMilestones(params.segments);
// Checks: check the durations and generate the canonical segments.
LockupDynamic.Segment[] memory segments = Helpers.checkDurationsAndCalculateTimestamps(params.segments);

// Checks, Effects and Interactions: create the stream.
streamId = _createWithTimestamps(
Expand Down Expand Up @@ -345,32 +345,32 @@ contract SablierV2LockupDynamic is

// Sum the amounts in all segments that precede the current time.
uint128 previousSegmentAmounts;
uint40 currentSegmentMilestone = stream.segments[0].milestone;
uint40 currentSegmentTimestamp = stream.segments[0].timestamp;
uint256 index = 0;
while (currentSegmentMilestone < currentTime) {
while (currentSegmentTimestamp < currentTime) {
previousSegmentAmounts += stream.segments[index].amount;
index += 1;
currentSegmentMilestone = stream.segments[index].milestone;
currentSegmentTimestamp = stream.segments[index].timestamp;
}

// After exiting the loop, the current segment is at `index`.
SD59x18 currentSegmentAmount = stream.segments[index].amount.intoSD59x18();
SD59x18 currentSegmentExponent = stream.segments[index].exponent.intoSD59x18();
currentSegmentMilestone = stream.segments[index].milestone;
currentSegmentTimestamp = stream.segments[index].timestamp;

uint40 previousMilestone;
uint40 previousTimestamp;
if (index > 0) {
// When the current segment's index is greater than or equal to 1, it implies that the segment is not
// the first. In this case, use the previous segment's milestone.
previousMilestone = stream.segments[index - 1].milestone;
// the first. In this case, use the previous segment's timestamp.
previousTimestamp = stream.segments[index - 1].timestamp;
} else {
// Otherwise, the current segment is the first, so use the start time as the previous milestone.
previousMilestone = stream.startTime;
// Otherwise, the current segment is the first, so use the start time as the previous timestamp.
previousTimestamp = stream.startTime;
}

// Calculate how much time has passed since the segment started, and the total time of the segment.
SD59x18 elapsedSegmentTime = (currentTime - previousMilestone).intoSD59x18();
SD59x18 totalSegmentTime = (currentSegmentMilestone - previousMilestone).intoSD59x18();
SD59x18 elapsedSegmentTime = (currentTime - previousTimestamp).intoSD59x18();
SD59x18 totalSegmentTime = (currentSegmentTimestamp - previousTimestamp).intoSD59x18();

// Divide the elapsed segment time by the total duration of the segment.
SD59x18 elapsedSegmentTimePercentage = elapsedSegmentTime.div(totalSegmentTime);
Expand Down Expand Up @@ -567,7 +567,7 @@ contract SablierV2LockupDynamic is
unchecked {
// The segment count cannot be zero at this point.
uint256 segmentCount = params.segments.length;
stream.endTime = params.segments[segmentCount - 1].milestone;
stream.endTime = params.segments[segmentCount - 1].timestamp;
stream.startTime = params.startTime;

// Effects: store the segments. Since Solidity lacks a syntax for copying arrays directly from
Expand Down
14 changes: 7 additions & 7 deletions src/interfaces/ISablierV2LockupDynamic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ interface ISablierV2LockupDynamic is ISablierV2Lockup {
//////////////////////////////////////////////////////////////////////////*/

/// @notice Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of
/// `block.timestamp` and all specified time deltas. The segment milestones are derived from these
/// deltas. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT.
/// `block.timestamp` and all specified time durations. The segment timestamps are derived from these
/// durations. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT.
///
/// @dev Emits a {Transfer} and {CreateLockupDynamicStream} event.
///
Expand All @@ -106,23 +106,23 @@ interface ISablierV2LockupDynamic is ISablierV2Lockup {
external
returns (uint256 streamId);

/// @notice Creates a stream with the provided segment milestones, implying the end time from the last milestone.
/// @notice Creates a stream with the provided segment timestamps, implying the end time from the last timestamp.
/// The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT.
///
/// @dev Emits a {Transfer} and {CreateLockupDynamicStream} event.
///
/// Notes:
/// - As long as the segment milestones are arranged in ascending order, it is not an error for some
/// - As long as the segment timestamps are arranged in ascending order, it is not an error for some
/// of them to be in the past.
///
/// Requirements:
/// - Must not be delegate called.
/// - `params.totalAmount` must be greater than zero.
/// - If set, `params.broker.fee` must not be greater than `MAX_FEE`.
/// - `params.segments` must have at least one segment, but not more than `MAX_SEGMENT_COUNT`.
/// - `params.startTime` must be less than the first segment's milestone.
/// - The segment milestones must be arranged in ascending order.
/// - The last segment milestone (i.e. the stream's end time) must be in the future.
/// - `params.startTime` must be less than the first segment's timestamp.
/// - The segment timestamps must be arranged in ascending order.
/// - The last segment timestamp (i.e. the stream's end time) must be in the future.
/// - The sum of the segment amounts must equal the deposit amount.
/// - `params.recipient` must not be the zero address.
/// - `msg.sender` must have allowed this contract to spend at least `params.totalAmount` assets.
Expand Down
12 changes: 6 additions & 6 deletions src/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ library Errors {
/// @notice Thrown when trying to create a stream with no segments.
error SablierV2LockupDynamic_SegmentCountZero();

/// @notice Thrown when trying to create a stream with unordered segment milestones.
error SablierV2LockupDynamic_SegmentMilestonesNotOrdered(
uint256 index, uint40 previousMilestone, uint40 currentMilestone
/// @notice Thrown when trying to create a stream with unordered segment timestampts.
error SablierV2LockupDynamic_SegmentTimestampsNotOrdered(
uint256 index, uint40 previousTimestamp, uint40 currentTimestamp
);

/// @notice Thrown when trying to create a stream with a start time not strictly less than the first
/// segment milestone.
error SablierV2LockupDynamic_StartTimeNotLessThanFirstSegmentMilestone(
uint40 startTime, uint40 firstSegmentMilestone
/// segment timestamp.
error SablierV2LockupDynamic_StartTimeNotLessThanFirstSegmentTimestamp(
uint40 startTime, uint40 firstSegmentTimestamp
);

/*//////////////////////////////////////////////////////////////////////////
Expand Down
66 changes: 33 additions & 33 deletions src/libraries/Helpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,34 +109,34 @@ library Helpers {
}
}

/// @dev Checks that the segment array counts match, and then adjusts the segments by calculating the milestones.
function checkDeltasAndCalculateMilestones(LockupDynamic.SegmentWithDelta[] memory segments)
/// @dev Checks that the segment array counts match, and then adjusts the segments by calculating the timestamps.
function checkDurationsAndCalculateTimestamps(LockupDynamic.SegmentWithDuration[] memory segments)
internal
view
returns (LockupDynamic.Segment[] memory segmentsWithMilestones)
returns (LockupDynamic.Segment[] memory segmentsWithTimestamps)
{
uint256 segmentCount = segments.length;
segmentsWithMilestones = new LockupDynamic.Segment[](segmentCount);
segmentsWithTimestamps = new LockupDynamic.Segment[](segmentCount);

// Make the current time the stream's start time.
uint40 startTime = uint40(block.timestamp);

// It is safe to use unchecked arithmetic because {_createWithMilestone} will nonetheless check the soundness
// of the calculated segment milestones.
// It is safe to use unchecked arithmetic because {_createWithTimestamps} will nonetheless check the soundness
// of the calculated segment timestamps.
unchecked {
// Precompute the first segment because of the need to add the start time to the first segment delta.
segmentsWithMilestones[0] = LockupDynamic.Segment({
// Precompute the first segment because of the need to add the start time to the first segment duration.
segmentsWithTimestamps[0] = LockupDynamic.Segment({
amount: segments[0].amount,
exponent: segments[0].exponent,
milestone: startTime + segments[0].delta
timestamp: startTime + segments[0].duration
});

// Copy the segment amounts and exponents, and calculate the segment milestones.
// Copy the segment amounts and exponents, and calculate the segment timestamps.
for (uint256 i = 1; i < segmentCount; ++i) {
segmentsWithMilestones[i] = LockupDynamic.Segment({
segmentsWithTimestamps[i] = LockupDynamic.Segment({
amount: segments[i].amount,
exponent: segments[i].exponent,
milestone: segmentsWithMilestones[i - 1].milestone + segments[i].delta
timestamp: segmentsWithTimestamps[i - 1].timestamp + segments[i].duration
});
}
}
Expand All @@ -148,9 +148,9 @@ library Helpers {

/// @dev Checks that:
///
/// 1. The first milestone is strictly greater than the start time.
/// 2. The milestones are ordered chronologically.
/// 3. There are no duplicate milestones.
/// 1. The first timestamp is strictly greater than the start time.
/// 2. The timestamps are ordered chronologically.
/// 3. There are no duplicate timestamps.
/// 4. The deposit amount is equal to the sum of all segment amounts.
function _checkSegments(
LockupDynamic.Segment[] memory segments,
Expand All @@ -160,44 +160,44 @@ library Helpers {
private
view
{
// Checks: the start time is strictly less than the first segment milestone.
if (startTime >= segments[0].milestone) {
revert Errors.SablierV2LockupDynamic_StartTimeNotLessThanFirstSegmentMilestone(
startTime, segments[0].milestone
// Checks: the start time is strictly less than the first segment timestamp.
if (startTime >= segments[0].timestamp) {
revert Errors.SablierV2LockupDynamic_StartTimeNotLessThanFirstSegmentTimestamp(
startTime, segments[0].timestamp
);
}

// Pre-declare the variables needed in the for loop.
uint128 segmentAmountsSum;
uint40 currentMilestone;
uint40 previousMilestone;
uint40 currentTimestamp;
uint40 previousTimestamp;

// Iterate over the segments to:
//
// 1. Calculate the sum of all segment amounts.
// 2. Check that the milestones are ordered.
// 2. Check that the timestamps are ordered.
uint256 count = segments.length;
for (uint256 index = 0; index < count; ++index) {
// Add the current segment amount to the sum.
segmentAmountsSum += segments[index].amount;

// Checks: the current milestone is strictly greater than the previous milestone.
currentMilestone = segments[index].milestone;
if (currentMilestone <= previousMilestone) {
revert Errors.SablierV2LockupDynamic_SegmentMilestonesNotOrdered(
index, previousMilestone, currentMilestone
// Checks: the current timestamp is strictly greater than the previous timestamp.
currentTimestamp = segments[index].timestamp;
if (currentTimestamp <= previousTimestamp) {
revert Errors.SablierV2LockupDynamic_SegmentTimestampsNotOrdered(
index, previousTimestamp, currentTimestamp
);
}

// Make the current milestone the previous milestone of the next loop iteration.
previousMilestone = currentMilestone;
// Make the current timestamp the previous timestamp of the next loop iteration.
previousTimestamp = currentTimestamp;
}

// Checks: the last milestone is in the future.
// When the loop exits, the current milestone is the last milestone, i.e. the stream's end time.
// Checks: the last timestamp is in the future.
// When the loop exits, the current timestamp is the last timestamp, i.e. the stream's end time.
uint40 currentTime = uint40(block.timestamp);
if (currentTime >= currentMilestone) {
revert Errors.SablierV2Lockup_EndTimeNotInTheFuture(currentTime, currentMilestone);
if (currentTime >= currentTimestamp) {
revert Errors.SablierV2Lockup_EndTimeNotInTheFuture(currentTime, currentTimestamp);
}

// Checks: the deposit amount is equal to the segment amounts sum.
Expand Down
16 changes: 8 additions & 8 deletions src/types/DataTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ library LockupDynamic {
/// @param asset The contract address of the ERC-20 asset used for streaming.
/// @param cancelable Indicates if the stream is cancelable.
/// @param transferable Indicates if the stream NFT is transferable.
/// @param segments Segments with deltas used to compose the custom streaming curve. Milestones are calculated by
/// starting from `block.timestamp` and adding each delta to the previous milestone.
/// @param segments Segments with durations used to compose the custom streaming curve. Timestamps are calculated by
/// starting from `block.timestamp` and adding each duration to the previous timestamp.
/// @param broker Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the
/// percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero.
struct CreateWithDurations {
Expand All @@ -90,7 +90,7 @@ library LockupDynamic {
IERC20 asset;
bool cancelable;
bool transferable;
SegmentWithDelta[] segments;
SegmentWithDuration[] segments;
Broker broker;
}

Expand Down Expand Up @@ -131,22 +131,22 @@ library LockupDynamic {
/// @notice Segment struct used in the Lockup Dynamic stream.
/// @param amount The amount of assets to be streamed in this segment, denoted in units of the asset's decimals.
/// @param exponent The exponent of this segment, denoted as a fixed-point number.
/// @param milestone The Unix timestamp indicating this segment's end.
/// @param timestamp The Unix timestamp indicating this segment's end.
struct Segment {
// slot 0
uint128 amount;
UD2x18 exponent;
uint40 milestone;
uint40 timestamp;
}

/// @notice Segment struct used at runtime in {SablierV2LockupDynamic.createWithDurations}.
/// @param amount The amount of assets to be streamed in this segment, denoted in units of the asset's decimals.
/// @param exponent The exponent of this segment, denoted as a fixed-point number.
/// @param delta The time difference in seconds between this segment and the previous one.
struct SegmentWithDelta {
/// @param duration The time difference in seconds between this segment and the previous one.
struct SegmentWithDuration {
uint128 amount;
UD2x18 exponent;
uint40 delta;
uint40 duration;
}

/// @notice Lockup Dynamic stream.
Expand Down
8 changes: 4 additions & 4 deletions test/fork/LockupDynamic.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ abstract contract LockupDynamic_Fork_Test is Fork_Test {
/// - Start time in the past
/// - Start time in the present
/// - Start time in the future
/// - Start time equal and not equal to the first segment milestone
/// - Start time equal and not equal to the first segment timestamp
/// - Multiple values for the broker fee, including zero
/// - Multiple values for the protocol fee, including zero
/// - Multiple values for the withdraw amount, including zero
Expand All @@ -122,8 +122,8 @@ abstract contract LockupDynamic_Fork_Test is Fork_Test {
params.startTime = boundUint40(params.startTime, 0, defaults.START_TIME());
params.transferable = true;

// Fuzz the segment milestones.
fuzzSegmentMilestones(params.segments, params.startTime);
// Fuzz the segment timestamps.
fuzzSegmentTimestamps(params.segments, params.startTime);

// Fuzz the segment amounts and calculate the create amounts (total, deposit, protocol fee, and broker fee).
Vars memory vars;
Expand Down Expand Up @@ -156,7 +156,7 @@ abstract contract LockupDynamic_Fork_Test is Fork_Test {

vars.streamId = lockupDynamic.nextStreamId();
vars.range =
LockupDynamic.Range({ start: params.startTime, end: params.segments[params.segments.length - 1].milestone });
LockupDynamic.Range({ start: params.startTime, end: params.segments[params.segments.length - 1].timestamp });

// Expect the relevant events to be emitted.
vm.expectEmit({ emitter: address(lockupDynamic) });
Expand Down
Loading

0 comments on commit bd64353

Please sign in to comment.