Section 15 of 18
Liquidation: Comptroller Side
What You Are Building
You are building the Comptroller's liquidation policy: the rules that decide WHEN someone can be liquidated, HOW MUCH of their debt can be repaid in one transaction, and HOW MANY collateral tokens the liquidator receives. This is the brain of the liquidation system. The next section builds the CToken execution side that carries out the actual seizure.
Uniswap V2 has no equivalent because it has no lending. Liquidation exists specifically because lending protocols allow borrowers to take on debt backed by volatile collateral. When collateral value drops below the debt, someone needs to step in and unwind the position before it creates bad debt that suppliers cannot recover.
When Liquidation Happens
A borrower becomes liquidatable when their account has shortfall. Shortfall means their total borrows exceed their risk-adjusted collateral:
For each market the borrower entered:
collateral += cTokenBalance * exchangeRate * oraclePrice * collateralFactor
borrows += borrowBalance * oraclePrice
shortfall = max(0, borrows - collateral)
If shortfall is 0, the borrower is healthy. If shortfall is positive, anyone can liquidate them. This is the same getAccountLiquidity calculation you built in section 10, now used as a gating check for liquidation.
The Close Factor: Partial Liquidation
The close factor (typically 50%) limits how much of a borrower's debt can be repaid in a single liquidation. This serves two purposes.
First, fairness. If a borrower deposits $150 of ETH and borrows $100 of USDC, and ETH drops so their collateral is worth $95, allowing a liquidator to repay all $100 of debt and seize all $95 of collateral would leave the borrower with nothing. With a 50% close factor, the liquidator can only repay $50, seizing roughly $54 of collateral (including the incentive). The borrower keeps the remaining collateral and can try to recover.
Second, security. If an attacker manipulates the oracle to make a healthy position appear underwater, the close factor limits the damage. They can only extract closeFactor percentage of the borrow, not the entire collateral.
uint256 maxClose = mul_ScalarTruncate(
Exp({ mantissa: closeFactorMantissa }),
borrowerBorrowBalance
);
require(repayAmount <= maxClose, "Comptroller: repay exceeds close factor limit");
The Seize Calculation
After a liquidator repays debt, the Comptroller calculates how many cTokens they receive. The formula converts the repaid debt value into collateral cTokens with a bonus:
seizeTokens = (repayAmount * liquidationIncentive * priceBorrowed)
/ (priceCollateral * exchangeRate)
Worked example: A liquidator repays 500 USDC of a borrower's debt.
- Dollar value of repayment: 500 USDC * $1.00 = $500
- With 8% incentive: $500 * 1.08 = $540 worth of collateral to seize
- In ETH terms: $540 / $2000 per ETH = 0.27 ETH
- In cETH terms: 0.27 ETH / 0.02 exchange rate = 13.5 cETH
The liquidator pays 500 USDC and receives 13.5 cETH (worth $540). The $40 difference is profit. This profit is what incentivizes liquidators to monitor the protocol 24/7 and act instantly when positions become underwater.
The exchange rate conversion is critical. We seize cTokens, not underlying, because seizing underlying would break the cToken accounting (totalSupply would no longer match the actual token balances).
Your Task
Build the liquidation policy functions that live in the Comptroller. These extend the hooks contract from section 11:
liquidateBorrowAllowed: Check that both markets are listed, the borrower has shortfall (is underwater), and the repay amount does not exceed closeFactor * borrowerBorrowBalanceliquidateCalculateSeizeTokens: Compute the number of cTokens to seize using oracle prices, the liquidation incentive, and the collateral market's exchange rate