chore: only exposing useFormatter (#7308)

This commit is contained in:
Jack Short 2023-09-18 20:21:21 -04:00 committed by GitHub
parent cf09e80934
commit 91c2013522
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 679 additions and 622 deletions

@ -2,6 +2,7 @@ import { TransactionStatus, useActivityQuery } from 'graphql/data/__generated__/
import { useEffect, useMemo } from 'react' import { useEffect, useMemo } from 'react'
import { usePendingOrders } from 'state/signatures/hooks' import { usePendingOrders } from 'state/signatures/hooks'
import { usePendingTransactions, useTransactionCanceller } from 'state/transactions/hooks' import { usePendingTransactions, useTransactionCanceller } from 'state/transactions/hooks'
import { useFormatter } from 'utils/formatNumbers'
import { useLocalActivities } from './parseLocal' import { useLocalActivities } from './parseLocal'
import { parseRemoteActivities } from './parseRemote' import { parseRemoteActivities } from './parseRemote'
@ -55,6 +56,7 @@ function combineActivities(localMap: ActivityMap = {}, remoteMap: ActivityMap =
} }
export function useAllActivities(account: string) { export function useAllActivities(account: string) {
const { formatNumberOrString } = useFormatter()
const { data, loading, refetch } = useActivityQuery({ const { data, loading, refetch } = useActivityQuery({
variables: { account }, variables: { account },
errorPolicy: 'all', errorPolicy: 'all',
@ -62,7 +64,10 @@ export function useAllActivities(account: string) {
}) })
const localMap = useLocalActivities(account) const localMap = useLocalActivities(account)
const remoteMap = useMemo(() => parseRemoteActivities(data?.portfolios?.[0].assetActivities), [data?.portfolios]) const remoteMap = useMemo(
() => parseRemoteActivities(formatNumberOrString, data?.portfolios?.[0].assetActivities),
[data?.portfolios, formatNumberOrString]
)
const updateCancelledTx = useTransactionCanceller() const updateCancelledTx = useTransactionCanceller()
/* Updates locally stored pendings tx's when remote data contains a conflicting cancellation tx */ /* Updates locally stored pendings tx's when remote data contains a conflicting cancellation tx */

@ -11,6 +11,7 @@ import {
TransactionType as MockTxType, TransactionType as MockTxType,
} from 'state/transactions/types' } from 'state/transactions/types'
import { renderHook } from 'test-utils/render' import { renderHook } from 'test-utils/render'
import { useFormatter } from 'utils/formatNumbers'
import { UniswapXOrderStatus } from '../../../../lib/hooks/orders/types' import { UniswapXOrderStatus } from '../../../../lib/hooks/orders/types'
import { SignatureDetails, SignatureType } from '../../../../state/signatures/types' import { SignatureDetails, SignatureType } from '../../../../state/signatures/types'
@ -237,6 +238,8 @@ jest.mock('../../../../state/transactions/hooks', () => {
describe('parseLocalActivity', () => { describe('parseLocalActivity', () => {
it('returns swap activity fields with known tokens, exact input', () => { it('returns swap activity fields with known tokens, exact input', () => {
const { formatNumber } = renderHook(() => useFormatter()).result.current
const details = { const details = {
info: mockSwapInfo( info: mockSwapInfo(
MockTradeType.EXACT_INPUT, MockTradeType.EXACT_INPUT,
@ -251,7 +254,7 @@ describe('parseLocalActivity', () => {
}, },
} as TransactionDetails } as TransactionDetails
const chainId = ChainId.MAINNET const chainId = ChainId.MAINNET
expect(transactionToActivity(details, chainId, mockTokenAddressMap)).toEqual({ expect(transactionToActivity(details, chainId, mockTokenAddressMap, formatNumber)).toEqual({
chainId: 1, chainId: 1,
currencies: [MockUSDC_MAINNET, MockDAI], currencies: [MockUSDC_MAINNET, MockDAI],
descriptor: '1.00 USDC for 1.00 DAI', descriptor: '1.00 USDC for 1.00 DAI',
@ -264,6 +267,8 @@ describe('parseLocalActivity', () => {
}) })
it('returns swap activity fields with known tokens, exact output', () => { it('returns swap activity fields with known tokens, exact output', () => {
const { formatNumber } = renderHook(() => useFormatter()).result.current
const details = { const details = {
info: mockSwapInfo( info: mockSwapInfo(
MockTradeType.EXACT_OUTPUT, MockTradeType.EXACT_OUTPUT,
@ -278,7 +283,7 @@ describe('parseLocalActivity', () => {
}, },
} as TransactionDetails } as TransactionDetails
const chainId = ChainId.MAINNET const chainId = ChainId.MAINNET
expect(transactionToActivity(details, chainId, mockTokenAddressMap)).toMatchObject({ expect(transactionToActivity(details, chainId, mockTokenAddressMap, formatNumber)).toMatchObject({
chainId: 1, chainId: 1,
currencies: [MockUSDC_MAINNET, MockDAI], currencies: [MockUSDC_MAINNET, MockDAI],
descriptor: '1.00 USDC for 1.00 DAI', descriptor: '1.00 USDC for 1.00 DAI',
@ -288,6 +293,8 @@ describe('parseLocalActivity', () => {
}) })
it('returns swap activity fields with unknown tokens', () => { it('returns swap activity fields with unknown tokens', () => {
const { formatNumber } = renderHook(() => useFormatter()).result.current
const details = { const details = {
info: mockSwapInfo( info: mockSwapInfo(
MockTradeType.EXACT_INPUT, MockTradeType.EXACT_INPUT,
@ -303,7 +310,7 @@ describe('parseLocalActivity', () => {
} as TransactionDetails } as TransactionDetails
const chainId = ChainId.MAINNET const chainId = ChainId.MAINNET
const tokens = {} as ChainTokenMap const tokens = {} as ChainTokenMap
expect(transactionToActivity(details, chainId, tokens)).toMatchObject({ expect(transactionToActivity(details, chainId, tokens, formatNumber)).toMatchObject({
chainId: 1, chainId: 1,
currencies: [undefined, undefined], currencies: [undefined, undefined],
descriptor: 'Unknown for Unknown', descriptor: 'Unknown for Unknown',
@ -496,13 +503,16 @@ describe('parseLocalActivity', () => {
}) })
it('Signature to activity - returns undefined if is on chain order', () => { it('Signature to activity - returns undefined if is on chain order', () => {
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect( expect(
signatureToActivity( signatureToActivity(
{ {
type: SignatureType.SIGN_UNISWAPX_ORDER, type: SignatureType.SIGN_UNISWAPX_ORDER,
status: UniswapXOrderStatus.FILLED, status: UniswapXOrderStatus.FILLED,
} as SignatureDetails, } as SignatureDetails,
{} {},
formatNumber
) )
).toBeUndefined() ).toBeUndefined()
@ -512,7 +522,8 @@ describe('parseLocalActivity', () => {
type: SignatureType.SIGN_UNISWAPX_ORDER, type: SignatureType.SIGN_UNISWAPX_ORDER,
status: UniswapXOrderStatus.CANCELLED, status: UniswapXOrderStatus.CANCELLED,
} as SignatureDetails, } as SignatureDetails,
{} {},
formatNumber
) )
).toBeUndefined() ).toBeUndefined()
}) })

@ -2,7 +2,6 @@ import { BigNumber } from '@ethersproject/bignumber'
import { t } from '@lingui/macro' import { t } from '@lingui/macro'
import { ChainId, Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { ChainId, Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import UniswapXBolt from 'assets/svg/bolt.svg' import UniswapXBolt from 'assets/svg/bolt.svg'
import { SupportedLocale } from 'constants/locales'
import { nativeOnChain } from 'constants/tokens' import { nativeOnChain } from 'constants/tokens'
import { TransactionStatus } from 'graphql/data/__generated__/types-and-hooks' import { TransactionStatus } from 'graphql/data/__generated__/types-and-hooks'
import { ChainTokenMap, useAllTokensMultichain } from 'hooks/Tokens' import { ChainTokenMap, useAllTokensMultichain } from 'hooks/Tokens'
@ -24,11 +23,13 @@ import {
TransactionType, TransactionType,
WrapTransactionInfo, WrapTransactionInfo,
} from 'state/transactions/types' } from 'state/transactions/types'
import { formatCurrencyAmount, useFormatterLocales } from 'utils/formatNumbers' import { NumberType, useFormatter } from 'utils/formatNumbers'
import { CancelledTransactionTitleTable, getActivityTitle, OrderTextTable } from '../constants' import { CancelledTransactionTitleTable, getActivityTitle, OrderTextTable } from '../constants'
import { Activity, ActivityMap } from './types' import { Activity, ActivityMap } from './types'
type FormatNumberFunctionType = ReturnType<typeof useFormatter>['formatNumber']
function getCurrency(currencyId: string, chainId: ChainId, tokens: ChainTokenMap): Currency | undefined { function getCurrency(currencyId: string, chainId: ChainId, tokens: ChainTokenMap): Currency | undefined {
return currencyId === 'ETH' ? nativeOnChain(chainId) : tokens[chainId]?.[currencyId] return currencyId === 'ETH' ? nativeOnChain(chainId) : tokens[chainId]?.[currencyId]
} }
@ -38,15 +39,21 @@ function buildCurrencyDescriptor(
amtA: string, amtA: string,
currencyB: Currency | undefined, currencyB: Currency | undefined,
amtB: string, amtB: string,
delimiter = t`for`, formatNumber: FormatNumberFunctionType,
locale?: SupportedLocale delimiter = t`for`
) { ) {
const formattedA = currencyA const formattedA = currencyA
? formatCurrencyAmount({ amount: CurrencyAmount.fromRawAmount(currencyA, amtA), locale }) ? formatNumber({
input: parseFloat(CurrencyAmount.fromRawAmount(currencyA, amtA).toSignificant()),
type: NumberType.TokenNonTx,
})
: t`Unknown` : t`Unknown`
const symbolA = currencyA?.symbol ?? '' const symbolA = currencyA?.symbol ?? ''
const formattedB = currencyB const formattedB = currencyB
? formatCurrencyAmount({ amount: CurrencyAmount.fromRawAmount(currencyB, amtB), locale }) ? formatNumber({
input: parseFloat(CurrencyAmount.fromRawAmount(currencyB, amtB).toSignificant()),
type: NumberType.TokenNonTx,
})
: t`Unknown` : t`Unknown`
const symbolB = currencyB?.symbol ?? '' const symbolB = currencyB?.symbol ?? ''
return [formattedA, symbolA, delimiter, formattedB, symbolB].filter(Boolean).join(' ') return [formattedA, symbolA, delimiter, formattedB, symbolB].filter(Boolean).join(' ')
@ -56,7 +63,7 @@ function parseSwap(
swap: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo, swap: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo,
chainId: ChainId, chainId: ChainId,
tokens: ChainTokenMap, tokens: ChainTokenMap,
locale?: SupportedLocale formatNumber: FormatNumberFunctionType
): Partial<Activity> { ): Partial<Activity> {
const tokenIn = getCurrency(swap.inputCurrencyId, chainId, tokens) const tokenIn = getCurrency(swap.inputCurrencyId, chainId, tokens)
const tokenOut = getCurrency(swap.outputCurrencyId, chainId, tokens) const tokenOut = getCurrency(swap.outputCurrencyId, chainId, tokens)
@ -66,18 +73,29 @@ function parseSwap(
: [swap.expectedInputCurrencyAmountRaw, swap.outputCurrencyAmountRaw] : [swap.expectedInputCurrencyAmountRaw, swap.outputCurrencyAmountRaw]
return { return {
descriptor: buildCurrencyDescriptor(tokenIn, inputRaw, tokenOut, outputRaw, undefined, locale), descriptor: buildCurrencyDescriptor(tokenIn, inputRaw, tokenOut, outputRaw, formatNumber, undefined),
currencies: [tokenIn, tokenOut], currencies: [tokenIn, tokenOut],
prefixIconSrc: swap.isUniswapXOrder ? UniswapXBolt : undefined, prefixIconSrc: swap.isUniswapXOrder ? UniswapXBolt : undefined,
} }
} }
function parseWrap(wrap: WrapTransactionInfo, chainId: ChainId, status: TransactionStatus): Partial<Activity> { function parseWrap(
wrap: WrapTransactionInfo,
chainId: ChainId,
status: TransactionStatus,
formatNumber: FormatNumberFunctionType
): Partial<Activity> {
const native = nativeOnChain(chainId) const native = nativeOnChain(chainId)
const wrapped = native.wrapped const wrapped = native.wrapped
const [input, output] = wrap.unwrapped ? [wrapped, native] : [native, wrapped] const [input, output] = wrap.unwrapped ? [wrapped, native] : [native, wrapped]
const descriptor = buildCurrencyDescriptor(input, wrap.currencyAmountRaw, output, wrap.currencyAmountRaw) const descriptor = buildCurrencyDescriptor(
input,
wrap.currencyAmountRaw,
output,
wrap.currencyAmountRaw,
formatNumber
)
const title = getActivityTitle(TransactionType.WRAP, status, wrap.unwrapped) const title = getActivityTitle(TransactionType.WRAP, status, wrap.unwrapped)
const currencies = wrap.unwrapped ? [wrapped, native] : [native, wrapped] const currencies = wrap.unwrapped ? [wrapped, native] : [native, wrapped]
@ -107,11 +125,16 @@ type GenericLPInfo = Omit<
AddLiquidityV3PoolTransactionInfo | RemoveLiquidityV3TransactionInfo | AddLiquidityV2PoolTransactionInfo, AddLiquidityV3PoolTransactionInfo | RemoveLiquidityV3TransactionInfo | AddLiquidityV2PoolTransactionInfo,
'type' 'type'
> >
function parseLP(lp: GenericLPInfo, chainId: ChainId, tokens: ChainTokenMap): Partial<Activity> { function parseLP(
lp: GenericLPInfo,
chainId: ChainId,
tokens: ChainTokenMap,
formatNumber: FormatNumberFunctionType
): Partial<Activity> {
const baseCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens) const baseCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens)
const quoteCurrency = getCurrency(lp.quoteCurrencyId, chainId, tokens) const quoteCurrency = getCurrency(lp.quoteCurrencyId, chainId, tokens)
const [baseRaw, quoteRaw] = [lp.expectedAmountBaseRaw, lp.expectedAmountQuoteRaw] const [baseRaw, quoteRaw] = [lp.expectedAmountBaseRaw, lp.expectedAmountQuoteRaw]
const descriptor = buildCurrencyDescriptor(baseCurrency, baseRaw, quoteCurrency, quoteRaw, t`and`) const descriptor = buildCurrencyDescriptor(baseCurrency, baseRaw, quoteCurrency, quoteRaw, formatNumber, t`and`)
return { descriptor, currencies: [baseCurrency, quoteCurrency] } return { descriptor, currencies: [baseCurrency, quoteCurrency] }
} }
@ -119,7 +142,8 @@ function parseLP(lp: GenericLPInfo, chainId: ChainId, tokens: ChainTokenMap): Pa
function parseCollectFees( function parseCollectFees(
collect: CollectFeesTransactionInfo, collect: CollectFeesTransactionInfo,
chainId: ChainId, chainId: ChainId,
tokens: ChainTokenMap tokens: ChainTokenMap,
formatNumber: FormatNumberFunctionType
): Partial<Activity> { ): Partial<Activity> {
// Adapts CollectFeesTransactionInfo to generic LP type // Adapts CollectFeesTransactionInfo to generic LP type
const { const {
@ -128,7 +152,12 @@ function parseCollectFees(
expectedCurrencyOwed0: expectedAmountBaseRaw, expectedCurrencyOwed0: expectedAmountBaseRaw,
expectedCurrencyOwed1: expectedAmountQuoteRaw, expectedCurrencyOwed1: expectedAmountQuoteRaw,
} = collect } = collect
return parseLP({ baseCurrencyId, quoteCurrencyId, expectedAmountBaseRaw, expectedAmountQuoteRaw }, chainId, tokens) return parseLP(
{ baseCurrencyId, quoteCurrencyId, expectedAmountBaseRaw, expectedAmountQuoteRaw },
chainId,
tokens,
formatNumber
)
} }
function parseMigrateCreateV3( function parseMigrateCreateV3(
@ -157,7 +186,7 @@ export function transactionToActivity(
details: TransactionDetails, details: TransactionDetails,
chainId: ChainId, chainId: ChainId,
tokens: ChainTokenMap, tokens: ChainTokenMap,
locale?: SupportedLocale formatNumber: FormatNumberFunctionType
): Activity | undefined { ): Activity | undefined {
try { try {
const status = getTransactionStatus(details) const status = getTransactionStatus(details)
@ -176,19 +205,19 @@ export function transactionToActivity(
let additionalFields: Partial<Activity> = {} let additionalFields: Partial<Activity> = {}
const info = details.info const info = details.info
if (info.type === TransactionType.SWAP) { if (info.type === TransactionType.SWAP) {
additionalFields = parseSwap(info, chainId, tokens, locale) additionalFields = parseSwap(info, chainId, tokens, formatNumber)
} else if (info.type === TransactionType.APPROVAL) { } else if (info.type === TransactionType.APPROVAL) {
additionalFields = parseApproval(info, chainId, tokens, status) additionalFields = parseApproval(info, chainId, tokens, status)
} else if (info.type === TransactionType.WRAP) { } else if (info.type === TransactionType.WRAP) {
additionalFields = parseWrap(info, chainId, status) additionalFields = parseWrap(info, chainId, status, formatNumber)
} else if ( } else if (
info.type === TransactionType.ADD_LIQUIDITY_V3_POOL || info.type === TransactionType.ADD_LIQUIDITY_V3_POOL ||
info.type === TransactionType.REMOVE_LIQUIDITY_V3 || info.type === TransactionType.REMOVE_LIQUIDITY_V3 ||
info.type === TransactionType.ADD_LIQUIDITY_V2_POOL info.type === TransactionType.ADD_LIQUIDITY_V2_POOL
) { ) {
additionalFields = parseLP(info, chainId, tokens) additionalFields = parseLP(info, chainId, tokens, formatNumber)
} else if (info.type === TransactionType.COLLECT_FEES) { } else if (info.type === TransactionType.COLLECT_FEES) {
additionalFields = parseCollectFees(info, chainId, tokens) additionalFields = parseCollectFees(info, chainId, tokens, formatNumber)
} else if (info.type === TransactionType.MIGRATE_LIQUIDITY_V3 || info.type === TransactionType.CREATE_V3_POOL) { } else if (info.type === TransactionType.MIGRATE_LIQUIDITY_V3 || info.type === TransactionType.CREATE_V3_POOL) {
additionalFields = parseMigrateCreateV3(info, chainId, tokens) additionalFields = parseMigrateCreateV3(info, chainId, tokens)
} }
@ -210,7 +239,7 @@ export function transactionToActivity(
export function signatureToActivity( export function signatureToActivity(
signature: SignatureDetails, signature: SignatureDetails,
tokens: ChainTokenMap, tokens: ChainTokenMap,
locale?: SupportedLocale formatNumber: FormatNumberFunctionType
): Activity | undefined { ): Activity | undefined {
switch (signature.type) { switch (signature.type) {
case SignatureType.SIGN_UNISWAPX_ORDER: { case SignatureType.SIGN_UNISWAPX_ORDER: {
@ -229,7 +258,7 @@ export function signatureToActivity(
from: signature.offerer, from: signature.offerer,
statusMessage, statusMessage,
prefixIconSrc: UniswapXBolt, prefixIconSrc: UniswapXBolt,
...parseSwap(signature.swapInfo, signature.chainId, tokens, locale), ...parseSwap(signature.swapInfo, signature.chainId, tokens, formatNumber),
} }
} }
default: default:
@ -241,24 +270,24 @@ export function useLocalActivities(account: string): ActivityMap {
const allTransactions = useMultichainTransactions() const allTransactions = useMultichainTransactions()
const allSignatures = useAllSignatures() const allSignatures = useAllSignatures()
const tokens = useAllTokensMultichain() const tokens = useAllTokensMultichain()
const { formatterLocale } = useFormatterLocales() const { formatNumber } = useFormatter()
return useMemo(() => { return useMemo(() => {
const activityMap: ActivityMap = {} const activityMap: ActivityMap = {}
for (const [transaction, chainId] of allTransactions) { for (const [transaction, chainId] of allTransactions) {
if (transaction.from !== account) continue if (transaction.from !== account) continue
const activity = transactionToActivity(transaction, chainId, tokens, formatterLocale) const activity = transactionToActivity(transaction, chainId, tokens, formatNumber)
if (activity) activityMap[transaction.hash] = activity if (activity) activityMap[transaction.hash] = activity
} }
for (const signature of Object.values(allSignatures)) { for (const signature of Object.values(allSignatures)) {
if (signature.offerer !== account) continue if (signature.offerer !== account) continue
const activity = signatureToActivity(signature, tokens, formatterLocale) const activity = signatureToActivity(signature, tokens, formatNumber)
if (activity) activityMap[signature.id] = activity if (activity) activityMap[signature.id] = activity
} }
return activityMap return activityMap
}, [account, allSignatures, allTransactions, formatterLocale, tokens]) }, [account, allSignatures, allTransactions, formatNumber, tokens])
} }

@ -21,7 +21,7 @@ import { gqlToCurrency, logSentryErrorForUnsupportedChain, supportedChainIdFromG
import ms from 'ms' import ms from 'ms'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { isAddress } from 'utils' import { isAddress } from 'utils'
import { formatFiatPrice, formatNumberOrString, NumberType } from 'utils/formatNumbers' import { NumberType, useFormatter } from 'utils/formatNumbers'
import { MOONPAY_SENDER_ADDRESSES, OrderStatusTable, OrderTextTable } from '../constants' import { MOONPAY_SENDER_ADDRESSES, OrderStatusTable, OrderTextTable } from '../constants'
import { Activity } from './types' import { Activity } from './types'
@ -34,6 +34,8 @@ type TransactionChanges = {
NftApproveForAll: NftApproveForAllPartsFragment[] NftApproveForAll: NftApproveForAllPartsFragment[]
} }
type FormatNumberOrStringFunctionType = ReturnType<typeof useFormatter>['formatNumberOrString']
// TODO: Move common contract metadata to a backend service // TODO: Move common contract metadata to a backend service
const UNI_IMG = const UNI_IMG =
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png' 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png'
@ -140,13 +142,13 @@ function getSwapDescriptor({
* @param transactedValue Transacted value amount from TokenTransfer API response * @param transactedValue Transacted value amount from TokenTransfer API response
* @returns parsed & formatted USD value as a string if currency is of type USD * @returns parsed & formatted USD value as a string if currency is of type USD
*/ */
function formatTransactedValue(transactedValue: TokenTransferPartsFragment['transactedValue']): string { function getTransactedValue(transactedValue: TokenTransferPartsFragment['transactedValue']): number | undefined {
if (!transactedValue) return '-' if (!transactedValue) return undefined
const price = transactedValue?.currency === GQLCurrency.Usd ? transactedValue.value ?? undefined : undefined const price = transactedValue?.currency === GQLCurrency.Usd ? transactedValue.value ?? undefined : undefined
return formatFiatPrice(price) return price
} }
function parseSwap(changes: TransactionChanges) { function parseSwap(changes: TransactionChanges, formatNumberOrString: FormatNumberOrStringFunctionType) {
if (changes.NftTransfer.length > 0 && changes.TokenTransfer.length === 1) { if (changes.NftTransfer.length > 0 && changes.TokenTransfer.length === 1) {
const collectionCounts = getCollectionCounts(changes.NftTransfer) const collectionCounts = getCollectionCounts(changes.NftTransfer)
@ -168,8 +170,8 @@ function parseSwap(changes: TransactionChanges) {
if (sent && received) { if (sent && received) {
const adjustedInput = parseFloat(sent.quantity) - parseFloat(refund?.quantity ?? '0') const adjustedInput = parseFloat(sent.quantity) - parseFloat(refund?.quantity ?? '0')
const inputAmount = formatNumberOrString(adjustedInput, NumberType.TokenNonTx) const inputAmount = formatNumberOrString({ input: adjustedInput, type: NumberType.TokenNonTx })
const outputAmount = formatNumberOrString(received.quantity, NumberType.TokenNonTx) const outputAmount = formatNumberOrString({ input: received.quantity, type: NumberType.TokenNonTx })
return { return {
title: getSwapTitle(sent, received), title: getSwapTitle(sent, received),
descriptor: getSwapDescriptor({ tokenIn: sent.asset, inputAmount, tokenOut: received.asset, outputAmount }), descriptor: getSwapDescriptor({ tokenIn: sent.asset, inputAmount, tokenOut: received.asset, outputAmount }),
@ -180,8 +182,8 @@ function parseSwap(changes: TransactionChanges) {
return { title: t`Unknown Swap` } return { title: t`Unknown Swap` }
} }
function parseSwapOrder(changes: TransactionChanges) { function parseSwapOrder(changes: TransactionChanges, formatNumberOrString: FormatNumberOrStringFunctionType) {
return { ...parseSwap(changes), prefixIconSrc: UniswapXBolt } return { ...parseSwap(changes, formatNumberOrString), prefixIconSrc: UniswapXBolt }
} }
function parseApprove(changes: TransactionChanges) { function parseApprove(changes: TransactionChanges) {
@ -194,12 +196,12 @@ function parseApprove(changes: TransactionChanges) {
return { title: t`Unknown Approval` } return { title: t`Unknown Approval` }
} }
function parseLPTransfers(changes: TransactionChanges) { function parseLPTransfers(changes: TransactionChanges, formatNumberOrString: FormatNumberOrStringFunctionType) {
const poolTokenA = changes.TokenTransfer[0] const poolTokenA = changes.TokenTransfer[0]
const poolTokenB = changes.TokenTransfer[1] const poolTokenB = changes.TokenTransfer[1]
const tokenAQuanitity = formatNumberOrString(poolTokenA.quantity, NumberType.TokenNonTx) const tokenAQuanitity = formatNumberOrString({ input: poolTokenA.quantity, type: NumberType.TokenNonTx })
const tokenBQuantity = formatNumberOrString(poolTokenB.quantity, NumberType.TokenNonTx) const tokenBQuantity = formatNumberOrString({ input: poolTokenB.quantity, type: NumberType.TokenNonTx })
return { return {
descriptor: `${tokenAQuanitity} ${poolTokenA.asset.symbol} and ${tokenBQuantity} ${poolTokenB.asset.symbol}`, descriptor: `${tokenAQuanitity} ${poolTokenA.asset.symbol} and ${tokenBQuantity} ${poolTokenB.asset.symbol}`,
@ -211,11 +213,15 @@ function parseLPTransfers(changes: TransactionChanges) {
type TransactionActivity = AssetActivityPartsFragment & { details: TransactionDetailsPartsFragment } type TransactionActivity = AssetActivityPartsFragment & { details: TransactionDetailsPartsFragment }
type OrderActivity = AssetActivityPartsFragment & { details: SwapOrderDetailsPartsFragment } type OrderActivity = AssetActivityPartsFragment & { details: SwapOrderDetailsPartsFragment }
function parseSendReceive(changes: TransactionChanges, assetActivity: TransactionActivity) { function parseSendReceive(
changes: TransactionChanges,
formatNumberOrString: FormatNumberOrStringFunctionType,
assetActivity: TransactionActivity
) {
// TODO(cartcrom): remove edge cases after backend implements // TODO(cartcrom): remove edge cases after backend implements
// Edge case: Receiving two token transfers in interaction w/ V3 manager === removing liquidity. These edge cases should potentially be moved to backend // Edge case: Receiving two token transfers in interaction w/ V3 manager === removing liquidity. These edge cases should potentially be moved to backend
if (changes.TokenTransfer.length === 2 && callsPositionManagerContract(assetActivity)) { if (changes.TokenTransfer.length === 2 && callsPositionManagerContract(assetActivity)) {
return { title: t`Removed Liquidity`, ...parseLPTransfers(changes) } return { title: t`Removed Liquidity`, ...parseLPTransfers(changes, formatNumberOrString) }
} }
let transfer: NftTransferPartsFragment | TokenTransferPartsFragment | undefined let transfer: NftTransferPartsFragment | TokenTransferPartsFragment | undefined
@ -230,7 +236,7 @@ function parseSendReceive(changes: TransactionChanges, assetActivity: Transactio
} else if (changes.TokenTransfer.length === 1) { } else if (changes.TokenTransfer.length === 1) {
transfer = changes.TokenTransfer[0] transfer = changes.TokenTransfer[0]
assetName = transfer.asset.symbol assetName = transfer.asset.symbol
amount = formatNumberOrString(transfer.quantity, NumberType.TokenNonTx) amount = formatNumberOrString({ input: transfer.quantity, type: NumberType.TokenNonTx })
currencies = [gqlToCurrency(transfer.asset)] currencies = [gqlToCurrency(transfer.asset)]
} }
@ -241,7 +247,10 @@ function parseSendReceive(changes: TransactionChanges, assetActivity: Transactio
return isMoonpayPurchase && transfer.__typename === 'TokenTransfer' return isMoonpayPurchase && transfer.__typename === 'TokenTransfer'
? { ? {
title: t`Purchased`, title: t`Purchased`,
descriptor: `${amount} ${assetName} ${t`for`} ${formatTransactedValue(transfer.transactedValue)}`, descriptor: `${amount} ${assetName} ${t`for`} ${formatNumberOrString({
input: getTransactedValue(transfer.transactedValue),
type: NumberType.FiatTokenPrice,
})}`,
logos: [moonpayLogoSrc], logos: [moonpayLogoSrc],
currencies, currencies,
} }
@ -263,25 +272,37 @@ function parseSendReceive(changes: TransactionChanges, assetActivity: Transactio
return { title: t`Unknown Send` } return { title: t`Unknown Send` }
} }
function parseMint(changes: TransactionChanges, assetActivity: TransactionActivity) { function parseMint(
changes: TransactionChanges,
formatNumberOrString: FormatNumberOrStringFunctionType,
assetActivity: TransactionActivity
) {
const collectionMap = getCollectionCounts(changes.NftTransfer) const collectionMap = getCollectionCounts(changes.NftTransfer)
if (Object.keys(collectionMap).length === 1) { if (Object.keys(collectionMap).length === 1) {
const collectionName = Object.keys(collectionMap)[0] const collectionName = Object.keys(collectionMap)[0]
// Edge case: Minting a v3 positon represents adding liquidity // Edge case: Minting a v3 positon represents adding liquidity
if (changes.TokenTransfer.length === 2 && callsPositionManagerContract(assetActivity)) { if (changes.TokenTransfer.length === 2 && callsPositionManagerContract(assetActivity)) {
return { title: t`Added Liquidity`, ...parseLPTransfers(changes) } return { title: t`Added Liquidity`, ...parseLPTransfers(changes, formatNumberOrString) }
} }
return { title: t`Minted`, descriptor: `${collectionMap[collectionName]} ${collectionName}` } return { title: t`Minted`, descriptor: `${collectionMap[collectionName]} ${collectionName}` }
} }
return { title: t`Unknown Mint` } return { title: t`Unknown Mint` }
} }
function parseUnknown(_changes: TransactionChanges, assetActivity: TransactionActivity) { function parseUnknown(
_changes: TransactionChanges,
_formatNumberOrString: FormatNumberOrStringFunctionType,
assetActivity: TransactionActivity
) {
return { title: t`Contract Interaction`, ...COMMON_CONTRACTS[assetActivity.details.to.toLowerCase()] } return { title: t`Contract Interaction`, ...COMMON_CONTRACTS[assetActivity.details.to.toLowerCase()] }
} }
type ActivityTypeParser = (changes: TransactionChanges, assetActivity: TransactionActivity) => Partial<Activity> type ActivityTypeParser = (
changes: TransactionChanges,
formatNumberOrString: FormatNumberOrStringFunctionType,
assetActivity: TransactionActivity
) => Partial<Activity>
const ActivityParserByType: { [key: string]: ActivityTypeParser | undefined } = { const ActivityParserByType: { [key: string]: ActivityTypeParser | undefined } = {
[ActivityType.Swap]: parseSwap, [ActivityType.Swap]: parseSwap,
[ActivityType.SwapOrder]: parseSwapOrder, [ActivityType.SwapOrder]: parseSwapOrder,
@ -345,7 +366,10 @@ function parseUniswapXOrder({ details, chain, timestamp }: OrderActivity): Activ
} }
} }
function parseRemoteActivity(assetActivity: AssetActivityPartsFragment): Activity | undefined { function parseRemoteActivity(
assetActivity: AssetActivityPartsFragment,
formatNumberOrString: FormatNumberOrStringFunctionType
): Activity | undefined {
try { try {
if (assetActivity.details.__typename === 'SwapOrderDetails') { if (assetActivity.details.__typename === 'SwapOrderDetails') {
return parseUniswapXOrder(assetActivity as OrderActivity) return parseUniswapXOrder(assetActivity as OrderActivity)
@ -385,6 +409,7 @@ function parseRemoteActivity(assetActivity: AssetActivityPartsFragment): Activit
const parsedFields = ActivityParserByType[assetActivity.details.type]?.( const parsedFields = ActivityParserByType[assetActivity.details.type]?.(
changes, changes,
formatNumberOrString,
assetActivity as TransactionActivity assetActivity as TransactionActivity
) )
return { ...defaultFields, ...parsedFields } return { ...defaultFields, ...parsedFields }
@ -394,9 +419,12 @@ function parseRemoteActivity(assetActivity: AssetActivityPartsFragment): Activit
} }
} }
export function parseRemoteActivities(assetActivities?: readonly AssetActivityPartsFragment[]) { export function parseRemoteActivities(
formatNumberOrString: FormatNumberOrStringFunctionType,
assetActivities?: readonly AssetActivityPartsFragment[]
) {
return assetActivities?.reduce((acc: { [hash: string]: Activity }, assetActivity) => { return assetActivities?.reduce((acc: { [hash: string]: Activity }, assetActivity) => {
const activity = parseRemoteActivity(assetActivity) const activity = parseRemoteActivity(assetActivity, formatNumberOrString)
if (activity) acc[activity.hash] = activity if (activity) acc[activity.hash] = activity
return acc return acc
}, {}) }, {})

@ -18,7 +18,7 @@ import { useCallback, useEffect, useState } from 'react'
import { Link, useNavigate } from 'react-router-dom' import { Link, useNavigate } from 'react-router-dom'
import styled from 'styled-components' import styled from 'styled-components'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
import { formatUSDPrice } from 'utils/formatNumbers' import { useFormatter } from 'utils/formatNumbers'
import { DeltaArrow, DeltaText } from '../Tokens/TokenDetails/Delta' import { DeltaArrow, DeltaText } from '../Tokens/TokenDetails/Delta'
import { useAddRecentlySearchedAsset } from './RecentlySearchedAssets' import { useAddRecentlySearchedAsset } from './RecentlySearchedAssets'
@ -128,6 +128,7 @@ interface TokenRowProps {
export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index, eventProperties }: TokenRowProps) => { export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index, eventProperties }: TokenRowProps) => {
const addRecentlySearchedAsset = useAddRecentlySearchedAsset() const addRecentlySearchedAsset = useAddRecentlySearchedAsset()
const navigate = useNavigate() const navigate = useNavigate()
const { formatFiatPrice } = useFormatter()
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
const address = !token.address && token.standard === TokenStandard.Native ? 'NATIVE' : token.address const address = !token.address && token.standard === TokenStandard.Native ? 'NATIVE' : token.address
@ -184,7 +185,7 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
{!!token.market?.price?.value && ( {!!token.market?.price?.value && (
<> <>
<Row gap="4"> <Row gap="4">
<Box className={styles.primaryText}>{formatUSDPrice(token.market.price.value)}</Box> <Box className={styles.primaryText}>{formatFiatPrice({ price: token.market.price.value })}</Box>
</Row> </Row>
<PriceChangeContainer> <PriceChangeContainer>
<DeltaArrow delta={token.market?.pricePercentChange?.value} /> <DeltaArrow delta={token.market?.pricePercentChange?.value} />

@ -17,6 +17,7 @@ import { useOrder } from 'state/signatures/hooks'
import { useTransaction } from 'state/transactions/hooks' import { useTransaction } from 'state/transactions/hooks'
import styled from 'styled-components' import styled from 'styled-components'
import { EllipsisStyle, ThemedText } from 'theme' import { EllipsisStyle, ThemedText } from 'theme'
import { useFormatter } from 'utils/formatNumbers'
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink' import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
const StyledClose = styled(X)<{ $padding: number }>` const StyledClose = styled(X)<{ $padding: number }>`
@ -137,9 +138,10 @@ export function TransactionPopupContent({
}) { }) {
const transaction = useTransaction(hash) const transaction = useTransaction(hash)
const tokens = useAllTokensMultichain() const tokens = useAllTokensMultichain()
const { formatNumber } = useFormatter()
if (!transaction) return null if (!transaction) return null
const activity = transactionToActivity(transaction, chainId, tokens) const activity = transactionToActivity(transaction, chainId, tokens, formatNumber)
if (!activity) return null if (!activity) return null
@ -153,9 +155,10 @@ export function UniswapXOrderPopupContent({ orderHash, onClose }: { orderHash: s
const order = useOrder(orderHash) const order = useOrder(orderHash)
const tokens = useAllTokensMultichain() const tokens = useAllTokensMultichain()
const openOffchainActivityModal = useOpenOffchainActivityModal() const openOffchainActivityModal = useOpenOffchainActivityModal()
const { formatNumber } = useFormatter()
if (!order) return null if (!order) return null
const activity = signatureToActivity(order, tokens) const activity = signatureToActivity(order, tokens, formatNumber)
if (!activity) return null if (!activity) return null

@ -15,7 +15,7 @@ import { Link } from 'react-router-dom'
import { Bound } from 'state/mint/v3/actions' import { Bound } from 'state/mint/v3/actions'
import styled from 'styled-components' import styled from 'styled-components'
import { HideSmall, MEDIA_WIDTHS, SmallOnly, ThemedText } from 'theme' import { HideSmall, MEDIA_WIDTHS, SmallOnly, ThemedText } from 'theme'
import { formatTickPrice } from 'utils/formatTickPrice' import { useFormatter } from 'utils/formatNumbers'
import { unwrappedToken } from 'utils/unwrappedToken' import { unwrappedToken } from 'utils/unwrappedToken'
import { DAI, USDC_MAINNET, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens' import { DAI, USDC_MAINNET, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
@ -172,6 +172,8 @@ export default function PositionListItem({
tickLower, tickLower,
tickUpper, tickUpper,
}: PositionListItemProps) { }: PositionListItemProps) {
const { formatTickPrice } = useFormatter()
const token0 = useToken(token0Address) const token0 = useToken(token0Address)
const token1 = useToken(token1Address) const token1 = useToken(token1Address)

@ -14,7 +14,7 @@ import { ReactNode, useCallback, useState } from 'react'
import { Bound } from 'state/mint/v3/actions' import { Bound } from 'state/mint/v3/actions'
import { useTheme } from 'styled-components' import { useTheme } from 'styled-components'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
import { formatTickPrice } from 'utils/formatTickPrice' import { useFormatter } from 'utils/formatNumbers'
import { unwrappedToken } from 'utils/unwrappedToken' import { unwrappedToken } from 'utils/unwrappedToken'
export const PositionPreview = ({ export const PositionPreview = ({
@ -31,6 +31,7 @@ export const PositionPreview = ({
ticksAtLimit: { [bound: string]: boolean | undefined } ticksAtLimit: { [bound: string]: boolean | undefined }
}) => { }) => {
const theme = useTheme() const theme = useTheme()
const { formatTickPrice } = useFormatter()
const currency0 = unwrappedToken(position.pool.token0) const currency0 = unwrappedToken(position.pool.token0)
const currency1 = unwrappedToken(position.pool.token1) const currency1 = unwrappedToken(position.pool.token1)

@ -17,7 +17,7 @@ import { Info, TrendingUp } from 'react-feather'
import styled, { useTheme } from 'styled-components' import styled, { useTheme } from 'styled-components'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
import { textFadeIn } from 'theme/styles' import { textFadeIn } from 'theme/styles'
import { formatUSDPrice } from 'utils/formatNumbers' import { useFormatter } from 'utils/formatNumbers'
import { calculateDelta, DeltaArrow, formatDelta } from './Delta' import { calculateDelta, DeltaArrow, formatDelta } from './Delta'
@ -87,6 +87,7 @@ interface PriceChartProps {
export function PriceChart({ width, height, prices: originalPrices, timePeriod }: PriceChartProps) { export function PriceChart({ width, height, prices: originalPrices, timePeriod }: PriceChartProps) {
const locale = useActiveLocale() const locale = useActiveLocale()
const theme = useTheme() const theme = useTheme()
const { formatFiatPrice } = useFormatter()
const { prices, blanks } = useMemo( const { prices, blanks } = useMemo(
() => () =>
@ -208,13 +209,13 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
<ChartHeader data-cy="chart-header"> <ChartHeader data-cy="chart-header">
{displayPrice.value ? ( {displayPrice.value ? (
<> <>
<TokenPrice>{formatUSDPrice(displayPrice.value)}</TokenPrice> <TokenPrice>{formatFiatPrice({ price: displayPrice.value })}</TokenPrice>
<ChartDelta startingPrice={startingPrice} endingPrice={displayPrice} /> <ChartDelta startingPrice={startingPrice} endingPrice={displayPrice} />
</> </>
) : lastPrice.value ? ( ) : lastPrice.value ? (
<OutdatedContainer> <OutdatedContainer>
<OutdatedPriceContainer> <OutdatedPriceContainer>
<TokenPrice>{formatUSDPrice(lastPrice.value)}</TokenPrice> <TokenPrice>{formatFiatPrice({ price: lastPrice.value })}</TokenPrice>
<MouseoverTooltip text={tooltipMessage}> <MouseoverTooltip text={tooltipMessage}>
<Info size={16} /> <Info size={16} />
</MouseoverTooltip> </MouseoverTooltip>

@ -16,7 +16,7 @@ import { CSSProperties, ReactNode } from 'react'
import { Link, useParams } from 'react-router-dom' import { Link, useParams } from 'react-router-dom'
import styled, { css, useTheme } from 'styled-components' import styled, { css, useTheme } from 'styled-components'
import { BREAKPOINTS, ClickableStyle } from 'theme' import { BREAKPOINTS, ClickableStyle } from 'theme'
import { formatUSDPrice, NumberType, useFormatter } from 'utils/formatNumbers' import { NumberType, useFormatter } from 'utils/formatNumbers'
import { import {
LARGE_MEDIA_BREAKPOINT, LARGE_MEDIA_BREAKPOINT,
@ -440,7 +440,7 @@ interface LoadedRowProps {
/* Loaded State: row component with token information */ /* Loaded State: row component with token information */
export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HTMLDivElement>) => { export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HTMLDivElement>) => {
const { formatNumber } = useFormatter() const { formatFiatPrice, formatNumber } = useFormatter()
const { tokenListIndex, tokenListLength, token, sortRank } = props const { tokenListIndex, tokenListLength, token, sortRank } = props
const filterString = useAtomValue(filterStringAtom) const filterString = useAtomValue(filterStringAtom)
@ -463,7 +463,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
} }
// A simple 0 price indicates the price is not currently available from the api // A simple 0 price indicates the price is not currently available from the api
const price = token.market?.price?.value === 0 ? '-' : formatUSDPrice(token.market?.price?.value) const price = token.market?.price?.value === 0 ? '-' : formatFiatPrice({ price: token.market?.price?.value })
// TODO: currency logo sizing mobile (32px) vs. desktop (24px) // TODO: currency logo sizing mobile (32px) vs. desktop (24px)
return ( return (

@ -17,7 +17,6 @@ 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 { FormatterRule, NumberType, SIX_SIG_FIGS_NO_COMMAS, useFormatter } from 'utils/formatNumbers' import { FormatterRule, NumberType, SIX_SIG_FIGS_NO_COMMAS, useFormatter } 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'
@ -85,7 +84,7 @@ export default function SwapModalFooter({
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 = formatNumber({ const formattedPrice = formatNumber({
input: priceToPreciseFloat(trade.executionPrice), input: trade.executionPrice ? parseFloat(trade.executionPrice.toFixed(9)) : undefined,
type: NumberType.TokenTx, type: NumberType.TokenTx,
}) })
const txCount = getTransactionCount(trade) const txCount = getTransactionCount(trade)

@ -6,7 +6,6 @@ import {
TEST_TRADE_EXACT_OUTPUT, TEST_TRADE_EXACT_OUTPUT,
} from 'test-utils/constants' } from 'test-utils/constants'
import { render, screen } from 'test-utils/render' import { render, screen } from 'test-utils/render'
import { formatCurrencyAmount, NumberType } from 'utils/formatNumbers'
import SwapModalHeader from './SwapModalHeader' import SwapModalHeader from './SwapModalHeader'
@ -17,16 +16,8 @@ describe('SwapModalHeader.tsx', () => {
) )
expect(asFragment()).toMatchSnapshot() expect(asFragment()).toMatchSnapshot()
expect(screen.getByText(/Output is estimated. You will receive at least /i)).toBeInTheDocument() expect(screen.getByText(/Output is estimated. You will receive at least /i)).toBeInTheDocument()
expect(screen.getByTestId('INPUT-amount')).toHaveTextContent( expect(screen.getByTestId('INPUT-amount')).toHaveTextContent(`<0.00001 ABC`)
`${formatCurrencyAmount({ amount: TEST_TRADE_EXACT_INPUT.inputAmount, type: NumberType.TokenTx })} ${ expect(screen.getByTestId('OUTPUT-amount')).toHaveTextContent(`<0.00001 DEF`)
TEST_TRADE_EXACT_INPUT.inputAmount.currency.symbol ?? ''
}`
)
expect(screen.getByTestId('OUTPUT-amount')).toHaveTextContent(
`${formatCurrencyAmount({ amount: TEST_TRADE_EXACT_INPUT.outputAmount, type: NumberType.TokenTx })} ${
TEST_TRADE_EXACT_INPUT.outputAmount.currency.symbol ?? ''
}`
)
}) })
it('renders ETH input token for an ETH input UniswapX swap', () => { it('renders ETH input token for an ETH input UniswapX swap', () => {
@ -39,16 +30,8 @@ describe('SwapModalHeader.tsx', () => {
) )
expect(asFragment()).toMatchSnapshot() expect(asFragment()).toMatchSnapshot()
expect(screen.getByText(/Output is estimated. You will receive at least /i)).toBeInTheDocument() expect(screen.getByText(/Output is estimated. You will receive at least /i)).toBeInTheDocument()
expect(screen.getByTestId('INPUT-amount')).toHaveTextContent( expect(screen.getByTestId('INPUT-amount')).toHaveTextContent(`<0.00001 ETH`)
`${formatCurrencyAmount({ amount: TEST_DUTCH_TRADE_ETH_INPUT.inputAmount, type: NumberType.TokenTx })} ${ expect(screen.getByTestId('OUTPUT-amount')).toHaveTextContent(`<0.00001 DEF`)
ETH_MAINNET.symbol
}`
)
expect(screen.getByTestId('OUTPUT-amount')).toHaveTextContent(
`${formatCurrencyAmount({ amount: TEST_DUTCH_TRADE_ETH_INPUT.outputAmount, type: NumberType.TokenTx })} ${
TEST_DUTCH_TRADE_ETH_INPUT.outputAmount.currency.symbol ?? ''
}`
)
}) })
it('test trade exact output, no recipient', () => { it('test trade exact output, no recipient', () => {
@ -58,15 +41,7 @@ describe('SwapModalHeader.tsx', () => {
expect(asFragment()).toMatchSnapshot() expect(asFragment()).toMatchSnapshot()
expect(screen.getByText(/Input is estimated. You will sell at most/i)).toBeInTheDocument() expect(screen.getByText(/Input is estimated. You will sell at most/i)).toBeInTheDocument()
expect(screen.getByTestId('INPUT-amount')).toHaveTextContent( expect(screen.getByTestId('INPUT-amount')).toHaveTextContent(`<0.00001 ABC`)
`${formatCurrencyAmount({ amount: TEST_TRADE_EXACT_OUTPUT.inputAmount, type: NumberType.TokenTx })} ${ expect(screen.getByTestId('OUTPUT-amount')).toHaveTextContent(`<0.00001 GHI`)
TEST_TRADE_EXACT_OUTPUT.inputAmount.currency.symbol ?? ''
}`
)
expect(screen.getByTestId('OUTPUT-amount')).toHaveTextContent(
`${formatCurrencyAmount({ amount: TEST_TRADE_EXACT_OUTPUT.outputAmount, type: NumberType.TokenTx })} ${
TEST_TRADE_EXACT_OUTPUT.outputAmount.currency.symbol ?? ''
}`
)
}) })
}) })

@ -6,7 +6,7 @@ import { Markets, TrendingCollection } from 'nft/types'
import { ethNumberStandardFormatter } from 'nft/utils' import { ethNumberStandardFormatter } from 'nft/utils'
import styled from 'styled-components' import styled from 'styled-components'
import { ThemedText } from 'theme/components/text' import { ThemedText } from 'theme/components/text'
import { formatNumberOrString, NumberType } from 'utils/formatNumbers' import { NumberType, useFormatter } from 'utils/formatNumbers'
const CarouselCardBorder = styled.div` const CarouselCardBorder = styled.div`
width: 100%; width: 100%;
@ -198,6 +198,8 @@ interface MarketplaceRowProps {
} }
const MarketplaceRow = ({ marketplace, floorInEth, listings }: MarketplaceRowProps) => { const MarketplaceRow = ({ marketplace, floorInEth, listings }: MarketplaceRowProps) => {
const { formatNumberOrString } = useFormatter()
return ( return (
<> <>
<TableElement> <TableElement>
@ -212,7 +214,7 @@ const MarketplaceRow = ({ marketplace, floorInEth, listings }: MarketplaceRowPro
<TableElement> <TableElement>
<ThemedText.BodySmall color="neutral2"> <ThemedText.BodySmall color="neutral2">
{Number(floorInEth) > 0 {Number(floorInEth) > 0
? `${formatNumberOrString(floorInEth, NumberType.NFTTokenFloorPriceTrailingZeros)} ETH` ? `${formatNumberOrString({ input: floorInEth, type: NumberType.NFTTokenFloorPriceTrailingZeros })} ETH`
: '-'} : '-'}
</ThemedText.BodySmall> </ThemedText.BodySmall>
</TableElement> </TableElement>

@ -38,7 +38,6 @@ 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 { NumberType, useFormatter } from 'utils/formatNumbers' import { NumberType, useFormatter } from 'utils/formatNumbers'
import { formatTickPrice } from 'utils/formatTickPrice'
import { unwrappedToken } from 'utils/unwrappedToken' import { unwrappedToken } from 'utils/unwrappedToken'
import RangeBadge from '../../components/Badge/RangeBadge' import RangeBadge from '../../components/Badge/RangeBadge'
@ -389,6 +388,7 @@ function PositionPageContent() {
const { tokenId: tokenIdFromUrl } = useParams<{ tokenId?: string }>() const { tokenId: tokenIdFromUrl } = useParams<{ tokenId?: string }>()
const { chainId, account, provider } = useWeb3React() const { chainId, account, provider } = useWeb3React()
const theme = useTheme() const theme = useTheme()
const { formatTickPrice } = useFormatter()
const parsedTokenId = tokenIdFromUrl ? BigNumber.from(tokenIdFromUrl) : undefined const parsedTokenId = tokenIdFromUrl ? BigNumber.from(tokenIdFromUrl) : undefined
const { loading, position: positionDetails } = useV3PositionFromTokenId(parsedTokenId) const { loading, position: positionDetails } = useV3PositionFromTokenId(parsedTokenId)

@ -1,523 +1,461 @@
import { CurrencyAmount, Percent, Price } from '@uniswap/sdk-core' import { renderHook } from '@testing-library/react'
import { USDC_MAINNET, WBTC } from 'constants/tokens' import { CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { USDC_MAINNET } from 'constants/tokens'
import { useCurrencyConversionFlagEnabled } from 'featureFlags/flags/currencyConversion'
import { Currency } from 'graphql/data/__generated__/types-and-hooks' import { Currency } from 'graphql/data/__generated__/types-and-hooks'
import { useLocalCurrencyConversionRate } from 'graphql/data/ConversionRate'
import { useActiveLocalCurrency } from 'hooks/useActiveLocalCurrency'
import { useActiveLocale } from 'hooks/useActiveLocale'
import { mocked } from 'test-utils/mocked'
import { import { NumberType, useFormatter } from './formatNumbers'
currencyAmountToPreciseFloat,
formatNumber,
formatPriceImpact,
formatReviewSwapCurrencyAmount,
formatSlippage,
formatUSDPrice,
NumberType,
priceToPreciseFloat,
} from './formatNumbers'
it('formats token reference numbers correctly', () => { jest.mock('hooks/useActiveLocale')
expect(formatNumber({ input: 1234567000000000, type: NumberType.TokenNonTx })).toBe('>999T') jest.mock('hooks/useActiveLocalCurrency')
expect(formatNumber({ input: 1234567000000000, type: NumberType.TokenNonTx, locale: 'de-DE' })).toBe('>999\xa0Bio.') jest.mock('graphql/data/ConversionRate')
expect(formatNumber({ input: 1002345, type: NumberType.TokenNonTx })).toBe('1.00M') jest.mock('featureFlags/flags/currencyConversion')
expect(formatNumber({ input: 1002345, type: NumberType.TokenNonTx, locale: 'de-DE' })).toBe('1,00\xa0Mio.')
expect(formatNumber({ input: 1234, type: NumberType.TokenNonTx })).toBe('1,234.00')
expect(formatNumber({ input: 1234, type: NumberType.TokenNonTx, locale: 'de-DE' })).toBe('1.234,00')
expect(formatNumber({ input: 0.00909, type: NumberType.TokenNonTx })).toBe('0.009')
expect(formatNumber({ input: 0.00909, type: NumberType.TokenNonTx, locale: 'de-DE' })).toBe('0,009')
expect(formatNumber({ input: 0.09001, type: NumberType.TokenNonTx })).toBe('0.090')
expect(formatNumber({ input: 0.09001, type: NumberType.TokenNonTx, locale: 'de-DE' })).toBe('0,090')
expect(formatNumber({ input: 0.00099, type: NumberType.TokenNonTx })).toBe('<0.001')
expect(formatNumber({ input: 0.00099, type: NumberType.TokenNonTx, locale: 'de-DE' })).toBe('<0,001')
expect(formatNumber({ input: 0, type: NumberType.TokenNonTx })).toBe('0')
expect(formatNumber({ input: 0, type: NumberType.TokenNonTx, locale: 'de-DE' })).toBe('0')
})
it('formats token transaction numbers correctly', () => {
expect(formatNumber({ input: 1234567.8901, type: NumberType.TokenTx })).toBe('1,234,567.89')
expect(formatNumber({ input: 1234567.8901, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('1\xa0234\xa0567,89')
expect(formatNumber({ input: 765432.1, type: NumberType.TokenTx })).toBe('765,432.10')
expect(formatNumber({ input: 765432.1, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('765\xa0432,10')
expect(formatNumber({ input: 7654.321, type: NumberType.TokenTx })).toBe('7,654.32') describe('formatNumber', () => {
expect(formatNumber({ input: 7654.321, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('7\xa0654,32') beforeEach(() => {
expect(formatNumber({ input: 765.4321, type: NumberType.TokenTx })).toBe('765.432') mocked(useLocalCurrencyConversionRate).mockReturnValue({ data: 1.0, isLoading: false })
expect(formatNumber({ input: 765.4321, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('765,432') mocked(useCurrencyConversionFlagEnabled).mockReturnValue(true)
expect(formatNumber({ input: 76.54321, type: NumberType.TokenTx })).toBe('76.5432') })
expect(formatNumber({ input: 76.54321, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('76,5432')
expect(formatNumber({ input: 7.654321, type: NumberType.TokenTx })).toBe('7.65432')
expect(formatNumber({ input: 7.654321, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('7,65432')
expect(formatNumber({ input: 7.60000054321, type: NumberType.TokenTx })).toBe('7.60')
expect(formatNumber({ input: 7.60000054321, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('7,60')
expect(formatNumber({ input: 7.6, type: NumberType.TokenTx })).toBe('7.60')
expect(formatNumber({ input: 7.6, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('7,60')
expect(formatNumber({ input: 7, type: NumberType.TokenTx })).toBe('7.00')
expect(formatNumber({ input: 7, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('7,00')
expect(formatNumber({ input: 0.987654321, type: NumberType.TokenTx })).toBe('0.98765') it('formats token reference numbers correctly', () => {
expect(formatNumber({ input: 0.987654321, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('0,98765') const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 0.9, type: NumberType.TokenTx })).toBe('0.90')
expect(formatNumber({ input: 0.9, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('0,90')
expect(formatNumber({ input: 0.901000123, type: NumberType.TokenTx })).toBe('0.901')
expect(formatNumber({ input: 0.901000123, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('0,901')
expect(formatNumber({ input: 0.000000001, type: NumberType.TokenTx })).toBe('<0.00001')
expect(formatNumber({ input: 0.000000001, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('<0,00001')
expect(formatNumber({ input: 0, type: NumberType.TokenTx })).toBe('0')
expect(formatNumber({ input: 0, type: NumberType.TokenTx, locale: 'ru-RU' })).toBe('0')
})
it('formats fiat estimates on token details pages correctly', () => { expect(formatNumber({ input: 1234567000000000, type: NumberType.TokenNonTx })).toBe('>999T')
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenDetails })).toBe('$1.23M') expect(formatNumber({ input: 1002345, type: NumberType.TokenNonTx })).toBe('1.00M')
expect( expect(formatNumber({ input: 1234, type: NumberType.TokenNonTx })).toBe('1,234.00')
formatNumber({ expect(formatNumber({ input: 0.00909, type: NumberType.TokenNonTx })).toBe('0.009')
input: 1234567.891, expect(formatNumber({ input: 0.09001, type: NumberType.TokenNonTx })).toBe('0.090')
type: NumberType.FiatTokenDetails, expect(formatNumber({ input: 0.00099, type: NumberType.TokenNonTx })).toBe('<0.001')
locale: 'fr-FR', expect(formatNumber({ input: 0, type: NumberType.TokenNonTx })).toBe('0')
localCurrency: Currency.Eur, })
})
).toBe('1,23\xa0M\xa0€')
expect(formatNumber({ input: 1234.5678, type: NumberType.FiatTokenDetails })).toBe('$1,234.57')
expect(
formatNumber({ input: 1234.5678, type: NumberType.FiatTokenDetails, locale: 'fr-FR', localCurrency: Currency.Eur })
).toBe('1\u202f234,57\xa0€')
expect(formatNumber({ input: 1.048942, type: NumberType.FiatTokenDetails })).toBe('$1.049')
expect(
formatNumber({ input: 1.048942, type: NumberType.FiatTokenDetails, locale: 'fr-FR', localCurrency: Currency.Eur })
).toBe('1,049\xa0€')
expect(formatNumber({ input: 0.001231, type: NumberType.FiatTokenDetails })).toBe('$0.00123') it('formats token reference numbers correctly with deutsch locale', () => {
expect( mocked(useActiveLocale).mockReturnValue('de-DE')
formatNumber({ input: 0.001231, type: NumberType.FiatTokenDetails, locale: 'fr-FR', localCurrency: Currency.Eur }) const { formatNumber } = renderHook(() => useFormatter()).result.current
).toBe('0,00123\xa0€')
expect(formatNumber({ input: 0.00001231, type: NumberType.FiatTokenDetails })).toBe('$0.0000123')
expect(
formatNumber({ input: 0.00001231, type: NumberType.FiatTokenDetails, locale: 'fr-FR', localCurrency: Currency.Eur })
).toBe('0,0000123\xa0€')
expect(formatNumber({ input: 0.0000001234, type: NumberType.FiatTokenDetails })).toBe('$0.000000123') expect(formatNumber({ input: 1234567000000000, type: NumberType.TokenNonTx })).toBe('>999\xa0Bio.')
expect( expect(formatNumber({ input: 1002345, type: NumberType.TokenNonTx })).toBe('1,00\xa0Mio.')
formatNumber({ expect(formatNumber({ input: 1234, type: NumberType.TokenNonTx })).toBe('1.234,00')
input: 0.0000001234, expect(formatNumber({ input: 0.00909, type: NumberType.TokenNonTx })).toBe('0,009')
type: NumberType.FiatTokenDetails, expect(formatNumber({ input: 0.09001, type: NumberType.TokenNonTx })).toBe('0,090')
locale: 'fr-FR', expect(formatNumber({ input: 0.00099, type: NumberType.TokenNonTx })).toBe('<0,001')
localCurrency: Currency.Eur, expect(formatNumber({ input: 0, type: NumberType.TokenNonTx })).toBe('0')
}) })
).toBe('0,000000123\xa0€')
expect(formatNumber({ input: 0.000000009876, type: NumberType.FiatTokenDetails })).toBe('<$0.00000001')
expect(
formatNumber({
input: 0.000000009876,
type: NumberType.FiatTokenDetails,
locale: 'fr-FR',
localCurrency: Currency.Eur,
})
).toBe('<0,00000001\xa0€')
})
it('formats fiat estimates for tokens correctly', () => { it('formats token transaction numbers correctly', () => {
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenPrice })).toBe('$1.23M') const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(
formatNumber({ input: 1234567.891, type: NumberType.FiatTokenPrice, locale: 'es-ES', localCurrency: Currency.Jpy })
).toBe('1,23\xa0M¥')
expect(formatNumber({ input: 1234.5678, type: NumberType.FiatTokenPrice })).toBe('$1,234.57')
expect(
formatNumber({ input: 1234.5678, type: NumberType.FiatTokenPrice, locale: 'es-ES', localCurrency: Currency.Jpy })
).toBe('1234,57\xa0¥')
expect(
formatNumber({ input: 12345.678, type: NumberType.FiatTokenPrice, locale: 'es-ES', localCurrency: Currency.Jpy })
).toBe('12.345,68\xa0¥')
expect(formatNumber({ input: 0.010235, type: NumberType.FiatTokenPrice })).toBe('$0.0102') expect(formatNumber({ input: 1234567.8901, type: NumberType.TokenTx })).toBe('1,234,567.89')
expect( expect(formatNumber({ input: 765432.1, type: NumberType.TokenTx })).toBe('765,432.10')
formatNumber({ input: 0.010235, type: NumberType.FiatTokenPrice, locale: 'es-ES', localCurrency: Currency.Jpy })
).toBe('0,0102\xa0¥')
expect(formatNumber({ input: 0.001231, type: NumberType.FiatTokenPrice })).toBe('$0.00123')
expect(
formatNumber({ input: 0.001231, type: NumberType.FiatTokenPrice, locale: 'es-ES', localCurrency: Currency.Jpy })
).toBe('0,00123\xa0¥')
expect(formatNumber({ input: 0.00001231, type: NumberType.FiatTokenPrice })).toBe('$0.0000123')
expect(
formatNumber({ input: 0.00001231, type: NumberType.FiatTokenPrice, locale: 'es-ES', localCurrency: Currency.Jpy })
).toBe('0,0000123\xa0¥')
expect(formatNumber({ input: 0.0000001234, type: NumberType.FiatTokenPrice })).toBe('$0.000000123') expect(formatNumber({ input: 7654.321, type: NumberType.TokenTx })).toBe('7,654.32')
expect( expect(formatNumber({ input: 765.4321, type: NumberType.TokenTx })).toBe('765.432')
formatNumber({ input: 0.0000001234, type: NumberType.FiatTokenPrice, locale: 'es-ES', localCurrency: Currency.Jpy }) expect(formatNumber({ input: 76.54321, type: NumberType.TokenTx })).toBe('76.5432')
).toBe('0,000000123\xa0¥') expect(formatNumber({ input: 7.654321, type: NumberType.TokenTx })).toBe('7.65432')
expect(formatNumber({ input: 0.000000009876, type: NumberType.FiatTokenPrice })).toBe('<$0.00000001') expect(formatNumber({ input: 7.60000054321, type: NumberType.TokenTx })).toBe('7.60')
expect( expect(formatNumber({ input: 7.6, type: NumberType.TokenTx })).toBe('7.60')
formatNumber({ expect(formatNumber({ input: 7, type: NumberType.TokenTx })).toBe('7.00')
input: 0.000000009876,
type: NumberType.FiatTokenPrice,
locale: 'es-ES',
localCurrency: Currency.Jpy,
})
).toBe('<0,00000001\xa0¥')
expect(formatNumber({ input: 10000000000000000000000000000000, type: NumberType.FiatTokenPrice })).toBe(
'$1.000000E31'
)
expect(
formatNumber({
input: 10000000000000000000000000000000,
type: NumberType.FiatTokenPrice,
locale: 'es-ES',
localCurrency: Currency.Jpy,
})
).toBe('1,000000E31\xa0¥')
})
it('formats fiat estimates for token stats correctly', () => { expect(formatNumber({ input: 0.987654321, type: NumberType.TokenTx })).toBe('0.98765')
expect(formatNumber({ input: 1234576, type: NumberType.FiatTokenStats })).toBe('$1.2M') expect(formatNumber({ input: 0.9, type: NumberType.TokenTx })).toBe('0.90')
expect( expect(formatNumber({ input: 0.901000123, type: NumberType.TokenTx })).toBe('0.901')
formatNumber({ input: 1234576, type: NumberType.FiatTokenStats, locale: 'ja-JP', localCurrency: Currency.Cad }) expect(formatNumber({ input: 0.000000001, type: NumberType.TokenTx })).toBe('<0.00001')
).toBe('CA$123.5万') expect(formatNumber({ input: 0, type: NumberType.TokenTx })).toBe('0')
expect(formatNumber({ input: 234567, type: NumberType.FiatTokenStats })).toBe('$234.6K') })
expect(
formatNumber({ input: 234567, type: NumberType.FiatTokenStats, locale: 'ja-JP', localCurrency: Currency.Cad })
).toBe('CA$23.5万')
expect(formatNumber({ input: 123.456, type: NumberType.FiatTokenStats })).toBe('$123.46')
expect(
formatNumber({ input: 123.456, type: NumberType.FiatTokenStats, locale: 'ja-JP', localCurrency: Currency.Cad })
).toBe('CA$123.46')
expect(formatNumber({ input: 1.23, type: NumberType.FiatTokenStats })).toBe('$1.23')
expect(
formatNumber({ input: 1.23, type: NumberType.FiatTokenStats, locale: 'ja-JP', localCurrency: Currency.Cad })
).toBe('CA$1.23')
expect(formatNumber({ input: 0.123, type: NumberType.FiatTokenStats })).toBe('$0.12')
expect(
formatNumber({ input: 0.123, type: NumberType.FiatTokenStats, locale: 'ja-JP', localCurrency: Currency.Cad })
).toBe('CA$0.12')
expect(formatNumber({ input: 0.00123, type: NumberType.FiatTokenStats })).toBe('<$0.01')
expect(
formatNumber({ input: 0.00123, type: NumberType.FiatTokenStats, locale: 'ja-JP', localCurrency: Currency.Cad })
).toBe('<CA$0.01')
expect(formatNumber({ input: 0, type: NumberType.FiatTokenStats })).toBe('-')
expect(
formatNumber({ input: 0, type: NumberType.FiatTokenStats, locale: 'ja-JP', localCurrency: Currency.Cad })
).toBe('-')
})
it('formats gas USD prices correctly', () => { it('formats token transaction numbers correctly with russian locale', () => {
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatGasPrice })).toBe('$1.23M') mocked(useActiveLocale).mockReturnValue('ru-RU')
expect( const { formatNumber } = renderHook(() => useFormatter()).result.current
formatNumber({ input: 1234567.891, type: NumberType.FiatGasPrice, locale: 'pt-PR', localCurrency: Currency.Thb })
).toBe('฿\xa01,23\xa0mi')
expect(formatNumber({ input: 18.448, type: NumberType.FiatGasPrice })).toBe('$18.45')
expect(
formatNumber({ input: 18.448, type: NumberType.FiatGasPrice, locale: 'pt-PR', localCurrency: Currency.Thb })
).toBe('฿\xa018,45')
expect(formatNumber({ input: 0.0099, type: NumberType.FiatGasPrice })).toBe('<$0.01')
expect(
formatNumber({ input: 0.0099, type: NumberType.FiatGasPrice, locale: 'pt-PR', localCurrency: Currency.Thb })
).toBe('<฿\xa00,01')
expect(formatNumber({ input: 0, type: NumberType.FiatGasPrice })).toBe('$0.00')
expect(formatNumber({ input: 0, type: NumberType.FiatGasPrice, locale: 'pt-PR', localCurrency: Currency.Thb })).toBe(
'฿\xa00,00'
)
})
it('formats USD token quantities prices correctly', () => { expect(formatNumber({ input: 1234567.8901, type: NumberType.TokenTx })).toBe('1\xa0234\xa0567,89')
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenQuantity })).toBe('$1.23M') expect(formatNumber({ input: 765432.1, type: NumberType.TokenTx })).toBe('765\xa0432,10')
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenQuantity, localCurrency: Currency.Ngn })).toBe(
'₦1.23M'
)
expect(formatNumber({ input: 18.448, type: NumberType.FiatTokenQuantity })).toBe('$18.45')
expect(formatNumber({ input: 18.448, type: NumberType.FiatTokenQuantity, localCurrency: Currency.Ngn })).toBe(
'₦18.45'
)
expect(formatNumber({ input: 0.0099, type: NumberType.FiatTokenQuantity })).toBe('<$0.01')
expect(formatNumber({ input: 0.0099, type: NumberType.FiatTokenQuantity, localCurrency: Currency.Ngn })).toBe(
'<₦0.01'
)
expect(formatNumber({ input: 0, type: NumberType.FiatTokenQuantity })).toBe('$0.00')
expect(formatNumber({ input: 0, type: NumberType.FiatTokenQuantity, localCurrency: Currency.Ngn })).toBe('₦0.00')
})
it('formats Swap text input/output numbers correctly', () => { expect(formatNumber({ input: 7654.321, type: NumberType.TokenTx })).toBe('7\xa0654,32')
expect(formatNumber({ input: 1234567.8901, type: NumberType.SwapTradeAmount })).toBe('1234570') expect(formatNumber({ input: 765.4321, type: NumberType.TokenTx })).toBe('765,432')
expect(formatNumber({ input: 1234567.8901, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('1234570') expect(formatNumber({ input: 76.54321, type: NumberType.TokenTx })).toBe('76,5432')
expect(formatNumber({ input: 765432.1, type: NumberType.SwapTradeAmount })).toBe('765432') expect(formatNumber({ input: 7.654321, type: NumberType.TokenTx })).toBe('7,65432')
expect(formatNumber({ input: 765432.1, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('765432') expect(formatNumber({ input: 7.60000054321, type: NumberType.TokenTx })).toBe('7,60')
expect(formatNumber({ input: 7.6, type: NumberType.TokenTx })).toBe('7,60')
expect(formatNumber({ input: 7, type: NumberType.TokenTx })).toBe('7,00')
expect(formatNumber({ input: 7654.321, type: NumberType.SwapTradeAmount })).toBe('7654.32') expect(formatNumber({ input: 0.987654321, type: NumberType.TokenTx })).toBe('0,98765')
expect(formatNumber({ input: 7654.321, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('7654.32') expect(formatNumber({ input: 0.9, type: NumberType.TokenTx })).toBe('0,90')
expect(formatNumber({ input: 765.4321, type: NumberType.SwapTradeAmount })).toBe('765.432') expect(formatNumber({ input: 0.901000123, type: NumberType.TokenTx })).toBe('0,901')
expect(formatNumber({ input: 765.4321, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('765.432') expect(formatNumber({ input: 0.000000001, type: NumberType.TokenTx })).toBe('<0,00001')
expect(formatNumber({ input: 76.54321, type: NumberType.SwapTradeAmount })).toBe('76.5432') expect(formatNumber({ input: 0, type: NumberType.TokenTx })).toBe('0')
expect(formatNumber({ input: 76.54321, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('76.5432') })
expect(formatNumber({ input: 7.654321, type: NumberType.SwapTradeAmount })).toBe('7.65432')
expect(formatNumber({ input: 7.654321, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('7.65432')
expect(formatNumber({ input: 7.60000054321, type: NumberType.SwapTradeAmount })).toBe('7.60')
expect(formatNumber({ input: 7.60000054321, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('7.60')
expect(formatNumber({ input: 7.6, type: NumberType.SwapTradeAmount })).toBe('7.60')
expect(formatNumber({ input: 7.6, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('7.60')
expect(formatNumber({ input: 7, type: NumberType.SwapTradeAmount })).toBe('7.00')
expect(formatNumber({ input: 7, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('7.00')
expect(formatNumber({ input: 0.987654321, type: NumberType.SwapTradeAmount })).toBe('0.98765') it('formats fiat estimates on token details pages correctly', () => {
expect(formatNumber({ input: 0.987654321, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('0.98765') const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 0.9, type: NumberType.SwapTradeAmount })).toBe('0.90')
expect(formatNumber({ input: 0.9, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('0.90')
expect(formatNumber({ input: 0.901000123, type: NumberType.SwapTradeAmount })).toBe('0.901')
expect(formatNumber({ input: 0.901000123, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('0.901')
expect(formatNumber({ input: 0.000000001, type: NumberType.SwapTradeAmount })).toBe('0.000000001')
expect(formatNumber({ input: 0.000000001, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('0.000000001')
expect(formatNumber({ input: 0, type: NumberType.SwapTradeAmount })).toBe('0')
expect(formatNumber({ input: 0, type: NumberType.SwapTradeAmount, locale: 'ko-KR' })).toBe('0')
})
it('formats NFT numbers correctly', () => { expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenDetails })).toBe('$1.23M')
expect(formatNumber({ input: 1234567000000000, type: NumberType.NFTTokenFloorPrice })).toBe('>999T') expect(formatNumber({ input: 1234.5678, type: NumberType.FiatTokenDetails })).toBe('$1,234.57')
expect( expect(formatNumber({ input: 1.048942, type: NumberType.FiatTokenDetails })).toBe('$1.049')
formatNumber({
input: 1234567000000000,
type: NumberType.NFTTokenFloorPrice,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('>999\xa0tri')
expect(formatNumber({ input: 1002345, type: NumberType.NFTTokenFloorPrice })).toBe('1M')
expect(
formatNumber({
input: 1002345,
type: NumberType.NFTTokenFloorPrice,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('1\xa0mi')
expect(formatNumber({ input: 1234, type: NumberType.NFTTokenFloorPrice })).toBe('1.23K')
expect(
formatNumber({
input: 1234,
type: NumberType.NFTTokenFloorPrice,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('1,23\xa0mil')
expect(formatNumber({ input: 12.34467, type: NumberType.NFTTokenFloorPrice })).toBe('12.34')
expect(
formatNumber({
input: 12.34467,
type: NumberType.NFTTokenFloorPrice,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('12,34')
expect(formatNumber({ input: 12.1, type: NumberType.NFTTokenFloorPrice })).toBe('12.1')
expect(
formatNumber({
input: 12.1,
type: NumberType.NFTTokenFloorPrice,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('12,1')
expect(formatNumber({ input: 0.00909, type: NumberType.NFTTokenFloorPrice })).toBe('0.009')
expect(
formatNumber({
input: 0.00909,
type: NumberType.NFTTokenFloorPrice,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('0,009')
expect(formatNumber({ input: 0.09001, type: NumberType.NFTTokenFloorPrice })).toBe('0.09')
expect(
formatNumber({
input: 0.09001,
type: NumberType.NFTTokenFloorPrice,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('0,09')
expect(formatNumber({ input: 0.00099, type: NumberType.NFTTokenFloorPrice })).toBe('<0.001')
expect(
formatNumber({
input: 0.00099,
type: NumberType.NFTTokenFloorPrice,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('<0,001')
expect(formatNumber({ input: 0, type: NumberType.NFTTokenFloorPrice })).toBe('0')
expect(
formatNumber({
input: 0,
type: NumberType.NFTTokenFloorPrice,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('0')
expect(formatNumber({ input: 12.1, type: NumberType.NFTTokenFloorPriceTrailingZeros })).toBe('12.10') expect(formatNumber({ input: 0.001231, type: NumberType.FiatTokenDetails })).toBe('$0.00123')
expect( expect(formatNumber({ input: 0.00001231, type: NumberType.FiatTokenDetails })).toBe('$0.0000123')
formatNumber({
input: 12.1,
type: NumberType.NFTTokenFloorPriceTrailingZeros,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('12,10')
expect(formatNumber({ input: 0.09001, type: NumberType.NFTTokenFloorPriceTrailingZeros })).toBe('0.090')
expect(
formatNumber({
input: 0.09001,
type: NumberType.NFTTokenFloorPriceTrailingZeros,
locale: 'pt-BR',
localCurrency: Currency.Brl,
})
).toBe('0,090')
expect(formatNumber({ input: 0.987654321, type: NumberType.NFTCollectionStats })).toBe('1') expect(formatNumber({ input: 0.0000001234, type: NumberType.FiatTokenDetails })).toBe('$0.000000123')
expect( expect(formatNumber({ input: 0.000000009876, type: NumberType.FiatTokenDetails })).toBe('<$0.00000001')
formatNumber({ })
input: 0.987654321,
type: NumberType.NFTCollectionStats, it('formats fiat estimates on token details pages correctly with french locale and euro currency', () => {
locale: 'pt-BR', mocked(useActiveLocale).mockReturnValue('fr-FR')
localCurrency: Currency.Brl, mocked(useActiveLocalCurrency).mockReturnValue(Currency.Eur)
}) const { formatNumber } = renderHook(() => useFormatter()).result.current
).toBe('1')
expect(formatNumber({ input: 0.9, type: NumberType.NFTCollectionStats })).toBe('1') expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenDetails })).toBe('1,23\xa0M\xa0€')
expect( expect(formatNumber({ input: 1234.5678, type: NumberType.FiatTokenDetails })).toBe('1\u202f234,57\xa0€')
formatNumber({ input: 0.9, type: NumberType.NFTCollectionStats, locale: 'pt-BR', localCurrency: Currency.Brl }) expect(formatNumber({ input: 1.048942, type: NumberType.FiatTokenDetails })).toBe('1,049\xa0€')
).toBe('1')
expect(formatNumber({ input: 76543.21, type: NumberType.NFTCollectionStats })).toBe('76.5K') expect(formatNumber({ input: 0.001231, type: NumberType.FiatTokenDetails })).toBe('0,00123\xa0€')
expect( expect(formatNumber({ input: 0.00001231, type: NumberType.FiatTokenDetails })).toBe('0,0000123\xa0€')
formatNumber({ input: 76543.21, type: NumberType.NFTCollectionStats, locale: 'pt-BR', localCurrency: Currency.Brl })
).toBe('76,5\xa0mil') expect(formatNumber({ input: 0.0000001234, type: NumberType.FiatTokenDetails })).toBe('0,000000123\xa0€')
expect(formatNumber({ input: 7.60000054321, type: NumberType.NFTCollectionStats })).toBe('8') expect(formatNumber({ input: 0.000000009876, type: NumberType.FiatTokenDetails })).toBe('<0,00000001\xa0€')
expect( })
formatNumber({
input: 7.60000054321, it('formats fiat estimates for tokens correctly', () => {
type: NumberType.NFTCollectionStats, const { formatNumber } = renderHook(() => useFormatter()).result.current
locale: 'pt-BR',
localCurrency: Currency.Brl, expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenPrice })).toBe('$1.23M')
}) expect(formatNumber({ input: 1234.5678, type: NumberType.FiatTokenPrice })).toBe('$1,234.57')
).toBe('8')
expect(formatNumber({ input: 1234567890, type: NumberType.NFTCollectionStats })).toBe('1.2B') expect(formatNumber({ input: 0.010235, type: NumberType.FiatTokenPrice })).toBe('$0.0102')
expect( expect(formatNumber({ input: 0.001231, type: NumberType.FiatTokenPrice })).toBe('$0.00123')
formatNumber({ expect(formatNumber({ input: 0.00001231, type: NumberType.FiatTokenPrice })).toBe('$0.0000123')
input: 1234567890,
type: NumberType.NFTCollectionStats, expect(formatNumber({ input: 0.0000001234, type: NumberType.FiatTokenPrice })).toBe('$0.000000123')
locale: 'pt-BR', expect(formatNumber({ input: 0.000000009876, type: NumberType.FiatTokenPrice })).toBe('<$0.00000001')
localCurrency: Currency.Brl, expect(formatNumber({ input: 10000000000000000000000000000000, type: NumberType.FiatTokenPrice })).toBe(
}) '$1.000000E31'
).toBe('1,2\xa0bi') )
expect(formatNumber({ input: 1234567000000000, type: NumberType.NFTCollectionStats })).toBe('1234.6T') })
expect(
formatNumber({ it('formats fiat estimates for tokens correctly with spanish locale and yen currency', () => {
input: 1234567000000000, mocked(useActiveLocale).mockReturnValue('es-ES')
type: NumberType.NFTCollectionStats, mocked(useActiveLocalCurrency).mockReturnValue(Currency.Jpy)
locale: 'pt-BR', const { formatNumber } = renderHook(() => useFormatter()).result.current
localCurrency: Currency.Brl,
}) expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenPrice })).toBe('1,23\xa0M¥')
).toBe('1234,6\xa0tri') expect(formatNumber({ input: 1234.5678, type: NumberType.FiatTokenPrice })).toBe('1234,57\xa0¥')
expect(formatNumber({ input: 12345.678, type: NumberType.FiatTokenPrice })).toBe('12.345,68\xa0¥')
expect(formatNumber({ input: 0.010235, type: NumberType.FiatTokenPrice })).toBe('0,0102\xa0¥')
expect(formatNumber({ input: 0.001231, type: NumberType.FiatTokenPrice })).toBe('0,00123\xa0¥')
expect(formatNumber({ input: 0.00001231, type: NumberType.FiatTokenPrice })).toBe('0,0000123\xa0¥')
expect(formatNumber({ input: 0.0000001234, type: NumberType.FiatTokenPrice })).toBe('0,000000123\xa0¥')
expect(formatNumber({ input: 0.000000009876, type: NumberType.FiatTokenPrice })).toBe('<0,00000001\xa0¥')
expect(formatNumber({ input: 10000000000000000000000000000000, type: NumberType.FiatTokenPrice })).toBe(
'1,000000E31\xa0¥'
)
})
it('formats fiat estimates for token stats correctly', () => {
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234576, type: NumberType.FiatTokenStats })).toBe('$1.2M')
expect(formatNumber({ input: 234567, type: NumberType.FiatTokenStats })).toBe('$234.6K')
expect(formatNumber({ input: 123.456, type: NumberType.FiatTokenStats })).toBe('$123.46')
expect(formatNumber({ input: 1.23, type: NumberType.FiatTokenStats })).toBe('$1.23')
expect(formatNumber({ input: 0.123, type: NumberType.FiatTokenStats })).toBe('$0.12')
expect(formatNumber({ input: 0.00123, type: NumberType.FiatTokenStats })).toBe('<$0.01')
expect(formatNumber({ input: 0, type: NumberType.FiatTokenStats })).toBe('-')
})
it('formats fiat estimates for token stats correctly with japenese locale and cad currency', () => {
mocked(useActiveLocale).mockReturnValue('ja-JP')
mocked(useActiveLocalCurrency).mockReturnValue(Currency.Cad)
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234576, type: NumberType.FiatTokenStats })).toBe('CA$123.5万')
expect(formatNumber({ input: 234567, type: NumberType.FiatTokenStats })).toBe('CA$23.5万')
expect(formatNumber({ input: 123.456, type: NumberType.FiatTokenStats })).toBe('CA$123.46')
expect(formatNumber({ input: 1.23, type: NumberType.FiatTokenStats })).toBe('CA$1.23')
expect(formatNumber({ input: 0.123, type: NumberType.FiatTokenStats })).toBe('CA$0.12')
expect(formatNumber({ input: 0.00123, type: NumberType.FiatTokenStats })).toBe('<CA$0.01')
expect(formatNumber({ input: 0, type: NumberType.FiatTokenStats })).toBe('-')
})
it('formats gas USD prices correctly', () => {
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatGasPrice })).toBe('$1.23M')
expect(formatNumber({ input: 18.448, type: NumberType.FiatGasPrice })).toBe('$18.45')
expect(formatNumber({ input: 0.0099, type: NumberType.FiatGasPrice })).toBe('<$0.01')
expect(formatNumber({ input: 0, type: NumberType.FiatGasPrice })).toBe('$0.00')
})
it('formats gas prices correctly with portugese locale and thai baht currency', () => {
mocked(useActiveLocale).mockReturnValue('pt-PR')
mocked(useActiveLocalCurrency).mockReturnValue(Currency.Thb)
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatGasPrice })).toBe('฿\xa01,23\xa0mi')
expect(formatNumber({ input: 18.448, type: NumberType.FiatGasPrice })).toBe('฿\xa018,45')
expect(formatNumber({ input: 0.0099, type: NumberType.FiatGasPrice })).toBe('<฿\xa00,01')
expect(formatNumber({ input: 0, type: NumberType.FiatGasPrice })).toBe('฿\xa00,00')
})
it('formats USD token quantities prices correctly', () => {
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenQuantity })).toBe('$1.23M')
expect(formatNumber({ input: 18.448, type: NumberType.FiatTokenQuantity })).toBe('$18.45')
expect(formatNumber({ input: 0.0099, type: NumberType.FiatTokenQuantity })).toBe('<$0.01')
expect(formatNumber({ input: 0, type: NumberType.FiatTokenQuantity })).toBe('$0.00')
})
it('formats token quantities prices correctly with nigerian naira currency', () => {
mocked(useActiveLocalCurrency).mockReturnValue(Currency.Ngn)
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatTokenQuantity })).toBe('₦1.23M')
expect(formatNumber({ input: 18.448, type: NumberType.FiatTokenQuantity })).toBe('₦18.45')
expect(formatNumber({ input: 0.0099, type: NumberType.FiatTokenQuantity })).toBe('<₦0.01')
expect(formatNumber({ input: 0, type: NumberType.FiatTokenQuantity })).toBe('₦0.00')
})
it('formats Swap text input/output numbers correctly', () => {
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234567.8901, type: NumberType.SwapTradeAmount })).toBe('1234570')
expect(formatNumber({ input: 765432.1, type: NumberType.SwapTradeAmount })).toBe('765432')
expect(formatNumber({ input: 7654.321, type: NumberType.SwapTradeAmount })).toBe('7654.32')
expect(formatNumber({ input: 765.4321, type: NumberType.SwapTradeAmount })).toBe('765.432')
expect(formatNumber({ input: 76.54321, type: NumberType.SwapTradeAmount })).toBe('76.5432')
expect(formatNumber({ input: 7.654321, type: NumberType.SwapTradeAmount })).toBe('7.65432')
expect(formatNumber({ input: 7.60000054321, type: NumberType.SwapTradeAmount })).toBe('7.60')
expect(formatNumber({ input: 7.6, type: NumberType.SwapTradeAmount })).toBe('7.60')
expect(formatNumber({ input: 7, type: NumberType.SwapTradeAmount })).toBe('7.00')
expect(formatNumber({ input: 0.987654321, type: NumberType.SwapTradeAmount })).toBe('0.98765')
expect(formatNumber({ input: 0.9, type: NumberType.SwapTradeAmount })).toBe('0.90')
expect(formatNumber({ input: 0.901000123, type: NumberType.SwapTradeAmount })).toBe('0.901')
expect(formatNumber({ input: 0.000000001, type: NumberType.SwapTradeAmount })).toBe('0.000000001')
expect(formatNumber({ input: 0, type: NumberType.SwapTradeAmount })).toBe('0')
})
it('formats Swap text input/output numbers correctly with Korean locale', () => {
mocked(useActiveLocale).mockReturnValue('ko-KR')
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234567.8901, type: NumberType.SwapTradeAmount })).toBe('1234570')
expect(formatNumber({ input: 765432.1, type: NumberType.SwapTradeAmount })).toBe('765432')
expect(formatNumber({ input: 7654.321, type: NumberType.SwapTradeAmount })).toBe('7654.32')
expect(formatNumber({ input: 765.4321, type: NumberType.SwapTradeAmount })).toBe('765.432')
expect(formatNumber({ input: 76.54321, type: NumberType.SwapTradeAmount })).toBe('76.5432')
expect(formatNumber({ input: 7.654321, type: NumberType.SwapTradeAmount })).toBe('7.65432')
expect(formatNumber({ input: 7.60000054321, type: NumberType.SwapTradeAmount })).toBe('7.60')
expect(formatNumber({ input: 7.6, type: NumberType.SwapTradeAmount })).toBe('7.60')
expect(formatNumber({ input: 7, type: NumberType.SwapTradeAmount })).toBe('7.00')
expect(formatNumber({ input: 0.987654321, type: NumberType.SwapTradeAmount })).toBe('0.98765')
expect(formatNumber({ input: 0.9, type: NumberType.SwapTradeAmount })).toBe('0.90')
expect(formatNumber({ input: 0.901000123, type: NumberType.SwapTradeAmount })).toBe('0.901')
expect(formatNumber({ input: 0.000000001, type: NumberType.SwapTradeAmount })).toBe('0.000000001')
expect(formatNumber({ input: 0, type: NumberType.SwapTradeAmount })).toBe('0')
})
it('formats NFT numbers correctly', () => {
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234567000000000, type: NumberType.NFTTokenFloorPrice })).toBe('>999T')
expect(formatNumber({ input: 1002345, type: NumberType.NFTTokenFloorPrice })).toBe('1M')
expect(formatNumber({ input: 1234, type: NumberType.NFTTokenFloorPrice })).toBe('1.23K')
expect(formatNumber({ input: 12.34467, type: NumberType.NFTTokenFloorPrice })).toBe('12.34')
expect(formatNumber({ input: 12.1, type: NumberType.NFTTokenFloorPrice })).toBe('12.1')
expect(formatNumber({ input: 0.00909, type: NumberType.NFTTokenFloorPrice })).toBe('0.009')
expect(formatNumber({ input: 0.09001, type: NumberType.NFTTokenFloorPrice })).toBe('0.09')
expect(formatNumber({ input: 0.00099, type: NumberType.NFTTokenFloorPrice })).toBe('<0.001')
expect(formatNumber({ input: 0, type: NumberType.NFTTokenFloorPrice })).toBe('0')
expect(formatNumber({ input: 12.1, type: NumberType.NFTTokenFloorPriceTrailingZeros })).toBe('12.10')
expect(formatNumber({ input: 0.09001, type: NumberType.NFTTokenFloorPriceTrailingZeros })).toBe('0.090')
expect(formatNumber({ input: 0.987654321, type: NumberType.NFTCollectionStats })).toBe('1')
expect(formatNumber({ input: 0.9, type: NumberType.NFTCollectionStats })).toBe('1')
expect(formatNumber({ input: 76543.21, type: NumberType.NFTCollectionStats })).toBe('76.5K')
expect(formatNumber({ input: 7.60000054321, type: NumberType.NFTCollectionStats })).toBe('8')
expect(formatNumber({ input: 1234567890, type: NumberType.NFTCollectionStats })).toBe('1.2B')
expect(formatNumber({ input: 1234567000000000, type: NumberType.NFTCollectionStats })).toBe('1234.6T')
})
it('formats NFT numbers correctly with brazilian portugese locale and braziliean real currency', () => {
mocked(useActiveLocale).mockReturnValue('pt-Br')
mocked(useActiveLocalCurrency).mockReturnValue(Currency.Brl)
const { formatNumber } = renderHook(() => useFormatter()).result.current
expect(formatNumber({ input: 1234567000000000, type: NumberType.NFTTokenFloorPrice })).toBe('>999\xa0tri')
expect(formatNumber({ input: 1002345, type: NumberType.NFTTokenFloorPrice })).toBe('1\xa0mi')
expect(formatNumber({ input: 1234, type: NumberType.NFTTokenFloorPrice })).toBe('1,23\xa0mil')
expect(formatNumber({ input: 12.34467, type: NumberType.NFTTokenFloorPrice })).toBe('12,34')
expect(formatNumber({ input: 12.1, type: NumberType.NFTTokenFloorPrice })).toBe('12,1')
expect(formatNumber({ input: 0.00909, type: NumberType.NFTTokenFloorPrice })).toBe('0,009')
expect(formatNumber({ input: 0.09001, type: NumberType.NFTTokenFloorPrice })).toBe('0,09')
expect(formatNumber({ input: 0.00099, type: NumberType.NFTTokenFloorPrice })).toBe('<0,001')
expect(formatNumber({ input: 0, type: NumberType.NFTTokenFloorPrice })).toBe('0')
expect(formatNumber({ input: 12.1, type: NumberType.NFTTokenFloorPriceTrailingZeros })).toBe('12,10')
expect(formatNumber({ input: 0.09001, type: NumberType.NFTTokenFloorPriceTrailingZeros })).toBe('0,090')
expect(formatNumber({ input: 0.987654321, type: NumberType.NFTCollectionStats })).toBe('1')
expect(formatNumber({ input: 0.9, type: NumberType.NFTCollectionStats })).toBe('1')
expect(formatNumber({ input: 76543.21, type: NumberType.NFTCollectionStats })).toBe('76,5\xa0mil')
expect(formatNumber({ input: 7.60000054321, type: NumberType.NFTCollectionStats })).toBe('8')
expect(formatNumber({ input: 1234567890, type: NumberType.NFTCollectionStats })).toBe('1,2\xa0bi')
expect(formatNumber({ input: 1234567000000000, type: NumberType.NFTCollectionStats })).toBe('1234,6\xa0tri')
})
}) })
describe('formatUSDPrice', () => { describe('formatUSDPrice', () => {
it('should correctly format 0.000000009876', () => { beforeEach(() => {
expect(formatUSDPrice(0.000000009876)).toBe('<$0.00000001') mocked(useLocalCurrencyConversionRate).mockReturnValue({ data: 1.0, isLoading: false })
mocked(useCurrencyConversionFlagEnabled).mockReturnValue(true)
}) })
it('should correctly format 0.00001231', () => {
expect(formatUSDPrice(0.00001231)).toBe('$0.0000123') it('format fiat price correctly', () => {
const { formatFiatPrice } = renderHook(() => useFormatter()).result.current
expect(formatFiatPrice({ price: 0.000000009876 })).toBe('<$0.00000001')
expect(formatFiatPrice({ price: 0.00001231 })).toBe('$0.0000123')
expect(formatFiatPrice({ price: 0.001231 })).toBe('$0.00123')
expect(formatFiatPrice({ price: 0.0 })).toBe('$0.00')
expect(formatFiatPrice({ price: 0 })).toBe('$0.00')
expect(formatFiatPrice({ price: 1.048942 })).toBe('$1.05')
expect(formatFiatPrice({ price: 0.10235 })).toBe('$0.102')
expect(formatFiatPrice({ price: 1_234.5678 })).toBe('$1,234.57')
expect(formatFiatPrice({ price: 1_234_567.891 })).toBe('$1.23M')
expect(formatFiatPrice({ price: 1_000_000_000_000 })).toBe('$1.00T')
expect(formatFiatPrice({ price: 1_000_000_000_000_000 })).toBe('$1000.00T')
}) })
it('should correctly format 0.001231', () => {
expect(formatUSDPrice(0.001231)).toBe('$0.00123') it('format fiat price correctly in euros with french locale', () => {
}) mocked(useActiveLocalCurrency).mockReturnValue(Currency.Eur)
it('should correctly format 0.0', () => { mocked(useActiveLocale).mockReturnValue('fr-FR')
expect(formatUSDPrice(0.0)).toBe('$0.00') const { formatFiatPrice } = renderHook(() => useFormatter()).result.current
})
it('should correctly format 0', () => { expect(formatFiatPrice({ price: 0.000000009876 })).toBe('<0,00000001\xa0€')
expect(formatUSDPrice(0)).toBe('$0.00') expect(formatFiatPrice({ price: 0.00001231 })).toBe('0,0000123\xa0€')
}) expect(formatFiatPrice({ price: 0.001231 })).toBe('0,00123\xa0€')
it('should correctly format 1.048942', () => { expect(formatFiatPrice({ price: 0.0 })).toBe('0,00\xa0€')
expect(formatUSDPrice(1.048942)).toBe('$1.05') expect(formatFiatPrice({ price: 0 })).toBe('0,00\xa0€')
}) expect(formatFiatPrice({ price: 1.048942 })).toBe('1,05\xa0€')
it('should correctly format 0.10235', () => { expect(formatFiatPrice({ price: 0.10235 })).toBe('0,102\xa0€')
expect(formatUSDPrice(0.10235)).toBe('$0.102') expect(formatFiatPrice({ price: 1_234.5678 })).toBe('1\u202f234,57\xa0€')
}) expect(formatFiatPrice({ price: 1_234_567.891 })).toBe('1,23\xa0M\xa0€')
it('should correctly format 1234.5678', () => { expect(formatFiatPrice({ price: 1_000_000_000_000 })).toBe('1,00\xa0Bn\xa0€')
expect(formatUSDPrice(1_234.5678)).toBe('$1,234.57') expect(formatFiatPrice({ price: 1_000_000_000_000_000 })).toBe('1000,00\xa0Bn\xa0€')
})
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', () => { describe('formatPriceImpact', () => {
beforeEach(() => {
mocked(useLocalCurrencyConversionRate).mockReturnValue({ data: 1.0, isLoading: false })
mocked(useCurrencyConversionFlagEnabled).mockReturnValue(true)
})
it('should correctly format undefined', () => { it('should correctly format undefined', () => {
const { formatPriceImpact } = renderHook(() => useFormatter()).result.current
expect(formatPriceImpact(undefined)).toBe('-') expect(formatPriceImpact(undefined)).toBe('-')
}) })
it('correctly formats a percent with 3 significant digits', () => { it('correctly formats a percent with 3 significant digits', () => {
const { formatPriceImpact } = renderHook(() => useFormatter()).result.current
expect(formatPriceImpact(new Percent(-1, 100000))).toBe('0.001%') expect(formatPriceImpact(new Percent(-1, 100000))).toBe('0.001%')
expect(formatPriceImpact(new Percent(-1, 1000))).toBe('0.100%') expect(formatPriceImpact(new Percent(-1, 1000))).toBe('0.100%')
expect(formatPriceImpact(new Percent(-1, 100))).toBe('1.000%') expect(formatPriceImpact(new Percent(-1, 100))).toBe('1.000%')
expect(formatPriceImpact(new Percent(-1, 10))).toBe('10.000%') expect(formatPriceImpact(new Percent(-1, 10))).toBe('10.000%')
expect(formatPriceImpact(new Percent(-1, 1))).toBe('100.000%') expect(formatPriceImpact(new Percent(-1, 1))).toBe('100.000%')
}) })
it('correctly formats a percent with 3 significant digits with french locale', () => {
mocked(useActiveLocale).mockReturnValue('fr-FR')
const { formatPriceImpact } = renderHook(() => useFormatter()).result.current
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', () => { describe('formatSlippage', () => {
beforeEach(() => {
mocked(useLocalCurrencyConversionRate).mockReturnValue({ data: 1.0, isLoading: false })
mocked(useCurrencyConversionFlagEnabled).mockReturnValue(true)
})
it('should correctly format undefined', () => { it('should correctly format undefined', () => {
const { formatSlippage } = renderHook(() => useFormatter()).result.current
expect(formatSlippage(undefined)).toBe('-') expect(formatSlippage(undefined)).toBe('-')
}) })
it('correctly formats a percent with 3 significant digits', () => { it('correctly formats a percent with 3 significant digits', () => {
const { formatSlippage } = renderHook(() => useFormatter()).result.current
expect(formatSlippage(new Percent(1, 100000))).toBe('0.001%') expect(formatSlippage(new Percent(1, 100000))).toBe('0.001%')
expect(formatSlippage(new Percent(1, 1000))).toBe('0.100%') expect(formatSlippage(new Percent(1, 1000))).toBe('0.100%')
expect(formatSlippage(new Percent(1, 100))).toBe('1.000%') expect(formatSlippage(new Percent(1, 100))).toBe('1.000%')
expect(formatSlippage(new Percent(1, 10))).toBe('10.000%') expect(formatSlippage(new Percent(1, 10))).toBe('10.000%')
expect(formatSlippage(new Percent(1, 1))).toBe('100.000%') expect(formatSlippage(new Percent(1, 1))).toBe('100.000%')
}) })
})
describe('currencyAmountToPreciseFloat', () => { it('correctly formats a percent with 3 significant digits with french locale', () => {
it('small number', () => { mocked(useActiveLocale).mockReturnValue('fr-FR')
const currencyAmount = CurrencyAmount.fromFractionalAmount(USDC_MAINNET, '20000', '7') const { formatSlippage } = renderHook(() => useFormatter()).result.current
expect(currencyAmountToPreciseFloat(currencyAmount)).toEqual(0.00285714)
})
it('tiny number', () => {
const currencyAmount = CurrencyAmount.fromFractionalAmount(USDC_MAINNET, '2', '7')
expect(currencyAmountToPreciseFloat(currencyAmount)).toEqual(0.000000285714)
})
it('lots of decimals', () => {
const currencyAmount = CurrencyAmount.fromFractionalAmount(USDC_MAINNET, '200000000', '7')
expect(currencyAmountToPreciseFloat(currencyAmount)).toEqual(28.571428)
})
it('integer', () => {
const currencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '20000000')
expect(currencyAmountToPreciseFloat(currencyAmount)).toEqual(20.0)
})
})
describe('priceToPreciseFloat', () => { expect(formatSlippage(new Percent(1, 100000))).toBe('0,001%')
it('small number', () => { expect(formatSlippage(new Percent(1, 1000))).toBe('0,100%')
const price = new Price(WBTC, USDC_MAINNET, 1234, 1) expect(formatSlippage(new Percent(1, 100))).toBe('1,000%')
expect(priceToPreciseFloat(price)).toEqual(0.0810373) expect(formatSlippage(new Percent(1, 10))).toBe('10,000%')
}) expect(formatSlippage(new Percent(1, 1))).toBe('100,000%')
it('tiny number', () => {
const price = new Price(WBTC, USDC_MAINNET, 12345600, 1)
expect(priceToPreciseFloat(price)).toEqual(0.00000810005)
})
it('lots of decimals', () => {
const price = new Price(WBTC, USDC_MAINNET, 123, 7)
expect(priceToPreciseFloat(price)).toEqual(5.691056911)
})
it('integer', () => {
const price = new Price(WBTC, USDC_MAINNET, 1, 7)
expect(priceToPreciseFloat(price)).toEqual(700)
}) })
}) })
describe('formatReviewSwapCurrencyAmount', () => { describe('formatReviewSwapCurrencyAmount', () => {
beforeEach(() => {
mocked(useLocalCurrencyConversionRate).mockReturnValue({ data: 1.0, isLoading: false })
mocked(useCurrencyConversionFlagEnabled).mockReturnValue(true)
})
it('should use TokenTx formatting under a default length', () => { it('should use TokenTx formatting under a default length', () => {
const { formatReviewSwapCurrencyAmount } = renderHook(() => useFormatter()).result.current
const currencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '2000000000') // 2,000 USDC const currencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '2000000000') // 2,000 USDC
expect(formatReviewSwapCurrencyAmount(currencyAmount)).toBe('2,000') expect(formatReviewSwapCurrencyAmount(currencyAmount)).toBe('2,000')
}) })
it('should use TokenTx formatting under a default length with french locales', () => {
mocked(useActiveLocale).mockReturnValue('fr-FR')
const { formatReviewSwapCurrencyAmount } = renderHook(() => useFormatter()).result.current
const currencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '2000000000') // 2,000 USDC
expect(formatReviewSwapCurrencyAmount(currencyAmount)).toBe('2\u202f000')
})
it('should use SwapTradeAmount formatting over the default length', () => { it('should use SwapTradeAmount formatting over the default length', () => {
const { formatReviewSwapCurrencyAmount } = renderHook(() => useFormatter()).result.current
const currencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '2000000000000') // 2,000,000 USDC
expect(formatReviewSwapCurrencyAmount(currencyAmount)).toBe('2000000')
})
it('should use SwapTradeAmount formatting over the default length with french locales', () => {
mocked(useActiveLocale).mockReturnValue('fr-FR')
const { formatReviewSwapCurrencyAmount } = renderHook(() => useFormatter()).result.current
const currencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '2000000000000') // 2,000,000 USDC const currencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '2000000000000') // 2,000,000 USDC
expect(formatReviewSwapCurrencyAmount(currencyAmount)).toBe('2000000') expect(formatReviewSwapCurrencyAmount(currencyAmount)).toBe('2000000')
}) })

@ -1,4 +1,4 @@
import { Currency, CurrencyAmount, Percent, Price } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent, Price, Token } from '@uniswap/sdk-core'
import { import {
DEFAULT_LOCAL_CURRENCY, DEFAULT_LOCAL_CURRENCY,
LOCAL_CURRENCY_SYMBOL_DISPLAY_TYPE, LOCAL_CURRENCY_SYMBOL_DISPLAY_TYPE,
@ -12,6 +12,7 @@ import { useActiveLocalCurrency } from 'hooks/useActiveLocalCurrency'
import { useActiveLocale } from 'hooks/useActiveLocale' import { useActiveLocale } from 'hooks/useActiveLocale'
import usePrevious from 'hooks/usePrevious' import usePrevious from 'hooks/usePrevious'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { Bound } from 'state/mint/v3/actions'
type Nullish<T> = T | null | undefined type Nullish<T> = T | null | undefined
type NumberFormatOptions = Intl.NumberFormatOptions type NumberFormatOptions = Intl.NumberFormatOptions
@ -392,7 +393,7 @@ interface FormatNumberOptions {
conversionRate?: number conversionRate?: number
} }
export function formatNumber({ function formatNumber({
input, input,
type = NumberType.TokenNonTx, type = NumberType.TokenNonTx,
placeholder = '-', placeholder = '-',
@ -434,7 +435,7 @@ interface FormatCurrencyAmountOptions {
conversionRate?: number conversionRate?: number
} }
export function formatCurrencyAmount({ function formatCurrencyAmount({
amount, amount,
type = NumberType.TokenNonTx, type = NumberType.TokenNonTx,
placeholder, placeholder,
@ -452,7 +453,7 @@ export function formatCurrencyAmount({
}) })
} }
export function formatPriceImpact(priceImpact: Percent | undefined, locale: SupportedLocale = DEFAULT_LOCALE): string { function formatPriceImpact(priceImpact: Percent | undefined, locale: SupportedLocale = DEFAULT_LOCALE): string {
if (!priceImpact) return '-' if (!priceImpact) return '-'
return `${Number(priceImpact.multiply(-1).toFixed(3)).toLocaleString(locale, { return `${Number(priceImpact.multiply(-1).toFixed(3)).toLocaleString(locale, {
@ -462,13 +463,17 @@ export function formatPriceImpact(priceImpact: Percent | undefined, locale: Supp
})}%` })}%`
} }
export function formatSlippage(slippage: Percent | undefined) { function formatSlippage(slippage: Percent | undefined, locale: SupportedLocale = DEFAULT_LOCALE) {
if (!slippage) return '-' if (!slippage) return '-'
return `${slippage.toFixed(3)}%` return `${Number(slippage.toFixed(3)).toLocaleString(locale, {
minimumFractionDigits: 3,
maximumFractionDigits: 3,
useGrouping: false,
})}%`
} }
interface FormatPriceProps { interface FormatPriceOptions {
price: Nullish<Price<Currency, Currency>> price: Nullish<Price<Currency, Currency>>
type: FormatterType type: FormatterType
locale?: SupportedLocale locale?: SupportedLocale
@ -476,13 +481,13 @@ interface FormatPriceProps {
conversionRate?: number conversionRate?: number
} }
export function formatPrice({ function formatPrice({
price, price,
type = NumberType.FiatTokenPrice, type = NumberType.FiatTokenPrice,
locale = DEFAULT_LOCALE, locale = DEFAULT_LOCALE,
localCurrency = DEFAULT_LOCAL_CURRENCY, localCurrency = DEFAULT_LOCAL_CURRENCY,
conversionRate, conversionRate,
}: FormatPriceProps): string { }: FormatPriceOptions): string {
if (price === null || price === undefined) { if (price === null || price === undefined) {
return '-' return '-'
} }
@ -490,45 +495,80 @@ export function formatPrice({
return formatNumber({ input: parseFloat(price.toSignificant()), type, locale, localCurrency, conversionRate }) return formatNumber({ input: parseFloat(price.toSignificant()), type, locale, localCurrency, conversionRate })
} }
export function formatNumberOrString(price: Nullish<number | string>, type: FormatterType): string { interface FormatTickPriceOptions {
if (price === null || price === undefined) return '-' price?: Price<Token, Token>
if (typeof price === 'string') return formatNumber({ input: parseFloat(price), type }) atLimit: { [bound in Bound]?: boolean | undefined }
return formatNumber({ input: price, type }) direction: Bound
placeholder?: string
numberType?: NumberType
locale?: SupportedLocale
localCurrency?: SupportedLocalCurrency
conversionRate?: number
} }
export function formatUSDPrice(price: Nullish<number | string>, type: NumberType = NumberType.FiatTokenPrice): string { function formatTickPrice({
return formatNumberOrString(price, type) price,
} atLimit,
direction,
/** Formats USD and non-USD prices */ placeholder,
export function formatFiatPrice(price: Nullish<number>, currency = 'USD'): string { numberType,
if (price === null || price === undefined) return '-' locale,
return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(price) localCurrency,
} conversionRate,
}: FormatTickPriceOptions) {
// Convert [CurrencyAmount] to number with necessary precision for price formatting. if (atLimit[direction]) {
export const currencyAmountToPreciseFloat = (currencyAmount: CurrencyAmount<Currency> | undefined) => { return direction === Bound.LOWER ? '0' : '∞'
if (!currencyAmount) return undefined
const floatForLargerNumbers = parseFloat(currencyAmount.toExact())
if (floatForLargerNumbers < 0.1) {
return parseFloat(currencyAmount.toSignificant(6))
} }
return floatForLargerNumbers
if (!price && placeholder !== undefined) {
return placeholder
}
return formatPrice({ price, type: numberType ?? NumberType.TokenNonTx, locale, localCurrency, conversionRate })
} }
// Convert [Price] to number with necessary precision for price formatting. interface FormatNumberOrStringOptions {
export const priceToPreciseFloat = (price: Price<Currency, Currency> | undefined) => { input: Nullish<number | string>
if (!price) return undefined type: FormatterType
const floatForLargerNumbers = parseFloat(price.toFixed(9)) locale?: SupportedLocale
if (floatForLargerNumbers < 0.1) { localCurrency?: SupportedLocalCurrency
return parseFloat(price.toSignificant(6)) conversionRate?: number
} }
return floatForLargerNumbers
function formatNumberOrString({
input,
type,
locale,
localCurrency,
conversionRate,
}: FormatNumberOrStringOptions): string {
if (input === null || input === undefined) return '-'
if (typeof input === 'string')
return formatNumber({ input: parseFloat(input), type, locale, localCurrency, conversionRate })
return formatNumber({ input, type, locale, localCurrency, conversionRate })
}
interface FormatFiatPriceOptions {
price: Nullish<number | string>
type?: FormatterType
locale?: SupportedLocale
localCurrency?: SupportedLocalCurrency
conversionRate?: number
}
function formatFiatPrice({
price,
type = NumberType.FiatTokenPrice,
locale,
localCurrency,
conversionRate,
}: FormatFiatPriceOptions): string {
return formatNumberOrString({ input: price, type, locale, localCurrency, conversionRate })
} }
const MAX_AMOUNT_STR_LENGTH = 9 const MAX_AMOUNT_STR_LENGTH = 9
export function formatReviewSwapCurrencyAmount( function formatReviewSwapCurrencyAmount(
amount: CurrencyAmount<Currency>, amount: CurrencyAmount<Currency>,
locale: SupportedLocale = DEFAULT_LOCALE locale: SupportedLocale = DEFAULT_LOCALE
): string { ): string {
@ -539,7 +579,7 @@ export function formatReviewSwapCurrencyAmount(
return formattedAmount return formattedAmount
} }
export function useFormatterLocales(): { function useFormatterLocales(): {
formatterLocale: SupportedLocale formatterLocale: SupportedLocale
formatterLocalCurrency: SupportedLocalCurrency formatterLocalCurrency: SupportedLocalCurrency
} { } {
@ -620,7 +660,7 @@ export function useFormatter() {
) )
const formatPriceWithLocales = useCallback( const formatPriceWithLocales = useCallback(
(options: Omit<FormatPriceProps, LocalesType>) => (options: Omit<FormatPriceOptions, LocalesType>) =>
formatPrice({ formatPrice({
...options, ...options,
locale: formatterLocale, locale: formatterLocale,
@ -640,20 +680,66 @@ export function useFormatter() {
[formatterLocale] [formatterLocale]
) )
const formatSlippageWithLocales = useCallback(
(slippage: Percent | undefined) => formatSlippage(slippage, formatterLocale),
[formatterLocale]
)
const formatTickPriceWithLocales = useCallback(
(options: Omit<FormatTickPriceOptions, LocalesType>) =>
formatTickPrice({
...options,
locale: formatterLocale,
localCurrency: currencyToFormatWith,
conversionRate: localCurrencyConversionRateToFormatWith,
}),
[currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith]
)
const formatNumberOrStringWithLocales = useCallback(
(options: Omit<FormatNumberOrStringOptions, LocalesType>) =>
formatNumberOrString({
...options,
locale: formatterLocale,
localCurrency: currencyToFormatWith,
conversionRate: localCurrencyConversionRateToFormatWith,
}),
[currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith]
)
const formatFiatPriceWithLocales = useCallback(
(options: Omit<FormatFiatPriceOptions, LocalesType>) =>
formatFiatPrice({
...options,
locale: formatterLocale,
localCurrency: currencyToFormatWith,
conversionRate: localCurrencyConversionRateToFormatWith,
}),
[currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith]
)
return useMemo( return useMemo(
() => ({ () => ({
formatCurrencyAmount: formatCurrencyAmountWithLocales, formatCurrencyAmount: formatCurrencyAmountWithLocales,
formatFiatPrice: formatFiatPriceWithLocales,
formatNumber: formatNumberWithLocales, formatNumber: formatNumberWithLocales,
formatNumberOrString: formatNumberOrStringWithLocales,
formatPrice: formatPriceWithLocales, formatPrice: formatPriceWithLocales,
formatPriceImpact: formatPriceImpactWithLocales, formatPriceImpact: formatPriceImpactWithLocales,
formatReviewSwapCurrencyAmount: formatReviewSwapCurrencyAmountWithLocales, formatReviewSwapCurrencyAmount: formatReviewSwapCurrencyAmountWithLocales,
formatSlippage: formatSlippageWithLocales,
formatTickPrice: formatTickPriceWithLocales,
}), }),
[ [
formatCurrencyAmountWithLocales, formatCurrencyAmountWithLocales,
formatFiatPriceWithLocales,
formatNumberOrStringWithLocales,
formatNumberWithLocales, formatNumberWithLocales,
formatPriceImpactWithLocales, formatPriceImpactWithLocales,
formatPriceWithLocales, formatPriceWithLocales,
formatReviewSwapCurrencyAmountWithLocales, formatReviewSwapCurrencyAmountWithLocales,
formatSlippageWithLocales,
formatTickPriceWithLocales,
] ]
) )
} }

@ -1,24 +0,0 @@
import { Price, Token } from '@uniswap/sdk-core'
import { formatPrice, NumberType } from 'utils/formatNumbers'
import { Bound } from '../state/mint/v3/actions'
interface FormatTickPriceArgs {
price?: Price<Token, Token>
atLimit: { [bound in Bound]?: boolean | undefined }
direction: Bound
placeholder?: string
numberType?: NumberType
}
export function formatTickPrice({ price, atLimit, direction, placeholder, numberType }: FormatTickPriceArgs) {
if (atLimit[direction]) {
return direction === Bound.LOWER ? '0' : '∞'
}
if (!price && placeholder !== undefined) {
return placeholder
}
return formatPrice({ price, type: numberType ?? NumberType.TokenNonTx })
}