Liquidation

📖 Liquidation Mechanism in Centuari Protocol

In the Centuari protocol, the liquidation mechanism serves as a risk management tool to maintain the health of the lending system. It allows third-party liquidators to close borrower positions under specific conditions:

  • The loan has passed its maturity date, or

  • The borrower's position has fallen below the required health factor.

Liquidators repay the borrower’s outstanding debt and, in return, receive the borrower’s collateral as compensation.


Liquidation flow

📌 Liquidation Execution Flow

� Liquidation Flow (General Overview)

In Centuari Protocol, liquidation is a process that helps maintain the stability and safety of the lending system. It occurs when a borrower fails to meet the agreed conditions—either because the loan has matured without repayment, or because the value of their collateral is no longer sufficient compared to their outstanding debt.

The liquidation process follows these general steps:

  1. Eligibility Check The system checks whether the borrower is eligible for liquidation based on maturity or collateral health.

  2. Position Assessment The protocol reviews the borrower’s loan and collateral details to determine how much debt is owed and what assets are available.

  3. Debt Calculation The borrower’s total outstanding debt is calculated proportionally based on their share of the lending pool.

  4. Position Closure The borrower’s position is closed, meaning their loan and collateral records are cleared from the system.

  5. Asset Settlement A third-party liquidator repays the borrower’s debt on their behalf. In return, the liquidator receives the borrower’s full collateral as compensation.

  6. Transparency Logging The liquidation event is recorded on-chain to ensure transparency and enable tracking by external services and users.

This process ensures that the protocol remains solvent and fair for all participants, especially lenders, by promptly resolving risky or overdue positions.

The liquidate() function handles the liquidation process through the following steps:


📌 Liquidation Execution Flow (Technical Details)

1 Validation of Borrower and Conditions

Before proceeding, the function validates:

  • That the borrower’s address is not zero.

  • That the borrower’s position is eligible for liquidation:

    • The maturity date has passed (if a maturity is set), or

    • The borrower's health factor is below the allowed threshold.

Example:

function liquidate(MarketConfig memory config, uint256 amount, uint256 maturity, address user)
    external
    nonReentrant
    onlyActiveMaturity(config.marketId(), maturity)
    whenNotPaused

Parameters

  • config: MarketConfig struct containing market-specific settings (e.g., marketId).

  • amount: Amount of debt (in loan tokens) to be repaid by the liquidator.

  • maturity: Timestamp of the position’s maturity.

  • user: Address of the user whose position is being liquidated.

Position Assessment The protocol reviews the borrower’s loan and collateral details to determine how much debt is owed and what assets are available.


2. Retrieve User Position

  • Queries IDataStore for the market (dataStores[config.marketId()]):

    • userBorrowValue: User’s total borrowed value (in loan tokens).

    • userCollateral: User’s total collateral (in collateral tokens).

3. Validate Liquidation Amount

  • Reverts with CentuariErrorsLib.InsufficientBorrowValue if amount > userBorrowValue.


4. Calculate Penalty

  • Fetches MarketRiskConfig from RISK_MANAGER.

  • Computes:

uint256 amountWithPenalty = amount + ((amount * riskConfig.lp) / PERCENTAGE_DENOMINATOR);
  • where lp is the liquidation penalty (in basis points, e.g., 500 for 5%).


5. 📈 Fetch Token Prices

  • Retrieves collateralToken and loanToken from dataStore.

  • Calls ORACLE_MANAGER.getPrice(collateralToken, loanToken) to get collateralPrice.


6. 🧾 Calculate Collateral Allocation

  • Calculates:

    totalCollateralToLiquidator = amountWithPenalty / collateralPrice;
    badDebt = int256(userCollateral) - int256(totalCollateralToLiquidator);
  • Derives:

    UserBorrowValue = userBorrowValue - amount;
    remainingUserCollateral = badDebt < 0 ? 0 : userCollateral - totalCollateralToLiquidator;
    collateralToLiquidator = badDebt < 0 
        ? totalCollateralToLiquidator - uint256(-badDebt) 
        : totalCollateralToLiquidator;

7. 🧑‍ Update User Position

  • Writes updated values to dataStore:

    • setUserBorrowValue

    • setUserCollateral

8. 💸 Execute Token Transfers

  • Transfers:

    • Loan tokens from liquidator to contract using IERC20.safeTransferFrom.

    • Collateral tokens from contract to liquidator using IERC20.safeTransfer.

9. 📢 Emit Event

Emits CentuariEventsLib.Liquidate.


📊 Example Liquidation Scenario

🧱 Assumptions

  • Borrower A:

    • Has borrowed 500 USDC.

    • Has deposited 1 WBTC as collateral.

    • Holds 5 borrow shares out of 50 total shares in the lending pool.

  • Total Pool Debt: 5,000 USDC

  • Oracle Price:

    • Original WBTC price: 25,000 USDC (healthy position)

    • Price drops to: 800 USDC per WBTC (position becomes undercollateralized)

  • Liquidation Penalty: 5% (500 basis points)

  • Maturity: Not yet reached (liquidation subject to eligibility check)

🧮 Borrower’s Debt Calculation

Borrowed amount is determined by share:

borrowedAmount = (5 / 50) * 5,000 = 500 USDC

⚠️ Liquidation Eligibility

  • Collateral Value = 1 WBTC × 800 USDC = 800 USDC

  • Debt = 500 USDC

  • Collateral Ratio = 800 / 500 = 160%

  • If the protocol’s minimum collateral ratio is, say, 170%, then: → Borrower A is eligible for liquidation

🔥 Liquidation Triggered by Liquidator (e.g., Bob)

The liquidator calls:

liquidate(config, 500e6, maturity, borrowerA)

Where:

  • config: Contains market ID and risk settings

  • amount: 500 USDC

  • user: Borrower A

  • maturity: Active maturity timestamp

💰 Penalty and Collateral Calculation

  1. Penalty Calculation:

penalty = 500 * 5% = 25 USDC
amountWithPenalty = 500 + 25 = 525 USDC
  1. Oracle Price (WBTC):

collateralPrice = 800 USDC per WBTC
  1. Collateral Transferred to Liquidator:

totalCollateralToLiquidator = 525 / 800 = 0.65625 WBTC
  1. Remaining User Position:

remainingBorrow = 0 USDC
remainingCollateral = 1 WBTC - 0.65625 WBTC = 0.34375 WBTC
  1. Bad Debt Calculation:

badDebt = 1 - 0.65625 = 0.34375 → no bad debt (value is still positive)

🔁 Token Transfers

  • Liquidator transfers 500 USDC to the protocol.

  • Protocol transfers 0.65625 WBTC to the liquidator.

  • Borrower A's position is updated:

    • Borrowed amount: 0

    • Collateral: 0.34375 WBTC


✨ Key Advantages of This Design

Open Liquidation System: Anyone can act as a liquidator and claim collateral from unhealthy positions. ✅ Share-Based Accounting: Scalable and dynamic, ensuring fair debt and collateral calculations. ✅ Event Logging: Transparent on-chain event emission enables robust tracking and off-chain integrations. ✅ Market-Specific Configurations: Liquidation eligibility and parameters are determined per-market basis.


📌 Summary

The Centuari protocol’s liquidation mechanism is designed to:

  • Secure the protocol against bad debt

  • Maintain lending pool health

  • Reward third-party liquidators

  • Protect the interests of other lenders in the pool

It operates through a secure, event-driven, share-based system that balances flexibility and protocol integrity.

Last updated