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('idle') const [error, setError] = useState(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, } }