diff --git a/apps/trading-e2e/src/integration/trading-deal-ticket-order.cy.ts b/apps/trading-e2e/src/integration/trading-deal-ticket-order.cy.ts index 00d4f1c9d..5ac9b5133 100644 --- a/apps/trading-e2e/src/integration/trading-deal-ticket-order.cy.ts +++ b/apps/trading-e2e/src/integration/trading-deal-ticket-order.cy.ts @@ -100,9 +100,8 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => { .within(() => { cy.get('[data-state="closed"]').should( 'have.text', - 'Total margin available' + 'Total margin available100,000.01 tDAI' ); - cy.get('.text-neutral-500').should('have.text', '100,000.01 tDAI'); }); }); }); diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx index 52969be14..4309d0069 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx @@ -33,24 +33,35 @@ export const DealTicketFeeDetails = (props: FeeDetails) => { const details = getFeeDetailsValues(props); return (
- {details.map(({ label, value, labelDescription, symbol, indent }) => ( -
-
- -
{label}
+ {details.map( + ({ + label, + value, + labelDescription, + symbol, + indent, + formattedValue, + }) => ( +
+
+ +
{label}
+
+
+ +
{`${ + formattedValue ?? '-' + } ${symbol || ''}`}
-
{`${ - value ?? '-' - } ${symbol || ''}`}
-
- ))} + ) + )}
); }; diff --git a/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.spec.tsx b/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.spec.tsx new file mode 100644 index 000000000..f5a899fbf --- /dev/null +++ b/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.spec.tsx @@ -0,0 +1,68 @@ +import { formatRange, formatValue } from './use-fee-deal-ticket-details'; + +describe('useFeeDealTicketDetails', () => { + it.each([ + { v: 123000, d: 5, o: '1.23' }, + { v: 123000, d: 3, o: '123.00' }, + { v: 123000, d: 1, o: '12,300.0' }, + { v: 123001000, d: 2, o: '1,230,010.00' }, + { v: 123001, d: 2, o: '1,230.01' }, + { + v: '123456789123456789', + d: 10, + o: '12,345,678.91234568', + }, + ])('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: 123001, d: 2, o: '1,230', q: '100' }, + { v: 123001, d: 2, o: '1,230.01', q: '0.1' }, + { + v: '123456789123456789', + d: 10, + o: '12,345,678.9123457', + q: '0.00003846', + }, + ])( + 'formats with formatValue with quantum given number correctly', + ({ v, d, o, q }) => { + expect(formatValue(v.toString(), d, q)).toStrictEqual(o); + } + ); + + it.each([ + { min: 123000, max: 12300011111, d: 5, o: '1.23 - 123,000.111', q: '0.1' }, + { + min: 123000, + max: 12300011111, + d: 3, + o: '123.00 - 12,300,011.111', + q: '0.1', + }, + { + min: 123000, + max: 12300011111, + d: 1, + o: '12,300.00 - 1,230,001,111.10', + q: '0.1', + }, + { + min: 123001000, + max: 12300011111, + d: 2, + o: '1,230,010 - 123,000,111', + q: '100', + }, + ])( + 'formats with formatValue with quantum given number correctly', + ({ min, max, d, o, q }) => { + expect(formatRange(min, max, d, q)).toStrictEqual(o); + } + ); +}); diff --git a/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.tsx b/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.tsx index df295e562..735c2b54f 100644 --- a/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.tsx +++ b/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.tsx @@ -1,5 +1,9 @@ import { FeesBreakdown } from '@vegaprotocol/markets'; -import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils'; +import { + addDecimalsFormatNumber, + addDecimalsFormatNumberQuantum, + isNumeric, +} from '@vegaprotocol/utils'; import { t } from '@vegaprotocol/i18n'; import { useVegaWallet } from '@vegaprotocol/wallet'; import type { Market } from '@vegaprotocol/markets'; @@ -52,21 +56,25 @@ export interface FeeDetails { } const emptyValue = '-'; -const formatValue = ( + +export const formatValue = ( value: string | number | null | undefined, - formatDecimals: number + formatDecimals: number, + quantum?: string ): string => { - return isNumeric(value) - ? addDecimalsFormatNumber(value, formatDecimals) - : emptyValue; + if (!isNumeric(value)) return emptyValue; + if (!quantum) return addDecimalsFormatNumber(value, formatDecimals); + return addDecimalsFormatNumberQuantum(value, formatDecimals, quantum); }; -const formatRange = ( + +export const formatRange = ( min: string | number | null | undefined, max: string | number | null | undefined, - formatDecimals: number + formatDecimals: number, + quantum?: string ) => { - const minFormatted = formatValue(min, formatDecimals); - const maxFormatted = formatValue(max, formatDecimals); + const minFormatted = formatValue(min, formatDecimals, quantum); + const maxFormatted = formatValue(max, formatDecimals, quantum); if (minFormatted !== maxFormatted) { return `${minFormatted} - ${maxFormatted}`; } @@ -93,9 +101,12 @@ export const getFeeDetailsValues = ({ BigInt(generalAccountBalance || '0') + BigInt(marginAccountBalance || '0'); const assetDecimals = market.tradableInstrument.instrument.product.settlementAsset.decimals; + const quantum = + market.tradableInstrument.instrument.product.settlementAsset.quantum; const details: { label: string; value?: string | null; + formattedValue?: string | null; symbol: string; indent?: boolean; labelDescription?: React.ReactNode; @@ -103,6 +114,7 @@ export const getFeeDetailsValues = ({ { label: t('Notional'), value: formatValue(notionalSize, assetDecimals), + formattedValue: formatValue(notionalSize, assetDecimals, quantum), symbol: assetSymbol, labelDescription: NOTIONAL_SIZE_TOOLTIP_TEXT(assetSymbol), }, @@ -111,6 +123,9 @@ export const getFeeDetailsValues = ({ value: feeEstimate?.totalFeeAmount && `~${formatValue(feeEstimate?.totalFeeAmount, assetDecimals)}`, + formattedValue: + feeEstimate?.totalFeeAmount && + `~${formatValue(feeEstimate?.totalFeeAmount, assetDecimals, quantum)}`, labelDescription: ( <> @@ -154,6 +169,12 @@ export const getFeeDetailsValues = ({ } details.push({ label: t('Margin required'), + formattedValue: formatRange( + marginRequiredBestCase, + marginRequiredWorstCase, + assetDecimals, + quantum + ), value: formatRange( marginRequiredBestCase, marginRequiredWorstCase, @@ -172,12 +193,13 @@ export const getFeeDetailsValues = ({ details.push({ indent: true, label: t('Total margin available'), + formattedValue: formatValue(totalMarginAvailable, assetDecimals, quantum), value: formatValue(totalMarginAvailable, assetDecimals), symbol: assetSymbol, labelDescription: TOTAL_MARGIN_AVAILABLE( - formatValue(generalAccountBalance, assetDecimals), - formatValue(marginAccountBalance, assetDecimals), - formatValue(currentMaintenanceMargin, assetDecimals), + formatValue(generalAccountBalance, assetDecimals, quantum), + formatValue(marginAccountBalance, assetDecimals, quantum), + formatValue(currentMaintenanceMargin, assetDecimals, quantum), assetSymbol ), }); @@ -203,6 +225,16 @@ export const getFeeDetailsValues = ({ : '0', assetDecimals ), + formattedValue: formatRange( + deductionFromCollateralBestCase > 0 + ? deductionFromCollateralBestCase.toString() + : '0', + deductionFromCollateralWorstCase > 0 + ? deductionFromCollateralWorstCase.toString() + : '0', + assetDecimals, + quantum + ), symbol: assetSymbol, labelDescription: DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT(assetSymbol), }); @@ -214,6 +246,12 @@ export const getFeeDetailsValues = ({ marginEstimate?.worstCase.initialLevel, assetDecimals ), + formattedValue: formatRange( + marginEstimate?.bestCase.initialLevel, + marginEstimate?.worstCase.initialLevel, + assetDecimals, + quantum + ), symbol: assetSymbol, labelDescription: EST_TOTAL_MARGIN_TOOLTIP_TEXT, }); @@ -223,9 +261,11 @@ export const getFeeDetailsValues = ({ value: formatValue(marginAccountBalance, assetDecimals), symbol: assetSymbol, labelDescription: MARGIN_ACCOUNT_TOOLTIP_TEXT, + formattedValue: formatValue(marginAccountBalance, assetDecimals, quantum), }); let liquidationPriceEstimate = emptyValue; + let liquidationPriceEstimateFormatted; if (liquidationEstimate) { const liquidationEstimateBestCaseIncludingBuyOrders = BigInt( @@ -262,11 +302,24 @@ export const getFeeDetailsValues = ({ ).toString(), assetDecimals ); + liquidationPriceEstimateFormatted = formatRange( + (liquidationEstimateBestCase < liquidationEstimateWorstCase + ? liquidationEstimateBestCase + : liquidationEstimateWorstCase + ).toString(), + (liquidationEstimateBestCase > liquidationEstimateWorstCase + ? liquidationEstimateBestCase + : liquidationEstimateWorstCase + ).toString(), + assetDecimals, + quantum + ); } details.push({ label: t('Liquidation price estimate'), value: liquidationPriceEstimate, + formattedValue: liquidationPriceEstimateFormatted, symbol: assetSymbol, labelDescription: LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT, }); diff --git a/libs/deal-ticket/src/test-helpers.ts b/libs/deal-ticket/src/test-helpers.ts index a332ade69..1081752f8 100644 --- a/libs/deal-ticket/src/test-helpers.ts +++ b/libs/deal-ticket/src/test-helpers.ts @@ -32,6 +32,7 @@ export function generateMarket(override?: PartialDeep): Market { symbol: 'tDAI', name: 'tDAI', decimals: 5, + quantum: '1', __typename: 'Asset', }, dataSourceSpecForTradingTermination: { diff --git a/libs/fills/src/lib/test-helpers.ts b/libs/fills/src/lib/test-helpers.ts index 3d815b355..1654dabb5 100644 --- a/libs/fills/src/lib/test-helpers.ts +++ b/libs/fills/src/lib/test-helpers.ts @@ -75,6 +75,7 @@ export const generateFill = (override?: PartialDeep) => { name: 'assset-id', symbol: 'SYM', decimals: 18, + quantum: '1', }, quoteName: '', dataSourceSpecForTradingTermination: { diff --git a/libs/markets/src/lib/__generated__/markets.ts b/libs/markets/src/lib/__generated__/markets.ts index 2e3432339..addf669a7 100644 --- a/libs/markets/src/lib/__generated__/markets.ts +++ b/libs/markets/src/lib/__generated__/markets.ts @@ -7,12 +7,12 @@ export type DataSourceFilterFragment = { __typename?: 'Filter', key: { __typenam export type DataSourceSpecFragment = { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }; -export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } }; +export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } }; export type MarketsQueryVariables = Types.Exact<{ [key: string]: never; }>; -export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null }; +export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null }; export const DataSourceFilterFragmentDoc = gql` fragment DataSourceFilter on Filter { @@ -77,6 +77,7 @@ export const MarketFieldsFragmentDoc = gql` symbol name decimals + quantum } quoteName dataSourceSpecForTradingTermination { diff --git a/libs/markets/src/lib/markets.graphql b/libs/markets/src/lib/markets.graphql index 74ae46100..84e25a6e2 100644 --- a/libs/markets/src/lib/markets.graphql +++ b/libs/markets/src/lib/markets.graphql @@ -58,6 +58,7 @@ fragment MarketFields on Market { symbol name decimals + quantum } quoteName dataSourceSpecForTradingTermination { diff --git a/libs/markets/src/lib/markets.mock.ts b/libs/markets/src/lib/markets.mock.ts index b634bd0d5..44ff183fe 100644 --- a/libs/markets/src/lib/markets.mock.ts +++ b/libs/markets/src/lib/markets.mock.ts @@ -60,6 +60,7 @@ export const createMarketFragment = ( symbol: 'tDAI', name: 'tDAI', decimals: 5, + quantum: '1', __typename: 'Asset', }, dataSourceSpecForTradingTermination: { diff --git a/libs/orders/src/lib/components/mocks/generate-orders.ts b/libs/orders/src/lib/components/mocks/generate-orders.ts index 1be8e2774..60ef14bf4 100644 --- a/libs/orders/src/lib/components/mocks/generate-orders.ts +++ b/libs/orders/src/lib/components/mocks/generate-orders.ts @@ -47,6 +47,7 @@ export const generateOrder = (partialOrder?: PartialDeep) => { decimals: 1, symbol: 'XYZ', name: 'XYZ', + quantum: '1', }, dataSourceSpecForTradingTermination: { __typename: 'DataSourceSpec', diff --git a/libs/utils/src/lib/format/number.spec.ts b/libs/utils/src/lib/format/number.spec.ts index 3c2024d38..621b1decd 100644 --- a/libs/utils/src/lib/format/number.spec.ts +++ b/libs/utils/src/lib/format/number.spec.ts @@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js'; import { addDecimalsFormatNumber, + addDecimalsFormatNumberQuantum, formatNumber, formatNumberPercentage, isNumeric, @@ -23,6 +24,28 @@ 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', q: 100 }, + { v: new BigNumber(123001), d: 2, o: '1,230.01', q: 0.1 }, + { + v: BigNumber('123456789123456789'), + d: 10, + o: '12,345,678.9123457', + q: '0.00003846', + }, + ])( + 'formats with addDecimalsFormatNumberQuantum given number correctly', + ({ v, d, o, q }) => { + expect(addDecimalsFormatNumberQuantum(v.toString(), d, q)).toStrictEqual( + o + ); + } + ); + it.each([ { v: new BigNumber(123), d: 3, o: '123.00' }, { v: new BigNumber(123.123), d: 3, o: '123.123' }, diff --git a/libs/utils/src/lib/format/number.ts b/libs/utils/src/lib/format/number.ts index 3cbf5a402..55e34cb1c 100644 --- a/libs/utils/src/lib/format/number.ts +++ b/libs/utils/src/lib/format/number.ts @@ -90,6 +90,18 @@ export const formatNumberFixed = ( return getFixedNumberFormat(formatDecimals).format(Number(rawValue)); }; +export const addDecimalsFormatNumberQuantum = ( + rawValue: string | number, + decimalPlaces: number, + quantum: number | string +) => { + if (isNaN(Number(quantum))) { + return addDecimalsFormatNumber(rawValue, decimalPlaces); + } + const numberDP = Math.max(0, Math.log10(100 / Number(quantum))); + return addDecimalsFormatNumber(rawValue, decimalPlaces, Math.ceil(numberDP)); +}; + export const addDecimalsFormatNumber = ( rawValue: string | number, decimalPlaces: number, diff --git a/libs/withdraws/src/lib/withdraw-form-container.spec.tsx b/libs/withdraws/src/lib/withdraw-form-container.spec.tsx index 69545b756..78ee7d7e4 100644 --- a/libs/withdraws/src/lib/withdraw-form-container.spec.tsx +++ b/libs/withdraws/src/lib/withdraw-form-container.spec.tsx @@ -165,6 +165,7 @@ describe('WithdrawFormContainer', () => { name: 'asset-id', symbol: 'tUSDC', decimals: 5, + quantum: '1', }, dataSourceSpecForTradingTermination: { __typename: 'DataSourceSpec',