# 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 │ └────────────────────────────┬────────────────────────────────────────┘ │ │ 1. 调用 withdraw(1000000e6) 进行借款 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ Lending.withdraw() │ │ ───────────────────────────────────────────────────────────────── │ │ function withdraw(uint256 amount) │ │ • 非重入保护: nonReentrant │ │ • 暂停检查: whenNotPaused │ │ • 参数: 1,000,000 USDC (1000000e6) │ │ • 注: withdraw 会自动处理借款逻辑 │ └────────────────────────────┬────────────────────────────────────────┘ │ │ 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 和清算参数。