feat: uniswapx deadline (#7376)
* feat: uniswapX time-to-sign * fix: animation timing * fix: bug * fix: improve props and remove memo
This commit is contained in:
parent
d63bdf1887
commit
e6362212c6
@ -28,6 +28,7 @@ import styled from 'styled-components'
|
|||||||
import { ThemedText } from 'theme/components'
|
import { ThemedText } from 'theme/components'
|
||||||
import invariant from 'tiny-invariant'
|
import invariant from 'tiny-invariant'
|
||||||
import { isL2ChainId } from 'utils/chains'
|
import { isL2ChainId } from 'utils/chains'
|
||||||
|
import { SignatureExpiredError } from 'utils/errors'
|
||||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||||
import { formatSwapPriceUpdatedEventProperties } from 'utils/loggingFormatters'
|
import { formatSwapPriceUpdatedEventProperties } from 'utils/loggingFormatters'
|
||||||
import { didUserReject } from 'utils/swapErrorToUserReadableMessage'
|
import { didUserReject } from 'utils/swapErrorToUserReadableMessage'
|
||||||
@ -265,6 +266,7 @@ export default function ConfirmSwapModal({
|
|||||||
onAcceptChanges,
|
onAcceptChanges,
|
||||||
allowedSlippage,
|
allowedSlippage,
|
||||||
allowance,
|
allowance,
|
||||||
|
clearSwapState,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
onDismiss,
|
onDismiss,
|
||||||
onCurrencySelection,
|
onCurrencySelection,
|
||||||
@ -280,6 +282,7 @@ export default function ConfirmSwapModal({
|
|||||||
allowedSlippage: Percent
|
allowedSlippage: Percent
|
||||||
allowance: Allowance
|
allowance: Allowance
|
||||||
onAcceptChanges: () => void
|
onAcceptChanges: () => void
|
||||||
|
clearSwapState: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
swapError?: Error
|
swapError?: Error
|
||||||
onDismiss: () => void
|
onDismiss: () => void
|
||||||
@ -293,7 +296,10 @@ export default function ConfirmSwapModal({
|
|||||||
useConfirmModalState({
|
useConfirmModalState({
|
||||||
trade,
|
trade,
|
||||||
allowedSlippage,
|
allowedSlippage,
|
||||||
onSwap: onConfirm,
|
onSwap: () => {
|
||||||
|
clearSwapState()
|
||||||
|
onConfirm()
|
||||||
|
},
|
||||||
onCurrencySelection,
|
onCurrencySelection,
|
||||||
allowance,
|
allowance,
|
||||||
doesTradeDiffer: Boolean(doesTradeDiffer),
|
doesTradeDiffer: Boolean(doesTradeDiffer),
|
||||||
@ -376,6 +382,8 @@ export default function ConfirmSwapModal({
|
|||||||
wrapTxHash={wrapTxHash}
|
wrapTxHash={wrapTxHash}
|
||||||
tokenApprovalPending={allowance.state === AllowanceState.REQUIRED && allowance.isApprovalPending}
|
tokenApprovalPending={allowance.state === AllowanceState.REQUIRED && allowance.isApprovalPending}
|
||||||
revocationPending={allowance.state === AllowanceState.REQUIRED && allowance.isRevocationPending}
|
revocationPending={allowance.state === AllowanceState.REQUIRED && allowance.isRevocationPending}
|
||||||
|
swapError={swapError}
|
||||||
|
onRetryUniswapXSignature={onConfirm}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}, [
|
}, [
|
||||||
@ -386,13 +394,14 @@ export default function ConfirmSwapModal({
|
|||||||
swapResult,
|
swapResult,
|
||||||
wrapTxHash,
|
wrapTxHash,
|
||||||
allowance,
|
allowance,
|
||||||
|
swapError,
|
||||||
|
startSwapFlow,
|
||||||
allowedSlippage,
|
allowedSlippage,
|
||||||
fiatValueInput,
|
fiatValueInput,
|
||||||
fiatValueOutput,
|
fiatValueOutput,
|
||||||
onAcceptChanges,
|
onAcceptChanges,
|
||||||
swapFailed,
|
swapFailed,
|
||||||
swapError?.message,
|
onConfirm,
|
||||||
startSwapFlow,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
const l2Badge = () => {
|
const l2Badge = () => {
|
||||||
@ -410,14 +419,20 @@ export default function ConfirmSwapModal({
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getErrorType = () => {
|
||||||
|
if (approvalError) return approvalError
|
||||||
|
// SignatureExpiredError is a special case. The UI is shown in the PendingModalContent component.
|
||||||
|
if (swapError instanceof SignatureExpiredError) return
|
||||||
|
if (swapError && !didUserReject(swapError)) return PendingModalError.CONFIRMATION_ERROR
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const errorType = getErrorType()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Trace modal={InterfaceModalName.CONFIRM_SWAP}>
|
<Trace modal={InterfaceModalName.CONFIRM_SWAP}>
|
||||||
<Modal isOpen $scrollOverlay onDismiss={onModalDismiss} maxHeight={90}>
|
<Modal isOpen $scrollOverlay onDismiss={onModalDismiss} maxHeight={90}>
|
||||||
{approvalError || swapFailed ? (
|
{errorType ? (
|
||||||
<ErrorModalContent
|
<ErrorModalContent errorType={errorType} onRetry={startSwapFlow} />
|
||||||
errorType={approvalError ?? PendingModalError.CONFIRMATION_ERROR}
|
|
||||||
onRetry={startSwapFlow}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<ConfirmationModalContent
|
<ConfirmationModalContent
|
||||||
title={confirmModalState === ConfirmModalState.REVIEWING ? <Trans>Review swap</Trans> : undefined}
|
title={confirmModalState === ConfirmModalState.REVIEWING ? <Trans>Review swap</Trans> : undefined}
|
||||||
|
81
src/components/swap/PendingModalContent/TransitionText.tsx
Normal file
81
src/components/swap/PendingModalContent/TransitionText.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { Trans } from '@lingui/macro'
|
||||||
|
import { useUnmountingAnimation } from 'hooks/useUnmountingAnimation'
|
||||||
|
import { ReactNode, useEffect, useRef, useState } from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
import { slideInAnimation, slideOutAnimation } from './animations'
|
||||||
|
import { AnimationType } from './Logos'
|
||||||
|
|
||||||
|
interface TransitionTextProps {
|
||||||
|
initialText: ReactNode
|
||||||
|
transitionText: ReactNode
|
||||||
|
transitionTimeMs?: number
|
||||||
|
onTransition?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 30px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const InitialTextContainer = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
transition: display ${({ theme }) => `${theme.transition.duration.fast} ${theme.transition.timing.inOut}`};
|
||||||
|
${slideInAnimation}
|
||||||
|
&.${AnimationType.EXITING} {
|
||||||
|
${slideOutAnimation}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const TransitionTextContainer = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
transition: display ${({ theme }) => `${theme.transition.duration.fast} ${theme.transition.timing.inOut}`};
|
||||||
|
${slideInAnimation}
|
||||||
|
&.${AnimationType.EXITING} {
|
||||||
|
${slideOutAnimation}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export function TransitionText({
|
||||||
|
initialText,
|
||||||
|
transitionText,
|
||||||
|
transitionTimeMs = 1500,
|
||||||
|
onTransition,
|
||||||
|
}: TransitionTextProps) {
|
||||||
|
const [transitioned, setTransitioned] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Transition from initial text to transition text.
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
if (!transitioned) {
|
||||||
|
setTransitioned(true)
|
||||||
|
onTransition?.()
|
||||||
|
}
|
||||||
|
}, transitionTimeMs)
|
||||||
|
|
||||||
|
return () => clearTimeout(timeout)
|
||||||
|
}, [onTransition, transitionTimeMs, transitioned])
|
||||||
|
|
||||||
|
const initialTextRef = useRef<HTMLDivElement>(null)
|
||||||
|
useUnmountingAnimation(initialTextRef, () => AnimationType.EXITING)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
{!transitioned && (
|
||||||
|
<InitialTextContainer ref={initialTextRef}>
|
||||||
|
<Trans>{initialText}</Trans>
|
||||||
|
</InitialTextContainer>
|
||||||
|
)}
|
||||||
|
{transitioned && (
|
||||||
|
<TransitionTextContainer>
|
||||||
|
<Trans>{transitionText}</Trans>
|
||||||
|
</TransitionTextContainer>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
16
src/components/swap/PendingModalContent/animations.ts
Normal file
16
src/components/swap/PendingModalContent/animations.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { css, keyframes } from 'styled-components'
|
||||||
|
|
||||||
|
const slideIn = keyframes`
|
||||||
|
from { opacity: 0; transform: translateX(40px) }
|
||||||
|
to { opacity: 1; transform: translateX(0px) }
|
||||||
|
`
|
||||||
|
export const slideInAnimation = css`
|
||||||
|
animation: ${slideIn} ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.inOut}`};
|
||||||
|
`
|
||||||
|
const slideOut = keyframes`
|
||||||
|
from { opacity: 1; transform: translateX(0px) }
|
||||||
|
to { opacity: 0; transform: translateX(-40px) }
|
||||||
|
`
|
||||||
|
export const slideOutAnimation = css`
|
||||||
|
animation: ${slideOut} ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.inOut}`};
|
||||||
|
`
|
@ -14,13 +14,15 @@ import { InterfaceTrade, TradeFillType } from 'state/routing/types'
|
|||||||
import { useOrder } from 'state/signatures/hooks'
|
import { useOrder } from 'state/signatures/hooks'
|
||||||
import { UniswapXOrderDetails } from 'state/signatures/types'
|
import { UniswapXOrderDetails } from 'state/signatures/types'
|
||||||
import { useIsTransactionConfirmed, useSwapTransactionStatus } from 'state/transactions/hooks'
|
import { useIsTransactionConfirmed, useSwapTransactionStatus } from 'state/transactions/hooks'
|
||||||
import styled, { css, keyframes } from 'styled-components'
|
import styled, { css } from 'styled-components'
|
||||||
import { ExternalLink } from 'theme/components'
|
import { ExternalLink } from 'theme/components'
|
||||||
import { ThemedText } from 'theme/components/text'
|
import { ThemedText } from 'theme/components/text'
|
||||||
|
import { SignatureExpiredError } from 'utils/errors'
|
||||||
import { getExplorerLink } from 'utils/getExplorerLink'
|
import { getExplorerLink } from 'utils/getExplorerLink'
|
||||||
import { ExplorerDataType } from 'utils/getExplorerLink'
|
import { ExplorerDataType } from 'utils/getExplorerLink'
|
||||||
|
|
||||||
import { ConfirmModalState } from '../ConfirmSwapModal'
|
import { ConfirmModalState } from '../ConfirmSwapModal'
|
||||||
|
import { slideInAnimation, slideOutAnimation } from './animations'
|
||||||
import {
|
import {
|
||||||
AnimatedEntranceConfirmationIcon,
|
AnimatedEntranceConfirmationIcon,
|
||||||
AnimatedEntranceSubmittedIcon,
|
AnimatedEntranceSubmittedIcon,
|
||||||
@ -31,6 +33,7 @@ import {
|
|||||||
PaperIcon,
|
PaperIcon,
|
||||||
} from './Logos'
|
} from './Logos'
|
||||||
import { TradeSummary } from './TradeSummary'
|
import { TradeSummary } from './TradeSummary'
|
||||||
|
import { TransitionText } from './TransitionText'
|
||||||
|
|
||||||
export const PendingModalContainer = styled(ColumnCenter)`
|
export const PendingModalContainer = styled(ColumnCenter)`
|
||||||
margin: 48px 0 8px;
|
margin: 48px 0 8px;
|
||||||
@ -51,21 +54,6 @@ const StepCircle = styled.div<{ active: boolean }>`
|
|||||||
transition: background-color ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.inOut}`};
|
transition: background-color ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.inOut}`};
|
||||||
`
|
`
|
||||||
|
|
||||||
const slideIn = keyframes`
|
|
||||||
from { opacity: 0; transform: translateX(40px) }
|
|
||||||
to { opacity: 1; transform: translateX(0px) }
|
|
||||||
`
|
|
||||||
const slideInAnimation = css`
|
|
||||||
animation: ${slideIn} ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.inOut}`};
|
|
||||||
`
|
|
||||||
const slideOut = keyframes`
|
|
||||||
from { opacity: 1; transform: translateX(0px) }
|
|
||||||
to { opacity: 0; transform: translateX(-40px) }
|
|
||||||
`
|
|
||||||
const slideOutAnimation = css`
|
|
||||||
animation: ${slideOut} ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.inOut}`};
|
|
||||||
`
|
|
||||||
|
|
||||||
const AnimationWrapper = styled.div`
|
const AnimationWrapper = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -77,7 +65,10 @@ const AnimationWrapper = styled.div`
|
|||||||
const StepTitleAnimationContainer = styled(Column)<{ disableEntranceAnimation?: boolean }>`
|
const StepTitleAnimationContainer = styled(Column)<{ disableEntranceAnimation?: boolean }>`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
transition: display ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.inOut}`};
|
transition: display ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.inOut}`};
|
||||||
${({ disableEntranceAnimation }) =>
|
${({ disableEntranceAnimation }) =>
|
||||||
!disableEntranceAnimation &&
|
!disableEntranceAnimation &&
|
||||||
@ -117,6 +108,8 @@ interface PendingModalContentProps {
|
|||||||
hideStepIndicators?: boolean
|
hideStepIndicators?: boolean
|
||||||
tokenApprovalPending?: boolean
|
tokenApprovalPending?: boolean
|
||||||
revocationPending?: boolean
|
revocationPending?: boolean
|
||||||
|
swapError?: Error | string
|
||||||
|
onRetryUniswapXSignature?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ContentArgs {
|
interface ContentArgs {
|
||||||
@ -130,6 +123,8 @@ interface ContentArgs {
|
|||||||
swapResult?: SwapResult
|
swapResult?: SwapResult
|
||||||
chainId?: number
|
chainId?: number
|
||||||
order?: UniswapXOrderDetails
|
order?: UniswapXOrderDetails
|
||||||
|
swapError?: Error | string
|
||||||
|
onRetryUniswapXSignature?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPendingConfirmationContent({
|
function getPendingConfirmationContent({
|
||||||
@ -138,7 +133,12 @@ function getPendingConfirmationContent({
|
|||||||
trade,
|
trade,
|
||||||
chainId,
|
chainId,
|
||||||
swapResult,
|
swapResult,
|
||||||
}: Pick<ContentArgs, 'swapConfirmed' | 'swapPending' | 'trade' | 'chainId' | 'swapResult'>): PendingModalStep {
|
swapError,
|
||||||
|
onRetryUniswapXSignature,
|
||||||
|
}: Pick<
|
||||||
|
ContentArgs,
|
||||||
|
'swapConfirmed' | 'swapPending' | 'trade' | 'chainId' | 'swapResult' | 'swapError' | 'onRetryUniswapXSignature'
|
||||||
|
>): PendingModalStep {
|
||||||
const title = swapPending ? t`Swap submitted` : swapConfirmed ? t`Swap success!` : t`Confirm Swap`
|
const title = swapPending ? t`Swap submitted` : swapConfirmed ? t`Swap success!` : t`Confirm Swap`
|
||||||
const tradeSummary = trade ? <TradeSummary trade={trade} /> : null
|
const tradeSummary = trade ? <TradeSummary trade={trade} /> : null
|
||||||
if (swapPending && trade?.fillType === TradeFillType.UniswapX) {
|
if (swapPending && trade?.fillType === TradeFillType.UniswapX) {
|
||||||
@ -174,6 +174,19 @@ function getPendingConfirmationContent({
|
|||||||
bottomLabel: null,
|
bottomLabel: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (swapError instanceof SignatureExpiredError) {
|
||||||
|
return {
|
||||||
|
title: (
|
||||||
|
<TransitionText
|
||||||
|
key={swapError.id}
|
||||||
|
initialText={<Trans>Time expired</Trans>}
|
||||||
|
transitionText={<Trans>Retry confirmation</Trans>}
|
||||||
|
onTransition={onRetryUniswapXSignature}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
subtitle: tradeSummary,
|
||||||
|
bottomLabel: t`Proceed in your wallet`,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
title,
|
title,
|
||||||
@ -194,6 +207,8 @@ function useStepContents(args: ContentArgs): Record<PendingConfirmModalState, Pe
|
|||||||
trade,
|
trade,
|
||||||
swapResult,
|
swapResult,
|
||||||
chainId,
|
chainId,
|
||||||
|
swapError,
|
||||||
|
onRetryUniswapXSignature,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
@ -236,6 +251,8 @@ function useStepContents(args: ContentArgs): Record<PendingConfirmModalState, Pe
|
|||||||
swapPending,
|
swapPending,
|
||||||
swapResult,
|
swapResult,
|
||||||
trade,
|
trade,
|
||||||
|
swapError,
|
||||||
|
onRetryUniswapXSignature,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
@ -248,6 +265,8 @@ function useStepContents(args: ContentArgs): Record<PendingConfirmModalState, Pe
|
|||||||
tokenApprovalPending,
|
tokenApprovalPending,
|
||||||
trade,
|
trade,
|
||||||
wrapPending,
|
wrapPending,
|
||||||
|
swapError,
|
||||||
|
onRetryUniswapXSignature,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -261,6 +280,8 @@ export function PendingModalContent({
|
|||||||
hideStepIndicators,
|
hideStepIndicators,
|
||||||
tokenApprovalPending = false,
|
tokenApprovalPending = false,
|
||||||
revocationPending = false,
|
revocationPending = false,
|
||||||
|
swapError,
|
||||||
|
onRetryUniswapXSignature,
|
||||||
}: PendingModalContentProps) {
|
}: PendingModalContentProps) {
|
||||||
const { chainId } = useWeb3React()
|
const { chainId } = useWeb3React()
|
||||||
|
|
||||||
@ -283,6 +304,8 @@ export function PendingModalContent({
|
|||||||
swapResult,
|
swapResult,
|
||||||
trade,
|
trade,
|
||||||
chainId,
|
chainId,
|
||||||
|
swapError,
|
||||||
|
onRetryUniswapXSignature,
|
||||||
})
|
})
|
||||||
|
|
||||||
const currentStepContainerRef = useRef<HTMLDivElement>(null)
|
const currentStepContainerRef = useRef<HTMLDivElement>(null)
|
||||||
@ -341,7 +364,7 @@ export function PendingModalContent({
|
|||||||
key={step}
|
key={step}
|
||||||
ref={step === currentStep ? currentStepContainerRef : undefined}
|
ref={step === currentStep ? currentStepContainerRef : undefined}
|
||||||
>
|
>
|
||||||
<ThemedText.SubHeaderLarge textAlign="center" data-testid="pending-modal-content-title">
|
<ThemedText.SubHeaderLarge width="100%" textAlign="center" data-testid="pending-modal-content-title">
|
||||||
{stepContents[step].title}
|
{stepContents[step].title}
|
||||||
</ThemedText.SubHeaderLarge>
|
</ThemedText.SubHeaderLarge>
|
||||||
<ThemedText.LabelSmall textAlign="center">{stepContents[step].subtitle}</ThemedText.LabelSmall>
|
<ThemedText.LabelSmall textAlign="center">{stepContents[step].subtitle}</ThemedText.LabelSmall>
|
||||||
|
@ -9,7 +9,7 @@ import { formatSwapSignedAnalyticsEventProperties } from 'lib/utils/analytics'
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { DutchOrderTrade, TradeFillType } from 'state/routing/types'
|
import { DutchOrderTrade, TradeFillType } from 'state/routing/types'
|
||||||
import { trace } from 'tracing/trace'
|
import { trace } from 'tracing/trace'
|
||||||
import { UserRejectedRequestError } from 'utils/errors'
|
import { SignatureExpiredError, UserRejectedRequestError } from 'utils/errors'
|
||||||
import { signTypedData } from 'utils/signing'
|
import { signTypedData } from 'utils/signing'
|
||||||
import { didUserReject, swapErrorToUserReadableMessage } from 'utils/swapErrorToUserReadableMessage'
|
import { didUserReject, swapErrorToUserReadableMessage } from 'utils/swapErrorToUserReadableMessage'
|
||||||
|
|
||||||
@ -90,11 +90,14 @@ export function useUniswapXSwapCallback({
|
|||||||
const { domain, types, values } = updatedOrder.permitData()
|
const { domain, types, values } = updatedOrder.permitData()
|
||||||
|
|
||||||
const signature = await signTypedData(provider.getSigner(account), domain, types, values)
|
const signature = await signTypedData(provider.getSigner(account), domain, types, values)
|
||||||
if (deadline < Math.floor(Date.now() / 1000)) {
|
if (startTime < Math.floor(Date.now() / 1000)) {
|
||||||
return signDutchOrder()
|
throw new SignatureExpiredError()
|
||||||
}
|
}
|
||||||
return { signature, updatedOrder }
|
return { signature, updatedOrder }
|
||||||
} catch (swapError) {
|
} catch (swapError) {
|
||||||
|
if (swapError instanceof SignatureExpiredError) {
|
||||||
|
throw swapError
|
||||||
|
}
|
||||||
if (didUserReject(swapError)) {
|
if (didUserReject(swapError)) {
|
||||||
setTraceStatus('cancelled')
|
setTraceStatus('cancelled')
|
||||||
throw new UserRejectedRequestError(swapErrorToUserReadableMessage(swapError))
|
throw new UserRejectedRequestError(swapErrorToUserReadableMessage(swapError))
|
||||||
|
@ -457,6 +457,14 @@ export function Swap({
|
|||||||
})
|
})
|
||||||
}, [trade])
|
}, [trade])
|
||||||
|
|
||||||
|
const clearSwapState = useCallback(() => {
|
||||||
|
setSwapState((currentState) => ({
|
||||||
|
...currentState,
|
||||||
|
swapError: undefined,
|
||||||
|
swapResult: undefined,
|
||||||
|
}))
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleSwap = useCallback(() => {
|
const handleSwap = useCallback(() => {
|
||||||
if (!swapCallback) {
|
if (!swapCallback) {
|
||||||
return
|
return
|
||||||
@ -464,11 +472,6 @@ export function Swap({
|
|||||||
if (preTaxStablecoinPriceImpact && !confirmPriceImpactWithoutFee(preTaxStablecoinPriceImpact)) {
|
if (preTaxStablecoinPriceImpact && !confirmPriceImpactWithoutFee(preTaxStablecoinPriceImpact)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setSwapState((currentState) => ({
|
|
||||||
...currentState,
|
|
||||||
swapError: undefined,
|
|
||||||
swapResult: undefined,
|
|
||||||
}))
|
|
||||||
swapCallback()
|
swapCallback()
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
setSwapState((currentState) => ({
|
setSwapState((currentState) => ({
|
||||||
@ -613,6 +616,7 @@ export function Swap({
|
|||||||
onCurrencySelection={onCurrencySelection}
|
onCurrencySelection={onCurrencySelection}
|
||||||
swapResult={swapResult}
|
swapResult={swapResult}
|
||||||
allowedSlippage={allowedSlippage}
|
allowedSlippage={allowedSlippage}
|
||||||
|
clearSwapState={clearSwapState}
|
||||||
onConfirm={handleSwap}
|
onConfirm={handleSwap}
|
||||||
allowance={allowance}
|
allowance={allowance}
|
||||||
swapError={swapError}
|
swapError={swapError}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// You may throw an instance of this class when the user rejects a request in their wallet.
|
|
||||||
|
|
||||||
import { t } from '@lingui/macro'
|
import { t } from '@lingui/macro'
|
||||||
|
import { v4 as uuid } from 'uuid'
|
||||||
|
|
||||||
|
// You may throw an instance of this class when the user rejects a request in their wallet.
|
||||||
// The benefit is that you can distinguish this error from other errors using didUserReject().
|
// The benefit is that you can distinguish this error from other errors using didUserReject().
|
||||||
export class UserRejectedRequestError extends Error {
|
export class UserRejectedRequestError extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
@ -23,3 +23,16 @@ export class WrongChainError extends Error {
|
|||||||
super(t`Your wallet is connected to the wrong network.`)
|
super(t`Your wallet is connected to the wrong network.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SignatureExpiredError extends Error {
|
||||||
|
private _id: string
|
||||||
|
constructor() {
|
||||||
|
super(t`Your signature has expired.`)
|
||||||
|
this.name = 'SignatureExpiredError'
|
||||||
|
this._id = `SignatureExpiredError-${uuid()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string {
|
||||||
|
return this._id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user