uniswap-interface-uncensored/src/hooks/useWrapCallback.tsx
lynn ec783fdb1c
feat: log approve token and wrap token events directly in hooks (#4258)
* init

* clean up

* remove unnecessary constants

* undo yarn.lock change
2022-08-03 12:45:45 -04:00

147 lines
5.7 KiB
TypeScript

import { Trans } from '@lingui/macro'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent } from 'components/AmplitudeAnalytics'
import { EventName } from 'components/AmplitudeAnalytics/constants'
import { formatToDecimal, getTokenAddress } from 'components/AmplitudeAnalytics/utils'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo } from 'react'
import { WRAPPED_NATIVE_CURRENCY } from '../constants/tokens'
import { useCurrencyBalance } from '../state/connection/hooks'
import { useTransactionAdder } from '../state/transactions/hooks'
import { TransactionType } from '../state/transactions/types'
import { useWETHContract } from './useContract'
export enum WrapType {
NOT_APPLICABLE,
WRAP,
UNWRAP,
}
const NOT_APPLICABLE = { wrapType: WrapType.NOT_APPLICABLE }
enum WrapInputError {
NO_ERROR, // must be equal to 0 so all other errors are truthy
ENTER_NATIVE_AMOUNT,
ENTER_WRAPPED_AMOUNT,
INSUFFICIENT_NATIVE_BALANCE,
INSUFFICIENT_WRAPPED_BALANCE,
}
export function WrapErrorText({ wrapInputError }: { wrapInputError: WrapInputError }) {
const native = useNativeCurrency()
const wrapped = native?.wrapped
switch (wrapInputError) {
case WrapInputError.NO_ERROR:
return null
case WrapInputError.ENTER_NATIVE_AMOUNT:
return <Trans>Enter {native?.symbol} amount</Trans>
case WrapInputError.ENTER_WRAPPED_AMOUNT:
return <Trans>Enter {wrapped?.symbol} amount</Trans>
case WrapInputError.INSUFFICIENT_NATIVE_BALANCE:
return <Trans>Insufficient {native?.symbol} balance</Trans>
case WrapInputError.INSUFFICIENT_WRAPPED_BALANCE:
return <Trans>Insufficient {wrapped?.symbol} balance</Trans>
}
}
/**
* Given the selected input and output currency, return a wrap callback
* @param inputCurrency the selected input currency
* @param outputCurrency the selected output currency
* @param typedValue the user input value
*/
export default function useWrapCallback(
inputCurrency: Currency | undefined | null,
outputCurrency: Currency | undefined | null,
typedValue: string | undefined
): { wrapType: WrapType; execute?: undefined | (() => Promise<void>); inputError?: WrapInputError } {
const { chainId, account } = useWeb3React()
const wethContract = useWETHContract()
const balance = useCurrencyBalance(account ?? undefined, inputCurrency ?? undefined)
// we can always parse the amount typed as the input currency, since wrapping is 1:1
const inputAmount = useMemo(
() => tryParseCurrencyAmount(typedValue, inputCurrency ?? undefined),
[inputCurrency, typedValue]
)
const addTransaction = useTransactionAdder()
return useMemo(() => {
if (!wethContract || !chainId || !inputCurrency || !outputCurrency) return NOT_APPLICABLE
const weth = WRAPPED_NATIVE_CURRENCY[chainId]
if (!weth) return NOT_APPLICABLE
const hasInputAmount = Boolean(inputAmount?.greaterThan('0'))
const sufficientBalance = inputAmount && balance && !balance.lessThan(inputAmount)
const eventProperties = {
token_in_address: getTokenAddress(inputCurrency),
token_out_address: getTokenAddress(outputCurrency),
token_in_symbol: inputCurrency.symbol,
token_out_symbol: outputCurrency.symbol,
chain_id: inputCurrency.chainId,
amount: inputAmount ? formatToDecimal(inputAmount, inputAmount?.currency.decimals) : undefined,
}
if (inputCurrency.isNative && weth.equals(outputCurrency)) {
return {
wrapType: WrapType.WRAP,
execute:
sufficientBalance && inputAmount
? async () => {
try {
const txReceipt = await wethContract.deposit({ value: `0x${inputAmount.quotient.toString(16)}` })
addTransaction(txReceipt, {
type: TransactionType.WRAP,
unwrapped: false,
currencyAmountRaw: inputAmount?.quotient.toString(),
chainId,
})
sendAnalyticsEvent(EventName.WRAP_TOKEN_TXN_SUBMITTED, { ...eventProperties, type: WrapType.WRAP })
} catch (error) {
console.error('Could not deposit', error)
}
}
: undefined,
inputError: sufficientBalance
? undefined
: hasInputAmount
? WrapInputError.INSUFFICIENT_NATIVE_BALANCE
: WrapInputError.ENTER_NATIVE_AMOUNT,
}
} else if (weth.equals(inputCurrency) && outputCurrency.isNative) {
return {
wrapType: WrapType.UNWRAP,
execute:
sufficientBalance && inputAmount
? async () => {
try {
const txReceipt = await wethContract.withdraw(`0x${inputAmount.quotient.toString(16)}`)
addTransaction(txReceipt, {
type: TransactionType.WRAP,
unwrapped: true,
currencyAmountRaw: inputAmount?.quotient.toString(),
chainId,
})
sendAnalyticsEvent(EventName.WRAP_TOKEN_TXN_SUBMITTED, { ...eventProperties, type: WrapType.UNWRAP })
} catch (error) {
console.error('Could not withdraw', error)
}
}
: undefined,
inputError: sufficientBalance
? undefined
: hasInputAmount
? WrapInputError.INSUFFICIENT_WRAPPED_BALANCE
: WrapInputError.ENTER_WRAPPED_AMOUNT,
}
} else {
return NOT_APPLICABLE
}
}, [wethContract, chainId, inputCurrency, outputCurrency, inputAmount, balance, addTransaction])
}