fix: 1875 & 1876 fix deal ticket fees display and remove insignificant trailing zeros (#1898)

This commit is contained in:
m.ray 2022-11-01 11:03:29 +00:00 committed by GitHub
parent 082683a541
commit 3e970f7023
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 116 additions and 48 deletions

View File

@ -7,7 +7,7 @@ import {
calcCandleVolume, calcCandleVolume,
} from '@vegaprotocol/market-list'; } from '@vegaprotocol/market-list';
import { import {
addDecimalsFormatNumber, addDecimalsNormalizeNumber,
PriceCell, PriceCell,
signedNumberCssClass, signedNumberCssClass,
t, t,
@ -236,7 +236,7 @@ export const columns = (
value: market.data?.markPrice ? ( value: market.data?.markPrice ? (
<PriceCell <PriceCell
value={Number(market.data?.markPrice)} value={Number(market.data?.markPrice)}
valueFormatted={addDecimalsFormatNumber( valueFormatted={addDecimalsNormalizeNumber(
market.data?.markPrice.toString(), market.data?.markPrice.toString(),
market.decimalPlaces, market.decimalPlaces,
2 2
@ -300,7 +300,7 @@ export const columns = (
value: candleHigh ? ( value: candleHigh ? (
<PriceCell <PriceCell
value={Number(candleHigh)} value={Number(candleHigh)}
valueFormatted={addDecimalsFormatNumber( valueFormatted={addDecimalsNormalizeNumber(
candleHigh.toString(), candleHigh.toString(),
market.decimalPlaces, market.decimalPlaces,
2 2
@ -317,7 +317,7 @@ export const columns = (
value: candleLow ? ( value: candleLow ? (
<PriceCell <PriceCell
value={Number(candleLow)} value={Number(candleLow)}
valueFormatted={addDecimalsFormatNumber( valueFormatted={addDecimalsNormalizeNumber(
candleLow.toString(), candleLow.toString(),
market.decimalPlaces, market.decimalPlaces,
2 2
@ -332,7 +332,7 @@ export const columns = (
{ {
kind: ColumnKind.Volume, kind: ColumnKind.Volume,
value: candleVolume value: candleVolume
? addDecimalsFormatNumber( ? addDecimalsNormalizeNumber(
candleVolume.toString(), candleVolume.toString(),
market.positionDecimalPlaces, market.positionDecimalPlaces,
2 2
@ -411,7 +411,7 @@ export const columnsPositionMarkets = (
value: market.data?.markPrice ? ( value: market.data?.markPrice ? (
<PriceCell <PriceCell
value={Number(market.data.markPrice)} value={Number(market.data.markPrice)}
valueFormatted={addDecimalsFormatNumber( valueFormatted={addDecimalsNormalizeNumber(
market.data.markPrice.toString(), market.data.markPrice.toString(),
market.decimalPlaces, market.decimalPlaces,
2 2
@ -475,7 +475,7 @@ export const columnsPositionMarkets = (
value: candleHigh ? ( value: candleHigh ? (
<PriceCell <PriceCell
value={Number(candleHigh)} value={Number(candleHigh)}
valueFormatted={addDecimalsFormatNumber( valueFormatted={addDecimalsNormalizeNumber(
candleHigh.toString(), candleHigh.toString(),
market.decimalPlaces, market.decimalPlaces,
2 2
@ -492,7 +492,7 @@ export const columnsPositionMarkets = (
value: candleLow ? ( value: candleLow ? (
<PriceCell <PriceCell
value={Number(candleLow)} value={Number(candleLow)}
valueFormatted={addDecimalsFormatNumber( valueFormatted={addDecimalsNormalizeNumber(
candleLow.toString(), candleLow.toString(),
market.decimalPlaces, market.decimalPlaces,
2 2
@ -507,7 +507,7 @@ export const columnsPositionMarkets = (
{ {
kind: ColumnKind.Volume, kind: ColumnKind.Volume,
value: candleVolume value: candleVolume
? addDecimalsFormatNumber( ? addDecimalsNormalizeNumber(
candleVolume.toString(), candleVolume.toString(),
market.positionDecimalPlaces, market.positionDecimalPlaces,
2 2

View File

@ -1,8 +1,8 @@
import React from 'react';
import { formatNumber, t } from '@vegaprotocol/react-helpers';
import { Button, Dialog } from '@vegaprotocol/ui-toolkit';
import { useState } from 'react';
import { DepositContainer } from '@vegaprotocol/deposits'; import { DepositContainer } from '@vegaprotocol/deposits';
import { normalizeFormatNumber, t } from '@vegaprotocol/react-helpers';
import { Button, Dialog } from '@vegaprotocol/ui-toolkit';
import React from 'react';
import { useState } from 'react';
interface Props { interface Props {
margin: string; margin: string;
@ -30,9 +30,11 @@ export const ValidateMargin = ({
{t("You don't have enough margin available to open this position.")} {t("You don't have enough margin available to open this position.")}
</p> </p>
<p> <p>
{`${formatNumber(margin, decimals)} ${symbol} ${t( {`${normalizeFormatNumber(margin, decimals)} ${symbol} ${t(
'currently required' 'currently required'
)}, ${formatNumber(balance, decimals)} ${symbol} ${t('available')}`} )}, ${normalizeFormatNumber(balance, decimals)} ${symbol} ${t(
'available'
)}`}
</p> </p>
<Button <Button
className="center mt-2" className="center mt-2"

View File

@ -1,4 +1,5 @@
import { Tooltip } from '@vegaprotocol/ui-toolkit'; import { Tooltip } from '@vegaprotocol/ui-toolkit';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
interface DealTicketFeeDetailsProps { interface DealTicketFeeDetailsProps {
@ -20,7 +21,7 @@ export const DealTicketFeeDetails = ({
{details.map(({ label, value, labelDescription, quoteName }) => ( {details.map(({ label, value, labelDescription, quoteName }) => (
<div <div
key={label} key={label}
className="text-sm mt-2 flex justify-between items-center gap-4 flex-wrap" className="text-xs mt-2 flex justify-between items-center gap-4 flex-wrap"
> >
<div> <div>
<Tooltip description={labelDescription}> <Tooltip description={labelDescription}>

View File

@ -1,27 +1,28 @@
import { useCallback, useEffect } from 'react'; import { addDecimal, removeDecimal, t } from '@vegaprotocol/react-helpers';
import { useForm, Controller } from 'react-hook-form';
import { t, removeDecimal, addDecimal } from '@vegaprotocol/react-helpers';
import { Button, InputError } from '@vegaprotocol/ui-toolkit';
import { TypeSelector } from './type-selector';
import { SideSelector } from './side-selector';
import { DealTicketAmount } from './deal-ticket-amount';
import { TimeInForceSelector } from './time-in-force-selector';
import type { DealTicketMarketFragment } from './__generated___/DealTicket';
import { ExpirySelector } from './expiry-selector';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Schema } from '@vegaprotocol/types'; import { Schema } from '@vegaprotocol/types';
import { Button, InputError } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useCallback, useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';
import {
getFeeDetailsValues,
useFeeDealTicketDetails,
} from '../../hooks/use-fee-deal-ticket-details';
import { getDefaultOrder } from '../deal-ticket-validation'; import { getDefaultOrder } from '../deal-ticket-validation';
import { import {
isMarketInAuction, isMarketInAuction,
useOrderValidation, useOrderValidation,
} from '../deal-ticket-validation/use-order-validation'; } from '../deal-ticket-validation/use-order-validation';
import { DealTicketAmount } from './deal-ticket-amount';
import { DealTicketFeeDetails } from './deal-ticket-fee-details'; import { DealTicketFeeDetails } from './deal-ticket-fee-details';
import { import { ExpirySelector } from './expiry-selector';
useFeeDealTicketDetails, import { SideSelector } from './side-selector';
getFeeDetailsValues, import { TimeInForceSelector } from './time-in-force-selector';
} from '../../hooks/use-fee-deal-ticket-details'; import { TypeSelector } from './type-selector';
import type { DealTicketMarketFragment } from './__generated___/DealTicket';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
export type TransactionStatus = 'default' | 'pending'; export type TransactionStatus = 'default' | 'pending';
export interface DealTicketProps { export interface DealTicketProps {

View File

@ -1,22 +1,23 @@
import { FeesBreakdown } from '@vegaprotocol/market-info'; import { FeesBreakdown } from '@vegaprotocol/market-info';
import { formatNumber, t } from '@vegaprotocol/react-helpers'; import { normalizeFormatNumber, t } from '@vegaprotocol/react-helpers';
import { Schema } from '@vegaprotocol/types'; import { Schema } from '@vegaprotocol/types';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { useMemo } from 'react'; import { useMemo } from 'react';
import type { DealTicketMarketFragment } from '../components';
import { import {
NOTIONAL_SIZE_TOOLTIP_TEXT,
EST_MARGIN_TOOLTIP_TEXT,
EST_CLOSEOUT_TOOLTIP_TEXT, EST_CLOSEOUT_TOOLTIP_TEXT,
EST_MARGIN_TOOLTIP_TEXT,
NOTIONAL_SIZE_TOOLTIP_TEXT,
} from '../components/constants'; } from '../components/constants';
import { usePartyBalanceQuery } from './__generated__/PartyBalance';
import { useCalculateSlippage } from './use-calculate-slippage'; import { useCalculateSlippage } from './use-calculate-slippage';
import { useOrderCloseOut } from './use-order-closeout'; import { useOrderCloseOut } from './use-order-closeout';
import type { OrderMargin } from './use-order-margin';
import { useOrderMargin } from './use-order-margin'; import { useOrderMargin } from './use-order-margin';
import { usePartyBalanceQuery } from './__generated__/PartyBalance';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import type { DealTicketMarketFragment } from '../components';
import type { OrderMargin } from './use-order-margin';
export const useFeeDealTicketDetails = ( export const useFeeDealTicketDetails = (
order: OrderSubmissionBody['orderSubmission'], order: OrderSubmissionBody['orderSubmission'],
market: DealTicketMarketFragment market: DealTicketMarketFragment
@ -97,12 +98,12 @@ export const getFeeDetailsValues = ({
}: FeeDetails) => { }: FeeDetails) => {
const formatValue = (value: string | number | null | undefined): string => { const formatValue = (value: string | number | null | undefined): string => {
return value && !isNaN(Number(value)) return value && !isNaN(Number(value))
? formatNumber(value, market.decimalPlaces) ? normalizeFormatNumber(value, market.decimalPlaces)
: '-'; : '-';
}; };
return [ return [
{ {
label: t('Notional value'), label: t('Notional'),
value: formatValue(notionalSize), value: formatValue(notionalSize),
quoteName, quoteName,
labelDescription: NOTIONAL_SIZE_TOOLTIP_TEXT, labelDescription: NOTIONAL_SIZE_TOOLTIP_TEXT,
@ -127,14 +128,14 @@ export const getFeeDetailsValues = ({
quoteName, quoteName,
}, },
{ {
label: t('Margin required'), label: t('Margin'),
value: estMargin?.margin && `~${formatValue(estMargin?.margin)}`, value: estMargin?.margin && `~${formatValue(estMargin?.margin)}`,
quoteName, quoteName,
labelDescription: EST_MARGIN_TOOLTIP_TEXT, labelDescription: EST_MARGIN_TOOLTIP_TEXT,
}, },
{ {
label: t('Liquidation price (variable)'), label: t('Liquidation'),
value: formatValue(estCloseOut), value: estCloseOut && `~${formatValue(estCloseOut)}`,
quoteName, quoteName,
labelDescription: EST_CLOSEOUT_TOOLTIP_TEXT, labelDescription: EST_CLOSEOUT_TOOLTIP_TEXT,
}, },

View File

@ -1,13 +1,45 @@
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { import {
addDecimalsNormalizeNumber,
compactNumber,
formatNumber, formatNumber,
formatNumberPercentage, formatNumberPercentage,
toNumberParts,
isNumeric, isNumeric,
compactNumber, normalizeFormatNumber,
toNumberParts,
} from './number'; } from './number';
describe('formatNumber and formatNumberPercentage', () => { describe('number react-helpers', () => {
it.each([
{ v: new BigNumber(123000), d: 5, o: '1.23' },
{ v: new BigNumber(123000), d: 3, o: '123' },
{ 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' },
])(
'formats with addDecimalsNormalizeNumber given number correctly',
({ v, d, o }) => {
expect(addDecimalsNormalizeNumber(v.toString(), d)).toStrictEqual(o);
}
);
it.each([
{ v: new BigNumber(123.0), d: 3, o: '123' },
{ v: new BigNumber(123.123), d: 3, o: '123.123' },
{ v: new BigNumber(123.6666), d: 3, o: '123.667' },
{ v: new BigNumber(123.003), d: 6, o: '123.003' },
{ v: new BigNumber(123.003), d: 0, o: '123' },
{ v: new BigNumber(123), d: undefined, o: '123' },
{ v: new BigNumber(30000), d: undefined, o: '30,000' },
{ v: new BigNumber(3.000001), d: undefined, o: '3' },
])(
`formats with normalizeFormatNumber given number correctly`,
({ v, d, o }) => {
expect(normalizeFormatNumber(v, d)).toStrictEqual(o);
}
);
it.each([ it.each([
{ v: new BigNumber(123), d: 3, o: '123.000' }, { v: new BigNumber(123), d: 3, o: '123.000' },
{ v: new BigNumber(123.123), d: 3, o: '123.123' }, { v: new BigNumber(123.123), d: 3, o: '123.123' },
@ -17,7 +49,7 @@ describe('formatNumber and formatNumberPercentage', () => {
{ v: new BigNumber(123), d: undefined, o: '123' }, { v: new BigNumber(123), d: undefined, o: '123' },
{ v: new BigNumber(30000), d: undefined, o: '30,000' }, { v: new BigNumber(30000), d: undefined, o: '30,000' },
{ v: new BigNumber(3.000001), d: undefined, o: '3' }, { v: new BigNumber(3.000001), d: undefined, o: '3' },
])('formats given number correctly', ({ v, d, o }) => { ])('formats with formatNumber given number correctly', ({ v, d, o }) => {
expect(formatNumber(v, d)).toStrictEqual(o); expect(formatNumber(v, d)).toStrictEqual(o);
}); });

View File

@ -3,6 +3,7 @@ import { BigNumber as EthersBigNumber } from 'ethers';
import isNil from 'lodash/isNil'; import isNil from 'lodash/isNil';
import memoize from 'lodash/memoize'; import memoize from 'lodash/memoize';
import React from 'react'; import React from 'react';
import { getUserLocale } from './utils'; import { getUserLocale } from './utils';
const MAX_FRACTION_DIGITS = 20; const MAX_FRACTION_DIGITS = 20;
@ -55,6 +56,10 @@ export const getDecimalSeparator = memoize(
.find((part) => part.type === 'decimal')?.value .find((part) => part.type === 'decimal')?.value
); );
/** formatNumber will format the number with fixed decimals
* @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
*/
export const formatNumber = ( export const formatNumber = (
rawValue: string | number | BigNumber, rawValue: string | number | BigNumber,
formatDecimals = 0 formatDecimals = 0
@ -62,6 +67,23 @@ export const formatNumber = (
return getNumberFormat(formatDecimals).format(Number(rawValue)); return getNumberFormat(formatDecimals).format(Number(rawValue));
}; };
/** normalizeFormatNumber will format the number with fixed decimals, but without insignificant trailing zeros
* @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
*/
export const normalizeFormatNumber = (
rawValue: string | number | BigNumber,
formatDecimals = 0
): string => {
const numberToFormat = getNumberFormat(formatDecimals).format(
Number(rawValue)
);
// Multiplying by 1 safely removes the insignificant trailing zeros from the formatted number
return !isNaN(Number(numberToFormat))
? (Number(numberToFormat) * 1).toString()
: numberToFormat;
};
export const addDecimalsFormatNumber = ( export const addDecimalsFormatNumber = (
rawValue: string | number, rawValue: string | number,
decimalPlaces: number, decimalPlaces: number,
@ -72,6 +94,15 @@ export const addDecimalsFormatNumber = (
return formatNumber(x, formatDecimals); return formatNumber(x, formatDecimals);
}; };
export const addDecimalsNormalizeNumber = (
rawValue: string | number,
decimalPlaces: number,
formatDecimals: number = decimalPlaces
) => {
const x = addDecimal(rawValue, decimalPlaces);
return normalizeFormatNumber(x, formatDecimals);
};
export const formatNumberPercentage = (value: BigNumber, decimals?: number) => { export const formatNumberPercentage = (value: BigNumber, decimals?: number) => {
const decimalPlaces = const decimalPlaces =
typeof decimals === 'undefined' ? Math.max(value.dp() || 0, 2) : decimals; typeof decimals === 'undefined' ? Math.max(value.dp() || 0, 2) : decimals;