From f274044f46f370d98a1484f8b170cf108d411f3b Mon Sep 17 00:00:00 2001
From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com>
Date: Wed, 8 May 2024 11:28:45 -0700
Subject: [PATCH] Fe/format amount (#2598)
* formatAmount util function, use rounded shortened values in Bridge/Swap Input
* Update Bridge/Swap page with parsed / formatted balances
* Clean imports
---
.../components/PortfolioTokenAsset.tsx | 10 +--
.../StateManagedBridge/InputContainer.tsx | 17 +++--
.../StateManagedSwap/SwapInputContainer.tsx | 14 ++--
.../ui/SelectSpecificTokenButton.tsx | 14 ++--
.../synapse-interface/utils/formatAmount.ts | 69 +++++++++++++++++++
.../utils/getParsedBalance.ts | 9 ++-
.../utils/trimTrailingZeroesAfterDecimal.ts | 2 +-
7 files changed, 105 insertions(+), 30 deletions(-)
create mode 100644 packages/synapse-interface/utils/formatAmount.ts
diff --git a/packages/synapse-interface/components/Portfolio/components/PortfolioTokenAsset.tsx b/packages/synapse-interface/components/Portfolio/components/PortfolioTokenAsset.tsx
index 75dfa98294..171826c312 100644
--- a/packages/synapse-interface/components/Portfolio/components/PortfolioTokenAsset.tsx
+++ b/packages/synapse-interface/components/Portfolio/components/PortfolioTokenAsset.tsx
@@ -13,6 +13,8 @@ import { getParsedBalance } from '@/utils/getParsedBalance'
import { useGasEstimator } from '@/utils/hooks/useGasEstimator'
import GasIcon from '@/components/icons/GasIcon'
import { trimTrailingZeroesAfterDecimal } from '@/utils/trimTrailingZeroesAfterDecimal'
+import { formatAmount } from '@/utils/formatAmount'
+import { formatBigIntToString } from '@/utils/bigint/format'
const handleFocusOnBridgeInput = () => {
inputRef.current?.focus()
@@ -39,8 +41,8 @@ export const PortfolioTokenAsset = ({
? decimals
: decimals[portfolioChainId]
- const parsedBalance = getParsedBalance(balance, tokenDecimals, 4)
- const parsedBalanceLong = getParsedBalance(balance, tokenDecimals, 8)
+ const parsedBalance = getParsedBalance(balance, tokenDecimals)
+ const formattedBalance = formatAmount(parsedBalance)
const isDisabled = false
const isPortfolioChainSelected = fromChainId === portfolioChainId
@@ -80,13 +82,13 @@ export const PortfolioTokenAsset = ({
) : (
- {parsedBalanceLong} {symbol}
+ {parsedBalance} {symbol}
)
}
>
- {parsedBalance} {symbol}
+ {formattedBalance} {symbol}
diff --git a/packages/synapse-interface/components/StateManagedBridge/InputContainer.tsx b/packages/synapse-interface/components/StateManagedBridge/InputContainer.tsx
index 144f256f06..9317d19d02 100644
--- a/packages/synapse-interface/components/StateManagedBridge/InputContainer.tsx
+++ b/packages/synapse-interface/components/StateManagedBridge/InputContainer.tsx
@@ -12,7 +12,6 @@ import {
import { ChainSelector } from '@/components/ui/ChainSelector'
import { TokenSelector } from '@/components/ui/TokenSelector'
import { AmountInput } from '@/components/ui/AmountInput'
-import { formatBigIntToString } from '@/utils/bigint/format'
import { cleanNumberInput } from '@/utils/cleanNumberInput'
import {
ConnectToNetworkButton,
@@ -30,6 +29,7 @@ import { AvailableBalance } from './AvailableBalance'
import { useGasEstimator } from '../../utils/hooks/useGasEstimator'
import { getParsedBalance } from '@/utils/getParsedBalance'
import { MaxButton } from './MaxButton'
+import { formatAmount } from '../../utils/formatAmount'
export const inputRef = React.createRef()
@@ -47,8 +47,8 @@ export const InputContainer = () => {
const balance: bigint = balances[fromChainId]?.find(
(token) => token.tokenAddress === addresses?.[fromChainId]
)?.balance
- const parsedBalance = getParsedBalance(balance, tokenDecimals, 4)
- const fullParsedBalance = formatBigIntToString(balance, tokenDecimals)
+ const parsedBalance = getParsedBalance(balance, tokenDecimals)
+ const formattedBalance = formatAmount(parsedBalance)
const hasValidFromSelections: boolean = useMemo(() => {
return Boolean(fromChainId && fromToken)
@@ -68,15 +68,14 @@ export const InputContainer = () => {
} = useGasEstimator()
const isInputMax =
- maxBridgeableGas?.toString() === fromValue ||
- fullParsedBalance === fromValue
+ maxBridgeableGas?.toString() === fromValue || parsedBalance === fromValue
const onMaxBalance = useCallback(async () => {
if (hasValidGasEstimateInputs()) {
const bridgeableBalance = await estimateBridgeableBalanceCallback()
if (isNull(bridgeableBalance)) {
- dispatch(updateFromValue(fullParsedBalance))
+ dispatch(updateFromValue(parsedBalance))
} else if (bridgeableBalance > 0) {
dispatch(updateFromValue(bridgeableBalance?.toString()))
} else {
@@ -87,12 +86,12 @@ export const InputContainer = () => {
})
}
} else {
- dispatch(updateFromValue(fullParsedBalance))
+ dispatch(updateFromValue(parsedBalance))
}
}, [
fromChainId,
fromToken,
- fullParsedBalance,
+ parsedBalance,
hasValidGasEstimateInputs,
estimateBridgeableBalanceCallback,
])
@@ -156,7 +155,7 @@ export const InputContainer = () => {
/>
{
const inputRef = useRef(null)
@@ -53,14 +55,12 @@ export const SwapInputContainer = () => {
(token) => token.tokenAddress === swapFromToken?.addresses[swapChainId]
)
- const parsedBalance = tokenData?.parsedBalance
const balance = tokenData?.balance
- const parsedFullBalance = formatBigIntToString(
- balance,
- tokenData?.token?.decimals[swapChainId]
- )
+ const decimals = tokenData?.token?.decimals[swapChainId]
+ const parsedBalance = getParsedBalance(balance, decimals)
+ const formattedBalance = formatAmount(parsedBalance)
- const isInputMax = parsedFullBalance === swapFromValue
+ const isInputMax = parsedBalance === swapFromValue
useEffect(() => {
if (
@@ -143,7 +143,7 @@ export const SwapInputContainer = () => {
Available:{' '}
- {parsedBalance ?? '0.0'}
+ {formattedBalance ?? '0.0'}
{
const portfolioBalances = usePortfolioBalances()
- const parsedBalance = portfolioBalances[chainId]?.find(
+ const tokenData = portfolioBalances[chainId]?.find(
(tb) => tb.token.addresses[chainId] === token.addresses[chainId]
- )?.parsedBalance
+ )
+ const decimals = tokenData?.token?.decimals[chainId]
+ const balance = tokenData?.balance
+ const parsedBalance = getParsedBalance(balance, decimals)
+ const formattedBalance = formatAmount(parsedBalance)
return (
@@ -91,7 +97,7 @@ const ButtonContent = memo(
token={token}
showAllChains={showAllChains}
isOrigin={isOrigin}
- parsedBalance={parsedBalance}
+ parsedBalance={formattedBalance}
/>
)
diff --git a/packages/synapse-interface/utils/formatAmount.ts b/packages/synapse-interface/utils/formatAmount.ts
new file mode 100644
index 0000000000..19f9c36d54
--- /dev/null
+++ b/packages/synapse-interface/utils/formatAmount.ts
@@ -0,0 +1,69 @@
+interface FormatOptions {
+ fullAmount?: boolean
+ standardDigits?: number
+ useCompactNotation?: boolean
+ compactDigits?: number
+ minimumAmount?: number
+ roundingMode?: string
+}
+
+export const formatAmount = (
+ amount: string,
+ options?: FormatOptions
+): string => {
+ if (amount === '') return ''
+
+ const floatAmount = parseFloat(amount)
+
+ try {
+ if (!Number.isFinite(floatAmount)) {
+ throw new TypeError(`"${amount}" is not a finite number`)
+ }
+ } catch ({ name, message }) {
+ console.error(name, message)
+ return amount
+ }
+
+ const fullAmount = options?.fullAmount ?? false
+ const standardDigits = options?.standardDigits ?? 4
+ const useCompactNotation = options?.useCompactNotation ?? true
+ const compactDigits = options?.compactDigits ?? useCompactNotation ? 2 : 0
+ const minimumAmount = options?.minimumAmount ?? 0.0001
+
+ const locales = 'en-UK'
+
+ if (!floatAmount) return '0.0'
+
+ if (fullAmount) return Intl.NumberFormat(locales).format(floatAmount)
+
+ if (floatAmount < minimumAmount) return `< ${minimumAmount}`
+
+ const absAmount = Math.abs(floatAmount)
+
+ if (absAmount < 0.0001)
+ return Intl.NumberFormat(locales, {
+ maximumSignificantDigits: 1,
+ }).format(floatAmount)
+
+ if (absAmount < 1)
+ return Intl.NumberFormat(locales, {
+ minimumFractionDigits: standardDigits,
+ }).format(floatAmount)
+
+ if (absAmount < 1000)
+ return Intl.NumberFormat(locales, {
+ minimumSignificantDigits: standardDigits,
+ maximumSignificantDigits: standardDigits,
+ }).format(floatAmount)
+
+ if (absAmount < 1000000)
+ return Intl.NumberFormat(locales, {
+ maximumFractionDigits: 0,
+ }).format(floatAmount)
+
+ return Intl.NumberFormat(locales, {
+ minimumFractionDigits: compactDigits,
+ maximumFractionDigits: compactDigits,
+ notation: useCompactNotation ? 'compact' : 'standard',
+ }).format(floatAmount)
+}
diff --git a/packages/synapse-interface/utils/getParsedBalance.ts b/packages/synapse-interface/utils/getParsedBalance.ts
index 7c76b042f8..4772339db7 100644
--- a/packages/synapse-interface/utils/getParsedBalance.ts
+++ b/packages/synapse-interface/utils/getParsedBalance.ts
@@ -1,13 +1,12 @@
+import { trimTrailingZeroesAfterDecimal } from '@/utils/trimTrailingZeroesAfterDecimal'
import { formatBigIntToString } from './bigint/format'
-import { hasOnlyZeroes } from './hasOnlyZeroes'
export const getParsedBalance = (
balance: bigint,
decimals: number,
places?: number
) => {
- const formattedBalance = formatBigIntToString(balance, decimals, places)
- const verySmallBalance = balance > 0n && hasOnlyZeroes(formattedBalance)
-
- return verySmallBalance ? '< 0.001' : formattedBalance
+ return trimTrailingZeroesAfterDecimal(
+ formatBigIntToString(balance, decimals, places)
+ )
}
diff --git a/packages/synapse-interface/utils/trimTrailingZeroesAfterDecimal.ts b/packages/synapse-interface/utils/trimTrailingZeroesAfterDecimal.ts
index a627a4361f..ee9e9850c1 100644
--- a/packages/synapse-interface/utils/trimTrailingZeroesAfterDecimal.ts
+++ b/packages/synapse-interface/utils/trimTrailingZeroesAfterDecimal.ts
@@ -1,7 +1,7 @@
export const trimTrailingZeroesAfterDecimal = (input: string): string => {
const parts = input?.split('.')
- if (parts.length === 2) {
+ if (parts?.length === 2) {
const integerPart = parts[0]
let fractionalPart = parts[1]