The initiateLiquidation and finalizeLiquidation functions are designed to handle the liquidation process for users whose health factor falls below a certain threshold. However, there are several issues in the implementation that can lead to incorrect behavior, potential economic losses, and vulnerabilities. Below is a detailed analysis of the issues.
Incorrect Health Factor Check:
The function checks if healthFactor >= healthFactorLiquidationThreshold to determine if liquidation should be initiated.
This logic is backwards. Liquidation should be initiated when the health factor is below the threshold, not above it.
Impact: Users who should be liquidated will not be liquidated, leading to under-collateralized positions and potential losses for the protocol.
Lack of Access Control:
The function does not restrict who can call it. Any external address can initiate liquidation for any user.
Impact: Malicious actors could abuse this function to initiate unnecessary liquidations, causing disruption and potential losses for users.
Missing Validation for userAddress:
The function does not validate whether userAddress is a valid user (e.g., whether userData[userAddress] exists).
Impact: Calling the function with an invalid address could lead to unexpected behavior or wasted gas.
Issue 1: The health factor check is incorrect. It should be healthFactor < healthFactorLiquidationThreshold.
Issue 2: No access control is applied to restrict who can call this function.
Issue 3: No validation is performed to ensure userAddress is a valid user.
finalizeLiquidationIncorrect Grace Period Check:
The function checks if block.timestamp <= liquidationStartTime[userAddress] + liquidationGracePeriod to determine if the grace period has expired.
This logic is backwards. The liquidation should proceed only if the grace period has expired (i.e., block.timestamp > liquidationStartTime[userAddress] + liquidationGracePeriod).
Impact: Liquidations will fail if the grace period has expired, allowing under-collateralized positions to persist.
Lack of Validation for userAddress:
Similar to initiateLiquidation, the function does not validate whether userAddress is a valid user.
Impact: Calling the function with an invalid address could lead to unexpected behavior or wasted gas.
Incorrect Debt Burning Logic:
The function burns the user's debt using IDebtToken(reserve.reserveDebtTokenAddress).burn(userAddress, userDebt, reserve.usageIndex).
However, the amountBurned returned from the burn function is not validated to ensure it matches the expected userDebt.
Impact: If the burn function does not burn the full debt, the user's scaledDebtBalance will not be updated correctly, leading to incorrect accounting.
Incorrect Asset Transfer Logic:
The function transfers reserve assets from the msg.sender (Stability Pool) to the reserve.reserveRTokenAddress using safeTransferFrom.
However, it does not ensure that the msg.sender has sufficient balance or allowance to cover the transfer.
Impact: The transfer could fail, causing the liquidation to revert and leaving the protocol with under-collateralized positions.
Incorrect Update of scaledDebtBalance:
The function subtracts amountBurned from user.scaledDebtBalance, but it does not ensure that amountBurned is less than or equal to user.scaledDebtBalance.
Impact: If amountBurned exceeds user.scaledDebtBalance, the subtraction will underflow, causing the transaction to revert.
Issue 1: The grace period check is incorrect. It should be block.timestamp > liquidationStartTime[userAddress] + liquidationGracePeriod.
Issue 2: No validation is performed to ensure userAddress is a valid user.
Issue 3: The burn function's output is not validated to ensure the full debt is burned.
Issue 4: The safeTransferFrom function does not check the msg.sender's balance or allowance.
Issue 5: The subtraction user.scaledDebtBalance -= amountBurned could underflow if amountBurned > user.scaledDebtBalance.
initiateLiquidation:Fix Health Factor Check:
Change the condition to if (healthFactor >= healthFactorLiquidationThreshold) to if (healthFactor < healthFactorLiquidationThreshold).
Add Access Control:
Restrict the function to authorized roles (e.g., onlyStabilityPool or onlyLiquidator).
Validate userAddress:
Add a check to ensure userData[userAddress] exists.
finalizeLiquidation:Fix Grace Period Check:
Change the condition to if (block.timestamp <= liquidationStartTime[userAddress] + liquidationGracePeriod) to if (block.timestamp > liquidationStartTime[userAddress] + liquidationGracePeriod).
Validate userAddress:
Add a check to ensure userData[userAddress] exists.
Validate Debt Burning:
Ensure amountBurned matches userDebt or handle partial burns appropriately.
Check msg.sender Balance and Allowance:
Add checks to ensure msg.sender has sufficient balance and allowance for the transfer.
Prevent Underflow:
Add a check to ensure amountBurned <= user.scaledDebtBalance.
The initiateLiquidation and finalizeLiquidation functions contain several critical issues, including incorrect health factor and grace period checks, lack of access control, and improper debt burning logic. By addressing these issues, the liquidation process will function as intended, ensuring the protocol's stability and fairness.
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.