Flow

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

Ambiguity Between Stream Balance and Total Debt in Covered Debt Calculation

Summary

In SablierFlow.sol, the internal function SablierFlow::_coveredDebtOf contains an ambiguous conditional check involving the stream balance and total debt. According to its NatSpec, this function "calculates the amount of covered debt based on the stream balance," which suggests the function’s concern should primarily be the stream balance rather than the total debt. For instance, if Alice has a certain amount of money (balance) in her possession, she can only cover her debts up to that amount.

See the issue below:

SablierFlow::_coveredDebtOf issue:

@> /// @dev Calculates the amount of covered debt based on the stream balance.
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();
}

The comments in this function create ambiguity. While the comment above the conditional check suggests focusing on the stream balance, the current logic sometimes appears to prioritize the total debt. Conceptually, the function should allow a user to pay only up to their available stream balance, which also minimizes gas costs by simplifying the code execution.

Referring to the Sablier Flow documentation, the current function implementation aligns with the functional requirements. However, the comments should be adjusted for accuracy. According to the documentation:

4. Covered debt
The portion of the total debt covered by the stream balance, equivalent to the withdrawable amount (an alias).
The covered debt (cd) is defined as the minimum of the total debt and the stream balance:
cd = {
td if td <= balance
or
balance if td > balance
}

Tools Used

Manual Review

Recommendations

Please update as shown below to align with documentation:

Revised Code Suggestions

Option 1:

/// @dev Calculates the amount of covered debt based on the stream balance.
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 the total debt is less than or equal to the stream balance, return the total debt.
- if (balance < totalDebt) {
+ if (totalDebt <= balance) {
return totalDebt.toUint128();
}
return balance;
}

Option 2:

/// @dev Calculates the amount of covered debt based on the stream balance.
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 the stream balance is less than the total debt, return the stream balance.
if (balance < totalDebt) {
return balance;
}
return totalDebt.toUint128();
}

Option 3:

/// @dev Calculates the amount of covered debt based on the stream balance.
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 the stream balance is less than or equal to the total debt, return the stream balance.
if (balance <= totalDebt) {
return balance;
}
return totalDebt.toUint128();
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

[INVALID]`_coveredDebtOf` discrepancy between condition and comment `balance < totalDebt`

Support

FAQs

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