hiCity-nft/src/hooks/useApproveConfirmTransactio...

140 lines
3.7 KiB
TypeScript

import { useEffect, useReducer, useRef } from 'react'
import { noop } from 'lodash'
import { useWeb3React } from '@web3-react/core'
import { ethers } from 'ethers'
import useToast from 'hooks/useToast'
import { useTranslation } from 'contexts/Localization'
type LoadingState = 'idle' | 'loading' | 'success' | 'fail'
type Action =
| { type: 'requires_approval' }
| { type: 'approve_sending' }
| { type: 'approve_receipt' }
| { type: 'approve_error' }
| { type: 'confirm_sending' }
| { type: 'confirm_receipt' }
| { type: 'confirm_error' }
interface State {
approvalState: LoadingState
confirmState: LoadingState
}
const initialState: State = {
approvalState: 'idle',
confirmState: 'idle',
}
const reducer = (state: State, actions: Action): State => {
switch (actions.type) {
case 'requires_approval':
return {
...state,
approvalState: 'success',
}
case 'approve_sending':
return {
...state,
approvalState: 'loading',
}
case 'approve_receipt':
return {
...state,
approvalState: 'success',
}
case 'approve_error':
return {
...state,
approvalState: 'fail',
}
case 'confirm_sending':
return {
...state,
confirmState: 'loading',
}
case 'confirm_receipt':
return {
...state,
confirmState: 'success',
}
case 'confirm_error':
return {
...state,
confirmState: 'fail',
}
default:
return state
}
}
interface ApproveConfirmTransaction {
onApprove: () => ethers.providers.TransactionResponse
onConfirm: () => ethers.providers.TransactionResponse
onRequiresApproval?: () => Promise<boolean>
onSuccess: (state: State) => void
onApproveSuccess?: (state: State) => void
}
const useApproveConfirmTransaction = ({
onApprove,
onConfirm,
onRequiresApproval,
onSuccess = noop,
onApproveSuccess = noop,
}: ApproveConfirmTransaction) => {
const { t } = useTranslation()
const { account } = useWeb3React()
const [state, dispatch] = useReducer(reducer, initialState)
const handlePreApprove = useRef(onRequiresApproval)
const { toastError } = useToast()
// Check if approval is necessary, re-check if account changes
useEffect(() => {
if (account && handlePreApprove.current) {
handlePreApprove.current().then((result) => {
if (result) {
dispatch({ type: 'requires_approval' })
}
})
}
}, [account, handlePreApprove, dispatch])
return {
isApproving: state.approvalState === 'loading',
isApproved: state.approvalState === 'success',
isConfirming: state.confirmState === 'loading',
isConfirmed: state.confirmState === 'success',
handleApprove: async () => {
try {
const tx = await onApprove()
dispatch({ type: 'approve_sending' })
const receipt = await tx.wait()
if (receipt.status) {
dispatch({ type: 'approve_receipt' })
onApproveSuccess(state)
}
} catch (error) {
dispatch({ type: 'approve_error' })
toastError(t('Error'), t('Please try again. Confirm the transaction and make sure you are paying enough gas!'))
}
},
handleConfirm: async () => {
dispatch({ type: 'confirm_sending' })
try {
const tx = await onConfirm()
const receipt = await tx.wait()
if (receipt.status) {
dispatch({ type: 'confirm_receipt' })
onSuccess(state)
}
} catch (error) {
dispatch({ type: 'confirm_error' })
toastError(t('Error'), t('Please try again. Confirm the transaction and make sure you are paying enough gas!'))
}
},
}
}
export default useApproveConfirmTransaction