From 5ded55e0613e585dfebb9e2f7e74bc78fbe6a500 Mon Sep 17 00:00:00 2001
From: eddie <66155195+just-toby@users.noreply.github.com>
Date: Thu, 16 Nov 2023 13:59:36 -0800
Subject: [PATCH] feat: x rollout cleanup (#7582)
* feat: cleanup post x rollout
* feat: remove feature flag
* fix: remove more unused styled components
* fix: delete deprecated value from redux store
* fix: lint
* fix: remove userOptedOutOfUniswapX
* fix: migrate verion in edge case, add test
---
cypress/e2e/swap/fees.test.ts | 5 +-
cypress/e2e/swap/uniswapx.test.ts | 17 +-
.../FeatureFlagModal/FeatureFlagModal.tsx | 7 -
src/components/Popover/index.tsx | 2 +-
.../RouterPreferenceSettings/index.tsx | 27 +--
src/components/swap/styled.tsx | 104 ---------
src/featureFlags/flags/uniswapXDefault.ts | 9 -
src/featureFlags/index.tsx | 1 -
.../hooks/routing/useRoutingAPIArguments.ts | 22 +-
src/pages/App.tsx | 29 +--
src/pages/Swap/UniswapXOptIn.tsx | 207 ------------------
src/pages/Swap/index.tsx | 7 -
src/state/migrations.test.ts | 2 +-
src/state/migrations.ts | 2 +
src/state/migrations/5.test.ts | 98 +++++++++
src/state/migrations/5.ts | 43 ++++
src/state/reducer.ts | 2 +-
src/state/reducerTypeTest.ts | 2 -
src/state/routing/slice.ts | 6 +-
src/state/routing/types.ts | 9 -
src/state/routing/useRoutingAPITrade.test.ts | 10 +-
src/state/routing/utils.ts | 17 +-
src/state/swap/hooks.tsx | 2 +-
src/state/user/hooks.test.tsx | 17 +-
src/state/user/hooks.tsx | 8 -
src/state/user/reducer.ts | 12 -
26 files changed, 172 insertions(+), 495 deletions(-)
delete mode 100644 src/featureFlags/flags/uniswapXDefault.ts
delete mode 100644 src/pages/Swap/UniswapXOptIn.tsx
create mode 100644 src/state/migrations/5.test.ts
create mode 100644 src/state/migrations/5.ts
diff --git a/cypress/e2e/swap/fees.test.ts b/cypress/e2e/swap/fees.test.ts
index aaae86b2c8..af811f81e6 100644
--- a/cypress/e2e/swap/fees.test.ts
+++ b/cypress/e2e/swap/fees.test.ts
@@ -120,10 +120,7 @@ describe('Swap with fees', () => {
describe('UniswapX swaps', () => {
it('displays UniswapX fee in UI', () => {
cy.visit('/swap', {
- featureFlags: [
- { name: FeatureFlag.feesEnabled, value: true },
- { name: FeatureFlag.uniswapXDefaultEnabled, value: true },
- ],
+ featureFlags: [{ name: FeatureFlag.feesEnabled, value: true }],
})
// Intercept the trade quote
diff --git a/cypress/e2e/swap/uniswapx.test.ts b/cypress/e2e/swap/uniswapx.test.ts
index c282568c2e..ec77ca0c36 100644
--- a/cypress/e2e/swap/uniswapx.test.ts
+++ b/cypress/e2e/swap/uniswapx.test.ts
@@ -1,6 +1,5 @@
import { ChainId, CurrencyAmount } from '@uniswap/sdk-core'
import { CyHttpMessages } from 'cypress/types/net-stubbing'
-import { FeatureFlag } from 'featureFlags'
import { DAI, nativeOnChain, USDC_MAINNET } from '../../../src/constants/tokens'
import { getTestSelector } from '../../utils'
@@ -45,9 +44,7 @@ function stubSwapTxReceipt() {
describe('UniswapX Toggle', () => {
beforeEach(() => {
stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter)
- cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`, {
- featureFlags: [{ name: FeatureFlag.uniswapXDefaultEnabled, value: true }],
- })
+ cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
})
it('displays uniswapx ui when setting is on', () => {
@@ -69,9 +66,7 @@ describe('UniswapX Orders', () => {
stubSwapTxReceipt()
cy.hardhat().then((hardhat) => hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(USDC_MAINNET, 3e8)))
- cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`, {
- featureFlags: [{ name: FeatureFlag.uniswapXDefaultEnabled, value: true }],
- })
+ cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
})
it('can swap exact-in trades using uniswapX', () => {
@@ -159,9 +154,7 @@ describe('UniswapX Eth Input', () => {
stubSwapTxReceipt()
- cy.visit(`/swap/?inputCurrency=ETH&outputCurrency=${DAI.address}`, {
- featureFlags: [{ name: FeatureFlag.uniswapXDefaultEnabled, value: true }],
- })
+ cy.visit(`/swap/?inputCurrency=ETH&outputCurrency=${DAI.address}`)
})
it('can swap using uniswapX with ETH as input', () => {
@@ -247,9 +240,7 @@ describe('UniswapX activity history', () => {
cy.hardhat().then(async (hardhat) => {
await hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(USDC_MAINNET, 3e8))
})
- cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`, {
- featureFlags: [{ name: FeatureFlag.uniswapXDefaultEnabled, value: true }],
- })
+ cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
})
it('can view UniswapX order status progress in activity', () => {
diff --git a/src/components/FeatureFlagModal/FeatureFlagModal.tsx b/src/components/FeatureFlagModal/FeatureFlagModal.tsx
index aca7d0f8e1..ae94013c72 100644
--- a/src/components/FeatureFlagModal/FeatureFlagModal.tsx
+++ b/src/components/FeatureFlagModal/FeatureFlagModal.tsx
@@ -14,7 +14,6 @@ import { useMultichainUXFlag } from 'featureFlags/flags/multichainUx'
import { useProgressIndicatorV2Flag } from 'featureFlags/flags/progressIndicatorV2'
import { useQuickRouteMainnetFlag } from 'featureFlags/flags/quickRouteMainnet'
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
-import { useUniswapXDefaultEnabledFlag } from 'featureFlags/flags/uniswapXDefault'
import { useUniswapXSyntheticQuoteFlag } from 'featureFlags/flags/uniswapXUseSyntheticQuote'
import { useFeesEnabledFlag } from 'featureFlags/flags/useFees'
import { useUpdateAtom } from 'jotai/utils'
@@ -324,12 +323,6 @@ export default function FeatureFlagModal() {
featureFlag={FeatureFlag.uniswapXSyntheticQuote}
label="Force synthetic quotes for UniswapX"
/>
-
@@ -46,21 +37,9 @@ export default function RouterPreferenceSettings() {
{
- if (uniswapXInEffect) {
- if (isUniswapXDefaultEnabled) {
- // We need to remember if a opts out of UniswapX, so we don't request UniswapX quotes.
- dispatch(updateOptedOutOfUniswapX({ optedOutOfUniswapX: true }))
- } else {
- // We need to remember if a user disables Uniswap X, so we don't show the opt-in flow again.
- dispatch(updateDisabledUniswapX({ disabledUniswapX: true }))
- }
- }
- setRouterPreference(uniswapXInEffect ? RouterPreference.API : RouterPreference.X)
+ setRouterPreference(routerPreference === RouterPreference.X ? RouterPreference.API : RouterPreference.X)
}}
/>
diff --git a/src/components/swap/styled.tsx b/src/components/swap/styled.tsx
index 2176852a95..5c4cbda6d3 100644
--- a/src/components/swap/styled.tsx
+++ b/src/components/swap/styled.tsx
@@ -4,7 +4,6 @@ import { AlertTriangle } from 'react-feather'
import styled, { css } from 'styled-components'
import { Z_INDEX } from 'theme/zIndex'
-import { useIsDarkMode } from '../../theme/components/ThemeToggle'
import { AutoColumn } from '../Column'
export const PageWrapper = styled.div`
@@ -61,109 +60,6 @@ const SwapWrapperInner = styled.div`
padding-top: 12px;
`
-export const UniswapPopoverContainer = styled.div`
- padding: 18px;
- color: ${({ theme }) => theme.neutral1};
- font-weight: 485;
- font-size: 12px;
- line-height: 16px;
- word-break: break-word;
- background: ${({ theme }) => theme.surface1};
- border-radius: 20px;
- border: 1px solid ${({ theme }) => theme.surface3};
- box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.9, theme.shadow1)};
- position: relative;
- overflow: hidden;
-`
-
-const springDownKeyframes = `@keyframes spring-down {
- 0% { transform: translateY(-80px); }
- 25% { transform: translateY(4px); }
- 50% { transform: translateY(-1px); }
- 75% { transform: translateY(0px); }
- 100% { transform: translateY(0px); }
-}`
-
-const backUpKeyframes = `@keyframes back-up {
- 0% { transform: translateY(0px); }
- 100% { transform: translateY(-80px); }
-}`
-
-export const UniswapXShine = (props: any) => {
- const isDarkMode = useIsDarkMode()
- return
-}
-
-const UniswapXShineInner = styled.div`
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- z-index: -1;
- pointer-events: none;
- background: linear-gradient(130deg, transparent 20%, ${({ theme }) => theme.accent1}, transparent 80%);
- opacity: 0.15;
-`
-
-// overflow hidden to hide the SwapMustacheShadow
-export const SwapOptInSmallContainer = styled.div<{ visible: boolean }>`
- visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
- overflow: hidden;
- margin-top: -14px;
- transform: translateY(${({ visible }) => (visible ? 0 : -80)}px);
- transition: all ease 400ms;
- animation: ${({ visible }) => (visible ? `spring-down 900ms ease forwards` : 'back-up 200ms ease forwards')};
-
- ${springDownKeyframes}
- ${backUpKeyframes}
-`
-
-export const UniswapXOptInLargeContainerPositioner = styled.div`
- position: absolute;
- top: 211px;
- right: ${-320 - 15}px;
- width: 320px;
- align-items: center;
- min-height: 170px;
- display: flex;
- pointer-events: none;
-`
-
-export const UniswapXOptInLargeContainer = styled.div<{ visible: boolean }>`
- opacity: ${({ visible }) => (visible ? 1 : 0)};
- transform: ${({ visible }) => `translateY(${visible ? 0 : -6}px)`};
- transition: all ease-in 300ms;
- transition-delay: ${({ visible }) => (visible ? '350ms' : '0')};
- pointer-events: ${({ visible }) => (visible ? 'auto' : 'none')};
-`
-
-export const SwapMustache = styled.main`
- position: relative;
- background: ${({ theme }) => theme.surface1};
- border-radius: 16px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- border: 1px solid ${({ theme }) => theme.surface3};
- border-top-width: 0;
- padding: 18px;
- padding-top: calc(12px + 18px);
- z-index: 0;
- transition: transform 250ms ease;
-`
-
-export const SwapMustacheShadow = styled.main`
- position: absolute;
- top: 0;
- left: 0;
- border-radius: 16px;
- height: 100%;
- width: 100%;
- transform: translateY(-100%);
- box-shadow: 0 0 20px 20px ${({ theme }) => theme.surface2};
- background: red;
-`
-
export const ArrowWrapper = styled.div<{ clickable: boolean }>`
border-radius: 12px;
height: 40px;
diff --git a/src/featureFlags/flags/uniswapXDefault.ts b/src/featureFlags/flags/uniswapXDefault.ts
deleted file mode 100644
index 2b34e8c2f9..0000000000
--- a/src/featureFlags/flags/uniswapXDefault.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
-
-export function useUniswapXDefaultEnabledFlag(): BaseVariant {
- return useBaseFlag(FeatureFlag.uniswapXDefaultEnabled)
-}
-
-export function useUniswapXDefaultEnabled(): boolean {
- return useUniswapXDefaultEnabledFlag() === BaseVariant.Enabled
-}
diff --git a/src/featureFlags/index.tsx b/src/featureFlags/index.tsx
index cc3cda3b0b..0f4a30bfcd 100644
--- a/src/featureFlags/index.tsx
+++ b/src/featureFlags/index.tsx
@@ -16,7 +16,6 @@ export enum FeatureFlag {
infoTDP = 'info_tdp',
infoPoolPage = 'info_pool_page',
infoLiveViews = 'info_live_views',
- uniswapXDefaultEnabled = 'uniswapx_default_enabled',
quickRouteMainnet = 'enable_quick_route_mainnet',
progressIndicatorV2 = 'progress_indicator_v2',
feesEnabled = 'fees_enabled',
diff --git a/src/lib/hooks/routing/useRoutingAPIArguments.ts b/src/lib/hooks/routing/useRoutingAPIArguments.ts
index 484feef7dd..dcac3b7b5f 100644
--- a/src/lib/hooks/routing/useRoutingAPIArguments.ts
+++ b/src/lib/hooks/routing/useRoutingAPIArguments.ts
@@ -1,12 +1,10 @@
import { SkipToken, skipToken } from '@reduxjs/toolkit/query/react'
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
-import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'
import { useUniswapXSyntheticQuoteEnabled } from 'featureFlags/flags/uniswapXUseSyntheticQuote'
import { useFeesEnabled } from 'featureFlags/flags/useFees'
import { useMemo } from 'react'
import { GetQuoteArgs, INTERNAL_ROUTER_PREFERENCE_PRICE, RouterPreference } from 'state/routing/types'
import { currencyAddressForSwapQuote } from 'state/routing/utils'
-import { useUserDisabledUniswapX, useUserOptedOutOfUniswapX } from 'state/user/hooks'
/**
* Returns query arguments for the Routing API query or undefined if the
@@ -29,9 +27,6 @@ export function useRoutingAPIArguments({
routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE
}): GetQuoteArgs | SkipToken {
const uniswapXForceSyntheticQuotes = useUniswapXSyntheticQuoteEnabled()
- const userDisabledUniswapX = useUserDisabledUniswapX()
- const userOptedOutOfUniswapX = useUserOptedOutOfUniswapX()
- const isUniswapXDefaultEnabled = useUniswapXDefaultEnabled()
const feesEnabled = useFeesEnabled()
// Don't enable fee logic if this is a quote for pricing
@@ -56,23 +51,8 @@ export function useRoutingAPIArguments({
tradeType,
needsWrapIfUniswapX: tokenIn.isNative,
uniswapXForceSyntheticQuotes,
- userDisabledUniswapX,
- userOptedOutOfUniswapX,
- isUniswapXDefaultEnabled,
sendPortionEnabled,
},
- [
- account,
- amount,
- routerPreference,
- tokenIn,
- tokenOut,
- tradeType,
- uniswapXForceSyntheticQuotes,
- userDisabledUniswapX,
- userOptedOutOfUniswapX,
- isUniswapXDefaultEnabled,
- sendPortionEnabled,
- ]
+ [account, amount, routerPreference, tokenIn, tokenOut, tradeType, uniswapXForceSyntheticQuotes, sendPortionEnabled]
)
}
diff --git a/src/pages/App.tsx b/src/pages/App.tsx
index 87e1bd67eb..7fdc75ce2a 100644
--- a/src/pages/App.tsx
+++ b/src/pages/App.tsx
@@ -5,8 +5,7 @@ import ErrorBoundary from 'components/ErrorBoundary'
import Loader from 'components/Icons/LoadingSpinner'
import NavBar, { PageTabs } from 'components/NavBar'
import { UK_BANNER_HEIGHT, UK_BANNER_HEIGHT_MD, UK_BANNER_HEIGHT_SM, UkBanner } from 'components/NavBar/UkBanner'
-import { FeatureFlag, useFeatureFlagsIsLoaded } from 'featureFlags'
-import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'
+import { useFeatureFlagsIsLoaded } from 'featureFlags'
import { useAtom } from 'jotai'
import { useBag } from 'nft/hooks/useBag'
import { lazy, Suspense, useEffect, useLayoutEffect, useMemo, useState } from 'react'
@@ -14,9 +13,8 @@ import { Navigate, Route, Routes, useLocation, useSearchParams } from 'react-rou
import { shouldDisableNFTRoutesAtom } from 'state/application/atoms'
import { useAppSelector } from 'state/hooks'
import { AppState } from 'state/reducer'
-import { RouterPreference } from 'state/routing/types'
-import { useRouterPreference, useUserOptedOutOfUniswapX } from 'state/user/hooks'
-import { StatsigProvider, StatsigUser, useGate } from 'statsig-react'
+import { useRouterPreference } from 'state/user/hooks'
+import { StatsigProvider, StatsigUser } from 'statsig-react'
import styled from 'styled-components'
import DarkModeQueryParamReader from 'theme/components/DarkModeQueryParamReader'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
@@ -212,9 +210,6 @@ function UserPropertyUpdater() {
const isDarkMode = useIsDarkMode()
const [routerPreference] = useRouterPreference()
- const userOptedOutOfUniswapX = useUserOptedOutOfUniswapX()
- const isUniswapXDefaultEnabled = useUniswapXDefaultEnabled()
- const { isLoading: isUniswapXDefaultLoading } = useGate(FeatureFlag.uniswapXDefaultEnabled)
const rehydrated = useAppSelector((state) => state._persist.rehydrated)
useEffect(() => {
@@ -248,22 +243,8 @@ function UserPropertyUpdater() {
}, [isDarkMode])
useEffect(() => {
- if (isUniswapXDefaultLoading || !rehydrated) return
-
- // If we're not in the transition period to UniswapX opt-out, set the router preference to whatever is specified.
- if (!isUniswapXDefaultEnabled) {
- user.set(CustomUserProperties.ROUTER_PREFERENCE, routerPreference)
- return
- }
-
- // In the transition period, override the stored API preference to UniswapX if the user hasn't opted out.
- if (routerPreference === RouterPreference.API && !userOptedOutOfUniswapX) {
- user.set(CustomUserProperties.ROUTER_PREFERENCE, RouterPreference.X)
- return
- }
-
- // Otherwise, the user has opted out or their preference is UniswapX/client, so set the preference to whatever is specified.
+ if (!rehydrated) return
user.set(CustomUserProperties.ROUTER_PREFERENCE, routerPreference)
- }, [routerPreference, isUniswapXDefaultEnabled, userOptedOutOfUniswapX, isUniswapXDefaultLoading, rehydrated])
+ }, [routerPreference, rehydrated])
return null
}
diff --git a/src/pages/Swap/UniswapXOptIn.tsx b/src/pages/Swap/UniswapXOptIn.tsx
deleted file mode 100644
index f75480ad6f..0000000000
--- a/src/pages/Swap/UniswapXOptIn.tsx
+++ /dev/null
@@ -1,207 +0,0 @@
-import { Trans } from '@lingui/macro'
-import { sendAnalyticsEvent, Trace } from 'analytics'
-import Column from 'components/Column'
-import UniswapXBrandMark from 'components/Logo/UniswapXBrandMark'
-import { Arrow } from 'components/Popover'
-import UniswapXRouterLabel from 'components/RouterLabel/UniswapXRouterLabel'
-import Row from 'components/Row'
-import {
- SwapMustache,
- SwapMustacheShadow,
- SwapOptInSmallContainer,
- UniswapPopoverContainer,
- UniswapXOptInLargeContainer,
- UniswapXOptInLargeContainerPositioner,
- UniswapXShine,
-} from 'components/swap/styled'
-import { formatCommonPropertiesForTrade } from 'lib/utils/analytics'
-import { PropsWithChildren, useRef, useState } from 'react'
-import { X } from 'react-feather'
-import { useLocation } from 'react-router-dom'
-import { Text } from 'rebass'
-import { useAppDispatch } from 'state/hooks'
-import { RouterPreference } from 'state/routing/types'
-import { isClassicTrade } from 'state/routing/utils'
-import { SwapInfo } from 'state/swap/hooks'
-import { useRouterPreference, useUserDisabledUniswapX } from 'state/user/hooks'
-import { updateDisabledUniswapX } from 'state/user/reducer'
-import styled from 'styled-components'
-import { ThemedText } from 'theme/components'
-
-export const UniswapXOptIn = (props: { swapInfo: SwapInfo; isSmall: boolean }) => {
- const {
- trade: { trade },
- allowedSlippage,
- } = props.swapInfo
- const userDisabledUniswapX = useUserDisabledUniswapX()
- const isOnClassic = Boolean(trade && isClassicTrade(trade) && trade.isUniswapXBetter && !userDisabledUniswapX)
- const [hasEverShown, setHasEverShown] = useState(false)
-
- if (isOnClassic && !hasEverShown) {
- setHasEverShown(true)
- }
-
- // avoid some work if never needed to show
- if (!hasEverShown) {
- return null
- }
-
- return (
-
-
-
- )
-}
-
-const OptInContents = ({
- swapInfo,
- isOnClassic,
- isSmall,
-}: {
- swapInfo: SwapInfo
- isOnClassic: boolean
- isSmall: boolean
-}) => {
- const {
- trade: { trade },
- allowedSlippage,
- } = swapInfo
- const [, setRouterPreference] = useRouterPreference()
- const dispatch = useAppDispatch()
- const [showYoureIn, setShowYoureIn] = useState(false)
- const isVisible = isOnClassic
- const location = useLocation()
-
- const tryItNowElement = (
- {
- // slight delay before hiding
- setTimeout(() => {
- setShowYoureIn(true)
- setTimeout(() => {
- setShowYoureIn(false)
- }, 5000)
- }, 200)
-
- if (!trade) return
- sendAnalyticsEvent('UniswapX Opt In Toggled', {
- ...formatCommonPropertiesForTrade(trade, allowedSlippage),
- new_preference: RouterPreference.X,
- })
- setRouterPreference(RouterPreference.X)
- }}
- style={{
- cursor: 'pointer',
- }}
- >
- Try it now
-
- )
-
- const containerRef = useRef()
-
- if (isSmall || location.pathname.includes('/tokens/')) {
- return (
-
-
-
-
-
-
- Try gas free swaps with the
-
- {' '}
- Beta
-
- {tryItNowElement}
-
-
-
- )
- }
-
- return (
- <>
- {/* first popover: intro */}
-
- {
- if (!trade) return
- sendAnalyticsEvent('UniswapX Opt In Toggled', {
- ...formatCommonPropertiesForTrade(trade, allowedSlippage),
- new_preference: RouterPreference.API,
- })
- setRouterPreference(RouterPreference.API)
- dispatch(updateDisabledUniswapX({ disabledUniswapX: true }))
- }}
- />
-
-
-
- Try the{' '}
- {' '}
- Beta
-
- -
- Gas free swaps
-
- -
- MEV protection
-
- -
- Better prices and more liquidity
-
-
-
-
-
- {tryItNowElement}
-
-
- {/* second popover: you're in! */}
-
-
-
- You're in!
-
-
-
-
- You can turn it off at anytime in settings
-
-
- >
- )
-}
-
-const UniswapXOptInPopover = (props: PropsWithChildren<{ visible: boolean; shiny?: boolean }>) => {
- return (
- // positioner ensures no matter the height of the inner content
- // it sits at the same position from the top of the swap area
-
-
-
-
- {props.shiny && }
- {props.children}
-
-
-
- )
-}
-
-const CloseIcon = styled(X)`
- color: ${({ theme }) => theme.neutral3};
- cursor: pointer;
- position: absolute;
- top: 14px;
- right: 14px;
-`
diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx
index 02ba2109a5..f65819ca8a 100644
--- a/src/pages/Swap/index.tsx
+++ b/src/pages/Swap/index.tsx
@@ -33,7 +33,6 @@ import { useConnectionReady } from 'connection/eagerlyConnect'
import { getChainInfo } from 'constants/chainInfo'
import { asSupportedChain, isSupportedChain } from 'constants/chains'
import { getSwapCurrencyId, TOKEN_SHORTHANDS } from 'constants/tokens'
-import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'
import { useCurrency, useDefaultActiveTokens } from 'hooks/Tokens'
import { useIsSwapUnsupported } from 'hooks/useIsSwapUnsupported'
import { useMaxAmountIn } from 'hooks/useMaxAmountIn'
@@ -64,10 +63,8 @@ import { maxAmountSpend } from 'utils/maxAmountSpend'
import { computeRealizedPriceImpact, warningSeverity } from 'utils/prices'
import { didUserReject } from 'utils/swapErrorToUserReadableMessage'
-import { useScreenSize } from '../../hooks/useScreenSize'
import { useIsDarkMode } from '../../theme/components/ThemeToggle'
import { OutputTaxTooltipBody } from './TaxTooltipBody'
-import { UniswapXOptIn } from './UniswapXOptIn'
export const ArrowContainer = styled.div`
display: inline-flex;
@@ -597,9 +594,7 @@ export function Swap({
const inputCurrency = currencies[Field.INPUT] ?? undefined
const switchChain = useSwitchChain()
const switchingChain = useAppSelector((state) => state.wallets.switchingChain)
- const showOptInSmall = !useScreenSize().navSearchInputVisible
const isDark = useIsDarkMode()
- const isUniswapXDefaultEnabled = useUniswapXDefaultEnabled()
const [currentTab, setCurrentTab] = useState(SwapTab.Swap)
@@ -832,7 +827,6 @@ export function Swap({
)}
- {!showOptInSmall && !isUniswapXDefaultEnabled && }
>
)
@@ -849,7 +843,6 @@ export function Swap({
/>
{/* todo: build Limit UI */}
{currentTab === SwapTab.Swap ? swapElement : undefined}
- {showOptInSmall && !isUniswapXDefaultEnabled && }
)
}
diff --git a/src/state/migrations.test.ts b/src/state/migrations.test.ts
index cec883cd38..e50ba03829 100644
--- a/src/state/migrations.test.ts
+++ b/src/state/migrations.test.ts
@@ -14,7 +14,7 @@ const defaultState = {
user: {},
_persist: {
rehydrated: true,
- version: 4,
+ version: 5,
},
application: {
chainId: null,
diff --git a/src/state/migrations.ts b/src/state/migrations.ts
index 476edd6629..6c58c5e341 100644
--- a/src/state/migrations.ts
+++ b/src/state/migrations.ts
@@ -6,6 +6,7 @@ import { migration1 } from './migrations/1'
import { migration2 } from './migrations/2'
import { migration3 } from './migrations/3'
import { migration4 } from './migrations/4'
+import { migration5 } from './migrations/5'
import { legacyLocalStorageMigration } from './migrations/legacy'
/**
@@ -23,6 +24,7 @@ export const migrations: MigrationManifest = {
2: migration2,
3: migration3,
4: migration4,
+ 5: migration5,
}
// We use a custom migration function for the initial state, because redux-persist
diff --git a/src/state/migrations/5.test.ts b/src/state/migrations/5.test.ts
new file mode 100644
index 0000000000..2988a80a67
--- /dev/null
+++ b/src/state/migrations/5.test.ts
@@ -0,0 +1,98 @@
+import { createMigrate } from 'redux-persist'
+import { RouterPreference } from 'state/routing/types'
+import { SlippageTolerance } from 'state/user/types'
+
+import { migration1 } from './1'
+import { migration2 } from './2'
+import { migration3 } from './3'
+import { migration4 } from './4'
+import { migration5, PersistAppStateV5 } from './5'
+
+const previousState: PersistAppStateV5 = {
+ user: {
+ userRouterPreference: RouterPreference.API,
+ optedOutOfUniswapX: false,
+ disabledUniswapX: false,
+ userLocale: null,
+ userHideClosedPositions: false,
+ userSlippageTolerance: SlippageTolerance.Auto,
+ userSlippageToleranceHasBeenMigratedToAuto: true,
+ userDeadline: 1800,
+ tokens: {},
+ pairs: {},
+ timestamp: Date.now(),
+ hideAndroidAnnouncementBanner: false,
+ },
+ _persist: {
+ version: 4,
+ rehydrated: true,
+ },
+}
+
+describe('migration to v5', () => {
+ it('should migrate users who currently have `API` router preference', async () => {
+ const migrator = createMigrate(
+ {
+ 1: migration1,
+ 2: migration2,
+ 3: migration3,
+ 4: migration4,
+ 5: migration5,
+ },
+ { debug: false }
+ )
+ const result: any = await migrator(previousState, 5)
+ expect(result?.user?.userRouterPreference).toEqual(RouterPreference.X)
+ expect(result?.user?.disabledUniswapX).toBeUndefined()
+ expect(result?.user?.optedOutOfUniswapX).toBeUndefined()
+ expect(result?._persist.version).toEqual(5)
+ })
+
+ it('should not migrate routerPreference if user disabled during rollout', async () => {
+ const migrator = createMigrate(
+ {
+ 1: migration1,
+ 2: migration2,
+ 3: migration3,
+ 4: migration4,
+ 5: migration5,
+ },
+ { debug: false }
+ )
+ const result: any = await migrator(
+ {
+ ...previousState,
+ user: {
+ ...previousState.user,
+ optedOutOfUniswapX: true,
+ },
+ } as PersistAppStateV5,
+ 5
+ )
+ expect(result?.user?.userRouterPreference).toEqual(RouterPreference.API)
+ expect(result?.user?.optedOutOfUniswapX).toBeUndefined()
+ expect(result?._persist.version).toEqual(5)
+ })
+
+ it('should not migrate user if user does not exist', async () => {
+ const migrator = createMigrate(
+ {
+ 1: migration1,
+ 2: migration2,
+ 3: migration3,
+ 4: migration4,
+ 5: migration5,
+ },
+ { debug: false }
+ )
+ const result: any = await migrator(
+ {
+ ...previousState,
+ user: undefined,
+ } as PersistAppStateV5,
+ 5
+ )
+ expect(result?.user).toBeUndefined()
+ expect(result?._persist.version).toEqual(5)
+ })
+})
diff --git a/src/state/migrations/5.ts b/src/state/migrations/5.ts
new file mode 100644
index 0000000000..3b73839773
--- /dev/null
+++ b/src/state/migrations/5.ts
@@ -0,0 +1,43 @@
+import { PersistState } from 'redux-persist'
+import { RouterPreference } from 'state/routing/types'
+import { UserState } from 'state/user/reducer'
+
+export type PersistAppStateV5 = {
+ _persist: PersistState
+} & { user?: UserState & { disabledUniswapX?: boolean; optedOutOfUniswapX?: boolean } }
+
+/**
+ * Migration to migrate users to UniswapX by default.
+ */
+export const migration5 = (state: PersistAppStateV5 | undefined) => {
+ if (!state) return state
+ // Remove a previously-persisted variable
+ if (state?.user && 'disabledUniswapX' in state.user) {
+ delete state.user['disabledUniswapX']
+ }
+ const userOptedOutOfUniswapX = state?.user?.optedOutOfUniswapX
+ if (state?.user && 'optedOutOfUniswapX' in state.user) {
+ delete state.user['optedOutOfUniswapX']
+ }
+ // If the the user has previously disabled UniswapX *during the opt-out rollout period*, we respect that preference.
+ if (state?.user && !userOptedOutOfUniswapX) {
+ return {
+ ...state,
+ user: {
+ ...state.user,
+ userRouterPreference: RouterPreference.X,
+ },
+ _persist: {
+ ...state._persist,
+ version: 5,
+ },
+ }
+ }
+ return {
+ ...state,
+ _persist: {
+ ...state._persist,
+ version: 5,
+ },
+ }
+}
diff --git a/src/state/reducer.ts b/src/state/reducer.ts
index 6591c1ffed..d4ec93ed3c 100644
--- a/src/state/reducer.ts
+++ b/src/state/reducer.ts
@@ -44,7 +44,7 @@ export type AppState = ReturnType
const persistConfig: PersistConfig = {
key: 'interface',
- version: 4, // see migrations.ts for more details about this version
+ version: 5, // see migrations.ts for more details about this version
storage: localForage.createInstance({
name: 'redux',
}),
diff --git a/src/state/reducerTypeTest.ts b/src/state/reducerTypeTest.ts
index fd32c3bbe7..28bc6469f0 100644
--- a/src/state/reducerTypeTest.ts
+++ b/src/state/reducerTypeTest.ts
@@ -89,8 +89,6 @@ interface ExpectedUserState {
timestamp: number
hideAndroidAnnouncementBanner: boolean
showSurveyPopup?: boolean
- disabledUniswapX?: boolean
- optedOutOfUniswapX?: boolean
originCountry?: string
}
diff --git a/src/state/routing/slice.ts b/src/state/routing/slice.ts
index 2661674649..0b691f83ed 100644
--- a/src/state/routing/slice.ts
+++ b/src/state/routing/slice.ts
@@ -63,9 +63,9 @@ function getRoutingAPIConfig(args: GetQuoteArgs): RoutingConfig {
if (
// If the user has opted out of UniswapX during the opt-out transition period, we should respect that preference and only request classic quotes.
- (args.userOptedOutOfUniswapX && routerPreference !== RouterPreference.X) ||
- !isUniswapXSupportedChain(tokenInChainId) ||
- routerPreference === INTERNAL_ROUTER_PREFERENCE_PRICE
+ routerPreference === RouterPreference.API ||
+ routerPreference === INTERNAL_ROUTER_PREFERENCE_PRICE ||
+ !isUniswapXSupportedChain(tokenInChainId)
) {
return [classic]
}
diff --git a/src/state/routing/types.ts b/src/state/routing/types.ts
index 44b84c4e8b..7fb6fc98a4 100644
--- a/src/state/routing/types.ts
+++ b/src/state/routing/types.ts
@@ -43,11 +43,6 @@ export interface GetQuoteArgs {
tradeType: TradeType
needsWrapIfUniswapX: boolean
uniswapXForceSyntheticQuotes: boolean
- // legacy field indicating the user disabled UniswapX during the opt-in period, or dismissed the UniswapX opt-in modal.
- userDisabledUniswapX: boolean
- // temporary field indicating the user disabled UniswapX during the transition to the opt-out model
- userOptedOutOfUniswapX: boolean
- isUniswapXDefaultEnabled: boolean
sendPortionEnabled: boolean
}
@@ -197,7 +192,6 @@ export class ClassicTrade extends Trade {
approveInfo: ApproveInfo
gasUseEstimateUSD?: number // gas estimate for swaps
blockNumber: string | null | undefined
- isUniswapXBetter: boolean | undefined
requestId: string | undefined
quoteMethod: QuoteMethod
swapFee: SwapFeeInfo | undefined
@@ -205,7 +199,6 @@ export class ClassicTrade extends Trade {
constructor({
gasUseEstimateUSD,
blockNumber,
- isUniswapXBetter,
requestId,
quoteMethod,
approveInfo,
@@ -215,7 +208,6 @@ export class ClassicTrade extends Trade {
gasUseEstimateUSD?: number
totalGasUseEstimateUSD?: number
blockNumber?: string | null
- isUniswapXBetter?: boolean
requestId?: string
quoteMethod: QuoteMethod
approveInfo: ApproveInfo
@@ -240,7 +232,6 @@ export class ClassicTrade extends Trade {
super(routes)
this.blockNumber = blockNumber
this.gasUseEstimateUSD = gasUseEstimateUSD
- this.isUniswapXBetter = isUniswapXBetter
this.requestId = requestId
this.quoteMethod = quoteMethod
this.approveInfo = approveInfo
diff --git a/src/state/routing/useRoutingAPITrade.test.ts b/src/state/routing/useRoutingAPITrade.test.ts
index 93f027b8f8..e5b29473c3 100644
--- a/src/state/routing/useRoutingAPITrade.test.ts
+++ b/src/state/routing/useRoutingAPITrade.test.ts
@@ -3,13 +3,12 @@ import { renderHook } from '@testing-library/react'
import { CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo'
import { USDC_MAINNET } from 'constants/tokens'
-import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'
import { useUniswapXSyntheticQuoteEnabled } from 'featureFlags/flags/uniswapXUseSyntheticQuote'
import { useFeesEnabled } from 'featureFlags/flags/useFees'
import useIsWindowVisible from 'hooks/useIsWindowVisible'
import ms from 'ms'
import { GetQuoteArgs, INTERNAL_ROUTER_PREFERENCE_PRICE, RouterPreference } from 'state/routing/types'
-import { useRouterPreference, useUserDisabledUniswapX, useUserOptedOutOfUniswapX } from 'state/user/hooks'
+import { useRouterPreference } from 'state/user/hooks'
import { ETH_MAINNET } from 'test-utils/constants'
import { mocked } from 'test-utils/mocked'
@@ -29,16 +28,12 @@ jest.mock('./slice', () => {
})
jest.mock('state/user/hooks')
jest.mock('featureFlags/flags/uniswapXUseSyntheticQuote')
-jest.mock('featureFlags/flags/uniswapXDefault')
jest.mock('featureFlags/flags/useFees')
beforeEach(() => {
mocked(useIsWindowVisible).mockReturnValue(true)
mocked(useRouterPreference).mockReturnValue([RouterPreference.API, () => undefined])
mocked(useUniswapXSyntheticQuoteEnabled).mockReturnValue(false)
- mocked(useUserDisabledUniswapX).mockReturnValue(false)
- mocked(useUserOptedOutOfUniswapX).mockReturnValue(false)
- mocked(useUniswapXDefaultEnabled).mockReturnValue(false)
mocked(useFeesEnabled).mockReturnValue(true)
// @ts-ignore we dont use the response from this hook in useRoutingAPITrade so fine to mock as undefined
mocked(useGetQuoteQuery).mockReturnValue(undefined)
@@ -66,9 +61,6 @@ const MOCK_ARGS: GetQuoteArgs = {
tradeType: TradeType.EXACT_INPUT,
needsWrapIfUniswapX: USDCAmount.currency.isNative,
uniswapXForceSyntheticQuotes: false,
- userDisabledUniswapX: false,
- userOptedOutOfUniswapX: false,
- isUniswapXDefaultEnabled: false,
sendPortionEnabled: true,
}
diff --git a/src/state/routing/utils.ts b/src/state/routing/utils.ts
index a5bf04a8b9..b5c0af4c59 100644
--- a/src/state/routing/utils.ts
+++ b/src/state/routing/utils.ts
@@ -219,18 +219,14 @@ export async function transformQuoteToTrade(
data: URAQuoteResponse,
quoteMethod: QuoteMethod
): Promise {
- const { tradeType, needsWrapIfUniswapX, routerPreference, account, amount, isUniswapXDefaultEnabled } = args
+ const { tradeType, needsWrapIfUniswapX, routerPreference, account, amount } = args
+
+ const showUniswapXTrade = data.routing === URAQuoteType.DUTCH_LIMIT && routerPreference === RouterPreference.X
- const showUniswapXTrade =
- data.routing === URAQuoteType.DUTCH_LIMIT &&
- (routerPreference === RouterPreference.X || (isUniswapXDefaultEnabled && routerPreference === RouterPreference.API))
const [currencyIn, currencyOut] = getTradeCurrencies(args, showUniswapXTrade)
const { gasUseEstimateUSD, blockNumber, routes, gasUseEstimate, swapFee } = getClassicTradeDetails(args, data)
- // If the top-level URA quote type is DUTCH_LIMIT, then UniswapX is better for the user
- const isUniswapXBetter = data.routing === URAQuoteType.DUTCH_LIMIT
-
// Some sus javascript float math but it's ok because its just an estimate for display purposes
const usdCostPerGas = gasUseEstimateUSD && gasUseEstimate ? gasUseEstimateUSD / gasUseEstimate : undefined
@@ -267,15 +263,14 @@ export async function transformQuoteToTrade(
gasUseEstimateUSD,
approveInfo,
blockNumber,
- isUniswapXBetter,
requestId: data.quote.requestId,
quoteMethod,
swapFee,
})
- // During the opt-in period, only return UniswapX quotes if the user has turned on the setting,
- // even if it is the better quote.
- if (isUniswapXBetter && (routerPreference === RouterPreference.X || isUniswapXDefaultEnabled)) {
+ // If the top-level URA quote type is DUTCH_LIMIT, then UniswapX is better for the user
+ const isUniswapXBetter = data.routing === URAQuoteType.DUTCH_LIMIT
+ if (isUniswapXBetter) {
const orderInfo = toDutchOrderInfo(data.quote.orderInfo)
const swapFee = getSwapFee(data.quote)
const wrapInfo = await getWrapInfo(needsWrapIfUniswapX, account, currencyIn.chainId, amount, usdCostPerGas)
diff --git a/src/state/swap/hooks.tsx b/src/state/swap/hooks.tsx
index b9126a4499..aba04e0244 100644
--- a/src/state/swap/hooks.tsx
+++ b/src/state/swap/hooks.tsx
@@ -77,7 +77,7 @@ const BAD_RECIPIENT_ADDRESSES: { [address: string]: true } = {
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D': true, // v2 router 02
}
-export type SwapInfo = {
+type SwapInfo = {
currencies: { [field in Field]?: Currency }
currencyBalances: { [field in Field]?: CurrencyAmount }
inputTax: Percent
diff --git a/src/state/user/hooks.test.tsx b/src/state/user/hooks.test.tsx
index 0c176748df..c86a37d099 100644
--- a/src/state/user/hooks.test.tsx
+++ b/src/state/user/hooks.test.tsx
@@ -5,13 +5,7 @@ import store from 'state'
import { RouterPreference } from 'state/routing/types'
import { renderHook } from 'test-utils/render'
-import {
- deserializeToken,
- serializeToken,
- useRouterPreference,
- useUserDisabledUniswapX,
- useUserSlippageTolerance,
-} from './hooks'
+import { deserializeToken, serializeToken, useRouterPreference, useUserSlippageTolerance } from './hooks'
import { updateUserSlippageTolerance } from './reducer'
import { SlippageTolerance } from './types'
@@ -81,12 +75,3 @@ describe('useRouterPreference', () => {
expect(routerPreference).toBe(RouterPreference.X)
})
})
-
-describe('useUserDisabledUniswapX', () => {
- it('returns `false` by default', () => {
- const {
- result: { current: disabledUniswapX },
- } = renderHook(() => useUserDisabledUniswapX())
- expect(disabledUniswapX).toBe(false)
- })
-})
diff --git a/src/state/user/hooks.tsx b/src/state/user/hooks.tsx
index 5072b4da4f..cd2ca0302d 100644
--- a/src/state/user/hooks.tsx
+++ b/src/state/user/hooks.tsx
@@ -217,14 +217,6 @@ export function useHideAndroidAnnouncementBanner(): [boolean, () => void] {
return [hideAndroidAnnouncementBanner, toggleHideAndroidAnnouncementBanner]
}
-export function useUserDisabledUniswapX(): boolean {
- return useAppSelector((state) => state.user.disabledUniswapX) ?? false
-}
-
-export function useUserOptedOutOfUniswapX(): boolean {
- return useAppSelector((state) => state.user.optedOutOfUniswapX) ?? false
-}
-
/**
* Given two tokens return the liquidity token that represents its liquidity shares
* @param tokenA one of the two tokens
diff --git a/src/state/user/reducer.ts b/src/state/user/reducer.ts
index 6b4d42b6d0..e9fd25aed1 100644
--- a/src/state/user/reducer.ts
+++ b/src/state/user/reducer.ts
@@ -48,10 +48,6 @@ export interface UserState {
timestamp: number
hideAndroidAnnouncementBanner: boolean
- // legacy field indicating the user disabled UniswapX during the opt-in period, or dismissed the UniswapX opt-in modal.
- disabledUniswapX?: boolean
- // temporary field indicating the user disabled UniswapX during the transition to the opt-out model
- optedOutOfUniswapX?: boolean
// undefined means has not gone through A/B split yet
showSurveyPopup?: boolean
@@ -111,12 +107,6 @@ const userSlice = createSlice({
updateHideAndroidAnnouncementBanner(state, action) {
state.hideAndroidAnnouncementBanner = action.payload.hideAndroidAnnouncementBanner
},
- updateDisabledUniswapX(state, action) {
- state.disabledUniswapX = action.payload.disabledUniswapX
- },
- updateOptedOutOfUniswapX(state, action) {
- state.optedOutOfUniswapX = action.payload.optedOutOfUniswapX
- },
addSerializedToken(state, { payload: { serializedToken } }) {
if (!state.tokens) {
state.tokens = {}
@@ -153,7 +143,5 @@ export const {
updateUserLocale,
updateUserSlippageTolerance,
updateHideAndroidAnnouncementBanner,
- updateDisabledUniswapX,
- updateOptedOutOfUniswapX,
} = userSlice.actions
export default userSlice.reducer