Flow

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

Anyone can withdraw from nonVoided streams which can lead to partial griefing through protocol fees

Summary

Withdraw functions allow anyone to withdraw from a stream which can be used to grief other users' streams through fees

Vulnerability Details

Withdraw functions allow anyone to withdraw from a stream as long as the to is either the sender, the recipient or an approved user. This however doesn't account for fact that the stream is still active and not voided, and that protocol fees are charged upon withdrawal which can be used to grief the streams.

By looking at the withdraw and withdrawMax functions, we can see that anyone can withdraw from the streams as long as the to is the sender, an approved user or the owner of the stream.

function withdraw(
uint256 streamId,
address to,
uint128 amount
)
external
override
noDelegateCall
notNull(streamId)
updateMetadata(streamId)
returns (uint128 withdrawnAmount, uint128 protocolFeeAmount)
{
// Checks, Effects, and Interactions: make the withdrawal.
(withdrawnAmount, protocolFeeAmount) = _withdraw(streamId, to, amount);
}
/// @inheritdoc ISablierFlow
function withdrawMax(
uint256 streamId,
address to
)
external
override
noDelegateCall
notNull(streamId)
updateMetadata(streamId)
returns (uint128 withdrawnAmount, uint128 protocolFeeAmount)
{
uint128 coveredDebt = _coveredDebtOf(streamId);
// Checks, Effects, and Interactions: make the withdrawal.
(withdrawnAmount, protocolFeeAmount) = _withdraw(streamId, to, coveredDebt);
}

However, we can also see that a stream doesn't have to be void or paused before it can be withdrawn from. A stream could actively be streaming, an anyone can temporarily block streaming by just withdrawing the tokens to the approved to. Also, when protocol fee is being charged on withdrawal, the amount received by the to will be significantly lesser due to the fees being charged. This can lead to some sort of indirect griefing of the stream's participant.

if (protocolFee > ZERO) {
// Calculate the protocol fee amount and the net withdraw amount.
(protocolFeeAmount, amount) = Helpers.calculateAmountsFromFee({ totalAmount: amount, fee: protocolFee });
// Safe to use unchecked because addition cannot overflow.
unchecked {
// Effect: update the protocol revenue.
protocolRevenue[token] += protocolFeeAmount;
}
}

Consider the following case

  1. Sender creates a stream of 100 tokens and is about to start streaming.

  2. Malicious user withdraws the tokens the stream

  3. Protocol fee of 5 is deducted and the to is left with 95 tokens.

  4. Since the stream is not voided, tokens can still be deposited to ensure that streaming can continue.

  5. Malicious user withdraws again and the protocol charges some more fees.

To prevent this from happening the stream participants have to either void the stream making it unusable or risk malicious interference in their streaming activity.

Impact

Malicious users can withdraw from active streams temporairily pausing streams while griefing stream participants through protocol fees.

Tools Used

Manual review

Recommendations

Allow only specified users to withdraw from a stream, or only allow withdrawals on voided streams.

Updates

Lead Judging Commences

inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

inh3l Submitter
8 months ago
inallhonesty Lead Judge
8 months ago
inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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