Flow

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

Missing Zero Address Check in `SablierFlow::_create` before state updates Leads to Unnecessary Gas Costs.

Summary

The SablierFlow::_create function in the Sablier contract lacks validation to ensure that the recipient address is not set to the zero address. Since this check occurs only after state updates, it violates the Checks-Effects-Interactions (CEI) pattern. This oversight could lead to wasted gas if a stream is created with a zero address recipient, as unnecessary state updates are performed before the failure condition is triggered.

Vulnerability Details

The SablierFlow::_create function does not validate the recipient address upfront, leading to potential gas loss if a zero address is provided as the recipient. This can result in inefficient gas usage due to unnecessary state updates.

Impact

Without this check, the function could incur unnecessary gas costs and unwanted state updates if the zero address is used as the recipient.

Tools Used

Manual review.

Recommendations

Add a validation check at the start of the _create function to ensure that the recipient is not the zero address, preventing unnecessary gas usage. Add an appropriate error code to the Errors library, and revert the transaction if the zero address is detected.

Suggested Code Changes

In Errors.sol:

+ /// @notice Thrown when the recipient is a zero address.
+ error SablierFlow_InvalidRecipientAddress();

In SablierFlow::_create:

/// @dev See the documentation for the user-facing functions that call this internal function.
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();
}
+ if (recipient == address(0)) {
+ revert Errors.SablierFlow_InvalidRecipientAddress();
+ }
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 10 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.