Flow

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

Anyone can trigger withdrawals, causing precision loss and even unexpected reverts

Summary

The _withdraw function allows anyone to trigger a withdrawal as long as the receiver of the tokens is passed in correctly. This can lead to excess rounding errors causing loss to the receiver and even unexpected reverts under certain cases.

Vulnerability Details

The _withdraw function only has one check on the sender of the transaction.

if (to != _ownerOf(streamId) && !_isCallerStreamRecipientOrApproved(streamId)) {
revert Errors.SablierFlow_WithdrawalAddressNotRecipient({ streamId: streamId, caller: msg.sender, to: to });
}

So anyone can call withdraw to trigger a withdrawal as long as they pass in the to address correctly, the owner of the NFT. This can lead to two issues:

Issue 1 - Rounding

The tokens to be dispensed is calculated as elapsedTime * ratePerSecond, in 18 decimals. If the decimals of the token is lower, say 6, then the result is scaled down to 6 decimals by dividing it by 1012. If the elapsedTime is too short, this can lead to the calculated dispensed amount to be lower than 1012, and thus the scaled down amount would be 0.

In this scenario, the receiver would get 0 tokens, but the snapshotTime would still be updated to the current block.timestamp. So the user will lose any rewards that they would have accrued due to rounding errors.

Issue 2 - DOS

Secondly, if the true receiver calls the function withdraw with the intention to withdraw say 100e6 USDC, and passes in 100e6 as the amount, any user can frontrun that transaction with their own withdraw transaction making the first withdraw fail. Say user Alice has accrued 110 USDC from the stream. She calls withdraw(100) to withdraw 100 USDC. Bob can frontrun this with a withdraw(11) call. This will withdraw 11 USDC to Alice, who will then have a pending balance of 99 USDC. This will cause her original withdraw(100) call to fail due to the code section below.

if (amount > withdrawableAmount) {
revert Errors.SablierFlow_Overdraw(streamId, amount, withdrawableAmount);
}

Thus Alice can have her transaction reverted by Bob.

Impact

Users can interfere with the receivers of streams to lose out funds due to rounding with frequent withdrawals, or can have their withdrawals revert due to frontrunning attacks.

Tools Used

Manual

Recommendations

Consider only allowing the NFT owner or approved users to call withdraw.

Updates

Lead Judging Commences

inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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