init: 初始化 AssetX 项目仓库
包含 webapp(Next.js 用户端)、webapp-back(Go 后端)、 antdesign(管理后台)、landingpage(营销落地页)、 数据库 SQL 和配置文件。
This commit is contained in:
179
webapp/hooks/useSwap.ts
Normal file
179
webapp/hooks/useSwap.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
|
||||
import { parseUnits } from 'viem'
|
||||
import { abis, getContractAddress } from '@/lib/contracts'
|
||||
import { handleContractCatchError, isValidAmount, parseContractError } from '@/lib/errors'
|
||||
import { SWAP_GAS_LIMIT } from '@/lib/constants'
|
||||
|
||||
type SwapStatus = 'idle' | 'approving' | 'approved' | 'swapping' | 'success' | 'error'
|
||||
|
||||
/**
|
||||
* Hook for swapping tokens via YTRewardRouter
|
||||
* Accepts contract addresses and decimals directly — no hardcoded token symbols.
|
||||
*/
|
||||
export function useSwap() {
|
||||
const { address, chainId } = useAccount()
|
||||
const [status, setStatus] = useState<SwapStatus>('idle')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [approveHash, setApproveHash] = useState<`0x${string}` | undefined>()
|
||||
const [swapHash, setSwapHash] = useState<`0x${string}` | undefined>()
|
||||
|
||||
const { writeContractAsync: approveWrite } = useWriteContract()
|
||||
const { writeContractAsync: swapWrite } = useWriteContract()
|
||||
|
||||
const { isLoading: isApproving, isSuccess: isApproveSuccess, isError: isApproveError, error: approveReceiptError } = useWaitForTransactionReceipt({
|
||||
hash: approveHash,
|
||||
})
|
||||
|
||||
const { isLoading: isSwapping, isSuccess: isSwapSuccess, isError: isSwapError, error: swapReceiptError } = useWaitForTransactionReceipt({
|
||||
hash: swapHash,
|
||||
})
|
||||
|
||||
const executeApprove = async (tokenInAddress: string, tokenInDecimals: number, amount: string) => {
|
||||
if (status !== 'idle') return false
|
||||
if (!chainId || !address) {
|
||||
setError('Please connect your wallet')
|
||||
return false
|
||||
}
|
||||
if (!isValidAmount(amount)) {
|
||||
setError('Invalid amount')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
setStatus('approving')
|
||||
setError(null)
|
||||
|
||||
const rewardRouterAddress = getContractAddress('YTRewardRouter', chainId)
|
||||
|
||||
if (!tokenInAddress || !rewardRouterAddress) {
|
||||
throw new Error('Contract not deployed on this chain')
|
||||
}
|
||||
|
||||
const amountInWei = parseUnits(amount, tokenInDecimals)
|
||||
|
||||
const hash = await approveWrite({
|
||||
address: tokenInAddress as `0x${string}`,
|
||||
abi: abis.USDY,
|
||||
functionName: 'approve',
|
||||
args: [rewardRouterAddress, amountInWei],
|
||||
})
|
||||
|
||||
setApproveHash(hash)
|
||||
return true
|
||||
} catch (err: unknown) {
|
||||
handleContractCatchError(err, 'Approval failed', setError, setStatus as (s: string) => void)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const executeSwap = async (
|
||||
tokenInAddress: string,
|
||||
tokenInDecimals: number,
|
||||
tokenOutAddress: string,
|
||||
tokenOutDecimals: number,
|
||||
amount: string,
|
||||
minOut: string = '0'
|
||||
) => {
|
||||
if (status === 'swapping' || status === 'success' || status === 'error') return false
|
||||
if (!chainId || !address) {
|
||||
setError('Please connect your wallet')
|
||||
return false
|
||||
}
|
||||
if (!isValidAmount(amount)) {
|
||||
setError('Invalid amount')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
setStatus('swapping')
|
||||
setError(null)
|
||||
|
||||
const rewardRouterAddress = getContractAddress('YTRewardRouter', chainId)
|
||||
|
||||
if (!tokenInAddress || !tokenOutAddress || !rewardRouterAddress) {
|
||||
throw new Error('Contract not deployed on this chain')
|
||||
}
|
||||
|
||||
const amountInWei = parseUnits(amount, tokenInDecimals)
|
||||
const minOutWei = parseUnits(minOut, tokenOutDecimals)
|
||||
|
||||
const hash = await swapWrite({
|
||||
address: rewardRouterAddress,
|
||||
abi: abis.YTRewardRouter,
|
||||
functionName: 'swapYT',
|
||||
args: [tokenInAddress as `0x${string}`, tokenOutAddress as `0x${string}`, amountInWei, minOutWei, address],
|
||||
gas: SWAP_GAS_LIMIT,
|
||||
type: 'legacy',
|
||||
})
|
||||
|
||||
setSwapHash(hash)
|
||||
return true
|
||||
} catch (err: unknown) {
|
||||
handleContractCatchError(err, 'Swap failed', setError, setStatus as (s: string) => void)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const executeApproveAndSwap = async (
|
||||
tokenInAddress: string,
|
||||
tokenInDecimals: number,
|
||||
tokenOutAddress: string,
|
||||
tokenOutDecimals: number,
|
||||
amount: string,
|
||||
minOut: string = '0'
|
||||
) => {
|
||||
if (status !== 'idle') return
|
||||
const approved = await executeApprove(tokenInAddress, tokenInDecimals, amount)
|
||||
if (!approved) return
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
|
||||
await executeSwap(tokenInAddress, tokenInDecimals, tokenOutAddress, tokenOutDecimals, amount, minOut)
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
setStatus('idle')
|
||||
setError(null)
|
||||
setApproveHash(undefined)
|
||||
setSwapHash(undefined)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isApproveSuccess && status === 'approving') {
|
||||
setStatus('approved')
|
||||
}
|
||||
}, [isApproveSuccess, status])
|
||||
|
||||
useEffect(() => {
|
||||
if (isApproveError && status === 'approving') {
|
||||
setError(parseContractError(approveReceiptError))
|
||||
setStatus('error')
|
||||
}
|
||||
}, [isApproveError, status, approveReceiptError])
|
||||
|
||||
useEffect(() => {
|
||||
if (isSwapSuccess && status === 'swapping') {
|
||||
setStatus('success')
|
||||
}
|
||||
}, [isSwapSuccess, status])
|
||||
|
||||
useEffect(() => {
|
||||
if (isSwapError && status === 'swapping') {
|
||||
setError(parseContractError(swapReceiptError))
|
||||
setStatus('error')
|
||||
}
|
||||
}, [isSwapError, status, swapReceiptError])
|
||||
|
||||
return {
|
||||
status,
|
||||
error,
|
||||
isLoading: isApproving || isSwapping,
|
||||
approveHash,
|
||||
swapHash,
|
||||
executeApprove,
|
||||
executeSwap,
|
||||
executeApproveAndSwap,
|
||||
reset,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user