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:
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()
|
||||
Reference in New Issue
Block a user