Files
Heyue_test/document/ytLp流动性池系统操作流程文档.md
2025-12-15 14:45:51 +00:00

682 lines
60 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

ytLp流动性池系统操作流程文档
目录
1. [添加流动性流程](#1-添加流动性流程)
2. [移除流动性流程](#2-移除流动性流程)
3. [代币互换流程](#3-代币互换流程)
4. [添加白名单代币流程](#4-添加白名单代币流程)
5. [移除白名单代币流程](#5-移除白名单代币流程)
1. 添加流动性流程
Plain Text
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (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滑点保护
│ ) │
└────────────────────────────┬────────────────────────────────────────┘
│ 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. 移除流动性流程
Plain Text
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (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 // 接收地址 │
│ ) │
└────────────────────────────┬────────────────────────────────────────┘
│ 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. 代币互换流程
Plain Text
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 (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 // 接收地址 │
│ ) │
└────────────────────────────┬────────────────────────────────────────┘
│ 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. 添加白名单代币流程
Plain Text
┌─────────────────────────────────────────────────────────────────────┐
│ 管理员 (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() 初始化价格(可选)
白名单代币要求:
Plain Text
┌─────────────────────────────────────────────────────────────────────┐
│ YT代币必须实现的接口 │
│ │
│ interface IYTToken { │
│ // 必需返回当前资产价格30位精度
│ function assetPrice() 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. 移除白名单代币流程
Plain Text
┌─────────────────────────────────────────────────────────────────────┐
│ 管理员 (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() 提取剩余代币(紧急模式下)
移除白名单的注意事项:
Plain Text
⚠️ 重要提示:
1. 移除前确保:
• 所有用户已移除该代币的流动性
• poolAmounts[token] = 0 或接近0
• usdyAmounts[token] = 0
• 没有pending的交易
2. 移除后影响:
• 该代币的所有操作立即失效
• 如果用户还持有该代币的ytLP无法再换回该代币
• 需要重新调整其他代币的权重以维持池子平衡
3. 不可逆操作:
• 移除后该代币在 allWhitelistedTokens 数组中仍然存在(历史记录)
• 但所有配置和权限已清除
• 如需重新添加,需要再次调用 setWhitelistedToken()
4. 最佳实践:
• 提前公告,给用户足够时间移除流动性
• 移除前暂停该代币的新增流动性
• 记录剩余余额,在紧急模式下安全提取
• 移除后验证AUM计算正确
核心合约职责:
合约 职责 关键功能
YTRewardRouter 用户入口 接收用户请求,处理代币转移
YTPoolManager 流动性管理 计算ytLP管理AUM
YTVault 资金池 存储资产执行swap管理手续费
YTPriceFeed 价格预言机 提供价格,应用价差
USDY 计价代币 内部记账单位
YTLPToken LP代币 代表用户份额