From 7a21ef777cd9b22806ae8060fcef79fe55e68af3 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 27 Mar 2026 11:32:29 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20=E7=A7=BB=E9=99=A4=E6=A0=B9=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E5=90=88=E7=BA=A6/=E8=AE=BE=E8=AE=A1=E6=96=87?= =?UTF-8?q?=E6=A1=A3=EF=BC=8C=E4=B8=8D=E7=BA=B3=E5=85=A5=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 + holder获取方案文档.txt | 528 -- usdc版本合约文档.txt | 8729 ------------------------- ytLending-USDC借贷系统操作流程图.txt | 1841 ------ ytLending清算文档-final.txt | 477 -- ytLending购买抵押品文档-new.txt | 362 - ytLp-USDC流动性池系统操作流程文档.txt | 1100 ---- ytLp用户前端交互文档.txt | 230 - ytVault-USDC金库系统操作流程文档.txt | 2085 ------ 设计大概要求.txt | 77 - 10 files changed, 6 insertions(+), 15429 deletions(-) delete mode 100644 holder获取方案文档.txt delete mode 100644 usdc版本合约文档.txt delete mode 100644 ytLending-USDC借贷系统操作流程图.txt delete mode 100644 ytLending清算文档-final.txt delete mode 100644 ytLending购买抵押品文档-new.txt delete mode 100644 ytLp-USDC流动性池系统操作流程文档.txt delete mode 100644 ytLp用户前端交互文档.txt delete mode 100644 ytVault-USDC金库系统操作流程文档.txt delete mode 100644 设计大概要求.txt diff --git a/.gitignore b/.gitignore index 6382657..2e00a9e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,5 +34,11 @@ tmp/ # Backup backup/ +# Root-level docs +*.txt +*.html +INTERNSHIP_EXPERIENCE.md +system-panorama.html + # Claude config (local) .claude/ diff --git a/holder获取方案文档.txt b/holder获取方案文档.txt deleted file mode 100644 index 3427d53..0000000 --- a/holder获取方案文档.txt +++ /dev/null @@ -1,528 +0,0 @@ -holder获取方案文档 -ytLp 协议 Holder 获取方案 - -本文档详细说明如何获取 ytLp 协议中三种角色的持有者(Holder)信息:YT 代币持有者、LP 代币持有者和 Lending 提供者。 -目录 -1.[YT 代币持有者](#1-yt-代币持有者) -2.[LP 代币持有者(ytLP)](#2-lp-代币持有者ytlp) -3.[Lending 提供者](#3-lending-提供者) -4.[完整解决方案](#3-完整解决方案示例) - -1. YT 代币持有者 -合约信息 -合约: `YTAssetVault.sol` -位置: `contracts/ytVault/YTAssetVault.sol` -标准: ERC20(继承自 `ERC20Upgradeable`) -已部署的 YT 代币 -代币名称 合约地址 -YT-A 0x03d2a3B21238CD65D92c30A81b3f80d8bA1A44AC -YT-B 0xf41fc97d8a3c9006Dd50Afa04d0a3D6D27f8cD0B -YT-C 0xBdE54f062C537CA7D96Cd97e5B827918E38E97b5 -获取方式 -监听事件 -YT 代币作为标准 ERC20 代币,会发出以下事件: -标准 ERC20 Transfer 事件: -Solidity -event Transfer(address indexed from, address indexed to, uint256 value); -获取步骤: -1.监听 Transfer 事件,收集所有接收过代币的地址 -2.过滤掉零地址 0x0000...0000(铸造)和合约地址本身(销毁) -3.对于每个地址,调用 balanceOf(address) 查询当前余额 -4.余额 > 0 的地址即为持有者 - -2. LP 代币持有者(ytLP) -合约信息 -合约: `YTLPToken.sol` -位置: `contracts/ytLp/tokens/YTLPToken.sol` -标准: ERC20(继承自 `ERC20Upgradeable`) -已部署地址: `0x102e3F25Ef0ad9b0695C8F2daF8A1262437eEfc3` -获取方式 -监听事件 -标准 ERC20 Transfer 事件: -Solidity -event Transfer(address indexed from, address indexed to, uint256 value); -获取步骤: -1.监听 ytLP 代币合约的 Transfer 事件 -2.收集所有接收过代币的地址(to 参数) -3.过滤零地址和合约地址 -4.调用 balanceOf(address) 获取当前余额 -5.余额 > 0 的即为 LP 持有者 - -3. Lending 提供者 -合约信息 -合约: `Lending.sol` -位置: `contracts/ytLending/Lending.sol` -已部署地址: `0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D` -特点: 非 ERC20 代币,通过 mapping 存储用户余额 -数据存储结构 -Solidity -// 用户基本信息(在 LendingStorage.sol 中定义) -struct UserBasic { - int104 principal; // 本金(正数=存款本金,负数=借款本金) -} -mapping(address => UserBasic) public userBasic; - -// 用户抵押品余额 -mapping(address => mapping(address => uint256)) public userCollateral; -获取方式 -监听事件 -Lending 相关事件 (定义在 `ILending.sol`): -Solidity -event Supply(address indexed from, address indexed dst, uint256 amount); -event Withdraw(address indexed src, address indexed to, uint256 amount); -event SupplyCollateral(address indexed from, address indexed dst, address indexed asset, uint256 amount); -event WithdrawCollateral(address indexed src, address indexed to, address indexed asset, uint256 amount); -event AbsorbDebt(address indexed absorber, address indexed borrower, uint256 basePaidOut, uint256 usdValue); -event AbsorbCollateral(address indexed absorber, address indexed borrower, address indexed asset, uint256 collateralAbsorbed, uint256 usdValue); -event BuyCollateral(address indexed buyer, address indexed asset, uint256 baseAmount, uint256 collateralAmount); -获取 USDC 提供者步骤: -1.监听 Supply 事件,收集所有曾经提供 USDC 的地址(dst 参数) -2.监听 Withdraw 事件,跟踪提现行为 -3.对每个地址调用 supplyBalanceOf(address) 查询当前供应余额 -4.供应余额 > 0 的即为当前的 Lending 提供者 -获取抵押品提供者步骤: -1.监听 SupplyCollateral 事件,收集提供抵押品的地址 -2.对每个地址和资产调用 getCollateral(address, asset) 查询余额 -3.抵押品余额 > 0 的即为抵押品提供者 -余额说明: -•principal > 0: 用户是供应者(存款方) -•principal < 0: 用户是借款者 -•principal = 0: 用户无供应也无借款 -•实际余额需要通过 supplyBalanceOf() 或 borrowBalanceOf() 获取(含利息累计) -注意事项 -1. 利息累计: Lending 使用复合式利息,余额会随时间增长 -•principal 是存储的本金(不变) -•supplyBalanceOf() 返回实时余额(含利息) -•需要调用 accrueInterest() 或读取函数自动累计利息 -2. 借款和供应同时存在: -•一个地址可能既是供应者又是借款者 -•principal > 0: 净供应者 -•principal < 0: 净借款者 -3. 抵押品种类: -•通过 assetList() 获取支持的抵押资产列表 -•对每个资产调用 getCollateral(user, asset) 查询余额 - -4. 完整解决方案示例 -以下是一个完整的 TypeScript 脚本,可以获取所有三种 holder: -从合约部署区块开始扫描,扫描到当前区块后(记录扫描最终区块),每隔10s从上次记录的区块扫描到最新区块,以此类推,存储到数据库自行加代码。 -TypeScript -import { ethers, Contract, JsonRpcProvider } from "ethers"; -import type { EventLog, Log } from "ethers"; - -// ==================== 类型定义 ==================== - -interface VaultConfig { - name: string; - address: string; -} - -interface YTHolderData { - address: string; - balance: string; -} - -interface LPHolderData { - address: string; - balance: string; - share: string; -} - -interface LendingSupplierData { - address: string; - supply: string; - borrow: string; - net: string; -} - -// ==================== 配置 ==================== - -const RPC_URL: string = "https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07"; - -// 合约配置(包含部署区块号,可以大幅减少查询时间) -const YT_VAULTS: VaultConfig[] = [ - { name: "YT-A", address: "0x97204190B35D9895a7a47aa7BaC61ac08De3cF05" }, - { name: "YT-B", address: "0x181ef4011c35C4a2Fda08eBC5Cf509Ef58E553fF" }, - { name: "YT-C", address: "0xE9A5b9f3a2Eda4358f81d4E2eF4f3280A664e5B0" }, -]; - -const YTLP_ADDRESS: string = "0x102e3F25Ef0ad9b0695C8F2daF8A1262437eEfc3"; -const LENDING_ADDRESS: string = "0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D"; - -// ==================== 部署区块配置 ==================== -// -// 配置说明: -// 1. 查询准确的部署区块号,直接填写 - -interface DeploymentConfig { - ytVaults: number; // YT 代币部署区块 - ytlp: number; // ytLP 部署区块 - lending: number; // Lending 部署区块 -} - -const DEPLOYMENT_BLOCKS: DeploymentConfig = { - ytVaults: 227339300, // YT-A/B/C 部署区块号 - ytlp: 227230270, // ytLP 部署区块号 - lending: 227746053, // Lending 部署区块号 -}; - -// ==================== ABIs ==================== - -const ERC20_ABI = [ - "event Transfer(address indexed from, address indexed to, uint256 value)", - "function balanceOf(address account) view returns (uint256)", - "function totalSupply() view returns (uint256)", -] as const; - -const LENDING_ABI = [ - "event Supply(address indexed from, address indexed dst, uint256 amount)", - "function supplyBalanceOf(address account) view returns (uint256)", - "function borrowBalanceOf(address account) view returns (uint256)", -] as const; - -// ==================== 工具函数 ==================== - -/** - * 分块查询事件,避免超出 RPC 限制 - * @param contract 合约实例 - * @param filter 事件过滤器 - * @param fromBlock 起始区块 - * @param toBlock 结束区块 - * @param batchSize 每批次的区块数量(默认 9999,低于 10000 限制) - */ -async function queryEventsInBatches( - contract: Contract, - filter: any, - fromBlock: number, - toBlock: number, - batchSize: number = 9999 -): Promise<(EventLog | Log)[]> { - const allEvents: (EventLog | Log)[] = []; - let currentBlock = fromBlock; - - console.log(` 查询区块范围: ${fromBlock} -> ${toBlock} (总共 ${toBlock - fromBlock + 1} 个区块)`); - - while (currentBlock <= toBlock) { - const endBlock = Math.min(currentBlock + batchSize, toBlock); - - console.log(` 正在查询区块 ${currentBlock} - ${endBlock}...`); - - try { - const events = await contract.queryFilter(filter, currentBlock, endBlock); - allEvents.push(...events); - console.log(` ✓ 获取到 ${events.length} 个事件`); - } catch (error) { - console.error(` ✗ 查询区块 ${currentBlock} - ${endBlock} 失败:`, error); - throw error; - } - - currentBlock = endBlock + 1; - - // 添加小延迟,避免触发 RPC 速率限制 - if (currentBlock <= toBlock) { - await new Promise(resolve => setTimeout(resolve, 100)); - } - } - - console.log(` 总计获取 ${allEvents.length} 个事件\n`); - return allEvents; -} - -/** - * 获取当前最新区块号 - */ -async function getLatestBlockNumber(provider: JsonRpcProvider, silent: boolean = false): Promise { - const blockNumber = await provider.getBlockNumber(); - if (!silent) { - console.log(`当前最新区块: ${blockNumber}\n`); - } - return blockNumber; -} - -// ==================== 主函数 ==================== - -// 记录上次扫描的区块号 -let lastScannedBlock: number = 0; -// 标记是否正在扫描,防止并发 -let isScanning: boolean = false; - -// 全局地址集合,用于追踪所有曾经出现过的地址 -const allYTAddresses: Map> = new Map(); // vault address -> holder addresses -const allLPAddresses: Set = new Set(); -const allLendingAddresses: Set = new Set(); - -async function getAllHolders( - provider: JsonRpcProvider, - fromBlock?: number, - toBlock?: number, - isInitialScan: boolean = false -): Promise { - // 获取最新区块号 - const latestBlock = toBlock || await getLatestBlockNumber(provider, fromBlock !== undefined); - - // 计算起始区块 - let ytVaultsStartBlock: number; - let ytlpStartBlock: number; - let lendingStartBlock: number; - - if (fromBlock !== undefined) { - // 增量扫描模式 - ytVaultsStartBlock = ytlpStartBlock = lendingStartBlock = fromBlock; - console.log(`\n🔄 增量扫描: 区块 ${fromBlock} -> ${latestBlock}\n`); - } else { - // 首次扫描:使用部署区块号 - ytVaultsStartBlock = DEPLOYMENT_BLOCKS.ytVaults; - ytlpStartBlock = DEPLOYMENT_BLOCKS.ytlp; - lendingStartBlock = DEPLOYMENT_BLOCKS.lending; - if (isInitialScan) { - console.log(`✨ 首次扫描,从部署区块开始:`); - console.log(` YT Vaults 起始区块: ${ytVaultsStartBlock}`); - console.log(` ytLP 起始区块: ${ytlpStartBlock}`); - console.log(` Lending 起始区块: ${lendingStartBlock}\n`); - } - } - - // 1. 获取 YT 代币持有者 - console.log("1. YT 代币持有者:"); - - for (const vault of YT_VAULTS) { - console.log(` 正在查询 ${vault.name} (${vault.address})...`); - const contract: Contract = new ethers.Contract(vault.address, ERC20_ABI, provider); - const filter = contract.filters.Transfer(); - const events: (EventLog | Log)[] = await queryEventsInBatches( - contract, - filter, - ytVaultsStartBlock, - latestBlock - ); - - // 初始化该 vault 的地址集合(如果不存在) - if (!allYTAddresses.has(vault.address)) { - allYTAddresses.set(vault.address, new Set()); - } - const vaultAddresses = allYTAddresses.get(vault.address)!; - - // 记录新增地址数量 - const previousCount = vaultAddresses.size; - - // 添加新发现的地址到全局集合 - for (const event of events) { - if ("args" in event && event.args.to !== ethers.ZeroAddress) { - vaultAddresses.add(event.args.to as string); - } - } - - const newAddressCount = vaultAddresses.size - previousCount; - if (newAddressCount > 0) { - console.log(` 发现 ${newAddressCount} 个新地址,总共追踪 ${vaultAddresses.size} 个地址`); - } - - // 查询所有曾经出现过的地址的当前余额 - const holders: YTHolderData[] = []; - for (const address of vaultAddresses) { - const balance: bigint = await contract.balanceOf(address); - if (balance > 0n) { - holders.push({ - address, - balance: ethers.formatEther(balance), - }); - } - } - - // 按余额降序排序 - holders.sort((a, b) => parseFloat(b.balance) - parseFloat(a.balance)); - - console.log(` ${vault.name}: ${holders.length} 持有者`); - if (holders.length > 0) { - console.log(` 前 10 名持有者:`); - const top10 = holders.slice(0, 10); - top10.forEach((h: YTHolderData, index: number) => - console.log(` ${index + 1}. ${h.address}: ${h.balance}`) - ); - } else { - console.log(` 暂无持有者`); - } - } - - // 2. 获取 LP 代币持有者 - console.log("\n2. LP 代币持有者 (ytLP):"); - console.log(` 正在查询 ytLP (${YTLP_ADDRESS})...`); - const lpContract: Contract = new ethers.Contract(YTLP_ADDRESS, ERC20_ABI, provider); - const lpFilter = lpContract.filters.Transfer(); - const lpEvents: (EventLog | Log)[] = await queryEventsInBatches( - lpContract, - lpFilter, - ytlpStartBlock, - latestBlock - ); - - // 记录新增地址数量 - const previousLPCount = allLPAddresses.size; - - // 添加新发现的地址到全局集合 - for (const event of lpEvents) { - if ("args" in event && event.args.to !== ethers.ZeroAddress) { - allLPAddresses.add(event.args.to as string); - } - } - - const newLPAddressCount = allLPAddresses.size - previousLPCount; - if (newLPAddressCount > 0) { - console.log(` 发现 ${newLPAddressCount} 个新地址,总共追踪 ${allLPAddresses.size} 个地址`); - } - - // 查询所有曾经出现过的地址的当前余额 - const lpHolders: LPHolderData[] = []; - const totalSupply: bigint = await lpContract.totalSupply(); - - for (const address of allLPAddresses) { - const balance: bigint = await lpContract.balanceOf(address); - if (balance > 0n) { - const share: string = (Number(balance) / Number(totalSupply) * 100).toFixed(4); - lpHolders.push({ - address, - balance: ethers.formatEther(balance), - share: share + "%", - }); - } - } - - // 按余额降序排序 - lpHolders.sort((a, b) => parseFloat(b.balance) - parseFloat(a.balance)); - - console.log(` 总计: ${lpHolders.length} 持有者`); - if (lpHolders.length > 0) { - console.log(` 前 10 名持有者:`); - const top10 = lpHolders.slice(0, 10); - top10.forEach((h: LPHolderData, index: number) => - console.log(` ${index + 1}. ${h.address}: ${h.balance} (${h.share})`) - ); - } else { - console.log(` 暂无持有者`); - } - - // 3. 获取 Lending 提供者 - console.log("\n3. Lending 提供者:"); - console.log(` 正在查询 Lending (${LENDING_ADDRESS})...`); - const lendingContract: Contract = new ethers.Contract(LENDING_ADDRESS, LENDING_ABI, provider); - const supplyFilter = lendingContract.filters.Supply(); - const supplyEvents: (EventLog | Log)[] = await queryEventsInBatches( - lendingContract, - supplyFilter, - lendingStartBlock, - latestBlock - ); - - // 记录新增地址数量 - const previousLendingCount = allLendingAddresses.size; - - // 添加新发现的地址到全局集合 - for (const event of supplyEvents) { - if ("args" in event) { - allLendingAddresses.add(event.args.dst as string); - } - } - - const newLendingAddressCount = allLendingAddresses.size - previousLendingCount; - if (newLendingAddressCount > 0) { - console.log(` 发现 ${newLendingAddressCount} 个新地址,总共追踪 ${allLendingAddresses.size} 个地址`); - } - - // 查询所有曾经出现过的地址的当前余额 - const suppliers: LendingSupplierData[] = []; - for (const address of allLendingAddresses) { - const supplyBalance: bigint = await lendingContract.supplyBalanceOf(address); - const borrowBalance: bigint = await lendingContract.borrowBalanceOf(address); - - if (supplyBalance > 0n || borrowBalance > 0n) { - suppliers.push({ - address, - supply: ethers.formatUnits(supplyBalance, 6), - borrow: ethers.formatUnits(borrowBalance, 6), - net: ethers.formatUnits(supplyBalance - borrowBalance, 6), - }); - } - } - - // 按净供应额降序排序 - suppliers.sort((a, b) => parseFloat(b.net) - parseFloat(a.net)); - - console.log(` 总计: ${suppliers.length} 参与者`); - if (suppliers.length > 0) { - console.log(` 前 10 名参与者:`); - const top10 = suppliers.slice(0, 10); - top10.forEach((s: LendingSupplierData, index: number) => - console.log( - ` ${index + 1}. ${s.address}: 供应=${s.supply} USDC, 借款=${s.borrow} USDC, 净额=${s.net} USDC` - ) - ); - } else { - console.log(` 暂无参与者`); - } - - // 更新上次扫描的区块号 - lastScannedBlock = latestBlock; - console.log(`\n📌 已记录扫描区块: ${lastScannedBlock}`); -} - -// ==================== 执行 ==================== - -const POLL_INTERVAL_MS = 10000; // 10秒轮询间隔 - -async function main(): Promise { - const provider: JsonRpcProvider = new ethers.JsonRpcProvider(RPC_URL); - - console.log("=== ytLp 协议 Holder 数据监控 ===\n"); - console.log(`轮询间隔: ${POLL_INTERVAL_MS / 1000} 秒\n`); - - try { - // 首次扫描:从部署区块到当前区块 - console.log("📊 开始首次扫描...\n"); - const startTime = Date.now(); - await getAllHolders(provider, undefined, undefined, true); - const endTime = Date.now(); - const duration = ((endTime - startTime) / 1000).toFixed(2); - console.log(`\n✓ 首次扫描完成,耗时 ${duration} 秒`); - - // 启动轮询 - console.log(`\n⏰ 开始轮询,每 ${POLL_INTERVAL_MS / 1000} 秒检查一次新区块...\n`); - - setInterval(async () => { - try { - // 如果正在扫描,跳过本次轮询 - if (isScanning) { - console.log(`⏰ [${new Date().toLocaleString()}] 跳过本次轮询(上次扫描仍在进行中)`); - return; - } - - const currentBlock = await provider.getBlockNumber(); - - // 如果有新区块,进行增量扫描 - if (currentBlock > lastScannedBlock) { - isScanning = true; // 标记开始扫描 - - console.log(`\n${"=".repeat(60)}`); - console.log(`⏰ [${new Date().toLocaleString()}] 发现新区块`); - console.log(`${"=".repeat(60)}`); - - const scanStart = Date.now(); - await getAllHolders(provider, lastScannedBlock + 1, currentBlock, false); - const scanDuration = ((Date.now() - scanStart) / 1000).toFixed(2); - console.log(`\n✓ 增量扫描完成,耗时 ${scanDuration} 秒`); - - isScanning = false; // 标记扫描完成 - } else { - console.log(`⏰ [${new Date().toLocaleString()}] 暂无新区块 (当前: ${currentBlock})`); - } - } catch (error) { - console.error(`\n✗ 轮询过程中发生错误:`, error); - isScanning = false; // 发生错误时也要重置标记 - } - }, POLL_INTERVAL_MS); - - } catch (error) { - console.error("\n✗ 发生错误:", error); - process.exit(1); - } -} - -main(); diff --git a/usdc版本合约文档.txt b/usdc版本合约文档.txt deleted file mode 100644 index 32afd21..0000000 --- a/usdc版本合约文档.txt +++ /dev/null @@ -1,8729 +0,0 @@ -usdc�汾��Լ�ĵ� -�������� -1��7��֮ǰ�������arb sepolia��������bnb testnet -����˽Կ -0xa082a7037105ebd606bee80906687e400d89899bbb6ba0273a61528c2f5fab89 -USDC��ַ��arb sepolia�� -0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d -USDC��ַ��bnb testnet�� -0x939cf46F7A4d05da2a37213E7379a8b04528F590 -1��7�ղ��� -��Լ��ַ -YTAssetFactory��0x37B2CD7D94ba1400a6FEB34804a32EfD555bbfc8 -JSON -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "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": "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": "_usdc", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_redemptionTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_initialYtPrice", - "type": "uint256" - }, - { - "internalType": "address", - "name": "_usdcPriceFeed", - "type": "address" - } - ], - "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": "_usdc", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "_redemptionTimes", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "_initialYtPrices", - "type": "uint256[]" - }, - { - "internalType": "address", - "name": "_usdcPriceFeed", - "type": "address" - } - ], - "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": "usdcPrice", - "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": [ - { - "internalType": "address", - "name": "_vault", - "type": "address" - } - ], - "name": "pauseVault", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "_vaults", - "type": "address[]" - } - ], - "name": "pauseVaultBatch", - "outputs": [], - "stateMutability": "nonpayable", - "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": "_vault", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_threshold", - "type": "uint256" - } - ], - "name": "setPriceStalenessThreshold", - "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" - } - ], - "name": "unpauseVault", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "_vaults", - "type": "address[]" - } - ], - "name": "unpauseVaultBatch", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_vault", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_ytPrice", - "type": "uint256" - } - ], - "name": "updateVaultPrices", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "_vaults", - "type": "address[]" - }, - { - "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��0x7f9eEA491eE53045594ee4669327f0355aCd0e58 -YT-B��0x20B94C5E5b7361552E0548161a58696aA6FeDBd4 -YT-C��0x0EF308D70cf35460E26a3Eb42F3442Ff28cbE07C -JSON -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "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": "EnforcedPause", - "type": "error" - }, - { - "inputs": [], - "name": "ExpectedPause", - "type": "error" - }, - { - "inputs": [], - "name": "FailedCall", - "type": "error" - }, - { - "inputs": [], - "name": "Forbidden", - "type": "error" - }, - { - "inputs": [], - "name": "HardCapExceeded", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientUSDC", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientYTA", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidAmount", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidBatchSize", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidChainlinkPrice", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidHardCap", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidPrice", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidPriceFeed", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [], - "name": "RequestAlreadyProcessed", - "type": "error" - }, - { - "inputs": [], - "name": "RequestNotFound", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "SafeERC20FailedOperation", - "type": "error" - }, - { - "inputs": [], - "name": "StalePrice", - "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": false, - "internalType": "uint256", - "name": "startIndex", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "endIndex", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "processedCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "totalUsdcDistributed", - "type": "uint256" - } - ], - "name": "BatchProcessed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "usdcAmount", - "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": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "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": "usdcAmount", - "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": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Unpaused", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "Upgraded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "requestId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "ytAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "usdcAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "queueIndex", - "type": "uint256" - } - ], - "name": "WithdrawRequestCreated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "requestId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "usdcAmount", - "type": "uint256" - } - ], - "name": "WithdrawRequestProcessed", - "type": "event" - }, - { - "inputs": [], - "name": "CHAINLINK_PRICE_PRECISION", - "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": [ - { - "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": "_usdcAmount", - "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": "getPendingRequestsCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getQueueProgress", - "outputs": [ - { - "internalType": "uint256", - "name": "currentIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "totalRequests", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pendingRequests", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_requestId", - "type": "uint256" - } - ], - "name": "getRequestDetails", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint256", - "name": "ytAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "usdcAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "requestTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "queueIndex", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "processed", - "type": "bool" - } - ], - "internalType": "struct YTAssetVault.WithdrawRequest", - "name": "request", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getTimeUntilNextRedemption", - "outputs": [ - { - "internalType": "uint256", - "name": "remainingTime", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_user", - "type": "address" - } - ], - "name": "getUserPendingRequests", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint256", - "name": "ytAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "usdcAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "requestTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "queueIndex", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "processed", - "type": "bool" - } - ], - "internalType": "struct YTAssetVault.WithdrawRequest[]", - "name": "pendingRequests", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_user", - "type": "address" - } - ], - "name": "getUserRequestIds", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "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": "_usdcPrice", - "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": "_usdc", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_redemptionTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_initialYtPrice", - "type": "uint256" - }, - { - "internalType": "address", - "name": "_usdcPriceFeed", - "type": "address" - } - ], - "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": [], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pendingRequestsCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_usdcAmount", - "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": "usdcAmount", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "priceStalenesThreshold", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_batchSize", - "type": "uint256" - } - ], - "name": "processBatchWithdrawals", - "outputs": [ - { - "internalType": "uint256", - "name": "processedCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "totalDistributed", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "processedUpToIndex", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxiableUUID", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "requestIdCounter", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "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": [ - { - "internalType": "uint256", - "name": "_threshold", - "type": "uint256" - } - ], - "name": "setPriceStalenessThreshold", - "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": [], - "name": "unpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "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": [], - "name": "usdcAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "usdcDecimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "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": "", - "type": "uint256" - } - ], - "name": "withdrawRequests", - "outputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint256", - "name": "ytAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "usdcAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "requestTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "queueIndex", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "processed", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_ytAmount", - "type": "uint256" - } - ], - "name": "withdrawYT", - "outputs": [ - { - "internalType": "uint256", - "name": "requestId", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "ytPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - } -] - -USDY��0x29774970556407fAE16BC07e87704fE0E9559BC4 -JSON -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "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��0x17c1100b02d560549b499561dB8d7D48fc391cf3 -JSON -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "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": "InvalidPoolManager", - "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": "poolManager", - "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": [ - { - "internalType": "address", - "name": "_poolManager", - "type": "address" - } - ], - "name": "setPoolManager", - "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��0x293d00fB683f15eA5a3143ecb551a0aCCbEC42Be -JSON -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "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": "InvalidAddress", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidChainlinkPrice", - "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": "StalePrice", - "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": [ - { - "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": [ - { - "internalType": "address", - "name": "_usdcAddress", - "type": "address" - }, - { - "internalType": "address", - "name": "_usdcPriceFeed", - "type": "address" - } - ], - "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": "priceStalenesThreshold", - "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": "uint256", - "name": "_threshold", - "type": "uint256" - } - ], - "name": "setPriceStalenessThreshold", - "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": "_usdcAddress", - "type": "address" - } - ], - "name": "setUSDCAddress", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_usdcPriceFeed", - "type": "address" - } - ], - "name": "setUSDCPriceFeed", - "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": "usdcAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - } -] - -YTVault��0xA6CD153c2fa0C2725605880668538660Ca21a790 -JSON -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "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": true, - "internalType": "address", - "name": "oldGov", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newGov", - "type": "address" - } - ], - "name": "GovChanged", - "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": "oldManager", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newManager", - "type": "address" - } - ], - "name": "PoolManagerChanged", - "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��0xb11824eAA659F8A4648711709dA60720d5Cdabd2 -JSON -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "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": "addition", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "deduction", - "type": "uint256" - } - ], - "name": "AumAdjustmentChanged", - "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": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cooldownTime", - "type": "uint256" - } - ], - "name": "CooldownInherited", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "oldGov", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newGov", - "type": "address" - } - ], - "name": "GovChanged", - "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": [ - { - "internalType": "address", - "name": "_from", - "type": "address" - }, - { - "internalType": "address", - "name": "_to", - "type": "address" - } - ], - "name": "onLPTransfer", - "outputs": [], - "stateMutability": "nonpayable", - "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��0x5af5A51F7702024E7387bba7497DC9965C00F16E -JSON -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "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": "EnforcedPause", - "type": "error" - }, - { - "inputs": [], - "name": "ExpectedPause", - "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": [], - "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": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", - "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": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Unpaused", - "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": "pause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "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": [], - "name": "unpause", - "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": "ytPoolManager", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "ytVault", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - } -] - -lendingProxy��0xfdbFD4656b74aD16F23264121eD9F58219f5C3Cf -JSON -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [], - "name": "BorrowTooSmall", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "ERC1967InvalidImplementation", - "type": "error" - }, - { - "inputs": [], - "name": "ERC1967NonPayable", - "type": "error" - }, - { - "inputs": [], - "name": "EnforcedPause", - "type": "error" - }, - { - "inputs": [], - "name": "ExpectedPause", - "type": "error" - }, - { - "inputs": [], - "name": "FailedCall", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientCollateral", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientReserves", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidBorrowCollateralFactor", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidLiquidateCollateralFactor", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidLiquidationFactor", - "type": "error" - }, - { - "inputs": [], - "name": "NotForSale", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [], - "name": "NotLiquidatable", - "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": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "SafeERC20FailedOperation", - "type": "error" - }, - { - "inputs": [], - "name": "SupplyCapExceeded", - "type": "error" - }, - { - "inputs": [], - "name": "UUPSUnauthorizedCallContext", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "slot", - "type": "bytes32" - } - ], - "name": "UUPSUnsupportedProxiableUUID", - "type": "error" - }, - { - "inputs": [], - "name": "Unauthorized", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "absorber", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "borrower", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "collateralAbsorbed", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "usdValue", - "type": "uint256" - } - ], - "name": "AbsorbCollateral", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "absorber", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "borrower", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "basePaidOut", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "usdValue", - "type": "uint256" - } - ], - "name": "AbsorbDebt", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "buyer", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "baseAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "collateralAmount", - "type": "uint256" - } - ], - "name": "BuyCollateral", - "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": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "dst", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Supply", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "dst", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "SupplyCollateral", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint104", - "name": "targetReserves", - "type": "uint104" - } - ], - "name": "TargetReservesUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Unpaused", - "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": "src", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Withdraw", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "src", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "WithdrawCollateral", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "WithdrawReserves", - "type": "event" - }, - { - "inputs": [], - "name": "UPGRADE_INTERFACE_VERSION", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "borrower", - "type": "address" - } - ], - "name": "absorb", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "absorber", - "type": "address" - }, - { - "internalType": "address[]", - "name": "accounts", - "type": "address[]" - } - ], - "name": "absorbMultiple", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "accrueInterest", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "assetConfigs", - "outputs": [ - { - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "internalType": "uint8", - "name": "decimals", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "borrowCollateralFactor", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "liquidateCollateralFactor", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "liquidationFactor", - "type": "uint64" - }, - { - "internalType": "uint128", - "name": "supplyCap", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "assetList", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "baseBorrowMin", - "outputs": [ - { - "internalType": "uint104", - "name": "", - "type": "uint104" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "baseToken", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "borrowBalanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "borrowIndex", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "borrowKink", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "borrowPerSecondInterestRateBase", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "borrowPerSecondInterestRateSlopeHigh", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "borrowPerSecondInterestRateSlopeLow", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "internalType": "uint256", - "name": "minAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "baseAmount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "buyCollateral", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "collateralReserves", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "getBalance", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getBorrowRate", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "address", - "name": "asset", - "type": "address" - } - ], - "name": "getCollateral", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "asset", - "type": "address" - } - ], - "name": "getCollateralReserves", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getReserves", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getSupplyRate", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getTotalBorrow", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getTotalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getUtilization", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "baseToken", - "type": "address" - }, - { - "internalType": "address", - "name": "lendingPriceSource", - "type": "address" - }, - { - "internalType": "uint64", - "name": "supplyKink", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "supplyPerYearInterestRateSlopeLow", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "supplyPerYearInterestRateSlopeHigh", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "supplyPerYearInterestRateBase", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "borrowKink", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "borrowPerYearInterestRateSlopeLow", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "borrowPerYearInterestRateSlopeHigh", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "borrowPerYearInterestRateBase", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "storeFrontPriceFactor", - "type": "uint64" - }, - { - "internalType": "uint104", - "name": "baseBorrowMin", - "type": "uint104" - }, - { - "internalType": "uint104", - "name": "targetReserves", - "type": "uint104" - }, - { - "components": [ - { - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "internalType": "uint8", - "name": "decimals", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "borrowCollateralFactor", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "liquidateCollateralFactor", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "liquidationFactor", - "type": "uint64" - }, - { - "internalType": "uint128", - "name": "supplyCap", - "type": "uint128" - } - ], - "internalType": "struct LendingConfiguration.AssetConfig[]", - "name": "assetConfigs", - "type": "tuple[]" - } - ], - "internalType": "struct LendingConfiguration.Configuration", - "name": "config", - "type": "tuple" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "isLiquidatable", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "lastAccrualTime", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "lendingPriceSource", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxiableUUID", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "internalType": "uint256", - "name": "baseAmount", - "type": "uint256" - } - ], - "name": "quoteCollateral", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint104", - "name": "newTargetReserves", - "type": "uint104" - } - ], - "name": "setTargetReserves", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "storeFrontPriceFactor", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "supply", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "supplyBalanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "supplyCollateral", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "supplyIndex", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "supplyKink", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "supplyPerSecondInterestRateBase", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "supplyPerSecondInterestRateSlopeHigh", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "supplyPerSecondInterestRateSlopeLow", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "targetReserves", - "outputs": [ - { - "internalType": "uint104", - "name": "", - "type": "uint104" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalBorrowBase", - "outputs": [ - { - "internalType": "uint104", - "name": "", - "type": "uint104" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupplyBase", - "outputs": [ - { - "internalType": "uint104", - "name": "", - "type": "uint104" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "unpause", - "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": "userBasic", - "outputs": [ - { - "internalType": "int104", - "name": "principal", - "type": "int104" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "userCollateral", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "withdraw", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "asset", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "withdrawCollateral", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "withdrawReserves", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] - -����Ȩ���ļ� -[deployments-lending.json] - - -{ - "97": { - "lendingFactory": "0x10E6Ad7b8a801857F3A96A5178D7baf5C34a5B83", - "lendingPriceFeedProxy": "0xa280F5FD7AA093572BEb791d7f9CC8Ec8EA57A15", - "lendingPriceFeed": "0xa280F5FD7AA093572BEb791d7f9CC8Ec8EA57A15", - "lendingPriceFeedImpl": "0xF1aAd358d6E20Ba946786Bb3E5d34D42F4740dd2", - "configuratorProxy": "0x189Fe9dbB2f7dA538D3191159fd661779D7BCBd6", - "configuratorImpl": "0xF553843cb7F35378Fd4020d2DFceb90BfD760552", - "lendingImpl": "0xc20e1Dd1eb781C344489828CE3BFCA318CB51430", - "usdcAddress": "0x939cf46F7A4d05da2a37213E7379a8b04528F590", - "usdcPriceFeed": "0x90c069C4538adAc136E051052E14c1cD799C41B7", - "deployTimestamp": "2026-01-12T05:59:53.030Z", - "deployer": "0xa013422A5918CD099C63c8CC35283EACa99a705d", - "lendingProxy": "0xfdbFD4656b74aD16F23264121eD9F58219f5C3Cf", - "collateralAssets": [ - { - "name": "YT Token A", - "symbol": "YT-A", - "address": "0x7f9eEA491eE53045594ee4669327f0355aCd0e58" - }, - { - "name": "YT Token B", - "symbol": "YT-B", - "address": "0x20B94C5E5b7361552E0548161a58696aA6FeDBd4" - }, - { - "name": "YT Token C", - "symbol": "YT-C", - "address": "0x0EF308D70cf35460E26a3Eb42F3442Ff28cbE07C" - } - ], - "configTimestamp": "2026-01-12T06:00:25.232Z" - } -} - - - -[deployments-vault-system.json] - - -{ - "network": "bscTestnet", - "chainId": "97", - "deployer": "0xa013422A5918CD099C63c8CC35283EACa99a705d", - "timestamp": "2026-01-12T05:54:13.800Z", - "usdcAddress": "0x939cf46F7A4d05da2a37213E7379a8b04528F590", - "usdcPriceFeedAddress": "0x90c069C4538adAc136E051052E14c1cD799C41B7", - "defaultHardCap": "10000000000000000000000000", - "contracts": { - "YTAssetVault": { - "implementation": "0xB9a9c4d1a8f81a479040fcD30Bf3fca413c203bf" - }, - "YTAssetFactory": { - "proxy": "0x37B2CD7D94ba1400a6FEB34804a32EfD555bbfc8", - "implementation": "0x6137aAF5903A480888bC72c7b091909421bef45D" - } - }, - "vaults": [ - { - "name": "YT Token A", - "symbol": "YT-A", - "address": "0x7f9eEA491eE53045594ee4669327f0355aCd0e58", - "index": "0", - "manager": "0xa013422A5918CD099C63c8CC35283EACa99a705d", - "hardCap": "10000000000000000000000000", - "redemptionTime": 1799733295, - "ytPrice": "1000000000000000000000000000000" - }, - { - "name": "YT Token B", - "symbol": "YT-B", - "address": "0x20B94C5E5b7361552E0548161a58696aA6FeDBd4", - "index": "1", - "manager": "0xa013422A5918CD099C63c8CC35283EACa99a705d", - "hardCap": "10000000000000000000000000", - "redemptionTime": 1799733295, - "ytPrice": "1000000000000000000000000000000" - }, - { - "name": "YT Token C", - "symbol": "YT-C", - "address": "0x0EF308D70cf35460E26a3Eb42F3442Ff28cbE07C", - "index": "2", - "manager": "0xa013422A5918CD099C63c8CC35283EACa99a705d", - "hardCap": "10000000000000000000000000", - "redemptionTime": 1799733295, - "ytPrice": "1000000000000000000000000000000" - } - ], - "lastUpdate": "2026-01-12T05:55:05.405Z" -} - - - -[deployments-usdc-config.json] - - -{ - "network": "bscTestnet", - "chainId": "97", - "deployer": "0xa013422A5918CD099C63c8CC35283EACa99a705d", - "timestamp": "2026-01-12T05:52:56.820Z", - "contracts": { - "USDC": { - "address": "0x939cf46F7A4d05da2a37213E7379a8b04528F590", - "description": "USDC���ҵ�ַ���Ѵ��ڵĺ�Լ��" - }, - "ChainlinkUSDCPriceFeed": { - "address": "0x90c069C4538adAc136E051052E14c1cD799C41B7", - "description": "Chainlink USDC/USD �۸�Ԥ�Ի�", - "precision": "1e8" - } - }, - "notes": { - "bsc": "BSC������USDC��18λ����", - "arbSepolia": "Arbitrum Sepolia��USDC��6λ����" - } -} - - - -[deployments-whitelist-config.json] - - -{ - "timestamp": "2026-01-12T05:56:01.673Z", - "operator": "0xa013422A5918CD099C63c8CC35283EACa99a705d", - "whitelistedTokens": { - "ytTokens": [ - { - "name": "YT Token A", - "symbol": "YT-A", - "address": "0x7f9eEA491eE53045594ee4669327f0355aCd0e58", - "weight": 4000, - "maxUsdyAmount": "45000000000000000000000000", - "price": "1000000000000000000000000000000", - "isStable": false - }, - { - "name": "YT Token B", - "symbol": "YT-B", - "address": "0x20B94C5E5b7361552E0548161a58696aA6FeDBd4", - "weight": 3000, - "maxUsdyAmount": "35000000000000000000000000", - "price": "1000000000000000000000000000000", - "isStable": false - }, - { - "name": "YT Token C", - "symbol": "YT-C", - "address": "0x0EF308D70cf35460E26a3Eb42F3442Ff28cbE07C", - "weight": 2000, - "maxUsdyAmount": "25000000000000000000000000", - "price": "1000000000000000000000000000000", - "isStable": false - } - ], - "usdc": { - "name": "USDC", - "symbol": "USDC", - "address": "0x939cf46F7A4d05da2a37213E7379a8b04528F590", - "weight": 1000, - "maxUsdyAmount": "30000000000000", - "priceSource": "Chainlink (�Զ�)", - "isStable": true - } - }, - "totalWeight": "10000", - "poolComposition": "USDC/YT-A/YT-B/YT-C" -} - - - -[deployments-ytlp-config.json] - - - -{ - "network": "bscTestnet", - "chainId": "97", - "configurer": "0xa013422A5918CD099C63c8CC35283EACa99a705d", - "timestamp": "2026-01-12T05:55:25.677Z", - "configuration": { - "permissions": { - "usdyVaults": [ - "0xA6CD153c2fa0C2725605880668538660Ca21a790", - "0xb11824eAA659F8A4648711709dA60720d5Cdabd2" - ], - "ytlpMinters": [ - "0xb11824eAA659F8A4648711709dA60720d5Cdabd2" - ], - "ytlpPoolManager": "0xb11824eAA659F8A4648711709dA60720d5Cdabd2", - "vaultPoolManager": "0xb11824eAA659F8A4648711709dA60720d5Cdabd2", - "vaultSwappers": [ - "0x5af5A51F7702024E7387bba7497DC9965C00F16E" - ], - "poolManagerHandlers": [ - "0x5af5A51F7702024E7387bba7497DC9965C00F16E" - ], - "priceFeedKeepers": [ - "0xa013422A5918CD099C63c8CC35283EACa99a705d" - ], - "usdcPriceSource": "Chainlink (�Զ�)" - }, - "parameters": { - "dynamicFees": true, - "maxSwapSlippageBps": 1000, - "maxPriceChangeBps": 500 - } - } -} - - \ No newline at end of file diff --git a/ytLending-USDC借贷系统操作流程图.txt b/ytLending-USDC借贷系统操作流程图.txt deleted file mode 100644 index a12f113..0000000 --- a/ytLending-USDC借贷系统操作流程图.txt +++ /dev/null @@ -1,1841 +0,0 @@ -ytLending-USDC借贷系统操作流程图 -重要说明 -本系统使用 USDC 作为基础借贷代币(baseToken),YT 资产代币作为抵押品。USDC 价格通过 Chainlink 价格预言机实时获取,YT 代币价格从其 Vault 合约的 ytPrice() 方法读取。 -目录 -1.[存入 USDC(Supply)](#1-存入-usdcsupply) -2.[提取 USDC(Withdraw)](#2-提取-ugetBalancesdcwithdraw) -3.[存入抵押品(SupplyCollateral)](#3-存入抵押品supplycollateral) -4.[取出抵押品(WithdrawCollateral)](#4-取出抵押品withdrawcollateral) -5.[借款流程(Withdraw)](#5-借款流程Withdraw) -6.[清算流程(Absorb)](#6-清算流程absorb) -7.[购买清算抵押品(BuyCollateral)](#7-购买清算抵押品buycollateral) -8.[利息计提机制](#8-利息计提机制) -9.[储备金管理(WithdrawReserves)](#9-储备金管理withdrawreserves) - -1. 存入 USDC(Supply) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 用户 (Alice) │ -│ 持有: 10,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 授权 USDC 给 Lending - │ USDC.approve(lending, 10000e6) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ USDC 授权检查 │ -│ ✓ allowance[Alice][Lending] >= 10,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 调用 supply(10000e6) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Lending.supply() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function supply(uint256 amount) │ -│ • 非重入保护: nonReentrant │ -│ • 暂停检查: whenNotPaused │ -│ • 参数: 10,000 USDC (10000e6) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 计提利息 - │ accrueInterest() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 利息计提 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 计算自上次计提以来的时间: │ -│ timeElapsed = block.timestamp - lastAccrualTime │ -│ │ -│ 如果 timeElapsed > 0: │ -│ ① 计算当前利用率: │ -│ totalSupply = totalSupplyBase × supplyIndex / 1e18 │ -│ totalBorrow = totalBorrowBase × borrowIndex / 1e18 │ -│ utilization = totalBorrow / totalSupply │ -│ │ -│ ② 根据利用率和拐点计算供应利率和借款利率(每秒): │ -│ supplyRate = getSupplyRate(utilization, ...) │ -│ borrowRate = getBorrowRate(utilization, ...) │ -│ │ -│ ③ 更新利息累计因子: │ -│ supplyIndex += supplyIndex × supplyRate × timeElapsed / 1e18 │ -│ borrowIndex += borrowIndex × borrowRate × timeElapsed / 1e18 │ -│ │ -│ ④ 更新上次计提时间: │ -│ lastAccrualTime = block.timestamp │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 转入 USDC - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移 │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(baseToken).transferFrom( │ -│ Alice, // 用户地址 │ -│ address(this), // Lending 地址 │ -│ 10000e6 // 10,000 USDC │ -│ ) │ -│ │ -│ 结果: │ -│ • Alice USDC 余额: 10,000 → 0 │ -│ • Lending USDC 余额: +10,000 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 计算用户余额变化 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 余额计算 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 获取用户当前本金: │ -│ oldPrincipal = userBasic[Alice].principal // 假设为 0(新用户) │ -│ │ -│ 计算旧余额(含利息): │ -│ index = oldPrincipal >= 0 ? supplyIndex : borrowIndex │ -│ = supplyIndex (因为是0,属于存款) │ -│ oldBalance = oldPrincipal × index / 1e18 = 0 │ -│ │ -│ 计算新余额: │ -│ newBalance = oldBalance + 10000e6 = 10000e6 │ -│ │ -│ 确定新余额使用的索引: │ -│ newIndex = newBalance >= 0 ? supplyIndex : borrowIndex │ -│ = supplyIndex (正数,是存款) │ -│ │ -│ 转换为新本金: │ -│ newPrincipal = (newBalance × 1e18) / newIndex │ -│ = (10000e6 × 1e18) / supplyIndex │ -│ = 10000e6 (假设 supplyIndex = 1e18) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 计算状态变化量 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 计算 repayAmount 和 supplyAmount │ -│ ───────────────────────────────────────────────────────────────── │ -│ 调用: repayAndSupplyAmount(oldPrincipal=0, newPrincipal=10000e6) │ -│ │ -│ 判断逻辑: │ -│ • newPrincipal (10000e6) > oldPrincipal (0) ✓ │ -│ • newPrincipal >= 0 ✓ │ -│ • oldPrincipal >= 0 ✓ │ -│ → 两个都是正数(增加存款) │ -│ │ -│ 结果: │ -│ repayAmount = 0 │ -│ supplyAmount = newPrincipal - oldPrincipal = 10000e6 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 更新全局状态 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新全局余额 │ -│ ───────────────────────────────────────────────────────────────── │ -│ totalBorrowBase -= repayAmount (0) │ -│ totalSupplyBase += supplyAmount (10000e6) │ -│ │ -│ 更新前: │ -│ • totalSupplyBase: 0 │ -│ • totalBorrowBase: 0 │ -│ │ -│ 更新后: │ -│ • totalSupplyBase: 10000e6 │ -│ • totalBorrowBase: 0 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 更新用户本金 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新用户状态 │ -│ ───────────────────────────────────────────────────────────────── │ -│ userBasic[Alice].principal = newPrincipal = 10000e6 │ -│ │ -│ 用户状态: │ -│ • principal: 0 → 10000e6 (正数 = 存款本金) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 9. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit Supply(Alice, Alice, 10000e6) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 10. 存款完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 存款完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Alice 最终状态: │ -│ • USDC 余额: 0 │ -│ • Lending principal: 10000e6 (存款本金) │ -│ • 实际余额 (含利息): principal × supplyIndex / 1e18 │ -│ = 10000e6 (刚存入,尚未计息) │ -│ │ -│ 协议状态: │ -│ • totalSupplyBase: 10000e6 │ -│ • totalBorrowBase: 0 │ -│ • 实际总供应: 10000e6 (含利息) │ -│ • 实际总借款: 0 │ -│ • 利用率: 0% │ -│ │ -│ 收益说明: │ -│ 随着时间推移,supplyIndex 会增长 │ -│ Alice 的实际余额 = principal × supplyIndex / 1e18 会增加 │ -│ 这就是利息收益的体现 │ -└─────────────────────────────────────────────────────────────────────┘ - -2. 提取 USDC(Withdraw) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 用户 (Alice) │ -│ 当前状态: │ -│ • principal: 10000e6 │ -│ • supplyIndex: 1.1e18 (经过一段时间,利息累积了 10%) │ -│ • 实际余额: 10000e6 × 1.1e18 / 1e18 = 11000e6 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 withdraw(5000e6) - │ 提取 5,000 USDC - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Lending.withdraw() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function withdraw(uint256 amount) │ -│ • 非重入保护: nonReentrant │ -│ • 暂停检查: whenNotPaused │ -│ • 参数: 5,000 USDC (5000e6) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 计提利息 - │ accrueInterest() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 利息计提 │ -│ (过程同 supply,更新 supplyIndex 和 borrowIndex) │ -│ 假设更新后 supplyIndex = 1.1e18 (已包含最新利息) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 计算余额变化 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 余额计算 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 获取用户当前本金: │ -│ oldPrincipal = userBasic[Alice].principal = 10000e6 │ -│ │ -│ 计算旧余额(含利息): │ -│ index = oldPrincipal >= 0 ? supplyIndex : borrowIndex │ -│ = supplyIndex (正数,是存款) │ -│ oldBalance = oldPrincipal × index / 1e18 │ -│ = 10000e6 × 1.1e18 / 1e18 = 11000e6 │ -│ │ -│ 计算新余额(减去提取额): │ -│ newBalance = oldBalance - 5000e6 = 6000e6 │ -│ │ -│ 确定新余额使用的索引: │ -│ newIndex = newBalance >= 0 ? supplyIndex : borrowIndex │ -│ = supplyIndex (正数,仍是存款) │ -│ │ -│ 转换为新本金: │ -│ newPrincipal = (newBalance × 1e18) / newIndex │ -│ = (6000e6 × 1e18) / 1.1e18 │ -│ = 5454.54e6 (约) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 计算状态变化量 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 计算 withdrawAmount 和 borrowAmount │ -│ ───────────────────────────────────────────────────────────────── │ -│ 调用: withdrawAndBorrowAmount( │ -│ oldPrincipal=10000e6, │ -│ newPrincipal=5454.54e6 │ -│ ) │ -│ │ -│ 判断逻辑: │ -│ • newPrincipal (5454.54e6) < oldPrincipal (10000e6) ✓ │ -│ • newPrincipal >= 0 ✓ │ -│ → 还是正数(提取存款) │ -│ │ -│ 结果: │ -│ withdrawAmount = oldPrincipal - newPrincipal = 4545.46e6 │ -│ borrowAmount = 0 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 更新全局状态 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新全局余额 │ -│ ───────────────────────────────────────────────────────────────── │ -│ totalSupplyBase -= withdrawAmount (4545.46e6) │ -│ totalBorrowBase += borrowAmount (0) │ -│ │ -│ 更新后: │ -│ • totalSupplyBase: 10000e6 → 5454.54e6 │ -│ • totalBorrowBase: 0 (不变) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 更新用户本金 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新用户状态 │ -│ ───────────────────────────────────────────────────────────────── │ -│ userBasic[Alice].principal = newPrincipal = 5454.54e6 │ -│ │ -│ 用户状态: │ -│ • principal: 10000e6 → 5454.54e6 │ -│ • 实际余额: 5454.54e6 × 1.1e18 / 1e18 = 6000e6 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 转出 USDC - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移 │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(baseToken).safeTransfer( │ -│ Alice, // 用户地址 │ -│ 5000e6 // 5,000 USDC │ -│ ) │ -│ │ -│ 结果: │ -│ • Lending USDC 余额: -5000e6 │ -│ • Alice USDC 余额: +5000e6 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit Withdraw(Alice, Alice, 5000e6) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 9. 提取完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 提取完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Alice 最终状态: │ -│ • USDC 余额: +5000e6 │ -│ • Lending principal: 5454.54e6 │ -│ • 实际余额 (含利息): 6000e6 USDC │ -│ │ -│ 收益说明: │ -│ • 初始存入: 10000 USDC │ -│ • 利息收益: 1000 USDC (10%) │ -│ • 提取: 5000 USDC │ -│ • 剩余: 6000 USDC (包含利息) │ -│ │ -│ 注意: │ -│ 如果提取金额大于实际余额,会自动转为借款 │ -│ 例如:提取 12000 USDC,超过 11000 USDC 余额 │ -│ → principal 会变成负数(-1000),表示借款 1000 USDC │ -└─────────────────────────────────────────────────────────────────────┘ - -3. 存入抵押品(SupplyCollateral) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 用户 (Bob) │ -│ 持有: 1000 YT-A 代币 │ -│ YT-A 价格: $2000 (2000e30) │ -│ 价值: $2,000,000 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 授权 YT-A 给 Lending - │ YTA.approve(lending, 1000e18) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YT-A 授权检查 │ -│ ✓ allowance[Bob][Lending] >= 1000 YT-A │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 调用 supplyCollateral(YTA, 1000e18) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Lending.supplyCollateral() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function supplyCollateral(address asset, uint256 amount) │ -│ • 非重入保护: nonReentrant │ -│ • 暂停检查: whenNotPaused │ -│ • 参数: asset=YTA, amount=1000e18 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 验证资产配置 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 资产配置检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ AssetConfig memory config = assetConfigs[YTA] │ -│ │ -│ 检查: │ -│ • config.asset != address(0) ✓ (资产已配置) │ -│ │ -│ 配置示例: │ -│ • asset: YTA │ -│ • decimals: 18 │ -│ • borrowCollateralFactor: 0.7e18 (70% LTV) │ -│ • liquidateCollateralFactor: 0.75e18 (75% 清算阈值) │ -│ • liquidationFactor: 0.9e18 (清算时价值打 90 折) │ -│ • supplyCap: 10000e18 (最多可存入 10,000 YT-A) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 检查供应上限 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 供应上限检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ uint256 newTotal = userCollateral[Bob][YTA] + 1000e18 │ -│ = 0 + 1000e18 = 1000e18 │ -│ │ -│ if (newTotal > config.supplyCap) revert SupplyCapExceeded() │ -│ • 1000e18 <= 10000e18 ✓ 通过 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 转入 YT-A 代币 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移 │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(asset).transferFrom( │ -│ Bob, // 用户地址 │ -│ address(this), // Lending 地址 │ -│ 1000e18 // 1000 YT-A │ -│ ) │ -│ │ -│ 结果: │ -│ • Bob YT-A 余额: 1000 → 0 │ -│ • Lending YT-A 余额: +1000 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 更新用户抵押品余额 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新抵押品记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ userCollateral[Bob][YTA] += 1000e18 │ -│ │ -│ 更新后: │ -│ • userCollateral[Bob][YTA]: 0 → 1000e18 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit SupplyCollateral(Bob, Bob, YTA, 1000e18) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 存入完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 抵押品存入完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Bob 最终状态: │ -│ • YT-A 余额: 0 │ -│ • 抵押品: 1000 YT-A (在 Lending 中) │ -│ • 抵押品价值: $2,000,000 │ -│ │ -│ 借款能力计算: │ -│ • 抵押品价值: 1000 × $2000 = $2,000,000 │ -│ • borrowCollateralFactor: 70% │ -│ • 最大借款额: $2,000,000 × 70% = $1,400,000 │ -│ • 以 USDC 计算 (假设 USDC = $1): │ -│ 最大借款: 1,400,000 USDC │ -│ │ -│ 清算阈值: │ -│ • liquidateCollateralFactor: 75% │ -│ • 清算阈值: $2,000,000 × 75% = $1,500,000 │ -│ • 当债务超过 $1,500,000 时会被清算 │ -│ │ -│ 注意: │ -│ • 存入抵押品不计提利息(不影响债务) │ -│ • 抵押品价值根据 LendingPriceFeed.getPrice() 实时获取 │ -│ • YT-A 价格从 YTAssetVault.ytPrice() 获取 │ -│ • USDC 价格从 Chainlink 获取 │ -└─────────────────────────────────────────────────────────────────────┘ - -4. 取出抵押品(WithdrawCollateral) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 用户 (Bob) │ -│ 当前状态: │ -│ • 抵押品: 1000 YT-A │ -│ • 借款: 0 USDC (未借款) │ -│ 计划: 取出 500 YT-A │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 withdrawCollateral(YTA, 500e18) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Lending.withdrawCollateral() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function withdrawCollateral(address asset, uint256 amount) │ -│ • 非重入保护: nonReentrant │ -│ • 暂停检查: whenNotPaused │ -│ • 参数: asset=YTA, amount=500e18 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 计提利息 - │ accrueInterest() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 利息计提 │ -│ (过程同前,更新 supplyIndex 和 borrowIndex) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 检查余额 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 余额检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (userCollateral[Bob][YTA] < 500e18) revert InsufficientBalance()│ -│ • 1000e18 >= 500e18 ✓ 通过 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 更新抵押品余额 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新抵押品记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ userCollateral[Bob][YTA] -= 500e18 │ -│ │ -│ 更新后: │ -│ • userCollateral[Bob][YTA]: 1000e18 → 500e18 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 检查偿付能力(如果有债务) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 偿付能力检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ int104 principal = userBasic[Bob].principal // 假设为 0 │ -│ │ -│ if (principal < 0) { │ -│ if (!_isSolvent(Bob)) revert InsufficientCollateral() │ -│ } │ -│ │ -│ 本例中 principal = 0,无债务,跳过检查 ✓ │ -│ │ -│ 偿付能力检查逻辑 (_isSolvent): │ -│ ① 计算实际债务 (含利息): │ -│ balance = principal × borrowIndex / 1e18 │ -│ debt = -balance (转为正数) │ -│ │ -│ ② 计算债务价值 (USD): │ -│ basePrice = LendingPriceFeed.getPrice(USDC) │ -│ debtValue = (debt × basePrice) / 10^usdcDecimals │ -│ │ -│ ③ 计算抵押品借款能力: │ -│ borrowCapacity = _getCollateralValue(account) │ -│ • 遍历所有抵押品资产 │ -│ • 计算每个资产的价值 (USD) │ -│ • 乘以 borrowCollateralFactor │ -│ • 汇总所有抵押品的借款能力 │ -│ │ -│ ④ 比较: │ -│ return borrowCapacity >= debtValue │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 转出 YT-A 代币 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移 │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(asset).safeTransfer( │ -│ Bob, // 用户地址 │ -│ 500e18 // 500 YT-A │ -│ ) │ -│ │ -│ 结果: │ -│ • Lending YT-A 余额: -500e18 │ -│ • Bob YT-A 余额: +500e18 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit WithdrawCollateral(Bob, Bob, YTA, 500e18) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 取出完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 抵押品取出完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Bob 最终状态: │ -│ • YT-A 余额: +500e18 │ -│ • 抵押品: 500 YT-A (在 Lending 中) │ -│ • 剩余抵押品价值: $1,000,000 │ -│ │ -│ 借款能力变化: │ -│ • 之前: $2,000,000 × 70% = $1,400,000 │ -│ • 现在: $1,000,000 × 70% = $700,000 │ -│ │ -│ 注意: │ -│ 如果 Bob 有借款,取出抵押品后必须确保: │ -│ 借款能力 (borrowCapacity) >= 债务价值 (debtValue) │ -│ 否则交易会 revert │ -└─────────────────────────────────────────────────────────────────────┘ - -5. 借款流程(Withdraw,如果余额不足会自动借款) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 用户 (Bob) │ -│ 当前状态: │ -│ • 抵押品: 1000 YT-A (价值 $2,000,000) │ -│ • principal: 0 (无存款/借款) │ -│ • 借款能力: $2,000,000 × 70% = $1,400,000 USDC │ -│ 计划: 借款 1,000,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 withdraw(1000000e6) 进行借款 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Lending.withdraw() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function withdraw(uint256 amount) │ -│ • 非重入保护: nonReentrant │ -│ • 暂停检查: whenNotPaused │ -│ • 参数: 1,000,000 USDC (1000000e6) │ -│ • 注: withdraw 会自动处理借款逻辑 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 计提利息 - │ accrueInterest() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 利息计提 │ -│ 更新 supplyIndex 和 borrowIndex 到最新状态 │ -│ 假设: borrowIndex = 1e18 (初始状态) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 计算余额变化 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 余额计算 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 获取用户当前本金: │ -│ oldPrincipal = userBasic[Bob].principal = 0 │ -│ │ -│ 计算旧余额(含利息): │ -│ index = oldPrincipal >= 0 ? supplyIndex : borrowIndex │ -│ = supplyIndex (0 视为存款) │ -│ oldBalance = 0 × supplyIndex / 1e18 = 0 │ -│ │ -│ 计算新余额(减去借款额): │ -│ newBalance = oldBalance - 1000000e6 = -1000000e6 │ -│ │ -│ 检查最小借款额: │ -│ if (newBalance < 0 && -newBalance < baseBorrowMin) │ -│ revert BorrowTooSmall() │ -│ • baseBorrowMin = 100e6 (最小借款 100 USDC) │ -│ • 1000000e6 >= 100e6 ✓ 通过 │ -│ │ -│ 确定新余额使用的索引: │ -│ newIndex = newBalance >= 0 ? supplyIndex : borrowIndex │ -│ = borrowIndex (负数,是借款) │ -│ │ -│ 转换为新本金: │ -│ newPrincipal = (newBalance × 1e18) / newIndex │ -│ = (-1000000e6 × 1e18) / 1e18 │ -│ = -1000000e6 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 计算状态变化量 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 计算 withdrawAmount 和 borrowAmount │ -│ ───────────────────────────────────────────────────────────────── │ -│ 调用: withdrawAndBorrowAmount( │ -│ oldPrincipal=0, │ -│ newPrincipal=-1000000e6 │ -│ ) │ -│ │ -│ 判断逻辑: │ -│ • newPrincipal (-1000000e6) < oldPrincipal (0) ✓ │ -│ • newPrincipal < 0 ✓ │ -│ • oldPrincipal >= 0 ✓ │ -│ → 从正数(或0)变负数(提取所有存款并借款) │ -│ │ -│ 结果: │ -│ withdrawAmount = oldPrincipal = 0 │ -│ borrowAmount = -newPrincipal = 1000000e6 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 更新全局状态 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新全局余额 │ -│ ───────────────────────────────────────────────────────────────── │ -│ totalSupplyBase -= withdrawAmount (0) │ -│ totalBorrowBase += borrowAmount (1000000e6) │ -│ │ -│ 更新后: │ -│ • totalSupplyBase: 不变 │ -│ • totalBorrowBase: 0 → 1000000e6 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 更新用户本金 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新用户状态 │ -│ ───────────────────────────────────────────────────────────────── │ -│ userBasic[Bob].principal = newPrincipal = -1000000e6 │ -│ │ -│ 用户状态: │ -│ • principal: 0 → -1000000e6 (负数 = 借款本金) │ -│ • 实际债务: -1000000e6 × borrowIndex / 1e18 = -1000000e6 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 检查抵押品是否充足 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 偿付能力检查 (_isSolvent) │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① 计算债务价值 (USD): │ -│ debt = 1000000e6 │ -│ basePrice = LendingPriceFeed.getPrice(USDC) │ -│ = 1e30 (假设 USDC = $1.00) │ -│ baseDecimals = 6 │ -│ debtValue = (1000000e6 × 1e30) / 10^6 │ -│ = 1000000e30 ($1,000,000 USD) │ -│ │ -│ ② 计算抵押品借款能力 (_getCollateralValue): │ -│ 遍历所有抵押品: │ -│ • YT-A: 1000e18 │ -│ - price = LendingPriceFeed.getPrice(YTA) │ -│ = IYTAssetVault(YTA).ytPrice() │ -│ = 2000e30 ($2000) │ -│ - decimals = 18 │ -│ - value = (1000e18 × 2000e30) / 10^18 │ -│ = 2000000e30 ($2,000,000 USD) │ -│ - borrowCollateralFactor = 0.7e18 (70%) │ -│ - borrowCapacity = 2000000e30 × 0.7e18 / 1e18 │ -│ = 1400000e30 ($1,400,000 USD) │ -│ │ -│ ③ 比较: │ -│ borrowCapacity (1400000e30) >= debtValue (1000000e30) ✓ │ -│ → 抵押品充足,可以借款 │ -│ │ -│ 如果不充足,会 revert InsufficientCollateral() │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 转出 USDC - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移 │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(baseToken).safeTransfer( │ -│ Bob, // 用户地址 │ -│ 1000000e6 // 1,000,000 USDC │ -│ ) │ -│ │ -│ 结果: │ -│ • Lending USDC 余额: -1000000e6 │ -│ • Bob USDC 余额: +1000000e6 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 9. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit Withdraw(Bob, Bob, 1000000e6) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 10. 借款完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 借款完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Bob 最终状态: │ -│ • USDC 余额: +1,000,000 USDC │ -│ • Lending principal: -1000000e6 (负数表示借款) │ -│ • 实际债务: -1000000e6 × borrowIndex / 1e18 │ -│ • 抵押品: 1000 YT-A │ -│ │ -│ 借款状态: │ -│ • 借款金额: 1,000,000 USDC │ -│ • 借款价值: $1,000,000 │ -│ • 抵押品价值: $2,000,000 │ -│ • LTV (Loan-to-Value): 50% │ -│ • 借款能力使用率: 1,000,000 / 1,400,000 = 71.4% │ -│ • 距离清算阈值: $1,500,000 - $1,000,000 = $500,000 │ -│ │ -│ 利息累积: │ -│ 随着时间推移,borrowIndex 会增长 │ -│ Bob 的实际债务 = principal × borrowIndex / 1e18 会增加 │ -│ 这就是借款利息的体现 │ -│ │ -│ 重要风险提示: │ -│ ⚠️ 如果 YT-A 价格下跌,抵押品价值减少 │ -│ ⚠️ 如果债务增长(利息累积),债务价值增加 │ -│ ⚠️ 当债务价值 > 清算阈值($1,500,000)时,会被清算 │ -└─────────────────────────────────────────────────────────────────────┘ - - -6. 清算流程(Absorb) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 场景设定 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Bob 的状态(价格变化后): │ -│ • 抵押品: 1000 YT-A │ -│ • YT-A 价格: $2000 → $1400 (下跌 30%) │ -│ • 抵押品价值: $1,400,000 │ -│ • 借款本金: -1000000e6 │ -│ • borrowIndex: 1.1e18 (利息累积 10%) │ -│ • 实际债务: 1000000e6 × 1.1 = 1,100,000 USDC │ -│ • 债务价值: $1,100,000 │ -│ │ -│ 清算判断: │ -│ • liquidateCollateralFactor: 75% │ -│ • 清算阈值: $1,400,000 × 75% = $1,050,000 │ -│ • 债务价值 ($1,100,000) > 清算阈值 ($1,050,000) ✓ │ -│ → 可以被清算 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 清算人调用 absorb(Bob) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Lending.absorb() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function absorb(address borrower) │ -│ • 非重入保护: nonReentrant │ -│ • 暂停检查: whenNotPaused │ -│ • 参数: borrower=Bob │ -│ • 调用者: 任何人都可以(清算人/机器人) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 计提利息 - │ accrueInterest() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 利息计提 │ -│ 更新到最新状态,确保债务和利息都是最新的 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 检查是否可清算 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 清算资格检查 (isLiquidatable) │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (!isLiquidatable(Bob)) revert NotLiquidatable() │ -│ │ -│ isLiquidatable 逻辑: │ -│ ① 检查是否有债务: │ -│ principal = -1000000e6 < 0 ✓ │ -│ │ -│ ② 计算实际债务 (含利息): │ -│ balance = principal × borrowIndex / 1e18 │ -│ = -1000000e6 × 1.1e18 / 1e18 = -1100000e6 │ -│ debt = -balance = 1100000e6 │ -│ │ -│ ③ 计算债务价值 (USD): │ -│ basePrice = LendingPriceFeed.getPrice(USDC) = 1e30 │ -│ debtValue = (1100000e6 × 1e30) / 10^6 = 1100000e30 │ -│ │ -│ ④ 计算抵押品清算阈值价值: │ -│ YT-A: 1000e18 │ -│ price = 1400e30 ($1400) │ -│ value = (1000e18 × 1400e30) / 10^18 = 1400000e30 │ -│ liquidateCollateralFactor = 0.75e18 (75%) │ -│ collateralValue = 1400000e30 × 0.75e18 / 1e18 │ -│ = 1050000e30 ($1,050,000) │ -│ │ -│ ⑤ 比较: │ -│ debtValue (1100000e30) > collateralValue (1050000e30) ✓ │ -│ → 可以清算 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 计算抵押品总价值 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 计算抵押品价值(清算折扣) │ -│ ───────────────────────────────────────────────────────────────── │ -│ 遍历所有抵押品资产: │ -│ │ -│ YT-A: │ -│ • collateralAmount = 1000e18 │ -│ • assetPrice = 1400e30 │ -│ • liquidationFactor = 0.9e18 (清算时价值打 90 折) │ -│ • decimals = 18 │ -│ │ -│ ① 计算原始价值 (USD, 用于事件): │ -│ collateralValueUSD = (1000e18 × 1400e30) / 10^18 │ -│ = 1400000e30 │ -│ │ -│ ② 计算折扣后价值: │ -│ discountedValue = (1000e18 × 1400e30 × 0.9e18) / (10^18 × 1e18) │ -│ = 1260000e30 ($1,260,000) │ -│ │ -│ ③ 将抵押品转移到清算库存: │ -│ userCollateral[Bob][YTA] = 0 │ -│ collateralReserves[YTA] += 1000e18 │ -│ │ -│ ④ 触发抵押品吸收事件: │ -│ emit AbsorbCollateral( │ -│ msg.sender, // 清算人 │ -│ Bob, // 借款人 │ -│ YTA, // 资产 │ -│ 1000e18, // 数量 │ -│ 1400000e30 // 原始价值 USD │ -│ ) │ -│ │ -│ totalCollateralValue = 1260000e30 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 将抵押品价值转换为 baseToken - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 转换为 USDC 数量 │ -│ ───────────────────────────────────────────────────────────────── │ -│ basePrice = 1e30 (USDC = $1.00) │ -│ baseScale = 10^6 (USDC 6位小数) │ -│ │ -│ collateralInBase = (totalCollateralValue × baseScale) / basePrice │ -│ = (1260000e30 × 10^6) / 1e30 │ -│ = 1260000e6 (1,260,000 USDC) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 计算新余额 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 计算清算后余额 │ -│ ───────────────────────────────────────────────────────────────── │ -│ oldBalance = -1100000e6 (债务) │ -│ collateralInBase = 1260000e6 (抵押品价值) │ -│ │ -│ newBalance = oldBalance + collateralInBase │ -│ = -1100000e6 + 1260000e6 │ -│ = 160000e6 (正数!抵押品价值 > 债务) │ -│ │ -│ ⚠️ 注意:如果 newBalance < 0,说明坏账,会强制归零 │ -│ if (newBalance < 0) { │ -│ newBalance = 0 // 坏账由协议储备金承担 │ -│ } │ -│ │ -│ 本例中 newBalance > 0,无坏账 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 转换为新本金 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 计算新本金 │ -│ ───────────────────────────────────────────────────────────────── │ -│ newPrincipal = (newBalance × 1e18) / supplyIndex │ -│ = (160000e6 × 1e18) / 1e18 │ -│ = 160000e6 │ -│ │ -│ 余额从负数变为正数: │ -│ • 旧本金: -1000000e6 (借款) │ -│ • 新本金: 160000e6 (存款!) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 更新用户本金 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新用户状态 │ -│ ───────────────────────────────────────────────────────────────── │ -│ userBasic[Bob].principal = newPrincipal = 160000e6 │ -│ │ -│ 用户状态变化: │ -│ • principal: -1000000e6 → 160000e6 │ -│ • 从借款人变为存款人! │ -│ • 抵押品: 1000 YT-A → 0 (全部被清算) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 9. 计算并更新全局状态 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新全局余额 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 调用: repayAndSupplyAmount( │ -│ oldPrincipal=-1000000e6, │ -│ newPrincipal=160000e6 │ -│ ) │ -│ │ -│ 判断逻辑: │ -│ • newPrincipal (160000e6) > oldPrincipal (-1000000e6) ✓ │ -│ • newPrincipal > 0 ✓ │ -│ • oldPrincipal < 0 ✓ │ -│ → 从负数变正数(偿还所有债务并存款) │ -│ │ -│ 结果: │ -│ repayAmount = -oldPrincipal = 1000000e6 │ -│ supplyAmount = newPrincipal = 160000e6 │ -│ │ -│ 更新: │ -│ totalSupplyBase += supplyAmount (160000e6) │ -│ totalBorrowBase -= repayAmount (1000000e6) │ -│ │ -│ ⚠️ 注意:储备金通过减少 totalBorrowBase 和增加 totalSupplyBase │ -│ 来承担坏账(如果有) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 10. 计算坏账(如果有) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 坏账计算 │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (collateralInBase < -oldBalance) { │ -│ // 抵押品不足以覆盖债务,差额由协议储备金承担 │ -│ basePaidOut = -oldBalance - collateralInBase │ -│ } else { │ -│ // 抵押品足够,无坏账 │ -│ basePaidOut = 0 │ -│ } │ -│ │ -│ 本例中: │ -│ • -oldBalance = 1100000e6 │ -│ • collateralInBase = 1260000e6 │ -│ • 1260000e6 >= 1100000e6 ✓ 无坏账 │ -│ • basePaidOut = 0 │ -│ │ -│ 坏账价值: │ -│ valueOfBasePaidOut = (basePaidOut × basePrice) / baseScale = 0 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 11. 触发债务吸收事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit AbsorbDebt( │ -│ msg.sender, // 清算人 │ -│ Bob, // 借款人 │ -│ 0, // basePaidOut (坏账) │ -│ 0 // valueOfBasePaidOut (坏账价值) │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 12. 清算完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 清算完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Bob 最终状态: │ -│ • principal: 160000e6 (正数 = 存款) │ -│ • 实际余额: 160000 USDC │ -│ • 抵押品: 0 (全部被清算) │ -│ • 债务: 0 (已清偿) │ -│ │ -│ 协议状态: │ -│ • collateralReserves[YTA]: +1000e18 │ -│ • 清算的抵押品进入协议库存,可以被购买 │ -│ │ -│ 清算流程总结: │ -│ ① 没收所有抵押品 (1000 YT-A) │ -│ ② 按清算折扣 (90%) 计算抵押品价值 ($1,260,000) │ -│ ③ 用于偿还债务 ($1,100,000) │ -│ ④ 剩余价值 ($160,000) 转为用户存款 │ -│ ⑤ 抵押品进入协议库存,等待被购买 │ -│ │ -│ 清算收益: │ -│ • Bob 损失: 1000 YT-A (价值 $1,400,000) │ -│ • Bob 保留: 160,000 USDC (价值 $160,000) │ -│ • 净损失: $1,400,000 - $160,000 = $1,240,000 │ -│ │ -│ 清算人收益: │ -│ • 清算人调用 absorb() 不直接获得收益 │ -│ • 但可以稍后通过 buyCollateral() 折价购买抵押品获利 │ -│ │ -│ 坏账场景示例: │ -│ 如果 YT-A 价格跌到 $1000: │ -│ • 抵押品价值: 1000 × $1000 × 90% = $900,000 │ -│ • 债务价值: $1,100,000 │ -│ • 差额: $200,000 (坏账,由协议储备金承担) │ -│ • Bob principal: 0 (债务被清零) │ -│ • basePaidOut: 200000 USDC │ -└─────────────────────────────────────────────────────────────────────┘ - -7. 购买清算抵押品(BuyCollateral) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 清算人 (Liquidator) │ -│ 当前状态: │ -│ • 持有: 足够的 USDC │ -│ • 目标: 购买清算后的 YT-A 抵押品 │ -│ │ -│ 协议状态: │ -│ • collateralReserves[YTA]: 1000e18 (可购买) │ -│ • 协议储备金不足 (< targetReserves) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 查询可购买数量 - │ quoteCollateral(YTA, 100000e6) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Lending.quoteCollateral() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function quoteCollateral(address asset, uint256 baseAmount) │ -│ │ -│ 计算逻辑(支付 100,000 USDC 可购买多少 YT-A): │ -│ │ -│ ① 获取价格: │ -│ assetPrice = LendingPriceFeed.getPrice(YTA) = 1400e30 │ -│ basePrice = LendingPriceFeed.getPrice(USDC) = 1e30 │ -│ │ -│ ② 获取配置: │ -│ liquidationFactor = 0.9e18 (90%) │ -│ storeFrontPriceFactor = 0.95e18 (额外 5% 折扣) │ -│ │ -│ ③ 计算折扣因子: │ -│ discountFactor = storeFrontPriceFactor × (1 - liquidationFactor) │ -│ = 0.95e18 × (1e18 - 0.9e18) / 1e18 │ -│ = 0.95e18 × 0.1e18 / 1e18 │ -│ = 0.095e18 (9.5%) │ -│ │ -│ ④ 计算有效资产价格: │ -│ effectiveAssetPrice = assetPrice × (1 - discountFactor) │ -│ = 1400e30 × (1e18 - 0.095e18) / 1e18 │ -│ = 1400e30 × 0.905e18 / 1e18 │ -│ = 1267e30 ($1267) │ -│ │ -│ ⑤ 计算可购买数量: │ -│ baseScale = 10^6 (USDC) │ -│ assetScale = 10^18 (YT-A) │ -│ │ -│ 由于精度相同简化: │ -│ result = (baseAmount × basePrice) / effectiveAssetPrice │ -│ = (100000e6 × 1e30) / 1267e30 │ -│ = 78.93e6 × 10^12 │ -│ = 78.93e18 (78.93 YT-A) │ -│ │ -│ 返回: 78.93e18 YT-A │ -└─────────────────────────────────────────────────────────────────────┘ - │ - │ 2. 调用 buyCollateral() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Lending.buyCollateral() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function buyCollateral( │ -│ address asset, // YTA │ -│ uint256 minAmount, // 75e18 (最少要买 75 YT-A,滑点保护) │ -│ uint256 baseAmount, // 100000e6 (支付 100,000 USDC) │ -│ address recipient // Liquidator (接收地址) │ -│ ) │ -│ • 非重入保护: nonReentrant │ -│ • 暂停检查: whenNotPaused │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 检查库存 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 库存检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (collateralReserves[YTA] == 0) revert InsufficientBalance() │ -│ • collateralReserves[YTA] = 1000e18 ✓ 有库存 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 检查储备金状态 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 储备金检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ int256 currentReserves = getReserves() │ -│ │ -│ getReserves() 计算: │ -│ ① 计算最新索引 (含未计提的利息): │ -│ (newSupplyIndex, newBorrowIndex) = accruedInterestIndices(...) │ -│ │ -│ ② 计算实际总供应和总借款: │ -│ balance = IERC20(USDC).balanceOf(address(this)) │ -│ totalSupply = totalSupplyBase × newSupplyIndex / 1e18 │ -│ totalBorrow = totalBorrowBase × newBorrowIndex / 1e18 │ -│ │ -│ ③ 计算储备金: │ -│ reserves = balance - totalSupply + totalBorrow │ -│ │ -│ 假设 reserves = 400000e6 (< targetReserves = 5000000e6) │ -│ │ -│ if (reserves >= 0 && reserves >= targetReserves) { │ -│ revert NotForSale() // 储备金充足,无需出售 │ -│ } │ -│ • 400000e6 < 5000000e6 ✓ 储备金不足,可以出售 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 计算可购买数量 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 计算购买数量 │ -│ ───────────────────────────────────────────────────────────────── │ -│ collateralAmount = quoteCollateral(YTA, 100000e6) = 78.93e18 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 验证数量 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 数量验证 │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① 滑点保护: │ -│ if (collateralAmount < minAmount) revert InsufficientBalance() │ -│ • 78.93e18 >= 75e18 ✓ 通过 │ -│ │ -│ ② 库存检查: │ -│ if (collateralAmount > collateralReserves[YTA]) │ -│ revert InsufficientBalance() │ -│ • 78.93e18 <= 1000e18 ✓ 通过 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 收取支付 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 收取 USDC │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(baseToken).transferFrom( │ -│ msg.sender, // 清算人 │ -│ address(this), // Lending 地址 │ -│ 100000e6 // 100,000 USDC │ -│ ) │ -│ │ -│ 结果: │ -│ • 清算人 USDC 余额: -100,000 │ -│ • Lending USDC 余额: +100,000 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 抵押品出库 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新库存 │ -│ ───────────────────────────────────────────────────────────────── │ -│ collateralReserves[YTA] -= collateralAmount │ -│ │ -│ 更新: │ -│ • collateralReserves[YTA]: 1000e18 → 921.07e18 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 9. 转出抵押品 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 转账抵押品 │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(asset).safeTransfer( │ -│ recipient, // Liquidator │ -│ 78.93e18 // 78.93 YT-A │ -│ ) │ -│ │ -│ 结果: │ -│ • Lending YT-A 余额: -78.93 │ -│ • 清算人 YT-A 余额: +78.93 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 10. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit BuyCollateral( │ -│ msg.sender, // 清算人 │ -│ YTA, // 资产 │ -│ 100000e6, // 支付的 USDC │ -│ 78.93e18 // 购买的 YT-A │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 11. 购买完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 购买完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 清算人收益分析: │ -│ • 支付: 100,000 USDC │ -│ • 获得: 78.93 YT-A │ -│ • 市场价: 78.93 × $1400 = $110,502 │ -│ • 购买价: $100,000 │ -│ • 折扣: 9.5% (storeFrontPriceFactor) │ -│ • 利润: $10,502 (10.5%) │ -│ │ -│ 协议收益: │ -│ • 收到 100,000 USDC,增加储备金 │ -│ • 储备金: 400,000 → 500,000 USDC │ -│ • 距离目标: 5,000,000 - 500,000 = 4,500,000 USDC │ -│ │ -│ 折扣机制说明: │ -│ • liquidationFactor (90%): 清算时抵押品打 9 折 │ -│ • storeFrontPriceFactor (95%): 在 liquidationFactor 基础上额外打折 │ -│ • 总折扣 = 1 - (1 - 0.1) × 0.95 = 1 - 0.905 = 9.5% │ -│ • 这个折扣激励清算人购买抵押品,补充协议储备金 │ -│ │ -│ 限制条件: │ -│ • 只有当协议储备金 < targetReserves 时才能购买 │ -│ • 储备金充足时会 revert NotForSale() │ -│ • 这确保协议不会过度出售抵押品 │ -│ │ -│ 注意: │ -│ 收入会自动体现在 getReserves() 中 │ -│ 因为 balance 增加了,而 totalSupply 和 totalBorrow 不变 │ -└─────────────────────────────────────────────────────────────────────┘ - -8. 利息计提机制 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 利息计提触发时机 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 每次以下操作时都会自动调用 accrueInterest(): │ -│ • supply() - 存入 USDC │ -│ • withdraw() - 提取 USDC / 借款 │ -│ • withdrawCollateral() - 取出抵押品 │ -│ • absorb() - 清算 │ -│ • buyCollateral() - 购买清算抵押品 │ -│ │ -│ 作用: │ -│ 确保所有操作使用最新的利息累计因子,实现公平的利息分配 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 检查是否需要计提 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 时间检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ timeElapsed = block.timestamp - lastAccrualTime │ -│ │ -│ if (timeElapsed == 0) { │ -│ return // 同一区块内已计提过,直接返回 │ -│ } │ -│ │ -│ 例如: │ -│ • lastAccrualTime: 1704067200 (2024-01-01 00:00:00) │ -│ • block.timestamp: 1704153600 (2024-01-02 00:00:00) │ -│ • timeElapsed: 86400 秒 (1 天) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 计算当前利用率 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 利用率计算 │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① 计算实际总供应和总借款(使用当前索引): │ -│ totalSupply = totalSupplyBase × supplyIndex / 1e18 │ -│ totalBorrow = totalBorrowBase × borrowIndex / 1e18 │ -│ │ -│ ② 计算利用率: │ -│ utilization = totalBorrow / totalSupply │ -│ = (totalBorrow × 1e18) / totalSupply │ -│ │ -│ 示例数据: │ -│ • totalSupplyBase: 10000000e6 (1000万 USDC 本金) │ -│ • supplyIndex: 1.05e18 │ -│ • totalSupply: 10000000e6 × 1.05e18 / 1e18 = 10500000e6 │ -│ │ -│ • totalBorrowBase: 7000000e6 (700万 USDC 本金) │ -│ • borrowIndex: 1.15e18 │ -│ • totalBorrow: 7000000e6 × 1.15e18 / 1e18 = 8050000e6 │ -│ │ -│ • utilization = (8050000e6 × 1e18) / 10500000e6 │ -│ = 0.7666...e18 (约 76.67%) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 计算供应利率(每秒) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 供应利率计算 (getSupplyRate) │ -│ ───────────────────────────────────────────────────────────────── │ -│ 利率模型(双斜率模型): │ -│ │ -│ if (utilization <= supplyKink) { │ -│ // 拐点前:线性增长 │ -│ rate = supplyPerSecondInterestRateBase + │ -│ (utilization × supplyPerSecondInterestRateSlopeLow) / 1e18│ -│ } else { │ -│ // 拐点后:陡峭增长 │ -│ excessUtil = utilization - supplyKink │ -│ rate = supplyPerSecondInterestRateBase + │ -│ supplyPerSecondInterestRateSlopeLow + │ -│ (excessUtil × supplyPerSecondInterestRateSlopeHigh) / 1e18│ -│ } │ -│ │ -│ 配置示例(年化利率转换为每秒利率): │ -│ • supplyKink: 0.8e18 (80%) │ -│ • supplyPerYearInterestRateBase: 0.02e18 (2% APY) │ -│ → supplyPerSecondInterestRateBase: 634e9 (2%/31536000) │ -│ • supplyPerYearInterestRateSlopeLow: 0.1e18 (10% APY) │ -│ → supplyPerSecondInterestRateSlopeLow: 3170e9 │ -│ • supplyPerYearInterestRateSlopeHigh: 2e18 (200% APY) │ -│ → supplyPerSecondInterestRateSlopeHigh: 63419e9 │ -│ │ -│ 本例计算(utilization = 76.67% < 80%): │ -│ rate = 634e9 + (0.7667e18 × 3170e9) / 1e18 │ -│ = 634e9 + 2430e9 │ -│ = 3064e9 (约 9.67% APY) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 计算借款利率(每秒) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 借款利率计算 (getBorrowRate) │ -│ ───────────────────────────────────────────────────────────────── │ -│ 利率模型(双斜率模型,同供应利率): │ -│ │ -│ 配置示例: │ -│ • borrowKink: 0.8e18 (80%) │ -│ • borrowPerYearInterestRateBase: 0.03e18 (3% APY) │ -│ → borrowPerSecondInterestRateBase: 951e9 │ -│ • borrowPerYearInterestRateSlopeLow: 0.15e18 (15% APY) │ -│ → borrowPerSecondInterestRateSlopeLow: 4756e9 │ -│ • borrowPerYearInterestRateSlopeHigh: 3e18 (300% APY) │ -│ → borrowPerSecondInterestRateSlopeHigh: 95129e9 │ -│ │ -│ 本例计算(utilization = 76.67% < 80%): │ -│ rate = 951e9 + (0.7667e18 × 4756e9) / 1e18 │ -│ = 951e9 + 3646e9 │ -│ = 4597e9 (约 14.5% APY) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 更新供应索引 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 供应索引更新 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 计算利息累积: │ -│ interestAccrued = (supplyIndex × supplyRate × timeElapsed) / 1e18 │ -│ = (1.05e18 × 3064e9 × 86400) / 1e18 │ -│ = 1.05e18 × 264729600e9 / 1e18 │ -│ = 0.00027796608e18 (约 0.02780%) │ -│ │ -│ 更新索引: │ -│ newSupplyIndex = supplyIndex + interestAccrued │ -│ = 1.05e18 + 0.00027796608e18 │ -│ = 1.05027796608e18 │ -│ │ -│ supplyIndex = newSupplyIndex │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 更新借款索引 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 借款索引更新 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 计算利息累积: │ -│ interestAccrued = (borrowIndex × borrowRate × timeElapsed) / 1e18 │ -│ = (1.15e18 × 4597e9 × 86400) / 1e18 │ -│ = 1.15e18 × 397180800e9 / 1e18 │ -│ = 0.00045675792e18 (约 0.04568%) │ -│ │ -│ 更新索引: │ -│ newBorrowIndex = borrowIndex + interestAccrued │ -│ = 1.15e18 + 0.00045675792e18 │ -│ = 1.15045675792e18 │ -│ │ -│ borrowIndex = newBorrowIndex │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 更新计提时间 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新时间戳 │ -│ ───────────────────────────────────────────────────────────────── │ -│ lastAccrualTime = block.timestamp │ -│ • 1704067200 → 1704153600 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 利息计提完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 利息计提效果 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 用户视角(Alice - 存款人): │ -│ • 存款本金: 10000e6 (不变) │ -│ • 旧余额: 10000e6 × 1.05e18 / 1e18 = 10500e6 │ -│ • 新余额: 10000e6 × 1.05027796608e18 / 1e18 = 10502.7796608e6 │ -│ • 利息收益: 2.7796608 USDC (1天) │ -│ │ -│ 用户视角(Bob - 借款人): │ -│ • 借款本金: -7000000e6 (不变) │ -│ • 旧债务: 7000000e6 × 1.15e18 / 1e18 = 8050000e6 │ -│ • 新债务: 7000000e6 × 1.15045675792e18 / 1e18 = 8053197.30544e6 │ -│ • 利息成本: 3197.30544 USDC (1天) │ -│ │ -│ 协议储备金增长: │ -│ 储备金 = 合约 USDC 余额 - 总供应 + 总借款 │ -│ • 借款利息 > 供应利息,差额进入协议储备金 │ -│ • 本例储备金增长: 3197.30544 - 2.7796608 = 3194.52578 USDC │ -│ │ -│ 关键概念: │ -│ • 本金(principal):用户的基础金额,不含利息 │ -│ • 索引(index):利息累计因子,初始为 1e18,随时间增长 │ -│ • 实际余额(balance):本金 × 索引 / 1e18,含利息 │ -│ • 利息:通过索引的增长自动体现,无需手动分配 │ -│ │ -│ 利率曲线特点: │ -│ ① 低利用率(< kink): 缓慢增长,鼓励借款 │ -│ ② 高利用率(> kink): 急剧增长,激励还款和存款 │ -│ ③ 借款利率 > 供应利率: 差额为协议收益 │ -│ ④ 动态调整: 每次操作都重新计算,实时响应市场状况 │ -└─────────────────────────────────────────────────────────────────────┘ - -9. 储备金管理(WithdrawReserves) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 协议管理员 (Owner) │ -│ 当前状态: │ -│ • 协议储备金: 6,000,000 USDC │ -│ • 目标储备金: 5,000,000 USDC │ -│ • 可提取: 1,000,000 USDC (超出目标的部分) │ -│ 计划: 提取 500,000 USDC 到指定地址 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 withdrawReserves(recipient, 500000e6) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Lending.withdrawReserves() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function withdrawReserves(address to, uint256 amount) │ -│ • 权限检查: onlyOwner │ -│ • 非重入保护: nonReentrant │ -│ • 参数: to=recipient, amount=500000e6 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 计提利息 - │ accrueInterest() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 利息计提 │ -│ 更新到最新状态,确保储备金计算准确 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 计算当前储备金 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 储备金计算 (getReserves) │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① 获取合约 USDC 余额: │ -│ balance = IERC20(USDC).balanceOf(address(this)) │ -│ │ -│ ② 计算实际总供应(含利息): │ -│ totalSupply = totalSupplyBase × supplyIndex / 1e18 │ -│ │ -│ ③ 计算实际总借款(含利息): │ -│ totalBorrow = totalBorrowBase × borrowIndex / 1e18 │ -│ │ -│ ④ 计算储备金: │ -│ reserves = balance - totalSupply + totalBorrow │ -│ = 6000000e6 (假设) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 检查可提取金额 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 可提取检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (reserves < 0) revert InsufficientBalance() │ -│ • reserves = 6000000e6 >= 0 ✓ │ -│ │ -│ availableReserves = reserves - targetReserves │ -│ = 6000000e6 - 5000000e6 │ -│ = 1000000e6 (可提取 100万) │ -│ │ -│ if (availableReserves < 0) revert InsufficientBalance() │ -│ • 1000000e6 >= 0 ✓ │ -│ │ -│ if (amount > availableReserves) revert InsufficientBalance() │ -│ • 500000e6 <= 1000000e6 ✓ 通过 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 转出储备金 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移 │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(baseToken).safeTransfer( │ -│ to, // 接收地址 │ -│ 500000e6 // 500,000 USDC │ -│ ) │ -│ │ -│ 结果: │ -│ • Lending USDC 余额: -500000e6 │ -│ • 接收地址 USDC 余额: +500000e6 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit WithdrawReserves(to, 500000e6) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 提取完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 提取完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 协议状态: │ -│ • 储备金: 6,000,000 → 5,500,000 USDC │ -│ • 目标储备金: 5,000,000 USDC │ -│ • 剩余可提取: 500,000 USDC │ -│ │ -│ 储备金来源: │ -│ • 借款利息 > 供应利息的差额 │ -│ • 清算时的抵押品折扣收益(如有) │ -│ • 清算后抵押品出售收益 │ -│ │ -│ 储备金用途: │ -│ • 覆盖坏账风险 │ -│ • 维持协议流动性 │ -│ • 协议治理和运营 │ -│ │ -│ 限制条件: │ -│ • 只有 Owner 可以提取 │ -│ • 只能提取超出 targetReserves 的部分 │ -│ • 确保协议始终保持足够的安全垫 │ -└─────────────────────────────────────────────────────────────────────┘ - -10. 关键概念说明 -10.1 本金 vs 余额 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 本金(Principal) │ -│ ───────────────────────────────────────────────────────────────── │ -│ • 定义:用户的基础金额,不含利息 │ -│ • 类型:int104(有符号整数) │ -│ • 正数:表示存款本金 │ -│ • 负数:表示借款本金 │ -│ • 零:表示既无存款也无借款 │ -│ • 存储:userBasic[user].principal │ -│ │ -│ 示例: │ -│ • Alice 存入 10,000 USDC → principal = 10000e6 │ -│ • Bob 借出 5,000 USDC → principal = -5000e6 │ -└─────────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────────┐ -│ 余额(Balance) │ -│ ───────────────────────────────────────────────────────────────── │ -│ • 定义:实际金额,包含利息 │ -│ • 计算:balance = principal × index / 1e18 │ -│ • 动态:随时间和利息累积变化 │ -│ • 不存储:每次需要时动态计算 │ -│ │ -│ 示例(假设 supplyIndex = 1.05e18): │ -│ • Alice 实际余额 = 10000e6 × 1.05e18 / 1e18 = 10500e6 │ -│ • Alice 利息收益 = 10500e6 - 10000e6 = 500e6 │ -│ │ -│ 示例(假设 borrowIndex = 1.15e18): │ -│ • Bob 实际债务 = -5000e6 × 1.15e18 / 1e18 = -5750e6 │ -│ • Bob 利息成本 = 5750e6 - 5000e6 = 750e6 │ -└─────────────────────────────────────────────────────────────────────┘ -10.2 利息索引(Index) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 利息索引机制 │ -│ ───────────────────────────────────────────────────────────────── │ -│ • supplyIndex: 供应利息累计因子 │ -│ • borrowIndex: 借款利息累计因子 │ -│ • 初始值:1e18(即 1.0) │ -│ • 单调递增:每次计提利息时增加 │ -│ • 全局共享:所有用户使用相同的索引 │ -│ │ -│ 增长公式: │ -│ newIndex = oldIndex + (oldIndex × rate × timeElapsed) / 1e18 │ -│ │ -│ 其中: │ -│ • rate: 每秒利率(从年化利率转换) │ -│ • timeElapsed: 自上次计提以来的秒数 │ -│ │ -│ 示例(1年后,假设年化利率 5%): │ -│ • 初始: supplyIndex = 1.0e18 │ -│ • 1年后: supplyIndex ≈ 1.05e18 │ -│ • 用户存款本金 10000e6 的实际余额: │ -│ 10000e6 × 1.05e18 / 1e18 = 10500e6 (+5%) │ -└─────────────────────────────────────────────────────────────────────┘ -10.3 抵押率和清算阈值 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 三个关键比率 │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① borrowCollateralFactor(借款抵押率,例如 70%) │ -│ • 决定最大借款能力 │ -│ • 最大借款 = 抵押品价值 × 70% │ -│ • 例:$2000 抵押品 → 最多借 $1400 │ -│ │ -│ ② liquidateCollateralFactor(清算阈值,例如 75%) │ -│ • 决定何时可被清算 │ -│ • 清算条件: 债务 > 抵押品价值 × 75% │ -│ • 例:$2000 抵押品 → 债务超过 $1500 时清算 │ -│ │ -│ ③ liquidationFactor(清算折扣,例如 90%) │ -│ • 清算时抵押品价值打折 │ -│ • 清算价值 = 抵押品市价 × 90% │ -│ • 例:$2000 抵押品 → 清算时按 $1800 计算 │ -│ • 10% 差额作为清算惩罚和协议安全垫 │ -│ │ -│ 关系约束: │ -│ • borrowCollateralFactor < liquidateCollateralFactor < 1 │ -│ • 例:70% < 75% < 100% │ -│ • 留出安全缓冲区,避免频繁清算 │ -└─────────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────────┐ -│ 健康度示例(YT-A 抵押品 1000个,价格 $2000) │ -│ ───────────────────────────────────────────────────────────────── │ -│ 安全状态: │ -│ • 抵押品价值: $2,000,000 │ -│ • 债务: $1,000,000 │ -│ • LTV: 50% │ -│ • 距离清算: $1,500,000 - $1,000,000 = $500,000 ✓ │ -│ │ -│ 接近清算: │ -│ • 抵押品价值: $2,000,000 │ -│ • 债务: $1,480,000 │ -│ • LTV: 74% │ -│ • 距离清算: $1,500,000 - $1,480,000 = $20,000 ⚠️ │ -│ │ -│ 可清算状态: │ -│ • 抵押品价值: $2,000,000 │ -│ • 债务: $1,510,000 │ -│ • LTV: 75.5% │ -│ • $1,510,000 > $1,500,000 ❌ 会被清算 │ -└─────────────────────────────────────────────────────────────────────┘ -10.4 利率曲线(双斜率模型) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 利率随利用率变化示意图 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 利率 │ -│ │ │ -│ │ ╱ │ -│ │ ╱ │ -│ │ ╱ 借款利率 │ -│ │ ╱ │ -│ │ ╱ │ -│ │ ╱ │ -│ 20│ ╱ │ -│ % │ ╱ │ -│ │ ╱ │ -│ │ ╱ │ -│ 10│ ╱ ← Kink (拐点,80%) │ -│ % │ ╱ │ -│ │ ╱ │ -│ │ ╱ 供应利率 │ -│ 5│ ╱ │ -│ % │ ╱ │ -│ │╱_____________________________________ │ -│ 0│ 40% 80% 100% │ -│ └─────────────────────────────────────→ 利用率 │ -│ │ -│ 特点: │ -│ • Kink 前(0-80%): 缓慢线性增长 │ -│ • Kink 后(80-100%): 急剧陡峭增长 │ -│ • 借款利率始终高于供应利率(协议收益) │ -│ • 高利用率时大幅提高借款成本,激励还款 │ -│ • 低利用率时降低借款成本,鼓励借款 │ -└─────────────────────────────────────────────────────────────────────┘ - -11. 系统部署和配置流程 -11.1 合约部署顺序 -Plain Text -1. 部署核心基础设施(07-deployLending.ts) - ├── LendingFactory - ├── LendingPriceFeed (UUPS Proxy) - ├── Configurator (UUPS Proxy) - └── Lending Implementation - -2. 配置市场参数并部署代理(08-configureLending.ts) - ├── 设置 Configuration(利率、抵押品等) - ├── 通过 Configurator 注册配置 - └── 部署 Lending Proxy(使用 LendingFactory) - -3. 合约升级(09-upgradeLending.ts,可选) - ├── 升级 LendingPriceFeed - ├── 升级 Configurator - └── 升级 Lending -11.2 配置参数说明 -Solidity -Configuration { - // 基础资产 - baseToken: USDC地址 - lendingPriceSource: LendingPriceFeed Proxy地址 - - // 供应利率参数 - supplyKink: 0.8e18 // 80% 拐点 - supplyPerYearInterestRateBase: 0.02e18 // 2% 基础年化 - supplyPerYearInterestRateSlopeLow: 0.1e18 // 拐点前斜率 10% - supplyPerYearInterestRateSlopeHigh: 2e18 // 拐点后斜率 200% - - // 借款利率参数 - borrowKink: 0.8e18 // 80% 拐点 - borrowPerYearInterestRateBase: 0.03e18 // 3% 基础年化 - borrowPerYearInterestRateSlopeLow: 0.15e18 // 拐点前斜率 15% - borrowPerYearInterestRateSlopeHigh: 3e18 // 拐点后斜率 300% - - // 清算参数 - storeFrontPriceFactor: 0.95e18 // 清算购买折扣 5% - baseBorrowMin: 100e6 // 最小借款 100 USDC - targetReserves: 5000000e6 // 目标储备 500万 - - // 抵押资产配置 - assetConfigs: [ - { - asset: YT-A地址, - decimals: 18, - borrowCollateralFactor: 0.7e18, // 70% LTV - liquidateCollateralFactor: 0.75e18, // 75% 清算阈值 - liquidationFactor: 0.9e18, // 90% 清算价值 - supplyCap: 10000e18 // 最多1万个 - }, - // 更多抵押资产... - ] -} - -12. 风险提示和最佳实践 -12.1 用户风险 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 存款人风险 │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① 流动性风险 │ -│ • 高利用率时可能无法立即提取全部存款 │ -│ • 建议:关注利用率,避免在极高利用率时大额存入 │ -│ │ -│ ② 智能合约风险 │ -│ • 合约漏洞可能导致资金损失 │ -│ • 建议:使用经过审计的版本,小额试用 │ -│ │ -│ ③ 预言机风险 │ -│ • 价格数据异常可能影响协议运行 │ -│ • 建议:关注 Chainlink 和 YT Vault 价格源状态 │ -└─────────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────────┐ -│ 借款人风险 │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① 清算风险 │ -│ • 抵押品价格下跌或债务增长可能导致清算 │ -│ • 清算损失:抵押品 × (1 - liquidationFactor) │ -│ • 建议:保持健康的 LTV,至少低于清算阈值 10% │ -│ │ -│ ② 利率风险 │ -│ • 利用率上升导致借款利率快速增长 │ -│ • 建议:监控利用率和利率变化,及时还款 │ -│ │ -│ ③ 抵押品波动风险 │ -│ • YT 代币价格波动较大 │ -│ • 建议:使用多种抵押品分散风险,定期补充抵押品 │ -└─────────────────────────────────────────────────────────────────────┘ -13. 常见问题(FAQ) -Q1: 为什么我的余额和本金不一样? -•余额包含利息,本金是基础金额。余额 = 本金 × index / 1e18,随时间增长。 -Q2: 如何计算我的借款利率? -•借款利率根据协议利用率动态调整。查看 getBorrowRate() 获取当前每秒利率,乘以 31536000 得到年化利率。 -Q3: 什么时候会被清算? -•当你的债务价值超过抵押品价值 × liquidateCollateralFactor 时会被清算。 -Q4: 清算后我的资金怎么办? -•抵押品被没收按折扣价计算,用于偿还债务。如有剩余会转为你的存款,可以提取。 -Q5: 为什么无法提取全部存款? -•协议需要保持足够流动性满足借款人。高利用率时可能需要等待借款被还款后才能提取。 -Q6: storeFrontPriceFactor 是什么? -•清算抵押品出售时的额外折扣,激励清算人购买抵押品,补充协议储备金。 -Q7: 如何查看协议储备金? -•调用 getReserves() 查看当前储备金。储备金 = 合约余额 - 总供应 + 总借款。 -Q8: 可以同时存款和借款吗? -•不可以。每个账户只能有一个 principal(正数=存款,负数=借款,零=无)。如需同时操作,使用多个地址。 -Q9: 利息多久结算一次? -•每次交互时自动计提利息,无需手动操作。利息通过 index 增长自动体现在余额中。 -Q10: 支持哪些抵押品? -•由配置决定。默认支持 YT 资产代币。每种抵押品有独立的 LTV 和清算参数。 \ No newline at end of file diff --git a/ytLending清算文档-final.txt b/ytLending清算文档-final.txt deleted file mode 100644 index 9bba742..0000000 --- a/ytLending清算文档-final.txt +++ /dev/null @@ -1,477 +0,0 @@ -ytLending清算文档 -重要说明 -本清算机器人采用无状态轮询设计,通过监听链上事件发现活跃借款人,并自动执行清算操作以保护协议免受坏账风险。 -核心特性: -- 无状态设计:无需数据库,所有数据从链上实时查询 -- 轮询模式:每 5 秒检查一次新区块 -- 事件驱动:监听 4 种事件发现活跃借款人 -- 批量清算:一次交易清算多个账户,节省 Gas - -目录 -1.[系统启动流程](#1-系统启动流程) -2.[主循环轮询流程](#2-主循环轮询流程) -3.[活跃地址获取流程](#3-活跃地址获取流程) -4.[清算检查流程](#4-清算检查流程) -5.[批量清算执行流程](#5-批量清算执行流程) -6.[重要参数说明](#6-重要参数说明) -7.[完整脚本](#7-完整脚本) -8.[运行和部署](#8-运行和部署) - -1. 系统启动流程 -启动命令: -Bash -npx hardhat run scripts/liquidation_bot/index.ts --network arbSepolia -启动步骤: -步骤 1 — 初始化 Hardhat 环境,读取网络配置 -Plain Text -network: arbSepolia -chainId: 421614 -rpcUrl: https://arbitrum-sepolia.gateway.tenderly.co -步骤 2 — 读取部署信息(`deployments-lending.json`),加载合约地址 -Plain Text -Lending Proxy: 0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D -Base Token (USDC): 0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d - -Collateral Assets: - YT-A: 0x97204190B35D9895a7a47aa7BaC61ac08De3cF05 - YT-B: 0x181ef4011c35C4a2Fda08eBC5Cf509Ef58E553fF - YT-C: 0xE9A5b9f3a2Eda4358f81d4E2eF4f3280A664e5B0 - -注:Price Feed 合约不再需要初始化 -步骤 3 — 初始化签名者(从 `PRIVATE_KEY` 环境变量读取) -Plain Text -Liquidator Address: 0x... -ETH Balance: 0.5 ETH - -要求: - - 需要 ETH 支付 Gas 费用 - - 建议保持至少 0.1 ETH 余额 -步骤 4 — 初始化合约实例 -TypeScript -lendingContract = await ethers.getContractAt( - 'Lending', - deployment.lendingProxy, - signer -) -步骤 5 — 进入主循环 - -2. 主循环轮询流程 -主循环参数:LOOP_DELAY = 5000ms(5 秒) -循环步骤: -1.调用 provider.getBlockNumber() 获取当前区块号 -2.判断是否为新区块: - - 新区块 → 执行清算逻辑(见下方步骤 3) - - 已检查过 → 等待 5 秒后继续 -3.执行 liquidateUnderwaterBorrowers(lendingContract, signer): -○① 获取活跃地址(查询事件) -○② 检查每个地址是否可清算 -○③ 批量执行清算 -4.更新 lastBlockNumber = currentBlockNumber,循环回步骤 1 -容错机制: -TypeScript -try { - // 执行清算逻辑 -} catch (error) { - console.error('Error in main loop:', error) - // 继续运行,不中断 -} -// 无论成功或失败,都等待 5 秒后继续 -await sleep(5000) -关键设计决策: -参数 值 说明 -LOOP_DELAY 5000ms 轮询间隔 -去重检查 lastBlockNumber 每个区块只处理一次,避免重复 -容错机制 try-catch 异常不中断,继续运行 - -3. 活跃地址获取流程 -函数:getUniqueAddresses(lendingContract),LOOKBACK_BLOCKS = 10000 -步骤 1 — 计算查询区块范围 -Plain Text -currentBlock = 12345678 -fromBlock = 12345678 - 10000 = 12335678 -toBlock = 12345678 - -时间窗口(Arbitrum Sepolia,出块 ~1 秒): -10000 区块 ≈ 2.7 小时 -步骤 2 — 查询 4 种事件,提取活跃地址 -事件 说明 提取字段 -Withdraw 用户借款/提取 USDC event.args.src、event.args.dst -Supply 用户存入 USDC event.args.from、event.args.dst -SupplyCollateral 用户存入抵押品 event.args.from、event.args.dst -WithdrawCollateral 用户提取抵押品 event.args.src、event.args.to -步骤 3 — 合并去重 -使用 Set 自动去重,示例: -Plain Text -总事件数: 150 + 80 + 120 + 90 = 440 个 -唯一地址数: 85 个(去重后) - -覆盖率对比: - Compound V3(仅 Withdraw): ~95% - 本机器人(4 种事件): ~98% -步骤 4 — 返回地址列表,进入清算检查 - -4. 清算检查流程 -遍历所有活跃地址,对每个地址执行: -1.调用 lendingContract.isLiquidatable(address)(链上计算) -2.结果判断: - - false → 跳过(账户健康) - - true → 加入清算列表 `liquidatableAccounts[]` -3.继续检查下一个地址,全部检查完毕后进入批量清算 - -5. 批量清算执行流程 -清算列表示例: `[0xAlice, 0xBob, 0xCharlie]`,共 3 个可清算账户 -步骤 1 — 检查列表是否为空 -•空列表 → 无清算,等待下一轮 -•非空 → 继续执行 -**步骤 2 — 调用 `absorbMultiple()`** -Solidity -function absorbMultiple( - address absorber, // 清算者地址(机器人) - address[] accounts // [0xAlice, 0xBob, 0xCharlie] -) external override nonReentrant - -// 修饰符检查: -// ✓ nonReentrant - 防重入保护 -// ✓ 未暂停检查 -步骤 3 — 累积利息(`accrueInternal()`) -•更新 supplyIndex(存款利息) -•更新 borrowIndex(借款利息) -•确保清算时使用最新的债务余额 -**步骤 4 — 对每个账户执行 `_absorbInternal()`** -以 Alice 为例: -1.再次验证可清算性:if (!isLiquidatable(Alice)) revert NotLiquidatable()(防止抢跑) -2.获取用户债务:principal = userBasic[Alice].principal(负数 = 借款,如 -1000e6) -3.遍历所有抵押资产 [YT-A, YT-B, YT-C],获取抵押数量 -4.计算抵押品价值:collateralValue = collateralAmount × assetPrice × liquidationFactor(0.95) -5.转移抵押品:userCollateral[Alice][asset] = 0,userCollateral[absorber][asset] += collateralAmount -6.清零债务:userBasic[Alice].principal = 0,totalBorrowBase -= 1000e6 -结果: Alice 债务清零,抵押品转移给清算者,清算者获得 5% 折扣 -步骤 5 — 继续清算 Bob、Charlie(相同流程) -步骤 6 — 发送交易并等待确认 -TypeScript -const tx = await lendingContract.absorbMultiple( - await signer.getAddress(), // 清算者地址 - liquidatableAccounts // [Alice, Bob, Charlie] -) -const receipt = await tx.wait() -清算结果示例: -Plain Text -✅ Liquidation successful! -Transaction: 0xabc123... -Gas used: 450,000 -Block: 12345679 - -清算了 3 个账户: - 0xAlice: $1,000 债务 - 0xBob: $500 债务 - 0xCharlie: $2,000 债务 - -清算者获得的抵押品: - YT-A: 500 个(估值 $1,750) - YT-B: 800 个(估值 $1,600) - 总价值: $3,350(覆盖 $3,500 债务,5% 折扣) - -6. 重要参数说明 -6.1 轮询参数 -参数名 值 单位 说明 调优建议 -LOOP_DELAY 5000 毫秒 轮询间隔 测试网 5000ms / 主网高活跃 2000ms / 激进模式 1000ms(注意 RPC 限流) -LOOKBACK_BLOCKS 10000 区块 事件查询范围 Arbitrum 测试网 50000(~14小时)/ Arbitrum 主网 10000-20000(~3-6小时)/ 以太坊主网 10000(~33小时) -时间窗口参考: -Plain Text -Arbitrum(出块 ~1 秒): - 10000 区块 ≈ 2.7 小时 - 50000 区块 ≈ 14 小时 - 100000 区块 ≈ 28 小时 - -Ethereum 主网(出块 ~12 秒): - 10000 区块 ≈ 33 小时 -6.2 合约参数(只读) -参数名 典型值 精度 说明 位置 -borrowCollateralFactor 0.80 18 decimals 借款抵押率(80% LTV) assetConfig -liquidateCollateralFactor 0.85 18 decimals 清算触发阈值(85%) assetConfig -liquidationFactor 0.95 18 decimals 清算激励因子(5% 折扣) 全局配置 -storeFrontPriceFactor 0.50 18 decimals buyCollateral 折扣系数 全局配置 -三个抵押率的关系: -Plain Text -正常借款 → 接近清算 → 触发清算 → 清算完成 - 80% 83% 85% 清零 - (借款) (预警) (清算) (债务) -健康因子计算: -Plain Text -healthFactor = (collateralValue × 0.85) / debtValue - -healthFactor > 1.0:安全 -healthFactor < 1.0:可清算 -6.3 监听事件类型 -事件名 触发场景 提取字段 说明 -Withdraw 用户借款/提取 USDC src, dst 最主要的借款信号 -Supply 用户存入 USDC from, dst 可能有借款人还款 -SupplyCollateral 用户存入抵押品 from, dst 借款前的准备动作 -WithdrawCollateral 用户提取抵押品 src, to 可能触发清算条件 -6.4 日志示例 -Plain Text -[2025-01-06T10:30:15.000Z] Block: 12345678 -📊 Querying events from block 12335678 to 12345678... - - Withdraw events: 150 - - Supply events: 80 - - SupplyCollateral events: 120 - - WithdrawCollateral events: 90 -✅ Found 85 unique addresses from all events -🔍 Checking 85 addresses for liquidation... -💰 Liquidatable: 0xAlice...1234 -💰 Liquidatable: 0xBob...5678 -💰 Liquidatable: 0xEve...9012 - -🎯 Found 3 liquidatable accounts -📤 Sending liquidation transaction... -🔗 Transaction sent: 0xabc123... -✅ Liquidation successful! - Gas used: 450000 - Block: 12345679 - -7. 完整脚本 -index.ts -TypeScript -import hre from 'hardhat'; -import { liquidateUnderwaterBorrowers } from './liquidateUnderwaterBorrowers'; -import * as fs from 'fs'; -import * as path from 'path'; -const LOOP_DELAY = 5000; // 5 秒轮询间隔 - -async function main() { - const network = hre.network.name; - const chainId = hre.network.config.chainId; - - console.log('\n=========================================='); - console.log('🤖 YT Lending Liquidation Bot Started'); - console.log('=========================================='); - console.log('Network:', network); - console.log('Chain ID:', chainId); - console.log('Loop Delay:', LOOP_DELAY, 'ms\n'); - - const deploymentsPath = path.join(__dirname, '../../deployments-lending.json'); - if (!fs.existsSync(deploymentsPath)) { - throw new Error('deployments-lending.json not found'); - } - - const deployments = JSON.parse(fs.readFileSync(deploymentsPath, 'utf-8')); - const deployment = deployments[chainId?.toString() || '421614']; - - if (!deployment) { - throw new Error(`No deployment found for chainId: ${chainId}`); - } - - console.log('📋 Contract Addresses:'); - console.log(' Lending Proxy:', deployment.lendingProxy); - console.log(' Base Token (USDC):', deployment.usdcAddress); - console.log(''); - - const [signer] = await hre.ethers.getSigners(); - console.log('👤 Liquidator Address:', await signer.getAddress()); - console.log('💰 Liquidator Balance:', hre.ethers.formatEther(await hre.ethers.provider.getBalance(signer)), 'ETH\n'); - - const lendingContract = await hre.ethers.getContractAt( - 'Lending', - deployment.lendingProxy, - signer - ); - - console.log('✅ Contracts initialized\n'); - console.log('=========================================='); - console.log('🔄 Starting main loop...\n'); - - let lastBlockNumber: number | undefined; - - while (true) { - try { - const currentBlockNumber = await hre.ethers.provider.getBlockNumber(); - console.log(`[${new Date().toISOString()}] Block: ${currentBlockNumber}`); - - if (currentBlockNumber !== lastBlockNumber) { - lastBlockNumber = currentBlockNumber; - await liquidateUnderwaterBorrowers(lendingContract, signer); - console.log(''); - } else { - console.log(`Block already checked; waiting ${LOOP_DELAY}ms...\n`); - } - - await new Promise(resolve => setTimeout(resolve, LOOP_DELAY)); - } catch (error) { - console.error('❌ Error in main loop:', error); - console.log(`Retrying in ${LOOP_DELAY}ms...\n`); - await new Promise(resolve => setTimeout(resolve, LOOP_DELAY)); - } - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error('❌ Fatal error:', error); - process.exit(1); - }); -liquidateUnderwaterBorrowers.ts -TypeScript -import hre from 'hardhat'; -import { Signer } from 'ethers'; - -const LOOKBACK_BLOCKS = 10000; - -export async function getUniqueAddresses(lendingContract: any): Promise { - const currentBlock = await hre.ethers.provider.getBlockNumber(); - const fromBlock = Math.max(currentBlock - LOOKBACK_BLOCKS, 0); - - console.log(`📊 Querying events from block ${fromBlock} to ${currentBlock}...`); - - const uniqueAddresses = new Set(); - - try { - const withdrawEvents = await lendingContract.queryFilter( - lendingContract.filters.Withdraw(), fromBlock, currentBlock - ); - for (const event of withdrawEvents) { - if (event.args?.src) uniqueAddresses.add(event.args.src); - if (event.args?.dst) uniqueAddresses.add(event.args.dst); - } - console.log(` - Withdraw events: ${withdrawEvents.length}`); - } catch (error) { - console.error(' ⚠️ Failed to query Withdraw events:', error); - } - - try { - const supplyEvents = await lendingContract.queryFilter( - lendingContract.filters.Supply(), fromBlock, currentBlock - ); - for (const event of supplyEvents) { - if (event.args?.from) uniqueAddresses.add(event.args.from); - if (event.args?.dst) uniqueAddresses.add(event.args.dst); - } - console.log(` - Supply events: ${supplyEvents.length}`); - } catch (error) { - console.error(' ⚠️ Failed to query Supply events:', error); - } - - try { - const supplyCollateralEvents = await lendingContract.queryFilter( - lendingContract.filters.SupplyCollateral(), fromBlock, currentBlock - ); - for (const event of supplyCollateralEvents) { - if (event.args?.from) uniqueAddresses.add(event.args.from); - if (event.args?.dst) uniqueAddresses.add(event.args.dst); - } - console.log(` - SupplyCollateral events: ${supplyCollateralEvents.length}`); - } catch (error) { - console.error(' ⚠️ Failed to query SupplyCollateral events:', error); - } - - try { - const withdrawCollateralEvents = await lendingContract.queryFilter( - lendingContract.filters.WithdrawCollateral(), fromBlock, currentBlock - ); - for (const event of withdrawCollateralEvents) { - if (event.args?.src) uniqueAddresses.add(event.args.src); - if (event.args?.to) uniqueAddresses.add(event.args.to); - } - console.log(` - WithdrawCollateral events: ${withdrawCollateralEvents.length}`); - } catch (error) { - console.error(' ⚠️ Failed to query WithdrawCollateral events:', error); - } - - console.log(`✅ Found ${uniqueAddresses.size} unique addresses from all events`); - return Array.from(uniqueAddresses); -} - -export async function liquidateUnderwaterBorrowers( - lendingContract: any, - signer: Signer -): Promise { - const uniqueAddresses = await getUniqueAddresses(lendingContract); - - if (uniqueAddresses.length === 0) { - console.log('ℹ️ No active addresses found'); - return false; - } - - console.log(`🔍 Checking ${uniqueAddresses.length} addresses for liquidation...`); - - const liquidatableAccounts: string[] = []; - - for (const address of uniqueAddresses) { - try { - const isLiquidatable = await lendingContract.isLiquidatable(address); - if (isLiquidatable) { - console.log(`💰 Liquidatable: ${address}`); - liquidatableAccounts.push(address); - } - } catch (error) { - console.error(`Error checking ${address}:`, error); - } - } - - if (liquidatableAccounts.length > 0) { - console.log(`\n🎯 Found ${liquidatableAccounts.length} liquidatable accounts`); - console.log('📤 Sending liquidation transaction...'); - - try { - const liquidatorAddress = await signer.getAddress(); - const tx = await lendingContract.connect(signer).absorbMultiple( - liquidatorAddress, - liquidatableAccounts - ); - - console.log(`🔗 Transaction sent: ${tx.hash}`); - const receipt = await tx.wait(); - console.log(`✅ Liquidation successful!`); - console.log(` Gas used: ${receipt.gasUsed.toString()}`); - console.log(` Block: ${receipt.blockNumber}`); - - return true; - } catch (error) { - console.error('❌ Liquidation transaction failed:', error); - return false; - } - } else { - console.log('✅ No liquidatable accounts found'); - return false; - } -} - -8. 运行和部署 -8.1 开发模式(前台运行) -Bash -npx hardhat run scripts/liquidation_bot/index.ts --network arbSepolia -8.2 生产部署(PM2 后台运行) -Bash -# 1. 安装 PM2 -npm install -g pm2 - -# 2. 启动清算机器人 -pm2 start scripts/liquidation_bot/index.ts \ - --name ytlp-liquidation-bot \ - --interpreter npx \ - --interpreter-args "hardhat run --network arbSepolia" - -# 3. 查看日志 -pm2 logs ytlp-liquidation-bot --lines 100 - -# 4. 保存配置(开机自启) -pm2 save -pm2 startup - -# 5. 监控管理 -pm2 list # 列出所有进程 -pm2 restart ytlp-liquidation-bot # 重启 -pm2 stop ytlp-liquidation-bot # 停止 -pm2 delete ytlp-liquidation-bot # 删除 -8.3 参数调优指南 -场景 1:测试网(低活跃度) -TypeScript -const LOOP_DELAY = 5000; // 5 秒(稳定) -const LOOKBACK_BLOCKS = 50000; // ~14 小时(覆盖更多历史) -场景 2:主网(高活跃度) -TypeScript -const LOOP_DELAY = 2000; // 2 秒(快速响应) -const LOOKBACK_BLOCKS = 10000; // ~2.7 小时(用户活跃,足够) - -总结 -本清算机器人采用无状态轮询设计,通过监听多种事件提高覆盖率,对所有 isLiquidatable 为 true 的账户无金额限制地执行清算,确保及时清算水下账户,保护协议免受坏账风险。 diff --git a/ytLending购买抵押品文档-new.txt b/ytLending购买抵押品文档-new.txt deleted file mode 100644 index 4fa06ad..0000000 --- a/ytLending购买抵押品文档-new.txt +++ /dev/null @@ -1,362 +0,0 @@ -ytLending购买抵押品文档 -目录 -1.[快速开始](#快速开始) -2.[脚本功能说明](#脚本功能说明) -3.[环境变量配置](#环境变量配置) -4.[执行流程](#执行流程) -5.[常见问题](#常见问题) - -快速开始 -Bash -# 最小配置运行(自动扫描所有资产) -export LENDING_ADDRESS="0x5FbDB2315678afecb367f032d93F642f64180aa3" -npx hardhat run scripts/utils/buyCollateral.ts --network sepolia - -# 指定滑点 -export LENDING_ADDRESS="0x5FbDB2315678afecb367f032d93F642f64180aa3" -export SLIPPAGE="2" -npx hardhat run scripts/utils/buyCollateral.ts --network sepolia - -脚本功能说明 -什么是购买清算抵押品? -当借贷协议的储备金低于目标储备金时,协议会开放清算抵押品的购买功能。用户可以用折扣价格购买协议中的抵押品资产,帮助协议补充储备金。 -主要功能 -1. 自动扫描所有抵押品资产 -脚本通过遍历合约的 assetList 数组,自动发现所有已配置的抵押品地址,无需手动指定。对每个资产检查其 collateralReserves,跳过储备为零的资产。 -2. 动态计算支付金额 -无需预先指定 BASE_AMOUNT。每次购买前实时读取买家当前余额作为 baseAmount 上限传入合约。合约内部会将购买量自动限制到实际储备量,并通过 quoteBaseAmount() 只收取对应的实际费用,买家不会多付。 -3. 基于储备量的滑点保护 -Plain Text -minAmount = collateralReserves[asset] * (1 - slippage) -滑点作用于链上实际储备量,而非 quote 估算值,更准确地反映价格波动风险。默认滑点 1%,允许合理的价格偏移。 -4. 多资产顺序购买,容错处理 -•逐资产执行,单个资产失败不影响其他资产 -•每次购买前重新读取买家余额,余额耗尽时提前退出并提示 -•最终输出所有资产的购买汇总 -5. 一次性 MaxUint256 授权 -如果授权额度低于 MaxUint256 / 2,自动执行一次 approve(MaxUint256),后续所有资产购买无需重复授权。 - -环境变量配置 -必需参数 -变量名 说明 示例 -LENDING_ADDRESS Lending 合约地址 0x5FbDB2315678afecb367f032d93F642f64180aa3 -可选参数 -变量名 说明 默认值 取值范围 -SLIPPAGE 滑点容忍度(百分比) 1 0-10,推荐 1-2 -> 说明: 原有的 `ASSET_ADDRESS` 和 `BASE_AMOUNT` 参数已移除。 -•资产地址:由脚本自动从合约 assetList 扫描获取 -•支付金额:由买家当前余额动态决定,合约只收取实际成本 -配置示例 -Bash -# 示例 1:最小配置,使用默认 1% 滑点 -export LENDING_ADDRESS="0x5FbDB2315678afecb367f032d93F642f64180aa3" - -# 示例 2:指定 2% 滑点(高波动市场) -export LENDING_ADDRESS="0x5FbDB2315678afecb367f032d93F642f64180aa3" -export SLIPPAGE="2" - -# 示例 3:测试环境,较高滑点 -export LENDING_ADDRESS="0x5FbDB2315678afecb367f032d93F642f64180aa3" -export SLIPPAGE="5" - -执行流程 -Plain Text -启动脚本 - └─ 读取环境变量,验证参数 - │ - ▼ - 1. 初始化 - 获取 Lending 合约、买家账户、baseToken 合约 - │ - ▼ - 2. 系统状态检查 - 检查 reserves < targetReserves - 不满足则退出 - │ - ▼ - 3. 扫描可购买资产 - 遍历合约 assetList[] - 过滤出 collateralReserves > 0 的资产 - │ - ▼ - 4. 授权检查 - allowance < MaxUint256/2 ? - 是 → approve(MaxUint256) - 否 → 跳过 - │ - ▼ - 5. 逐资产购买循环 - for each asset with reserves: - ├─ 读取买家当前余额 buyerBalance - ├─ 余额为 0 → break - ├─ minAmount = reserves * (1 - slippage) - ├─ buyCollateral(asset, minAmount, buyerBalance, buyer) - │ 合约内部: - │ collateralAmount = min(quoteCollateral(buyerBalance), reserves) - │ actualCost = quoteBaseAmount(collateralAmount) - │ transferFrom(buyer, actualCost) ← 只收实际费用 - └─ 解析 BuyCollateral 事件,输出结果 - │ - ▼ - 6. 输出汇总 - 成功购买资产数 / 总数 - 累计支付 baseToken - 买家剩余余额 -### `getBuyCollateralInfo()` 辅助函数 -该函数供外部调用,返回单个资产的购买预估信息: -返回字段 说明 -availableReserve 链上可用抵押品储备量 -expectedAmount 按 baseAmount 估算可购买数量(可能超过储备) -actualAmount 实际可购买数量(受储备限制后) -minAmount 应用滑点后的最小接受量 -actualBaseAmount 估算实际需支付的 baseToken 数量 -isLimited 是否受储备量限制 - -完整代码 -TypeScript -import { ethers } from "hardhat"; -import { Lending } from "../../typechain-types"; - -/** - * 购买清算抵押品脚本 - * - * 自动扫描合约中所有抵押品资产,对有储备的资产执行购买。 - * 传入买家当前余额作为 baseAmount 上限,合约自动按实际储备量收费。 - * 无需指定具体资产地址,脚本会自动遍历合约的 assetList。 - * - * 环境变量: - * - LENDING_ADDRESS: Lending 合约地址(必填) - * - SLIPPAGE (可选): 滑点容忍度百分比 (1-5),默认 1 - */ -async function main() { - // ==================== 配置 ==================== - const LENDING_ADDRESS = process.env.LENDING_ADDRESS; - const SLIPPAGE_PERCENT = parseFloat(process.env.SLIPPAGE || "1"); - - if (!LENDING_ADDRESS || LENDING_ADDRESS === "0x...") { - throw new Error("请设置 LENDING_ADDRESS 环境变量"); - } - if (SLIPPAGE_PERCENT < 0 || SLIPPAGE_PERCENT > 10) { - throw new Error("SLIPPAGE 应在 0-10 之间"); - } - - const SLIPPAGE = SLIPPAGE_PERCENT / 100; - - console.log("==================== 购买清算抵押品 ===================="); - console.log(`Lending 合约: ${LENDING_ADDRESS}`); - console.log(`滑点容忍度: ${SLIPPAGE_PERCENT}%`); - console.log(""); - - // ==================== 初始化 ==================== - const lending = await ethers.getContractAt("Lending", LENDING_ADDRESS) as unknown as Lending; - const [buyer] = await ethers.getSigners(); - const baseToken = await lending.baseToken(); - const base = await ethers.getContractAt("IERC20Metadata", baseToken); - const baseDecimals = await base.decimals(); - - console.log(`买家地址: ${buyer.address}`); - - // ==================== 系统状态检查 ==================== - console.log("\n检查系统状态..."); - const reserves = await lending.getReserves(); - const targetReserves = await lending.targetReserves(); - console.log(`当前储备金: ${ethers.formatUnits(reserves, baseDecimals)} baseToken`); - console.log(`目标储备金: ${ethers.formatUnits(targetReserves, baseDecimals)} baseToken`); - - if (reserves >= 0n && BigInt(reserves.toString()) >= targetReserves) { - throw new Error("储备金充足,当前无法购买抵押品"); - } - - // ==================== 扫描可购买资产 ==================== - const assetsToProcess = await getAllAssets(lending); - console.log(`\n发现 ${assetsToProcess.length} 个抵押品资产`); - - // 过滤出有储备的资产 - const assetsWithReserves: { address: string; reserve: bigint; decimals: number }[] = []; - for (const assetAddr of assetsToProcess) { - const reserve = await lending.getCollateralReserves(assetAddr); - if (reserve > 0n) { - const assetToken = await ethers.getContractAt("IERC20Metadata", assetAddr); - const dec = await assetToken.decimals(); - assetsWithReserves.push({ address: assetAddr, reserve, decimals: dec }); - console.log(` ${assetAddr}: 储备 ${ethers.formatUnits(reserve, dec)} 代币`); - } - } - - if (assetsWithReserves.length === 0) { - console.log("\n所有资产储备均为零,无需购买。"); - return; - } - - // ==================== 授权(一次性 MaxUint256)==================== - console.log("\n检查授权..."); - const allowance = await base.allowance(buyer.address, LENDING_ADDRESS); - if (allowance < ethers.MaxUint256 / 2n) { - console.log("正在授权 MaxUint256..."); - const approveTx = await base.approve(LENDING_ADDRESS, ethers.MaxUint256); - await approveTx.wait(); - console.log("授权成功"); - } else { - console.log("授权充足,无需重复授权"); - } - - // ==================== 逐资产购买 ==================== - let totalPaid = 0n; - let successCount = 0; - - for (const { address: assetAddr, reserve, decimals: assetDecimals } of assetsWithReserves) { - console.log(`\n---- 购买资产: ${assetAddr} ----`); - - // 读取买家当前余额作为本次最大支付额 - const buyerBalance = await base.balanceOf(buyer.address); - if (buyerBalance === 0n) { - console.log("买家余额已耗尽,跳过剩余资产"); - break; - } - console.log(`买家当前余额: ${ethers.formatUnits(buyerBalance, baseDecimals)} baseToken`); - console.log(`可用储备: ${ethers.formatUnits(reserve, assetDecimals)} 代币`); - - // minAmount = 储备量 * (1 - slippage),允许价格轻微偏移 - const slippageMultiplier = BigInt(Math.floor((1 - SLIPPAGE) * 1e18)); - const minAmount = (reserve * slippageMultiplier) / BigInt(1e18); - console.log(`最小接受量 (${SLIPPAGE_PERCENT}% 滑点): ${ethers.formatUnits(minAmount, assetDecimals)} 代币`); - - // 以买家全部余额作为 baseAmount 上限;合约内部按实际储备量收费 - try { - const tx = await lending.buyCollateral( - assetAddr, - minAmount, - buyerBalance, - buyer.address - ); - console.log(`交易已提交: ${tx.hash}`); - const receipt = await tx.wait(); - console.log(`交易确认,Gas 消耗: ${receipt?.gasUsed.toString()}`); - - // 解析事件 - const buyEvent = receipt?.logs.find((log: any) => { - try { return lending.interface.parseLog(log)?.name === "BuyCollateral"; } - catch { return false; } - }); - - if (buyEvent) { - const parsed = lending.interface.parseLog(buyEvent); - const paidAmount: bigint = parsed?.args.baseAmount; - const receivedAmount: bigint = parsed?.args.collateralAmount; - totalPaid += paidAmount; - successCount++; - - console.log(`实际支付: ${ethers.formatUnits(paidAmount, baseDecimals)} baseToken`); - console.log(`实际获得: ${ethers.formatUnits(receivedAmount, assetDecimals)} 代币`); - - // 折扣信息 - const marketAmount = await lending.quoteCollateral(assetAddr, paidAmount); - if (receivedAmount > marketAmount && marketAmount > 0n) { - const discount = ((receivedAmount - marketAmount) * 10000n) / marketAmount; - console.log(`折扣收益: +${ethers.formatUnits(receivedAmount - marketAmount, assetDecimals)} 代币 (${Number(discount) / 100}%)`); - } - } - } catch (err: any) { - console.log(`跳过 ${assetAddr}:${err.message?.split("\n")[0] ?? err}`); - } - } - - // ==================== 汇总 ==================== - console.log("\n==================== 购买汇总 ===================="); - console.log(`成功购买资产数: ${successCount} / ${assetsWithReserves.length}`); - console.log(`累计支付: ${ethers.formatUnits(totalPaid, baseDecimals)} baseToken`); - - const finalBalance = await base.balanceOf(buyer.address); - console.log(`买家剩余余额: ${ethers.formatUnits(finalBalance, baseDecimals)} baseToken`); - console.log("==================================================="); -} - -/** - * 遍历合约 assetList 数组,获取所有抵押品地址 - */ -async function getAllAssets(lending: Lending): Promise { - const assets: string[] = []; - let i = 0; - while (true) { - try { - const asset = await (lending as any).assetList(i); - assets.push(asset); - i++; - } catch { - break; // 数组越界,遍历结束 - } - } - return assets; -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error("\n执行失败:", error.message || error); - process.exit(1); - }); - -/** - * 获取单个资产的购买详情(供外部调用) - */ -export async function getBuyCollateralInfo( - lendingContract: Lending, - asset: string, - baseAmount: bigint, - slippageTolerance: number = 0.01 -) { - const availableReserve = await lendingContract.getCollateralReserves(asset); - // minAmount 基于实际储备量而非 quote,允许 slippage 偏移 - const slippageMultiplier = BigInt(Math.floor((1 - slippageTolerance) * 1e18)); - const minAmount = (availableReserve * slippageMultiplier) / BigInt(1e18); - - // 用于展示:预估 baseAmount 能买到多少(可能超过储备,合约会自动限制) - const expectedAmount = await lendingContract.quoteCollateral(asset, baseAmount); - const actualAmount = expectedAmount < availableReserve ? expectedAmount : availableReserve; - const actualBaseAmount = actualAmount < expectedAmount - ? (baseAmount * actualAmount) / expectedAmount - : baseAmount; - - return { - availableReserve, - expectedAmount, - actualAmount, - minAmount, - baseAmount, - actualBaseAmount, - isLimited: actualAmount < expectedAmount, - }; -} - -常见问题 -Q1: 什么时候可以购买抵押品? -当协议储备金低于目标储备金时才开放购买: -Plain Text -可购买条件:reserves < targetReserves -可通过以下方式检查: -TypeScript -const reserves = await lending.getReserves(); -const targetReserves = await lending.targetReserves(); -console.log(`可以购买: ${reserves < targetReserves}`); -Q2: 滑点应该设置多少? -市场状况 建议滑点 -正常市场 1(默认) -轻微波动 2 -高波动市场 3-5 -测试环境 5 或更高 -滑点保护的含义:如果实际到手的抵押品数量低于 minAmount(储备量的 1 - slippage 倍),交易会 revert。 -Q3: 如果某个资产购买失败会怎样? -单个资产失败(如该资产储备金刚好被其他人买走、价格剧烈波动超出滑点)不会中断整个脚本,会打印错误信息后继续处理下一个资产。 -Q4: 购买的折扣是如何计算的? -折扣通过清算因子(liquidationFactor)和价格因子(storeFrontPriceFactor)计算: -Plain Text -discountFactor = storeFrontPriceFactor × liquidationFactor -effectiveAssetPrice = assetPrice × (1 - discountFactor) -示例: -Plain Text -assetPrice = $2.00 -liquidationFactor = 0.05 (5%) -storeFrontPriceFactor = 0.90 (10%) -discountFactor = 0.90 × 0.05 = 0.045 -effectivePrice = $2.00 × (1 - 0.045) ≈ $1.91 (折扣约 4.5%) -脚本会在每笔交易完成后自动计算实际折扣并输出。 diff --git a/ytLp-USDC流动性池系统操作流程文档.txt b/ytLp-USDC流动性池系统操作流程文档.txt deleted file mode 100644 index 0e8c6fa..0000000 --- a/ytLp-USDC流动性池系统操作流程文档.txt +++ /dev/null @@ -1,1100 +0,0 @@ -ytLp-USDC流动性池系统操作流程文档 -目录 -1.[添加流动性流程](#1-添加流动性流程) -2.[移除流动性流程](#2-移除流动性流程) -3.[代币互换流程](#3-代币互换流程) -4.[添加白名单代币流程](#4-添加白名单代币流程) -5.[移除白名单代币流程](#5-移除白名单代币流程) -6.[系统部署和初始化流程](#6-系统部署和初始化流程) -7.[路由器暂停功能流程](#7-路由器暂停功能流程) - -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(滑点保护) │ -│ ) external nonReentrant whenNotPaused │ -│ │ -│ 修饰符检查: │ -│ ✓ nonReentrant - 防重入保护 │ -│ ✓ whenNotPaused - 暂停检查 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. transferFrom(user → YTRewardRouter) - │ 转移1000个YT-A到Router - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移检查 │ -│ ✓ 检查用户授权额度 │ -│ ✓ 转移代币到Router │ -│ ✓ approve(YTPoolManager, amount) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 调用 addLiquidityForAccount() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTPoolManager.sol │ -│ ───────────────────────────────────────────────────────────────── │ -│ function addLiquidityForAccount( │ -│ address _fundingAccount, // YTRewardRouter │ -│ address _account, // 用户地址 │ -│ address _token, // YT-A │ -│ uint256 _amount, // 1000 │ -│ uint256 _minUsdy, // 997 │ -│ uint256 _minYtLP // 995 │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 计算当前AUM(使用MaxPrice) - │ getAumInUsdy(true) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ AUM 计算(对用户保守) │ -│ ┌───────────────────────────────────────────────────────┐ │ -│ │ YTVault.getPoolValue(true) │ │ -│ │ • 获取池中所有YT代币数量 │ │ -│ │ • 使用 MaxPrice(上浮价差)计算每个代币价值 │ │ -│ │ • 汇总得到 AUM = $100,200 │ │ -│ └───────────────────────────────────────────────────────┘ │ -│ 当前ytLP供应量: 100,000 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 调用 buyUSDY() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTVault.sol │ -│ ───────────────────────────────────────────────────────────────── │ -│ function buyUSDY(address _token, address _receiver) │ -│ │ -│ 步骤: │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ ① transferIn(_token) │ │ -│ │ • 接收1000个YT-A │ │ -│ │ │ │ -│ │ ② 获取价格(使用MinPrice,低估用户资产) │ │ -│ │ price = _getPrice(_token, false) │ │ -│ │ • 基础价格: $1.00 │ │ -│ │ • MinPrice: $0.998 (下压0.2%价差) │ │ -│ │ │ │ -│ │ ③ 计算理论USDY价值 │ │ -│ │ usdyAmount = 1000 × $0.998 = $998 │ │ -│ │ │ │ -│ │ ④ 获取手续费率(动态) │ │ -│ │ feeBps = getSwapFeeBasisPoints(_token, usdy, 998) │ │ -│ │ • 检查是否稳定币互换 │ │ -│ │ • YT代币: 30 bps (0.3%) │ │ -│ │ • 稳定币: 4 bps (0.04%) │ │ -│ │ • 根据池子平衡动态调整 │ │ -│ │ → 本次: 30 bps │ │ -│ │ │ │ -│ │ ⑤ 计算扣费后的USDY │ │ -│ │ feeAmount = 1000 × 0.3% = 3个代币 │ │ -│ │ amountAfterFees = 997个代币 │ │ -│ │ usdyAmountAfterFees = 997 × $0.998 = $995.006 │ │ -│ │ │ │ -│ │ ⑥ 池子记账 │ │ -│ │ _increasePoolAmount(_token, 1000) // 全部代币入池 │ │ -│ │ _increaseUsdyAmount(_token, 995) // 只记扣费后的债务 │ │ -│ │ │ │ -│ │ ⑦ 铸造USDY │ │ -│ │ USDY.mint(_receiver, 995) │ │ -│ │ • 手续费3个代币留在池中 │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ -│ 返回: 995 USDY │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 检查 USDY 数量 - │ require(995 >= 997) ❌ 会失败 - │ (实际应该传入更小的 minUsdy) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 回到 YTPoolManager - 铸造 ytLP │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ 计算铸造数量: │ │ -│ │ ytLPSupply = 100,000 │ │ -│ │ aumInUsdy = $100,200 │ │ -│ │ usdyAmount = $995 │ │ -│ │ │ │ -│ │ mintAmount = 995 × 100,000 / 100,200 = 993.03 ytLP │ │ -│ │ │ │ -│ │ 检查滑点保护: │ │ -│ │ require(993.03 >= 995) ❌ 会失败 │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ -│ 执行铸造: │ -│ YTLPToken.mint(user, 993.03) │ -│ lastAddedAt[user] = block.timestamp // 记录时间(冷却期) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 返回结果 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 用户收到 │ -│ ✓ 993.03 ytLP │ -│ ✓ 代表在池中的份额 │ -│ │ -│ 成本分析: │ -│ 存入: 1000 YT-A │ -│ 手续费: 3 YT-A (0.3%) │ -│ 价差损失: ~4 YT-A (0.4%) │ -│ 总成本: ~0.7% │ -└─────────────────────────────────────────────────────────────────────┘ -涉及的合约函数: -合约 函数 作用 -YTRewardRouter addLiquidity() 用户入口,处理代币转移 -YTPoolManager addLiquidityForAccount() 流动性管理,计算ytLP -YTPoolManager getAumInUsdy(true) 获取AUM(使用MaxPrice) -YTVault buyUSDY() 接收代币,铸造USDY -YTVault getPoolValue(true) 计算池子总价值 -YTVault getSwapFeeBasisPoints() 获取动态手续费率 -YTPriceFeed getPrice(_token, false) 获取MinPrice -USDY mint() 铸造USDY代币 -YTLPToken mint() 铸造ytLP代币 - -2. 移除流动性流程 -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 // 接收地址 │ -│ ) external nonReentrant whenNotPaused │ -│ │ -│ 修饰符检查: │ -│ ✓ nonReentrant - 防重入保护 │ -│ ✓ whenNotPaused - 暂停检查 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 调用 removeLiquidityForAccount() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTPoolManager.sol │ -│ ───────────────────────────────────────────────────────────────── │ -│ function removeLiquidityForAccount(...) │ -│ │ -│ 步骤: │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ ① 检查冷却期 │ │ -│ │ require(lastAddedAt[user] + 15分钟 <= now) │ │ -│ │ │ │ -│ │ ② 计算AUM(使用MinPrice,对用户保守) │ │ -│ │ aumInUsdy = getAumInUsdy(false) │ │ -│ │ • YTVault.getPoolValue(false) │ │ -│ │ • 使用MinPrice(下压价差) │ │ -│ │ • AUM = $99,800 │ │ -│ │ │ │ -│ │ ③ 计算USDY价值 │ │ -│ │ ytLPSupply = 100,000 │ │ -│ │ usdyAmount = 1000 × 99,800 / 100,000 = $998 │ │ -│ │ │ │ -│ │ ④ 检查并铸造USDY(如果余额不足) │ │ -│ │ balance = USDY.balanceOf(PoolManager) │ │ -│ │ if (998 > balance) { │ │ -│ │ USDY.mint(PoolManager, 998 - balance) │ │ -│ │ } │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 销毁ytLP - │ YTLPToken.burn(user, 1000) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ ytLP 销毁完成 │ -│ 用户的ytLP余额 -1000 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 调用 sellUSDY() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTVault.sol │ -│ ───────────────────────────────────────────────────────────────── │ -│ function sellUSDY(address _token, address _receiver) │ -│ │ -│ 步骤: │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ ① transferIn(usdy) │ │ -│ │ • 接收998 USDY │ │ -│ │ │ │ -│ │ ② 获取价格(使用MaxPrice,高估需支付的价值) │ │ -│ │ price = _getPrice(_token, true) │ │ -│ │ • 基础价格: $1.00 │ │ -│ │ • MaxPrice: $1.002 (上浮0.2%价差) │ │ -│ │ │ │ -│ │ ③ 计算理论赎回数量 │ │ -│ │ redemptionAmount = 998 / 1.002 = 996.01 YT-B │ │ -│ │ │ │ -│ │ ④ 获取赎回手续费率 │ │ -│ │ feeBps = _getSwapFeeBasisPoints(_token, 996.01) │ │ -│ │ • 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 _getSwapFeeBasisPoints() 获取赎回手续费率 -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 // 接收地址 │ -│ ) external nonReentrant whenNotPaused │ -│ │ -│ 修饰符检查: │ -│ ✓ nonReentrant - 防重入保护 │ -│ ✓ whenNotPaused - 暂停检查 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. transferFrom(user → YTVault) - │ 转移1000个YT-A直接到Vault - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTVault.sol │ -│ ───────────────────────────────────────────────────────────────── │ -│ function swap( │ -│ address _tokenIn, // YT-A │ -│ address _tokenOut, // YT-B │ -│ address _receiver // 用户 │ -│ ) │ -│ │ -│ 步骤: │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ ① 安全检查 │ │ -│ │ ✓ swap已启用 │ │ -│ │ ✓ tokenIn已白名单 │ │ -│ │ ✓ tokenOut已白名单 │ │ -│ │ ✓ tokenIn != tokenOut │ │ -│ │ │ │ -│ │ ② transferIn(_tokenIn) │ │ -│ │ • 检测转入的YT-A数量: 1000 │ │ -│ │ │ │ -│ │ ③ 检查单笔交易限额 │ │ -│ │ if (maxSwapAmount[tokenIn] > 0) { │ │ -│ │ require(1000 <= maxSwapAmount[tokenIn]) │ │ -│ │ } │ │ -│ │ │ │ -│ │ ④ 获取价格(对协议有利的定价) │ │ -│ │ priceIn = _getPrice(_tokenIn, false) // MinPrice │ │ -│ │ = $0.998 (低估输入) │ │ -│ │ priceOut = _getPrice(_tokenOut, true) // MaxPrice │ │ -│ │ = $1.002 (高估输出) │ │ -│ │ │ │ -│ │ ⑤ 计算理论输出(扣费前) │ │ -│ │ 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() 全局滑点保护 -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% │ │ │ -│ │ │ USDC (原有) │ 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代币需要实现 ytPrice() 接口 │ -│ • 价格预言机会自动读取该接口获取YT价格 │ -│ • USDC价格自动从Chainlink获取,无需手动配置 │ -└─────────────────────────────────────────────────────────────────────┘ - │ - │ 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 IYTAssetVault { │ -│ // 必需:返回当前YT资产价格(30位精度) │ -│ function ytPrice() external view returns (uint256); │ -│ │ -│ // 可选:返回最后价格更新时间 │ -│ function lastPriceUpdate() external view returns (uint256); │ -│ │ -│ // ERC20标准接口 │ -│ function decimals() external view returns (uint8); │ -│ function balanceOf(address) external view returns (uint256); │ -│ function transfer(address, uint256) external returns (bool); │ -│ function transferFrom(address, address, uint256) │ -│ external returns (bool); │ -│ } │ -│ │ -│ YT价格示例 (1e30精度): │ -│ $1.00 = 1 × 10^30 = 1000000000000000000000000000000 │ -│ $0.998 = 998000000000000000000000000000 │ -│ │ -│ USDC价格(从Chainlink获取,1e8精度): │ -│ $1.00 = 100000000 (1e8) │ -│ $0.998 = 99800000 │ -│ 自动转换为1e30精度用于内部计算 │ -└─────────────────────────────────────────────────────────────────────┘ - -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% │ │ │ -│ │ │ USDC │ 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计算正确 - -总结:合约调用关系 -Plain Text - ┌─────────────────┐ - │ User │ - └────────┬────────┘ - │ - ┌────────▼────────┐ - │ YTRewardRouter │ ◄── 用户入口 - │ • addLiquidity │ - │ • removeLiq... │ - │ • swapYT │ - └────────┬────────┘ - │ - ┌────────▼────────┐ - │ YTPoolManager │ ◄── 流动性管理 - │ • addLiq... │ - │ • removeLiq... │ - │ • getAumInUsdy │ - └────────┬────────┘ - │ - ┌───────────────────┼───────────────────┐ - │ │ │ - ┌────▼────┐ ┌──────▼──────┐ ┌────▼─────┐ - │ YTVault │ │ YTLPToken │ │ USDY │ - │ • buyUS │ │ • mint │ │ • mint │ - │ • sellU │ │ • burn │ │ • burn │ - │ • swap │ └─────────────┘ └──────────┘ - └────┬────┘ - │ - ┌────▼────────┐ - │ YTPriceFeed │ ◄── 价格预言机 - │ • getPrice │ - │ • 价差配置 │ - └─────────────┘ -核心合约职责: -合约 职责 关键功能 -YTRewardRouter 用户入口 接收用户请求,处理代币转移 -YTPoolManager 流动性管理 计算ytLP,管理AUM -YTVault 资金池 存储资产,执行swap,管理手续费 -YTPriceFeed 价格预言机 提供价格,应用价差 -USDY 计价代币 内部记账单位 -YTLPToken LP代币 代表用户份额 - -6. 系统部署和初始化流程 -6.1 部署 YTPriceFeed(使用 Chainlink) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 部署者 (Deployer) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 准备 USDC 和 Chainlink 地址 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 准备外部合约地址 │ -│ ───────────────────────────────────────────────────────────────── │ -│ • USDC 代币地址(BSC主网) │ -│ usdc = 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d │ -│ 注意:BSC主网USDC是18位精度 │ -│ │ -│ • Chainlink USDC/USD 价格预言机地址(BSC主网) │ -│ usdcPriceFeed = 0x51597f405303C4377E36123cBc172b13269EA163 │ -│ 精度:1e8(Chainlink标准) │ -│ 价格示例:$1.00 = 100000000 (1e8) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 部署 YTPriceFeed(传入USDC和Chainlink地址) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 部署 YTPriceFeed(可升级) │ -│ ───────────────────────────────────────────────────────────────── │ -│ const YTPriceFeed = await ethers.getContractFactory("YTPriceFeed") │ -│ const priceFeed = await upgrades.deployProxy( │ -│ YTPriceFeed, │ -│ [ │ -│ usdcAddress, // ← USDC代币地址 │ -│ usdcPriceFeedAddress // ← Chainlink预言机地址 │ -│ ], │ -│ { │ -│ kind: "uups", │ -│ initializer: "initialize" │ -│ } │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. initialize(address _usdc, address _usdcPriceFeed) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTPriceFeed.initialize() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function initialize( │ -│ address _usdc, │ -│ address _usdcPriceFeed │ -│ ) external initializer │ -│ │ -│ 步骤: │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ ① __UUPSUpgradeable_init() │ │ -│ │ • 初始化UUPS升级功能 │ │ -│ │ │ │ -│ │ ② 验证USDC地址 │ │ -│ │ if (_usdc == address(0)) revert InvalidAddress() │ │ -│ │ ✓ 0x8AC... 有效 │ │ -│ │ │ │ -│ │ ③ 验证Chainlink预言机地址 │ │ -│ │ if (_usdcPriceFeed == address(0)) revert InvalidAddress()│ │ -│ │ ✓ 0x515... 有效 │ │ -│ │ │ │ -│ │ ④ 保存地址 │ │ -│ │ usdc = _usdc │ │ -│ │ usdcPriceFeed = AggregatorV3Interface(_usdcPriceFeed) │ │ -│ │ │ │ -│ │ ⑤ 设置治理地址 │ │ -│ │ gov = msg.sender (部署者) │ │ -│ │ │ │ -│ │ ⑥ 设置默认参数 │ │ -│ │ maxPriceChangeBps = 500 // 5% 最大价格变动 │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 初始化完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTPriceFeed 就绪 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 状态: │ -│ • usdc: 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d ✓ │ -│ • usdcPriceFeed: 0x51597f405303C4377E36123cBc172b13269EA163 ✓ │ -│ • gov: 已设置 ✓ │ -│ • maxPriceChangeBps: 500 (5%) │ -│ │ -│ 价格获取方式: │ -│ • USDC价格:从Chainlink实时获取(自动更新) │ -│ • YT代币价格:从YTAssetVault.ytPrice()读取(需keeper更新) │ -│ │ -│ 优势: │ -│ ✓ USDC价格实时准确(Chainlink提供) │ -│ ✓ 无需手动更新USDC价格 │ -│ ✓ 价格数据去中心化,更可靠 │ -│ ✓ 支持价格验证(负数/零值检查) │ -└─────────────────────────────────────────────────────────────────────┘ -6.2 完整系统部署流程 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 系统部署顺序 │ -│ │ -│ 步骤 1: 准备 USDC 和 Chainlink 地址 │ -│ ├─→ USDC: 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d (BSC主网) │ -│ └─→ Chainlink USDC/USD: 0x51597f405303C4377E36123cBc172b13269EA163 │ -│ │ -│ 步骤 2: 部署代币合约 │ -│ ├─→ USDY.initialize() │ -│ └─→ YTLPToken.initialize() │ -│ │ -│ 步骤 3: 部署 YTPriceFeed │ -│ └─→ YTPriceFeed.initialize(usdcAddress, usdcPriceFeedAddress) │ -│ │ -│ 步骤 4: 部署 YTVault │ -│ └─→ YTVault.initialize(usdyAddress, priceFeedAddress) │ -│ │ -│ 步骤 5: 部署 YTPoolManager │ -│ └─→ YTPoolManager.initialize( │ -│ vaultAddress, │ -│ usdyAddress, │ -│ ytLPAddress, │ -│ cooldownDuration │ -│ ) │ -│ │ -│ 步骤 6: 部署 YTRewardRouter │ -│ └─→ YTRewardRouter.initialize( │ -│ usdyAddress, │ -│ ytLPAddress, │ -│ poolManagerAddress, │ -│ vaultAddress │ -│ ) │ -│ │ -│ 步骤 7: 配置权限 │ -│ ├─→ usdy.addVault(vaultAddress) │ -│ ├─→ usdy.addVault(poolManagerAddress) │ -│ ├─→ ytlp.setMinter(poolManagerAddress, true) │ -│ ├─→ vault.setPoolManager(poolManagerAddress) │ -│ ├─→ vault.setSwapper(routerAddress, true) │ -│ └─→ poolManager.setHandler(routerAddress, true) │ -│ │ -│ 步骤 8: 配置 YTPriceFeed │ -│ ├─→ priceFeed.setKeeper(keeperAddress, true) │ -│ ├─→ priceFeed.setMaxPriceChangeBps(500) │ -│ └─→ 注意:USDC价格自动从Chainlink获取,无需手动设置 │ -│ │ -│ 步骤 9: 配置 YTVault 参数 │ -│ ├─→ vault.setSwapFees(30, 4, 50, 20) │ -│ ├─→ vault.setDynamicFees(true) │ -│ └─→ vault.setMaxSwapSlippageBps(1000) │ -│ │ -│ 步骤 10: 添加白名单代币 │ -│ ├─→ vault.setWhitelistedToken(ytTokenA, 18, 4000, maxAmount, false) │ -│ ├─→ vault.setWhitelistedToken(ytTokenB, 18, 3000, maxAmount, false) │ -│ └─→ vault.setWhitelistedToken(ytTokenC, 18, 2000, maxAmount, false) │ -│ │ -│ 步骤 11: 初始化价格 │ -│ ├─→ priceFeed.forceUpdatePrice(ytTokenA, 1e30) │ -│ ├─→ priceFeed.forceUpdatePrice(ytTokenB, 1e30) │ -│ └─→ priceFeed.forceUpdatePrice(ytTokenC, 1e30) │ -│ │ -│ ✓ 系统部署完成,可以开始使用 │ -└─────────────────────────────────────────────────────────────────────┘ - -7. 路由器暂停功能流程 -7.1 暂停路由器 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Gov (系统管理员) │ -│ 检测到: 需要紧急暂停用户操作 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 router.pause() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTRewardRouter.pause() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function pause() external onlyGov │ -│ │ -│ 权限检查: │ -│ ✓ onlyGov - 只有治理地址可调用 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 执行暂停 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Pausable._pause() │ -│ ───────────────────────────────────────────────────────────────── │ -│ internal _pause() │ -│ • 设置 paused = true │ -│ • 触发 Paused(msg.sender) 事件 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 暂停生效 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Router已暂停 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 被阻止的操作(revert EnforcedPause): │ -│ ✗ addLiquidity() - 用户无法添加流动性 │ -│ ✗ removeLiquidity() - 用户无法移除流动性 │ -│ ✗ swapYT() - 用户无法进行代币互换 │ -│ │ -│ 仍可用的操作: │ -│ ✓ getYtLPPrice() - 查询ytLP价格 │ -│ ✓ getAccountValue() - 查询账户价值 │ -│ │ -│ 系统状态: │ -│ • 所有用户资金操作暂停 │ -│ • 底层 YTVault 和 YTPoolManager 仍然运行 │ -│ • 直接调用 PoolManager 也会被阻止(权限检查) │ -│ • 查询功能不受影响 │ -│ │ -│ 用户影响: │ -│ • 无法通过Router进行任何交易 │ -│ • 资产安全锁定 │ -│ • 可以查看余额和价值 │ -└─────────────────────────────────────────────────────────────────────┘ -7.2 恢复路由器 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Gov (系统管理员) │ -│ 问题已解决,恢复正常运行 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 router.unpause() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTRewardRouter.unpause() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function unpause() external onlyGov │ -│ │ -│ 权限检查: │ -│ ✓ onlyGov - 只有治理地址可调用 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 执行恢复 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Pausable._unpause() │ -│ ───────────────────────────────────────────────────────────────── │ -│ internal _unpause() │ -│ • 设置 paused = false │ -│ • 触发 Unpaused(msg.sender) 事件 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 恢复完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Router恢复正常 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 所有功能恢复: │ -│ ✓ addLiquidity() - 用户可以添加流动性 │ -│ ✓ removeLiquidity() - 用户可以移除流动性 │ -│ ✓ swapYT() - 用户可以进行代币互换 │ -│ │ -│ 系统状态: │ -│ • 所有操作恢复正常 │ -│ • 用户可以继续交易 │ -│ • 暂停期间的数据和状态完全保留 │ -└─────────────────────────────────────────────────────────────────────┘ -7.3 多层暂停策略 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 紧急情况处理策略 │ -│ │ -│ 场景 1: 轻度风险 - 仅暂停Router │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ 适用场景: │ -│ • Router合约发现问题 │ -│ • 前端交互异常 │ -│ • 用户操作需要临时限制 │ -│ │ -│ 操作: │ -│ router.pause() │ -│ │ -│ 影响: │ -│ • 用户无法通过Router操作 │ -│ • YTVault、YTPoolManager 继续运行 │ -│ • 其他集成方可能仍可直接调用 PoolManager(如有权限) │ -│ │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ 场景 2: 中度风险 - 暂停Router + 启用Vault紧急模式 │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ 适用场景: │ -│ • Vault合约发现潜在问题 │ -│ • 需要全面阻止交易 │ -│ • 准备系统升级 │ -│ │ -│ 操作: │ -│ router.pause() │ -│ vault.setEmergencyMode(true) │ -│ │ -│ 影响: │ -│ • Router完全暂停 │ -│ • Vault的swap、buyUSDY、sellUSDY等操作全部阻止 │ -│ • 系统几乎完全冻结 │ -│ │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ 场景 3: 高度风险 - 全面暂停(Router + 所有YTAssetVaults) │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ 适用场景: │ -│ • 发现重大安全漏洞 │ -│ • YTAssetVault代币价格异常 │ -│ • 系统性风险 │ -│ │ -│ 操作: │ -│ router.pause() │ -│ vault.setEmergencyMode(true) │ -│ factory.pauseVaultBatch([ytTokenA, ytTokenB, ytTokenC]) │ -│ │ -│ 影响: │ -│ • 整个系统完全冻结 │ -│ • 用户无法进行任何资金操作 │ -│ • YTAssetVault的存款/提款也被暂停 │ -│ • 最大程度保护用户资产 │ -└─────────────────────────────────────────────────────────────────────┘ -7.4 暂停功能的最佳实践 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 使用暂停功能的建议 │ -│ │ -│ 何时使用暂停: │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ 1️⃣ 安全事件响应 │ -│ • 发现安全漏洞或异常行为 │ -│ • 立即暂停相关操作 │ -│ • 评估影响范围 │ -│ • 修复后恢复 │ -│ │ -│ 2️⃣ 系统维护升级 │ -│ • 计划性系统升级 │ -│ • 提前公告用户 │ -│ • 暂停操作执行升级 │ -│ • 测试验证后恢复 │ -│ │ -│ 3️⃣ 市场异常波动 │ -│ • 价格剧烈波动 │ -│ • 流动性枯竭 │ -│ • 暂停防止损失扩大 │ -│ • 市场稳定后恢复 │ -│ │ -│ 4️⃣ 合规要求 │ -│ • 监管调查配合 │ -│ • 临时限制操作 │ -│ • 保持透明沟通 │ -│ │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ 操作流程: │ -│ ✓ 发现问题 → 立即评估 │ -│ ✓ 确定暂停范围(Router / Vault / 全系统) │ -│ ✓ 执行暂停命令 │ -│ ✓ 公告用户(如可能) │ -│ ✓ 调查和修复 │ -│ ✓ 测试验证 │ -│ ✓ 恢复系统 │ -│ ✓ 监控运行 │ -│ │ -│ 注意事项: │ -│ ⚠️ 暂停不影响资产价值和余额 │ -│ ⚠️ 查询功能始终可用 │ -│ ⚠️ 暂停期间可以更新价格(仅gov) │ -│ ⚠️ 恢复前应充分测试 │ -│ ⚠️ 保持与用户的沟通透明 │ -└─────────────────────────────────────────────────────────────────────┘ diff --git a/ytLp用户前端交互文档.txt b/ytLp用户前端交互文档.txt deleted file mode 100644 index 59e56bd..0000000 --- a/ytLp用户前端交互文档.txt +++ /dev/null @@ -1,230 +0,0 @@ -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/ytVault-USDC金库系统操作流程文档.txt b/ytVault-USDC金库系统操作流程文档.txt deleted file mode 100644 index 58c3174..0000000 --- a/ytVault-USDC金库系统操作流程文档.txt +++ /dev/null @@ -1,2085 +0,0 @@ -ytVault-USDC金库系统操作流程文档 -重要说明 -使用 USDC 作为支付代币,USDC 价格通过 Chainlink 价格预言机实时获取。 - -目录 -1.[创建Vault流程](#1-创建vault流程) -2.[用户存款流程(depositYT)](#2-用户存款流程deposityt) -3.[用户提款流程(withdrawYT - 两阶段提现)](#3-用户提款流程withdrawyt---两阶段提现) -○3.1 [第一阶段:提交提现请求](#31-第一阶段提交提现请求) -○3.2 [第二阶段:批量处理提现请求](#32-第二阶段批量处理提现请求) -○3.3 [资金不足时的处理](#33-资金不足时的处理) -4.[价格更新流程](#4-价格更新流程) -5.[资产管理流程 - 提取投资](#5-资产管理流程---提取投资) -6.[资产管理流程 - 归还资产](#6-资产管理流程---归还资产) -7.[批量操作流程](#7-批量操作流程) -8.[查询信息流程](#8-查询信息流程) -9.[暂停功能流程](#9-暂停功能流程) - -1. 创建Vault流程 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Owner (系统管理员) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 createVault() - │ name: "YT-A Token" - │ symbol: "YT-A" - │ manager: 0x123... - │ hardCap: 1,000,000 YT - │ usdc: 0x7Cd...(或0使用默认BSC USDC) - │ redemptionTime: 2025-02-15 00:00:00 - │ initialYtPrice: 1.05e30 - │ usdcPriceFeed: 0xABC...(Chainlink价格Feed) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 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..., // usdc地址 │ -│ 1739577600, // redemptionTime (Unix时间戳) │ -│ 1050000000000000000000000000000, // initialYtPrice (1.05e30) │ -│ 0xABC... // usdcPriceFeed地址 │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 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 │ -│ usdcAddress = 0x7Cd... │ -│ usdcPriceFeed = 0xABC... (Chainlink价格Feed) │ -│ usdcDecimals = 18 (从USDC合约读取,BSC主网为18) │ -│ │ -│ ⑤ __Pausable_init() │ -│ • 初始化暂停功能(默认未暂停) │ -│ │ -│ ⑥ 设置价格(精度1e30) │ -│ ytPrice = 1.05e30 (初始YT价格1.05) │ -│ 注意:USDC价格通过Chainlink实时获取,不存储 │ -│ │ -│ ⑦ 设置赎回时间 │ -│ 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 USDC │ -│ • ytPrice: 1.05 (精度1e30) │ -│ • usdcPrice: 通过Chainlink实时获取 (~$1.00, 精度1e8) │ -│ • hardCap: 1,000,000 YT │ -│ • nextRedemptionTime: 2025-02-15 00:00:00 │ -│ • 可接受用户存款 ✓ │ -└─────────────────────────────────────────────────────────────────────┘ - -2. 用户存款流程(depositYT) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 用户 (User) │ -│ 持有: 10,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 0. 预览操作(可选) - │ 调用 previewBuy(10000e18) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.sol │ -│ ───────────────────────────────────────────────────────────────── │ -│ function previewBuy(10000e18) returns (uint256 ytAmount) │ -│ │ -│ 计算逻辑: │ -│ ytAmount = (usdcAmount × usdcPrice) ÷ ytPrice │ -│ = (10,000 × 1.05e30) ÷ 1.05e30 │ -│ = 10,000 YT │ -│ │ -│ 返回预览结果: 10,000 YT │ -└─────────────────────────────────────────────────────────────────────┘ - │ - │ 1. 授权USDC给Vault - │ USDC.approve(vault, 10000e18) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ USDC授权检查 │ -│ ✓ allowance[user][vault] >= 10,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 调用 depositYT(10000e18) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.depositYT() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function depositYT(uint256 _usdcAmount) │ -│ • 非重入保护: nonReentrant │ -│ • 暂停检查: whenNotPaused │ -│ • 参数: 10,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 参数验证 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 参数检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① _usdcAmount > 0 │ -│ ✓ 10,000 > 0 通过 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 计算可获得的YT数量 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 计算YT数量 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 公式:ytAmount = (usdcAmount × usdcPrice) ÷ ytPrice │ -│ │ -│ 当前价格: │ -│ • usdcPrice = 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. 转入USDC(CEI模式 - Checks完成) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移(Effects) │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(usdcAddress).safeTransferFrom( │ -│ msg.sender, // 用户地址 │ -│ address(this), // Vault地址 │ -│ 10000e18 // 转入10,000 USDC │ -│ ) │ -│ │ -│ 结果: │ -│ • 用户USDC余额: 10,000 → 0 │ -│ • Vault USDC余额: 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, // USDC数量 │ -│ 10000e18 // YT数量 │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 9. 返回YT数量 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 存款完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 用户最终状态: │ -│ • USDC余额: 0 │ -│ • YT余额: 10,000 YT │ -│ │ -│ Vault最终状态: │ -│ • totalSupply: 10,000 YT │ -│ • totalAssets: 10,000 USDC │ -│ • idleAssets: 10,000 USDC │ -│ • managedAssets: 0 USDC │ -│ │ -│ 返回值: 10,000 YT │ -└─────────────────────────────────────────────────────────────────────┘ - -3. 用户提款流程(withdrawYT - 两阶段提现) -3.1 第一阶段:提交提现请求 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 用户 (User) │ -│ 持有: 5,000 YT │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 0. 预览操作(可选) - │ 调用 previewSell(5000e18) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.sol │ -│ ───────────────────────────────────────────────────────────────── │ -│ function previewSell(5000e18) returns (uint256 usdcAmount) │ -│ │ -│ 计算逻辑: │ -│ usdcAmount = (ytAmount × ytPrice) ÷ usdcPrice │ -│ = (5,000 × 1.05e30) ÷ 1.05e30 │ -│ = 5,000 USDC │ -│ │ -│ 返回预览结果: 5,000 USDC │ -└─────────────────────────────────────────────────────────────────────┘ - │ - │ 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) returns (uint256 requestId) │ -│ • 非重入保护: nonReentrant │ -│ • 暂停检查: whenNotPaused │ -│ • 参数: 5,000 YT │ -│ │ -│ 🔴 重要变化:此函数不再立即发放USDC! │ -│ 只创建提现请求,进入排队等待 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 多重验证 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 参数检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① _ytAmount > 0 │ -│ ✓ 5,000 > 0 通过 │ -│ │ -│ ② balanceOf(msg.sender) >= _ytAmount │ -│ ✓ 10,000 >= 5,000 通过 │ -│ (用户持有足够的YT) │ -│ │ -│ ③ block.timestamp >= nextRedemptionTime │ -│ ✓ 2025-02-16 >= 2025-02-15 通过 │ -│ (已到赎回时间) │ -│ │ -│ ⚠️ 不再检查流动性!即使vault中USDC不足也可以提交请求 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 计算应得的USDC数量 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 计算USDC数量 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 公式:usdcAmount = (ytAmount × ytPrice) ÷ usdcPrice │ -│ │ -│ 假设价格已更新: │ -│ • usdcPrice = 1.00e30 (USDC价格回落到1.0) │ -│ • ytPrice = 1.10e30 (YT价格上涨到1.1) │ -│ │ -│ 计算过程: │ -│ usdcAmount = (5,000e18 × 1.10e30) ÷ 1.00e30 │ -│ = 5,500e18 │ -│ = 5,500 USDC │ -│ │ -│ 💡 这个金额会锁定在请求中,不受后续价格变化影响 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 立即销毁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 │ -│ │ -│ ⚠️ 注意:YT已销毁,但USDC还未发放! │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 创建提现请求记录 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 创建WithdrawRequest │ -│ ───────────────────────────────────────────────────────────────── │ -│ requestId = requestIdCounter // 当前假设为 requestId = 42 │ -│ │ -│ withdrawRequests[42] = WithdrawRequest({ │ -│ user: msg.sender, // 用户地址 │ -│ ytAmount: 5000e18, // 5,000 YT │ -│ usdcAmount: 5500e18, // 应得5,500 USDC │ -│ requestTime: block.timestamp, // 当前时间戳 │ -│ queueIndex: 42, // 队列位置 │ -│ processed: false // 未处理 │ -│ }) │ -│ │ -│ userRequestIds[msg.sender].push(42) // 记录到用户请求列表 │ -│ requestIdCounter++ // 43 │ -│ pendingRequestsCount++ // 待处理计数+1 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit WithdrawRequestCreated( │ -│ 42, // requestId │ -│ msg.sender, // 用户地址 │ -│ 5000e18, // YT数量 │ -│ 5500e18, // 应得USDC数量 │ -│ 42 // queueIndex │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 返回请求ID - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 请求提交完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 返回值: requestId = 42 │ -│ │ -│ 用户当前状态: │ -│ • YT余额: 5,000 YT (已减少) │ -│ • USDC余额: 0 USDC (尚未到账) ⏳ │ -│ • 提现请求: requestId = 42 (排队中) │ -│ │ -│ Vault状态: │ -│ • totalSupply: 5,000 YT (已减少) │ -│ • pendingRequestsCount: +1 │ -│ • requestIdCounter: 43 │ -│ │ -│ 📍 下一步: │ -│ 用户需要等待Manager或Factory调用processBatchWithdrawals() │ -│ 批量处理提现请求后,USDC才会到账 │ -│ │ -│ 查询请求状态: │ -│ • getRequestDetails(42) - 查看请求详情 │ -│ • getUserPendingRequests(user) - 查看所有待处理请求 │ -│ • getQueueProgress() - 查看队列处理进度 │ -└─────────────────────────────────────────────────────────────────────┘ -3.2 第二阶段:批量处理提现请求 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Manager/Factory (资产管理方) │ -│ 当前状态: │ -│ • Vault中有足够的USDC (基金赎回资金已到账) │ -│ • 待处理请求: 150个 │ -│ • 准备批量发放USDC给用户 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 查询队列状态(可选) - │ getQueueProgress() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 查询队列进度 │ -│ ───────────────────────────────────────────────────────────────── │ -│ function getQueueProgress() returns ( │ -│ uint256 currentIndex, // 当前处理到的位置: 100 │ -│ uint256 totalRequests, // 总请求数: 250 │ -│ uint256 pendingRequests // 待处理数: 150 │ -│ ) │ -│ │ -│ 状态信息: │ -│ • 已处理: 100个请求 │ -│ • 待处理: 150个请求 │ -│ • 处理进度: 100/250 = 40% │ -└─────────────────────────────────────────────────────────────────────┘ - │ - │ 2. 调用 processBatchWithdrawals(50) - │ 每批处理50个请求 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.processBatchWithdrawals() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function processBatchWithdrawals(uint256 _batchSize) │ -│ returns (uint256 processedCount, uint256 totalDistributed) │ -│ │ -│ 参数: │ -│ • _batchSize: 50 (本批次最多处理50个) │ -│ │ -│ 权限检查: │ -│ • msg.sender == manager ✓ 或 │ -│ • msg.sender == factory ✓ │ -│ │ -│ 保护机制: │ -│ • nonReentrant - 重入保护 │ -│ • whenNotPaused - 暂停检查 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 验证批次大小 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 参数检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (_batchSize == 0) revert InvalidBatchSize() │ -│ ✓ 50 > 0 通过 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 获取可用USDC并开始循环处理 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 循环处理请求 │ -│ ───────────────────────────────────────────────────────────────── │ -│ uint256 availableUSDC = vault.balance(USDC) // 假设: 100,000 USDC │ -│ uint256 processedCount = 0 │ -│ uint256 totalDistributed = 0 │ -│ │ -│ for (i = processedUpToIndex; i < requestIdCounter; i++) { │ -│ if (processedCount >= _batchSize) break // 达到批次限制 │ -│ │ -│ WithdrawRequest storage request = withdrawRequests[i] │ -│ │ -│ if (request.processed) continue // 跳过已处理的 │ -│ │ -│ if (availableUSDC >= request.usdcAmount) { │ -│ // ✅ 可以处理此请求 │ -│ 处理逻辑 ▼ │ -│ } else { │ -│ // ❌ USDC不足,停止处理 │ -│ break │ -│ } │ -│ } │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 处理单个请求(循环内) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 处理请求详细步骤 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 以 requestId = 42 为例: │ -│ │ -│ ① 转账USDC给用户 │ -│ IERC20(usdc).safeTransfer(request.user, 5500e18) │ -│ • 用户USDC余额: 0 → 5,500 USDC ✅ │ -│ │ -│ ② 标记为已处理 │ -│ request.processed = true │ -│ │ -│ ③ 更新统计数据 │ -│ availableUSDC -= 5500e18 // 剩余可用USDC减少 │ -│ totalDistributed += 5500e18 // 累计发放增加 │ -│ processedCount++ // 处理计数+1 │ -│ pendingRequestsCount-- // 待处理计数-1 │ -│ │ -│ ④ 触发事件 │ -│ emit WithdrawRequestProcessed(42, user, 5500e18) │ -│ │ -│ 然后继续处理下一个请求... │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 更新处理进度指针 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新processedUpToIndex │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (processedCount > 0) { │ -│ // 找到下一个未处理的位置 │ -│ for (i = processedUpToIndex; i < requestIdCounter; i++) { │ -│ if (!withdrawRequests[i].processed) { │ -│ processedUpToIndex = i │ -│ break │ -│ } │ -│ } │ -│ } │ -│ │ -│ 更新结果: │ -│ • processedUpToIndex: 100 → 150 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 触发批次处理事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 批次处理完成事件 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit BatchProcessed( │ -│ 100, // startIndex (开始位置) │ -│ 150, // endIndex (结束位置) │ -│ 50, // processedCount (实际处理数量) │ -│ 275000e18 // totalDistributed (总发放USDC) │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 8. 返回处理结果 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 批量处理完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 返回值: │ -│ • processedCount: 50 (处理了50个请求) │ -│ • totalDistributed: 275,000 USDC (总共发放) │ -│ │ -│ 更新后的状态: │ -│ • pendingRequestsCount: 150 → 100 (还剩100个待处理) │ -│ • processedUpToIndex: 100 → 150 │ -│ • Vault USDC余额: 100,000 → (100,000 - 275,000) = -175,000 ❌ │ -│ (这里假设vault有足够资金,实际会提前检查) │ -│ │ -│ 用户影响(requestId = 42的用户): │ -│ ✅ YT余额: 5,000 YT (已销毁) │ -│ ✅ USDC余额: 5,500 USDC (已到账!) │ -│ ✅ 提现完成,可以自由使用USDC │ -│ │ -│ 队列状态: │ -│ • 已处理: 150/250 = 60% │ -│ • 待处理: 100个 │ -│ • 可继续调用processBatchWithdrawals()处理剩余请求 │ -└─────────────────────────────────────────────────────────────────────┘ -3.3 资金不足时的处理 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 场景:USDC资金不足 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 当前状态: │ -│ • Vault中USDC: 50,000 │ -│ • 待处理请求: 100个 │ -│ • 前10个请求需要: 60,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 调用 processBatchWithdrawals(100) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 处理过程 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 循环处理: │ -│ • Request 1: 需要5,000 USDC ✅ 处理成功 (剩余45,000) │ -│ • Request 2: 需要5,000 USDC ✅ 处理成功 (剩余40,000) │ -│ • Request 3: 需要5,000 USDC ✅ 处理成功 (剩余35,000) │ -│ • Request 4: 需要5,000 USDC ✅ 处理成功 (剩余30,000) │ -│ • Request 5: 需要5,000 USDC ✅ 处理成功 (剩余25,000) │ -│ • Request 6: 需要5,000 USDC ✅ 处理成功 (剩余20,000) │ -│ • Request 7: 需要5,000 USDC ✅ 处理成功 (剩余15,000) │ -│ • Request 8: 需要5,000 USDC ✅ 处理成功 (剩余10,000) │ -│ • Request 9: 需要5,000 USDC ✅ 处理成功 (剩余5,000) │ -│ • Request 10: 需要5,000 USDC ✅ 处理成功 (剩余0) │ -│ • Request 11: 需要5,000 USDC ❌ 资金不足,停止处理 │ -│ │ -│ 处理结果: │ -│ • processedCount: 10 (只处理了10个,而不是100个) │ -│ • totalDistributed: 50,000 USDC │ -│ • 剩余90个请求继续排队 │ -│ │ -│ ⚠️ 不会revert,优雅地停止! │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 后续处理 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 等待资金到账 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Manager操作: │ -│ ① 等待基金赎回下一批资金到账 │ -│ ② 调用 depositManagedAssets(100000e18) 充值 │ -│ ③ 再次调用 processBatchWithdrawals(100) 继续处理 │ -│ │ -│ 用户体验: │ -│ • Request 1-10的用户: ✅ USDC已到账 │ -│ • Request 11+的用户: ⏳ 继续排队等待 │ -│ • 可通过getUserPendingRequests()查询状态 │ -└─────────────────────────────────────────────────────────────────────┘ - -4. 价格更新流程 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Price Oracle / Manager │ -│ YT价格更新: │ -│ • YT-A新价格: $1.15 │ -│ 注意:USDC价格通过Chainlink自动获取,无需手动更新 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 方式1: Factory更新 - │ 调用 factory.updateVaultPrices() - │ - │ 方式2: 直接更新(仅Factory) - │ 调用 vault.updatePrices() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.updatePrices() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function updatePrices( │ -│ uint256 _ytPrice // 1.15e30 │ -│ ) external onlyFactory │ -│ │ -│ 权限检查: │ -│ • msg.sender == factory ✓ │ -│ 注意:只有Factory可以调用,Manager无权限 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 价格验证 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 价格有效性检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (_ytPrice == 0) │ -│ revert InvalidPrice() │ -│ │ -│ 检查: │ -│ • _ytPrice = 1.15e30 ≠ 0 ✓ │ -│ │ -│ 注意: │ -│ • 价格必须 > 0 │ -│ • 价格精度为1e30 │ -│ • 没有时间间隔限制,可随时更新 │ -│ • USDC价格由Chainlink提供,实时获取 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 更新价格状态 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新存储状态 │ -│ ───────────────────────────────────────────────────────────────── │ -│ ytPrice = _ytPrice │ -│ │ -│ 更新前: │ -│ • ytPrice: 1.05e30 → 1.15e30 (上涨9.52%) │ -│ │ -│ USDC价格说明: │ -│ • USDC价格不存储在合约中 │ -│ • 每次交易时从Chainlink实时获取 │ -│ • Chainlink价格精度:1e8 (如 $1.00 = 100000000) │ -│ • 自动转换为合约内部计算精度 │ -│ │ -│ 影响: │ -│ ① 后续depositYT计算变化 │ -│ ytAmount = usdcAmount × usdcPrice(Chainlink) × 10^22 / ytPrice │ -│ → 用户用相同USDC可能获得更少YT │ -│ │ -│ ② 后续withdrawYT计算变化 │ -│ usdcAmount = ytAmount × ytPrice / (usdcPrice(Chainlink) × 10^22)│ -│ → 用户用相同YT可能获得更多USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit PriceUpdated( │ -│ 1150000000000000000000000000000, // ytPrice │ -│ 1739692800 // timestamp │ -│ ) │ -│ │ -│ 链下监听: │ -│ • 前端可监听此事件更新UI显示 │ -│ • 用户可看到最新的YT价格 │ -│ • USDC价格可直接从Chainlink获取 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 完成更新 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 更新完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 当前兑换比率示例(假设USDC = $1.00): │ -│ │ -│ 存款 (depositYT): │ -│ • 1,000 USDC → (1,000 × 1.00 × 10^22) / 1.15e30 = 869.57 YT │ -│ │ -│ 提款 (withdrawYT): │ -│ • 1,000 YT → (1,000 × 1.15e30) / (1.00 × 10^22) = 1,150 USDC │ -│ │ -│ YT持有者收益: │ -│ • 价格从1.05到1.15,增值9.52% │ -│ • 持有1,000 YT相当于价值1,150 USDC(在USDC=$1时) │ -└─────────────────────────────────────────────────────────────────────┘ - -5. 资产管理流程 - 提取投资 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Manager (资产管理员) │ -│ 计划: 提取50,000 USDC进行外部投资 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 withdrawForManagement() - │ _to: manager地址 - │ _amount: 50,000 USDC - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.withdrawForManagement() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function withdrawForManagement( │ -│ address _to, // 0x123... (manager) │ -│ uint256 _amount // 50,000 USDC │ -│ ) external onlyManager nonReentrant whenNotPaused │ -│ │ -│ 权限检查: │ -│ ✓ onlyManager - 只有manager可调用 │ -│ ✓ nonReentrant - 重入保护 │ -│ ✓ whenNotPaused - 暂停检查 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 参数验证 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 验证检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ ① if (_amount == 0) revert InvalidAmount() │ -│ ✓ 50,000 > 0 通过 │ -│ │ -│ ② uint256 availableAssets = vault.balance(USDC) │ -│ if (_amount > availableAssets) revert InvalidAmount() │ -│ │ -│ Vault当前状态: │ -│ • totalAssets: 100,000 USDC │ -│ • idleAssets: 100,000 USDC (全部在vault中) │ -│ • managedAssets: 0 USDC │ -│ │ -│ 检查: │ -│ • availableAssets = 100,000 USDC │ -│ • 50,000 ≤ 100,000 ✓ 通过 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 更新managedAssets(CEI - Effects) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 状态更新 │ -│ ───────────────────────────────────────────────────────────────── │ -│ managedAssets += _amount │ -│ │ -│ 更新: │ -│ • managedAssets: 0 → 50,000 USDC │ -│ │ -│ 重要说明: │ -│ managedAssets记录了被管理员提取、正在进行外部投资的USDC数量 │ -│ 这部分资产不在vault合约中,但仍计入totalAssets │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 转出USDC(Interactions) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移 │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(usdcAddress).safeTransfer( │ -│ _to, // manager地址 │ -│ 50000e18 // 50,000 USDC │ -│ ) │ -│ │ -│ 转账结果: │ -│ • Vault USDC余额: 100,000 → 50,000 USDC │ -│ • Manager USDC余额: +50,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit AssetsWithdrawn( │ -│ 0x123..., // manager地址 │ -│ 50000e18 // 提取数量 │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 提取完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 提取完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Vault最终状态: │ -│ • totalAssets(): 100,000 USDC (不变!) │ -│ 计算: idleAssets + managedAssets │ -│ = 50,000 + 50,000 = 100,000 │ -│ │ -│ • idleAssets(): 50,000 USDC │ -│ (vault合约实际持有的USDC) │ -│ │ -│ • managedAssets: 50,000 USDC │ -│ (manager正在管理的USDC) │ -│ │ -│ • totalSupply: 100,000 YT (不变) │ -│ │ -│ 用户影响: │ -│ ✓ totalAssets不变,YT价值不受影响 │ -│ ✓ 用户依然持有相同价值的YT份额 │ -│ ✗ 暂时无法提款(流动性不足),需等待manager归还 │ -│ │ -│ Manager后续操作: │ -│ → 用50,000 USDC进行DeFi投资 │ -│ → 赚取收益 │ -│ → 通过depositManagedAssets归还 │ -└─────────────────────────────────────────────────────────────────────┘ - -6. 资产管理流程 - 归还资产 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Manager (资产管理员) │ -│ 投资收益情况: │ -│ • 提取: 50,000 USDC │ -│ • 投资收益: +5,000 USDC │ -│ • 准备归还: 55,000 USDC (本金+利润) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 授权USDC给Vault - │ USDC.approve(vault, 55000e18) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ USDC授权检查 │ -│ ✓ allowance[manager][vault] >= 55,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 调用 depositManagedAssets() - │ _amount: 55,000 USDC - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.depositManagedAssets() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function depositManagedAssets( │ -│ uint256 _amount // 55,000 USDC │ -│ ) external onlyManager nonReentrant whenNotPaused │ -│ │ -│ 权限检查: │ -│ ✓ onlyManager - 只有manager可调用 │ -│ ✓ nonReentrant - 重入保护 │ -│ ✓ whenNotPaused - 暂停检查 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 参数验证 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 验证检查 │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (_amount == 0) revert InvalidAmount() │ -│ ✓ 55,000 > 0 通过 │ -│ │ -│ 当前Vault状态: │ -│ • idleAssets: 50,000 USDC │ -│ • managedAssets: 50,000 USDC │ -│ • totalAssets: 100,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 4. 更新managedAssets(CEI - Effects) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 状态更新(关键逻辑) │ -│ ───────────────────────────────────────────────────────────────── │ -│ if (_amount >= managedAssets) { │ -│ // 归还金额 >= 提取金额,清零managedAssets │ -│ managedAssets = 0 │ -│ } else { │ -│ // 归还金额 < 提取金额,部分归还 │ -│ managedAssets -= _amount │ -│ } │ -│ │ -│ 本例计算: │ -│ • _amount = 55,000 USDC │ -│ • managedAssets = 50,000 USDC │ -│ • 55,000 >= 50,000 ✓ 进入第一个分支 │ -│ • managedAssets = 0 │ -│ │ -│ 多余的5,000 USDC如何处理? │ -│ → 自动增加到vault余额,成为利润 │ -│ → totalAssets会增加 │ -│ → 所有YT持有者共享这部分收益 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 5. 转入USDC(Interactions) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 代币转移 │ -│ ───────────────────────────────────────────────────────────────── │ -│ IERC20(usdcAddress).safeTransferFrom( │ -│ msg.sender, // manager地址 │ -│ address(this), // vault地址 │ -│ 55000e18 // 55,000 USDC │ -│ ) │ -│ │ -│ 转账结果: │ -│ • Manager USDC余额: -55,000 USDC │ -│ • Vault USDC余额: 50,000 → 105,000 USDC │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 6. 触发事件 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 事件记录 │ -│ ───────────────────────────────────────────────────────────────── │ -│ emit AssetsDeposited( │ -│ 55000e18 // 归还数量 │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 7. 归还完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 归还完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ Vault最终状态: │ -│ • totalAssets(): 105,000 USDC (增加了5,000!) │ -│ 计算: idleAssets + managedAssets │ -│ = 105,000 + 0 = 105,000 │ -│ │ -│ • idleAssets(): 105,000 USDC │ -│ (vault合约实际持有的USDC,包含收益) │ -│ │ -│ • managedAssets: 0 USDC │ -│ (所有资产已归还) │ -│ │ -│ • totalSupply: 100,000 YT (不变) │ -│ │ -│ 收益分配: │ -│ • 投资收益: 5,000 USDC │ -│ • 收益率: 5,000 / 50,000 = 10% │ -│ • 每个YT的价值提升: │ -│ 之前: 100,000 USDC / 100,000 YT = 1.0 USDC/YT │ -│ 现在: 105,000 USDC / 100,000 YT = 1.05 USDC/YT │ -│ │ -│ 用户影响: │ -│ ✓ 所有YT持有者自动获得5%增值 │ -│ ✓ 可以提款了(流动性恢复) │ -│ ✓ 如果用户提取YT,会按1.05的比例获得更多USDC │ -│ │ -│ 示例: │ -│ 用户持有10,000 YT,提取时可获得: │ -│ (10,000 × 1.05 USDC/YT) = 10,500 USDC │ -│ 相比初始存入的10,000 USDC,获利500 USDC (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] - │ • usdc: 0x7Cd... (USDC代币地址) - │ • redemptionTimes: [time1, time2, time3] - │ • initialYtPrices: [1.05e30, 1.02e30, 1.10e30] - │ • usdcPriceFeed: 0xABC... (Chainlink价格Feed) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 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 -┌─────────────────────────────────────────────────────────────────────┐ -│ Price Oracle / Owner │ -│ 接收到3个vault的最新YT价格数据 │ -│ 注意:USDC价格通过Chainlink自动获取,无需批量更新 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 调用 updateVaultPricesBatch() - │ • vaults: [0xVault001, 0xVault002, 0xVault003] - │ • ytPrices: [1.15e30, 1.08e30, 1.20e30] - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetFactory.updateVaultPricesBatch() │ -│ ───────────────────────────────────────────────────────────────── │ -│ 1. 参数验证 │ -│ require(数组长度相等) │ -│ ✓ 所有数组长度都是3 │ -│ │ -│ 2. 循环更新 │ -│ for (i = 0; i < 3; i++) { │ -│ YTAssetVault(vaults[i]).updatePrices( │ -│ ytPrices[i] │ -│ ) │ -│ emit PricesUpdated(vaults[i], ytPrices[i]) │ -│ } │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - ├─────────┬─────────┐ - │ │ │ - Vault001 │ Vault002 │ Vault003 - 价格更新 │ 价格更新 │ 价格更新 - ▼ │ ▼ │ ▼ -┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ -│ YT: 1.15 │ │ YT: 1.08 │ │ YT: 1.20 │ -│ 涨幅: +9.52% │ │ 涨幅: +2.88% │ │ 涨幅: +9.09% │ -│ USDC: Chainlink │ │ USDC: Chainlink │ │ USDC: Chainlink │ -└───────────────────┘ └───────────────────┘ └───────────────────┘ - │ │ │ - └─────────┴─────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 批量更新完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 优势: │ -│ ✓ 一次交易更新多个vault的YT价格 │ -│ ✓ 节省gas费用 │ -│ ✓ 确保所有vault价格同时更新 │ -│ ✓ 适合定时任务批量更新 │ -│ ✓ USDC价格由Chainlink提供,无需手动维护 │ -│ │ -│ 触发的事件: │ -│ • PricesUpdated × 3 (from factory) │ -│ • PriceUpdated × 3 (from vaults) │ -└─────────────────────────────────────────────────────────────────────┘ -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信息](#81-查询单个vault信息) -•8.2 [通过Factory查询Vault信息](#82-通过factory查询vault信息) -•8.3 [查询所有Vault列表](#83-查询所有vault列表) -•8.4 [查询提现请求信息(新增)](#84-查询提现请求信息新增) -8.1 查询单个Vault信息 -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 前端应用 / 查询者 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 调用 vault.getVaultInfo() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.getVaultInfo() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function getVaultInfo() returns ( │ -│ uint256 _totalAssets, │ -│ uint256 _idleAssets, │ -│ uint256 _managedAssets, │ -│ uint256 _totalSupply, │ -│ uint256 _hardCap, │ -│ uint256 _usdcPrice, │ -│ uint256 _ytPrice, │ -│ uint256 _nextRedemptionTime │ -│ ) │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 读取状态 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 返回完整信息 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 返回示例: │ -│ { │ -│ totalAssets: 105000e18, // 105,000 USDC │ -│ idleAssets: 105000e18, // 105,000 USDC │ -│ managedAssets: 0, // 0 USDC │ -│ totalSupply: 100000e18, // 100,000 YT │ -│ hardCap: 1000000e18, // 1,000,000 YT │ -│ usdcPrice: 1020000000000000000000000000000, // 1.02 │ -│ ytPrice: 1150000000000000000000000000000, // 1.15 │ -│ nextRedemptionTime: 1739577600 // 2025-02-15 00:00:00 │ -│ } │ -│ │ -│ 计算衍生指标: │ -│ • 每YT价值: 105,000 / 100,000 = 1.05 USDC │ -│ • 资金利用率: (105,000 - 0) / 105,000 = 100% │ -│ • 硬顶使用率: 100,000 / 1,000,000 = 10% │ -│ • 当前兑换率: 1 USDC = 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 usdcPrice, │ -│ 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, │ -│ usdcPrice: 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消耗 │ -│ ✓ 改善前端加载性能 │ -└─────────────────────────────────────────────────────────────────────┘ -8.4 查询提现请求信息(新增) -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 用户 / 前端应用 │ -│ 需求:查询提现请求的状态 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - ├──────────────┬──────────────┬──────────────┐ - │ │ │ │ - 方式1: 查询单个请求 方式2: 查询用户请求 方式3: 查询队列进度 - │ │ │ │ - ▼ ▼ ▼ ▼ -┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ -│ getRequestDetails │ │getUserRequestIds │ │ getQueueProgress │ -│ (requestId) │ │(userAddress) │ │() │ -└──────────┬──────────┘ └──────────┬──────────┘ └──────────┬──────────┘ - │ │ │ - ▼ ▼ ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 方式1: 查询单个请求详情 │ -│ ───────────────────────────────────────────────────────────────── │ -│ function getRequestDetails(uint256 _requestId) │ -│ returns (WithdrawRequest memory) │ -│ │ -│ 输入: requestId = 42 │ -│ │ -│ 返回: │ -│ { │ -│ user: 0xUser123..., │ -│ ytAmount: 5000e18, // 提现的YT数量 │ -│ usdcAmount: 5500e18, // 应得USDC数量 │ -│ requestTime: 1739692800, // 请求时间戳 │ -│ queueIndex: 42, // 队列位置 │ -│ processed: false // 是否已处理 │ -│ } │ -│ │ -│ 前端展示: │ -│ ┌────────────────────────────────────────┐ │ -│ │ 📋 提现请求 #42 │ │ -│ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │ -│ │ 状态: ⏳ 排队中 │ │ -│ │ YT数量: 5,000 YT │ │ -│ │ 应得USDC: 5,500 USDC │ │ -│ │ 提交时间: 2025-02-16 10:00:00 │ │ -│ │ 队列位置: 第42位 │ │ -│ └────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 方式2: 查询用户所有请求 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 步骤1: 获取用户的请求ID列表 │ -│ function getUserRequestIds(address _user) │ -│ returns (uint256[] memory) │ -│ │ -│ 返回: [15, 42, 68] (用户有3个请求) │ -│ │ -│ 步骤2: 获取用户待处理的请求 │ -│ function getUserPendingRequests(address _user) │ -│ returns (WithdrawRequest[] memory) │ -│ │ -│ 返回: 只返回 processed = false 的请求 │ -│ [ │ -│ { │ -│ user: 0xUser123..., │ -│ ytAmount: 5000e18, │ -│ usdcAmount: 5500e18, │ -│ requestTime: 1739692800, │ -│ queueIndex: 42, │ -│ processed: false │ -│ } │ -│ ] │ -│ │ -│ 前端展示: │ -│ ┌────────────────────────────────────────┐ │ -│ │ 👤 我的提现请求 │ │ -│ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │ -│ │ ✅ 请求#15 1,000 YT → 1,100 USDC │ │ -│ │ 状态: 已完成 2025-02-10 │ │ -│ │ │ │ -│ │ ⏳ 请求#42 5,000 YT → 5,500 USDC │ │ -│ │ 状态: 排队中 队列第42位 │ │ -│ │ 提交于: 2025-02-16 10:00 │ │ -│ │ │ │ -│ │ ⏳ 请求#68 3,000 YT → 3,300 USDC │ │ -│ │ 状态: 排队中 队列第68位 │ │ -│ │ 提交于: 2025-02-16 15:30 │ │ -│ └────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 方式3: 查询队列处理进度 │ -│ ───────────────────────────────────────────────────────────────── │ -│ function getQueueProgress() returns ( │ -│ uint256 currentIndex, │ -│ uint256 totalRequests, │ -│ uint256 pendingRequests │ -│ ) │ -│ │ -│ 返回示例: │ -│ { │ -│ currentIndex: 38, // 当前处理到第38个 │ -│ totalRequests: 150, // 总共150个请求 │ -│ pendingRequests: 112 // 还有112个待处理 │ -│ } │ -│ │ -│ 衍生计算: │ -│ • 已处理: 38个 │ -│ • 处理进度: 38/150 = 25.3% │ -│ • 待处理: 112个 │ -│ │ -│ 前端展示: │ -│ ┌────────────────────────────────────────┐ │ -│ │ 🔄 提现处理进度 │ │ -│ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │ -│ │ [████████░░░░░░░░░░░░░░░] 25.3% │ │ -│ │ │ │ -│ │ 已处理: 38 / 150 │ │ -│ │ 待处理: 112个请求 │ │ -│ │ 当前位置: 第38位 │ │ -│ │ │ │ -│ │ 💡 提示: │ │ -│ │ 您的请求#42在第42位,前面还有4个请求 │ │ -│ │ 预计等待时间: 约5分钟 │ │ -│ └────────────────────────────────────────┘ │ -│ │ -│ 优势: │ -│ ✓ 实时查看全局处理进度 │ -│ ✓ 估算自己的等待时间 │ -│ ✓ O(1)查询,gas友好 │ -└─────────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 方式4: 查询待处理总数 │ -│ ───────────────────────────────────────────────────────────────── │ -│ function getPendingRequestsCount() returns (uint256) │ -│ │ -│ 返回: 112 (还有112个待处理) │ -│ │ -│ 特点: │ -│ • O(1)复杂度,实时维护的计数器 │ -│ • 不需要循环,避免gas爆炸 │ -│ • 可用于前端显示统计信息 │ -│ │ -│ 前端展示: │ -│ ┌────────────────────────────────────────┐ │ -│ │ 📊 系统统计 │ │ -│ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │ -│ │ 待处理提现: 112个 │ │ -│ │ Vault余额: 50,000 USDC │ │ -│ │ 预计可处理: 约45个请求 │ │ -│ └────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────────┘ - -附录:重要概念说明 -A. 价格精度和Chainlink集成 -Plain Text -YT价格精度 (PRICE_PRECISION): -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -精度: 1e30 (10^30) - -示例: -• 价格 1.0 表示为: 1000000000000000000000000000000 (1e30) -• 价格 1.05 表示为: 1050000000000000000000000000000 (1.05e30) -• 价格 0.98 表示为: 980000000000000000000000000000 (0.98e30) - -为什么使用1e30而不是1e18? -✓ 更高的精度,减少舍入误差 -✓ 适合复杂的价格计算 -✓ 支持更精确的价格波动 - - -Chainlink USDC价格精度: -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -精度: 1e8 (Chainlink标准) - -示例: -• $1.00 表示为: 100000000 -• $1.05 表示为: 105000000 -• $0.98 表示为: 98000000 - -特点: -✓ 实时从Chainlink预言机获取 -✓ 去中心化价格源,安全可靠 -✓ 自动处理int256到uint256的转换 -✓ 验证价格为正数(price > 0) - - -转换因子 (CONVERSION_FACTOR): -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -动态计算: 10^ytDecimals × 10^30 / (10^usdcDecimals × 10^8) - -对于BSC USDC (18位) 和 YT (18位): -转换因子 = 10^18 × 10^30 / (10^18 × 10^8) = 10^22 - -作用: -✓ 统一不同精度之间的计算 -✓ 支持不同decimals的USDC实现 -✓ 动态从代币合约读取decimals -✓ 避免硬编码,提高灵活性 -B. 兑换计算公式 -Plain Text -重要说明: -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -• USDC价格从Chainlink获取,精度1e8 (如 $1.00 = 100000000) -• YT价格存储在合约中,精度1e30 -• USDC在BSC主网为18位小数 (1 USDC = 10^18) -• YT代币为18位小数 (1 YT = 10^18) -• 转换因子 = 10^22 (用于精度转换) - -存款 (depositYT): -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -ytAmount = (usdcAmount × usdcPrice × 10^22) ÷ ytPrice - -其中: -• usdcAmount: USDC数量 (18位小数) -• usdcPrice: Chainlink价格 (8位精度) -• ytPrice: YT价格 (30位精度) -• 10^22: 转换因子 - -示例: -• 存入1,000 USDC (1000 × 10^18) -• usdcPrice = 100000000 ($1.00, Chainlink格式) -• ytPrice = 1.10e30 -• ytAmount = (1000e18 × 100000000 × 10^22) ÷ 1.10e30 -• = 909.09e18 YT - - -提款 (withdrawYT): -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -usdcAmount = (ytAmount × ytPrice) ÷ (usdcPrice × 10^22) - -示例: -• 提取1,000 YT (1000 × 10^18) -• ytPrice = 1.10e30 -• usdcPrice = 100000000 ($1.00, Chainlink格式) -• usdcAmount = (1000e18 × 1.10e30) ÷ (100000000 × 10^22) -• = 1100e18 USDC - - -关键点: -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -✓ USDC价格实时从Chainlink获取,确保准确性 -✓ 转换因子自动处理不同精度之间的转换 -✓ 支持USDC价格波动(稳定币微小波动) -✓ 计算在Solidity中使用整数运算,无浮点数 -C. 资产状态计算 -Plain Text -Vault资产状态: -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -totalAssets = idleAssets + managedAssets - -• idleAssets: vault合约中实际持有的USDC -• managedAssets: manager提取、正在投资的USDC -• totalAssets: 用户可赎回的总价值 - -示例: -vault余额: 50,000 USDC -managedAssets: 50,000 USDC -totalAssets: 100,000 USDC (不变) - -用户持有10,000 YT,价值: -(10,000 / 100,000) × 100,000 = 10,000 USDC -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 -F. CEI模式 -Plain Text -CEI模式 (Checks-Effects-Interactions): -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -安全的合约编写模式,防止重入攻击 - -1. Checks (检查) - • 验证参数 - • 检查权限 - • 检查余额 - -2. Effects (状态更新) - • 更新合约状态 - • 修改余额 - • 记录变化 - -3. Interactions (外部交互) - • 转账代币 - • 调用外部合约 - • 触发事件 - -配合nonReentrant modifier,确保安全 -G. Chainlink价格预言机集成 -Plain Text -Chainlink集成说明: -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -系统使用Chainlink作为USDC价格数据源 - -初始化时设置: -• 合约初始化时传入 usdcPriceFeed 地址 -• 该地址指向Chainlink的USDC/USD价格Feed -• 例如:Chainlink USDC/USD on BSC - -价格获取流程: -┌──────────────────────────────────┐ -│ 用户调用 depositYT() 或 │ -│ withdrawYT() │ -└──────────┬───────────────────────┘ - │ - ▼ -┌──────────────────────────────────┐ -│ 合约调用 _getUSDCPrice() │ -└──────────┬───────────────────────┘ - │ - ▼ -┌──────────────────────────────────┐ -│ 调用 usdcPriceFeed │ -│ .latestRoundData() │ -└──────────┬───────────────────────┘ - │ - ▼ -┌──────────────────────────────────┐ -│ 返回价格数据: │ -│ • int256 price (如: 100000000) │ -│ • roundId, timestamps等元数据 │ -└──────────┬───────────────────────┘ - │ - ▼ -┌──────────────────────────────────┐ -│ 价格验证: │ -│ • if (price <= 0) revert │ -│ • 确保价格为正 │ -└──────────┬───────────────────────┘ - │ - ▼ -┌──────────────────────────────────┐ -│ 类型转换: │ -│ • int256 → uint256 │ -│ • 返回 uint256(price) │ -└──────────┬───────────────────────┘ - │ - ▼ -┌──────────────────────────────────┐ -│ 用于计算: │ -│ • ytAmount = usdcAmount × │ -│ usdcPrice × 10^22 / ytPrice │ -└──────────────────────────────────┘ - -安全特性: -✓ 价格验证:拒绝负数或零价格 -✓ 实时更新:每次交易都获取最新价格 -✓ 去中心化:Chainlink多节点共识 -✓ 类型安全:安全的int256→uint256转换 - -优势: -✓ 无需手动更新USDC价格 -✓ 价格始终准确反映市场 -✓ 减少管理员操作负担 -✓ 提高系统自动化程度 - -错误处理: -• InvalidChainlinkPrice: 价格≤0时触发 -• 交易自动revert,保护用户资金 -H. UUPS升级模式 -Plain Text -UUPS (Universal Upgradeable Proxy Standard): -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -可升级合约的标准实现方式 - -结构: -┌─────────────┐ -│ ERC1967Proxy│ ← 用户交互的地址(不变) -└──────┬──────┘ - │ delegatecall - ▼ -┌─────────────┐ -│ Implementation│ ← 逻辑合约(可升级) -└─────────────┘ - -优势: -✓ 合约地址不变 -✓ 状态数据保留 -✓ 可以修复bug和添加功能 -✓ 升级权限由Factory控制 - -注意: -• 不能在状态变量声明时初始化 -• 必须使用initialize函数 -• 保留__gap数组用于未来扩展 -I. 两阶段提现机制(Withdraw Queue) -Plain Text -两阶段提现机制: -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -解决资金分批到账的提现排队问题 - -设计目的: -• 基金赎回需要时间,资金不会一次性到账 -• 按用户提现请求时间先后排队(FIFO) -• 资金到账后由后端统一批量发放 - -阶段一:用户提交请求 -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -用户调用 withdrawYT(_ytAmount) - -执行内容: -1. 验证参数和赎回时间 -2. 立即销毁用户的YT代币 -3. 计算应得USDC数量(锁定当前价格) -4. 创建WithdrawRequest记录 -5. 返回requestId供用户查询 - -关键特点: -✓ YT立即销毁(防止重复提现) -✓ USDC暂不发放(等待批量处理) -✓ 金额已锁定(不受后续价格变化影响) -✓ 即使vault资金不足也可提交请求 - -阶段二:批量处理发放 -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Manager/Factory调用 processBatchWithdrawals(_batchSize) - -执行内容: -1. 获取vault当前可用USDC -2. 按requestId顺序(FIFO)处理请求 -3. 依次给用户转账USDC -4. 标记请求为已处理 -5. 资金不足时自动停止 - -关键特点: -✓ FIFO严格保证(先提交先处理) -✓ 支持分批处理(避免gas超限) -✓ 断点续传(记录处理进度) -✓ 资金不足不会revert(优雅停止) - -数据结构: -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -struct WithdrawRequest { - address user; // 用户地址 - uint256 ytAmount; // YT数量 - uint256 usdcAmount; // 应得USDC数量(锁定) - uint256 requestTime; // 请求时间 - uint256 queueIndex; // 队列位置 - bool processed; // 是否已处理 -} - -状态变量: -• withdrawRequests[requestId] - 所有请求记录 -• userRequestIds[user] - 用户的请求ID列表 -• requestIdCounter - 请求ID计数器(递增) -• processedUpToIndex - 已处理到的位置 -• pendingRequestsCount - 待处理请求数(O(1)查询) - -查询功能: -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -• getUserRequestIds(user) - 用户的所有请求ID -• getRequestDetails(requestId) - 请求详情 -• getUserPendingRequests(user) - 用户待处理请求 -• getPendingRequestsCount() - 待处理总数 -• getQueueProgress() - 队列处理进度 - -优势: -✓ 解决资金分批到账问题 -✓ 公平的FIFO排队机制 -✓ Gas优化(计数器避免循环) -✓ 用户体验好(可查询状态) -✓ 管理灵活(支持分批处理) - -使用场景: -1. 基金赎回需要T+1或T+N到账 -2. 资金分多批次回流 -3. 避免流动性挤兑 -4. 统一管理提现流程 - -风险控制: -⚠️ YT已销毁但USDC未到账的风险 - → 解决:Manager有责任及时处理请求 - → 解决:Factory可以代为处理 - -⚠️ 用户长时间等待的风险 - → 解决:可查询队列进度 - → 解决:前端显示预计等待时间 - -⚠️ 价格锁定可能错失市场波动 - → 解决:这是设计特性,确保公平性 - -9. 暂停功能流程 -9.1 暂停单个Vault -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Factory Owner (紧急情况) │ -│ 检测到: YT-A Vault 存在安全问题 │ -└────────────────────────────────┬────────────────────────────────────┘ - │ - │ 1. 调用 pauseVault(vaultAddress) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetFactory.pauseVault() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function pauseVault(address _vault) external onlyOwner │ -│ │ -│ 权限检查: │ -│ ✓ onlyOwner - 只有Factory owner可调用 │ -│ ✓ isVault[_vault] - 验证vault存在 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 调用 vault.pause() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.pause() │ -│ ───────────────────────────────────────────────────────────────── │ -│ internal _pause() │ -│ • 设置 paused = true │ -│ • 触发 Paused 事件 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 暂停生效 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Vault已暂停 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 被阻止的操作(revert EnforcedPause): │ -│ ✗ depositYT() - 用户无法存款 │ -│ ✗ withdrawYT() - 用户无法提款 │ -│ ✗ withdrawForManagement() - Manager无法提取资产 │ -│ ✗ depositManagedAssets() - Manager无法归还资产 │ -│ │ -│ 仍可用的操作: │ -│ ✓ balanceOf() - 查询余额 │ -│ ✓ totalSupply() - 查询总供应 │ -│ ✓ totalAssets() - 查询总资产 │ -│ ✓ getVaultInfo() - 查询详细信息 │ -│ ✓ previewBuy() - 预览存款 │ -│ ✓ previewSell() - 预览提款 │ -│ ✓ canRedeemNow() - 检查赎回状态 │ -│ │ -│ 用户影响: │ -│ • 所有资金操作暂停 │ -│ • 资产安全锁定 │ -│ • 可以查看但无法操作 │ -└─────────────────────────────────────────────────────────────────────┘ -9.2 恢复单个Vault -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Factory Owner (问题已解决) │ -│ 安全问题已修复,可以恢复正常运行 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 unpauseVault(vaultAddress) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetFactory.unpauseVault() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function unpauseVault(address _vault) external onlyOwner │ -│ │ -│ 权限检查: │ -│ ✓ onlyOwner - 只有Factory owner可调用 │ -│ ✓ isVault[_vault] - 验证vault存在 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 2. 调用 vault.unpause() - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetVault.unpause() │ -│ ───────────────────────────────────────────────────────────────── │ -│ internal _unpause() │ -│ • 设置 paused = false │ -│ • 触发 Unpaused 事件 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 3. 恢复完成 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Vault恢复正常 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 所有功能恢复: │ -│ ✓ depositYT() - 用户可以存款 │ -│ ✓ withdrawYT() - 用户可以提款(如已过赎回时间) │ -│ ✓ withdrawForManagement() - Manager可以提取资产 │ -│ ✓ depositManagedAssets() - Manager可以归还资产 │ -│ │ -│ 系统状态: │ -│ • 所有操作恢复正常 │ -│ • 用户资产未受影响 │ -│ • 暂停期间的价格更新仍然有效 │ -└─────────────────────────────────────────────────────────────────────┘ -9.3 批量暂停Vaults -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Factory Owner (系统级紧急) │ -│ 检测到系统性风险,需要暂停多个Vault │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 pauseVaultBatch(vaultAddresses) - │ vaults: [0xVault001, 0xVault002, 0xVault003] - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetFactory.pauseVaultBatch() │ -│ ───────────────────────────────────────────────────────────────── │ -│ function pauseVaultBatch(address[] memory _vaults) │ -│ │ -│ 批量处理: │ -│ for (i = 0; i < _vaults.length; i++) { │ -│ require(isVault[_vaults[i]]) // 验证每个vault │ -│ YTAssetVault(_vaults[i]).pause() │ -│ } │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - ├─────────┬─────────┐ - │ │ │ - Vault001 │ Vault002 │ Vault003 - 暂停 │ 暂停 │ 暂停 - ▼ │ ▼ │ ▼ -┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ -│ YT-A Vault │ │ YT-B Vault │ │ YT-C Vault │ -│ 状态: PAUSED │ │ 状态: PAUSED │ │ 状态: PAUSED │ -│ 所有操作冻结 │ │ 所有操作冻结 │ │ 所有操作冻结 │ -└───────────────────┘ └───────────────────┘ └───────────────────┘ - │ │ │ - └─────────┴─────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 批量暂停完成 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 优势: │ -│ ✓ 一次交易暂停多个vault │ -│ ✓ 节省gas费用 │ -│ ✓ 原子操作,全部成功或全部失败 │ -│ ✓ 快速响应系统性风险 │ -│ │ -│ 系统影响: │ -│ • 所有指定vault的资金操作立即冻结 │ -│ • 用户资产安全保护 │ -│ • 管理员可以调查和修复问题 │ -└─────────────────────────────────────────────────────────────────────┘ -9.4 批量恢复Vaults -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ Factory Owner (风险已消除) │ -│ 问题已解决,恢复所有暂停的Vault │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - │ 1. 调用 unpauseVaultBatch(vaultAddresses) - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ YTAssetFactory.unpauseVaultBatch() │ -│ ───────────────────────────────────────────────────────────────── │ -│ 批量恢复所有vault │ -│ • 验证每个vault存在 │ -│ • 调用每个vault的unpause() │ -│ • 原子操作执行 │ -└────────────────────────────┬────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 系统全面恢复 │ -│ ───────────────────────────────────────────────────────────────── │ -│ 所有vault恢复正常运行 │ -│ 用户可以继续所有操作 │ -└─────────────────────────────────────────────────────────────────────┘ -暂停功能的应用场景: -Plain Text -┌─────────────────────────────────────────────────────────────────────┐ -│ 使用场景和最佳实践 │ -│ │ -│ 何时使用暂停功能: │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ 1️⃣ 安全漏洞发现 │ -│ • 发现合约安全问题 │ -│ • 暂停受影响的vault │ -│ • 保护用户资产 │ -│ • 修复后恢复 │ -│ │ -│ 2️⃣ 价格异常 │ -│ • 价格预言机故障 │ -│ • 价格剧烈波动 │ -│ • 暂停交易防止损失 │ -│ • 价格稳定后恢复 │ -│ │ -│ 3️⃣ 系统升级 │ -│ • 准备合约升级 │ -│ • 暂停所有操作 │ -│ • 执行升级 │ -│ • 验证后恢复 │ -│ │ -│ 4️⃣ 流动性危机 │ -│ • Manager提取资金后未及时归还 │ -│ • 暂停提款防止挤兑 │ -│ • 等待资金回流 │ -│ • 流动性恢复后解除 │ -│ │ -│ 5️⃣ 监管要求 │ -│ • 配合监管调查 │ -│ • 临时冻结操作 │ -│ • 保留完整记录 │ -│ │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ 最佳实践: │ -│ ✓ 暂停前公告(如有时间) │ -│ ✓ 记录暂停原因 │ -│ ✓ 快速调查和修复 │ -│ ✓ 恢复前全面测试 │ -│ ✓ 恢复后公告说明 │ -│ │ -│ 注意事项: │ -│ ⚠️ 暂停不影响已持有的资产价值 │ -│ ⚠️ 暂停期间价格仍可更新(由Factory) │ -│ ⚠️ Manager在暂停期间也无法操作 │ -│ ⚠️ 查询功能不受影响 │ -└─────────────────────────────────────────────────────────────────────┘ diff --git a/设计大概要求.txt b/设计大概要求.txt deleted file mode 100644 index 88e0904..0000000 --- a/设计大概要求.txt +++ /dev/null @@ -1,77 +0,0 @@ -# assetx 后台内容 - -都需要admin 身份: - -后台: - -每次请求需要带token参数,然后把token参数去认证中心请求, - -```jsx -post/api/auth/validate-token -Shell Curl - -curl https://auth.upay01.com/api/auth/validate-token \ - --request POST \ - --header 'Authorization: Bearer YOUR_SECRET_TOKEN' -``` - -```jsx -curl https://auth.upay01.com/api/auth/validate-token \ - -X POST \ - -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2OTgzMGJhYzNiM2QzOTJkNjJlODU3NDMiLCJ1c2VybmFtZSI6ImdfU29maW8iLCJlbWFpbCI6ImF3ZWkwMTAzMjVAZ21haWwuY29tIiwicm9sZXMiOlsidXNlciJdLCJ0eXBlIjoiYWNjZXNzIiwiaXNzIjoibWF4aWZpZ2h0LWF1dGgiLCJzdWIiOiI2OTgzMGJhYzNiM2QzOTJkNjJlODU3NDMiLCJleHAiOjE3NzEyOTg1NTEsIm5iZiI6MTc3MDY5Mzc1MSwiaWF0IjoxNzcwNjkzNzUxLCJqdGkiOiI2N2Q2ZjM2Mi01NzE2LTQ3OWYtODc3OS02OGE3NTVhNDBjMmMifQ.5sUndYufoz816YHz4oSJT1yvVNdiycS32yMGCVMFF-s" -``` - -```jsx - -{"success":true,"message":"Token is valid","data":{"email":"awei010325@gmail.com","roles":["user"],"status":"active","tokenInfo":{"exp":"2026-02-17T03:22:31Z","iat":"2026-02-10T03:22:31Z","jti":"67d6f362-5716-479f-8779-68a755a40c2c"},"userID":1000000,"username":"g_Sofio"}}% sofio@sofiodeMac-Studio ~ % - -``` - -token在服务端,缓存5-60分钟 - -### 合约 - -合约交互(迁移旧版本的钱包交互的合约) - -### 数据 - -1, 资产信息 - -2、资产details - -3、overview - -4、APY数据是实时的,(通过合约去查询计算) - -5、APY快照(合约交互,每2小时,或者1小时,快照apy数据,存数据库,对应的合约地址→ APY数据,参考怎么去查看holder) - -6、人工传参数 - -7、asset detail → custody,(人工传信息,CRUD) - -8、/product footer 跳转链接, - -9、没有提到的,查看旧项目管理 - -### 10、ALP,Lending list,Liquidity Allocation是自己维护 - -11、有k线数据的地方也是快照 - -12、transparency先不做 - -13、points,(holders每个小时快照)→ holder持有A合约,控制积分倍率。YT、LP、Lending。holders的积分数据,根据现在的快照到数据库, - -1 - -### 邀请 - -1、web2的。生成:连接钱包、请求签名(类似于身份认证的token,确认身份,sign以后拿到hash值,有expired,7d,存用户localstore)→ 自动获取自己的邀请码 -2、绑定:前置也需要需要拿sign去绑定,如果没有sign,需要先sign - -3、升级:先不管 - -4、overview:总积分,RANK,前10排名 - -5、我的邀请:直推 - -6、每个操作对应一个积分规则,倍率规则。规则的信息, \ No newline at end of file