Commit 91c20135 authored by Jack Short's avatar Jack Short Committed by GitHub

chore: only exposing useFormatter (#7308)

parent cf09e809
...@@ -2,6 +2,7 @@ import { TransactionStatus, useActivityQuery } from 'graphql/data/__generated__/ ...@@ -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 = ...@@ -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) { ...@@ -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 { ...@@ -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', () => { ...@@ -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', () => { ...@@ -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', () => { ...@@ -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', () => { ...@@ -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', () => { ...@@ -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', () => { ...@@ -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', () => { ...@@ -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', () => { ...@@ -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' ...@@ -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 { ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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< ...@@ -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 ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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 { ...@@ -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 ...@@ -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 = { ...@@ -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({ ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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' ...@@ -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 { ...@@ -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, ...@@ -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' ...@@ -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({ ...@@ -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 ...@@ -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' ...@@ -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({ ...@@ -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' ...@@ -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 = ({ ...@@ -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' ...@@ -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 { ...@@ -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 } ...@@ -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' ...@@ -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 { ...@@ -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 ...@@ -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' ...@@ -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({ ...@@ -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 { ...@@ -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', () => { ...@@ -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', () => { ...@@ -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', () => { ...@@ -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' ...@@ -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 { ...@@ -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 ...@@ -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' ...@@ -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() { ...@@ -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)
......
This diff is collapsed.
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' ...@@ -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 { ...@@ -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 { ...@@ -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({ ...@@ -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 ...@@ -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 { ...@@ -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({ ...@@ -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
}
function formatTickPrice({
price,
atLimit,
direction,
placeholder,
numberType,
locale,
localCurrency,
conversionRate,
}: FormatTickPriceOptions) {
if (atLimit[direction]) {
return direction === Bound.LOWER ? '0' : ''
}
if (!price && placeholder !== undefined) {
return placeholder
}
return formatPrice({ price, type: numberType ?? NumberType.TokenNonTx, locale, localCurrency, conversionRate })
} }
export function formatUSDPrice(price: Nullish<number | string>, type: NumberType = NumberType.FiatTokenPrice): string { interface FormatNumberOrStringOptions {
return formatNumberOrString(price, type) input: Nullish<number | string>
type: FormatterType
locale?: SupportedLocale
localCurrency?: SupportedLocalCurrency
conversionRate?: number
} }
/** Formats USD and non-USD prices */ function formatNumberOrString({
export function formatFiatPrice(price: Nullish<number>, currency = 'USD'): string { input,
if (price === null || price === undefined) return '-' type,
return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(price) 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 })
} }
// Convert [CurrencyAmount] to number with necessary precision for price formatting. interface FormatFiatPriceOptions {
export const currencyAmountToPreciseFloat = (currencyAmount: CurrencyAmount<Currency> | undefined) => { price: Nullish<number | string>
if (!currencyAmount) return undefined type?: FormatterType
const floatForLargerNumbers = parseFloat(currencyAmount.toExact()) locale?: SupportedLocale
if (floatForLargerNumbers < 0.1) { localCurrency?: SupportedLocalCurrency
return parseFloat(currencyAmount.toSignificant(6)) conversionRate?: number
}
return floatForLargerNumbers
} }
// Convert [Price] to number with necessary precision for price formatting. function formatFiatPrice({
export const priceToPreciseFloat = (price: Price<Currency, Currency> | undefined) => { price,
if (!price) return undefined type = NumberType.FiatTokenPrice,
const floatForLargerNumbers = parseFloat(price.toFixed(9)) locale,
if (floatForLargerNumbers < 0.1) { localCurrency,
return parseFloat(price.toSignificant(6)) conversionRate,
} }: FormatFiatPriceOptions): string {
return floatForLargerNumbers 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( ...@@ -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() { ...@@ -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() { ...@@ -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,
] ]
) )
} }
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 })
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment