> For the complete documentation index, see [llms.txt](https://docs.curvance.com/app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.curvance.com/app/developer-docs/quick-start-guides/liquidations.md).

# Liquidations

## Quick Start: Liquidations

### Overview

Liquidations are the mechanism that keeps Curvance solvent. When a borrower's position becomes undercollateralized, third parties (liquidators) can repay part or all of the debt in exchange for seizing the borrower's collateral at a discount. Liquidators are compensated for the service; the protocol avoids bad debt accumulation.

***

### How Liquidations Work

#### Collateral requirements

Each collateral-capable cToken is configured with a collateral ratio plus two liquidation thresholds:

* **Soft requirement:** the adjusted collateral value where a position first becomes partially liquidatable.
* **Hard requirement:** the adjusted collateral value where standard protocol-derived liquidation can close the full debt position and may realize bad debt if posted collateral is insufficient.

Read the values for a given market with `marketManager.collConfig(cToken)`, which returns `(collRatio, collReqSoft, collReqHard)`. These values are BPS-denominated. `collReqSoft` and `collReqHard` are stored as `BPS + configuredPremium`, so integrations should read the returned values rather than reconstructing them from deployment inputs.

#### Health factor (`lFactor`)

| lFactor           | Meaning          | Behaviour                                                          |
| ----------------- | ---------------- | ------------------------------------------------------------------ |
| `0`               | Healthy          | Not liquidatable                                                   |
| `1` ... `WAD - 1` | Soft liquidation | Partial liquidation allowed; size and penalty scale with `lFactor` |
| `WAD` (`1e18`)    | Hard liquidation | Full position can be closed; can realize bad debt                  |

`lFactor` is computed as `mulDivUp(debt - cSoft, WAD, cHard - cSoft)` when the debt lands between the soft and hard thresholds (`LiquidityManagerIsolated.sol:575`). It interpolates linearly from "just crossed soft" to "at hard."

#### Dynamic close factor and liquidation incentive

* **Close factor:** when no auction-specific close factor is supplied, the protocol computes `closeFactor = closeFactorBase + (closeFactorCurve * lFactor / WAD)`. `closeFactorCurve` is stored as `BPS - closeFactorBase`, so a standard hard liquidation reaches a 100% close factor.
* **Liquidation incentive:** when no auction-specific incentive is supplied, the protocol computes `liqInc = liqIncBase + (liqIncCurve * lFactor / WAD)`. `liqIncBase` is stored as `BPS + baseIncentive`, and `liqIncCurve` is the distance from the base incentive to the hard-liquidation incentive.

Read the stored liquidation parameters with `marketManager.liquidationConfig(cToken)`. They are configured per cToken inside a market through `updateTokenConfig`; auction flows can temporarily supply a bounded incentive or close factor with `setTransientLiquidationConfig`, while passing zero uses protocol-derived values.

***

### When Positions Become Liquidatable

A position crosses into `lFactor > 0` when any of these hold:

* Collateral value falls relative to debt (collateral price drop).
* Debt asset value rises relative to collateral (debt price spike).
* Interest accrues on debt, pushing the position past the soft requirement.
* Any combination of the above.

***

### Two Types of Liquidations

#### Non-Auction Liquidations

Standard liquidations are permissionless when market-wide liquidations are not paused and the account has a repayable liquidation amount. Without an active auction unlock, the MarketManager uses the protocol-derived close factor and liquidation incentive from the current `lFactor`.

#### Auction-Enabled Liquidations

Auction-enabled liquidations apply only inside an authorized auction flow. The auction setup stores an unlocked collateral cToken plus optional transient incentive and close-factor values, then unlocks the market for the transaction. The MarketManager applies `AUCTION_BUFFER` only when both the market is unlocked and the requested collateral token matches the unlocked cToken; otherwise the attempted auction liquidation reverts.\
\
`AUCTION_BUFFER` discounts `cSoft` and `cHard`, so auction calculations can cross the soft threshold earlier or produce a higher `lFactor` than the non-auction path. The buffer is 9990 for correlated markets and 9950 for uncorrelated markets. Auction-specific incentive and close-factor values are bounded by each cToken's configured min/max values; zero means the protocol derives that value from `lFactor`.

***

### Liquidator Workflow

1. **Discover liquidatable accounts** by indexing position and debt activity, including `PositionUpdated`, `Borrow`, `Repay`, `CollateralUpdated`, and `Liquidated`. Account health can also change because oracle prices move or interest accrues, so event indexing should be paired with current reads.<br>

   For screening, use `ProtocolReader.liquidationValuesOf(marketManager, account, false)` for the standard path or `true` for the auction-buffered path. It returns `(cSoft, cHard, debt, lFactor, errorCodeHit)`. Treat that as a read-side screen, then simulate the liquidation entrypoint or `canLiquidate` before submitting a transaction.
2. **Validate via `canLiquidate`.** Get exact amounts, seized shares, and any bad-debt realization before committing to a tx.
3. **Execute** `liquidate()` (max amount) or `liquidateExact()` (caller-specified per-account amounts) on the debt cToken.
4. **Receive collateral.** Seized collateral shares are transferred directly to the liquidator's address.

#### Entrypoints

```solidity
// BorrowableCToken.sol:333 - liquidate as much as possible across all accounts
function liquidate(
    address[] calldata accounts,
    address collateralToken
) external;

// BorrowableCToken.sol:306 - liquidate caller-specified amounts per account
function liquidateExact(
    uint256[] calldata debtAmounts,
    address[] calldata accounts,
    address collateralToken
) external;
```

{% hint style="warning" %}
**Critical non-obvious requirement:** `accounts` **must be sorted in strictly ascending address order with no duplicates**. `canLiquidate` reverts with `MarketManager__Unauthorized()` when `priorAccount >= cachedAccount`, so sorting and deduping must happen client-side.

Self-liquidation is also blocked. If `liquidator == accounts[i]`, `canLiquidate` reverts with `MarketManager__Unauthorized()`.
{% endhint %}

**Self-liquidation is blocked.** `liquidator == accounts[i]` reverts (`:598`). You cannot liquidate yourself.

#### Pre-liquidation check via `canLiquidate`

`canLiquidate` at `MarketManagerIsolated.sol:569` is the primary off-chain helper. It's not a `view` function (it reads mutable state during price accrual) but can be simulated via `staticCall`:

```js
const debtAmounts = Array(accounts.length).fill(0n);

const [result, adjustedDebtAmounts] =
  await marketManager.canLiquidate.staticCall(
    debtAmounts,
    liquidatorAddress,
    accounts,
    {
      collateralToken: COLLATERAL_CTOKEN,
      debtToken: DEBT_CTOKEN,
      numAccounts: BigInt(accounts.length),
      liquidateExact: false,
      liquidatedShares: 0n,
      debtRepaid: 0n,
      badDebt: 0n,
    }
  );
```

For maximum liquidation through `canLiquidate`, pass a zero-filled `debtAmounts` array with the same length as `accounts` and set `liquidateExact: false`. For exact liquidation, pass one desired debt amount per account and set `liquidateExact: true`. The helper returns a tuple, not an object with `result` and `remaining` fields.

If you want the closest end-to-end simulation of the public liquidation path, simulate `debtCToken.liquidate.staticCall(accounts, collateralToken)` or `debtCToken.liquidateExact.staticCall(debtAmounts, accounts, collateralToken)`. Those paths run the debt cToken's pre-liquidation accrual and then call `marketManager.canLiquidate`.

`result` contains `liquidatedShares[]` (collateral shares you'd receive per account), total `debtRepaid`, and any `badDebtRealized`. Use this to compute expected profit after gas.

***

### Key Considerations

* **Account discovery:** index `PositionUpdated`, `Borrow`, `CollateralUpdated`, and `Repay` events. Cross-reference with a fresh `lFactor` read via `liquidationValuesOf(account, auctionBuffer=0)` before committing to a liquidation tx.
* **Profitability:** `liquidatedShares` are cToken shares, so convert seized shares through the current exchange rate and collateral price before comparing against `debtRepaid`, swap costs, flash-loan costs, and gas. In standard protocol-derived liquidations, the incentive increases with `lFactor`, but profit is not guaranteed. For `liquidateExact`, the call reverts if the requested debt amount exceeds the computed max debt or if the resulting seized shares exceed the borrower's posted collateral.
* **Smart contracts over scripts:** a Solidity liquidator contract can atomically check profitability and revert if conditions change mid-tx. Off-chain scripts can't guarantee atomicity; a competing liquidator's tx landing first can either empty the position or change pricing state.
* F**lash loans:** a liquidator contract can use `flashLoan` on the debt `BorrowableCToken` to source the repayment asset. The callback is `onFlashLoan(assets, assetsReturned, data)`. Inside the callback, execute the liquidation, convert seized collateral as needed, and end with enough debt asset plus allowance for the cToken to pull `assetsReturned`. See Flash Loans.
* **Competition:** popular liquidation opportunities are MEV-competitive. Optimize for speed (direct node RPC, private mempools), gas efficiency, and atomic tx design.
* **Oracle gating:** liquidation pricing asks the Oracle Manager for the collateral cToken share price and the debt cToken underlying price with `BAD_SOURCE` as the error-code breakpoint. If either side reaches that breakpoint, the oracle call reverts and the liquidation does not proceed. Lower-severity `CAUTION` responses do not block this path.

***


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.curvance.com/app/developer-docs/quick-start-guides/liquidations.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
