2025-12-26 13:23:50 +08:00
|
|
|
|
# YT Lending 借贷系统操作流程图
|
|
|
|
|
|
|
|
|
|
|
|
## 重要说明
|
|
|
|
|
|
本系统使用 USDC 作为基础借贷代币(baseToken),YT 资产代币作为抵押品。USDC 价格通过 Chainlink 价格预言机实时获取,YT 代币价格从其 Vault 合约的 `ytPrice()` 方法读取。
|
|
|
|
|
|
|
|
|
|
|
|
## 目录
|
|
|
|
|
|
1. [存入 USDC(Supply)](#1-存入-usdcsupply)
|
|
|
|
|
|
2. [提取 USDC(Withdraw)](#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. 存入 USDC(Supply)
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 用户 (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. 提取 USDC(Withdraw)
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 用户 (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. 借款流程(Borrow)
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 用户 (Bob) │
|
|
|
|
|
|
│ 当前状态: │
|
|
|
|
|
|
│ • 抵押品: 1000 YT-A (价值 $2,000,000) │
|
|
|
|
|
|
│ • principal: 0 (无存款/借款) │
|
|
|
|
|
|
│ • 借款能力: $2,000,000 × 70% = $1,400,000 USDC │
|
|
|
|
|
|
│ 计划: 借款 1,000,000 USDC │
|
|
|
|
|
|
└────────────────────────────┬────────────────────────────────────────┘
|
|
|
|
|
|
│
|
2025-12-29 15:30:10 +08:00
|
|
|
|
│ 1. 调用 withdraw(1000000e6) 进行借款
|
2025-12-26 13:23:50 +08:00
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
2025-12-29 15:30:10 +08:00
|
|
|
|
│ Lending.withdraw() │
|
2025-12-26 13:23:50 +08:00
|
|
|
|
│ ───────────────────────────────────────────────────────────────── │
|
2025-12-29 15:30:10 +08:00
|
|
|
|
│ function withdraw(uint256 amount) │
|
2025-12-26 13:23:50 +08:00
|
|
|
|
│ • 非重入保护: nonReentrant │
|
|
|
|
|
|
│ • 暂停检查: whenNotPaused │
|
|
|
|
|
|
│ • 参数: 1,000,000 USDC (1000000e6) │
|
2025-12-29 15:30:10 +08:00
|
|
|
|
│ • 注: withdraw 会自动处理借款逻辑 │
|
2025-12-26 13:23:50 +08:00
|
|
|
|
└────────────────────────────┬────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
│ 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 和清算参数。
|