Flow

Sablier
FoundryDeFi
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

A Complete Refund Cannot Be Guarunteed When The Stream Is Not Paused

Summary

A user cannot guaruntee the exact block a refund function call will be accepted into a block. Since the amount corresponding to a full refund changes every block, then this makes any attempt to get a full refund likely to revert.

Vulnerability Details

The maximum refundable amount constantly decreases every block as long as ratePerSecond is not 0. Since the user cannot guarantee the exact block which their transaction gets accepted, they also do not know the exact amount input parameter into the refund function which would withdraw their entire refundable balance.

Any amount exceeding the maximum refund will revert:

// Check: the refund amount is not greater than the refundable amount.
if (amount > refundableAmount) {
revert Errors.SablierFlow_RefundOverflow(streamId, amount, refundableAmount);
}

A similar situation would occur with withdrawals in Sablier Flow if there was not withdrawMax function which always withdraws the maximum amount of a token. There is no corresponding refundMax function. In other protocols, similar situations where a withdrawal amount is constantly changing (such a lending protocol where interest accrues every block), there is either a maximum withdraw function or the type(uint256).max input amount withdraws the entire balance.

Impact

A complete refund is very difficult to achieve due to the time the user submitting the transaction not necessarily being the same timestamp as the block that their transaction is accepted. This results in unexpected reversion due to the user specifying an amount exceeding the maximum refundable balance, or forces the user to leave excessive dust behind to guaruntee that their refund succeds.

Note that the sender could pause the stream first which would make the maximum refundable amount constant, and then refund, which is why this is just Medium Severity. Also note that forcing a user to do this partly nullifies the point of having the `pauseAndRefund` function, which also cannot achieve a reliable full refund due to this vulnearbility.

Tools Used

Manual Review

Recommendations

Add a refundMax function which refunds the entire refundable balance.

An alternative solution is to make refund amount inputs which exceed the refundable amount to refund the maximum rather than reverting.

Updates

Lead Judging Commences

inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.