Commit c560b943 authored by eddie's avatar eddie Committed by GitHub

feat: create feature flag for swap widget (#5909)

* feat: create feature flag for swap widget

* feat: add new flag to modal

* fix: missing defaultField usage
parent 93a4f002
......@@ -3,6 +3,7 @@ import { useFiatOnrampFlag } from 'featureFlags/flags/fiatOnramp'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { PayWithAnyTokenVariant, usePayWithAnyTokenFlag } from 'featureFlags/flags/payWithAnyToken'
import { Permit2Variant, usePermit2Flag } from 'featureFlags/flags/permit2'
import { SwapWidgetVariant, useSwapWidgetFlag } from 'featureFlags/flags/swapWidget'
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
......@@ -229,6 +230,12 @@ export default function FeatureFlagModal() {
featureFlag={FeatureFlag.payWithAnyToken}
label="Pay With Any Token"
/>
<FeatureFlagOption
variant={SwapWidgetVariant}
value={useSwapWidgetFlag()}
featureFlag={FeatureFlag.swapWidget}
label="Swap Widget"
/>
<FeatureFlagGroup name="Debug">
<FeatureFlagOption
variant={TraceJsonRpcVariant}
......
import { WidgetSkeleton } from 'components/Widget'
import { WIDGET_WIDTH } from 'components/Widget'
import { DEFAULT_WIDGET_WIDTH } from 'components/Widget'
import { ArrowLeft } from 'react-feather'
import { useParams } from 'react-router-dom'
import styled, { useTheme } from 'styled-components/macro'
......@@ -44,7 +44,7 @@ export const RightPanel = styled.div`
display: none;
flex-direction: column;
gap: 20px;
width: ${WIDGET_WIDTH}px;
width: ${DEFAULT_WIDGET_WIDTH}px;
@media screen and (min-width: ${({ theme }) => theme.breakpoint.lg}px) {
display: flex;
......
......@@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro'
import { Trace } from '@uniswap/analytics'
import { InterfacePageName } from '@uniswap/analytics-events'
import { Currency } from '@uniswap/sdk-core'
import { Field } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { AboutSection } from 'components/Tokens/TokenDetails/About'
......@@ -223,6 +224,7 @@ export default function TokenDetails({
<RightPanel>
<Widget
token={token ?? undefined}
defaultField={Field.OUTPUT}
onTokenChange={navigateToWidgetSelectedToken}
onReviewSwapClick={onReviewSwapClick}
/>
......
......@@ -10,6 +10,7 @@ import { Currency, TradeType } from '@uniswap/sdk-core'
import {
AddEthereumChainParameter,
EMPTY_TOKEN_LIST,
Field,
OnReviewSwapClick,
SwapWidget,
SwapWidgetSkeleton,
......@@ -36,7 +37,7 @@ import { useSyncWidgetSettings } from './settings'
import { DARK_THEME, LIGHT_THEME } from './theme'
import { useSyncWidgetTransactions } from './transactions'
export const WIDGET_WIDTH = 360
export const DEFAULT_WIDGET_WIDTH = 360
const WIDGET_ROUTER_URL = 'https://api.uniswap.org/v1/'
......@@ -46,15 +47,23 @@ function useWidgetTheme() {
interface WidgetProps {
token?: Currency
width?: number | string
defaultField: Field
onTokenChange?: (token: Currency) => void
onReviewSwapClick?: OnReviewSwapClick
}
export default function Widget({ token, onTokenChange, onReviewSwapClick }: WidgetProps) {
export default function Widget({
token,
width = DEFAULT_WIDGET_WIDTH,
defaultField,
onTokenChange,
onReviewSwapClick,
}: WidgetProps) {
const { connector, provider } = useWeb3React()
const locale = useActiveLocale()
const theme = useWidgetTheme()
const { inputs, tokenSelector } = useSyncWidgetInputs({ token, onTokenChange })
const { inputs, tokenSelector } = useSyncWidgetInputs({ token, onTokenChange, defaultField })
const { settings } = useSyncWidgetSettings()
const { transactions } = useSyncWidgetTransactions()
......@@ -159,7 +168,7 @@ export default function Widget({ token, onTokenChange, onReviewSwapClick }: Widg
routerUrl={WIDGET_ROUTER_URL}
locale={locale}
theme={theme}
width={WIDGET_WIDTH}
width={width}
// defaultChainId is excluded - it is always inferred from the passed provider
onConnectWalletClick={onConnectWalletClick}
provider={provider}
......@@ -182,5 +191,5 @@ export default function Widget({ token, onTokenChange, onReviewSwapClick }: Widg
export function WidgetSkeleton() {
const theme = useWidgetTheme()
return <SwapWidgetSkeleton theme={theme} width={WIDGET_WIDTH} />
return <SwapWidgetSkeleton theme={theme} width={DEFAULT_WIDGET_WIDTH} />
}
......@@ -21,26 +21,29 @@ function includesDefaultToken(tokens: SwapTokens) {
*/
export function useSyncWidgetInputs({
token,
defaultField,
onTokenChange,
}: {
token?: Currency
defaultField: Field
onTokenChange?: (token: Currency) => void
}) {
const trace = useTrace({ section: InterfaceSectionName.WIDGET })
const [type, setType] = useState<SwapValue['type']>(TradeType.EXACT_INPUT)
const [amount, setAmount] = useState<SwapValue['amount']>(EMPTY_AMOUNT)
const [tokens, setTokens] = useState<SwapTokens>({ [Field.OUTPUT]: token, default: token })
const [tokens, setTokens] = useState<SwapTokens>({ [defaultField]: token, default: token })
useEffect(() => {
setTokens((tokens) => {
const update = { ...tokens, default: token }
if (!includesDefaultToken(update)) {
return { [Field.OUTPUT]: update.default, default: update.default }
return { [defaultField]: update.default, default: update.default }
}
return update
})
}, [token])
}, [defaultField, token])
const onAmountChange = useCallback(
(field: Field, amount: string, origin?: 'max') => {
......
......@@ -4,4 +4,5 @@ export enum FeatureFlag {
permit2 = 'permit2',
nftListV2 = 'nftListV2',
payWithAnyToken = 'payWithAnyToken',
swapWidget = 'swapWidget',
}
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
export function useSwapWidgetFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.swapWidget, BaseVariant.Control)
}
export function useSwapWidgetEnabled(): boolean {
return useSwapWidgetFlag() === BaseVariant.Enabled
}
export { BaseVariant as SwapWidgetVariant }
......@@ -19,8 +19,10 @@ import SwapDetailsDropdown from 'components/swap/SwapDetailsDropdown'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import { MouseoverTooltip } from 'components/Tooltip'
import Widget from 'components/Widget'
import { isSupportedChain } from 'constants/chains'
import { usePermit2Enabled } from 'featureFlags/flags/permit2'
import { useSwapWidgetEnabled } from 'featureFlags/flags/swapWidget'
import usePermit2Allowance, { AllowanceState } from 'hooks/usePermit2Allowance'
import { useSwapCallback } from 'hooks/useSwapCallback'
import useTransactionDeadline from 'hooks/useTransactionDeadline'
......@@ -157,6 +159,7 @@ export default function Swap({ className }: { className?: string }) {
const loadedUrlParams = useDefaultsFromURLSearch()
const [newSwapQuoteNeedsLogging, setNewSwapQuoteNeedsLogging] = useState(true)
const [fetchingSwapQuoteStartTime, setFetchingSwapQuoteStartTime] = useState<Date | undefined>()
const swapWidgetEnabled = useSwapWidgetEnabled()
// token warning stuff
const [loadedInputCurrency, loadedOutputCurrency] = [
......@@ -559,319 +562,329 @@ export default function Swap({ className }: { className?: string }) {
showCancel={true}
/>
<PageWrapper>
<SwapWrapper className={className} id="swap-page">
<SwapHeader allowedSlippage={allowedSlippage} />
<ConfirmSwapModal
isOpen={showConfirm}
trade={trade}
originalTrade={tradeToConfirm}
onAcceptChanges={handleAcceptChanges}
attemptingTxn={attemptingTxn}
txHash={txHash}
recipient={recipient}
allowedSlippage={allowedSlippage}
onConfirm={handleSwap}
swapErrorMessage={swapErrorMessage}
onDismiss={handleConfirmDismiss}
swapQuoteReceivedDate={swapQuoteReceivedDate}
fiatValueInput={fiatValueTradeInput}
fiatValueOutput={fiatValueTradeOutput}
/>
<div style={{ display: 'relative' }}>
<SwapSection>
<Trace section={InterfaceSectionName.CURRENCY_INPUT_PANEL}>
<SwapCurrencyInputPanel
label={
independentField === Field.OUTPUT && !showWrap ? (
<Trans>From (at most)</Trans>
) : (
<Trans>From</Trans>
)
}
value={formattedAmounts[Field.INPUT]}
showMaxButton={showMaxButton}
currency={currencies[Field.INPUT] ?? null}
onUserInput={handleTypeInput}
onMax={handleMaxInput}
fiatValue={fiatValueInput ?? undefined}
onCurrencySelect={handleInputSelect}
otherCurrency={currencies[Field.OUTPUT]}
showCommonBases={true}
id={InterfaceSectionName.CURRENCY_INPUT_PANEL}
loading={independentField === Field.OUTPUT && routeIsSyncing}
/>
</Trace>
</SwapSection>
<ArrowWrapper clickable={isSupportedChain(chainId)}>
<TraceEvent
events={[BrowserEvent.onClick]}
name={SwapEventName.SWAP_TOKENS_REVERSED}
element={InterfaceElementName.SWAP_TOKENS_REVERSE_ARROW_BUTTON}
>
<ArrowContainer
onClick={() => {
setApprovalSubmitted(false) // reset 2 step UI for approvals
onSwitchTokens()
}}
color={theme.textPrimary}
>
<ArrowDown
size="16"
color={
currencies[Field.INPUT] && currencies[Field.OUTPUT] ? theme.textPrimary : theme.textTertiary
}
/>
</ArrowContainer>
</TraceEvent>
</ArrowWrapper>
</div>
<AutoColumn gap="md">
<div>
<OutputSwapSection showDetailsDropdown={showDetailsDropdown}>
<Trace section={InterfaceSectionName.CURRENCY_OUTPUT_PANEL}>
{swapWidgetEnabled ? (
<Widget token={loadedInputCurrency ?? undefined} width="100%" defaultField={Field.INPUT} />
) : (
<SwapWrapper className={className} id="swap-page">
<SwapHeader allowedSlippage={allowedSlippage} />
<ConfirmSwapModal
isOpen={showConfirm}
trade={trade}
originalTrade={tradeToConfirm}
onAcceptChanges={handleAcceptChanges}
attemptingTxn={attemptingTxn}
txHash={txHash}
recipient={recipient}
allowedSlippage={allowedSlippage}
onConfirm={handleSwap}
swapErrorMessage={swapErrorMessage}
onDismiss={handleConfirmDismiss}
swapQuoteReceivedDate={swapQuoteReceivedDate}
fiatValueInput={fiatValueTradeInput}
fiatValueOutput={fiatValueTradeOutput}
/>
<div style={{ display: 'relative' }}>
<SwapSection>
<Trace section={InterfaceSectionName.CURRENCY_INPUT_PANEL}>
<SwapCurrencyInputPanel
value={formattedAmounts[Field.OUTPUT]}
onUserInput={handleTypeOutput}
label={
independentField === Field.INPUT && !showWrap ? <Trans>To (at least)</Trans> : <Trans>To</Trans>
independentField === Field.OUTPUT && !showWrap ? (
<Trans>From (at most)</Trans>
) : (
<Trans>From</Trans>
)
}
showMaxButton={false}
hideBalance={false}
fiatValue={fiatValueOutput ?? undefined}
priceImpact={stablecoinPriceImpact}
currency={currencies[Field.OUTPUT] ?? null}
onCurrencySelect={handleOutputSelect}
otherCurrency={currencies[Field.INPUT]}
value={formattedAmounts[Field.INPUT]}
showMaxButton={showMaxButton}
currency={currencies[Field.INPUT] ?? null}
onUserInput={handleTypeInput}
onMax={handleMaxInput}
fiatValue={fiatValueInput ?? undefined}
onCurrencySelect={handleInputSelect}
otherCurrency={currencies[Field.OUTPUT]}
showCommonBases={true}
id={InterfaceSectionName.CURRENCY_OUTPUT_PANEL}
loading={independentField === Field.INPUT && routeIsSyncing}
id={InterfaceSectionName.CURRENCY_INPUT_PANEL}
loading={independentField === Field.OUTPUT && routeIsSyncing}
/>
</Trace>
{recipient !== null && !showWrap ? (
<>
<AutoRow justify="space-between" style={{ padding: '0 1rem' }}>
<ArrowWrapper clickable={false}>
<ArrowDown size="16" color={theme.textSecondary} />
</ArrowWrapper>
<LinkStyledButton id="remove-recipient-button" onClick={() => onChangeRecipient(null)}>
<Trans>- Remove recipient</Trans>
</LinkStyledButton>
</AutoRow>
<AddressInputPanel id="recipient" value={recipient} onChange={onChangeRecipient} />
</>
) : null}
</OutputSwapSection>
{showDetailsDropdown && (
<DetailsSwapSection>
<SwapDetailsDropdown
trade={trade}
syncing={routeIsSyncing}
loading={routeIsLoading}
allowedSlippage={allowedSlippage}
/>
</DetailsSwapSection>
)}
</div>
{showPriceImpactWarning && <PriceImpactWarning priceImpact={largerPriceImpact} />}
<div>
{swapIsUnsupported ? (
<ButtonPrimary disabled={true}>
<ThemedText.DeprecatedMain mb="4px">
<Trans>Unsupported Asset</Trans>
</ThemedText.DeprecatedMain>
</ButtonPrimary>
) : !account ? (
</SwapSection>
<ArrowWrapper clickable={isSupportedChain(chainId)}>
<TraceEvent
events={[BrowserEvent.onClick]}
name={InterfaceEventName.CONNECT_WALLET_BUTTON_CLICKED}
properties={{ received_swap_quote: getIsValidSwapQuote(trade, tradeState, swapInputError) }}
element={InterfaceElementName.CONNECT_WALLET_BUTTON}
name={SwapEventName.SWAP_TOKENS_REVERSED}
element={InterfaceElementName.SWAP_TOKENS_REVERSE_ARROW_BUTTON}
>
<ButtonLight onClick={toggleWalletModal} fontWeight={600}>
<Trans>Connect Wallet</Trans>
</ButtonLight>
</TraceEvent>
) : showWrap ? (
<ButtonPrimary disabled={Boolean(wrapInputError)} onClick={onWrap} fontWeight={600}>
{wrapInputError ? (
<WrapErrorText wrapInputError={wrapInputError} />
) : wrapType === WrapType.WRAP ? (
<Trans>Wrap</Trans>
) : wrapType === WrapType.UNWRAP ? (
<Trans>Unwrap</Trans>
) : null}
</ButtonPrimary>
) : routeNotFound && userHasSpecifiedInputOutput && !routeIsLoading && !routeIsSyncing ? (
<GrayCard style={{ textAlign: 'center' }}>
<ThemedText.DeprecatedMain mb="4px">
<Trans>Insufficient liquidity for this trade.</Trans>
</ThemedText.DeprecatedMain>
</GrayCard>
) : showApproveFlow ? (
<AutoRow style={{ flexWrap: 'nowrap', width: '100%' }}>
<AutoColumn style={{ width: '100%' }} gap="12px">
<ButtonConfirmed
fontWeight={600}
onClick={handleApprove}
disabled={approveTokenButtonDisabled}
width="100%"
altDisabledStyle={approvalState === ApprovalState.PENDING} // show solid button while waiting
confirmed={
approvalState === ApprovalState.APPROVED || signatureState === UseERC20PermitState.SIGNED
<ArrowContainer
onClick={() => {
setApprovalSubmitted(false) // reset 2 step UI for approvals
onSwitchTokens()
}}
color={theme.textPrimary}
>
<ArrowDown
size="16"
color={
currencies[Field.INPUT] && currencies[Field.OUTPUT] ? theme.textPrimary : theme.textTertiary
}
>
<AutoRow justify="space-between" style={{ flexWrap: 'nowrap' }} height="20px">
{/* we need to shorten this string on mobile */}
{approvalState === ApprovalState.APPROVED || signatureState === UseERC20PermitState.SIGNED ? (
<ThemedText.SubHeader width="100%" textAlign="center" color="textSecondary">
<Trans>You can now trade {currencies[Field.INPUT]?.symbol}</Trans>
</ThemedText.SubHeader>
/>
</ArrowContainer>
</TraceEvent>
</ArrowWrapper>
</div>
<AutoColumn gap="md">
<div>
<OutputSwapSection showDetailsDropdown={showDetailsDropdown}>
<Trace section={InterfaceSectionName.CURRENCY_OUTPUT_PANEL}>
<SwapCurrencyInputPanel
value={formattedAmounts[Field.OUTPUT]}
onUserInput={handleTypeOutput}
label={
independentField === Field.INPUT && !showWrap ? (
<Trans>To (at least)</Trans>
) : (
<ThemedText.SubHeader width="100%" textAlign="center" color="white">
<Trans>Allow the Uniswap Protocol to use your {currencies[Field.INPUT]?.symbol}</Trans>
</ThemedText.SubHeader>
)}
{approvalPending || approvalState === ApprovalState.PENDING ? (
<Loader stroke={theme.white} />
) : (approvalSubmitted && approvalState === ApprovalState.APPROVED) ||
<Trans>To</Trans>
)
}
showMaxButton={false}
hideBalance={false}
fiatValue={fiatValueOutput ?? undefined}
priceImpact={stablecoinPriceImpact}
currency={currencies[Field.OUTPUT] ?? null}
onCurrencySelect={handleOutputSelect}
otherCurrency={currencies[Field.INPUT]}
showCommonBases={true}
id={InterfaceSectionName.CURRENCY_OUTPUT_PANEL}
loading={independentField === Field.INPUT && routeIsSyncing}
/>
</Trace>
{recipient !== null && !showWrap ? (
<>
<AutoRow justify="space-between" style={{ padding: '0 1rem' }}>
<ArrowWrapper clickable={false}>
<ArrowDown size="16" color={theme.textSecondary} />
</ArrowWrapper>
<LinkStyledButton id="remove-recipient-button" onClick={() => onChangeRecipient(null)}>
<Trans>- Remove recipient</Trans>
</LinkStyledButton>
</AutoRow>
<AddressInputPanel id="recipient" value={recipient} onChange={onChangeRecipient} />
</>
) : null}
</OutputSwapSection>
{showDetailsDropdown && (
<DetailsSwapSection>
<SwapDetailsDropdown
trade={trade}
syncing={routeIsSyncing}
loading={routeIsLoading}
allowedSlippage={allowedSlippage}
/>
</DetailsSwapSection>
)}
</div>
{showPriceImpactWarning && <PriceImpactWarning priceImpact={largerPriceImpact} />}
<div>
{swapIsUnsupported ? (
<ButtonPrimary disabled={true}>
<ThemedText.DeprecatedMain mb="4px">
<Trans>Unsupported Asset</Trans>
</ThemedText.DeprecatedMain>
</ButtonPrimary>
) : !account ? (
<TraceEvent
events={[BrowserEvent.onClick]}
name={InterfaceEventName.CONNECT_WALLET_BUTTON_CLICKED}
properties={{ received_swap_quote: getIsValidSwapQuote(trade, tradeState, swapInputError) }}
element={InterfaceElementName.CONNECT_WALLET_BUTTON}
>
<ButtonLight onClick={toggleWalletModal} fontWeight={600}>
<Trans>Connect Wallet</Trans>
</ButtonLight>
</TraceEvent>
) : showWrap ? (
<ButtonPrimary disabled={Boolean(wrapInputError)} onClick={onWrap} fontWeight={600}>
{wrapInputError ? (
<WrapErrorText wrapInputError={wrapInputError} />
) : wrapType === WrapType.WRAP ? (
<Trans>Wrap</Trans>
) : wrapType === WrapType.UNWRAP ? (
<Trans>Unwrap</Trans>
) : null}
</ButtonPrimary>
) : routeNotFound && userHasSpecifiedInputOutput && !routeIsLoading && !routeIsSyncing ? (
<GrayCard style={{ textAlign: 'center' }}>
<ThemedText.DeprecatedMain mb="4px">
<Trans>Insufficient liquidity for this trade.</Trans>
</ThemedText.DeprecatedMain>
</GrayCard>
) : showApproveFlow ? (
<AutoRow style={{ flexWrap: 'nowrap', width: '100%' }}>
<AutoColumn style={{ width: '100%' }} gap="12px">
<ButtonConfirmed
fontWeight={600}
onClick={handleApprove}
disabled={approveTokenButtonDisabled}
width="100%"
altDisabledStyle={approvalState === ApprovalState.PENDING} // show solid button while waiting
confirmed={
approvalState === ApprovalState.APPROVED || signatureState === UseERC20PermitState.SIGNED
}
>
<AutoRow justify="space-between" style={{ flexWrap: 'nowrap' }} height="20px">
{/* we need to shorten this string on mobile */}
{approvalState === ApprovalState.APPROVED ||
signatureState === UseERC20PermitState.SIGNED ? (
<CheckCircle size="20" color={theme.accentSuccess} />
) : (
<ThemedText.SubHeader width="100%" textAlign="center" color="textSecondary">
<Trans>You can now trade {currencies[Field.INPUT]?.symbol}</Trans>
</ThemedText.SubHeader>
) : (
<ThemedText.SubHeader width="100%" textAlign="center" color="white">
<Trans>Allow the Uniswap Protocol to use your {currencies[Field.INPUT]?.symbol}</Trans>
</ThemedText.SubHeader>
)}
{approvalPending || approvalState === ApprovalState.PENDING ? (
<Loader stroke={theme.white} />
) : (approvalSubmitted && approvalState === ApprovalState.APPROVED) ||
signatureState === UseERC20PermitState.SIGNED ? (
<CheckCircle size="20" color={theme.accentSuccess} />
) : (
<MouseoverTooltip
text={
<Trans>
You must give the Uniswap smart contracts permission to use your{' '}
{currencies[Field.INPUT]?.symbol}. You only have to do this once per token.
</Trans>
}
>
<HelpCircle size="20" color={theme.white} style={{ marginLeft: '8px' }} />
</MouseoverTooltip>
)}
</AutoRow>
</ButtonConfirmed>
<ButtonError
onClick={() => {
if (isExpertMode) {
handleSwap()
} else {
setSwapState({
tradeToConfirm: trade,
attemptingTxn: false,
swapErrorMessage: undefined,
showConfirm: true,
txHash: undefined,
})
}
}}
width="100%"
id="swap-button"
disabled={
!isValid ||
routeIsSyncing ||
routeIsLoading ||
(approvalState !== ApprovalState.APPROVED &&
signatureState !== UseERC20PermitState.SIGNED) ||
priceImpactTooHigh
}
error={isValid && priceImpactSeverity > 2}
>
<Text fontSize={16} fontWeight={600}>
{priceImpactTooHigh ? (
<Trans>High Price Impact</Trans>
) : trade && priceImpactSeverity > 2 ? (
<Trans>Swap Anyway</Trans>
) : (
<Trans>Swap</Trans>
)}
</Text>
</ButtonError>
</AutoColumn>
</AutoRow>
) : isValid && allowance.state === AllowanceState.REQUIRED ? (
<ButtonPrimary
onClick={updateAllowance}
disabled={isAllowancePending || isApprovalLoading}
style={{ gap: 14 }}
>
{isAllowancePending ? (
<>
<Loader size="20px" />
<Trans>Approve in your wallet</Trans>
</>
) : isApprovalLoading ? (
<>
<Loader size="20px" />
<Trans>Approval pending</Trans>
</>
) : (
<>
<div style={{ height: 20 }}>
<MouseoverTooltip
text={
<Trans>
You must give the Uniswap smart contracts permission to use your{' '}
{currencies[Field.INPUT]?.symbol}. You only have to do this once per token.
Permission is required for Uniswap to swap each token. This will expire after one
month for your security.
</Trans>
}
>
<HelpCircle size="20" color={theme.white} style={{ marginLeft: '8px' }} />
<Info size={20} />
</MouseoverTooltip>
)}
</AutoRow>
</ButtonConfirmed>
<ButtonError
onClick={() => {
if (isExpertMode) {
handleSwap()
} else {
setSwapState({
tradeToConfirm: trade,
attemptingTxn: false,
swapErrorMessage: undefined,
showConfirm: true,
txHash: undefined,
})
}
}}
width="100%"
id="swap-button"
disabled={
!isValid ||
routeIsSyncing ||
routeIsLoading ||
(approvalState !== ApprovalState.APPROVED && signatureState !== UseERC20PermitState.SIGNED) ||
priceImpactTooHigh
</div>
<Trans>Approve use of {currencies[Field.INPUT]?.symbol}</Trans>
</>
)}
</ButtonPrimary>
) : (
<ButtonError
onClick={() => {
if (isExpertMode) {
handleSwap()
} else {
setSwapState({
tradeToConfirm: trade,
attemptingTxn: false,
swapErrorMessage: undefined,
showConfirm: true,
txHash: undefined,
})
}
error={isValid && priceImpactSeverity > 2}
>
<Text fontSize={16} fontWeight={600}>
{priceImpactTooHigh ? (
<Trans>High Price Impact</Trans>
) : trade && priceImpactSeverity > 2 ? (
<Trans>Swap Anyway</Trans>
) : (
<Trans>Swap</Trans>
)}
</Text>
</ButtonError>
</AutoColumn>
</AutoRow>
) : isValid && allowance.state === AllowanceState.REQUIRED ? (
<ButtonPrimary
onClick={updateAllowance}
disabled={isAllowancePending || isApprovalLoading}
style={{ gap: 14 }}
>
{isAllowancePending ? (
<>
<Loader size="20px" />
<Trans>Approve in your wallet</Trans>
</>
) : isApprovalLoading ? (
<>
<Loader size="20px" />
<Trans>Approval pending</Trans>
</>
) : (
<>
<div style={{ height: 20 }}>
<MouseoverTooltip
text={
<Trans>
Permission is required for Uniswap to swap each token. This will expire after one month
for your security.
</Trans>
}
>
<Info size={20} />
</MouseoverTooltip>
</div>
<Trans>Approve use of {currencies[Field.INPUT]?.symbol}</Trans>
</>
)}
</ButtonPrimary>
) : (
<ButtonError
onClick={() => {
if (isExpertMode) {
handleSwap()
} else {
setSwapState({
tradeToConfirm: trade,
attemptingTxn: false,
swapErrorMessage: undefined,
showConfirm: true,
txHash: undefined,
})
}}
id="swap-button"
disabled={
!isValid ||
routeIsSyncing ||
routeIsLoading ||
priceImpactTooHigh ||
(permit2Enabled ? allowance.state !== AllowanceState.ALLOWED : Boolean(swapCallbackError))
}
}}
id="swap-button"
disabled={
!isValid ||
routeIsSyncing ||
routeIsLoading ||
priceImpactTooHigh ||
(permit2Enabled ? allowance.state !== AllowanceState.ALLOWED : Boolean(swapCallbackError))
}
error={
isValid &&
priceImpactSeverity > 2 &&
(permit2Enabled ? allowance.state === AllowanceState.ALLOWED : !swapCallbackError)
}
>
<Text fontSize={20} fontWeight={600}>
{swapInputError ? (
swapInputError
) : routeIsSyncing || routeIsLoading ? (
<Trans>Swap</Trans>
) : priceImpactTooHigh ? (
<Trans>Price Impact Too High</Trans>
) : priceImpactSeverity > 2 ? (
<Trans>Swap Anyway</Trans>
) : (
<Trans>Swap</Trans>
)}
</Text>
</ButtonError>
)}
{isExpertMode && swapErrorMessage ? <SwapCallbackError error={swapErrorMessage} /> : null}
</div>
</AutoColumn>
</SwapWrapper>
error={
isValid &&
priceImpactSeverity > 2 &&
(permit2Enabled ? allowance.state === AllowanceState.ALLOWED : !swapCallbackError)
}
>
<Text fontSize={20} fontWeight={600}>
{swapInputError ? (
swapInputError
) : routeIsSyncing || routeIsLoading ? (
<Trans>Swap</Trans>
) : priceImpactTooHigh ? (
<Trans>Price Impact Too High</Trans>
) : priceImpactSeverity > 2 ? (
<Trans>Swap Anyway</Trans>
) : (
<Trans>Swap</Trans>
)}
</Text>
</ButtonError>
)}
{isExpertMode && swapErrorMessage ? <SwapCallbackError error={swapErrorMessage} /> : null}
</div>
</AutoColumn>
</SwapWrapper>
)}
<NetworkAlert />
</PageWrapper>
<SwitchLocaleLink />
......
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