Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fd23c4adb | ||
|
|
f2f01c8be7 | ||
|
|
e5d61a7475 | ||
|
|
9f6758bf86 | ||
|
|
b5e23c0223 |
@@ -82,32 +82,6 @@ describe('Txs infinite list item', () => {
|
||||
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders data even with missing time', () => {
|
||||
render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<table>
|
||||
<tbody>
|
||||
<TxsInfiniteListItem
|
||||
type="testType"
|
||||
submitter="testPubKey"
|
||||
hash="testTxHash"
|
||||
block="1"
|
||||
code={0}
|
||||
command={{}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByTestId('tx-hash')).toHaveTextContent('testTxHash');
|
||||
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
||||
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
||||
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
||||
expect(screen.getByTestId('tx-time')).toHaveTextContent('-');
|
||||
});
|
||||
|
||||
it('renders data correctly', () => {
|
||||
render(
|
||||
<MockedProvider>
|
||||
@@ -121,7 +95,6 @@ describe('Txs infinite list item', () => {
|
||||
block="1"
|
||||
code={0}
|
||||
command={{}}
|
||||
createdAt="1970-11-01T18:07:15Z"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -132,6 +105,5 @@ describe('Txs infinite list item', () => {
|
||||
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
||||
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
||||
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
||||
expect(screen.getByTestId('tx-time').textContent).toMatch(/years ago/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,6 @@ import { PartyLink } from '../links';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import type { Screen } from '@vegaprotocol/react-helpers';
|
||||
import { useMemo } from 'react';
|
||||
import { TimeAgo } from '../time-ago';
|
||||
|
||||
const DEFAULT_TRUNCATE_LENGTH = 7;
|
||||
|
||||
@@ -33,7 +32,6 @@ export const TxsInfiniteListItem = ({
|
||||
type,
|
||||
block,
|
||||
command,
|
||||
createdAt,
|
||||
}: Partial<BlockExplorerTransactionResult>) => {
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const idTruncateLength = useMemo(
|
||||
@@ -87,11 +85,6 @@ export const TxsInfiniteListItem = ({
|
||||
endChars={5}
|
||||
/>
|
||||
</td>
|
||||
{['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize) && (
|
||||
<td className="text-sm items-center font-mono" data-testid="tx-time">
|
||||
{createdAt ? <TimeAgo date={createdAt} /> : '-'}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ import { TxsInfiniteListItem } from './txs-infinite-list-item';
|
||||
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
|
||||
import EmptyList from '../empty-list/empty-list';
|
||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
|
||||
interface TxsInfiniteListProps {
|
||||
hasMoreTxs: boolean;
|
||||
@@ -20,16 +19,7 @@ interface ItemProps {
|
||||
}
|
||||
|
||||
const Item = ({ tx }: ItemProps) => {
|
||||
const {
|
||||
hash,
|
||||
submitter,
|
||||
type,
|
||||
command,
|
||||
block,
|
||||
code,
|
||||
createdAt,
|
||||
index: blockIndex,
|
||||
} = tx;
|
||||
const { hash, submitter, type, command, block, code, index: blockIndex } = tx;
|
||||
return (
|
||||
<TxsInfiniteListItem
|
||||
type={type}
|
||||
@@ -39,7 +29,6 @@ const Item = ({ tx }: ItemProps) => {
|
||||
hash={hash}
|
||||
block={block}
|
||||
index={blockIndex}
|
||||
createdAt={createdAt}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -50,7 +39,6 @@ export const TxsInfiniteList = ({
|
||||
className,
|
||||
hasFilters = false,
|
||||
}: TxsInfiniteListProps) => {
|
||||
const { screenSize } = useScreenDimensions();
|
||||
if (!txs || txs.length === 0) {
|
||||
if (!areTxsLoading) {
|
||||
return (
|
||||
@@ -78,9 +66,6 @@ export const TxsInfiniteList = ({
|
||||
<th>{t('Type')}</th>
|
||||
<th className="text-left">{t('From')}</th>
|
||||
<th>{t('Block')}</th>
|
||||
{['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize) && (
|
||||
<th>{t('Time')}</th>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -36,50 +36,30 @@ const PERCENTAGE_PARAMS = [
|
||||
'governance.proposal.updateNetParam.requiredMajority',
|
||||
'governance.proposal.updateNetParam.requiredParticipation',
|
||||
'governance.proposal.updateMarket.minProposerEquityLikeShare',
|
||||
'governance.proposal.VolumeDiscountProgram.requiredMajority',
|
||||
'governance.proposal.VolumeDiscountProgram.requiredParticipation',
|
||||
'governance.proposal.referralProgram.requiredMajority',
|
||||
'governance.proposal.transfer.requiredMajority',
|
||||
'governance.proposal.updateAsset.requiredMajority',
|
||||
'governance.proposal.updateAsset.requiredParticipation',
|
||||
'governance.proposal.transfer.requiredParticipation',
|
||||
'governance.proposal.referralProgram.requiredParticipation',
|
||||
'network.validators.ersatz.rewardFactor',
|
||||
'network.validators.ersatz.multipleOfTendermintValidators',
|
||||
'validators.vote.required',
|
||||
'referralProgram.maxReferralRewardFactor',
|
||||
'referralProgram.maxReferralDiscountFactor',
|
||||
'referralProgram.maxReferralRewardProportion',
|
||||
].map((p) => p.toLowerCase());
|
||||
];
|
||||
|
||||
const BIG_NUMBER_PARAMS = [
|
||||
'spam.protection.delegation.min.tokens',
|
||||
'validators.delegation.minAmount',
|
||||
'governance.proposal.transfer.maxAmount',
|
||||
'reward.staking.delegation.minimumValidatorStake',
|
||||
'reward.staking.delegation.maxPayoutPerParticipant',
|
||||
'reward.staking.delegation.maxPayoutPerEpoch',
|
||||
'spam.protection.voting.min.tokens',
|
||||
'spam.protection.proposal.min.tokens',
|
||||
'governance.proposal.transfer.minVoterBalance',
|
||||
'governance.proposal.freeform.minProposerBalance',
|
||||
'governance.proposal.updateNetParam.minVoterBalance',
|
||||
'governance.proposal.updateMarket.minVoterBalance',
|
||||
'governance.proposal.asset.minVoterBalance',
|
||||
'governance.proposal.updateNetParam.minProposerBalance',
|
||||
'governance.proposal.freeform.minVoterBalance',
|
||||
'spam.protection.proposal.min.tokens',
|
||||
'governance.proposal.updateMarket.minProposerBalance',
|
||||
'governance.proposal.asset.minProposerBalance',
|
||||
'governance.proposal.transfer.minProposerBalance',
|
||||
'governance.proposal.market.minProposerBalance',
|
||||
'governance.proposal.market.minVoterBalance',
|
||||
'governance.proposal.updateAsset.minProposerBalance',
|
||||
'governance.proposal.updateAsset.minVoterBalance',
|
||||
'governance.proposal.referralProgram.minProposerBalance',
|
||||
'governance.proposal.referralProgram.minVoterBalance',
|
||||
'governance.proposal.VolumeDiscountProgram.minProposerBalance',
|
||||
'governance.proposal.VolumeDiscountProgram.minVoterBalance',
|
||||
].map((p) => p.toLowerCase());
|
||||
];
|
||||
|
||||
export const renderGroupedParams = (
|
||||
group: GroupedParams,
|
||||
@@ -151,12 +131,12 @@ export const NetworkParameterRow = ({
|
||||
<div className="pb-2">
|
||||
<SyntaxHighlighter data={JSON.parse(value)} />
|
||||
</div>
|
||||
) : BIG_NUMBER_PARAMS.includes(key.toLowerCase()) ? (
|
||||
addDecimalsFormatNumber(Number(value), 18)
|
||||
) : PERCENTAGE_PARAMS.includes(key.toLowerCase()) ? (
|
||||
`${formatNumber(Number(value) * 100, 0)}%`
|
||||
) : isNaN(Number(value)) ? (
|
||||
value
|
||||
) : BIG_NUMBER_PARAMS.includes(key) ? (
|
||||
addDecimalsFormatNumber(Number(value), 18)
|
||||
) : PERCENTAGE_PARAMS.includes(key) ? (
|
||||
`${formatNumber(Number(value) * 100, 0)}%`
|
||||
) : (
|
||||
formatNumber(Number(value), 4)
|
||||
)}
|
||||
|
||||
@@ -14,10 +14,6 @@ export interface BlockExplorerTransactionResult {
|
||||
value: string;
|
||||
};
|
||||
error?: string;
|
||||
// These aren't strictly optional but are new in 0.73.0 so we need to make them optional
|
||||
createdAt?: string;
|
||||
version?: string;
|
||||
pow?: string;
|
||||
}
|
||||
|
||||
export interface BlockExplorerTransactions {
|
||||
|
||||
@@ -112,8 +112,6 @@ const USE_ACCOUNT_TYPES = [
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
AccountType.ACCOUNT_TYPE_FEES_MAKER,
|
||||
AccountType.ACCOUNT_TYPE_PENDING_TRANSFERS,
|
||||
AccountType.ACCOUNT_TYPE_VESTED_REWARDS,
|
||||
AccountType.ACCOUNT_TYPE_VESTING_REWARDS,
|
||||
];
|
||||
|
||||
const getAssetIds = (data: Account[]) =>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { truncateByChars } from '@vegaprotocol/utils';
|
||||
import { addDecimal, truncateByChars } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
NetworkParams,
|
||||
@@ -12,6 +12,7 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useCallback } from 'react';
|
||||
import { accountsDataProvider } from './accounts-data-provider';
|
||||
import { TransferForm } from './transfer-form';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import { Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const ALLOWED_ACCOUNTS = [
|
||||
@@ -41,6 +42,16 @@ export const TransferContainer = ({ assetId }: { assetId?: string }) => {
|
||||
? data.filter((account) => ALLOWED_ACCOUNTS.includes(account.type))
|
||||
: [];
|
||||
|
||||
const assets = accounts.map((account) => ({
|
||||
id: account.asset.id,
|
||||
symbol: account.asset.symbol,
|
||||
name: account.asset.name,
|
||||
decimals: account.asset.decimals,
|
||||
balance: addDecimal(account.balance, account.asset.decimals),
|
||||
}));
|
||||
|
||||
if (data === null) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="mb-4 text-sm" data-testid="transfer-intro-text">
|
||||
@@ -58,6 +69,7 @@ export const TransferContainer = ({ assetId }: { assetId?: string }) => {
|
||||
<TransferForm
|
||||
pubKey={pubKey}
|
||||
pubKeys={pubKeys ? pubKeys?.map((pk) => pk.publicKey) : null}
|
||||
assets={sortBy(assets, 'name')}
|
||||
assetId={assetId}
|
||||
feeFactor={param}
|
||||
submitTransfer={transfer}
|
||||
|
||||
@@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { AddressField, TransferFee, TransferForm } from './transfer-form';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
import { removeDecimal } from '@vegaprotocol/utils';
|
||||
import { addDecimal, formatNumber, removeDecimal } from '@vegaprotocol/utils';
|
||||
|
||||
describe('TransferForm', () => {
|
||||
const submit = async () => {
|
||||
@@ -14,6 +14,7 @@ describe('TransferForm', () => {
|
||||
|
||||
const selectAsset = async (asset: {
|
||||
id: string;
|
||||
balance: string;
|
||||
name: string;
|
||||
decimals: number;
|
||||
}) => {
|
||||
@@ -27,6 +28,9 @@ describe('TransferForm', () => {
|
||||
expect(await screen.findByTestId('select-asset')).toHaveTextContent(
|
||||
asset.name
|
||||
);
|
||||
expect(await screen.findByTestId('asset-balance')).toHaveTextContent(
|
||||
formatNumber(asset.balance, asset.decimals)
|
||||
);
|
||||
};
|
||||
|
||||
const amount = '100';
|
||||
@@ -37,6 +41,7 @@ describe('TransferForm', () => {
|
||||
symbol: '€',
|
||||
name: 'EUR',
|
||||
decimals: 2,
|
||||
balance: addDecimal(100000, 2), // 1000
|
||||
};
|
||||
const props = {
|
||||
pubKey,
|
||||
@@ -44,18 +49,19 @@ describe('TransferForm', () => {
|
||||
pubKey,
|
||||
'a4b6e3de5d7ef4e31ae1b090be49d1a2ef7bcefff60cccf7658a0d4922651cce',
|
||||
],
|
||||
assets: [asset],
|
||||
feeFactor: '0.001',
|
||||
submitTransfer: jest.fn(),
|
||||
accounts: [
|
||||
{
|
||||
type: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
asset,
|
||||
balance: '100000',
|
||||
balance: '100',
|
||||
},
|
||||
{
|
||||
type: AccountType.ACCOUNT_TYPE_VESTED_REWARDS,
|
||||
asset,
|
||||
balance: '100000',
|
||||
balance: '100',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -69,7 +75,7 @@ describe('TransferForm', () => {
|
||||
render(<TransferForm {...props} />);
|
||||
// Select a pubkey
|
||||
await userEvent.selectOptions(
|
||||
screen.getByLabelText('To Vega key'),
|
||||
screen.getByLabelText('Vega key'),
|
||||
props.pubKeys[1]
|
||||
);
|
||||
|
||||
@@ -115,19 +121,13 @@ describe('TransferForm', () => {
|
||||
// 1003-TRAN-004
|
||||
render(<TransferForm {...props} />);
|
||||
await submit();
|
||||
expect(await screen.findAllByText('Required')).toHaveLength(3); // pubkey is set as default value
|
||||
expect(await screen.findAllByText('Required')).toHaveLength(4);
|
||||
const toggle = screen.getByText('Enter manually');
|
||||
await userEvent.click(toggle);
|
||||
// has switched to input
|
||||
expect(toggle).toHaveTextContent('Select from wallet');
|
||||
expect(screen.getByLabelText('To Vega key')).toHaveAttribute(
|
||||
'type',
|
||||
'text'
|
||||
);
|
||||
await userEvent.type(
|
||||
screen.getByLabelText('To Vega key'),
|
||||
'invalid-address'
|
||||
);
|
||||
expect(screen.getByLabelText('Vega key')).toHaveAttribute('type', 'text');
|
||||
await userEvent.type(screen.getByLabelText('Vega key'), 'invalid-address');
|
||||
expect(screen.getAllByTestId('input-error-text')[0]).toHaveTextContent(
|
||||
'Invalid Vega key'
|
||||
);
|
||||
@@ -142,7 +142,7 @@ describe('TransferForm', () => {
|
||||
render(<TransferForm {...props} />);
|
||||
|
||||
// check current pubkey not shown
|
||||
const keySelect = screen.getByLabelText<HTMLSelectElement>('To Vega key');
|
||||
const keySelect = screen.getByLabelText<HTMLSelectElement>('Vega key');
|
||||
expect(keySelect.children).toHaveLength(3);
|
||||
expect(Array.from(keySelect.options).map((o) => o.value)).toEqual([
|
||||
'',
|
||||
@@ -151,17 +151,25 @@ describe('TransferForm', () => {
|
||||
]);
|
||||
|
||||
await submit();
|
||||
expect(await screen.findAllByText('Required')).toHaveLength(3); // pubkey is set as default value
|
||||
expect(await screen.findAllByText('Required')).toHaveLength(4);
|
||||
|
||||
// Select a pubkey
|
||||
await userEvent.selectOptions(
|
||||
screen.getByLabelText('To Vega key'),
|
||||
screen.getByLabelText('Vega key'),
|
||||
props.pubKeys[1]
|
||||
);
|
||||
|
||||
// Select asset
|
||||
await selectAsset(asset);
|
||||
|
||||
// assert rich select as updated
|
||||
expect(await screen.findByTestId('select-asset')).toHaveTextContent(
|
||||
asset.name
|
||||
);
|
||||
expect(await screen.findByTestId('asset-balance')).toHaveTextContent(
|
||||
formatNumber(asset.balance, asset.decimals)
|
||||
);
|
||||
|
||||
await userEvent.selectOptions(
|
||||
screen.getByLabelText('From account'),
|
||||
AccountType.ACCOUNT_TYPE_VESTED_REWARDS
|
||||
@@ -209,7 +217,7 @@ describe('TransferForm', () => {
|
||||
render(<TransferForm {...props} submitTransfer={mockSubmit} />);
|
||||
|
||||
// check current pubkey not shown
|
||||
const keySelect = screen.getByLabelText<HTMLSelectElement>('To Vega key');
|
||||
const keySelect = screen.getByLabelText<HTMLSelectElement>('Vega key');
|
||||
const pubKeyOptions = ['', pubKey, props.pubKeys[1]];
|
||||
expect(keySelect.children).toHaveLength(pubKeyOptions.length);
|
||||
expect(Array.from(keySelect.options).map((o) => o.value)).toEqual(
|
||||
@@ -217,11 +225,11 @@ describe('TransferForm', () => {
|
||||
);
|
||||
|
||||
await submit();
|
||||
expect(await screen.findAllByText('Required')).toHaveLength(3); // pubkey set as default value
|
||||
expect(await screen.findAllByText('Required')).toHaveLength(4);
|
||||
|
||||
// Select a pubkey
|
||||
await userEvent.selectOptions(
|
||||
screen.getByLabelText('To Vega key'),
|
||||
screen.getByLabelText('Vega key'),
|
||||
props.pubKeys[1]
|
||||
);
|
||||
|
||||
@@ -278,7 +286,7 @@ describe('TransferForm', () => {
|
||||
render(<TransferForm {...props} />);
|
||||
|
||||
// check current pubkey not shown
|
||||
const keySelect: HTMLSelectElement = screen.getByLabelText('To Vega key');
|
||||
const keySelect: HTMLSelectElement = screen.getByLabelText('Vega key');
|
||||
const pubKeyOptions = ['', pubKey, props.pubKeys[1]];
|
||||
expect(keySelect.children).toHaveLength(pubKeyOptions.length);
|
||||
expect(Array.from(keySelect.options).map((o) => o.value)).toEqual(
|
||||
@@ -286,11 +294,11 @@ describe('TransferForm', () => {
|
||||
);
|
||||
|
||||
await submit();
|
||||
expect(await screen.findAllByText('Required')).toHaveLength(3); // pubkey set as default value
|
||||
expect(await screen.findAllByText('Required')).toHaveLength(4);
|
||||
|
||||
// Select a pubkey
|
||||
await userEvent.selectOptions(
|
||||
screen.getByLabelText('To Vega key'),
|
||||
screen.getByLabelText('Vega key'),
|
||||
props.pubKeys[1]
|
||||
);
|
||||
|
||||
@@ -316,6 +324,7 @@ describe('TransferForm', () => {
|
||||
|
||||
describe('AddressField', () => {
|
||||
const props = {
|
||||
pubKeys: ['pubkey-1', 'pubkey-2'],
|
||||
select: <div>select</div>,
|
||||
input: <div>input</div>,
|
||||
onChange: jest.fn(),
|
||||
@@ -325,7 +334,7 @@ describe('TransferForm', () => {
|
||||
const mockOnChange = jest.fn();
|
||||
render(<AddressField {...props} onChange={mockOnChange} />);
|
||||
|
||||
// select should be shown by default
|
||||
// select should be shown as multiple pubkeys provided
|
||||
expect(screen.getByText('select')).toBeInTheDocument();
|
||||
expect(screen.queryByText('input')).not.toBeInTheDocument();
|
||||
await userEvent.click(screen.getByText('Enter manually'));
|
||||
@@ -337,6 +346,12 @@ describe('TransferForm', () => {
|
||||
expect(screen.queryByText('input')).not.toBeInTheDocument();
|
||||
expect(mockOnChange).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('Does not provide select option if there is only a single key', () => {
|
||||
render(<AddressField {...props} pubKeys={['single-pubKey']} />);
|
||||
expect(screen.getByText('input')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Select from wallet')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('TransferFee', () => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import {
|
||||
minSafe,
|
||||
maxSafe,
|
||||
@@ -6,7 +5,6 @@ import {
|
||||
vegaPublicKey,
|
||||
addDecimal,
|
||||
formatNumber,
|
||||
addDecimalsFormatNumber,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
@@ -29,19 +27,27 @@ import { AssetOption, Balance } from '@vegaprotocol/assets';
|
||||
import { AccountType, AccountTypeMapping } from '@vegaprotocol/types';
|
||||
|
||||
interface FormFields {
|
||||
toVegaKey: string;
|
||||
toAddress: string;
|
||||
asset: string;
|
||||
amount: string;
|
||||
fromAccount: AccountType;
|
||||
}
|
||||
|
||||
interface Asset {
|
||||
id: string;
|
||||
symbol: string;
|
||||
name: string;
|
||||
decimals: number;
|
||||
balance: string;
|
||||
}
|
||||
|
||||
interface TransferFormProps {
|
||||
pubKey: string | null;
|
||||
pubKeys: string[] | null;
|
||||
assets: Array<Asset>;
|
||||
accounts: Array<{
|
||||
type: AccountType;
|
||||
balance: string;
|
||||
asset: { id: string; symbol: string; name: string; decimals: number };
|
||||
asset: { id: string; symbol: string };
|
||||
}>;
|
||||
assetId?: string;
|
||||
feeFactor: string | null;
|
||||
@@ -51,6 +57,7 @@ interface TransferFormProps {
|
||||
export const TransferForm = ({
|
||||
pubKey,
|
||||
pubKeys,
|
||||
assets,
|
||||
assetId: initialAssetId,
|
||||
feeFactor,
|
||||
submitTransfer,
|
||||
@@ -66,50 +73,15 @@ export const TransferForm = ({
|
||||
} = useForm<FormFields>({
|
||||
defaultValues: {
|
||||
asset: initialAssetId,
|
||||
toVegaKey: pubKey || '',
|
||||
},
|
||||
});
|
||||
|
||||
const assets = sortBy(
|
||||
accounts
|
||||
.filter((a) => a.type === AccountType.ACCOUNT_TYPE_GENERAL)
|
||||
.map((account) => ({
|
||||
...account.asset,
|
||||
balance: addDecimal(account.balance, account.asset.decimals),
|
||||
})),
|
||||
'name'
|
||||
);
|
||||
|
||||
const selectedPubKey = watch('toVegaKey');
|
||||
const selectedPubKey = watch('toAddress');
|
||||
const amount = watch('amount');
|
||||
const fromAccount = watch('fromAccount');
|
||||
const assetId = watch('asset');
|
||||
|
||||
const asset = assets.find((a) => a.id === assetId);
|
||||
|
||||
const account = accounts.find(
|
||||
(a) => a.asset.id === assetId && a.type === fromAccount
|
||||
);
|
||||
const accountBalance =
|
||||
account && addDecimal(account.balance, account.asset.decimals);
|
||||
|
||||
// General account for the selected asset
|
||||
const generalAccount = accounts.find((a) => {
|
||||
return (
|
||||
a.asset.id === assetId && a.type === AccountType.ACCOUNT_TYPE_GENERAL
|
||||
);
|
||||
});
|
||||
|
||||
const [includeFee, setIncludeFee] = useState(false);
|
||||
|
||||
// Min viable amount given asset decimals EG for WEI 0.000000000000000001
|
||||
const min = asset
|
||||
? new BigNumber(addDecimal('1', asset.decimals))
|
||||
: new BigNumber(0);
|
||||
|
||||
// Max amount given selected asset and from account
|
||||
const max = accountBalance ? new BigNumber(accountBalance) : new BigNumber(0);
|
||||
|
||||
const transferAmount = useMemo(() => {
|
||||
if (!amount) return undefined;
|
||||
if (includeFee && feeFactor) {
|
||||
@@ -128,6 +100,10 @@ export const TransferForm = ({
|
||||
);
|
||||
}, [amount, includeFee, transferAmount, feeFactor]);
|
||||
|
||||
const asset = useMemo(() => {
|
||||
return assets.find((a) => a.id === assetId);
|
||||
}, [assets, assetId]);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(fields: FormFields) => {
|
||||
if (!asset) {
|
||||
@@ -137,7 +113,7 @@ export const TransferForm = ({
|
||||
throw new Error('Submitted transfer with no amount selected');
|
||||
}
|
||||
const transfer = normalizeTransfer(
|
||||
fields.toVegaKey,
|
||||
fields.toAddress,
|
||||
transferAmount,
|
||||
fields.fromAccount,
|
||||
AccountType.ACCOUNT_TYPE_GENERAL, // field is readonly in the form
|
||||
@@ -151,6 +127,19 @@ export const TransferForm = ({
|
||||
[asset, submitTransfer, transferAmount]
|
||||
);
|
||||
|
||||
const min = useMemo(() => {
|
||||
// Min viable amount given asset decimals EG for WEI 0.000000000000000001
|
||||
const minViableAmount = asset
|
||||
? new BigNumber(addDecimal('1', asset.decimals))
|
||||
: new BigNumber(0);
|
||||
return minViableAmount;
|
||||
}, [asset]);
|
||||
|
||||
const max = useMemo(() => {
|
||||
const maxAmount = asset ? new BigNumber(asset.balance) : new BigNumber(0);
|
||||
return maxAmount;
|
||||
}, [asset]);
|
||||
|
||||
// reset for placeholder workaround https://github.com/radix-ui/primitives/issues/1569
|
||||
useEffect(() => {
|
||||
if (!pubKey) {
|
||||
@@ -164,32 +153,38 @@ export const TransferForm = ({
|
||||
className="text-sm"
|
||||
data-testid="transfer-form"
|
||||
>
|
||||
<TradingFormGroup label="To Vega key" labelFor="toVegaKey">
|
||||
<TradingFormGroup label="Vega key" labelFor="toAddress">
|
||||
<AddressField
|
||||
onChange={() => setValue('toVegaKey', '')}
|
||||
pubKeys={pubKeys}
|
||||
onChange={() => setValue('toAddress', '')}
|
||||
select={
|
||||
<TradingSelect {...register('toVegaKey')} id="toVegaKey">
|
||||
<TradingSelect
|
||||
{...register('toAddress')}
|
||||
id="toAddress"
|
||||
defaultValue=""
|
||||
>
|
||||
<option value="" disabled={true}>
|
||||
{t('Please select')}
|
||||
</option>
|
||||
{pubKeys?.map((pk) => {
|
||||
const text = pk === pubKey ? t('Current key: ') + pk : pk;
|
||||
{pubKeys?.length &&
|
||||
pubKeys.map((pk) => {
|
||||
const text = pk === pubKey ? t('Current key: ') + pk : pk;
|
||||
|
||||
return (
|
||||
<option key={pk} value={pk}>
|
||||
{text}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<option key={pk} value={pk}>
|
||||
{text}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</TradingSelect>
|
||||
}
|
||||
input={
|
||||
<TradingInput
|
||||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||
autoFocus={true} // focus input immediately after is shown
|
||||
id="toVegaKey"
|
||||
id="toAddress"
|
||||
type="text"
|
||||
{...register('toVegaKey', {
|
||||
{...register('toAddress', {
|
||||
validate: {
|
||||
required,
|
||||
vegaPublicKey,
|
||||
@@ -198,9 +193,9 @@ export const TransferForm = ({
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{errors.toVegaKey?.message && (
|
||||
<TradingInputError forInput="toVegaKey">
|
||||
{errors.toVegaKey.message}
|
||||
{errors.toAddress?.message && (
|
||||
<TradingInputError forInput="toAddress">
|
||||
{errors.toAddress.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
@@ -277,9 +272,7 @@ export const TransferForm = ({
|
||||
.map((a) => {
|
||||
return (
|
||||
<option value={a.type} key={`${a.type}-${a.asset.id}`}>
|
||||
{AccountTypeMapping[a.type]} (
|
||||
{addDecimalsFormatNumber(a.balance, a.asset.decimals)}{' '}
|
||||
{a.asset.symbol})
|
||||
{AccountTypeMapping[a.type]} ({a.asset.symbol})
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
@@ -296,13 +289,10 @@ export const TransferForm = ({
|
||||
defaultValue={AccountType.ACCOUNT_TYPE_GENERAL}
|
||||
>
|
||||
<option value={AccountType.ACCOUNT_TYPE_GENERAL}>
|
||||
{generalAccount
|
||||
? `${
|
||||
AccountTypeMapping[AccountType.ACCOUNT_TYPE_GENERAL]
|
||||
} (${addDecimalsFormatNumber(
|
||||
generalAccount.balance,
|
||||
generalAccount.asset.decimals
|
||||
)} ${generalAccount.asset.symbol})`
|
||||
{asset
|
||||
? `${AccountTypeMapping[AccountType.ACCOUNT_TYPE_GENERAL]} (${
|
||||
asset.symbol
|
||||
})`
|
||||
: AccountTypeMapping[AccountType.ACCOUNT_TYPE_GENERAL]}
|
||||
</option>
|
||||
</TradingSelect>
|
||||
@@ -321,24 +311,15 @@ export const TransferForm = ({
|
||||
maxSafe: (v) => {
|
||||
const value = new BigNumber(v);
|
||||
if (value.isGreaterThan(max)) {
|
||||
return t('You cannot transfer more than available');
|
||||
return t(
|
||||
'You cannot transfer more than your available collateral'
|
||||
);
|
||||
}
|
||||
return maxSafe(max)(v);
|
||||
},
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{accountBalance && (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-0 right-0 ml-auto text-xs underline"
|
||||
onClick={() =>
|
||||
setValue('amount', parseFloat(accountBalance).toString())
|
||||
}
|
||||
>
|
||||
{t('Use max')}
|
||||
</button>
|
||||
)}
|
||||
{errors.amount?.message && (
|
||||
<TradingInputError forInput="amount">
|
||||
{errors.amount.message}
|
||||
@@ -450,31 +431,40 @@ export const TransferFee = ({
|
||||
};
|
||||
|
||||
interface AddressInputProps {
|
||||
pubKeys: string[] | null;
|
||||
select: ReactNode;
|
||||
input: ReactNode;
|
||||
onChange: () => void;
|
||||
}
|
||||
|
||||
export const AddressField = ({
|
||||
pubKeys,
|
||||
select,
|
||||
input,
|
||||
onChange,
|
||||
}: AddressInputProps) => {
|
||||
const [isInput, setIsInput] = useState(false);
|
||||
const [isInput, setIsInput] = useState(() => {
|
||||
if (pubKeys && pubKeys.length <= 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{isInput ? input : select}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setIsInput((curr) => !curr);
|
||||
onChange();
|
||||
}}
|
||||
className="absolute top-0 right-0 ml-auto text-xs underline"
|
||||
>
|
||||
{isInput ? t('Select from wallet') : t('Enter manually')}
|
||||
</button>
|
||||
{pubKeys && pubKeys.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setIsInput((curr) => !curr);
|
||||
onChange();
|
||||
}}
|
||||
className="absolute top-0 right-0 ml-auto text-xs underline"
|
||||
>
|
||||
{isInput ? t('Select from wallet') : t('Enter manually')}
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,11 +20,7 @@ query Candles($marketId: ID!, $interval: Interval!, $since: String!) {
|
||||
code
|
||||
}
|
||||
}
|
||||
candlesConnection(
|
||||
interval: $interval
|
||||
since: $since
|
||||
pagination: { last: 5000 }
|
||||
) {
|
||||
candlesConnection(interval: $interval, since: $since) {
|
||||
edges {
|
||||
node {
|
||||
...CandleFields
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ export const CandlesDocument = gql`
|
||||
code
|
||||
}
|
||||
}
|
||||
candlesConnection(interval: $interval, since: $since, pagination: {last: 5000}) {
|
||||
candlesConnection(interval: $interval, since: $since) {
|
||||
edges {
|
||||
node {
|
||||
...CandleFields
|
||||
|
||||
@@ -32,7 +32,7 @@ export const Pagination = ({
|
||||
{false}
|
||||
{showRetentionMessage &&
|
||||
t(
|
||||
'Depending on data node retention you may not be able see the full history'
|
||||
'Depending on data node retention you may not be able see the "full" history'
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center text-xs">
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ export const MarketCandlesDocument = gql`
|
||||
marketsConnection(id: $marketId) {
|
||||
edges {
|
||||
node {
|
||||
candlesConnection(interval: $interval, since: $since, pagination: {last: 1000}) {
|
||||
candlesConnection(interval: $interval, since: $since) {
|
||||
edges {
|
||||
node {
|
||||
...MarketCandlesFields
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ export const MarketsCandlesDocument = gql`
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
candlesConnection(interval: $interval, since: $since, pagination: {last: 1000}) {
|
||||
candlesConnection(interval: $interval, since: $since) {
|
||||
edges {
|
||||
node {
|
||||
...MarketCandlesFields
|
||||
|
||||
@@ -11,11 +11,7 @@ query MarketCandles($interval: Interval!, $since: String!, $marketId: ID!) {
|
||||
marketsConnection(id: $marketId) {
|
||||
edges {
|
||||
node {
|
||||
candlesConnection(
|
||||
interval: $interval
|
||||
since: $since
|
||||
pagination: { last: 1000 }
|
||||
) {
|
||||
candlesConnection(interval: $interval, since: $since) {
|
||||
edges {
|
||||
node {
|
||||
...MarketCandlesFields
|
||||
|
||||
@@ -12,11 +12,7 @@ query MarketsCandles($interval: Interval!, $since: String!) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
candlesConnection(
|
||||
interval: $interval
|
||||
since: $since
|
||||
pagination: { last: 1000 }
|
||||
) {
|
||||
candlesConnection(interval: $interval, since: $since) {
|
||||
edges {
|
||||
node {
|
||||
...MarketCandlesFields
|
||||
|
||||
@@ -3,7 +3,10 @@ import { tradesWithMarketProvider } from './trades-data-provider';
|
||||
import { TradesTable } from './trades-table';
|
||||
import { useDealTicketFormValues } from '@vegaprotocol/deal-ticket';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Pagination } from '@vegaprotocol/datagrid';
|
||||
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { useCallback, useState } from 'react';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
|
||||
interface TradesContainerProps {
|
||||
marketId: string;
|
||||
@@ -16,19 +19,43 @@ export const TradesManager = ({
|
||||
}: TradesContainerProps) => {
|
||||
const update = useDealTicketFormValues((state) => state.updateAll);
|
||||
|
||||
const { data, error } = useDataProvider({
|
||||
const { data, error, load, pageInfo } = useDataProvider({
|
||||
dataProvider: tradesWithMarketProvider,
|
||||
variables: { marketId },
|
||||
});
|
||||
const [hasDisplayedRow, setHasDisplayedRow] = useState<boolean | undefined>(
|
||||
undefined
|
||||
);
|
||||
const { onFilterChanged, ...props } = gridProps || {};
|
||||
const onRowDataUpdated = useCallback(
|
||||
({ api }: { api: AgGridReact['api'] }) => {
|
||||
setHasDisplayedRow(!!api.getDisplayedRowCount());
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<TradesTable
|
||||
rowData={data}
|
||||
onClick={(price?: string) => {
|
||||
update(marketId, { price });
|
||||
}}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No trades')}
|
||||
{...gridProps}
|
||||
/>
|
||||
<div className="flex flex-col h-full">
|
||||
<TradesTable
|
||||
rowData={data}
|
||||
onClick={(price?: string) => {
|
||||
update(marketId, { price });
|
||||
}}
|
||||
onFilterChanged={(event) => {
|
||||
onRowDataUpdated(event);
|
||||
onFilterChanged(event);
|
||||
}}
|
||||
onRowDataUpdated={onRowDataUpdated}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No trades')}
|
||||
{...props}
|
||||
/>
|
||||
<Pagination
|
||||
count={data?.length || 0}
|
||||
pageInfo={pageInfo}
|
||||
onLoad={load}
|
||||
hasDisplayedRows={hasDisplayedRow || false}
|
||||
showRetentionMessage={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user