diff --git a/document/Vault金库系统操作流程文档.md b/document/Vault金库系统操作流程文档.md new file mode 100644 index 0000000..969cc65 --- /dev/null +++ b/document/Vault金库系统操作流程文档.md @@ -0,0 +1,1184 @@ +Vault金库系统操作流程文档 +目录 +1. [创建Vault流程](#1-创建vault流程) +2. [用户存款流程(depositYT)](#2-用户存款流程deposityt) +3. [用户提款流程(withdrawYT)](#3-用户提款流程withdrawyt) +4. [价格更新流程](#4-价格更新流程) +5. [资产管理流程 - 提取投资](#5-资产管理流程---提取投资) +6. [资产管理流程 - 归还资产](#6-资产管理流程---归还资产) +7. [批量操作流程](#7-批量操作流程) +8. [查询信息流程](#8-查询信息流程) + +1. 创建Vault流程 +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ Owner (系统管理员) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 1. 调用 createVault() + │ name: "YT-A Token" + │ symbol: "YT-A" + │ manager: 0x123... + │ hardCap: 1,000,000 YT + │ wusd: 0x7Cd...(或0使用默认) + │ redemptionTime: 2025-02-15 00:00:00 + │ initialWusdPrice: 1.05e30 + │ initialYtPrice: 1.05e30 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetFactory.sol │ +│ ───────────────────────────────────────────────────────────────── │ +│ function createVault(...) │ +│ │ +│ 权限检查: │ +│ ✓ onlyOwner - 只有Factory owner可以创建 │ +│ ✓ manager != address(0) - 管理员地址有效 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 2. 确定硬顶值 + │ _hardCap == 0 ? defaultHardCap : _hardCap + │ → 使用传入的 1,000,000 YT + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 编码初始化数据 │ +│ ───────────────────────────────────────────────────────────────── │ +│ bytes memory initData = abi.encodeWithSelector( │ +│ YTAssetVault.initialize.selector, │ +│ "YT-A Token", │ +│ "YT-A", │ +│ 0x123..., // manager │ +│ 1000000e18, // hardCap │ +│ 0x7Cd..., // wusd │ +│ 1739577600, // redemptionTime (Unix时间戳) │ +│ 1050000000000000000000000000000, // 1.05e30 │ +│ 1050000000000000000000000000000 // 1.05e30 │ +│ ) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 3. 部署ERC1967代理合约 + │ new ERC1967Proxy(vaultImplementation, initData) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 部署并初始化YTAssetVault │ +│ ───────────────────────────────────────────────────────────────── │ +│ 新合约地址: 0xVault001... │ +│ │ +│ ① __ERC20_init("YT-A Token", "YT-A") │ +│ • 初始化ERC20代币 │ +│ • name: "YT-A Token" │ +│ • symbol: "YT-A" │ +│ • decimals: 18 │ +│ │ +│ ② __UUPSUpgradeable_init() │ +│ • 初始化UUPS升级模式 │ +│ │ +│ ③ __ReentrancyGuard_init() │ +│ • 初始化重入保护 │ +│ │ +│ ④ 设置基本参数 │ +│ factory = msg.sender (YTAssetFactory地址) │ +│ manager = 0x123... │ +│ hardCap = 1,000,000 * 1e18 │ +│ wusdAddress = 0x7Cd... │ +│ │ +│ ⑤ 设置价格(精度1e30) │ +│ wusdPrice = 1.05e30 (初始价格1.05) │ +│ ytPrice = 1.05e30 (初始价格1.05) │ +│ │ +│ ⑥ 设置赎回时间 │ +│ nextRedemptionTime = 1739577600 (2025-02-15) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 4. 记录到Factory + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ Factory状态更新 │ +│ ───────────────────────────────────────────────────────────────── │ +│ allVaults.push(0xVault001...) │ +│ isVault[0xVault001...] = true │ +│ │ +│ 触发事件: │ +│ emit VaultCreated( │ +│ 0xVault001..., │ +│ 0x123..., // manager │ +│ "YT-A Token", │ +│ "YT-A", │ +│ 1000000e18, // hardCap │ +│ 0 // index │ +│ ) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 5. 返回vault地址 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 创建完成 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 返回值: 0xVault001... │ +│ │ +│ Vault状态: │ +│ • totalSupply: 0 │ +│ • totalAssets: 0 WUSD │ +│ • wusdPrice: 1.05 (精度1e30) │ +│ • ytPrice: 1.05 (精度1e30) │ +│ • hardCap: 1,000,000 YT │ +│ • nextRedemptionTime: 2025-02-15 00:00:00 │ +│ • 可接受用户存款 ✓ │ +└─────────────────────────────────────────────────────────────────────┘ + +2. 用户存款流程(depositYT) +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ 用户 (User) │ +│ 持有: 10,000 WUSD │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 0. 预览操作(可选) + │ 调用 previewBuy(10000e18) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetVault.sol │ +│ ───────────────────────────────────────────────────────────────── │ +│ function previewBuy(10000e18) returns (uint256 ytAmount) │ +│ │ +│ 计算逻辑: │ +│ ytAmount = (wusdAmount × wusdPrice) ÷ ytPrice │ +│ = (10,000 × 1.05e30) ÷ 1.05e30 │ +│ = 10,000 YT │ +│ │ +│ 返回预览结果: 10,000 YT │ +└─────────────────────────────────────────────────────────────────────┘ + │ + │ 1. 授权WUSD给Vault + │ WUSD.approve(vault, 10000e18) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ WUSD授权检查 │ +│ ✓ allowance[user][vault] >= 10,000 WUSD │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 2. 调用 depositYT(10000e18) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetVault.depositYT() │ +│ ───────────────────────────────────────────────────────────────── │ +│ function depositYT(uint256 _wusdAmount) │ +│ • 非重入保护: nonReentrant │ +│ • 参数: 10,000 WUSD │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 3. 参数验证 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 参数检查 │ +│ ───────────────────────────────────────────────────────────────── │ +│ ① _wusdAmount > 0 │ +│ ✓ 10,000 > 0 通过 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 4. 计算可获得的YT数量 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 计算YT数量 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 公式:ytAmount = (wusdAmount × wusdPrice) ÷ ytPrice │ +│ │ +│ 当前价格: │ +│ • wusdPrice = 1.05e30 │ +│ • ytPrice = 1.05e30 │ +│ │ +│ 计算过程: │ +│ ytAmount = (10,000e18 × 1.05e30) ÷ 1.05e30 │ +│ = 10,000e18 │ +│ = 10,000 YT │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 5. 检查硬顶限制 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 硬顶检查 │ +│ ───────────────────────────────────────────────────────────────── │ +│ if (hardCap > 0 && totalSupply() + ytAmount > hardCap) │ +│ revert HardCapExceeded() │ +│ │ +│ 当前状态: │ +│ • hardCap = 1,000,000 YT │ +│ • totalSupply() = 0 YT (首次存款) │ +│ • ytAmount = 10,000 YT │ +│ • 0 + 10,000 = 10,000 ≤ 1,000,000 ✓ 通过 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 6. 转入WUSD(CEI模式 - Checks完成) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 代币转移(Effects) │ +│ ───────────────────────────────────────────────────────────────── │ +│ IERC20(wusdAddress).safeTransferFrom( │ +│ msg.sender, // 用户地址 │ +│ address(this), // Vault地址 │ +│ 10000e18 // 转入10,000 WUSD │ +│ ) │ +│ │ +│ 结果: │ +│ • 用户WUSD余额: 10,000 → 0 │ +│ • Vault WUSD余额: 0 → 10,000 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 7. 铸造YT代币给用户 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 铸造YT代币 │ +│ ───────────────────────────────────────────────────────────────── │ +│ _mint(msg.sender, 10000e18) │ +│ │ +│ ERC20铸造: │ +│ • balanceOf[user] += 10,000 YT │ +│ • totalSupply += 10,000 YT │ +│ │ +│ 结果: │ +│ • 用户YT余额: 0 → 10,000 YT │ +│ • 总供应量: 0 → 10,000 YT │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 8. 触发事件 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 事件记录(Interactions) │ +│ ───────────────────────────────────────────────────────────────── │ +│ emit Buy( │ +│ msg.sender, // 用户地址 │ +│ 10000e18, // WUSD数量 │ +│ 10000e18 // YT数量 │ +│ ) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 9. 返回YT数量 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 存款完成 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 用户最终状态: │ +│ • WUSD余额: 0 │ +│ • YT余额: 10,000 YT │ +│ │ +│ Vault最终状态: │ +│ • totalSupply: 10,000 YT │ +│ • totalAssets: 10,000 WUSD │ +│ • idleAssets: 10,000 WUSD │ +│ • managedAssets: 0 WUSD │ +│ │ +│ 返回值: 10,000 YT │ +└─────────────────────────────────────────────────────────────────────┘ + +3. 用户提款流程(withdrawYT) +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ 用户 (User) │ +│ 持有: 5,000 YT │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 0. 预览操作(可选) + │ 调用 previewSell(5000e18) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetVault.sol │ +│ ───────────────────────────────────────────────────────────────── │ +│ function previewSell(5000e18) returns (uint256 wusdAmount) │ +│ │ +│ 计算逻辑: │ +│ wusdAmount = (ytAmount × ytPrice) ÷ wusdPrice │ +│ = (5,000 × 1.05e30) ÷ 1.05e30 │ +│ = 5,000 WUSD │ +│ │ +│ 返回预览结果: 5,000 WUSD │ +└─────────────────────────────────────────────────────────────────────┘ + │ + │ 1. 检查赎回时间(可选) + │ 调用 canRedeemNow() + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 赎回时间检查 │ +│ ───────────────────────────────────────────────────────────────── │ +│ function canRedeemNow() returns (bool) │ +│ return block.timestamp >= nextRedemptionTime │ +│ │ +│ 检查: │ +│ • 当前时间: 2025-02-16 10:00:00 (1739692800) │ +│ • 赎回时间: 2025-02-15 00:00:00 (1739577600) │ +│ • 1739692800 >= 1739577600 ✓ 可以赎回 │ +│ │ +│ getTimeUntilNextRedemption() = 0 秒 │ +└─────────────────────────────────────────────────────────────────────┘ + │ + │ 2. 调用 withdrawYT(5000e18) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetVault.withdrawYT() │ +│ ───────────────────────────────────────────────────────────────── │ +│ function withdrawYT(uint256 _ytAmount) │ +│ • 非重入保护: nonReentrant │ +│ • 参数: 5,000 YT │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 3. 多重验证 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 参数检查 │ +│ ───────────────────────────────────────────────────────────────── │ +│ ① _ytAmount > 0 │ +│ ✓ 5,000 > 0 通过 │ +│ │ +│ ② balanceOf(msg.sender) >= _ytAmount │ +│ ✓ 10,000 >= 5,000 通过 │ +│ (用户持有足够的YT) │ +│ │ +│ ③ block.timestamp >= nextRedemptionTime │ +│ ✓ 2025-02-16 >= 2025-02-15 通过 │ +│ (已到赎回时间) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 4. 计算可获得的WUSD数量 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 计算WUSD数量 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 公式:wusdAmount = (ytAmount × ytPrice) ÷ wusdPrice │ +│ │ +│ 假设价格已更新: │ +│ • wusdPrice = 1.00e30 (WUSD价格回落到1.0) │ +│ • ytPrice = 1.10e30 (YT价格上涨到1.1) │ +│ │ +│ 计算过程: │ +│ wusdAmount = (5,000e18 × 1.10e30) ÷ 1.00e30 │ +│ = (5,000e18 × 1.10) ÷ 1.00 │ +│ = 5,500e18 │ +│ = 5,500 WUSD │ +│ │ +│ 用户获得收益: 5,500 - 5,000 = 500 WUSD (10%增值) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 5. 检查Vault流动性 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 流动性检查 │ +│ ───────────────────────────────────────────────────────────────── │ +│ uint256 availableWUSD = IERC20(wusdAddress).balanceOf(vault) │ +│ if (wusdAmount > availableWUSD) revert InsufficientWUSD() │ +│ │ +│ 当前状态: │ +│ • Vault中WUSD余额: 10,000 WUSD │ +│ • 需要支付: 5,500 WUSD │ +│ • 5,500 ≤ 10,000 ✓ 流动性充足 │ +│ │ +│ 注意: │ +│ 如果manager已提取部分资金进行投资,availableWUSD可能不足 │ +│ 此时用户需要等待manager归还资金 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 6. 销毁用户的YT(CEI - Effects) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 销毁YT代币 │ +│ ───────────────────────────────────────────────────────────────── │ +│ _burn(msg.sender, 5000e18) │ +│ │ +│ ERC20销毁: │ +│ • balanceOf[user] -= 5,000 YT │ +│ • totalSupply -= 5,000 YT │ +│ │ +│ 结果: │ +│ • 用户YT余额: 10,000 → 5,000 YT │ +│ • 总供应量: 10,000 → 5,000 YT │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 7. 转出WUSD给用户 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 代币转移(Interactions) │ +│ ───────────────────────────────────────────────────────────────── │ +│ IERC20(wusdAddress).safeTransfer( │ +│ msg.sender, // 用户地址 │ +│ 5500e18 // 转出5,500 WUSD │ +│ ) │ +│ │ +│ 结果: │ +│ • Vault WUSD余额: 10,000 → 4,500 │ +│ • 用户WUSD余额: 0 → 5,500 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 8. 触发事件 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 事件记录 │ +│ ───────────────────────────────────────────────────────────────── │ +│ emit Sell( │ +│ msg.sender, // 用户地址 │ +│ 5000e18, // YT数量 │ +│ 5500e18 // WUSD数量 │ +│ ) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 9. 返回WUSD数量 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 提款完成 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 用户最终状态: │ +│ • YT余额: 5,000 YT (剩余) │ +│ • WUSD余额: 5,500 WUSD (获得) │ +│ • 收益: 500 WUSD (10%增值) │ +│ │ +│ Vault最终状态: │ +│ • totalSupply: 5,000 YT │ +│ • totalAssets: 4,500 WUSD (假设无managedAssets) │ +│ • idleAssets: 4,500 WUSD │ +│ │ +│ 返回值: 5,500 WUSD │ +└─────────────────────────────────────────────────────────────────────┘ + +4. 价格更新流程 +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ Oracle / Manager │ +│ 接收到最新价格数据: │ +│ • WUSD价格: $1.02 │ +│ • YT-A价格: $1.15 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 方式1: Manager直接更新 + │ 调用 vault.updatePrices() + │ + │ 方式2: Factory批量更新 + │ 调用 factory.updateVaultPrices() + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetVault.updatePrices() │ +│ ───────────────────────────────────────────────────────────────── │ +│ function updatePrices( │ +│ uint256 _wusdPrice, // 1.02e30 │ +│ uint256 _ytPrice // 1.15e30 │ +│ ) external onlyManager │ +│ │ +│ 权限检查: │ +│ • msg.sender == manager ✓ 或 │ +│ • msg.sender == factory ✓ │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 1. 价格验证 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 价格有效性检查 │ +│ ───────────────────────────────────────────────────────────────── │ +│ if (_wusdPrice == 0 || _ytPrice == 0) │ +│ revert InvalidPrice() │ +│ │ +│ 检查: │ +│ • _wusdPrice = 1.02e30 ≠ 0 ✓ │ +│ • _ytPrice = 1.15e30 ≠ 0 ✓ │ +│ │ +│ 注意: │ +│ • 价格必须 > 0 │ +│ • 价格精度为1e30 │ +│ • 没有时间间隔限制,可随时更新 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 2. 更新价格状态 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 更新存储状态 │ +│ ───────────────────────────────────────────────────────────────── │ +│ wusdPrice = _wusdPrice │ +│ ytPrice = _ytPrice │ +│ │ +│ 更新前: │ +│ • wusdPrice: 1.05e30 → 1.02e30 (下降2.86%) │ +│ • ytPrice: 1.05e30 → 1.15e30 (上涨9.52%) │ +│ │ +│ 影响: │ +│ ① 后续depositYT计算变化 │ +│ ytAmount = wusdAmount × 1.02 / 1.15 │ +│ → 用户用相同WUSD获得更少YT │ +│ │ +│ ② 后续withdrawYT计算变化 │ +│ wusdAmount = ytAmount × 1.15 / 1.02 │ +│ → 用户用相同YT获得更多WUSD │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 3. 触发事件 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 事件记录 │ +│ ───────────────────────────────────────────────────────────────── │ +│ emit PriceUpdated( │ +│ 1020000000000000000000000000000, // wusdPrice │ +│ 1150000000000000000000000000000, // ytPrice │ +│ 1739692800 // timestamp │ +│ ) │ +│ │ +│ 链下监听: │ +│ • 前端可监听此事件更新UI显示 │ +│ • 用户可看到最新的兑换比率 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 4. 完成更新 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 更新完成 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 当前兑换比率示例: │ +│ │ +│ 存款 (depositYT): │ +│ • 1,000 WUSD → (1,000 × 1.02) / 1.15 = 886.96 YT │ +│ │ +│ 提款 (withdrawYT): │ +│ • 1,000 YT → (1,000 × 1.15) / 1.02 = 1,127.45 WUSD │ +│ │ +│ YT持有者收益: │ +│ • 价格从1.05到1.15,增值9.52% │ +│ • 持有1,000 YT相当于价值1,127.45 WUSD │ +└─────────────────────────────────────────────────────────────────────┘ + +5. 资产管理流程 - 提取投资 +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ Manager (资产管理员) │ +│ 计划: 提取50,000 WUSD进行外部投资 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 1. 调用 withdrawForManagement() + │ _to: manager地址 + │ _amount: 50,000 WUSD + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetVault.withdrawForManagement() │ +│ ───────────────────────────────────────────────────────────────── │ +│ function withdrawForManagement( │ +│ address _to, // 0x123... (manager) │ +│ uint256 _amount // 50,000 WUSD │ +│ ) external onlyManager nonReentrant │ +│ │ +│ 权限检查: │ +│ ✓ onlyManager - 只有manager可调用 │ +│ ✓ nonReentrant - 重入保护 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 2. 参数验证 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 验证检查 │ +│ ───────────────────────────────────────────────────────────────── │ +│ ① if (_amount == 0) revert InvalidAmount() │ +│ ✓ 50,000 > 0 通过 │ +│ │ +│ ② uint256 availableAssets = vault.balance(WUSD) │ +│ if (_amount > availableAssets) revert InvalidAmount() │ +│ │ +│ Vault当前状态: │ +│ • totalAssets: 100,000 WUSD │ +│ • idleAssets: 100,000 WUSD (全部在vault中) │ +│ • managedAssets: 0 WUSD │ +│ │ +│ 检查: │ +│ • availableAssets = 100,000 WUSD │ +│ • 50,000 ≤ 100,000 ✓ 通过 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 3. 更新managedAssets(CEI - Effects) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 状态更新 │ +│ ───────────────────────────────────────────────────────────────── │ +│ managedAssets += _amount │ +│ │ +│ 更新: │ +│ • managedAssets: 0 → 50,000 WUSD │ +│ │ +│ 重要说明: │ +│ managedAssets记录了被管理员提取、正在进行外部投资的WUSD数量 │ +│ 这部分资产不在vault合约中,但仍计入totalAssets │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 4. 转出WUSD(Interactions) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 代币转移 │ +│ ───────────────────────────────────────────────────────────────── │ +│ IERC20(wusdAddress).safeTransfer( │ +│ _to, // manager地址 │ +│ 50000e18 // 50,000 WUSD │ +│ ) │ +│ │ +│ 转账结果: │ +│ • Vault WUSD余额: 100,000 → 50,000 WUSD │ +│ • Manager WUSD余额: +50,000 WUSD │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 5. 触发事件 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 事件记录 │ +│ ───────────────────────────────────────────────────────────────── │ +│ emit AssetsWithdrawn( │ +│ 0x123..., // manager地址 │ +│ 50000e18 // 提取数量 │ +│ ) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 6. 提取完成 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 提取完成 │ +│ ───────────────────────────────────────────────────────────────── │ +│ Vault最终状态: │ +│ • totalAssets(): 100,000 WUSD (不变!) │ +│ 计算: idleAssets + managedAssets │ +│ = 50,000 + 50,000 = 100,000 │ +│ │ +│ • idleAssets(): 50,000 WUSD │ +│ (vault合约实际持有的WUSD) │ +│ │ +│ • managedAssets: 50,000 WUSD │ +│ (manager正在管理的WUSD) │ +│ │ +│ • totalSupply: 100,000 YT (不变) │ +│ │ +│ 用户影响: │ +│ ✓ totalAssets不变,YT价值不受影响 │ +│ ✓ 用户依然持有相同价值的YT份额 │ +│ ✗ 暂时无法提款(流动性不足),需等待manager归还 │ +│ │ +│ Manager后续操作: │ +│ → 用50,000 WUSD进行DeFi投资 │ +│ → 赚取收益 │ +│ → 通过depositManagedAssets归还 │ +└─────────────────────────────────────────────────────────────────────┘ + +6. 资产管理流程 - 归还资产 +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ Manager (资产管理员) │ +│ 投资收益情况: │ +│ • 提取: 50,000 WUSD │ +│ • 投资收益: +5,000 WUSD │ +│ • 准备归还: 55,000 WUSD (本金+利润) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 1. 授权WUSD给Vault + │ WUSD.approve(vault, 55000e18) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ WUSD授权检查 │ +│ ✓ allowance[manager][vault] >= 55,000 WUSD │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 2. 调用 depositManagedAssets() + │ _amount: 55,000 WUSD + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetVault.depositManagedAssets() │ +│ ───────────────────────────────────────────────────────────────── │ +│ function depositManagedAssets( │ +│ uint256 _amount // 55,000 WUSD │ +│ ) external onlyManager nonReentrant │ +│ │ +│ 权限检查: │ +│ ✓ onlyManager - 只有manager可调用 │ +│ ✓ nonReentrant - 重入保护 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 3. 参数验证 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 验证检查 │ +│ ───────────────────────────────────────────────────────────────── │ +│ if (_amount == 0) revert InvalidAmount() │ +│ ✓ 55,000 > 0 通过 │ +│ │ +│ 当前Vault状态: │ +│ • idleAssets: 50,000 WUSD │ +│ • managedAssets: 50,000 WUSD │ +│ • totalAssets: 100,000 WUSD │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 4. 更新managedAssets(CEI - Effects) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 状态更新(关键逻辑) │ +│ ───────────────────────────────────────────────────────────────── │ +│ if (_amount >= managedAssets) { │ +│ // 归还金额 >= 提取金额,清零managedAssets │ +│ managedAssets = 0 │ +│ } else { │ +│ // 归还金额 < 提取金额,部分归还 │ +│ managedAssets -= _amount │ +│ } │ +│ │ +│ 本例计算: │ +│ • _amount = 55,000 WUSD │ +│ • managedAssets = 50,000 WUSD │ +│ • 55,000 >= 50,000 ✓ 进入第一个分支 │ +│ • managedAssets = 0 │ +│ │ +│ 多余的5,000 WUSD如何处理? │ +│ → 自动增加到vault余额,成为利润 │ +│ → totalAssets会增加 │ +│ → 所有YT持有者共享这部分收益 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 5. 转入WUSD(Interactions) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 代币转移 │ +│ ───────────────────────────────────────────────────────────────── │ +│ IERC20(wusdAddress).safeTransferFrom( │ +│ msg.sender, // manager地址 │ +│ address(this), // vault地址 │ +│ 55000e18 // 55,000 WUSD │ +│ ) │ +│ │ +│ 转账结果: │ +│ • Manager WUSD余额: -55,000 WUSD │ +│ • Vault WUSD余额: 50,000 → 105,000 WUSD │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 6. 触发事件 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 事件记录 │ +│ ───────────────────────────────────────────────────────────────── │ +│ emit AssetsDeposited( │ +│ 55000e18 // 归还数量 │ +│ ) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 7. 归还完成 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 归还完成 │ +│ ───────────────────────────────────────────────────────────────── │ +│ Vault最终状态: │ +│ • totalAssets(): 105,000 WUSD (增加了5,000!) │ +│ 计算: idleAssets + managedAssets │ +│ = 105,000 + 0 = 105,000 │ +│ │ +│ • idleAssets(): 105,000 WUSD │ +│ (vault合约实际持有的WUSD,包含收益) │ +│ │ +│ • managedAssets: 0 WUSD │ +│ (所有资产已归还) │ +│ │ +│ • totalSupply: 100,000 YT (不变) │ +│ │ +│ 收益分配: │ +│ • 投资收益: 5,000 WUSD │ +│ • 收益率: 5,000 / 50,000 = 10% │ +│ • 每个YT的价值提升: │ +│ 之前: 100,000 WUSD / 100,000 YT = 1.0 WUSD/YT │ +│ 现在: 105,000 WUSD / 100,000 YT = 1.05 WUSD/YT │ +│ │ +│ 用户影响: │ +│ ✓ 所有YT持有者自动获得5%增值 │ +│ ✓ 可以提款了(流动性恢复) │ +│ ✓ 如果用户提取YT,会按1.05的比例获得更多WUSD │ +│ │ +│ 示例: │ +│ 用户持有10,000 YT,提取时可获得: │ +│ (10,000 × 1.05 WUSD/YT) = 10,500 WUSD │ +│ 相比初始存入的10,000 WUSD,获利500 WUSD (5%) │ +└─────────────────────────────────────────────────────────────────────┘ + +7. 批量操作流程 +7.1 批量创建Vault +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ Owner (系统管理员) │ +│ 计划: 批量创建3个不同的YT Vault │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 调用 createVaultBatch() + │ • names: ["YT-A", "YT-B", "YT-C"] + │ • symbols: ["YTA", "YTB", "YTC"] + │ • managers: [0x111, 0x222, 0x333] + │ • hardCaps: [1000000e18, 500000e18, 2000000e18] + │ • wusd: 0x7Cd... + │ • redemptionTimes: [time1, time2, time3] + │ • initialWusdPrices: [1.05e30, 1.05e30, 1.05e30] + │ • initialYtPrices: [1.05e30, 1.02e30, 1.10e30] + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetFactory.createVaultBatch() │ +│ ───────────────────────────────────────────────────────────────── │ +│ 1. 参数长度验证 │ +│ require(所有数组长度相等) │ +│ ✓ 所有数组长度都是3 │ +│ │ +│ 2. 循环创建 │ +│ for (i = 0; i < 3; i++) { │ +│ vaults[i] = this.createVault(...) │ +│ } │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + ├─────────┬─────────┐ + │ │ │ + 第1个Vault │ 第2个Vault │ 第3个Vault + ▼ │ ▼ │ ▼ +┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ +│ YT-A Token │ │ YT-B Token │ │ YT-C Token │ +│ Symbol: YTA │ │ Symbol: YTB │ │ Symbol: YTC │ +│ Manager: 0x111 │ │ Manager: 0x222 │ │ Manager: 0x333 │ +│ HardCap: 1M YT │ │ HardCap: 500K YT │ │ HardCap: 2M YT │ +│ YT Price: 1.05 │ │ YT Price: 1.02 │ │ YT Price: 1.10 │ +│ 地址: 0xVault001 │ │ 地址: 0xVault002 │ │ 地址: 0xVault003 │ +└───────────────────┘ └───────────────────┘ └───────────────────┘ + │ │ │ + └─────────┴─────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 批量创建完成 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 返回值: [0xVault001, 0xVault002, 0xVault003] │ +│ │ +│ Factory状态: │ +│ • allVaults.length: 3 │ +│ • isVault[0xVault001] = true │ +│ • isVault[0xVault002] = true │ +│ • isVault[0xVault003] = true │ +│ │ +│ 优势: │ +│ ✓ 一次交易创建多个vault,节省gas │ +│ ✓ 原子操作,全部成功或全部失败 │ +│ ✓ 统一管理多个资产池 │ +└─────────────────────────────────────────────────────────────────────┘ +7.2 批量更新价格 +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ Oracle / Owner │ +│ 接收到3个vault的最新价格数据 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 调用 updateVaultPricesBatch() + │ • vaults: [0xVault001, 0xVault002, 0xVault003] + │ • wusdPrices: [1.02e30, 1.03e30, 1.01e30] + │ • ytPrices: [1.15e30, 1.08e30, 1.20e30] + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetFactory.updateVaultPricesBatch() │ +│ ───────────────────────────────────────────────────────────────── │ +│ 1. 参数验证 │ +│ require(数组长度相等) │ +│ ✓ 所有数组长度都是3 │ +│ │ +│ 2. 循环更新 │ +│ for (i = 0; i < 3; i++) { │ +│ YTAssetVault(vaults[i]).updatePrices( │ +│ wusdPrices[i], │ +│ ytPrices[i] │ +│ ) │ +│ emit PricesUpdated(vaults[i], wusdPrices[i], ytPrices[i]) │ +│ } │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + ├─────────┬─────────┐ + │ │ │ + Vault001 │ Vault002 │ Vault003 + 价格更新 │ 价格更新 │ 价格更新 + ▼ │ ▼ │ ▼ +┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ +│ WUSD: 1.02 │ │ WUSD: 1.03 │ │ WUSD: 1.01 │ +│ YT: 1.15 │ │ YT: 1.08 │ │ YT: 1.20 │ +│ 涨幅: +9.52% │ │ 涨幅: +2.88% │ │ 涨幅: +9.09% │ +└───────────────────┘ └───────────────────┘ └───────────────────┘ + │ │ │ + └─────────┴─────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 批量更新完成 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 优势: │ +│ ✓ 一次交易更新多个vault价格 │ +│ ✓ 节省gas费用 │ +│ ✓ 确保所有vault价格同时更新 │ +│ ✓ 适合定时任务批量更新 │ +│ │ +│ 触发的事件: │ +│ • PricesUpdated × 3 │ +│ • PriceUpdated × 3 (从各个vault) │ +└─────────────────────────────────────────────────────────────────────┘ +7.3 批量设置赎回时间 +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ Owner (系统管理员) │ +│ 统一设置赎回时间: 2025-03-15 00:00:00 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 调用 setVaultNextRedemptionTimeBatch() + │ • vaults: [0xVault001, 0xVault002, 0xVault003] + │ • nextRedemptionTime: 1741996800 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetFactory.setVaultNextRedemptionTimeBatch() │ +│ ───────────────────────────────────────────────────────────────── │ +│ for (i = 0; i < vaults.length; i++) { │ +│ YTAssetVault(vaults[i]).setNextRedemptionTime( │ +│ _nextRedemptionTime │ +│ ) │ +│ emit NextRedemptionTimeSet(vaults[i], _nextRedemptionTime) │ +│ } │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 批量设置完成 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 所有3个vault的赎回时间都设置为: │ +│ 2025-03-15 00:00:00 │ +│ │ +│ 用户影响: │ +│ ✓ 所有vault的用户在同一天可以赎回 │ +│ ✓ 统一管理赎回周期 │ +│ ✓ 类似基金的统一开放日 │ +└─────────────────────────────────────────────────────────────────────┘ + +8. 查询信息流程 +8.1 查询单个Vault信息 +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ 前端应用 / 查询者 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 调用 vault.getVaultInfo() + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetVault.getVaultInfo() │ +│ ───────────────────────────────────────────────────────────────── │ +│ function getVaultInfo() returns ( │ +│ uint256 _totalAssets, │ +│ uint256 _idleAssets, │ +│ uint256 _managedAssets, │ +│ uint256 _totalSupply, │ +│ uint256 _hardCap, │ +│ uint256 _wusdPrice, │ +│ uint256 _ytPrice, │ +│ uint256 _nextRedemptionTime │ +│ ) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 读取状态 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 返回完整信息 │ +│ ───────────────────────────────────────────────────────────────── │ +│ 返回示例: │ +│ { │ +│ totalAssets: 105000e18, // 105,000 WUSD │ +│ idleAssets: 105000e18, // 105,000 WUSD │ +│ managedAssets: 0, // 0 WUSD │ +│ totalSupply: 100000e18, // 100,000 YT │ +│ hardCap: 1000000e18, // 1,000,000 YT │ +│ wusdPrice: 1020000000000000000000000000000, // 1.02 │ +│ ytPrice: 1150000000000000000000000000000, // 1.15 │ +│ nextRedemptionTime: 1739577600 // 2025-02-15 00:00:00 │ +│ } │ +│ │ +│ 计算衍生指标: │ +│ • 每YT价值: 105,000 / 100,000 = 1.05 WUSD │ +│ • 资金利用率: (105,000 - 0) / 105,000 = 100% │ +│ • 硬顶使用率: 100,000 / 1,000,000 = 10% │ +│ • 当前兑换率: 1 WUSD = 1.02/1.15 = 0.887 YT │ +└─────────────────────────────────────────────────────────────────────┘ +8.2 通过Factory查询Vault信息 +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ 前端应用 / 查询者 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 调用 factory.getVaultInfo(vaultAddress) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ YTAssetFactory.getVaultInfo() │ +│ ───────────────────────────────────────────────────────────────── │ +│ function getVaultInfo(address _vault) returns ( │ +│ bool exists, │ +│ uint256 totalAssets, │ +│ uint256 idleAssets, │ +│ uint256 managedAssets, │ +│ uint256 totalSupply, │ +│ uint256 hardCap, │ +│ uint256 wusdPrice, │ +│ uint256 ytPrice, │ +│ uint256 nextRedemptionTime │ +│ ) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 1. 检查vault是否存在 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 存在性检查 │ +│ ───────────────────────────────────────────────────────────────── │ +│ exists = isVault[_vault] │ +│ if (!exists) return (false, 0, 0, 0, 0, 0, 0, 0, 0) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 2. 调用vault获取信息 + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 返回完整信息 │ +│ ───────────────────────────────────────────────────────────────── │ +│ { │ +│ exists: true, // ✓ vault存在 │ +│ totalAssets: 105000e18, │ +│ idleAssets: 105000e18, │ +│ managedAssets: 0, │ +│ totalSupply: 100000e18, │ +│ hardCap: 1000000e18, │ +│ wusdPrice: 1020000000000000000000000000000, │ +│ ytPrice: 1150000000000000000000000000000, │ +│ nextRedemptionTime: 1739577600 │ +│ } │ +│ │ +│ 优势: │ +│ ✓ 通过factory统一查询 │ +│ ✓ 可验证vault是否为合法vault │ +│ ✓ 一次调用获取所有信息 │ +└─────────────────────────────────────────────────────────────────────┘ +8.3 查询所有Vault列表 +Plain Text +┌─────────────────────────────────────────────────────────────────────┐ +│ 前端应用 / 查询者 │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 1. 获取总数量 + │ factory.getVaultCount() + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 获取Vault总数 │ +│ ───────────────────────────────────────────────────────────────── │ +│ function getVaultCount() returns (uint256) │ +│ return allVaults.length │ +│ │ +│ 返回: 3 (有3个vault) │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 2. 获取所有vault地址 + │ factory.getAllVaults() + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 获取所有Vault地址 │ +│ ───────────────────────────────────────────────────────────────── │ +│ function getAllVaults() returns (address[] memory) │ +│ return allVaults │ +│ │ +│ 返回: [0xVault001, 0xVault002, 0xVault003] │ +└────────────────────────────┬────────────────────────────────────────┘ + │ + │ 3. 或分页查询 + │ factory.getVaults(0, 2) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 分页查询Vault地址 │ +│ ───────────────────────────────────────────────────────────────── │ +│ function getVaults(uint256 _start, uint256 _end) │ +│ │ +│ 参数: │ +│ • _start: 0 (起始索引) │ +│ • _end: 2 (结束索引,不包含) │ +│ │ +│ 返回: [0xVault001, 0xVault002] │ +│ │ +│ 优势: │ +│ ✓ 适合vault数量很多时的分页加载 │ +│ ✓ 减少单次调用的gas消耗 │ +│ ✓ 改善前端加载性能 │ +└─────────────────────────────────────────────────────────────────────┘ + +附录:重要概念说明 +A. 价格精度 (PRICE_PRECISION) +Plain Text +精度: 1e30 (10^30) + +示例: +• 价格 1.0 表示为: 1000000000000000000000000000000 (1e30) +• 价格 1.05 表示为: 1050000000000000000000000000000 (1.05e30) +• 价格 0.98 表示为: 980000000000000000000000000000 (0.98e30) + +为什么使用1e30而不是1e18? +✓ 更高的精度,减少舍入误差 +✓ 适合复杂的价格计算 +✓ 支持更精确的价格波动 +B. 兑换计算公式 +Plain Text +存款 (depositYT): +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +ytAmount = (wusdAmount × wusdPrice) ÷ ytPrice + +示例: +• 存入1,000 WUSD +• wusdPrice = 1.05e30 +• ytPrice = 1.10e30 +• ytAmount = (1,000 × 1.05) ÷ 1.10 = 954.55 YT + + +提款 (withdrawYT): +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +wusdAmount = (ytAmount × ytPrice) ÷ wusdPrice + +示例: +• 提取1,000 YT +• ytPrice = 1.10e30 +• wusdPrice = 1.05e30 +• wusdAmount = (1,000 × 1.10) ÷ 1.05 = 1,047.62 WUSD +C. 资产状态计算 +Plain Text +Vault资产状态: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +totalAssets = idleAssets + managedAssets + +• idleAssets: vault合约中实际持有的WUSD +• managedAssets: manager提取、正在投资的WUSD +• totalAssets: 用户可赎回的总价值 + +示例: +vault余额: 50,000 WUSD +managedAssets: 50,000 WUSD +totalAssets: 100,000 WUSD (不变) + +用户持有10,000 YT,价值: +(10,000 / 100,000) × 100,000 = 10,000 WUSD +D. 硬顶机制 +Plain Text +硬顶 (Hard Cap): +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +限制YT代币的最大供应量 + +作用: +✓ 控制vault规模 +✓ 风险管理 +✓ 防止过度铸造 + +检查时机: +• 每次depositYT时检查 +• if (totalSupply + ytAmount > hardCap) revert + +动态调整: +• Factory owner可以通过setHardCap调整 +• 但不能低于当前totalSupply +E. 统一赎回时间 +Plain Text +赎回时间 (nextRedemptionTime): +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +所有用户统一的赎回开放时间 + +特点: +✓ 类似基金的开放日 +✓ 所有用户同时可赎回 +✓ 不是个人锁定期 + +检查: +• block.timestamp >= nextRedemptionTime +• 未到时间调用withdrawYT会revert + +管理: +• Factory owner可以设置 +• 支持批量设置多个vault + diff --git a/document/ytLp池子合约流程文档.md b/document/ytLp池子合约流程文档.md new file mode 100644 index 0000000..f9a8993 --- /dev/null +++ b/document/ytLp池子合约流程文档.md @@ -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
一站式入口] + end + + subgraph "核心业务层" + PoolManager[YTPoolManager
流动性管理] + Vault[YTVault
资金池 + 动态手续费] + USDY[USDY Token
统一计价单位] + end + + subgraph "资产层" + YTA[YT-A Token
40% 目标占比] + YTB[YT-B Token
30% 目标占比] + YTC[YT-C Token
20% 目标占比] + WUSD[WUSD Token
10% 目标占比
0x7Cd017...c41] + end + + subgraph "LP代币层" + YTLP[ytLP Token
流动性凭证] + end + + subgraph "奖励层" + FeeTracker[FeeYtLPTracker
手续费奖励追踪] + 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代币
10,000 YT-A] + end + + subgraph "Router处理" + B[收集用户代币] + end + + subgraph "PoolManager处理" + C[调用Vault.buyUSDY] + D[计算ytLP数量
ytLP = USDY * supply / AUM] + E[铸造ytLP] + end + + subgraph "Vault处理" + F[接收YT代币
poolAmounts增加] + G[计算手续费
0.3% = 30 YT-A] + H[更新USDY债务
usdyAmounts增加] + I[铸造USDY
9,970 USDY] + end + + subgraph "输出" + J[用户获得ytLP
价值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
5,000 ytLP] + end + + subgraph "PoolManager处理" + B[销毁ytLP] + C[计算USDY价值
USDY = ytLP * AUM / supply] + D[转USDY给Vault
5,000 USDY] + end + + subgraph "Vault处理" + E[收到USDY并销毁] + F[计算YT数量
根据价格] + G[计算手续费
动态费率] + H[减少poolAmounts
减少usdyAmounts] + I[转YT代币] + end + + subgraph "输出" + J[用户收到YT
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
1,000 YT-A] + end + + subgraph "Vault处理" + B[收到YT-A
poolAmounts增加] + C[查询价格
priceA, priceB] + D[计算USDY中间值
1,000 * priceA] + E[计算YT-B数量
USDY / priceB] + F[计算动态手续费
检查两端占比] + G[更新状态
YT-A增, YT-B减
USDY债务转移] + H[转出YT-B] + end + + subgraph "输出" + I[用户收到YT-B
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[用户操作
存入/取出/Swap] + Token[操作的代币
YT-A/YT-B/YT-C/WUSD] + Amount[操作数量] + end + + subgraph "状态查询层" + Current[当前USDY债务
usdyAmounts] + Target[目标USDY债务
getTargetUsdyAmount] + Weight[代币权重
tokenWeights] + Supply[USDY总供应
totalSupply] + end + + subgraph "计算层" + Calc1[计算操作后债务
nextAmount] + Calc2[计算偏离度
initialDiff vs nextDiff] + Calc3[判断改善/恶化] + end + + subgraph "手续费决策层" + Decision{改善平衡?} + Rebate[降低手续费
baseFee - rebate] + Penalty[提高手续费
baseFee + tax] + end + + subgraph "输出层" + Final[最终手续费
0% - 0.8%] + Effect[效果反馈
引导池子平衡] + 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查询价格
getPrice] + end + + subgraph "YTPriceFeed" + Feed[价格读取器] + Check{是WUSD?} + end + + subgraph "WUSD处理" + Fixed[返回固定价格
1.0 USDY] + end + + subgraph "YT代币处理" + YTContract[YT代币合约] + ReadVar[ 直接读取public变量
assetPrice] + AddSpread[添加价差
0.2%] + end + + subgraph "返回" + Price[返回USDY价格
30位小数] + end + + Query --> Feed + Feed --> Check + + Check -->|是| Fixed + Check -->|否| YTContract + + YTContract --> ReadVar + Note1[:只读一个变量
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): +├─ 商店不知道你有权拿商品 +├─ 商店无法验证价值 +└─ ❌ 系统无法运转 + diff --git a/document/ytLp流动性池系统操作流程文档.md b/document/ytLp流动性池系统操作流程文档.md new file mode 100644 index 0000000..f464dab --- /dev/null +++ b/document/ytLp流动性池系统操作流程文档.md @@ -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代币 代表用户份额 + diff --git a/document/ytLp用户前端交互文档.md b/document/ytLp用户前端交互文档.md new file mode 100644 index 0000000..ce07b9e --- /dev/null +++ b/document/ytLp用户前端交互文档.md @@ -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; + } +} + diff --git a/document/合约文档.md b/document/合约文档.md new file mode 100644 index 0000000..62a1200 --- /dev/null +++ b/document/合约文档.md @@ -0,0 +1,7000 @@ +合约文档 +测试网络 +arb sepolia +测试私钥 +0xa082a7037105ebd606bee80906687e400d89899bbb6ba0273a61528c2f5fab89 +合约地址 +YTAssetFactory:0x6DaB73519DbaFf23F36FEd24110e2ef5Cfc8aAC9 +JSON +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidHardCap", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "inputs": [], + "name": "VaultNotExists", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "newDefaultHardCap", + "type": "uint256" + } + ], + "name": "DefaultHardCapSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newHardCap", + "type": "uint256" + } + ], + "name": "HardCapSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "redemptionTime", + "type": "uint256" + } + ], + "name": "NextRedemptionTimeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wusdPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ytPrice", + "type": "uint256" + } + ], + "name": "PricesUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "hardCap", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "VaultCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "VaultImplementationUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allVaults", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "address", + "name": "_manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_hardCap", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_wusd", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_redemptionTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_initialWusdPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_initialYtPrice", + "type": "uint256" + } + ], + "name": "createVault", + "outputs": [ + { + "internalType": "address", + "name": "vault", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string[]", + "name": "_names", + "type": "string[]" + }, + { + "internalType": "string[]", + "name": "_symbols", + "type": "string[]" + }, + { + "internalType": "address[]", + "name": "_managers", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_hardCaps", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "_wusd", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_redemptionTimes", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_initialWusdPrices", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_initialYtPrices", + "type": "uint256[]" + } + ], + "name": "createVaultBatch", + "outputs": [ + { + "internalType": "address[]", + "name": "vaults", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "defaultHardCap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllVaults", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVaultCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + } + ], + "name": "getVaultInfo", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "totalAssets", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "idleAssets", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managedAssets", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "hardCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "wusdPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ytPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nextRedemptionTime", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_end", + "type": "uint256" + } + ], + "name": "getVaults", + "outputs": [ + { + "internalType": "address[]", + "name": "vaults", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vaultImplementation", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_defaultHardCap", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isVault", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_defaultHardCap", + "type": "uint256" + } + ], + "name": "setDefaultHardCap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_hardCap", + "type": "uint256" + } + ], + "name": "setHardCap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_vaults", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_hardCaps", + "type": "uint256[]" + } + ], + "name": "setHardCapBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "setVaultImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + }, + { + "internalType": "address", + "name": "_manager", + "type": "address" + } + ], + "name": "setVaultManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_nextRedemptionTime", + "type": "uint256" + } + ], + "name": "setVaultNextRedemptionTime", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_vaults", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "_nextRedemptionTime", + "type": "uint256" + } + ], + "name": "setVaultNextRedemptionTimeBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_wusdPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_ytPrice", + "type": "uint256" + } + ], + "name": "updateVaultPrices", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_vaults", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_wusdPrices", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_ytPrices", + "type": "uint256[]" + } + ], + "name": "updateVaultPricesBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + }, + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "upgradeVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_vaults", + "type": "address[]" + }, + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "upgradeVaultBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vaultImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] + +YT-A:0x0cA35994F033685E7a57ef9bc5d00dd3cf927330 +YT-B:0x333805C9EE75f59Aa2Cc79DfDe2499F920c7b408 +YT-C:0x6DF0ED6f0345F601A206974973dE9fC970598587 +JSON +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "Forbidden", + "type": "error" + }, + { + "inputs": [], + "name": "HardCapExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientWUSD", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientYTA", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidHardCap", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPrice", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "inputs": [], + "name": "StillInLockPeriod", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "AssetsDeposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "AssetsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wusdAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ytAmount", + "type": "uint256" + } + ], + "name": "Buy", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "newHardCap", + "type": "uint256" + } + ], + "name": "HardCapSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newManager", + "type": "address" + } + ], + "name": "ManagerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "newRedemptionTime", + "type": "uint256" + } + ], + "name": "NextRedemptionTimeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "wusdPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ytPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "PriceUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ytAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wusdAmount", + "type": "uint256" + } + ], + "name": "Sell", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "PRICE_PRECISION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "canRedeemNow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "depositManagedAssets", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_wusdAmount", + "type": "uint256" + } + ], + "name": "depositYT", + "outputs": [ + { + "internalType": "uint256", + "name": "ytAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTimeUntilNextRedemption", + "outputs": [ + { + "internalType": "uint256", + "name": "remainingTime", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVaultInfo", + "outputs": [ + { + "internalType": "uint256", + "name": "_totalAssets", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_idleAssets", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_managedAssets", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_hardCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_wusdPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_ytPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_nextRedemptionTime", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hardCap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "idleAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "address", + "name": "_manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_hardCap", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_wusd", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_redemptionTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_initialWusdPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_initialYtPrice", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "managedAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "manager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextRedemptionTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_wusdAmount", + "type": "uint256" + } + ], + "name": "previewBuy", + "outputs": [ + { + "internalType": "uint256", + "name": "ytAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_ytAmount", + "type": "uint256" + } + ], + "name": "previewSell", + "outputs": [ + { + "internalType": "uint256", + "name": "wusdAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_hardCap", + "type": "uint256" + } + ], + "name": "setHardCap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_manager", + "type": "address" + } + ], + "name": "setManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_nextRedemptionTime", + "type": "uint256" + } + ], + "name": "setNextRedemptionTime", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_wusdPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_ytPrice", + "type": "uint256" + } + ], + "name": "updatePrices", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdrawForManagement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_ytAmount", + "type": "uint256" + } + ], + "name": "withdrawYT", + "outputs": [ + { + "internalType": "uint256", + "name": "wusdAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "wusdAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "wusdPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ytPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] + +WUSD(fork 测试用):0x939cf46F7A4d05da2a37213E7379a8b04528F590 +JSON +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] + +USDY:0x631Bd6834C50f6d2B07035c9253b4a19132E888c +JSON +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "Forbidden", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidVault", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + } + ], + "name": "VaultAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + } + ], + "name": "VaultRemoved", + "type": "event" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + } + ], + "name": "addVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + } + ], + "name": "removeVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "vaults", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] + +YTLPToken:0x1b96F219E8aeE557DD8bD905a6c72cc64eA5BD7B +JSON +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidMinter", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "NotMinter", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isActive", + "type": "bool" + } + ], + "name": "MinterSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isMinter", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_minter", + "type": "address" + }, + { + "internalType": "bool", + "name": "_isActive", + "type": "bool" + } + ], + "name": "setMinter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] + +YTPriceFeed:0x0f2d930EE73972132E3a36b7eD6F709Af6E5B879 +JSON +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "Forbidden", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "MaxChangeTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "PriceChangeTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "SpreadTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isActive", + "type": "bool" + } + ], + "name": "KeeperSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "PriceUpdate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "spreadBps", + "type": "uint256" + } + ], + "name": "SpreadUpdate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "BASIS_POINTS_DIVISOR", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_SPREAD_BASIS_POINTS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PRICE_PRECISION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WUSD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_price", + "type": "uint256" + } + ], + "name": "forceUpdatePrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "getMaxPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "getMinPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "bool", + "name": "_maximise", + "type": "bool" + } + ], + "name": "getPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "getPriceInfo", + "outputs": [ + { + "internalType": "uint256", + "name": "currentPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cachedPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "spread", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gov", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isKeeper", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "lastPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxPriceChangeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_keeper", + "type": "address" + }, + { + "internalType": "bool", + "name": "_isActive", + "type": "bool" + } + ], + "name": "setKeeper", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxPriceChangeBps", + "type": "uint256" + } + ], + "name": "setMaxPriceChangeBps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_spreadBasisPoints", + "type": "uint256" + } + ], + "name": "setSpreadBasisPoints", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_spreadBasisPoints", + "type": "uint256[]" + } + ], + "name": "setSpreadBasisPointsForMultiple", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_wusdPriceSource", + "type": "address" + } + ], + "name": "setWusdPriceSource", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "spreadBasisPoints", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "updatePrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "wusdPriceSource", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] + +YTVault:0x19982e5145ca5401A1084c0BF916c0E0bB343Af9 +JSON +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [], + "name": "AmountExceedsLimit", + "type": "error" + }, + { + "inputs": [], + "name": "DailyLimitExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "EmergencyMode", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "Forbidden", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientPool", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientUSDYAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidFee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPoolAmount", + "type": "error" + }, + { + "inputs": [], + "name": "MaxUSDYExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "NotInEmergency", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "NotSwapper", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyPoolManager", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "inputs": [], + "name": "SameToken", + "type": "error" + }, + { + "inputs": [], + "name": "SlippageTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "SwapDisabled", + "type": "error" + }, + { + "inputs": [], + "name": "TokenNotWhitelisted", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "usdyAmount", + "type": "uint256" + } + ], + "name": "AddLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "EmergencyModeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "usdyAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "name": "RemoveLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "feeBasisPoints", + "type": "uint256" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "SwapEnabledSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "BASIS_POINTS_DIVISOR", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PRICE_PRECISION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "USDY_DECIMALS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allWhitelistedTokens", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_receiver", + "type": "address" + } + ], + "name": "buyUSDY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "clearWhitelistedToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "emergencyMode", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllPoolTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_usdyDelta", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_feeBasisPoints", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_taxBasisPoints", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_increment", + "type": "bool" + } + ], + "name": "getFeeBasisPoints", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "getMaxPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "getMinPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_maximise", + "type": "bool" + } + ], + "name": "getPoolValue", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "bool", + "name": "_maximise", + "type": "bool" + } + ], + "name": "getPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_usdyAmount", + "type": "uint256" + } + ], + "name": "getRedemptionFeeBasisPoints", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "_tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_usdyAmount", + "type": "uint256" + } + ], + "name": "getSwapFeeBasisPoints", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "getTargetUsdyAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gov", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hasDynamicFees", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_usdy", + "type": "address" + }, + { + "internalType": "address", + "name": "_priceFeed", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isSwapEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isSwapper", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "maxSwapAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSwapSlippageBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "maxUsdyAmounts", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "poolAmounts", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "priceFeed", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_receiver", + "type": "address" + } + ], + "name": "sellUSDY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_hasDynamicFees", + "type": "bool" + } + ], + "name": "setDynamicFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_emergencyMode", + "type": "bool" + } + ], + "name": "setEmergencyMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_gov", + "type": "address" + } + ], + "name": "setGov", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "setMaxSwapAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_slippageBps", + "type": "uint256" + } + ], + "name": "setMaxSwapSlippageBps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_manager", + "type": "address" + } + ], + "name": "setPoolManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_isSwapEnabled", + "type": "bool" + } + ], + "name": "setSwapEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_swapFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_stableSwapFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_taxBasisPoints", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_stableTaxBasisPoints", + "type": "uint256" + } + ], + "name": "setSwapFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_swapper", + "type": "address" + }, + { + "internalType": "bool", + "name": "_isActive", + "type": "bool" + } + ], + "name": "setSwapper", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_weight", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxUsdyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_isStable", + "type": "bool" + } + ], + "name": "setWhitelistedToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stableSwapFeeBasisPoints", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stableTaxBasisPoints", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "stableTokens", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "_tokenOut", + "type": "address" + }, + { + "internalType": "address", + "name": "_receiver", + "type": "address" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "swapFeeBasisPoints", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "taxBasisPoints", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenBalances", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenDecimals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenWeights", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalTokenWeights", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "usdy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "usdyAmounts", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "whitelistedTokens", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdrawToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "ytPoolManager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] + +YTPoolManager:0x14246886a1E1202cb6b5a2db793eF3359d536302 +JSON +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [], + "name": "CooldownNotPassed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "Forbidden", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientOutput", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDuration", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "PrivateMode", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "aumInUsdy", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ytLPSupply", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "usdyAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "AddLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "CooldownDurationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "handler", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isActive", + "type": "bool" + } + ], + "name": "HandlerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ytLPAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "aumInUsdy", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ytLPSupply", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "usdyAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "name": "RemoveLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "BASIS_POINTS_DIVISOR", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_COOLDOWN_DURATION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PRICE_PRECISION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "YTLP_PRECISION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_fundingAccount", + "type": "address" + }, + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minUsdy", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minYtLP", + "type": "uint256" + } + ], + "name": "addLiquidityForAccount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "aumAddition", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "aumDeduction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cooldownDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_maximise", + "type": "bool" + } + ], + "name": "getAumInUsdy", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_maximise", + "type": "bool" + } + ], + "name": "getPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gov", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_ytVault", + "type": "address" + }, + { + "internalType": "address", + "name": "_usdy", + "type": "address" + }, + { + "internalType": "address", + "name": "_ytLP", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_cooldownDuration", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isHandler", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "lastAddedAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "_tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_ytLPAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minOut", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_receiver", + "type": "address" + } + ], + "name": "removeLiquidityForAccount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_addition", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_deduction", + "type": "uint256" + } + ], + "name": "setAumAdjustment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_duration", + "type": "uint256" + } + ], + "name": "setCooldownDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_gov", + "type": "address" + } + ], + "name": "setGov", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_handler", + "type": "address" + }, + { + "internalType": "bool", + "name": "_isActive", + "type": "bool" + } + ], + "name": "setHandler", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "usdy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ytLP", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ytVault", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] + +YTRewardRouter:0x51eEF57eC57c867AC23945f0ce21aA5A9a2C246c +JSON +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "Forbidden", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientOutput", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minUsdy", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minYtLP", + "type": "uint256" + } + ], + "name": "addLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "getAccountValue", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getYtLPPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gov", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_usdy", + "type": "address" + }, + { + "internalType": "address", + "name": "_ytLP", + "type": "address" + }, + { + "internalType": "address", + "name": "_ytPoolManager", + "type": "address" + }, + { + "internalType": "address", + "name": "_ytVault", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_ytLPAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minOut", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_receiver", + "type": "address" + } + ], + "name": "removeLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "_tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minOut", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_receiver", + "type": "address" + } + ], + "name": "swapYT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "usdy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ytLP", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ytPoolManager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ytVault", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] + +部署及权限文件 +[deployments-vault-system.json] + +{ + "network": "arbSepolia", + "chainId": "421614", + "deployer": "0xa013422A5918CD099C63c8CC35283EACa99a705d", + "timestamp": "2025-12-15T08:56:02.155Z", + "wusdAddress": "0x939cf46F7A4d05da2a37213E7379a8b04528F590", + "defaultHardCap": "10000000000000000000000000", + "contracts": { + "YTAssetVault": { + "implementation": "0x6cBD32731742004471ce16FcB80a6db0844E8b13" + }, + "YTAssetFactory": { + "proxy": "0x6DaB73519DbaFf23F36FEd24110e2ef5Cfc8aAC9", + "implementation": "0xc22a07Cf4bbDc323bC3288a82E85d1367a470b75" + } + }, + "vaults": [ + { + "name": "YT Token A", + "symbol": "YT-A", + "address": "0x0cA35994F033685E7a57ef9bc5d00dd3cf927330", + "index": "0", + "manager": "0xa013422A5918CD099C63c8CC35283EACa99a705d", + "hardCap": "10000000000000000000000000", + "redemptionTime": 1797324969, + "wusdPrice": "1000000000000000000000000000000", + "ytPrice": "1000000000000000000000000000000" + }, + { + "name": "YT Token B", + "symbol": "YT-B", + "address": "0x333805C9EE75f59Aa2Cc79DfDe2499F920c7b408", + "index": "1", + "manager": "0xa013422A5918CD099C63c8CC35283EACa99a705d", + "hardCap": "10000000000000000000000000", + "redemptionTime": 1797324969, + "wusdPrice": "1000000000000000000000000000000", + "ytPrice": "1000000000000000000000000000000" + }, + { + "name": "YT Token C", + "symbol": "YT-C", + "address": "0x6DF0ED6f0345F601A206974973dE9fC970598587", + "index": "2", + "manager": "0xa013422A5918CD099C63c8CC35283EACa99a705d", + "hardCap": "10000000000000000000000000", + "redemptionTime": 1797324969, + "wusdPrice": "1000000000000000000000000000000", + "ytPrice": "1000000000000000000000000000000" + } + ], + "lastUpdate": "2025-12-15T08:56:22.614Z" +} + + +[deployments-wusd.json] + + +{ + "network": "arbSepolia", + "chainId": "421614", + "deployer": "0xa013422A5918CD099C63c8CC35283EACa99a705d", + "timestamp": "2025-12-15T08:53:30.301Z", + "contracts": { + "WUSD": { + "proxy": "0x939cf46F7A4d05da2a37213E7379a8b04528F590", + "implementation": "0xA6674E25670563f881aABCc25845757cEecb8d86", + "name": "Wrapped USD", + "symbol": "WUSD", + "decimals": 18 + } + } +} + + + +[deployments-ytlp.json] + + +{ + "network": "arbSepolia", + "chainId": "421614", + "deployer": "0xa013422A5918CD099C63c8CC35283EACa99a705d", + "timestamp": "2025-12-15T08:55:35.962Z", + "contracts": { + "USDY": { + "proxy": "0x631Bd6834C50f6d2B07035c9253b4a19132E888c", + "implementation": "0xb14d186d4EAcE8131a449126c6208165a3F5FC5b" + }, + "YTLPToken": { + "proxy": "0x1b96F219E8aeE557DD8bD905a6c72cc64eA5BD7B", + "implementation": "0x0C3fa01b2D0596B4190edEF1B77534237231C77e" + }, + "YTPriceFeed": { + "proxy": "0x0f2d930EE73972132E3a36b7eD6F709Af6E5B879", + "implementation": "0x2201c2B382E1decD933fc8d3503bEcE221B6C46c" + }, + "YTVault": { + "proxy": "0x19982e5145ca5401A1084c0BF916c0E0bB343Af9", + "implementation": "0x61278a2EBFC07eF0F7f84407291aAD07DA596AB2" + }, + "YTPoolManager": { + "proxy": "0x14246886a1E1202cb6b5a2db793eF3359d536302", + "implementation": "0x96Fe19188c3c7d0EDA441dafC7976fBB3526d28c" + }, + "YTRewardRouter": { + "proxy": "0x51eEF57eC57c867AC23945f0ce21aA5A9a2C246c", + "implementation": "0x3688CDd7A25613E7b1E7E0ee1aA46c21F66D27F3" + } + } +} + + + +[deployments-ytlp-config.json] + +{ + "network": "arbSepolia", + "chainId": "421614", + "configurer": "0xa013422A5918CD099C63c8CC35283EACa99a705d", + "timestamp": "2025-12-15T08:57:09.075Z", + "configuration": { + "permissions": { + "usdyVaults": [ + "0x19982e5145ca5401A1084c0BF916c0E0bB343Af9", + "0x14246886a1E1202cb6b5a2db793eF3359d536302" + ], + "ytlpMinters": [ + "0x14246886a1E1202cb6b5a2db793eF3359d536302" + ], + "vaultPoolManager": "0x14246886a1E1202cb6b5a2db793eF3359d536302", + "vaultSwappers": [ + "0x51eEF57eC57c867AC23945f0ce21aA5A9a2C246c" + ], + "poolManagerHandlers": [ + "0x51eEF57eC57c867AC23945f0ce21aA5A9a2C246c" + ], + "priceFeedKeepers": [ + "0xa013422A5918CD099C63c8CC35283EACa99a705d" + ], + "priceFeedWusdSource": "0x0cA35994F033685E7a57ef9bc5d00dd3cf927330" + }, + "parameters": { + "dynamicFees": true, + "maxSwapSlippageBps": 1000, + "maxPriceChangeBps": 500 + } + } +} + + +[deployments-whitelist-config.json] + +{ + "timestamp": "2025-12-15T08:57:53.055Z", + "operator": "0xa013422A5918CD099C63c8CC35283EACa99a705d", + "whitelistedVaults": [ + { + "name": "YT Token A", + "symbol": "YT-A", + "address": "0x0cA35994F033685E7a57ef9bc5d00dd3cf927330", + "weight": 4000, + "maxUsdyAmount": "45000000000000000000000000", + "price": "1000000000000000000000000000000" + }, + { + "name": "YT Token B", + "symbol": "YT-B", + "address": "0x333805C9EE75f59Aa2Cc79DfDe2499F920c7b408", + "weight": 3000, + "maxUsdyAmount": "35000000000000000000000000", + "price": "1000000000000000000000000000000" + }, + { + "name": "YT Token C", + "symbol": "YT-C", + "address": "0x6DF0ED6f0345F601A206974973dE9fC970598587", + "weight": 2000, + "maxUsdyAmount": "25000000000000000000000000", + "price": "1000000000000000000000000000000" + } + ], + "totalWeight": "9000", + "wusdPriceSource": "0x0cA35994F033685E7a57ef9bc5d00dd3cf927330" +} + + +相关文档 +ytLp池子合约流程文档 +ytLp用户前端交互文档 +ytLp流动性池系统操作流程文档 +Vault金库系统操作流程文档