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.
The existence of depositCollateralAndMintDsc
may not be necessary since users can perform separate operations of depositCollateral
and mintDsc
.
The functionality of liquidate
may not be useful for users with substantial funds, as they can always ensure their DSC tokens are not liquidated.
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
.
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
.
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.
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
.
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
.
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
.
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
.
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)
Entering the _calculateHealthFactor
function, we calculate the value of collateralAdjustedForThreshold
and use that value to calculate the health factor.
To calculate collateralAdjustedForThreshold
, referencing the code: uint256 collateralAdjustedForThreshold = (collateralValueInUsd * LIQUIDATION_THRESHOLD) / LIQUIDATION_PRECISION;
In this case: (1000 * 50) / 100 = 500
.
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
.
After obtaining the health factor from the _healthFactor
function, it will be checked using the _revertIfHealthFactorIsBroken
function. Referencing the code:
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)
).
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.
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.
Manual Review
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.
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).
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.
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.