Commit bd8113d0 authored by Tina's avatar Tina Committed by GitHub

chore: Goodbye widget :( (#6543)

* goodbye widget :(

* remove unused function

* modify trace tests

* fix lint

* is github down?
parent 14e3ef04
...@@ -8,6 +8,5 @@ updates: ...@@ -8,6 +8,5 @@ updates:
allow: allow:
- dependency-name: '@uniswap/default-token-list' - dependency-name: '@uniswap/default-token-list'
- dependency-name: '@uniswap/token-lists' - dependency-name: '@uniswap/token-lists'
- dependency-name: '@uniswap/widgets'
reviewers: reviewers:
- 'Uniswap/dependabot-reviewers' - 'Uniswap/dependabot-reviewers'
import { SupportedChainId, WETH9 } from '@uniswap/sdk-core' import { SupportedChainId, WETH9 } from '@uniswap/sdk-core'
import { UNI } from '../../src/constants/tokens' import { UNI } from '../../src/constants/tokens'
import { FeatureFlag } from '../../src/featureFlags/flags/featureFlags'
import { getTestSelector } from '../utils' import { getTestSelector } from '../utils'
const UNI_MAINNET = UNI[SupportedChainId.MAINNET] const UNI_MAINNET = UNI[SupportedChainId.MAINNET]
...@@ -96,7 +95,6 @@ describe('Token details', () => { ...@@ -96,7 +95,6 @@ describe('Token details', () => {
cy.viewport(1200, 800) cy.viewport(1200, 800)
cy.visit(`/tokens/ethereum/${UNI_MAINNET.address}`, { cy.visit(`/tokens/ethereum/${UNI_MAINNET.address}`, {
ethereum: 'hardhat', ethereum: 'hardhat',
featureFlags: [FeatureFlag.removeWidget],
}).then(() => { }).then(() => {
cy.wait('@eth_blockNumber') cy.wait('@eth_blockNumber')
cy.scrollTo('top') cy.scrollTo('top')
...@@ -121,7 +119,7 @@ describe('Token details', () => { ...@@ -121,7 +119,7 @@ describe('Token details', () => {
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'UNI') cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'UNI')
cy.get(`#swap-currency-input .open-currency-select-button`).click() cy.get(`#swap-currency-input .open-currency-select-button`).click()
cy.contains('WETH').click() cy.contains('WETH').click()
cy.visit('/swap', { featureFlags: [FeatureFlag.removeWidget] }) cy.visit('/swap')
cy.contains('UNI').should('not.exist') cy.contains('UNI').should('not.exist')
cy.contains('WETH').should('not.exist') cy.contains('WETH').should('not.exist')
}) })
...@@ -147,7 +145,7 @@ describe('Token details', () => { ...@@ -147,7 +145,7 @@ describe('Token details', () => {
}) })
it('should show a L2 token even if the user is connected to a different network', () => { it('should show a L2 token even if the user is connected to a different network', () => {
cy.visit('/tokens', { ethereum: 'hardhat', featureFlags: [FeatureFlag.removeWidget] }) cy.visit('/tokens', { ethereum: 'hardhat' })
cy.get(getTestSelector('tokens-network-filter-selected')).click() cy.get(getTestSelector('tokens-network-filter-selected')).click()
cy.get(getTestSelector('tokens-network-filter-option-arbitrum')).click() cy.get(getTestSelector('tokens-network-filter-option-arbitrum')).click()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Arbitrum') cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Arbitrum')
......
...@@ -98,8 +98,8 @@ ...@@ -98,8 +98,8 @@
"@types/ua-parser-js": "^0.7.35", "@types/ua-parser-js": "^0.7.35",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"@types/wcag-contrast": "^3.0.0", "@types/wcag-contrast": "^3.0.0",
"@uniswap/eslint-config": "^1.2.0",
"@uniswap/default-token-list": "^9.4.0", "@uniswap/default-token-list": "^9.4.0",
"@uniswap/eslint-config": "^1.2.0",
"@vanilla-extract/babel-plugin": "^1.1.7", "@vanilla-extract/babel-plugin": "^1.1.7",
"@vanilla-extract/jest-transform": "^1.1.1", "@vanilla-extract/jest-transform": "^1.1.1",
"@vanilla-extract/webpack-plugin": "^2.1.11", "@vanilla-extract/webpack-plugin": "^2.1.11",
...@@ -169,7 +169,6 @@ ...@@ -169,7 +169,6 @@
"@uniswap/v3-core": "1.0.0", "@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1", "@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.9.0", "@uniswap/v3-sdk": "^3.9.0",
"@uniswap/widgets": "^2.49.0",
"@vanilla-extract/css": "^1.7.2", "@vanilla-extract/css": "^1.7.2",
"@vanilla-extract/css-utils": "^0.1.2", "@vanilla-extract/css-utils": "^0.1.2",
"@vanilla-extract/dynamic": "^2.0.2", "@vanilla-extract/dynamic": "^2.0.2",
...@@ -254,18 +253,6 @@ ...@@ -254,18 +253,6 @@
"workbox-routing": "^6.1.0", "workbox-routing": "^6.1.0",
"zustand": "^4.3.6" "zustand": "^4.3.6"
}, },
"resolutions": {
"@web3-react/coinbase-wallet": "^8.2.0",
"@web3-react/core": "^8.2.0",
"@web3-react/eip1193": "^8.2.0",
"@web3-react/empty": "^8.2.0",
"@web3-react/gnosis-safe": "^8.2.0",
"@web3-react/metamask": "^8.2.0",
"@web3-react/network": "^8.2.0",
"@web3-react/types": "^8.2.0",
"@web3-react/url": "^8.2.0",
"@web3-react/walletconnect": "^8.2.0"
},
"engines": { "engines": {
"npm": "please-use-yarn", "npm": "please-use-yarn",
"node": "14", "node": "14",
......
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags' import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
import { DetailsV2Variant, useDetailsV2Flag } from 'featureFlags/flags/nftDetails' import { DetailsV2Variant, useDetailsV2Flag } from 'featureFlags/flags/nftDetails'
import { useWidgetRemovalFlag, WidgetRemovalVariant } from 'featureFlags/flags/removeWidgetTdp'
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc' import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
import { useUpdateAtom } from 'jotai/utils' import { useUpdateAtom } from 'jotai/utils'
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react' import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
...@@ -208,12 +207,6 @@ export default function FeatureFlagModal() { ...@@ -208,12 +207,6 @@ export default function FeatureFlagModal() {
featureFlag={FeatureFlag.detailsV2} featureFlag={FeatureFlag.detailsV2}
label="Use the new details page for nfts" label="Use the new details page for nfts"
/> />
<FeatureFlagOption
variant={WidgetRemovalVariant}
value={useWidgetRemovalFlag()}
featureFlag={FeatureFlag.removeWidget}
label="Swap Component on TDP"
/>
<FeatureFlagGroup name="Debug"> <FeatureFlagGroup name="Debug">
<FeatureFlagOption <FeatureFlagOption
variant={TraceJsonRpcVariant} variant={TraceJsonRpcVariant}
......
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { Trace } from '@uniswap/analytics' import { Trace } from '@uniswap/analytics'
import { InterfacePageName } from '@uniswap/analytics-events' import { InterfacePageName } from '@uniswap/analytics-events'
import { Currency } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import CurrencyLogo from 'components/Logo/CurrencyLogo' import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { AboutSection } from 'components/Tokens/TokenDetails/About' import { AboutSection } from 'components/Tokens/TokenDetails/About'
...@@ -22,19 +21,14 @@ import TokenDetailsSkeleton, { ...@@ -22,19 +21,14 @@ import TokenDetailsSkeleton, {
import StatsSection from 'components/Tokens/TokenDetails/StatsSection' import StatsSection from 'components/Tokens/TokenDetails/StatsSection'
import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage' import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal' import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import Widget from 'components/Widget'
import { SwapTokens } from 'components/Widget/inputs'
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens' import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { checkWarning } from 'constants/tokenSafety' import { checkWarning } from 'constants/tokenSafety'
import { useWidgetRemovalEnabled } from 'featureFlags/flags/removeWidgetTdp'
import { TokenPriceQuery } from 'graphql/data/__generated__/types-and-hooks' import { TokenPriceQuery } from 'graphql/data/__generated__/types-and-hooks'
import { Chain, TokenQuery, TokenQueryData } from 'graphql/data/Token' import { Chain, TokenQuery, TokenQueryData } from 'graphql/data/Token'
import { QueryToken } from 'graphql/data/Token' import { QueryToken } from 'graphql/data/Token'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util' import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util'
import { useIsUserAddedTokenOnChain } from 'hooks/Tokens'
import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch' import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch'
import { UNKNOWN_TOKEN_SYMBOL, useTokenFromActiveNetwork } from 'lib/hooks/useCurrency' import { UNKNOWN_TOKEN_SYMBOL, useTokenFromActiveNetwork } from 'lib/hooks/useCurrency'
import { getTokenAddress } from 'lib/utils/analytics'
import { Swap } from 'pages/Swap' import { Swap } from 'pages/Swap'
import { useCallback, useMemo, useState, useTransition } from 'react' import { useCallback, useMemo, useState, useTransition } from 'react'
import { ArrowLeft } from 'react-feather' import { ArrowLeft } from 'react-feather'
...@@ -130,12 +124,10 @@ export default function TokenDetails({ ...@@ -130,12 +124,10 @@ export default function TokenDetails({
) )
const { token: detailedToken, didFetchFromChain } = useRelevantToken(address, pageChainId, tokenQueryData) const { token: detailedToken, didFetchFromChain } = useRelevantToken(address, pageChainId, tokenQueryData)
const { token: widgetInputToken } = useRelevantToken(inputTokenAddress, pageChainId, undefined)
const tokenWarning = address ? checkWarning(address) : null const tokenWarning = address ? checkWarning(address) : null
const isBlockedToken = tokenWarning?.canProceed === false const isBlockedToken = tokenWarning?.canProceed === false
const navigate = useNavigate() const navigate = useNavigate()
const widgetRemovalEnabled = useWidgetRemovalEnabled()
// Wrapping navigate in a transition prevents Suspense from unnecessarily showing fallbacks again. // Wrapping navigate in a transition prevents Suspense from unnecessarily showing fallbacks again.
const [isPending, startTokenTransition] = useTransition() const [isPending, startTokenTransition] = useTransition()
...@@ -152,22 +144,6 @@ export default function TokenDetails({ ...@@ -152,22 +144,6 @@ export default function TokenDetails({
[address, crossChainMap, didFetchFromChain, navigate, detailedToken?.isNative] [address, crossChainMap, didFetchFromChain, navigate, detailedToken?.isNative]
) )
useOnGlobalChainSwitch(navigateToTokenForChain) useOnGlobalChainSwitch(navigateToTokenForChain)
const navigateToWidgetSelectedToken = useCallback(
(tokens: SwapTokens) => {
const newDefaultToken = tokens[Field.OUTPUT] ?? tokens.default
const address = newDefaultToken?.isNative ? NATIVE_CHAIN_ID : newDefaultToken?.address
startTokenTransition(() =>
navigate(
getTokenDetailsURL({
address,
chain,
inputAddress: tokens[Field.INPUT] ? getTokenAddress(tokens[Field.INPUT] as Currency) : null,
})
)
)
},
[chain, navigate]
)
const handleCurrencyChange = useCallback( const handleCurrencyChange = useCallback(
(tokens: Pick<SwapState, Field.INPUT | Field.OUTPUT>) => { (tokens: Pick<SwapState, Field.INPUT | Field.OUTPUT>) => {
...@@ -202,12 +178,6 @@ export default function TokenDetails({ ...@@ -202,12 +178,6 @@ export default function TokenDetails({
const [openTokenSafetyModal, setOpenTokenSafetyModal] = useState(false) const [openTokenSafetyModal, setOpenTokenSafetyModal] = useState(false)
const shouldShowSpeedbump = !useIsUserAddedTokenOnChain(address, pageChainId) && tokenWarning !== null
const onReviewSwapClick = useCallback(
() => new Promise<boolean>((resolve) => (shouldShowSpeedbump ? setContinueSwap({ resolve }) : resolve(true))),
[shouldShowSpeedbump]
)
const onResolveSwap = useCallback( const onResolveSwap = useCallback(
(value: boolean) => { (value: boolean) => {
continueSwap?.resolve(value) continueSwap?.resolve(value)
...@@ -268,26 +238,15 @@ export default function TokenDetails({ ...@@ -268,26 +238,15 @@ export default function TokenDetails({
<RightPanel onClick={() => isBlockedToken && setOpenTokenSafetyModal(true)}> <RightPanel onClick={() => isBlockedToken && setOpenTokenSafetyModal(true)}>
<div style={{ pointerEvents: isBlockedToken ? 'none' : 'auto' }}> <div style={{ pointerEvents: isBlockedToken ? 'none' : 'auto' }}>
{widgetRemovalEnabled ? ( <Swap
<Swap chainId={pageChainId}
chainId={pageChainId} prefilledState={{
prefilledState={{ [Field.INPUT]: { currencyId: inputTokenAddress },
[Field.INPUT]: { currencyId: inputTokenAddress }, [Field.OUTPUT]: { currencyId: address === NATIVE_CHAIN_ID ? 'ETH' : address },
[Field.OUTPUT]: { currencyId: address === NATIVE_CHAIN_ID ? 'ETH' : address }, }}
}} onCurrencyChange={handleCurrencyChange}
onCurrencyChange={handleCurrencyChange} disableTokenInputs={pageChainId !== connectedChainId}
disableTokenInputs={pageChainId !== connectedChainId} />
/>
) : (
<Widget
defaultTokens={{
[Field.INPUT]: widgetInputToken ?? undefined,
default: detailedToken ?? undefined,
}}
onDefaultTokenChange={navigateToWidgetSelectedToken}
onReviewSwapClick={onReviewSwapClick}
/>
)}
</div> </div>
{tokenWarning && <TokenSafetyMessage tokenAddress={address} warning={tokenWarning} />} {tokenWarning && <TokenSafetyMessage tokenAddress={address} warning={tokenWarning} />}
{detailedToken && <BalanceSummary token={detailedToken} />} {detailedToken && <BalanceSummary token={detailedToken} />}
......
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import {
InterfaceEventName,
InterfaceSectionName,
SwapEventName,
SwapPriceUpdateUserResponse,
} from '@uniswap/analytics-events'
import { Trade } from '@uniswap/router-sdk'
import { Currency, TradeType } from '@uniswap/sdk-core'
import {
AddEthereumChainParameter,
DialogAnimationType,
EMPTY_TOKEN_LIST,
OnReviewSwapClick,
SwapWidget,
SwapWidgetSkeleton,
} from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import { useToggleAccountDrawer } from 'components/AccountDrawer'
import { useActiveLocale } from 'hooks/useActiveLocale'
import {
formatPercentInBasisPointsNumber,
formatSwapQuoteReceivedEventProperties,
formatToDecimal,
getDurationFromDateMilliseconds,
getPriceUpdateBasisPoints,
getTokenAddress,
} from 'lib/utils/analytics'
import { useCallback, useState } from 'react'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
import { computeRealizedPriceImpact } from 'utils/prices'
import { switchChain } from 'utils/switchChain'
import { DefaultTokens, SwapTokens, useSyncWidgetInputs } from './inputs'
import { useSyncWidgetSettings } from './settings'
import { DARK_THEME, LIGHT_THEME } from './theme'
import { useSyncWidgetTransactions } from './transactions'
const DEFAULT_WIDGET_WIDTH = 360
const WIDGET_ROUTER_URL = 'https://api.uniswap.org/v1/'
function useWidgetTheme() {
return useIsDarkMode() ? DARK_THEME : LIGHT_THEME
}
interface WidgetProps {
defaultTokens: DefaultTokens
width?: number | string
onDefaultTokenChange?: (tokens: SwapTokens) => void
onReviewSwapClick?: OnReviewSwapClick
}
// TODO: Remove this component once the TDP is fully migrated to the swap component.
// eslint-disable-next-line import/no-unused-modules
export default function Widget({
defaultTokens,
width = DEFAULT_WIDGET_WIDTH,
onDefaultTokenChange,
onReviewSwapClick,
}: WidgetProps) {
const { connector, provider, chainId } = useWeb3React()
const locale = useActiveLocale()
const theme = useWidgetTheme()
const { inputs, tokenSelector } = useSyncWidgetInputs({
defaultTokens,
onDefaultTokenChange,
})
const { settings } = useSyncWidgetSettings()
const { transactions } = useSyncWidgetTransactions()
const toggleWalletDrawer = useToggleAccountDrawer()
const onConnectWalletClick = useCallback(() => {
toggleWalletDrawer()
return false // prevents the in-widget wallet modal from opening
}, [toggleWalletDrawer])
const onSwitchChain = useCallback(
// TODO(WEB-1757): Widget should not break if this rejects - upstream the catch to ignore it.
({ chainId }: AddEthereumChainParameter) => switchChain(connector, Number(chainId)).catch(() => undefined),
[connector]
)
const trace = useTrace({ section: InterfaceSectionName.WIDGET })
const [initialQuoteDate, setInitialQuoteDate] = useState<Date>()
const onInitialSwapQuote = useCallback(
(trade: Trade<Currency, Currency, TradeType>) => {
setInitialQuoteDate(new Date())
const eventProperties = {
// TODO(1416): Include undefined values.
...formatSwapQuoteReceivedEventProperties(
trade,
/* gasUseEstimateUSD= */ undefined,
/* fetchingSwapQuoteStartTime= */ undefined
),
...trace,
}
sendAnalyticsEvent(SwapEventName.SWAP_QUOTE_RECEIVED, eventProperties)
},
[trace]
)
const onApproveToken = useCallback(() => {
const input = inputs.value.INPUT
if (!input) return
const eventProperties = {
chain_id: input.chainId,
token_symbol: input.symbol,
token_address: getTokenAddress(input),
...trace,
}
sendAnalyticsEvent(InterfaceEventName.APPROVE_TOKEN_TXN_SUBMITTED, eventProperties)
}, [inputs.value.INPUT, trace])
const onExpandSwapDetails = useCallback(() => {
sendAnalyticsEvent(SwapEventName.SWAP_DETAILS_EXPANDED, { ...trace })
}, [trace])
const onSwapPriceUpdateAck = useCallback(
(stale: Trade<Currency, Currency, TradeType>, update: Trade<Currency, Currency, TradeType>) => {
const eventProperties = {
chain_id: update.inputAmount.currency.chainId,
response: SwapPriceUpdateUserResponse.ACCEPTED,
token_in_symbol: update.inputAmount.currency.symbol,
token_out_symbol: update.outputAmount.currency.symbol,
price_update_basis_points: getPriceUpdateBasisPoints(stale.executionPrice, update.executionPrice),
...trace,
}
sendAnalyticsEvent(SwapEventName.SWAP_PRICE_UPDATE_ACKNOWLEDGED, eventProperties)
},
[trace]
)
const onSubmitSwapClick = useCallback(
(trade: Trade<Currency, Currency, TradeType>) => {
const eventProperties = {
// TODO(1416): Include undefined values.
estimated_network_fee_usd: undefined,
transaction_deadline_seconds: undefined,
token_in_address: getTokenAddress(trade.inputAmount.currency),
token_out_address: getTokenAddress(trade.outputAmount.currency),
token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
token_in_amount_usd: undefined,
token_out_amount_usd: undefined,
price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)),
allowed_slippage_basis_points: undefined,
is_auto_router_api: undefined,
is_auto_slippage: undefined,
chain_id: trade.inputAmount.currency.chainId,
duration_from_first_quote_to_swap_submission_milliseconds: getDurationFromDateMilliseconds(initialQuoteDate),
swap_quote_block_number: undefined,
...trace,
}
sendAnalyticsEvent(SwapEventName.SWAP_SUBMITTED_BUTTON_CLICKED, eventProperties)
},
[initialQuoteDate, trace]
)
if (!(inputs.value.INPUT || inputs.value.OUTPUT)) {
return <WidgetSkeleton />
}
return (
<>
<div style={{ zIndex: 1, position: 'relative' }}>
<SwapWidget
hideConnectionUI
brandedFooter={false}
permit2
routerUrl={WIDGET_ROUTER_URL}
locale={locale}
theme={theme}
width={width}
defaultChainId={chainId}
onConnectWalletClick={onConnectWalletClick}
provider={provider}
onSwitchChain={onSwitchChain}
tokenList={EMPTY_TOKEN_LIST} // prevents loading the default token list, as we use our own token selector UI
{...inputs}
{...settings}
{...transactions}
onExpandSwapDetails={onExpandSwapDetails}
onReviewSwapClick={onReviewSwapClick}
onSubmitSwapClick={onSubmitSwapClick}
onSwapApprove={onApproveToken}
onInitialSwapQuote={onInitialSwapQuote}
onSwapPriceUpdateAck={onSwapPriceUpdateAck}
dialogOptions={{
pageCentered: true,
animationType: DialogAnimationType.FADE,
}}
onError={(error, errorInfo) => {
sendAnalyticsEvent(SwapEventName.SWAP_ERROR, { error, errorInfo, ...trace })
}}
/>
</div>
{tokenSelector}
</>
)
}
function WidgetSkeleton({ width = DEFAULT_WIDGET_WIDTH }: { width?: number | string }) {
const theme = useWidgetTheme()
return <SwapWidgetSkeleton theme={theme} width={width} />
}
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import { InterfaceSectionName, SwapEventName } from '@uniswap/analytics-events'
import { Currency, Field, SwapController, SwapEventHandlers, TradeType } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
import { isSupportedChain } from 'constants/chains'
import usePrevious from 'hooks/usePrevious'
import { useCallback, useEffect, useMemo, useState } from 'react'
const EMPTY_AMOUNT = ''
type SwapValue = Required<SwapController>['value']
export type SwapTokens = Pick<SwapValue, Field.INPUT | Field.OUTPUT> & { default?: Currency }
export type DefaultTokens = Partial<SwapTokens>
function missingDefaultToken(tokens: SwapTokens) {
if (!tokens.default) return false
return !tokens[Field.INPUT]?.equals(tokens.default) && !tokens[Field.OUTPUT]?.equals(tokens.default)
}
function currenciesEqual(a: Currency | undefined, b: Currency | undefined) {
if (a && b) {
return a.equals(b)
} else {
return !a && !b
}
}
function tokensEqual(a: SwapTokens | undefined, b: SwapTokens | undefined) {
if (!a || !b) {
return !a && !b
}
return (
currenciesEqual(a[Field.INPUT], b[Field.INPUT]) &&
currenciesEqual(a[Field.OUTPUT], b[Field.OUTPUT]) &&
currenciesEqual(a.default, b.default)
)
}
/**
* Integrates the Widget's inputs.
* Treats the Widget as a controlled component, using the app's own token selector for selection.
* Enforces that token is a part of the returned value.
*/
export function useSyncWidgetInputs({
defaultTokens,
onDefaultTokenChange,
}: {
defaultTokens: DefaultTokens
onDefaultTokenChange?: (tokens: SwapTokens) => void
}) {
const trace = useTrace({ section: InterfaceSectionName.WIDGET })
const { chainId } = useWeb3React()
const previousChainId = usePrevious(chainId)
const [type, setType] = useState<SwapValue['type']>(TradeType.EXACT_INPUT)
const [amount, setAmount] = useState<SwapValue['amount']>(EMPTY_AMOUNT)
const [tokens, setTokens] = useState<SwapTokens>({
...defaultTokens,
[Field.OUTPUT]: defaultTokens[Field.OUTPUT] ?? defaultTokens.default,
})
// The most recent set of defaults, which can be used to check when the defaults are actually changing.
const baseTokens = usePrevious(defaultTokens)
useEffect(() => {
if (!tokensEqual(baseTokens, defaultTokens)) {
const input = defaultTokens[Field.INPUT]
const output = defaultTokens[Field.OUTPUT] ?? defaultTokens.default
setTokens({
...defaultTokens,
[Field.OUTPUT]: currenciesEqual(output, input) ? undefined : output,
})
}
}, [baseTokens, defaultTokens])
/**
* Clear the tokens if the chain changes.
*/
useEffect(() => {
if (chainId !== previousChainId && !!previousChainId && isSupportedChain(chainId)) {
setTokens({
...defaultTokens,
[Field.OUTPUT]: defaultTokens[Field.OUTPUT] ?? defaultTokens.default,
})
setAmount(EMPTY_AMOUNT)
}
}, [chainId, defaultTokens, previousChainId, tokens])
const onAmountChange = useCallback(
(field: Field, amount: string, origin?: 'max') => {
if (origin === 'max') {
sendAnalyticsEvent(SwapEventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED, { ...trace })
}
setType(field === Field.INPUT ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT)
setAmount(amount)
},
[trace]
)
const onSwitchTokens = useCallback(() => {
sendAnalyticsEvent(SwapEventName.SWAP_TOKENS_REVERSED, { ...trace })
setType((type) => invertTradeType(type))
setTokens((tokens) => ({
[Field.INPUT]: tokens[Field.OUTPUT],
[Field.OUTPUT]: tokens[Field.INPUT],
default: tokens.default,
}))
}, [trace])
const [selectingField, setSelectingField] = useState<Field>()
const onTokenSelectorClick = useCallback((field: Field) => {
setSelectingField(field)
return false
}, [])
const onTokenSelect = useCallback(
(selectingToken: Currency) => {
if (selectingField === undefined) return
const otherField = invertField(selectingField)
const isFlip = tokens[otherField]?.equals(selectingToken)
const update: SwapTokens = {
[selectingField]: selectingToken,
[otherField]: isFlip ? tokens[selectingField] : tokens[otherField],
default: tokens.default,
}
setType((type) => {
// If flipping the tokens, also flip the type/amount.
if (isFlip) {
return invertTradeType(type)
}
// Setting a new token should clear its amount, if it is set.
const activeField = type === TradeType.EXACT_INPUT ? Field.INPUT : Field.OUTPUT
if (selectingField === activeField) {
setAmount(() => EMPTY_AMOUNT)
}
return type
})
if (missingDefaultToken(update)) {
onDefaultTokenChange?.({
...update,
default: update[Field.OUTPUT] ?? selectingToken,
})
return
}
setTokens(update)
},
[onDefaultTokenChange, selectingField, tokens]
)
const tokenSelector = (
<CurrencySearchModal
isOpen={selectingField !== undefined}
onDismiss={() => setSelectingField(undefined)}
selectedCurrency={selectingField && tokens[selectingField]}
otherSelectedCurrency={selectingField && tokens[invertField(selectingField)]}
onCurrencySelect={onTokenSelect}
showCommonBases
/>
)
const value: SwapValue = useMemo(
() => ({
type,
amount,
// If the initial state has not yet been set, preemptively disable the widget by passing no tokens. Effectively,
// this resets the widget - avoiding rendering stale state - because with no tokens the skeleton will be rendered.
...(tokens[Field.INPUT] || tokens[Field.OUTPUT] ? tokens : undefined),
}),
[amount, tokens, type]
)
const valueHandlers: SwapEventHandlers = useMemo(
() => ({ onAmountChange, onSwitchTokens, onTokenSelectorClick }),
[onAmountChange, onSwitchTokens, onTokenSelectorClick]
)
return { inputs: { value, ...valueHandlers }, tokenSelector }
}
// TODO(zzmp): Move to @uniswap/widgets.
function invertField(field: Field) {
switch (field) {
case Field.INPUT:
return Field.OUTPUT
case Field.OUTPUT:
return Field.INPUT
}
}
// TODO(zzmp): Include in @uniswap/sdk-core (on TradeType, if possible).
function invertTradeType(tradeType: TradeType) {
switch (tradeType) {
case TradeType.EXACT_INPUT:
return TradeType.EXACT_OUTPUT
case TradeType.EXACT_OUTPUT:
return TradeType.EXACT_INPUT
}
}
import { Percent } from '@uniswap/sdk-core'
import { RouterPreference, Slippage, SwapController, SwapEventHandlers } from '@uniswap/widgets'
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
import { useCallback, useMemo, useState } from 'react'
import { useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
import { SlippageTolerance } from 'state/user/types'
/**
* Integrates the Widget's settings, keeping the widget and app settings in sync.
* NB: This acts as an integration layer, so certain values are duplicated in order to translate
* between app and widget representations.
*/
export function useSyncWidgetSettings() {
const [appTtl, setAppTtl] = useUserTransactionTTL()
const [widgetTtl, setWidgetTtl] = useState<number | undefined>(appTtl / 60)
const onTransactionDeadlineChange = useCallback(
(widgetTtl: number | undefined) => {
setWidgetTtl(widgetTtl)
const appTtl = widgetTtl === undefined ? widgetTtl : widgetTtl * 60
setAppTtl(appTtl ?? DEFAULT_DEADLINE_FROM_NOW)
},
[setAppTtl]
)
const [appSlippage, setAppSlippage] = useUserSlippageTolerance()
const [widgetSlippage, setWidgetSlippage] = useState<string | undefined>(
appSlippage === SlippageTolerance.Auto ? undefined : appSlippage.toFixed(2)
)
const onSlippageChange = useCallback(
(widgetSlippage: Slippage) => {
setWidgetSlippage(widgetSlippage.max)
if (widgetSlippage.auto || !widgetSlippage.max) {
setAppSlippage(SlippageTolerance.Auto)
} else {
setAppSlippage(new Percent(Math.floor(Number(widgetSlippage.max) * 100), 10_000))
}
},
[setAppSlippage]
)
const [routerPreference, onRouterPreferenceChange] = useState(RouterPreference.API)
const onSettingsReset = useCallback(() => {
setWidgetTtl(undefined)
setAppTtl(DEFAULT_DEADLINE_FROM_NOW)
setWidgetSlippage(undefined)
setAppSlippage(SlippageTolerance.Auto)
}, [setAppSlippage, setAppTtl])
const settings: SwapController['settings'] = useMemo(() => {
const auto = appSlippage === SlippageTolerance.Auto
return {
slippage: { auto, max: widgetSlippage },
transactionTtl: widgetTtl,
routerPreference,
}
}, [appSlippage, widgetSlippage, widgetTtl, routerPreference])
const settingsHandlers: SwapEventHandlers = useMemo(
() => ({ onSettingsReset, onSlippageChange, onTransactionDeadlineChange, onRouterPreferenceChange }),
[onSettingsReset, onSlippageChange, onTransactionDeadlineChange, onRouterPreferenceChange]
)
return { settings: { settings, ...settingsHandlers } }
}
import { Theme } from '@uniswap/widgets'
import { darkTheme, lightTheme } from 'theme/colors'
import { Z_INDEX } from 'theme/zIndex'
const zIndex = {
modal: Z_INDEX.modal,
}
const fonts = {
fontFamily: 'Inter custom',
}
export const LIGHT_THEME: Theme = {
// surface
accent: lightTheme.accentAction,
accentSoft: lightTheme.accentActionSoft,
container: lightTheme.backgroundSurface,
module: lightTheme.backgroundModule,
interactive: lightTheme.backgroundInteractive,
outline: lightTheme.backgroundOutline,
dialog: lightTheme.backgroundBackdrop,
scrim: lightTheme.backgroundScrim,
// text
onAccent: lightTheme.white,
primary: lightTheme.textPrimary,
secondary: lightTheme.textSecondary,
hint: lightTheme.textTertiary,
onInteractive: lightTheme.accentTextDarkPrimary,
// shadow
deepShadow: lightTheme.deepShadow,
networkDefaultShadow: lightTheme.networkDefaultShadow,
// state
success: lightTheme.accentSuccess,
warning: lightTheme.accentWarning,
error: lightTheme.accentCritical,
...fonts,
zIndex,
}
export const DARK_THEME: Theme = {
// surface
accent: darkTheme.accentAction,
accentSoft: darkTheme.accentActionSoft,
container: darkTheme.backgroundSurface,
module: darkTheme.backgroundModule,
interactive: darkTheme.backgroundInteractive,
outline: darkTheme.backgroundOutline,
dialog: darkTheme.backgroundBackdrop,
scrim: darkTheme.backgroundScrim,
// text
onAccent: darkTheme.white,
primary: darkTheme.textPrimary,
secondary: darkTheme.textSecondary,
hint: darkTheme.textTertiary,
onInteractive: darkTheme.accentTextLightPrimary,
// shadow
deepShadow: darkTheme.deepShadow,
networkDefaultShadow: darkTheme.networkDefaultShadow,
// state
success: darkTheme.accentSuccess,
warning: darkTheme.accentWarning,
error: darkTheme.accentCritical,
...fonts,
zIndex,
}
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import { InterfaceEventName, InterfaceSectionName, SwapEventName } from '@uniswap/analytics-events'
import { Trade } from '@uniswap/router-sdk'
import { Currency, Percent } from '@uniswap/sdk-core'
import {
OnTxSuccess,
TradeType,
Transaction,
TransactionEventHandlers,
TransactionInfo,
TransactionType,
TransactionType as WidgetTransactionType,
} from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import {
formatPercentInBasisPointsNumber,
formatSwapSignedAnalyticsEventProperties,
formatToDecimal,
getTokenAddress,
} from 'lib/utils/analytics'
import { useCallback, useMemo } from 'react'
import { useTransactionAdder } from 'state/transactions/hooks'
import {
ExactInputSwapTransactionInfo,
ExactOutputSwapTransactionInfo,
TransactionType as AppTransactionType,
WrapTransactionInfo,
} from 'state/transactions/types'
import { currencyId } from 'utils/currencyId'
import { computeRealizedPriceImpact } from 'utils/prices'
interface AnalyticsEventProps {
trade: Trade<Currency, Currency, TradeType>
gasUsed: string | undefined
blockNumber: number | undefined
hash: string | undefined
allowedSlippage: Percent
succeeded: boolean
}
const formatAnalyticsEventProperties = ({
trade,
hash,
allowedSlippage,
succeeded,
gasUsed,
blockNumber,
}: AnalyticsEventProps) => ({
estimated_network_fee_usd: gasUsed,
transaction_hash: hash,
token_in_address: getTokenAddress(trade.inputAmount.currency),
token_out_address: getTokenAddress(trade.outputAmount.currency),
token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)),
allowed_slippage_basis_points: formatPercentInBasisPointsNumber(allowedSlippage),
chain_id:
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId
? trade.inputAmount.currency.chainId
: undefined,
swap_quote_block_number: blockNumber,
succeeded,
})
/** Integrates the Widget's transactions, showing the widget's transactions in the app. */
export function useSyncWidgetTransactions() {
const trace = useTrace({ section: InterfaceSectionName.WIDGET })
const { chainId } = useWeb3React()
const addTransaction = useTransactionAdder()
const onTxSubmit = useCallback(
(_hash: string, transaction: Transaction<TransactionInfo>) => {
const { type, response } = transaction.info
if (!type || !response) {
return
} else if (type === WidgetTransactionType.WRAP || type === WidgetTransactionType.UNWRAP) {
const { type, amount: transactionAmount } = transaction.info
const eventProperties = {
// get this info from widget handlers
token_in_address: getTokenAddress(transactionAmount.currency),
token_out_address: getTokenAddress(transactionAmount.currency.wrapped),
token_in_symbol: transactionAmount.currency.symbol,
token_out_symbol: transactionAmount.currency.wrapped.symbol,
chain_id: transactionAmount.currency.chainId,
amount: transactionAmount
? formatToDecimal(transactionAmount, transactionAmount?.currency.decimals)
: undefined,
type: type === WidgetTransactionType.WRAP ? TransactionType.WRAP : TransactionType.UNWRAP,
...trace,
}
sendAnalyticsEvent(InterfaceEventName.WRAP_TOKEN_TXN_SUBMITTED, eventProperties)
const { amount } = transaction.info
addTransaction(response, {
type: AppTransactionType.WRAP,
unwrapped: type === WidgetTransactionType.UNWRAP,
currencyAmountRaw: amount.quotient.toString(),
chainId,
} as WrapTransactionInfo)
} else if (type === WidgetTransactionType.SWAP) {
const { slippageTolerance, trade, tradeType } = transaction.info
const eventProperties = {
...formatSwapSignedAnalyticsEventProperties({
trade,
// TODO: add once Widgets adds fiat values to callback
fiatValues: { amountIn: undefined, amountOut: undefined },
txHash: transaction.receipt?.transactionHash ?? '',
}),
...trace,
}
sendAnalyticsEvent(SwapEventName.SWAP_SIGNED, eventProperties)
const baseTxInfo = {
type: AppTransactionType.SWAP,
tradeType,
inputCurrencyId: currencyId(trade.inputAmount.currency),
outputCurrencyId: currencyId(trade.outputAmount.currency),
}
if (tradeType === TradeType.EXACT_OUTPUT) {
addTransaction(response, {
...baseTxInfo,
maximumInputCurrencyAmountRaw: trade.maximumAmountIn(slippageTolerance).quotient.toString(),
outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
} as ExactOutputSwapTransactionInfo)
} else {
addTransaction(response, {
...baseTxInfo,
inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(slippageTolerance).quotient.toString(),
} as ExactInputSwapTransactionInfo)
}
}
},
[addTransaction, chainId, trace]
)
const onTxSuccess: OnTxSuccess = useCallback((hash: string, tx) => {
if (tx.info.type === TransactionType.SWAP) {
const { trade, slippageTolerance } = tx.info
sendAnalyticsEvent(
SwapEventName.SWAP_TRANSACTION_COMPLETED,
formatAnalyticsEventProperties({
trade,
hash,
gasUsed: tx.receipt?.gasUsed?.toString(),
blockNumber: tx.receipt?.blockNumber,
allowedSlippage: slippageTolerance,
succeeded: tx.receipt?.status === 1,
})
)
}
}, [])
const txHandlers: TransactionEventHandlers = useMemo(() => ({ onTxSubmit, onTxSuccess }), [onTxSubmit, onTxSuccess])
return { transactions: { ...txHandlers } }
}
...@@ -6,5 +6,4 @@ export enum FeatureFlag { ...@@ -6,5 +6,4 @@ export enum FeatureFlag {
permit2 = 'permit2', permit2 = 'permit2',
fiatOnRampButtonOnSwap = 'fiat_on_ramp_button_on_swap_page', fiatOnRampButtonOnSwap = 'fiat_on_ramp_button_on_swap_page',
detailsV2 = 'details_v2', detailsV2 = 'details_v2',
removeWidget = 'remove_widget_tdp',
} }
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
export function useWidgetRemovalFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.removeWidget, BaseVariant.Control)
}
export function useWidgetRemovalEnabled(): boolean {
return useWidgetRemovalFlag() === BaseVariant.Enabled
}
export { BaseVariant as WidgetRemovalVariant }
...@@ -12,7 +12,7 @@ import { isL2ChainId } from 'utils/chains' ...@@ -12,7 +12,7 @@ import { isL2ChainId } from 'utils/chains'
import { useAllLists, useCombinedActiveList, useCombinedTokenMapFromUrls } from '../state/lists/hooks' import { useAllLists, useCombinedActiveList, useCombinedTokenMapFromUrls } from '../state/lists/hooks'
import { WrappedTokenInfo } from '../state/lists/wrappedTokenInfo' import { WrappedTokenInfo } from '../state/lists/wrappedTokenInfo'
import { deserializeToken, useUserAddedTokens, useUserAddedTokensOnChain } from '../state/user/hooks' import { deserializeToken, useUserAddedTokens } from '../state/user/hooks'
import { useUnsupportedTokenList } from './../state/lists/hooks' import { useUnsupportedTokenList } from './../state/lists/hooks'
type Maybe<T> = T | null | undefined type Maybe<T> = T | null | undefined
...@@ -182,20 +182,6 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool ...@@ -182,20 +182,6 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool
return !!userAddedTokens.find((token) => currency.equals(token)) return !!userAddedTokens.find((token) => currency.equals(token))
} }
// Check if currency on specific chain is included in custom list from user storage
export function useIsUserAddedTokenOnChain(
address: string | undefined | null,
chain: number | undefined | null
): boolean {
const userAddedTokens = useUserAddedTokensOnChain(chain)
if (!address || !chain) {
return false
}
return !!userAddedTokens.find((token) => token.address === address)
}
// undefined if invalid or does not exist // undefined if invalid or does not exist
// null if loading or null was passed // null if loading or null was passed
// otherwise returns the token // otherwise returns the token
......
...@@ -52,77 +52,73 @@ export function useUniversalRouterSwapCallback( ...@@ -52,77 +52,73 @@ export function useUniversalRouterSwapCallback(
const analyticsContext = useTrace() const analyticsContext = useTrace()
return useCallback(async (): Promise<TransactionResponse> => { return useCallback(async (): Promise<TransactionResponse> => {
return trace( return trace('swap.send', async ({ setTraceData, setTraceStatus, setTraceError }) => {
'swap.send', try {
async ({ setTraceData, setTraceStatus, setTraceError }) => { if (!account) throw new Error('missing account')
try { if (!chainId) throw new Error('missing chainId')
if (!account) throw new Error('missing account') if (!provider) throw new Error('missing provider')
if (!chainId) throw new Error('missing chainId') if (!trade) throw new Error('missing trade')
if (!provider) throw new Error('missing provider')
if (!trade) throw new Error('missing trade')
setTraceData('slippageTolerance', options.slippageTolerance.toFixed(2)) setTraceData('slippageTolerance', options.slippageTolerance.toFixed(2))
const { calldata: data, value } = SwapRouter.swapERC20CallParameters(trade, { const { calldata: data, value } = SwapRouter.swapERC20CallParameters(trade, {
slippageTolerance: options.slippageTolerance, slippageTolerance: options.slippageTolerance,
deadlineOrPreviousBlockhash: options.deadline?.toString(), deadlineOrPreviousBlockhash: options.deadline?.toString(),
inputTokenPermit: options.permit, inputTokenPermit: options.permit,
fee: options.feeOptions, fee: options.feeOptions,
}) })
const tx = { const tx = {
from: account, from: account,
to: UNIVERSAL_ROUTER_ADDRESS(chainId), to: UNIVERSAL_ROUTER_ADDRESS(chainId),
data, data,
// TODO(https://github.com/Uniswap/universal-router-sdk/issues/113): universal-router-sdk returns a non-hexlified value. // TODO(https://github.com/Uniswap/universal-router-sdk/issues/113): universal-router-sdk returns a non-hexlified value.
...(value && !isZero(value) ? { value: toHex(value) } : {}), ...(value && !isZero(value) ? { value: toHex(value) } : {}),
} }
let gasEstimate: BigNumber let gasEstimate: BigNumber
try { try {
gasEstimate = await provider.estimateGas(tx) gasEstimate = await provider.estimateGas(tx)
} catch (gasError) { } catch (gasError) {
setTraceStatus('failed_precondition') setTraceStatus('failed_precondition')
setTraceError(gasError) setTraceError(gasError)
console.warn(gasError) console.warn(gasError)
throw new GasEstimationError() throw new GasEstimationError()
} }
const gasLimit = calculateGasMargin(gasEstimate) const gasLimit = calculateGasMargin(gasEstimate)
setTraceData('gasLimit', gasLimit.toNumber()) setTraceData('gasLimit', gasLimit.toNumber())
const response = await provider const response = await provider
.getSigner() .getSigner()
.sendTransaction({ ...tx, gasLimit }) .sendTransaction({ ...tx, gasLimit })
.then((response) => { .then((response) => {
sendAnalyticsEvent(SwapEventName.SWAP_SIGNED, { sendAnalyticsEvent(SwapEventName.SWAP_SIGNED, {
...formatSwapSignedAnalyticsEventProperties({ ...formatSwapSignedAnalyticsEventProperties({
trade, trade,
fiatValues, fiatValues,
txHash: response.hash, txHash: response.hash,
}), }),
...analyticsContext,
})
if (tx.data !== response.data) {
sendAnalyticsEvent(SwapEventName.SWAP_MODIFIED_IN_WALLET, {
txHash: response.hash,
...analyticsContext, ...analyticsContext,
}) })
if (tx.data !== response.data) { throw new ModifiedSwapError()
sendAnalyticsEvent(SwapEventName.SWAP_MODIFIED_IN_WALLET, { }
txHash: response.hash, return response
...analyticsContext, })
}) return response
throw new ModifiedSwapError() } catch (swapError: unknown) {
} if (swapError instanceof ModifiedSwapError) throw swapError
return response
})
return response
} catch (swapError: unknown) {
if (swapError instanceof ModifiedSwapError) throw swapError
// Cancellations are not failures, and must be accounted for as 'cancelled'. // Cancellations are not failures, and must be accounted for as 'cancelled'.
if (didUserReject(swapError)) setTraceStatus('cancelled') if (didUserReject(swapError)) setTraceStatus('cancelled')
// GasEstimationErrors are already traced when they are thrown. // GasEstimationErrors are already traced when they are thrown.
if (!(swapError instanceof GasEstimationError)) setTraceError(swapError) if (!(swapError instanceof GasEstimationError)) setTraceError(swapError)
throw new Error(swapErrorToUserReadableMessage(swapError)) throw new Error(swapErrorToUserReadableMessage(swapError))
} }
}, })
{ tags: { is_widget: false } }
)
}, [ }, [
account, account,
analyticsContext, analyticsContext,
......
...@@ -6,7 +6,6 @@ import { InterfacePageName } from '@uniswap/analytics-events' ...@@ -6,7 +6,6 @@ import { InterfacePageName } from '@uniswap/analytics-events'
import { formatPrice, NumberType } from '@uniswap/conedison/format' import { formatPrice, NumberType } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core'
import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk' import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk'
import { SupportedChainId } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { sendEvent } from 'components/analytics' import { sendEvent } from 'components/analytics'
import Badge from 'components/Badge' import Badge from 'components/Badge'
...@@ -20,7 +19,7 @@ import { RowBetween, RowFixed } from 'components/Row' ...@@ -20,7 +19,7 @@ import { RowBetween, RowFixed } from 'components/Row'
import { Dots } from 'components/swap/styleds' import { Dots } from 'components/swap/styleds'
import Toggle from 'components/Toggle' import Toggle from 'components/Toggle'
import TransactionConfirmationModal, { ConfirmationModalContent } from 'components/TransactionConfirmationModal' import TransactionConfirmationModal, { ConfirmationModalContent } from 'components/TransactionConfirmationModal'
import { CHAIN_IDS_TO_NAMES, isSupportedChain } from 'constants/chains' import { CHAIN_IDS_TO_NAMES, isSupportedChain, SupportedChainId } from 'constants/chains'
import { isGqlSupportedChain } from 'graphql/data/util' import { isGqlSupportedChain } from 'graphql/data/util'
import { useToken } from 'hooks/Tokens' import { useToken } from 'hooks/Tokens'
import { useV3NFTPositionManagerContract } from 'hooks/useContract' import { useV3NFTPositionManagerContract } from 'hooks/useContract'
......
...@@ -115,7 +115,6 @@ export const routingApi = createApi({ ...@@ -115,7 +115,6 @@ export const routingApi = createApi({
isAutoRouter: isAutoRouter:
args.routerPreference === RouterPreference.AUTO || args.routerPreference === RouterPreference.API, args.routerPreference === RouterPreference.AUTO || args.routerPreference === RouterPreference.API,
}, },
tags: { is_widget: false },
} }
) )
}, },
......
...@@ -209,7 +209,7 @@ export function useAddUserToken(): (token: Token) => void { ...@@ -209,7 +209,7 @@ export function useAddUserToken(): (token: Token) => void {
) )
} }
export function useUserAddedTokensOnChain(chainId: number | undefined | null): Token[] { function useUserAddedTokensOnChain(chainId: number | undefined | null): Token[] {
const serializedTokensMap = useAppSelector(({ user: { tokens } }) => tokens) const serializedTokensMap = useAppSelector(({ user: { tokens } }) => tokens)
return useMemo(() => { return useMemo(() => {
......
...@@ -38,12 +38,13 @@ describe('trace', () => { ...@@ -38,12 +38,13 @@ describe('trace', () => {
}) })
it('records transaction', async () => { it('records transaction', async () => {
const metadata = { data: { a: 'a', b: 2 }, tags: { is_widget: true } } const metadata = { data: { a: 'a', b: 2 }, tags: { test_tag: true } }
// @ts-ignore test_tag is not an expected key for `tags` but force it for testing purposes
await trace('test', () => Promise.resolve(), metadata) await trace('test', () => Promise.resolve(), metadata)
const transaction = getTransaction() const transaction = getTransaction()
expect(transaction.name).toBe('test') expect(transaction.name).toBe('test')
expect(transaction.data).toEqual({ a: 'a', b: 2 }) expect(transaction.data).toEqual({ a: 'a', b: 2 })
expect(transaction.tags).toEqual({ is_widget: true }) expect(transaction.tags).toEqual({ test_tag: true })
}) })
describe('defaults status', () => { describe('defaults status', () => {
...@@ -77,11 +78,12 @@ describe('trace', () => { ...@@ -77,11 +78,12 @@ describe('trace', () => {
describe('setTraceTag', () => { describe('setTraceTag', () => {
it('sets a transaction tag', async () => { it('sets a transaction tag', async () => {
await trace('test', ({ setTraceTag }) => { await trace('test', ({ setTraceTag }) => {
setTraceTag('is_widget', true) // @ts-ignore test_tag is not an expected key for `tags` but force it for testing purposes
setTraceTag('test_tag', true)
return Promise.resolve() return Promise.resolve()
}) })
const transaction = getTransaction() const transaction = getTransaction()
expect(transaction.tags).toEqual({ is_widget: true }) expect(transaction.tags).toEqual({ test_tag: true })
}) })
}) })
...@@ -166,7 +168,8 @@ describe('trace', () => { ...@@ -166,7 +168,8 @@ describe('trace', () => {
describe('traceChild', () => { describe('traceChild', () => {
it('starts a span under a transaction', async () => { it('starts a span under a transaction', async () => {
await trace('test', ({ traceChild }) => { await trace('test', ({ traceChild }) => {
traceChild('child', () => Promise.resolve(), { data: { e: 'e' }, tags: { is_widget: true } }) // @ts-ignore test_tag is not an expected key for `tags` but force it for testing purposes
traceChild('child', () => Promise.resolve(), { data: { e: 'e' }, tags: { test_tag: true } })
return Promise.resolve() return Promise.resolve()
}) })
const transaction = getTransaction() const transaction = getTransaction()
...@@ -174,7 +177,7 @@ describe('trace', () => { ...@@ -174,7 +177,7 @@ describe('trace', () => {
assert(span) assert(span)
expect(span.op).toBe('child') expect(span.op).toBe('child')
expect(span.data).toEqual({ e: 'e' }) expect(span.data).toEqual({ e: 'e' })
expect(span.tags).toEqual({ is_widget: true }) expect(span.tags).toEqual({ test_tag: true })
}) })
}) })
}) })
import * as Sentry from '@sentry/react' import * as Sentry from '@sentry/react'
import { Span, SpanStatusType } from '@sentry/tracing' import { Span, SpanStatusType } from '@sentry/tracing'
type TraceTags = { // Modify this type if you want to add any Trace tags.
is_widget: boolean type TraceTags = Record<string, never>
}
interface TraceMetadata { interface TraceMetadata {
/** Arbitrary data stored on a trace. */ /** Arbitrary data stored on a trace. */
......
This diff is collapsed.
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