Flow

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

Insufficient Withdrawable Balance due to `_coveredDebtOf` Logic in `SablierFlow` Contract

Github

Summary

The _coveredDebtOf function in the SablierFlow contract has a logic flaw that restricts users from withdrawing their full balance in certain conditions. When the contract balance exceeds the totalDebt, _coveredDebtOf only allows withdrawals up to the totalDebt amount, effectively locking any remaining balance beyond that amount. This discrepancy could lead to user funds being partially inaccessible, which contradicts expected functionality and diminishes user experience.

Vulnerability Details

In detail, the _coveredDebtOf function evaluates whether the contract’s balance is less than totalDebt. If balance is less than totalDebt, it simply returns the full balance as the covered debt. However, when balance exceeds totalDebt, _coveredDebtOf returns only the totalDebt amount. This approach prevents users from withdrawing the difference between balance and totalDebt, creating a situation where part of the user’s funds are effectively locked within the contract.

function _coveredDebtOf(uint256 streamId) internal view returns (uint128) {
uint128 balance = _streams[streamId].balance;
// If the balance is zero, return zero.
if (balance == 0) {
return 0;
}
uint256 totalDebt = _totalDebtOf(streamId);
// If the stream balance is less than or equal to the total debt, return the stream balance.
if (balance < totalDebt) {
return balance;
}
// At this point, the total debt fits within `uint128`, as it is less than or equal to the balance.
return totalDebt.toUint128();
}

This issue particularly affects two key functions: withdrawableAmountOf and withdrawMax. Both of these functions call _coveredDebtOf to determine the amount available for withdrawal. Since _coveredDebtOf limits the withdrawal amount to totalDebt when balance > totalDebt, users are prevented from withdrawing any funds beyond the totalDebt threshold, even if the actual balance is higher.

A related impact also exists on the refundableAmountOf function. This function calculates the amount refundable to the sender, again relying on _coveredDebtOf. As a result, the logic discrepancy in _coveredDebtOf affects refundableAmountOf by potentially restricting the refundable amount, preventing users from retrieving the full balance that should ideally be available to them.

In practice, this can lead to a situation where users find their funds partially inaccessible, which not only disrupts the intended functionality of the contract but also diminishes the user experience by creating confusion and frustration over why the full balance isn’t withdrawable.

By failing to provide users with access to their full balance, this issue goes against user expectations and common practices in contract interactions, where users typically anticipate having unrestricted access to their deposited balance.

Impact

This vulnerability significantly impacts users by potentially locking part of their funds in the contract. Users would be unable to withdraw or refund their entire balance when balance > totalDebt. Users may be unable to access the portion of their balance above totalDebt, leading to frustration and a negative user experience. While funds aren’t permanently lost, the restriction could lead to unexpected delays in accessing these funds.

Tools Used

Manual Review

Recommendations

Update _coveredDebtOf to return the full balance if balance exceeds totalDebt. This change would ensure users have access to their full balance in withdrawableAmountOf or withdrawMax functions.

Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.