diff --git a/cypress.config.ts b/cypress.config.ts
index d2491fe956..2d8a9bc626 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -6,7 +6,7 @@ export default defineConfig({
defaultCommandTimeout: 24000, // 2x average block time
chromeWebSecurity: false,
experimentalMemoryManagement: true, // better memory management, see https://github.com/cypress-io/cypress/pull/25462
- retries: { runMode: process.env.CYPRESS_RETRIES ? +process.env.CYPRESS_RETRIES : 2 },
+ retries: { runMode: process.env.CYPRESS_RETRIES ? +process.env.CYPRESS_RETRIES : 1 },
video: false, // GH provides 2 CPUs, and cypress video eats one up, see https://github.com/cypress-io/cypress/issues/20468#issuecomment-1307608025
e2e: {
async setupNodeEvents(on, config) {
diff --git a/cypress/e2e/swap/swapFlowLogging.test.ts b/cypress/e2e/swap/logging.test.ts
similarity index 100%
rename from cypress/e2e/swap/swapFlowLogging.test.ts
rename to cypress/e2e/swap/logging.test.ts
diff --git a/cypress/e2e/swap/uniswapx.test.ts b/cypress/e2e/swap/uniswapx.test.ts
index d42981047f..fa5e358b1c 100644
--- a/cypress/e2e/swap/uniswapx.test.ts
+++ b/cypress/e2e/swap/uniswapx.test.ts
@@ -1,18 +1,36 @@
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'
-const QuoteEndpoint = 'https://api.uniswap.org/v2/quote'
const QuoteWhereUniswapXIsBetter = 'uniswapx/quote1.json'
const QuoteWithEthInput = 'uniswapx/quote2.json'
+const QuoteEndpoint = 'https://api.uniswap.org/v2/quote'
const OrderSubmissionEndpoint = 'https://api.uniswap.org/v2/order'
-
const OrderStatusEndpoint =
'https://api.uniswap.org/v2/orders?swapper=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266&orderHashes=0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19'
+/**
+ * Stubs quote to return a quote for non-price requests
+ * Price quotes are blocked with 409, as the backend would not accept them regardless
+ */
+function stubNonPriceQuoteWith(fixture: string) {
+ cy.intercept(QuoteEndpoint, (req: CyHttpMessages.IncomingHttpRequest) => {
+ let body = req.body
+ if (typeof body === 'string') {
+ body = JSON.parse(body)
+ }
+ if (body.intent === 'pricing') {
+ req.reply({ statusCode: 409 })
+ } else {
+ req.reply({ fixture })
+ }
+ }).as('quote')
+}
+
/** Stubs the provider to return a tx receipt corresponding to the mock filled uniswapx order's txHash */
function stubSwapTxReceipt() {
cy.hardhat().then((hardhat) => {
@@ -26,15 +44,16 @@ function stubSwapTxReceipt() {
describe('UniswapX Toggle', () => {
beforeEach(() => {
- cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
+ stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter)
cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`, {
featureFlags: [{ name: FeatureFlag.uniswapXDefaultEnabled, value: false }],
})
})
- it('only displays uniswapx ui when setting is on', () => {
+ it('displays uniswapx ui when setting is on', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
+ cy.wait('@quote')
// UniswapX UI should not be visible
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('not.exist')
@@ -49,6 +68,7 @@ describe('UniswapX Toggle', () => {
it('prompts opt-in if UniswapX is better', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
+ cy.wait('@quote')
// UniswapX should not display in gas estimate row before opt-in
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('not.exist')
@@ -72,7 +92,7 @@ describe('UniswapX Toggle', () => {
describe('UniswapX Orders', () => {
beforeEach(() => {
- cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
+ stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter)
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
@@ -87,6 +107,8 @@ describe('UniswapX Orders', () => {
it('can swap exact-in trades using uniswapX', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
+ cy.wait('@quote')
+
cy.contains('Try it now').click()
// Submit uniswapx order signature
@@ -106,6 +128,8 @@ describe('UniswapX Orders', () => {
it('can swap exact-out trades using uniswapX', () => {
// Setup a swap
cy.get('#swap-currency-output .token-amount-input').type('300')
+ cy.wait('@quote')
+
cy.contains('Try it now').click()
// Submit uniswapx order signature
@@ -125,6 +149,8 @@ describe('UniswapX Orders', () => {
it('renders proper view if uniswapx order expires', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
+ cy.wait('@quote')
+
cy.contains('Try it now').click()
// Submit uniswapx order signature
@@ -141,6 +167,8 @@ describe('UniswapX Orders', () => {
it('renders proper view if uniswapx order has insufficient funds', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
+ cy.wait('@quote')
+
cy.contains('Try it now').click()
// Submit uniswapx order signature
@@ -157,7 +185,7 @@ describe('UniswapX Orders', () => {
describe('UniswapX Eth Input', () => {
beforeEach(() => {
- cy.intercept(QuoteEndpoint, { fixture: QuoteWithEthInput })
+ stubNonPriceQuoteWith(QuoteWithEthInput)
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
@@ -177,6 +205,8 @@ describe('UniswapX Eth Input', () => {
it('can swap using uniswapX with ETH as input', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('1')
+
+ cy.wait('@quote')
cy.contains('Try it now').click()
// Prompt ETH wrap to use for order
@@ -209,6 +239,8 @@ describe('UniswapX Eth Input', () => {
it('switches swap input to WETH after wrap', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('1')
+ cy.wait('@quote')
+
cy.contains('Try it now').click()
// Prompt ETH wrap and confirm
@@ -344,7 +376,7 @@ describe('UniswapX activity history', () => {
// Open activity history
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()
- // Ensure gql and local order have been deduped, such that there is only one swap activity listed
+ // Ensure gql and local order have been deduped, such that there is one swap activity listed
cy.get(getTestSelector('activity-content')).contains('Swapped').should('have.length', 1)
})
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index 3c7659f279..df81ac0976 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -74,18 +74,14 @@ Cypress.Commands.overwrite(
}
)
-Cypress.Commands.add('waitForAmplitudeEvent', (eventName, timeout = 5000 /* 5s */) => {
- const startTime = new Date().getTime()
-
+Cypress.Commands.add('waitForAmplitudeEvent', (eventName) => {
function checkRequest() {
- return cy.wait('@amplitude', { timeout }).then((interception) => {
+ return cy.wait('@amplitude').then((interception) => {
const events = interception.request.body.events
const event = events.find((event: any) => event.event_type === eventName)
if (event) {
return cy.wrap(event)
- } else if (new Date().getTime() - startTime > timeout) {
- throw new Error(`Event ${eventName} not found within the specified timeout`)
} else {
return checkRequest()
}
diff --git a/cypress/support/setupTests.ts b/cypress/support/setupTests.ts
index 7ae34a206d..fac176c6f3 100644
--- a/cypress/support/setupTests.ts
+++ b/cypress/support/setupTests.ts
@@ -34,6 +34,9 @@ beforeEach(() => {
)
}).intercept('https://*.sentry.io', { statusCode: 200 })
+ // Mock statsig to allow us to mock flags.
+ cy.intercept(/statsig/, { statusCode: 409 })
+
// Mock our own token list responses to avoid the latency of IPFS.
cy.intercept('https://gateway.ipfs.io/ipns/tokens.uniswap.org', TokenListJSON)
.intercept('https://gateway.ipfs.io/ipns/extendedtokens.uniswap.org', { statusCode: 404 })
diff --git a/src/components/Logo/AssetLogo.tsx b/src/components/Logo/AssetLogo.tsx
index ad8fa1e017..e302cb060e 100644
--- a/src/components/Logo/AssetLogo.tsx
+++ b/src/components/Logo/AssetLogo.tsx
@@ -79,6 +79,7 @@ export default function AssetLogo({
onLoad={() => void setImgLoaded(true)}
onError={nextSrc}
imgLoaded={imgLoaded}
+ loading="lazy"
/>
) : (
diff --git a/src/components/SearchModal/__snapshots__/CommonBases.test.tsx.snap b/src/components/SearchModal/__snapshots__/CommonBases.test.tsx.snap
index 98a8802397..14c274dc6f 100644
--- a/src/components/SearchModal/__snapshots__/CommonBases.test.tsx.snap
+++ b/src/components/SearchModal/__snapshots__/CommonBases.test.tsx.snap
@@ -105,6 +105,7 @@ exports[`CommonBases renders without crashing 1`] = `
@@ -130,6 +131,7 @@ exports[`CommonBases renders without crashing 1`] = `
@@ -155,6 +157,7 @@ exports[`CommonBases renders without crashing 1`] = `
@@ -180,6 +183,7 @@ exports[`CommonBases renders without crashing 1`] = `
@@ -205,6 +209,7 @@ exports[`CommonBases renders without crashing 1`] = `
@@ -230,6 +235,7 @@ exports[`CommonBases renders without crashing 1`] = `
diff --git a/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap b/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap
index 0ac8eae691..4daf2a27fc 100644
--- a/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap
+++ b/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap
@@ -2762,6 +2762,7 @@ exports[`SwapLineItem.tsx exact input 1`] = `
@@ -2828,6 +2829,7 @@ exports[`SwapLineItem.tsx exact input 1`] = `
@@ -2846,6 +2848,7 @@ exports[`SwapLineItem.tsx exact input 1`] = `
@@ -2887,6 +2890,7 @@ exports[`SwapLineItem.tsx exact input 1`] = `
@@ -4429,6 +4433,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = `
@@ -4495,6 +4500,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = `
@@ -4513,6 +4519,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = `
@@ -4554,6 +4561,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = `
@@ -6096,6 +6104,7 @@ exports[`SwapLineItem.tsx exact output 1`] = `
@@ -6162,6 +6171,7 @@ exports[`SwapLineItem.tsx exact output 1`] = `
@@ -6180,6 +6190,7 @@ exports[`SwapLineItem.tsx exact output 1`] = `
@@ -6221,6 +6232,7 @@ exports[`SwapLineItem.tsx exact output 1`] = `
@@ -7971,6 +7983,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
@@ -8037,6 +8050,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
@@ -8055,6 +8069,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
@@ -8096,6 +8111,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
@@ -9846,6 +9862,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
@@ -9912,6 +9929,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
@@ -9930,6 +9948,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
@@ -9971,6 +9990,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
diff --git a/src/components/swap/__snapshots__/SwapModalHeader.test.tsx.snap b/src/components/swap/__snapshots__/SwapModalHeader.test.tsx.snap
index b0cf7d1fbe..e1d366f260 100644
--- a/src/components/swap/__snapshots__/SwapModalHeader.test.tsx.snap
+++ b/src/components/swap/__snapshots__/SwapModalHeader.test.tsx.snap
@@ -165,6 +165,7 @@ exports[`SwapModalHeader.tsx matches base snapshot, test trade exact input 1`] =
@@ -213,6 +214,7 @@ exports[`SwapModalHeader.tsx matches base snapshot, test trade exact input 1`] =
@@ -388,6 +390,7 @@ exports[`SwapModalHeader.tsx renders ETH input token for an ETH input UniswapX s
@@ -436,6 +439,7 @@ exports[`SwapModalHeader.tsx renders ETH input token for an ETH input UniswapX s
@@ -611,6 +615,7 @@ exports[`SwapModalHeader.tsx renders preview trades with loading states 1`] = `
@@ -659,6 +664,7 @@ exports[`SwapModalHeader.tsx renders preview trades with loading states 1`] = `
@@ -834,6 +840,7 @@ exports[`SwapModalHeader.tsx test trade exact output, no recipient 1`] = `
@@ -882,6 +889,7 @@ exports[`SwapModalHeader.tsx test trade exact output, no recipient 1`] = `
diff --git a/src/components/swap/styled.tsx b/src/components/swap/styled.tsx
index d8c1dcc7c0..2176852a95 100644
--- a/src/components/swap/styled.tsx
+++ b/src/components/swap/styled.tsx
@@ -107,14 +107,13 @@ const UniswapXShineInner = styled.div`
`
// overflow hidden to hide the SwapMustacheShadow
-export const SwapOptInSmallContainer = styled.div<{ visible: boolean; shouldAnimate: boolean }>`
+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, shouldAnimate }) =>
- !shouldAnimate ? '' : visible ? `spring-down 900ms ease forwards` : 'back-up 200ms ease forwards'};
+ animation: ${({ visible }) => (visible ? `spring-down 900ms ease forwards` : 'back-up 200ms ease forwards')};
${springDownKeyframes}
${backUpKeyframes}
diff --git a/src/hooks/useStablecoinPrice.ts b/src/hooks/useStablecoinPrice.ts
index 70851a0ef0..695f773d83 100644
--- a/src/hooks/useStablecoinPrice.ts
+++ b/src/hooks/useStablecoinPrice.ts
@@ -2,7 +2,7 @@ import { ChainId, Currency, CurrencyAmount, Price, Token, TradeType } from '@uni
import { useWeb3React } from '@web3-react/core'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo, useRef } from 'react'
-import { INTERNAL_ROUTER_PREFERENCE_PRICE } from 'state/routing/types'
+import { ClassicTrade, INTERNAL_ROUTER_PREFERENCE_PRICE } from 'state/routing/types'
import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade'
import {
@@ -55,7 +55,8 @@ export default function useStablecoinPrice(currency?: Currency): Price
@@ -4548,6 +4549,7 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
diff --git a/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap b/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap
index 03b49fca7b..8f406f71a0 100644
--- a/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap
+++ b/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap
@@ -255,6 +255,7 @@ exports[`PoolDetailsStats pool balance chart not visible on mobile 1`] = `
@@ -274,6 +275,7 @@ exports[`PoolDetailsStats pool balance chart not visible on mobile 1`] = `
diff --git a/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap b/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap
index 1aeec655d2..81a57f01ea 100644
--- a/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap
+++ b/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap
@@ -911,6 +911,7 @@ exports[`PoolDetailsPage pool header is displayed when data is received from the
@@ -985,6 +986,7 @@ exports[`PoolDetailsPage pool header is displayed when data is received from the
diff --git a/src/pages/Swap/UniswapXOptIn.tsx b/src/pages/Swap/UniswapXOptIn.tsx
index 0ea44934d4..f75480ad6f 100644
--- a/src/pages/Swap/UniswapXOptIn.tsx
+++ b/src/pages/Swap/UniswapXOptIn.tsx
@@ -15,7 +15,7 @@ import {
UniswapXShine,
} from 'components/swap/styled'
import { formatCommonPropertiesForTrade } from 'lib/utils/analytics'
-import { PropsWithChildren, useEffect, useRef, useState } from 'react'
+import { PropsWithChildren, useRef, useState } from 'react'
import { X } from 'react-feather'
import { useLocation } from 'react-router-dom'
import { Text } from 'rebass'
@@ -76,16 +76,6 @@ const OptInContents = ({
const isVisible = isOnClassic
const location = useLocation()
- // adding this as we need to mount and then set shouldAnimate = true after it mounts to avoid a flicker on initial mount
- const [shouldAnimate, setShouldAnimate] = useState(false)
-
- useEffect(() => {
- if (!isVisible || shouldAnimate) return
- // delay visible animation a bit
- const tm = setTimeout(() => setShouldAnimate(true), 350)
- return () => clearTimeout(tm)
- }, [isVisible, shouldAnimate])
-
const tryItNowElement = (
+