AaveV2分析(三)Liquidation在defi中,当债务人的抵押资产代币可能不足以抵扣债务时,任何人都可以参与清算。清算的过程是通过repay部分债务,获得部分抵押资产。functionliquidationCall(addresscollateralAsset,
在defi中,当债务人的抵押资产代币可能不足以抵扣债务时,任何人都可以参与清算。清算的过程是通过repay部分债务,获得部分抵押资产。
function liquidationCall(
address collateralAsset,
address debtAsset,
address user,
uint256 debtToCover,
bool receiveAToken
) external override whenNotPaused {
address collateralManager = _addressesProvider.getLendingPoolCollateralManager();
//solium-disable-next-line
(bool success, bytes memory result) =
collateralManager.delegatecall(
abi.encodeWithSignature(
'liquidationCall(address,address,address,uint256,bool)',
collateralAsset,
debtAsset,
user,
debtToCover,
receiveAToken
)
);
require(success, Errors.LP_LIQUIDATION_CALL_FAILED);
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
require(returnCode == 0, string(abi.encodePacked(returnMessage)));
}
这是清算的入口函数,在函数中首先拿到质押品管理者的地址,然后调用管理者的liquidationCall函数进行实际的清算处理。接下来让我们分析它的具体执行过程。
function liquidationCall(
address collateralAsset,
address debtAsset,
address user,
uint256 debtToCover,
bool receiveAToken
) external override returns (uint256, string memory) {
// ......
}
首先拿到清算债务要用到的数据,包括质押品储备池,债务储备池,用户配置,健康因子。
DataTypes.ReserveData storage collateralReserve = _reserves[collateralAsset];
DataTypes.ReserveData storage debtReserve = _reserves[debtAsset];
DataTypes.UserConfigurationMap storage userConfig = _usersConfig[user];
LiquidationCallLocalVars memory vars;
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
user,
_reserves,
userConfig,
_reservesList,
_reservesCount,
_addressesProvider.getPriceOracle()
);
然后获取被清算用户的债务信息,包括固定债务和流动债务。
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve);
然后验证清算是否合格,包括用户的Health factor是否低于清算所需阈值,用户的债务是否为0,用户的资产是否设置为可抵押
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall(
collateralReserve,
debtReserve,
userConfig,
vars.healthFactor,
vars.userStableDebt,
vars.userVariableDebt
);
然后获得被清算人可清算的债务的上限。
vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);
vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user);
vars.maxLiquidatableDebt = vars.userStableDebt.add(vars.userVariableDebt).percentMul(
LIQUIDATION_CLOSE_FACTOR_PERCENT
);
vars.actualDebtToLiquidate = debtToCover > vars.maxLiquidatableDebt
? vars.maxLiquidatableDebt
: debtToCover;
然后计算实际可清算债务。(因为被清算人的质押品可能无法完全覆盖债务)
(
vars.maxCollateralToLiquidate,
vars.debtAmountNeeded
) = _calculateAvailableCollateralToLiquidate(
collateralReserve,
debtReserve,
collateralAsset,
debtAsset,
vars.actualDebtToLiquidate,
vars.userCollateralBalance
);
if (vars.debtAmountNeeded < vars.actualDebtToLiquidate) {
vars.actualDebtToLiquidate = vars.debtAmountNeeded;
}
函数_calculateAvailableCollateralToLiquidate逻辑很简单,就是根据以下公式换算代币数量:
debt token price debt amount liqudation bonus= collateral token price* collateral token amount
然后判断清算者是否想要抵押的代币,如果清算者想要抵押的代币而非aToken,那么AAVE检查整个抵押代币池是否足够应对清算要求。
if (!receiveAToken) {
uint256 currentAvailableCollateral =
IERC20(collateralAsset).balanceOf(address(vars.collateralAtoken));
if (currentAvailableCollateral < vars.maxCollateralToLiquidate) {
return (
uint256(Errors.CollateralManagerErrors.NOT_ENOUGH_LIQUIDITY),
Errors.LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE
);
}
}
然后更新复利索引,因为清算改变了债务数量,也因此改变了浮动利率。
debtReserve.updateState();
先清算浮动利率债务,再清算固定利率债务,直到完成本次清算所需数量。
if (vars.userVariableDebt >= vars.actualDebtToLiquidate) {
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
user,
vars.actualDebtToLiquidate,
debtReserve.variableBorrowIndex
);
} else {
// If the user doesn't have variable debt, no need to try to burn variable debt tokens
if (vars.userVariableDebt > 0) {
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
user,
vars.userVariableDebt,
debtReserve.variableBorrowIndex
);
}
IStableDebtToken(debtReserve.stableDebtTokenAddress).burn(
user,
vars.actualDebtToLiquidate.sub(vars.userVariableDebt)
);
}
然后更新利率。
debtReserve.updateInterestRates(
debtAsset,
debtReserve.aTokenAddress,
vars.actualDebtToLiquidate,
0
);
然后区分是发送aToken还是Underlyaing asset给清算者。
if (receiveAToken) {
vars.liquidatorPreviousATokenBalance = IERC20(vars.collateralAtoken).balanceOf(msg.sender);
vars.collateralAtoken.transferOnLiquidation(user, msg.sender, vars.maxCollateralToLiquidate);
if (vars.liquidatorPreviousATokenBalance == 0) {
DataTypes.UserConfigurationMap storage liquidatorConfig = _usersConfig[msg.sender];
liquidatorConfig.setUsingAsCollateral(collateralReserve.id, true);
emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender);
}
} else {
collateralReserve.updateState();
collateralReserve.updateInterestRates(
collateralAsset,
address(vars.collateralAtoken),
0,
vars.maxCollateralToLiquidate
);
vars.collateralAtoken.burn(
user,
msg.sender,
vars.maxCollateralToLiquidate,
collateralReserve.liquidityIndex
);
}
清算者将偿还的代币转给AAVE
IERC20(debtAsset).safeTransferFrom(
msg.sender,
debtReserve.aTokenAddress,
vars.actualDebtToLiquidate
);
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!