Flow

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

Issue with the Enhancement of NFT Transferability: Addressing Limitations due to the absence of a Toggling Functionality

## Summary

The smart contract implementation for an NFT (Non-Fungible Token) stream includes a function to restrict the transferability of tokens upon minting. The `_update` function checks this `isTransferable` flag during each transfer attempt, ensuring compliance with the specified transferability restriction. However, a limitation exists: once a token is marked as non-transferable, it remains permanently non-transferable. This lack of a toggling mechanism may limit flexibility in token transfers in future scenarios where such transfers become necessary.

## Vulnerability Details

1. **Transferability Limitation**:

- The `_update` function enforces a one-time setting of transferability for each token at creation, based on the `isTransferable` flag. Once the token is minted as non-transferable, it cannot later be toggled to become transferable.

- This restriction affects any subsequent recipient of the token, preventing them from ever transferring it, even if such an action become authorized or necessary.

2. **Code Review**:

/// @dev See the documentation for the user-facing functions that call this internal function.
function _create(
address sender,
address recipient,
UD21x18 ratePerSecond,
IERC20 token,
@auit>><1.>>> 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,
@auit>><2.Hardcoded once created>>> 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
});
}
function _update(
address to,
uint256 streamId,
address auth
)
internal
override
updateMetadata(streamId)
returns (address)
{
address from = _ownerOf(streamId);
@auit>><2.>>> if (from != address(0) && !_streams[streamId].isTransferable) {>
revert Errors.SablierFlowBase_NotTransferable(streamId);
}

- The `_update` function in its current form does not allow for the transferability status of a token to be changed post-mint.

- While preventing accidental burns and unauthorized transfers, this static transferability setting introduces a rigidity that could be improved with a toggling feature.

## Impact

The inability to toggle the transferability status of tokens post-creation can have several implications:

- A token that was initially marked as non-transferable may require transferability in later stages, for example, due to an ownership change or an authorization granted to a third party. E.g Payroll tracking, promotion of staff.

- Certain use cases may require tokens to become transferable over time or under specific conditions, such as upon reaching a specific period or an authorized change in status.

- Token holders who initially agreed to non-transferable status might need to later transfer or exchange the token, and the lack of flexibility in doing so could reduce the token’s overall utility.

## Tools Used

- **Manual Review**:

## Recommendations

1. **Introduce a Transferability Toggle Function**:

Implement a function to enable authorized parties (e.g., original sender, current owner, or approved third parties) to toggle the `isTransferable` status of a token. This could allow flexibility in token usage while maintaining controlled access.

2. **Controlled Transferability Update**:

Introduce conditions that can modify `isTransferable` only under specific, controlled circumstances. For example, a token could become transferable if it reaches a designated age or if approved by the contract owner, thus preserving intended security while enabling flexibility.

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.