Skip to content

Commit 1b50332

Browse files
authored
Merge pull request #1495 from curvefi/fix/rounding-boogaloo-2
feat: enhance balance input precision
2 parents 680f17e + 2608428 commit 1b50332

File tree

4 files changed

+30
-15
lines changed

4 files changed

+30
-15
lines changed

packages/curve-ui-kit/src/shared/ui/Balance.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import type { ReactNode } from 'react'
2+
import { notFalsy } from '@curvefi/prices-api/objects.util'
23
import AccountBalanceWalletOutlinedIcon from '@mui/icons-material/AccountBalanceWalletOutlined'
34
import Button from '@mui/material/Button'
45
import Stack from '@mui/material/Stack'
56
import Typography from '@mui/material/Typography'
67
import { t } from '@ui-kit/lib/i18n'
8+
import { Tooltip } from '@ui-kit/shared/ui/Tooltip'
79
import { WithSkeleton } from '@ui-kit/shared/ui/WithSkeleton'
810
import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces'
911
import { type Amount, formatNumber, type SxProps } from '@ui-kit/utils'
@@ -58,20 +60,22 @@ type BalanceTextProps<T> = {
5860

5961
const BalanceText = <T extends Amount>({ symbol, balance, loading = false }: BalanceTextProps<T>) => (
6062
<WithSkeleton loading={loading}>
61-
<Stack direction="row" gap={Spacing.xs} alignItems="center">
62-
<Typography
63-
className="balance"
64-
variant="highlightS"
65-
color={balance != null ? 'textPrimary' : 'textTertiary'}
66-
data-testid="balance-value"
67-
>
68-
{balance == null ? '-' : formatNumber(balance, { abbreviate: true })}
69-
</Typography>
63+
<Tooltip title={t`Wallet balance`} body={notFalsy(balance?.toString(), symbol).join(' ')} clickable>
64+
<Stack direction="row" gap={Spacing.xs} alignItems="center">
65+
<Typography
66+
className="balance"
67+
variant="highlightS"
68+
color={balance != null ? 'textPrimary' : 'textTertiary'}
69+
data-testid="balance-value"
70+
>
71+
{balance == null ? '-' : formatNumber(balance, { abbreviate: true, highPrecision: true })}
72+
</Typography>
7073

71-
<Typography variant="highlightS" color="textPrimary">
72-
{symbol}
73-
</Typography>
74-
</Stack>
74+
<Typography variant="highlightS" color="textPrimary">
75+
{symbol}
76+
</Typography>
77+
</Stack>
78+
</Tooltip>
7579
</WithSkeleton>
7680
)
7781

packages/curve-ui-kit/src/shared/ui/LargeTokenInput.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ type Props<T> = DataType<T> & {
189189
* Calculate the new balance based on max balance and percentage, rounding to specified decimals
190190
*/
191191
function calculateNewBalance<T extends Amount>(max: T, newPercentage: Decimal, balanceDecimals: number | undefined): T {
192+
// Avoid loss of precision when clicking 'Max'
193+
if (Number(newPercentage) === 100) return max
194+
192195
let newBalance = new BigNumber(max).times(newPercentage).div(100)
193196
if (balanceDecimals != null) {
194197
// toFixed can make the newBalance>max due to rounding, so ensure it doesn't exceed maxBalance

packages/curve-ui-kit/src/themes/components/mui-input-base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const defineMuiInputBase = (
4242
input: {
4343
height: ButtonSize.md,
4444
boxSizing: 'border-box',
45+
textOverflow: 'ellipsis',
4546
},
4647
},
4748
})

packages/curve-ui-kit/src/utils/number.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ const formatterReset = { style: undefined, currency: undefined, notation: undefi
101101
*/
102102
export const defaultNumberFormatter = (
103103
value: Amount,
104-
{ decimals = 2, trailingZeroDisplay = 'stripIfInteger', ...options }: Partial<NumberFormatOptions> = {},
104+
{
105+
decimals = 2,
106+
trailingZeroDisplay = 'stripIfInteger',
107+
highPrecision = false,
108+
...options
109+
}: Partial<NumberFormatOptions> = {},
105110
): string => {
106111
if (typeof value !== 'number') value = Number(value)
107112
if (isNaN(value)) return 'NaN'
@@ -114,7 +119,7 @@ export const defaultNumberFormatter = (
114119

115120
const formatted = value.toLocaleString(LOCALE, {
116121
minimumFractionDigits: Math.min(decimals, options.maximumFractionDigits ?? Infinity),
117-
maximumFractionDigits: decimals,
122+
maximumFractionDigits: highPrecision ? Math.max(4, decimals) : decimals,
118123
trailingZeroDisplay,
119124
...options,
120125
...formatterReset,
@@ -163,6 +168,8 @@ export type NumberFormatOptions = {
163168
decimals?: number
164169
/** If the value should be abbreviated to 1.23k or 3.45m */
165170
abbreviate: boolean
171+
/** Some use cases require high precision, like user input. When enabled, ensures at least a certain amount decimals are shown. */
172+
highPrecision?: boolean
166173
/** Optional formatter for value */
167174
formatter?: (value: Amount) => string
168175
} & Omit<Intl.NumberFormatOptions, 'unit' | 'style' | 'compact' | 'notation' | 'currency'>

0 commit comments

Comments
 (0)