feat: 添加 LP 流动性池边界测试
新增 LP 边界测试功能: - 添加流动性边界测试: - 添加金额为0 (InvalidAmount) - 添加超过代币余额 (InsufficientBalance) - 移除流动性边界测试: - 移除金额为0 (InvalidAmount) - 移除超过ytLP余额 (InsufficientBalance) - 移除时minOut过高 (InsufficientOutput) - 代币互换边界测试: - 互换金额为0 (InvalidAmount) - 相同代币互换 (SameToken) - 互换超过余额 (InsufficientBalance) UI功能: - 可折叠的边界测试区域 - 显示冷却时间和ytLP余额状态 - 中英文翻译支持 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,7 @@ export function LPPanel() {
|
||||
slippage: '0.5',
|
||||
})
|
||||
const [activeTab, setActiveTab] = useState<'add' | 'remove' | 'swap'>('add')
|
||||
const [showBoundaryTest, setShowBoundaryTest] = useState(false)
|
||||
|
||||
// Read pool data
|
||||
const { data: ytLPBalance, refetch: refetchBalance } = useReadContract({
|
||||
@@ -295,6 +296,88 @@ export function LPPanel() {
|
||||
return token?.symbol || 'Unknown'
|
||||
}
|
||||
|
||||
// Boundary test function
|
||||
const runBoundaryTest = (testType: string) => {
|
||||
if (!address) return
|
||||
recordTx('test', undefined, 'LP')
|
||||
|
||||
switch (testType) {
|
||||
case 'add_zero':
|
||||
// 添加流动性金额为0
|
||||
writeContract({
|
||||
address: CONTRACTS.YT_REWARD_ROUTER,
|
||||
abi: YT_REWARD_ROUTER_ABI,
|
||||
functionName: 'addLiquidity',
|
||||
args: [CONTRACTS.VAULTS.YT_A, BigInt(0), BigInt(0), BigInt(0)],
|
||||
})
|
||||
break
|
||||
case 'add_exceed_balance':
|
||||
// 添加超过余额
|
||||
writeContract({
|
||||
address: CONTRACTS.YT_REWARD_ROUTER,
|
||||
abi: YT_REWARD_ROUTER_ABI,
|
||||
functionName: 'addLiquidity',
|
||||
args: [CONTRACTS.VAULTS.YT_A, parseUnits('999999999', 18), BigInt(0), BigInt(0)],
|
||||
})
|
||||
break
|
||||
case 'remove_zero':
|
||||
// 移除流动性金额为0
|
||||
writeContract({
|
||||
address: CONTRACTS.YT_REWARD_ROUTER,
|
||||
abi: YT_REWARD_ROUTER_ABI,
|
||||
functionName: 'removeLiquidity',
|
||||
args: [CONTRACTS.VAULTS.YT_A, BigInt(0), BigInt(0), address],
|
||||
})
|
||||
break
|
||||
case 'remove_exceed_balance':
|
||||
// 移除超过ytLP余额
|
||||
const exceedAmount = ytLPBalance ? ytLPBalance + parseUnits('999999', 18) : parseUnits('999999999', 18)
|
||||
writeContract({
|
||||
address: CONTRACTS.YT_REWARD_ROUTER,
|
||||
abi: YT_REWARD_ROUTER_ABI,
|
||||
functionName: 'removeLiquidity',
|
||||
args: [CONTRACTS.VAULTS.YT_A, exceedAmount, BigInt(0), address],
|
||||
})
|
||||
break
|
||||
case 'remove_high_minout':
|
||||
// 移除时minOut过高
|
||||
writeContract({
|
||||
address: CONTRACTS.YT_REWARD_ROUTER,
|
||||
abi: YT_REWARD_ROUTER_ABI,
|
||||
functionName: 'removeLiquidity',
|
||||
args: [CONTRACTS.VAULTS.YT_A, parseUnits('1', 18), parseUnits('999999999', 18), address],
|
||||
})
|
||||
break
|
||||
case 'swap_zero':
|
||||
// 互换金额为0
|
||||
writeContract({
|
||||
address: CONTRACTS.YT_REWARD_ROUTER,
|
||||
abi: YT_REWARD_ROUTER_ABI,
|
||||
functionName: 'swapYT',
|
||||
args: [CONTRACTS.VAULTS.YT_A, CONTRACTS.VAULTS.YT_B, BigInt(0), BigInt(0), address],
|
||||
})
|
||||
break
|
||||
case 'swap_same_token':
|
||||
// 相同代币互换
|
||||
writeContract({
|
||||
address: CONTRACTS.YT_REWARD_ROUTER,
|
||||
abi: YT_REWARD_ROUTER_ABI,
|
||||
functionName: 'swapYT',
|
||||
args: [CONTRACTS.VAULTS.YT_A, CONTRACTS.VAULTS.YT_A, parseUnits('1', 18), BigInt(0), address],
|
||||
})
|
||||
break
|
||||
case 'swap_exceed_balance':
|
||||
// 互换超过余额
|
||||
writeContract({
|
||||
address: CONTRACTS.YT_REWARD_ROUTER,
|
||||
abi: YT_REWARD_ROUTER_ABI,
|
||||
functionName: 'swapYT',
|
||||
args: [CONTRACTS.VAULTS.YT_A, CONTRACTS.VAULTS.YT_B, parseUnits('999999999', 18), BigInt(0), address],
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate cooldown remaining time
|
||||
const getCooldownRemaining = () => {
|
||||
if (!lastAddedAt || !cooldownDuration) return 0
|
||||
@@ -615,6 +698,122 @@ export function LPPanel() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 边界测试区域 */}
|
||||
<div className="section">
|
||||
<div className="section-header" onClick={() => setShowBoundaryTest(!showBoundaryTest)} style={{ cursor: 'pointer' }}>
|
||||
<h3>{t('test.boundaryTests')} {showBoundaryTest ? '[-]' : '[+]'}</h3>
|
||||
</div>
|
||||
|
||||
{showBoundaryTest && (
|
||||
<>
|
||||
<div className="test-hint">{t('lp.boundaryHint')}</div>
|
||||
|
||||
{/* 冷却时间状态 */}
|
||||
<div className="redeem-status">
|
||||
<span>{t('lp.cooldownRemaining')}: </span>
|
||||
<strong className={getCooldownRemaining() > 0 ? 'text-warning' : 'text-success'}>
|
||||
{formatCooldown(getCooldownRemaining())}
|
||||
</strong>
|
||||
<span style={{ marginLeft: '16px' }}>{t('lp.yourBalance')}: </span>
|
||||
<strong>{ytLPBalance ? formatUnits(ytLPBalance, 18) : '0'} ytLP</strong>
|
||||
</div>
|
||||
|
||||
<div className="test-grid">
|
||||
{/* 添加流动性边界测试 */}
|
||||
<div className="test-card">
|
||||
<div className="test-header">
|
||||
<span className="test-name">{t('lp.testAddZero')}</span>
|
||||
<span className="test-error">InvalidAmount</span>
|
||||
</div>
|
||||
<p className="test-desc">{t('lp.testAddZeroDesc')}</p>
|
||||
<button onClick={() => runBoundaryTest('add_zero')} disabled={isPending || isConfirming} className="btn btn-warning btn-sm">
|
||||
{t('test.run')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="test-card">
|
||||
<div className="test-header">
|
||||
<span className="test-name">{t('lp.testAddExceed')}</span>
|
||||
<span className="test-error">InsufficientBalance</span>
|
||||
</div>
|
||||
<p className="test-desc">{t('lp.testAddExceedDesc')}</p>
|
||||
<button onClick={() => runBoundaryTest('add_exceed_balance')} disabled={isPending || isConfirming} className="btn btn-warning btn-sm">
|
||||
{t('test.run')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 移除流动性边界测试 */}
|
||||
<div className="test-card">
|
||||
<div className="test-header">
|
||||
<span className="test-name">{t('lp.testRemoveZero')}</span>
|
||||
<span className="test-error">InvalidAmount</span>
|
||||
</div>
|
||||
<p className="test-desc">{t('lp.testRemoveZeroDesc')}</p>
|
||||
<button onClick={() => runBoundaryTest('remove_zero')} disabled={isPending || isConfirming} className="btn btn-warning btn-sm">
|
||||
{t('test.run')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="test-card">
|
||||
<div className="test-header">
|
||||
<span className="test-name">{t('lp.testRemoveExceed')}</span>
|
||||
<span className="test-error">InsufficientBalance</span>
|
||||
</div>
|
||||
<p className="test-desc">{t('lp.testRemoveExceedDesc')}</p>
|
||||
<button onClick={() => runBoundaryTest('remove_exceed_balance')} disabled={isPending || isConfirming} className="btn btn-warning btn-sm">
|
||||
{t('test.run')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="test-card">
|
||||
<div className="test-header">
|
||||
<span className="test-name">{t('lp.testRemoveHighMinout')}</span>
|
||||
<span className="test-error">InsufficientOutput</span>
|
||||
</div>
|
||||
<p className="test-desc">{t('lp.testRemoveHighMinoutDesc')}</p>
|
||||
<button onClick={() => runBoundaryTest('remove_high_minout')} disabled={isPending || isConfirming} className="btn btn-warning btn-sm">
|
||||
{t('test.run')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 代币互换边界测试 */}
|
||||
<div className="test-card">
|
||||
<div className="test-header">
|
||||
<span className="test-name">{t('lp.testSwapZero')}</span>
|
||||
<span className="test-error">InvalidAmount</span>
|
||||
</div>
|
||||
<p className="test-desc">{t('lp.testSwapZeroDesc')}</p>
|
||||
<button onClick={() => runBoundaryTest('swap_zero')} disabled={isPending || isConfirming} className="btn btn-warning btn-sm">
|
||||
{t('test.run')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="test-card">
|
||||
<div className="test-header">
|
||||
<span className="test-name">{t('lp.testSwapSame')}</span>
|
||||
<span className="test-error">SameToken</span>
|
||||
</div>
|
||||
<p className="test-desc">{t('lp.testSwapSameDesc')}</p>
|
||||
<button onClick={() => runBoundaryTest('swap_same_token')} disabled={isPending || isConfirming} className="btn btn-warning btn-sm">
|
||||
{t('test.run')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="test-card">
|
||||
<div className="test-header">
|
||||
<span className="test-name">{t('lp.testSwapExceed')}</span>
|
||||
<span className="test-error">InsufficientBalance</span>
|
||||
</div>
|
||||
<p className="test-desc">{t('lp.testSwapExceedDesc')}</p>
|
||||
<button onClick={() => runBoundaryTest('swap_exceed_balance')} disabled={isPending || isConfirming} className="btn btn-warning btn-sm">
|
||||
{t('test.run')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Transaction History */}
|
||||
<TransactionHistory transactions={transactions} onClear={clearHistory} />
|
||||
</div>
|
||||
|
||||
@@ -178,6 +178,23 @@
|
||||
"swap": "Swap",
|
||||
"cooldownNotPassed": "Cooldown not passed, please try later",
|
||||
"insufficientOutput": "Insufficient output amount",
|
||||
"cooldownWarning": "Cooldown remaining {{time}}, cannot remove liquidity yet"
|
||||
"cooldownWarning": "Cooldown remaining {{time}}, cannot remove liquidity yet",
|
||||
"boundaryHint": "These tests are designed to trigger LP contract errors. Expected errors are shown for each test.",
|
||||
"testAddZero": "Add Liquidity Zero",
|
||||
"testAddZeroDesc": "Test addLiquidity(token, 0, 0, 0)",
|
||||
"testAddExceed": "Add Exceed Balance",
|
||||
"testAddExceedDesc": "Add liquidity exceeding token balance",
|
||||
"testRemoveZero": "Remove Liquidity Zero",
|
||||
"testRemoveZeroDesc": "Test removeLiquidity(token, 0, 0, receiver)",
|
||||
"testRemoveExceed": "Remove Exceed Balance",
|
||||
"testRemoveExceedDesc": "Remove liquidity exceeding ytLP balance",
|
||||
"testRemoveHighMinout": "Remove High MinOut",
|
||||
"testRemoveHighMinoutDesc": "Set impossible minimum output amount",
|
||||
"testSwapZero": "Swap Amount Zero",
|
||||
"testSwapZeroDesc": "Test swapYT(tokenIn, tokenOut, 0, 0, receiver)",
|
||||
"testSwapSame": "Swap Same Token",
|
||||
"testSwapSameDesc": "Test swapYT(YT-A, YT-A, amount, 0, receiver)",
|
||||
"testSwapExceed": "Swap Exceed Balance",
|
||||
"testSwapExceedDesc": "Swap amount exceeding token balance"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,23 @@
|
||||
"swap": "交换",
|
||||
"cooldownNotPassed": "冷却期未过,请稍后再试",
|
||||
"insufficientOutput": "输出金额不足",
|
||||
"cooldownWarning": "冷却期剩余 {{time}},暂时无法移除流动性"
|
||||
"cooldownWarning": "冷却期剩余 {{time}},暂时无法移除流动性",
|
||||
"boundaryHint": "这些测试旨在触发LP合约错误。每个测试都显示了预期的错误类型。",
|
||||
"testAddZero": "添加流动性为0",
|
||||
"testAddZeroDesc": "测试 addLiquidity(token, 0, 0, 0)",
|
||||
"testAddExceed": "添加超过余额",
|
||||
"testAddExceedDesc": "添加流动性金额超过代币余额",
|
||||
"testRemoveZero": "移除流动性为0",
|
||||
"testRemoveZeroDesc": "测试 removeLiquidity(token, 0, 0, receiver)",
|
||||
"testRemoveExceed": "移除超过余额",
|
||||
"testRemoveExceedDesc": "移除流动性金额超过ytLP余额",
|
||||
"testRemoveHighMinout": "移除minOut过高",
|
||||
"testRemoveHighMinoutDesc": "设置不可能达到的最小输出金额",
|
||||
"testSwapZero": "互换金额为0",
|
||||
"testSwapZeroDesc": "测试 swapYT(tokenIn, tokenOut, 0, 0, receiver)",
|
||||
"testSwapSame": "相同代币互换",
|
||||
"testSwapSameDesc": "测试 swapYT(YT-A, YT-A, amount, 0, receiver)",
|
||||
"testSwapExceed": "互换超过余额",
|
||||
"testSwapExceedDesc": "互换金额超过代币余额"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user