chore(utils): improve formatNumber to keep precision (#5761)

This commit is contained in:
Bartłomiej Głownia 2024-02-09 11:31:15 +01:00 committed by GitHub
parent 41fd14dd00
commit a21feea699
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 92 additions and 88 deletions

View File

@ -1,4 +1,4 @@
import { getNumberFormat } from '@vegaprotocol/utils';
import { formatNumber } from '@vegaprotocol/utils';
import sortBy from 'lodash/sortBy';
import omit from 'lodash/omit';
import { useReferralProgramQuery } from './__generated__/CurrentReferralProgram';
@ -107,9 +107,7 @@ export const useReferralProgram = () => {
discountFactor: Number(t.referralDiscountFactor),
discount: BigNumber(t.referralDiscountFactor).times(100).toFixed(2) + '%',
minimumVolume: Number(t.minimumRunningNotionalTakerVolume),
volume: getNumberFormat(0).format(
Number(t.minimumRunningNotionalTakerVolume)
),
volume: formatNumber(t.minimumRunningNotionalTakerVolume, 0),
epochs: Number(t.minimumEpochs),
};
});

View File

@ -14,9 +14,9 @@ import {
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
addDecimalsFormatNumber,
formatNumber,
getDateFormat,
getDateTimeFormat,
getNumberFormat,
getUserLocale,
removePaginationWrapper,
} from '@vegaprotocol/utils';
@ -323,7 +323,7 @@ export const Statistics = ({
}
description={<QUSDTooltip />}
>
{getNumberFormat(0).format(Number(totalCommissionValue))}
{formatNumber(totalCommissionValue, 0)}
</StatTile>
);
@ -563,8 +563,8 @@ export const RefereesTable = ({
)
.map((r) => ({
...r,
volume: getNumberFormat(0).format(r.volume),
commission: getNumberFormat(0).format(r.commission),
volume: formatNumber(r.volume, 0),
commission: formatNumber(r.commission, 0),
}))
.reverse()}
/>

View File

@ -1,6 +1,6 @@
import { Link } from 'react-router-dom';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { getNumberFormat } from '@vegaprotocol/utils';
import { formatNumber } from '@vegaprotocol/utils';
import { type useTeams } from '../../lib/hooks/use-teams';
import { useT } from '../../lib/use-t';
import { Table } from '../table';
@ -15,8 +15,7 @@ export const CompetitionsLeaderboard = ({
}) => {
const t = useT();
const num = (n?: number | string) =>
!n ? '-' : getNumberFormat(0).format(Number(n));
const num = (n?: number | string) => (!n ? '-' : formatNumber(n, 0));
if (!data || data.length === 0) {
return <Splash>{t('Could not find any teams')}</Splash>;

View File

@ -94,7 +94,7 @@ export const LiquidityTable = ({
return `${addDecimalsFormatNumberQuantum(
value,
assetDecimalPlaces ?? 0,
quantum ?? 0
quantum ?? 1
)}`;
};
@ -165,7 +165,7 @@ export const LiquidityTable = ({
return `${addDecimalsFormatNumberQuantum(
newValue,
assetDecimalPlaces ?? 0,
quantum ?? 0
quantum ?? 1
)}`;
};
@ -227,7 +227,7 @@ export const LiquidityTable = ({
addDecimalsFormatNumberQuantum(
pendingCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
quantum ?? 1
);
if (
@ -238,7 +238,7 @@ export const LiquidityTable = ({
addDecimalsFormatNumberQuantum(
currentCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
quantum ?? 1
);
return (
@ -286,7 +286,7 @@ export const LiquidityTable = ({
addDecimalsFormatNumberQuantum(
pendingCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
quantum ?? 1
);
if (
@ -297,7 +297,7 @@ export const LiquidityTable = ({
addDecimalsFormatNumberQuantum(
currentCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
quantum ?? 1
);
return (

View File

@ -1,7 +1,7 @@
import { DepthChart } from 'pennant';
import throttle from 'lodash/throttle';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { addDecimal, getNumberFormat } from '@vegaprotocol/utils';
import { addDecimal, formatNumber } from '@vegaprotocol/utils';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketDepthProvider } from './market-depth-provider';
@ -216,13 +216,12 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
const volumeFormat = useCallback(
(volume: number) =>
getNumberFormat(market?.positionDecimalPlaces || 0).format(volume),
formatNumber(volume, market?.positionDecimalPlaces || 0),
[market?.positionDecimalPlaces]
);
const priceFormat = useCallback(
(price: number) =>
getNumberFormat(market?.decimalPlaces || 0).format(price),
(price: number) => formatNumber(price, market?.decimalPlaces || 0),
[market?.decimalPlaces]
);

View File

@ -23,6 +23,7 @@ describe('number utils', () => {
{ v: new BigNumber(123000), d: 1, o: '12,300.0' },
{ v: new BigNumber(123001), d: 2, o: '1,230.01' },
{ v: new BigNumber(123001000), d: 2, o: '1,230,010.00' },
{ v: '100000000000000000001', d: 18, o: '100.000000000000000001' },
])(
'formats with addDecimalsFormatNumber given number correctly',
({ v, d, o }) => {
@ -31,27 +32,10 @@ describe('number utils', () => {
);
it.each([
{ v: new BigNumber(123000), d: 5, o: '1.23', q: 0.1 },
{ v: new BigNumber(123000), d: 3, o: '123.00', q: 0.1 },
{ v: new BigNumber(123000), d: 1, o: '12,300.00', q: 0.1 },
{ v: new BigNumber(123001000), d: 2, o: '1,230,010.00', q: 0.1 },
{ v: new BigNumber(123001), d: 2, o: '1,230.01', q: 100 },
{ v: new BigNumber(123001), d: 2, o: '1,230.01', q: 0.1 },
{ v: new BigNumber(123001), d: 2, o: '1,230.01', q: 1 },
{
v: BigNumber('123456789123456789'),
d: 10,
o: '12,345,678.91234568',
q: '0.00003846',
},
{
v: BigNumber('123456789123456789'),
d: 10,
o: '12,345,678.91234568',
q: '1',
},
// USDT / USDC
{ v: new BigNumber(12345678), d: 6, o: '12.35', q: 1000000 },
{ v: '1234000000000000000', d: 18, q: '1000000000000000000', o: '1.23' }, //vega
{ v: '1235000000000000000', d: 18, q: '1000000000000000000', o: '1.24' }, //vega
{ v: '1230012', d: 6, q: '1000000', o: '1.23' }, // USDT
{ v: '1234560000000000000', d: 18, q: '500000000000000', o: '1.2346' }, // WEth
])(
'formats with addDecimalsFormatNumberQuantum given number correctly',
({ v, d, o, q }) => {

View File

@ -1,5 +1,4 @@
import { BigNumber } from 'bignumber.js';
import isNil from 'lodash/isNil';
import memoize from 'lodash/memoize';
import { getUserLocale } from '../get-user-locale';
@ -53,36 +52,36 @@ export function removeDecimal(
return new BigNumber(value || 0).times(times).toFixed(0);
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
export const getNumberFormat = memoize((digits: number) => {
if (isNil(digits) || digits < 0) {
return new Intl.NumberFormat(getUserLocale());
}
return new Intl.NumberFormat(getUserLocale(), {
minimumFractionDigits: Math.min(Math.max(0, digits), MIN_FRACTION_DIGITS),
maximumFractionDigits: Math.min(Math.max(0, digits), MAX_FRACTION_DIGITS),
});
});
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
export const getFixedNumberFormat = memoize((digits: number) => {
if (isNil(digits) || digits < 0) {
return new Intl.NumberFormat(getUserLocale());
}
return new Intl.NumberFormat(getUserLocale(), {
minimumFractionDigits: Math.min(Math.max(0, digits), MAX_FRACTION_DIGITS),
maximumFractionDigits: Math.min(Math.max(0, digits), MAX_FRACTION_DIGITS),
});
});
export const getDecimalSeparator = memoize(
() =>
getNumberFormat(1)
new Intl.NumberFormat(getUserLocale())
.formatToParts(1.1)
.find((part) => part.type === 'decimal')?.value
.find((part) => part.type === 'decimal')?.value ?? '.'
);
/** formatNumber will format the number with fixed decimals
export const getGroupFormat = memoize(() => {
const parts = new Intl.NumberFormat(getUserLocale()).formatToParts(
100000000000.1
);
const groupSeparator = parts.find((part) => part.type === 'group')?.value;
const groupSize =
(groupSeparator &&
parts.reverse().find((part) => part.type === 'integer')?.value.length) ||
0;
return {
groupSize,
groupSeparator,
};
});
const getFormat = memoize(() => ({
decimalSeparator: getDecimalSeparator(),
...getGroupFormat(),
}));
/**
* formatNumber will format the number with maximum number of decimals
* trailing zeros are removed but min(MIN_FRACTION_DIGITS, formatDecimals) decimal places will be kept
* @param rawValue - should be a number that is not outside the safe range fail as in https://mikemcl.github.io/bignumber.js/#toN
* @param formatDecimals - number of decimals to use
*/
@ -90,7 +89,23 @@ export const formatNumber = (
rawValue: string | number | BigNumber,
formatDecimals = 0
) => {
return getNumberFormat(formatDecimals).format(Number(rawValue));
const decimalPlaces = Math.min(
Math.max(0, formatDecimals),
MAX_FRACTION_DIGITS
);
const format = getFormat();
const formatted = new BigNumber(rawValue).toFormat(decimalPlaces, format);
// if there are no decimal places just return formatted value
if (!decimalPlaces) {
return formatted;
}
// minimum number of decimal places to keep when removing trailing zeros
const minimumFractionDigits = Math.min(decimalPlaces, MIN_FRACTION_DIGITS);
const parts = formatted.split(format.decimalSeparator);
parts[1] = (parts[1] || '')
.replace(/0+$/, '')
.padEnd(minimumFractionDigits, '0');
return parts.join(format.decimalSeparator);
};
/** formatNumberFixed will format the number with fixed decimals
@ -101,7 +116,10 @@ export const formatNumberFixed = (
rawValue: string | number | BigNumber,
formatDecimals = 0
) => {
return getFixedNumberFormat(formatDecimals).format(Number(rawValue));
return new BigNumber(rawValue).toFormat(
Math.min(Math.max(0, formatDecimals), MAX_FRACTION_DIGITS),
getFormat()
);
};
export const quantumDecimalPlaces = (
@ -131,9 +149,14 @@ export const addDecimalsFormatNumberQuantum = (
if (isNaN(Number(quantum))) {
return addDecimalsFormatNumber(rawValue, decimalPlaces);
}
const quantumValue = addDecimal(quantum, decimalPlaces);
const numberDP = Math.max(0, Math.log10(100 / Number(quantumValue)));
return addDecimalsFormatNumber(rawValue, decimalPlaces, Math.ceil(numberDP));
const numberDP = Math.ceil(
Math.abs(Math.log10(toBigNum(quantum, decimalPlaces).toNumber()))
);
return addDecimalsFormatNumber(
rawValue,
decimalPlaces,
Math.max(MIN_FRACTION_DIGITS, numberDP)
);
};
export const addDecimalsFormatNumber = (
@ -141,9 +164,10 @@ export const addDecimalsFormatNumber = (
decimalPlaces: number,
formatDecimals: number = decimalPlaces
) => {
const x = addDecimal(rawValue, decimalPlaces);
return formatNumber(x, formatDecimals);
return formatNumber(
new BigNumber(rawValue || 0).dividedBy(Math.pow(10, decimalPlaces)),
formatDecimals
);
};
export const addDecimalsFixedFormatNumber = (

View File

@ -10,24 +10,24 @@ describe('formatValue', () => {
{
v: '123456789123456789',
d: 10,
o: '12,345,678.91234568',
o: '12,345,678.9123456789',
},
])('formats values correctly', ({ v, d, o }) => {
expect(formatValue(v, d)).toStrictEqual(o);
});
it.each([
{ v: 123000, d: 5, o: '1.23', q: '0.1' },
{ v: 123000, d: 3, o: '123.00', q: '0.1' },
{ v: 123000, d: 1, o: '12,300.00', q: '0.1' },
{ v: 123001000, d: 2, o: '1,230,010.00', q: '0.1' },
{ v: 123000, d: 5, o: '1.23', q: '1' },
{ v: 123000, d: 3, o: '123.00', q: '1' },
{ v: 123000, d: 1, o: '12,300.00', q: '1' },
{ v: 123001000, d: 2, o: '1,230,010.00', q: '1' },
{ v: 123001, d: 2, o: '1,230.01', q: '100' },
{ v: 123001, d: 2, o: '1,230.01', q: '0.1' },
{ v: 123001, d: 2, o: '1,230.01', q: '1' },
{
v: '123456789123456789',
d: 10,
o: '12,345,678.91234568',
q: '0.00003846',
o: '12,345,678.91235',
q: '384600',
},
])(
'formats with formatValue with quantum given number correctly',
@ -42,15 +42,15 @@ describe('formatRange', () => {
min: 123000,
max: 12300011111,
d: 5,
o: '1.23 - 123,000.11111',
q: '0.1',
o: '1.23 - 123,000.11',
q: '1000',
},
{
min: 123000,
max: 12300011111,
d: 3,
o: '123.00 - 12,300,011.111',
q: '0.1',
o: '123.00 - 12,300,011.11',
q: '100',
},
{
min: 123000,