-
Notifications
You must be signed in to change notification settings - Fork 48
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
Extend status enum #467
Extend status enum #467
Conversation
docs: improve writing and formatting docs: fix typos in NatSpec feat: add "_calculateStreamedAmount" in linear contract feat: add "isCanceled", "isDepleted", "isStream" fields in in stream structs perf: dedup `_withdrawableAmountOf` refactor: alphabetical ordering refactor: delete "isSettled" refactor: delete "status" field in stream structs refactor: explicit null checks refactor: improve error names refactor: move withdraw amount check in each streaming contract refactor: rename "isActive" to "isWarm" refactor: rename "getStatus" to "statusOf" test: add "has been canceled" branch in state trees test: add a new fuzz test for "cancel" test: add a new fuzz test for "withdrawMax" test: add constant "MARCH_1_2023" test: allow depleted streams in dynamic fuzz tests test: allow depleted streams in fork tests test: delete unused imports test: de-dup "streamedAmountOf" tests test: de-dup "withdrawableAmountOf" tests test: expect transfer calls in all "withdraw" fuzz tests test: improve function and variable names test: improve nesting structures in state trees test: improve wording in comments test: improve wording in state trees test: remove status check in "createWithDeltas" tests test: remove superfluous comments test: rename "whenStreamActive" to "whenStatusStreaming" test: test post-withdraw status in fork tests test: update tests in light of new contract API test: use keywords for time calculations test: various enhancements
Rename `WithdrawAmountGreaterThanWithdrawableAmount` to `Overdraw` Close #458
docs: improve writing refactor: delete "Status.NULL" refactor: rename "isNotNull" to "notNull" refactor: rename "isWarm" to "warm" refactor: revert on null streams in "statusOf" test: add tests for "isStream" test: fix pre-conditions in "LockupHandler" test: improve function names test: refactor modifiers in "LockupHandler" test: update tests for "statusOf"
refactor: refactor "warm" into "notCold" refactor: rename "StreamNotWarm" error to "StreamCold" test: add tests for "isCold" and "isWarm" test: delete superfluous tests test: improve names of state trees test: improve orders of operations in tests
Refactor `lockup/shared` to `lockup` Refactor `lockup/linear` to `lockup-linear` Refactor `lockup/dynamic` to `lockup-dynamic`
test: improve writing in comments test: improve writing in function and variable names test: improve state trees
docs: improve writing in comments test: remove "whenCaller" tests in "withdrawMultiple" test: fix, improve, and simplify "withdrawMultiple" tests test: fix and improve "withdraw" tests test: improve naming in state trees
Rename "WithdrawSenderUnauthorized" to "InvalidSenderWithdrawal"
test: run "cancelMultiple" fuzz tests Closes #463
test: change order of streams in "withdrawMultiple" test: use named args in expect calls helpers
test: alphabetical ordering test: add "deployBaseTestContracts" test: add "labelBaseTestContracts" test: add "SEGMENT_COUNT" test: bound instead of assume in "createWithRange" test test: deploy base contract via bespoke helper function test: fix variable names test: improve wording in comments test: label base contracts via bespoke helper function test: delete "DEFAULT_ASSET" test: delete unneeded "Calculations" inheritance test: deploy and label base test contracts in "Base_Test.setUp" test: hard code bound values in invariant tests test: inherit from "Constants" in "Base_Test" test: improve wording in comments test: replace local max uint256 constant with PRBTest constant test: rename "dai" to "usdc"
perf: mark "isCold" and "isWarm" as "external" refactor: change declaration place of "segmentCount" test: add "assertNotEq" for "Lockup.Status" test: group status invariant tests test: improve wording
test: add all shared modifiers in "streamedAmountOf" test: add all shared modifiers in "withdrawableAmountOf" test: alphabetical ordering
test: add private "params" in shared tests test: delete internal "defaultParams" test: deploy and label directly in "Base_Test.setUp" test: define "createDefaultStreamWithTotalAmount" in shared contract text: expand "Defaults" with more default values test: use direct values instead of loading them from default params test: move statements around so make "vm.expectRevert" work test: move "Users" in "test/utils/Types" test: provide default create function params in "Defaults" test: various refactors and improvements
docs: improve writing test: test cold streams in "isCancelable" test: fix mistaken comment in invariant tests
test: prank back to sender after setting protocol fee
d015f00
to
7f8045e
Compare
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.
SRC
In the create function from linear contract you explicitly set the isCanceled
and isDepleted
flags to false, but in the dynamic contract you didn't. What version should we choose?
I noticed this pattern in both cancel
and withdraw
function. At the start of these functions, statusOf
is called to perform checks to determine if the function can be called. This thing is very inefficient because it will calculate the streamed amount twice if the status is STREAMING
or SETTLED
. Imagine a dynamic stream with multiple segments and
a timestamp in the last segment.
Once here:
Cancel
To address the issue, we should make the check on streamedAmount
variable.
We can even eliminate the CANCELED
and DEPLETED
status checks altogether. This is because we have an invariant where if either of the isCanceled
or isDepleted
flags are true, then the isCancelable
flag is false.
Although, if we do want to make additionals checks on the isCanceled
isDepleted
flags we should add getters and use them instead of calling statusOf
.
Withdraw
To address the issue, we should use a getter function for isDepleted
flag.
We can eliminate the PENDING
check due to SablierV2Lockup_WithdrawAmountZero
or SablierV2Lockup_Overdraw
.
Let's rename the isCold
and isWarm
functions to isStatusCold
and isStatusWarm
respectively (and anything else related to the status). This way, we will indicate that we are not referring to storage properties.
Based on what I mentioned above, I would recommend to:
- add a functions that returns a boolean on a specific status
- move anything related to the status in the lockup contract
- make checks in the
_cancel
and_withdraw
functions only on storage properties - remove the
notCold
modifier
No need to declare streamId
here, we can use streamIds[i]
:
Given that the _calculateStreamedAmount
function is called only in the context when not depleted I think we can remove these lines from:
Is the _checkParamsAndWithdraw
function really necessary? We can move its logic in the withdraw
function and change the withdrawMultiple
as we did to cancelMultiple
.
Code snip
/// @inheritdoc ISablierV2Lockup
function withdrawMultiple(
uint256[] calldata streamIds,
address to,
uint128[] calldata amounts
)
external
override
noDelegateCall
{
// Checks: there is an equal number of `streamIds` and `amounts`.
uint256 streamIdsCount = streamIds.length;
uint256 amountsCount = amounts.length;
if (streamIdsCount != amountsCount) {
revert Errors.SablierV2Lockup_WithdrawArrayCountsNotEqual(streamIdsCount, amountsCount);
}
// Iterate over the provided array of stream ids and withdraw from each stream.
for (uint256 i = 0; i < streamIdsCount;) {
// Checks, Effects, and Interactions: check the parameters and make the withdrawal.
withdraw(streamIds[i], to, amounts[i]);
// Increment the loop iterator.
unchecked {
i += 1;
}
}
}
Regarding the new isCanceled
flag, I think it would be more appropriate to name it wasCanceled
instead, and we should not set it to false when the stream becomes depleted. This is because the stream can be canceled once and (as you said before) not to rely on the amount variable, we need to have a historical component which shows that.
I noticed that you moved the withdrawableAmountOf
function in the abstract contract SablierV2Lockup
because we marked the internal function as virtual.
I think it would be a good idea if we also move the refundableAmountOf
functions, ofc this would require to make _calculateStreamedAmount
virtual.
In the _withdraw
function you loaded the amounts
struct in memory, I suggest we do the same in the _cancel
function:
Test
I don't have much to say other than it looks cleaner now. Although, I think it would be better if we change Defaults
to a library (as I did initially), especially for these reasons:
- feat(forge): exclude certain contracts from being eavesdropped on by
vm.expectRevert
foundry-rs/foundry#4762 Copying of type struct LockupDynamic.SegmentWithDelta memory[] memory to storage not yet supported.
The only downside is that you need pass the users and the asset in the default create functions in the shared dir, which IMO is not a big deal. Design wise a library is just superior.
test/fuzz/lockup-linear/create-with-range/createWithRange.t.sol
Outdated
Show resolved
Hide resolved
test/fuzz/lockup-linear/create-with-range/createWithRange.t.sol
Outdated
Show resolved
Hide resolved
test/fuzz/lockup-linear/create-with-range/createWithRange.t.sol
Outdated
Show resolved
Hide resolved
Thanks for the extensive review, Andrei. I have responded to all of your specific comments above, and implemented the ones I have agreed with in my latest commit. Explicit Flags
In the linear contract's case, those flags must be provided because otherwise, Solidity would throw a syntax error (all struct parameters must be provided when initializing a struct).
Side note - "version" wasn't a fitting word in this context. It would have been much clearer to say "approach".
|
docs: document numerical values for "Status" enum perf: remove unneeded "streamId" variable perf: remove unneeded "uin256()" casting test: add missing error strings in assertions
It's not just about efficiency from the gas cost point of view.
This is where we disagree. You keep saying that adding getters for Please double check what I am trying to achieve with my branch, I strongly believe that is the right design decision we should make. Ofc the work is not finished and it can be improved.
I don't think so, the stream is not cold, the status is.
Yes, it isn't. We also use it for other things, but we are very precise about them: Let's rename it.
Don't have a strong opinion here, so I give up on this.
Yes, I know, but the difference is just too small. I think readability outweighs the gas cost.
We can just pass them in the function parameters, we only need the returned values in memory.
Actually there are not that many places. Plus, it's only about passing arguments; this is just not a big deal.
It is a problem because we still need to have these storage variables here: We can't do this: _params = defaults.linearParams();
The places where we would need to call the defaults function, with the users and the asset, are in the |
docs: improve writing in comments docs: remove superfluous comments
I'll make a compromise and concede this point even if I disagree. To be productive, I won't respond to the other assertions you made. I will follow the design laid out on your branch.
Hairsplitting. See Hyrum's Law - users will refer to streams being "cold" or "warm" regardless of how we present the nomenclature. Again, the English language is sufficiently rich to permit these expressions.
My point was the
I'm sorry. Rename what?
I don't think it's small, but there's also the potential risk of StackTooDeep, too. I'll have a look.
Ah, yes, you're right. However, this issue will go away once Foundry fixes foundry-rs/foundry#4762.
For now* (you missed my point about the maintenance cost). Let's keep it as a contract, please. I have a strong opinion. |
feat: add "_isDepleted" internal getter refactor: delete "_checkParamsAndWithdraw" function refactor: delete "StreamPending" error test: refine "cancelMultiple" and "withdrawMultiple" tests test: update state trees in light of new contract API
docs: improve writing in comments feat: add "_wasCanceled" getter refactor: check cold statuses directly in "renounce" refactor: delete "notCold" modifier refactor: emit specific statuses instead of "StreamCold" test: update state trees in light of new contract API
docs: improve writing in comments test: test "isDepleted" getter test: test "wasCanceled" getter
@andreivladbrg, the PR is ready for your re-review. I refactored the implementation according to your suggestions, with one exception: I did not refactor the |
Yes, I did understand, my point was that the getter should match exactly the value from storage. Actually I think this I don't see why we would keep both external and internal functions for Plus, not alphabetically ordered: Did you leave it like this on purpose? It would be easier if we remove the The |
It looks like GitHub hadn't updated the diff in this PR, so you reviewed the branch directly instead of leaving comments in this PR.
I see. I agree that it is not clear what is the best solution, but I would be inclined to lean toward the solution I implemented in my latest commits, namely, returning Imagine a smart contract that depends upon the value returned by
The only place where the In this PR, let's follow my implementation to merge this quickly.
Fair enough, I see where you're coming from. Having only one getter for each would be more KISS, so I agree that we should do it.
Good catch.
Yes, I did that on purpose. I want to revert with an explicit error rather than
Good catch. |
I implemented the changes as per the discussion above. Ready for re-review, @andreivladbrg! FYI: I ended up removing the
|
That's ok. Looks like it's ready to be merged now. I hope I haven't missed anything😅. |
Awesome!! Thanks, Andrei. Let's wait for the CI to finish, and we will merge. |
refactor: delete unneeded modifier uses refactor: merge "wasCanceled" with "_wasCanceled" refactor: remove "onlySenderOrRecipient" modifier
1085efb
to
2a70537
Compare
Can you approve it? |
This is a big PR. Its primary goal is to implement #459 by extending the status enum, but it also closes a number of other issues:
_withdrawableAmountOf
#455WithdrawAmountGreaterThanWithdrawableAmount
error #458cancelMultiple
are not run #463See the diagram in #459 for a visual guide to the changes introduced by this PR.