fix: included origin of token, moved formatters (#1141)
* fix: included origin of token, moved formatters * fix: removed title attr, not needed * fix: added distinction between wallet and contract if they have the same key * fix: included origin of token, moved formatters * fix: removed title attr, not needed * fix: added distinction between wallet and contract if they have the same key
This commit is contained in:
parent
b33caa922f
commit
2970aae670
@ -4,7 +4,7 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import { useAnimateValue } from '../../hooks/use-animate-value';
|
import { useAnimateValue } from '../../hooks/use-animate-value';
|
||||||
import type { BigNumber } from '../../lib/bignumber';
|
import type { BigNumber } from '../../lib/bignumber';
|
||||||
import { useNumberParts } from '../../lib/format-number';
|
import { useNumberParts } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
interface WalletCardProps {
|
interface WalletCardProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
@ -631,5 +631,7 @@
|
|||||||
"Freeform proposal": "Freeform proposal",
|
"Freeform proposal": "Freeform proposal",
|
||||||
"NewProposal": "New proposal",
|
"NewProposal": "New proposal",
|
||||||
"MinProposalRequirements": "You must have at least 1 VEGA associated to make a proposal",
|
"MinProposalRequirements": "You must have at least 1 VEGA associated to make a proposal",
|
||||||
"totalSupply": "Total Supply"
|
"totalSupply": "Total Supply",
|
||||||
|
"viaWallet": "via wallet",
|
||||||
|
"viaContract": "via vesting"
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
import { BigNumber } from './bignumber';
|
import { BigNumber } from './bignumber';
|
||||||
import {
|
import { formatNumber } from './format-number';
|
||||||
formatNumber,
|
|
||||||
formatNumberPercentage,
|
|
||||||
toNumberParts,
|
|
||||||
} from './format-number';
|
|
||||||
|
|
||||||
describe('formatNumber and formatNumberPercentage', () => {
|
describe('formatNumber and formatNumberPercentage', () => {
|
||||||
it.each([
|
it.each([
|
||||||
@ -12,22 +8,9 @@ describe('formatNumber and formatNumberPercentage', () => {
|
|||||||
{ v: new BigNumber(123.123), d: 6, o: '123.123000' },
|
{ v: new BigNumber(123.123), d: 6, o: '123.123000' },
|
||||||
{ v: new BigNumber(123.123), d: 0, o: '123' },
|
{ v: new BigNumber(123.123), d: 0, o: '123' },
|
||||||
{ v: new BigNumber(123), d: undefined, o: '123.00' }, // it default to 2 decimal places
|
{ v: new BigNumber(123), d: undefined, o: '123.00' }, // it default to 2 decimal places
|
||||||
|
{ v: new BigNumber(30000), d: undefined, o: '30,000.00' },
|
||||||
|
{ v: new BigNumber(3.000001), d: undefined, o: '3.000001' },
|
||||||
])('formats given number correctly', ({ v, d, o }) => {
|
])('formats given number correctly', ({ v, d, o }) => {
|
||||||
expect(formatNumber(v, d)).toStrictEqual(o);
|
expect(formatNumber(v, d)).toStrictEqual(o);
|
||||||
expect(formatNumberPercentage(v, d)).toStrictEqual(`${o}%`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('toNumberParts', () => {
|
|
||||||
it.each([
|
|
||||||
{ v: null, d: 3, o: ['0', '000'] },
|
|
||||||
{ v: undefined, d: 3, o: ['0', '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: 6, o: ['123', '123000'] },
|
|
||||||
{ v: new BigNumber(123.123), d: 0, o: ['123', ''] },
|
|
||||||
{ v: new BigNumber(123), d: undefined, o: ['123', '000000000000000000'] },
|
|
||||||
])('returns correct tuple given the different arguments', ({ v, d, o }) => {
|
|
||||||
expect(toNumberParts(v, d)).toStrictEqual(o);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,33 +1,9 @@
|
|||||||
import React from 'react';
|
import type { BigNumber } from './bignumber';
|
||||||
import { BigNumber } from './bignumber';
|
import { formatNumber as format } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
export const formatNumber = (value: BigNumber, decimals?: number) => {
|
export const formatNumber = (value: BigNumber, decimals?: number) => {
|
||||||
const decimalPlaces =
|
return format(
|
||||||
typeof decimals === 'undefined' ? Math.max(value.dp(), 2) : decimals;
|
value,
|
||||||
return value.dp(decimalPlaces).toFormat(decimalPlaces);
|
typeof decimals === 'undefined' ? Math.max(value.dp(), 2) : decimals
|
||||||
};
|
);
|
||||||
|
|
||||||
export const formatNumberPercentage = (value: BigNumber, decimals?: number) =>
|
|
||||||
`${formatNumber(value, decimals)}%`;
|
|
||||||
|
|
||||||
export const toNumberParts = (
|
|
||||||
value: BigNumber | null | undefined,
|
|
||||||
decimals = 18
|
|
||||||
): [integers: string, decimalPlaces: string] => {
|
|
||||||
if (!value) {
|
|
||||||
return ['0', '0'.repeat(decimals)];
|
|
||||||
}
|
|
||||||
// @ts-ignore confident not undefined
|
|
||||||
const separator = BigNumber.config().FORMAT.decimalSeparator as string;
|
|
||||||
const [integers, decimalsPlaces] = formatNumber(value, decimals)
|
|
||||||
.toString()
|
|
||||||
.split(separator);
|
|
||||||
return [integers, decimalsPlaces || ''];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useNumberParts = (
|
|
||||||
value: BigNumber | null | undefined,
|
|
||||||
decimals: number
|
|
||||||
): [integers: string, decimalPlaces: string] => {
|
|
||||||
return React.useMemo(() => toNumberParts(value, decimals), [decimals, value]);
|
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
|||||||
import {
|
import {
|
||||||
formatNumber,
|
formatNumber,
|
||||||
formatNumberPercentage,
|
formatNumberPercentage,
|
||||||
} from '../../../../lib/format-number';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { useVoteInformation } from '../../hooks';
|
import { useVoteInformation } from '../../hooks';
|
||||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||||
|
@ -9,14 +9,27 @@ import { useAppState } from '../../../contexts/app-state/app-state-context';
|
|||||||
import { useRefreshAssociatedBalances } from '../../../hooks/use-refresh-associated-balances';
|
import { useRefreshAssociatedBalances } from '../../../hooks/use-refresh-associated-balances';
|
||||||
import { useRemoveStake } from './hooks';
|
import { useRemoveStake } from './hooks';
|
||||||
import type { RemoveStakePayload } from './hooks';
|
import type { RemoveStakePayload } from './hooks';
|
||||||
import { useState, useEffect, useMemo } from 'react';
|
import { useState, useEffect, useMemo, useCallback } from 'react';
|
||||||
|
import type { ChangeEvent } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import type { BigNumber } from '../../../lib/bignumber';
|
import type { BigNumber } from '../../../lib/bignumber';
|
||||||
|
import { truncateMiddle } from '../../../lib/truncate-middle';
|
||||||
|
|
||||||
type Association = {
|
type Association = {
|
||||||
|
/**
|
||||||
|
* An unique id of association (combination of staking method and wallet key)
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* A vega wallet key
|
||||||
|
*/
|
||||||
key: string;
|
key: string;
|
||||||
value: BigNumber;
|
/**
|
||||||
|
* Amount of associated tokens
|
||||||
|
*/
|
||||||
|
amount: BigNumber;
|
||||||
stakingMethod: StakingMethod;
|
stakingMethod: StakingMethod;
|
||||||
|
label: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const toListOfAssociations = (
|
const toListOfAssociations = (
|
||||||
@ -25,11 +38,13 @@ const toListOfAssociations = (
|
|||||||
): Association[] =>
|
): Association[] =>
|
||||||
Object.keys(obj)
|
Object.keys(obj)
|
||||||
.map((k) => ({
|
.map((k) => ({
|
||||||
|
id: `${stakingMethod.toLowerCase()}-${remove0x(k)}`,
|
||||||
key: remove0x(k),
|
key: remove0x(k),
|
||||||
value: obj[k],
|
amount: obj[k],
|
||||||
stakingMethod,
|
stakingMethod,
|
||||||
|
label: '',
|
||||||
}))
|
}))
|
||||||
.filter((k) => k.value.isGreaterThan(0));
|
.filter((k) => k.amount.isGreaterThan(0));
|
||||||
|
|
||||||
export const DisassociatePage = ({
|
export const DisassociatePage = ({
|
||||||
address,
|
address,
|
||||||
@ -47,11 +62,16 @@ export const DisassociatePage = ({
|
|||||||
} = useAppState();
|
} = useAppState();
|
||||||
|
|
||||||
const associations = useMemo(
|
const associations = useMemo(
|
||||||
() => [
|
() =>
|
||||||
|
[
|
||||||
...toListOfAssociations(stakingAssociations, StakingMethod.Wallet),
|
...toListOfAssociations(stakingAssociations, StakingMethod.Wallet),
|
||||||
...toListOfAssociations(vestingAssociations, StakingMethod.Contract),
|
...toListOfAssociations(vestingAssociations, StakingMethod.Contract),
|
||||||
],
|
].map((a) => ({
|
||||||
[stakingAssociations, vestingAssociations]
|
...a,
|
||||||
|
label: `${truncateMiddle(a.key)} ${t(`via${a.stakingMethod}`)}
|
||||||
|
(${formatNumber(a.amount, 18)} ${t('tokens')})`,
|
||||||
|
})),
|
||||||
|
[stakingAssociations, vestingAssociations, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -60,7 +80,7 @@ export const DisassociatePage = ({
|
|||||||
|
|
||||||
const [chosen, setChosen] = useState<Association>();
|
const [chosen, setChosen] = useState<Association>();
|
||||||
|
|
||||||
const maximum = chosen?.value || toBigNum(0, 0);
|
const maximum = chosen?.amount || toBigNum(0, 0);
|
||||||
const [amount, setAmount] = useState<string>('');
|
const [amount, setAmount] = useState<string>('');
|
||||||
|
|
||||||
const refreshBalances = useRefreshAssociatedBalances();
|
const refreshBalances = useRefreshAssociatedBalances();
|
||||||
@ -83,6 +103,18 @@ export const DisassociatePage = ({
|
|||||||
}
|
}
|
||||||
}, [txState, refreshBalances, address, chosen]);
|
}, [txState, refreshBalances, address, chosen]);
|
||||||
|
|
||||||
|
const onChange = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
if (!e.target.value) return;
|
||||||
|
const chosen = associations.find((a) => a.id === e.target.value);
|
||||||
|
if (chosen) {
|
||||||
|
setChosen(chosen);
|
||||||
|
setAmount('');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[associations]
|
||||||
|
);
|
||||||
|
|
||||||
if (txState.txState !== TxState.Default && payload) {
|
if (txState.txState !== TxState.Default && payload) {
|
||||||
return (
|
return (
|
||||||
<DisassociateTransaction
|
<DisassociateTransaction
|
||||||
@ -110,23 +142,12 @@ export const DisassociatePage = ({
|
|||||||
className="font-mono"
|
className="font-mono"
|
||||||
disabled={associations.length === 1}
|
disabled={associations.length === 1}
|
||||||
id="vega-key-selector"
|
id="vega-key-selector"
|
||||||
onChange={(e) => {
|
onChange={onChange}
|
||||||
if (!e.target.value) return;
|
value={chosen?.id}
|
||||||
const chosen = associations.find((k) => k.key === e.target.value);
|
|
||||||
if (chosen) {
|
|
||||||
setChosen(chosen);
|
|
||||||
setAmount('');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={chosen?.key}
|
|
||||||
>
|
>
|
||||||
{associations.map((k) => (
|
{associations.map((a) => (
|
||||||
<option
|
<option key={a.id} value={a.id}>
|
||||||
key={k.key}
|
{a.label}
|
||||||
value={k.key}
|
|
||||||
title={`${t(k.stakingMethod)}: ${formatNumber(k.value, 18)}`}
|
|
||||||
>
|
|
||||||
{k.key}
|
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
48
libs/react-helpers/src/lib/format/number.spec.ts
Normal file
48
libs/react-helpers/src/lib/format/number.spec.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { formatNumber, formatNumberPercentage, toNumberParts } from './number';
|
||||||
|
|
||||||
|
describe('formatNumber and formatNumberPercentage', () => {
|
||||||
|
it.each([
|
||||||
|
{ v: new BigNumber(123), d: 3, o: '123.000' },
|
||||||
|
{ v: new BigNumber(123.123), d: 3, o: '123.123' },
|
||||||
|
{ v: new BigNumber(123.6666), d: 3, o: '123.667' },
|
||||||
|
{ v: new BigNumber(123.123), d: 6, o: '123.123000' },
|
||||||
|
{ v: new BigNumber(123.123), 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 given number correctly', ({ v, d, o }) => {
|
||||||
|
expect(formatNumber(v, d)).toStrictEqual(o);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ 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: 6, o: '123.123000%' },
|
||||||
|
{ v: new BigNumber(123.123), d: 0, o: '123%' },
|
||||||
|
{ v: new BigNumber(123), d: undefined, o: '123.00%' }, // it default to 2 decimal places
|
||||||
|
{ v: new BigNumber(30000), d: undefined, o: '30,000.00%' },
|
||||||
|
{ v: new BigNumber(3.000001), d: undefined, o: '3.000001%' },
|
||||||
|
])('formats given number correctly', ({ v, d, o }) => {
|
||||||
|
expect(formatNumberPercentage(v, d)).toStrictEqual(o);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toNumberParts', () => {
|
||||||
|
it.each([
|
||||||
|
{ v: null, d: 3, o: ['0', '000'] },
|
||||||
|
{ v: undefined, d: 3, o: ['0', '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: 6, o: ['123', '123000'] },
|
||||||
|
{ v: new BigNumber(123.123), d: 0, o: ['123', ''] },
|
||||||
|
{ v: new BigNumber(123), d: undefined, o: ['123', '000000000000000000'] },
|
||||||
|
{
|
||||||
|
v: new BigNumber(30000),
|
||||||
|
d: undefined,
|
||||||
|
o: ['30,000', '000000000000000000'],
|
||||||
|
},
|
||||||
|
])('returns correct tuple given the different arguments', ({ v, d, o }) => {
|
||||||
|
expect(toNumberParts(v, d)).toStrictEqual(o);
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,7 @@
|
|||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
import { BigNumber as EthersBigNumber } from 'ethers';
|
import { BigNumber as EthersBigNumber } from 'ethers';
|
||||||
import memoize from 'lodash/memoize';
|
import memoize from 'lodash/memoize';
|
||||||
|
import React from 'react';
|
||||||
import { getUserLocale } from './utils';
|
import { getUserLocale } from './utils';
|
||||||
|
|
||||||
export function toDecimal(numberOfDecimals: number) {
|
export function toDecimal(numberOfDecimals: number) {
|
||||||
@ -66,5 +67,26 @@ export const addDecimalsFormatNumber = (
|
|||||||
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(), 2) : decimals;
|
typeof decimals === 'undefined' ? Math.max(value.dp(), 2) : decimals;
|
||||||
return `${value.dp(decimalPlaces).toFormat(decimalPlaces)}%`;
|
return `${formatNumber(value, decimalPlaces)}%`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toNumberParts = (
|
||||||
|
value: BigNumber | null | undefined,
|
||||||
|
decimals = 18
|
||||||
|
): [integers: string, decimalPlaces: string] => {
|
||||||
|
if (!value) {
|
||||||
|
return ['0', '0'.repeat(decimals)];
|
||||||
|
}
|
||||||
|
const separator = getDecimalSeparator() || '.';
|
||||||
|
const [integers, decimalsPlaces] = formatNumber(value, decimals)
|
||||||
|
.toString()
|
||||||
|
.split(separator);
|
||||||
|
return [integers, decimalsPlaces || ''];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useNumberParts = (
|
||||||
|
value: BigNumber | null | undefined,
|
||||||
|
decimals: number
|
||||||
|
): [integers: string, decimalPlaces: string] => {
|
||||||
|
return React.useMemo(() => toNumberParts(value, decimals), [decimals, value]);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user