Flow

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

Fee Not Deducted from `SablierFlow::withdrawableAmountOf`

Summary

The withdrawableAmountOf function in the SablierFlow contract does not account for protocol fees when calculating the withdrawableAmount for a specific stream. As a result, users are shown an inflated withdrawableAmount that includes fees, potentially leading to over-withdrawal and impacting protocol revenue.

Vulnerability Details

The withdrawableAmountOf function calculates the amount available for withdrawal based on the stream's covered debt using the _coveredDebtOf function. However, it fails to deduct the protocol fee specified for the associated token in the protocolFee mapping.

Due to this missing fee deduction, the protocol shows users an inaccurate withdrawable amount that does not account for fees. This may lead users to believe they can withdraw the total amount. This oversight could result in funds being withdrawn without the appropriate fee, affecting both protocol revenue and the stability of the balance calculations.

Impact

  1. Revenue Loss: The protocol fee set by the protocol is not deducted from the withdrawable amount, leading to potential revenue loss for the protocol. Over time, this could result in significant financial losses.

  2. User Confusion: Users may assume that they can withdraw the full amount displayed, resulting in confusion if the protocol later requires fee adjustments or additional transactions to cover uncollected fees.

  3. Protocol Stability: Failure to deduct fees can affect the integrity of balance and debt calculations, potentially impacting other contract functions that depend on accurate fund tracking.

Tools Used

Manual Review

Recommendations

Modify the withdrawableAmountOf function to incorporate the protocol fee deduction by using the calculateAmountsFromFee helper function. This can be done by first calculating the gross withdrawableAmount Then, the protocol fee is applied, returning only the net amount as shown below

function withdrawableAmountOf(uint256 streamId)
external
view
override
notNull(streamId)
returns (uint128 withdrawableAmount)
{
//Retrieve the initial withdrawable amount based on covered debt.
- withdrawableAmount = _coveredDebtOf(streamId);
+ uint128 initialWithdrawableAmount = _coveredDebtOf(streamId);
//Retrieve the protocol fee for the token associated with the stream.
+ UD60x18 protocolFee = protocolFee[_streams[streamId].token];
//If there is a non-zero protocol fee, calculate the net withdrawable amount.
+ if (protocolFee > ud(0)) {
+ (, withdrawableAmount) = Helpers.calculateAmountsFromFee(initialWithdrawableAmount, protocolFee);
+ } else {
//If no fee is set, return the entire initial amount as withdrawable.
+ withdrawableAmount = initialWithdrawableAmount;
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
8 months ago
inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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