The contract performs several calculations related to token amounts and their USD values, and if the tokens have varying decimal lengths, it can lead to incorrect results.
The issue arises due to the difference in token precision caused by varying decimal lengths. For example, if one token has 18 decimal places (like most ERC-20 tokens) and another token has fewer decimal places, say 6, performing direct arithmetic operations between them could result in precision loss and inaccurate calculations.
For instance, when converting token amounts to USD values or calculating ratios, the contract relies on mathematical operations that may not consider the varying decimal lengths, leading to potential miscalculations.
The Chainlink USD price feeds return values with 8 decimal places represented as int256.
The DSCEngine contract fails to consider tokens with decimal places other than 18 when calculating collateral values and the health factor. This oversight poses potential risks, especially when dealing with tokens with varying decimal lengths. Here are instances where this issue occurs:
This function is used to convert USD values to token amounts for a given token. However, the calculation assumes that both USD and token values have 18 decimal places, which may not be true for all tokens. If tokens with a different number of decimals are involved, this calculation can lead to inaccurate results.
This function calculates the USD value of a given token amount using the token's price obtained from an external Chainlink price feed. However, similar to the previously mentioned functions, the calculation assumes that both the token price and the token amount have 18 decimal places. This can lead to incorrect USD value calculations for tokens with a different number of decimals.
This function calculates the total collateral value in USD for a specific user by looping through each collateral token deposited by the user. However, the calculation may not account for tokens with different decimal lengths, potentially leading to incorrect total collateral value.
The vulnerability revolves around the health factor calculation performed by the _healthFactor()
and _calculateHealthFactor()
functions. These functions are crucial for determining the financial health of a user's account within the DSCEngine
contract, particularly when assessing whether the account is at risk of liquidation.
The vulnerability arises because the health factor calculation does not account for tokens with different decimal lengths. Both functions assume that all token values, including the collateral and DSC amounts, have 18 decimal places. However, some tokens may have a different number of decimals, leading to potential inaccuracies in the health factor calculation.
The liquidate function involves calculations related to debt, collateral value, and liquidation bonus, which may not consider the varying decimal lengths of tokens involved. This can result in incorrect liquidation outcomes.
The vulnerability in the DSCEngine contract, arising from the failure to account for tokens with different decimal lengths, can have significant consequences on the system's stability and security. The inaccurate health factor calculations may lead to improper decisions regarding minting, collateralization, and liquidation of DSC tokens, potentially resulting in financial losses, system instability, and an increased risk of liquidation events. Addressing this vulnerability is crucial to ensure accurate and reliable assessments of user account health, maintaining the stability and integrity of the decentralized stablecoin system. Below are different events that may occur:
User A has deposited 10.5 tokens of "TokenX" as collateral, where "TokenX" has 6 decimal places.
User A has minted 500 DSC tokens, where DSC has 18 decimal places.
The contract calculates the collateral value in USD by using getUsdValue(token, amount) and assumes both the collateral and DSC have 18 decimal places.
Since "TokenX" has 6 decimal places, the calculated collateral value will be 10.5 USD instead of the correct value.
As a result, the health factor will be inaccurately calculated, potentially leading to incorrect decisions regarding liquidation or minting, and posing risks to the stability of the system.
User B has deposited 1000 "TokenY" as collateral, where "TokenY" has 8 decimal places.
User B has minted 1000 DSC tokens, where DSC has 18 decimal places.
The contract calculates the health factor using _calculateHealthFactor(totalDscMinted, collateralValueInUsd).
Due to the different decimal lengths, the health factor is incorrectly computed, leading the contract to believe that User B's account is sufficiently collateralized.
In reality, User B's collateral value might be below the liquidation threshold, putting the system at risk if it does not take the correct liquidation actions.
Multiple users with tokens of different decimal lengths participate in the system.
The health factor calculation consistently fails to account for the varying decimal lengths.
The inaccurate health factor calculations lead to improper assessments of user account health, resulting in over-minting or under-collateralization of DSC tokens.
Over time, this can lead to system instability, potential losses, and a higher likelihood of liquidation events.
VSCode, Foundry
To ensure accurate calculations in the contract, it's essential to handle token amounts and conversions correctly, taking into account the different decimal lengths. This can be achieved by normalizing token amounts to a common precision level, like 18 decimal places, before performing any arithmetic operations involving different tokens. Additionally, using libraries or built-in functions specifically designed for handling token arithmetic can help prevent precision loss and ensure accurate calculations regardless of the token decimal lengths.
Implementing proper handling of token amounts with varying decimal lengths is crucial to maintain the integrity of the contract's functionality and prevent potential calculation errors. Below is an example of how this can be mitigated:
If a token has 6 decimal places, multiply its amount by 1e12 (10^12) to adjust it to 18 decimal places.
If a token has 8 decimal places, multiply its amount by 1e10 (10^10) to adjust it to 18 decimal places.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.