Flow

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

Missing Protocol Fee Check During Stream Creation, lead to create and withdraw from stream without paying protcol fee.

Summary

Vulnerability Details

https://github.com/Cyfrin/2024-10-sablier/blob/8a2eac7a916080f2022527408b004578b21c51d0/src/SablierFlow.sol#L564-L622

When creating a new stream for any token, the protocol fee should be applied to ensure revenue is captured upon withdrawals. However, in the SablierFlow.sol::_create function, there is no check to confirm that a protocol fee has been set for the token. If an admin has not established a fee, streams can be created for that token, allowing the sender or recipient to withdraw or void the stream without incurring any protocol fees. This oversight could result in streams bypassing fee collection entirely.

```solidity

function _create(

address sender,

address recipient,

UD21x18 ratePerSecond,

IERC20 token,

bool transferable

)

internal

returns (uint256 streamId)

{

if (sender == address(0)) {

revert Errors.SablierFlow_SenderZeroAddress();

}

uint8 tokenDecimals = IERC20Metadata(address(token)).decimals();

if (tokenDecimals > 18) {

revert Errors.SablierFlow_InvalidTokenDecimals(address(token));

}

streamId = nextStreamId;

_streams[streamId] = Flow.Stream({

balance: 0,

isStream: true,

isTransferable: transferable,

isVoided: false,

ratePerSecond: ratePerSecond,

sender: sender,

snapshotDebtScaled: 0,

snapshotTime: uint40(block.timestamp),

token: token,

tokenDecimals: tokenDecimals

});

}

```

Impact

Without the protocol fee, the protocol will lose revenue for streams created with tokens lacking an admin-set fee.

Tools Used

Manual code review

Recommendations

Implement a check for tokens lacking a protocol fee, and trigger an event to notify the admin to set the fee for those tokens. This way, the stream can still be created, but the protocol is alerted to adjust fees as needed.

```solidity

function _create(

address sender,

address recipient,

UD21x18 ratePerSecond,

IERC20 token,

bool transferable

)

internal

returns (uint256 streamId)

{

if (sender == address(0)) {

revert Errors.SablierFlow_SenderZeroAddress();

}

uint8 tokenDecimals = IERC20Metadata(address(token)).decimals();

if (tokenDecimals > 18) {

revert Errors.SablierFlow_InvalidTokenDecimals(address(token));

}

if(protocolFee[token] == 0){

emit setProtocolFeeForToken(token);

}

streamId = nextStreamId;

_streams[streamId] = Flow.Stream({

balance: 0,

isStream: true,

isTransferable: transferable,

isVoided: false,

ratePerSecond: ratePerSecond,

sender: sender,

snapshotDebtScaled: 0,

snapshotTime: uint40(block.timestamp),

token: token,

tokenDecimals: tokenDecimals

});

}

```

Updates

Lead Judging Commences

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

Appeal created

aua_oo7 Submitter
8 months ago
inallhonesty Lead Judge
8 months ago
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.