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

Move all duplicated logic in the abstract contract SablierV2Lockup #813

Merged
merged 14 commits into from
Feb 15, 2024

Conversation

andreivladbrg
Copy link
Member

@andreivladbrg andreivladbrg commented Feb 2, 2024

Addresses #805.

Depends on #806.

Changes

  • Introduce a new Datatype Stream within Lockup namespace
  • Move _streams mapping to SablierV2Lockup and use the new data type
  • Move all duplicated logic in the abstract contract SablierV2Lockup. This change introduced the need of:
    • _calculateStreamedAmount virtual function
    • Additional logic in the getStream function
  • Introduce _cliffs and _segments mappings in linear contract, and dynamic respectively
  • Allow cliff time to be zero, which means there is a simple linear stream created. This change introduced the need of:
    • Check if start time is zero
    • Check if start time is strictly less than the end time

Also, it would be recommended to read the 805 discussion for the rational behind.

Other decisions

It no longer made sense to allow the start time to equal the cliff time, since now it can be zero (cliff).

I could have DRY-FY the _createWithTimestamps function since there is common logic between linear and dynamic. But the reasons I did not were:

  • Checks, Effects, Interactions pattern was not respected anymore
  • Less flexibility for the child contract inheriting SablierV2Lockup
  • Introduces the "stack too deep error" in dynamic contract

Gas comparison

Contract Type of stream Staging branch gas This branch gas cost Increased by
LockupLinear Simple linear stream 116184 116052 -132
LockupLinear Cliff linear stream 116184 138073 21889
LockupDynamic Two segments stream 182841 182967 126

Notes and questions

After this PR the app would need to change how it works when creating a simple linear stream (passing 0 as the cliff time).

Should we rename the Stream structs within LockupLinear and LockupDynamic namespaces (which are used only for getStream function)?
Should we re-order the variables in these structs?

@andreivladbrg andreivladbrg force-pushed the refactor/common-logic branch 2 times, most recently from 8ebf8d8 to aabd935 Compare February 8, 2024 14:28
@andreivladbrg
Copy link
Member Author

The Codecov report is now failing. After debugging, I can see that the lines marked as "Uncovered" are the same as in other PRs.

Current PR: https://app.codecov.io/gh/sablier-labs/v2-core/pull/813/blob/src/SablierV2LockupDynamic.sol
Other PR: https://app.codecov.io/gh/sablier-labs/v2-core/pull/814/blob/src/SablierV2LockupDynamic.sol

The issue may be from the fact that the contracts (Linear and Dynamic) now have fewer lines, thereby increasing the percentage of uncovered lines. This is why the report's coverage has decreased.

Atm, I am not sure how to address this issue, other than by reducing the coverage threshold on Codecov.

What do you say @PaulRBerg ?

@PaulRBerg
Copy link
Member

PaulRBerg commented Feb 8, 2024

Sorry no time to advise on this issue now. I recommend asking for help on StackOverflow or the Codecov forum.

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.

Great work @andreivladbrg. I have left some comments below but I haven't finished the review yet.

src/types/DataTypes.sol Outdated Show resolved Hide resolved
src/types/DataTypes.sol Outdated Show resolved Hide resolved
src/types/DataTypes.sol Outdated Show resolved Hide resolved
src/libraries/Helpers.sol Outdated Show resolved Hide resolved
src/abstracts/SablierV2Lockup.sol Outdated Show resolved Hide resolved
src/abstracts/SablierV2Lockup.sol Show resolved Hide resolved
src/abstracts/SablierV2Lockup.sol Show resolved Hide resolved
@andreivladbrg andreivladbrg force-pushed the refactor/common-logic branch 2 times, most recently from cfc07af to 8e244ac Compare February 12, 2024 14:01
@andreivladbrg
Copy link
Member Author

Regarding the codecov coverage mention in this comment

I did some research, and I was correct. The reason why the coverage fails is because there are fewer lines of code in the targeted files (SablierV2LockupLinear and SablierV2LockupDynamic).

GPT answer

"
This usually occurs because code coverage is calculated as the ratio of the number of lines of code executed by tests to the total number of lines of code. When you refactor code to be more efficient (i.e., reducing the total number of lines), even if the number of lines tested remains the same, the percentage of code covered can decrease because there's a smaller total number of lines of code.
"

One potential solution, is to temporarily add a threshold of 0.3% on codecov.yml file, and after we merge this PR to remove it.

What do you say? @PaulRBerg @smol-ninja

@PaulRBerg
Copy link
Member

Thanks for investigating, @andreivladbrg.

One potential solution, is to temporarily add a threshold of 0.3%

That shouldn't be needed - as an admin, I have the power to merge the PRs even when the checks fail. Let me know when you want to merge it (and how - squashing or not), and I'll do it.

@andreivladbrg
Copy link
Member Author

andreivladbrg commented Feb 12, 2024

That shouldn't be needed - as an admin, I have the power to merge the PRs even when the checks fail. Let me know when you want to merge it (and how - squashing or not), and I'll do it.

I can do that as well. I was just saying this to avoid having the workflow fail. But I guess it is not a problem since the test- utils is also failing

@PaulRBerg
Copy link
Member

Oh ok, got it. Letting the workflow fail it's fine in this case.

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.

I have reviewed the PR. Please see comments below.

src/types/DataTypes.sol Show resolved Hide resolved
src/SablierV2LockupDynamic.sol Outdated Show resolved Hide resolved
src/SablierV2LockupLinear.sol Outdated Show resolved Hide resolved
src/SablierV2LockupLinear.sol Show resolved Hide resolved
src/abstracts/SablierV2Lockup.sol Show resolved Hide resolved
src/interfaces/ISablierV2LockupLinear.sol Show resolved Hide resolved
test/invariant/LockupLinear.t.sol Outdated Show resolved Hide resolved
test/invariant/LockupLinear.t.sol Outdated Show resolved Hide resolved
test/invariant/LockupLinear.t.sol Outdated Show resolved Hide 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.

LGTM now.

andreivladbrg and others added 12 commits February 15, 2024 14:06
refactor: remove _after and _before hooks and implement _update function
refactor: use newly added _requireOwned function
refactor: use the new named parameter in ERC20 functions
test: add mocks for ERC20 and ERC721
test: use openeppelin's custom errors in cheatcode
chore: provide a more precise explanation for _update
chore: define "_update" alphabetically
docs: document "_update" function
test: check IERC20Errors custom error revert
feat: add mapping for cliffs in linear
feat: add mapping for segments in dynamic
refactor: move all common functions in SablierV2Lockup
feat: check if the start time is greater than end time
test: update tests accordingly
refactor: make constant functions external
chore: use "override" specifier on nftDescriptor
chore: order functions alphabetically in SablierV2Lockup
@smol-ninja
Copy link
Member

smol-ninja commented Feb 15, 2024

Should we rename the Stream structs within LockupLinear and LockupDynamic namespaces (which are used only for getStream function)?

In my opinion, it is better to rename them to avoid mentioning namespaces everytime we are referring to Stream structs. (Later: it might not be a problem as the default is the one defined in Lockup namespace but from the documentation pov, it looks better to use a different name for them).

Suggestions: StreamLL, StreamLD?

Should we re-order the variables in these structs?

Yes. I am in favour of this (comment).

@andreivladbrg
Copy link
Member Author

andreivladbrg commented Feb 15, 2024

In my opinion, it is better to rename them to avoid mentioning namespaces everytime we are referring to Stream structs. (Later: it might not be a problem as the default is the one defined in Lockup namespace but from the documentation pov, it looks better to use a different name for them).

Agree

Suggestions: StreamLL, StreamLD?

i like these

Yes. I am in favour of this (#813 (comment)).

I was ok with that, but I now changed my mind.
I would like to be consistent here, so I suggest we either:

  • keep the current version, which has the order from Lockup.Stream and the segments/cliffs at the end
  • change it to the order from CreateWithTimestamps struct

@smol-ninja
Copy link
Member

keep the current version, which has the order from Lockup.Stream and at the segments/cliffs at the

We can do this. I am good with this as well.

@smol-ninja
Copy link
Member

looks good now.

@andreivladbrg andreivladbrg merged commit c47eebd into staging Feb 15, 2024
6 of 8 checks passed
@smol-ninja smol-ninja deleted the refactor/common-logic branch February 15, 2024 15:37
andreivladbrg added a commit that referenced this pull request Feb 17, 2024
…813)

* build: update openzeppelin to 5.0.0

refactor: remove _after and _before hooks and implement _update function
refactor: use newly added _requireOwned function
refactor: use the new named parameter in ERC20 functions
test: add mocks for ERC20 and ERC721
test: use openeppelin's custom errors in cheatcode
chore: provide a more precise explanation for _update
chore: define "_update" alphabetically
docs: document "_update" function
test: check IERC20Errors custom error revert

* feat: add Lockup.Stream struct

feat: add mapping for cliffs in linear
feat: add mapping for segments in dynamic
refactor: move all common functions in SablierV2Lockup

* perf: use the storages instead of the getters

* feat: check if the start time is zero

feat: check if the start time is greater than end time
test: update tests accordingly

* docs: re-add natspec for _calculateStreamedAmountForMultipleSegments

* refactor: make start time strictly less than cliff time

* chore: update branch from staging

* docs: use plural

* refactor: order constant functions alphabetically

refactor: make constant functions external

* refactor: make cliff time strictly greater than start time

* docs: correct natspec

chore: use "override" specifier on nftDescriptor

* test: update comment for using unchecked to allow overflow

* docs: improve natspec

chore: order functions alphabetically in SablierV2Lockup

* refactor: rename Stream structs within LockupLinear and LockupDynamic namespaces

---------

Co-authored-by: smol-ninja <[email protected]>
@andreivladbrg andreivladbrg self-assigned this Mar 24, 2024
Copy link
Member

Choose a reason for hiding this comment

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

This tree is not aligned with the order of the checks in {Helpers-checkCreateWithTimestamps}.

It is critical that we align them because one of the goals of BTT is to build a circuit-like representation of the function's execution paths.

Specifically, we need to reverse the cliff time checks with the end time check here:

// Checks: the start time is strictly less than the end time.
if (range.start >= range.end) {
revert Errors.SablierV2LockupLinear_StartTimeNotLessThanEndTime(range.start, range.end);
}
// Checks: the start time is strictly less than the cliff time when cliff time is not zero.
if (range.cliff > 0 && range.start >= range.cliff) {
revert Errors.SablierV2LockupLinear_StartTimeNotLessThanCliffTime(range.start, range.cliff);
}
// Checks: the cliff time is strictly less than the end time.
if (range.cliff >= range.end) {
revert Errors.SablierV2LockupLinear_CliffTimeNotLessThanEndTime(range.cliff, range.end);
}

@andreivladbrg andreivladbrg mentioned this pull request Jun 16, 2024
andreivladbrg added a commit that referenced this pull request Jul 3, 2024
…813)

* build: update openzeppelin to 5.0.0

refactor: remove _after and _before hooks and implement _update function
refactor: use newly added _requireOwned function
refactor: use the new named parameter in ERC20 functions
test: add mocks for ERC20 and ERC721
test: use openeppelin's custom errors in cheatcode
chore: provide a more precise explanation for _update
chore: define "_update" alphabetically
docs: document "_update" function
test: check IERC20Errors custom error revert

* feat: add Lockup.Stream struct

feat: add mapping for cliffs in linear
feat: add mapping for segments in dynamic
refactor: move all common functions in SablierV2Lockup

* perf: use the storages instead of the getters

* feat: check if the start time is zero

feat: check if the start time is greater than end time
test: update tests accordingly

* docs: re-add natspec for _calculateStreamedAmountForMultipleSegments

* refactor: make start time strictly less than cliff time

* chore: update branch from staging

* docs: use plural

* refactor: order constant functions alphabetically

refactor: make constant functions external

* refactor: make cliff time strictly greater than start time

* docs: correct natspec

chore: use "override" specifier on nftDescriptor

* test: update comment for using unchecked to allow overflow

* docs: improve natspec

chore: order functions alphabetically in SablierV2Lockup

* refactor: rename Stream structs within LockupLinear and LockupDynamic namespaces

---------

Co-authored-by: smol-ninja <[email protected]>
andreivladbrg added a commit that referenced this pull request Jul 3, 2024
…813)

* build: update openzeppelin to 5.0.0

refactor: remove _after and _before hooks and implement _update function
refactor: use newly added _requireOwned function
refactor: use the new named parameter in ERC20 functions
test: add mocks for ERC20 and ERC721
test: use openeppelin's custom errors in cheatcode
chore: provide a more precise explanation for _update
chore: define "_update" alphabetically
docs: document "_update" function
test: check IERC20Errors custom error revert

* feat: add Lockup.Stream struct

feat: add mapping for cliffs in linear
feat: add mapping for segments in dynamic
refactor: move all common functions in SablierV2Lockup

* perf: use the storages instead of the getters

* feat: check if the start time is zero

feat: check if the start time is greater than end time
test: update tests accordingly

* docs: re-add natspec for _calculateStreamedAmountForMultipleSegments

* refactor: make start time strictly less than cliff time

* chore: update branch from staging

* docs: use plural

* refactor: order constant functions alphabetically

refactor: make constant functions external

* refactor: make cliff time strictly greater than start time

* docs: correct natspec

chore: use "override" specifier on nftDescriptor

* test: update comment for using unchecked to allow overflow

* docs: improve natspec

chore: order functions alphabetically in SablierV2Lockup

* refactor: rename Stream structs within LockupLinear and LockupDynamic namespaces

---------

Co-authored-by: smol-ninja <[email protected]>
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.

3 participants