Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add unlock amounts functionality #1075

Merged
merged 8 commits into from
Nov 19, 2024
Merged

Conversation

andreivladbrg
Copy link
Member

@andreivladbrg andreivladbrg commented Nov 4, 2024

Closes #1043 and #1067

Since we have the invariant: $\text{cliff time} = 0 \implies \text{cliff unlock amount} = 0$, there are 6 possible scenarios in the streamedAmountOf function:

Scenario Shape Cliff Time Start Amount Cliff Amount
1 Simple Linear
3 Simple Cliff
3 Unlock Linear
4 Unlock Cliff
5 Constant, then linear (see below)
6 Initial amount, constant, then linear (see below)
Plot graphs with docs code

Scenario 5

image

Code to update the docs shapes with:

<FunctionPlot
  options={{
    data: [
      { fn: "0", range: [0, 25], color: "#f77423" },
      { fn: "(4/3) * (x - 25)", range: [25, 100], color: "#f77423" },
      {
        points: [
          [0, 0],
          [25, 0],
        ],
        fnType: "points",
        graphType: "polyline",
        color: "#f77423",
      },
    ],
  }}
/>

Scenario 6

image

Code to update the docs shapes with:

<FunctionPlot
  options={{
    data: [
      { fn: "25", range: [0, 50], color: "#f77423" }, // Constant at y=25 from x=0 to x=50
      { fn: "1.5 * (x - 50) + 25", range: [50, 100], color: "#f77423" }, // Linear rise with slope 1.5 starting from y=25 at x=50
      {
        points: [
          [0, 25],
          [50, 25],
        ],
        fnType: "points",
        graphType: "polyline",
        color: "#f77423",
      },
    ],
  }}
/>


Note: Since the mathematical logic has changed in SablierLockupLinear._calculateStreamedAmount, in order to achieve the previous cliff linear shape, it is required to pass an unlockAmount.cliff > 0; otherwise, the 5th scenario shape will be achieved. This is also the reason I needed to change the tests from warping to WARP_26_PERCENT instead of CLIFF_TIME, as many tests were failing. The cliff amount has a remainder of 2534 due to a different "streamable" range.

As you will see in the code, I am referring to the range that calculates the elapsed time as "streamable," which might be confusing since the entire function is called "streamed amount." However, we will eventually change this terminology to "vesting," so don’t mind the current terminology if you find it unclear.


Here's a diagram for visualizing the new function flow while reviewing.

New function flow

flowchart TD
    start_time_check([Is startTime >= current block timestamp?]):::check
    return_zero([Return 0]):::return
    end_time_check([Is current block timestamp >= endTime?]):::check
    return_deposited([Return depositedAmount]):::return
    cliff_time_check([Is cliffTime set and cliffTime > current block timestamp?]):::check
    return_start_unlock([Return start unlock amount]):::return
    unlock_sum_check([Is unlockAmountsSum >= depositedAmount?]):::check
    elapsed_calculation_check([Calculate elapsedTime and streamableDuration]):::calculation
    etp_calculation([Calculate elapsedTimePercentage]):::calculation
    streamable_amount_calc([Calculate streamableAmount]):::calculation
    streamed_amount_calc([Calculate streamedAmount]):::calculation
    final_check([Is streamedAmount > depositedAmount?]):::check
    return_withdrawn([Return withdrawn amount]):::return
    return_streamed([Return streamedAmount]):::return

    start_time_check -- "Yes" --> return_zero
    start_time_check -- "No" --> end_time_check
    end_time_check -- "Yes" --> return_deposited
    end_time_check -- "No" --> cliff_time_check
    cliff_time_check -- "Yes" --> return_start_unlock
    cliff_time_check -- "No" --> unlock_sum_check
    unlock_sum_check -- "Yes" --> return_deposited
    unlock_sum_check -- "No" --> elapsed_calculation_check

    elapsed_calculation_check --> etp_calculation
    etp_calculation --> streamable_amount_calc
    streamable_amount_calc --> streamed_amount_calc
    streamed_amount_calc --> final_check
    final_check -- "Yes" --> return_withdrawn
    final_check -- "No" --> return_streamed

    classDef check fill:#FFC107,stroke:#333,stroke-width:2px;
    classDef return fill:#4CAF50,stroke:#333,stroke-width:2px;
    classDef calculation fill:#03A9F4,stroke:#333,stroke-width:2px;
Loading

@andreivladbrg andreivladbrg marked this pull request as draft November 4, 2024 16:32
@andreivladbrg andreivladbrg marked this pull request as ready for review November 5, 2024 23:19
@andreivladbrg
Copy link
Member Author

@smol-ninja the PR is ready for your review, I have updated the OP with some useful comments, please get first through them before reviewing,

@smol-ninja smol-ninja self-requested a review November 6, 2024 10:31
@andreivladbrg andreivladbrg force-pushed the feat/unlock-amounts branch 2 times, most recently from b65cb8f to d4d8187 Compare November 8, 2024 23:14
@andreivladbrg andreivladbrg changed the base branch from staging to refactor/singleton-contract November 8, 2024 23:16
@andreivladbrg
Copy link
Member Author

@smol-ninja just rebased from refactor/singleton-contract. in case i have missed something, i have created a recovery branch.

Note: the size of the contract is at its limit, adding new things to the contract would require us to decrease the optimizer runs.

Contract Size (B) Margin (B)
SablierLockup 24,450 126

@smol-ninja

This comment was marked as resolved.

@andreivladbrg

This comment was marked as resolved.

@smol-ninja

This comment was marked as resolved.

@andreivladbrg

This comment was marked as resolved.

Copy link
Member

@smol-ninja smol-ninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on the PR. Reviewed the src and left my comments below.

@andreivladbrg andreivladbrg force-pushed the refactor/singleton-contract branch from 151dbdd to 57d7def Compare November 11, 2024 09:16
@smol-ninja smol-ninja force-pushed the refactor/singleton-contract branch from 57d7def to 8ca1274 Compare November 11, 2024 10:01
@smol-ninja smol-ninja force-pushed the refactor/singleton-contract branch from 8ca1274 to 9050f98 Compare November 11, 2024 10:33
@andreivladbrg andreivladbrg changed the base branch from refactor/singleton-contract to avb/test-singleton-review November 13, 2024 16:02
@andreivladbrg andreivladbrg force-pushed the feat/unlock-amounts branch 2 times, most recently from 237cadb to e98d613 Compare November 13, 2024 16:32
@andreivladbrg
Copy link
Member Author

@smol-ninja I have addressed all the src feedback, and I have finally rebased from test PRs—it was a mess… but we’re here. I hope I didn’t miss anything while rebasing.

@andreivladbrg andreivladbrg force-pushed the avb/test-singleton-review branch 3 times, most recently from 62d26a3 to 20e40ba Compare November 13, 2024 22:23
Base automatically changed from avb/test-singleton-review to staging November 13, 2024 22:23
@andreivladbrg andreivladbrg force-pushed the feat/unlock-amounts branch 2 times, most recently from 267b7fd to 4b8a424 Compare November 13, 2024 22:32
refactor: some polishes

docs: update explanatory comments
test: add new test branches in create function

test: streamed amount in lockup linear

test: fix common streamed amount tests
test: remove unneeded branches

build: update deps

test: fix tranches default function

feat: include unlock amounts in StreamLL

docs: fix some comments
test: improve fuzz streamed amount tests
test: update fork with unlock amounts

test: fix last failing tests

docs: last polishes
Copy link
Member

@smol-ninja smol-ninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on tests. Left my review below.

Also created a PR: #1088 about Schedule struct that can be merged into this.

@@ -140,6 +140,7 @@ contract CreateWithTimestampsLT_Integration_Concrete_Test is CreateWithTimestamp
whenStartTimeLessThanFirstTimestamp
{
// Swap the tranche timestamps.
// LockupTranched.Tranche[] memory tranches = defaults.tranches();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be removed.

@@ -3,10 +3,7 @@ StreamedAmountOf_Lockup_Tranched_Integration_Concrete_Test
├── given start time in present
│ └── it should return zero
└── given start time in past
├── given end time not in future
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rationale behind removing them? Also, modifiers still exist in the corresponding test contracts.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same reason as here

#1075 (comment)

already tested in SETTLED status branch

also re this #1075 (comment)

we should consider for SETTLED this modifier for both now == end && now > end:

modifier runBothCases() {
    vm.warp({ newTimestamp: defaults.END_TIME() });
    _;

    vm.warp({ newTimestamp: defaults.END_TIME() + 1 });
    _;
}

test: add lockupCreateEvent in Defaults
Copy link
Member

@smol-ninja smol-ninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving the PR. There are still some unresolved comments regarding tests but since they are related to tests we can look into them later. The contract code looks good to me so given the audit date is near, feel free to merge it now.

@andreivladbrg
Copy link
Member Author

Thanks, let's goo 🚀

@andreivladbrg andreivladbrg merged commit 9e037a4 into staging Nov 19, 2024
9 checks passed
@andreivladbrg andreivladbrg deleted the feat/unlock-amounts branch November 19, 2024 15:37
andreivladbrg added a commit that referenced this pull request Jan 27, 2025
* feat: add unlock amounts functionality

refactor: some polishes

docs: update explanatory comments
test: add new test branches in create function

test: streamed amount in lockup linear

test: fix common streamed amount tests
test: remove unneeded branches

build: update deps

test: fix tranches default function

feat: include unlock amounts in StreamLL

docs: fix some comments
test: improve fuzz streamed amount tests
test: update fork with unlock amounts

test: fix last failing tests

docs: last polishes

* refactor: emit common parameters in Create event through Common struct

* refactor: extend Schedule struct to have unlock amounts for Airstreams

* test: remove redundant comment

* refactor: rename Common struct to CreateEventCommon

* refactor: pass timestamps in linear calculate function

shub feedback

* docs: add return natspec in _create

test: add lockupCreateEvent in Defaults

---------

Co-authored-by: smol-ninja <shubhamy2015@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Upgrade to PRBMath v4.1.0 Instant unlock + more flexible cliff in LockupLinear
2 participants