上传文件至 document
This commit is contained in:
1184
document/Vault金库系统操作流程文档.md
Normal file
1184
document/Vault金库系统操作流程文档.md
Normal file
File diff suppressed because it is too large
Load Diff
551
document/ytLp池子合约流程文档.md
Normal file
551
document/ytLp池子合约流程文档.md
Normal file
@@ -0,0 +1,551 @@
|
||||
ytLp池子合约流程文档
|
||||
1. 项目概述
|
||||
1.1 设计目标
|
||||
基于GMX GLP原理,设计一个支持多种YT(Yield Token)代币的流动性池系统。
|
||||
1.2 架构对比
|
||||
Plain Text
|
||||
GMX GLP池子: YT流动性池:
|
||||
├─ ETH ├─ YT-A
|
||||
├─ WBTC ├─ YT-B
|
||||
├─ USDC ├─ YT-C
|
||||
├─ USDT ├─ YT-D
|
||||
└─ DAI └─ WUSD
|
||||
↓ ↓
|
||||
以USDG统一计价 以USDY统一计价
|
||||
核心特点:
|
||||
Markdown
|
||||
- 一篮子YT代币: YT-A, YT-B, YT-C等(不同的收益代币)
|
||||
- WUSD代币支持: 支持WUSD稳定币(地址: `0x7Cd017ca5ddb86861FA983a34b5F495C6F898c41`)
|
||||
- 统一底层: 所有YT都基于USDC
|
||||
- LP代币(ytLP): 代表用户在池子中的份额
|
||||
- Swap功能: YT-A ↔ YT-B ↔ YT-C ↔ WUSD
|
||||
- 计价单位: USDY(完全模拟GMX的USDG)
|
||||
- 动态手续费: 根据池子占比自动调整手续费
|
||||
- 自动复利: 手续费直接留在池子中,ytLP价值自动增长
|
||||
2. 系统流程图
|
||||
Plain Text
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 用户交互层 │
|
||||
│ YTRewardRouter.sol │
|
||||
│ (addLiquidity / removeLiquidity) │
|
||||
└──────────────┬──────────────────────────┘
|
||||
│
|
||||
┌───────────┴─────────────┐
|
||||
▼ ▼
|
||||
┌───────────────┐ ┌──────────────────┐
|
||||
│ YTPoolManager │◄── │ YTVault │
|
||||
│ (流动性管理) │ │ (资金池+动态费率) │
|
||||
└───────┬───────┘ └──────┬───────────┘
|
||||
│ │
|
||||
│ ├──► YT-A (40%)
|
||||
│ ├──► YT-B (30%)
|
||||
│ ├──► YT-C (20%)
|
||||
│ └──► WUSD (10%)
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ ytLP Token │
|
||||
│ (ERC20) │
|
||||
│ 持有即增值 │
|
||||
└───────────────┘
|
||||
|
||||
辅助合约:
|
||||
├─ USDY.sol (统一计价代币)
|
||||
└─ YTPriceFeed.sol (价格读取器)
|
||||
流程图查看网站:https://www.processon.com/mermaid
|
||||
2.1 系统总览架构图
|
||||
Plain Text
|
||||
graph TB
|
||||
subgraph "用户交互层"
|
||||
User[用户]
|
||||
Router[YTRewardRouter<br/>一站式入口]
|
||||
end
|
||||
|
||||
subgraph "核心业务层"
|
||||
PoolManager[YTPoolManager<br/>流动性管理]
|
||||
Vault[YTVault<br/>资金池 + 动态手续费]
|
||||
USDY[USDY Token<br/>统一计价单位]
|
||||
end
|
||||
|
||||
subgraph "资产层"
|
||||
YTA[YT-A Token<br/>40% 目标占比]
|
||||
YTB[YT-B Token<br/>30% 目标占比]
|
||||
YTC[YT-C Token<br/>20% 目标占比]
|
||||
WUSD[WUSD Token<br/>10% 目标占比<br/>0x7Cd017...c41]
|
||||
end
|
||||
|
||||
subgraph "LP代币层"
|
||||
YTLP[ytLP Token<br/>流动性凭证]
|
||||
end
|
||||
|
||||
subgraph "奖励层"
|
||||
FeeTracker[FeeYtLPTracker<br/>手续费奖励追踪]
|
||||
end
|
||||
|
||||
User -->|存入/取出/交换| Router
|
||||
Router -->|管理流动性| PoolManager
|
||||
Router -->|质押ytLP| FeeTracker
|
||||
PoolManager -->|buyUSDY/sellUSDY| Vault
|
||||
PoolManager <-->|铸造/销毁| YTLP
|
||||
PoolManager <-->|转移USDY| USDY
|
||||
|
||||
Vault -->|持有| YTA
|
||||
Vault -->|持有| YTB
|
||||
Vault -->|持有| YTC
|
||||
Vault -->|持有| WUSD
|
||||
Vault <-->|铸造/销毁| USDY
|
||||
|
||||
FeeTracker -->|分发奖励| USDY
|
||||
|
||||
style WUSD fill:#90EE90
|
||||
style USDY fill:#FFD700
|
||||
style Vault fill:#87CEEB
|
||||
2.2 用户存入流程图
|
||||
Plain Text
|
||||
flowchart LR
|
||||
subgraph "输入"
|
||||
A[用户YT代币<br/>10,000 YT-A]
|
||||
end
|
||||
|
||||
subgraph "Router处理"
|
||||
B[收集用户代币]
|
||||
end
|
||||
|
||||
subgraph "PoolManager处理"
|
||||
C[调用Vault.buyUSDY]
|
||||
D[计算ytLP数量<br/>ytLP = USDY * supply / AUM]
|
||||
E[铸造ytLP]
|
||||
end
|
||||
|
||||
subgraph "Vault处理"
|
||||
F[接收YT代币<br/>poolAmounts增加]
|
||||
G[计算手续费<br/>0.3% = 30 YT-A]
|
||||
H[更新USDY债务<br/>usdyAmounts增加]
|
||||
I[铸造USDY<br/>9,970 USDY]
|
||||
end
|
||||
|
||||
subgraph "输出"
|
||||
J[用户获得ytLP<br/>价值9,970 USDY]
|
||||
end
|
||||
|
||||
A --> B
|
||||
B --> C
|
||||
C --> F
|
||||
F --> G
|
||||
G --> H
|
||||
H --> I
|
||||
I --> D
|
||||
D --> E
|
||||
E --> J
|
||||
|
||||
style A fill:#90EE90
|
||||
style J fill:#FFD700
|
||||
style G fill:#FFA07A
|
||||
2.3 用户取出流程图
|
||||
Plain Text
|
||||
flowchart LR
|
||||
subgraph "输入"
|
||||
A[用户ytLP<br/>5,000 ytLP]
|
||||
end
|
||||
|
||||
subgraph "PoolManager处理"
|
||||
B[销毁ytLP]
|
||||
C[计算USDY价值<br/>USDY = ytLP * AUM / supply]
|
||||
D[转USDY给Vault<br/>5,000 USDY]
|
||||
end
|
||||
|
||||
subgraph "Vault处理"
|
||||
E[收到USDY并销毁]
|
||||
F[计算YT数量<br/>根据价格]
|
||||
G[计算手续费<br/>动态费率]
|
||||
H[减少poolAmounts<br/>减少usdyAmounts]
|
||||
I[转YT代币]
|
||||
end
|
||||
|
||||
subgraph "输出"
|
||||
J[用户收到YT<br/>4,985 YT-A]
|
||||
end
|
||||
|
||||
A --> B
|
||||
B --> C
|
||||
C --> D
|
||||
D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> H
|
||||
H --> I
|
||||
I --> J
|
||||
|
||||
style A fill:#FFD700
|
||||
style J fill:#90EE90
|
||||
style G fill:#FFA07A
|
||||
2.4 YT代币互换流程图
|
||||
Plain Text
|
||||
flowchart LR
|
||||
subgraph "输入"
|
||||
A[用户YT-A<br/>1,000 YT-A]
|
||||
end
|
||||
|
||||
subgraph "Vault处理"
|
||||
B[收到YT-A<br/>poolAmounts增加]
|
||||
C[查询价格<br/>priceA, priceB]
|
||||
D[计算USDY中间值<br/>1,000 * priceA]
|
||||
E[计算YT-B数量<br/>USDY / priceB]
|
||||
F[计算动态手续费<br/>检查两端占比]
|
||||
G[更新状态<br/>YT-A增, YT-B减<br/>USDY债务转移]
|
||||
H[转出YT-B]
|
||||
end
|
||||
|
||||
subgraph "输出"
|
||||
I[用户收到YT-B<br/>997 YT-B]
|
||||
end
|
||||
|
||||
A --> B
|
||||
B --> C
|
||||
C --> D
|
||||
D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> H
|
||||
H --> I
|
||||
|
||||
style A fill:#90EE90
|
||||
style I fill:#87CEEB
|
||||
style F fill:#FFA07A
|
||||
2.5 USDY铸造销毁流程图
|
||||
Plain Text
|
||||
graph TB
|
||||
subgraph "USDY生命周期"
|
||||
Create[创建: 用户存入YT]
|
||||
Mint[Vault铸造USDY]
|
||||
Hold[PoolManager持有USDY]
|
||||
Transfer[PoolManager转USDY给Vault]
|
||||
Burn[Vault销毁USDY]
|
||||
Destroy[结束: 用户取回YT]
|
||||
end
|
||||
|
||||
subgraph "关键状态"
|
||||
Supply[USDY总供应量]
|
||||
PoolBalance[PoolManager余额]
|
||||
Debt[各代币USDY债务]
|
||||
end
|
||||
|
||||
Create --> Mint
|
||||
Mint --> Supply
|
||||
Mint --> Hold
|
||||
Hold --> PoolBalance
|
||||
Hold --> Transfer
|
||||
Transfer --> Burn
|
||||
Burn --> Supply
|
||||
Burn --> Destroy
|
||||
|
||||
Mint -.更新.-> Debt
|
||||
Burn -.更新.-> Debt
|
||||
|
||||
style Mint fill:#90EE90
|
||||
style Burn fill:#FFB6C1
|
||||
style Supply fill:#FFD700
|
||||
2.6 动态手续费架构图
|
||||
Plain Text
|
||||
graph TB
|
||||
subgraph "输入层"
|
||||
User[用户操作<br/>存入/取出/Swap]
|
||||
Token[操作的代币<br/>YT-A/YT-B/YT-C/WUSD]
|
||||
Amount[操作数量]
|
||||
end
|
||||
|
||||
subgraph "状态查询层"
|
||||
Current[当前USDY债务<br/>usdyAmounts]
|
||||
Target[目标USDY债务<br/>getTargetUsdyAmount]
|
||||
Weight[代币权重<br/>tokenWeights]
|
||||
Supply[USDY总供应<br/>totalSupply]
|
||||
end
|
||||
|
||||
subgraph "计算层"
|
||||
Calc1[计算操作后债务<br/>nextAmount]
|
||||
Calc2[计算偏离度<br/>initialDiff vs nextDiff]
|
||||
Calc3[判断改善/恶化]
|
||||
end
|
||||
|
||||
subgraph "手续费决策层"
|
||||
Decision{改善平衡?}
|
||||
Rebate[降低手续费<br/>baseFee - rebate]
|
||||
Penalty[提高手续费<br/>baseFee + tax]
|
||||
end
|
||||
|
||||
subgraph "输出层"
|
||||
Final[最终手续费<br/>0% - 0.8%]
|
||||
Effect[效果反馈<br/>引导池子平衡]
|
||||
end
|
||||
|
||||
User --> Token
|
||||
Token --> Amount
|
||||
Amount --> Current
|
||||
|
||||
Current --> Calc1
|
||||
Target --> Calc1
|
||||
Weight --> Target
|
||||
Supply --> Target
|
||||
|
||||
Calc1 --> Calc2
|
||||
Calc2 --> Calc3
|
||||
Calc3 --> Decision
|
||||
|
||||
Decision -->|是| Rebate
|
||||
Decision -->|否| Penalty
|
||||
|
||||
Rebate --> Final
|
||||
Penalty --> Final
|
||||
Final --> Effect
|
||||
|
||||
style Decision fill:#FFD700
|
||||
style Rebate fill:#90EE90
|
||||
style Penalty fill:#FFA07A
|
||||
style Final fill:#87CEEB
|
||||
2.7 各YT代币价格获取图
|
||||
Plain Text
|
||||
graph TB
|
||||
subgraph "价格查询层"
|
||||
Query[Vault查询价格<br/>getPrice]
|
||||
end
|
||||
|
||||
subgraph "YTPriceFeed"
|
||||
Feed[价格读取器]
|
||||
Check{是WUSD?}
|
||||
end
|
||||
|
||||
subgraph "WUSD处理"
|
||||
Fixed[返回固定价格<br/>1.0 USDY]
|
||||
end
|
||||
|
||||
subgraph "YT代币处理"
|
||||
YTContract[YT代币合约]
|
||||
ReadVar[ 直接读取public变量<br/>assetPrice]
|
||||
AddSpread[添加价差<br/>0.2%]
|
||||
end
|
||||
|
||||
subgraph "返回"
|
||||
Price[返回USDY价格<br/>30位小数]
|
||||
end
|
||||
|
||||
Query --> Feed
|
||||
Feed --> Check
|
||||
|
||||
Check -->|是| Fixed
|
||||
Check -->|否| YTContract
|
||||
|
||||
YTContract --> ReadVar
|
||||
Note1[:只读一个变量<br/>Gas成本极低]
|
||||
ReadVar -.说明.-> Note1
|
||||
ReadVar --> AddSpread
|
||||
|
||||
AddSpread --> Price
|
||||
Fixed --> Price
|
||||
|
||||
style Check fill:#FFD700
|
||||
style Fixed fill:#90EE90
|
||||
style ReadVar fill:#87CEEB
|
||||
style Note1 fill:#FFE4B5
|
||||
3. 收益机制设计
|
||||
3.1 自动复利机制
|
||||
Plain Text
|
||||
ytLP自动增值模型
|
||||
|
||||
┌──────────────────────┐
|
||||
│ 用户持有 ytLP │
|
||||
│ (无需质押,无需领取) │
|
||||
└────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ 池子收取手续费 │
|
||||
│ ├─ Swap手续费 │
|
||||
│ ├─ 存取手续费 │
|
||||
│ └─ 全部留在池子中 │
|
||||
└────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ 池子总价值(AUM)自动增长 │
|
||||
│ AUM = Σ(poolAmounts * prices) │
|
||||
│ 手续费 → poolAmounts ↑ │
|
||||
└────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ ytLP价格自动上涨 │
|
||||
│ ytLP价格 = AUM / ytLP总量 │
|
||||
│ 持有者自动获得收益 │
|
||||
└──────────────────────┘
|
||||
3.2 收益来源
|
||||
Plain Text
|
||||
| 收益来源 | 实现方式 | 说明 |
|
||||
|---------|---------|------|
|
||||
| **YT Swap手续费** | 留在池子 | 手续费不转出,直接增加poolAmounts |
|
||||
| **存入手续费** | 留在池子 | 用户存入时收取的手续费留在池子 |
|
||||
| **取出手续费** | 留在池子 | 用户取出时收取的手续费留在池子 |
|
||||
| **YT 增值** | 自动 | YT代币本身增值,体现在价格 |
|
||||
优势:
|
||||
• ✅ 100%手续费归LP
|
||||
• ✅ 自动复利,无需操作
|
||||
• ✅ Gas高效,零额外成本
|
||||
3.3 自动复利效果演示
|
||||
Plain Text
|
||||
场景:Alice持有1000 ytLP
|
||||
|
||||
T0: 初始状态
|
||||
├─ 池子AUM: 100M USDY
|
||||
├─ ytLP总量: 100M
|
||||
├─ ytLP价格: 1.0 USDY
|
||||
├─ Alice持有: 1000 ytLP
|
||||
└─ Alice价值: 1000 USDY
|
||||
|
||||
T1: 1天后(产生手续费)
|
||||
├─ 每日交易量: 5M USDY
|
||||
├─ 平均手续费: 0.3%
|
||||
├─ 日手续费: 15,000 USDY
|
||||
├─ 手续费留在池子 ✅
|
||||
│
|
||||
├─ 池子AUM: 100,015,000 USDY
|
||||
├─ ytLP总量: 100M (不变)
|
||||
├─ ytLP价格: 1.00015 USDY ⬆️
|
||||
├─ Alice持有: 1000 ytLP (不变)
|
||||
└─ Alice价值: 1000.15 USDY ✅ (+0.015%)
|
||||
|
||||
T30: 30天后(复利累积)
|
||||
├─ 累积手续费: ~450,000 USDY
|
||||
├─ 池子AUM: 100,450,000 USDY
|
||||
├─ ytLP价格: 1.0045 USDY ⬆️
|
||||
├─ Alice持有: 1000 ytLP
|
||||
└─ Alice价值: 1004.5 USDY ✅ (+0.45%)
|
||||
|
||||
T365: 1年后(年化收益)
|
||||
├─ 累积手续费: ~5.475M USDY
|
||||
├─ 加上YT增值: ~8M USDY
|
||||
├─ 池子AUM: 113.475M USDY
|
||||
├─ ytLP价格: 1.13475 USDY ⬆️
|
||||
├─ Alice持有: 1000 ytLP
|
||||
└─ Alice价值: 1134.75 USDY ✅ (+13.475%)
|
||||
|
||||
Alice全程无需任何操作!
|
||||
只需持有ytLP,价值自动增长
|
||||
4. 动态手续费
|
||||
4.1 动态手续费机制
|
||||
场景1:YT-A占比过高
|
||||
公式:
|
||||
avg_dev = (|D_after| + |D_before|)/2
|
||||
adjustment = K * avg_dev / target
|
||||
Plain Text
|
||||
当前状态:
|
||||
YT-A: usdyAmounts = 50M, 目标 = 40M (+10M, 占比过高)
|
||||
YT-B: usdyAmounts = 30M, 目标 = 30M (正好)
|
||||
YT-C: usdyAmounts = 15M, 目标 = 20M (-5M, 占比过低)
|
||||
WUSD: usdyAmounts = 5M, 目标 = 10M (-5M, 占比过低)
|
||||
|
||||
用户A想存入1M USDY价值的YT-A:
|
||||
┌─────────────────────────────────────┐
|
||||
│ 初始偏离: +10M │
|
||||
│ 操作后偏离: +11M (恶化平衡) │
|
||||
│ 基础手续费: 0.3% (30 bps) │
|
||||
│ 税费计算: 50 * 10.5M / 40M ≈ 13 bps │
|
||||
│ 最终手续费: 30 + 13 = 43 bps (0.43%) │
|
||||
└─────────────────────────────────────┘
|
||||
结果:手续费提高 43% → 😱 惩罚
|
||||
|
||||
用户B想存入1M USDY价值的YT-C:
|
||||
┌─────────────────────────────────────┐
|
||||
│ 初始偏离: -5M │
|
||||
│ 操作后偏离: -4M (改善平衡) │
|
||||
│ 基础手续费: 0.3% (30 bps) │
|
||||
│ 折扣计算: 50 * 5M / 20M = 12.5 bps │
|
||||
│ 最终手续费: 30 - 12.5 = 17.5 bps │
|
||||
└─────────────────────────────────────┘
|
||||
结果:手续费降低 42% → 🎉 激励
|
||||
|
||||
用户C想从YT-A换到YT-C:
|
||||
┌─────────────────────────────────────┐
|
||||
│ YT-A减少 → 改善平衡 ✅ │
|
||||
│ YT-C增加 → 改善平衡 ✅ │
|
||||
│ 最终手续费: 可能接近0% 🎁 │
|
||||
└─────────────────────────────────────┘
|
||||
结果:双重鼓励!
|
||||
场景2:WUSD作为稳定资产
|
||||
Plain Text
|
||||
WUSD (0x7Cd017ca5ddb86861FA983a34b5F495C6F898c41)
|
||||
|
||||
特点:
|
||||
✅ 稳定币,价格 ≈ $1
|
||||
✅ 可以直接存入池子
|
||||
✅ 目标占比: 50%
|
||||
✅ 作为池子的"稳定锚"
|
||||
|
||||
动态手续费:
|
||||
当WUSD占比过低时 → 鼓励存入WUSD
|
||||
当WUSD占比过高时 → 鼓励取出WUSD
|
||||
|
||||
用户存入WUSD场景:
|
||||
当前: WUSD占5M,目标10M
|
||||
存入5M WUSD:
|
||||
- 改善平衡 → 手续费可能只有0.05% 🎉
|
||||
4.2 手续费范围
|
||||
Plain Text
|
||||
YT代币互换:
|
||||
- 最低: 0% (大幅改善平衡)
|
||||
- 正常: 0.3% (平衡状态)
|
||||
- 最高: 0.8% (严重失衡)
|
||||
|
||||
涉及WUSD的操作:
|
||||
- 最低: 0% (大幅改善平衡)
|
||||
- 正常: 0.04% (平衡状态)
|
||||
- 最高: 0.24% (严重失衡)
|
||||
4.3 USDY债务追踪机制
|
||||
Solidity
|
||||
// usdyAmounts: 追踪每个代币的USDY债务
|
||||
mapping(address => uint256) public usdyAmounts;
|
||||
|
||||
// 存入YT-A时:
|
||||
buyUSDY(YT-A) {
|
||||
// 1. 收YT-A
|
||||
// 2. 增加池子中YT-A数量
|
||||
// 3. 增加usdyAmounts[YT-A] ← 关键!
|
||||
// 4. 铸造USDY给用户
|
||||
}
|
||||
|
||||
// 取出YT-B时:
|
||||
sellUSDY(YT-B) {
|
||||
// 1. 收用户的USDY并销毁
|
||||
// 2. 减少usdyAmounts[YT-B] ← 关键!
|
||||
// 3. 减少池子中YT-B数量
|
||||
// 4. 转YT-B给用户
|
||||
}
|
||||
|
||||
// Swap: YT-A → YT-C时:
|
||||
swap(YT-A, YT-C) {
|
||||
// 1. 收YT-A,增加usdyAmounts[YT-A]
|
||||
// 2. 转YT-C,减少usdyAmounts[YT-C]
|
||||
// 3. 根据两边的偏离度计算手续费
|
||||
}
|
||||
4.4 USDY主要作用
|
||||
✅ 统一计价:所有YT转换为USDY价值
|
||||
✅ 价值传递:PoolManager和Vault通过USDY交互
|
||||
✅ 动态手续费:usdyAmounts追踪债务,自动平衡
|
||||
✅ 按需铸造:资金高效利用,会计清晰
|
||||
4.4.1 举例说明
|
||||
Plain Text
|
||||
场景:你去商店用积分换商品
|
||||
├─ 你:有1000积分(ytLP)
|
||||
├─ 积分中心(PoolManager):销毁你的积分
|
||||
├─ 商店(Vault):有商品(YT代币)
|
||||
└─ 问题:商店凭什么给你商品?
|
||||
|
||||
解决方案:
|
||||
1. 积分中心销毁你的积分 ✅
|
||||
2. 积分中心给商店"提货券"(USDY) ✅ ← 关键!
|
||||
3. 商店验证提货券 ✅
|
||||
4. 商店给你商品 ✅
|
||||
5. 商店销毁提货券 ✅
|
||||
|
||||
如果没有第2步(转USDY):
|
||||
├─ 商店不知道你有权拿商品
|
||||
├─ 商店无法验证价值
|
||||
└─ ❌ 系统无法运转
|
||||
|
||||
681
document/ytLp流动性池系统操作流程文档.md
Normal file
681
document/ytLp流动性池系统操作流程文档.md
Normal file
@@ -0,0 +1,681 @@
|
||||
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代币 代表用户份额
|
||||
|
||||
231
document/ytLp用户前端交互文档.md
Normal file
231
document/ytLp用户前端交互文档.md
Normal file
@@ -0,0 +1,231 @@
|
||||
ytLp用户前端交互文档
|
||||
1.用户添加流动性
|
||||
Solidity
|
||||
function addLiquidity(
|
||||
address _token, //YT代币或WUSD地址
|
||||
uint256 _amount, //代币数量
|
||||
uint256 _minUsdy, //最小USDY数量
|
||||
uint256 _minYtLP //最小ytLP数量
|
||||
)
|
||||
_minUsdy和_minYtLP计算方式(滑点可以让用户在界面选择)
|
||||
TypeScript
|
||||
/**
|
||||
* 计算添加流动性的 _minUsdy 和 _minYtLP 参数
|
||||
* @param {string} token - 代币地址
|
||||
* @param {BigNumber} amount - 代币数量
|
||||
* @param {number} slippageTolerance - 滑点容忍度 (0.005 = 0.5%)
|
||||
* @returns {Promise<{minUsdy, minYtLP, expectedYtLP, feeInfo}>}
|
||||
*/
|
||||
async function calculateAddLiquidityParams(token, amount, slippageTolerance = 0.005) {
|
||||
const PRICE_PRECISION = ethers.BigNumber.from("1000000000000000000000000000000");
|
||||
const BASIS_POINTS = 10000;
|
||||
|
||||
try {
|
||||
// 1. 获取价格(使用MinPrice)
|
||||
const tokenPrice = await ytVault.getMinPrice(token);
|
||||
|
||||
// 2. 计算理论USDY(扣费前)
|
||||
const theoreticalUsdy = amount.mul(tokenPrice).div(PRICE_PRECISION);
|
||||
|
||||
// 3. 获取手续费率
|
||||
const feeBasisPoints = await ytVault.getSwapFeeBasisPoints(
|
||||
token,
|
||||
USDY_ADDRESS,
|
||||
theoreticalUsdy
|
||||
);
|
||||
|
||||
// 4. 计算扣费后的代币和USDY
|
||||
const amountAfterFees = amount
|
||||
.mul(BASIS_POINTS - feeBasisPoints)
|
||||
.div(BASIS_POINTS);
|
||||
|
||||
const usdyAmount = amountAfterFees.mul(tokenPrice).div(PRICE_PRECISION);
|
||||
|
||||
// 5. 获取AUM和供应量
|
||||
const [aum, ytLPSupply] = await Promise.all([
|
||||
ytPoolManager.getAumInUsdy(true), // 使用MaxPrice
|
||||
ytLP.totalSupply()
|
||||
]);
|
||||
|
||||
// 6. 计算预期ytLP
|
||||
let expectedYtLP;
|
||||
if (ytLPSupply.eq(0)) {
|
||||
expectedYtLP = usdyAmount;
|
||||
} else {
|
||||
expectedYtLP = usdyAmount.mul(ytLPSupply).div(aum);
|
||||
}
|
||||
|
||||
// 7. 应用滑点
|
||||
const minUsdy = usdyAmount.mul(
|
||||
ethers.BigNumber.from(Math.floor((1 - slippageTolerance) * 10000))
|
||||
).div(10000);
|
||||
|
||||
const minYtLP = expectedYtLP.mul(
|
||||
ethers.BigNumber.from(Math.floor((1 - slippageTolerance) * 10000))
|
||||
).div(10000);
|
||||
|
||||
return {
|
||||
minUsdy,
|
||||
minYtLP,
|
||||
expectedYtLP,
|
||||
usdyAmount,
|
||||
feeInfo: {
|
||||
feeBasisPoints: feeBasisPoints.toNumber(),
|
||||
feeAmount: amount.sub(amountAfterFees),
|
||||
amountAfterFees
|
||||
},
|
||||
priceInfo: {
|
||||
tokenPrice: ethers.utils.formatUnits(tokenPrice, 30),
|
||||
aum: ethers.utils.formatEther(aum),
|
||||
ytLPSupply: ethers.utils.formatEther(ytLPSupply)
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("计算添加流动性参数失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
2.用户移除流动性
|
||||
Solidity
|
||||
function removeLiquidity(
|
||||
address _tokenOut, //出代币地址
|
||||
uint256 _ytLPAmount, //ytLP数量
|
||||
uint256 _minOut, //最小输出数量
|
||||
address _receiver //接收地址
|
||||
)
|
||||
_minOut计算方式(滑点可以让用户在界面选择)
|
||||
TypeScript
|
||||
/**
|
||||
* 计算移除流动性的 _minOut 参数
|
||||
* @param {string} tokenOut - 目标代币地址
|
||||
* @param {BigNumber} ytLPAmount - ytLP数量
|
||||
* @param {number} slippageTolerance - 滑点容忍度 (0.01 = 1%)
|
||||
* @returns {Promise<{minOut, expectedOut, feeBps, priceInfo}>}
|
||||
*/
|
||||
async function calculateMinOut(tokenOut, ytLPAmount, slippageTolerance = 0.01) {
|
||||
const PRICE_PRECISION = ethers.BigNumber.from("1000000000000000000000000000000");
|
||||
const BASIS_POINTS = 10000;
|
||||
|
||||
try {
|
||||
// 1. 获取AUM和供应量
|
||||
const [aum, ytLPSupply] = await Promise.all([
|
||||
ytPoolManager.getAumInUsdy(false),
|
||||
ytLP.totalSupply()
|
||||
]);
|
||||
|
||||
// 2. 计算USDY价值
|
||||
const usdyAmount = ytLPAmount.mul(aum).div(ytLPSupply);
|
||||
|
||||
// 3. 获取代币价格和手续费(并行查询)
|
||||
const [tokenPrice, feeBasisPoints] = await Promise.all([
|
||||
ytVault.getMaxPrice(tokenOut),
|
||||
ytVault.getRedemptionFeeBasisPoints(tokenOut, usdyAmount)
|
||||
]);
|
||||
|
||||
// 4. 计算理论输出
|
||||
const theoreticalOut = usdyAmount.mul(PRICE_PRECISION).div(tokenPrice);
|
||||
|
||||
// 5. 扣除手续费
|
||||
const expectedOut = theoreticalOut
|
||||
.mul(BASIS_POINTS - feeBasisPoints)
|
||||
.div(BASIS_POINTS);
|
||||
|
||||
// 6. 应用滑点
|
||||
const minOut = expectedOut.mul(
|
||||
ethers.BigNumber.from(Math.floor((1 - slippageTolerance) * 10000))
|
||||
).div(10000);
|
||||
|
||||
return {
|
||||
minOut,
|
||||
expectedOut,
|
||||
theoreticalOut,
|
||||
usdyAmount,
|
||||
feeBasisPoints: feeBasisPoints.toNumber(),
|
||||
priceInfo: {
|
||||
tokenPrice: ethers.utils.formatUnits(tokenPrice, 30),
|
||||
aum: ethers.utils.formatEther(aum),
|
||||
ytLPSupply: ethers.utils.formatEther(ytLPSupply)
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("计算_minOut失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
3.用户交换代币
|
||||
TypeScript
|
||||
function swapYT(
|
||||
address _tokenIn, //输入代币地址
|
||||
address _tokenOut, //输出代币地址
|
||||
uint256 _amountIn, //输入数量
|
||||
uint256 _minOut, //最小输出数量
|
||||
address _receiver //接收地址
|
||||
)
|
||||
_minOut计算方式(滑点可以让用户在界面选择)
|
||||
TypeScript
|
||||
/**
|
||||
* 计算 swapYT 的 _minOut 参数
|
||||
* @param {string} tokenIn - 输入代币地址
|
||||
* @param {string} tokenOut - 输出代币地址
|
||||
* @param {BigNumber} amountIn - 输入数量
|
||||
* @param {number} slippageTolerance - 滑点容忍度 (0.005 = 0.5%)
|
||||
* @returns {Promise<{minOut, expectedOut, feeInfo, priceInfo}>}
|
||||
*/
|
||||
async function calculateSwapMinOut(tokenIn, tokenOut, amountIn, slippageTolerance = 0.005) {
|
||||
const PRICE_PRECISION = ethers.BigNumber.from("1000000000000000000000000000000");
|
||||
const BASIS_POINTS = 10000;
|
||||
|
||||
try {
|
||||
// 1. 获取价格(并行查询)
|
||||
const [priceIn, priceOut] = await Promise.all([
|
||||
ytVault.getMinPrice(tokenIn), // 输入用MinPrice
|
||||
ytVault.getMaxPrice(tokenOut) // 输出用MaxPrice
|
||||
]);
|
||||
|
||||
// 2. 计算USDY价值
|
||||
const usdyAmount = amountIn.mul(priceIn).div(PRICE_PRECISION);
|
||||
|
||||
// 3. 获取手续费率
|
||||
const feeBasisPoints = await ytVault.getSwapFeeBasisPoints(
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
usdyAmount
|
||||
);
|
||||
|
||||
// 4. 计算理论输出
|
||||
const theoreticalOut = usdyAmount.mul(PRICE_PRECISION).div(priceOut);
|
||||
|
||||
// 5. 扣除手续费
|
||||
const expectedOut = theoreticalOut
|
||||
.mul(BASIS_POINTS - feeBasisPoints)
|
||||
.div(BASIS_POINTS);
|
||||
|
||||
// 6. 应用滑点
|
||||
const minOut = expectedOut.mul(
|
||||
ethers.BigNumber.from(Math.floor((1 - slippageTolerance) * 10000))
|
||||
).div(10000);
|
||||
|
||||
return {
|
||||
minOut,
|
||||
expectedOut,
|
||||
theoreticalOut,
|
||||
usdyAmount,
|
||||
feeBasisPoints: feeBasisPoints.toNumber(),
|
||||
priceInfo: {
|
||||
priceIn: ethers.utils.formatUnits(priceIn, 30),
|
||||
priceOut: ethers.utils.formatUnits(priceOut, 30),
|
||||
effectiveRate: theoreticalOut.mul(10000).div(amountIn).toNumber() / 100
|
||||
},
|
||||
feeInfo: {
|
||||
feeBps: feeBasisPoints.toNumber(),
|
||||
feeAmount: theoreticalOut.sub(expectedOut)
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("计算 _minOut 失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
7000
document/合约文档.md
Normal file
7000
document/合约文档.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user