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:
parent
acc6b0a740
commit
4bdef2b601
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
src/graphql/data/TokenSpotPrice.ts
Normal file
23
src/graphql/data/TokenSpotPrice.ts
Normal file
@ -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
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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user