+
{t('lp.title')}
+
+ {/* Pool Info */}
+
+
+ {t('lp.rewardRouter')}:
+ {CONTRACTS.YT_REWARD_ROUTER}
+
+
+ {t('lp.ytLPToken')}:
+ {CONTRACTS.YT_LP_TOKEN}
+
+
+ {t('lp.poolAUM')}:
+ {aumInUsdy ? formatUnits(aumInUsdy, 18) : '0'} USDY
+
+
+ {t('lp.ytLPPrice')}:
+ {ytLPPrice ? formatUnits(ytLPPrice, 30) : '1'} USDY
+
+
+ {t('lp.totalSupply')}:
+ {ytLPTotalSupply ? formatUnits(ytLPTotalSupply, 18) : '0'} ytLP
+
+
+ {t('lp.yourBalance')}:
+ {ytLPBalance ? formatUnits(ytLPBalance, 18) : '0'} ytLP
+
+
+ {t('lp.cooldownRemaining')}:
+ {formatCooldown(getCooldownRemaining())}
+
+
+
+ {/* Tab Navigation */}
+
+
+
+
+
+
+ {/* Add Liquidity Form */}
+ {activeTab === 'add' && (
+
+
{t('lp.addLiquidity')}
+
{t('lp.addLiquidityDesc')}
+
+
+
+
+
+
+
+
+
+ setAddLiquidityForm({ ...addLiquidityForm, amount: e.target.value })}
+ placeholder="0.0"
+ className="input"
+ />
+
+
+
+
+ setAddLiquidityForm({ ...addLiquidityForm, slippage: e.target.value })}
+ placeholder="0.5"
+ className="input"
+ step="0.1"
+ />
+
+
+
+ {needsApproval() ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+ {/* Remove Liquidity Form */}
+ {activeTab === 'remove' && (
+
+
{t('lp.removeLiquidity')}
+
{t('lp.removeLiquidityDesc')}
+
+ {getCooldownRemaining() > 0 && (
+
+ {t('lp.cooldownWarning', { time: formatCooldown(getCooldownRemaining()) })}
+
+ )}
+
+
+
+
+
+
+
+
+
+ setRemoveLiquidityForm({ ...removeLiquidityForm, amount: e.target.value })}
+ placeholder="0.0"
+ className="input"
+ />
+
+
+
+
+
+ setRemoveLiquidityForm({ ...removeLiquidityForm, slippage: e.target.value })}
+ placeholder="1"
+ className="input"
+ step="0.1"
+ />
+
+
+
+ {needsYtLPApproval() ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+ {/* Swap Form */}
+ {activeTab === 'swap' && (
+
+
{t('lp.swapTokens')}
+
{t('lp.swapDesc')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setSwapForm({ ...swapForm, amount: e.target.value })}
+ placeholder="0.0"
+ className="input"
+ />
+
+
+
+
+ setSwapForm({ ...swapForm, slippage: e.target.value })}
+ placeholder="0.5"
+ className="input"
+ step="0.1"
+ />
+
+
+
+
+
+ )}
+
+ {/* Transaction History */}
+
+
+ )
+}
diff --git a/frontend/src/config/contracts.ts b/frontend/src/config/contracts.ts
index 99f8789..15cd647 100644
--- a/frontend/src/config/contracts.ts
+++ b/frontend/src/config/contracts.ts
@@ -5,7 +5,13 @@ export const CONTRACTS = {
YT_A: '0x0cA35994F033685E7a57ef9bc5d00dd3cf927330' as const,
YT_B: '0x333805C9EE75f59Aa2Cc79DfDe2499F920c7b408' as const,
YT_C: '0x6DF0ED6f0345F601A206974973dE9fC970598587' as const,
- }
+ },
+ // LP Pool contracts
+ YT_REWARD_ROUTER: '0x51eEF57eC57c867AC23945f0ce21aA5A9a2C246c' as const,
+ YT_LP_TOKEN: '0x1b96F219E8aeE557DD8bD905a6c72cc64eA5BD7B' as const,
+ YT_POOL_MANAGER: '0x14246886a1E1202cb6b5a2db793eF3359d536302' as const,
+ YT_VAULT: '0x19982e5145ca5401A1084c0BF916c0E0bB343Af9' as const,
+ USDY: '0x631Bd6834C50f6d2B07035c9253b4a19132E888c' as const,
}
export const FACTORY_ABI = [
@@ -508,3 +514,254 @@ export const WUSD_ABI = [
type: 'function'
}
] as const
+
+// YT Reward Router ABI - for addLiquidity, removeLiquidity, swapYT
+export const YT_REWARD_ROUTER_ABI = [
+ {
+ 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: '_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: 'getYtLPPrice',
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ type: 'function'
+ },
+ {
+ inputs: [{ internalType: 'address', name: '_account', type: 'address' }],
+ name: 'getAccountValue',
+ 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: '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'
+ }
+] as const
+
+// YT LP Token ABI (ERC20)
+export const YT_LP_TOKEN_ABI = [
+ {
+ inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
+ name: 'balanceOf',
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ type: 'function'
+ },
+ {
+ inputs: [],
+ name: 'totalSupply',
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ type: 'function'
+ },
+ {
+ inputs: [],
+ name: 'decimals',
+ outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }],
+ stateMutability: 'view',
+ type: 'function'
+ },
+ {
+ inputs: [],
+ name: 'symbol',
+ outputs: [{ internalType: 'string', name: '', type: 'string' }],
+ stateMutability: 'view',
+ type: 'function'
+ },
+ {
+ inputs: [],
+ name: 'name',
+ outputs: [{ internalType: 'string', name: '', type: 'string' }],
+ 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: 'owner', type: 'address' },
+ { internalType: 'address', name: 'spender', type: 'address' }
+ ],
+ name: 'allowance',
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ type: 'function'
+ }
+] as const
+
+// YT Pool Manager ABI
+export const YT_POOL_MANAGER_ABI = [
+ {
+ inputs: [{ internalType: 'bool', name: '_maximise', type: 'bool' }],
+ name: 'getAumInUsdy',
+ 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: 'address', name: '', type: 'address' }],
+ name: 'lastAddedAt',
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ type: 'function'
+ },
+ {
+ inputs: [],
+ name: 'gov',
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
+ stateMutability: 'view',
+ type: 'function'
+ }
+] as const
+
+// YT Vault ABI - for pool info
+export const YT_VAULT_ABI = [
+ {
+ inputs: [],
+ name: 'getAllWhitelistedTokens',
+ outputs: [{ internalType: 'address[]', name: '', type: 'address[]' }],
+ stateMutability: 'view',
+ type: 'function'
+ },
+ {
+ inputs: [{ internalType: 'address', name: '_token', type: 'address' }],
+ name: 'poolAmounts',
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ type: 'function'
+ },
+ {
+ inputs: [{ internalType: 'address', name: '_token', type: 'address' }],
+ name: 'usdyAmounts',
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ type: 'function'
+ },
+ {
+ inputs: [{ internalType: 'address', name: '_token', 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: '_token', type: 'address' }],
+ name: 'whitelistedTokens',
+ outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
+ 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: 'getMinPrice',
+ 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: [],
+ name: 'gov',
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
+ stateMutability: 'view',
+ type: 'function'
+ }
+] as const
diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json
index 134d6ae..cad91d9 100644
--- a/frontend/src/i18n/locales/en.json
+++ b/frontend/src/i18n/locales/en.json
@@ -18,7 +18,8 @@
"nav": {
"wusd": "WUSD",
"vaultTrading": "Vault Trading",
- "factory": "Factory"
+ "factory": "Factory",
+ "lpPool": "LP Pool"
},
"header": {
"title": "YT Asset Test"
@@ -147,5 +148,36 @@
"updatePriceNotOwnerDesc": "Non-owner calls updateVaultPrices",
"setManagerNotOwner": "Set Manager (Not Owner)",
"setManagerNotOwnerDesc": "Non-owner calls setVaultManager"
+ },
+ "lp": {
+ "title": "YT Liquidity Pool",
+ "rewardRouter": "Reward Router Contract",
+ "ytLPToken": "ytLP Token",
+ "poolAUM": "Pool AUM",
+ "ytLPPrice": "ytLP Price",
+ "totalSupply": "Total Supply",
+ "yourBalance": "Your Balance",
+ "cooldownRemaining": "Cooldown Remaining",
+ "noCooldown": "No cooldown",
+ "addLiquidity": "Add Liquidity",
+ "addLiquidityDesc": "Deposit YT tokens or WUSD to receive ytLP tokens",
+ "removeLiquidity": "Remove Liquidity",
+ "removeLiquidityDesc": "Burn ytLP to get tokens back",
+ "swapTokens": "Swap Tokens",
+ "swapDesc": "Swap between YT tokens and WUSD in the pool",
+ "selectToken": "Select Token",
+ "amount": "Amount",
+ "slippage": "Slippage Tolerance",
+ "approveToken": "Approve Token",
+ "approveYtLP": "Approve ytLP",
+ "outputToken": "Output Token",
+ "ytLPAmount": "ytLP Amount",
+ "max": "Max",
+ "fromToken": "From Token",
+ "toToken": "To Token",
+ "swap": "Swap",
+ "cooldownNotPassed": "Cooldown not passed, please try later",
+ "insufficientOutput": "Insufficient output amount",
+ "cooldownWarning": "Cooldown remaining {{time}}, cannot remove liquidity yet"
}
}
diff --git a/frontend/src/i18n/locales/zh.json b/frontend/src/i18n/locales/zh.json
index 888c4d7..987909a 100644
--- a/frontend/src/i18n/locales/zh.json
+++ b/frontend/src/i18n/locales/zh.json
@@ -18,7 +18,8 @@
"nav": {
"wusd": "WUSD",
"vaultTrading": "金库交易",
- "factory": "工厂管理"
+ "factory": "工厂管理",
+ "lpPool": "LP 流动池"
},
"header": {
"title": "YT 资产测试"
@@ -147,5 +148,36 @@
"updatePriceNotOwnerDesc": "非 Owner 调用 updateVaultPrices",
"setManagerNotOwner": "设置Manager(非Owner)",
"setManagerNotOwnerDesc": "非 Owner 调用 setVaultManager"
+ },
+ "lp": {
+ "title": "YT 流动性池",
+ "rewardRouter": "奖励路由合约",
+ "ytLPToken": "ytLP 代币",
+ "poolAUM": "池子总资产(AUM)",
+ "ytLPPrice": "ytLP 价格",
+ "totalSupply": "总供应量",
+ "yourBalance": "你的余额",
+ "cooldownRemaining": "冷却时间剩余",
+ "noCooldown": "无冷却",
+ "addLiquidity": "添加流动性",
+ "addLiquidityDesc": "存入 YT 代币或 WUSD 获得 ytLP 凭证",
+ "removeLiquidity": "移除流动性",
+ "removeLiquidityDesc": "销毁 ytLP 获取代币",
+ "swapTokens": "代币互换",
+ "swapDesc": "在池内交换 YT 代币和 WUSD",
+ "selectToken": "选择代币",
+ "amount": "数量",
+ "slippage": "滑点容忍度",
+ "approveToken": "授权代币",
+ "approveYtLP": "授权 ytLP",
+ "outputToken": "输出代币",
+ "ytLPAmount": "ytLP 数量",
+ "max": "最大",
+ "fromToken": "输入代币",
+ "toToken": "输出代币",
+ "swap": "交换",
+ "cooldownNotPassed": "冷却期未过,请稍后再试",
+ "insufficientOutput": "输出金额不足",
+ "cooldownWarning": "冷却期剩余 {{time}},暂时无法移除流动性"
}
}