fix: standardize decimals for token prices in explore, token details, search (#4821)
* token prices in explore, token details, search * use correct dollar format function * additional price corrections * remove oopsies * changes in notion from andy * use currencyAmountToPreciseFloat everywhere
This commit is contained in:
parent
2338255a54
commit
859258c25c
@ -14,6 +14,7 @@ import { ethNumberStandardFormatter } from 'nft/utils/currency'
|
||||
import { putCommas } from 'nft/utils/putCommas'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { formatDollar } from 'utils/formatDollarAmt'
|
||||
|
||||
import * as styles from './SearchBar.css'
|
||||
|
||||
@ -189,7 +190,7 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, traceE
|
||||
<Column className={styles.suggestionSecondaryContainer}>
|
||||
{token.priceUsd && (
|
||||
<Row gap="4">
|
||||
<Box className={styles.primaryText}>{ethNumberStandardFormatter(token.priceUsd, true)}</Box>
|
||||
<Box className={styles.primaryText}>{formatDollar(token.priceUsd, true)}</Box>
|
||||
</Row>
|
||||
)}
|
||||
{token.price24hChange && (
|
||||
|
@ -7,7 +7,7 @@ import { useStablecoinValue } from 'hooks/useStablecoinPrice'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
import { StyledInternalLink } from 'theme'
|
||||
import { formatDollarAmount } from 'utils/formatDollarAmt'
|
||||
import { currencyAmountToPreciseFloat, formatDollar } from 'utils/formatDollarAmt'
|
||||
|
||||
const BalancesCard = styled.div`
|
||||
box-shadow: ${({ theme }) => theme.shallowShadow};
|
||||
@ -50,14 +50,14 @@ const BalanceRowLink = styled(StyledInternalLink)`
|
||||
color: unset;
|
||||
`
|
||||
|
||||
function BalanceRow({ currency, formattedBalance, formattedUSDValue, href }: BalanceRowData) {
|
||||
function BalanceRow({ currency, formattedBalance, usdValue, href }: BalanceRowData) {
|
||||
const content = (
|
||||
<TotalBalance key={currency.wrapped.address}>
|
||||
<TotalBalanceItem>
|
||||
<CurrencyLogo currency={currency} />
|
||||
{formattedBalance} {currency?.symbol}
|
||||
</TotalBalanceItem>
|
||||
<TotalBalanceItem>{formatDollarAmount(formattedUSDValue === 0 ? undefined : formattedUSDValue)}</TotalBalanceItem>
|
||||
<TotalBalanceItem>{formatDollar(usdValue === 0 ? undefined : usdValue, true)}</TotalBalanceItem>
|
||||
</TotalBalance>
|
||||
)
|
||||
if (href) {
|
||||
@ -69,7 +69,7 @@ function BalanceRow({ currency, formattedBalance, formattedUSDValue, href }: Bal
|
||||
interface BalanceRowData {
|
||||
currency: Currency
|
||||
formattedBalance: number
|
||||
formattedUSDValue: number | undefined
|
||||
usdValue: number | undefined
|
||||
href?: string
|
||||
}
|
||||
export interface BalanceSummaryProps {
|
||||
@ -79,8 +79,8 @@ export interface BalanceSummaryProps {
|
||||
}
|
||||
|
||||
export default function BalanceSummary({ tokenAmount, nativeCurrencyAmount, isNative }: BalanceSummaryProps) {
|
||||
const balanceUsdValue = useStablecoinValue(tokenAmount)?.toFixed(2)
|
||||
const nativeBalanceUsdValue = useStablecoinValue(nativeCurrencyAmount)?.toFixed(2)
|
||||
const balanceUsdValue = useStablecoinValue(tokenAmount)
|
||||
const nativeBalanceUsdValue = useStablecoinValue(nativeCurrencyAmount)
|
||||
|
||||
const { chainName } = useParams<{ chainName?: string }>()
|
||||
const pageChainName = validateUrlChainParam(chainName).toLowerCase()
|
||||
@ -105,7 +105,7 @@ export default function BalanceSummary({ tokenAmount, nativeCurrencyAmount, isNa
|
||||
const tokenData: BalanceRowData = {
|
||||
currency: tokenAmount.currency,
|
||||
formattedBalance: formatToDecimal(tokenAmount, Math.min(tokenAmount.currency.decimals, 2)),
|
||||
formattedUSDValue: balanceUsdValue ? parseFloat(balanceUsdValue) : undefined,
|
||||
usdValue: balanceUsdValue ? currencyAmountToPreciseFloat(balanceUsdValue) : undefined,
|
||||
}
|
||||
if (isNative) {
|
||||
tokenData.href = `/tokens/${pageChainName}/${tokenAmount.currency.address}`
|
||||
@ -116,7 +116,7 @@ export default function BalanceSummary({ tokenAmount, nativeCurrencyAmount, isNa
|
||||
const nativeData: BalanceRowData = {
|
||||
currency: nativeCurrencyAmount.currency,
|
||||
formattedBalance: formatToDecimal(nativeCurrencyAmount, Math.min(nativeCurrencyAmount.currency.decimals, 2)),
|
||||
formattedUSDValue: nativeBalanceUsdValue ? parseFloat(nativeBalanceUsdValue) : undefined,
|
||||
usdValue: nativeBalanceUsdValue ? currencyAmountToPreciseFloat(nativeBalanceUsdValue) : undefined,
|
||||
}
|
||||
if (isNative) {
|
||||
currencies.unshift(nativeData)
|
||||
|
@ -3,7 +3,7 @@ import { formatToDecimal } from 'analytics/utils'
|
||||
import { useStablecoinValue } from 'hooks/useStablecoinPrice'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
import { formatDollarAmount } from 'utils/formatDollarAmt'
|
||||
import { currencyAmountToPreciseFloat, formatDollar } from 'utils/formatDollarAmt'
|
||||
|
||||
import { SMALLEST_MOBILE_MEDIA_BREAKPOINT } from '../constants'
|
||||
import { BalanceSummaryProps } from './BalanceSummary'
|
||||
@ -87,19 +87,19 @@ export default function MobileBalanceSummaryFooter({
|
||||
nativeCurrencyAmount,
|
||||
isNative,
|
||||
}: BalanceSummaryProps) {
|
||||
const balanceUsdValue = useStablecoinValue(tokenAmount)?.toFixed(2)
|
||||
const nativeBalanceUsdValue = useStablecoinValue(nativeCurrencyAmount)?.toFixed(2)
|
||||
const balanceUsdValue = useStablecoinValue(tokenAmount)
|
||||
const nativeBalanceUsdValue = useStablecoinValue(nativeCurrencyAmount)
|
||||
|
||||
const formattedBalance = tokenAmount
|
||||
? formatToDecimal(tokenAmount, Math.min(tokenAmount.currency.decimals, 2))
|
||||
: undefined
|
||||
|
||||
const balanceUsd = balanceUsdValue ? parseFloat(balanceUsdValue) : undefined
|
||||
const balanceUsd = balanceUsdValue ? currencyAmountToPreciseFloat(balanceUsdValue) : undefined
|
||||
|
||||
const formattedNativeBalance = nativeCurrencyAmount
|
||||
? formatToDecimal(nativeCurrencyAmount, Math.min(nativeCurrencyAmount.currency.decimals, 2))
|
||||
: undefined
|
||||
const nativeBalanceUsd = nativeBalanceUsdValue ? parseFloat(nativeBalanceUsdValue) : undefined
|
||||
const nativeBalanceUsd = nativeBalanceUsdValue ? currencyAmountToPreciseFloat(nativeBalanceUsdValue) : undefined
|
||||
|
||||
if ((!tokenAmount && !nativeCurrencyAmount) || (nativeCurrencyAmount?.equalTo(0) && tokenAmount?.equalTo(0))) {
|
||||
return null
|
||||
@ -117,7 +117,7 @@ export default function MobileBalanceSummaryFooter({
|
||||
<BalanceValue>
|
||||
{formattedBalance} {tokenAmount?.currency?.symbol}
|
||||
</BalanceValue>
|
||||
<FiatValue>{formatDollarAmount(balanceUsd)}</FiatValue>
|
||||
<FiatValue>{formatDollar(balanceUsd, true)}</FiatValue>
|
||||
</BalanceTotal>
|
||||
</BalanceInfo>
|
||||
)}
|
||||
@ -128,7 +128,7 @@ export default function MobileBalanceSummaryFooter({
|
||||
<BalanceValue>
|
||||
{formattedNativeBalance} {nativeCurrencyAmount?.currency?.symbol}
|
||||
</BalanceValue>
|
||||
<FiatValue>{formatDollarAmount(nativeBalanceUsd)}</FiatValue>
|
||||
<FiatValue>{formatDollar(nativeBalanceUsd, true)}</FiatValue>
|
||||
</BalanceTotal>
|
||||
</BalanceInfo>
|
||||
)}
|
||||
|
@ -4,7 +4,7 @@ import { ReactNode } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
import { textFadeIn } from 'theme/animations'
|
||||
import { formatDollarAmount } from 'utils/formatDollarAmt'
|
||||
import { formatDollar } from 'utils/formatDollarAmt'
|
||||
|
||||
import { HEADER_DESCRIPTIONS } from '../TokenTable/TokenRow'
|
||||
import InfoTip from './InfoTip'
|
||||
@ -51,7 +51,17 @@ const Wrapper = styled.div`
|
||||
|
||||
type NumericStat = number | undefined | null
|
||||
|
||||
function Stat({ value, title, description }: { value: NumericStat; title: ReactNode; description?: ReactNode }) {
|
||||
function Stat({
|
||||
value,
|
||||
title,
|
||||
description,
|
||||
isPrice = false,
|
||||
}: {
|
||||
value: NumericStat
|
||||
title: ReactNode
|
||||
description?: ReactNode
|
||||
isPrice?: boolean
|
||||
}) {
|
||||
return (
|
||||
<StatWrapper>
|
||||
<StatTitle>
|
||||
@ -59,7 +69,7 @@ function Stat({ value, title, description }: { value: NumericStat; title: ReactN
|
||||
{description && <InfoTip text={description}></InfoTip>}
|
||||
</StatTitle>
|
||||
|
||||
<StatPrice>{value ? formatDollarAmount(value) : '-'}</StatPrice>
|
||||
<StatPrice>{formatDollar(value, isPrice)}</StatPrice>
|
||||
</StatWrapper>
|
||||
)
|
||||
}
|
||||
@ -96,8 +106,8 @@ export default function StatsSection(props: StatsSectionProps) {
|
||||
/>
|
||||
</StatPair>
|
||||
<StatPair>
|
||||
<Stat value={priceLow52W} title={<Trans>52W low</Trans>} />
|
||||
<Stat value={priceHigh52W} title={<Trans>52W high</Trans>} />
|
||||
<Stat value={priceLow52W} title={<Trans>52W low</Trans>} isPrice={true} />
|
||||
<Stat value={priceHigh52W} title={<Trans>52W high</Trans>} isPrice={true} />
|
||||
</StatPair>
|
||||
</TokenStatsSection>
|
||||
</Wrapper>
|
||||
|
@ -16,7 +16,7 @@ import { Link, useParams } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import styled, { css, useTheme } from 'styled-components/macro'
|
||||
import { ClickableStyle } from 'theme'
|
||||
import { formatDollarAmount } from 'utils/formatDollarAmt'
|
||||
import { formatDollar } from 'utils/formatDollarAmt'
|
||||
|
||||
import {
|
||||
LARGE_MEDIA_BREAKPOINT,
|
||||
@ -532,7 +532,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
|
||||
price={
|
||||
<ClickableContent>
|
||||
<PriceInfoCell>
|
||||
{token.market?.price?.value ? formatDollarAmount(token.market.price.value) : '-'}
|
||||
{token.market?.price?.value ? formatDollar(token.market.price.value, true) : '-'}
|
||||
<PercentChangeInfoCell>
|
||||
{formattedDelta}
|
||||
{arrow}
|
||||
@ -548,12 +548,12 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
|
||||
}
|
||||
marketCap={
|
||||
<ClickableContent>
|
||||
{token.market?.totalValueLocked?.value ? formatDollarAmount(token.market.totalValueLocked.value) : '-'}
|
||||
{token.market?.totalValueLocked?.value ? formatDollar(token.market.totalValueLocked.value) : '-'}
|
||||
</ClickableContent>
|
||||
}
|
||||
volume={
|
||||
<ClickableContent>
|
||||
{token.market?.volume?.value ? formatDollarAmount(token.market.volume.value) : '-'}
|
||||
{token.market?.volume?.value ? formatDollar(token.market.volume.value) : '-'}
|
||||
</ClickableContent>
|
||||
}
|
||||
sparkLine={
|
||||
|
58
src/utils/formatDollarAmt.test.ts
Normal file
58
src/utils/formatDollarAmt.test.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { AddressZero } from '@ethersproject/constants'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
|
||||
import { currencyAmountToPreciseFloat, formatDollar } from './formatDollarAmt'
|
||||
describe('currencyAmountToPreciseFloat', () => {
|
||||
it('lots of decimals', () => {
|
||||
const currencyAmount = CurrencyAmount.fromFractionalAmount(new Token(1, AddressZero, 0), 101230, 7)
|
||||
expect(currencyAmountToPreciseFloat(currencyAmount)).toEqual(14461.42857142857)
|
||||
})
|
||||
it('integer', () => {
|
||||
const currencyAmount = CurrencyAmount.fromRawAmount(new Token(1, AddressZero, 0), 101230)
|
||||
expect(currencyAmountToPreciseFloat(currencyAmount)).toEqual(101230)
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatDollar for a price', () => {
|
||||
it('undefined or null', () => {
|
||||
expect(formatDollar(undefined, true)).toEqual('-')
|
||||
expect(formatDollar(null, true)).toEqual('-')
|
||||
})
|
||||
it('0', () => {
|
||||
expect(formatDollar(0, true)).toEqual('$0.00')
|
||||
})
|
||||
it('< 0.000001', () => {
|
||||
expect(formatDollar(0.00000000011231231432, true)).toEqual('$1.12e-10')
|
||||
})
|
||||
it('num >= 0.000001 && num < 0.1', () => {
|
||||
expect(formatDollar(0.00123123124, true)).toEqual('$0.00123')
|
||||
})
|
||||
it('num >= 0.1 && num < 1.05', () => {
|
||||
expect(formatDollar(0.812831, true)).toEqual('$0.813')
|
||||
})
|
||||
it('number is greater than 1.05', () => {
|
||||
expect(formatDollar(102312.408, true)).toEqual('$102312.41')
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatDollar for a non-price amount', () => {
|
||||
it('undefined or null', () => {
|
||||
expect(formatDollar(undefined)).toEqual('-')
|
||||
expect(formatDollar(null)).toEqual('-')
|
||||
})
|
||||
it('0', () => {
|
||||
expect(formatDollar(0)).toEqual('0')
|
||||
})
|
||||
it('< 0.000001', () => {
|
||||
expect(formatDollar(0.0000000001)).toEqual('$<0.000001')
|
||||
})
|
||||
it('num >= 0.000001 && num < 0.1', () => {
|
||||
expect(formatDollar(0.00123123124)).toEqual('$0.00123')
|
||||
})
|
||||
it('num >= 0.1 && num < 1.05', () => {
|
||||
expect(formatDollar(0.812831)).toEqual('$0.813')
|
||||
})
|
||||
it('number is greater than 1.05', () => {
|
||||
expect(formatDollar(102312.408)).toEqual('$102.31K')
|
||||
})
|
||||
})
|
69
src/utils/formatDollarAmt.ts
Normal file
69
src/utils/formatDollarAmt.ts
Normal file
@ -0,0 +1,69 @@
|
||||
/* Copied from Uniswap/v-3: https://github.com/Uniswap/v3-info/blob/master/src/utils/numbers.ts */
|
||||
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
|
||||
import numbro from 'numbro'
|
||||
|
||||
// Convert [CurrencyAmount] to float with no loss of precision / information.
|
||||
export const currencyAmountToPreciseFloat = (currencyAmount: CurrencyAmount<Currency>) => {
|
||||
return Number(currencyAmount.numerator) / Number(currencyAmount.denominator)
|
||||
}
|
||||
|
||||
// Using a currency library here in case we want to add more in future.
|
||||
export const formatDollar = (num: number | undefined | null, isPrice = false, digits = 2, round = true) => {
|
||||
if (isPrice) {
|
||||
if (num === 0) return '$0.00'
|
||||
if (!num) return '-'
|
||||
if (num < 0.000001) {
|
||||
return `$${num.toExponential(2)}`
|
||||
}
|
||||
if (num >= 0.000001 && num < 0.1) {
|
||||
return `$${Number(num).toPrecision(3)}`
|
||||
}
|
||||
if (num >= 0.1 && num < 1.05) {
|
||||
return `$${num.toFixed(3)}`
|
||||
}
|
||||
// if number is greater than 1.05:
|
||||
return `$${num.toFixed(2)}`
|
||||
}
|
||||
// For volume dollar amounts, like market cap, total value locked, etc.
|
||||
else {
|
||||
if (num === 0) return '0'
|
||||
if (!num) return '-'
|
||||
if (num < 0.000001) {
|
||||
return '$<0.000001'
|
||||
}
|
||||
if (num >= 0.000001 && num < 0.1) {
|
||||
return `$${Number(num).toPrecision(3)}`
|
||||
}
|
||||
if (num >= 0.1 && num < 1.05) {
|
||||
return `$${num.toFixed(3)}`
|
||||
}
|
||||
|
||||
return numbro(num)
|
||||
.formatCurrency({
|
||||
average: round,
|
||||
mantissa: num > 1000 ? 2 : digits,
|
||||
abbreviations: {
|
||||
million: 'M',
|
||||
billion: 'B',
|
||||
},
|
||||
})
|
||||
.toUpperCase()
|
||||
}
|
||||
}
|
||||
|
||||
// using a currency library here in case we want to add more in future
|
||||
export const formatAmount = (num: number | undefined, digits = 2) => {
|
||||
if (num === 0) return '0'
|
||||
if (!num) return '-'
|
||||
if (num < 0.001) {
|
||||
return '$<0.001'
|
||||
}
|
||||
return numbro(num).format({
|
||||
average: true,
|
||||
mantissa: num > 1000 ? 2 : digits,
|
||||
abbreviations: {
|
||||
million: 'M',
|
||||
billion: 'B',
|
||||
},
|
||||
})
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/* Copied from Uniswap/v-3: https://github.com/Uniswap/v3-info/blob/master/src/utils/numbers.ts */
|
||||
import numbro from 'numbro'
|
||||
|
||||
// using a currency library here in case we want to add more in future
|
||||
export const formatDollarAmount = (num: number | undefined, digits = 2, round = true) => {
|
||||
if (num === 0) return '0'
|
||||
if (!num) return '-'
|
||||
if (num < 0.001 && digits <= 3) {
|
||||
return '$<0.001'
|
||||
}
|
||||
|
||||
return numbro(num)
|
||||
.formatCurrency({
|
||||
average: round,
|
||||
mantissa: num > 1000 ? 2 : digits,
|
||||
abbreviations: {
|
||||
million: 'M',
|
||||
billion: 'B',
|
||||
},
|
||||
})
|
||||
.toUpperCase()
|
||||
}
|
||||
|
||||
// using a currency library here in case we want to add more in future
|
||||
export const formatAmount = (num: number | undefined, digits = 2) => {
|
||||
if (num === 0) return '0'
|
||||
if (!num) return '-'
|
||||
if (num < 0.001) {
|
||||
return '$<0.001'
|
||||
}
|
||||
return numbro(num).format({
|
||||
average: true,
|
||||
mantissa: num > 1000 ? 2 : digits,
|
||||
abbreviations: {
|
||||
million: 'M',
|
||||
billion: 'B',
|
||||
},
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user