chore: updating all numbers in swap flow to be converted properly (#7301)
* format price impact * format price * adding confirm swap modal * removing export * adding export back * correct active currency * activeLocalCurrencyIsUSD * fallback to usd if no conversion rate for previous
This commit is contained in:
parent
185cb52501
commit
6303293037
@ -6,7 +6,7 @@ import { MouseoverTooltip } from 'components/Tooltip'
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { ThemedText } from 'theme'
|
import { ThemedText } from 'theme'
|
||||||
import { formatPriceImpact, NumberType, useFormatter } from 'utils/formatNumbers'
|
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||||
import { warningSeverity } from 'utils/prices'
|
import { warningSeverity } from 'utils/prices'
|
||||||
|
|
||||||
const FiatLoadingBubble = styled(LoadingBubble)`
|
const FiatLoadingBubble = styled(LoadingBubble)`
|
||||||
@ -22,7 +22,7 @@ export function FiatValue({
|
|||||||
fiatValue: { data?: number; isLoading: boolean }
|
fiatValue: { data?: number; isLoading: boolean }
|
||||||
priceImpact?: Percent
|
priceImpact?: Percent
|
||||||
}) {
|
}) {
|
||||||
const { formatNumber } = useFormatter()
|
const { formatNumber, formatPriceImpact } = useFormatter()
|
||||||
|
|
||||||
const priceImpactColor = useMemo(() => {
|
const priceImpactColor = useMemo(() => {
|
||||||
if (!priceImpact) return undefined
|
if (!priceImpact) return undefined
|
||||||
|
@ -9,7 +9,7 @@ import { ZERO_PERCENT } from 'constants/misc'
|
|||||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||||
import { ClassicTrade, InterfaceTrade } from 'state/routing/types'
|
import { ClassicTrade, InterfaceTrade } from 'state/routing/types'
|
||||||
import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
|
import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
|
||||||
import { formatPriceImpact, NumberType, useFormatter } from 'utils/formatNumbers'
|
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||||
|
|
||||||
import { ExternalLink, Separator, ThemedText } from '../../theme'
|
import { ExternalLink, Separator, ThemedText } from '../../theme'
|
||||||
import Column from '../Column'
|
import Column from '../Column'
|
||||||
@ -47,7 +47,7 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }:
|
|||||||
const { chainId } = useWeb3React()
|
const { chainId } = useWeb3React()
|
||||||
const nativeCurrency = useNativeCurrency(chainId)
|
const nativeCurrency = useNativeCurrency(chainId)
|
||||||
const txCount = getTransactionCount(trade)
|
const txCount = getTransactionCount(trade)
|
||||||
const { formatNumber, formatCurrencyAmount } = useFormatter()
|
const { formatCurrencyAmount, formatNumber, formatPriceImpact } = useFormatter()
|
||||||
|
|
||||||
const supportsGasEstimate = chainId && SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId)
|
const supportsGasEstimate = chainId && SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId)
|
||||||
|
|
||||||
@ -194,6 +194,8 @@ function TokenTaxLineItem({
|
|||||||
type: 'input' | 'output'
|
type: 'input' | 'output'
|
||||||
syncing: boolean
|
syncing: boolean
|
||||||
}) {
|
}) {
|
||||||
|
const { formatPriceImpact } = useFormatter()
|
||||||
|
|
||||||
if (syncing) return null
|
if (syncing) return null
|
||||||
|
|
||||||
const [currency, percentage] =
|
const [currency, percentage] =
|
||||||
|
@ -4,10 +4,12 @@ import { ArrowRight } from 'react-feather'
|
|||||||
import { InterfaceTrade } from 'state/routing/types'
|
import { InterfaceTrade } from 'state/routing/types'
|
||||||
import { useTheme } from 'styled-components'
|
import { useTheme } from 'styled-components'
|
||||||
import { ThemedText } from 'theme'
|
import { ThemedText } from 'theme'
|
||||||
import { formatReviewSwapCurrencyAmount } from 'utils/formatNumbers'
|
import { useFormatter } from 'utils/formatNumbers'
|
||||||
|
|
||||||
export function TradeSummary({ trade }: { trade: Pick<InterfaceTrade, 'inputAmount' | 'postTaxOutputAmount'> }) {
|
export function TradeSummary({ trade }: { trade: Pick<InterfaceTrade, 'inputAmount' | 'postTaxOutputAmount'> }) {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
const { formatReviewSwapCurrencyAmount } = useFormatter()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row gap="sm" justify="center" align="center">
|
<Row gap="sm" justify="center" align="center">
|
||||||
<CurrencyLogo currency={trade.inputAmount.currency} size="16px" />
|
<CurrencyLogo currency={trade.inputAmount.currency} size="16px" />
|
||||||
|
@ -6,7 +6,7 @@ import Row from 'components/Row'
|
|||||||
import { AlertTriangle } from 'react-feather'
|
import { AlertTriangle } from 'react-feather'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { CloseIcon, ThemedText } from 'theme'
|
import { CloseIcon, ThemedText } from 'theme'
|
||||||
import { formatPriceImpact } from 'utils/formatNumbers'
|
import { useFormatter } from 'utils/formatNumbers'
|
||||||
|
|
||||||
import Modal from '../Modal'
|
import Modal from '../Modal'
|
||||||
|
|
||||||
@ -37,6 +37,8 @@ interface PriceImpactModalProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function PriceImpactModal({ priceImpact, onDismiss, onContinue }: PriceImpactModalProps) {
|
export default function PriceImpactModal({ priceImpact, onDismiss, onContinue }: PriceImpactModalProps) {
|
||||||
|
const { formatPriceImpact } = useFormatter()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen onDismiss={onDismiss}>
|
<Modal isOpen onDismiss={onDismiss}>
|
||||||
<Wrapper gap="md">
|
<Wrapper gap="md">
|
||||||
|
@ -3,7 +3,7 @@ import { Percent } from '@uniswap/sdk-core'
|
|||||||
import { OutlineCard } from 'components/Card'
|
import { OutlineCard } from 'components/Card'
|
||||||
import styled, { useTheme } from 'styled-components'
|
import styled, { useTheme } from 'styled-components'
|
||||||
import { opacify } from 'theme/utils'
|
import { opacify } from 'theme/utils'
|
||||||
import formatPriceImpact from 'utils/formatPriceImpact'
|
import { useFormatter } from 'utils/formatNumbers'
|
||||||
|
|
||||||
import { ThemedText } from '../../theme'
|
import { ThemedText } from '../../theme'
|
||||||
import { AutoColumn } from '../Column'
|
import { AutoColumn } from '../Column'
|
||||||
@ -20,6 +20,7 @@ interface PriceImpactWarningProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function PriceImpactWarning({ priceImpact }: PriceImpactWarningProps) {
|
export default function PriceImpactWarning({ priceImpact }: PriceImpactWarningProps) {
|
||||||
|
const { formatPriceImpact } = useFormatter()
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -16,8 +16,8 @@ import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
|
|||||||
import { useRouterPreference, useUserSlippageTolerance } from 'state/user/hooks'
|
import { useRouterPreference, useUserSlippageTolerance } from 'state/user/hooks'
|
||||||
import styled, { DefaultTheme, useTheme } from 'styled-components'
|
import styled, { DefaultTheme, useTheme } from 'styled-components'
|
||||||
import { ExternalLink, ThemedText } from 'theme'
|
import { ExternalLink, ThemedText } from 'theme'
|
||||||
import { formatPriceImpact, NumberType, useFormatter } from 'utils/formatNumbers'
|
import { FormatterRule, NumberType, SIX_SIG_FIGS_NO_COMMAS, useFormatter } from 'utils/formatNumbers'
|
||||||
import { formatTransactionAmount, priceToPreciseFloat } from 'utils/formatNumbers'
|
import { priceToPreciseFloat } from 'utils/formatNumbers'
|
||||||
import getRoutingDiagramEntries from 'utils/getRoutingDiagramEntries'
|
import getRoutingDiagramEntries from 'utils/getRoutingDiagramEntries'
|
||||||
import { formatSwapButtonClickEventProperties } from 'utils/loggingFormatters'
|
import { formatSwapButtonClickEventProperties } from 'utils/loggingFormatters'
|
||||||
import { getPriceImpactColor } from 'utils/prices'
|
import { getPriceImpactColor } from 'utils/prices'
|
||||||
@ -28,6 +28,8 @@ import { GasBreakdownTooltip } from './GasBreakdownTooltip'
|
|||||||
import { SwapCallbackError, SwapShowAcceptChanges } from './styled'
|
import { SwapCallbackError, SwapShowAcceptChanges } from './styled'
|
||||||
import { Label } from './SwapModalHeaderAmount'
|
import { Label } from './SwapModalHeaderAmount'
|
||||||
|
|
||||||
|
const sixFigsFormatterRules: FormatterRule[] = [{ upperBound: Infinity, formatterOptions: SIX_SIG_FIGS_NO_COMMAS }]
|
||||||
|
|
||||||
const DetailsContainer = styled(Column)`
|
const DetailsContainer = styled(Column)`
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
`
|
`
|
||||||
@ -78,11 +80,14 @@ export default function SwapModalFooter({
|
|||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const { chainId } = useWeb3React()
|
const { chainId } = useWeb3React()
|
||||||
const nativeCurrency = useNativeCurrency(chainId)
|
const nativeCurrency = useNativeCurrency(chainId)
|
||||||
const { formatNumber } = useFormatter()
|
const { formatCurrencyAmount, formatNumber, formatPriceImpact } = useFormatter()
|
||||||
|
|
||||||
const label = `${trade.executionPrice.baseCurrency?.symbol} `
|
const label = `${trade.executionPrice.baseCurrency?.symbol} `
|
||||||
const labelInverted = `${trade.executionPrice.quoteCurrency?.symbol}`
|
const labelInverted = `${trade.executionPrice.quoteCurrency?.symbol}`
|
||||||
const formattedPrice = formatTransactionAmount(priceToPreciseFloat(trade.executionPrice))
|
const formattedPrice = formatNumber({
|
||||||
|
input: priceToPreciseFloat(trade.executionPrice),
|
||||||
|
type: NumberType.TokenTx,
|
||||||
|
})
|
||||||
const txCount = getTransactionCount(trade)
|
const txCount = getTransactionCount(trade)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -164,8 +169,14 @@ export default function SwapModalFooter({
|
|||||||
</MouseoverTooltip>
|
</MouseoverTooltip>
|
||||||
<DetailRowValue>
|
<DetailRowValue>
|
||||||
{trade.tradeType === TradeType.EXACT_INPUT
|
{trade.tradeType === TradeType.EXACT_INPUT
|
||||||
? `${trade.minimumAmountOut(allowedSlippage).toSignificant(6)} ${trade.outputAmount.currency.symbol}`
|
? `${formatCurrencyAmount({
|
||||||
: `${trade.maximumAmountIn(allowedSlippage).toSignificant(6)} ${trade.inputAmount.currency.symbol}`}
|
amount: trade.minimumAmountOut(allowedSlippage),
|
||||||
|
type: sixFigsFormatterRules,
|
||||||
|
})} ${trade.outputAmount.currency.symbol}`
|
||||||
|
: `${formatCurrencyAmount({
|
||||||
|
amount: trade.maximumAmountIn(allowedSlippage),
|
||||||
|
type: sixFigsFormatterRules,
|
||||||
|
})} ${trade.inputAmount.currency.symbol}`}
|
||||||
</DetailRowValue>
|
</DetailRowValue>
|
||||||
</Row>
|
</Row>
|
||||||
</ThemedText.BodySmall>
|
</ThemedText.BodySmall>
|
||||||
@ -223,6 +234,8 @@ export default function SwapModalFooter({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TokenTaxLineItem({ trade, type }: { trade: ClassicTrade; type: 'input' | 'output' }) {
|
function TokenTaxLineItem({ trade, type }: { trade: ClassicTrade; type: 'input' | 'output' }) {
|
||||||
|
const { formatPriceImpact } = useFormatter()
|
||||||
|
|
||||||
const [currency, percentage] =
|
const [currency, percentage] =
|
||||||
type === 'input' ? [trade.inputAmount.currency, trade.inputTax] : [trade.outputAmount.currency, trade.outputTax]
|
type === 'input' ? [trade.inputAmount.currency, trade.inputTax] : [trade.outputAmount.currency, trade.outputTax]
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import { Field } from 'state/swap/actions'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { BREAKPOINTS, ThemedText } from 'theme'
|
import { BREAKPOINTS, ThemedText } from 'theme'
|
||||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||||
import { formatReviewSwapCurrencyAmount } from 'utils/formatNumbers'
|
|
||||||
|
|
||||||
export const Label = styled(ThemedText.BodySmall)<{ cursor?: string }>`
|
export const Label = styled(ThemedText.BodySmall)<{ cursor?: string }>`
|
||||||
cursor: ${({ cursor }) => cursor};
|
cursor: ${({ cursor }) => cursor};
|
||||||
@ -45,7 +44,7 @@ interface AmountProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SwapModalHeaderAmount({ tooltipText, label, amount, usdAmount, field, currency }: AmountProps) {
|
export function SwapModalHeaderAmount({ tooltipText, label, amount, usdAmount, field, currency }: AmountProps) {
|
||||||
const { formatNumber } = useFormatter()
|
const { formatNumber, formatReviewSwapCurrencyAmount } = useFormatter()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row align="center" justify="space-between" gap="md">
|
<Row align="center" justify="space-between" gap="md">
|
||||||
|
@ -5,7 +5,7 @@ import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
|||||||
import { useCallback, useMemo, useState } from 'react'
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { ThemedText } from 'theme'
|
import { ThemedText } from 'theme'
|
||||||
import { formatPrice, NumberType, useFormatter } from 'utils/formatNumbers'
|
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||||
|
|
||||||
interface TradePriceProps {
|
interface TradePriceProps {
|
||||||
price: Price<Currency, Currency>
|
price: Price<Currency, Currency>
|
||||||
@ -28,7 +28,7 @@ const StyledPriceContainer = styled.button`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export default function TradePrice({ price }: TradePriceProps) {
|
export default function TradePrice({ price }: TradePriceProps) {
|
||||||
const { formatNumber } = useFormatter()
|
const { formatNumber, formatPrice } = useFormatter()
|
||||||
|
|
||||||
const [showInverted, setShowInverted] = useState<boolean>(false)
|
const [showInverted, setShowInverted] = useState<boolean>(false)
|
||||||
|
|
||||||
@ -37,11 +37,11 @@ export default function TradePrice({ price }: TradePriceProps) {
|
|||||||
|
|
||||||
const formattedPrice = useMemo(() => {
|
const formattedPrice = useMemo(() => {
|
||||||
try {
|
try {
|
||||||
return formatPrice(showInverted ? price : price.invert(), NumberType.TokenTx)
|
return formatPrice({ price: showInverted ? price : price.invert(), type: NumberType.TokenTx })
|
||||||
} catch {
|
} catch {
|
||||||
return '0'
|
return '0'
|
||||||
}
|
}
|
||||||
}, [price, showInverted])
|
}, [formatPrice, price, showInverted])
|
||||||
|
|
||||||
const label = showInverted ? `${price.quoteCurrency?.symbol}` : `${price.baseCurrency?.symbol} `
|
const label = showInverted ? `${price.quoteCurrency?.symbol}` : `${price.baseCurrency?.symbol} `
|
||||||
const labelInverted = showInverted ? `${price.baseCurrency?.symbol} ` : `${price.quoteCurrency?.symbol}`
|
const labelInverted = showInverted ? `${price.baseCurrency?.symbol} ` : `${price.quoteCurrency?.symbol}`
|
||||||
|
@ -37,7 +37,7 @@ import { ExternalLink, HideExtraSmall, HideSmall, StyledRouterLink, ThemedText }
|
|||||||
import { currencyId } from 'utils/currencyId'
|
import { currencyId } from 'utils/currencyId'
|
||||||
import { WrongChainError } from 'utils/errors'
|
import { WrongChainError } from 'utils/errors'
|
||||||
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
|
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
|
||||||
import { formatPrice, NumberType } from 'utils/formatNumbers'
|
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||||
import { formatTickPrice } from 'utils/formatTickPrice'
|
import { formatTickPrice } from 'utils/formatTickPrice'
|
||||||
import { unwrappedToken } from 'utils/unwrappedToken'
|
import { unwrappedToken } from 'utils/unwrappedToken'
|
||||||
|
|
||||||
@ -179,6 +179,8 @@ function CurrentPriceCard({
|
|||||||
currencyQuote?: Currency
|
currencyQuote?: Currency
|
||||||
currencyBase?: Currency
|
currencyBase?: Currency
|
||||||
}) {
|
}) {
|
||||||
|
const { formatPrice } = useFormatter()
|
||||||
|
|
||||||
if (!pool || !currencyQuote || !currencyBase) {
|
if (!pool || !currencyQuote || !currencyBase) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -190,7 +192,7 @@ function CurrentPriceCard({
|
|||||||
<Trans>Current price</Trans>
|
<Trans>Current price</Trans>
|
||||||
</ExtentsText>
|
</ExtentsText>
|
||||||
<ThemedText.DeprecatedMediumHeader textAlign="center">
|
<ThemedText.DeprecatedMediumHeader textAlign="center">
|
||||||
{formatPrice(inverted ? pool.token1Price : pool.token0Price, NumberType.TokenTx)}
|
{formatPrice({ price: inverted ? pool.token1Price : pool.token0Price, type: NumberType.TokenTx })}
|
||||||
</ThemedText.DeprecatedMediumHeader>
|
</ThemedText.DeprecatedMediumHeader>
|
||||||
<ExtentsText>
|
<ExtentsText>
|
||||||
<Trans>
|
<Trans>
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
formatPriceImpact,
|
formatPriceImpact,
|
||||||
formatReviewSwapCurrencyAmount,
|
formatReviewSwapCurrencyAmount,
|
||||||
formatSlippage,
|
formatSlippage,
|
||||||
formatTransactionAmount,
|
|
||||||
formatUSDPrice,
|
formatUSDPrice,
|
||||||
NumberType,
|
NumberType,
|
||||||
priceToPreciseFloat,
|
priceToPreciseFloat,
|
||||||
@ -513,53 +512,6 @@ describe('priceToPreciseFloat', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('formatTransactionAmount', () => {
|
|
||||||
it('undefined or null', () => {
|
|
||||||
expect(formatTransactionAmount(undefined)).toEqual('')
|
|
||||||
expect(formatTransactionAmount(null)).toEqual('')
|
|
||||||
})
|
|
||||||
it('0', () => {
|
|
||||||
expect(formatTransactionAmount(0)).toEqual('0.00')
|
|
||||||
})
|
|
||||||
it('< 0.00001', () => {
|
|
||||||
expect(formatTransactionAmount(0.000000001)).toEqual('<0.00001')
|
|
||||||
})
|
|
||||||
it('1 > number ≥ .00001 full precision', () => {
|
|
||||||
expect(formatTransactionAmount(0.987654321)).toEqual('0.98765')
|
|
||||||
})
|
|
||||||
it('1 > number ≥ .00001 minimum 2 decimals', () => {
|
|
||||||
expect(formatTransactionAmount(0.9)).toEqual('0.90')
|
|
||||||
})
|
|
||||||
it('1 > number ≥ .00001 no trailing zeros beyond 2nd decimal', () => {
|
|
||||||
expect(formatTransactionAmount(0.901000123)).toEqual('0.901')
|
|
||||||
})
|
|
||||||
it('10,000 > number ≥ 1 round to 6 sig figs', () => {
|
|
||||||
expect(formatTransactionAmount(7654.3210789)).toEqual('7,654.32')
|
|
||||||
})
|
|
||||||
it('10,000 > number ≥ 1 round to 6 sig figs 2nd case', () => {
|
|
||||||
expect(formatTransactionAmount(76.3210789)).toEqual('76.3211')
|
|
||||||
})
|
|
||||||
it('10,000 > number ≥ 1 no trailing zeros beyond 2nd decimal place', () => {
|
|
||||||
expect(formatTransactionAmount(7.60000054321)).toEqual('7.60')
|
|
||||||
})
|
|
||||||
it('10,000 > number ≥ 1 always show at least 2 decimal places', () => {
|
|
||||||
expect(formatTransactionAmount(7)).toEqual('7.00')
|
|
||||||
})
|
|
||||||
it('1M > number ≥ 10,000 few decimals', () => {
|
|
||||||
expect(formatTransactionAmount(765432.1)).toEqual('765,432.10')
|
|
||||||
})
|
|
||||||
it('1M > number ≥ 10,000 lots of decimals', () => {
|
|
||||||
expect(formatTransactionAmount(76543.2123424)).toEqual('76,543.21')
|
|
||||||
})
|
|
||||||
it('Number ≥ 1M', () => {
|
|
||||||
expect(formatTransactionAmount(1234567.8901)).toEqual('1,234,567.89')
|
|
||||||
})
|
|
||||||
it('Number ≥ 1M extra long', () => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
|
|
||||||
expect(formatTransactionAmount(1234567890123456.789)).toEqual('1.234568e+15')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('formatReviewSwapCurrencyAmount', () => {
|
describe('formatReviewSwapCurrencyAmount', () => {
|
||||||
it('should use TokenTx formatting under a default length', () => {
|
it('should use TokenTx formatting under a default length', () => {
|
||||||
const currencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '2000000000') // 2,000 USDC
|
const currencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '2000000000') // 2,000 USDC
|
||||||
|
@ -118,7 +118,7 @@ const SIX_SIG_FIGS_TWO_DECIMALS: NumberFormatOptions = {
|
|||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SIX_SIG_FIGS_NO_COMMAS: NumberFormatOptions = {
|
export const SIX_SIG_FIGS_NO_COMMAS: NumberFormatOptions = {
|
||||||
notation: 'standard',
|
notation: 'standard',
|
||||||
maximumSignificantDigits: 6,
|
maximumSignificantDigits: 6,
|
||||||
useGrouping: false,
|
useGrouping: false,
|
||||||
@ -177,7 +177,7 @@ type FormatterBaseRule = { formatterOptions: NumberFormatOptions }
|
|||||||
type FormatterExactRule = { upperBound?: undefined; exact: number } & FormatterBaseRule
|
type FormatterExactRule = { upperBound?: undefined; exact: number } & FormatterBaseRule
|
||||||
type FormatterUpperBoundRule = { upperBound: number; exact?: undefined } & FormatterBaseRule
|
type FormatterUpperBoundRule = { upperBound: number; exact?: undefined } & FormatterBaseRule
|
||||||
|
|
||||||
type FormatterRule = (FormatterExactRule | FormatterUpperBoundRule) & { hardCodedInput?: HardCodedInputFormat }
|
export type FormatterRule = (FormatterExactRule | FormatterUpperBoundRule) & { hardCodedInput?: HardCodedInputFormat }
|
||||||
|
|
||||||
// these formatter objects dictate which formatter rule to use based on the interval that
|
// these formatter objects dictate which formatter rule to use based on the interval that
|
||||||
// the number falls into. for example, based on the rule set below, if your number
|
// the number falls into. for example, based on the rule set below, if your number
|
||||||
@ -349,6 +349,7 @@ export enum NumberType {
|
|||||||
NFTTokenFloorPriceTrailingZeros = 'nft-token-floor-price-trailing-zeros',
|
NFTTokenFloorPriceTrailingZeros = 'nft-token-floor-price-trailing-zeros',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FormatterType = NumberType | FormatterRule[]
|
||||||
const TYPE_TO_FORMATTER_RULES = {
|
const TYPE_TO_FORMATTER_RULES = {
|
||||||
[NumberType.TokenNonTx]: tokenNonTxFormatter,
|
[NumberType.TokenNonTx]: tokenNonTxFormatter,
|
||||||
[NumberType.TokenTx]: tokenTxFormatter,
|
[NumberType.TokenTx]: tokenTxFormatter,
|
||||||
@ -365,8 +366,8 @@ const TYPE_TO_FORMATTER_RULES = {
|
|||||||
[NumberType.NFTCollectionStats]: ntfCollectionStatsFormatter,
|
[NumberType.NFTCollectionStats]: ntfCollectionStatsFormatter,
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFormatterRule(input: number, type: NumberType, conversionRate?: number): FormatterRule {
|
function getFormatterRule(input: number, type: FormatterType, conversionRate?: number): FormatterRule {
|
||||||
const rules = TYPE_TO_FORMATTER_RULES[type]
|
const rules = Array.isArray(type) ? type : TYPE_TO_FORMATTER_RULES[type]
|
||||||
for (const rule of rules) {
|
for (const rule of rules) {
|
||||||
const shouldConvertInput = rule.formatterOptions.currency && conversionRate
|
const shouldConvertInput = rule.formatterOptions.currency && conversionRate
|
||||||
const convertedInput = shouldConvertInput ? input * conversionRate : input
|
const convertedInput = shouldConvertInput ? input * conversionRate : input
|
||||||
@ -384,7 +385,7 @@ function getFormatterRule(input: number, type: NumberType, conversionRate?: numb
|
|||||||
|
|
||||||
interface FormatNumberOptions {
|
interface FormatNumberOptions {
|
||||||
input: Nullish<number>
|
input: Nullish<number>
|
||||||
type?: NumberType
|
type?: FormatterType
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
locale?: SupportedLocale
|
locale?: SupportedLocale
|
||||||
localCurrency?: SupportedLocalCurrency
|
localCurrency?: SupportedLocalCurrency
|
||||||
@ -426,7 +427,7 @@ export function formatNumber({
|
|||||||
|
|
||||||
interface FormatCurrencyAmountOptions {
|
interface FormatCurrencyAmountOptions {
|
||||||
amount: Nullish<CurrencyAmount<Currency>>
|
amount: Nullish<CurrencyAmount<Currency>>
|
||||||
type?: NumberType
|
type?: FormatterType
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
locale?: SupportedLocale
|
locale?: SupportedLocale
|
||||||
localCurrency?: SupportedLocalCurrency
|
localCurrency?: SupportedLocalCurrency
|
||||||
@ -451,10 +452,14 @@ export function formatCurrencyAmount({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatPriceImpact(priceImpact: Percent | undefined): string {
|
export function formatPriceImpact(priceImpact: Percent | undefined, locale: SupportedLocale = DEFAULT_LOCALE): string {
|
||||||
if (!priceImpact) return '-'
|
if (!priceImpact) return '-'
|
||||||
|
|
||||||
return `${priceImpact.multiply(-1).toFixed(3)}%`
|
return `${Number(priceImpact.multiply(-1).toFixed(3)).toLocaleString(locale, {
|
||||||
|
minimumFractionDigits: 3,
|
||||||
|
maximumFractionDigits: 3,
|
||||||
|
useGrouping: false,
|
||||||
|
})}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatSlippage(slippage: Percent | undefined) {
|
export function formatSlippage(slippage: Percent | undefined) {
|
||||||
@ -463,18 +468,29 @@ export function formatSlippage(slippage: Percent | undefined) {
|
|||||||
return `${slippage.toFixed(3)}%`
|
return `${slippage.toFixed(3)}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatPrice(
|
interface FormatPriceProps {
|
||||||
price: Nullish<Price<Currency, Currency>>,
|
price: Nullish<Price<Currency, Currency>>
|
||||||
type: NumberType = NumberType.FiatTokenPrice
|
type: FormatterType
|
||||||
): string {
|
locale?: SupportedLocale
|
||||||
|
localCurrency?: SupportedLocalCurrency
|
||||||
|
conversionRate?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatPrice({
|
||||||
|
price,
|
||||||
|
type = NumberType.FiatTokenPrice,
|
||||||
|
locale = DEFAULT_LOCALE,
|
||||||
|
localCurrency = DEFAULT_LOCAL_CURRENCY,
|
||||||
|
conversionRate,
|
||||||
|
}: FormatPriceProps): string {
|
||||||
if (price === null || price === undefined) {
|
if (price === null || price === undefined) {
|
||||||
return '-'
|
return '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatNumber({ input: parseFloat(price.toSignificant()), type })
|
return formatNumber({ input: parseFloat(price.toSignificant()), type, locale, localCurrency, conversionRate })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatNumberOrString(price: Nullish<number | string>, type: NumberType): string {
|
export function formatNumberOrString(price: Nullish<number | string>, type: FormatterType): string {
|
||||||
if (price === null || price === undefined) return '-'
|
if (price === null || price === undefined) return '-'
|
||||||
if (typeof price === 'string') return formatNumber({ input: parseFloat(price), type })
|
if (typeof price === 'string') return formatNumber({ input: parseFloat(price), type })
|
||||||
return formatNumber({ input: price, type })
|
return formatNumber({ input: price, type })
|
||||||
@ -510,50 +526,15 @@ export const priceToPreciseFloat = (price: Price<Currency, Currency> | undefined
|
|||||||
return floatForLargerNumbers
|
return floatForLargerNumbers
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a numerical amount of any token formatted in human readable string for use in template.
|
|
||||||
*
|
|
||||||
* For transaction review numbers, such as token quantities, NFT price (token-denominated),
|
|
||||||
* network fees, transaction history items. Adheres to guidelines defined here:
|
|
||||||
* https://www.notion.so/uniswaplabs/Number-standards-fbb9f533f10e4e22820722c2f66d23c0
|
|
||||||
* @param num numerical value denominated in any token
|
|
||||||
* @param maxDigits the maximum number of digits that should be shown for the quantity
|
|
||||||
*/
|
|
||||||
export const formatTransactionAmount = (num: number | undefined | null, maxDigits = 9) => {
|
|
||||||
if (num === 0) return '0.00'
|
|
||||||
if (!num) return ''
|
|
||||||
if (num < 0.00001) {
|
|
||||||
return '<0.00001'
|
|
||||||
}
|
|
||||||
if (num >= 0.00001 && num < 1) {
|
|
||||||
return `${Number(num.toFixed(5)).toLocaleString(DEFAULT_LOCALE, {
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 5,
|
|
||||||
})}`
|
|
||||||
}
|
|
||||||
if (num >= 1 && num < 10000) {
|
|
||||||
return `${Number(num.toPrecision(6)).toLocaleString(DEFAULT_LOCALE, {
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 6,
|
|
||||||
})}`
|
|
||||||
}
|
|
||||||
if (num >= 10000 && num < 1000000) {
|
|
||||||
return `${Number(num.toFixed(2)).toLocaleString(DEFAULT_LOCALE, { minimumFractionDigits: 2 })}`
|
|
||||||
}
|
|
||||||
// For very large numbers, switch to scientific notation and show as much precision
|
|
||||||
// as permissible by maxDigits param.
|
|
||||||
if (num >= Math.pow(10, maxDigits - 1)) {
|
|
||||||
return `${num.toExponential(maxDigits - 3)}`
|
|
||||||
}
|
|
||||||
return `${Number(num.toFixed(2)).toLocaleString(DEFAULT_LOCALE, { minimumFractionDigits: 2 })}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAX_AMOUNT_STR_LENGTH = 9
|
const MAX_AMOUNT_STR_LENGTH = 9
|
||||||
|
|
||||||
export function formatReviewSwapCurrencyAmount(amount: CurrencyAmount<Currency>): string {
|
export function formatReviewSwapCurrencyAmount(
|
||||||
let formattedAmount = formatCurrencyAmount({ amount, type: NumberType.TokenTx })
|
amount: CurrencyAmount<Currency>,
|
||||||
|
locale: SupportedLocale = DEFAULT_LOCALE
|
||||||
|
): string {
|
||||||
|
let formattedAmount = formatCurrencyAmount({ amount, type: NumberType.TokenTx, locale })
|
||||||
if (formattedAmount.length > MAX_AMOUNT_STR_LENGTH) {
|
if (formattedAmount.length > MAX_AMOUNT_STR_LENGTH) {
|
||||||
formattedAmount = formatCurrencyAmount({ amount, type: NumberType.SwapTradeAmount })
|
formattedAmount = formatCurrencyAmount({ amount, type: NumberType.SwapTradeAmount, locale })
|
||||||
}
|
}
|
||||||
return formattedAmount
|
return formattedAmount
|
||||||
}
|
}
|
||||||
@ -582,24 +563,24 @@ export function useFormatterLocales(): {
|
|||||||
function handleFallbackCurrency(
|
function handleFallbackCurrency(
|
||||||
selectedCurrency: SupportedLocalCurrency,
|
selectedCurrency: SupportedLocalCurrency,
|
||||||
previousSelectedCurrency: SupportedLocalCurrency | undefined,
|
previousSelectedCurrency: SupportedLocalCurrency | undefined,
|
||||||
|
previousConversionRate: number | undefined,
|
||||||
shouldFallbackToUSD: boolean,
|
shouldFallbackToUSD: boolean,
|
||||||
shouldFallbackToPrevious: boolean
|
shouldFallbackToPrevious: boolean
|
||||||
) {
|
) {
|
||||||
if (shouldFallbackToUSD) return DEFAULT_LOCAL_CURRENCY
|
if (shouldFallbackToUSD) return DEFAULT_LOCAL_CURRENCY
|
||||||
if (shouldFallbackToPrevious) return previousSelectedCurrency
|
if (shouldFallbackToPrevious) return previousConversionRate ? previousSelectedCurrency : DEFAULT_LOCAL_CURRENCY
|
||||||
return selectedCurrency
|
return selectedCurrency
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructs an object that injects the correct locale and local currency into each of the above formatter functions.
|
// Constructs an object that injects the correct locale and local currency into each of the above formatter functions.
|
||||||
export function useFormatter() {
|
export function useFormatter() {
|
||||||
const activeLocalCurrency = useActiveLocalCurrency()
|
|
||||||
const { formatterLocale, formatterLocalCurrency } = useFormatterLocales()
|
const { formatterLocale, formatterLocalCurrency } = useFormatterLocales()
|
||||||
|
|
||||||
const activeLocalCurrencyIsUSD = activeLocalCurrency === GqlCurrency.Usd
|
const formatterLocalCurrencyIsUSD = formatterLocalCurrency === GqlCurrency.Usd
|
||||||
const { data: localCurrencyConversionRate, isLoading: localCurrencyConversionRateIsLoading } =
|
const { data: localCurrencyConversionRate, isLoading: localCurrencyConversionRateIsLoading } =
|
||||||
useLocalCurrencyConversionRate(activeLocalCurrency, activeLocalCurrencyIsUSD)
|
useLocalCurrencyConversionRate(formatterLocalCurrency, formatterLocalCurrencyIsUSD)
|
||||||
|
|
||||||
const previousSelectedCurrency = usePrevious(activeLocalCurrency)
|
const previousSelectedCurrency = usePrevious(formatterLocalCurrency)
|
||||||
const previousConversionRate = usePrevious(localCurrencyConversionRate)
|
const previousConversionRate = usePrevious(localCurrencyConversionRate)
|
||||||
|
|
||||||
const shouldFallbackToPrevious = !localCurrencyConversionRate && localCurrencyConversionRateIsLoading
|
const shouldFallbackToPrevious = !localCurrencyConversionRate && localCurrencyConversionRateIsLoading
|
||||||
@ -607,6 +588,7 @@ export function useFormatter() {
|
|||||||
const currencyToFormatWith = handleFallbackCurrency(
|
const currencyToFormatWith = handleFallbackCurrency(
|
||||||
formatterLocalCurrency,
|
formatterLocalCurrency,
|
||||||
previousSelectedCurrency,
|
previousSelectedCurrency,
|
||||||
|
previousConversionRate,
|
||||||
shouldFallbackToUSD,
|
shouldFallbackToUSD,
|
||||||
shouldFallbackToPrevious
|
shouldFallbackToPrevious
|
||||||
)
|
)
|
||||||
@ -614,8 +596,9 @@ export function useFormatter() {
|
|||||||
? previousConversionRate
|
? previousConversionRate
|
||||||
: localCurrencyConversionRate
|
: localCurrencyConversionRate
|
||||||
|
|
||||||
|
type LocalesType = 'locale' | 'localCurrency' | 'conversionRate'
|
||||||
const formatNumberWithLocales = useCallback(
|
const formatNumberWithLocales = useCallback(
|
||||||
(options: Omit<FormatNumberOptions, 'locale' | 'localCurrency' | 'conversionRate'>) =>
|
(options: Omit<FormatNumberOptions, LocalesType>) =>
|
||||||
formatNumber({
|
formatNumber({
|
||||||
...options,
|
...options,
|
||||||
locale: formatterLocale,
|
locale: formatterLocale,
|
||||||
@ -626,7 +609,7 @@ export function useFormatter() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const formatCurrencyAmountWithLocales = useCallback(
|
const formatCurrencyAmountWithLocales = useCallback(
|
||||||
(options: Omit<FormatCurrencyAmountOptions, 'locale' | 'localCurrency' | 'conversionRate'>) =>
|
(options: Omit<FormatCurrencyAmountOptions, LocalesType>) =>
|
||||||
formatCurrencyAmount({
|
formatCurrencyAmount({
|
||||||
...options,
|
...options,
|
||||||
locale: formatterLocale,
|
locale: formatterLocale,
|
||||||
@ -636,11 +619,41 @@ export function useFormatter() {
|
|||||||
[currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith]
|
[currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const formatPriceWithLocales = useCallback(
|
||||||
|
(options: Omit<FormatPriceProps, LocalesType>) =>
|
||||||
|
formatPrice({
|
||||||
|
...options,
|
||||||
|
locale: formatterLocale,
|
||||||
|
localCurrency: currencyToFormatWith,
|
||||||
|
conversionRate: localCurrencyConversionRateToFormatWith,
|
||||||
|
}),
|
||||||
|
[currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith]
|
||||||
|
)
|
||||||
|
|
||||||
|
const formatPriceImpactWithLocales = useCallback(
|
||||||
|
(priceImpact: Percent | undefined) => formatPriceImpact(priceImpact, formatterLocale),
|
||||||
|
[formatterLocale]
|
||||||
|
)
|
||||||
|
|
||||||
|
const formatReviewSwapCurrencyAmountWithLocales = useCallback(
|
||||||
|
(amount: CurrencyAmount<Currency>) => formatReviewSwapCurrencyAmount(amount, formatterLocale),
|
||||||
|
[formatterLocale]
|
||||||
|
)
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
formatNumber: formatNumberWithLocales,
|
|
||||||
formatCurrencyAmount: formatCurrencyAmountWithLocales,
|
formatCurrencyAmount: formatCurrencyAmountWithLocales,
|
||||||
|
formatNumber: formatNumberWithLocales,
|
||||||
|
formatPrice: formatPriceWithLocales,
|
||||||
|
formatPriceImpact: formatPriceImpactWithLocales,
|
||||||
|
formatReviewSwapCurrencyAmount: formatReviewSwapCurrencyAmountWithLocales,
|
||||||
}),
|
}),
|
||||||
[formatCurrencyAmountWithLocales, formatNumberWithLocales]
|
[
|
||||||
|
formatCurrencyAmountWithLocales,
|
||||||
|
formatNumberWithLocales,
|
||||||
|
formatPriceImpactWithLocales,
|
||||||
|
formatPriceWithLocales,
|
||||||
|
formatReviewSwapCurrencyAmountWithLocales,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import { Percent } from '@uniswap/sdk-core'
|
|
||||||
|
|
||||||
import formatPriceImpact from './formatPriceImpact'
|
|
||||||
|
|
||||||
describe('formatPriceImpact', () => {
|
|
||||||
it('formats price impact', () => {
|
|
||||||
expect(formatPriceImpact(new Percent(5, 10_000))).toEqual('-0.05%')
|
|
||||||
})
|
|
||||||
// While there's theoretically no such thing as "positive price impact", this can show up
|
|
||||||
// due to a bug in routing-api, so it's still tested for
|
|
||||||
it('formats price impact when given a negative value', () => {
|
|
||||||
expect(formatPriceImpact(new Percent(-5, 10_000))).toEqual('0.05%')
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,5 +0,0 @@
|
|||||||
import { Percent } from '@uniswap/sdk-core'
|
|
||||||
|
|
||||||
export default function formatPriceImpact(priceImpact: Percent) {
|
|
||||||
return `${priceImpact.multiply(-1).toFixed(2)}%`
|
|
||||||
}
|
|
@ -20,5 +20,5 @@ export function formatTickPrice({ price, atLimit, direction, placeholder, number
|
|||||||
return placeholder
|
return placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatPrice(price, numberType ?? NumberType.TokenNonTx)
|
return formatPrice({ price, type: numberType ?? NumberType.TokenNonTx })
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user