Skip to content

Commit

Permalink
fix: improve token amount formatting (#339)
Browse files Browse the repository at this point in the history
  • Loading branch information
chybisov authored Jan 22, 2025
1 parent ba57082 commit a6a3d10
Show file tree
Hide file tree
Showing 19 changed files with 2,925 additions and 4,630 deletions.
8 changes: 5 additions & 3 deletions packages/widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"link:bigmi": "pnpm link --global @bigmi/client",
"unlink:bigmi": "pnpm link --global @bigmi/client",
"link:sdk": "pnpm link --global @lifi/sdk",
"unlink:sdk": "pnpm link --global @lifi/sdk"
"unlink:sdk": "pnpm link --global @lifi/sdk",
"test": "vitest run"
},
"author": "Eugene Chybisov <[email protected]>",
"homepage": "https://github.com/lifinance/widget",
Expand Down Expand Up @@ -75,10 +76,11 @@
"madge": "^8.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"typescript": "^5.7.2"
"typescript": "^5.7.3",
"vitest": "^2.1.8"
},
"peerDependencies": {
"@bigmi/react": ">=0.0.7",
"@bigmi/react": ">=0.1.0",
"@solana/wallet-adapter-react": "^0.15.35",
"@tanstack/react-query": "^5.62.0",
"react": ">=18",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export const PriceFormHelperTextBase: React.FC<
const { t } = useTranslation()
const [amount] = useFieldValues(FormKeyHelper.getAmountKey(formType))

const fromAmountTokenPrice = formatTokenPrice(amount, token?.priceUSD)
const tokenAmount = token
? formatTokenAmount(token.amount, token.decimals)
: '0'
const tokenPrice = formatTokenPrice(amount, token?.priceUSD)

return (
<FormHelperText
Expand All @@ -59,7 +62,7 @@ export const PriceFormHelperTextBase: React.FC<
}}
>
{t('format.currency', {
value: fromAmountTokenPrice,
value: tokenPrice,
})}
</Typography>
{isLoading && tokenAddress ? (
Expand All @@ -73,9 +76,10 @@ export const PriceFormHelperTextBase: React.FC<
lineHeight: 1,
pl: 0.25,
}}
title={tokenAmount}
>
{`/ ${t('format.number', {
value: formatTokenAmount(token.amount, token.decimals),
{`/ ${t('format.tokenAmount', {
value: tokenAmount,
})}`}
</Typography>
) : null}
Expand Down
7 changes: 3 additions & 4 deletions packages/widget/src/components/FeeBreakdownTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Box, Tooltip, Typography } from '@mui/material'
import type { TFunction } from 'i18next'
import type { ReactElement } from 'react'
import { useTranslation } from 'react-i18next'
import { formatUnits } from 'viem'
import type { FeesBreakdown } from '../utils/fees.js'
import { formatTokenAmount } from '../utils/format.js'

export interface FeeBreakdownTooltipProps {
gasCosts?: FeesBreakdown[]
Expand Down Expand Up @@ -60,9 +60,8 @@ export const getFeeBreakdownTypography = (
}}
>
{t('format.currency', { value: fee.amountUSD })} (
{t('format.number', {
value: Number.parseFloat(formatUnits(fee.amount, fee.token.decimals)),
maximumFractionDigits: Math.min(fee.token.decimals, 9),
{t('format.tokenAmount', {
value: formatTokenAmount(fee.amount, fee.token.decimals),
})}{' '}
{fee.token.symbol})
</Typography>
Expand Down
29 changes: 16 additions & 13 deletions packages/widget/src/components/StepActions/StepActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import {
import type { MouseEventHandler } from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { formatUnits } from 'viem'
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
import { LiFiToolLogo } from '../../icons/lifi.js'
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
import { HiddenUI } from '../../types/widget.js'
import { formatTokenAmount } from '../../utils/format.js'
import { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'
import { SmallAvatar } from '../Avatar/SmallAvatar.js'
import { CardIconButton } from '../Card/CardIconButton.js'
import {
Expand Down Expand Up @@ -215,8 +214,8 @@ export const StepDetailsContent: React.FC<{

fromAmount =
estimatedFromAmount > 0n
? formatUnits(estimatedFromAmount, step.action.fromToken.decimals)
: formatUnits(
? formatTokenAmount(estimatedFromAmount, step.action.fromToken.decimals)
: formatTokenAmount(
BigInt(step.estimate.fromAmount),
step.action.fromToken.decimals
)
Expand All @@ -243,22 +242,24 @@ export const StepDetailsContent: React.FC<{
>
{!showToAmount ? (
<>
{formatUnits(
BigInt(step.estimate.fromAmount),
step.action.fromToken.decimals
)}{' '}
{t('format.tokenAmount', {
value: formatTokenAmount(
BigInt(step.estimate.fromAmount),
step.action.fromToken.decimals
),
})}{' '}
{step.action.fromToken.symbol}
{' - '}
</>
) : null}
{t('format.number', {
{t('format.tokenAmount', {
value: fromAmount,
})}{' '}
{step.action.fromToken.symbol}
{showToAmount ? (
<>
<ArrowForward sx={{ fontSize: 18, paddingX: 0.5, height: 12 }} />
{t('format.number', {
{t('format.tokenAmount', {
value: formatTokenAmount(
BigInt(step.execution?.toAmount ?? step.estimate.toAmount),
step.execution?.toToken?.decimals ?? step.action.toToken.decimals
Expand All @@ -268,9 +269,11 @@ export const StepDetailsContent: React.FC<{
</>
) : (
` (${t('format.currency', {
value:
Number.parseFloat(fromAmount) *
Number.parseFloat(step.action.fromToken.priceUSD),
value: formatTokenPrice(
fromAmount,
step.action.fromToken.priceUSD,
step.action.fromToken.decimals
),
})})`
)}
</Typography>
Expand Down
9 changes: 7 additions & 2 deletions packages/widget/src/components/Token/Token.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ export const TokenBase: FC<TokenProps & BoxProps> = ({
}

const tokenAmount = formatTokenAmount(token.amount, token.decimals)
const tokenPrice = formatTokenPrice(tokenAmount, token.priceUSD)
const tokenPrice = formatTokenPrice(
token.amount,
token.priceUSD,
token.decimals
)

let priceImpact: number | undefined = undefined
let priceImpactPercent: number | undefined = undefined
Expand Down Expand Up @@ -127,14 +131,15 @@ export const TokenBase: FC<TokenProps & BoxProps> = ({
display: 'flex',
alignItems: 'center',
}}
title={tokenAmount}
>
<TextFitter
height={30}
textStyle={{
fontWeight: 700,
}}
>
{t('format.number', {
{t('format.tokenAmount', {
value: tokenAmount,
})}
</TextFitter>
Expand Down
18 changes: 9 additions & 9 deletions packages/widget/src/components/TokenList/TokenListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
import type { MouseEventHandler } from 'react'
import { useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { formatUnits } from 'viem'
import { useExplorer } from '../../hooks/useExplorer.js'
import { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'
import { shortenAddress } from '../../utils/wallet.js'
Expand Down Expand Up @@ -66,12 +65,6 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
const { t } = useTranslation()
const { getAddressLink } = useExplorer()

const tokenPrice = token.amount
? formatTokenPrice(
formatUnits(token.amount, token.decimals),
token.priceUSD
)
: undefined
const container = useRef(null)
const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)
const [showAddress, setShowAddress] = useState(false)
Expand All @@ -93,6 +86,12 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
setShowAddress(false)
}
}
const tokenAmount = formatTokenAmount(token.amount, token.decimals)
const tokenPrice = formatTokenPrice(
token.amount,
token.priceUSD,
token.decimals
)

return (
<ListItemButton
Expand Down Expand Up @@ -186,9 +185,10 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
sx={{
fontWeight: 600,
}}
title={tokenAmount}
>
{t('format.number', {
value: formatTokenAmount(token.amount, token.decimals),
{t('format.tokenAmount', {
value: tokenAmount,
})}
</Typography>
) : null}
Expand Down
16 changes: 5 additions & 11 deletions packages/widget/src/components/TokenRate/TokenRate.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import type { RouteExtended } from '@lifi/sdk'
import type { TypographyProps } from '@mui/material'
import type { MouseEventHandler } from 'react'
import { useTranslation } from 'react-i18next'
import { formatUnits } from 'viem'
import { create } from 'zustand'
import {
convertToSubscriptFormat,
precisionFormatter,
} from '../../utils/format.js'
import { TokenRateTypography } from './TokenRate.style.js'

interface TokenRateProps extends TypographyProps {
Expand All @@ -24,6 +21,7 @@ const useTokenRate = create<TokenRateState>((set) => ({
}))

export const TokenRate: React.FC<TokenRateProps> = ({ route }) => {
const { t } = useTranslation()
const { isForward, toggleIsForward } = useTokenRate()

const toggleRate: MouseEventHandler<HTMLSpanElement> = (e) => {
Expand Down Expand Up @@ -54,16 +52,12 @@ export const TokenRate: React.FC<TokenRateProps> = ({ route }) => {
Number.parseFloat(formatUnits(toToken.amount!, toToken.decimals))

const rateText = isForward
? `1 ${fromToken.symbol}${convertToSubscriptFormat(fromToRate)} ${toToken.symbol}`
: `1 ${toToken.symbol}${convertToSubscriptFormat(toFromRate)} ${fromToken.symbol}`

const rateTitle = isForward
? `1 ${fromToken.symbol}${precisionFormatter.format(fromToRate)} ${toToken.symbol}`
: `1 ${toToken.symbol}${precisionFormatter.format(toFromRate)} ${fromToken.symbol}`
? `1 ${fromToken.symbol}${t('format.tokenAmount', { value: fromToRate })} ${toToken.symbol}`
: `1 ${toToken.symbol}${t('format.tokenAmount', { value: toFromRate })} ${fromToken.symbol}`

return (
// biome-ignore lint/a11y/useSemanticElements:
<TokenRateTypography onClick={toggleRate} role="button" title={rateTitle}>
<TokenRateTypography onClick={toggleRate} role="button">
{rateText}
</TokenRateTypography>
)
Expand Down
12 changes: 4 additions & 8 deletions packages/widget/src/components/TransactionDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ import type { CardProps } from '@mui/material'
import { Box, Collapse, Tooltip, Typography } from '@mui/material'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { formatUnits } from 'viem'
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
import { isRouteDone } from '../stores/routes/utils.js'
import { getAccumulatedFeeCostsBreakdown } from '../utils/fees.js'
import { formatTokenAmount } from '../utils/format.js'
import { formatTokenAmount, formatTokenPrice } from '../utils/format.js'
import { getPriceImpact } from '../utils/getPriceImpact.js'
import { Card } from './Card/Card.js'
import { CardIconButton } from './Card/CardIconButton.js'
Expand Down Expand Up @@ -56,14 +55,11 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
BigInt(feeCollectionStep.estimate.fromAmount) -
BigInt(feeCollectionStep.estimate.toAmount)

const feeAmount = formatUnits(
feeAmountUSD = formatTokenPrice(
estimatedFromAmount,
feeCollectionStep.action.fromToken.priceUSD,
feeCollectionStep.action.fromToken.decimals
)

feeAmountUSD =
Number.parseFloat(feeAmount) *
Number.parseFloat(feeCollectionStep.action.fromToken.priceUSD)
}

const showIntegratorFeeCollectionDetails =
Expand Down Expand Up @@ -245,7 +241,7 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
sx={{ cursor: 'help' }}
>
<Typography variant="body2">
{t('format.number', {
{t('format.tokenAmount', {
value: formatTokenAmount(
BigInt(route.toAmountMin),
route.toToken.decimals
Expand Down
3 changes: 2 additions & 1 deletion packages/widget/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"format": {
"currency": "{{value, currencyExt(currency: USD)}}",
"number": "{{value, number(maximumFractionDigits: 9)}}",
"percent": "{{value, percent(maximumFractionDigits: 2)}}"
"percent": "{{value, percent(maximumFractionDigits: 2)}}",
"tokenAmount": "{{value, numberExt}}"
},
"button": {
"auto": "Auto",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const ExchangeRateBottomSheetContent: React.FC<
fontWeight: 600,
}}
>
{t('format.number', {
{t('format.tokenAmount', {
value: formatTokenAmount(
BigInt(data.oldToAmount),
data.toToken.decimals
Expand All @@ -168,7 +168,7 @@ const ExchangeRateBottomSheetContent: React.FC<
fontWeight: 600,
}}
>
{t('format.number', {
{t('format.tokenAmount', {
value: formatTokenAmount(
BigInt(data?.newToAmount),
data?.toToken.decimals
Expand Down
4 changes: 3 additions & 1 deletion packages/widget/src/providers/I18nProvider/I18nProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { useMemo } from 'react'
import { I18nextProvider } from 'react-i18next'
import * as supportedLanguages from '../../i18n/index.js'
import { useSettings } from '../../stores/settings/useSettings.js'
import { compactNumberFormatter } from '../../utils/compactNumberFormatter.js'
import { deepMerge } from '../../utils/deepMerge.js'
import { isItemAllowed } from '../../utils/item.js'
import { percentFormatter } from '../../utils/percentFormatter.js'
import { useWidgetConfig } from '../WidgetProvider/WidgetProvider.js'
import { currencyExtendedFormatter } from './currencyExtendedFormatter.js'
import { percentFormatter } from './percentFormatter.js'
import type { LanguageKey, LanguageTranslationResources } from './types.js'

export const I18nProvider: React.FC<React.PropsWithChildren> = ({
Expand Down Expand Up @@ -63,6 +64,7 @@ export const I18nProvider: React.FC<React.PropsWithChildren> = ({

i18n.init()

i18n.services.formatter?.addCached('numberExt', compactNumberFormatter)
i18n.services.formatter?.addCached('currencyExt', currencyExtendedFormatter)
i18n.services.formatter?.addCached('percent', percentFormatter)

Expand Down
Loading

0 comments on commit a6a3d10

Please sign in to comment.