init: 初始化 AssetX 项目仓库
包含 webapp(Next.js 用户端)、webapp-back(Go 后端)、 antdesign(管理后台)、landingpage(营销落地页)、 数据库 SQL 和配置文件。
This commit is contained in:
215
webapp/hooks/useLendingSupply.ts
Normal file
215
webapp/hooks/useLendingSupply.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import { useState, useCallback, useEffect } from 'react'
|
||||
import { useAccount, useWriteContract, useWaitForTransactionReceipt, useReadContract } from 'wagmi'
|
||||
import { parseUnits, formatUnits } from 'viem'
|
||||
import { abis, getContractAddress } from '@/lib/contracts'
|
||||
import { useTokenList } from './useTokenList'
|
||||
import { handleContractCatchError, isValidAmount, parseContractError } from '@/lib/errors'
|
||||
|
||||
type SupplyStatus = 'idle' | 'approving' | 'approved' | 'supplying' | 'success' | 'error'
|
||||
|
||||
export function useLendingSupply() {
|
||||
const { address, chainId } = useAccount()
|
||||
const { bySymbol } = useTokenList()
|
||||
const usdcToken = bySymbol['USDC']
|
||||
const [status, setStatus] = useState<SupplyStatus>('idle')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [pendingAmount, setPendingAmount] = useState<string>('')
|
||||
|
||||
const {
|
||||
writeContractAsync: approveWrite,
|
||||
data: approveHash,
|
||||
isPending: isApprovePending,
|
||||
reset: resetApprove,
|
||||
} = useWriteContract()
|
||||
|
||||
const {
|
||||
isLoading: isApproveConfirming,
|
||||
isSuccess: isApproveSuccess,
|
||||
isError: isApproveError,
|
||||
error: approveReceiptError,
|
||||
} = useWaitForTransactionReceipt({ hash: approveHash })
|
||||
|
||||
const {
|
||||
writeContractAsync: supplyWrite,
|
||||
data: supplyHash,
|
||||
isPending: isSupplyPending,
|
||||
reset: resetSupply,
|
||||
} = useWriteContract()
|
||||
|
||||
const {
|
||||
isLoading: isSupplyConfirming,
|
||||
isSuccess: isSupplySuccess,
|
||||
isError: isSupplyError,
|
||||
error: supplyReceiptError,
|
||||
} = useWaitForTransactionReceipt({ hash: supplyHash })
|
||||
|
||||
const executeApprove = useCallback(async (amount: string) => {
|
||||
if (status !== 'idle') return false
|
||||
if (!address || !chainId || !usdcToken?.contractAddress || !amount) {
|
||||
setError('Missing required parameters')
|
||||
return false
|
||||
}
|
||||
if (!isValidAmount(amount)) {
|
||||
setError('Invalid amount')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
setError(null)
|
||||
setStatus('approving')
|
||||
|
||||
const lendingProxyAddress = getContractAddress('lendingProxy', chainId)
|
||||
if (!lendingProxyAddress) {
|
||||
throw new Error('Contract not deployed on this chain')
|
||||
}
|
||||
|
||||
const usdcDecimals = usdcToken.onChainDecimals ?? usdcToken.decimals
|
||||
const amountInWei = parseUnits(amount, usdcDecimals)
|
||||
|
||||
await approveWrite({
|
||||
address: usdcToken.contractAddress as `0x${string}`,
|
||||
abi: abis.USDY,
|
||||
functionName: 'approve',
|
||||
args: [lendingProxyAddress, amountInWei],
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (err: unknown) {
|
||||
handleContractCatchError(err, 'Approve failed', setError, setStatus as (s: string) => void)
|
||||
return false
|
||||
}
|
||||
}, [address, chainId, approveWrite, usdcToken, status])
|
||||
|
||||
const executeSupply = useCallback(async (amount: string) => {
|
||||
if (status === 'supplying' || status === 'success' || status === 'error') return false
|
||||
if (!address || !chainId || !usdcToken || !amount) {
|
||||
setError('Missing required parameters')
|
||||
return false
|
||||
}
|
||||
if (!isValidAmount(amount)) {
|
||||
setError('Invalid amount')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
setError(null)
|
||||
setStatus('supplying')
|
||||
|
||||
const lendingProxyAddress = getContractAddress('lendingProxy', chainId)
|
||||
if (!lendingProxyAddress) {
|
||||
throw new Error('Contract not deployed on this chain')
|
||||
}
|
||||
|
||||
const usdcDecimals = usdcToken.onChainDecimals ?? usdcToken.decimals
|
||||
const amountInWei = parseUnits(amount, usdcDecimals)
|
||||
|
||||
await supplyWrite({
|
||||
address: lendingProxyAddress,
|
||||
abi: abis.lendingProxy,
|
||||
functionName: 'supply',
|
||||
args: [amountInWei],
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (err: unknown) {
|
||||
handleContractCatchError(err, 'Supply failed', setError, setStatus as (s: string) => void)
|
||||
return false
|
||||
}
|
||||
}, [address, chainId, supplyWrite, usdcToken, status])
|
||||
|
||||
const executeApproveAndSupply = useCallback(async (amount: string) => {
|
||||
if (status !== 'idle') return
|
||||
setPendingAmount(amount)
|
||||
const approveSuccess = await executeApprove(amount)
|
||||
if (!approveSuccess) return
|
||||
}, [executeApprove, status])
|
||||
|
||||
// Auto-execute supply when approve is successful
|
||||
useEffect(() => {
|
||||
if (isApproveSuccess && status === 'approving' && pendingAmount) {
|
||||
setStatus('approved')
|
||||
executeSupply(pendingAmount)
|
||||
}
|
||||
}, [isApproveSuccess, status, pendingAmount, executeSupply])
|
||||
|
||||
useEffect(() => {
|
||||
if (isApproveError && status === 'approving') {
|
||||
setError(parseContractError(approveReceiptError))
|
||||
setStatus('error')
|
||||
}
|
||||
}, [isApproveError, status, approveReceiptError])
|
||||
|
||||
useEffect(() => {
|
||||
if (isSupplySuccess && status === 'supplying') {
|
||||
setStatus('success')
|
||||
}
|
||||
}, [isSupplySuccess, status])
|
||||
|
||||
useEffect(() => {
|
||||
if (isSupplyError && status === 'supplying') {
|
||||
setError(parseContractError(supplyReceiptError))
|
||||
setStatus('error')
|
||||
}
|
||||
}, [isSupplyError, status, supplyReceiptError])
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setStatus('idle')
|
||||
setError(null)
|
||||
setPendingAmount('')
|
||||
resetApprove()
|
||||
resetSupply()
|
||||
}, [resetApprove, resetSupply])
|
||||
|
||||
return {
|
||||
status,
|
||||
error,
|
||||
isLoading: isApprovePending || isApproveConfirming || isSupplyPending || isSupplyConfirming,
|
||||
approveHash,
|
||||
supplyHash,
|
||||
executeApprove,
|
||||
executeSupply,
|
||||
executeApproveAndSupply,
|
||||
reset,
|
||||
}
|
||||
}
|
||||
|
||||
// Query supplied balance
|
||||
export function useSuppliedBalance() {
|
||||
const { address, chainId } = useAccount()
|
||||
const { bySymbol } = useTokenList()
|
||||
const usdcToken = bySymbol['USDC']
|
||||
const lendingProxyAddress = chainId ? getContractAddress('lendingProxy', chainId) : undefined
|
||||
|
||||
const {
|
||||
data: balance,
|
||||
error: queryError,
|
||||
isError,
|
||||
isLoading,
|
||||
refetch
|
||||
} = useReadContract({
|
||||
address: lendingProxyAddress,
|
||||
abi: abis.lendingProxy,
|
||||
functionName: 'supplyBalanceOf',
|
||||
args: address ? [address] : undefined,
|
||||
query: {
|
||||
enabled: !!address && !!lendingProxyAddress,
|
||||
retry: false,
|
||||
},
|
||||
})
|
||||
|
||||
if (isError && queryError) {
|
||||
console.error('[useSuppliedBalance] Query error:', queryError)
|
||||
}
|
||||
|
||||
const usdcDecimals = usdcToken?.onChainDecimals ?? usdcToken?.decimals ?? 18
|
||||
const suppliedBalance = balance ? (balance as bigint) : 0n
|
||||
const formattedBalance = suppliedBalance > 0n
|
||||
? (Number(suppliedBalance) / Math.pow(10, usdcDecimals)).toFixed(2)
|
||||
: '0.00'
|
||||
|
||||
return {
|
||||
balance: suppliedBalance,
|
||||
formattedBalance,
|
||||
refetch,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user