15,000 USDC
View results
Submission Details
Severity: high

Users can potentially bypass liquidation by depositing collateral without initiating a minting operation and having the ability to redeem the collateral at any time

Summary

  • I'm sorry, as this is my first time participating and submitting a report, I wasn't sure if I described it clearly enough.

  • Users can potentially bypass liquidation by depositing collateral without performing the minting operation and redeeming the collateral at any time. It's simple because the quantity of the collateral keeps increasing while the quantity of DSC remains constant. Therefore, users can potentially bypass liquidation.

  • I understand that you might say "this method is meant to avoid liquidation." However, I still think there are some issues here.

    1. The existence of depositCollateralAndMintDsc may not be necessary since users can perform separate operations of depositCollateral and mintDsc.

    2. The functionality of liquidate may not be useful for users with substantial funds, as they can always ensure their DSC tokens are not liquidated.

Vulnerability Details

  1. Users can use the functionality of the depositCollateral function to input the whitelisted collateral address tokenCollateralAddress and the collateral amount amountCollateral. -->> Assuming 1 collateral is deposited, with the whitelisted collateral address being 0x01.

  2. The deposited amountCollateral is stored in the s_collateralDeposited mapping. Referencing the code, s_collateralDeposited[msg.sender][tokenCollateralAddress] += amountCollateral; -- >> So, the current user has deposited 1 collateral with the whitelisted collateral address 0x01, meaning s_collateralDeposited = 1.

  3. This s_collateralDeposited value affects the amount of collateral the user has on that collateral in the getAccountCollateralValue function. Referencing the code: uint256 amount = s_collateralDeposited[user][token]; -->> Since s_collateralDeposited = 1, the value of amount is 1.

  4. The amount in the getAccountCollateralValue function is then passed into the getUsdValue function to obtain the total collateral value in USD. Referencing the code: totalCollateralValueInUsd += getUsdValue(token, amount);. -- >> Since amount = 1, the getUsdValue function calculates the USD value of each collateral and accumulates it into totalCollateralValueInUsd.

  5. Going deeper into the getUsdValue function, the current price is obtained from the Chainlink oracle, and the value of the current collateral is calculated. Referencing the code: ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION; -->> Assuming uint256 = $1000e8, the calculation is (1000e8 * 1e10) * 1 / 1e18 = $1000. This means that the value of the collateral ETH is equal to the value of the stablecoin DSC, with a 1:1 ratio. So, going back to the getAccountCollateralValue function, we have totalCollateralValueInUsd = $1000.

  6. Now, the current user's totalCollateralValueInUsd is $1000. Based on the information from the getAccountCollateralValue function, the _getAccountInformation function calls the getAccountCollateralValue function to obtain collateralValueInUsd. Referencing the code: collateralValueInUsd = getAccountCollateralValue(user); ==>> So, in the _getAccountInformation function, collateralValueInUsd = $1000, and totalDscMinted = 1.

  7. Continuing to trace the code, we find that the _healthFactor function calls _getAccountInformation to retrieve the values collateralValueInUsd and totalDscMinted. Referencing the code: (uint256 totalDscMinted, uint256 collateralValueInUsd) = _getAccountInformation(user); -->> So, in the _healthFactor function, the variable values are totalDscMinted = 1 and collateralValueInUsd = $1000.

  8. Going further into the _healthFactor function, it calls the _calculateHealthFactor function and passes totalDscMinted = 1 and collateralValueInUsd = $1000. Referencing the code: return _calculateHealthFactor(totalDscMinted, collateralValueInUsd); -->> _calculateHealthFactor(1, 1000)

  9. Entering the _calculateHealthFactor function, we calculate the value of collateralAdjustedForThreshold and use that value to calculate the health factor.

    1. To calculate collateralAdjustedForThreshold, referencing the code: uint256 collateralAdjustedForThreshold = (collateralValueInUsd * LIQUIDATION_THRESHOLD) / LIQUIDATION_PRECISION; In this case: (1000 * 50) / 100 = 500.

    2. To calculate the health factor, referencing the code: return (collateralAdjustedForThreshold * 1e18) / totalDscMinted; In this case: (500 * 1e18) / 1 = 500e18 -->> So, the final return value of the _healthFactor function is 500e18.

  10. After obtaining the health factor from the _healthFactor function, it will be checked using the _revertIfHealthFactorIsBroken function. Referencing the code:

    uint256 userHealthFactor = _healthFactor(user);
    if (userHealthFactor < MIN_HEALTH_FACTOR) {
    revert DSCEngine__BreaksHealthFactor(userHealthFactor);
    }
  11. OK, now we can use the deposited $1000 ETH to call the mintDsc function and mint $500 DSC. So, our account now has $1000 ETH (depositCollateral(0x1,1000)) and $500 DSC (mintDsc(500)).

  12. Here's the important part! If the value of $1000 ETH decreases, then the $500 DSC will be at risk of being liquidated. At this point, the user can prevent liquidation by calling the depositCollateral(0x1,1000) function. (PS: After minting, you can call the depositCollateral function again to deposit collateral. This is not for minting more tokens but to avoid liquidation.) So now, our account has $2000 ETH and $500 DSC, which significantly reduces the risk of liquidation.

  13. In conclusion, users can avoid liquidation in this way, and when the value of the collateral increases, they can profit by using the redeemCollateral function to redeem their DSC tokens at any time.

Tools Used

  • Manual Review

impact

  1. Users can deposit collateral without using it to mint DSC tokens and then quickly redeem it, achieving the effect of virtual collateral. This will indirectly impact the conditions for forced liquidation.

  2. When the price of the collateral rises, users can profit by redeeming it in a timely manner. When the price falls, they can continue to hold this virtual collateral (depositing collateral without using it to mint DSC tokens).

Recommendations

  1. Set depositCollateral as private and allow users to directly use the depositCollateralAndMintDsc function, which is used for depositing collateral and minting DSC tokens simultaneously. This prevents users from only depositing collateral without minting DSC tokens, thereby bypassing the liquidation mechanism.

Support

FAQs

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