# DeFi 借贷概念 #2 - 清算

• 超额抵押和坏账

• 清算和清算阈值

• Compound：账户流动性

• Maker

• AAVE V2 - 健康系数

• 穿仓头寸分析

## Compound：账户流动性

Compound，指的是他们在`账户流动性`参数下的头寸清算阈值，由Compound 的主合约--Comptroller计算。

Comptroller 有一个名为`getAccountLiquidity()`的函数，返回关于账户流动性的信息。在内部这个函数调用`getHypotheticalAccountLiquidityInternal()`

``````pragma solidity ^ 0.8 .13;

struct AccountLiquidityLocalVars {
uint sumCollateral;
uint sumBorrowPlusEffects;
uint cTokenBalance;
uint borrowBalance;
uint exchangeRateMantissa;
uint oraclePriceMantissa;
Exp collateralFactor;
Exp exchangeRate;
Exp oraclePrice;
Exp tokensToDenom;
}

// ...

function getHypotheticalAccountLiquidityInternal(
CToken cTokenModify,
uint redeemTokens,
uint borrowAmount) internal view returns(Error, uint, uint) {

AccountLiquidityLocalVars memory vars;
uint oErr;

CToken[] memory assets = accountAssets[account];
for (uint i = 0; i &lt; assets.length; i++) {
CToken asset = assets[i];

(oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) =
asset.getAccountSnapshot(account);
if (oErr != 0) {
return (Error.SNAPSHOT_ERROR, 0, 0);
}
vars.collateralFactor = Exp({
});
vars.exchangeRate = Exp({
mantissa: vars.exchangeRateMantissa
});

vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset);
if (vars.oraclePriceMantissa == 0) {
return (Error.PRICE_ERROR, 0, 0);
}
vars.oraclePrice = Exp({
mantissa: vars.oraclePriceMantissa
});
vars.tokensToDenom = mul_(mul_(vars.collateralFactor, vars.exchangeRate),
vars.oraclePrice);
vars.cTokenBalance, vars.sumCollateral);
vars.borrowBalance, vars.sumBorrowPlusEffects);

if (asset == cTokenModify) {
redeemTokens, vars.sumBorrowPlusEffects);
borrowAmount, vars.sumBorrowPlusEffects);
}
}

if (vars.sumCollateral > vars.sumBorrowPlusEffects) {
return (Error.NO_ERROR, vars.sumCollateral - vars.sumBorrowPlusEffects, 0);
} else {
return (Error.NO_ERROR, 0, vars.sumBorrowPlusEffects - vars.sumCollateral);
}
}``````

Compound的治理（governance）会根据市场情况改变抵押品系数，但在任何时候，他们的抵押品系数不能超过0.9--最多只能借到你所存抵押品的90%：

``````pragma solidity ^ 0.8 .13;

uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9``````

Oracles 是借贷协议中使用的合约，用于获取以某种通用货币（通常是美元、ETH或协议使用的稳定币）计价的某种资产的价格。

## Maker

• `Dog`：在迁移到清算2.0 后部署的（正如Maker治理描述的）。这里的清算功能是`bark()`
• `Cat`：清算1.2，`bite()`
• `grab()`：在部署Cat合约之前，作为清算的方式使用`vat`合约。

``````pragma solidity ^ 0.8 .13;

function bite(bytes32 ilk, address urn) external returns(uint id) {
(, uint rate, uint spot) = vat.ilks(ilk);
(uint ink, uint art) = vat.urns(ilk, urn);

require(live == 1, "Cat/not-live");
require(spot > 0 && mul(ink, spot) &lt; mul(art, rate), "Cat/not-unsafe");``````

``````pragma solidity ^ 0.8 .13;

(uint256 id) {
require(live == 1, "Dog/not-live");

(uint256 ink, uint256 art) = vat.urns(ilk, urn);
Ilk memory milk = ilks[ilk];
uint256 dart;
uint256 rate;
uint256 dust; {
uint256 spot;
(, rate, spot, , dust) = vat.ilks(ilk);
require(spot > 0 && mul(ink, spot) &lt; mul(art, rate), "Dog/not-unsafe");``````

• `spot_{ilk}`在不等式中被用作抵押品的价格，以DAI为单位并除以抵押品的清算率（由governance合约决定）。

• `ink_{urn}`是头寸的抵押品余额。

• `rate_{ilk}`是特定抵押品类型的累积债务。当与`art_{urn}`（即一个头寸借入的债务额）相乘，我们可以得到DAI的总债务。

## AAVE V2 - 健康系数

AAVE V2 也定义了他们自己的阈值，`健康系数（HealthFactor）`。一个健康系数值为`H_{f}`<1的用户可以被清算。

``````pragma solidity ^ 0.8 .13;

vars.healthFactor = calculateHealthFactorFromBalances(
vars.totalCollateralInETH,
vars.totalDebtInETH,
vars.avgLiquidationThreshold
);

// ...

/**
* @dev Calculates the health factor from the corresponding balances
* @param totalCollateralInETH The total collateral in ETH
* @param totalDebtInETH The total debt in ETH
* @param liquidationThreshold The avg liquidation threshold
* @return The health factor calculated from the balances provided
**/
function calculateHealthFactorFromBalances(
uint256 totalCollateralInETH,
uint256 totalDebtInETH,
uint256 liquidationThreshold
) internal pure returns(uint256) {
if (totalDebtInETH == 0) return uint256(-1);

}``````

## 穿仓头寸分析

• 债务总额（`totalDebtETH`）：17.83508595148699eth
• 抵押品总额（`totalCollateralETH`）：0.013596360502551568 eth

0.00032838 * 700000 = 229.866 ETH

``````➜~cast call - b 13517657 0xA50ba011c48153De246E5192C8f9258A2ba79Ca9 "getAssetPrice(address)"
0x0F5D2fB29fb7d3CFeE444a200298f468908cC942
0x000000000000000000000000000000000000000000000000000131d14dce4400``````

0.00033625 * 700000 = 235.375 eth

``````select
evt_block_number,
collateralAsset,
debtAsset,
debtToCover,
liquidatedCollateralAmount,
liquidator
from
aave_v2_ethereum.LendingPool_evt_LiquidationCall
where
user = from_hex('0x227cAa7eF6D955A92F483dB2BD01172997A1a623')
order by
evt_block_number desc;``````

``````+------------------+--------------------------------------------+--------------------------------------------+-------------------------+----------------------------+--------------------------------------------+
| evt_block_number | collateralAsset                            | debtAsset                                  | debtToCover             | liquidatedCollateralAmount | liquidator                                 |
+------------------+--------------------------------------------+--------------------------------------------+-------------------------+----------------------------+--------------------------------------------+
|         13520838 | 0x6B175474E89094C44DA98B954EEDEAC495271D0F | 0x0F5D2FB29FB7D3CFEE444A200298F468908CC942 | 17919685927295406794873 |    58271102282974799175987 | 0xB2B3D5B6215D4FB23BF8DD642D385C4B44AADB2A |
+------------------+--------------------------------------------+--------------------------------------------+-------------------------+----------------------------+--------------------------------------------+``````

``````select
count( * ) as num_liquidations
from
aave_v2_ethereum.LendingPool_evt_LiquidationCall
where
user = from_hex('0x227cAa7eF6D955A92F483dB2BD01172997A1a623')
and evt_block_number &lt;= 13522070 and evt_block_number >= 13520838``````
``````+ - - - - - - - - - +
| num_liquidations |
+ - - - - - - - - - +
| 87 |
+ - - - - - - - - - +``````

``````select
SUM(liquidatedCollateralAmount) as amountSeized,
collateralAsset
from
aave_v2_ethereum.LendingPool_evt_LiquidationCall
where
user = from_hex('0x227cAa7eF6D955A92F483dB2BD01172997A1a623')
and evt_block_number &lt;= 13522070 and evt_block_number >= 13520838
group by collateralAsset``````

``````+--------------------------+--------------------------------------------+
| amountSeized             | collateralAsset                            |
+--------------------------+--------------------------------------------+
| 387663228503220484547359 | 0x6B175474E89094C44DA98B954EEDEAC495271D0F |
+--------------------------+--------------------------------------------+
+--------------------------+--------------------------------------------+``````

• ~50 ETH
• ~387,663 DAI

``````pragma solidity ^ 0.8 .13;

// from ...

uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000;

function liquidationCall(
uint256 debtToCover,
) external override returns(uint256, string memory) {

// ...

vars.maxLiquidatableDebt =
LIQUIDATION_CLOSE_FACTOR_PERCENT
);

// ...``````

``````
from io import BytesIO
from binascii import unhexlify
from dataclasses import dataclass

@dataclass(frozen=True)
class UserAccountData:
totalCollateralETH: int
totalDebtETH: int
availableBorrowsETH: int
currentLiquidationThreshold: int
ltv: int
healthFactor: int

def parse_user_account_data(uacd: str) -> UserAccountData:
uacd_bytes = unhexlify(uacd[2:])

assert len(uacd_bytes) == 192

uacd_bytes = BytesIO(uacd_bytes)

return UserAccountData(
totalCollateralETH=total_collateral_eth,
totalDebtETH=total_debt_eth,
availableBorrowsETH=available_borrows_eth,
currentLiquidationThreshold=current_liquidation_threshold,
ltv=ltv,
healthFactor=health_factor,
)``````

``````➜  ~ cast call -b 13522070 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9 "getUserAccountData(address)" 0x227cAa7eF6D955A92F483dB2BD01172997A1a623
0x000000000000000000000000000000000000000000000000085b5b5e846685f4000000000000000000000000000000000000000000000002743544e203a3e4ae00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f710000000000000000000000000000000000000000000000000000000000001d9500000000000000000000000000000000000000000000000000260a45667b706b``````

``````parse_user_account_data('0x000000000000000000000000000000000000000000000000085b5b5e846685f4000000000000000000000000000000000000000000000002743544e203a3e4ae00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f710000000000000000000000000000000000000000
000000000000000000001d9500000000000000000000000000000000000000000000000000260a45667b706b')
UserAccountData(totalCollateralETH=602175436690458100, totalDebtETH=45267162967098778798, availableBorrowsETH=0, currentLiquidationThreshold=8049, ltv=7573, healthFactor=10707342303391851)``````

``````➜~cast call - b 13522070 0xA50ba011c48153De246E5192C8f9258A2ba79Ca9 "getAssetPrice(address)"
0x0F5D2fB29fb7d3CFeE444a200298f468908cC942
0x00000000000000000000000000000000000000000000000000031015cc1da8f2``````

0.000862110734985458 eth!

## 小节

• 所有协议都将其阈值定义为抵押品与债务的某个函数（无论是比率还是差值）。

• 所有协议都留有一定的治理空间，以决定每个抵押品风险参数的价值，以应对市场条件的变化，因为一些资产比其他资产更不稳定。

• 所有协议都使用预言机对其抵押品和债务价格进行计价，并使用广泛接受的货币（例如，ETH，USD，DAI）。

0x9e64...7c84