Sablier

Sablier
DeFiFoundry
53,440 USDC
View results
Submission Details
Severity: low
Valid

Sender will be unable to cancel the stream if he is blacklisted by the stream's token

Summary

The SablierV2Lockup::cancel function cancels the stream and refunds any remaining assets to the sender. The sender, who created the stream, possesses the power to cancel it at any point between the stream start and end time, retrieving the remaining amount (total - already streamed amount). However, if the sender is blacklisted by the token, they will not be able to cancel the stream, and the funds will flow to all recipients. The protocol uses ERC20 tokens with pause/blacklisting properties like USDC.

Vulnerability Details

File: v2-core/src/abstracts/SablierV2Lockup.sol
function cancel(uint256 streamId) public override noDelegateCall notNull(streamId) {
// Check: the stream is neither depleted nor canceled.
if (_streams[streamId].isDepleted) {
revert Errors.SablierV2Lockup_StreamDepleted(streamId);
} else if (_streams[streamId].wasCanceled) {
revert Errors.SablierV2Lockup_StreamCanceled(streamId);
}
// Check: `msg.sender` is the stream's sender.
if (!_isCallerStreamSender(streamId)) {
revert Errors.SablierV2Lockup_Unauthorized(streamId, msg.sender);
}
// Checks, Effects and Interactions: cancel the stream.
_cancel(streamId);
}
function _cancel(uint256 streamId) internal {
.............................
// Effect: if there are no assets left for the recipient to withdraw, mark the stream as depleted.
if (recipientAmount == 0) {
_streams[streamId].isDepleted = true;
}
// Effect: set the refunded amount.
_streams[streamId].amounts.refunded = senderAmount;
// Retrieve the sender and the recipient from storage.
address sender = _streams[streamId].sender;
address recipient = _ownerOf(streamId);
// Retrieve the ERC-20 asset from storage.
IERC20 asset = _streams[streamId].asset;
// Interaction: refund the sender.
//@audit-issue if sender becomes blacklisted he won't able to cancel the stream
asset.safeTransfer({ to: sender, value: senderAmount });
..........................
}
}

https://github.com/Cyfrin/2024-05-Sablier/blob/main/v2-core/src/abstracts/SablierV2Lockup.sol#L256

In the current implementation, the protocol refunds the funds to the sender's address when canceling the stream. If the sender is blacklisted by the asset, the refund will revert, preventing the stream from being canceled and causing it to continue

The root cause of this issue is that the implementation does not follow a 2-step withdrawal process. This process is implemented for recipients, who claim in one transaction and withdraw in another, but not for the sender. If a sender/team decides to stream a large amount of funds and gets blacklisted, they will lose all the funds in the stream due to the blocked cancel stream functionality

Instead, the protocol should allow the stream to be canceled and wait for the sender to be unblacklisted so that they can withdraw their funds later

Impact

The sender will lose all of their funds due to the blocked cancel functionality.

Tools Used

Manual

Recommendations

Implement a 2-step withdrawal process: cancel the stream in one step and provide a withdrawal functionality in a second step

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Info/Gas/Invalid as per Docs

https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity

tripathi Submitter
about 1 year ago
inallhonesty Lead Judge
about 1 year ago
0xnevi Judge
about 1 year ago
tripathi Submitter
about 1 year ago
tripathi Submitter
about 1 year ago
inallhonesty Lead Judge
about 1 year ago
tripathi Submitter
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Push over pull for `_cancel` in context of Pausable tokens

Support

FAQs

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