feat: 添加多链支持和 Lending 借贷系统
- 新增 ARB Sepolia + BNB Testnet 多链支持 - 添加 LendingPanel 借贷系统组件 - 添加 LendingAdminPanel 管理面板 - 添加 USDCPanel USDC 操作组件 - 添加 HoldersPanel 持有人信息组件 - 添加 AutoTestPanel 自动化测试组件 - 重构 LP 组件为模块化结构 (LP/) - 添加多个调试和测试脚本 - 修复 USDC 精度动态配置 - 优化合约配置支持多链切换 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
377
frontend/scripts/batch-buy-simulation.ts
Normal file
377
frontend/scripts/batch-buy-simulation.ts
Normal file
@@ -0,0 +1,377 @@
|
||||
/**
|
||||
* 批量用户买入模拟脚本
|
||||
*
|
||||
* 功能:
|
||||
* 1. 从 HD 钱包派生多个测试账户
|
||||
* 2. 从主账户给测试账户转 WUSD
|
||||
* 3. 批量执行 depositYT 买入交易
|
||||
*
|
||||
* 使用方法:
|
||||
* npx tsx scripts/batch-buy-simulation.ts
|
||||
*/
|
||||
|
||||
import {
|
||||
createPublicClient,
|
||||
createWalletClient,
|
||||
http,
|
||||
parseUnits,
|
||||
formatUnits,
|
||||
type Address,
|
||||
type Hex
|
||||
} from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
import { privateKeyToAccount, mnemonicToAccount } from 'viem/accounts'
|
||||
|
||||
// ============ 配置 ============
|
||||
|
||||
// 合约地址
|
||||
const CONTRACTS = {
|
||||
WUSD: '0x6d2bf81a631dFE19B2f348aE92cF6Ef41ca2DF98' as Address,
|
||||
FACTORY: '0x982716f32F10BCB5B5944c1473a8992354bF632b' as Address,
|
||||
VAULTS: {
|
||||
YT_A: '0x0cA35994F033685E7a57ef9bc5d00dd3cf927330' as Address,
|
||||
YT_B: '0x333805C9EE75f59Aa2Cc79DfDe2499F920c7b408' as Address,
|
||||
YT_C: '0x6DF0ED6f0345F601A206974973dE9fC970598587' as Address,
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟配置
|
||||
const CONFIG = {
|
||||
// 主账户私钥 (用于分发 WUSD)
|
||||
MAIN_PRIVATE_KEY: '0xa082a7037105ebd606bee80906687e400d89899bbb6ba0273a61528c2f5fab89' as Hex,
|
||||
|
||||
// HD 钱包助记词 (用于生成测试账户)
|
||||
// 测试用,请勿在生产环境使用
|
||||
TEST_MNEMONIC: 'test test test test test test test test test test test junk',
|
||||
|
||||
// 模拟用户数量
|
||||
USER_COUNT: 3,
|
||||
|
||||
// 每个用户的买入金额范围 (WUSD)
|
||||
MIN_BUY_AMOUNT: 10,
|
||||
MAX_BUY_AMOUNT: 100,
|
||||
|
||||
// 目标金库
|
||||
TARGET_VAULT: CONTRACTS.VAULTS.YT_A,
|
||||
|
||||
// 交易间隔 (毫秒)
|
||||
TX_INTERVAL: 2000,
|
||||
|
||||
// RPC URL
|
||||
RPC_URL: 'https://sepolia-rollup.arbitrum.io/rpc',
|
||||
}
|
||||
|
||||
// ============ ABI ============
|
||||
|
||||
const WUSD_ABI = [
|
||||
{
|
||||
inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }],
|
||||
name: 'transfer',
|
||||
outputs: [{ type: 'bool' }],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'spender', type: 'address' }, { name: 'amount', type: 'uint256' }],
|
||||
name: 'approve',
|
||||
outputs: [{ type: 'bool' }],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'balanceOf',
|
||||
outputs: [{ type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
] as const
|
||||
|
||||
const VAULT_ABI = [
|
||||
{
|
||||
inputs: [{ name: '_wusdAmount', type: 'uint256' }],
|
||||
name: 'depositYT',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [{ name: '_wusdAmount', type: 'uint256' }],
|
||||
name: 'previewBuy',
|
||||
outputs: [{ type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'balanceOf',
|
||||
outputs: [{ type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'symbol',
|
||||
outputs: [{ type: 'string' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
] as const
|
||||
|
||||
// ============ 工具函数 ============
|
||||
|
||||
function randomAmount(min: number, max: number): bigint {
|
||||
const amount = Math.random() * (max - min) + min
|
||||
return parseUnits(amount.toFixed(2), 18)
|
||||
}
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
// ============ 主逻辑 ============
|
||||
|
||||
async function main() {
|
||||
console.log('🚀 批量用户买入模拟开始\n')
|
||||
|
||||
// 创建客户端
|
||||
const publicClient = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http(CONFIG.RPC_URL),
|
||||
})
|
||||
|
||||
// 主账户
|
||||
const mainAccount = privateKeyToAccount(CONFIG.MAIN_PRIVATE_KEY)
|
||||
const mainWalletClient = createWalletClient({
|
||||
account: mainAccount,
|
||||
chain: arbitrumSepolia,
|
||||
transport: http(CONFIG.RPC_URL),
|
||||
})
|
||||
|
||||
console.log(`📍 主账户: ${mainAccount.address}`)
|
||||
|
||||
// 检查主账户 WUSD 余额
|
||||
const mainBalance = await publicClient.readContract({
|
||||
address: CONTRACTS.WUSD,
|
||||
abi: WUSD_ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [mainAccount.address],
|
||||
})
|
||||
console.log(`💰 主账户 WUSD 余额: ${formatUnits(mainBalance, 18)}`)
|
||||
|
||||
// 获取金库 symbol
|
||||
const vaultSymbol = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'symbol',
|
||||
})
|
||||
console.log(`🏦 目标金库: ${vaultSymbol} (${CONFIG.TARGET_VAULT})`)
|
||||
|
||||
// 生成测试账户
|
||||
console.log(`\n👥 生成 ${CONFIG.USER_COUNT} 个测试账户...\n`)
|
||||
|
||||
const testAccounts = []
|
||||
for (let i = 0; i < CONFIG.USER_COUNT; i++) {
|
||||
const account = mnemonicToAccount(CONFIG.TEST_MNEMONIC, { addressIndex: i })
|
||||
const buyAmount = randomAmount(CONFIG.MIN_BUY_AMOUNT, CONFIG.MAX_BUY_AMOUNT)
|
||||
testAccounts.push({ account, buyAmount })
|
||||
console.log(` 账户 ${i + 1}: ${account.address} | 计划买入: ${formatUnits(buyAmount, 18)} WUSD`)
|
||||
}
|
||||
|
||||
// 计算总需要的 WUSD
|
||||
const totalNeeded = testAccounts.reduce((sum, a) => sum + a.buyAmount, 0n)
|
||||
console.log(`\n📊 总计需要: ${formatUnits(totalNeeded, 18)} WUSD`)
|
||||
|
||||
if (mainBalance < totalNeeded) {
|
||||
console.error(`❌ 主账户余额不足! 需要 ${formatUnits(totalNeeded, 18)}, 只有 ${formatUnits(mainBalance, 18)}`)
|
||||
return
|
||||
}
|
||||
|
||||
// 第一步: 给测试账户转 ETH (gas 费)
|
||||
console.log('\n⛽ 步骤 1: 分发 ETH (gas 费) 到测试账户...\n')
|
||||
|
||||
const gasAmount = parseUnits('0.05', 18) // 0.05 ETH 足够多次交易 (Arbitrum Sepolia gas 较高)
|
||||
|
||||
for (let i = 0; i < testAccounts.length; i++) {
|
||||
const { account } = testAccounts[i]
|
||||
|
||||
// 检查 ETH 余额
|
||||
const ethBalance = await publicClient.getBalance({ address: account.address })
|
||||
|
||||
if (ethBalance >= gasAmount) {
|
||||
console.log(` ✓ 账户 ${i + 1} 已有足够 ETH: ${formatUnits(ethBalance, 18)} ETH`)
|
||||
continue
|
||||
}
|
||||
|
||||
const transferAmount = gasAmount - ethBalance
|
||||
console.log(` → 转 ETH 给账户 ${i + 1}: ${formatUnits(transferAmount, 18)} ETH...`)
|
||||
|
||||
try {
|
||||
const hash = await mainWalletClient.sendTransaction({
|
||||
to: account.address,
|
||||
value: transferAmount,
|
||||
})
|
||||
await publicClient.waitForTransactionReceipt({ hash })
|
||||
console.log(` ✓ ETH 转账成功`)
|
||||
} catch (error: any) {
|
||||
console.error(` ✗ ETH 转账失败: ${error.message}`)
|
||||
}
|
||||
|
||||
await sleep(500)
|
||||
}
|
||||
|
||||
// 第二步: 给测试账户转 WUSD
|
||||
console.log('\n📤 步骤 2: 分发 WUSD 到测试账户...\n')
|
||||
|
||||
for (let i = 0; i < testAccounts.length; i++) {
|
||||
const { account, buyAmount } = testAccounts[i]
|
||||
|
||||
// 检查是否已有足够余额
|
||||
const currentBalance = await publicClient.readContract({
|
||||
address: CONTRACTS.WUSD,
|
||||
abi: WUSD_ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [account.address],
|
||||
})
|
||||
|
||||
if (currentBalance >= buyAmount) {
|
||||
console.log(` ✓ 账户 ${i + 1} 已有足够余额: ${formatUnits(currentBalance, 18)} WUSD`)
|
||||
continue
|
||||
}
|
||||
|
||||
const transferAmount = buyAmount - currentBalance
|
||||
console.log(` → 转账给账户 ${i + 1}: ${formatUnits(transferAmount, 18)} WUSD...`)
|
||||
|
||||
try {
|
||||
const hash = await mainWalletClient.writeContract({
|
||||
address: CONTRACTS.WUSD,
|
||||
abi: WUSD_ABI,
|
||||
functionName: 'transfer',
|
||||
args: [account.address, transferAmount],
|
||||
})
|
||||
|
||||
await publicClient.waitForTransactionReceipt({ hash })
|
||||
console.log(` ✓ 转账成功: ${hash}`)
|
||||
} catch (error: any) {
|
||||
console.error(` ✗ 转账失败: ${error.message}`)
|
||||
}
|
||||
|
||||
await sleep(1000)
|
||||
}
|
||||
|
||||
// 第三步: 批量买入
|
||||
console.log('\n🛒 步骤 3: 执行批量买入...\n')
|
||||
|
||||
const results: { address: string; amount: string; ytReceived: string; status: string; hash?: string }[] = []
|
||||
|
||||
for (let i = 0; i < testAccounts.length; i++) {
|
||||
const { account, buyAmount } = testAccounts[i]
|
||||
|
||||
console.log(`\n[${i + 1}/${testAccounts.length}] 账户: ${account.address}`)
|
||||
console.log(` 买入金额: ${formatUnits(buyAmount, 18)} WUSD`)
|
||||
|
||||
const walletClient = createWalletClient({
|
||||
account,
|
||||
chain: arbitrumSepolia,
|
||||
transport: http(CONFIG.RPC_URL),
|
||||
})
|
||||
|
||||
try {
|
||||
// 预览买入
|
||||
const previewYT = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'previewBuy',
|
||||
args: [buyAmount],
|
||||
})
|
||||
console.log(` 预计获得: ${formatUnits(previewYT, 18)} ${vaultSymbol}`)
|
||||
|
||||
// 授权
|
||||
console.log(` → 授权 WUSD...`)
|
||||
const approveHash = await walletClient.writeContract({
|
||||
address: CONTRACTS.WUSD,
|
||||
abi: WUSD_ABI,
|
||||
functionName: 'approve',
|
||||
args: [CONFIG.TARGET_VAULT, buyAmount],
|
||||
})
|
||||
await publicClient.waitForTransactionReceipt({ hash: approveHash })
|
||||
console.log(` ✓ 授权成功`)
|
||||
|
||||
// 买入
|
||||
console.log(` → 执行买入...`)
|
||||
const buyHash = await walletClient.writeContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'depositYT',
|
||||
args: [buyAmount],
|
||||
})
|
||||
await publicClient.waitForTransactionReceipt({ hash: buyHash })
|
||||
|
||||
// 检查 YT 余额
|
||||
const ytBalance = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [account.address],
|
||||
})
|
||||
|
||||
console.log(` ✓ 买入成功! YT 余额: ${formatUnits(ytBalance, 18)} ${vaultSymbol}`)
|
||||
console.log(` 交易哈希: ${buyHash}`)
|
||||
|
||||
results.push({
|
||||
address: account.address,
|
||||
amount: formatUnits(buyAmount, 18),
|
||||
ytReceived: formatUnits(ytBalance, 18),
|
||||
status: 'success',
|
||||
hash: buyHash,
|
||||
})
|
||||
|
||||
} catch (error: any) {
|
||||
console.error(` ✗ 买入失败: ${error.message}`)
|
||||
results.push({
|
||||
address: account.address,
|
||||
amount: formatUnits(buyAmount, 18),
|
||||
ytReceived: '0',
|
||||
status: 'failed',
|
||||
})
|
||||
}
|
||||
|
||||
// 交易间隔
|
||||
if (i < testAccounts.length - 1) {
|
||||
console.log(` ⏳ 等待 ${CONFIG.TX_INTERVAL / 1000} 秒...`)
|
||||
await sleep(CONFIG.TX_INTERVAL)
|
||||
}
|
||||
}
|
||||
|
||||
// 输出汇总
|
||||
console.log('\n' + '='.repeat(60))
|
||||
console.log('📊 模拟结果汇总')
|
||||
console.log('='.repeat(60))
|
||||
|
||||
const successCount = results.filter(r => r.status === 'success').length
|
||||
const failCount = results.filter(r => r.status === 'failed').length
|
||||
const totalBought = results
|
||||
.filter(r => r.status === 'success')
|
||||
.reduce((sum, r) => sum + parseFloat(r.amount), 0)
|
||||
const totalYT = results
|
||||
.filter(r => r.status === 'success')
|
||||
.reduce((sum, r) => sum + parseFloat(r.ytReceived), 0)
|
||||
|
||||
console.log(`\n成功: ${successCount} 笔`)
|
||||
console.log(`失败: ${failCount} 笔`)
|
||||
console.log(`总买入 WUSD: ${totalBought.toFixed(2)}`)
|
||||
console.log(`总获得 ${vaultSymbol}: ${totalYT.toFixed(2)}`)
|
||||
|
||||
console.log('\n详细记录:')
|
||||
console.table(results.map(r => ({
|
||||
地址: r.address.slice(0, 10) + '...',
|
||||
买入WUSD: r.amount,
|
||||
获得YT: r.ytReceived,
|
||||
状态: r.status,
|
||||
})))
|
||||
|
||||
console.log('\n✅ 模拟完成!')
|
||||
}
|
||||
|
||||
// 运行
|
||||
main().catch(console.error)
|
||||
343
frontend/scripts/batch-sell-simulation.ts
Normal file
343
frontend/scripts/batch-sell-simulation.ts
Normal file
@@ -0,0 +1,343 @@
|
||||
/**
|
||||
* 批量用户卖出模拟脚本
|
||||
*
|
||||
* 功能:
|
||||
* 1. 读取测试账户的 YT 余额
|
||||
* 2. 批量执行 withdrawYT 卖出交易(进入排队)
|
||||
* 3. 可选:管理员批量处理队列
|
||||
*
|
||||
* 使用方法:
|
||||
* npx tsx scripts/batch-sell-simulation.ts
|
||||
*/
|
||||
|
||||
import {
|
||||
createPublicClient,
|
||||
createWalletClient,
|
||||
http,
|
||||
parseUnits,
|
||||
formatUnits,
|
||||
type Address,
|
||||
type Hex
|
||||
} from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
import { privateKeyToAccount, mnemonicToAccount } from 'viem/accounts'
|
||||
|
||||
// ============ 配置 ============
|
||||
|
||||
const CONTRACTS = {
|
||||
WUSD: '0x6d2bf81a631dFE19B2f348aE92cF6Ef41ca2DF98' as Address,
|
||||
FACTORY: '0x982716f32F10BCB5B5944c1473a8992354bF632b' as Address,
|
||||
VAULTS: {
|
||||
YT_A: '0x0cA35994F033685E7a57ef9bc5d00dd3cf927330' as Address,
|
||||
YT_B: '0x333805C9EE75f59Aa2Cc79DfDe2499F920c7b408' as Address,
|
||||
YT_C: '0x6DF0ED6f0345F601A206974973dE9fC970598587' as Address,
|
||||
}
|
||||
}
|
||||
|
||||
const CONFIG = {
|
||||
// 主账户私钥 (Owner,用于处理队列)
|
||||
MAIN_PRIVATE_KEY: '0xa082a7037105ebd606bee80906687e400d89899bbb6ba0273a61528c2f5fab89' as Hex,
|
||||
|
||||
// HD 钱包助记词 (与买入脚本相同)
|
||||
TEST_MNEMONIC: 'test test test test test test test test test test test junk',
|
||||
|
||||
// 模拟用户数量
|
||||
USER_COUNT: 10,
|
||||
|
||||
// 卖出比例 (0.5 = 卖出 50% 的 YT)
|
||||
SELL_RATIO: 0.5,
|
||||
|
||||
// 目标金库
|
||||
TARGET_VAULT: CONTRACTS.VAULTS.YT_A,
|
||||
|
||||
// 交易间隔 (毫秒)
|
||||
TX_INTERVAL: 2000,
|
||||
|
||||
// 是否自动处理队列
|
||||
AUTO_PROCESS_QUEUE: true,
|
||||
|
||||
// 批量处理大小
|
||||
BATCH_PROCESS_SIZE: 10,
|
||||
|
||||
// RPC URL
|
||||
RPC_URL: 'https://sepolia-rollup.arbitrum.io/rpc',
|
||||
}
|
||||
|
||||
// ============ ABI ============
|
||||
|
||||
const VAULT_ABI = [
|
||||
{
|
||||
inputs: [{ name: '_ytAmount', type: 'uint256' }],
|
||||
name: 'withdrawYT',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [{ name: '_ytAmount', type: 'uint256' }],
|
||||
name: 'previewSell',
|
||||
outputs: [{ type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'balanceOf',
|
||||
outputs: [{ type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'symbol',
|
||||
outputs: [{ type: 'string' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'canRedeemNow',
|
||||
outputs: [{ type: 'bool' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'getQueueProgress',
|
||||
outputs: [
|
||||
{ name: 'currentIndex', type: 'uint256' },
|
||||
{ name: 'totalRequests', type: 'uint256' },
|
||||
{ name: 'pendingCount', type: 'uint256' },
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [{ name: '_batchSize', type: 'uint256' }],
|
||||
name: 'processBatchWithdrawals',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
] as const
|
||||
|
||||
// ============ 工具函数 ============
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
// ============ 主逻辑 ============
|
||||
|
||||
async function main() {
|
||||
console.log('🚀 批量用户卖出模拟开始\n')
|
||||
|
||||
const publicClient = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http(CONFIG.RPC_URL),
|
||||
})
|
||||
|
||||
// 主账户 (Owner)
|
||||
const mainAccount = privateKeyToAccount(CONFIG.MAIN_PRIVATE_KEY)
|
||||
const mainWalletClient = createWalletClient({
|
||||
account: mainAccount,
|
||||
chain: arbitrumSepolia,
|
||||
transport: http(CONFIG.RPC_URL),
|
||||
})
|
||||
|
||||
console.log(`📍 Owner 账户: ${mainAccount.address}`)
|
||||
|
||||
// 获取金库信息
|
||||
const vaultSymbol = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'symbol',
|
||||
})
|
||||
console.log(`🏦 目标金库: ${vaultSymbol} (${CONFIG.TARGET_VAULT})`)
|
||||
|
||||
// 检查赎回状态
|
||||
const canRedeem = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'canRedeemNow',
|
||||
})
|
||||
console.log(`📅 赎回状态: ${canRedeem ? '✅ 可赎回' : '❌ 锁定中'}`)
|
||||
|
||||
if (!canRedeem) {
|
||||
console.error('\n⚠️ 当前不在赎回期,无法执行卖出操作')
|
||||
console.log('请先通过管理员设置赎回时间: setVaultNextRedemptionTime')
|
||||
return
|
||||
}
|
||||
|
||||
// 获取队列状态
|
||||
const queueProgress = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'getQueueProgress',
|
||||
})
|
||||
console.log(`📋 队列状态: 已处理 ${queueProgress[0]}, 总请求 ${queueProgress[1]}, 待处理 ${queueProgress[2]}`)
|
||||
|
||||
// 获取测试账户
|
||||
console.log(`\n👥 检查 ${CONFIG.USER_COUNT} 个测试账户的 YT 余额...\n`)
|
||||
|
||||
const testAccounts = []
|
||||
for (let i = 0; i < CONFIG.USER_COUNT; i++) {
|
||||
const account = mnemonicToAccount(CONFIG.TEST_MNEMONIC, { addressIndex: i })
|
||||
|
||||
const ytBalance = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [account.address],
|
||||
})
|
||||
|
||||
if (ytBalance > 0n) {
|
||||
const sellAmount = (ytBalance * BigInt(Math.floor(CONFIG.SELL_RATIO * 100))) / 100n
|
||||
testAccounts.push({ account, ytBalance, sellAmount })
|
||||
console.log(` 账户 ${i + 1}: ${account.address} | YT余额: ${formatUnits(ytBalance, 18)} | 计划卖出: ${formatUnits(sellAmount, 18)}`)
|
||||
} else {
|
||||
console.log(` 账户 ${i + 1}: ${account.address} | YT余额: 0 (跳过)`)
|
||||
}
|
||||
}
|
||||
|
||||
if (testAccounts.length === 0) {
|
||||
console.log('\n⚠️ 没有账户有 YT 余额,请先运行 npm run sim:buy')
|
||||
return
|
||||
}
|
||||
|
||||
// 执行卖出
|
||||
console.log('\n💸 步骤 1: 执行批量卖出 (进入排队)...\n')
|
||||
|
||||
const results: { address: string; sellAmount: string; expectedWusd: string; status: string; hash?: string }[] = []
|
||||
|
||||
for (let i = 0; i < testAccounts.length; i++) {
|
||||
const { account, sellAmount } = testAccounts[i]
|
||||
|
||||
console.log(`\n[${i + 1}/${testAccounts.length}] 账户: ${account.address}`)
|
||||
console.log(` 卖出金额: ${formatUnits(sellAmount, 18)} ${vaultSymbol}`)
|
||||
|
||||
const walletClient = createWalletClient({
|
||||
account,
|
||||
chain: arbitrumSepolia,
|
||||
transport: http(CONFIG.RPC_URL),
|
||||
})
|
||||
|
||||
try {
|
||||
// 预览卖出
|
||||
const previewWusd = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'previewSell',
|
||||
args: [sellAmount],
|
||||
})
|
||||
console.log(` 预计获得: ${formatUnits(previewWusd, 18)} WUSD`)
|
||||
|
||||
// 执行卖出 (进入队列)
|
||||
console.log(` → 提交卖出请求...`)
|
||||
const sellHash = await walletClient.writeContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'withdrawYT',
|
||||
args: [sellAmount],
|
||||
})
|
||||
await publicClient.waitForTransactionReceipt({ hash: sellHash })
|
||||
|
||||
console.log(` ✓ 请求已进入队列`)
|
||||
console.log(` 交易哈希: ${sellHash}`)
|
||||
|
||||
results.push({
|
||||
address: account.address,
|
||||
sellAmount: formatUnits(sellAmount, 18),
|
||||
expectedWusd: formatUnits(previewWusd, 18),
|
||||
status: 'queued',
|
||||
hash: sellHash,
|
||||
})
|
||||
|
||||
} catch (error: any) {
|
||||
console.error(` ✗ 卖出失败: ${error.message}`)
|
||||
results.push({
|
||||
address: account.address,
|
||||
sellAmount: formatUnits(sellAmount, 18),
|
||||
expectedWusd: '0',
|
||||
status: 'failed',
|
||||
})
|
||||
}
|
||||
|
||||
if (i < testAccounts.length - 1) {
|
||||
console.log(` ⏳ 等待 ${CONFIG.TX_INTERVAL / 1000} 秒...`)
|
||||
await sleep(CONFIG.TX_INTERVAL)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取更新后的队列状态
|
||||
const newQueueProgress = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'getQueueProgress',
|
||||
})
|
||||
console.log(`\n📋 更新后队列状态: 已处理 ${newQueueProgress[0]}, 总请求 ${newQueueProgress[1]}, 待处理 ${newQueueProgress[2]}`)
|
||||
|
||||
// 处理队列 (如果启用)
|
||||
if (CONFIG.AUTO_PROCESS_QUEUE && newQueueProgress[2] > 0n) {
|
||||
console.log('\n⚙️ 步骤 2: 管理员批量处理队列...\n')
|
||||
|
||||
const pendingCount = Number(newQueueProgress[2])
|
||||
const batches = Math.ceil(pendingCount / CONFIG.BATCH_PROCESS_SIZE)
|
||||
|
||||
for (let batch = 0; batch < batches; batch++) {
|
||||
const batchSize = Math.min(CONFIG.BATCH_PROCESS_SIZE, pendingCount - batch * CONFIG.BATCH_PROCESS_SIZE)
|
||||
console.log(` 处理批次 ${batch + 1}/${batches} (${batchSize} 笔)...`)
|
||||
|
||||
try {
|
||||
const processHash = await mainWalletClient.writeContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'processBatchWithdrawals',
|
||||
args: [BigInt(batchSize)],
|
||||
})
|
||||
await publicClient.waitForTransactionReceipt({ hash: processHash })
|
||||
console.log(` ✓ 批次处理完成: ${processHash}`)
|
||||
} catch (error: any) {
|
||||
console.error(` ✗ 处理失败: ${error.message}`)
|
||||
break
|
||||
}
|
||||
|
||||
await sleep(2000)
|
||||
}
|
||||
|
||||
// 最终队列状态
|
||||
const finalQueue = await publicClient.readContract({
|
||||
address: CONFIG.TARGET_VAULT,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'getQueueProgress',
|
||||
})
|
||||
console.log(`\n📋 最终队列状态: 已处理 ${finalQueue[0]}, 总请求 ${finalQueue[1]}, 待处理 ${finalQueue[2]}`)
|
||||
}
|
||||
|
||||
// 汇总
|
||||
console.log('\n' + '='.repeat(60))
|
||||
console.log('📊 模拟结果汇总')
|
||||
console.log('='.repeat(60))
|
||||
|
||||
const queuedCount = results.filter(r => r.status === 'queued').length
|
||||
const failCount = results.filter(r => r.status === 'failed').length
|
||||
const totalSold = results
|
||||
.filter(r => r.status === 'queued')
|
||||
.reduce((sum, r) => sum + parseFloat(r.sellAmount), 0)
|
||||
|
||||
console.log(`\n成功排队: ${queuedCount} 笔`)
|
||||
console.log(`失败: ${failCount} 笔`)
|
||||
console.log(`总卖出 ${vaultSymbol}: ${totalSold.toFixed(2)}`)
|
||||
|
||||
console.log('\n详细记录:')
|
||||
console.table(results.map(r => ({
|
||||
地址: r.address.slice(0, 10) + '...',
|
||||
卖出YT: r.sellAmount,
|
||||
预期WUSD: r.expectedWusd,
|
||||
状态: r.status,
|
||||
})))
|
||||
|
||||
console.log('\n✅ 模拟完成!')
|
||||
}
|
||||
|
||||
main().catch(console.error)
|
||||
169
frontend/scripts/check-account-status.js
Normal file
169
frontend/scripts/check-account-status.js
Normal file
@@ -0,0 +1,169 @@
|
||||
import { createPublicClient, http, parseUnits, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
const USDC = getAddress('0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d')
|
||||
const USER = getAddress('0xa013422A5918CD099c63c8CC35283EACa99a705d')
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: 'account', type: 'address' }
|
||||
],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
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: 'address', name: 'asset', type: 'address' }
|
||||
],
|
||||
name: 'collateralBalanceOf',
|
||||
outputs: [{ internalType: 'uint128', name: '', type: 'uint128' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'baseToken',
|
||||
outputs: [{ internalType: 'address', name: '', type: 'address' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'baseMinForRewards',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'baseBorrowMin',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
const ERC20_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: 'account', type: 'address' }
|
||||
],
|
||||
name: 'balanceOf',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
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'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查用户账户状态...\n')
|
||||
console.log('用户地址:', USER)
|
||||
|
||||
try {
|
||||
// 检查 YT-A 余额和授权
|
||||
const ytBalance = await client.readContract({
|
||||
address: YT_A,
|
||||
abi: ERC20_ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
const ytAllowance = await client.readContract({
|
||||
address: YT_A,
|
||||
abi: ERC20_ABI,
|
||||
functionName: 'allowance',
|
||||
args: [USER, LENDING_PROXY]
|
||||
})
|
||||
|
||||
console.log('\n=== YT-A 代币状态 ===')
|
||||
console.log('钱包余额:', Number(ytBalance) / 1e18, 'YT-A')
|
||||
console.log('授权额度:', Number(ytAllowance) / 1e18, 'YT-A')
|
||||
console.log('想要存入:', 10, 'YT-A')
|
||||
console.log('余额足够:', Number(ytBalance) >= 10e18 ? '✓' : '✗')
|
||||
console.log('授权足够:', Number(ytAllowance) >= 10e18 ? '✓' : '✗')
|
||||
|
||||
// 检查 USDC 余额
|
||||
const usdcBalance = await client.readContract({
|
||||
address: USDC,
|
||||
abi: ERC20_ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
console.log('\n=== USDC 代币状态 ===')
|
||||
console.log('USDC 余额:', Number(usdcBalance) / 1e6, 'USDC')
|
||||
|
||||
// 检查 Lending 账户状态
|
||||
const baseBalance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
const borrowBalance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'borrowBalanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
const collateralBalance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'collateralBalanceOf',
|
||||
args: [USER, YT_A]
|
||||
})
|
||||
|
||||
console.log('\n=== Lending 账户状态 ===')
|
||||
console.log('供应余额 (USDC):', Number(baseBalance) / 1e6, 'USDC')
|
||||
console.log('借款余额 (USDC):', Number(borrowBalance) / 1e6, 'USDC')
|
||||
console.log('抵押品余额 (YT-A):', Number(collateralBalance) / 1e18, 'YT-A')
|
||||
|
||||
// 检查合约参数
|
||||
const baseBorrowMin = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'baseBorrowMin'
|
||||
})
|
||||
|
||||
console.log('\n=== 合约参数 ===')
|
||||
console.log('最小借款额:', Number(baseBorrowMin) / 1e6, 'USDC')
|
||||
|
||||
} catch (error) {
|
||||
console.error('✗ 检查失败:', error.message)
|
||||
console.error('详细错误:', error)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
30
frontend/scripts/check-all-balances.ts
Normal file
30
frontend/scripts/check-all-balances.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { createPublicClient, http, formatEther } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
import { mnemonicToAccount } from 'viem/accounts'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc'),
|
||||
})
|
||||
|
||||
const MAIN_ADDRESS = '0xa013422A5918CD099C63c8CC35283EACa99a705d'
|
||||
const TEST_MNEMONIC = 'test test test test test test test test test test test junk'
|
||||
|
||||
async function main() {
|
||||
console.log('📊 所有账户 ETH 余额:\n')
|
||||
|
||||
// 主账户
|
||||
const mainBalance = await client.getBalance({ address: MAIN_ADDRESS })
|
||||
console.log(`主账户: ${MAIN_ADDRESS}`)
|
||||
console.log(` ETH: ${formatEther(mainBalance)}\n`)
|
||||
|
||||
// 测试账户
|
||||
console.log('测试账户:')
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const account = mnemonicToAccount(TEST_MNEMONIC, { addressIndex: i })
|
||||
const balance = await client.getBalance({ address: account.address })
|
||||
console.log(` ${i + 1}. ${account.address}: ${formatEther(balance)} ETH`)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
23
frontend/scripts/check-balance.ts
Normal file
23
frontend/scripts/check-balance.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { createPublicClient, http, formatEther } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc'),
|
||||
})
|
||||
|
||||
async function main() {
|
||||
const balance = await client.getBalance({ address: '0xa013422A5918CD099C63c8CC35283EACa99a705d' })
|
||||
console.log('主账户 ETH 余额:', formatEther(balance), 'ETH')
|
||||
|
||||
// 检查是否足够 (10 个账户 × 0.01 ETH = 0.1 ETH)
|
||||
const needed = 0.1
|
||||
if (parseFloat(formatEther(balance)) < needed) {
|
||||
console.log(`\n⚠️ ETH 不足! 需要至少 ${needed} ETH 来分发 gas 费`)
|
||||
console.log('请先获取测试网 ETH: https://www.alchemy.com/faucets/arbitrum-sepolia')
|
||||
} else {
|
||||
console.log('\n✅ ETH 足够运行模拟脚本')
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
171
frontend/scripts/check-borrow-history.js
Normal file
171
frontend/scripts/check-borrow-history.js
Normal file
@@ -0,0 +1,171 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USDC = getAddress('0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
|
||||
const TRANSFER_EVENT = {
|
||||
type: 'event',
|
||||
name: 'Transfer',
|
||||
inputs: [
|
||||
{ indexed: true, name: 'from', type: 'address' },
|
||||
{ indexed: true, name: 'to', type: 'address' },
|
||||
{ indexed: false, name: 'value', type: 'uint256' }
|
||||
]
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('\n=== 检查借款历史(USDC 从 Lending 转出给用户)===\n')
|
||||
console.log('用户:', USER)
|
||||
console.log('Lending 合约:', LENDING_PROXY)
|
||||
console.log('USDC:', USDC)
|
||||
console.log()
|
||||
|
||||
const latestBlock = await client.getBlockNumber()
|
||||
console.log('最新区块:', latestBlock)
|
||||
console.log('查询范围: 最近 10000 个区块\n')
|
||||
|
||||
try {
|
||||
// 查找 USDC 从 Lending 转给用户的记录(这才是真正的借款/提取)
|
||||
const logs = await client.getLogs({
|
||||
address: USDC,
|
||||
event: TRANSFER_EVENT,
|
||||
args: {
|
||||
from: LENDING_PROXY,
|
||||
to: USER
|
||||
},
|
||||
fromBlock: latestBlock - 10000n,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
|
||||
if (logs.length > 0) {
|
||||
console.log(`✓ 找到 ${logs.length} 笔 USDC 转出记录:\n`)
|
||||
let totalWithdrawn = 0n
|
||||
|
||||
for (let i = 0; i < logs.length; i++) {
|
||||
const log = logs[i]
|
||||
const { value } = log.args
|
||||
totalWithdrawn += value
|
||||
|
||||
console.log(`${i + 1}. 区块 ${log.blockNumber}`)
|
||||
console.log(` 交易: ${log.transactionHash}`)
|
||||
console.log(` 数量: ${Number(value) / 1e6} USDC`)
|
||||
|
||||
// 获取交易详情以确认是 withdraw 还是其他操作
|
||||
try {
|
||||
const tx = await client.getTransaction({ hash: log.transactionHash })
|
||||
const selector = tx.input.slice(0, 10)
|
||||
|
||||
// withdraw(uint256) = 0x2e1a7d4d
|
||||
// withdrawFrom(...) = ...
|
||||
if (selector === '0x2e1a7d4d') {
|
||||
console.log(` 函数: withdraw (借款/提取)`)
|
||||
} else {
|
||||
console.log(` 函数选择器: ${selector}`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(` (无法获取交易详情)`)
|
||||
}
|
||||
|
||||
console.log()
|
||||
}
|
||||
|
||||
console.log(`总计: ${Number(totalWithdrawn) / 1e6} USDC`)
|
||||
} else {
|
||||
console.log('✗ 未找到任何 USDC 转出记录')
|
||||
console.log()
|
||||
console.log('这意味着:')
|
||||
console.log(' - 用户从未真正从 Lending 提取或借款 USDC')
|
||||
console.log(' - 如果用户看到"借款成功"的消息,那些都是前端误报')
|
||||
}
|
||||
|
||||
console.log('\n=== 对比:USDC 转入 Lending(存款)===\n')
|
||||
|
||||
const supplyLogs = await client.getLogs({
|
||||
address: USDC,
|
||||
event: TRANSFER_EVENT,
|
||||
args: {
|
||||
from: USER,
|
||||
to: LENDING_PROXY
|
||||
},
|
||||
fromBlock: latestBlock - 10000n,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
|
||||
if (supplyLogs.length > 0) {
|
||||
console.log(`找到 ${supplyLogs.length} 笔 USDC 存入记录:\n`)
|
||||
let totalSupplied = 0n
|
||||
|
||||
supplyLogs.forEach((log, i) => {
|
||||
const { value } = log.args
|
||||
totalSupplied += value
|
||||
console.log(`${i + 1}. 区块 ${log.blockNumber}`)
|
||||
console.log(` 交易: ${log.transactionHash}`)
|
||||
console.log(` 数量: ${Number(value) / 1e6} USDC`)
|
||||
console.log()
|
||||
})
|
||||
|
||||
console.log(`总计存入: ${Number(totalSupplied) / 1e6} USDC`)
|
||||
} else {
|
||||
console.log('未找到 USDC 存入记录')
|
||||
}
|
||||
|
||||
// 检查当前余额
|
||||
console.log('\n=== 当前账户状态 ===\n')
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'getBalance',
|
||||
outputs: [{ name: '', type: 'int256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
const balance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getBalance',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
const borrowBalance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'borrowBalanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
console.log('USDC 余额(存款):', balance > 0 ? `${Number(balance) / 1e6} USDC` : '0 USDC')
|
||||
console.log('借款余额:', Number(borrowBalance) / 1e6, 'USDC')
|
||||
console.log()
|
||||
|
||||
if (balance > 0) {
|
||||
console.log('⚠️ 用户当前有 USDC 存款在 Lending 中')
|
||||
console.log(` 存款金额: ${Number(balance) / 1e6} USDC`)
|
||||
console.log()
|
||||
console.log('根据 Compound V3 设计:')
|
||||
console.log(' - withdraw() 会先从存款中扣除')
|
||||
console.log(` - 只有 withdraw 金额 > ${Number(balance) / 1e6} USDC 时,才会产生真正的借款`)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
55
frontend/scripts/check-collateral-balance.js
Normal file
55
frontend/scripts/check-collateral-balance.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: '_user', type: 'address' },
|
||||
{ internalType: 'address', name: '_collateralAsset', type: 'address' }
|
||||
],
|
||||
name: 'getUserCollateral',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查抵押品余额...\n')
|
||||
console.log('用户:', USER)
|
||||
console.log('抵押品:', YT_A)
|
||||
|
||||
try {
|
||||
const collateralBalance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getUserCollateral',
|
||||
args: [USER, YT_A]
|
||||
})
|
||||
|
||||
console.log('\n✓ 抵押品余额:', Number(collateralBalance) / 1e18, 'YT-A')
|
||||
|
||||
if (collateralBalance > 0n) {
|
||||
console.log('\n成功!代币已存入合约作为抵押品。')
|
||||
} else {
|
||||
console.log('\n警告:抵押品余额为 0,但钱包代币已被扣除。')
|
||||
console.log('可能原因:')
|
||||
console.log('1. 交易失败但代币被锁在某处')
|
||||
console.log('2. 函数调用错误,需要检查交易哈希')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n✗ 读取失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
119
frontend/scripts/check-compound-v3-storage.js
Normal file
119
frontend/scripts/check-compound-v3-storage.js
Normal file
@@ -0,0 +1,119 @@
|
||||
import { createPublicClient, http, getAddress, keccak256, toHex, pad, concat } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
|
||||
// Compound V3 使用的公开状态变量
|
||||
const COMET_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ name: 'account', type: 'address' },
|
||||
{ name: 'asset', type: 'address' }
|
||||
],
|
||||
name: 'userCollateral',
|
||||
outputs: [
|
||||
{
|
||||
components: [
|
||||
{ name: 'balance', type: 'uint128' },
|
||||
{ name: '_reserved', type: 'uint128' }
|
||||
],
|
||||
name: '',
|
||||
type: 'tuple'
|
||||
}
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'balanceOf',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{ name: 'account', type: 'address' },
|
||||
{ name: 'asset', type: 'address' }
|
||||
],
|
||||
name: 'collateralBalanceOf',
|
||||
outputs: [{ name: '', type: 'uint128' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查 Compound V3 存储结构\n')
|
||||
console.log('合约:', LENDING_PROXY)
|
||||
console.log('用户:', USER)
|
||||
console.log('抵押品:', YT_A)
|
||||
console.log()
|
||||
|
||||
// 测试 Compound V3 原生函数
|
||||
for (const func of COMET_ABI) {
|
||||
console.log(`--- ${func.name} ---`)
|
||||
try {
|
||||
let result
|
||||
if (func.name === 'userCollateral') {
|
||||
result = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [func],
|
||||
functionName: func.name,
|
||||
args: [USER, YT_A]
|
||||
})
|
||||
console.log('✓ 成功!')
|
||||
console.log(' balance:', result.balance.toString(), '(', Number(result.balance) / 1e18, 'YT-A )')
|
||||
console.log(' _reserved:', result._reserved.toString())
|
||||
} else if (func.name === 'collateralBalanceOf') {
|
||||
result = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [func],
|
||||
functionName: func.name,
|
||||
args: [USER, YT_A]
|
||||
})
|
||||
console.log('✓ 成功:', result.toString(), '(', Number(result) / 1e18, 'YT-A )')
|
||||
} else if (func.name === 'balanceOf') {
|
||||
result = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [func],
|
||||
functionName: func.name,
|
||||
args: [USER]
|
||||
})
|
||||
console.log('✓ 成功:', result.toString(), '(', Number(result) / 1e6, 'USDC )')
|
||||
} else {
|
||||
result = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [func],
|
||||
functionName: func.name,
|
||||
args: [USER]
|
||||
})
|
||||
console.log('✓ 成功:', result.toString())
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('✗ 失败:', error.message.split('\n')[0])
|
||||
}
|
||||
console.log()
|
||||
}
|
||||
|
||||
console.log('=== 结论 ===')
|
||||
console.log('如果上面的 Compound V3 原生函数能工作,')
|
||||
console.log('说明合约使用了 Compound V3 的数据结构,')
|
||||
console.log('前端应该调用这些函数而不是自定义的 getUserCollateral 等。\n')
|
||||
}
|
||||
|
||||
main()
|
||||
78
frontend/scripts/check-configurator-setup.js
Normal file
78
frontend/scripts/check-configurator-setup.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import { createPublicClient, http, parseAbi } from 'viem';
|
||||
import { arbitrumSepolia } from 'viem/chains';
|
||||
|
||||
const CONFIGURATOR_ADDRESS = '0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc';
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D';
|
||||
|
||||
// 扩展的 ABI,包含可能的其他函数
|
||||
const CONFIGURATOR_ABI = parseAbi([
|
||||
'function owner() view returns (address)',
|
||||
'function lending() view returns (address)',
|
||||
'function lendingContract() view returns (address)',
|
||||
'function getLending() view returns (address)',
|
||||
'function setLending(address) external',
|
||||
'function initialize(address) external',
|
||||
'function collateralConfigs(address) view returns (bool, uint256, uint256, uint256)'
|
||||
]);
|
||||
|
||||
const publicClient = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc')
|
||||
});
|
||||
|
||||
async function checkSetup() {
|
||||
console.log('🔍 检查 Configurator 设置\n');
|
||||
|
||||
// 尝试读取lending地址(可能有不同的函数名)
|
||||
const possibleGetters = ['lending', 'lendingContract', 'getLending'];
|
||||
|
||||
for (const getter of possibleGetters) {
|
||||
try {
|
||||
console.log(`尝试调用 ${getter}()...`);
|
||||
const lendingAddr = await publicClient.readContract({
|
||||
address: CONFIGURATOR_ADDRESS,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: getter
|
||||
});
|
||||
console.log(`✅ 找到! ${getter}() = ${lendingAddr}`);
|
||||
console.log(` 期望地址: ${LENDING_PROXY}`);
|
||||
console.log(` 匹配: ${lendingAddr.toLowerCase() === LENDING_PROXY.toLowerCase() ? '✅' : '❌'}\n`);
|
||||
|
||||
if (lendingAddr.toLowerCase() !== LENDING_PROXY.toLowerCase()) {
|
||||
console.log('⚠️ 警告: Lending 地址不匹配!');
|
||||
console.log('💡 可能需要调用 setLending() 来设置正确的地址\n');
|
||||
}
|
||||
|
||||
return;
|
||||
} catch (error) {
|
||||
console.log(` ❌ ${getter}() 不存在或调用失败\n`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('💡 建议:');
|
||||
console.log(' 1. Configurator 可能需要先通过 initialize() 或 setLending() 设置 Lending 合约地址');
|
||||
console.log(' 2. 检查合约源码或部署脚本中的初始化步骤');
|
||||
console.log(' 3. 可能需要 Lending owner 先在 Configurator 中注册\n');
|
||||
|
||||
// 检查是否有其他状态变量
|
||||
console.log('🔍 尝试读取其他可能的状态...\n');
|
||||
|
||||
// 尝试直接读取存储槽
|
||||
try {
|
||||
// Slot 0 通常是第一个状态变量
|
||||
const slot0 = await publicClient.getStorageAt({
|
||||
address: CONFIGURATOR_ADDRESS,
|
||||
slot: '0x0'
|
||||
});
|
||||
console.log('Storage Slot 0:', slot0);
|
||||
|
||||
if (slot0 && slot0 !== '0x' + '0'.repeat(64)) {
|
||||
const addr = '0x' + slot0.slice(-40);
|
||||
console.log('可能的地址值:', addr);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('无法读取存储槽');
|
||||
}
|
||||
}
|
||||
|
||||
checkSetup();
|
||||
28
frontend/scripts/check-function-selector.js
Normal file
28
frontend/scripts/check-function-selector.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { keccak256, toHex } from 'viem'
|
||||
|
||||
// 计算函数选择器
|
||||
function getFunctionSelector(signature) {
|
||||
const hash = keccak256(toHex(signature))
|
||||
return hash.slice(0, 10) // 前4字节
|
||||
}
|
||||
|
||||
console.log('\n计算函数选择器...\n')
|
||||
|
||||
const signatures = [
|
||||
'deposit(address,uint256)',
|
||||
'supplyCollateral(address,uint256)',
|
||||
'supply(address,uint256)',
|
||||
'supplyTo(address,address,uint256)'
|
||||
]
|
||||
|
||||
signatures.forEach(sig => {
|
||||
const selector = getFunctionSelector(sig)
|
||||
console.log(`${sig}`)
|
||||
console.log(` 选择器: ${selector}`)
|
||||
if (selector === '0x47e7ef24') {
|
||||
console.log(' ✓ 匹配!')
|
||||
}
|
||||
console.log()
|
||||
})
|
||||
|
||||
console.log('错误消息中的选择器: 0x47e7ef24')
|
||||
122
frontend/scripts/check-interest-rates.js
Normal file
122
frontend/scripts/check-interest-rates.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [],
|
||||
name: 'getBorrowRate',
|
||||
outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'getSupplyRate',
|
||||
outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'getUtilization',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查利率和使用率...\n')
|
||||
|
||||
try {
|
||||
// 1. 获取借款利率
|
||||
const borrowRate = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getBorrowRate'
|
||||
})
|
||||
console.log('=== getBorrowRate ===')
|
||||
console.log('原始值:', borrowRate.toString())
|
||||
console.log('类型: uint64')
|
||||
console.log()
|
||||
|
||||
// Compound V3 利率通常是每秒利率,精度 1e18
|
||||
// APY = (1 + ratePerSecond)^(365*24*60*60) - 1
|
||||
// 简化近似:APY ≈ ratePerSecond * secondsPerYear * 100%
|
||||
const secondsPerYear = 365 * 24 * 60 * 60
|
||||
|
||||
// 如果是 1e18 精度的每秒利率
|
||||
if (borrowRate > 0n) {
|
||||
const apyE18 = Number(borrowRate) * secondsPerYear / 1e18
|
||||
console.log('假设精度 1e18 (每秒利率):')
|
||||
console.log(' 每秒利率:', Number(borrowRate) / 1e18)
|
||||
console.log(' 年化利率 (APY):', apyE18.toFixed(2), '%')
|
||||
console.log()
|
||||
|
||||
// 如果是 1e16 精度(百分比形式)
|
||||
const apyE16 = Number(borrowRate) * secondsPerYear / 1e16
|
||||
console.log('假设精度 1e16:')
|
||||
console.log(' 年化利率 (APY):', apyE16.toFixed(2), '%')
|
||||
console.log()
|
||||
|
||||
// 如果是 1e4 精度(basis points)
|
||||
const apyE4 = Number(borrowRate) / 1e4
|
||||
console.log('假设精度 1e4 (basis points):')
|
||||
console.log(' 年化利率 (APY):', apyE4.toFixed(2), '%')
|
||||
console.log()
|
||||
}
|
||||
|
||||
// 2. 获取存款利率
|
||||
const supplyRate = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getSupplyRate'
|
||||
})
|
||||
console.log('=== getSupplyRate ===')
|
||||
console.log('原始值:', supplyRate.toString())
|
||||
console.log()
|
||||
|
||||
if (supplyRate > 0n) {
|
||||
const apyE18 = Number(supplyRate) * secondsPerYear / 1e18
|
||||
console.log('假设精度 1e18 (每秒利率):')
|
||||
console.log(' 年化利率 (APY):', apyE18.toFixed(2), '%')
|
||||
console.log()
|
||||
}
|
||||
|
||||
// 3. 获取使用率
|
||||
const utilization = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getUtilization'
|
||||
})
|
||||
console.log('=== getUtilization ===')
|
||||
console.log('原始值:', utilization.toString())
|
||||
console.log()
|
||||
|
||||
if (utilization > 0n) {
|
||||
// Compound V3 使用率通常是 1e18 精度
|
||||
const utilizationPercent = Number(utilization) / 1e18 * 100
|
||||
console.log('假设精度 1e18:')
|
||||
console.log(' 使用率:', utilizationPercent.toFixed(2), '%')
|
||||
console.log()
|
||||
|
||||
// 如果是 1e4 精度
|
||||
const utilizationE4 = Number(utilization) / 1e4
|
||||
console.log('假设精度 1e4:')
|
||||
console.log(' 使用率:', utilizationE4.toFixed(2), '%')
|
||||
console.log()
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
153
frontend/scripts/check-lending-config.js
Normal file
153
frontend/scripts/check-lending-config.js
Normal file
@@ -0,0 +1,153 @@
|
||||
import { createPublicClient, http } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const CONFIGURATOR = '0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc'
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D'
|
||||
const YT_A = '0x97204190B35D9895a7a47aa7BaC61ac08De3cF05'
|
||||
const USDC = '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d'
|
||||
|
||||
const CONFIGURATOR_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: 'lendingProxy', type: 'address' }
|
||||
],
|
||||
name: 'getConfiguration',
|
||||
outputs: [
|
||||
{
|
||||
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: 'uint64', name: 'trackingIndexScale', 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: '',
|
||||
type: 'tuple'
|
||||
}
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [],
|
||||
name: 'paused',
|
||||
outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查 Lending 合约配置状态...\n')
|
||||
|
||||
try {
|
||||
// 读取配置
|
||||
const config = await client.readContract({
|
||||
address: CONFIGURATOR,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'getConfiguration',
|
||||
args: [LENDING_PROXY]
|
||||
})
|
||||
|
||||
// 检查暂停状态
|
||||
const isPaused = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'paused'
|
||||
})
|
||||
|
||||
console.log('=== 基础配置 ===')
|
||||
console.log('baseToken:', config.baseToken)
|
||||
console.log('是否为 USDC:', config.baseToken.toLowerCase() === USDC.toLowerCase() ? '✓ 是' : '✗ 否')
|
||||
console.log('lendingPriceSource:', config.lendingPriceSource)
|
||||
console.log('是否为零地址:', config.lendingPriceSource === '0x0000000000000000000000000000000000000000' ? '✗ 是(错误)' : '✓ 否')
|
||||
|
||||
console.log('\n=== 利率参数 ===')
|
||||
console.log('supplyKink:', config.supplyKink.toString())
|
||||
console.log('borrowKink:', config.borrowKink.toString())
|
||||
console.log('baseBorrowMin:', config.baseBorrowMin.toString())
|
||||
console.log('targetReserves:', config.targetReserves.toString())
|
||||
|
||||
console.log('\n=== 系统状态 ===')
|
||||
console.log('是否暂停:', isPaused ? '✗ 是(无法操作)' : '✓ 否')
|
||||
|
||||
console.log('\n=== 抵押品配置 ===')
|
||||
console.log('配置的抵押品数量:', config.assetConfigs.length)
|
||||
|
||||
const ytAConfig = config.assetConfigs.find(
|
||||
cfg => cfg.asset.toLowerCase() === YT_A.toLowerCase()
|
||||
)
|
||||
|
||||
if (ytAConfig) {
|
||||
console.log('\nYT-A 配置:')
|
||||
console.log(' 地址:', ytAConfig.asset)
|
||||
console.log(' 精度:', ytAConfig.decimals)
|
||||
console.log(' 借款抵押率:', Number(ytAConfig.borrowCollateralFactor) / 1e16, '%')
|
||||
console.log(' 清算抵押率:', Number(ytAConfig.liquidateCollateralFactor) / 1e16, '%')
|
||||
console.log(' 清算奖励:', Number(ytAConfig.liquidationFactor) / 1e16, '%')
|
||||
console.log(' 供应上限:', Number(ytAConfig.supplyCap) / 1e18, 'tokens')
|
||||
} else {
|
||||
console.log('\n✗ YT-A 未配置!')
|
||||
}
|
||||
|
||||
// 诊断
|
||||
console.log('\n=== 诊断结果 ===')
|
||||
const issues = []
|
||||
|
||||
if (config.baseToken === '0x0000000000000000000000000000000000000000') {
|
||||
issues.push('✗ baseToken 未设置(零地址)')
|
||||
}
|
||||
if (config.lendingPriceSource === '0x0000000000000000000000000000000000000000') {
|
||||
issues.push('✗ lendingPriceSource 未设置(零地址)')
|
||||
}
|
||||
if (isPaused) {
|
||||
issues.push('✗ 系统已暂停')
|
||||
}
|
||||
if (!ytAConfig) {
|
||||
issues.push('✗ YT-A 未配置')
|
||||
}
|
||||
|
||||
if (issues.length > 0) {
|
||||
console.log('发现问题:')
|
||||
issues.forEach(issue => console.log(' ' + issue))
|
||||
} else {
|
||||
console.log('✓ 所有配置正常')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('✗ 读取配置失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
237
frontend/scripts/check-lending-setup.js
Normal file
237
frontend/scripts/check-lending-setup.js
Normal file
@@ -0,0 +1,237 @@
|
||||
import { createPublicClient, http, parseAbi } from 'viem';
|
||||
import { arbitrumSepolia } from 'viem/chains';
|
||||
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D';
|
||||
const CONFIGURATOR_ADDRESS = '0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc';
|
||||
|
||||
const LENDING_ABI = parseAbi([
|
||||
'function owner() view returns (address)',
|
||||
'function configurator() view returns (address)',
|
||||
'function getConfigurator() view returns (address)',
|
||||
'function setConfigurator(address) external',
|
||||
'function paused() view returns (bool)',
|
||||
'function getTotalSupply() view returns (uint256)',
|
||||
'function getTotalBorrow() view returns (uint256)',
|
||||
'function getUtilization() view returns (uint256)',
|
||||
'function getBorrowRate() view returns (uint256)',
|
||||
'function getSupplyRate(uint256) view returns (uint256)',
|
||||
'function initialize(address,address,address) external'
|
||||
]);
|
||||
|
||||
const CONFIGURATOR_ABI = [
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "lendingProxy", "type": "address" }
|
||||
],
|
||||
"name": "getConfiguration",
|
||||
"outputs": [
|
||||
{
|
||||
"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": "uint64", "name": "trackingIndexScale", "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": "",
|
||||
"type": "tuple"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
];
|
||||
|
||||
const publicClient = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc')
|
||||
});
|
||||
|
||||
async function checkLending() {
|
||||
console.log('🔍 检查 Lending 合约设置\n');
|
||||
|
||||
try {
|
||||
// 检查 owner
|
||||
const owner = await publicClient.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'owner'
|
||||
});
|
||||
console.log('1️⃣ Lending Owner:', owner);
|
||||
} catch (error) {
|
||||
console.log('1️⃣ ❌ 读取 owner 失败');
|
||||
}
|
||||
|
||||
// 尝试读取configurator地址
|
||||
const getters = ['configurator', 'getConfigurator'];
|
||||
let foundConfigurator = null;
|
||||
|
||||
for (const getter of getters) {
|
||||
try {
|
||||
console.log(`\n2️⃣ 尝试调用 ${getter}()...`);
|
||||
const configuratorAddr = await publicClient.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: getter
|
||||
});
|
||||
console.log(` ✅ ${getter}() = ${configuratorAddr}`);
|
||||
console.log(` 期望地址: ${CONFIGURATOR_ADDRESS}`);
|
||||
console.log(` 匹配: ${configuratorAddr.toLowerCase() === CONFIGURATOR_ADDRESS.toLowerCase() ? '✅' : '❌'}`);
|
||||
|
||||
foundConfigurator = configuratorAddr;
|
||||
|
||||
if (configuratorAddr.toLowerCase() !== CONFIGURATOR_ADDRESS.toLowerCase()) {
|
||||
console.log('\n⚠️ 警告: Configurator 地址不匹配!');
|
||||
console.log('💡 Lending 合约指向了不同的 Configurator');
|
||||
console.log(` Lending 中的: ${configuratorAddr}`);
|
||||
console.log(` 前端使用的: ${CONFIGURATOR_ADDRESS}`);
|
||||
}
|
||||
|
||||
break;
|
||||
} catch (error) {
|
||||
console.log(` ❌ ${getter}() 不存在或调用失败`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundConfigurator) {
|
||||
console.log('\n⚠️ 未找到 Configurator 地址!');
|
||||
console.log('💡 Lending 合约可能需要先通过 setConfigurator() 设置 Configurator 地址\n');
|
||||
}
|
||||
|
||||
// 检查是否暂停
|
||||
try {
|
||||
const isPaused = await publicClient.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'paused'
|
||||
});
|
||||
console.log('\n3️⃣ 系统暂停状态:', isPaused ? '已暂停' : '运行中');
|
||||
} catch (error) {
|
||||
console.log('\n3️⃣ ❌ 读取暂停状态失败');
|
||||
}
|
||||
|
||||
// 检查流动性
|
||||
try {
|
||||
const liquidity = await publicClient.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getTotalSupply'
|
||||
});
|
||||
console.log('4️⃣ 总供应量:', (Number(liquidity) / 1e6).toFixed(2), 'USDC');
|
||||
} catch (error) {
|
||||
console.log('4️⃣ ❌ 读取总供应量失败');
|
||||
}
|
||||
|
||||
// 检查系统数据查询函数
|
||||
console.log('\n📊 系统数据查询:');
|
||||
|
||||
try {
|
||||
const totalBorrow = await publicClient.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getTotalBorrow'
|
||||
});
|
||||
console.log(' 总借款:', (Number(totalBorrow) / 1e6).toFixed(2), 'USDC');
|
||||
} catch (error) {
|
||||
console.log(' ❌ getTotalBorrow() 调用失败');
|
||||
}
|
||||
|
||||
try {
|
||||
const utilization = await publicClient.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getUtilization'
|
||||
});
|
||||
console.log(' 资金利用率:', (Number(utilization) / 1e18 * 100).toFixed(2), '%');
|
||||
} catch (error) {
|
||||
console.log(' ❌ getUtilization() 调用失败');
|
||||
}
|
||||
|
||||
try {
|
||||
const borrowRate = await publicClient.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getBorrowRate'
|
||||
});
|
||||
const borrowAPR = (Number(borrowRate) / 1e18 * 100).toFixed(2);
|
||||
console.log(' 借款年利率:', borrowAPR, '%');
|
||||
} catch (error) {
|
||||
console.log(' ❌ getBorrowRate() 调用失败');
|
||||
}
|
||||
|
||||
try {
|
||||
const utilization = await publicClient.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getUtilization'
|
||||
});
|
||||
const supplyRate = await publicClient.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getSupplyRate',
|
||||
args: [utilization]
|
||||
});
|
||||
const supplyAPR = (Number(supplyRate) / 1e18 * 100).toFixed(2);
|
||||
console.log(' 存款年利率:', supplyAPR, '%');
|
||||
} catch (error) {
|
||||
console.log(' ❌ getSupplyRate() 调用失败');
|
||||
}
|
||||
|
||||
// 检查 Configurator 配置
|
||||
console.log('\n⚙️ Configurator 配置:');
|
||||
|
||||
try {
|
||||
const config = await publicClient.readContract({
|
||||
address: CONFIGURATOR_ADDRESS,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'getConfiguration',
|
||||
args: [LENDING_PROXY]
|
||||
});
|
||||
|
||||
console.log(' 基础资产:', config.baseToken);
|
||||
console.log(' 价格源:', config.lendingPriceSource);
|
||||
console.log(' 抵押品资产数量:', config.assetConfigs.length);
|
||||
|
||||
config.assetConfigs.forEach((asset, index) => {
|
||||
console.log(`\n 抵押品 ${index + 1}:`);
|
||||
console.log(` 地址: ${asset.asset}`);
|
||||
console.log(` 精度: ${asset.decimals}`);
|
||||
console.log(` 借款抵押率: ${(Number(asset.borrowCollateralFactor) / 1e18 * 100).toFixed(2)}%`);
|
||||
console.log(` 清算抵押率: ${(Number(asset.liquidateCollateralFactor) / 1e18 * 100).toFixed(2)}%`);
|
||||
console.log(` 清算奖励: ${(Number(asset.liquidationFactor) / 1e18 * 100).toFixed(2)}%`);
|
||||
console.log(` 供应上限: ${(Number(asset.supplyCap) / (10 ** asset.decimals)).toFixed(2)}`);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(' ❌ 读取 Configurator 配置失败:', error.message);
|
||||
}
|
||||
|
||||
console.log('\n💡 总结:');
|
||||
console.log(' - 检查 Lending 合约是否正确设置了 Configurator 地址');
|
||||
console.log(' - 检查 Configurator 合约是否正确设置了 Lending 地址');
|
||||
console.log(' - 这两个合约需要相互关联才能正常工作');
|
||||
}
|
||||
|
||||
checkLending();
|
||||
46
frontend/scripts/check-owner.js
Normal file
46
frontend/scripts/check-owner.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import { createPublicClient, http } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const CONFIGURATOR = '0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc'
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D'
|
||||
|
||||
const OWNER_ABI = [
|
||||
{
|
||||
inputs: [],
|
||||
name: 'owner',
|
||||
outputs: [{ internalType: 'address', name: '', type: 'address' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('🔍 检查合约 owner...\n')
|
||||
|
||||
try {
|
||||
const configuratorOwner = await client.readContract({
|
||||
address: CONFIGURATOR,
|
||||
abi: OWNER_ABI,
|
||||
functionName: 'owner'
|
||||
})
|
||||
|
||||
const lendingOwner = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: OWNER_ABI,
|
||||
functionName: 'owner'
|
||||
})
|
||||
|
||||
console.log('Configurator Owner:', configuratorOwner)
|
||||
console.log('Lending Proxy Owner:', lendingOwner)
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 读取 owner 失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
110
frontend/scripts/check-price-decimals.js
Normal file
110
frontend/scripts/check-price-decimals.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PRICE_FEED = getAddress('0xE82c7cB9CfA42D6eb7e443956b78f8290249c316')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查价格精度\n')
|
||||
|
||||
try {
|
||||
// 检查 decimals
|
||||
const decimals = await client.readContract({
|
||||
address: LENDING_PRICE_FEED,
|
||||
abi: [{
|
||||
inputs: [],
|
||||
name: 'decimals',
|
||||
outputs: [{ name: '', type: 'uint8' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'decimals'
|
||||
})
|
||||
console.log('✓ decimals:', decimals)
|
||||
|
||||
const price = await client.readContract({
|
||||
address: LENDING_PRICE_FEED,
|
||||
abi: [{
|
||||
inputs: [{ name: 'asset', type: 'address' }],
|
||||
name: 'getPrice',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'getPrice',
|
||||
args: [YT_A]
|
||||
})
|
||||
|
||||
console.log('\nYT-A 价格:')
|
||||
console.log(' 原始值:', price.toString())
|
||||
console.log(' ÷ 1e8:', Number(price) / 1e8)
|
||||
console.log(' ÷ 1e18:', Number(price) / 1e18)
|
||||
console.log(' ÷ 1e', decimals, ':', Number(price) / (10 ** Number(decimals)))
|
||||
|
||||
// 如果价格是 1e22,可能是:
|
||||
// - 错误地设置为 1e30 的固定价格(Compound V3 使用 1e30 作为基准)
|
||||
// - 或者配置了错误的精度
|
||||
|
||||
if (price > 1e20) {
|
||||
console.log('\n⚠️ 警告:价格异常高!')
|
||||
console.log('可能原因:')
|
||||
console.log('1. 价格设置错误(使用了 1e30 而不是合适的精度)')
|
||||
console.log('2. 精度配置错误')
|
||||
console.log('\n建议:')
|
||||
console.log('如果 YT-A 应该价值 $1,价格应该设置为:')
|
||||
console.log(` - 如果精度是 8: ${1e8}`)
|
||||
console.log(` - 如果精度是 18: ${1e18}`)
|
||||
console.log(` - 如果使用 Compound V3 格式: ${1e18} (价格) * ${1e18} (精度) / (资产精度)`)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('✗ 查询失败:', error.message)
|
||||
console.log('\n尝试读取价格(无 decimals):')
|
||||
|
||||
try {
|
||||
const price = await client.readContract({
|
||||
address: LENDING_PRICE_FEED,
|
||||
abi: [{
|
||||
inputs: [{ name: 'asset', type: 'address' }],
|
||||
name: 'getPrice',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'getPrice',
|
||||
args: [YT_A]
|
||||
})
|
||||
console.log('价格:', price.toString())
|
||||
console.log('\n如果这个值是 1e30 数量级,说明使用了 Compound V3 的价格格式')
|
||||
console.log('Compound V3 价格 = (USD价格 * 1e', await getBaseScale(), ') / (10^资产精度)')
|
||||
} catch (e2) {
|
||||
console.error('仍然失败:', e2.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getBaseScale() {
|
||||
try {
|
||||
const scale = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
inputs: [],
|
||||
name: 'baseScale',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'baseScale'
|
||||
})
|
||||
return scale
|
||||
} catch {
|
||||
return '?'
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
75
frontend/scripts/check-price-oracle.js
Normal file
75
frontend/scripts/check-price-oracle.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PRICE_FEED = getAddress('0xE82c7cB9CfA42D6eb7e443956b78f8290249c316')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
const USDC = getAddress('0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d')
|
||||
|
||||
const PRICE_FEED_ABI = [
|
||||
{
|
||||
inputs: [{ name: 'asset', type: 'address' }],
|
||||
name: 'getPrice',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'asset', type: 'address' }],
|
||||
name: 'getAssetPrice',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function testPrice(asset, name) {
|
||||
console.log(`\n=== ${name} (${asset}) ===`)
|
||||
|
||||
// 尝试 getPrice
|
||||
try {
|
||||
const price1 = await client.readContract({
|
||||
address: LENDING_PRICE_FEED,
|
||||
abi: PRICE_FEED_ABI,
|
||||
functionName: 'getPrice',
|
||||
args: [asset]
|
||||
})
|
||||
console.log('✓ getPrice:', price1.toString(), '(', Number(price1) / 1e8, 'USD )')
|
||||
} catch (error) {
|
||||
console.log('✗ getPrice 失败:', error.message.split('\n')[0])
|
||||
}
|
||||
|
||||
// 尝试 getAssetPrice
|
||||
try {
|
||||
const price2 = await client.readContract({
|
||||
address: LENDING_PRICE_FEED,
|
||||
abi: PRICE_FEED_ABI,
|
||||
functionName: 'getAssetPrice',
|
||||
args: [asset]
|
||||
})
|
||||
console.log('✓ getAssetPrice:', price2.toString(), '(', Number(price2) / 1e8, 'USD )')
|
||||
} catch (error) {
|
||||
console.log('✗ getAssetPrice 失败:', error.message.split('\n')[0])
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('检查价格预言机\n')
|
||||
console.log('Price Feed 地址:', LENDING_PRICE_FEED)
|
||||
|
||||
await testPrice(YT_A, 'YT-A')
|
||||
await testPrice(USDC, 'USDC')
|
||||
|
||||
console.log('\n=== 诊断 ===')
|
||||
console.log('如果价格查询失败,这就是为什么 getUserAccountData 会 revert')
|
||||
console.log('需要:')
|
||||
console.log('1. 检查价格预言机合约是否正确部署')
|
||||
console.log('2. 检查是否为 YT-A 设置了价格')
|
||||
console.log('3. 可能需要手动调用 setPrice() 来设置价格')
|
||||
}
|
||||
|
||||
main()
|
||||
55
frontend/scripts/check-price.js
Normal file
55
frontend/scripts/check-price.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { createPublicClient, http } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const PRICE_FEED = '0xE82c7cB9CfA42D6eb7e443956b78f8290249c316'
|
||||
const YT_A = '0x97204190B35D9895a7a47aa7BaC61ac08De3cF05'
|
||||
|
||||
const PRICE_FEED_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: 'asset', type: 'address' }
|
||||
],
|
||||
name: 'getPrice',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查价格源...\n')
|
||||
|
||||
try {
|
||||
const price = await client.readContract({
|
||||
address: PRICE_FEED,
|
||||
abi: PRICE_FEED_ABI,
|
||||
functionName: 'getPrice',
|
||||
args: [YT_A]
|
||||
})
|
||||
|
||||
console.log('YT-A 地址:', YT_A)
|
||||
console.log('价格 (原始值):', price.toString())
|
||||
console.log('价格 (格式化):', Number(price) / 1e8, 'USD')
|
||||
|
||||
if (price === 0n) {
|
||||
console.log('\n✗ 错误: 价格为 0!')
|
||||
console.log(' 这会导致存入失败,因为合约无法计算抵押品价值')
|
||||
} else {
|
||||
console.log('\n✓ 价格正常')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n✗ 读取价格失败:', error.message)
|
||||
console.log('\n可能的原因:')
|
||||
console.log(' 1. 价格源合约没有设置 YT-A 的价格')
|
||||
console.log(' 2. getPrice 函数不存在或签名不匹配')
|
||||
console.log(' 3. YT-A 地址在价格源中未注册')
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
76
frontend/scripts/check-recent-events.js
Normal file
76
frontend/scripts/check-recent-events.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
|
||||
const SUPPLY_COLLATERAL_EVENT = {
|
||||
type: 'event',
|
||||
name: 'SupplyCollateral',
|
||||
inputs: [
|
||||
{ indexed: true, name: 'from', type: 'address' },
|
||||
{ indexed: true, name: 'dst', type: 'address' },
|
||||
{ indexed: true, name: 'asset', type: 'address' },
|
||||
{ indexed: false, name: 'amount', type: 'uint256' }
|
||||
]
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查最近的 SupplyCollateral 事件...\n')
|
||||
|
||||
const latestBlock = await client.getBlockNumber()
|
||||
console.log('最新区块:', latestBlock)
|
||||
console.log('查询范围: 最近 1000 个区块\n')
|
||||
|
||||
try {
|
||||
const logs = await client.getLogs({
|
||||
address: LENDING_PROXY,
|
||||
event: SUPPLY_COLLATERAL_EVENT,
|
||||
fromBlock: latestBlock - 1000n,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
|
||||
if (logs.length === 0) {
|
||||
console.log('未找到任何 SupplyCollateral 事件')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('找到', logs.length, '个事件:\n')
|
||||
|
||||
logs.forEach((log, i) => {
|
||||
const { from, dst, asset, amount } = log.args
|
||||
const isCurrentUser = from.toLowerCase() === USER.toLowerCase()
|
||||
|
||||
console.log((i + 1) + '. 区块', log.blockNumber)
|
||||
console.log(' 交易:', log.transactionHash)
|
||||
console.log(' From:', from, isCurrentUser ? '<- 你的地址' : '')
|
||||
console.log(' To:', dst)
|
||||
console.log(' Asset:', asset)
|
||||
console.log(' Amount:', Number(amount) / 1e18)
|
||||
console.log()
|
||||
})
|
||||
|
||||
const userLogs = logs.filter(log =>
|
||||
log.args.from.toLowerCase() === USER.toLowerCase()
|
||||
)
|
||||
|
||||
if (userLogs.length > 0) {
|
||||
console.log('\n找到你的', userLogs.length, '笔存入记录!')
|
||||
console.log('最近一笔:')
|
||||
const latest = userLogs[userLogs.length - 1]
|
||||
console.log(' 交易哈希:', latest.transactionHash)
|
||||
console.log(' 数量:', Number(latest.args.amount) / 1e18, 'YT-A')
|
||||
console.log(' 区块:', latest.blockNumber)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
108
frontend/scripts/check-token-transfers.js
Normal file
108
frontend/scripts/check-token-transfers.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
|
||||
const TRANSFER_EVENT = {
|
||||
type: 'event',
|
||||
name: 'Transfer',
|
||||
inputs: [
|
||||
{ indexed: true, name: 'from', type: 'address' },
|
||||
{ indexed: true, name: 'to', type: 'address' },
|
||||
{ indexed: false, name: 'value', type: 'uint256' }
|
||||
]
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查 YT-A 代币转账... \n')
|
||||
|
||||
const latestBlock = await client.getBlockNumber()
|
||||
console.log('最新区块:', latestBlock)
|
||||
console.log('查询范围: 最近 1000 个区块\n')
|
||||
|
||||
try {
|
||||
// 检查转入 Lending 合约的 Transfer
|
||||
console.log('--- 1. 查找转入 Lending 合约的转账 ---')
|
||||
const logsTo = await client.getLogs({
|
||||
address: YT_A,
|
||||
event: TRANSFER_EVENT,
|
||||
args: {
|
||||
to: LENDING_PROXY
|
||||
},
|
||||
fromBlock: latestBlock - 1000n,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
|
||||
if (logsTo.length === 0) {
|
||||
console.log('✗ 未找到任何转入 Lending 合约的转账\n')
|
||||
} else {
|
||||
console.log('✓ 找到', logsTo.length, '笔转入记录:\n')
|
||||
logsTo.forEach((log, i) => {
|
||||
const { from, to, value } = log.args
|
||||
const isFromUser = from.toLowerCase() === USER.toLowerCase()
|
||||
console.log((i + 1) + '. 区块', log.blockNumber)
|
||||
console.log(' 交易:', log.transactionHash)
|
||||
console.log(' From:', from, isFromUser ? '<- 你的地址' : '')
|
||||
console.log(' To:', to)
|
||||
console.log(' Amount:', Number(value) / 1e18, 'YT-A')
|
||||
console.log()
|
||||
})
|
||||
}
|
||||
|
||||
// 检查从用户发出的 Transfer
|
||||
console.log('--- 2. 查找从你的地址发出的转账 ---')
|
||||
const logsFrom = await client.getLogs({
|
||||
address: YT_A,
|
||||
event: TRANSFER_EVENT,
|
||||
args: {
|
||||
from: USER
|
||||
},
|
||||
fromBlock: latestBlock - 1000n,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
|
||||
if (logsFrom.length === 0) {
|
||||
console.log('✗ 未找到任何从你地址发出的转账\n')
|
||||
} else {
|
||||
console.log('✓ 找到', logsFrom.length, '笔转出记录:\n')
|
||||
logsFrom.forEach((log, i) => {
|
||||
const { from, to, value } = log.args
|
||||
const isToLending = to.toLowerCase() === LENDING_PROXY.toLowerCase()
|
||||
console.log((i + 1) + '. 区块', log.blockNumber)
|
||||
console.log(' 交易:', log.transactionHash)
|
||||
console.log(' From:', from)
|
||||
console.log(' To:', to, isToLending ? '<- Lending 合约' : '')
|
||||
console.log(' Amount:', Number(value) / 1e18, 'YT-A')
|
||||
console.log()
|
||||
})
|
||||
}
|
||||
|
||||
// 检查用户的 YT-A 余额
|
||||
console.log('--- 3. 检查当前 YT-A 余额 ---')
|
||||
const balance = await client.readContract({
|
||||
address: YT_A,
|
||||
abi: [{
|
||||
inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
|
||||
name: 'balanceOf',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'balanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
console.log('你的 YT-A 余额:', Number(balance) / 1e18, '\n')
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
75
frontend/scripts/check-transaction-details.js
Normal file
75
frontend/scripts/check-transaction-details.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const TX_HASH = '0xf38e4201b397b2e267402e2b355c811fb0d99ce2c9f67b3b0fff26028a7a4df4'
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查交易详情...\n')
|
||||
console.log('交易哈希:', TX_HASH, '\n')
|
||||
|
||||
try {
|
||||
// 获取交易详情
|
||||
const tx = await client.getTransaction({ hash: TX_HASH })
|
||||
|
||||
console.log('=== 交易基本信息 ===')
|
||||
console.log('From:', tx.from)
|
||||
console.log('To:', tx.to)
|
||||
console.log('区块:', tx.blockNumber)
|
||||
console.log('Gas Used:', tx.gas.toString())
|
||||
console.log('\n函数调用 (input):', tx.input.slice(0, 200) + '...')
|
||||
console.log('函数选择器:', tx.input.slice(0, 10))
|
||||
|
||||
// 获取交易回执
|
||||
const receipt = await client.getTransactionReceipt({ hash: TX_HASH })
|
||||
|
||||
console.log('\n=== 交易回执 ===')
|
||||
console.log('状态:', receipt.status === 'success' ? '✓ 成功' : '✗ 失败')
|
||||
console.log('Gas 实际使用:', receipt.gasUsed.toString())
|
||||
console.log('事件数量:', receipt.logs.length)
|
||||
|
||||
console.log('\n=== 事件日志 ===')
|
||||
receipt.logs.forEach((log, i) => {
|
||||
console.log(`\n事件 ${i + 1}:`)
|
||||
console.log(' 合约:', log.address)
|
||||
console.log(' Topics[0]:', log.topics[0])
|
||||
|
||||
// 识别 Transfer 事件 (keccak256("Transfer(address,address,uint256)"))
|
||||
if (log.topics[0] === '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef') {
|
||||
console.log(' 类型: Transfer 事件')
|
||||
if (log.topics[1]) console.log(' From:', '0x' + log.topics[1].slice(26))
|
||||
if (log.topics[2]) console.log(' To:', '0x' + log.topics[2].slice(26))
|
||||
}
|
||||
// 识别 SupplyCollateral 事件
|
||||
else if (log.topics[0] === '0x...') { // 需要知道实际的事件签名
|
||||
console.log(' 类型: SupplyCollateral 事件')
|
||||
}
|
||||
else {
|
||||
console.log(' 类型: 未知事件')
|
||||
}
|
||||
|
||||
console.log(' Data:', log.data.slice(0, 66) + (log.data.length > 66 ? '...' : ''))
|
||||
})
|
||||
|
||||
// 解析函数选择器
|
||||
const selector = tx.input.slice(0, 10)
|
||||
console.log('\n=== 函数识别 ===')
|
||||
const functionMap = {
|
||||
'0x47e7ef24': 'deposit(address,uint256)',
|
||||
'0xe8eda9df': 'supplyCollateral(address,uint256)',
|
||||
'0x23b872dd': 'transferFrom(address,address,uint256)',
|
||||
'0xf213159c': 'supply(uint256)',
|
||||
'0x2e1a7d4d': 'withdraw(uint256)'
|
||||
}
|
||||
console.log('调用函数:', functionMap[selector] || '未知: ' + selector)
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
158
frontend/scripts/check-usdc-supply-history.js
Normal file
158
frontend/scripts/check-usdc-supply-history.js
Normal file
@@ -0,0 +1,158 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USDC = getAddress('0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
|
||||
const TRANSFER_EVENT = {
|
||||
type: 'event',
|
||||
name: 'Transfer',
|
||||
inputs: [
|
||||
{ indexed: true, name: 'from', type: 'address' },
|
||||
{ indexed: true, name: 'to', type: 'address' },
|
||||
{ indexed: false, name: 'value', type: 'uint256' }
|
||||
]
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查 USDC 供应/提取历史...\n')
|
||||
console.log('用户:', USER)
|
||||
console.log('Lending 合约:', LENDING_PROXY)
|
||||
console.log()
|
||||
|
||||
const latestBlock = await client.getBlockNumber()
|
||||
console.log('最新区块:', latestBlock)
|
||||
console.log('查询范围: 最近 10000 个区块\n')
|
||||
|
||||
try {
|
||||
// 1. 检查用户转入 Lending 的 USDC(supply 操作)
|
||||
console.log('=== 1. USDC 转入 Lending 合约(Supply)===')
|
||||
const logsToLending = await client.getLogs({
|
||||
address: USDC,
|
||||
event: TRANSFER_EVENT,
|
||||
args: {
|
||||
from: USER,
|
||||
to: LENDING_PROXY
|
||||
},
|
||||
fromBlock: latestBlock - 10000n,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
|
||||
if (logsToLending.length > 0) {
|
||||
console.log(`找到 ${logsToLending.length} 笔转入记录:\n`)
|
||||
let totalSupplied = 0n
|
||||
logsToLending.forEach((log, i) => {
|
||||
const { value } = log.args
|
||||
totalSupplied += value
|
||||
console.log(`${i + 1}. 区块 ${log.blockNumber}`)
|
||||
console.log(` 交易: ${log.transactionHash}`)
|
||||
console.log(` 数量: ${Number(value) / 1e6} USDC`)
|
||||
console.log()
|
||||
})
|
||||
console.log(`总存入: ${Number(totalSupplied) / 1e6} USDC\n`)
|
||||
} else {
|
||||
console.log('未找到转入记录\n')
|
||||
}
|
||||
|
||||
// 2. 检查 Lending 转给用户的 USDC(withdraw 操作)
|
||||
console.log('=== 2. USDC 从 Lending 转出(Withdraw)===')
|
||||
const logsFromLending = await client.getLogs({
|
||||
address: USDC,
|
||||
event: TRANSFER_EVENT,
|
||||
args: {
|
||||
from: LENDING_PROXY,
|
||||
to: USER
|
||||
},
|
||||
fromBlock: latestBlock - 10000n,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
|
||||
if (logsFromLending.length > 0) {
|
||||
console.log(`找到 ${logsFromLending.length} 笔转出记录:\n`)
|
||||
let totalWithdrawn = 0n
|
||||
logsFromLending.forEach((log, i) => {
|
||||
const { value } = log.args
|
||||
totalWithdrawn += value
|
||||
console.log(`${i + 1}. 区块 ${log.blockNumber}`)
|
||||
console.log(` 交易: ${log.transactionHash}`)
|
||||
console.log(` 数量: ${Number(value) / 1e6} USDC`)
|
||||
console.log()
|
||||
})
|
||||
console.log(`总提取: ${Number(totalWithdrawn) / 1e6} USDC\n`)
|
||||
} else {
|
||||
console.log('未找到转出记录\n')
|
||||
}
|
||||
|
||||
// 3. 获取当前余额
|
||||
console.log('=== 3. 当前状态 ===')
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'getBalance',
|
||||
outputs: [{ name: '', type: 'int256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
const balance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getBalance',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
const borrowBalance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'borrowBalanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
console.log('USDC 余额(在 Lending 中):', Number(balance) / 1e6, 'USDC')
|
||||
console.log(' 原始值:', balance.toString())
|
||||
console.log(' 正数 = 存款,负数 = 借款')
|
||||
console.log()
|
||||
console.log('借款余额:', Number(borrowBalance) / 1e6, 'USDC')
|
||||
console.log()
|
||||
|
||||
// 4. 分析
|
||||
console.log('=== 分析 ===')
|
||||
if (balance > 0) {
|
||||
console.log('⚠️ 你当前有存款!')
|
||||
console.log(` 存款金额: ${Number(balance) / 1e6} USDC`)
|
||||
console.log()
|
||||
console.log('这意味着:')
|
||||
console.log(` - 如果你借款 ≤ ${Number(balance) / 1e6} USDC,只是提取存款`)
|
||||
console.log(` - 只有借款 > ${Number(balance) / 1e6} USDC,才会产生债务`)
|
||||
console.log()
|
||||
console.log('建议:')
|
||||
console.log(' 1. 如果要真正借款,请借款金额大于当前存款')
|
||||
console.log(` 例如:借款 ${Number(balance) / 1e6 + 100} USDC`)
|
||||
console.log(' 2. 或者先提取所有存款,再借款')
|
||||
} else if (balance < 0) {
|
||||
console.log('✓ 你有借款!')
|
||||
console.log(` 借款金额: ${-Number(balance) / 1e6} USDC`)
|
||||
} else {
|
||||
console.log('✓ 你没有存款也没有借款')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
100
frontend/scripts/check-user-account-data.js
Normal file
100
frontend/scripts/check-user-account-data.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [{ internalType: 'address', name: '_user', type: 'address' }],
|
||||
name: 'getUserAccountData',
|
||||
outputs: [
|
||||
{ internalType: 'uint256', name: 'totalCollateralValue', type: 'uint256' },
|
||||
{ internalType: 'uint256', name: 'totalBorrowValue', type: 'uint256' },
|
||||
{ internalType: 'uint256', name: 'availableToBorrow', type: 'uint256' },
|
||||
{ internalType: 'uint256', name: 'healthFactor', type: 'uint256' }
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: '_user', type: 'address' },
|
||||
{ internalType: 'address', name: '_collateralAsset', type: 'address' }
|
||||
],
|
||||
name: 'getUserCollateral',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n检查用户账户数据...\n')
|
||||
console.log('用户地址:', USER)
|
||||
console.log('Lending 合约:', LENDING_PROXY)
|
||||
|
||||
try {
|
||||
// 1. 检查抵押品余额
|
||||
console.log('\n--- 1. 检查 getUserCollateral (YT-A) ---')
|
||||
const collateral = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getUserCollateral',
|
||||
args: [USER, YT_A]
|
||||
})
|
||||
console.log('✓ getUserCollateral:', Number(collateral) / 1e18, 'YT-A')
|
||||
console.log(' 原始值:', collateral.toString())
|
||||
|
||||
// 2. 检查账户数据
|
||||
console.log('\n--- 2. 检查 getUserAccountData ---')
|
||||
const accountData = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getUserAccountData',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
console.log('✓ getUserAccountData 返回:')
|
||||
console.log(' totalCollateralValue:', Number(accountData[0]) / 1e6, 'USD')
|
||||
console.log(' totalBorrowValue:', Number(accountData[1]) / 1e6, 'USD')
|
||||
console.log(' availableToBorrow:', Number(accountData[2]) / 1e6, 'USD')
|
||||
console.log(' healthFactor:', Number(accountData[3]) / 1e4, '%')
|
||||
|
||||
console.log('\n 原始值:')
|
||||
console.log(' [0]:', accountData[0].toString())
|
||||
console.log(' [1]:', accountData[1].toString())
|
||||
console.log(' [2]:', accountData[2].toString())
|
||||
console.log(' [3]:', accountData[3].toString())
|
||||
|
||||
// 3. 诊断
|
||||
console.log('\n--- 诊断 ---')
|
||||
if (collateral > 0n && accountData[0] === 0n) {
|
||||
console.log('⚠️ 问题发现:')
|
||||
console.log(' - getUserCollateral 返回有值(', Number(collateral) / 1e18, 'YT-A)')
|
||||
console.log(' - 但 getUserAccountData 返回总抵押价值为 0')
|
||||
console.log(' 可能原因:')
|
||||
console.log(' 1. 价格预言机返回 0(YT-A 价格未设置)')
|
||||
console.log(' 2. getUserAccountData 函数逻辑错误')
|
||||
console.log(' 3. 抵押品资产未在配置中激活')
|
||||
} else if (collateral === 0n) {
|
||||
console.log('✓ 抵押品余额为 0,这是正常的(如果之前交易失败)')
|
||||
} else if (accountData[0] > 0n) {
|
||||
console.log('✓ 一切正常,抵押品价值已正确计算')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n✗ 调用失败:', error.message)
|
||||
if (error.message.includes('Contract function')) {
|
||||
console.log('\n可能原因:getUserAccountData 函数不存在或签名不匹配')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
106
frontend/scripts/check-withdraw-transactions.js
Normal file
106
frontend/scripts/check-withdraw-transactions.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import { createPublicClient, http, getAddress, keccak256, toBytes } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
|
||||
// 计算函数选择器
|
||||
const withdrawSelector = keccak256(toBytes('withdraw(uint256)')).slice(0, 10)
|
||||
const supplyCollateralSelector = keccak256(toBytes('supplyCollateral(address,uint256)')).slice(0, 10)
|
||||
|
||||
console.log('\n=== 查找用户的交易记录 ===\n')
|
||||
console.log('用户地址:', USER)
|
||||
console.log('Lending 合约:', LENDING_PROXY)
|
||||
console.log('\n函数选择器:')
|
||||
console.log(' withdraw:', withdrawSelector)
|
||||
console.log(' supplyCollateral:', supplyCollateralSelector)
|
||||
console.log()
|
||||
|
||||
async function main() {
|
||||
const latestBlock = await client.getBlockNumber()
|
||||
console.log('最新区块:', latestBlock)
|
||||
console.log('查询范围: 最近 10000 个区块\n')
|
||||
|
||||
// 获取用户发送到 Lending 合约的所有交易
|
||||
const fromBlock = latestBlock - 10000n
|
||||
const toBlock = latestBlock
|
||||
|
||||
console.log('=== 查找所有用户 → Lending 的交易 ===\n')
|
||||
|
||||
let blockNum = fromBlock
|
||||
const transactions = []
|
||||
|
||||
while (blockNum <= toBlock) {
|
||||
const endBlock = blockNum + 1000n > toBlock ? toBlock : blockNum + 1000n
|
||||
|
||||
try {
|
||||
const block = await client.getBlock({
|
||||
blockNumber: blockNum,
|
||||
includeTransactions: true
|
||||
})
|
||||
|
||||
for (const tx of block.transactions) {
|
||||
if (typeof tx === 'object' &&
|
||||
tx.from.toLowerCase() === USER.toLowerCase() &&
|
||||
tx.to?.toLowerCase() === LENDING_PROXY.toLowerCase()) {
|
||||
transactions.push({
|
||||
hash: tx.hash,
|
||||
blockNumber: block.number,
|
||||
input: tx.input
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Skip blocks without transactions
|
||||
}
|
||||
|
||||
blockNum += 1000n
|
||||
}
|
||||
|
||||
console.log(`找到 ${transactions.length} 笔交易\n`)
|
||||
|
||||
if (transactions.length === 0) {
|
||||
console.log('没有找到任何交易')
|
||||
return
|
||||
}
|
||||
|
||||
// 分析每笔交易
|
||||
for (const tx of transactions) {
|
||||
console.log('---')
|
||||
console.log('交易哈希:', tx.hash)
|
||||
console.log('区块:', tx.blockNumber.toString())
|
||||
|
||||
const selector = tx.input.slice(0, 10)
|
||||
console.log('函数选择器:', selector)
|
||||
|
||||
let functionName = '未知'
|
||||
if (selector === withdrawSelector) {
|
||||
functionName = 'withdraw (借款/提取)'
|
||||
// 解析参数 (uint256)
|
||||
const amountHex = '0x' + tx.input.slice(10)
|
||||
const amount = BigInt(amountHex)
|
||||
console.log('调用函数:', functionName)
|
||||
console.log('金额:', Number(amount) / 1e6, 'USDC')
|
||||
} else if (selector === supplyCollateralSelector) {
|
||||
functionName = 'supplyCollateral (存入抵押品)'
|
||||
// 解析参数 (address, uint256)
|
||||
const assetAddress = '0x' + tx.input.slice(34, 74)
|
||||
const amountHex = '0x' + tx.input.slice(74)
|
||||
const amount = BigInt(amountHex)
|
||||
console.log('调用函数:', functionName)
|
||||
console.log('资产:', getAddress(assetAddress))
|
||||
console.log('金额:', Number(amount) / 1e18)
|
||||
} else {
|
||||
console.log('调用函数:', functionName, '-', selector)
|
||||
}
|
||||
|
||||
console.log()
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error)
|
||||
169
frontend/scripts/configure-collateral.js
Normal file
169
frontend/scripts/configure-collateral.js
Normal file
@@ -0,0 +1,169 @@
|
||||
import { createWalletClient, http, createPublicClient } from 'viem'
|
||||
import { privateKeyToAccount } from 'viem/accounts'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
// 从环境变量或命令行参数获取私钥
|
||||
const PRIVATE_KEY = process.env.PRIVATE_KEY || process.argv[2]
|
||||
|
||||
if (!PRIVATE_KEY || !PRIVATE_KEY.startsWith('0x')) {
|
||||
console.error('❌ 请提供私钥:')
|
||||
console.error(' 方式1: export PRIVATE_KEY=0x...')
|
||||
console.error(' 方式2: node configure-collateral.js 0x...')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const account = privateKeyToAccount(PRIVATE_KEY)
|
||||
|
||||
const publicClient = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http()
|
||||
})
|
||||
|
||||
const walletClient = createWalletClient({
|
||||
account,
|
||||
chain: arbitrumSepolia,
|
||||
transport: http()
|
||||
})
|
||||
|
||||
const CONFIGURATOR = '0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc'
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D'
|
||||
|
||||
const COLLATERAL_ASSETS = [
|
||||
{
|
||||
name: 'YT-A',
|
||||
address: '0x97204190B35D9895a7a47aa7BaC61ac08De3cF05',
|
||||
collateralFactor: 7500, // 75%
|
||||
liquidationThreshold: 8500, // 85%
|
||||
liquidationBonus: 1000 // 10%
|
||||
},
|
||||
{
|
||||
name: 'YT-B',
|
||||
address: '0x181ef4011c35C4a2Fda08eBC5Cf509Ef58E553fF',
|
||||
collateralFactor: 7500,
|
||||
liquidationThreshold: 8500,
|
||||
liquidationBonus: 1000
|
||||
},
|
||||
{
|
||||
name: 'YT-C',
|
||||
address: '0xE9A5b9f3a2Eda4358f81d4E2eF4f3280A664e5B0',
|
||||
collateralFactor: 7500,
|
||||
liquidationThreshold: 8500,
|
||||
liquidationBonus: 1000
|
||||
}
|
||||
]
|
||||
|
||||
// Configurator ABI
|
||||
const CONFIGURATOR_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: '_asset', type: 'address' },
|
||||
{ internalType: 'uint256', name: '_collateralFactor', type: 'uint256' },
|
||||
{ internalType: 'uint256', name: '_liquidationThreshold', type: 'uint256' },
|
||||
{ internalType: 'uint256', name: '_liquidationBonus', type: 'uint256' }
|
||||
],
|
||||
name: 'setCollateralConfig',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: '_asset', type: 'address' },
|
||||
{ internalType: 'bool', name: '_isActive', type: 'bool' }
|
||||
],
|
||||
name: 'setCollateralActive',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'owner',
|
||||
outputs: [{ internalType: 'address', name: '', type: 'address' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function configureCollateral() {
|
||||
console.log('\n🔧 配置借贷抵押品\n')
|
||||
console.log('Configurator:', CONFIGURATOR)
|
||||
console.log('操作者:', account.address)
|
||||
console.log('')
|
||||
|
||||
// 检查权限
|
||||
try {
|
||||
const owner = await publicClient.readContract({
|
||||
address: CONFIGURATOR,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'owner'
|
||||
})
|
||||
console.log('Configurator Owner:', owner)
|
||||
|
||||
if (owner.toLowerCase() !== account.address.toLowerCase()) {
|
||||
console.error('\n❌ 错误: 当前账户不是 Configurator 的 owner')
|
||||
console.error(' 需要使用 owner 账户的私钥')
|
||||
process.exit(1)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ 无法检查owner,继续尝试配置...\n')
|
||||
}
|
||||
|
||||
// 配置每个抵押品
|
||||
for (const asset of COLLATERAL_ASSETS) {
|
||||
console.log(`\n📝 配置 ${asset.name} (${asset.address})`)
|
||||
console.log(` - 抵押率: ${asset.collateralFactor / 100}%`)
|
||||
console.log(` - 清算阈值: ${asset.liquidationThreshold / 100}%`)
|
||||
console.log(` - 清算奖励: ${asset.liquidationBonus / 100}%`)
|
||||
|
||||
try {
|
||||
// 1. 设置抵押品参数
|
||||
console.log(' → 设置参数...')
|
||||
const hash1 = await walletClient.writeContract({
|
||||
address: CONFIGURATOR,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'setCollateralConfig',
|
||||
args: [
|
||||
asset.address,
|
||||
asset.collateralFactor,
|
||||
asset.liquidationThreshold,
|
||||
asset.liquidationBonus
|
||||
]
|
||||
})
|
||||
console.log(' ✅ 参数设置交易:', hash1)
|
||||
|
||||
// 等待确认
|
||||
await publicClient.waitForTransactionReceipt({ hash: hash1 })
|
||||
console.log(' ✅ 交易已确认')
|
||||
|
||||
// 2. 激活抵押品
|
||||
console.log(' → 激活抵押品...')
|
||||
const hash2 = await walletClient.writeContract({
|
||||
address: CONFIGURATOR,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'setCollateralActive',
|
||||
args: [asset.address, true]
|
||||
})
|
||||
console.log(' ✅ 激活交易:', hash2)
|
||||
|
||||
// 等待确认
|
||||
await publicClient.waitForTransactionReceipt({ hash: hash2 })
|
||||
console.log(' ✅ 交易已确认')
|
||||
|
||||
console.log(` ✅ ${asset.name} 配置完成!`)
|
||||
} catch (error) {
|
||||
console.error(` ❌ 配置失败:`, error.message.split('\n')[0])
|
||||
|
||||
// 继续处理下一个
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ 所有抵押品配置完成!\n')
|
||||
console.log('现在可以尝试存入抵押品了。')
|
||||
}
|
||||
|
||||
configureCollateral().catch((error) => {
|
||||
console.error('\n❌ 配置过程出错:', error.message)
|
||||
process.exit(1)
|
||||
})
|
||||
107
frontend/scripts/debug-contract-view-functions.js
Normal file
107
frontend/scripts/debug-contract-view-functions.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import { createPublicClient, http, getAddress, decodeErrorResult } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
|
||||
async function testWithDetails(name, abi, args) {
|
||||
console.log(`\n=== 测试 ${name} ===`)
|
||||
try {
|
||||
const result = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [abi],
|
||||
functionName: name,
|
||||
args: args
|
||||
})
|
||||
console.log('✓ 成功:', result)
|
||||
return result
|
||||
} catch (error) {
|
||||
console.log('✗ 失败')
|
||||
console.log('错误类型:', error.name)
|
||||
console.log('错误消息:', error.message.split('\n')[0])
|
||||
|
||||
// 尝试解码错误
|
||||
if (error.data) {
|
||||
console.log('错误数据:', error.data)
|
||||
}
|
||||
|
||||
// 打印完整堆栈
|
||||
console.log('\n完整错误:')
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('诊断 Lending 合约 View 函数\n')
|
||||
console.log('合约:', LENDING_PROXY)
|
||||
console.log('用户:', USER)
|
||||
console.log('抵押品:', YT_A)
|
||||
|
||||
// 测试 1: borrowBalanceOf (已知能工作)
|
||||
await testWithDetails('borrowBalanceOf', {
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [USER])
|
||||
|
||||
// 测试 2: getUserAccountData
|
||||
await testWithDetails('getUserAccountData', {
|
||||
inputs: [{ name: '_user', type: 'address' }],
|
||||
name: 'getUserAccountData',
|
||||
outputs: [
|
||||
{ name: 'totalCollateralValue', type: 'uint256' },
|
||||
{ name: 'totalBorrowValue', type: 'uint256' },
|
||||
{ name: 'availableToBorrow', type: 'uint256' },
|
||||
{ name: 'healthFactor', type: 'uint256' }
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [USER])
|
||||
|
||||
// 测试 3: getUserCollateral
|
||||
await testWithDetails('getUserCollateral', {
|
||||
inputs: [
|
||||
{ name: '_user', type: 'address' },
|
||||
{ name: '_collateralAsset', type: 'address' }
|
||||
],
|
||||
name: 'getUserCollateral',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [USER, YT_A])
|
||||
|
||||
// 测试 4: getCollateralConfig (检查资产是否配置)
|
||||
await testWithDetails('getCollateralConfig', {
|
||||
inputs: [{ name: '_asset', type: 'address' }],
|
||||
name: 'getCollateralConfig',
|
||||
outputs: [
|
||||
{ name: 'isActive', type: 'bool' },
|
||||
{ name: 'decimals', type: 'uint8' },
|
||||
{ name: 'borrowCollateralFactor', type: 'uint64' },
|
||||
{ name: 'liquidateCollateralFactor', type: 'uint64' },
|
||||
{ name: 'liquidationFactor', type: 'uint64' },
|
||||
{ name: 'supplyCap', type: 'uint128' }
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [YT_A])
|
||||
|
||||
// 测试 5: paused (检查是否暂停)
|
||||
await testWithDetails('paused', {
|
||||
inputs: [],
|
||||
name: 'paused',
|
||||
outputs: [{ name: '', type: 'bool' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [])
|
||||
}
|
||||
|
||||
main()
|
||||
60
frontend/scripts/debug-events.js
Normal file
60
frontend/scripts/debug-events.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import { createPublicClient, http, getAddress, parseAbiItem } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
|
||||
async function main() {
|
||||
const latestBlock = await client.getBlockNumber()
|
||||
console.log('Latest Block:', latestBlock)
|
||||
const fromBlock = latestBlock - 10000n
|
||||
|
||||
console.log(`Checking events from block ${fromBlock} to ${latestBlock}..\n`)
|
||||
|
||||
// Check SupplyCollateral
|
||||
const supplyEvent = parseAbiItem('event SupplyCollateral(address indexed from, address indexed dst, address indexed asset, uint256 amount)')
|
||||
const supplyLogs = await client.getLogs({
|
||||
address: LENDING_PROXY,
|
||||
event: supplyEvent,
|
||||
fromBlock,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
console.log(`SupplyCollateral Events found: ${supplyLogs.length}`)
|
||||
|
||||
// Check Deposit
|
||||
const depositEvent = parseAbiItem('event Deposit(address indexed user, address indexed collateralAsset, uint256 amount)')
|
||||
const depositLogs = await client.getLogs({
|
||||
address: LENDING_PROXY,
|
||||
event: depositEvent,
|
||||
fromBlock,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
console.log(`Deposit Events found: ${depositLogs.length}`)
|
||||
|
||||
// Check WithdrawCollateral
|
||||
const withdrawColEvent = parseAbiItem('event WithdrawCollateral(address indexed src, address indexed to, address indexed asset, uint256 amount)')
|
||||
const withdrawColLogs = await client.getLogs({
|
||||
address: LENDING_PROXY,
|
||||
event: withdrawColEvent,
|
||||
fromBlock,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
console.log(`WithdrawCollateral Events found: ${withdrawColLogs.length}`)
|
||||
|
||||
// Check Withdraw
|
||||
const withdrawEvent = parseAbiItem('event Withdraw(address indexed user, address indexed collateralAsset, uint256 amount)')
|
||||
const withdrawLogs = await client.getLogs({
|
||||
address: LENDING_PROXY,
|
||||
event: withdrawEvent,
|
||||
fromBlock,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
console.log(`Withdraw Events (custom) found: ${withdrawLogs.length}`)
|
||||
|
||||
}
|
||||
|
||||
main().catch(console.error)
|
||||
82
frontend/scripts/debug-lending-functions.js
Normal file
82
frontend/scripts/debug-lending-functions.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import { createPublicClient, http, getAddress, parseAbi, encodeFunctionData } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
// A random user address or the deployer to test view functions
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
|
||||
async function main() {
|
||||
console.log('Checking Lending Proxy View Functions...')
|
||||
|
||||
// Check borrowBalanceOf
|
||||
try {
|
||||
const data = encodeFunctionData({
|
||||
abi: parseAbi(['function borrowBalanceOf(address) view returns (uint256)']),
|
||||
functionName: 'borrowBalanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
const result = await client.call({
|
||||
to: LENDING_PROXY,
|
||||
data
|
||||
})
|
||||
console.log(`✅ borrowBalanceOf exists. Result: ${result.data}`)
|
||||
} catch (e) {
|
||||
console.log(`❌ borrowBalanceOf failed: ${e.message.slice(0, 100)}...`)
|
||||
}
|
||||
|
||||
// Check getBorrowBalance
|
||||
try {
|
||||
const data = encodeFunctionData({
|
||||
abi: parseAbi(['function getBorrowBalance(address) view returns (uint256)']),
|
||||
functionName: 'getBorrowBalance',
|
||||
args: [USER]
|
||||
})
|
||||
const result = await client.call({
|
||||
to: LENDING_PROXY,
|
||||
data
|
||||
})
|
||||
console.log(`✅ getBorrowBalance exists. Result: ${result.data}`)
|
||||
} catch (e) {
|
||||
console.log(`❌ getBorrowBalance failed: ${e.message.slice(0, 100)}...`)
|
||||
}
|
||||
|
||||
// Check getTotalSupply
|
||||
try {
|
||||
const data = encodeFunctionData({
|
||||
abi: parseAbi(['function getTotalSupply() view returns (uint256)']),
|
||||
functionName: 'getTotalSupply',
|
||||
args: []
|
||||
})
|
||||
const result = await client.call({
|
||||
to: LENDING_PROXY,
|
||||
data
|
||||
})
|
||||
console.log(`✅ getTotalSupply exists. Result: ${result.data}`)
|
||||
} catch (e) {
|
||||
console.log(`❌ getTotalSupply failed: ${e.message.slice(0, 100)}...`)
|
||||
}
|
||||
|
||||
// Check getTotalLiquidity
|
||||
try {
|
||||
const data = encodeFunctionData({
|
||||
abi: parseAbi(['function getTotalLiquidity() view returns (uint256)']),
|
||||
functionName: 'getTotalLiquidity',
|
||||
args: []
|
||||
})
|
||||
const result = await client.call({
|
||||
to: LENDING_PROXY,
|
||||
data
|
||||
})
|
||||
console.log(`✅ getTotalLiquidity exists. Result: ${result.data}`)
|
||||
} catch (e) {
|
||||
console.log(`❌ getTotalLiquidity failed: ${e.message.slice(0, 100)}...`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
main()
|
||||
33
frontend/scripts/decode-tx-data.js
Normal file
33
frontend/scripts/decode-tx-data.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { decodeAbiParameters, parseAbiParameters } from 'viem';
|
||||
|
||||
// 从错误日志中的 data 字段
|
||||
const txData = '0x5fcbde4700000000000000000000000097204190b35d9895a7a47aa7bac61ac08de3cf050000000000000000000000000000000000000000000000000000000000001d4c000000000000000000000000000000000000000000000000000000000000213400000000000000000000000000000000000000000000000000000000000003e8';
|
||||
|
||||
// 函数选择器 (前4字节)
|
||||
const selector = txData.slice(0, 10);
|
||||
console.log('函数选择器:', selector);
|
||||
|
||||
// 参数数据 (从第10个字符开始)
|
||||
const paramData = '0x' + txData.slice(10);
|
||||
|
||||
try {
|
||||
// 解码参数
|
||||
const decoded = decodeAbiParameters(
|
||||
parseAbiParameters('address, uint256, uint256, uint256'),
|
||||
paramData
|
||||
);
|
||||
|
||||
console.log('\n解码的参数:');
|
||||
console.log(' _asset (YT-A地址):', decoded[0]);
|
||||
console.log(' _collateralFactor:', decoded[1].toString(), '(即', Number(decoded[1]) / 100, '%)');
|
||||
console.log(' _liquidationThreshold:', decoded[2].toString(), '(即', Number(decoded[2]) / 100, '%)');
|
||||
console.log(' _liquidationBonus:', decoded[3].toString(), '(即', Number(decoded[3]) / 100, '%)');
|
||||
|
||||
console.log('\n✅ 参数解码成功,看起来都是正常的值');
|
||||
console.log('💡 问题可能在于:');
|
||||
console.log(' 1. 合约内部的require条件未满足');
|
||||
console.log(' 2. 可能需要先调用其他初始化函数');
|
||||
console.log(' 3. 可能Lending合约需要先设置到Configurator中');
|
||||
} catch (error) {
|
||||
console.error('解码失败:', error);
|
||||
}
|
||||
107
frontend/scripts/diagnose-configurator.js
Normal file
107
frontend/scripts/diagnose-configurator.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import { createPublicClient, http, parseAbi } from 'viem';
|
||||
import { arbitrumSepolia } from 'viem/chains';
|
||||
|
||||
// 合约地址
|
||||
const CONFIGURATOR_ADDRESS = '0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc';
|
||||
const YT_A_ADDRESS = '0x97204190B35D9895a7a47aa7BaC61ac08De3cF05';
|
||||
const USER_ADDRESS = '0xa013422A5918CD099C63c8CC35283EACa99a705d';
|
||||
|
||||
const CONFIGURATOR_ABI = parseAbi([
|
||||
'function owner() view returns (address)',
|
||||
'function collateralConfigs(address) view returns (bool isActive, uint256 collateralFactor, uint256 liquidationThreshold, uint256 liquidationBonus)',
|
||||
'function setCollateralConfig(address _asset, uint256 _collateralFactor, uint256 _liquidationThreshold, uint256 _liquidationBonus)',
|
||||
'function setCollateralActive(address _asset, bool _isActive)'
|
||||
]);
|
||||
|
||||
const publicClient = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc')
|
||||
});
|
||||
|
||||
async function diagnose() {
|
||||
console.log('🔍 Configurator 诊断工具\n');
|
||||
|
||||
try {
|
||||
// 1. 检查 owner
|
||||
console.log('1️⃣ 检查合约 Owner:');
|
||||
const owner = await publicClient.readContract({
|
||||
address: CONFIGURATOR_ADDRESS,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'owner'
|
||||
});
|
||||
console.log(` Owner: ${owner}`);
|
||||
console.log(` 你的地址: ${USER_ADDRESS}`);
|
||||
const isOwner = owner.toLowerCase() === USER_ADDRESS.toLowerCase();
|
||||
console.log(` 匹配: ${isOwner ? '✅' : '❌'}\n`);
|
||||
|
||||
if (!isOwner) {
|
||||
console.log(' ⚠️ 你不是 owner!这就是交易失败的原因。\n');
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 检查当前配置
|
||||
console.log('2️⃣ 检查 YT-A 当前配置:');
|
||||
try {
|
||||
const config = await publicClient.readContract({
|
||||
address: CONFIGURATOR_ADDRESS,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'collateralConfigs',
|
||||
args: [YT_A_ADDRESS]
|
||||
});
|
||||
console.log(` isActive: ${config[0]}`);
|
||||
console.log(` collateralFactor: ${config[1]}`);
|
||||
console.log(` liquidationThreshold: ${config[2]}`);
|
||||
console.log(` liquidationBonus: ${config[3]}\n`);
|
||||
} catch (error) {
|
||||
console.log(` ❌ 读取失败: ${error.message}\n`);
|
||||
}
|
||||
|
||||
// 3. 尝试模拟调用 setCollateralConfig
|
||||
console.log('3️⃣ 模拟调用 setCollateralConfig(YT-A, 7500, 8500, 1000):');
|
||||
try {
|
||||
const result = await publicClient.simulateContract({
|
||||
address: CONFIGURATOR_ADDRESS,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'setCollateralConfig',
|
||||
args: [YT_A_ADDRESS, 7500n, 8500n, 1000n],
|
||||
account: USER_ADDRESS
|
||||
});
|
||||
console.log(' ✅ 模拟成功!交易应该可以执行\n');
|
||||
console.log(' 模拟结果:', result);
|
||||
} catch (error) {
|
||||
console.log(' ❌ 模拟失败!');
|
||||
console.log(` 错误类型: ${error.name}`);
|
||||
console.log(` 错误信息: ${error.shortMessage || error.message}`);
|
||||
if (error.cause) {
|
||||
console.log(` 底层原因: ${JSON.stringify(error.cause, null, 2)}`);
|
||||
}
|
||||
if (error.details) {
|
||||
console.log(` 详情: ${error.details}`);
|
||||
}
|
||||
if (error.metaMessages) {
|
||||
console.log(` 元信息: ${error.metaMessages.join(', ')}`);
|
||||
}
|
||||
console.log('\n');
|
||||
}
|
||||
|
||||
// 4. 检查合约代码
|
||||
console.log('4️⃣ 检查合约代码:');
|
||||
const bytecode = await publicClient.getBytecode({ address: CONFIGURATOR_ADDRESS });
|
||||
console.log(` 代码大小: ${bytecode ? bytecode.length : 0} bytes`);
|
||||
console.log(` 合约已部署: ${bytecode && bytecode.length > 2 ? '✅' : '❌'}\n`);
|
||||
|
||||
// 5. 建议
|
||||
console.log('💡 诊断建议:');
|
||||
console.log(' 1. 检查合约是否有访问控制(如 Ownable, AccessControl)');
|
||||
console.log(' 2. 参数可能有范围限制(如 collateralFactor 必须 <= 10000)');
|
||||
console.log(' 3. 可能需要先调用其他初始化函数');
|
||||
console.log(' 4. 查看合约源码了解具体的 require 条件');
|
||||
console.log('\n📊 在 Arbiscan 查看合约:');
|
||||
console.log(` https://sepolia.arbiscan.io/address/${CONFIGURATOR_ADDRESS}#code`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 诊断过程出错:', error);
|
||||
}
|
||||
}
|
||||
|
||||
diagnose();
|
||||
130
frontend/scripts/find-missing-tokens.js
Normal file
130
frontend/scripts/find-missing-tokens.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
|
||||
const TRANSFER_EVENT = {
|
||||
type: 'event',
|
||||
name: 'Transfer',
|
||||
inputs: [
|
||||
{ indexed: true, name: 'from', type: 'address' },
|
||||
{ indexed: true, name: 'to', type: 'address' },
|
||||
{ indexed: false, name: 'value', type: 'uint256' }
|
||||
]
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('\n追踪丢失的 YT-A 代币...\n')
|
||||
console.log('你的地址:', USER)
|
||||
console.log('当前余额: 10 YT-A')
|
||||
console.log('之前余额: 400 YT-A')
|
||||
console.log('丢失数量: 390 YT-A\n')
|
||||
|
||||
const latestBlock = await client.getBlockNumber()
|
||||
console.log('最新区块:', latestBlock)
|
||||
console.log('查询范围: 最近 10000 个区块\n')
|
||||
|
||||
try {
|
||||
// 查找所有从用户地址发出的转账
|
||||
console.log('=== 查找所有转出记录 ===\n')
|
||||
const logsOut = await client.getLogs({
|
||||
address: YT_A,
|
||||
event: TRANSFER_EVENT,
|
||||
args: { from: USER },
|
||||
fromBlock: latestBlock - 10000n,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
|
||||
if (logsOut.length === 0) {
|
||||
console.log('✗ 未找到任何转出记录(最近 10000 个区块)\n')
|
||||
} else {
|
||||
console.log(`✓ 找到 ${logsOut.length} 笔转出记录:\n`)
|
||||
|
||||
let totalOut = 0n
|
||||
logsOut.forEach((log, i) => {
|
||||
const { from, to, value } = log.args
|
||||
const isToLending = to.toLowerCase() === LENDING_PROXY.toLowerCase()
|
||||
totalOut += value
|
||||
|
||||
console.log(`${i + 1}. 区块 ${log.blockNumber}`)
|
||||
console.log(` 交易: ${log.transactionHash}`)
|
||||
console.log(` To: ${to} ${isToLending ? '<- Lending 合约' : ''}`)
|
||||
console.log(` 数量: ${Number(value) / 1e18} YT-A`)
|
||||
console.log()
|
||||
})
|
||||
|
||||
console.log(`总转出: ${Number(totalOut) / 1e18} YT-A\n`)
|
||||
}
|
||||
|
||||
// 查找所有转入用户地址的转账
|
||||
console.log('=== 查找所有转入记录 ===\n')
|
||||
const logsIn = await client.getLogs({
|
||||
address: YT_A,
|
||||
event: TRANSFER_EVENT,
|
||||
args: { to: USER },
|
||||
fromBlock: latestBlock - 10000n,
|
||||
toBlock: latestBlock
|
||||
})
|
||||
|
||||
if (logsIn.length === 0) {
|
||||
console.log('✗ 未找到任何转入记录\n')
|
||||
} else {
|
||||
console.log(`✓ 找到 ${logsIn.length} 笔转入记录:\n`)
|
||||
|
||||
let totalIn = 0n
|
||||
logsIn.forEach((log, i) => {
|
||||
const { from, to, value } = log.args
|
||||
totalIn += value
|
||||
|
||||
console.log(`${i + 1}. 区块 ${log.blockNumber}`)
|
||||
console.log(` 交易: ${log.transactionHash}`)
|
||||
console.log(` From: ${from}`)
|
||||
console.log(` 数量: ${Number(value) / 1e18} YT-A`)
|
||||
console.log()
|
||||
})
|
||||
|
||||
console.log(`总转入: ${Number(totalIn) / 1e18} YT-A\n`)
|
||||
}
|
||||
|
||||
// 计算净流出
|
||||
if (logsOut.length > 0 || logsIn.length > 0) {
|
||||
const totalOut = logsOut.reduce((sum, log) => sum + log.args.value, 0n)
|
||||
const totalIn = logsIn.reduce((sum, log) => sum + log.args.value, 0n)
|
||||
const netFlow = Number(totalIn - totalOut) / 1e18
|
||||
|
||||
console.log('=== 汇总 ===')
|
||||
console.log(`总转入: ${Number(totalIn) / 1e18} YT-A`)
|
||||
console.log(`总转出: ${Number(totalOut) / 1e18} YT-A`)
|
||||
console.log(`净变化: ${netFlow > 0 ? '+' : ''}${netFlow} YT-A`)
|
||||
console.log(`当前余额: 10 YT-A\n`)
|
||||
|
||||
// 检查代币是否在 Lending 合约中
|
||||
console.log('=== 检查 Lending 合约余额 ===')
|
||||
const lendingBalance = await client.readContract({
|
||||
address: YT_A,
|
||||
abi: [{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'balanceOf',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'balanceOf',
|
||||
args: [LENDING_PROXY]
|
||||
})
|
||||
console.log(`Lending 合约持有的 YT-A: ${Number(lendingBalance) / 1e18}\n`)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
41
frontend/scripts/get-proxy-implementation.js
Normal file
41
frontend/scripts/get-proxy-implementation.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { createPublicClient, http } from 'viem';
|
||||
import { arbitrumSepolia } from 'viem/chains';
|
||||
|
||||
const CONFIGURATOR_ADDRESS = '0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc';
|
||||
|
||||
// ERC-1967 标准存储槽
|
||||
const IMPLEMENTATION_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
|
||||
|
||||
const publicClient = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc')
|
||||
});
|
||||
|
||||
async function getImplementation() {
|
||||
console.log('🔍 查找代理合约的实现地址\n');
|
||||
|
||||
try {
|
||||
// 读取 ERC-1967 实现槽
|
||||
const slot = await publicClient.getStorageAt({
|
||||
address: CONFIGURATOR_ADDRESS,
|
||||
slot: IMPLEMENTATION_SLOT
|
||||
});
|
||||
|
||||
if (slot && slot !== '0x' + '0'.repeat(64)) {
|
||||
// 从存储槽中提取地址(去掉前面的0)
|
||||
const implementationAddress = '0x' + slot.slice(-40);
|
||||
console.log('✅ 找到实现合约:');
|
||||
console.log(` 代理合约 (Configurator): ${CONFIGURATOR_ADDRESS}`);
|
||||
console.log(` 实现合约 (Implementation): ${implementationAddress}`);
|
||||
console.log(`\n📊 查看实现合约源码:`);
|
||||
console.log(` https://sepolia.arbiscan.io/address/${implementationAddress}#code`);
|
||||
console.log(`\n💡 提示: 需要使用实现合约的 ABI,但调用代理合约的地址`);
|
||||
} else {
|
||||
console.log('❌ 未找到实现地址');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 错误:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
getImplementation();
|
||||
60
frontend/scripts/init-lending-config.js
Normal file
60
frontend/scripts/init-lending-config.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 初始化 Lending 合约基础配置
|
||||
*
|
||||
* ⚠️ 需要使用 Configurator Owner 钱包执行
|
||||
* Owner: 0xa013422A5918CD099C63c8CC35283EACa99a705d
|
||||
*
|
||||
* 使用方法:
|
||||
* 1. 确保钱包连接到 Arbitrum Sepolia
|
||||
* 2. 在管理员面板中手动调用 Configurator 的配置函数
|
||||
*/
|
||||
|
||||
console.log(`
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ Lending 合约初始化配置说明 ║
|
||||
╚════════════════════════════════════════════════════════════╝
|
||||
|
||||
❌ 问题: Lending 合约基础配置未初始化
|
||||
- USDC 地址: 0x0000...0000 ❌
|
||||
- 价格源地址: 0x0000...0000 ❌
|
||||
|
||||
✅ 解决方案: 需要调用 Configurator 初始化函数
|
||||
|
||||
📝 合约信息:
|
||||
- Configurator: 0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc
|
||||
- Lending Proxy: 0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D
|
||||
- Owner: 0xa013422A5918CD099C63c8CC35283EACa99a705d
|
||||
|
||||
🔧 需要设置的参数:
|
||||
1. baseToken (USDC): 0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d
|
||||
2. lendingPriceSource: 0xE82c7cB9CfA42D6eb7e443956b78f8290249c316
|
||||
3. 利率参数 (borrowKink, supplyKink 等)
|
||||
4. baseBorrowMin, targetReserves 等
|
||||
|
||||
⚠️ 这需要合约开发者或管理员操作!
|
||||
|
||||
💡 可能的原因:
|
||||
1. 合约刚部署,还没有初始化
|
||||
2. 初始化函数调用失败
|
||||
3. 配置被重置了
|
||||
|
||||
📞 建议:
|
||||
联系合约部署者 (0xa013422A5918CD099C63c8CC35283EACa99a705d)
|
||||
使用 Configurator 合约的初始化函数设置基础配置
|
||||
`)
|
||||
|
||||
// 显示需要调用的函数签名
|
||||
console.log(`
|
||||
🔍 需要调用的 Configurator 函数示例:
|
||||
|
||||
function setConfiguration(
|
||||
address lendingProxy,
|
||||
Configuration memory configuration
|
||||
) external onlyOwner
|
||||
|
||||
其中 Configuration 结构包括:
|
||||
- baseToken: 0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d (USDC)
|
||||
- lendingPriceSource: 0xE82c7cB9CfA42D6eb7e443956b78f8290249c316
|
||||
- 利率参数(根据文档配置)
|
||||
- assetConfigs: 已经配置了 YT-A ✅
|
||||
`)
|
||||
134
frontend/scripts/simulate-deposit.js
Normal file
134
frontend/scripts/simulate-deposit.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import { createPublicClient, http, encodeFunctionData, formatUnits } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http()
|
||||
})
|
||||
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D'
|
||||
const YT_A = '0x97204190B35D9895a7a47aa7BaC61ac08De3cF05'
|
||||
const USER = '0xa013422a5918cd099c63c8cc35283eaca99a705d'
|
||||
const CONFIGURATOR = '0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc'
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: '_collateralAsset', type: 'address' },
|
||||
{ internalType: 'uint256', name: '_amount', type: 'uint256' }
|
||||
],
|
||||
name: 'deposit',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
const CONFIGURATOR_ABI = [
|
||||
{
|
||||
inputs: [{ internalType: 'address', name: '_asset', type: 'address' }],
|
||||
name: 'getCollateralConfig',
|
||||
outputs: [
|
||||
{ internalType: 'bool', name: 'isActive', type: 'bool' },
|
||||
{ internalType: 'uint256', name: 'collateralFactor', type: 'uint256' },
|
||||
{ internalType: 'uint256', name: 'liquidationThreshold', type: 'uint256' },
|
||||
{ internalType: 'uint256', name: 'liquidationBonus', type: 'uint256' }
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [{ internalType: 'address', name: '', type: 'address' }],
|
||||
name: 'collateralConfigs',
|
||||
outputs: [
|
||||
{ internalType: 'bool', name: 'isActive', type: 'bool' },
|
||||
{ internalType: 'uint256', name: 'collateralFactor', type: 'uint256' },
|
||||
{ internalType: 'uint256', name: 'liquidationThreshold', type: 'uint256' },
|
||||
{ internalType: 'uint256', name: 'liquidationBonus', type: 'uint256' }
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function simulateDeposit() {
|
||||
console.log('🔍 模拟存款操作...\n')
|
||||
console.log('合约地址:', LENDING_PROXY)
|
||||
console.log('抵押品地址:', YT_A)
|
||||
console.log('用户地址:', USER)
|
||||
console.log('存款金额: 10 YT-A\n')
|
||||
|
||||
// 1. 检查 Configurator 中的配置
|
||||
console.log('=== 检查 Configurator 配置 ===')
|
||||
try {
|
||||
const config = await client.readContract({
|
||||
address: CONFIGURATOR,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'getCollateralConfig',
|
||||
args: [YT_A]
|
||||
})
|
||||
console.log('✅ 通过 getCollateralConfig 读取:')
|
||||
console.log(' - 是否激活:', config[0])
|
||||
console.log(' - 抵押率:', Number(config[1]) / 100 + '%')
|
||||
console.log(' - 清算阈值:', Number(config[2]) / 100 + '%')
|
||||
console.log(' - 清算奖励:', Number(config[3]) / 100 + '%')
|
||||
} catch (error) {
|
||||
console.log('❌ getCollateralConfig 失败:', error.message.split('\n')[0])
|
||||
|
||||
// 尝试直接读取mapping
|
||||
try {
|
||||
const config = await client.readContract({
|
||||
address: CONFIGURATOR,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'collateralConfigs',
|
||||
args: [YT_A]
|
||||
})
|
||||
console.log('✅ 通过 collateralConfigs mapping 读取:')
|
||||
console.log(' - 是否激活:', config[0])
|
||||
console.log(' - 抵押率:', Number(config[1]) / 100 + '%')
|
||||
console.log(' - 清算阈值:', Number(config[2]) / 100 + '%')
|
||||
console.log(' - 清算奖励:', Number(config[3]) / 100 + '%')
|
||||
|
||||
if (!config[0]) {
|
||||
console.log('\n⚠️ 警告: 抵押品未激活!这就是存款失败的原因。')
|
||||
}
|
||||
} catch (error2) {
|
||||
console.log('❌ collateralConfigs mapping 也失败:', error2.message.split('\n')[0])
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 模拟调用 deposit
|
||||
console.log('\n=== 模拟存款调用 ===')
|
||||
const depositAmount = 10n * 10n ** 18n // 10 YT-A
|
||||
|
||||
try {
|
||||
await client.simulateContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'deposit',
|
||||
args: [YT_A, depositAmount],
|
||||
account: USER
|
||||
})
|
||||
console.log('✅ 模拟存款成功!交易应该可以执行。')
|
||||
} catch (error) {
|
||||
console.log('❌ 模拟存款失败')
|
||||
console.log('\n详细错误信息:')
|
||||
console.log(error.message)
|
||||
|
||||
// 尝试解析错误原因
|
||||
if (error.message.includes('Collateral not active')) {
|
||||
console.log('\n💡 原因: 抵押品未激活')
|
||||
console.log(' 需要管理员通过 Configurator 激活此抵押品')
|
||||
} else if (error.message.includes('insufficient')) {
|
||||
console.log('\n💡 原因: 余额或授权不足')
|
||||
} else if (error.message.includes('paused')) {
|
||||
console.log('\n💡 原因: 合约已暂停')
|
||||
} else {
|
||||
console.log('\n💡 这可能是由于抵押品未在借贷合约中配置')
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n=== 诊断完成 ===\n')
|
||||
}
|
||||
|
||||
simulateDeposit().catch(console.error)
|
||||
117
frontend/scripts/simulate-supply.js
Normal file
117
frontend/scripts/simulate-supply.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import { createPublicClient, http, parseEther } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D'
|
||||
const YT_A = '0x97204190B35D9895a7a47aa7BaC61ac08De3cF05'
|
||||
const USDC = '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d'
|
||||
const USER = '0xa013422A5918CD099C63c8CC35283EACa99a705d'
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: 'asset', type: 'address' },
|
||||
{ internalType: 'uint256', name: 'amount', type: 'uint256' }
|
||||
],
|
||||
name: 'supplyCollateral',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: 'asset', type: 'address' }
|
||||
],
|
||||
name: 'getAssetInfo',
|
||||
outputs: [
|
||||
{ internalType: 'uint8', name: 'offset', type: 'uint8' },
|
||||
{ internalType: 'address', name: 'priceFeed', type: 'address' },
|
||||
{ internalType: 'uint64', name: 'scale', type: 'uint64' },
|
||||
{ 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: [],
|
||||
name: 'baseToken',
|
||||
outputs: [{ internalType: 'address', name: '', type: 'address' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n模拟 supplyCollateral 调用...\n')
|
||||
|
||||
try {
|
||||
// 先检查 Lending 合约能否读取到配置
|
||||
console.log('=== 检查 Lending 合约状态 ===')
|
||||
|
||||
const baseToken = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'baseToken'
|
||||
})
|
||||
|
||||
console.log('Lending.baseToken:', baseToken)
|
||||
console.log('是否为 USDC:', baseToken.toLowerCase() === USDC.toLowerCase() ? '✓' : '✗')
|
||||
|
||||
// 检查资产信息
|
||||
try {
|
||||
const assetInfo = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getAssetInfo',
|
||||
args: [YT_A]
|
||||
})
|
||||
|
||||
console.log('\nLending.getAssetInfo(YT-A):')
|
||||
console.log(' borrowCollateralFactor:', assetInfo[3].toString())
|
||||
console.log(' liquidateCollateralFactor:', assetInfo[4].toString())
|
||||
console.log(' supplyCap:', assetInfo[6].toString())
|
||||
} catch (e) {
|
||||
console.log('\n✗ getAssetInfo 失败:', e.shortMessage || e.message)
|
||||
}
|
||||
|
||||
// 尝试模拟调用
|
||||
console.log('\n=== 模拟 supplyCollateral 调用 ===')
|
||||
const amount = parseEther('10')
|
||||
|
||||
try {
|
||||
await client.simulateContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'supplyCollateral',
|
||||
args: [YT_A, amount],
|
||||
account: USER
|
||||
})
|
||||
console.log('✓ 模拟成功!理论上应该可以执行')
|
||||
} catch (error) {
|
||||
console.log('✗ 模拟失败:')
|
||||
console.log(' 错误类型:', error.name)
|
||||
console.log(' 错误信息:', error.shortMessage || error.message)
|
||||
|
||||
if (error.cause) {
|
||||
console.log('\n详细错误:')
|
||||
console.log(' ', error.cause.message || error.cause)
|
||||
}
|
||||
|
||||
if (error.data) {
|
||||
console.log('\n错误数据:', error.data)
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n意外错误:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
118
frontend/scripts/single-buy-test.ts
Normal file
118
frontend/scripts/single-buy-test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* 单账户买入测试脚本
|
||||
* 使用主账户直接执行买入,不需要分发 ETH
|
||||
*/
|
||||
|
||||
import {
|
||||
createPublicClient,
|
||||
createWalletClient,
|
||||
http,
|
||||
parseUnits,
|
||||
formatUnits,
|
||||
type Address,
|
||||
type Hex
|
||||
} from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
import { privateKeyToAccount } from 'viem/accounts'
|
||||
|
||||
const CONTRACTS = {
|
||||
WUSD: '0x6d2bf81a631dFE19B2f348aE92cF6Ef41ca2DF98' as Address,
|
||||
VAULT_YT_A: '0x0cA35994F033685E7a57ef9bc5d00dd3cf927330' as Address,
|
||||
}
|
||||
|
||||
const MAIN_PRIVATE_KEY = '0xa082a7037105ebd606bee80906687e400d89899bbb6ba0273a61528c2f5fab89' as Hex
|
||||
|
||||
const WUSD_ABI = [
|
||||
{ inputs: [{ name: 'spender', type: 'address' }, { name: 'amount', type: 'uint256' }], name: 'approve', outputs: [{ type: 'bool' }], stateMutability: 'nonpayable', type: 'function' },
|
||||
{ inputs: [{ name: 'account', type: 'address' }], name: 'balanceOf', outputs: [{ type: 'uint256' }], stateMutability: 'view', type: 'function' },
|
||||
] as const
|
||||
|
||||
const VAULT_ABI = [
|
||||
{ inputs: [{ name: '_wusdAmount', type: 'uint256' }], name: 'depositYT', outputs: [], stateMutability: 'nonpayable', type: 'function' },
|
||||
{ inputs: [{ name: '_wusdAmount', type: 'uint256' }], name: 'previewBuy', outputs: [{ type: 'uint256' }], stateMutability: 'view', type: 'function' },
|
||||
{ inputs: [{ name: 'account', type: 'address' }], name: 'balanceOf', outputs: [{ type: 'uint256' }], stateMutability: 'view', type: 'function' },
|
||||
{ inputs: [], name: 'symbol', outputs: [{ type: 'string' }], stateMutability: 'view', type: 'function' },
|
||||
] as const
|
||||
|
||||
async function main() {
|
||||
console.log('🚀 单账户买入测试\n')
|
||||
|
||||
const publicClient = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc'),
|
||||
})
|
||||
|
||||
const account = privateKeyToAccount(MAIN_PRIVATE_KEY)
|
||||
const walletClient = createWalletClient({
|
||||
account,
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://sepolia-rollup.arbitrum.io/rpc'),
|
||||
})
|
||||
|
||||
console.log(`📍 账户: ${account.address}`)
|
||||
|
||||
// 检查余额
|
||||
const ethBalance = await publicClient.getBalance({ address: account.address })
|
||||
const wusdBalance = await publicClient.readContract({
|
||||
address: CONTRACTS.WUSD,
|
||||
abi: WUSD_ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [account.address],
|
||||
})
|
||||
|
||||
console.log(`⛽ ETH 余额: ${formatUnits(ethBalance, 18)} ETH`)
|
||||
console.log(`💰 WUSD 余额: ${formatUnits(wusdBalance, 18)} WUSD`)
|
||||
|
||||
// 获取金库 symbol
|
||||
const vaultSymbol = await publicClient.readContract({
|
||||
address: CONTRACTS.VAULT_YT_A,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'symbol',
|
||||
})
|
||||
console.log(`🏦 目标金库: ${vaultSymbol}`)
|
||||
|
||||
// 买入金额
|
||||
const buyAmount = parseUnits('50', 18) // 50 WUSD
|
||||
console.log(`\n📝 买入金额: ${formatUnits(buyAmount, 18)} WUSD`)
|
||||
|
||||
// 预览
|
||||
const previewYT = await publicClient.readContract({
|
||||
address: CONTRACTS.VAULT_YT_A,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'previewBuy',
|
||||
args: [buyAmount],
|
||||
})
|
||||
console.log(`📊 预计获得: ${formatUnits(previewYT, 18)} ${vaultSymbol}`)
|
||||
|
||||
// 执行买入
|
||||
console.log('\n1️⃣ 授权 WUSD...')
|
||||
const approveHash = await walletClient.writeContract({
|
||||
address: CONTRACTS.WUSD,
|
||||
abi: WUSD_ABI,
|
||||
functionName: 'approve',
|
||||
args: [CONTRACTS.VAULT_YT_A, buyAmount],
|
||||
})
|
||||
await publicClient.waitForTransactionReceipt({ hash: approveHash })
|
||||
console.log(` ✓ 授权成功: ${approveHash}`)
|
||||
|
||||
console.log('\n2️⃣ 执行买入...')
|
||||
const buyHash = await walletClient.writeContract({
|
||||
address: CONTRACTS.VAULT_YT_A,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'depositYT',
|
||||
args: [buyAmount],
|
||||
})
|
||||
await publicClient.waitForTransactionReceipt({ hash: buyHash })
|
||||
console.log(` ✓ 买入成功: ${buyHash}`)
|
||||
|
||||
// 检查结果
|
||||
const ytBalance = await publicClient.readContract({
|
||||
address: CONTRACTS.VAULT_YT_A,
|
||||
abi: VAULT_ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [account.address],
|
||||
})
|
||||
console.log(`\n✅ ${vaultSymbol} 余额: ${formatUnits(ytBalance, 18)}`)
|
||||
}
|
||||
|
||||
main().catch(console.error)
|
||||
182
frontend/scripts/test-calculations.js
Normal file
182
frontend/scripts/test-calculations.js
Normal file
@@ -0,0 +1,182 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const LENDING_PRICE_FEED = getAddress('0xE82c7cB9CfA42D6eb7e443956b78f8290249c316')
|
||||
const CONFIGURATOR = getAddress('0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
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: 'account', type: 'address' }],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
const PRICE_FEED_ABI = [
|
||||
{
|
||||
inputs: [{ internalType: 'address', name: 'asset', type: 'address' }],
|
||||
name: 'getPrice',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
const CONFIGURATOR_ABI = [
|
||||
{
|
||||
inputs: [{ internalType: 'address', name: 'lendingProxy', type: 'address' }],
|
||||
name: 'getConfiguration',
|
||||
outputs: [
|
||||
{
|
||||
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: 'uint64', name: 'trackingIndexScale', 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: '',
|
||||
type: 'tuple'
|
||||
}
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n=== 测试抵押价值计算逻辑 ===\n')
|
||||
console.log('用户:', USER)
|
||||
|
||||
// 1. 获取抵押品余额
|
||||
console.log('\n--- 1. 获取抵押品余额 ---')
|
||||
const collateral = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getCollateral',
|
||||
args: [USER, YT_A]
|
||||
})
|
||||
console.log('YT-A 抵押品:', Number(collateral) / 1e18, 'YT-A')
|
||||
console.log('原始值:', collateral.toString())
|
||||
|
||||
// 2. 获取价格
|
||||
console.log('\n--- 2. 获取 YT-A 价格 ---')
|
||||
const price = await client.readContract({
|
||||
address: LENDING_PRICE_FEED,
|
||||
abi: PRICE_FEED_ABI,
|
||||
functionName: 'getPrice',
|
||||
args: [YT_A]
|
||||
})
|
||||
console.log('YT-A 价格(Compound V3 格式):', price.toString())
|
||||
console.log(' = 1e30 规模')
|
||||
|
||||
// 3. 获取配置
|
||||
console.log('\n--- 3. 获取配置 ---')
|
||||
const config = await client.readContract({
|
||||
address: CONFIGURATOR,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'getConfiguration',
|
||||
args: [LENDING_PROXY]
|
||||
})
|
||||
|
||||
const ytAConfig = config.assetConfigs.find(
|
||||
cfg => cfg.asset.toLowerCase() === YT_A.toLowerCase()
|
||||
)
|
||||
|
||||
if (ytAConfig) {
|
||||
console.log('YT-A 配置:')
|
||||
console.log(' 借款抵押率 (borrowCollateralFactor):', ytAConfig.borrowCollateralFactor.toString())
|
||||
console.log(' = ', Number(ytAConfig.borrowCollateralFactor) / 1e18 * 100, '%')
|
||||
console.log(' 清算阈值 (liquidateCollateralFactor):', ytAConfig.liquidateCollateralFactor.toString())
|
||||
console.log(' = ', Number(ytAConfig.liquidateCollateralFactor) / 1e18 * 100, '%')
|
||||
}
|
||||
|
||||
// 4. 获取借款余额
|
||||
console.log('\n--- 4. 获取借款余额 ---')
|
||||
const borrowBalance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'borrowBalanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
console.log('借款余额:', Number(borrowBalance) / 1e6, 'USDC')
|
||||
|
||||
// 5. 计算总抵押价值
|
||||
console.log('\n--- 5. 计算总抵押价值 ---')
|
||||
// 价值(USDC 6位精度)= (balance * price) / 1e42
|
||||
const valueInUSD = (collateral * price) / BigInt(10 ** 42)
|
||||
console.log('总抵押价值:', Number(valueInUSD) / 1e6, 'USDC')
|
||||
console.log('原始值 (6位精度):', valueInUSD.toString())
|
||||
|
||||
// 6. 计算可借额度
|
||||
console.log('\n--- 6. 计算可借额度 ---')
|
||||
if (ytAConfig) {
|
||||
const borrowCollateralFactor = BigInt(ytAConfig.borrowCollateralFactor)
|
||||
const maxBorrow = (valueInUSD * borrowCollateralFactor) / BigInt(10 ** 18)
|
||||
const availableToBorrow = maxBorrow > borrowBalance ? maxBorrow - borrowBalance : 0n
|
||||
console.log('最大可借:', Number(maxBorrow) / 1e6, 'USDC')
|
||||
console.log('已借:', Number(borrowBalance) / 1e6, 'USDC')
|
||||
console.log('剩余可借:', Number(availableToBorrow) / 1e6, 'USDC')
|
||||
}
|
||||
|
||||
// 7. 计算健康因子
|
||||
console.log('\n--- 7. 计算健康因子 ---')
|
||||
if (borrowBalance > 0n && ytAConfig) {
|
||||
const liquidationFactor = BigInt(ytAConfig.liquidateCollateralFactor)
|
||||
const liquidationThreshold = (valueInUSD * liquidationFactor) / BigInt(10 ** 18)
|
||||
// healthFactor 以 10000 为基数
|
||||
const healthFactor = (liquidationThreshold * BigInt(10000)) / borrowBalance
|
||||
console.log('清算阈值价值:', Number(liquidationThreshold) / 1e6, 'USDC')
|
||||
console.log('健康因子 (10000=100%):', Number(healthFactor))
|
||||
console.log(' = ', Number(healthFactor) / 100, '%')
|
||||
} else if (borrowBalance === 0n) {
|
||||
console.log('无借款,健康因子: ∞(无限大,非常安全)')
|
||||
}
|
||||
|
||||
console.log('\n=== 计算完成 ===\n')
|
||||
}
|
||||
|
||||
main()
|
||||
111
frontend/scripts/test-collateral-functions.js
Normal file
111
frontend/scripts/test-collateral-functions.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
|
||||
async function testFunction(name, abi, args) {
|
||||
try {
|
||||
const result = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [abi],
|
||||
functionName: name,
|
||||
args: args
|
||||
})
|
||||
console.log(`✓ ${name} 成功:`, result)
|
||||
return { success: true, result }
|
||||
} catch (error) {
|
||||
console.log(`✗ ${name} 失败:`, error.message.split('\n')[0])
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('\n测试不同的抵押品查询函数...\n')
|
||||
console.log('合约:', LENDING_PROXY)
|
||||
console.log('用户:', USER)
|
||||
console.log('抵押品:', YT_A, '\n')
|
||||
|
||||
// 测试 1: userCollateral (Compound V3 标准)
|
||||
console.log('--- 测试 1: userCollateral(address, address) ---')
|
||||
await testFunction('userCollateral', {
|
||||
inputs: [
|
||||
{ name: 'account', type: 'address' },
|
||||
{ name: 'asset', type: 'address' }
|
||||
],
|
||||
name: 'userCollateral',
|
||||
outputs: [
|
||||
{ name: 'balance', type: 'uint128' },
|
||||
{ name: '_reserved', type: 'uint128' }
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [USER, YT_A])
|
||||
|
||||
// 测试 2: getUserCollateral
|
||||
console.log('\n--- 测试 2: getUserCollateral(address, address) ---')
|
||||
await testFunction('getUserCollateral', {
|
||||
inputs: [
|
||||
{ name: '_user', type: 'address' },
|
||||
{ name: '_collateralAsset', type: 'address' }
|
||||
],
|
||||
name: 'getUserCollateral',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [USER, YT_A])
|
||||
|
||||
// 测试 3: collateralBalanceOf
|
||||
console.log('\n--- 测试 3: collateralBalanceOf(address, address) ---')
|
||||
await testFunction('collateralBalanceOf', {
|
||||
inputs: [
|
||||
{ name: 'account', type: 'address' },
|
||||
{ name: 'asset', type: 'address' }
|
||||
],
|
||||
name: 'collateralBalanceOf',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [USER, YT_A])
|
||||
|
||||
// 测试 4: getUserAccountData
|
||||
console.log('\n--- 测试 4: getUserAccountData(address) ---')
|
||||
const result = await testFunction('getUserAccountData', {
|
||||
inputs: [{ name: '_user', type: 'address' }],
|
||||
name: 'getUserAccountData',
|
||||
outputs: [
|
||||
{ name: 'totalCollateralValue', type: 'uint256' },
|
||||
{ name: 'totalBorrowValue', type: 'uint256' },
|
||||
{ name: 'availableToBorrow', type: 'uint256' },
|
||||
{ name: 'healthFactor', type: 'uint256' }
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [USER])
|
||||
|
||||
if (result.success) {
|
||||
console.log('\n详细结果:')
|
||||
console.log(' totalCollateralValue:', result.result[0].toString())
|
||||
console.log(' totalBorrowValue:', result.result[1].toString())
|
||||
console.log(' availableToBorrow:', result.result[2].toString())
|
||||
console.log(' healthFactor:', result.result[3].toString())
|
||||
}
|
||||
|
||||
// 测试 5: borrowBalanceOf (检查借款)
|
||||
console.log('\n--- 测试 5: borrowBalanceOf(address) ---')
|
||||
await testFunction('borrowBalanceOf', {
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [USER])
|
||||
}
|
||||
|
||||
main()
|
||||
94
frontend/scripts/test-config-read.js
Normal file
94
frontend/scripts/test-config-read.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import { createPublicClient, http } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const CONFIGURATOR = '0x488409CE9A3Fcd8EbD373dCb7e025cF8AB96fcdc'
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D'
|
||||
const YT_A = '0x97204190B35D9895a7a47aa7BaC61ac08De3cF05'
|
||||
|
||||
const CONFIGURATOR_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: 'lendingProxy', type: 'address' }
|
||||
],
|
||||
name: 'getConfiguration',
|
||||
outputs: [
|
||||
{
|
||||
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: 'uint64', name: 'trackingIndexScale', 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: '',
|
||||
type: 'tuple'
|
||||
}
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('🔍 测试从 Configurator 读取配置...\n')
|
||||
|
||||
try {
|
||||
const config = await client.readContract({
|
||||
address: CONFIGURATOR,
|
||||
abi: CONFIGURATOR_ABI,
|
||||
functionName: 'getConfiguration',
|
||||
args: [LENDING_PROXY]
|
||||
})
|
||||
|
||||
console.log('✅ 成功读取配置!\n')
|
||||
console.log('基础代币 (USDC):', config.baseToken)
|
||||
console.log('价格源:', config.lendingPriceSource)
|
||||
console.log('\n抵押品配置数量:', config.assetConfigs.length)
|
||||
|
||||
config.assetConfigs.forEach((asset, index) => {
|
||||
console.log(`\n${index + 1}. 抵押品:`, asset.asset)
|
||||
console.log(' 精度:', asset.decimals)
|
||||
console.log(' 借款抵押率:', Number(asset.borrowCollateralFactor) / 1e16, '%')
|
||||
console.log(' 清算抵押率:', Number(asset.liquidateCollateralFactor) / 1e16, '%')
|
||||
console.log(' 清算奖励:', Number(asset.liquidationFactor) / 1e16, '%')
|
||||
console.log(' 供应上限:', Number(asset.supplyCap) / 1e18)
|
||||
|
||||
if (asset.asset.toLowerCase() === YT_A.toLowerCase()) {
|
||||
console.log(' ✅ 这是 YT-A')
|
||||
}
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 读取配置失败:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
134
frontend/scripts/test-correct-function-names.js
Normal file
134
frontend/scripts/test-correct-function-names.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
const YT_A = getAddress('0x97204190B35D9895a7a47aa7BaC61ac08De3cF05')
|
||||
|
||||
async function main() {
|
||||
console.log('\n测试合约文档中的正确函数名\n')
|
||||
console.log('合约:', LENDING_PROXY)
|
||||
console.log('用户:', USER)
|
||||
console.log('抵押品:', YT_A)
|
||||
console.log()
|
||||
|
||||
// 1. 测试 getCollateral(正确的函数名)
|
||||
console.log('=== 1. getCollateral(account, asset) ===')
|
||||
try {
|
||||
const collateral = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
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'
|
||||
}],
|
||||
functionName: 'getCollateral',
|
||||
args: [USER, YT_A]
|
||||
})
|
||||
console.log('✓ 成功!')
|
||||
console.log(' 抵押品余额:', Number(collateral) / 1e18, 'YT-A')
|
||||
console.log(' 原始值:', collateral.toString())
|
||||
} catch (error) {
|
||||
console.log('✗ 失败:', error.message.split('\n')[0])
|
||||
}
|
||||
|
||||
// 2. 测试 getBalance(用户USDC余额)
|
||||
console.log('\n=== 2. getBalance(account) ===')
|
||||
try {
|
||||
const balance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
|
||||
name: 'getBalance',
|
||||
outputs: [{ internalType: 'int256', name: '', type: 'int256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'getBalance',
|
||||
args: [USER]
|
||||
})
|
||||
console.log('✓ 成功!')
|
||||
console.log(' USDC 余额:', Number(balance) / 1e6, 'USDC')
|
||||
console.log(' 原始值:', balance.toString())
|
||||
} catch (error) {
|
||||
console.log('✗ 失败:', error.message.split('\n')[0])
|
||||
}
|
||||
|
||||
// 3. 测试 borrowBalanceOf
|
||||
console.log('\n=== 3. borrowBalanceOf(account) ===')
|
||||
try {
|
||||
const borrowBalance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'borrowBalanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
console.log('✓ 成功!')
|
||||
console.log(' 借款余额:', Number(borrowBalance) / 1e6, 'USDC')
|
||||
} catch (error) {
|
||||
console.log('✗ 失败:', error.message.split('\n')[0])
|
||||
}
|
||||
|
||||
// 4. 测试系统函数
|
||||
console.log('\n=== 4. getTotalBorrow() ===')
|
||||
try {
|
||||
const totalBorrow = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
inputs: [],
|
||||
name: 'getTotalBorrow',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'getTotalBorrow'
|
||||
})
|
||||
console.log('✓ 成功!')
|
||||
console.log(' 系统总借款:', Number(totalBorrow) / 1e6, 'USDC')
|
||||
} catch (error) {
|
||||
console.log('✗ 失败:', error.message.split('\n')[0])
|
||||
}
|
||||
|
||||
console.log('\n=== 5. getTotalSupply() ===')
|
||||
try {
|
||||
const totalSupply = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
inputs: [],
|
||||
name: 'getTotalSupply',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'getTotalSupply'
|
||||
})
|
||||
console.log('✓ 成功!')
|
||||
console.log(' 系统总供应:', Number(totalSupply) / 1e6, 'USDC')
|
||||
} catch (error) {
|
||||
console.log('✗ 失败:', error.message.split('\n')[0])
|
||||
}
|
||||
|
||||
console.log('\n=== 结论 ===')
|
||||
console.log('如果上面的函数都能工作,说明:')
|
||||
console.log('1. 合约使用的是文档中的函数名(getCollateral 等)')
|
||||
console.log('2. 前端 ABI 需要更新为文档中的正确函数名')
|
||||
console.log('3. getUserCollateral 和 getUserAccountData 不存在于合约中\n')
|
||||
}
|
||||
|
||||
main()
|
||||
67
frontend/scripts/test-supply-with-trace.js
Normal file
67
frontend/scripts/test-supply-with-trace.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { createPublicClient, createWalletClient, http, parseEther } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
import { privateKeyToAccount } from 'viem/accounts'
|
||||
|
||||
const publicClient = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D'
|
||||
const YT_A = '0x97204190B35D9895a7a47aa7BaC61ac08De3cF05'
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [
|
||||
{ internalType: 'address', name: 'asset', type: 'address' },
|
||||
{ internalType: 'uint256', name: 'amount', type: 'uint256' }
|
||||
],
|
||||
name: 'supplyCollateral',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n尝试捕获详细错误...\n')
|
||||
|
||||
try {
|
||||
// 尝试估算 gas
|
||||
const gasEstimate = await publicClient.estimateContractGas({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'supplyCollateral',
|
||||
args: [YT_A, parseEther('10')],
|
||||
account: '0xa013422A5918CD099C63c8CC35283EACa99a705d'
|
||||
})
|
||||
|
||||
console.log('✓ Gas 估算成功:', gasEstimate.toString())
|
||||
|
||||
} catch (error) {
|
||||
console.log('✗ Gas 估算失败\n')
|
||||
console.log('错误类型:', error.name)
|
||||
console.log('错误消息:', error.shortMessage || error.message)
|
||||
|
||||
if (error.cause) {
|
||||
console.log('\n详细原因:')
|
||||
console.log(' Reason:', error.cause.reason)
|
||||
console.log(' Details:', error.cause.details)
|
||||
|
||||
// 尝试解析 revert 原因
|
||||
if (error.cause.data) {
|
||||
console.log(' Raw Data:', error.cause.data)
|
||||
}
|
||||
}
|
||||
|
||||
if (error.details) {
|
||||
console.log('\n更多细节:', error.details)
|
||||
}
|
||||
|
||||
// 打印完整错误用于调试
|
||||
console.log('\n=== 完整错误对象 ===')
|
||||
console.log(JSON.stringify(error, null, 2))
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
104
frontend/scripts/test-total-functions.js
Normal file
104
frontend/scripts/test-total-functions.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import { createPublicClient, http, formatUnits } from 'viem';
|
||||
import { arbitrumSepolia } from 'viem/chains';
|
||||
|
||||
const RPC_URL = 'https://sepolia-rollup.arbitrum.io/rpc';
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D';
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http(RPC_URL)
|
||||
});
|
||||
|
||||
async function testBothFunctions() {
|
||||
console.log('\n=== 测试 getTotalSupply vs getTotalLiquidity ===\n');
|
||||
|
||||
// 测试 getTotalSupply
|
||||
console.log('1️⃣ 测试 getTotalSupply():');
|
||||
try {
|
||||
const totalSupply = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
inputs: [],
|
||||
name: 'getTotalSupply',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'getTotalSupply'
|
||||
});
|
||||
console.log(' ✅ 调用成功!');
|
||||
console.log(' 值:', totalSupply.toString());
|
||||
console.log(' 格式化:', formatUnits(totalSupply, 6), 'USDC');
|
||||
} catch (error) {
|
||||
console.log(' ❌ 调用失败!');
|
||||
console.log(' 错误:', error.message.split('\n')[0]);
|
||||
}
|
||||
|
||||
console.log('\n2️⃣ 测试 getTotalLiquidity():');
|
||||
try {
|
||||
const totalLiquidity = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
inputs: [],
|
||||
name: 'getTotalLiquidity',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'getTotalLiquidity'
|
||||
});
|
||||
console.log(' ✅ 调用成功!');
|
||||
console.log(' 值:', totalLiquidity.toString());
|
||||
console.log(' 格式化:', formatUnits(totalLiquidity, 6), 'USDC');
|
||||
} catch (error) {
|
||||
console.log(' ❌ 调用失败!');
|
||||
console.log(' 错误:', error.message.split('\n')[0]);
|
||||
}
|
||||
|
||||
// 测试其他可能的函数名
|
||||
console.log('\n3️⃣ 测试 totalSupply() (Compound V3 标准):');
|
||||
try {
|
||||
const totalSupply = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
inputs: [],
|
||||
name: 'totalSupply',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'totalSupply'
|
||||
});
|
||||
console.log(' ✅ 调用成功!');
|
||||
console.log(' 值:', totalSupply.toString());
|
||||
console.log(' 格式化:', formatUnits(totalSupply, 6), 'USDC');
|
||||
} catch (error) {
|
||||
console.log(' ❌ 调用失败!');
|
||||
console.log(' 错误:', error.message.split('\n')[0]);
|
||||
}
|
||||
|
||||
console.log('\n4️⃣ 测试 totalBorrow():');
|
||||
try {
|
||||
const totalBorrow = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [{
|
||||
inputs: [],
|
||||
name: 'totalBorrow',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}],
|
||||
functionName: 'totalBorrow'
|
||||
});
|
||||
console.log(' ✅ 调用成功!');
|
||||
console.log(' 值:', totalBorrow.toString());
|
||||
console.log(' 格式化:', formatUnits(totalBorrow, 6), 'USDC');
|
||||
} catch (error) {
|
||||
console.log(' ❌ 调用失败!');
|
||||
console.log(' 错误:', error.message.split('\n')[0]);
|
||||
}
|
||||
|
||||
console.log('\n');
|
||||
}
|
||||
|
||||
testBothFunctions().catch(console.error);
|
||||
169
frontend/scripts/verify-all-lending-functions.js
Normal file
169
frontend/scripts/verify-all-lending-functions.js
Normal file
@@ -0,0 +1,169 @@
|
||||
import { createPublicClient, http, formatUnits } from 'viem';
|
||||
import { arbitrumSepolia } from 'viem/chains';
|
||||
|
||||
const RPC_URL = 'https://sepolia-rollup.arbitrum.io/rpc';
|
||||
const LENDING_PROXY = '0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D';
|
||||
const TEST_USER = '0xa013422A5918CD099C63c8CC35283EACa99a705d'; // 有抵押品的用户
|
||||
const YT_A = '0x97204190B35D9895a7a47aa7BaC61ac08De3cF05';
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http(RPC_URL)
|
||||
});
|
||||
|
||||
const testResults = {
|
||||
success: [],
|
||||
failed: []
|
||||
};
|
||||
|
||||
async function testFunction(name, abi, params = []) {
|
||||
try {
|
||||
const result = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: [abi],
|
||||
functionName: name,
|
||||
args: params
|
||||
});
|
||||
testResults.success.push(name);
|
||||
return { success: true, value: result };
|
||||
} catch (error) {
|
||||
testResults.failed.push(name);
|
||||
return { success: false, error: error.message.split('\n')[0] };
|
||||
}
|
||||
}
|
||||
|
||||
async function runAllTests() {
|
||||
console.log('\n=== 验证所有前端 LENDING ABI 函数 ===\n');
|
||||
|
||||
// 1. getCollateral - 用户查询
|
||||
console.log('1️⃣ getCollateral(account, asset):');
|
||||
const r1 = await testFunction('getCollateral', {
|
||||
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'
|
||||
}, [TEST_USER, YT_A]);
|
||||
console.log(r1.success ? ` ✅ 成功 - 抵押量: ${formatUnits(r1.value, 18)}` : ` ❌ 失败 - ${r1.error}`);
|
||||
|
||||
// 2. getBalance - 用户余额
|
||||
console.log('\n2️⃣ getBalance(account):');
|
||||
const r2 = await testFunction('getBalance', {
|
||||
inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
|
||||
name: 'getBalance',
|
||||
outputs: [{ internalType: 'int256', name: '', type: 'int256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [TEST_USER]);
|
||||
console.log(r2.success ? ` ✅ 成功 - 余额: ${r2.value.toString()}` : ` ❌ 失败 - ${r2.error}`);
|
||||
|
||||
// 3. borrowBalanceOf - 借款余额
|
||||
console.log('\n3️⃣ borrowBalanceOf(account):');
|
||||
const r3 = await testFunction('borrowBalanceOf', {
|
||||
inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}, [TEST_USER]);
|
||||
console.log(r3.success ? ` ✅ 成功 - 借款: ${formatUnits(r3.value, 6)} USDC` : ` ❌ 失败 - ${r3.error}`);
|
||||
|
||||
// 4. getTotalBorrow - 系统总借款
|
||||
console.log('\n4️⃣ getTotalBorrow():');
|
||||
const r4 = await testFunction('getTotalBorrow', {
|
||||
inputs: [],
|
||||
name: 'getTotalBorrow',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
});
|
||||
console.log(r4.success ? ` ✅ 成功 - 总借款: ${formatUnits(r4.value, 6)} USDC` : ` ❌ 失败 - ${r4.error}`);
|
||||
|
||||
// 5. getTotalSupply - 系统总供应
|
||||
console.log('\n5️⃣ getTotalSupply():');
|
||||
const r5 = await testFunction('getTotalSupply', {
|
||||
inputs: [],
|
||||
name: 'getTotalSupply',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
});
|
||||
console.log(r5.success ? ` ✅ 成功 - 总供应: ${formatUnits(r5.value, 6)} USDC` : ` ❌ 失败 - ${r5.error}`);
|
||||
|
||||
// 6. getUtilization - 资金利用率
|
||||
console.log('\n6️⃣ getUtilization():');
|
||||
const r6 = await testFunction('getUtilization', {
|
||||
inputs: [],
|
||||
name: 'getUtilization',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
});
|
||||
console.log(r6.success ? ` ✅ 成功 - 利用率: ${Number(r6.value) / 1e16}%` : ` ❌ 失败 - ${r6.error}`);
|
||||
|
||||
// 7. getBorrowRate - 借款利率
|
||||
console.log('\n7️⃣ getBorrowRate():');
|
||||
const r7 = await testFunction('getBorrowRate', {
|
||||
inputs: [],
|
||||
name: 'getBorrowRate',
|
||||
outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
});
|
||||
console.log(r7.success ? ` ✅ 成功 - 借款利率: ${Number(r7.value) / 1e16}%` : ` ❌ 失败 - ${r7.error}`);
|
||||
|
||||
// 8. getSupplyRate - 存款利率
|
||||
console.log('\n8️⃣ getSupplyRate():');
|
||||
const r8 = await testFunction('getSupplyRate', {
|
||||
inputs: [],
|
||||
name: 'getSupplyRate',
|
||||
outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
});
|
||||
console.log(r8.success ? ` ✅ 成功 - 存款利率: ${Number(r8.value) / 1e16}%` : ` ❌ 失败 - ${r8.error}`);
|
||||
|
||||
// 9. owner - 管理员
|
||||
console.log('\n9️⃣ owner():');
|
||||
const r9 = await testFunction('owner', {
|
||||
inputs: [],
|
||||
name: 'owner',
|
||||
outputs: [{ internalType: 'address', name: '', type: 'address' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
});
|
||||
console.log(r9.success ? ` ✅ 成功 - Owner: ${r9.value}` : ` ❌ 失败 - ${r9.error}`);
|
||||
|
||||
// 10. paused - 暂停状态
|
||||
console.log('\n🔟 paused():');
|
||||
const r10 = await testFunction('paused', {
|
||||
inputs: [],
|
||||
name: 'paused',
|
||||
outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
});
|
||||
console.log(r10.success ? ` ✅ 成功 - Paused: ${r10.value}` : ` ❌ 失败 - ${r10.error}`);
|
||||
|
||||
// 汇总
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('📊 测试结果汇总:');
|
||||
console.log('='.repeat(60));
|
||||
console.log(`✅ 成功: ${testResults.success.length}/10`);
|
||||
console.log(` ${testResults.success.join(', ')}`);
|
||||
if (testResults.failed.length > 0) {
|
||||
console.log(`❌ 失败: ${testResults.failed.length}/10`);
|
||||
console.log(` ${testResults.failed.join(', ')}`);
|
||||
}
|
||||
console.log('='.repeat(60) + '\n');
|
||||
|
||||
// 对比测试脚本中的错误函数
|
||||
console.log('⚠️ check-lending-setup.js 中使用的错误函数:');
|
||||
console.log(' getTotalLiquidity() - 不存在,应改为 getTotalSupply()');
|
||||
console.log('');
|
||||
}
|
||||
|
||||
runAllTests().catch(console.error);
|
||||
53
frontend/scripts/verify-apy-calculation.js
Normal file
53
frontend/scripts/verify-apy-calculation.js
Normal file
@@ -0,0 +1,53 @@
|
||||
// 验证 APY 计算逻辑
|
||||
|
||||
const borrowRatePerSecond = 14999999976144000n
|
||||
const secondsPerYear = BigInt(365 * 24 * 60 * 60) // 31536000
|
||||
|
||||
console.log('\n=== APY 计算验证 ===\n')
|
||||
|
||||
console.log('原始数据:')
|
||||
console.log(' borrowRatePerSecond:', borrowRatePerSecond.toString())
|
||||
console.log(' secondsPerYear:', secondsPerYear.toString())
|
||||
console.log()
|
||||
|
||||
// 方法1:错误的计算(之前的方式)
|
||||
const wrongAPY = Number(borrowRatePerSecond) / 10000
|
||||
console.log('❌ 错误计算(除以10000):')
|
||||
console.log(' APY:', wrongAPY.toFixed(2), '%')
|
||||
console.log(' = 1,499,999,997,614.40 %(明显错误)')
|
||||
console.log()
|
||||
|
||||
// 方法2:正确的计算
|
||||
// APY% = (ratePerSecond / 1e18) × secondsPerYear × 100
|
||||
const borrowAPY = (borrowRatePerSecond * secondsPerYear * BigInt(100)) / BigInt(10 ** 18)
|
||||
console.log('✅ 正确计算(Compound V3 格式):')
|
||||
console.log(' 每秒利率:', Number(borrowRatePerSecond) / 1e18)
|
||||
console.log(' 年化倍数 (1 + rate)^seconds ≈ rate × seconds:')
|
||||
console.log(' ', Number(borrowRatePerSecond) / 1e18, '× 31,536,000 =', Number(borrowRatePerSecond) * 31536000 / 1e18)
|
||||
console.log(' APY:', Number(borrowAPY).toFixed(2), '%')
|
||||
console.log()
|
||||
|
||||
// 详细计算步骤
|
||||
console.log('计算步骤:')
|
||||
console.log(' 1. borrowRatePerSecond × secondsPerYear × 100')
|
||||
console.log(' =', borrowRatePerSecond.toString(), '×', secondsPerYear.toString(), '× 100')
|
||||
const step1 = borrowRatePerSecond * secondsPerYear * BigInt(100)
|
||||
console.log(' =', step1.toString())
|
||||
console.log()
|
||||
console.log(' 2. 结果 ÷ 10^18')
|
||||
console.log(' =', step1.toString(), '÷ 1000000000000000000')
|
||||
console.log(' =', borrowAPY.toString())
|
||||
console.log()
|
||||
|
||||
console.log('最终结果: ', Number(borrowAPY).toFixed(2), '%')
|
||||
console.log()
|
||||
|
||||
// 验证使用率计算
|
||||
console.log('=== 使用率计算验证 ===\n')
|
||||
console.log('如果 utilizationRate = 0(当前没有借款)')
|
||||
const utilizationRate = 0n
|
||||
const utilizationPercent = utilizationRate > 0n
|
||||
? (utilizationRate * BigInt(100)) / BigInt(10 ** 18)
|
||||
: 0n
|
||||
console.log(' 使用率:', Number(utilizationPercent).toFixed(2), '%')
|
||||
console.log()
|
||||
116
frontend/scripts/verify-current-borrow.js
Normal file
116
frontend/scripts/verify-current-borrow.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import { createPublicClient, http, getAddress } from 'viem'
|
||||
import { arbitrumSepolia } from 'viem/chains'
|
||||
|
||||
const client = createPublicClient({
|
||||
chain: arbitrumSepolia,
|
||||
transport: http('https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07')
|
||||
})
|
||||
|
||||
const LENDING_PROXY = getAddress('0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D')
|
||||
const USER = getAddress('0xa013422A5918CD099C63c8CC35283EACa99a705d')
|
||||
|
||||
const LENDING_ABI = [
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'getBalance',
|
||||
outputs: [{ name: '', type: 'int256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [{ name: 'account', type: 'address' }],
|
||||
name: 'borrowBalanceOf',
|
||||
outputs: [{ name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: 'getBorrowRate',
|
||||
outputs: [{ name: '', type: 'uint64' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('\n=== 验证当前借款状态 ===\n')
|
||||
console.log('用户:', USER)
|
||||
console.log('Lending 合约:', LENDING_PROXY)
|
||||
console.log()
|
||||
|
||||
// 获取账户余额
|
||||
const balance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getBalance',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
// 获取借款余额
|
||||
const borrowBalance = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'borrowBalanceOf',
|
||||
args: [USER]
|
||||
})
|
||||
|
||||
// 获取借款利率
|
||||
const borrowRate = await client.readContract({
|
||||
address: LENDING_PROXY,
|
||||
abi: LENDING_ABI,
|
||||
functionName: 'getBorrowRate'
|
||||
})
|
||||
|
||||
console.log('=== 账户状态 ===')
|
||||
console.log()
|
||||
console.log('getBalance() 返回值:', balance.toString())
|
||||
if (balance > 0) {
|
||||
console.log(' → 用户有存款:', Number(balance) / 1e6, 'USDC')
|
||||
} else if (balance < 0) {
|
||||
console.log(' → 用户有负余额(债务):', Number(-balance) / 1e6, 'USDC')
|
||||
} else {
|
||||
console.log(' → 用户余额为 0')
|
||||
}
|
||||
console.log()
|
||||
|
||||
console.log('borrowBalanceOf() 返回值:', borrowBalance.toString())
|
||||
console.log(' → 借款金额:', Number(borrowBalance) / 1e6, 'USDC')
|
||||
console.log()
|
||||
|
||||
console.log('=== 利息信息 ===')
|
||||
console.log()
|
||||
console.log('借款年化利率 (APY):', (Number(borrowRate) / 1e18 * 100).toFixed(2), '%')
|
||||
console.log()
|
||||
|
||||
// 分析结果
|
||||
console.log('=== 分析 ===')
|
||||
console.log()
|
||||
|
||||
if (borrowBalance > 0n) {
|
||||
console.log('✓ 用户确实有借款!')
|
||||
console.log()
|
||||
console.log('借款金额:', Number(borrowBalance) / 1e6, 'USDC')
|
||||
console.log()
|
||||
|
||||
if (balance === 0n) {
|
||||
console.log('余额状态: balance = 0,这意味着:')
|
||||
console.log(' - 用户之前可能有存款')
|
||||
console.log(' - withdraw 金额超过了存款')
|
||||
console.log(' - 超出部分形成了债务')
|
||||
console.log()
|
||||
console.log('这正是 Compound V3 的设计:')
|
||||
console.log(' withdraw(amount) 会:')
|
||||
console.log(' 1. 优先从存款中扣除')
|
||||
console.log(' 2. 存款不足时,自动创建债务')
|
||||
}
|
||||
|
||||
console.log()
|
||||
console.log('用户需要归还的总额:', Number(borrowBalance) / 1e6, 'USDC')
|
||||
console.log('(注意:每秒都在计息,实际金额会略高)')
|
||||
} else {
|
||||
console.log('✗ 用户当前没有借款')
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user