Files
assetx/webapp/hooks/useSwap.ts

180 lines
5.3 KiB
TypeScript
Raw Permalink Normal View History

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,
}
}