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:
Art 2022-08-30 10:21:27 +02:00 committed by GitHub
parent b33caa922f
commit 2970aae670
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 132 additions and 80 deletions

View File

@ -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;

View File

@ -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"
} }

View File

@ -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);
}); });
}); });

View File

@ -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]);
}; };

View File

@ -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';

View File

@ -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>

View 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);
});
});

View File

@ -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]);
}; };