diff --git a/apps/trading-e2e/src/integration/withdraw.cy.ts b/apps/trading-e2e/src/integration/withdraw.cy.ts
index 00329c0bb..47989bf5d 100644
--- a/apps/trading-e2e/src/integration/withdraw.cy.ts
+++ b/apps/trading-e2e/src/integration/withdraw.cy.ts
@@ -77,18 +77,24 @@ describe('withdraw', { tags: '@smoke' }, () => {
},
});
selectAsset(asset1Name);
- cy.getByTestId('balance-available')
- .should('contain.text', 'Balance available')
- .find('td')
- .should('have.text', '1,000.00000');
- cy.getByTestId('withdrawal-threshold')
- .should('contain.text', 'Delayed withdrawal threshold')
- .find('td')
- .should('contain.text', '1m+');
- cy.getByTestId('delay-time')
- .should('contain.text', 'Delay time')
- .find('td')
- .should('have.text', 'None');
+ cy.getByTestId('BALANCE_AVAILABLE_label').should(
+ 'contain.text',
+ 'Balance available'
+ );
+ cy.getByTestId('BALANCE_AVAILABLE_value').should(
+ 'have.text',
+ '1,000.00000'
+ );
+ cy.getByTestId('WITHDRAWAL_THRESHOLD_label').should(
+ 'contain.text',
+ 'Delayed withdrawal threshold'
+ );
+ cy.getByTestId('WITHDRAWAL_THRESHOLD_value').should(
+ 'contain.text',
+ '100.00000'
+ );
+ cy.getByTestId('DELAY_TIME_label').should('contain.text', 'Delay time');
+ cy.getByTestId('DELAY_TIME_value').should('have.text', 'None');
cy.get(amountField).clear().type('10');
cy.getByTestId(submitWithdrawBtn).click();
cy.getByTestId('dialog-title').should(
diff --git a/libs/deposits/src/lib/deposit-form.spec.tsx b/libs/deposits/src/lib/deposit-form.spec.tsx
index 4c4632918..29fa82f8e 100644
--- a/libs/deposits/src/lib/deposit-form.spec.tsx
+++ b/libs/deposits/src/lib/deposit-form.spec.tsx
@@ -227,20 +227,12 @@ describe('Deposit form', () => {
);
// Check deposit limit is displayed
- expect(
- screen.getByText('Balance available', { selector: 'th' })
- .nextElementSibling
- ).toHaveTextContent(balance.toString());
- expect(
- screen.getByText('Maximum total deposit amount', { selector: 'th' })
- .nextElementSibling
- ).toHaveTextContent(max.toString());
- expect(
- screen.getByText('Deposited', { selector: 'th' }).nextElementSibling
- ).toHaveTextContent(deposited.toString());
- expect(
- screen.getByText('Remaining', { selector: 'th' }).nextElementSibling
- ).toHaveTextContent(max.minus(deposited).toString());
+ expect(screen.getByTestId('BALANCE_AVAILABLE_value')).toHaveTextContent(
+ '50'
+ );
+ expect(screen.getByTestId('MAX_LIMIT_value')).toHaveTextContent('20');
+ expect(screen.getByTestId('DEPOSITED_value')).toHaveTextContent('10');
+ expect(screen.getByTestId('REMAINING_value')).toHaveTextContent('10');
fireEvent.change(screen.getByLabelText('Amount'), {
target: { value: '8' },
diff --git a/libs/deposits/src/lib/deposit-form.tsx b/libs/deposits/src/lib/deposit-form.tsx
index 54143ee32..08aa7d3f5 100644
--- a/libs/deposits/src/lib/deposit-form.tsx
+++ b/libs/deposits/src/lib/deposit-form.tsx
@@ -220,7 +220,12 @@ export const DepositForm = ({
{selectedAsset && max && deposited && (
-
+
)}
diff --git a/libs/deposits/src/lib/deposit-limits.tsx b/libs/deposits/src/lib/deposit-limits.tsx
index 4bfe63249..c026fe1ee 100644
--- a/libs/deposits/src/lib/deposit-limits.tsx
+++ b/libs/deposits/src/lib/deposit-limits.tsx
@@ -1,60 +1,65 @@
-import { formatNumber, t } from '@vegaprotocol/react-helpers';
+import type { Asset } from '@vegaprotocol/assets';
+import { compactNumber, t } from '@vegaprotocol/react-helpers';
+import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import type BigNumber from 'bignumber.js';
+// Note: all of the values here are with correct asset's decimals
+// See `libs/deposits/src/lib/use-deposit-balances.ts`
+
interface DepositLimitsProps {
max: BigNumber;
deposited: BigNumber;
+ asset: Asset;
balance?: BigNumber;
}
export const DepositLimits = ({
max,
deposited,
+ asset,
balance,
}: DepositLimitsProps) => {
- let maxLimit = '';
- if (max.isEqualTo(Infinity)) {
- maxLimit = t('No limit');
- } else if (max.isGreaterThan(1_000_000)) {
- maxLimit = t('1m+');
- } else {
- maxLimit = max.toString();
- }
-
- let remaining = '';
- if (deposited.isEqualTo(0)) {
- remaining = maxLimit;
- } else {
- const amountRemaining = max.minus(deposited);
- remaining = amountRemaining.isGreaterThan(1_000_000)
- ? t('1m+')
- : amountRemaining.toString();
- }
+ const limits = [
+ {
+ key: 'BALANCE_AVAILABLE',
+ label: t('Balance available'),
+ rawValue: balance,
+ value: balance ? compactNumber(balance, asset.decimals) : '-',
+ },
+ {
+ key: 'MAX_LIMIT',
+ label: t('Maximum total deposit amount'),
+ rawValue: max,
+ value: compactNumber(max, asset.decimals),
+ },
+ {
+ key: 'DEPOSITED',
+ label: t('Deposited'),
+ rawValue: deposited,
+ value: compactNumber(deposited, asset.decimals),
+ },
+ {
+ key: 'REMAINING',
+ label: t('Remaining'),
+ rawValue: max.minus(deposited),
+ value: compactNumber(max.minus(deposited), asset.decimals),
+ },
+ ];
return (
-
-
-
- {t('Balance available')} |
-
- {balance ? formatNumber(balance) : '-'}
- |
-
-
-
- {t('Maximum total deposit amount')}
- |
- {maxLimit} |
-
-
- {t('Deposited')} |
- {formatNumber(deposited)} |
-
-
- {t('Remaining')} |
- {remaining} |
-
-
-
+
+ {limits.map(({ key, label, rawValue, value }) => (
+
+ {label}
+
+ {value}
+
+
+ ))}
+
);
};
diff --git a/libs/react-helpers/src/lib/format/number.spec.ts b/libs/react-helpers/src/lib/format/number.spec.tsx
similarity index 53%
rename from libs/react-helpers/src/lib/format/number.spec.ts
rename to libs/react-helpers/src/lib/format/number.spec.tsx
index 331b3f336..cf5d4ff34 100644
--- a/libs/react-helpers/src/lib/format/number.spec.ts
+++ b/libs/react-helpers/src/lib/format/number.spec.tsx
@@ -4,6 +4,7 @@ import {
formatNumberPercentage,
toNumberParts,
isNumeric,
+ compactNumber,
} from './number';
describe('formatNumber and formatNumberPercentage', () => {
@@ -89,3 +90,93 @@ describe('isNumeric', () => {
}
);
});
+
+describe('compactNumber', () => {
+ const short: [BigNumber, string | JSX.Element, number | 'infer'][] = [
+ [new BigNumber(Infinity), '∞', 'infer'],
+ [new BigNumber(-Infinity), '-∞', 'infer'],
+ [new BigNumber(0), '0', 'infer'],
+ [new BigNumber(1), '1', 'infer'],
+ [new BigNumber(100), '100', 'infer'],
+ [new BigNumber(100.456601), '100.456601', 'infer'],
+ [new BigNumber(1_000), '1,000', 'infer'],
+ [new BigNumber(999_999), '999,999', 'infer'],
+ [new BigNumber(1_000_000), '1M', 'infer'],
+ [new BigNumber(100_000_000), '100M', 'infer'],
+ [new BigNumber(1_000_000_000), '1B', 'infer'],
+ [new BigNumber(1_000_000_000_000), '1T', 'infer'],
+ [new BigNumber(3.23e12), '3.23T', 2],
+ [new BigNumber(3.23e12), '3.23000T', 5],
+ [
+ new BigNumber(3.23e24),
+
+ 3.23000{' '}
+
+ × 1024
+
+ ,
+ 5,
+ ],
+ [
+ new BigNumber(1.579208923731619e59),
+
+ 1.57921{' '}
+
+ × 10
+ 59
+
+ ,
+ 5,
+ ],
+ ];
+ it.each(short)(
+ 'compacts %d to %p (decimal places: %p)',
+ (input, output, decimals) => {
+ expect(compactNumber(input, decimals)).toEqual(output);
+ }
+ );
+
+ const long: [BigNumber, string | JSX.Element, number | 'infer'][] = [
+ [new BigNumber(Infinity), '∞', 'infer'],
+ [new BigNumber(-Infinity), '-∞', 'infer'],
+ [new BigNumber(0), '0', 'infer'],
+ [new BigNumber(1), '1', 'infer'],
+ [new BigNumber(100), '100', 'infer'],
+ [new BigNumber(100.456601), '100.456601', 'infer'],
+ [new BigNumber(1_000), '1,000', 'infer'],
+ [new BigNumber(999_999), '999,999', 'infer'],
+ [new BigNumber(1_000_000), '1 million', 'infer'],
+ [new BigNumber(100_000_000), '100 million', 'infer'],
+ [new BigNumber(1_000_000_000), '1 billion', 'infer'],
+ [new BigNumber(1_000_000_000_000), '1 trillion', 'infer'],
+ [new BigNumber(3.23e12), '3.23 trillion', 2],
+ [new BigNumber(3.23e12), '3.23000 trillion', 5],
+ [
+ new BigNumber(3.23e24),
+
+ 3.23000{' '}
+
+ × 1024
+
+ ,
+ 5,
+ ],
+ [
+ new BigNumber(1.579208923731619e59),
+
+ 1.57921{' '}
+
+ × 10
+ 59
+
+ ,
+ 5,
+ ],
+ ];
+ it.each(long)(
+ 'compacts %d to %p (decimal places: %p)',
+ (input, output, decimals) => {
+ expect(compactNumber(input, decimals, 'long')).toEqual(output);
+ }
+ );
+});
diff --git a/libs/react-helpers/src/lib/format/number.ts b/libs/react-helpers/src/lib/format/number.tsx
similarity index 59%
rename from libs/react-helpers/src/lib/format/number.ts
rename to libs/react-helpers/src/lib/format/number.tsx
index 2f309cad6..2c93b8279 100644
--- a/libs/react-helpers/src/lib/format/number.ts
+++ b/libs/react-helpers/src/lib/format/number.tsx
@@ -100,3 +100,67 @@ export const useNumberParts = (
export const isNumeric = (
value?: string | number | BigNumber | null
): value is NonNullable => /^-?\d*\.?\d+$/.test(String(value));
+
+const INFINITY = '∞';
+const DEFAULT_COMPACT_ABOVE = 1_000_000;
+const DEFAULT_COMPACT_CAP = new BigNumber(1e24);
+/**
+ * Compacts given number to human readable format.
+ * @param number
+ * @param decimals Number of decimal places
+ * @param compactDisplay Display mode; short -> 1e6 == 1M; ling -> 1e6 1 million
+ * @param compactAbove Compact number above threshold
+ * @param cap Use scientific notation above threshold
+ */
+export const compactNumber = (
+ number: BigNumber,
+ decimals: number | 'infer' = 'infer',
+ compactDisplay: 'short' | 'long' = 'short',
+ compactAbove = DEFAULT_COMPACT_ABOVE,
+ cap = DEFAULT_COMPACT_CAP
+) => {
+ if (!number.isFinite()) return `${number.isNegative() ? '-' : ''}${INFINITY}`;
+
+ const decimalPlaces =
+ (decimals === 'infer' ? number.decimalPlaces() : decimals) || 0;
+
+ if (number.isLessThan(DEFAULT_COMPACT_ABOVE)) {
+ return formatNumber(number, decimalPlaces);
+ }
+
+ /**
+ * Note: it compacts number up to 1_000_000_000_000 (1e12) -> 1T, all above is formatted as iteration of T.
+ * Example: 1.579208923731619e59 -> 157,920,892,373,161,900,000,000,000,000,000,000,000,000,000,000T
+ */
+ const compactNumFormat = new Intl.NumberFormat(getUserLocale(), {
+ minimumFractionDigits: Math.max(0, decimalPlaces),
+ maximumFractionDigits: Math.max(0, decimalPlaces),
+ notation: 'compact',
+ compactDisplay,
+ });
+ const scientificNumFormat = new Intl.NumberFormat(getUserLocale(), {
+ minimumFractionDigits: Math.max(0, decimalPlaces),
+ maximumFractionDigits: Math.max(0, decimalPlaces),
+ notation: 'scientific',
+ });
+
+ if (number.isGreaterThan(DEFAULT_COMPACT_CAP)) {
+ const r = /E(\d+)$/i;
+ const formatted = scientificNumFormat.format(Number(number));
+ const eNotation = formatted.match(r);
+ if (eNotation && eNotation.length > 1) {
+ const power = eNotation[1];
+ return (
+
+ {formatted.replace(r, '')}{' '}
+
+ × 10
+ {power}
+
+
+ );
+ }
+ }
+
+ return compactNumFormat.format(Number(number));
+};
diff --git a/libs/withdraws/src/lib/withdraw-form.tsx b/libs/withdraws/src/lib/withdraw-form.tsx
index ccf90a0fd..60eac34e9 100644
--- a/libs/withdraws/src/lib/withdraw-form.tsx
+++ b/libs/withdraws/src/lib/withdraw-form.tsx
@@ -1,5 +1,4 @@
import type { Asset } from '@vegaprotocol/assets';
-import { formatNumber } from '@vegaprotocol/react-helpers';
import {
ethereumAddress,
minSafe,
@@ -147,7 +146,8 @@ export const WithdrawForm = ({
amount={amount}
threshold={threshold}
delay={delay}
- balance={formatNumber(balance, selectedAsset.decimals)}
+ balance={balance}
+ asset={selectedAsset}
/>
)}
diff --git a/libs/withdraws/src/lib/withdraw-limits.tsx b/libs/withdraws/src/lib/withdraw-limits.tsx
index cd94fc799..2432321e0 100644
--- a/libs/withdraws/src/lib/withdraw-limits.tsx
+++ b/libs/withdraws/src/lib/withdraw-limits.tsx
@@ -1,12 +1,15 @@
-import { t } from '@vegaprotocol/react-helpers';
+import type { Asset } from '@vegaprotocol/assets';
+import { compactNumber, t } from '@vegaprotocol/react-helpers';
+import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import BigNumber from 'bignumber.js';
import { formatDistanceToNow } from 'date-fns';
interface WithdrawLimitsProps {
amount: string;
threshold: BigNumber;
- balance: string;
+ balance: BigNumber;
delay: number | undefined;
+ asset: Asset;
}
export const WithdrawLimits = ({
@@ -14,40 +17,47 @@ export const WithdrawLimits = ({
threshold,
balance,
delay,
+ asset,
}: WithdrawLimitsProps) => {
- let text = '';
-
- if (threshold.isEqualTo(Infinity)) {
- text = t('No limit');
- } else if (threshold.isGreaterThan(1_000_000)) {
- text = t('1m+');
- } else {
- text = threshold.toString();
- }
-
const delayTime =
new BigNumber(amount).isGreaterThan(threshold) && delay
? formatDistanceToNow(Date.now() + delay * 1000)
: t('None');
+ const limits = [
+ {
+ key: 'BALANCE_AVAILABLE',
+ label: t('Balance available'),
+ rawValue: balance,
+ value: balance ? compactNumber(balance, asset.decimals) : '-',
+ },
+ {
+ key: 'WITHDRAWAL_THRESHOLD',
+ label: t('Delayed withdrawal threshold'),
+ rawValue: threshold,
+ value: compactNumber(threshold, asset.decimals),
+ },
+ {
+ key: 'DELAY_TIME',
+ label: t('Delay time'),
+ value: delayTime,
+ },
+ ];
+
return (
-
-
-
- {t('Balance available')} |
- {balance} |
-
-
-
- {t('Delayed withdrawal threshold')}
- |
- {text} |
-
-
- {t('Delay time')} |
- {delayTime} |
-
-
-
+
+ {limits.map(({ key, label, rawValue, value }) => (
+
+ {label}
+
+ {value}
+
+
+ ))}
+
);
};