18 Decimal Assumption Scaling: Loss of Precision in Asset Conversion Due to Incorrect Scaling

In Solidity and other smart contract systems, precision loss is a common issue that can occur when performing calculations involving assets with different decimal precisions. This type of vulnerability arises when the precision of calculations is reduced prematurely, often due to dividing by or multiplying by scaling factors like 1e18 (which is commonly used for standard ERC20 tokens with 18 decimals). If not handled correctly, this precision loss can result in users losing portions of their funds.

This tutorial explores how precision loss occurs when converting between assets with varying decimal places and provides a solution to mitigate the issue.


The Vulnerability: Precision Loss During Asset Conversion

In many financial smart contracts, when users deposit or withdraw assets, the contract needs to convert the asset amount into shares or another internal unit of measurement. This conversion is typically based on the ratio of total assets to the number of shares or other internal units.

However, if the asset being converted has a decimal precision that is less than 18 (such as Bitcoin with 8 decimals), the calculations can lose precision when dividing by or multiplying by scaling factors like 1e18. This leads to the loss of small fractions of the user's funds, which may seem negligible at first but can add up, especially when high-value tokens are involved.

Example of the Problem

Consider a scenario where the smart contract uses the following formula to calculate shares:

shares = (assetAmount * totalShares) / totalAssets;

If the token involved has fewer than 18 decimals, say 8 decimals like Bitcoin (BTC), precision loss can occur when dividing and multiplying by scaling factors such as 1e18.

For example:

  1. Initial Setup:

    • totalAssets = 1000

    • totalShares = 1e13 (scaled to match 18 decimals)

    • assetAmount = 0.0009 BTC (which is represented as 0.0009e8)

  2. Calculation: Using the formula:

shares = (assetAmount * totalShares) / totalAssets;
shares = (0.0009e8 * 1e13) / 1000;

The result would be 900000000000000 shares, which is correct.

However, if there's an unnecessary scaling operation (such as dividing and then multiplying by 1e18), you could end up with:

shares = (0.0009e8 * 1e13 / 1e18) * 1e18 / totalAssets;

This results in the value being rounded down to zero, causing the user to lose their deposit value

Impact of the Vulnerability

When precision is lost, users can lose a significant amount of value, particularly when dealing with high-value assets like Bitcoin (BTC) or Wrapped Bitcoin (WBTC). For example, with Bitcoin valued at around $40,000, the loss of even a small fraction due to precision loss can result in real monetary damage.

If the smart contract allows deposits of low decimal tokens like BTC, users could see their deposits being "rounded down" to zero, effectively losing their funds.


Mitigation: Avoid Premature Scaling and Division

The root cause of this vulnerability is performing scaling operations (like dividing by 1e18) too early in the calculation, which leads to rounding errors and precision loss. The recommended solution is to avoid scaling unnecessarily and to use higher-precision types for calculations.

Conclusion

Precision loss during asset conversion is a critical issue that can cause users to lose small but valuable amounts of their deposited funds. This issue becomes especially significant when working with tokens that have fewer than 18 decimals, such as Bitcoin or other high-value assets.

To mitigate this vulnerability:

  • Avoid premature scaling (such as dividing by 1e18).

  • Use higher-precision calculations to ensure the correct result.

By following these best practices, developers can ensure that their smart contracts handle asset conversions accurately and prevent users from losing value due to precision loss.

Last updated