chore: moving coned formatting to interface (#7114)

This commit is contained in:
Jack Short 2023-08-09 14:01:37 -04:00 committed by GitHub
parent add6df46e6
commit b1c99d4b37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 594 additions and 35 deletions

@ -57,9 +57,8 @@ module.exports = {
'\\.(t|j)sx?$': '@swc/jest',
},
// Use @uniswap/conedison's build directly, as jest does not support its exports.
transformIgnorePatterns: ['@uniswap/conedison/format', '@uniswap/conedison/provider'],
transformIgnorePatterns: ['@uniswap/conedison/provider'],
moduleNameMapper: {
'@uniswap/conedison/format': '@uniswap/conedison/dist/format',
'@uniswap/conedison/provider': '@uniswap/conedison/dist/provider',
},
})

@ -1,6 +1,5 @@
import { Trans } from '@lingui/macro'
import { BrowserEvent, InterfaceElementName, InterfaceEventName, SharedEventName } from '@uniswap/analytics-events'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent, TraceEvent } from 'analytics'
@ -24,6 +23,7 @@ import { updateSelectedWallet } from 'state/user/reducer'
import styled, { useTheme } from 'styled-components'
import { CopyHelper, ExternalLink, ThemedText } from 'theme'
import { shortenAddress } from 'utils'
import { formatNumber, NumberType } from 'utils/formatNumbers'
import { useCloseModal, useFiatOnrampAvailability, useOpenModal, useToggleModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'

@ -1,6 +1,5 @@
import { BigNumber } from '@ethersproject/bignumber'
import { t } from '@lingui/macro'
import { formatCurrencyAmount } from '@uniswap/conedison/format'
import { ChainId, Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { nativeOnChain } from '@uniswap/smart-order-router'
import UniswapXBolt from 'assets/svg/bolt.svg'
@ -24,6 +23,7 @@ import {
TransactionType,
WrapTransactionInfo,
} from 'state/transactions/types'
import { formatCurrencyAmount } from 'utils/formatNumbers'
import { CancelledTransactionTitleTable, getActivityTitle, OrderTextTable } from '../constants'
import { Activity, ActivityMap } from './types'

@ -1,5 +1,4 @@
import { t } from '@lingui/macro'
import { formatFiatPrice, formatNumberOrString, NumberType } from '@uniswap/conedison/format'
import { ChainId, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, UNI_ADDRESSES } from '@uniswap/sdk-core'
import UniswapXBolt from 'assets/svg/bolt.svg'
import moonpayLogoSrc from 'assets/svg/moonpay.svg'
@ -22,6 +21,7 @@ import { logSentryErrorForUnsupportedChain, supportedChainIdFromGQLChain } from
import ms from 'ms'
import { useEffect, useState } from 'react'
import { isAddress } from 'utils'
import { formatFiatPrice, formatNumberOrString, NumberType } from 'utils/formatNumbers'
import { MOONPAY_SENDER_ADDRESSES, OrderStatusTable, OrderTextTable } from '../constants'
import { Activity } from './types'

@ -1,6 +1,5 @@
import { t } from '@lingui/macro'
import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { Position } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
import { TraceEvent } from 'analytics'
@ -14,6 +13,7 @@ import { useCallback, useMemo, useReducer } from 'react'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components'
import { ThemedText } from 'theme'
import { formatNumber, NumberType } from 'utils/formatNumbers'
import { ExpandoRow } from '../ExpandoRow'
import { PortfolioLogo } from '../PortfolioLogo'

@ -1,5 +1,4 @@
import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { TraceEvent } from 'analytics'
import { useCachedPortfolioBalancesQuery } from 'components/AccountDrawer/PrefetchBalancesWrapper'
import Row from 'components/Row'
@ -12,6 +11,7 @@ import { useCallback, useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components'
import { EllipsisStyle, ThemedText } from 'theme'
import { formatNumber, NumberType } from 'utils/formatNumbers'
import { useToggleAccountDrawer } from '../..'
import { PortfolioArrow } from '../../AuthenticatedHeader'

@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import { formatNumber, formatPriceImpact, NumberType } from '@uniswap/conedison/format'
import { Percent } from '@uniswap/sdk-core'
import Row from 'components/Row'
import { LoadingBubble } from 'components/Tokens/loading'
@ -7,6 +6,7 @@ import { MouseoverTooltip } from 'components/Tooltip'
import { useMemo } from 'react'
import styled from 'styled-components'
import { ThemedText } from 'theme'
import { formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers'
import { warningSeverity } from 'utils/prices'
const FiatLoadingBubble = styled(LoadingBubble)`

@ -1,6 +1,5 @@
import { Trans } from '@lingui/macro'
import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core'
@ -15,6 +14,7 @@ import { ReactNode, useCallback, useState } from 'react'
import { Lock } from 'react-feather'
import styled, { useTheme } from 'styled-components'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useCurrencyBalance } from '../../state/connection/hooks'

@ -1,5 +1,4 @@
import { InterfaceEventName } from '@uniswap/analytics-events'
import { formatUSDPrice } from '@uniswap/conedison/format'
import { sendAnalyticsEvent } from 'analytics'
import clsx from 'clsx'
import QueryTokenLogo from 'components/Logo/QueryTokenLogo'
@ -19,6 +18,7 @@ import { useCallback, useEffect, useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import styled from 'styled-components'
import { ThemedText } from 'theme'
import { formatUSDPrice } from 'utils/formatNumbers'
import { DeltaText, getDeltaArrow } from '../Tokens/TokenDetails/PriceChart'
import { useAddRecentlySearchedAsset } from './RecentlySearchedAssets'

@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { ChainId, Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
@ -9,6 +8,7 @@ import { useStablecoinValue } from 'hooks/useStablecoinPrice'
import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
import styled, { useTheme } from 'styled-components'
import { ThemedText } from 'theme'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
const BalancesCard = styled.div`
box-shadow: ${({ theme }) => theme.shallowShadow};

@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
@ -8,6 +7,7 @@ import { useStablecoinValue } from 'hooks/useStablecoinPrice'
import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
import styled from 'styled-components'
import { StyledInternalLink } from 'theme'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
const Wrapper = styled.div`
align-content: center;

@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import { formatUSDPrice } from '@uniswap/conedison/format'
import { AxisBottom, TickFormatter } from '@visx/axis'
import { localPoint } from '@visx/event'
import { EventType } from '@visx/event/lib/types'
@ -24,6 +23,7 @@ import {
monthYearDayFormatter,
weekFormatter,
} from 'utils/formatChartTimes'
import { formatUSDPrice } from 'utils/formatNumbers'
const DATA_EMPTY = { value: 0, timestamp: 0 }

@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { ChainId } from '@uniswap/sdk-core'
import { MouseoverTooltip } from 'components/Tooltip'
import { getChainInfo } from 'constants/chainInfo'
@ -7,6 +6,7 @@ import { ReactNode } from 'react'
import styled from 'styled-components'
import { ExternalLink, ThemedText } from 'theme'
import { textFadeIn } from 'theme/styles'
import { formatNumber, NumberType } from 'utils/formatNumbers'
import { UNSUPPORTED_METADATA_CHAINS } from '../constants'
import { TokenSortMethod } from '../state'

@ -1,6 +1,5 @@
import { Trans } from '@lingui/macro'
import { InterfaceEventName } from '@uniswap/analytics-events'
import { formatNumber, formatUSDPrice, NumberType } from '@uniswap/conedison/format'
import { ParentSize } from '@visx/responsive'
import { sendAnalyticsEvent } from 'analytics'
import SparklineChart from 'components/Charts/SparklineChart'
@ -15,6 +14,7 @@ import { ArrowDown, ArrowUp, Info } from 'react-feather'
import { Link, useParams } from 'react-router-dom'
import styled, { css, useTheme } from 'styled-components'
import { BREAKPOINTS, ClickableStyle } from 'theme'
import { formatNumber, formatUSDPrice, NumberType } from 'utils/formatNumbers'
import {
LARGE_MEDIA_BREAKPOINT,

@ -1,6 +1,5 @@
import { Plural, Trans } from '@lingui/macro'
import { InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
import { formatCurrencyAmount, formatNumber, formatPriceImpact, NumberType } from '@uniswap/conedison/format'
import { Percent, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent } from 'analytics'
@ -9,6 +8,7 @@ import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { InterfaceTrade } from 'state/routing/types'
import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
import { formatCurrencyAmount, formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers'
import { Separator, ThemedText } from '../../theme'
import Column from '../Column'

@ -5,7 +5,6 @@ import {
SwapEventName,
SwapPriceUpdateUserResponse,
} from '@uniswap/analytics-events'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { Currency, Percent } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent, Trace, useTrace } from 'analytics'
@ -30,6 +29,7 @@ import styled from 'styled-components'
import { ThemedText } from 'theme'
import invariant from 'tiny-invariant'
import { isL2ChainId } from 'utils/chains'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
import { formatSwapPriceUpdatedEventProperties } from 'utils/loggingFormatters'
import { didUserReject } from 'utils/swapErrorToUserReadableMessage'
import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer'

@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { AutoColumn } from 'components/Column'
import UniswapXRouterLabel, { UniswapXGradient } from 'components/RouterLabel/UniswapXRouterLabel'
import Row from 'components/Row'
@ -8,6 +7,7 @@ import { InterfaceTrade } from 'state/routing/types'
import { isClassicTrade, isUniswapXTrade } from 'state/routing/utils'
import styled from 'styled-components'
import { Divider, ExternalLink, ThemedText } from 'theme'
import { formatNumber, NumberType } from 'utils/formatNumbers'
const Container = styled(AutoColumn)`
padding: 4px;

@ -1,5 +1,4 @@
import { InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent } from 'analytics'
import { LoadingOpacityContainer } from 'components/Loader/styled'
@ -11,6 +10,7 @@ import { InterfaceTrade } from 'state/routing/types'
import { isUniswapXTrade } from 'state/routing/utils'
import styled from 'styled-components'
import { ThemedText } from 'theme'
import { formatNumber, NumberType } from 'utils/formatNumbers'
import { ReactComponent as GasIcon } from '../../assets/images/gas-icon.svg'
import { GasBreakdownTooltip } from './GasBreakdownTooltip'

@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import { formatPriceImpact } from '@uniswap/conedison/format'
import { Percent } from '@uniswap/sdk-core'
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
import { ColumnCenter } from 'components/Column'
@ -7,6 +6,7 @@ import Row from 'components/Row'
import { AlertTriangle } from 'react-feather'
import styled from 'styled-components'
import { CloseIcon, ThemedText } from 'theme'
import { formatPriceImpact } from 'utils/formatNumbers'
import Modal from '../Modal'

@ -1,6 +1,5 @@
import { Plural, Trans } from '@lingui/macro'
import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
import { formatNumber, formatPriceImpact, NumberType } from '@uniswap/conedison/format'
import { Percent, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { TraceEvent } from 'analytics'
@ -16,6 +15,7 @@ import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
import { useRouterPreference, useUserSlippageTolerance } from 'state/user/hooks'
import styled, { useTheme } from 'styled-components'
import { ThemedText } from 'theme'
import { formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers'
import { formatTransactionAmount, priceToPreciseFloat } from 'utils/formatNumbers'
import getRoutingDiagramEntries from 'utils/getRoutingDiagramEntries'
import { formatSwapButtonClickEventProperties } from 'utils/loggingFormatters'

@ -1,4 +1,3 @@
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import {
ETH_MAINNET,
TEST_ALLOWED_SLIPPAGE,
@ -7,6 +6,7 @@ import {
TEST_TRADE_EXACT_OUTPUT,
} from 'test-utils/constants'
import { render, screen } from 'test-utils/render'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
import SwapModalHeader from './SwapModalHeader'

@ -1,4 +1,3 @@
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
import Column from 'components/Column'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
@ -10,6 +9,7 @@ import { TextProps } from 'rebass'
import { Field } from 'state/swap/actions'
import styled from 'styled-components'
import { BREAKPOINTS, ThemedText } from 'theme'
import { formatNumber, NumberType } from 'utils/formatNumbers'
import { formatReviewSwapCurrencyAmount } from 'utils/formatNumbers'
export const Label = styled(ThemedText.BodySmall)<{ cursor?: string }>`

@ -1,11 +1,11 @@
import { Trans } from '@lingui/macro'
import { formatNumber, formatPrice, NumberType } from '@uniswap/conedison/format'
import { Currency, Price } from '@uniswap/sdk-core'
import { useUSDPrice } from 'hooks/useUSDPrice'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useCallback, useMemo, useState } from 'react'
import styled from 'styled-components'
import { ThemedText } from 'theme'
import { formatNumber, formatPrice, NumberType } from 'utils/formatNumbers'
interface TradePriceProps {
price: Price<Currency, Currency>

@ -1,4 +1,3 @@
import { formatNumberOrString, NumberType } from '@uniswap/conedison/format'
import { loadingAnimation } from 'components/Loader/styled'
import { LoadingBubble } from 'components/Tokens/loading'
import { useCollection } from 'graphql/data/nft/Collection'
@ -7,6 +6,7 @@ import { Markets, TrendingCollection } from 'nft/types'
import { ethNumberStandardFormatter } from 'nft/utils'
import styled from 'styled-components'
import { ThemedText } from 'theme/components/text'
import { formatNumberOrString, NumberType } from 'utils/formatNumbers'
const CarouselCardBorder = styled.div`
width: 100%;

@ -1,6 +1,5 @@
import { Trans } from '@lingui/macro'
import { InterfaceModalName, NFTEventName } from '@uniswap/analytics-events'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent, useTrace } from 'analytics'
import Column from 'components/Column'
@ -26,6 +25,7 @@ import { ArrowLeft } from 'react-feather'
import styled from 'styled-components'
import { BREAKPOINTS, ThemedText } from 'theme'
import { Z_INDEX } from 'theme/zIndex'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
import { shallow } from 'zustand/shallow'
import { ListModal } from './Modal/ListModal'

@ -1,6 +1,5 @@
import { Trans } from '@lingui/macro'
import { InterfaceModalName, NFTEventName } from '@uniswap/analytics-events'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent, Trace, useTrace } from 'analytics'
import { useStablecoinValue } from 'hooks/useStablecoinPrice'
@ -16,6 +15,7 @@ import { X } from 'react-feather'
import styled from 'styled-components'
import { BREAKPOINTS, ThemedText } from 'theme'
import { Z_INDEX } from 'theme/zIndex'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
import { shallow } from 'zustand/shallow'
import { TitleRow } from '../shared'

@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { useWeb3React } from '@web3-react/core'
import Column from 'components/Column'
import { ScrollBarStyles } from 'components/Common'
@ -14,6 +13,7 @@ import { useMemo } from 'react'
import { Twitter, X } from 'react-feather'
import styled, { css, useTheme } from 'styled-components'
import { BREAKPOINTS, ThemedText } from 'theme'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
import { TitleRow } from '../shared'

@ -2,7 +2,6 @@ import { BigNumber } from '@ethersproject/bignumber'
import type { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { InterfacePageName } from '@uniswap/analytics-events'
import { formatPrice, NumberType } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core'
import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
@ -38,6 +37,7 @@ import styled, { useTheme } from 'styled-components'
import { ExternalLink, HideExtraSmall, HideSmall, StyledRouterLink, ThemedText } from 'theme'
import { currencyId } from 'utils/currencyId'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { formatPrice, NumberType } from 'utils/formatNumbers'
import { formatTickPrice } from 'utils/formatTickPrice'
import { unwrappedToken } from 'utils/unwrappedToken'

@ -8,7 +8,6 @@ import {
SharedEventName,
SwapEventName,
} from '@uniswap/analytics-events'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { ChainId, Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import { UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk'
import { useWeb3React } from '@web3-react/core'
@ -58,6 +57,7 @@ import swapReducer, { initialState as initialSwapState, SwapState } from 'state/
import styled, { useTheme } from 'styled-components'
import { LinkStyledButton, ThemedText } from 'theme'
import { computeFiatValuePriceImpact } from 'utils/computeFiatValuePriceImpact'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
import { maxAmountSpend } from 'utils/maxAmountSpend'
import { computeRealizedPriceImpact, warningSeverity } from 'utils/prices'
import { didUserReject } from 'utils/swapErrorToUserReadableMessage'

@ -1,13 +1,201 @@
import { CurrencyAmount, Price } from '@uniswap/sdk-core'
import { CurrencyAmount, Percent, Price } from '@uniswap/sdk-core'
import { renBTC, USDC_MAINNET } from 'constants/tokens'
import {
currencyAmountToPreciseFloat,
formatNumber,
formatPriceImpact,
formatReviewSwapCurrencyAmount,
formatSlippage,
formatTransactionAmount,
formatUSDPrice,
NumberType,
priceToPreciseFloat,
} from './formatNumbers'
it('formats token reference numbers correctly', () => {
expect(formatNumber(1234567000000000, NumberType.TokenNonTx)).toBe('>999T')
expect(formatNumber(1002345, NumberType.TokenNonTx)).toBe('1.00M')
expect(formatNumber(1234, NumberType.TokenNonTx)).toBe('1,234.00')
expect(formatNumber(0.00909, NumberType.TokenNonTx)).toBe('0.009')
expect(formatNumber(0.09001, NumberType.TokenNonTx)).toBe('0.090')
expect(formatNumber(0.00099, NumberType.TokenNonTx)).toBe('<0.001')
expect(formatNumber(0, NumberType.TokenNonTx)).toBe('0')
})
it('formats token transaction numbers correctly', () => {
expect(formatNumber(1234567.8901, NumberType.TokenTx)).toBe('1,234,567.89')
expect(formatNumber(765432.1, NumberType.TokenTx)).toBe('765,432.10')
expect(formatNumber(7654.321, NumberType.TokenTx)).toBe('7,654.32')
expect(formatNumber(765.4321, NumberType.TokenTx)).toBe('765.432')
expect(formatNumber(76.54321, NumberType.TokenTx)).toBe('76.5432')
expect(formatNumber(7.654321, NumberType.TokenTx)).toBe('7.65432')
expect(formatNumber(7.60000054321, NumberType.TokenTx)).toBe('7.60')
expect(formatNumber(7.6, NumberType.TokenTx)).toBe('7.60')
expect(formatNumber(7, NumberType.TokenTx)).toBe('7.00')
expect(formatNumber(0.987654321, NumberType.TokenTx)).toBe('0.98765')
expect(formatNumber(0.9, NumberType.TokenTx)).toBe('0.90')
expect(formatNumber(0.901000123, NumberType.TokenTx)).toBe('0.901')
expect(formatNumber(0.000000001, NumberType.TokenTx)).toBe('<0.00001')
expect(formatNumber(0, NumberType.TokenTx)).toBe('0')
})
it('formats fiat estimates on token details pages correctly', () => {
expect(formatNumber(1234567.891, NumberType.FiatTokenDetails)).toBe('$1.23M')
expect(formatNumber(1234.5678, NumberType.FiatTokenDetails)).toBe('$1,234.57')
expect(formatNumber(1.048942, NumberType.FiatTokenDetails)).toBe('$1.049')
expect(formatNumber(0.001231, NumberType.FiatTokenDetails)).toBe('$0.00123')
expect(formatNumber(0.00001231, NumberType.FiatTokenDetails)).toBe('$0.0000123')
expect(formatNumber(0.0000001234, NumberType.FiatTokenDetails)).toBe('$0.000000123')
expect(formatNumber(0.000000009876, NumberType.FiatTokenDetails)).toBe('<$0.00000001')
})
it('formats fiat estimates for tokens correctly', () => {
expect(formatNumber(1234567.891, NumberType.FiatTokenPrice)).toBe('$1.23M')
expect(formatNumber(1234.5678, NumberType.FiatTokenPrice)).toBe('$1,234.57')
expect(formatNumber(0.010235, NumberType.FiatTokenPrice)).toBe('$0.0102')
expect(formatNumber(0.001231, NumberType.FiatTokenPrice)).toBe('$0.00123')
expect(formatNumber(0.00001231, NumberType.FiatTokenPrice)).toBe('$0.0000123')
expect(formatNumber(0.0000001234, NumberType.FiatTokenPrice)).toBe('$0.000000123')
expect(formatNumber(0.000000009876, NumberType.FiatTokenPrice)).toBe('<$0.00000001')
expect(formatNumber(10000000000000000000000000000000, NumberType.FiatTokenPrice)).toBe('$1.000000E31')
})
it('formats fiat estimates for token stats correctly', () => {
expect(formatNumber(1234576, NumberType.FiatTokenStats)).toBe('$1.2M')
expect(formatNumber(234567, NumberType.FiatTokenStats)).toBe('$234.6K')
expect(formatNumber(123.456, NumberType.FiatTokenStats)).toBe('$123.46')
expect(formatNumber(1.23, NumberType.FiatTokenStats)).toBe('$1.23')
expect(formatNumber(0.123, NumberType.FiatTokenStats)).toBe('$0.12')
expect(formatNumber(0.00123, NumberType.FiatTokenStats)).toBe('<$0.01')
expect(formatNumber(0, NumberType.FiatTokenStats)).toBe('-')
})
it('formats gas USD prices correctly', () => {
expect(formatNumber(1234567.891, NumberType.FiatGasPrice)).toBe('$1.23M')
expect(formatNumber(18.448, NumberType.FiatGasPrice)).toBe('$18.45')
expect(formatNumber(0.0099, NumberType.FiatGasPrice)).toBe('<$0.01')
expect(formatNumber(0, NumberType.FiatGasPrice)).toBe('$0.00')
})
it('formats USD token quantities prices correctly', () => {
expect(formatNumber(1234567.891, NumberType.FiatTokenQuantity)).toBe('$1.23M')
expect(formatNumber(18.448, NumberType.FiatTokenQuantity)).toBe('$18.45')
expect(formatNumber(0.0099, NumberType.FiatTokenQuantity)).toBe('<$0.01')
expect(formatNumber(0, NumberType.FiatTokenQuantity)).toBe('$0.00')
})
it('formats Swap text input/output numbers correctly', () => {
expect(formatNumber(1234567.8901, NumberType.SwapTradeAmount)).toBe('1234570')
expect(formatNumber(765432.1, NumberType.SwapTradeAmount)).toBe('765432')
expect(formatNumber(7654.321, NumberType.SwapTradeAmount)).toBe('7654.32')
expect(formatNumber(765.4321, NumberType.SwapTradeAmount)).toBe('765.432')
expect(formatNumber(76.54321, NumberType.SwapTradeAmount)).toBe('76.5432')
expect(formatNumber(7.654321, NumberType.SwapTradeAmount)).toBe('7.65432')
expect(formatNumber(7.60000054321, NumberType.SwapTradeAmount)).toBe('7.60')
expect(formatNumber(7.6, NumberType.SwapTradeAmount)).toBe('7.60')
expect(formatNumber(7, NumberType.SwapTradeAmount)).toBe('7.00')
expect(formatNumber(0.987654321, NumberType.SwapTradeAmount)).toBe('0.98765')
expect(formatNumber(0.9, NumberType.SwapTradeAmount)).toBe('0.90')
expect(formatNumber(0.901000123, NumberType.SwapTradeAmount)).toBe('0.901')
expect(formatNumber(0.000000001, NumberType.SwapTradeAmount)).toBe('0.000000001')
expect(formatNumber(0, NumberType.SwapTradeAmount)).toBe('0')
})
it('formats NFT numbers correctly', () => {
expect(formatNumber(1234567000000000, NumberType.NFTTokenFloorPrice)).toBe('>999T')
expect(formatNumber(1002345, NumberType.NFTTokenFloorPrice)).toBe('1M')
expect(formatNumber(1234, NumberType.NFTTokenFloorPrice)).toBe('1.23K')
expect(formatNumber(12.34467, NumberType.NFTTokenFloorPrice)).toBe('12.34')
expect(formatNumber(12.1, NumberType.NFTTokenFloorPrice)).toBe('12.1')
expect(formatNumber(0.00909, NumberType.NFTTokenFloorPrice)).toBe('0.009')
expect(formatNumber(0.09001, NumberType.NFTTokenFloorPrice)).toBe('0.09')
expect(formatNumber(0.00099, NumberType.NFTTokenFloorPrice)).toBe('<0.001')
expect(formatNumber(0, NumberType.NFTTokenFloorPrice)).toBe('0')
expect(formatNumber(12.1, NumberType.NFTTokenFloorPriceTrailingZeros)).toBe('12.10')
expect(formatNumber(0.09001, NumberType.NFTTokenFloorPriceTrailingZeros)).toBe('0.090')
expect(formatNumber(0.987654321, NumberType.NFTCollectionStats)).toBe('1')
expect(formatNumber(0.9, NumberType.NFTCollectionStats)).toBe('1')
expect(formatNumber(76543.21, NumberType.NFTCollectionStats)).toBe('76.5K')
expect(formatNumber(7.60000054321, NumberType.NFTCollectionStats)).toBe('8')
expect(formatNumber(1234567890, NumberType.NFTCollectionStats)).toBe('1.2B')
expect(formatNumber(1234567000000000, NumberType.NFTCollectionStats)).toBe('1234.6T')
})
describe('formatUSDPrice', () => {
it('should correctly format 0.000000009876', () => {
expect(formatUSDPrice(0.000000009876)).toBe('<$0.00000001')
})
it('should correctly format 0.00001231', () => {
expect(formatUSDPrice(0.00001231)).toBe('$0.0000123')
})
it('should correctly format 0.001231', () => {
expect(formatUSDPrice(0.001231)).toBe('$0.00123')
})
it('should correctly format 0.0', () => {
expect(formatUSDPrice(0.0)).toBe('$0.00')
})
it('should correctly format 0', () => {
expect(formatUSDPrice(0)).toBe('$0.00')
})
it('should correctly format 1.048942', () => {
expect(formatUSDPrice(1.048942)).toBe('$1.05')
})
it('should correctly format 0.10235', () => {
expect(formatUSDPrice(0.10235)).toBe('$0.102')
})
it('should correctly format 1234.5678', () => {
expect(formatUSDPrice(1_234.5678)).toBe('$1,234.57')
})
it('should correctly format 1234567.8910', () => {
expect(formatUSDPrice(1_234_567.891)).toBe('$1.23M')
})
it('should correctly format 1000000000000', () => {
expect(formatUSDPrice(1_000_000_000_000)).toBe('$1.00T')
})
it('should correctly format 1000000000000000', () => {
expect(formatUSDPrice(1_000_000_000_000_000)).toBe('$1000.00T')
})
})
describe('formatPriceImpact', () => {
it('should correctly format undefined', () => {
expect(formatPriceImpact(undefined)).toBe('-')
})
it('correctly formats a percent with 3 significant digits', () => {
expect(formatPriceImpact(new Percent(-1, 100000))).toBe('0.001%')
expect(formatPriceImpact(new Percent(-1, 1000))).toBe('0.100%')
expect(formatPriceImpact(new Percent(-1, 100))).toBe('1.000%')
expect(formatPriceImpact(new Percent(-1, 10))).toBe('10.000%')
expect(formatPriceImpact(new Percent(-1, 1))).toBe('100.000%')
})
})
describe('formatSlippage', () => {
it('should correctly format undefined', () => {
expect(formatSlippage(undefined)).toBe('-')
})
it('correctly formats a percent with 3 significant digits', () => {
expect(formatSlippage(new Percent(1, 100000))).toBe('0.001%')
expect(formatSlippage(new Percent(1, 1000))).toBe('0.100%')
expect(formatSlippage(new Percent(1, 100))).toBe('1.000%')
expect(formatSlippage(new Percent(1, 10))).toBe('10.000%')
expect(formatSlippage(new Percent(1, 1))).toBe('100.000%')
})
})
describe('currencyAmountToPreciseFloat', () => {
it('small number', () => {
const currencyAmount = CurrencyAmount.fromFractionalAmount(USDC_MAINNET, '20000', '7')

@ -1,8 +1,380 @@
/* Copied from Uniswap/v-3: https://github.com/Uniswap/v3-info/blob/master/src/utils/numbers.ts */
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount, Price } from '@uniswap/sdk-core'
import { Currency, CurrencyAmount, Percent, Price } from '@uniswap/sdk-core'
import { DEFAULT_LOCALE } from 'constants/locales'
type Nullish<T> = T | null | undefined
// Number formatting follows the standards laid out in this spec:
// https://www.notion.so/uniswaplabs/Number-standards-fbb9f533f10e4e22820722c2f66d23c0
const FIVE_DECIMALS_MAX_TWO_DECIMALS_MIN = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumFractionDigits: 5,
minimumFractionDigits: 2,
})
const FIVE_DECIMALS_MAX_TWO_DECIMALS_MIN_NO_COMMAS = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumFractionDigits: 5,
minimumFractionDigits: 2,
useGrouping: false,
})
const NO_DECIMALS = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumFractionDigits: 0,
minimumFractionDigits: 0,
})
const THREE_DECIMALS_NO_TRAILING_ZEROS = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumFractionDigits: 3,
minimumFractionDigits: 0,
})
const THREE_DECIMALS = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumFractionDigits: 3,
minimumFractionDigits: 3,
})
const THREE_DECIMALS_USD = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumFractionDigits: 3,
minimumFractionDigits: 3,
currency: 'USD',
style: 'currency',
})
const TWO_DECIMALS_NO_TRAILING_ZEROS = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumFractionDigits: 2,
})
const TWO_DECIMALS = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumFractionDigits: 2,
minimumFractionDigits: 2,
})
const TWO_DECIMALS_USD = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumFractionDigits: 2,
minimumFractionDigits: 2,
currency: 'USD',
style: 'currency',
})
const SHORTHAND_TWO_DECIMALS = new Intl.NumberFormat('en-US', {
notation: 'compact',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
const SHORTHAND_TWO_DECIMALS_NO_TRAILING_ZEROS = new Intl.NumberFormat('en-US', {
notation: 'compact',
maximumFractionDigits: 2,
})
const SHORTHAND_ONE_DECIMAL = new Intl.NumberFormat('en-US', {
notation: 'compact',
minimumFractionDigits: 1,
maximumFractionDigits: 1,
})
const SHORTHAND_USD_TWO_DECIMALS = new Intl.NumberFormat('en-US', {
notation: 'compact',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
currency: 'USD',
style: 'currency',
})
const SHORTHAND_USD_ONE_DECIMAL = new Intl.NumberFormat('en-US', {
notation: 'compact',
minimumFractionDigits: 1,
maximumFractionDigits: 1,
currency: 'USD',
style: 'currency',
})
const SIX_SIG_FIGS_TWO_DECIMALS = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumSignificantDigits: 6,
minimumSignificantDigits: 3,
maximumFractionDigits: 2,
minimumFractionDigits: 2,
})
const SIX_SIG_FIGS_NO_COMMAS = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumSignificantDigits: 6,
useGrouping: false,
})
const SIX_SIG_FIGS_TWO_DECIMALS_NO_COMMAS = new Intl.NumberFormat('en-US', {
notation: 'standard',
maximumSignificantDigits: 6,
minimumSignificantDigits: 3,
maximumFractionDigits: 2,
minimumFractionDigits: 2,
useGrouping: false,
})
const THREE_SIG_FIGS_USD = new Intl.NumberFormat('en-US', {
notation: 'standard',
minimumSignificantDigits: 3,
maximumSignificantDigits: 3,
currency: 'USD',
style: 'currency',
})
const SEVEN_SIG_FIGS__SCI_NOTATION_USD = new Intl.NumberFormat('en-US', {
notation: 'scientific',
minimumSignificantDigits: 7,
maximumSignificantDigits: 7,
currency: 'USD',
style: 'currency',
})
type Format = Intl.NumberFormat | string
// each rule must contain either an `upperBound` or an `exact` value.
// upperBound => number will use that formatter as long as it is < upperBound
// exact => number will use that formatter if it is === exact
type FormatterRule =
| { upperBound?: undefined; exact: number; formatter: Format }
| { upperBound: number; exact?: undefined; formatter: Format }
// 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
// falls between 1 and 1e6, you'd use TWO_DECIMALS as the formatter.
const tokenNonTxFormatter: FormatterRule[] = [
{ exact: 0, formatter: '0' },
{ upperBound: 0.001, formatter: '<0.001' },
{ upperBound: 1, formatter: THREE_DECIMALS },
{ upperBound: 1e6, formatter: TWO_DECIMALS },
{ upperBound: 1e15, formatter: SHORTHAND_TWO_DECIMALS },
{ upperBound: Infinity, formatter: '>999T' },
]
const tokenTxFormatter: FormatterRule[] = [
{ exact: 0, formatter: '0' },
{ upperBound: 0.00001, formatter: '<0.00001' },
{ upperBound: 1, formatter: FIVE_DECIMALS_MAX_TWO_DECIMALS_MIN },
{ upperBound: 10000, formatter: SIX_SIG_FIGS_TWO_DECIMALS },
{ upperBound: Infinity, formatter: TWO_DECIMALS },
]
const swapTradeAmountFormatter: FormatterRule[] = [
{ exact: 0, formatter: '0' },
{ upperBound: 0.1, formatter: SIX_SIG_FIGS_NO_COMMAS },
{ upperBound: 1, formatter: FIVE_DECIMALS_MAX_TWO_DECIMALS_MIN_NO_COMMAS },
{ upperBound: Infinity, formatter: SIX_SIG_FIGS_TWO_DECIMALS_NO_COMMAS },
]
const swapPriceFormatter: FormatterRule[] = [
{ exact: 0, formatter: '0' },
{ upperBound: 0.00001, formatter: '<0.00001' },
...swapTradeAmountFormatter,
]
const fiatTokenDetailsFormatter: FormatterRule[] = [
{ exact: 0, formatter: '$0.00' },
{ upperBound: 0.00000001, formatter: '<$0.00000001' },
{ upperBound: 0.1, formatter: THREE_SIG_FIGS_USD },
{ upperBound: 1.05, formatter: THREE_DECIMALS_USD },
{ upperBound: 1e6, formatter: TWO_DECIMALS_USD },
{ upperBound: Infinity, formatter: SHORTHAND_USD_TWO_DECIMALS },
]
const fiatTokenPricesFormatter: FormatterRule[] = [
{ exact: 0, formatter: '$0.00' },
{ upperBound: 0.00000001, formatter: '<$0.00000001' },
{ upperBound: 1, formatter: THREE_SIG_FIGS_USD },
{ upperBound: 1e6, formatter: TWO_DECIMALS_USD },
{ upperBound: 1e16, formatter: SHORTHAND_USD_TWO_DECIMALS },
{ upperBound: Infinity, formatter: SEVEN_SIG_FIGS__SCI_NOTATION_USD },
]
const fiatTokenStatsFormatter: FormatterRule[] = [
// if token stat value is 0, we probably don't have the data for it, so show '-' as a placeholder
{ exact: 0, formatter: '-' },
{ upperBound: 0.01, formatter: '<$0.01' },
{ upperBound: 1000, formatter: TWO_DECIMALS_USD },
{ upperBound: Infinity, formatter: SHORTHAND_USD_ONE_DECIMAL },
]
const fiatGasPriceFormatter: FormatterRule[] = [
{ exact: 0, formatter: '$0.00' },
{ upperBound: 0.01, formatter: '<$0.01' },
{ upperBound: 1e6, formatter: TWO_DECIMALS_USD },
{ upperBound: Infinity, formatter: SHORTHAND_USD_TWO_DECIMALS },
]
const fiatTokenQuantityFormatter = [{ exact: 0, formatter: '$0.00' }, ...fiatGasPriceFormatter]
const portfolioBalanceFormatter: FormatterRule[] = [
{ exact: 0, formatter: '$0.00' },
{ upperBound: Infinity, formatter: TWO_DECIMALS_USD },
]
const ntfTokenFloorPriceFormatterTrailingZeros: FormatterRule[] = [
{ exact: 0, formatter: '0' },
{ upperBound: 0.001, formatter: '<0.001' },
{ upperBound: 1, formatter: THREE_DECIMALS },
{ upperBound: 1000, formatter: TWO_DECIMALS },
{ upperBound: 1e15, formatter: SHORTHAND_TWO_DECIMALS },
{ upperBound: Infinity, formatter: '>999T' },
]
const ntfTokenFloorPriceFormatter: FormatterRule[] = [
{ exact: 0, formatter: '0' },
{ upperBound: 0.001, formatter: '<0.001' },
{ upperBound: 1, formatter: THREE_DECIMALS_NO_TRAILING_ZEROS },
{ upperBound: 1000, formatter: TWO_DECIMALS_NO_TRAILING_ZEROS },
{ upperBound: 1e15, formatter: SHORTHAND_TWO_DECIMALS_NO_TRAILING_ZEROS },
{ upperBound: Infinity, formatter: '>999T' },
]
const ntfCollectionStatsFormatter: FormatterRule[] = [
{ upperBound: 1000, formatter: NO_DECIMALS },
{ upperBound: Infinity, formatter: SHORTHAND_ONE_DECIMAL },
]
export enum NumberType {
// used for token quantities in non-transaction contexts (e.g. portfolio balances)
TokenNonTx = 'token-non-tx',
// used for token quantities in transaction contexts (e.g. swap, send)
TokenTx = 'token-tx',
// this formatter is used for displaying swap price conversions
// below the input/output amounts
SwapPrice = 'swap-price',
// this formatter is only used for displaying the swap trade output amount
// in the text input boxes. Output amounts on review screen should use the above TokenTx formatter
SwapTradeAmount = 'swap-trade-amount',
// fiat prices in any component that belongs in the Token Details flow (except for token stats)
FiatTokenDetails = 'fiat-token-details',
// fiat prices everywhere except Token Details flow
FiatTokenPrice = 'fiat-token-price',
// fiat values for market cap, TVL, volume in the Token Details screen
FiatTokenStats = 'fiat-token-stats',
// fiat price of token balances
FiatTokenQuantity = 'fiat-token-quantity',
// fiat gas prices
FiatGasPrice = 'fiat-gas-price',
// portfolio balance
PortfolioBalance = 'portfolio-balance',
// nft floor price denominated in a token (e.g, ETH)
NFTTokenFloorPrice = 'nft-token-floor-price',
// nft collection stats like number of items, holder, and sales
NFTCollectionStats = 'nft-collection-stats',
// nft floor price with trailing zeros
NFTTokenFloorPriceTrailingZeros = 'nft-token-floor-price-trailing-zeros',
}
const TYPE_TO_FORMATTER_RULES = {
[NumberType.TokenNonTx]: tokenNonTxFormatter,
[NumberType.TokenTx]: tokenTxFormatter,
[NumberType.SwapPrice]: swapPriceFormatter,
[NumberType.SwapTradeAmount]: swapTradeAmountFormatter,
[NumberType.FiatTokenQuantity]: fiatTokenQuantityFormatter,
[NumberType.FiatTokenDetails]: fiatTokenDetailsFormatter,
[NumberType.FiatTokenPrice]: fiatTokenPricesFormatter,
[NumberType.FiatTokenStats]: fiatTokenStatsFormatter,
[NumberType.FiatGasPrice]: fiatGasPriceFormatter,
[NumberType.PortfolioBalance]: portfolioBalanceFormatter,
[NumberType.NFTTokenFloorPrice]: ntfTokenFloorPriceFormatter,
[NumberType.NFTTokenFloorPriceTrailingZeros]: ntfTokenFloorPriceFormatterTrailingZeros,
[NumberType.NFTCollectionStats]: ntfCollectionStatsFormatter,
}
function getFormatterRule(input: number, type: NumberType): Format {
const rules = TYPE_TO_FORMATTER_RULES[type]
for (const rule of rules) {
if (
(rule.exact !== undefined && input === rule.exact) ||
(rule.upperBound !== undefined && input < rule.upperBound)
) {
return rule.formatter
}
}
throw new Error(`formatter for type ${type} not configured correctly`)
}
export function formatNumber(
input: Nullish<number>,
type: NumberType = NumberType.TokenNonTx,
placeholder = '-'
): string {
if (input === null || input === undefined) {
return placeholder
}
const formatter = getFormatterRule(input, type)
if (typeof formatter === 'string') return formatter
return formatter.format(input)
}
export function formatCurrencyAmount(
amount: Nullish<CurrencyAmount<Currency>>,
type: NumberType = NumberType.TokenNonTx,
placeholder?: string
): string {
return formatNumber(amount ? parseFloat(amount.toSignificant()) : undefined, type, placeholder)
}
export function formatPriceImpact(priceImpact: Percent | undefined): string {
if (!priceImpact) return '-'
return `${priceImpact.multiply(-1).toFixed(3)}%`
}
export function formatSlippage(slippage: Percent | undefined) {
if (!slippage) return '-'
return `${slippage.toFixed(3)}%`
}
export function formatPrice(
price: Nullish<Price<Currency, Currency>>,
type: NumberType = NumberType.FiatTokenPrice
): string {
if (price === null || price === undefined) {
return '-'
}
return formatNumber(parseFloat(price.toSignificant()), type)
}
export function formatNumberOrString(price: Nullish<number | string>, type: NumberType): string {
if (price === null || price === undefined) return '-'
if (typeof price === 'string') return formatNumber(parseFloat(price), type)
return formatNumber(price, type)
}
export function formatUSDPrice(price: Nullish<number | string>, type: NumberType = NumberType.FiatTokenPrice): string {
return formatNumberOrString(price, type)
}
/** Formats USD and non-USD prices */
export function formatFiatPrice(price: Nullish<number>, currency = 'USD'): string {
if (price === null || price === undefined) return '-'
return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(price)
}
// Convert [CurrencyAmount] to number with necessary precision for price formatting.
export const currencyAmountToPreciseFloat = (currencyAmount: CurrencyAmount<Currency> | undefined) => {
if (!currencyAmount) return undefined

@ -1,5 +1,5 @@
import { formatPrice, NumberType } from '@uniswap/conedison/format'
import { Price, Token } from '@uniswap/sdk-core'
import { formatPrice, NumberType } from 'utils/formatNumbers'
import { Bound } from '../state/mint/v3/actions'