SC Day 80
Mini Lending — 清算机制 + 清算奖励
### 1. 清算机制为什么必要?
2026-06-29
第四阶段:综合实战 (73-80)lendingliquidationliquidation-bonusbad-debtmevliquidation-bot
日期: 2026-06-29 方向: Solidity 阶段: 第四阶段:综合实战 (73-80) 标签: #lending #liquidation #liquidation-bonus #bad-debt #mev #liquidation-bot
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | 清算机制原理、部分清算、清算奖励数学、坏账处理 |
| 实操 | 实现 liquidate() 函数 + 完整测试场景 |
| 产出 | 完整的清算功能 + 清算场景测试 + 清算机器人经济学分析 |
核心概念
1. 清算机制为什么必要?
借贷协议的核心风险是坏账——当抵押品价值低于债务价值时,借款人没有动力还款:
正常状态:
抵押品: 10 ETH × $2000 = $20,000
债务: $12,000 USDC
→ 借款人有动力还款(还 $12,000 可以拿回 $20,000 的 ETH)
水下状态:
ETH 跌到 $1100
抵押品: 10 ETH × $1100 = $11,000
债务: $12,000 USDC
→ 借款人没有动力还款(还 $12,000 只能拿回 $11,000 的 ETH)
→ 如果不清算,协议承担 $1,000 坏账
清算的作用:在抵押品价值接近但尚未低于债务时(HF < 1 但抵押品仍 > 债务),允许第三方代为还款并获得折价抵押品。
2. 清算流程
清算人发起清算
↓
检查 1:borrower 的 HF < 1?
↓
检查 2:清算金额 ≤ 最大可清算金额(通常为 50% 的债务)
↓
计算:清算人偿还的债务金额
计算:清算人获得的抵押品金额 = 债务金额 + 清算奖励
↓
执行:清算人转入债务 Token
执行:清算人获得抵押品 Token
↓
更新:借款人的债务减少
更新:借款人的抵押品减少
↓
验证:清算后借款人的 HF 应该改善
3. 清算数学
清算奖励计算
清算人偿还的债务: debtToRepay (in debt token)
清算奖励: liquidationBonus (e.g., 5%)
清算人获得的抵押品价值(USD):
collateralValueUSD = debtToRepay * debtPrice * (1 + liquidationBonus)
清算人获得的抵押品数量:
collateralAmount = collateralValueUSD / collateralPrice
示例:
清算人偿还: 5,000 USDC (价值 $5,000)
清算奖励: 5%
ETH 价格: $1,100
抵押品价值: $5,000 × 1.05 = $5,250
获得 ETH: $5,250 / $1,100 = 4.7727 ETH
清算人利润: 4.7727 × $1,100 - $5,000 = $250 (5% 奖励)
最大清算金额
为什么限制清算比例(通常 50%)?
全额清算的问题:
- 价格波动可能导致清算过度
- 借款人丧失全部抵押品(用户体验差)
- 大额抛售加剧价格下跌(清算级联)
部分清算的优势:
- 只清算足够恢复 HF > 1 的部分
- 保护借款人剩余抵押品
- 减少市场冲击
Aave 的规则:
- HF < 1: 最多清算 50% 的债务
- HF < 0.95: 可以清算 100% 的债务(紧急情况)
坏账场景
当抵押品 < 债务时(极端市场情况):
场景:
抵押品: 10 ETH × $800 = $8,000
债务: $12,000 USDC
差额: -$4,000(坏账)
处理方式:
1. 清算人获得全部抵押品(10 ETH = $8,000)
2. 清算人只需偿还: $8,000 / 1.05 ≈ $7,619
3. 剩余债务: $12,000 - $7,619 = $4,381(坏账)
4. 坏账从协议储备金中覆盖
5. 如果储备金不足 → 社会化损失(所有存款人分摊)
Aave 的Safety Module:
- AAVE token 质押者作为最后的安全网
- 坏账从 Safety Module 中拍卖 AAVE 覆盖
代码实战
liquidate() 函数实现
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
// 接续 Day 77 的 MiniLendingPool 合约
/// @title MiniLendingPool - 清算功能
/// @notice Day 80 版本:新增 liquidate() + 完善清算机制
contract MiniLendingPool_V3 is MiniLendingPool_V2 {
// ============ 清算常量 ============
/// @dev 默认最大清算因子(基点):50%
uint256 public constant DEFAULT_CLOSE_FACTOR_BPS = 5000;
/// @dev 严重水下时的最大清算因子(基点):100%
uint256 public constant FULL_CLOSE_FACTOR_BPS = 10000;
/// @dev 严重水下的 HF 阈值(0.95)
uint256 public constant SEVERE_HF_THRESHOLD = 95e16;
// ============ 清算事件 ============
event Liquidation(
address indexed liquidator,
address indexed borrower,
address indexed debtAsset,
address collateralAsset,
uint256 debtRepaid,
uint256 collateralSeized,
uint256 liquidationBonus
);
event BadDebtRealized(
address indexed borrower,
address indexed asset,
uint256 badDebtAmount
);
// ============ 清算错误 ============
error HealthFactorAboveThreshold();
error InvalidLiquidationAmount();
error InsufficientCollateral();
error LiquidationAmountExceedsCloseFactor();
// ============ 核心函数:清算 ============
/// @notice 清算不健康的借款仓位
/// @param borrower 被清算的借款人
/// @param debtAsset 清算人偿还的债务资产
/// @param collateralAsset 清算人获得的抵押品资产
/// @param debtToRepay 清算人偿还的债务数量
function liquidate(
address borrower,
address debtAsset,
address collateralAsset,
uint256 debtToRepay
) external nonReentrant {
if (debtToRepay == 0) revert ZeroAmount();
// 步骤 1:累计利息(两个相关市场都要更新)
_accrueInterest(debtAsset);
_accrueInterest(collateralAsset);
// 步骤 2:验证借款人 HF < 1
uint256 borrowerHF = _calculateHealthFactor(borrower);
if (borrowerHF >= HEALTH_FACTOR_THRESHOLD) {
revert HealthFactorAboveThreshold();
}
// 步骤 3:计算最大可清算金额
uint256 maxRepayable = _getMaxLiquidationAmount(borrower, debtAsset, borrowerHF);
if (debtToRepay > maxRepayable) {
revert LiquidationAmountExceedsCloseFactor();
}
// 步骤 4:计算清算人获得的抵押品数量
(uint256 collateralToSeize, uint256 bonusCollateral) =
_calculateCollateralToSeize(
debtAsset,
collateralAsset,
debtToRepay
);
// 步骤 5:验证借款人有足够的抵押品
UserPosition storage borrowerCollateral = positions[borrower][collateralAsset];
MarketState storage collateralState = marketStates[collateralAsset];
uint256 borrowerCollateralBalance = _convertToAssets(
borrowerCollateral.supplyShares,
collateralState.totalSupplyShares,
collateralState.totalSupplyAssets
);
if (collateralToSeize > borrowerCollateralBalance) {
// 坏账情况:抵押品不足以覆盖债务+奖励
// 清算人获得全部抵押品,但只需偿还对应金额的债务
collateralToSeize = borrowerCollateralBalance;
debtToRepay = _calculateDebtFromCollateral(
debtAsset,
collateralAsset,
collateralToSeize
);
// 记录坏账
uint256 remainingDebt = _getUserDebt(borrower, debtAsset) - debtToRepay;
if (remainingDebt > 0) {
_handleBadDebt(borrower, debtAsset, remainingDebt);
}
}
// 步骤 6:执行清算
// 6a. 减少借款人的债务
_repayDebt(borrower, debtAsset, debtToRepay);
// 6b. 减少借款人的抵押品(转换为份额)
uint256 sharesToSeize = _assetsToSharesRoundUp(
collateralToSeize,
collateralState.totalSupplyShares,
collateralState.totalSupplyAssets
);
borrowerCollateral.supplyShares -= sharesToSeize;
collateralState.totalSupplyShares -= sharesToSeize;
collateralState.totalSupplyAssets -= collateralToSeize;
// 6c. 清算人转入债务 Token
IERC20(debtAsset).safeTransferFrom(msg.sender, address(this), debtToRepay);
// 6d. 清算人获得抵押品 Token
IERC20(collateralAsset).safeTransfer(msg.sender, collateralToSeize);
// 步骤 7:验证清算后状态
// 借款人的 HF 应该改善(不强制 > 1,坏账情况下可能仍 < 1)
uint256 newHF = _calculateHealthFactor(borrower);
require(
newHF > borrowerHF || _getUserTotalDebt(borrower) == 0,
"Liquidation did not improve health factor"
);
emit Liquidation(
msg.sender,
borrower,
debtAsset,
collateralAsset,
debtToRepay,
collateralToSeize,
bonusCollateral
);
}
// ============ 清算计算辅助函数 ============
/// @notice 计算最大可清算金额
function _getMaxLiquidationAmount(
address borrower,
address debtAsset,
uint256 currentHF
) internal view returns (uint256) {
uint256 totalDebt = _getUserDebt(borrower, debtAsset);
// 确定 close factor
uint256 closeFactorBps;
if (currentHF < SEVERE_HF_THRESHOLD) {
// 严重水下:允许清算 100%
closeFactorBps = FULL_CLOSE_FACTOR_BPS;
} else {
// 一般情况:最多清算 50%
closeFactorBps = DEFAULT_CLOSE_FACTOR_BPS;
}
return totalDebt * closeFactorBps / PERCENTAGE_FACTOR;
}
/// @notice 计算清算人获得的抵押品数量
/// @return collateralAmount 总抵押品数量(含奖励)
/// @return bonusAmount 奖励部分的抵押品数量
function _calculateCollateralToSeize(
address debtAsset,
address collateralAsset,
uint256 debtToRepay
) internal view returns (uint256 collateralAmount, uint256 bonusAmount) {
uint256 debtPrice = _getAssetPriceUSD(debtAsset);
uint256 collateralPrice = _getAssetPriceUSD(collateralAsset);
MarketConfig storage collateralConfig = marketConfigs[collateralAsset];
uint8 debtDecimals = marketConfigs[debtAsset].decimals;
uint8 colDecimals = collateralConfig.decimals;
// 债务价值(USD,18位精度)
uint256 debtValueUSD = debtToRepay * debtPrice / (10 ** debtDecimals);
// 含清算奖励的抵押品价值
uint256 bonusFactorBps = PERCENTAGE_FACTOR + collateralConfig.liquidationBonusBps;
uint256 collateralValueUSD = debtValueUSD * bonusFactorBps / PERCENTAGE_FACTOR;
// 转换为抵押品数量
collateralAmount = collateralValueUSD * (10 ** colDecimals) / collateralPrice;
// 奖励部分
uint256 baseCollateral = debtValueUSD * (10 ** colDecimals) / collateralPrice;
bonusAmount = collateralAmount - baseCollateral;
}
/// @notice 从抵押品金额反算应偿还的债务
function _calculateDebtFromCollateral(
address debtAsset,
address collateralAsset,
uint256 collateralAmount
) internal view returns (uint256) {
uint256 debtPrice = _getAssetPriceUSD(debtAsset);
uint256 collateralPrice = _getAssetPriceUSD(collateralAsset);
uint8 debtDecimals = marketConfigs[debtAsset].decimals;
uint8 colDecimals = marketConfigs[collateralAsset].decimals;
uint256 collateralValueUSD = collateralAmount * collateralPrice / (10 ** colDecimals);
MarketConfig storage colConfig = marketConfigs[collateralAsset];
uint256 bonusFactorBps = PERCENTAGE_FACTOR + colConfig.liquidationBonusBps;
// 反向计算:debt = collateralValue / (1 + bonus)
uint256 debtValueUSD = collateralValueUSD * PERCENTAGE_FACTOR / bonusFactorBps;
return debtValueUSD * (10 ** debtDecimals) / debtPrice;
}
// ============ 内部辅助函数 ============
/// @notice 内部还款逻辑(清算时使用)
function _repayDebt(address borrower, address asset, uint256 amount) internal {
MarketState storage state = marketStates[asset];
UserPosition storage position = positions[borrower][asset];
uint256 currentDebt = _getUserDebt(borrower, asset);
require(amount <= currentDebt, "Repay exceeds debt");
uint256 newDebt = currentDebt - amount;
position.borrowBalance = newDebt;
position.userBorrowIndex = state.borrowIndex;
state.totalBorrows -= amount;
if (newDebt == 0) {
_removeFromBorrowList(borrower, asset);
}
}
/// @notice 处理坏账
function _handleBadDebt(
address borrower,
address asset,
uint256 badDebtAmount
) internal {
MarketState storage state = marketStates[asset];
// 方案 1:从协议储备金覆盖
if (state.reserveBalance >= badDebtAmount) {
state.reserveBalance -= badDebtAmount;
} else {
// 方案 2:社会化损失(从总存款中扣除)
uint256 covered = state.reserveBalance;
state.reserveBalance = 0;
uint256 uncovered = badDebtAmount - covered;
// 所有存款人分摊损失
state.totalSupplyAssets -= uncovered;
}
// 清除借款人的剩余债务
UserPosition storage position = positions[borrower][asset];
position.borrowBalance = 0;
position.userBorrowIndex = state.borrowIndex;
_removeFromBorrowList(borrower, asset);
state.totalBorrows -= badDebtAmount;
emit BadDebtRealized(borrower, asset, badDebtAmount);
}
function _getUserTotalDebt(address user) internal view returns (uint256 total) {
for (uint256 i = 0; i < marketList.length; i++) {
total += _getUserDebt(user, marketList[i])
* _getAssetPriceUSD(marketList[i])
/ (10 ** marketConfigs[marketList[i]].decimals);
}
}
function _assetsToSharesRoundUp(
uint256 assets,
uint256 totalShares,
uint256 totalAssets
) internal pure returns (uint256) {
uint256 numerator = assets * (totalShares + VIRTUAL_SHARES);
uint256 denominator = totalAssets + VIRTUAL_ASSETS;
return (numerator + denominator - 1) / denominator;
}
}
完整清算测试场景
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
contract LiquidationTest is Test {
// ... setUp() 省略
/// @notice 测试 1:基础清算流程
function test_Liquidate_BasicFlow() public {
// 设置:Alice 存 10 ETH,借 12,000 USDC
_setupAliceBorrowPosition(10 ether, 12_000e6);
// ETH 跌到 $1,300
// 加权抵押: 10 * 1300 * 0.85 = $11,050
// 债务: $12,000
// HF = 11050/12000 = 0.92 < 1
ethPriceFeed.setPrice(1300e8);
uint256 hfBefore = pool.getHealthFactor(alice);
assertLt(hfBefore, 1e18, "Should be underwater");
// 清算人偿还 6,000 USDC (50%)
uint256 debtToRepay = 6_000e6;
vm.prank(liquidator);
pool.liquidate(alice, address(usdc), address(weth), debtToRepay);
// 验证清算人获得了抵押品
uint256 liquidatorWETH = weth.balanceOf(liquidator);
assertGt(liquidatorWETH, 0, "Liquidator should receive collateral");
// 验证清算奖励 (5%)
// 债务价值: $6,000
// 含奖励抵押品价值: $6,000 × 1.05 = $6,300
// 获得 ETH: $6,300 / $1,300 ≈ 4.846 ETH
uint256 expectedCollateral = 6300e18 / 1300; // 约 4.846 ETH
assertApproxEqRel(
liquidatorWETH,
expectedCollateral,
1e16 // 1% 误差
);
// 验证 Alice 的 HF 改善
uint256 hfAfter = pool.getHealthFactor(alice);
assertGt(hfAfter, hfBefore, "HF should improve after liquidation");
}
/// @notice 测试 2:HF > 1 时不能清算
function test_Liquidate_HealthyPosition_Reverts() public {
_setupAliceBorrowPosition(10 ether, 8_000e6);
// ETH = $2000, HF > 1
vm.prank(liquidator);
vm.expectRevert(
MiniLendingPool.HealthFactorAboveThreshold.selector
);
pool.liquidate(alice, address(usdc), address(weth), 4_000e6);
}
/// @notice 测试 3:超过 close factor 的清算被拒绝
function test_Liquidate_ExceedsCloseFactor_Reverts() public {
_setupAliceBorrowPosition(10 ether, 12_000e6);
ethPriceFeed.setPrice(1300e8);
// 尝试清算 60% (> 50% close factor)
uint256 debtToRepay = 7_200e6;
vm.prank(liquidator);
vm.expectRevert(
MiniLendingPool.LiquidationAmountExceedsCloseFactor.selector
);
pool.liquidate(alice, address(usdc), address(weth), debtToRepay);
}
/// @notice 测试 4:严重水下时可以 100% 清算
function test_Liquidate_SeverelyUnderwater_FullClose() public {
_setupAliceBorrowPosition(10 ether, 15_000e6);
// ETH 暴跌到 $1,000
// HF = (10 * 1000 * 0.85) / 15000 = 0.567 < 0.95
ethPriceFeed.setPrice(1000e8);
uint256 hf = pool.getHealthFactor(alice);
assertLt(hf, 95e16, "HF should be < 0.95");
// 可以清算 100%
uint256 fullDebt = pool.getUserDebt(alice, address(usdc));
vm.prank(liquidator);
pool.liquidate(alice, address(usdc), address(weth), fullDebt);
// Alice 应该无债务了
assertEq(pool.getUserDebt(alice, address(usdc)), 0);
}
/// @notice 测试 5:坏账处理
function test_Liquidate_BadDebt() public {
_setupAliceBorrowPosition(10 ether, 15_000e6);
// ETH 暴跌到 $500
// 抵押品: 10 * 500 = $5,000
// 债务: $15,000
// 坏账: $10,000
ethPriceFeed.setPrice(500e8);
uint256 reserveBefore = pool.getReserveBalance(address(usdc));
vm.prank(liquidator);
pool.liquidate(alice, address(usdc), address(weth), 15_000e6);
// 清算人应该获得了全部 10 ETH
uint256 liquidatorWETH = weth.balanceOf(liquidator);
assertEq(liquidatorWETH, 10 ether);
// 应该有坏账被记录
// 具体坏账金额取决于清算人实际偿还了多少
}
/// @notice 测试 6:带利息的清算
function test_Liquidate_WithAccruedInterest() public {
_setupAliceBorrowPosition(10 ether, 12_000e6);
// 时间推进 180 天
vm.warp(block.timestamp + 180 days);
// 利息累计后债务增加
uint256 debtWithInterest = pool.getUserDebt(alice, address(usdc));
assertGt(debtWithInterest, 12_000e6, "Debt should have grown");
// ETH 价格下跌触发清算
ethPriceFeed.setPrice(1400e8);
uint256 hf = pool.getHealthFactor(alice);
if (hf < 1e18) {
vm.prank(liquidator);
pool.liquidate(
alice,
address(usdc),
address(weth),
debtWithInterest / 2 // 50% 清算
);
}
}
/// @notice 测试 7:清算利润计算验证
function test_Liquidate_ProfitabilityCheck() public {
_setupAliceBorrowPosition(10 ether, 12_000e6);
ethPriceFeed.setPrice(1300e8);
uint256 debtToRepay = 5_000e6;
uint256 liquidatorUSDCBefore = usdc.balanceOf(liquidator);
uint256 liquidatorWETHBefore = weth.balanceOf(liquidator);
vm.prank(liquidator);
pool.liquidate(alice, address(usdc), address(weth), debtToRepay);
uint256 usdcSpent = liquidatorUSDCBefore - usdc.balanceOf(liquidator);
uint256 wethReceived = weth.balanceOf(liquidator) - liquidatorWETHBefore;
// 计算利润
uint256 wethValueUSD = wethReceived * 1300 / 1e18; // 简化计算
uint256 profit = wethValueUSD - usdcSpent / 1e6;
// 利润应该约等于 5% 的清算奖励
uint256 expectedProfit = usdcSpent / 1e6 * 5 / 100;
assertApproxEqRel(
profit,
expectedProfit,
5e16 // 5% 误差
);
}
// 辅助函数
function _setupAliceBorrowPosition(uint256 ethAmount, uint256 usdcBorrow) internal {
vm.startPrank(alice);
weth.approve(address(pool), ethAmount);
pool.supply(address(weth), ethAmount);
pool.borrow(address(usdc), usdcBorrow);
vm.stopPrank();
}
}
4. 清算机器人经济学
清算机器人的盈利模型:
收入:
- 清算奖励 (通常 5-10%)
- 例如:清算 $100,000 债务 → 获得 $105,000 抵押品 → 利润 $5,000
成本:
- Gas 费用:$10-100 (以太坊主网)
- 闪电贷手续费:0.09% (Aave) 或 0 (dYdX)
- 监控基础设施:~$200/月
- 价格滑点(卖出抵押品时):0.1-1%
净利润 = 清算奖励 - Gas - 闪电贷费 - 滑点
盈利条件:
清算金额 > 约 $2,000 (以太坊主网)
清算金额 > 约 $50 (L2)
竞争格局:
┌────────────────────────────────────┐
│ 清算 MEV 竞争 │
│ │
│ 1. 监控:订阅 pending txs │
│ 2. 检测:计算所有借款人的 HF │
│ 3. 抢跑:用高 Gas 费提交清算交易 │
│ 4. Flashbots:通过 MEV 拍卖 │
│ │
│ 利润压缩:竞争导致清算利润趋近 0 │
└────────────────────────────────────┘
/// @title 清算机器人示例(闪电贷清算)
contract FlashLiquidator {
IPool public lendingPool;
IFlashLoanProvider public flashLoan;
ISwapRouter public swapRouter;
/// @notice 使用闪电贷执行清算
function executeLiquidation(
address borrower,
address debtAsset,
address collateralAsset,
uint256 debtToRepay
) external {
// 步骤 1:闪电贷借入债务 Token
bytes memory params = abi.encode(
borrower, debtAsset, collateralAsset, debtToRepay
);
flashLoan.flashLoan(address(this), debtAsset, debtToRepay, params);
}
/// @notice 闪电贷回调
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata params
) external returns (bytes32) {
(address borrower, address debtAsset, address collateralAsset, uint256 debtToRepay) =
abi.decode(params, (address, address, address, uint256));
// 步骤 2:批准并执行清算
IERC20(debtAsset).approve(address(lendingPool), debtToRepay);
lendingPool.liquidate(borrower, debtAsset, collateralAsset, debtToRepay);
// 步骤 3:将获得的抵押品换成债务 Token
uint256 collateralReceived = IERC20(collateralAsset).balanceOf(address(this));
IERC20(collateralAsset).approve(address(swapRouter), collateralReceived);
swapRouter.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: collateralAsset,
tokenOut: debtAsset,
fee: 3000, // 0.3%
recipient: address(this),
deadline: block.timestamp,
amountIn: collateralReceived,
amountOutMinimum: amount + fee, // 至少够还闪电贷
sqrtPriceLimitX96: 0
})
);
// 步骤 4:还闪电贷(amount + fee)
IERC20(debtAsset).approve(msg.sender, amount + fee);
// 利润留在合约中
return keccak256("FlashLoan");
}
/// @notice 提取利润
function withdrawProfit(address token) external {
// onlyOwner
uint256 balance = IERC20(token).balanceOf(address(this));
IERC20(token).transfer(msg.sender, balance);
}
}
关键要点总结
| 要点 | 说明 |
|---|---|
| 清算保护协议 | 在坏账发生前通过第三方介入偿还债务 |
| 部分清算 | 默认最多清算 50%,减少市场冲击 |
| 清算奖励 | 5-10% 激励清算人快速行动 |
| 坏账处理 | 储备金覆盖 → 社会化损失 → Safety Module |
| 清算级联 | 大规模清算可能形成死亡螺旋 |
| MEV 竞争 | 清算利润被 MEV 搜索者竞争压缩 |
常见误区
- "清算对借款人是惩罚" — 清算实际上保护了整个协议和所有存款人
- "清算总是有利润的" — 在坏账场景下清算人可能无法获得足够奖励
- "清算越快越好" — 过于激进的清算可能加剧价格下跌(清算级联)
- "50% close factor 是固定的" — 不同协议有不同的策略,Aave V3 根据 HF 动态调整
- "清算机器人很容易赚钱" — MEV 竞争非常激烈,利润正在被压缩
面试关联
面试题:借贷协议的清算机制如何设计?需要考虑哪些问题?
30 秒回答: 清算在 HF < 1 时触发,允许第三方代为还款并获得折价抵押品(5-10% 奖励)。关键设计包括:部分清算限制(50%)防止过度清算、坏账处理机制(储备金/社会化损失)、以及清算奖励的平衡(太低无人清算,太高损害借款人)。
2 分钟回答: 清算机制是借贷协议的安全基石。设计时需要考虑六个维度:第一是触发条件——基于健康因子(加权抵押品/总债务),低于 1 即可清算。第二是清算比例——通常限制为 50% 部分清算,减少对借款人和市场的冲击;严重水下时(HF < 0.95)允许 100% 清算。第三是清算奖励——通常 5% 作为清算人的激励,需要平衡:太低没人清算,太高损害借款人。第四是坏账处理——当抵押品已不足以覆盖债务时,依次从协议储备金、Safety Module、或社会化损失中处理。第五是清算级联风险——大规模清算导致抵押品抛售、价格下跌、更多清算的死亡螺旋。可以通过设置合理的 LTV 和清算阈值之间的缓冲区来缓解。第六是 MEV 影响——清算交易的利润会被 MEV 搜索者竞争,实际上形成了高效的清算市场。
追问准备
- Q: 如果没人来清算怎么办? A: 协议可以自建清算机器人、与 Keeper 网络集成(如 Gelato/Chainlink Keepers)、或者设置更高的清算奖励吸引清算人。
- Q: 坏账如何社会化? A: 从 totalSupplyAssets 中直接扣除,相当于所有存款人的份额对应的资产减少了。Aave 使用 Safety Module(AAVE 质押者)作为第一道防线。
参考资源
| 资源 | 说明 |
|---|---|
| Aave V3 清算文档 | 生产级清算设计 |
| Compound 清算文档 | Compound 清算机制 |
| Liquidation MEV 分析 | 学术论文 |
| Euler Finance 清算 | 创新的清算设计 |
| Morpho Blue 清算 | 极简清算实现 |
| DeFi 坏账分析 | 监控协议健康状态 |