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

Open
wants to merge 1 commit into
base: staging
Choose a base branch
from

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$, we would have these 6 possible scenarios for the streamed amount function:

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

Scenario 2

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 4

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 function 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 second 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.


Below you can see an useful diagram to vizualize while reviewing the new function.

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.

src/core/SablierLockup.sol Show resolved Hide resolved
src/core/SablierLockup.sol Outdated Show resolved Hide resolved
src/core/SablierLockup.sol Outdated Show resolved Hide resolved
src/core/libraries/Helpers.sol Outdated Show resolved Hide resolved
src/core/interfaces/ISablierLockup.sol Show resolved Hide resolved
src/core/libraries/VestingMath.sol Show resolved Hide resolved
src/core/libraries/VestingMath.sol Show resolved Hide resolved
src/core/libraries/VestingMath.sol Outdated Show resolved Hide resolved
src/core/libraries/VestingMath.sol Show resolved Hide resolved
@@ -36,6 +36,9 @@ contract SablierMerkleLL is
/// @inheritdoc ISablierMerkleLL
MerkleLL.Schedule public override schedule;

/// @inheritdoc ISablierMerkleLL
LockupLinear.UnlockAmounts public override unlockAmounts;
Copy link
Member

Choose a reason for hiding this comment

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

Instead of creating is as a separate variable, how about merging it into schedule. The name schedule is very generic and can include amounts as well.

Copy link
Member Author

@andreivladbrg andreivladbrg Nov 11, 2024

Choose a reason for hiding this comment

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

hmm, not sure what to say, will think more about this

Copy link
Member Author

@andreivladbrg andreivladbrg Nov 13, 2024

Choose a reason for hiding this comment

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

i would like to keep the current version to have two different structs 1. for time related variables 2. amount related amounts

schedule name is more suited for time related variables IMO

@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.

@@ -408,36 +433,47 @@ contract SablierLockup is ISablierLockup, SablierLockupBase {
});
}

struct CreateLLParams {
Copy link
Member Author

Choose a reason for hiding this comment

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

temporarily added this due to stack too deep error

will try to think of a better option, but couldn't find one yet (tried scoping {} blocks of code, tried a different function for the event, tried splitting the logic, and didn't work unfortunetely)

@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
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