Files
assetxContracts/doc/YT-Lending借贷系统操作流程图.md
2025-12-29 15:38:25 +08:00

1951 lines
171 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# YT Lending 借贷系统操作流程图
## 重要说明
本系统使用 USDC 作为基础借贷代币baseTokenYT 资产代币作为抵押品。USDC 价格通过 Chainlink 价格预言机实时获取YT 代币价格从其 Vault 合约的 `ytPrice()` 方法读取。
## 目录
1. [存入 USDCSupply](#1-存入-usdcsupply)
2. [提取 USDCWithdraw](#2-提取-usdcwithdraw)
3. [存入抵押品SupplyCollateral](#3-存入抵押品supplycollateral)
4. [取出抵押品WithdrawCollateral](#4-取出抵押品withdrawcollateral)
5. [借款流程Borrow](#5-借款流程borrow)
6. [清算流程Absorb](#6-清算流程absorb)
7. [购买清算抵押品BuyCollateral](#7-购买清算抵押品buycollateral)
8. [利息计提机制](#8-利息计提机制)
9. [储备金管理WithdrawReserves](#9-储备金管理withdrawreserves)
---
## 1. 存入 USDCSupply
```
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (Alice) │
│ 持有: 10,000 USDC │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 授权 USDC 给 Lending
│ USDC.approve(lending, 10000e6)
┌─────────────────────────────────────────────────────────────────────┐
│ USDC 授权检查 │
│ ✓ allowance[Alice][Lending] >= 10,000 USDC │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 调用 supply(10000e6)
┌─────────────────────────────────────────────────────────────────────┐
│ Lending.supply() │
│ ───────────────────────────────────────────────────────────────── │
│ function supply(uint256 amount) │
│ • 非重入保护: nonReentrant │
│ • 暂停检查: whenNotPaused │
│ • 参数: 10,000 USDC (10000e6) │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 计提利息
│ accrueInterest()
┌─────────────────────────────────────────────────────────────────────┐
│ 利息计提 │
│ ───────────────────────────────────────────────────────────────── │
│ 计算自上次计提以来的时间: │
│ timeElapsed = block.timestamp - lastAccrualTime │
│ │
│ 如果 timeElapsed > 0: │
│ ① 计算当前利用率: │
│ totalSupply = totalSupplyBase × supplyIndex / 1e18 │
│ totalBorrow = totalBorrowBase × borrowIndex / 1e18 │
│ utilization = totalBorrow / totalSupply │
│ │
│ ② 根据利用率和拐点计算供应利率和借款利率(每秒): │
│ supplyRate = getSupplyRate(utilization, ...) │
│ borrowRate = getBorrowRate(utilization, ...) │
│ │
│ ③ 更新利息累计因子: │
│ supplyIndex += supplyIndex × supplyRate × timeElapsed / 1e18 │
│ borrowIndex += borrowIndex × borrowRate × timeElapsed / 1e18 │
│ │
│ ④ 更新上次计提时间: │
│ lastAccrualTime = block.timestamp │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 转入 USDC
┌─────────────────────────────────────────────────────────────────────┐
│ 代币转移 │
│ ───────────────────────────────────────────────────────────────── │
│ IERC20(baseToken).transferFrom( │
│ Alice, // 用户地址 │
│ address(this), // Lending 地址 │
│ 10000e6 // 10,000 USDC │
│ ) │
│ │
│ 结果: │
│ • Alice USDC 余额: 10,000 → 0 │
│ • Lending USDC 余额: +10,000 │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 计算用户余额变化
┌─────────────────────────────────────────────────────────────────────┐
│ 余额计算 │
│ ───────────────────────────────────────────────────────────────── │
│ 获取用户当前本金: │
│ oldPrincipal = userBasic[Alice].principal // 假设为 0新用户
│ │
│ 计算旧余额(含利息): │
│ index = oldPrincipal >= 0 ? supplyIndex : borrowIndex │
│ = supplyIndex (因为是0属于存款) │
│ oldBalance = oldPrincipal × index / 1e18 = 0 │
│ │
│ 计算新余额: │
│ newBalance = oldBalance + 10000e6 = 10000e6 │
│ │
│ 确定新余额使用的索引: │
│ newIndex = newBalance >= 0 ? supplyIndex : borrowIndex │
│ = supplyIndex (正数,是存款) │
│ │
│ 转换为新本金: │
│ newPrincipal = (newBalance × 1e18) / newIndex │
│ = (10000e6 × 1e18) / supplyIndex │
│ = 10000e6 (假设 supplyIndex = 1e18) │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 计算状态变化量
┌─────────────────────────────────────────────────────────────────────┐
│ 计算 repayAmount 和 supplyAmount │
│ ───────────────────────────────────────────────────────────────── │
│ 调用: repayAndSupplyAmount(oldPrincipal=0, newPrincipal=10000e6) │
│ │
│ 判断逻辑: │
│ • newPrincipal (10000e6) > oldPrincipal (0) ✓ │
│ • newPrincipal >= 0 ✓ │
│ • oldPrincipal >= 0 ✓ │
│ → 两个都是正数(增加存款) │
│ │
│ 结果: │
│ repayAmount = 0 │
│ supplyAmount = newPrincipal - oldPrincipal = 10000e6 │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 更新全局状态
┌─────────────────────────────────────────────────────────────────────┐
│ 更新全局余额 │
│ ───────────────────────────────────────────────────────────────── │
│ totalBorrowBase -= repayAmount (0) │
│ totalSupplyBase += supplyAmount (10000e6) │
│ │
│ 更新前: │
│ • totalSupplyBase: 0 │
│ • totalBorrowBase: 0 │
│ │
│ 更新后: │
│ • totalSupplyBase: 10000e6 │
│ • totalBorrowBase: 0 │
└────────────────────────────┬────────────────────────────────────────┘
│ 8. 更新用户本金
┌─────────────────────────────────────────────────────────────────────┐
│ 更新用户状态 │
│ ───────────────────────────────────────────────────────────────── │
│ userBasic[Alice].principal = newPrincipal = 10000e6 │
│ │
│ 用户状态: │
│ • principal: 0 → 10000e6 (正数 = 存款本金) │
└────────────────────────────┬────────────────────────────────────────┘
│ 9. 触发事件
┌─────────────────────────────────────────────────────────────────────┐
│ 事件记录 │
│ ───────────────────────────────────────────────────────────────── │
│ emit Supply(Alice, Alice, 10000e6) │
└────────────────────────────┬────────────────────────────────────────┘
│ 10. 存款完成
┌─────────────────────────────────────────────────────────────────────┐
│ 存款完成 │
│ ───────────────────────────────────────────────────────────────── │
│ Alice 最终状态: │
│ • USDC 余额: 0 │
│ • Lending principal: 10000e6 (存款本金) │
│ • 实际余额 (含利息): principal × supplyIndex / 1e18 │
│ = 10000e6 (刚存入,尚未计息) │
│ │
│ 协议状态: │
│ • totalSupplyBase: 10000e6 │
│ • totalBorrowBase: 0 │
│ • 实际总供应: 10000e6 (含利息) │
│ • 实际总借款: 0 │
│ • 利用率: 0% │
│ │
│ 收益说明: │
│ 随着时间推移supplyIndex 会增长 │
│ Alice 的实际余额 = principal × supplyIndex / 1e18 会增加 │
│ 这就是利息收益的体现 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 2. 提取 USDCWithdraw
```
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (Alice) │
│ 当前状态: │
│ • principal: 10000e6 │
│ • supplyIndex: 1.1e18 (经过一段时间,利息累积了 10%) │
│ • 实际余额: 10000e6 × 1.1e18 / 1e18 = 11000e6 USDC │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 withdraw(5000e6)
│ 提取 5,000 USDC
┌─────────────────────────────────────────────────────────────────────┐
│ Lending.withdraw() │
│ ───────────────────────────────────────────────────────────────── │
│ function withdraw(uint256 amount) │
│ • 非重入保护: nonReentrant │
│ • 暂停检查: whenNotPaused │
│ • 参数: 5,000 USDC (5000e6) │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 计提利息
│ accrueInterest()
┌─────────────────────────────────────────────────────────────────────┐
│ 利息计提 │
│ (过程同 supply更新 supplyIndex 和 borrowIndex
│ 假设更新后 supplyIndex = 1.1e18 (已包含最新利息) │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 计算余额变化
┌─────────────────────────────────────────────────────────────────────┐
│ 余额计算 │
│ ───────────────────────────────────────────────────────────────── │
│ 获取用户当前本金: │
│ oldPrincipal = userBasic[Alice].principal = 10000e6 │
│ │
│ 计算旧余额(含利息): │
│ index = oldPrincipal >= 0 ? supplyIndex : borrowIndex │
│ = supplyIndex (正数,是存款) │
│ oldBalance = oldPrincipal × index / 1e18 │
│ = 10000e6 × 1.1e18 / 1e18 = 11000e6 │
│ │
│ 计算新余额(减去提取额): │
│ newBalance = oldBalance - 5000e6 = 6000e6 │
│ │
│ 确定新余额使用的索引: │
│ newIndex = newBalance >= 0 ? supplyIndex : borrowIndex │
│ = supplyIndex (正数,仍是存款) │
│ │
│ 转换为新本金: │
│ newPrincipal = (newBalance × 1e18) / newIndex │
│ = (6000e6 × 1e18) / 1.1e18 │
│ = 5454.54e6 (约) │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 计算状态变化量
┌─────────────────────────────────────────────────────────────────────┐
│ 计算 withdrawAmount 和 borrowAmount │
│ ───────────────────────────────────────────────────────────────── │
│ 调用: withdrawAndBorrowAmount( │
│ oldPrincipal=10000e6, │
│ newPrincipal=5454.54e6 │
│ ) │
│ │
│ 判断逻辑: │
│ • newPrincipal (5454.54e6) < oldPrincipal (10000e6) ✓ │
│ • newPrincipal >= 0 ✓ │
│ → 还是正数(提取存款) │
│ │
│ 结果: │
│ withdrawAmount = oldPrincipal - newPrincipal = 4545.46e6 │
│ borrowAmount = 0 │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 更新全局状态
┌─────────────────────────────────────────────────────────────────────┐
│ 更新全局余额 │
│ ───────────────────────────────────────────────────────────────── │
│ totalSupplyBase -= withdrawAmount (4545.46e6) │
│ totalBorrowBase += borrowAmount (0) │
│ │
│ 更新后: │
│ • totalSupplyBase: 10000e6 → 5454.54e6 │
│ • totalBorrowBase: 0 (不变) │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 更新用户本金
┌─────────────────────────────────────────────────────────────────────┐
│ 更新用户状态 │
│ ───────────────────────────────────────────────────────────────── │
│ userBasic[Alice].principal = newPrincipal = 5454.54e6 │
│ │
│ 用户状态: │
│ • principal: 10000e6 → 5454.54e6 │
│ • 实际余额: 5454.54e6 × 1.1e18 / 1e18 = 6000e6 USDC │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 转出 USDC
┌─────────────────────────────────────────────────────────────────────┐
│ 代币转移 │
│ ───────────────────────────────────────────────────────────────── │
│ IERC20(baseToken).safeTransfer( │
│ Alice, // 用户地址 │
│ 5000e6 // 5,000 USDC │
│ ) │
│ │
│ 结果: │
│ • Lending USDC 余额: -5000e6 │
│ • Alice USDC 余额: +5000e6 │
└────────────────────────────┬────────────────────────────────────────┘
│ 8. 触发事件
┌─────────────────────────────────────────────────────────────────────┐
│ 事件记录 │
│ ───────────────────────────────────────────────────────────────── │
│ emit Withdraw(Alice, Alice, 5000e6) │
└────────────────────────────┬────────────────────────────────────────┘
│ 9. 提取完成
┌─────────────────────────────────────────────────────────────────────┐
│ 提取完成 │
│ ───────────────────────────────────────────────────────────────── │
│ Alice 最终状态: │
│ • USDC 余额: +5000e6 │
│ • Lending principal: 5454.54e6 │
│ • 实际余额 (含利息): 6000e6 USDC │
│ │
│ 收益说明: │
│ • 初始存入: 10000 USDC │
│ • 利息收益: 1000 USDC (10%) │
│ • 提取: 5000 USDC │
│ • 剩余: 6000 USDC (包含利息) │
│ │
│ 注意: │
│ 如果提取金额大于实际余额,会自动转为借款 │
│ 例如:提取 12000 USDC超过 11000 USDC 余额 │
│ → principal 会变成负数(-1000表示借款 1000 USDC │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 3. 存入抵押品SupplyCollateral
```
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (Bob) │
│ 持有: 1000 YT-A 代币 │
│ YT-A 价格: $2000 (2000e30) │
│ 价值: $2,000,000 │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 授权 YT-A 给 Lending
│ YTA.approve(lending, 1000e18)
┌─────────────────────────────────────────────────────────────────────┐
│ YT-A 授权检查 │
│ ✓ allowance[Bob][Lending] >= 1000 YT-A │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 调用 supplyCollateral(YTA, 1000e18)
┌─────────────────────────────────────────────────────────────────────┐
│ Lending.supplyCollateral() │
│ ───────────────────────────────────────────────────────────────── │
│ function supplyCollateral(address asset, uint256 amount) │
│ • 非重入保护: nonReentrant │
│ • 暂停检查: whenNotPaused │
│ • 参数: asset=YTA, amount=1000e18 │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 验证资产配置
┌─────────────────────────────────────────────────────────────────────┐
│ 资产配置检查 │
│ ───────────────────────────────────────────────────────────────── │
│ AssetConfig memory config = assetConfigs[YTA] │
│ │
│ 检查: │
│ • config.asset != address(0) ✓ (资产已配置) │
│ │
│ 配置示例: │
│ • asset: YTA │
│ • decimals: 18 │
│ • borrowCollateralFactor: 0.7e18 (70% LTV) │
│ • liquidateCollateralFactor: 0.75e18 (75% 清算阈值) │
│ • liquidationFactor: 0.9e18 (清算时价值打 90 折) │
│ • supplyCap: 10000e18 (最多可存入 10,000 YT-A) │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 检查供应上限
┌─────────────────────────────────────────────────────────────────────┐
│ 供应上限检查 │
│ ───────────────────────────────────────────────────────────────── │
│ uint256 newTotal = userCollateral[Bob][YTA] + 1000e18 │
│ = 0 + 1000e18 = 1000e18 │
│ │
│ if (newTotal > config.supplyCap) revert SupplyCapExceeded() │
│ • 1000e18 <= 10000e18 ✓ 通过 │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 转入 YT-A 代币
┌─────────────────────────────────────────────────────────────────────┐
│ 代币转移 │
│ ───────────────────────────────────────────────────────────────── │
│ IERC20(asset).transferFrom( │
│ Bob, // 用户地址 │
│ address(this), // Lending 地址 │
│ 1000e18 // 1000 YT-A │
│ ) │
│ │
│ 结果: │
│ • Bob YT-A 余额: 1000 → 0 │
│ • Lending YT-A 余额: +1000 │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 更新用户抵押品余额
┌─────────────────────────────────────────────────────────────────────┐
│ 更新抵押品记录 │
│ ───────────────────────────────────────────────────────────────── │
│ userCollateral[Bob][YTA] += 1000e18 │
│ │
│ 更新后: │
│ • userCollateral[Bob][YTA]: 0 → 1000e18 │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 触发事件
┌─────────────────────────────────────────────────────────────────────┐
│ 事件记录 │
│ ───────────────────────────────────────────────────────────────── │
│ emit SupplyCollateral(Bob, Bob, YTA, 1000e18) │
└────────────────────────────┬────────────────────────────────────────┘
│ 8. 存入完成
┌─────────────────────────────────────────────────────────────────────┐
│ 抵押品存入完成 │
│ ───────────────────────────────────────────────────────────────── │
│ Bob 最终状态: │
│ • YT-A 余额: 0 │
│ • 抵押品: 1000 YT-A (在 Lending 中) │
│ • 抵押品价值: $2,000,000 │
│ │
│ 借款能力计算: │
│ • 抵押品价值: 1000 × $2000 = $2,000,000 │
│ • borrowCollateralFactor: 70% │
│ • 最大借款额: $2,000,000 × 70% = $1,400,000 │
│ • 以 USDC 计算 (假设 USDC = $1): │
│ 最大借款: 1,400,000 USDC │
│ │
│ 清算阈值: │
│ • liquidateCollateralFactor: 75% │
│ • 清算阈值: $2,000,000 × 75% = $1,500,000 │
│ • 当债务超过 $1,500,000 时会被清算 │
│ │
│ 注意: │
│ • 存入抵押品不计提利息(不影响债务) │
│ • 抵押品价值根据 LendingPriceFeed.getPrice() 实时获取 │
│ • YT-A 价格从 YTAssetVault.ytPrice() 获取 │
│ • USDC 价格从 Chainlink 获取 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 4. 取出抵押品WithdrawCollateral
```
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (Bob) │
│ 当前状态: │
│ • 抵押品: 1000 YT-A │
│ • 借款: 0 USDC (未借款) │
│ 计划: 取出 500 YT-A │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 withdrawCollateral(YTA, 500e18)
┌─────────────────────────────────────────────────────────────────────┐
│ Lending.withdrawCollateral() │
│ ───────────────────────────────────────────────────────────────── │
│ function withdrawCollateral(address asset, uint256 amount) │
│ • 非重入保护: nonReentrant │
│ • 暂停检查: whenNotPaused │
│ • 参数: asset=YTA, amount=500e18 │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 计提利息
│ accrueInterest()
┌─────────────────────────────────────────────────────────────────────┐
│ 利息计提 │
│ (过程同前,更新 supplyIndex 和 borrowIndex
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 检查余额
┌─────────────────────────────────────────────────────────────────────┐
│ 余额检查 │
│ ───────────────────────────────────────────────────────────────── │
│ if (userCollateral[Bob][YTA] < 500e18) revert InsufficientBalance()│
│ • 1000e18 >= 500e18 ✓ 通过 │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 更新抵押品余额
┌─────────────────────────────────────────────────────────────────────┐
│ 更新抵押品记录 │
│ ───────────────────────────────────────────────────────────────── │
│ userCollateral[Bob][YTA] -= 500e18 │
│ │
│ 更新后: │
│ • userCollateral[Bob][YTA]: 1000e18 → 500e18 │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 检查偿付能力(如果有债务)
┌─────────────────────────────────────────────────────────────────────┐
│ 偿付能力检查 │
│ ───────────────────────────────────────────────────────────────── │
│ int104 principal = userBasic[Bob].principal // 假设为 0 │
│ │
│ if (principal < 0) { │
│ if (!_isSolvent(Bob)) revert InsufficientCollateral() │
│ } │
│ │
│ 本例中 principal = 0无债务跳过检查 ✓ │
│ │
│ 偿付能力检查逻辑 (_isSolvent): │
│ ① 计算实际债务 (含利息): │
│ balance = principal × borrowIndex / 1e18 │
│ debt = -balance (转为正数) │
│ │
│ ② 计算债务价值 (USD): │
│ basePrice = LendingPriceFeed.getPrice(USDC) │
│ debtValue = (debt × basePrice) / 10^usdcDecimals │
│ │
│ ③ 计算抵押品借款能力: │
│ borrowCapacity = _getCollateralValue(account) │
│ • 遍历所有抵押品资产 │
│ • 计算每个资产的价值 (USD) │
│ • 乘以 borrowCollateralFactor │
│ • 汇总所有抵押品的借款能力 │
│ │
│ ④ 比较: │
│ return borrowCapacity >= debtValue │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 转出 YT-A 代币
┌─────────────────────────────────────────────────────────────────────┐
│ 代币转移 │
│ ───────────────────────────────────────────────────────────────── │
│ IERC20(asset).safeTransfer( │
│ Bob, // 用户地址 │
│ 500e18 // 500 YT-A │
│ ) │
│ │
│ 结果: │
│ • Lending YT-A 余额: -500e18 │
│ • Bob YT-A 余额: +500e18 │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 触发事件
┌─────────────────────────────────────────────────────────────────────┐
│ 事件记录 │
│ ───────────────────────────────────────────────────────────────── │
│ emit WithdrawCollateral(Bob, Bob, YTA, 500e18) │
└────────────────────────────┬────────────────────────────────────────┘
│ 8. 取出完成
┌─────────────────────────────────────────────────────────────────────┐
│ 抵押品取出完成 │
│ ───────────────────────────────────────────────────────────────── │
│ Bob 最终状态: │
│ • YT-A 余额: +500e18 │
│ • 抵押品: 500 YT-A (在 Lending 中) │
│ • 剩余抵押品价值: $1,000,000 │
│ │
│ 借款能力变化: │
│ • 之前: $2,000,000 × 70% = $1,400,000 │
│ • 现在: $1,000,000 × 70% = $700,000 │
│ │
│ 注意: │
│ 如果 Bob 有借款,取出抵押品后必须确保: │
│ 借款能力 (borrowCapacity) >= 债务价值 (debtValue) │
│ 否则交易会 revert │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 5. 借款流程Withdraw如果余额不足会自动借款
```
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (Bob) │
│ 当前状态: │
│ • 抵押品: 1000 YT-A (价值 $2,000,000) │
│ • principal: 0 (无存款/借款) │
│ • 借款能力: $2,000,000 × 70% = $1,400,000 USDC │
│ 计划: 借款 1,000,000 USDC │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 withdraw(1000000e6)进行借款
┌─────────────────────────────────────────────────────────────────────┐
│ Lending.withdraw() │
│ ───────────────────────────────────────────────────────────────── │
│ function withdraw(uint256 amount) │
│ • 非重入保护: nonReentrant │
│ • 暂停检查: whenNotPaused │
│ • 参数: 1,000,000 USDC (1000000e6) │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 计提利息
│ accrueInterest()
┌─────────────────────────────────────────────────────────────────────┐
│ 利息计提 │
│ 更新 supplyIndex 和 borrowIndex 到最新状态 │
│ 假设: borrowIndex = 1e18 (初始状态) │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 计算余额变化
┌─────────────────────────────────────────────────────────────────────┐
│ 余额计算 │
│ ───────────────────────────────────────────────────────────────── │
│ 获取用户当前本金: │
│ oldPrincipal = userBasic[Bob].principal = 0 │
│ │
│ 计算旧余额(含利息): │
│ index = oldPrincipal >= 0 ? supplyIndex : borrowIndex │
│ = supplyIndex (0 视为存款) │
│ oldBalance = 0 × supplyIndex / 1e18 = 0 │
│ │
│ 计算新余额(减去借款额): │
│ newBalance = oldBalance - 1000000e6 = -1000000e6 │
│ │
│ 检查最小借款额: │
│ if (newBalance < 0 && -newBalance < baseBorrowMin) │
│ revert BorrowTooSmall() │
│ • baseBorrowMin = 100e6 (最小借款 100 USDC) │
│ • 1000000e6 >= 100e6 ✓ 通过 │
│ │
│ 确定新余额使用的索引: │
│ newIndex = newBalance >= 0 ? supplyIndex : borrowIndex │
│ = borrowIndex (负数,是借款) │
│ │
│ 转换为新本金: │
│ newPrincipal = (newBalance × 1e18) / newIndex │
│ = (-1000000e6 × 1e18) / 1e18 │
│ = -1000000e6 │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 计算状态变化量
┌─────────────────────────────────────────────────────────────────────┐
│ 计算 withdrawAmount 和 borrowAmount │
│ ───────────────────────────────────────────────────────────────── │
│ 调用: withdrawAndBorrowAmount( │
│ oldPrincipal=0, │
│ newPrincipal=-1000000e6 │
│ ) │
│ │
│ 判断逻辑: │
│ • newPrincipal (-1000000e6) < oldPrincipal (0) ✓ │
│ • newPrincipal < 0 ✓ │
│ • oldPrincipal >= 0 ✓ │
│ → 从正数或0变负数提取所有存款并借款
│ │
│ 结果: │
│ withdrawAmount = oldPrincipal = 0 │
│ borrowAmount = -newPrincipal = 1000000e6 │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 更新全局状态
┌─────────────────────────────────────────────────────────────────────┐
│ 更新全局余额 │
│ ───────────────────────────────────────────────────────────────── │
│ totalSupplyBase -= withdrawAmount (0) │
│ totalBorrowBase += borrowAmount (1000000e6) │
│ │
│ 更新后: │
│ • totalSupplyBase: 不变 │
│ • totalBorrowBase: 0 → 1000000e6 │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 更新用户本金
┌─────────────────────────────────────────────────────────────────────┐
│ 更新用户状态 │
│ ───────────────────────────────────────────────────────────────── │
│ userBasic[Bob].principal = newPrincipal = -1000000e6 │
│ │
│ 用户状态: │
│ • principal: 0 → -1000000e6 (负数 = 借款本金) │
│ • 实际债务: -1000000e6 × borrowIndex / 1e18 = -1000000e6 │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 检查抵押品是否充足
┌─────────────────────────────────────────────────────────────────────┐
│ 偿付能力检查 (_isSolvent) │
│ ───────────────────────────────────────────────────────────────── │
│ ① 计算债务价值 (USD): │
│ debt = 1000000e6 │
│ basePrice = LendingPriceFeed.getPrice(USDC) │
│ = 1e30 (假设 USDC = $1.00) │
│ baseDecimals = 6 │
│ debtValue = (1000000e6 × 1e30) / 10^6 │
│ = 1000000e30 ($1,000,000 USD) │
│ │
│ ② 计算抵押品借款能力 (_getCollateralValue): │
│ 遍历所有抵押品: │
│ • YT-A: 1000e18 │
│ - price = LendingPriceFeed.getPrice(YTA) │
│ = IYTAssetVault(YTA).ytPrice() │
│ = 2000e30 ($2000) │
│ - decimals = 18 │
│ - value = (1000e18 × 2000e30) / 10^18 │
│ = 2000000e30 ($2,000,000 USD) │
│ - borrowCollateralFactor = 0.7e18 (70%) │
│ - borrowCapacity = 2000000e30 × 0.7e18 / 1e18 │
│ = 1400000e30 ($1,400,000 USD) │
│ │
│ ③ 比较: │
│ borrowCapacity (1400000e30) >= debtValue (1000000e30) ✓ │
│ → 抵押品充足,可以借款 │
│ │
│ 如果不充足,会 revert InsufficientCollateral() │
└────────────────────────────┬────────────────────────────────────────┘
│ 8. 转出 USDC
┌─────────────────────────────────────────────────────────────────────┐
│ 代币转移 │
│ ───────────────────────────────────────────────────────────────── │
│ IERC20(baseToken).safeTransfer( │
│ Bob, // 用户地址 │
│ 1000000e6 // 1,000,000 USDC │
│ ) │
│ │
│ 结果: │
│ • Lending USDC 余额: -1000000e6 │
│ • Bob USDC 余额: +1000000e6 │
└────────────────────────────┬────────────────────────────────────────┘
│ 9. 触发事件
┌─────────────────────────────────────────────────────────────────────┐
│ 事件记录 │
│ ───────────────────────────────────────────────────────────────── │
│ emit Withdraw(Bob, Bob, 1000000e6) │
└────────────────────────────┬────────────────────────────────────────┘
│ 10. 借款完成
┌─────────────────────────────────────────────────────────────────────┐
│ 借款完成 │
│ ───────────────────────────────────────────────────────────────── │
│ Bob 最终状态: │
│ • USDC 余额: +1,000,000 USDC │
│ • Lending principal: -1000000e6 (负数表示借款) │
│ • 实际债务: -1000000e6 × borrowIndex / 1e18 │
│ • 抵押品: 1000 YT-A │
│ │
│ 借款状态: │
│ • 借款金额: 1,000,000 USDC │
│ • 借款价值: $1,000,000 │
│ • 抵押品价值: $2,000,000 │
│ • LTV (Loan-to-Value): 50% │
│ • 借款能力使用率: 1,000,000 / 1,400,000 = 71.4% │
│ • 距离清算阈值: $1,500,000 - $1,000,000 = $500,000 │
│ │
│ 利息累积: │
│ 随着时间推移borrowIndex 会增长 │
│ Bob 的实际债务 = principal × borrowIndex / 1e18 会增加 │
│ 这就是借款利息的体现 │
│ │
│ 重要风险提示: │
│ ⚠️ 如果 YT-A 价格下跌,抵押品价值减少 │
│ ⚠️ 如果债务增长(利息累积),债务价值增加 │
│ ⚠️ 当债务价值 > 清算阈值($1,500,000会被清算 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 6. 清算流程Absorb
```
┌─────────────────────────────────────────────────────────────────────┐
│ 场景设定 │
│ ───────────────────────────────────────────────────────────────── │
│ Bob 的状态(价格变化后): │
│ • 抵押品: 1000 YT-A │
│ • YT-A 价格: $2000 → $1400 (下跌 30%) │
│ • 抵押品价值: $1,400,000 │
│ • 借款本金: -1000000e6 │
│ • borrowIndex: 1.1e18 (利息累积 10%) │
│ • 实际债务: 1000000e6 × 1.1 = 1,100,000 USDC │
│ • 债务价值: $1,100,000 │
│ │
│ 清算判断: │
│ • liquidateCollateralFactor: 75% │
│ • 清算阈值: $1,400,000 × 75% = $1,050,000 │
│ • 债务价值 ($1,100,000) > 清算阈值 ($1,050,000) ✓ │
│ → 可以被清算 │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 清算人调用 absorb(Bob)
┌─────────────────────────────────────────────────────────────────────┐
│ Lending.absorb() │
│ ───────────────────────────────────────────────────────────────── │
│ function absorb(address borrower) │
│ • 非重入保护: nonReentrant │
│ • 暂停检查: whenNotPaused │
│ • 参数: borrower=Bob │
│ • 调用者: 任何人都可以(清算人/机器人) │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 计提利息
│ accrueInterest()
┌─────────────────────────────────────────────────────────────────────┐
│ 利息计提 │
│ 更新到最新状态,确保债务和利息都是最新的 │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 检查是否可清算
┌─────────────────────────────────────────────────────────────────────┐
│ 清算资格检查 (isLiquidatable) │
│ ───────────────────────────────────────────────────────────────── │
│ if (!isLiquidatable(Bob)) revert NotLiquidatable() │
│ │
│ isLiquidatable 逻辑: │
│ ① 检查是否有债务: │
│ principal = -1000000e6 < 0 ✓ │
│ │
│ ② 计算实际债务 (含利息): │
│ balance = principal × borrowIndex / 1e18 │
│ = -1000000e6 × 1.1e18 / 1e18 = -1100000e6 │
│ debt = -balance = 1100000e6 │
│ │
│ ③ 计算债务价值 (USD): │
│ basePrice = LendingPriceFeed.getPrice(USDC) = 1e30 │
│ debtValue = (1100000e6 × 1e30) / 10^6 = 1100000e30 │
│ │
│ ④ 计算抵押品清算阈值价值: │
│ YT-A: 1000e18 │
│ price = 1400e30 ($1400) │
│ value = (1000e18 × 1400e30) / 10^18 = 1400000e30 │
│ liquidateCollateralFactor = 0.75e18 (75%) │
│ collateralValue = 1400000e30 × 0.75e18 / 1e18 │
│ = 1050000e30 ($1,050,000) │
│ │
│ ⑤ 比较: │
│ debtValue (1100000e30) > collateralValue (1050000e30) ✓ │
│ → 可以清算 │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 计算抵押品总价值
┌─────────────────────────────────────────────────────────────────────┐
│ 计算抵押品价值(清算折扣) │
│ ───────────────────────────────────────────────────────────────── │
│ 遍历所有抵押品资产: │
│ │
│ YT-A: │
│ • collateralAmount = 1000e18 │
│ • assetPrice = 1400e30 │
│ • liquidationFactor = 0.9e18 (清算时价值打 90 折) │
│ • decimals = 18 │
│ │
│ ① 计算原始价值 (USD, 用于事件): │
│ collateralValueUSD = (1000e18 × 1400e30) / 10^18 │
│ = 1400000e30 │
│ │
│ ② 计算折扣后价值: │
│ discountedValue = (1000e18 × 1400e30 × 0.9e18) / (10^18 × 1e18) │
│ = 1260000e30 ($1,260,000) │
│ │
│ ③ 将抵押品转移到清算库存: │
│ userCollateral[Bob][YTA] = 0 │
│ collateralReserves[YTA] += 1000e18 │
│ │
│ ④ 触发抵押品吸收事件: │
│ emit AbsorbCollateral( │
│ msg.sender, // 清算人 │
│ Bob, // 借款人 │
│ YTA, // 资产 │
│ 1000e18, // 数量 │
│ 1400000e30 // 原始价值 USD │
│ ) │
│ │
│ totalCollateralValue = 1260000e30 │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 将抵押品价值转换为 baseToken
┌─────────────────────────────────────────────────────────────────────┐
│ 转换为 USDC 数量 │
│ ───────────────────────────────────────────────────────────────── │
│ basePrice = 1e30 (USDC = $1.00) │
│ baseScale = 10^6 (USDC 6位小数) │
│ │
│ collateralInBase = (totalCollateralValue × baseScale) / basePrice │
│ = (1260000e30 × 10^6) / 1e30 │
│ = 1260000e6 (1,260,000 USDC) │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 计算新余额
┌─────────────────────────────────────────────────────────────────────┐
│ 计算清算后余额 │
│ ───────────────────────────────────────────────────────────────── │
│ oldBalance = -1100000e6 (债务) │
│ collateralInBase = 1260000e6 (抵押品价值) │
│ │
│ newBalance = oldBalance + collateralInBase │
│ = -1100000e6 + 1260000e6 │
│ = 160000e6 (正数!抵押品价值 > 债务) │
│ │
│ ⚠️ 注意:如果 newBalance < 0说明坏账会强制归零 │
│ if (newBalance < 0) { │
│ newBalance = 0 // 坏账由协议储备金承担 │
│ } │
│ │
│ 本例中 newBalance > 0无坏账 │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 转换为新本金
┌─────────────────────────────────────────────────────────────────────┐
│ 计算新本金 │
│ ───────────────────────────────────────────────────────────────── │
│ newPrincipal = (newBalance × 1e18) / supplyIndex │
│ = (160000e6 × 1e18) / 1e18 │
│ = 160000e6 │
│ │
│ 余额从负数变为正数: │
│ • 旧本金: -1000000e6 (借款) │
│ • 新本金: 160000e6 (存款!) │
└────────────────────────────┬────────────────────────────────────────┘
│ 8. 更新用户本金
┌─────────────────────────────────────────────────────────────────────┐
│ 更新用户状态 │
│ ───────────────────────────────────────────────────────────────── │
│ userBasic[Bob].principal = newPrincipal = 160000e6 │
│ │
│ 用户状态变化: │
│ • principal: -1000000e6 → 160000e6 │
│ • 从借款人变为存款人! │
│ • 抵押品: 1000 YT-A → 0 (全部被清算) │
└────────────────────────────┬────────────────────────────────────────┘
│ 9. 计算并更新全局状态
┌─────────────────────────────────────────────────────────────────────┐
│ 更新全局余额 │
│ ───────────────────────────────────────────────────────────────── │
│ 调用: repayAndSupplyAmount( │
│ oldPrincipal=-1000000e6, │
│ newPrincipal=160000e6 │
│ ) │
│ │
│ 判断逻辑: │
│ • newPrincipal (160000e6) > oldPrincipal (-1000000e6) ✓ │
│ • newPrincipal > 0 ✓ │
│ • oldPrincipal < 0 ✓ │
│ → 从负数变正数(偿还所有债务并存款) │
│ │
│ 结果: │
│ repayAmount = -oldPrincipal = 1000000e6 │
│ supplyAmount = newPrincipal = 160000e6 │
│ │
│ 更新: │
│ totalSupplyBase += supplyAmount (160000e6) │
│ totalBorrowBase -= repayAmount (1000000e6) │
│ │
│ ⚠️ 注意:储备金通过减少 totalBorrowBase 和增加 totalSupplyBase │
│ 来承担坏账(如果有) │
└────────────────────────────┬────────────────────────────────────────┘
│ 10. 计算坏账(如果有)
┌─────────────────────────────────────────────────────────────────────┐
│ 坏账计算 │
│ ───────────────────────────────────────────────────────────────── │
│ if (collateralInBase < -oldBalance) { │
│ // 抵押品不足以覆盖债务,差额由协议储备金承担 │
│ basePaidOut = -oldBalance - collateralInBase │
│ } else { │
│ // 抵押品足够,无坏账 │
│ basePaidOut = 0 │
│ } │
│ │
│ 本例中: │
│ • -oldBalance = 1100000e6 │
│ • collateralInBase = 1260000e6 │
│ • 1260000e6 >= 1100000e6 ✓ 无坏账 │
│ • basePaidOut = 0 │
│ │
│ 坏账价值: │
│ valueOfBasePaidOut = (basePaidOut × basePrice) / baseScale = 0 │
└────────────────────────────┬────────────────────────────────────────┘
│ 11. 触发债务吸收事件
┌─────────────────────────────────────────────────────────────────────┐
│ 事件记录 │
│ ───────────────────────────────────────────────────────────────── │
│ emit AbsorbDebt( │
│ msg.sender, // 清算人 │
│ Bob, // 借款人 │
│ 0, // basePaidOut (坏账) │
│ 0 // valueOfBasePaidOut (坏账价值) │
│ ) │
└────────────────────────────┬────────────────────────────────────────┘
│ 12. 清算完成
┌─────────────────────────────────────────────────────────────────────┐
│ 清算完成 │
│ ───────────────────────────────────────────────────────────────── │
│ Bob 最终状态: │
│ • principal: 160000e6 (正数 = 存款) │
│ • 实际余额: 160000 USDC │
│ • 抵押品: 0 (全部被清算) │
│ • 债务: 0 (已清偿) │
│ │
│ 协议状态: │
│ • collateralReserves[YTA]: +1000e18 │
│ • 清算的抵押品进入协议库存,可以被购买 │
│ │
│ 清算流程总结: │
│ ① 没收所有抵押品 (1000 YT-A) │
│ ② 按清算折扣 (90%) 计算抵押品价值 ($1,260,000) │
│ ③ 用于偿还债务 ($1,100,000) │
│ ④ 剩余价值 ($160,000) 转为用户存款 │
│ ⑤ 抵押品进入协议库存,等待被购买 │
│ │
│ 清算收益: │
│ • Bob 损失: 1000 YT-A (价值 $1,400,000) │
│ • Bob 保留: 160,000 USDC (价值 $160,000) │
│ • 净损失: $1,400,000 - $160,000 = $1,240,000 │
│ │
│ 清算人收益: │
│ • 清算人调用 absorb() 不直接获得收益 │
│ • 但可以稍后通过 buyCollateral() 折价购买抵押品获利 │
│ │
│ 坏账场景示例: │
│ 如果 YT-A 价格跌到 $1000: │
│ • 抵押品价值: 1000 × $1000 × 90% = $900,000 │
│ • 债务价值: $1,100,000 │
│ • 差额: $200,000 (坏账,由协议储备金承担) │
│ • Bob principal: 0 (债务被清零) │
│ • basePaidOut: 200000 USDC │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 7. 购买清算抵押品BuyCollateral
```
┌─────────────────────────────────────────────────────────────────────┐
│ 清算人 (Liquidator) │
│ 当前状态: │
│ • 持有: 足够的 USDC │
│ • 目标: 购买清算后的 YT-A 抵押品 │
│ │
│ 协议状态: │
│ • collateralReserves[YTA]: 1000e18 (可购买) │
│ • 协议储备金不足 (< targetReserves) │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 查询可购买数量
│ quoteCollateral(YTA, 100000e6)
┌─────────────────────────────────────────────────────────────────────┐
│ Lending.quoteCollateral() │
│ ───────────────────────────────────────────────────────────────── │
│ function quoteCollateral(address asset, uint256 baseAmount) │
│ │
│ 计算逻辑(支付 100,000 USDC 可购买多少 YT-A: │
│ │
│ ① 获取价格: │
│ assetPrice = LendingPriceFeed.getPrice(YTA) = 1400e30 │
│ basePrice = LendingPriceFeed.getPrice(USDC) = 1e30 │
│ │
│ ② 获取配置: │
│ liquidationFactor = 0.9e18 (90%) │
│ storeFrontPriceFactor = 0.95e18 (额外 5% 折扣) │
│ │
│ ③ 计算折扣因子: │
│ discountFactor = storeFrontPriceFactor × (1 - liquidationFactor) │
│ = 0.95e18 × (1e18 - 0.9e18) / 1e18 │
│ = 0.95e18 × 0.1e18 / 1e18 │
│ = 0.095e18 (9.5%) │
│ │
│ ④ 计算有效资产价格: │
│ effectiveAssetPrice = assetPrice × (1 - discountFactor) │
│ = 1400e30 × (1e18 - 0.095e18) / 1e18 │
│ = 1400e30 × 0.905e18 / 1e18 │
│ = 1267e30 ($1267) │
│ │
│ ⑤ 计算可购买数量: │
│ baseScale = 10^6 (USDC) │
│ assetScale = 10^18 (YT-A) │
│ │
│ 由于精度相同简化: │
│ result = (baseAmount × basePrice) / effectiveAssetPrice │
│ = (100000e6 × 1e30) / 1267e30 │
│ = 78.93e6 × 10^12 │
│ = 78.93e18 (78.93 YT-A) │
│ │
│ 返回: 78.93e18 YT-A │
└─────────────────────────────────────────────────────────────────────┘
│ 2. 调用 buyCollateral()
┌─────────────────────────────────────────────────────────────────────┐
│ Lending.buyCollateral() │
│ ───────────────────────────────────────────────────────────────── │
│ function buyCollateral( │
│ address asset, // YTA │
│ uint256 minAmount, // 75e18 (最少要买 75 YT-A滑点保护) │
│ uint256 baseAmount, // 100000e6 (支付 100,000 USDC) │
│ address recipient // Liquidator (接收地址) │
│ ) │
│ • 非重入保护: nonReentrant │
│ • 暂停检查: whenNotPaused │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 检查库存
┌─────────────────────────────────────────────────────────────────────┐
│ 库存检查 │
│ ───────────────────────────────────────────────────────────────── │
│ if (collateralReserves[YTA] == 0) revert InsufficientBalance() │
│ • collateralReserves[YTA] = 1000e18 ✓ 有库存 │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 检查储备金状态
┌─────────────────────────────────────────────────────────────────────┐
│ 储备金检查 │
│ ───────────────────────────────────────────────────────────────── │
│ int256 currentReserves = getReserves() │
│ │
│ getReserves() 计算: │
│ ① 计算最新索引 (含未计提的利息): │
│ (newSupplyIndex, newBorrowIndex) = accruedInterestIndices(...) │
│ │
│ ② 计算实际总供应和总借款: │
│ balance = IERC20(USDC).balanceOf(address(this)) │
│ totalSupply = totalSupplyBase × newSupplyIndex / 1e18 │
│ totalBorrow = totalBorrowBase × newBorrowIndex / 1e18 │
│ │
│ ③ 计算储备金: │
│ reserves = balance - totalSupply + totalBorrow │
│ │
│ 假设 reserves = 400000e6 (< targetReserves = 5000000e6) │
│ │
│ if (reserves >= 0 && reserves >= targetReserves) { │
│ revert NotForSale() // 储备金充足,无需出售 │
│ } │
│ • 400000e6 < 5000000e6 ✓ 储备金不足,可以出售 │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 计算可购买数量
┌─────────────────────────────────────────────────────────────────────┐
│ 计算购买数量 │
│ ───────────────────────────────────────────────────────────────── │
│ collateralAmount = quoteCollateral(YTA, 100000e6) = 78.93e18 │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 验证数量
┌─────────────────────────────────────────────────────────────────────┐
│ 数量验证 │
│ ───────────────────────────────────────────────────────────────── │
│ ① 滑点保护: │
│ if (collateralAmount < minAmount) revert InsufficientBalance() │
│ • 78.93e18 >= 75e18 ✓ 通过 │
│ │
│ ② 库存检查: │
│ if (collateralAmount > collateralReserves[YTA]) │
│ revert InsufficientBalance() │
│ • 78.93e18 <= 1000e18 ✓ 通过 │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 收取支付
┌─────────────────────────────────────────────────────────────────────┐
│ 收取 USDC │
│ ───────────────────────────────────────────────────────────────── │
│ IERC20(baseToken).transferFrom( │
│ msg.sender, // 清算人 │
│ address(this), // Lending 地址 │
│ 100000e6 // 100,000 USDC │
│ ) │
│ │
│ 结果: │
│ • 清算人 USDC 余额: -100,000 │
│ • Lending USDC 余额: +100,000 │
└────────────────────────────┬────────────────────────────────────────┘
│ 8. 抵押品出库
┌─────────────────────────────────────────────────────────────────────┐
│ 更新库存 │
│ ───────────────────────────────────────────────────────────────── │
│ collateralReserves[YTA] -= collateralAmount │
│ │
│ 更新: │
│ • collateralReserves[YTA]: 1000e18 → 921.07e18 │
└────────────────────────────┬────────────────────────────────────────┘
│ 9. 转出抵押品
┌─────────────────────────────────────────────────────────────────────┐
│ 转账抵押品 │
│ ───────────────────────────────────────────────────────────────── │
│ IERC20(asset).safeTransfer( │
│ recipient, // Liquidator │
│ 78.93e18 // 78.93 YT-A │
│ ) │
│ │
│ 结果: │
│ • Lending YT-A 余额: -78.93 │
│ • 清算人 YT-A 余额: +78.93 │
└────────────────────────────┬────────────────────────────────────────┘
│ 10. 触发事件
┌─────────────────────────────────────────────────────────────────────┐
│ 事件记录 │
│ ───────────────────────────────────────────────────────────────── │
│ emit BuyCollateral( │
│ msg.sender, // 清算人 │
│ YTA, // 资产 │
│ 100000e6, // 支付的 USDC │
│ 78.93e18 // 购买的 YT-A │
│ ) │
└────────────────────────────┬────────────────────────────────────────┘
│ 11. 购买完成
┌─────────────────────────────────────────────────────────────────────┐
│ 购买完成 │
│ ───────────────────────────────────────────────────────────────── │
│ 清算人收益分析: │
│ • 支付: 100,000 USDC │
│ • 获得: 78.93 YT-A │
│ • 市场价: 78.93 × $1400 = $110,502 │
│ • 购买价: $100,000 │
│ • 折扣: 9.5% (storeFrontPriceFactor) │
│ • 利润: $10,502 (10.5%) │
│ │
│ 协议收益: │
│ • 收到 100,000 USDC增加储备金 │
│ • 储备金: 400,000 → 500,000 USDC │
│ • 距离目标: 5,000,000 - 500,000 = 4,500,000 USDC │
│ │
│ 折扣机制说明: │
│ • liquidationFactor (90%): 清算时抵押品打 9 折 │
│ • storeFrontPriceFactor (95%): 在 liquidationFactor 基础上额外打折 │
│ • 总折扣 = 1 - (1 - 0.1) × 0.95 = 1 - 0.905 = 9.5% │
│ • 这个折扣激励清算人购买抵押品,补充协议储备金 │
│ │
│ 限制条件: │
│ • 只有当协议储备金 < targetReserves 时才能购买 │
│ • 储备金充足时会 revert NotForSale() │
│ • 这确保协议不会过度出售抵押品 │
│ │
│ 注意: │
│ 收入会自动体现在 getReserves() 中 │
│ 因为 balance 增加了,而 totalSupply 和 totalBorrow 不变 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 8. 利息计提机制
```
┌─────────────────────────────────────────────────────────────────────┐
│ 利息计提触发时机 │
│ ───────────────────────────────────────────────────────────────── │
│ 每次以下操作时都会自动调用 accrueInterest(): │
│ • supply() - 存入 USDC │
│ • withdraw() - 提取 USDC / 借款 │
│ • withdrawCollateral() - 取出抵押品 │
│ • absorb() - 清算 │
│ • buyCollateral() - 购买清算抵押品 │
│ │
│ 作用: │
│ 确保所有操作使用最新的利息累计因子,实现公平的利息分配 │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 检查是否需要计提
┌─────────────────────────────────────────────────────────────────────┐
│ 时间检查 │
│ ───────────────────────────────────────────────────────────────── │
│ timeElapsed = block.timestamp - lastAccrualTime │
│ │
│ if (timeElapsed == 0) { │
│ return // 同一区块内已计提过,直接返回 │
│ } │
│ │
│ 例如: │
│ • lastAccrualTime: 1704067200 (2024-01-01 00:00:00) │
│ • block.timestamp: 1704153600 (2024-01-02 00:00:00) │
│ • timeElapsed: 86400 秒 (1 天) │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 计算当前利用率
┌─────────────────────────────────────────────────────────────────────┐
│ 利用率计算 │
│ ───────────────────────────────────────────────────────────────── │
│ ① 计算实际总供应和总借款(使用当前索引): │
│ totalSupply = totalSupplyBase × supplyIndex / 1e18 │
│ totalBorrow = totalBorrowBase × borrowIndex / 1e18 │
│ │
│ ② 计算利用率: │
│ utilization = totalBorrow / totalSupply │
│ = (totalBorrow × 1e18) / totalSupply │
│ │
│ 示例数据: │
│ • totalSupplyBase: 10000000e6 (1000万 USDC 本金) │
│ • supplyIndex: 1.05e18 │
│ • totalSupply: 10000000e6 × 1.05e18 / 1e18 = 10500000e6 │
│ │
│ • totalBorrowBase: 7000000e6 (700万 USDC 本金) │
│ • borrowIndex: 1.15e18 │
│ • totalBorrow: 7000000e6 × 1.15e18 / 1e18 = 8050000e6 │
│ │
│ • utilization = (8050000e6 × 1e18) / 10500000e6 │
│ = 0.7666...e18 (约 76.67%) │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 计算供应利率(每秒)
┌─────────────────────────────────────────────────────────────────────┐
│ 供应利率计算 (getSupplyRate) │
│ ───────────────────────────────────────────────────────────────── │
│ 利率模型(双斜率模型): │
│ │
│ if (utilization <= supplyKink) { │
│ // 拐点前:线性增长 │
│ rate = supplyPerSecondInterestRateBase + │
│ (utilization × supplyPerSecondInterestRateSlopeLow) / 1e18│
│ } else { │
│ // 拐点后:陡峭增长 │
│ excessUtil = utilization - supplyKink │
│ rate = supplyPerSecondInterestRateBase + │
│ supplyPerSecondInterestRateSlopeLow + │
│ (excessUtil × supplyPerSecondInterestRateSlopeHigh) / 1e18│
│ } │
│ │
│ 配置示例(年化利率转换为每秒利率): │
│ • supplyKink: 0.8e18 (80%) │
│ • supplyPerYearInterestRateBase: 0.02e18 (2% APY) │
│ → supplyPerSecondInterestRateBase: 634e9 (2%/31536000) │
│ • supplyPerYearInterestRateSlopeLow: 0.1e18 (10% APY) │
│ → supplyPerSecondInterestRateSlopeLow: 3170e9 │
│ • supplyPerYearInterestRateSlopeHigh: 2e18 (200% APY) │
│ → supplyPerSecondInterestRateSlopeHigh: 63419e9 │
│ │
│ 本例计算utilization = 76.67% < 80%: │
│ rate = 634e9 + (0.7667e18 × 3170e9) / 1e18 │
│ = 634e9 + 2430e9 │
│ = 3064e9 (约 9.67% APY) │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 计算借款利率(每秒)
┌─────────────────────────────────────────────────────────────────────┐
│ 借款利率计算 (getBorrowRate) │
│ ───────────────────────────────────────────────────────────────── │
│ 利率模型(双斜率模型,同供应利率): │
│ │
│ 配置示例: │
│ • borrowKink: 0.8e18 (80%) │
│ • borrowPerYearInterestRateBase: 0.03e18 (3% APY) │
│ → borrowPerSecondInterestRateBase: 951e9 │
│ • borrowPerYearInterestRateSlopeLow: 0.15e18 (15% APY) │
│ → borrowPerSecondInterestRateSlopeLow: 4756e9 │
│ • borrowPerYearInterestRateSlopeHigh: 3e18 (300% APY) │
│ → borrowPerSecondInterestRateSlopeHigh: 95129e9 │
│ │
│ 本例计算utilization = 76.67% < 80%: │
│ rate = 951e9 + (0.7667e18 × 4756e9) / 1e18 │
│ = 951e9 + 3646e9 │
│ = 4597e9 (约 14.5% APY) │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 更新供应索引
┌─────────────────────────────────────────────────────────────────────┐
│ 供应索引更新 │
│ ───────────────────────────────────────────────────────────────── │
│ 计算利息累积: │
│ interestAccrued = (supplyIndex × supplyRate × timeElapsed) / 1e18 │
│ = (1.05e18 × 3064e9 × 86400) / 1e18 │
│ = 1.05e18 × 264729600e9 / 1e18 │
│ = 0.00027796608e18 (约 0.02780%) │
│ │
│ 更新索引: │
│ newSupplyIndex = supplyIndex + interestAccrued │
│ = 1.05e18 + 0.00027796608e18 │
│ = 1.05027796608e18 │
│ │
│ supplyIndex = newSupplyIndex │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 更新借款索引
┌─────────────────────────────────────────────────────────────────────┐
│ 借款索引更新 │
│ ───────────────────────────────────────────────────────────────── │
│ 计算利息累积: │
│ interestAccrued = (borrowIndex × borrowRate × timeElapsed) / 1e18 │
│ = (1.15e18 × 4597e9 × 86400) / 1e18 │
│ = 1.15e18 × 397180800e9 / 1e18 │
│ = 0.00045675792e18 (约 0.04568%) │
│ │
│ 更新索引: │
│ newBorrowIndex = borrowIndex + interestAccrued │
│ = 1.15e18 + 0.00045675792e18 │
│ = 1.15045675792e18 │
│ │
│ borrowIndex = newBorrowIndex │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 更新计提时间
┌─────────────────────────────────────────────────────────────────────┐
│ 更新时间戳 │
│ ───────────────────────────────────────────────────────────────── │
│ lastAccrualTime = block.timestamp │
│ • 1704067200 → 1704153600 │
└────────────────────────────┬────────────────────────────────────────┘
│ 8. 利息计提完成
┌─────────────────────────────────────────────────────────────────────┐
│ 利息计提效果 │
│ ───────────────────────────────────────────────────────────────── │
│ 用户视角Alice - 存款人): │
│ • 存款本金: 10000e6 (不变) │
│ • 旧余额: 10000e6 × 1.05e18 / 1e18 = 10500e6 │
│ • 新余额: 10000e6 × 1.05027796608e18 / 1e18 = 10502.7796608e6 │
│ • 利息收益: 2.7796608 USDC (1天) │
│ │
│ 用户视角Bob - 借款人): │
│ • 借款本金: -7000000e6 (不变) │
│ • 旧债务: 7000000e6 × 1.15e18 / 1e18 = 8050000e6 │
│ • 新债务: 7000000e6 × 1.15045675792e18 / 1e18 = 8053197.30544e6 │
│ • 利息成本: 3197.30544 USDC (1天) │
│ │
│ 协议储备金增长: │
│ 储备金 = 合约 USDC 余额 - 总供应 + 总借款 │
│ • 借款利息 > 供应利息,差额进入协议储备金 │
│ • 本例储备金增长: 3197.30544 - 2.7796608 = 3194.52578 USDC │
│ │
│ 关键概念: │
│ • 本金principal用户的基础金额不含利息 │
│ • 索引index利息累计因子初始为 1e18随时间增长 │
│ • 实际余额balance本金 × 索引 / 1e18含利息 │
│ • 利息:通过索引的增长自动体现,无需手动分配 │
│ │
│ 利率曲线特点: │
│ ① 低利用率(< kink: 缓慢增长,鼓励借款 │
│ ② 高利用率(> kink: 急剧增长,激励还款和存款 │
│ ③ 借款利率 > 供应利率: 差额为协议收益 │
│ ④ 动态调整: 每次操作都重新计算,实时响应市场状况 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 9. 储备金管理WithdrawReserves
```
┌─────────────────────────────────────────────────────────────────────┐
│ 协议管理员 (Owner) │
│ 当前状态: │
│ • 协议储备金: 6,000,000 USDC │
│ • 目标储备金: 5,000,000 USDC │
│ • 可提取: 1,000,000 USDC (超出目标的部分) │
│ 计划: 提取 500,000 USDC 到指定地址 │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 withdrawReserves(recipient, 500000e6)
┌─────────────────────────────────────────────────────────────────────┐
│ Lending.withdrawReserves() │
│ ───────────────────────────────────────────────────────────────── │
│ function withdrawReserves(address to, uint256 amount) │
│ • 权限检查: onlyOwner │
│ • 非重入保护: nonReentrant │
│ • 参数: to=recipient, amount=500000e6 │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 计提利息
│ accrueInterest()
┌─────────────────────────────────────────────────────────────────────┐
│ 利息计提 │
│ 更新到最新状态,确保储备金计算准确 │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 计算当前储备金
┌─────────────────────────────────────────────────────────────────────┐
│ 储备金计算 (getReserves) │
│ ───────────────────────────────────────────────────────────────── │
│ ① 获取合约 USDC 余额: │
│ balance = IERC20(USDC).balanceOf(address(this)) │
│ │
│ ② 计算实际总供应(含利息): │
│ totalSupply = totalSupplyBase × supplyIndex / 1e18 │
│ │
│ ③ 计算实际总借款(含利息): │
│ totalBorrow = totalBorrowBase × borrowIndex / 1e18 │
│ │
│ ④ 计算储备金: │
│ reserves = balance - totalSupply + totalBorrow │
│ = 6000000e6 (假设) │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 检查可提取金额
┌─────────────────────────────────────────────────────────────────────┐
│ 可提取检查 │
│ ───────────────────────────────────────────────────────────────── │
│ if (reserves < 0) revert InsufficientBalance() │
│ • reserves = 6000000e6 >= 0 ✓ │
│ │
│ availableReserves = reserves - targetReserves │
│ = 6000000e6 - 5000000e6 │
│ = 1000000e6 (可提取 100万) │
│ │
│ if (availableReserves < 0) revert InsufficientBalance() │
│ • 1000000e6 >= 0 ✓ │
│ │
│ if (amount > availableReserves) revert InsufficientBalance() │
│ • 500000e6 <= 1000000e6 ✓ 通过 │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 转出储备金
┌─────────────────────────────────────────────────────────────────────┐
│ 代币转移 │
│ ───────────────────────────────────────────────────────────────── │
│ IERC20(baseToken).safeTransfer( │
│ to, // 接收地址 │
│ 500000e6 // 500,000 USDC │
│ ) │
│ │
│ 结果: │
│ • Lending USDC 余额: -500000e6 │
│ • 接收地址 USDC 余额: +500000e6 │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 触发事件
┌─────────────────────────────────────────────────────────────────────┐
│ 事件记录 │
│ ───────────────────────────────────────────────────────────────── │
│ emit WithdrawReserves(to, 500000e6) │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 提取完成
┌─────────────────────────────────────────────────────────────────────┐
│ 提取完成 │
│ ───────────────────────────────────────────────────────────────── │
│ 协议状态: │
│ • 储备金: 6,000,000 → 5,500,000 USDC │
│ • 目标储备金: 5,000,000 USDC │
│ • 剩余可提取: 500,000 USDC │
│ │
│ 储备金来源: │
│ • 借款利息 > 供应利息的差额 │
│ • 清算时的抵押品折扣收益(如有) │
│ • 清算后抵押品出售收益 │
│ │
│ 储备金用途: │
│ • 覆盖坏账风险 │
│ • 维持协议流动性 │
│ • 协议治理和运营 │
│ │
│ 限制条件: │
│ • 只有 Owner 可以提取 │
│ • 只能提取超出 targetReserves 的部分 │
│ • 确保协议始终保持足够的安全垫 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 10. 关键概念说明
### 10.1 本金 vs 余额
```
┌─────────────────────────────────────────────────────────────────────┐
│ 本金Principal
│ ───────────────────────────────────────────────────────────────── │
│ • 定义:用户的基础金额,不含利息 │
│ • 类型int104有符号整数
│ • 正数:表示存款本金 │
│ • 负数:表示借款本金 │
│ • 零:表示既无存款也无借款 │
│ • 存储userBasic[user].principal │
│ │
│ 示例: │
│ • Alice 存入 10,000 USDC → principal = 10000e6 │
│ • Bob 借出 5,000 USDC → principal = -5000e6 │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 余额Balance
│ ───────────────────────────────────────────────────────────────── │
│ • 定义:实际金额,包含利息 │
│ • 计算balance = principal × index / 1e18 │
│ • 动态:随时间和利息累积变化 │
│ • 不存储:每次需要时动态计算 │
│ │
│ 示例(假设 supplyIndex = 1.05e18: │
│ • Alice 实际余额 = 10000e6 × 1.05e18 / 1e18 = 10500e6 │
│ • Alice 利息收益 = 10500e6 - 10000e6 = 500e6 │
│ │
│ 示例(假设 borrowIndex = 1.15e18: │
│ • Bob 实际债务 = -5000e6 × 1.15e18 / 1e18 = -5750e6 │
│ • Bob 利息成本 = 5750e6 - 5000e6 = 750e6 │
└─────────────────────────────────────────────────────────────────────┘
```
### 10.2 利息索引Index
```
┌─────────────────────────────────────────────────────────────────────┐
│ 利息索引机制 │
│ ───────────────────────────────────────────────────────────────── │
│ • supplyIndex: 供应利息累计因子 │
│ • borrowIndex: 借款利息累计因子 │
│ • 初始值1e18即 1.0
│ • 单调递增:每次计提利息时增加 │
│ • 全局共享:所有用户使用相同的索引 │
│ │
│ 增长公式: │
│ newIndex = oldIndex + (oldIndex × rate × timeElapsed) / 1e18 │
│ │
│ 其中: │
│ • rate: 每秒利率(从年化利率转换) │
│ • timeElapsed: 自上次计提以来的秒数 │
│ │
│ 示例1年后假设年化利率 5%: │
│ • 初始: supplyIndex = 1.0e18 │
│ • 1年后: supplyIndex ≈ 1.05e18 │
│ • 用户存款本金 10000e6 的实际余额: │
│ 10000e6 × 1.05e18 / 1e18 = 10500e6 (+5%) │
└─────────────────────────────────────────────────────────────────────┘
```
### 10.3 抵押率和清算阈值
```
┌─────────────────────────────────────────────────────────────────────┐
│ 三个关键比率 │
│ ───────────────────────────────────────────────────────────────── │
│ ① borrowCollateralFactor借款抵押率例如 70%
│ • 决定最大借款能力 │
│ • 最大借款 = 抵押品价值 × 70% │
│ • 例:$2000 抵押品 → 最多借 $1400 │
│ │
│ ② liquidateCollateralFactor清算阈值例如 75%
│ • 决定何时可被清算 │
│ • 清算条件: 债务 > 抵押品价值 × 75% │
│ • 例:$2000 抵押品 → 债务超过 $1500 时清算 │
│ │
│ ③ liquidationFactor清算折扣例如 90%
│ • 清算时抵押品价值打折 │
│ • 清算价值 = 抵押品市价 × 90% │
│ • 例:$2000 抵押品 → 清算时按 $1800 计算 │
│ • 10% 差额作为清算惩罚和协议安全垫 │
│ │
│ 关系约束: │
│ • borrowCollateralFactor < liquidateCollateralFactor < 1 │
│ • 例70% < 75% < 100% │
│ • 留出安全缓冲区,避免频繁清算 │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 健康度示例YT-A 抵押品 1000个价格 $2000
│ ───────────────────────────────────────────────────────────────── │
│ 安全状态: │
│ • 抵押品价值: $2,000,000 │
│ • 债务: $1,000,000 │
│ • LTV: 50% │
│ • 距离清算: $1,500,000 - $1,000,000 = $500,000 ✓ │
│ │
│ 接近清算: │
│ • 抵押品价值: $2,000,000 │
│ • 债务: $1,480,000 │
│ • LTV: 74% │
│ • 距离清算: $1,500,000 - $1,480,000 = $20,000 ⚠️ │
│ │
│ 可清算状态: │
│ • 抵押品价值: $2,000,000 │
│ • 债务: $1,510,000 │
│ • LTV: 75.5% │
│ • $1,510,000 > $1,500,000 ❌ 会被清算 │
└─────────────────────────────────────────────────────────────────────┘
```
### 10.4 利率曲线(双斜率模型)
```
┌─────────────────────────────────────────────────────────────────────┐
│ 利率随利用率变化示意图 │
│ ───────────────────────────────────────────────────────────────── │
│ 利率 │
│ │ │
│ │
│ │
│ │ 借款利率 │
│ │
│ │
│ │
│ 20│
│ % │
│ │
│ │
│ 10│ ← Kink (拐点80%) │
│ % │
│ │
│ │ 供应利率 │
│ 5│
│ % │
│ │_____________________________________ │
│ 0│ 40% 80% 100% │
│ └─────────────────────────────────────→ 利用率 │
│ │
│ 特点: │
│ • Kink 前0-80%: 缓慢线性增长 │
│ • Kink 后80-100%: 急剧陡峭增长 │
│ • 借款利率始终高于供应利率(协议收益) │
│ • 高利用率时大幅提高借款成本,激励还款 │
│ • 低利用率时降低借款成本,鼓励借款 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 11. 系统部署和配置流程
### 11.1 合约部署顺序
```
1. 部署核心基础设施07-deployLending.ts
├── LendingFactory
├── LendingPriceFeed (UUPS Proxy)
├── Configurator (UUPS Proxy)
└── Lending Implementation
2. 配置市场参数并部署代理08-configureLending.ts
├── 设置 Configuration利率、抵押品等
├── 通过 Configurator 注册配置
└── 部署 Lending Proxy使用 LendingFactory
3. 合约升级09-upgradeLending.ts可选
├── 升级 LendingPriceFeed
├── 升级 Configurator
└── 升级 Lending
```
### 11.2 配置参数说明
```solidity
Configuration {
// 基础资产
baseToken: USDC地址
lendingPriceSource: LendingPriceFeed Proxy地址
// 供应利率参数
supplyKink: 0.8e18 // 80% 拐点
supplyPerYearInterestRateBase: 0.02e18 // 2% 基础年化
supplyPerYearInterestRateSlopeLow: 0.1e18 // 拐点前斜率 10%
supplyPerYearInterestRateSlopeHigh: 2e18 // 拐点后斜率 200%
// 借款利率参数
borrowKink: 0.8e18 // 80% 拐点
borrowPerYearInterestRateBase: 0.03e18 // 3% 基础年化
borrowPerYearInterestRateSlopeLow: 0.15e18 // 拐点前斜率 15%
borrowPerYearInterestRateSlopeHigh: 3e18 // 拐点后斜率 300%
// 清算参数
storeFrontPriceFactor: 0.95e18 // 清算购买折扣 5%
baseBorrowMin: 100e6 // 最小借款 100 USDC
targetReserves: 5000000e6 // 目标储备 500万
// 抵押资产配置
assetConfigs: [
{
asset: YT-A地址,
decimals: 18,
borrowCollateralFactor: 0.7e18, // 70% LTV
liquidateCollateralFactor: 0.75e18, // 75% 清算阈值
liquidationFactor: 0.9e18, // 90% 清算价值
supplyCap: 10000e18 // 最多1万个
},
// 更多抵押资产...
]
}
```
---
## 12. 风险提示和最佳实践
### 12.1 用户风险
```
┌─────────────────────────────────────────────────────────────────────┐
│ 存款人风险 │
│ ───────────────────────────────────────────────────────────────── │
│ ① 流动性风险 │
│ • 高利用率时可能无法立即提取全部存款 │
│ • 建议:关注利用率,避免在极高利用率时大额存入 │
│ │
│ ② 智能合约风险 │
│ • 合约漏洞可能导致资金损失 │
│ • 建议:使用经过审计的版本,小额试用 │
│ │
│ ③ 预言机风险 │
│ • 价格数据异常可能影响协议运行 │
│ • 建议:关注 Chainlink 和 YT Vault 价格源状态 │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 借款人风险 │
│ ───────────────────────────────────────────────────────────────── │
│ ① 清算风险 │
│ • 抵押品价格下跌或债务增长可能导致清算 │
│ • 清算损失:抵押品 × (1 - liquidationFactor) │
│ • 建议:保持健康的 LTV至少低于清算阈值 10% │
│ │
│ ② 利率风险 │
│ • 利用率上升导致借款利率快速增长 │
│ • 建议:监控利用率和利率变化,及时还款 │
│ │
│ ③ 抵押品波动风险 │
│ • YT 代币价格波动较大 │
│ • 建议:使用多种抵押品分散风险,定期补充抵押品 │
└─────────────────────────────────────────────────────────────────────┘
```
### 12.2 最佳实践
```
┌─────────────────────────────────────────────────────────────────────┐
│ 存款人建议 │
│ ───────────────────────────────────────────────────────────────── │
│ • 分散存款:不要将全部资金存入单一协议 │
│ • 定期复利:提取利息后重新存入,实现复利增长 │
│ • 关注利用率:利用率过高(>90%)时谨慎存入 │
│ • 预留流动性:保留部分资金在钱包,应对紧急需求 │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 借款人建议 │
│ ───────────────────────────────────────────────────────────────── │
│ • 保守借款LTV 保持在 50-60%,低于清算阈值 20%+ │
│ • 设置预警:监控抵押品价格,接近清算线时补充抵押品 │
│ • 及时还款:利率上升时优先偿还高息债务 │
│ • 多元抵押:如果支持,使用多种抵押品降低单一资产风险 │
│ • 准备金:保留一定 USDC 以应对紧急还款或补仓需求 │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 清算人建议 │
│ ───────────────────────────────────────────────────────────────── │
│ • 自动化监控:运行机器人持续监控可清算账户 │
│ • Gas 优化批量清算absorbMultiple降低成本 │
│ • 快速响应:第一时间清算获得购买抵押品的机会 │
│ • 价格监控:关注抵押品市场价格,计算套利空间 │
│ • 充足准备:保持足够 USDC 购买清算抵押品 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 13. 常见问题FAQ
**Q1: 为什么我的余额和本金不一样?**
- 余额包含利息,本金是基础金额。余额 = 本金 × index / 1e18随时间增长。
**Q2: 如何计算我的借款利率?**
- 借款利率根据协议利用率动态调整。查看 `getBorrowRate()` 获取当前每秒利率,乘以 31536000 得到年化利率。
**Q3: 什么时候会被清算?**
- 当你的债务价值超过抵押品价值 × liquidateCollateralFactor 时会被清算。
**Q4: 清算后我的资金怎么办?**
- 抵押品被没收按折扣价计算,用于偿还债务。如有剩余会转为你的存款,可以提取。
**Q5: 为什么无法提取全部存款?**
- 协议需要保持足够流动性满足借款人。高利用率时可能需要等待借款被还款后才能提取。
**Q6: storeFrontPriceFactor 是什么?**
- 清算抵押品出售时的额外折扣,激励清算人购买抵押品,补充协议储备金。
**Q7: 如何查看协议储备金?**
- 调用 `getReserves()` 查看当前储备金。储备金 = 合约余额 - 总供应 + 总借款。
**Q8: 可以同时存款和借款吗?**
- 不可以。每个账户只能有一个 principal正数=存款,负数=借款,零=无)。如需同时操作,使用多个地址。
**Q9: 利息多久结算一次?**
- 每次交互时自动计提利息,无需手动操作。利息通过 index 增长自动体现在余额中。
**Q10: 支持哪些抵押品?**
- 由配置决定。默认支持 YT 资产代币。每种抵押品有独立的 LTV 和清算参数。