Risks Control Model

Details of how liquidation works in DeFiner

- 1._liquidator: address of the liquidator.
- 2._borrower: address of the borrower, also the account which will be liquidated
- 3._borrowedToken: address of the borrowed token
- 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.Liquidator deposits collateral tokens equivalent to the
`payAmount`

calculated - 2.Withdraws borrowed tokens equivalent to the
`repayAmount`

to the liquidator - 3.Withdraws collateral tokens equivalent to the
`payAmount`

. - 4.Repays the borrower's loan.

Please refer to the pseudocode given below for more details on how these calculations are performed.

Examples:

We have to make sure that:

$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.
**Full liquidation**

(Assuming 1 USDT = 1 DAI = $1)
Collateral price drops to 65% after User 1 borrows

User1:

$UAAL=(60-39)*100/(95-60)=60$

- 1.Deposits: 100 USDT
- 2.Loans: 60 DAI
- 3.Collateral price drops to 65%, new collateral value = $65
- 4.LTV: 0.92, liquidatable
- 5.New borrow power = initial BP * %age price drop = 60 * 0.65 = 39

User2:

- 1.Deposits 200 DAI
- 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.Deposits: 5 USDT
- 2.Borrows: 0
- 3.User is not liquidatable

User 2:

- 1.Deposits: 95 USDT

Before Liquidation:

User1:

- 1.Deposits: 100 USDT
- 2.Loans: 60 DAI
- 3.Collateral price drops to 65%, new collateral value = $65
- 4.LTV: 0.92, liquidatable
- 5.borrow power = initial BP * %age price drop = 60 * 0.65 = 39

User2:

- 1.Deposits 50 DAI
- 2.It calls liquidate(user1, DAIAdress, USDTAddress)

Explanation:

Here, UAAL is computed the same way as the previous example.

$UAAL=(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.Deposits: 100 - 50/0.95 = 47.4 USDT
- 2.Borrows: 60-50 = 10DAI
- 3.LTV: 10 / 47.4 = 0.21, not liquidatable

User 2:

- 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!

â€‹

Last modified 3mo ago

Copy link