Flow

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

Missing Zero Address Check for Recipient in `create` Function

Summary

The create function lacks a validation check for the recipient address, allowing the possibility of minting NFTs to the zero address. This oversight can lead to streams being unowned and funds becoming irretrievable. Implementing a check to prevent zero address assignments will enhance the security and reliability of the contract.

Vulnerability Details

In the current implementation of the create function, there is no validation to ensure that the recipient address is not the zero address

function _create(
address sender,
address recipient,
UD21x18 ratePerSecond,
IERC20 token,
bool transferable
)
internal
returns (uint256 streamId)
{
// Check: the sender is not the zero address.
if (sender == address(0)) {
revert Errors.SablierFlow_SenderZeroAddress();
}
uint8 tokenDecimals = IERC20Metadata(address(token)).decimals();
// Check: the token decimals are not greater than 18.
if (tokenDecimals > 18) {
revert Errors.SablierFlow_InvalidTokenDecimals(address(token));
}
// Load the stream ID.
streamId = nextStreamId;
// Effect: create the stream.
_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
});
// Using unchecked arithmetic because this calculation can never realistically overflow.
unchecked {
// Effect: bump the next stream ID.
nextStreamId = streamId + 1;
}
// Effect: mint the NFT to the recipient.
_mint({ to: recipient, tokenId: streamId });
// Log the newly created stream.
emit ISablierFlow.CreateFlowStream({
streamId: streamId,
sender: sender,
recipient: recipient,
ratePerSecond: ratePerSecond,
token: token,
transferable: transferable
});
}

As a result, if a stream is created with the recipient set to the zero address, the NFT representing the stream is minted to this invalid address. This leads to the stream being unowned, as the zero address cannot manage or withdraw from the stream. This will exposes the contract to potential loss of funds and operational inefficiencies, as the stream cannot be accessed or controlled by any user.

Impact

This will result into

  • Loss of Ownership: The NFT is minted to the zero address, making it unowned and unmanageable by any user.

  • Loss of Funds: Funds deposited into the stream could become irretrievable, leading to a potential financial loss for the sender.

  • Operational Disruption: The inability to manage or withdraw from the stream could disrupt the intended flow of funds and operations.

Tools Used

Manual Review

Recommendations

Introduce a check in the create function to ensure the recipient address is not the zero address.

function _create(
address sender,
address recipient,
UD21x18 ratePerSecond,
IERC20 token,
bool transferable
)
internal
returns (uint256 streamId)
{
// Check: the sender is not the zero address.
if (sender == address(0)) {
revert Errors.SablierFlow_SenderZeroAddress();
}
+ // Check: the recipient is not the zero address.
+ if (recipient == address(0)) {
+ revert Errors.SablierFlow_recipientZeroAddress();
+ }
uint8 tokenDecimals = IERC20Metadata(address(token)).decimals();
// Check: the token decimals are not greater than 18.
if (tokenDecimals > 18) {
revert Errors.SablierFlow_InvalidTokenDecimals(address(token));
}
// Load the stream ID.
streamId = nextStreamId;
// Effect: create the stream.
_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
});
// Using unchecked arithmetic because this calculation can never realistically overflow.
unchecked {
// Effect: bump the next stream ID.
nextStreamId = streamId + 1;
}
// Effect: mint the NFT to the recipient.
_mint({ to: recipient, tokenId: streamId });
// Log the newly created stream.
emit ISablierFlow.CreateFlowStream({
streamId: streamId,
sender: sender,
recipient: recipient,
ratePerSecond: ratePerSecond,
token: token,
transferable: transferable
});
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 9 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.