Risks Control Model
Details of how liquidation works in DeFiner

Parameters

  1. 1.
    _liquidator: address of the liquidator.
  2. 2.
    _borrower: address of the borrower, also the account which will be liquidated
  3. 3.
    _borrowedToken: address of the borrowed token
  4. 4.
    _collateralToken: address of the collateral token

Design Overview

By calling this function, the liquidator repays the borrower's loan if the borrower is liquidatable and also ends up buying the borrower's collateral at a 5% discounted price. This also resets the borrower's LTV back to what it was initially. The tokens that the liquidator uses to liquidate the borrower's account should be deposited in DeFiner.
By calling this function, the liquidator repays the borrower's loan if the borrower is liquidatable and also ends up buying the borrower's collateral at a 5% discounted price. This resets the borrower's LTV back to what it was initially. The tokens that the liquidator uses to liquidate the borrower's account should be deposited in DeFiner.
In order to be liquidatable, an account's LTV should be greater than 85%, since there is a high risk that this account will not repay it's debt once the LTV goes above 85%.
In order to be liquidatable, an account's LTV should be greater than 85%, since there is a high risk that this account will not repay it's debt once the LTV goes above 85%.
If all conditions are met, the liquidate function majorly executed the following operations:
  1. 1.
    Liquidator deposits collateral tokens equivalent to the payAmount calculated
  2. 2.
    Withdraws borrowed tokens equivalent to the repayAmount to the liquidator
  3. 3.
    Withdraws collateral tokens equivalent to the payAmount.
  4. 4.
    Repays the borrower's loan.
Please refer to the pseudocode given below for more details on how these calculations are performed.

Examples:

Terminology used:
CBB: Current borrow balance = principle + accrued interest
LDR: Liquidation Discount ratio: The discount ratio the liquidator will get when buying other's assets during the liquidation process.
CCV: Current Collateral Value = Collateral price * Collateral Amount
UAAL: User asset at Liquidation: The maximum collateral that can be liquidated or swapped.
BP: Borrow Power of borrower
ILTV: Initial LTV ratio of collateral token: 0.6 currently for most tokens
We have to make sure that:
UAAL=(CBBBP)100/(LDRILTV)UAAL= (CBB – BP) *100 / (LDR-ILTV)
Before liquidation:
(Assuming 1 DAI = 1 USDT = $1) USDT Price drops by 65% after user deposits, setting LTV to 92%
  1. 1.
    Full liquidation
(Assuming 1 USDT = 1 DAI = $1) Collateral price drops to 65% after User 1 borrows
User1:
UAAL=(6039)100/(9560)=60UAAL=(60-39)*100/(95-60)=60
  1. 1.
    Deposits: 100 USDT
  2. 2.
    Loans: 60 DAI
  3. 3.
    Collateral price drops to 65%, new collateral value = $65
  4. 4.
    LTV: 0.92, liquidatable
  5. 5.
    New borrow power = initial BP * %age price drop = 60 * 0.65 = 39
User2:
  1. 1.
    Deposits 200 DAI
  2. 2.
    It calls liquidate(user1, DAIAdress, USDTAddress)
Explanation
The maximum amount of DAI that user2 can transfer to user1 is 200 since it doesn't have any borrows.
Since DAI's price is 1, and 60 < 200, so user2 is able to pay the maximum value, which is 60 DAI. This is called full liquidation.
After Liquidation: User 1:
  1. 1.
    Deposits: 5 USDT
  2. 2.
    Borrows: 0
  3. 3.
    User is not liquidatable
User 2:
  1. 1.
    Deposits: 95 USDT
2. Partial Liquidation
Before Liquidation:
User1:
  1. 1.
    Deposits: 100 USDT
  2. 2.
    Loans: 60 DAI
  3. 3.
    Collateral price drops to 65%, new collateral value = $65
  4. 4.
    LTV: 0.92, liquidatable
  5. 5.
    borrow power = initial BP * %age price drop = 60 * 0.65 = 39
User2:
  1. 1.
    Deposits 50 DAI
  2. 2.
    It calls liquidate(user1, DAIAdress, USDTAddress)
Explanation:
Here, UAAL is computed the same way as the previous example.
UAAL=(6039)100/(9560)=60UAAL=(60-39)*100/(95-60)=60
But here user2 only has 50DAI, which worth $50 and 50 < 60 so user2 can't be swapped to the maximum value UAAL. That way, user2 only pays 50 DAI and user1 will pay 50 / 0.95 = 52.6 USDC.
After liquidation
User 1:
  1. 1.
    Deposits: 100 - 50/0.95 = 47.4 USDT
  2. 2.
    Borrows: 60-50 = 10DAI
  3. 3.
    LTV: 10 / 47.4 = 0.21, not liquidatable
User 2:
  1. 1.
    Deposits: 50/0.95 = 52.6 USDC
Notice here although user2 doesn't fully liquidate user1, user1 is not liquidatable after liquidation. This is because there is a gap between the initial borrow LTV and the LTV to become liquidatable.

Pseudocode

1
require(isAccountLiquidatable(_borrower);
2
3
if (liquidator has borrows){
4
require(Liquidator's borrow value < Liquidator's borrow power;
5
}
6
7
uint tokenBalLiquidator = get deposit balance of Liquidator;
8
9
uint tokenBalBorrowedUser = get borrow balance of Borrower;
10
11
uint borrowedTokenAmountForLiquidation = tokenBalLiquidator.min(tokenBalBorrowedUser)
12
13
uint borrowerCollateralVal = getDepositBalanceCurrent(_collateralToken, _borrower);
14
15
uint collateralLTV = 60%;
16
17
uint totalBorrowwValBorwer = getBorrowETH(_borrower);
18
uint borrowPowerBorrower = getBorrowPower(borrower);
19
uint liquidationDiscountRatio = 95%;
20
21
uint limiRepaymentVal = (totalBorrowwValBorwer - borrowPowerBorrower) / (liquidationDiscountRatio - collateralLTV);
22
23
uint collateralTokenValueForLiquidation = limiRepaymentVal.min(tokenBalLiquidator);
24
25
uint liquidationVal = collateralTokenValueForLiquidation.min(borrowedTokenAmountForLiquidation * borrowedTokenPrice / liquidationDiscountRatio);
26
27
uint repaymentAmount = (liquidationVal * borrowTokenDivisor) / borrowTokenPrice;
28
uint payAmount = (repaymentAmount * liquidateTokenDivisor * borrowTokenPrice) / (borrowTokenDivisor * liquidationDiscountRatio * liquidateTokenPrice);
29
30
deposit(_liquidator, _collateralToken, payAmount);
31
_withdrawLiquidate(_liquidator, _borrowedToken, repaymentAmount);
32
_withdrawLiquidate(_borrower, _collateralToken, payAmount);
33
repay(_borrower, _borrowedToken, repaymentAmount);
34
35
return (repaymentAmount, payAmount);
36
Copied!

Source Code

1
function liquidate(
2
address _liquidator,
3
address _borrower,
4
address _borrowedToken,
5
address _collateralToken
6
) external onlyAuthorized returns (uint256, uint256) {
7
initCollateralFlag(_liquidator);
8
initCollateralFlag(_borrower);
9
require(isAccountLiquidatable(_borrower), "borrower is not liquidatable");
10
11
// It is required that the liquidator doesn't exceed it's borrow power.
12
// if liquidator has any borrows, then only check for borrowPower condition
13
Account storage liquidateAcc = accounts[_liquidator];
14
if (liquidateAcc.borrowBitmap > 0) {
15
require(getBorrowETH(_liquidator) < getBorrowPower(_liquidator), "No extra funds used for liquidation");
16
}
17
18
LiquidationVars memory vars;
19
20
ITokenRegistry tokenRegistry = globalConfig.tokenInfoRegistry();
21
22
// _borrowedToken balance of the liquidator (deposit balance)
23
vars.targetTokenBalance = getDepositBalanceCurrent(_borrowedToken, _liquidator);
24
require(vars.targetTokenBalance > 0, "amount must be > 0");
25
26
// _borrowedToken balance of the borrower (borrow balance)
27
vars.targetTokenBalanceBorrowed = getBorrowBalanceCurrent(_borrowedToken, _borrower);
28
require(vars.targetTokenBalanceBorrowed > 0, "borrower not own any debt token");
29
30
// _borrowedToken available for liquidation
31
uint256 borrowedTokenAmountForLiquidation = vars.targetTokenBalance.min(vars.targetTokenBalanceBorrowed);
32
33
// _collateralToken balance of the borrower (deposit balance)
34
vars.liquidateTokenBalance = getDepositBalanceCurrent(_collateralToken, _borrower);
35
36
uint256 targetTokenDivisor;
37
(, targetTokenDivisor, vars.targetTokenPrice, vars.borrowTokenLTV) = tokenRegistry.getTokenInfoFromAddress(
38
_borrowedToken
39
);
40
41
uint256 liquidateTokendivisor;
42
uint256 collateralLTV;
43
(, liquidateTokendivisor, vars.liquidateTokenPrice, collateralLTV) = tokenRegistry.getTokenInfoFromAddress(
44
_collateralToken
45
);
46
47
// _collateralToken to purchase so that borrower's balance matches its borrow power
48
vars.totalBorrow = getBorrowETH(_borrower);
49
vars.borrowPower = getBorrowPower(_borrower);
50
vars.liquidationDiscountRatio = globalConfig.liquidationDiscountRatio();
51
vars.limitRepaymentValue = vars.totalBorrow.sub(vars.borrowPower).mul(100).div(
52
vars.liquidationDiscountRatio.sub(collateralLTV)
53
);
54
55
uint256 collateralTokenValueForLiquidation = vars.limitRepaymentValue.min(
56
vars.liquidateTokenBalance.mul(vars.liquidateTokenPrice).div(liquidateTokendivisor)
57
);
58
59
uint256 liquidationValue = collateralTokenValueForLiquidation.min(
60
borrowedTokenAmountForLiquidation.mul(vars.targetTokenPrice).mul(100).div(targetTokenDivisor).div(
61
vars.liquidationDiscountRatio
62
)
63
);
64
65
vars.repayAmount = liquidationValue.mul(vars.liquidationDiscountRatio).mul(targetTokenDivisor).div(100).div(
66
vars.targetTokenPrice
67
);
68
vars.payAmount = vars.repayAmount.mul(liquidateTokendivisor).mul(100).mul(vars.targetTokenPrice);
69
vars.payAmount = vars.payAmount.div(targetTokenDivisor).div(vars.liquidationDiscountRatio).div(
70
vars.liquidateTokenPrice
71
);
72
73
deposit(_liquidator, _collateralToken, vars.payAmount);
74
_withdrawLiquidate(_liquidator, _borrowedToken, vars.repayAmount);
75
_withdrawLiquidate(_borrower, _collateralToken, vars.payAmount);
76
repay(_borrower, _borrowedToken, vars.repayAmount);
77
78
return (vars.repayAmount, vars.payAmount);
79
}
Copied!