fix: pricing sources (#6156)

* Revert "fix: "revert feat: Use ETH based pricing instead of USDC based pricing for quote USD values (#6132) (#6150)"

This reverts commit 645d92185a9e3a60d6ae92943515b4cb426dfb53.

* Revert "fix: Use non-subgraph fields for calculating USD prices on explore and token details pages (#6134)"

This reverts commit 5c8b45c8e5d7b3a485537867960a8d948221af4e.
This commit is contained in:
cartcrom 2023-03-14 14:37:58 -04:00 committed by GitHub
parent acc6b0a740
commit 4bdef2b601
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 290 additions and 184 deletions

@ -49,6 +49,10 @@ describe('Token details', () => {
// Shiba predator token, low trading volume and also has warning modal // Shiba predator token, low trading volume and also has warning modal
cy.visit('/tokens/ethereum/0xa71d0588EAf47f12B13cF8eC750430d21DF04974') cy.visit('/tokens/ethereum/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
// Should have missing price chart when price unavailable (expected for this token)
if (cy.get('[data-cy="chart-header"]').contains('Price Unavailable')) {
cy.get('[data-cy="missing-chart"]').should('exist')
}
// Stats should have: TVL, 24H Volume, 52W low, 52W high // Stats should have: TVL, 24H Volume, 52W low, 52W high
cy.get(getTestSelector('token-details-stats')).should('exist') cy.get(getTestSelector('token-details-stats')).should('exist')
cy.get(getTestSelector('token-details-stats')).within(() => { cy.get(getTestSelector('token-details-stats')).within(() => {

@ -1,11 +1,11 @@
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import { t } from '@lingui/macro' import { t } from '@lingui/macro'
import { formatCurrencyAmount, formatPriceImpact, NumberType } from '@uniswap/conedison/format' import { formatNumber, formatPriceImpact, NumberType } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' import { Percent } from '@uniswap/sdk-core'
import { LoadingBubble } from 'components/Tokens/loading' import { LoadingBubble } from 'components/Tokens/loading'
import { MouseoverTooltip } from 'components/Tooltip' import { MouseoverTooltip } from 'components/Tooltip'
import { useEffect, useMemo, useState } from 'react' import { useMemo } from 'react'
import styled, { useTheme } from 'styled-components/macro' import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from '../../theme' import { ThemedText } from '../../theme'
@ -20,11 +20,9 @@ const FiatLoadingBubble = styled(LoadingBubble)`
export function FiatValue({ export function FiatValue({
fiatValue, fiatValue,
priceImpact, priceImpact,
isLoading = false,
}: { }: {
fiatValue: CurrencyAmount<Currency> | null | undefined fiatValue?: { data?: number; isLoading: boolean }
priceImpact?: Percent priceImpact?: Percent
isLoading?: boolean
}) { }) {
const theme = useTheme() const theme = useTheme()
const priceImpactColor = useMemo(() => { const priceImpactColor = useMemo(() => {
@ -35,27 +33,14 @@ export function FiatValue({
if (severity < 3) return theme.deprecated_yellow1 if (severity < 3) return theme.deprecated_yellow1
return theme.accentFailure return theme.accentFailure
}, [priceImpact, theme.accentSuccess, theme.accentFailure, theme.textTertiary, theme.deprecated_yellow1]) }, [priceImpact, theme.accentSuccess, theme.accentFailure, theme.textTertiary, theme.deprecated_yellow1])
const [showLoadingPlaceholder, setShowLoadingPlaceholder] = useState(false)
useEffect(() => {
const stale = false
let timeoutId = 0
if (isLoading && !fiatValue) {
timeoutId = setTimeout(() => {
if (!stale) setShowLoadingPlaceholder(true)
}, 200) as unknown as number
} else {
setShowLoadingPlaceholder(false)
}
return () => clearTimeout(timeoutId)
}, [isLoading, fiatValue])
return ( return (
<ThemedText.DeprecatedBody fontSize={14} color={theme.textSecondary}> <ThemedText.DeprecatedBody fontSize={14} color={theme.textSecondary}>
{showLoadingPlaceholder ? ( {fiatValue?.isLoading ? (
<FiatLoadingBubble /> <FiatLoadingBubble />
) : ( ) : (
<div> <div>
{fiatValue ? <>{formatCurrencyAmount(fiatValue, NumberType.FiatTokenPrice)}</> : undefined} {fiatValue?.data ? formatNumber(fiatValue.data, NumberType.FiatTokenPrice) : undefined}
{priceImpact && ( {priceImpact && (
<span style={{ color: priceImpactColor }}> <span style={{ color: priceImpactColor }}>
{' '} {' '}

@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { TraceEvent } from '@uniswap/analytics' import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events' import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk' import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { AutoColumn } from 'components/Column' import { AutoColumn } from 'components/Column'
@ -195,7 +195,7 @@ interface SwapCurrencyInputPanelProps {
pair?: Pair | null pair?: Pair | null
hideInput?: boolean hideInput?: boolean
otherCurrency?: Currency | null otherCurrency?: Currency | null
fiatValue: CurrencyAmount<Token> | null fiatValue: { data?: number; isLoading: boolean }
priceImpact?: Percent priceImpact?: Percent
id: string id: string
showCommonBases?: boolean showCommonBases?: boolean

@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { TraceEvent } from '@uniswap/analytics' import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events' import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk' import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { AutoColumn } from 'components/Column' import { AutoColumn } from 'components/Column'
@ -182,7 +182,7 @@ interface CurrencyInputPanelProps {
pair?: Pair | null pair?: Pair | null
hideInput?: boolean hideInput?: boolean
otherCurrency?: Currency | null otherCurrency?: Currency | null
fiatValue?: CurrencyAmount<Token> | null fiatValue?: { data?: number; isLoading: boolean }
priceImpact?: Percent priceImpact?: Percent
id: string id: string
showCommonBases?: boolean showCommonBases?: boolean

@ -156,7 +156,7 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
} }
}, [toggleOpen, isHovered, token, navigate, handleClick, tokenDetailsPath]) }, [toggleOpen, isHovered, token, navigate, handleClick, tokenDetailsPath])
const arrow = getDeltaArrow(token.project?.markets?.[0]?.pricePercentChange?.value, 18) const arrow = getDeltaArrow(token.market?.pricePercentChange?.value, 18)
return ( return (
<Link <Link
@ -186,16 +186,16 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
</Row> </Row>
<Column className={styles.suggestionSecondaryContainer}> <Column className={styles.suggestionSecondaryContainer}>
{!!token.project?.markets?.[0]?.price?.value && ( {!!token.market?.price?.value && (
<> <>
<Row gap="4"> <Row gap="4">
<Box className={styles.primaryText}>{formatUSDPrice(token.project.markets[0].price.value)}</Box> <Box className={styles.primaryText}>{formatUSDPrice(token.market.price.value)}</Box>
</Row> </Row>
<PriceChangeContainer> <PriceChangeContainer>
<ArrowCell>{arrow}</ArrowCell> <ArrowCell>{arrow}</ArrowCell>
<ThemedText.BodySmall> <ThemedText.BodySmall>
<DeltaText delta={token.project.markets[0].pricePercentChange?.value}> <DeltaText delta={token.market?.pricePercentChange?.value}>
{Math.abs(token.project.markets[0].pricePercentChange?.value ?? 0).toFixed(2)}% {Math.abs(token.market?.pricePercentChange?.value ?? 0).toFixed(2)}%
</DeltaText> </DeltaText>
</ThemedText.BodySmall> </ThemedText.BodySmall>
</PriceChangeContainer> </PriceChangeContainer>

@ -13,7 +13,7 @@ import TimePeriodSelector from './TimeSelector'
function usePriceHistory(tokenPriceData: TokenPriceQuery): PricePoint[] | undefined { function usePriceHistory(tokenPriceData: TokenPriceQuery): PricePoint[] | undefined {
// Appends the current price to the end of the priceHistory array // Appends the current price to the end of the priceHistory array
const priceHistory = useMemo(() => { const priceHistory = useMemo(() => {
const market = tokenPriceData.token?.project?.markets?.[0] const market = tokenPriceData.token?.market
const priceHistory = market?.priceHistory?.filter(isPricePoint) const priceHistory = market?.priceHistory?.filter(isPricePoint)
const currentPrice = market?.price?.value const currentPrice = market?.price?.value
if (Array.isArray(priceHistory) && currentPrice !== undefined) { if (Array.isArray(priceHistory) && currentPrice !== undefined) {

@ -138,6 +138,15 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
) )
const chartAvailable = !!prices && prices.length > 0 const chartAvailable = !!prices && prices.length > 0
const missingPricesMessage = !chartAvailable ? (
prices?.length === 0 ? (
<>
<Trans>Missing price data due to recently low trading volume on Uniswap v3</Trans>
</>
) : (
<Trans>Missing chart data</Trans>
)
) : null
// first price point on the x-axis of the current time period's chart // first price point on the x-axis of the current time period's chart
const startingPrice = originalPrices?.[0] ?? DATA_EMPTY const startingPrice = originalPrices?.[0] ?? DATA_EMPTY
@ -278,18 +287,12 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
) : ( ) : (
<> <>
<MissingPrice>Price Unavailable</MissingPrice> <MissingPrice>Price Unavailable</MissingPrice>
<ThemedText.Caption style={{ color: theme.textTertiary }}> <ThemedText.Caption style={{ color: theme.textTertiary }}>{missingPricesMessage}</ThemedText.Caption>
<Trans>Missing price data</Trans>
</ThemedText.Caption>
</> </>
)} )}
</ChartHeader> </ChartHeader>
{!chartAvailable ? ( {!chartAvailable ? (
<MissingPriceChart <MissingPriceChart width={width} height={graphHeight} message={!!displayPrice.value && missingPricesMessage} />
width={width}
height={graphHeight}
message={!!displayPrice.value && <Trans>Price history unavailable</Trans>}
/>
) : ( ) : (
<svg data-cy="price-chart" width={width} height={graphHeight} style={{ minWidth: '100%' }}> <svg data-cy="price-chart" width={width} height={graphHeight} style={{ minWidth: '100%' }}>
<AnimatedInLineChart <AnimatedInLineChart

@ -200,8 +200,8 @@ export default function TokenDetails({
<StatsSection <StatsSection
TVL={tokenQueryData?.market?.totalValueLocked?.value} TVL={tokenQueryData?.market?.totalValueLocked?.value}
volume24H={tokenQueryData?.market?.volume24H?.value} volume24H={tokenQueryData?.market?.volume24H?.value}
priceHigh52W={tokenQueryData?.project?.markets?.[0]?.priceHigh52W?.value} priceHigh52W={tokenQueryData?.market?.priceHigh52W?.value}
priceLow52W={tokenQueryData?.project?.markets?.[0]?.priceLow52W?.value} priceLow52W={tokenQueryData?.market?.priceLow52W?.value}
/> />
<Hr /> <Hr />
<AboutSection <AboutSection

@ -438,7 +438,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
const filterNetwork = lowercaseChainName.toUpperCase() const filterNetwork = lowercaseChainName.toUpperCase()
const chainId = CHAIN_NAME_TO_CHAIN_ID[filterNetwork] const chainId = CHAIN_NAME_TO_CHAIN_ID[filterNetwork]
const timePeriod = useAtomValue(filterTimeAtom) const timePeriod = useAtomValue(filterTimeAtom)
const delta = token.project?.markets?.[0]?.pricePercentChange?.value const delta = token.market?.pricePercentChange?.value
const arrow = getDeltaArrow(delta) const arrow = getDeltaArrow(delta)
const smallArrow = getDeltaArrow(delta, 14) const smallArrow = getDeltaArrow(delta, 14)
const formattedDelta = formatDelta(delta) const formattedDelta = formatDelta(delta)
@ -478,7 +478,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
price={ price={
<ClickableContent> <ClickableContent>
<PriceInfoCell> <PriceInfoCell>
{formatUSDPrice(token.project?.markets?.[0]?.price?.value)} {formatUSDPrice(token.market?.price?.value)}
<PercentChangeInfoCell> <PercentChangeInfoCell>
<ArrowCell>{smallArrow}</ArrowCell> <ArrowCell>{smallArrow}</ArrowCell>
<DeltaText delta={delta}>{formattedDelta}</DeltaText> <DeltaText delta={delta}>{formattedDelta}</DeltaText>
@ -509,7 +509,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
width={width} width={width}
height={height} height={height}
tokenData={token} tokenData={token}
pricePercentChange={token.project?.markets?.[0]?.pricePercentChange?.value} pricePercentChange={token.market?.pricePercentChange?.value}
sparklineMap={props.sparklineMap} sparklineMap={props.sparklineMap}
/> />
) )

@ -108,7 +108,7 @@ export function useSyncWidgetTransactions() {
...formatSwapSignedAnalyticsEventProperties({ ...formatSwapSignedAnalyticsEventProperties({
trade, trade,
// TODO: add once Widgets adds fiat values to callback // TODO: add once Widgets adds fiat values to callback
fiatValues: { amountIn: null, amountOut: null }, fiatValues: { amountIn: undefined, amountOut: undefined },
txHash: transaction.receipt?.transactionHash ?? '', txHash: transaction.receipt?.transactionHash ?? '',
}), }),
...trace, ...trace,

@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
import { Trace } from '@uniswap/analytics' import { Trace } from '@uniswap/analytics'
import { InterfaceModalName } from '@uniswap/analytics-events' import { InterfaceModalName } from '@uniswap/analytics-events'
import { Trade } from '@uniswap/router-sdk' import { Trade } from '@uniswap/router-sdk'
import { Currency, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core' import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { ReactNode, useCallback, useMemo, useState } from 'react' import { ReactNode, useCallback, useMemo, useState } from 'react'
import { InterfaceTrade } from 'state/routing/types' import { InterfaceTrade } from 'state/routing/types'
import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer' import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer'
@ -42,8 +42,8 @@ export default function ConfirmSwapModal({
swapErrorMessage: ReactNode | undefined swapErrorMessage: ReactNode | undefined
onDismiss: () => void onDismiss: () => void
swapQuoteReceivedDate: Date | undefined swapQuoteReceivedDate: Date | undefined
fiatValueInput: CurrencyAmount<Token> | null fiatValueInput: { data?: number; isLoading: boolean }
fiatValueOutput: CurrencyAmount<Token> | null fiatValueOutput: { data?: number; isLoading: boolean }
}) { }) {
// shouldLogModalCloseEvent lets the child SwapModalHeader component know when modal has been closed // shouldLogModalCloseEvent lets the child SwapModalHeader component know when modal has been closed
// and an event triggered by modal closing should be logged. // and an event triggered by modal closing should be logged.

@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { TraceEvent } from '@uniswap/analytics' import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events' import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
import { Currency, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core' import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import useTransactionDeadline from 'hooks/useTransactionDeadline' import useTransactionDeadline from 'hooks/useTransactionDeadline'
import { import {
formatPercentInBasisPointsNumber, formatPercentInBasisPointsNumber,
@ -31,8 +31,8 @@ interface AnalyticsEventProps {
isAutoRouterApi: boolean isAutoRouterApi: boolean
swapQuoteReceivedDate: Date | undefined swapQuoteReceivedDate: Date | undefined
routes: RoutingDiagramEntry[] routes: RoutingDiagramEntry[]
fiatValueInput?: CurrencyAmount<Token> | null fiatValueInput?: number
fiatValueOutput?: CurrencyAmount<Token> | null fiatValueOutput?: number
} }
const formatRoutesEventProperties = (routes: RoutingDiagramEntry[]) => { const formatRoutesEventProperties = (routes: RoutingDiagramEntry[]) => {
@ -83,8 +83,8 @@ const formatAnalyticsEventProperties = ({
token_out_symbol: trade.outputAmount.currency.symbol, token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals), token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals), token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
token_in_amount_usd: fiatValueInput ? parseFloat(fiatValueInput.toFixed(2)) : undefined, token_in_amount_usd: fiatValueInput,
token_out_amount_usd: fiatValueOutput ? parseFloat(fiatValueOutput.toFixed(2)) : undefined, token_out_amount_usd: fiatValueOutput,
price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)), price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)),
allowed_slippage_basis_points: formatPercentInBasisPointsNumber(allowedSlippage), allowed_slippage_basis_points: formatPercentInBasisPointsNumber(allowedSlippage),
is_auto_router_api: isAutoRouterApi, is_auto_router_api: isAutoRouterApi,
@ -118,8 +118,8 @@ export default function SwapModalFooter({
swapErrorMessage: ReactNode | undefined swapErrorMessage: ReactNode | undefined
disabledConfirm: boolean disabledConfirm: boolean
swapQuoteReceivedDate: Date | undefined swapQuoteReceivedDate: Date | undefined
fiatValueInput: CurrencyAmount<Token> | null fiatValueInput: { data?: number; isLoading: boolean }
fiatValueOutput: CurrencyAmount<Token> | null fiatValueOutput: { data?: number; isLoading: boolean }
}) { }) {
const transactionDeadlineSecondsSinceEpoch = useTransactionDeadline()?.toNumber() // in seconds since epoch const transactionDeadlineSecondsSinceEpoch = useTransactionDeadline()?.toNumber() // in seconds since epoch
const isAutoSlippage = useUserSlippageTolerance()[0] === 'auto' const isAutoSlippage = useUserSlippageTolerance()[0] === 'auto'
@ -142,8 +142,8 @@ export default function SwapModalFooter({
isAutoRouterApi: !clientSideRouter, isAutoRouterApi: !clientSideRouter,
swapQuoteReceivedDate, swapQuoteReceivedDate,
routes, routes,
fiatValueInput, fiatValueInput: fiatValueInput.data,
fiatValueOutput, fiatValueOutput: fiatValueOutput.data,
})} })}
> >
<ButtonError <ButtonError

@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro'
import { sendAnalyticsEvent } from '@uniswap/analytics' import { sendAnalyticsEvent } from '@uniswap/analytics'
import { SwapEventName, SwapPriceUpdateUserResponse } from '@uniswap/analytics-events' import { SwapEventName, SwapPriceUpdateUserResponse } from '@uniswap/analytics-events'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core' import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { useUSDPrice } from 'hooks/useUSDPrice'
import { getPriceUpdateBasisPoints } from 'lib/utils/analytics' import { getPriceUpdateBasisPoints } from 'lib/utils/analytics'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { AlertTriangle, ArrowDown } from 'react-feather' import { AlertTriangle, ArrowDown } from 'react-feather'
@ -9,7 +10,6 @@ import { Text } from 'rebass'
import { InterfaceTrade } from 'state/routing/types' import { InterfaceTrade } from 'state/routing/types'
import styled, { useTheme } from 'styled-components/macro' import styled, { useTheme } from 'styled-components/macro'
import { useStablecoinValue } from '../../hooks/useStablecoinPrice'
import { ThemedText } from '../../theme' import { ThemedText } from '../../theme'
import { isAddress, shortenAddress } from '../../utils' import { isAddress, shortenAddress } from '../../utils'
import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact' import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact'
@ -78,8 +78,8 @@ export default function SwapModalHeader({
const [lastExecutionPrice, setLastExecutionPrice] = useState(trade.executionPrice) const [lastExecutionPrice, setLastExecutionPrice] = useState(trade.executionPrice)
const [priceUpdate, setPriceUpdate] = useState<number | undefined>() const [priceUpdate, setPriceUpdate] = useState<number | undefined>()
const fiatValueInput = useStablecoinValue(trade.inputAmount) const fiatValueInput = useUSDPrice(trade.inputAmount)
const fiatValueOutput = useStablecoinValue(trade.outputAmount) const fiatValueOutput = useUSDPrice(trade.outputAmount)
useEffect(() => { useEffect(() => {
if (!trade.executionPrice.equalTo(lastExecutionPrice)) { if (!trade.executionPrice.equalTo(lastExecutionPrice)) {
@ -145,7 +145,7 @@ export default function SwapModalHeader({
<ThemedText.DeprecatedBody fontSize={14} color={theme.textTertiary}> <ThemedText.DeprecatedBody fontSize={14} color={theme.textTertiary}>
<FiatValue <FiatValue
fiatValue={fiatValueOutput} fiatValue={fiatValueOutput}
priceImpact={computeFiatValuePriceImpact(fiatValueInput, fiatValueOutput)} priceImpact={computeFiatValuePriceImpact(fiatValueInput.data, fiatValueOutput.data)}
/> />
</ThemedText.DeprecatedBody> </ThemedText.DeprecatedBody>
</RowBetween> </RowBetween>

@ -1,10 +1,12 @@
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { Currency, Price } from '@uniswap/sdk-core' import { Currency, Price } from '@uniswap/sdk-core'
import useStablecoinPrice from 'hooks/useStablecoinPrice' import { useUSDPrice } from 'hooks/useUSDPrice'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
import { formatDollar, formatTransactionAmount, priceToPreciseFloat } from 'utils/formatNumbers' import { formatTransactionAmount, priceToPreciseFloat } from 'utils/formatNumbers'
interface TradePriceProps { interface TradePriceProps {
price: Price<Currency, Currency> price: Price<Currency, Currency>
@ -30,7 +32,8 @@ const StyledPriceContainer = styled.button`
export default function TradePrice({ price }: TradePriceProps) { export default function TradePrice({ price }: TradePriceProps) {
const [showInverted, setShowInverted] = useState<boolean>(false) const [showInverted, setShowInverted] = useState<boolean>(false)
const usdcPrice = useStablecoinPrice(showInverted ? price.baseCurrency : price.quoteCurrency) const { baseCurrency, quoteCurrency } = price
const { data: usdPrice } = useUSDPrice(tryParseCurrencyAmount('1', showInverted ? baseCurrency : quoteCurrency))
let formattedPrice: string let formattedPrice: string
try { try {
@ -56,9 +59,9 @@ export default function TradePrice({ price }: TradePriceProps) {
title={text} title={text}
> >
<ThemedText.BodySmall>{text}</ThemedText.BodySmall>{' '} <ThemedText.BodySmall>{text}</ThemedText.BodySmall>{' '}
{usdcPrice && ( {usdPrice && (
<ThemedText.DeprecatedDarkGray> <ThemedText.DeprecatedDarkGray>
<Trans>({formatDollar({ num: priceToPreciseFloat(usdcPrice), isPrice: true })})</Trans> <Trans>({formatNumber(usdPrice, NumberType.FiatTokenPrice)})</Trans>
</ThemedText.DeprecatedDarkGray> </ThemedText.DeprecatedDarkGray>
)} )}
</StyledPriceContainer> </StyledPriceContainer>

@ -34,6 +34,15 @@ gql`
symbol symbol
market(currency: USD) { market(currency: USD) {
id id
price {
id
value
currency
}
pricePercentChange(duration: DAY) {
id
value
}
volume24H: volume(duration: DAY) { volume24H: volume(duration: DAY) {
id id
value value
@ -44,17 +53,6 @@ gql`
id id
logoUrl logoUrl
safetyLevel safetyLevel
markets(currencies: [USD]) {
id
price {
id
value
}
pricePercentChange(duration: DAY) {
id
value
}
}
} }
} }
} }

@ -17,6 +17,15 @@ gql`
symbol symbol
market(currency: USD) { market(currency: USD) {
id id
price {
id
value
currency
}
pricePercentChange(duration: DAY) {
id
value
}
volume24H: volume(duration: DAY) { volume24H: volume(duration: DAY) {
id id
value value
@ -27,17 +36,6 @@ gql`
id id
logoUrl logoUrl
safetyLevel safetyLevel
markets(currencies: [USD]) {
id
price {
id
value
}
pricePercentChange(duration: DAY) {
id
value
}
}
} }
} }
} }

@ -30,11 +30,24 @@ gql`
value value
currency currency
} }
price {
id
value
currency
}
volume24H: volume(duration: DAY) { volume24H: volume(duration: DAY) {
id id
value value
currency currency
} }
priceHigh52W: priceHighLow(duration: YEAR, highLow: HIGH) {
id
value
}
priceLow52W: priceHighLow(duration: YEAR, highLow: LOW) {
id
value
}
} }
project { project {
id id
@ -47,22 +60,6 @@ gql`
chain chain
address address
} }
markets(currencies: [USD]) {
id
price {
id
value
currency
}
priceHigh52W: priceHighLow(duration: YEAR, highLow: HIGH) {
id
value
}
priceLow52W: priceHighLow(duration: YEAR, highLow: LOW) {
id
value
}
}
} }
} }
} }

@ -6,19 +6,16 @@ gql`
id id
address address
chain chain
project { market(currency: USD) {
id id
markets(currencies: [USD]) { price {
id id
price { value
id }
value priceHistory(duration: $duration) {
} id
priceHistory(duration: $duration) { timestamp
id value
timestamp
value
}
} }
} }
} }

@ -0,0 +1,23 @@
import gql from 'graphql-tag'
gql`
query TokenSpotPrice($chain: Chain!, $address: String) {
token(chain: $chain, address: $address) {
id
address
chain
name
symbol
project {
id
markets(currencies: [USD]) {
id
price {
id
value
}
}
}
}
}
`

@ -36,12 +36,22 @@ gql`
standard standard
market(currency: USD) { market(currency: USD) {
id id
volume(duration: $duration) { totalValueLocked {
id id
value value
currency currency
} }
totalValueLocked { price {
id
value
currency
}
pricePercentChange(duration: $duration) {
id
currency
value
}
volume(duration: $duration) {
id id
value value
currency currency
@ -50,18 +60,6 @@ gql`
project { project {
id id
logoUrl logoUrl
markets(currencies: [USD]) {
id
price {
id
value
}
pricePercentChange(duration: $duration) {
id
currency
value
}
}
} }
} }
} }
@ -74,14 +72,12 @@ gql`
id id
address address
chain chain
project { market(currency: USD) {
markets(currencies: [USD]) { id
priceHistory(duration: $duration) {
id id
priceHistory(duration: $duration) { timestamp
id value
timestamp
value
}
} }
} }
} }
@ -97,15 +93,11 @@ function useSortedTokens(tokens: TopTokens100Query['topTokens']) {
let tokenArray = Array.from(tokens) let tokenArray = Array.from(tokens)
switch (sortMethod) { switch (sortMethod) {
case TokenSortMethod.PRICE: case TokenSortMethod.PRICE:
tokenArray = tokenArray.sort( tokenArray = tokenArray.sort((a, b) => (b?.market?.price?.value ?? 0) - (a?.market?.price?.value ?? 0))
(a, b) => (b?.project?.markets?.[0]?.price?.value ?? 0) - (a?.project?.markets?.[0]?.price?.value ?? 0)
)
break break
case TokenSortMethod.PERCENT_CHANGE: case TokenSortMethod.PERCENT_CHANGE:
tokenArray = tokenArray.sort( tokenArray = tokenArray.sort(
(a, b) => (a, b) => (b?.market?.pricePercentChange?.value ?? 0) - (a?.market?.pricePercentChange?.value ?? 0)
(b?.project?.markets?.[0]?.pricePercentChange?.value ?? 0) -
(a?.project?.markets?.[0]?.pricePercentChange?.value ?? 0)
) )
break break
case TokenSortMethod.TOTAL_VALUE_LOCKED: case TokenSortMethod.TOTAL_VALUE_LOCKED:
@ -169,8 +161,7 @@ export function useTopTokens(chain: Chain): UseTopTokensReturnValue {
const unwrappedTokens = sparklineQuery?.topTokens?.map((topToken) => unwrapToken(chainId, topToken)) const unwrappedTokens = sparklineQuery?.topTokens?.map((topToken) => unwrapToken(chainId, topToken))
const map: SparklineMap = {} const map: SparklineMap = {}
unwrappedTokens?.forEach( unwrappedTokens?.forEach(
(current) => (current) => current?.address && (map[current.address] = current?.market?.priceHistory?.filter(isPricePoint))
current?.address && (map[current.address] = current?.project?.markets?.[0]?.priceHistory?.filter(isPricePoint))
) )
return map return map
}, [chainId, sparklineQuery?.topTokens]) }, [chainId, sparklineQuery?.topTokens])

@ -16,6 +16,15 @@ gql`
symbol symbol
market(currency: USD) { market(currency: USD) {
id id
price {
id
value
currency
}
pricePercentChange(duration: DAY) {
id
value
}
volume24H: volume(duration: DAY) { volume24H: volume(duration: DAY) {
id id
value value
@ -26,18 +35,6 @@ gql`
id id
logoUrl logoUrl
safetyLevel safetyLevel
markets(currencies: [USD]) {
id
price {
id
value
}
pricePercentChange(duration: DAY) {
id
currency
value
}
}
} }
} }
} }

@ -73,6 +73,18 @@ export function chainIdToBackendName(chainId: number | undefined) {
: CHAIN_ID_TO_BACKEND_NAME[SupportedChainId.MAINNET] : CHAIN_ID_TO_BACKEND_NAME[SupportedChainId.MAINNET]
} }
const GQL_CHAINS: number[] = [
SupportedChainId.MAINNET,
SupportedChainId.OPTIMISM,
SupportedChainId.POLYGON,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.CELO,
]
export function isGqlSupportedChain(chainId: number | undefined): chainId is SupportedChainId {
return !!chainId && GQL_CHAINS.includes(chainId)
}
const URL_CHAIN_PARAM_TO_BACKEND: { [key: string]: Chain } = { const URL_CHAIN_PARAM_TO_BACKEND: { [key: string]: Chain } = {
ethereum: Chain.Ethereum, ethereum: Chain.Ethereum,
polygon: Chain.Polygon, polygon: Chain.Polygon,

@ -1,5 +1,5 @@
import { Trade } from '@uniswap/router-sdk' import { Trade } from '@uniswap/router-sdk'
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core' import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { PermitSignature } from 'hooks/usePermitAllowance' import { PermitSignature } from 'hooks/usePermitAllowance'
import { useMemo } from 'react' import { useMemo } from 'react'
@ -13,7 +13,7 @@ import { useUniversalRouterSwapCallback } from './useUniversalRouter'
// and the user has approved the slippage adjusted input amount for the trade // and the user has approved the slippage adjusted input amount for the trade
export function useSwapCallback( export function useSwapCallback(
trade: Trade<Currency, Currency, TradeType> | undefined, // trade to execute, required trade: Trade<Currency, Currency, TradeType> | undefined, // trade to execute, required
fiatValues: { amountIn: CurrencyAmount<Currency> | null; amountOut: CurrencyAmount<Currency> | null }, // usd values for amount in and out, logged for analytics fiatValues: { amountIn: number | undefined; amountOut: number | undefined }, // usd values for amount in and out, logged for analytics
allowedSlippage: Percent, // in bips allowedSlippage: Percent, // in bips
permitSignature: PermitSignature | undefined permitSignature: PermitSignature | undefined
): { callback: null | (() => Promise<string>) } { ): { callback: null | (() => Promise<string>) } {

81
src/hooks/useUSDPrice.ts Normal file

@ -0,0 +1,81 @@
import { NetworkStatus } from '@apollo/client'
import { Currency, CurrencyAmount, Price, SupportedChainId, TradeType } from '@uniswap/sdk-core'
import { nativeOnChain } from 'constants/tokens'
import { Chain, useTokenSpotPriceQuery } from 'graphql/data/__generated__/types-and-hooks'
import { chainIdToBackendName, isGqlSupportedChain, PollingInterval } from 'graphql/data/util'
import { RouterPreference } from 'state/routing/slice'
import { TradeState } from 'state/routing/types'
import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade'
import { getNativeTokenDBAddress } from 'utils/nativeTokens'
import useStablecoinPrice from './useStablecoinPrice'
// ETH amounts used when calculating spot price for a given currency.
// The amount is large enough to filter low liquidity pairs.
const ETH_AMOUNT_OUT: { [chainId: number]: CurrencyAmount<Currency> } = {
[SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(nativeOnChain(SupportedChainId.MAINNET), 100e18),
[SupportedChainId.ARBITRUM_ONE]: CurrencyAmount.fromRawAmount(nativeOnChain(SupportedChainId.ARBITRUM_ONE), 10e18),
[SupportedChainId.OPTIMISM]: CurrencyAmount.fromRawAmount(nativeOnChain(SupportedChainId.OPTIMISM), 10e18),
[SupportedChainId.POLYGON]: CurrencyAmount.fromRawAmount(nativeOnChain(SupportedChainId.POLYGON), 10_000e18),
[SupportedChainId.CELO]: CurrencyAmount.fromRawAmount(nativeOnChain(SupportedChainId.CELO), 10e18),
}
function useETHValue(currencyAmount?: CurrencyAmount<Currency>): {
data: CurrencyAmount<Currency> | undefined
isLoading: boolean
} {
const chainId = currencyAmount?.currency?.chainId
const amountOut = isGqlSupportedChain(chainId) ? ETH_AMOUNT_OUT[chainId] : undefined
const { trade, state } = useRoutingAPITrade(
TradeType.EXACT_OUTPUT,
amountOut,
currencyAmount?.currency,
RouterPreference.PRICE
)
// Get ETH value of ETH or WETH
if (chainId && currencyAmount && currencyAmount.currency.wrapped.equals(nativeOnChain(chainId).wrapped)) {
return {
data: new Price(currencyAmount.currency, currencyAmount.currency, '1', '1').quote(currencyAmount),
isLoading: false,
}
}
if (!trade || !currencyAmount?.currency || !isGqlSupportedChain(chainId)) {
return { data: undefined, isLoading: state === TradeState.LOADING || state === TradeState.SYNCING }
}
const { numerator, denominator } = trade.routes[0].midPrice
const price = new Price(currencyAmount?.currency, nativeOnChain(chainId), denominator, numerator)
return { data: price.quote(currencyAmount), isLoading: false }
}
export function useUSDPrice(currencyAmount?: CurrencyAmount<Currency>): {
data: number | undefined
isLoading: boolean
} {
const chain = currencyAmount?.currency.chainId ? chainIdToBackendName(currencyAmount?.currency.chainId) : undefined
const currency = currencyAmount?.currency
const { data: ethValue, isLoading: isEthValueLoading } = useETHValue(currencyAmount)
const { data, networkStatus } = useTokenSpotPriceQuery({
variables: { chain: chain ?? Chain.Ethereum, address: getNativeTokenDBAddress(chain ?? Chain.Ethereum) },
skip: !chain || !isGqlSupportedChain(currency?.chainId),
pollInterval: PollingInterval.Normal,
notifyOnNetworkStatusChange: true,
})
// Use USDC price for chains not supported by backend yet
const stablecoinPrice = useStablecoinPrice(!isGqlSupportedChain(currency?.chainId) ? currency : undefined)
if (!isGqlSupportedChain(currency?.chainId) && currencyAmount && stablecoinPrice) {
return { data: parseFloat(stablecoinPrice.quote(currencyAmount).toSignificant()), isLoading: false }
}
const isFirstLoad = networkStatus === NetworkStatus.loading
// Otherwise, get the price of the token in ETH, and then multiple by the price of ETH
const ethUSDPrice = data?.token?.project?.markets?.[0]?.price?.value
if (!ethUSDPrice || !ethValue) return { data: undefined, isLoading: isEthValueLoading || isFirstLoad }
return { data: parseFloat(ethValue.toExact()) * ethUSDPrice, isLoading: false }
}

@ -4,7 +4,7 @@ import { t } from '@lingui/macro'
import { sendAnalyticsEvent } from '@uniswap/analytics' import { sendAnalyticsEvent } from '@uniswap/analytics'
import { SwapEventName } from '@uniswap/analytics-events' import { SwapEventName } from '@uniswap/analytics-events'
import { Trade } from '@uniswap/router-sdk' import { Trade } from '@uniswap/router-sdk'
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core' import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { SwapRouter, UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk' import { SwapRouter, UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk'
import { FeeOptions, toHex } from '@uniswap/v3-sdk' import { FeeOptions, toHex } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
@ -27,7 +27,7 @@ interface SwapOptions {
export function useUniversalRouterSwapCallback( export function useUniversalRouterSwapCallback(
trade: Trade<Currency, Currency, TradeType> | undefined, trade: Trade<Currency, Currency, TradeType> | undefined,
fiatValues: { amountIn: CurrencyAmount<Currency> | null; amountOut: CurrencyAmount<Currency> | null }, fiatValues: { amountIn: number | undefined; amountOut: number | undefined },
options: SwapOptions options: SwapOptions
) { ) {
const { account, chainId, provider } = useWeb3React() const { account, chainId, provider } = useWeb3React()

@ -40,7 +40,7 @@ export const formatSwapSignedAnalyticsEventProperties = ({
txHash, txHash,
}: { }: {
trade: InterfaceTrade<Currency, Currency, TradeType> | Trade<Currency, Currency, TradeType> trade: InterfaceTrade<Currency, Currency, TradeType> | Trade<Currency, Currency, TradeType>
fiatValues: { amountIn: CurrencyAmount<Currency> | null; amountOut: CurrencyAmount<Currency> | null } fiatValues: { amountIn: number | undefined; amountOut: number | undefined }
txHash: string txHash: string
}) => ({ }) => ({
transaction_hash: txHash, transaction_hash: txHash,
@ -50,8 +50,8 @@ export const formatSwapSignedAnalyticsEventProperties = ({
token_out_symbol: trade.outputAmount.currency.symbol, token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals), token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals), token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
token_in_amount_usd: fiatValues.amountIn ? parseFloat(fiatValues.amountIn.toFixed(2)) : undefined, token_in_amount_usd: fiatValues.amountIn,
token_out_amount_usd: fiatValues.amountOut ? parseFloat(fiatValues.amountOut.toFixed(2)) : undefined, token_out_amount_usd: fiatValues.amountOut,
price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)), price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)),
chain_id: chain_id:
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId

@ -9,7 +9,7 @@ import { useWeb3React } from '@web3-react/core'
import { sendEvent } from 'components/analytics' import { sendEvent } from 'components/analytics'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter' import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import usePrevious from 'hooks/usePrevious' import usePrevious from 'hooks/usePrevious'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import { AlertTriangle } from 'react-feather' import { AlertTriangle } from 'react-feather'
import { useNavigate, useParams, useSearchParams } from 'react-router-dom' import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
import { Text } from 'rebass' import { Text } from 'rebass'
@ -532,6 +532,22 @@ export default function AddLiquidity() {
</AutoColumn> </AutoColumn>
) )
const usdcValueCurrencyA = usdcValues[Field.CURRENCY_A]
const usdcValueCurrencyB = usdcValues[Field.CURRENCY_B]
const currencyAFiat = useMemo(
() => ({
data: usdcValueCurrencyA ? parseFloat(usdcValueCurrencyA.toSignificant()) : undefined,
isLoading: false,
}),
[usdcValueCurrencyA]
)
const currencyBFiat = useMemo(
() => ({
data: usdcValueCurrencyB ? parseFloat(usdcValueCurrencyB.toSignificant()) : undefined,
isLoading: false,
}),
[usdcValueCurrencyB]
)
return ( return (
<> <>
<ScrollablePage> <ScrollablePage>
@ -682,7 +698,7 @@ export default function AddLiquidity() {
showMaxButton={!atMaxAmounts[Field.CURRENCY_A]} showMaxButton={!atMaxAmounts[Field.CURRENCY_A]}
currency={currencies[Field.CURRENCY_A] ?? null} currency={currencies[Field.CURRENCY_A] ?? null}
id="add-liquidity-input-tokena" id="add-liquidity-input-tokena"
fiatValue={usdcValues[Field.CURRENCY_A]} fiatValue={currencyAFiat}
showCommonBases showCommonBases
locked={depositADisabled} locked={depositADisabled}
/> />
@ -694,7 +710,7 @@ export default function AddLiquidity() {
onFieldBInput(maxAmounts[Field.CURRENCY_B]?.toExact() ?? '') onFieldBInput(maxAmounts[Field.CURRENCY_B]?.toExact() ?? '')
}} }}
showMaxButton={!atMaxAmounts[Field.CURRENCY_B]} showMaxButton={!atMaxAmounts[Field.CURRENCY_B]}
fiatValue={usdcValues[Field.CURRENCY_B]} fiatValue={currencyBFiat}
currency={currencies[Field.CURRENCY_B] ?? null} currency={currencies[Field.CURRENCY_B] ?? null}
id="add-liquidity-input-tokenb" id="add-liquidity-input-tokenb"
showCommonBases showCommonBases

@ -25,6 +25,7 @@ import { useSwapWidgetEnabled } from 'featureFlags/flags/swapWidget'
import useENSAddress from 'hooks/useENSAddress' import useENSAddress from 'hooks/useENSAddress'
import usePermit2Allowance, { AllowanceState } from 'hooks/usePermit2Allowance' import usePermit2Allowance, { AllowanceState } from 'hooks/usePermit2Allowance'
import { useSwapCallback } from 'hooks/useSwapCallback' import { useSwapCallback } from 'hooks/useSwapCallback'
import { useUSDPrice } from 'hooks/useUSDPrice'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { formatSwapQuoteReceivedEventProperties } from 'lib/utils/analytics' import { formatSwapQuoteReceivedEventProperties } from 'lib/utils/analytics'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
@ -54,7 +55,6 @@ import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
import { TOKEN_SHORTHANDS } from '../../constants/tokens' import { TOKEN_SHORTHANDS } from '../../constants/tokens'
import { useAllTokens, useCurrency } from '../../hooks/Tokens' import { useAllTokens, useCurrency } from '../../hooks/Tokens'
import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported' import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported'
import { useStablecoinValue } from '../../hooks/useStablecoinPrice'
import useWrapCallback, { WrapErrorText, WrapType } from '../../hooks/useWrapCallback' import useWrapCallback, { WrapErrorText, WrapType } from '../../hooks/useWrapCallback'
import { Field } from '../../state/swap/actions' import { Field } from '../../state/swap/actions'
import { import {
@ -230,19 +230,21 @@ export default function Swap({ className }: { className?: string }) {
}, },
[independentField, parsedAmount, showWrap, trade] [independentField, parsedAmount, showWrap, trade]
) )
const fiatValueInput = useStablecoinValue(parsedAmounts[Field.INPUT]) const fiatValueInput = useUSDPrice(parsedAmounts[Field.INPUT])
const fiatValueOutput = useStablecoinValue(parsedAmounts[Field.OUTPUT]) const fiatValueOutput = useUSDPrice(parsedAmounts[Field.OUTPUT])
const [routeNotFound, routeIsLoading, routeIsSyncing] = useMemo( const [routeNotFound, routeIsLoading, routeIsSyncing] = useMemo(
() => [!trade?.swaps, TradeState.LOADING === tradeState, TradeState.SYNCING === tradeState], () => [!trade?.swaps, TradeState.LOADING === tradeState, TradeState.SYNCING === tradeState],
[trade, tradeState] [trade, tradeState]
) )
const fiatValueTradeInput = useStablecoinValue(trade?.inputAmount) const fiatValueTradeInput = useUSDPrice(trade?.inputAmount)
const fiatValueTradeOutput = useStablecoinValue(trade?.outputAmount) const fiatValueTradeOutput = useUSDPrice(trade?.outputAmount)
const stablecoinPriceImpact = useMemo( const stablecoinPriceImpact = useMemo(
() => () =>
routeIsSyncing || !trade ? undefined : computeFiatValuePriceImpact(fiatValueTradeInput, fiatValueTradeOutput), routeIsSyncing || !trade
? undefined
: computeFiatValuePriceImpact(fiatValueTradeInput.data, fiatValueTradeOutput.data),
[fiatValueTradeInput, fiatValueTradeOutput, routeIsSyncing, trade] [fiatValueTradeInput, fiatValueTradeOutput, routeIsSyncing, trade]
) )
@ -334,7 +336,7 @@ export default function Swap({ className }: { className?: string }) {
) )
const showMaxButton = Boolean(maxInputAmount?.greaterThan(0) && !parsedAmounts[Field.INPUT]?.equalTo(maxInputAmount)) const showMaxButton = Boolean(maxInputAmount?.greaterThan(0) && !parsedAmounts[Field.INPUT]?.equalTo(maxInputAmount))
const swapFiatValues = useMemo(() => { const swapFiatValues = useMemo(() => {
return { amountIn: fiatValueTradeInput, amountOut: fiatValueTradeOutput } return { amountIn: fiatValueTradeInput.data, amountOut: fiatValueTradeOutput.data }
}, [fiatValueTradeInput, fiatValueTradeOutput]) }, [fiatValueTradeInput, fiatValueTradeOutput])
// the callback to execute the swap // the callback to execute the swap

@ -62,7 +62,7 @@ export function useRoutingAPITrade<TTradeType extends TradeType>(
const isSyncing = currentData !== data const isSyncing = currentData !== data
return useMemo(() => { return useMemo(() => {
if (!currencyIn || !currencyOut) { if (!currencyIn || !currencyOut || currencyIn.equals(currencyOut)) {
return { return {
state: TradeState.INVALID, state: TradeState.INVALID,
trade: undefined, trade: undefined,

@ -1,15 +1,14 @@
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' import { Percent } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import { ONE_HUNDRED_PERCENT } from '../constants/misc'
const PRECISION = 10000
export function computeFiatValuePriceImpact( export function computeFiatValuePriceImpact(
fiatValueInput: CurrencyAmount<Currency> | undefined | null, fiatValueInput: number | undefined | null,
fiatValueOutput: CurrencyAmount<Currency> | undefined | null fiatValueOutput: number | undefined | null
): Percent | undefined { ): Percent | undefined {
if (!fiatValueOutput || !fiatValueInput) return undefined if (!fiatValueOutput || !fiatValueInput) return undefined
if (!fiatValueInput.currency.equals(fiatValueOutput.currency)) return undefined if (fiatValueInput === 0) return undefined
if (JSBI.equal(fiatValueInput.quotient, JSBI.BigInt(0))) return undefined
const pct = ONE_HUNDRED_PERCENT.subtract(fiatValueOutput.divide(fiatValueInput)) const ratio = 1 - fiatValueOutput / fiatValueInput
return new Percent(pct.numerator, pct.denominator) const numerator = Math.floor(ratio * PRECISION)
return new Percent(numerator, PRECISION)
} }