Flow

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

`protocol fee` amount and the `net withdraw` amount are wrongly calculated.

Summary

when withdrawing amount the protocolFee and Withdrawn amount wrongly calculated because of different decimals precision.

Vulnerability Details

As the protocol uses any token with decimals lower or equal to 18, but the fee is in 18 decimal precision.

Attack Scenario

sender deposit 5 USDC (5 * 1e6), the aggregate balance of the token increased by the 5e6, in 6 decimal precision, but when withdrawing amount, the amount is multiplied by 18 decimal precision which wrongly calculated protocol and withdrawan amount.

https://github.com/Cyfrin/2024-10-sablier/blob/main/src/SablierFlow.sol#L772-L880

function _withdraw(
uint256 streamId,
address to,
uint128 amount
)
internal
returns (uint128 withdrawnAmount, uint128 protocolFeeAmount)
{
...
// Check: the withdraw amount is not greater than the withdrawable amount.
if (amount > withdrawableAmount) {
revert Errors.SablierFlow_Overdraw(streamId, amount, withdrawableAmount);
}
// Calculate the amount scaled.
uint256 amountScaled = Helpers.scaleAmount(amount, tokenDecimals);
...
if (protocolFee > ZERO) {
// Calculate the protocol fee amount and the net withdraw amount.
// @audit amount is in the token decimal precision and protocolFee is in 18 decimal precision
(protocolFeeAmount, amount) = Helpers.calculateAmountsFromFee({ totalAmount: amount, fee: protocolFee }); // @FOUND
// Safe to use unchecked because addition cannot overflow.
unchecked {
// Effect: update the protocol revenue.
protocolRevenue[token] += protocolFeeAmount;
}
}
unchecked {
// Effect: update the aggregate balance.
aggregateBalance[token] -= amount;
}
// Interaction: perform the ERC-20 transfer.
token.safeTransfer({ to: to, value: amount });
...
return (amount, protocolFeeAmount);
}

}

Impact

Medium

Tools Used

Foundry

Recommendations

When calculating protocolFee and `withdraw` amount, use the Scaled amount with 18 decimal precision as protocolFee is in 18 decimal precision.

if (protocolFee > ZERO) {
// Calculate the protocol fee amount and the net withdraw amount.
- (protocolFeeAmount, amount) = Helpers.calculateAmountsFromFee({ totalAmount: amount, fee: protocolFee });
+ (protocolFeeAmount, amount) = Helpers.calculateAmountsFromFee({ totalAmount: amountScaled, fee: protocolFee });
// Safe to use unchecked because addition cannot overflow.
unchecked {
// Effect: update the protocol revenue.
protocolRevenue[token] += protocolFeeAmount;
}
}
unchecked {
// Effect: update the aggregate balance.
- aggregateBalance[token] -= amount;
+ aggregateBalance[token] -= Helpers.descaleAmount({ amount: amount, decimals: _streams[streamId].tokenDecimals });
}
// Interaction: perform the ERC-20 transfer.
token.safeTransfer({ to: to, value: amount });
Updates

Lead Judging Commences

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.