168 lines
5.1 KiB
TypeScript
168 lines
5.1 KiB
TypeScript
|
|
import { useState, useCallback, useEffect } from 'react'
|
||
|
|
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
|
||
|
|
import { parseUnits } from 'viem'
|
||
|
|
import { abis } from '@/lib/contracts'
|
||
|
|
import { Token } from '@/lib/api/tokens'
|
||
|
|
import { useTokenList } from './useTokenList'
|
||
|
|
import { handleContractCatchError, isValidAmount, parseContractError } from '@/lib/errors'
|
||
|
|
import { DEFAULT_GAS_LIMIT } from '@/lib/constants'
|
||
|
|
|
||
|
|
type DepositStatus = 'idle' | 'approving' | 'approved' | 'depositing' | 'success' | 'error'
|
||
|
|
|
||
|
|
export function useDeposit(ytToken: Token | undefined) {
|
||
|
|
const { address, chainId } = useAccount()
|
||
|
|
const { bySymbol } = useTokenList()
|
||
|
|
const usdcToken = bySymbol['USDC']
|
||
|
|
const [status, setStatus] = useState<DepositStatus>('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: depositWrite,
|
||
|
|
data: depositHash,
|
||
|
|
isPending: isDepositPending,
|
||
|
|
reset: resetDeposit,
|
||
|
|
} = useWriteContract()
|
||
|
|
|
||
|
|
const {
|
||
|
|
isLoading: isDepositConfirming,
|
||
|
|
isSuccess: isDepositSuccess,
|
||
|
|
isError: isDepositError,
|
||
|
|
error: depositReceiptError,
|
||
|
|
} = useWaitForTransactionReceipt({ hash: depositHash })
|
||
|
|
|
||
|
|
const executeApprove = useCallback(async (amount: string) => {
|
||
|
|
if (status !== 'idle') return false
|
||
|
|
if (!address || !ytToken?.contractAddress || !usdcToken?.contractAddress || !amount) {
|
||
|
|
setError('Missing required parameters')
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
if (!isValidAmount(amount)) {
|
||
|
|
setError('Invalid amount')
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
setError(null)
|
||
|
|
setStatus('approving')
|
||
|
|
|
||
|
|
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: [ytToken.contractAddress as `0x${string}`, amountInWei],
|
||
|
|
})
|
||
|
|
|
||
|
|
return true
|
||
|
|
} catch (err: unknown) {
|
||
|
|
handleContractCatchError(err, 'Approve failed', setError, setStatus as (s: string) => void)
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
}, [address, approveWrite, ytToken, usdcToken, status])
|
||
|
|
|
||
|
|
const executeDeposit = useCallback(async (amount: string) => {
|
||
|
|
if (status === 'depositing' || status === 'success' || status === 'error') return false
|
||
|
|
if (!address || !ytToken?.contractAddress || !usdcToken || !amount) {
|
||
|
|
setError('Missing required parameters')
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
if (!isValidAmount(amount)) {
|
||
|
|
setError('Invalid amount')
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
setError(null)
|
||
|
|
setStatus('depositing')
|
||
|
|
|
||
|
|
const usdcDecimals = usdcToken.onChainDecimals ?? usdcToken.decimals
|
||
|
|
const amountInWei = parseUnits(amount, usdcDecimals)
|
||
|
|
|
||
|
|
await depositWrite({
|
||
|
|
address: ytToken.contractAddress as `0x${string}`,
|
||
|
|
abi: abis.YTToken,
|
||
|
|
functionName: 'depositYT',
|
||
|
|
args: [amountInWei],
|
||
|
|
gas: DEFAULT_GAS_LIMIT,
|
||
|
|
})
|
||
|
|
|
||
|
|
return true
|
||
|
|
} catch (err: unknown) {
|
||
|
|
handleContractCatchError(err, 'Deposit failed', setError, setStatus as (s: string) => void)
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
}, [address, depositWrite, ytToken, usdcToken, status])
|
||
|
|
|
||
|
|
const executeApproveAndDeposit = useCallback(async (amount: string) => {
|
||
|
|
if (status !== 'idle') return
|
||
|
|
setPendingAmount(amount)
|
||
|
|
const approveSuccess = await executeApprove(amount)
|
||
|
|
if (!approveSuccess) return
|
||
|
|
}, [executeApprove, status])
|
||
|
|
|
||
|
|
// Auto-execute deposit when approve is successful
|
||
|
|
useEffect(() => {
|
||
|
|
if (isApproveSuccess && status === 'approving' && pendingAmount) {
|
||
|
|
setStatus('approved')
|
||
|
|
executeDeposit(pendingAmount)
|
||
|
|
}
|
||
|
|
}, [isApproveSuccess, status, pendingAmount, executeDeposit])
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (isApproveError && status === 'approving') {
|
||
|
|
setError(parseContractError(approveReceiptError))
|
||
|
|
setStatus('error')
|
||
|
|
}
|
||
|
|
}, [isApproveError, status, approveReceiptError])
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (isDepositSuccess && status === 'depositing') {
|
||
|
|
setStatus('success')
|
||
|
|
}
|
||
|
|
}, [isDepositSuccess, status])
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (isDepositError && status === 'depositing') {
|
||
|
|
setError(parseContractError(depositReceiptError))
|
||
|
|
setStatus('error')
|
||
|
|
}
|
||
|
|
}, [isDepositError, status, depositReceiptError])
|
||
|
|
|
||
|
|
const reset = useCallback(() => {
|
||
|
|
setStatus('idle')
|
||
|
|
setError(null)
|
||
|
|
setPendingAmount('')
|
||
|
|
resetApprove()
|
||
|
|
resetDeposit()
|
||
|
|
}, [resetApprove, resetDeposit])
|
||
|
|
|
||
|
|
return {
|
||
|
|
status,
|
||
|
|
error,
|
||
|
|
isLoading: isApprovePending || isApproveConfirming || isDepositPending || isDepositConfirming,
|
||
|
|
approveHash,
|
||
|
|
depositHash,
|
||
|
|
executeApprove,
|
||
|
|
executeDeposit,
|
||
|
|
executeApproveAndDeposit,
|
||
|
|
reset,
|
||
|
|
}
|
||
|
|
}
|