Commit bdcf761d authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

chore: refactor swap button for maintainability (#3579)

* chore: mv SwapButton to dir

* chore: mv approval data to its own hook

* chore: mv approval actions to approvals hook

* chore: simplify SwapButton logic

* fix: pass through approval amount

* fix: mv error handling to consumer
parent e876267d
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { ActionButtonProps } from 'lib/components/ActionButton'
import EtherscanLink from 'lib/components/EtherscanLink'
import {
ApproveOrPermitState,
useApproveOrPermit,
useSwapApprovalOptimizedTrade,
useSwapRouterAddress,
} from 'lib/hooks/swap/useSwapApproval'
import { useAddTransaction, usePendingApproval } from 'lib/hooks/transactions'
import { Slippage } from 'lib/hooks/useSlippage'
import { Spinner } from 'lib/icons'
import { TransactionType } from 'lib/state/transactions'
import { useCallback, useMemo } from 'react'
import { ExplorerDataType } from 'utils/getExplorerLink'
export function useIsPendingApproval(token?: Token, spender?: string): boolean {
return Boolean(usePendingApproval(token, spender))
}
export default function useApprovalData(
trade: ReturnType<typeof useSwapApprovalOptimizedTrade>,
slippage: Slippage,
currencyAmount?: CurrencyAmount<Currency>
) {
const currency = currencyAmount?.currency
const { approvalState, signatureData, handleApproveOrPermit } = useApproveOrPermit(
trade,
slippage.allowed,
useIsPendingApproval,
currencyAmount
)
const addTransaction = useAddTransaction()
const onApprove = useCallback(async () => {
const transaction = await handleApproveOrPermit()
if (transaction) {
addTransaction({ type: TransactionType.APPROVAL, ...transaction })
}
}, [addTransaction, handleApproveOrPermit])
const approvalHash = usePendingApproval(currency?.isToken ? currency : undefined, useSwapRouterAddress(trade))
const approvalData = useMemo((): Partial<ActionButtonProps> | undefined => {
if (!trade || !currency) return
if (approvalState === ApproveOrPermitState.REQUIRES_APPROVAL) {
return {
action: {
message: <Trans>Approve {currency.symbol} first</Trans>,
onClick: onApprove,
children: <Trans>Approve</Trans>,
},
}
} else if (approvalState === ApproveOrPermitState.REQUIRES_SIGNATURE) {
return {
action: {
message: <Trans>Allow {currency.symbol} first</Trans>,
onClick: onApprove,
children: <Trans>Allow</Trans>,
},
}
}
if (approvalState === ApproveOrPermitState.PENDING_APPROVAL) {
return {
disabled: true,
action: {
message: (
<EtherscanLink type={ExplorerDataType.TRANSACTION} data={approvalHash}>
<Trans>Approval pending</Trans>
</EtherscanLink>
),
icon: Spinner,
children: <Trans>Approve</Trans>,
},
}
}
if (approvalState === ApproveOrPermitState.PENDING_SIGNATURE) {
return {
disabled: true,
action: {
message: <Trans>Allowance pending</Trans>,
icon: Spinner,
children: <Trans>Allow</Trans>,
},
}
}
return
}, [approvalHash, approvalState, currency, onApprove, trade])
return { approvalData, signatureData: signatureData ?? undefined }
}
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
import { useAtom } from 'jotai'
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { pickAtom } from 'lib/state/atoms'
......@@ -63,16 +63,6 @@ export function useIsSwapFieldIndependent(field: Field): boolean {
return independentField === field
}
export function useSwapTradeType(): TradeType {
const independentField = useAtomValue(independentFieldAtom)
switch (independentField) {
case Field.INPUT:
return TradeType.EXACT_INPUT
case Field.OUTPUT:
return TradeType.EXACT_OUTPUT
}
}
const amountAtom = pickAtom(swapAtom, 'amount')
// check if any amount has been entered by user
......
......@@ -20,8 +20,8 @@ export enum SwapCallbackState {
interface UseSwapCallbackReturns {
state: SwapCallbackState
callback: null | (() => Promise<TransactionResponse>)
error: ReactNode | null
callback?: () => Promise<TransactionResponse>
error?: ReactNode
}
interface UseSwapCallbackArgs {
trade: AnyTrade | undefined // trade to execute, required
......@@ -59,24 +59,19 @@ export function useSwapCallback({
return useMemo(() => {
if (!trade || !library || !account || !chainId || !callback) {
return { state: SwapCallbackState.INVALID, callback: null, error: <Trans>Missing dependencies</Trans> }
return { state: SwapCallbackState.INVALID, error: <Trans>Missing dependencies</Trans> }
}
if (!recipient) {
if (recipientAddressOrName !== null) {
return { state: SwapCallbackState.INVALID, callback: null, error: <Trans>Invalid recipient</Trans> }
return { state: SwapCallbackState.INVALID, error: <Trans>Invalid recipient</Trans> }
} else {
return { state: SwapCallbackState.LOADING, callback: null, error: null }
return { state: SwapCallbackState.LOADING }
}
}
return {
state: SwapCallbackState.VALID,
callback: async function onSwap(): Promise<TransactionResponse> {
return callback().then((response) => {
return response
})
},
error: null,
callback: async () => callback(),
}
}, [trade, library, account, chainId, callback, recipient, recipientAddressOrName])
}
......@@ -3,7 +3,7 @@ import { useWETHContract } from 'hooks/useContract'
import { useAtomValue } from 'jotai/utils'
import { Field, swapAtom } from 'lib/state/swap'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useCallback, useMemo } from 'react'
import { useMemo } from 'react'
import { WRAPPED_NATIVE_CURRENCY } from '../../../constants/tokens'
import useActiveWeb3React from '../useActiveWeb3React'
......@@ -15,7 +15,7 @@ export enum WrapType {
UNWRAP,
}
interface UseWrapCallbackReturns {
callback: () => Promise<ContractTransaction | undefined>
callback?: () => Promise<ContractTransaction>
type: WrapType
}
......@@ -42,29 +42,21 @@ export default function useWrapCallback(): UseWrapCallbackReturns {
)
const balanceIn = useCurrencyBalance(account, inputCurrency)
const callback = useCallback(async () => {
if (wrapType === WrapType.NONE) {
return Promise.reject('Wrapping not applicable to this asset.')
}
if (!parsedAmountIn) {
return Promise.reject('Must provide an input amount to wrap.')
}
if (!balanceIn || balanceIn.lessThan(parsedAmountIn)) {
return Promise.reject('Insufficient balance to wrap desired amount.')
}
if (!wrappedNativeCurrencyContract) {
return Promise.reject('Wrap contract not found.')
const callback = useMemo(() => {
if (
wrapType === WrapType.NONE ||
!parsedAmountIn ||
!balanceIn ||
balanceIn.lessThan(parsedAmountIn) ||
!wrappedNativeCurrencyContract
) {
return
}
try {
return await (wrapType === WrapType.WRAP
return async () =>
wrapType === WrapType.WRAP
? wrappedNativeCurrencyContract.deposit({ value: `0x${parsedAmountIn.quotient.toString(16)}` })
: wrappedNativeCurrencyContract.withdraw(`0x${parsedAmountIn.quotient.toString(16)}`))
} catch (e) {
// TODO(zzmp): add error handling
console.error(e)
return
}
: wrappedNativeCurrencyContract.withdraw(`0x${parsedAmountIn.quotient.toString(16)}`)
}, [wrapType, parsedAmountIn, balanceIn, wrappedNativeCurrencyContract])
return useMemo(() => ({ callback, type: wrapType }), [callback, wrapType])
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment