Commit 3f169adc authored by Jack Short's avatar Jack Short Committed by GitHub

chore: cleaning up buy button states (#6618)

parent 774368f3
import { BigNumber } from '@ethersproject/bignumber'
import { parseEther } from '@ethersproject/units'
import { Percent, SupportedChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { nativeOnChain } from 'constants/tokens'
import { useNftUniversalRouterAddress } from 'graphql/data/nft/NftUniversalRouterAddress'
import { useCurrency } from 'hooks/Tokens'
import { AllowanceState } from 'hooks/usePermit2Allowance'
import { useTokenBalance } from 'lib/hooks/useCurrencyBalance'
import { useBag, useWalletBalance } from 'nft/hooks'
import { useBagTotalEthPrice } from 'nft/hooks/useBagTotalEthPrice'
import useDerivedPayWithAnyTokenSwapInfo from 'nft/hooks/useDerivedPayWithAnyTokenSwapInfo'
import usePayWithAnyTokenSwap from 'nft/hooks/usePayWithAnyTokenSwap'
import usePermit2Approval from 'nft/hooks/usePermit2Approval'
import { usePriceImpact } from 'nft/hooks/usePriceImpact'
import { useTokenInput } from 'nft/hooks/useTokenInput'
import { BagStatus } from 'nft/types'
import { TradeState } from 'state/routing/types'
import { TEST_TOKEN_1, TEST_TRADE_EXACT_INPUT, toCurrencyAmount } from 'test-utils/constants'
import { mocked } from 'test-utils/mocked'
import { render, screen } from 'test-utils/render'
import { BagFooter } from './BagFooter'
jest.mock('nft/hooks/useBagTotalEthPrice')
jest.mock('nft/hooks/useWalletBalance')
jest.mock('nft/hooks/useBag')
jest.mock('nft/hooks/useTokenInput')
jest.mock('lib/hooks/useCurrencyBalance')
jest.mock('hooks/Tokens')
jest.mock('nft/hooks/usePayWithAnyTokenSwap')
jest.mock('nft/hooks/useDerivedPayWithAnyTokenSwapInfo')
jest.mock('graphql/data/nft/NftUniversalRouterAddress')
jest.mock('nft/hooks/usePermit2Approval')
jest.mock('nft/hooks/usePriceImpact')
const renderBagFooter = () => {
render(<BagFooter setModalIsOpen={() => undefined} eventProperties={{}} />)
}
const getBuyButton = () => screen.queryByTestId('nft-buy-button') as HTMLButtonElement
const getBuyButtonWarning = () => screen.queryByTestId('nft-buy-button-warning') as HTMLDivElement
describe('BagFooter.tsx', () => {
beforeEach(() => {
mocked(useWeb3React).mockReturnValue({
chainId: 1,
account: '0x52270d8234b864dcAC9947f510CE9275A8a116Db',
} as ReturnType<typeof useWeb3React>)
mocked(useBag).mockReturnValue({
bagStatus: BagStatus.ADDING_TO_BAG,
setBagStatus: () => undefined,
setBagExpanded: () => undefined,
isLocked: false,
itemsInBag: [],
}) as ReturnType<typeof useBag>
mocked(useBagTotalEthPrice).mockReturnValue(BigNumber.from(12))
mocked(useWalletBalance).mockReturnValue({
address: '',
balance: '100',
weiBalance: parseEther('0'),
provider: undefined,
})
mocked(usePermit2Approval).mockReturnValue({
allowance: {
state: AllowanceState.ALLOWED,
permitSignature: undefined,
},
isApprovalLoading: false,
isAllowancePending: false,
updateAllowance: () => Promise.resolve(),
})
mocked(useNftUniversalRouterAddress).mockReturnValue({
universalRouterAddress: undefined,
universalRouterAddressIsLoading: false,
})
mocked(usePriceImpact).mockReturnValue(undefined)
mocked(useDerivedPayWithAnyTokenSwapInfo).mockReturnValue({
state: TradeState.INVALID,
trade: undefined,
maximumAmountIn: undefined,
allowedSlippage: new Percent(10, 100),
})
mocked(usePayWithAnyTokenSwap).mockReturnValue()
mocked(useCurrency).mockReturnValue(nativeOnChain(SupportedChainId.MAINNET))
mocked(useTokenInput).mockReturnValue({
inputCurrency: undefined,
setInputCurrency: () => undefined,
clearInputCurrency: () => undefined,
tokenTradeInput: undefined,
setTokenTradeInput: () => undefined,
})
mocked(useTokenBalance).mockReturnValue(undefined)
})
it('pay', () => {
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton).toBeInTheDocument()
expect(buyButton.textContent).toBe('Pay')
expect(buyButton).not.toBeDisabled()
})
it('wallet not connected', () => {
mocked(useWeb3React).mockReturnValue({
chainId: 1,
account: undefined,
} as ReturnType<typeof useWeb3React>)
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton).toBeInTheDocument()
expect(buyButton.textContent).toBe('Connect wallet')
expect(buyButton).not.toBeDisabled()
})
it('connected to wrong network', () => {
mocked(useWeb3React).mockReturnValue({
chainId: 2,
account: '0x52270d8234b864dcAC9947f510CE9275A8a116Db',
} as ReturnType<typeof useWeb3React>)
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Switch networks')
expect(buyButton).not.toBeDisabled()
})
it('insufficient balance', () => {
mocked(useWalletBalance).mockReturnValue({
address: '',
balance: '0',
weiBalance: parseEther('0'),
provider: undefined,
})
renderBagFooter()
const buyButton = getBuyButton()
const buyButtonWarning = getBuyButtonWarning()
expect(buyButtonWarning.textContent).toBe('Insufficient funds')
expect(buyButton).toBeDisabled()
})
it('transaction error', () => {
mocked(useBag).mockReturnValue({
bagStatus: BagStatus.WARNING,
setBagStatus: () => undefined,
setBagExpanded: () => undefined,
isLocked: false,
itemsInBag: [],
}) as ReturnType<typeof useBag>
renderBagFooter()
const buyButtonWarning = getBuyButtonWarning()
const buyButton = getBuyButton()
expect(buyButtonWarning.textContent).toBe('Something went wrong. Please try again.')
expect(buyButton).toBeDisabled()
})
it('is in wallet confirmation for fetching route', () => {
mocked(useBag).mockReturnValue({
bagStatus: BagStatus.FETCHING_FINAL_ROUTE,
setBagStatus: () => undefined,
setBagExpanded: () => undefined,
isLocked: false,
itemsInBag: [],
}) as ReturnType<typeof useBag>
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Proceed in wallet')
expect(buyButton).toBeDisabled()
})
it('is in wallet confirmation for confirming in wallet', () => {
mocked(useBag).mockReturnValue({
bagStatus: BagStatus.CONFIRMING_IN_WALLET,
setBagStatus: () => undefined,
setBagExpanded: () => undefined,
isLocked: false,
itemsInBag: [],
}) as ReturnType<typeof useBag>
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Proceed in wallet')
expect(buyButton).toBeDisabled()
})
it('is in pending state while transaction pending', () => {
mocked(useBag).mockReturnValue({
bagStatus: BagStatus.PROCESSING_TRANSACTION,
setBagStatus: () => undefined,
setBagExpanded: () => undefined,
isLocked: false,
itemsInBag: [],
}) as ReturnType<typeof useBag>
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Transaction pending')
expect(buyButton).toBeDisabled()
})
it('insufficient funds for token trade', () => {
mocked(useTokenInput).mockReturnValue({
inputCurrency: TEST_TOKEN_1,
setInputCurrency: () => undefined,
clearInputCurrency: () => undefined,
tokenTradeInput: undefined,
setTokenTradeInput: () => undefined,
})
mocked(useTokenBalance).mockReturnValue(toCurrencyAmount(TEST_TOKEN_1, 20))
mocked(useDerivedPayWithAnyTokenSwapInfo).mockReturnValue({
state: TradeState.INVALID,
trade: TEST_TRADE_EXACT_INPUT,
maximumAmountIn: undefined,
allowedSlippage: new Percent(10, 100),
})
renderBagFooter()
const buyButton = getBuyButton()
const buyButtonWarning = getBuyButtonWarning()
expect(buyButtonWarning.textContent).toBe('Insufficient funds')
expect(buyButton).toBeDisabled()
})
it('invalid token trade', () => {
mocked(useTokenInput).mockReturnValue({
inputCurrency: TEST_TOKEN_1,
setInputCurrency: () => undefined,
clearInputCurrency: () => undefined,
tokenTradeInput: undefined,
setTokenTradeInput: () => undefined,
})
mocked(useTokenBalance).mockReturnValue(toCurrencyAmount(TEST_TOKEN_1, 1000))
mocked(useDerivedPayWithAnyTokenSwapInfo).mockReturnValue({
state: TradeState.INVALID,
trade: TEST_TRADE_EXACT_INPUT,
maximumAmountIn: undefined,
allowedSlippage: new Percent(10, 100),
})
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Pay')
expect(buyButton).toBeDisabled()
})
it('no token route found', () => {
mocked(useTokenInput).mockReturnValue({
inputCurrency: TEST_TOKEN_1,
setInputCurrency: () => undefined,
clearInputCurrency: () => undefined,
tokenTradeInput: undefined,
setTokenTradeInput: () => undefined,
})
mocked(useTokenBalance).mockReturnValue(toCurrencyAmount(TEST_TOKEN_1, 1000))
mocked(useDerivedPayWithAnyTokenSwapInfo).mockReturnValue({
state: TradeState.NO_ROUTE_FOUND,
trade: TEST_TRADE_EXACT_INPUT,
maximumAmountIn: undefined,
allowedSlippage: new Percent(10, 100),
})
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Insufficient liquidity')
expect(buyButton).toBeDisabled()
})
it('fetching token route', () => {
mocked(useTokenInput).mockReturnValue({
inputCurrency: TEST_TOKEN_1,
setInputCurrency: () => undefined,
clearInputCurrency: () => undefined,
tokenTradeInput: undefined,
setTokenTradeInput: () => undefined,
})
mocked(useTokenBalance).mockReturnValue(toCurrencyAmount(TEST_TOKEN_1, 1000))
mocked(useDerivedPayWithAnyTokenSwapInfo).mockReturnValue({
state: TradeState.LOADING,
trade: TEST_TRADE_EXACT_INPUT,
maximumAmountIn: undefined,
allowedSlippage: new Percent(10, 100),
})
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Fetching Route')
expect(buyButton).toBeDisabled()
})
it('confirm price change', () => {
mocked(useBag).mockReturnValue({
bagStatus: BagStatus.CONFIRM_QUOTE,
setBagStatus: () => undefined,
setBagExpanded: () => undefined,
isLocked: false,
itemsInBag: [],
}) as ReturnType<typeof useBag>
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Pay')
expect(buyButton).not.toBeDisabled()
})
it('loading allowance', () => {
mocked(usePermit2Approval).mockReturnValue({
allowance: {
state: AllowanceState.LOADING,
},
isAllowancePending: false,
isApprovalLoading: false,
updateAllowance: () => Promise.resolve(),
})
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Loading Allowance')
expect(buyButton).toBeDisabled()
})
it('approval is loading', () => {
mocked(usePermit2Approval).mockReturnValue({
allowance: {
state: AllowanceState.REQUIRED,
isApprovalLoading: false,
token: TEST_TOKEN_1,
approveAndPermit: () => Promise.resolve(),
},
isAllowancePending: false,
isApprovalLoading: true,
updateAllowance: () => Promise.resolve(),
})
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Approval pending')
expect(buyButton).toBeDisabled()
})
it('allowance to be confirmed in wallet', () => {
mocked(usePermit2Approval).mockReturnValue({
allowance: {
state: AllowanceState.REQUIRED,
isApprovalLoading: false,
token: TEST_TOKEN_1,
approveAndPermit: () => Promise.resolve(),
},
isAllowancePending: true,
isApprovalLoading: false,
updateAllowance: () => Promise.resolve(),
})
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Approve in your wallet')
expect(buyButton).toBeDisabled()
})
it('approve', () => {
mocked(usePermit2Approval).mockReturnValue({
allowance: {
state: AllowanceState.REQUIRED,
isApprovalLoading: false,
token: TEST_TOKEN_1,
approveAndPermit: () => Promise.resolve(),
},
isAllowancePending: false,
isApprovalLoading: false,
updateAllowance: () => Promise.resolve(),
})
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Approve')
expect(buyButton).not.toBeDisabled()
})
it('price impact high', () => {
mocked(usePriceImpact).mockReturnValue({
priceImpactSeverity: {
type: 'error',
color: 'accentCritical',
},
displayPercentage: () => '5%',
})
renderBagFooter()
const buyButton = getBuyButton()
expect(buyButton.textContent).toBe('Pay Anyway')
expect(buyButton).not.toBeDisabled()
})
})
......@@ -40,6 +40,8 @@ import { ThemedText } from 'theme'
import { switchChain } from 'utils/switchChain'
import { shallow } from 'zustand/shallow'
import { BuyButtonStateData, BuyButtonStates, getBuyButtonStateData } from './ButtonStates'
const FooterContainer = styled.div`
padding: 0px 12px;
`
......@@ -98,7 +100,7 @@ const CurrencyInput = styled(Row)`
cursor: pointer;
`
const PayButton = styled.button<{ $backgroundColor: string; $color: string }>`
const ActionButton = styled.button<{ $backgroundColor: string; $color: string }>`
display: flex;
background: ${({ $backgroundColor }) => $backgroundColor};
color: ${({ $color }) => $color};
......@@ -149,27 +151,6 @@ const ValueText = styled(ThemedText.BodyPrimary)`
}
`
interface ActionButtonProps {
disabled?: boolean
onClick: () => void
backgroundColor: string
textColor: string
}
const ActionButton = ({
disabled,
children,
onClick,
backgroundColor,
textColor,
}: PropsWithChildren<ActionButtonProps>) => {
return (
<PayButton disabled={disabled} onClick={onClick} $backgroundColor={backgroundColor} $color={textColor}>
{children}
</PayButton>
)
}
interface HelperTextProps {
color: string
}
......@@ -179,7 +160,7 @@ const Warning = ({ color, children }: PropsWithChildren<HelperTextProps>) => {
return null
}
return (
<WarningText fontSize="14px" lineHeight="20px" $color={color}>
<WarningText data-testid="nft-buy-button-warning" fontSize="14px" lineHeight="20px" $color={color}>
<WarningIcon />
{children}
</WarningText>
......@@ -274,11 +255,6 @@ const FiatValue = ({
)
}
interface BagFooterProps {
setModalIsOpen: (open: boolean) => void
eventProperties: Record<string, unknown>
}
const PENDING_BAG_STATUSES = [
BagStatus.FETCHING_ROUTE,
BagStatus.CONFIRMING_IN_WALLET,
......@@ -286,13 +262,18 @@ const PENDING_BAG_STATUSES = [
BagStatus.PROCESSING_TRANSACTION,
]
interface BagFooterProps {
setModalIsOpen: (open: boolean) => void
eventProperties: Record<string, unknown>
}
export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) => {
const toggleWalletDrawer = useToggleAccountDrawer()
const theme = useTheme()
const { account, chainId, connector } = useWeb3React()
const connected = Boolean(account && chainId)
const totalEthPrice = useBagTotalEthPrice()
const inputCurrency = useTokenInput((state) => state.inputCurrency)
const { inputCurrency } = useTokenInput(({ inputCurrency }) => ({ inputCurrency }), shallow)
const setInputCurrency = useTokenInput((state) => state.setInputCurrency)
const defaultCurrency = useCurrency('ETH')
const inputCurrencyBalance = useTokenBalance(
......@@ -377,105 +358,78 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) =
helperTextColor,
handleClick,
buttonColor,
} = useMemo(() => {
let handleClick: (() => void) | (() => Promise<void>) = fetchAssets
let buttonText = <Trans>Something went wrong</Trans>
let disabled = true
let warningText = undefined
let warningTextColor = theme.accentWarning
let helperText = undefined
let helperTextColor = theme.textSecondary
let buttonColor = theme.accentAction
let buttonTextColor = theme.accentTextLightPrimary
} = useMemo((): BuyButtonStateData => {
if (connected && chainId !== SupportedChainId.MAINNET) {
handleClick = () => switchChain(connector, SupportedChainId.MAINNET)
buttonText = <Trans>Switch networks</Trans>
disabled = false
warningText = <Trans>Wrong network</Trans>
} else if (sufficientBalance === false) {
buttonText = <Trans>Pay</Trans>
disabled = true
warningText = <Trans>Insufficient funds</Trans>
} else if (bagStatus === BagStatus.WARNING) {
warningText = <Trans>Something went wrong. Please try again.</Trans>
} else if (!connected) {
handleClick = () => {
const handleClick = () => switchChain(connector, SupportedChainId.MAINNET)
return getBuyButtonStateData(BuyButtonStates.NOT_SUPPORTED_CHAIN, theme, handleClick)
}
if (sufficientBalance === false) {
return getBuyButtonStateData(BuyButtonStates.INSUFFICIENT_BALANCE, theme)
}
if (bagStatus === BagStatus.WARNING) {
return getBuyButtonStateData(BuyButtonStates.ERROR, theme)
}
if (!connected) {
const handleClick = () => {
toggleWalletDrawer()
setBagExpanded({ bagExpanded: false })
}
disabled = false
buttonText = <Trans>Connect wallet</Trans>
} else if (bagStatus === BagStatus.FETCHING_FINAL_ROUTE || bagStatus === BagStatus.CONFIRMING_IN_WALLET) {
disabled = true
buttonText = <Trans>Proceed in wallet</Trans>
} else if (bagStatus === BagStatus.PROCESSING_TRANSACTION) {
disabled = true
buttonText = <Trans>Transaction pending</Trans>
} else if (usingPayWithAnyToken && tradeState !== TradeState.VALID) {
disabled = true
buttonText = <Trans>Fetching Route</Trans>
return getBuyButtonStateData(BuyButtonStates.WALLET_NOT_CONNECTED, theme, handleClick)
}
if (bagStatus === BagStatus.FETCHING_FINAL_ROUTE || bagStatus === BagStatus.CONFIRMING_IN_WALLET) {
return getBuyButtonStateData(BuyButtonStates.IN_WALLET_CONFIRMATION, theme)
}
if (bagStatus === BagStatus.PROCESSING_TRANSACTION) {
return getBuyButtonStateData(BuyButtonStates.PROCESSING_TRANSACTION, theme)
}
if (usingPayWithAnyToken && tradeState !== TradeState.VALID) {
if (tradeState === TradeState.INVALID) {
buttonText = <Trans>Pay</Trans>
return getBuyButtonStateData(BuyButtonStates.INVALID_TOKEN_ROUTE, theme)
}
if (tradeState === TradeState.NO_ROUTE_FOUND) {
buttonText = <Trans>Insufficient liquidity</Trans>
buttonColor = theme.backgroundInteractive
buttonTextColor = theme.textPrimary
helperText = <Trans>Insufficient pool liquidity to complete transaction</Trans>
return getBuyButtonStateData(BuyButtonStates.NO_TOKEN_ROUTE_FOUND, theme)
}
} else if (allowance.state === AllowanceState.REQUIRED || loadingAllowance) {
handleClick = () => updateAllowance()
disabled = isAllowancePending || isApprovalLoading || loadingAllowance
return getBuyButtonStateData(BuyButtonStates.FETCHING_TOKEN_ROUTE, theme)
}
if (allowance.state === AllowanceState.REQUIRED || loadingAllowance) {
const handleClick = () => updateAllowance()
if (loadingAllowance) {
buttonText = <Trans>Loading Allowance</Trans>
return getBuyButtonStateData(BuyButtonStates.LOADING_ALLOWANCE, theme, handleClick)
} else if (isAllowancePending) {
buttonText = <Trans>Approve in your wallet</Trans>
return getBuyButtonStateData(BuyButtonStates.IN_WALLET_ALLOWANCE_APPROVAL, theme, handleClick)
} else if (isApprovalLoading) {
buttonText = <Trans>Approval pending</Trans>
} else {
helperText = <Trans>An approval is needed to use this token. </Trans>
buttonText = <Trans>Approve</Trans>
return getBuyButtonStateData(BuyButtonStates.PROCESSING_APPROVAL, theme, handleClick)
}
} else if (bagStatus === BagStatus.CONFIRM_QUOTE) {
disabled = false
warningTextColor = theme.accentAction
warningText = <Trans>Price updated</Trans>
buttonText = <Trans>Pay</Trans>
} else if (priceImpact && priceImpact.priceImpactSeverity.type === 'error') {
disabled = false
buttonColor = priceImpact.priceImpactSeverity.color
helperText = <Trans>Price impact warning</Trans>
helperTextColor = priceImpact.priceImpactSeverity.color
buttonText = <Trans>Pay Anyway</Trans>
} else if (sufficientBalance === true) {
disabled = false
buttonText = <Trans>Pay</Trans>
helperText = usingPayWithAnyToken ? <Trans>Refunds for unavailable items will be given in ETH</Trans> : undefined
return getBuyButtonStateData(BuyButtonStates.REQUIRE_APPROVAL, theme, handleClick)
}
return {
buttonText,
buttonTextColor,
disabled,
warningText,
warningTextColor,
helperText,
helperTextColor,
handleClick,
buttonColor,
if (bagStatus === BagStatus.CONFIRM_QUOTE) {
return getBuyButtonStateData(BuyButtonStates.CONFIRM_UPDATED_PRICE, theme, fetchAssets)
}
if (priceImpact && priceImpact.priceImpactSeverity.type === 'error') {
return getBuyButtonStateData(
BuyButtonStates.PRICE_IMPACT_HIGH,
theme,
fetchAssets,
usingPayWithAnyToken,
priceImpact
)
}
return getBuyButtonStateData(BuyButtonStates.PAY, theme, fetchAssets, usingPayWithAnyToken)
}, [
fetchAssets,
theme.accentWarning,
theme.textSecondary,
theme.accentAction,
theme.accentTextLightPrimary,
theme.backgroundInteractive,
theme.textPrimary,
connected,
chainId,
sufficientBalance,
......@@ -485,6 +439,8 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) =
allowance.state,
loadingAllowance,
priceImpact,
theme,
fetchAssets,
connector,
toggleWalletDrawer,
setBagExpanded,
......@@ -553,10 +509,11 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) =
<Warning color={warningTextColor}>{warningText}</Warning>
<Helper color={helperTextColor}>{helperText}</Helper>
<ActionButton
data-testid="nft-buy-button"
onClick={handleClick}
disabled={disabled || isPending}
backgroundColor={buttonColor}
textColor={buttonTextColor}
$backgroundColor={buttonColor}
$color={buttonTextColor}
>
{isPending && <Loader size="20px" stroke="white" />}
{buttonText}
......
import { Trans } from '@lingui/macro'
import { PriceImpact } from 'nft/hooks/usePriceImpact'
import { ReactNode } from 'react'
import { DefaultTheme } from 'styled-components/macro'
export enum BuyButtonStates {
WALLET_NOT_CONNECTED,
NOT_SUPPORTED_CHAIN,
INSUFFICIENT_BALANCE,
ERROR,
IN_WALLET_CONFIRMATION,
PROCESSING_TRANSACTION,
FETCHING_TOKEN_ROUTE,
INVALID_TOKEN_ROUTE,
NO_TOKEN_ROUTE_FOUND,
LOADING_ALLOWANCE,
IN_WALLET_ALLOWANCE_APPROVAL,
PROCESSING_APPROVAL,
REQUIRE_APPROVAL,
CONFIRM_UPDATED_PRICE,
PRICE_IMPACT_HIGH,
PAY,
}
export interface BuyButtonStateData {
handleClick: (() => void) | (() => Promise<void>)
buttonText: ReactNode
disabled: boolean
warningText?: ReactNode
warningTextColor: string
helperText?: ReactNode
helperTextColor: string
buttonColor: string
buttonTextColor: string
}
export function getBuyButtonStateData(
buyButtonState: BuyButtonStates,
theme: DefaultTheme,
handleClickOverride?: (() => void) | (() => Promise<void>),
usingPayWithAnyToken?: boolean,
priceImpact?: PriceImpact
): BuyButtonStateData {
const defaultBuyButtonState: BuyButtonStateData = {
handleClick: () => undefined,
buttonText: <Trans>Something went wrong</Trans>,
disabled: true,
warningText: undefined,
warningTextColor: theme.accentWarning,
helperText: undefined,
helperTextColor: theme.textSecondary,
buttonColor: theme.accentAction,
buttonTextColor: theme.accentTextLightPrimary,
}
const buyButtonStateData: Record<BuyButtonStates, BuyButtonStateData> = {
[BuyButtonStates.WALLET_NOT_CONNECTED]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
disabled: false,
buttonText: <Trans>Connect wallet</Trans>,
},
[BuyButtonStates.NOT_SUPPORTED_CHAIN]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
buttonText: <Trans>Switch networks</Trans>,
disabled: false,
warningText: <Trans>Wrong network</Trans>,
},
[BuyButtonStates.INSUFFICIENT_BALANCE]: {
...defaultBuyButtonState,
buttonText: <Trans>Pay</Trans>,
warningText: <Trans>Insufficient funds</Trans>,
},
[BuyButtonStates.ERROR]: {
...defaultBuyButtonState,
warningText: <Trans>Something went wrong. Please try again.</Trans>,
},
[BuyButtonStates.IN_WALLET_CONFIRMATION]: {
...defaultBuyButtonState,
buttonText: <Trans>Proceed in wallet</Trans>,
},
[BuyButtonStates.PROCESSING_TRANSACTION]: {
...defaultBuyButtonState,
buttonText: <Trans>Transaction pending</Trans>,
},
[BuyButtonStates.FETCHING_TOKEN_ROUTE]: {
...defaultBuyButtonState,
buttonText: <Trans>Fetching Route</Trans>,
},
[BuyButtonStates.INVALID_TOKEN_ROUTE]: {
...defaultBuyButtonState,
buttonText: <Trans>Pay</Trans>,
},
[BuyButtonStates.NO_TOKEN_ROUTE_FOUND]: {
...defaultBuyButtonState,
buttonText: <Trans>Insufficient liquidity</Trans>,
buttonColor: theme.backgroundInteractive,
buttonTextColor: theme.textPrimary,
helperText: <Trans>Insufficient pool liquidity to complete transaction</Trans>,
},
[BuyButtonStates.LOADING_ALLOWANCE]: {
...defaultBuyButtonState,
buttonText: <Trans>Loading Allowance</Trans>,
},
[BuyButtonStates.IN_WALLET_ALLOWANCE_APPROVAL]: {
...defaultBuyButtonState,
buttonText: <Trans>Approve in your wallet</Trans>,
},
[BuyButtonStates.PROCESSING_APPROVAL]: {
...defaultBuyButtonState,
buttonText: <Trans>Approval pending</Trans>,
},
[BuyButtonStates.REQUIRE_APPROVAL]: {
...defaultBuyButtonState,
disabled: false,
handleClick: handleClickOverride ?? (() => undefined),
helperText: <Trans>An approval is needed to use this token. </Trans>,
buttonText: <Trans>Approve</Trans>,
},
[BuyButtonStates.CONFIRM_UPDATED_PRICE]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
disabled: false,
warningTextColor: theme.accentAction,
warningText: <Trans>Price updated</Trans>,
buttonText: <Trans>Pay</Trans>,
},
[BuyButtonStates.PRICE_IMPACT_HIGH]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
disabled: false,
buttonColor: priceImpact ? priceImpact.priceImpactSeverity.color : defaultBuyButtonState.buttonColor,
helperText: <Trans>Price impact warning</Trans>,
helperTextColor: priceImpact ? priceImpact.priceImpactSeverity.color : defaultBuyButtonState.helperTextColor,
buttonText: <Trans>Pay Anyway</Trans>,
},
[BuyButtonStates.PAY]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
disabled: false,
buttonText: <Trans>Pay</Trans>,
helperText: usingPayWithAnyToken ? <Trans>Refunds for unavailable items will be given in ETH</Trans> : undefined,
},
}
return buyButtonStateData[buyButtonState]
}
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