-
Notifications
You must be signed in to change notification settings - Fork 50
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: merge all lockups into a single contract #1069
Conversation
@andreivladbrg let me know if you get a chance to review this approach. For the records, this is just to demonstrate the idea and does not confirm the actual changes that should be made. |
@smol-ninja at your request, I am going to leave here some of the suggestion/things I would change/improve: I would rename the current I believe we should have different Add Move this logic in uint256 blockTimestamp = block.timestamp;
if (_streams[streamId].startTime <= blockTimestamp) {
return 0;
}
if (block.timestamp >= _streams[streamId].endTime) {
return _streams[streamId].amounts.deposited;
} I would move There is no need to pass the entire stream struct into We can keep the current approach to updating storage for the stream struct, as I find it more readable: _streams[streamId] = Lockup.Stream({
amounts: Lockup.Amounts({ deposited: createAmounts.deposit, refunded: 0, withdrawn: 0 }),
asset: params.asset,
endTime: params.timestamps.end,
isCancelable: params.cancelable,
isDepleted: false,
isStream: true,
isTransferable: params.transferable,
sender: params.sender,
startTime: params.timestamps.start,
wasCanceled: false
}); I am not a fan of the The
For the first one, we can place all calculation logic for all models in one place. Click to see what I mean
function _calculateStreamedAmount(uint256 streamId) internal view override returns (uint128 streamedAmount) {
// If the end time is not in the future, return the deposited amount.
uint256 blockTimestamp = block.timestamp;
if (_streams[streamId].startTime <= blockTimestamp) {
return 0;
}
if (block.timestamp >= _streams[streamId].endTime) {
return _streams[streamId].amounts.deposited;
}
Family family = _streams[streamId].family;
if (family == Lockup.Family.LOCKUP_LINEAR) {
streamedAmount = Calculation.linearStream(params);
} else if (family == Lockup.Family.LOCKUP_DYNAMIC) {
streamedAmount = Calculation.calculateStreamedAmountForSegments(params);
} else if (family == Lockup.Family.LOCKUP_TRANCHED) {
streamedAmount = Calculation.sumOfPassedTranches(params);
}
} A major benefit of this is that we can create a mock for this library independently and isolate tests for calculation logic, which is arguably the most fragile part of the code. If possible, and assuming no significant downsides, I would divide the dynamic calculation function into two parts:
|
Agree.
Unique params related to the model are still emitted through separate events. But your suggestion sounds good to me. If it increase the contract size by a good margin, would it be OK to have shared event and a separate lockup specific event?
Sounds good. Will experiment with this idea.
Why? This would increase contract size.
Sounds good.
Agree.
"Fan" is a personal choice. My rationale is that (1) its a single word (2) Inspired from Family of Curves which means each curves in the family share the same formula which is indeed a case here. Therefore I find
May be it is not hard to follow. We have bigger files than than in our repos. To increase readability, I can put categories in it.
Sorry, I do not understand it. Can you please re-explain?
Sounds good. Will try this approach. |
I would suggest |
Why isn't IMO, streaming should be defined as the unlocking of tokens over a period of time (referring to multiple cadences discussion). So, as long as there are multiple unlocks periodically, it can be considered a stream. |
We discussed the streaming terminology here: #708 |
Sounds great. Thanks for pointing me to the other discussion. I am in for |
1968e23
to
0a17911
Compare
re terminology , i like the simple
hmm, didn't see them, i talking about: cliff for LL, segments for LD, and tranches for LT
on contrary, this would decrease the size as there would only one time this function called (in the common Click to see code
function _createLD(Params memory params) internal returns(uint256) {
Helpers.checkAndCalculateBrokerFee(params);
// --snip--
_create(params);
}
function _createLL(Params memory params) internal returns(uint256) {
Helpers.checkAndCalculateBrokerFee(params);
// --snip--
_create(params);
}
function _createLD(Params memory params) internal returns(uint256) {
Helpers.checkAndCalculateBrokerFee(params);
// --snip--
_create(params);
}
function _create(Params memory params) internal returns(uint256) {
// --snip--
}
I am suggesting: function _createLD(Params memory params) internal returns(uint256) {
// --snip--
_create(params);
}
function _createLL(Params memory params) internal returns(uint256) {
// --snip--
_create(params);
}
function _createLD(Params memory params) internal returns(uint256) {
// --snip--
_create(params);
}
function _create(Params memory params) internal returns(uint256) {
Helpers.checkAndCalculateBrokerFee(params);
}
My idea was to divide the logic as follows:
This structure would improve organization and allow testing to be divided between the two parts. flowchart TD
subgraph SmartContractSystem
direction TB
A[Contract A]
B(Library Calculation Logic)
C(Mock Library for Testing)
A -->|Check: if X then Y, if W then Z| ACheck[Conditional Checks]
end
%% Contract A structure
%% A -->|Calls| B[Library Calculation Logic]
%% Testing Structure
subgraph Testing
direction TB
T[Test Calculation Logic]
T1[Test Conditional Checks]
T1 --> |Uses| ACheck
T -->|Uses| C
end
%% Explanation
ACheck -->|If Conditions Met| B
C -.->|Mock Contract| B
|
Its not possible to do so. The reason is that the returned value of @andreivladbrg I have included all your suggestions including the library split. They were all good. I have also updated the core source contracts. Feel free to review the The size of the |
15240b6
to
070214a
Compare
@andreivladbrg this PR is ready for your review. A separate PR is opened to implement the tests and the remaining changes (#1077). Please see the description in both the PRs to see what they cover. The CI will succeed in #1077. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work!
Left some comments which were tentatively addressed in the upcoming PR created on top of this.
5634eff
to
b4d9ea6
Compare
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
151dbdd
to
57d7def
Compare
57d7def
to
8ca1274
Compare
feat: adds ISablierLockup and ISablierLockupBase refactor: modifies Datatypes refactor: deletes individual lockup contracts and interfaces feat: adds SablierLockupBase and SablierLockup feat: spin out VestingMath library out of Helpers library refactor: update errors refactor: make periphery and merkle lockup contracts compatible with singelton lockup chore: update count of inherited components chore: update deployment scripts build: update shell scripts chore: update precompiles contract style: fix lint warnings feat: include libraries in artifacts fix: bug refactor: move endTime check into helpers refactor: address feedback (#1079) * refactor: address feedback * refactor: move calculate segments/tranches in Helpers refactor: revert constant in library change refactor: re introduce _checkCliffAndEndTime function * refactor: polish code * chore: remove unneeded import * add stream id in _create * use ISablierLockupBase.isTransferable refactor: prefix errors name used in Helpers with SablierHelpers --------- Co-authored-by: Andrei Vlad Birgaoanu <[email protected]>
8ca1274
to
9050f98
Compare
feat: adds ISablierLockup and ISablierLockupBase refactor: modifies Datatypes refactor: deletes individual lockup contracts and interfaces feat: adds SablierLockupBase and SablierLockup feat: spin out VestingMath library out of Helpers library refactor: update errors refactor: make periphery and merkle lockup contracts compatible with singelton lockup chore: update count of inherited components chore: update deployment scripts build: update shell scripts chore: update precompiles contract style: fix lint warnings feat: include libraries in artifacts fix: bug refactor: move endTime check into helpers refactor: address feedback (#1079) * refactor: address feedback * refactor: move calculate segments/tranches in Helpers refactor: revert constant in library change refactor: re introduce _checkCliffAndEndTime function * refactor: polish code * chore: remove unneeded import * add stream id in _create * use ISablierLockupBase.isTransferable refactor: prefix errors name used in Helpers with SablierHelpers --------- Co-authored-by: Andrei Vlad Birgaoanu <[email protected]>
Closes #1074.
Since the work is split into two PRs. The order of merging would be:
Model
enumSablierLockup
toSablierLockupBase
andSablierLockupOne
toSablierLockup
endTime
inCreateWithTimestamps
structcalculateStreamedAmountForSegments
to disallow unnecessary inputsStream
using JSON in create functions