Files
assetxContracts/doc/YTLp-系统操作流程图.md
2025-12-18 13:07:35 +08:00

1151 lines
96 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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

# YT流动性池系统操作流程图
## 目录
1. [添加流动性流程](#1-添加流动性流程)
2. [移除流动性流程](#2-移除流动性流程)
3. [代币互换流程](#3-代币互换流程)
4. [添加白名单代币流程](#4-添加白名单代币流程)
5. [移除白名单代币流程](#5-移除白名单代币流程)
6. [系统部署和初始化流程](#6-系统部署和初始化流程)
7. [路由器暂停功能流程](#7-路由器暂停功能流程)
---
## 1. 添加流动性流程
```
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (User) │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 addLiquidity(token, amount, minUsdy, minYtLP)
┌─────────────────────────────────────────────────────────────────────┐
│ YTRewardRouter.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function addLiquidity( │
│ address _token, // YT-A 代币地址 │
│ uint256 _amount, // 1000 个代币 │
│ uint256 _minUsdy, // 最小997 USDY滑点保护
│ uint256 _minYtLP // 最小995 ytLP滑点保护
│ ) external nonReentrant whenNotPaused │
│ │
│ 修饰符检查: │
│ ✓ nonReentrant - 防重入保护 │
│ ✓ whenNotPaused - 暂停检查 │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. transferFrom(user → YTRewardRouter)
│ 转移1000个YT-A到Router
┌─────────────────────────────────────────────────────────────────────┐
│ 代币转移检查 │
│ ✓ 检查用户授权额度 │
│ ✓ 转移代币到Router │
│ ✓ approve(YTPoolManager, amount) │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 调用 addLiquidityForAccount()
┌─────────────────────────────────────────────────────────────────────┐
│ YTPoolManager.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function addLiquidityForAccount( │
│ address _fundingAccount, // YTRewardRouter │
│ address _account, // 用户地址 │
│ address _token, // YT-A │
│ uint256 _amount, // 1000 │
│ uint256 _minUsdy, // 997 │
│ uint256 _minYtLP // 995 │
│ ) │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 计算当前AUM使用MaxPrice
│ getAumInUsdy(true)
┌─────────────────────────────────────────────────────────────────────┐
│ AUM 计算(对用户保守) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ YTVault.getPoolValue(true) │ │
│ │ • 获取池中所有YT代币数量 │ │
│ │ • 使用 MaxPrice上浮价差计算每个代币价值 │ │
│ │ • 汇总得到 AUM = $100,200 │ │
│ └───────────────────────────────────────────────────────┘ │
│ 当前ytLP供应量: 100,000 │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 调用 buyUSDY()
┌─────────────────────────────────────────────────────────────────────┐
│ YTVault.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function buyUSDY(address _token, address _receiver) │
│ │
│ 步骤: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ① transferIn(_token) │ │
│ │ • 接收1000个YT-A │ │
│ │ │ │
│ │ ② 获取价格使用MinPrice低估用户资产 │ │
│ │ price = _getPrice(_token, false) │ │
│ │ • 基础价格: $1.00 │ │
│ │ • MinPrice: $0.998 (下压0.2%价差) │ │
│ │ │ │
│ │ ③ 计算理论USDY价值 │ │
│ │ usdyAmount = 1000 × $0.998 = $998 │ │
│ │ │ │
│ │ ④ 获取手续费率(动态) │ │
│ │ feeBps = getSwapFeeBasisPoints(_token, usdy, 998) │ │
│ │ • 检查是否稳定币互换 │ │
│ │ • YT代币: 30 bps (0.3%) │ │
│ │ • 稳定币: 4 bps (0.04%) │ │
│ │ • 根据池子平衡动态调整 │ │
│ │ → 本次: 30 bps │ │
│ │ │ │
│ │ ⑤ 计算扣费后的USDY │ │
│ │ feeAmount = 1000 × 0.3% = 3个代币 │ │
│ │ amountAfterFees = 997个代币 │ │
│ │ usdyAmountAfterFees = 997 × $0.998 = $995.006 │ │
│ │ │ │
│ │ ⑥ 池子记账 │ │
│ │ _increasePoolAmount(_token, 1000) // 全部代币入池 │ │
│ │ _increaseUsdyAmount(_token, 995) // 只记扣费后的债务 │ │
│ │ │ │
│ │ ⑦ 铸造USDY │ │
│ │ USDY.mint(_receiver, 995) │ │
│ │ • 手续费3个代币留在池中 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 返回: 995 USDY │
└────────────────────────────┬────────────────────────────────────────┘
│ 6. 检查 USDY 数量
│ require(995 >= 997) ❌ 会失败
│ (实际应该传入更小的 minUsdy)
┌─────────────────────────────────────────────────────────────────────┐
│ 回到 YTPoolManager - 铸造 ytLP │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 计算铸造数量: │ │
│ │ ytLPSupply = 100,000 │ │
│ │ aumInUsdy = $100,200 │ │
│ │ usdyAmount = $995 │ │
│ │ │ │
│ │ mintAmount = 995 × 100,000 / 100,200 = 993.03 ytLP │ │
│ │ │ │
│ │ 检查滑点保护: │ │
│ │ require(993.03 >= 995) ❌ 会失败 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 执行铸造: │
│ YTLPToken.mint(user, 993.03) │
│ lastAddedAt[user] = block.timestamp // 记录时间(冷却期) │
└────────────────────────────┬────────────────────────────────────────┘
│ 7. 返回结果
┌─────────────────────────────────────────────────────────────────────┐
│ 用户收到 │
│ ✓ 993.03 ytLP │
│ ✓ 代表在池中的份额 │
│ │
│ 成本分析: │
│ 存入: 1000 YT-A │
│ 手续费: 3 YT-A (0.3%) │
│ 价差损失: ~4 YT-A (0.4%) │
│ 总成本: ~0.7% │
└─────────────────────────────────────────────────────────────────────┘
```
### 涉及的合约函数:
| 合约 | 函数 | 作用 |
|------|------|------|
| **YTRewardRouter** | `addLiquidity()` | 用户入口,处理代币转移 |
| **YTPoolManager** | `addLiquidityForAccount()` | 流动性管理计算ytLP |
| **YTPoolManager** | `getAumInUsdy(true)` | 获取AUM使用MaxPrice|
| **YTVault** | `buyUSDY()` | 接收代币铸造USDY |
| **YTVault** | `getPoolValue(true)` | 计算池子总价值 |
| **YTVault** | `getSwapFeeBasisPoints()` | 获取动态手续费率 |
| **YTPriceFeed** | `getPrice(_token, false)` | 获取MinPrice |
| **USDY** | `mint()` | 铸造USDY代币 |
| **YTLPToken** | `mint()` | 铸造ytLP代币 |
---
## 2. 移除流动性流程
```
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (User) │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 removeLiquidity(tokenOut, ytLPAmount, minOut, receiver)
┌─────────────────────────────────────────────────────────────────────┐
│ YTRewardRouter.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function removeLiquidity( │
│ address _tokenOut, // YT-B 代币地址 │
│ uint256 _ytLPAmount, // 1000 ytLP │
│ uint256 _minOut, // 最小990 YT-B滑点保护
│ address _receiver // 接收地址 │
│ ) external nonReentrant whenNotPaused │
│ │
│ 修饰符检查: │
│ ✓ nonReentrant - 防重入保护 │
│ ✓ whenNotPaused - 暂停检查 │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 调用 removeLiquidityForAccount()
┌─────────────────────────────────────────────────────────────────────┐
│ YTPoolManager.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function removeLiquidityForAccount(...) │
│ │
│ 步骤: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ① 检查冷却期 │ │
│ │ require(lastAddedAt[user] + 15分钟 <= now) │ │
│ │ │ │
│ │ ② 计算AUM使用MinPrice对用户保守 │ │
│ │ aumInUsdy = getAumInUsdy(false) │ │
│ │ • YTVault.getPoolValue(false) │ │
│ │ • 使用MinPrice下压价差 │ │
│ │ • AUM = $99,800 │ │
│ │ │ │
│ │ ③ 计算USDY价值 │ │
│ │ ytLPSupply = 100,000 │ │
│ │ usdyAmount = 1000 × 99,800 / 100,000 = $998 │ │
│ │ │ │
│ │ ④ 检查并铸造USDY如果余额不足 │ │
│ │ balance = USDY.balanceOf(PoolManager) │ │
│ │ if (998 > balance) { │ │
│ │ USDY.mint(PoolManager, 998 - balance) │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 销毁ytLP
│ YTLPToken.burn(user, 1000)
┌─────────────────────────────────────────────────────────────────────┐
│ ytLP 销毁完成 │
│ 用户的ytLP余额 -1000 │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 调用 sellUSDY()
┌─────────────────────────────────────────────────────────────────────┐
│ YTVault.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function sellUSDY(address _token, address _receiver) │
│ │
│ 步骤: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ① transferIn(usdy) │ │
│ │ • 接收998 USDY │ │
│ │ │ │
│ │ ② 获取价格使用MaxPrice高估需支付的价值 │ │
│ │ price = _getPrice(_token, true) │ │
│ │ • 基础价格: $1.00 │ │
│ │ • MaxPrice: $1.002 (上浮0.2%价差) │ │
│ │ │ │
│ │ ③ 计算理论赎回数量 │ │
│ │ redemptionAmount = 998 / 1.002 = 996.01 YT-B │ │
│ │ │ │
│ │ ④ 获取赎回手续费率 │ │
│ │ feeBps = getRedemptionFeeBasisPoints(_token, 998) │ │
│ │ • USDY已被标记为稳定币 │ │
│ │ • 如果_token也是稳定币: 4 bps │ │
│ │ • 如果_token是YT代币: 30 bps │ │
│ │ → 本次(YT-B): 30 bps │ │
│ │ │ │
│ │ ⑤ 计算扣费后的输出 │ │
│ │ amountOut = 996.01 × (1 - 0.3%) │ │
│ │ = 993.02 YT-B │ │
│ │ │ │
│ │ ⑥ 池子记账 │ │
│ │ _decreasePoolAmount(_token, 993.02) // 减少池中代币 │ │
│ │ _decreaseUsdyAmount(_token, ...) // 减少债务 │ │
│ │ │ │
│ │ ⑦ 销毁USDY │ │
│ │ USDY.burn(address(this), 998) │ │
│ │ │ │
│ │ ⑧ 转出代币 │ │
│ │ transfer(_receiver, 993.02 YT-B) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 返回: 993.02 YT-B │
└────────────────────────────┬────────────────────────────────────────┘
│ 5. 检查滑点保护
│ require(993.02 >= 990) ✓
┌─────────────────────────────────────────────────────────────────────┐
│ 用户收到 │
│ ✓ 993.02 YT-B │
│ │
│ 成本分析: │
│ 赎回: 1000 ytLP │
│ 手续费: ~3 YT-B (0.3%) │
│ 价差损失: ~4 YT-B (0.4%) │
│ 总成本: ~0.7% │
└─────────────────────────────────────────────────────────────────────┘
```
### 涉及的合约函数:
| 合约 | 函数 | 作用 |
|------|------|------|
| **YTRewardRouter** | `removeLiquidity()` | 用户入口 |
| **YTPoolManager** | `removeLiquidityForAccount()` | 计算USDY价值 |
| **YTPoolManager** | `getAumInUsdy(false)` | 获取AUM使用MinPrice|
| **YTVault** | `sellUSDY()` | 用USDY换回代币 |
| **YTVault** | `getRedemptionFeeBasisPoints()` | 获取赎回手续费率 |
| **YTPriceFeed** | `getPrice(_token, true)` | 获取MaxPrice |
| **USDY** | `mint()` | 补充USDY如需要|
| **USDY** | `burn()` | 销毁USDY |
| **YTLPToken** | `burn()` | 销毁ytLP |
---
## 3. 代币互换流程
```
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (User) │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 swapYT(tokenIn, tokenOut, amountIn, minOut, receiver)
┌─────────────────────────────────────────────────────────────────────┐
│ YTRewardRouter.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function swapYT( │
│ address _tokenIn, // YT-A │
│ address _tokenOut, // YT-B │
│ uint256 _amountIn, // 1000 │
│ uint256 _minOut, // 990滑点保护
│ address _receiver // 接收地址 │
│ ) external nonReentrant whenNotPaused │
│ │
│ 修饰符检查: │
│ ✓ nonReentrant - 防重入保护 │
│ ✓ whenNotPaused - 暂停检查 │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. transferFrom(user → YTVault)
│ 转移1000个YT-A直接到Vault
┌─────────────────────────────────────────────────────────────────────┐
│ YTVault.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function swap( │
│ address _tokenIn, // YT-A │
│ address _tokenOut, // YT-B │
│ address _receiver // 用户 │
│ ) │
│ │
│ 步骤: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ① 安全检查 │ │
│ │ ✓ swap已启用 │ │
│ │ ✓ tokenIn已白名单 │ │
│ │ ✓ tokenOut已白名单 │ │
│ │ ✓ tokenIn != tokenOut │ │
│ │ │ │
│ │ ② transferIn(_tokenIn) │ │
│ │ • 检测转入的YT-A数量: 1000 │ │
│ │ │ │
│ │ ③ 检查单笔交易限额 │ │
│ │ if (maxSwapAmount[tokenIn] > 0) { │ │
│ │ require(1000 <= maxSwapAmount[tokenIn]) │ │
│ │ } │ │
│ │ │ │
│ │ ④ 获取价格(对协议有利的定价) │ │
│ │ priceIn = _getPrice(_tokenIn, false) // MinPrice │ │
│ │ = $0.998 (低估输入) │ │
│ │ priceOut = _getPrice(_tokenOut, true) // MaxPrice │ │
│ │ = $1.002 (高估输出) │ │
│ │ │ │
│ │ ⑤ 计算中间USDY价值 │ │
│ │ usdyAmount = 1000 × $0.998 / 1e30 = $998 │ │
│ │ │ │
│ │ ⑥ 检查每日交易量限制 │ │
│ │ _checkDailySwapLimit(998) │ │
│ │ │ │
│ │ ⑦ 计算理论输出(扣费前) │ │
│ │ amountOut = 998 × 1e30 / $1.002 │ │
│ │ = 996.01 YT-B │ │
│ │ │ │
│ │ ⑧ 获取swap手续费率动态 │ │
│ │ feeBps = getSwapFeeBasisPoints(tokenIn, tokenOut, 998) │ │
│ │ │ │
│ │ 判断逻辑: │ │
│ │ • 是否稳定币互换? │ │
│ │ isStableSwap = stableTokens[YT-A] && stableTokens[YT-B] │ │
│ │ = false && false = false │ │
│ │ │ │
│ │ • 基础费率: 30 bps (非稳定币) │ │
│ │ • 税率: 50 bps │ │
│ │ │ │
│ │ • 动态调整(改善平衡 → 降低费率): │ │
│ │ ├─ YT-A: 当前50k, 目标40k → 过多 → 增加费率 │ │
│ │ └─ YT-B: 当前30k, 目标40k → 不足 → 减少费率 │ │
│ │ │ │
│ │ • 计算两个代币的费率,取较高者 │ │
│ │ fee_A = getFeeBasisPoints(YT-A, 998, 30, 50, true) │ │
│ │ = 40 bps (恶化平衡,提高) │ │
│ │ fee_B = getFeeBasisPoints(YT-B, 998, 30, 50, false) │ │
│ │ = 20 bps (改善平衡,降低) │ │
│ │ │ │
│ │ → 最终费率: max(40, 20) = 40 bps (0.4%) │ │
│ │ │ │
│ │ ⑨ 扣除手续费 │ │
│ │ amountOutAfterFees = 996.01 × (1 - 0.4%) │ │
│ │ = 992.02 YT-B │ │
│ │ │ │
│ │ ⑩ 全局滑点保护 │ │
│ │ _validateSwapSlippage(1000, 992.02, priceIn, priceOut) │ │
│ │ │ │
│ │ ⑪ 更新池子状态 │ │
│ │ _increasePoolAmount(YT-A, 1000) // YT-A入池 │ │
│ │ _decreasePoolAmount(YT-B, 992.02) // YT-B出池 │ │
│ │ _increaseUsdyAmount(YT-A, 998) // YT-A债务+998 │ │
│ │ _decreaseUsdyAmount(YT-B, 998) // YT-B债务-998 │ │
│ │ │ │
│ │ ⑫ 转出代币 │ │
│ │ transfer(_receiver, 992.02 YT-B) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 返回: 992.02 YT-B │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 检查滑点保护
│ require(992.02 >= 990) ✓
┌─────────────────────────────────────────────────────────────────────┐
│ 用户收到 │
│ ✓ 992.02 YT-B │
│ │
│ 成本分析: │
│ 输入: 1000 YT-A │
│ 价差损失: ~4 YT (0.4%) │
│ 手续费: ~4 YT (0.4% 动态) │
│ 输出: 992.02 YT-B │
│ 总成本: ~0.8% │
│ │
│ 有效汇率: 1 YT-A = 0.9920 YT-B │
└─────────────────────────────────────────────────────────────────────┘
```
### 涉及的合约函数:
| 合约 | 函数 | 作用 |
|------|------|------|
| **YTRewardRouter** | `swapYT()` | 用户入口,转移代币 |
| **YTVault** | `swap()` | 执行代币互换 |
| **YTVault** | `getSwapFeeBasisPoints()` | 获取动态swap费率 |
| **YTVault** | `getFeeBasisPoints()` | 计算单个代币的动态费率 |
| **YTVault** | `_validateSwapSlippage()` | 全局滑点保护 |
| **YTVault** | `_checkDailySwapLimit()` | 检查每日限额 |
| **YTPriceFeed** | `getMinPrice()` | 输入代币价格 |
| **YTPriceFeed** | `getMaxPrice()` | 输出代币价格 |
---
## 4. 添加白名单代币流程
```
┌─────────────────────────────────────────────────────────────────────┐
│ 管理员 (Gov) │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 setWhitelistedToken(...)
┌─────────────────────────────────────────────────────────────────────┐
│ YTVault.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function setWhitelistedToken( │
│ address _token, // 新YT代币地址 │
│ uint256 _decimals, // 18 │
│ uint256 _weight, // 权重 10000 (10%) │
│ uint256 _maxUsdyAmount, // 最大USDY债务 1000000e18 │
│ bool _isStable // false (YT代币非稳定币) │
│ ) │
│ │
│ 步骤: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ① 安全检查 │ │
│ │ require(_token != address(0)) │ │
│ │ require(msg.sender == gov) // 只有管理员 │ │
│ │ │ │
│ │ ② 检查是否已在白名单 │ │
│ │ if (!whitelistedTokens[_token]) { │ │
│ │ allWhitelistedTokens.push(_token) // 添加到数组 │ │
│ │ whitelistedTokens[_token] = true // 标记为白名单 │ │
│ │ } │ │
│ │ │ │
│ │ ③ 更新总权重 │ │
│ │ oldWeight = tokenWeights[_token] // 0新代币 │ │
│ │ totalTokenWeights = totalTokenWeights - oldWeight + 10000 │ │
│ │ │ │
│ │ 示例(自动比例调整): │ │
│ │ ┌──────────────┬────────┬──────────┬──────────┐ │ │
│ │ │ 代币 │ 权重 │ 旧占比 │ 新占比 │ │ │
│ │ ├──────────────┼────────┼──────────┼──────────┤ │ │
│ │ │ YT-A (原有) │ 30000 │ 30.0% │ 27.27% │ ← 自动变化│ │
│ │ │ YT-B (原有) │ 30000 │ 30.0% │ 27.27% │ │ │
│ │ │ YT-C (原有) │ 30000 │ 30.0% │ 27.27% │ │ │
│ │ │ WUSD (原有) │ 10000 │ 10.0% │ 9.09% │ │ │
│ │ │ YT-D (新增) │ 10000 │ - │ 9.09% │ ← 新增 │ │
│ │ ├──────────────┼────────┼──────────┼──────────┤ │ │
│ │ │ 总计 │ 110000 │ 100% │ 100% │ │ │
│ │ └──────────────┴────────┴──────────┴──────────┘ │ │
│ │ │ │
│ │ ⚠️ 重要使用相对权重不需要保持总和为100000 │ │
│ │ totalTokenWeights可以是任意值关键是相对比例 │ │
│ │ │ │
│ │ ④ 设置代币参数 │ │
│ │ tokenDecimals[_token] = 18 │ │
│ │ tokenWeights[_token] = 10000 │ │
│ │ maxUsdyAmounts[_token] = 1000000e18 // 最大100万USDY债务 │ │
│ │ stableTokens[_token] = false // 标记非稳定币 │ │
│ │ │ │
│ │ ⑤ 白名单添加完成 │ │
│ │ ✓ 代币可以被存入 │ │
│ │ ✓ 代币可以被swap │ │
│ │ ✓ 代币开始计入AUM │ │
│ └─────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 需要配置价格预言机
┌─────────────────────────────────────────────────────────────────────┐
│ YTPriceFeed.sol │
│ ───────────────────────────────────────────────────────────────── │
│ 需要执行的配置: │
│ │
│ ① 设置价差 (可选但推荐) │
│ setSpreadBasisPoints(YT-D, 20) // 0.2% 价差 │
│ │
│ ② 初始化价格 (如果需要) │
│ forceUpdatePrice(YT-D, 1e30) // 初始价格 $1.00 │
│ │
│ 注意: YT代币需要实现 assetPrice() 接口 │
│ 价格预言机会自动读取该接口获取价格 │
└─────────────────────────────────────────────────────────────────────┘
│ 3. 配置完成
┌─────────────────────────────────────────────────────────────────────┐
│ 白名单生效 │
│ │
│ 用户可以: │
│ ✓ 用YT-D添加流动性 │
│ ✓ 用ytLP换回YT-D │
│ ✓ YT-D与其他YT代币互换 │
│ │
│ 协议会: │
│ ✓ 将YT-D计入AUM按10%权重) │
│ ✓ 对YT-D使用0.3%的swap费率非稳定币
│ ✓ 动态调整费率以维持池子平衡 │
└─────────────────────────────────────────────────────────────────────┘
```
### 涉及的合约函数:
| 合约 | 函数 | 作用 |
|------|------|------|
| **YTVault** | `setWhitelistedToken()` | 添加代币到白名单 |
| **YTPriceFeed** | `setSpreadBasisPoints()` | 设置代币价差 |
| **YTPriceFeed** | `forceUpdatePrice()` | 初始化价格(可选)|
### 白名单代币要求:
```
┌─────────────────────────────────────────────────────────────────────┐
│ YT代币必须实现的接口 │
│ │
│ interface IYTToken { │
│ // 必需返回当前资产价格30位精度
│ function assetPrice() external view returns (uint256); │
│ │
│ // 可选:返回最后价格更新时间 │
│ function lastPriceUpdate() external view returns (uint256); │
│ │
│ // ERC20标准接口 │
│ function decimals() external view returns (uint8); │
│ function balanceOf(address) external view returns (uint256); │
│ function transfer(address, uint256) external returns (bool); │
│ function transferFrom(address, address, uint256) │
│ external returns (bool); │
│ } │
│ │
│ 价格示例: │
│ $1.00 = 1 × 10^30 = 1000000000000000000000000000000 │
│ $0.998 = 998000000000000000000000000000 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 5. 移除白名单代币流程
```
┌─────────────────────────────────────────────────────────────────────┐
│ 管理员 (Gov) │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 先确保池中该代币已清空
│ (用户需先移除流动性)
┌─────────────────────────────────────────────────────────────────────┐
│ 检查代币状态 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 检查池子余额: │ │
│ │ poolAmounts[YT-D] = ? │ │
│ │ usdyAmounts[YT-D] = ? │ │
│ │ │ │
│ │ 安全建议: │ │
│ │ ✓ 池中余额应为0或接近0 │ │
│ │ ✓ USDY债务应为0 │ │
│ │ ✓ 没有待处理的用户流动性 │ │
│ └─────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 调用 clearWhitelistedToken(token)
┌─────────────────────────────────────────────────────────────────────┐
│ YTVault.sol │
│ ───────────────────────────────────────────────────────────────── │
│ function clearWhitelistedToken(address _token) │
│ │
│ 步骤: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ① 安全检查 │ │
│ │ require(whitelistedTokens[_token]) // 必须已在白名单 │ │
│ │ require(msg.sender == gov) // 只有管理员 │ │
│ │ │ │
│ │ ② 更新总权重 │ │
│ │ oldWeight = tokenWeights[YT-D] = 10000 │ │
│ │ totalTokenWeights = totalTokenWeights - 10000 │ │
│ │ │ │
│ │ 结果: │ │
│ │ ┌──────────────┬────────┬────────┐ │ │
│ │ │ 代币 │ 权重 │ 占比 │ │ │
│ │ ├──────────────┼────────┼────────┤ │ │
│ │ │ YT-A │ 30000 │ 30% │ │ │
│ │ │ YT-B │ 30000 │ 30% │ │ │
│ │ │ YT-C │ 30000 │ 30% │ │ │
│ │ │ WUSD │ 10000 │ 10% │ │ │
│ │ │ YT-D (删除) │ 0 │ - │ ← 已移除 │ │
│ │ ├──────────────┼────────┼────────┤ │ │
│ │ │ 总计 │ 100000 │ 100% │ │ │
│ │ └──────────────┴────────┴────────┘ │ │
│ │ │ │
│ │ ③ 清除所有配置 │ │
│ │ delete whitelistedTokens[_token] // 从白名单移除 │ │
│ │ delete stableTokens[_token] // 清除稳定币标记 │ │
│ │ delete tokenDecimals[_token] // 清除精度 │ │
│ │ delete tokenWeights[_token] // 清除权重 │ │
│ │ delete maxUsdyAmounts[_token] // 清除最大债务限制 │ │
│ │ │ │
│ │ ④ 白名单移除完成 │ │
│ │ ✓ 代币无法被存入 │ │
│ │ ✓ 代币无法被swap │ │
│ │ ✓ 代币不再计入AUM │ │
│ └─────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 后续处理(可选)
┌─────────────────────────────────────────────────────────────────────┐
│ 可选的清理操作 │
│ │
│ ① 从 allWhitelistedTokens 数组中移除 │
│ (注意: 合约当前没有自动移除需要考虑gas成本) │
│ │
│ ② 如果池中还有少量余额,可以提取 │
│ (启用紧急模式后) │
│ emergencyMode = true │
│ withdrawToken(YT-D, gov, amount) │
│ │
│ ③ 从价格预言机移除配置(可选) │
│ 不强制,但可以节省存储 │
└─────────────────────────────────────────────────────────────────────┘
│ 4. 完成
┌─────────────────────────────────────────────────────────────────────┐
│ 移除完成 │
│ │
│ 用户无法: │
│ ✗ 用YT-D添加流动性 │
│ ✗ 用ytLP换回YT-D │
│ ✗ YT-D与其他YT代币互换 │
│ │
│ 协议: │
│ ✓ YT-D不再计入AUM计算 │
│ ✓ 权重已重新分配 │
└─────────────────────────────────────────────────────────────────────┘
```
### 涉及的合约函数:
| 合约 | 函数 | 作用 |
|------|------|------|
| **YTVault** | `clearWhitelistedToken()` | 从白名单移除代币 |
| **YTVault** | `setEmergencyMode()` | 启用紧急模式(如需提取余额)|
| **YTVault** | `withdrawToken()` | 提取剩余代币(紧急模式下)|
### 移除白名单的注意事项:
```
⚠️ 重要提示:
1. 移除前确保:
• 所有用户已移除该代币的流动性
• poolAmounts[token] = 0 或接近0
• usdyAmounts[token] = 0
• 没有pending的交易
2. 移除后影响:
• 该代币的所有操作立即失效
• 如果用户还持有该代币的ytLP无法再换回该代币
• 总权重自动减少,其他代币占比自动增加(不需要手动调整)
3. 不可逆操作:
• 移除后该代币在 allWhitelistedTokens 数组中仍然存在(历史记录)
• 但所有配置和权限已清除
• 如需重新添加,需要再次调用 setWhitelistedToken()
4. 最佳实践:
• 提前公告,给用户足够时间移除流动性
• 移除前暂停该代币的新增流动性
• 记录剩余余额,在紧急模式下安全提取
• 移除后验证AUM计算正确
```
---
## 总结:合约调用关系
```
┌─────────────────┐
│ User │
└────────┬────────┘
┌────────▼────────┐
│ YTRewardRouter │ ◄── 用户入口
│ • addLiquidity │
│ • removeLiq... │
│ • swapYT │
└────────┬────────┘
┌────────▼────────┐
│ YTPoolManager │ ◄── 流动性管理
│ • addLiq... │
│ • removeLiq... │
│ • getAumInUsdy │
└────────┬────────┘
┌───────────────────┼───────────────────┐
│ │ │
┌────▼────┐ ┌──────▼──────┐ ┌────▼─────┐
│ YTVault │ │ YTLPToken │ │ USDY │
│ • buyUS │ │ • mint │ │ • mint │
│ • sellU │ │ • burn │ │ • burn │
│ • swap │ └─────────────┘ └──────────┘
└────┬────┘
┌────▼────────┐
│ YTPriceFeed │ ◄── 价格预言机
│ • getPrice │
│ • 价差配置 │
└─────────────┘
```
### 核心合约职责:
| 合约 | 职责 | 关键功能 |
|------|------|----------|
| **YTRewardRouter** | 用户入口 | 接收用户请求,处理代币转移 |
| **YTPoolManager** | 流动性管理 | 计算ytLP管理AUM |
| **YTVault** | 资金池 | 存储资产执行swap管理手续费 |
| **YTPriceFeed** | 价格预言机 | 提供价格,应用价差 |
| **USDY** | 计价代币 | 内部记账单位 |
| **YTLPToken** | LP代币 | 代表用户份额 |
---
## 6. 系统部署和初始化流程
### 6.1 部署 YTPriceFeed优化版
```
┌─────────────────────────────────────────────────────────────────────┐
│ 部署者 (Deployer) │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 首先部署 WUSD
┌─────────────────────────────────────────────────────────────────────┐
│ 部署 WUSD 合约 │
│ ───────────────────────────────────────────────────────────────── │
│ WUSD wusd = new WUSD() │
│ wusd.initialize("Wrapped USD", "WUSD") │
│ │
│ 得到: 0x7Cd017ca5ddb86861FA983a34b5F495C6F898c41 │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 部署 YTPriceFeed传入WUSD地址
┌─────────────────────────────────────────────────────────────────────┐
│ 部署 YTPriceFeed可升级
│ ───────────────────────────────────────────────────────────────── │
│ const YTPriceFeed = await ethers.getContractFactory("YTPriceFeed") │
│ const priceFeed = await upgrades.deployProxy( │
│ YTPriceFeed, │
│ [wusdAddress], // ← 初始化参数WUSD地址 │
│ { │
│ kind: "uups", │
│ initializer: "initialize" │
│ } │
│ ) │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. initialize(address _wusdAddress)
┌─────────────────────────────────────────────────────────────────────┐
│ YTPriceFeed.initialize() │
│ ───────────────────────────────────────────────────────────────── │
│ function initialize(address _wusdAddress) external initializer │
│ │
│ 步骤: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ① __UUPSUpgradeable_init() │ │
│ │ • 初始化UUPS升级功能 │ │
│ │ │ │
│ │ ② 验证WUSD地址 │ │
│ │ if (_wusdAddress == address(0)) revert InvalidAddress() │ │
│ │ ✓ 0x7Cd... 有效 │ │
│ │ │ │
│ │ ③ 保存WUSD地址 │ │
│ │ wusdAddress = _wusdAddress │ │
│ │ │ │
│ │ ④ 设置治理地址 │ │
│ │ gov = msg.sender (部署者) │ │
│ │ │ │
│ │ ⑤ 设置默认参数 │ │
│ │ maxPriceChangeBps = 500 // 5% 最大价格变动 │ │
│ └─────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────────┘
│ 4. 初始化完成
┌─────────────────────────────────────────────────────────────────────┐
│ YTPriceFeed 就绪 │
│ ───────────────────────────────────────────────────────────────── │
│ 状态: │
│ • wusdAddress: 已设置 ✓ │
│ • gov: 已设置 ✓ │
│ • maxPriceChangeBps: 500 (5%) │
│ • wusdPriceSource: 未设置(稍后配置) │
│ │
│ 优势: │
│ ✓ 减少一个初始化参数 │
│ ✓ WUSD地址在初始化时就确定 │
│ ✓ 避免后续单独设置WUSD地址 │
│ ✓ 简化部署流程 │
└─────────────────────────────────────────────────────────────────────┘
```
### 6.2 完整系统部署流程
```
┌─────────────────────────────────────────────────────────────────────┐
│ 系统部署顺序 │
│ │
│ 步骤 1: 部署 WUSD │
│ └─→ WUSD.initialize("Wrapped USD", "WUSD") │
│ │
│ 步骤 2: 部署代币合约 │
│ ├─→ USDY.initialize() │
│ └─→ YTLPToken.initialize() │
│ │
│ 步骤 3: 部署 YTPriceFeed │
│ └─→ YTPriceFeed.initialize(wusdAddress) ← 传入WUSD地址 │
│ │
│ 步骤 4: 部署 YTVault │
│ └─→ YTVault.initialize(usdyAddress, priceFeedAddress) │
│ │
│ 步骤 5: 部署 YTPoolManager │
│ └─→ YTPoolManager.initialize( │
│ vaultAddress, │
│ usdyAddress, │
│ ytLPAddress, │
│ cooldownDuration │
│ ) │
│ │
│ 步骤 6: 部署 YTRewardRouter │
│ └─→ YTRewardRouter.initialize( │
│ usdyAddress, │
│ ytLPAddress, │
│ poolManagerAddress, │
│ vaultAddress │
│ ) │
│ │
│ 步骤 7: 配置权限 │
│ ├─→ usdy.addVault(vaultAddress) │
│ ├─→ usdy.addVault(poolManagerAddress) │
│ ├─→ ytlp.setMinter(poolManagerAddress, true) │
│ ├─→ vault.setPoolManager(poolManagerAddress) │
│ ├─→ vault.setSwapper(routerAddress, true) │
│ └─→ poolManager.setHandler(routerAddress, true) │
│ │
│ 步骤 8: 配置 YTPriceFeed │
│ ├─→ priceFeed.setWusdPriceSource(ytAssetVaultAddress) │
│ ├─→ priceFeed.setKeeper(keeperAddress, true) │
│ └─→ priceFeed.setMaxPriceChangeBps(500) │
│ │
│ 步骤 9: 配置 YTVault 参数 │
│ ├─→ vault.setSwapFees(30, 4, 50, 20) │
│ ├─→ vault.setDynamicFees(true) │
│ └─→ vault.setMaxSwapSlippageBps(1000) │
│ │
│ 步骤 10: 添加白名单代币 │
│ ├─→ vault.setWhitelistedToken(ytTokenA, 18, 4000, maxAmount, false) │
│ ├─→ vault.setWhitelistedToken(ytTokenB, 18, 3000, maxAmount, false) │
│ └─→ vault.setWhitelistedToken(ytTokenC, 18, 2000, maxAmount, false) │
│ │
│ 步骤 11: 初始化价格 │
│ ├─→ priceFeed.forceUpdatePrice(ytTokenA, 1e30) │
│ ├─→ priceFeed.forceUpdatePrice(ytTokenB, 1e30) │
│ └─→ priceFeed.forceUpdatePrice(ytTokenC, 1e30) │
│ │
│ ✓ 系统部署完成,可以开始使用 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 7. 路由器暂停功能流程
### 7.1 暂停路由器
```
┌─────────────────────────────────────────────────────────────────────┐
│ Gov (系统管理员) │
│ 检测到: 需要紧急暂停用户操作 │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 router.pause()
┌─────────────────────────────────────────────────────────────────────┐
│ YTRewardRouter.pause() │
│ ───────────────────────────────────────────────────────────────── │
│ function pause() external onlyGov │
│ │
│ 权限检查: │
│ ✓ onlyGov - 只有治理地址可调用 │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 执行暂停
┌─────────────────────────────────────────────────────────────────────┐
│ Pausable._pause() │
│ ───────────────────────────────────────────────────────────────── │
│ internal _pause() │
│ • 设置 paused = true │
│ • 触发 Paused(msg.sender) 事件 │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 暂停生效
┌─────────────────────────────────────────────────────────────────────┐
│ Router已暂停 │
│ ───────────────────────────────────────────────────────────────── │
│ 被阻止的操作revert EnforcedPause
│ ✗ addLiquidity() - 用户无法添加流动性 │
│ ✗ removeLiquidity() - 用户无法移除流动性 │
│ ✗ swapYT() - 用户无法进行代币互换 │
│ │
│ 仍可用的操作: │
│ ✓ getYtLPPrice() - 查询ytLP价格 │
│ ✓ getAccountValue() - 查询账户价值 │
│ │
│ 系统状态: │
│ • 所有用户资金操作暂停 │
│ • 底层 YTVault 和 YTPoolManager 仍然运行 │
│ • 直接调用 PoolManager 也会被阻止(权限检查) │
│ • 查询功能不受影响 │
│ │
│ 用户影响: │
│ • 无法通过Router进行任何交易 │
│ • 资产安全锁定 │
│ • 可以查看余额和价值 │
└─────────────────────────────────────────────────────────────────────┘
```
### 7.2 恢复路由器
```
┌─────────────────────────────────────────────────────────────────────┐
│ Gov (系统管理员) │
│ 问题已解决,恢复正常运行 │
└────────────────────────────┬────────────────────────────────────────┘
│ 1. 调用 router.unpause()
┌─────────────────────────────────────────────────────────────────────┐
│ YTRewardRouter.unpause() │
│ ───────────────────────────────────────────────────────────────── │
│ function unpause() external onlyGov │
│ │
│ 权限检查: │
│ ✓ onlyGov - 只有治理地址可调用 │
└────────────────────────────┬────────────────────────────────────────┘
│ 2. 执行恢复
┌─────────────────────────────────────────────────────────────────────┐
│ Pausable._unpause() │
│ ───────────────────────────────────────────────────────────────── │
│ internal _unpause() │
│ • 设置 paused = false │
│ • 触发 Unpaused(msg.sender) 事件 │
└────────────────────────────┬────────────────────────────────────────┘
│ 3. 恢复完成
┌─────────────────────────────────────────────────────────────────────┐
│ Router恢复正常 │
│ ───────────────────────────────────────────────────────────────── │
│ 所有功能恢复: │
│ ✓ addLiquidity() - 用户可以添加流动性 │
│ ✓ removeLiquidity() - 用户可以移除流动性 │
│ ✓ swapYT() - 用户可以进行代币互换 │
│ │
│ 系统状态: │
│ • 所有操作恢复正常 │
│ • 用户可以继续交易 │
│ • 暂停期间的数据和状态完全保留 │
└─────────────────────────────────────────────────────────────────────┘
```
### 7.3 多层暂停策略
```
┌─────────────────────────────────────────────────────────────────────┐
│ 紧急情况处理策略 │
│ │
│ 场景 1: 轻度风险 - 仅暂停Router │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ 适用场景: │
│ • Router合约发现问题 │
│ • 前端交互异常 │
│ • 用户操作需要临时限制 │
│ │
│ 操作: │
│ router.pause() │
│ │
│ 影响: │
│ • 用户无法通过Router操作 │
│ • YTVault、YTPoolManager 继续运行 │
│ • 其他集成方可能仍可直接调用 PoolManager如有权限
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ 场景 2: 中度风险 - 暂停Router + 启用Vault紧急模式 │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ 适用场景: │
│ • Vault合约发现潜在问题 │
│ • 需要全面阻止交易 │
│ • 准备系统升级 │
│ │
│ 操作: │
│ router.pause() │
│ vault.setEmergencyMode(true) │
│ │
│ 影响: │
│ • Router完全暂停 │
│ • Vault的swap、buyUSDY、sellUSDY等操作全部阻止 │
│ • 系统几乎完全冻结 │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ 场景 3: 高度风险 - 全面暂停Router + 所有YTAssetVaults
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ 适用场景: │
│ • 发现重大安全漏洞 │
│ • YTAssetVault代币价格异常 │
│ • 系统性风险 │
│ │
│ 操作: │
│ router.pause() │
│ vault.setEmergencyMode(true) │
│ factory.pauseVaultBatch([ytTokenA, ytTokenB, ytTokenC]) │
│ │
│ 影响: │
│ • 整个系统完全冻结 │
│ • 用户无法进行任何资金操作 │
│ • YTAssetVault的存款/提款也被暂停 │
│ • 最大程度保护用户资产 │
└─────────────────────────────────────────────────────────────────────┘
```
### 7.4 暂停功能的最佳实践
```
┌─────────────────────────────────────────────────────────────────────┐
│ 使用暂停功能的建议 │
│ │
│ 何时使用暂停: │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ 1⃣ 安全事件响应 │
│ • 发现安全漏洞或异常行为 │
│ • 立即暂停相关操作 │
│ • 评估影响范围 │
│ • 修复后恢复 │
│ │
│ 2⃣ 系统维护升级 │
│ • 计划性系统升级 │
│ • 提前公告用户 │
│ • 暂停操作执行升级 │
│ • 测试验证后恢复 │
│ │
│ 3⃣ 市场异常波动 │
│ • 价格剧烈波动 │
│ • 流动性枯竭 │
│ • 暂停防止损失扩大 │
│ • 市场稳定后恢复 │
│ │
│ 4⃣ 合规要求 │
│ • 监管调查配合 │
│ • 临时限制操作 │
│ • 保持透明沟通 │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ 操作流程: │
│ ✓ 发现问题 → 立即评估 │
│ ✓ 确定暂停范围Router / Vault / 全系统) │
│ ✓ 执行暂停命令 │
│ ✓ 公告用户(如可能) │
│ ✓ 调查和修复 │
│ ✓ 测试验证 │
│ ✓ 恢复系统 │
│ ✓ 监控运行 │
│ │
│ 注意事项: │
│ ⚠️ 暂停不影响资产价值和余额 │
│ ⚠️ 查询功能始终可用 │
│ ⚠️ 暂停期间可以更新价格仅gov
│ ⚠️ 恢复前应充分测试 │
│ ⚠️ 保持与用户的沟通透明 │
└─────────────────────────────────────────────────────────────────────┘
```