Remove amount
parameter in withdraw
function
#4
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The Issue
In the current version of the contracts, I wanted to mirror the
withdraw
function signature from the Lockup contracts.This doesn't work for open-ended streams due to the
amount
parameter. More specific, when the amount passed to the function is less than the maximum withdrawable amount, it can lead to lost funds for the recipient—not completely frozen, since the sender can still perform a refund—but unable to withdraw them as the recipient, this is due to updating thestream.lastTimeUpdate
toblock.timestamp
and how the streamed amount is calculated.We calculate the streamed amount by multiplying the rate per second (rps) by the delta between
lastTimeUpdate
andblock.timestamp
.https://github.com/sablier-labs/v2-open-ended/blob/34cab8914430ea22845a5e9c98104a11720ae662/src/SablierV2OpenEnded.sol#L382-L386
Consider this example: Alice sets up a stream at time$\ t_0 $ , with an rps corresponding to 1000 assets per month, i.e. $\ rps \approx 0.00038580246 $ , and deposits sufficient assets for several months. After one month, at $\ t_1 $ , the recipient decides to withdraw 500 assets. Here's what happens:
lastTimeUpdate
is set to the currentblock.timestamp
, i.e.If the recipient tries to withdraw the remaining 500 "streamed" assets just one second later,$\ t_2 $ , the transaction will fail at step two because $(t_2 - t_1) \times rps = 1 \times rps < 500 $ , resulting in a loss for the recipient.
The Fix
To address this, I have removed the
amount
parameter fromwithdraw
function and added atime
one in bothstreamedAmountOf
andwithdraw
functions. @IaroslavMazur, please lmk if you have suggestions for a better name.This change fixes the issue, as the streamed amount will now be calculated by multiplying rps with the delta between the provided
time
parameter andlastTimeUpdate
. Ofc, additional checks are necessary to validate thetime
parameter:https://github.com/sablier-labs/v2-open-ended/blob/75d9f7d12de3d3024dffb5a95421c147e6a7c63a/src/SablierV2OpenEnded.sol#L693-L701
Furthermore,
lastTimeUpdate
will now be set to the giventime
parameter, notblock.timestamp
.Following the above example, instead of specifying an amount of 500 for withdrawal, the recipient would now input ($\dfrac{t_1}{2} $ , basically saying "I want to withdraw assets equivalent to two weeks' worth of streaming."