🎓 A1Learner
🔍
0 of 45 lessons completed

Solidity Liquidation Mechanics

Liquidation is the forced repayment of an unhealthy loan by a third party. When a position health factor falls below 1, anyone may repay part of the debt and take collateral at a discount.

A healthy position has a health factor above 100

Collateral of 100 at a price of 100 is worth 10000, against a debt of 5000. The health factor is 160, so the position is healthy.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract LendingPosition {
    address public owner;
    uint256 public collateral;
    uint256 public debt;
    uint256 public price;
    mapping(address => uint256) public liquidatorCollateral;

    constructor(uint256 collateral_, uint256 debt_, uint256 price_) {
        owner = msg.sender;
        collateral = collateral_;
        debt = debt_;
        price = price_;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    function setPrice(uint256 newPrice) public onlyOwner {
        price = newPrice;
    }

    function healthFactor() public view returns (uint256) {
        return (collateral * price * 80) / debt;
    }

    function liquidate(uint256 repayAmount) public returns (uint256 taken) {
        require(healthFactor() < 100, "position is healthy");
        require(repayAmount <= debt, "repay too much");
        uint256 base = repayAmount / price;
        taken = (base * 110) / 100;
        require(taken <= collateral, "not enough collateral");
        debt -= repayAmount;
        collateral -= taken;
        liquidatorCollateral[msg.sender] += taken;
    }
}

A healthy position cannot be liquidated

The health factor is 160, which is above 100. A liquidation attempt reverts and nothing changes.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract LendingPosition {
    address public owner;
    uint256 public collateral;
    uint256 public debt;
    uint256 public price;
    mapping(address => uint256) public liquidatorCollateral;

    constructor(uint256 collateral_, uint256 debt_, uint256 price_) {
        owner = msg.sender;
        collateral = collateral_;
        debt = debt_;
        price = price_;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    function setPrice(uint256 newPrice) public onlyOwner {
        price = newPrice;
    }

    function healthFactor() public view returns (uint256) {
        return (collateral * price * 80) / debt;
    }

    function liquidate(uint256 repayAmount) public returns (uint256 taken) {
        require(healthFactor() < 100, "position is healthy");
        require(repayAmount <= debt, "repay too much");
        uint256 base = repayAmount / price;
        taken = (base * 110) / 100;
        require(taken <= collateral, "not enough collateral");
        debt -= repayAmount;
        collateral -= taken;
        liquidatorCollateral[msg.sender] += taken;
    }
}

A price drop makes the position unhealthy

When the price falls from 100 to 25, the collateral value falls to 2500. The health factor falls to 40, which is below 100.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract LendingPosition {
    address public owner;
    uint256 public collateral;
    uint256 public debt;
    uint256 public price;
    mapping(address => uint256) public liquidatorCollateral;

    constructor(uint256 collateral_, uint256 debt_, uint256 price_) {
        owner = msg.sender;
        collateral = collateral_;
        debt = debt_;
        price = price_;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    function setPrice(uint256 newPrice) public onlyOwner {
        price = newPrice;
    }

    function healthFactor() public view returns (uint256) {
        return (collateral * price * 80) / debt;
    }

    function liquidate(uint256 repayAmount) public returns (uint256 taken) {
        require(healthFactor() < 100, "position is healthy");
        require(repayAmount <= debt, "repay too much");
        uint256 base = repayAmount / price;
        taken = (base * 110) / 100;
        require(taken <= collateral, "not enough collateral");
        debt -= repayAmount;
        collateral -= taken;
        liquidatorCollateral[msg.sender] += taken;
    }
}

Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract LendingPosition {
    address public owner;
    uint256 public collateral;
    uint256 public debt;
    uint256 public price;
    mapping(address => uint256) public liquidatorCollateral;

    constructor(uint256 collateral_, uint256 debt_, uint256 price_) {
        owner = msg.sender;
        collateral = collateral_;
        debt = debt_;
        price = price_;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    function setPrice(uint256 newPrice) public onlyOwner {
        price = newPrice;
    }

    function healthFactor() public view returns (uint256) {
        return (collateral * price * 80) / debt;
    }

    function liquidate(uint256 repayAmount) public returns (uint256 taken) {
        require(healthFactor() < 100, "position is healthy");
        require(repayAmount <= debt, "repay too much");
        uint256 base = repayAmount / price;
        taken = (base * 110) / 100;
        require(taken <= collateral, "not enough collateral");
        debt -= repayAmount;
        collateral -= taken;
        liquidatorCollateral[msg.sender] += taken;
    }
}

Key points

  • Health factor: A health factor compares collateral value against debt, with 100 meaning exactly covered at the safety threshold.
  • Liquidation threshold: The liquidation threshold is the safety fraction, here 80, applied to collateral value before comparing it with debt.
  • Liquidator: A liquidator is anyone who repays part of an unhealthy debt in exchange for collateral.
  • Discount: A discount is the extra collateral a liquidator receives above the repaid value, here 10 percent.
  • Healthy position: A healthy position has a health factor of at least 100 and cannot be liquidated.
  • Restatement: Liquidation lets a stranger repay an unhealthy loan and take discounted collateral for doing so.

In this example, the position starts with 100 collateral and 5000 debt at a price of 100. After the price drops to 25, the liquidator repays 1000 of debt. The base collateral for 1000 at a price of 25 is 40, plus a 10 percent discount gives 44, so the debt falls to 4000, the collateral falls to 56, and the liquidator holds 44.

Note

Liquidation uses a mock oracle price in this lesson, and a real protocol would read its price from a defended feed.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Remix IDE

Exercise

A position has a health factor of 40. Can anyone liquidate it?