Sablier

Sablier
DeFiFoundry
53,440 USDC
View results
Submission Details
Severity: low
Invalid

Precision Loss and Handling of Small Amounts issue in abbreviateAmount Function

Summary

The abbreviateAmount function attempts to abbreviate large numeric amounts by dividing them and appending appropriate suffixes. However, due to repeated division and limited handling of fractional parts, the function can lose significant precision. Additionally, it does not handle very small but non-zero amounts effectively, leading to potential misrepresentation.

Proof of concept

  1. Loss of Precision:

Imagine a financial application displaying token balances using this function. For a balance of 1,234,567,890,123,456 (1.234 quadrillion), truncating it multiple times could result in a representation that does not accurately reflect the actual value, potentially leading to incorrect financial decisions.

  1. Small Amounts Close to Zero:

Consider a scenario where a token balance is 0.0001 with 6 decimals (0.0000001 actual value). The function may truncate this to zero, displaying "< 1", which might mislead users into thinking they have no tokens, affecting their transaction decisions.

Impact

Tools Used

Manual review

Recommendations

Here is an example update to the abbreviateAmount function:

function abbreviateAmount(uint256 amount, uint256 decimals) internal pure returns (string memory) {
if (amount == 0) {
return "0";
}
uint256 truncatedAmount;
unchecked {
truncatedAmount = decimals == 0 ? amount : amount / 10 ** decimals;
}
// Return dummy values when the truncated amount is either very small or very big.
if (truncatedAmount < 1) {
return string.concat(SVGElements.SIGN_LT, " 1");
}
// Handle very large amounts dynamically.
string memory suffix;
uint256 suffixIndex = 0;
uint256 fractionalAmount;
while (truncatedAmount >= 1000) {
fractionalAmount = (truncatedAmount / 10) % 100; // Keep the first two digits after the decimal point
truncatedAmount /= 1000;
suffixIndex += 1;
}
// Define suffixes dynamically to handle amounts beyond trillions.
if (suffixIndex == 0) {
suffix = "";
} else if (suffixIndex == 1) {
suffix = "K";
} else if (suffixIndex == 2) {
suffix = "M";
} else if (suffixIndex == 3) {
suffix = "B";
} else if (suffixIndex == 4) {
suffix = "T";
} else {
// Use scientific notation for amounts beyond trillions.
return string.concat(">= 1e", (suffixIndex * 3).toString());
}
// Concatenate the calculated parts to form the final string.
string memory prefix = string.concat(SVGElements.SIGN_GE, " ");
string memory wholePart = truncatedAmount.toString();
string memory fractionalPart = stringifyFractionalAmount(fractionalAmount, 2); // Allow up to 2 decimal places
return string.concat(prefix, wholePart, fractionalPart, suffix);
}
function stringifyFractionalAmount(uint256 fractionalAmount, uint256 precision) internal pure returns (string memory) {
// Return the empty string if the fractional amount is zero.
if (fractionalAmount == 0) {
return "";
}
// Add leading zeros if necessary.
string memory fractionalString = fractionalAmount.toString();
while (bytes(fractionalString).length < precision) {
fractionalString = string.concat("0", fractionalString);
}
return string.concat(".", fractionalString);
}

Explanation of the updated function

  • The stringifyFractionalAmount function now allows for up to 2 decimal places, ensuring that fractional amounts are represented accurately.

  • The check for truncatedAmount < 1 ensures that very small amounts are handled appropriately by returning a value with the "less than" sign.

  • The function now dynamically handles very large amounts by using scientific notation for amounts beyond trillions, ensuring that it can represent extremely large values accurately.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Info/Gas/Invalid as per Docs

https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity

Support

FAQs

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