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
});
}
```
Without the protocol fee, the protocol will lose revenue for streams created with tokens lacking an admin-set fee.
Manual code review
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
});
}
```
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.