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:
lynn 2022-10-06 20:27:23 -04:00 committed by GitHub
parent 2338255a54
commit 859258c25c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 163 additions and 64 deletions

@ -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} />
&nbsp;{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={

@ -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')
})
})

@ -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',
},
})
}