chore(utils): improve formatNumber to keep precision (#5761)
This commit is contained in:
parent
41fd14dd00
commit
a21feea699
@ -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),
|
||||
};
|
||||
});
|
||||
|
@ -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()}
|
||||
/>
|
||||
|
@ -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>;
|
||||
|
@ -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 (
|
||||
|
@ -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]
|
||||
);
|
||||
|
||||
|
@ -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 }) => {
|
||||
|
@ -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 = (
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user