Compare commits

..
16 changed files with 31 additions and 211 deletions
@@ -8,7 +8,6 @@ import { ENV } from '../../config';
import noIcon from '../../images/token-no-icon.png';
import vegaBlack from '../../images/vega_black.png';
import vegaVesting from '../../images/vega_vesting.png';
import { BigNumber } from '../../lib/bignumber';
import type { WalletCardAssetProps } from '../wallet-card';
import { useVegaWallet } from '@vegaprotocol/wallet';
@@ -103,10 +102,7 @@ export const usePollForDelegations = () => {
setAccounts(
accounts
.filter(
(a) =>
a.type === Schema.AccountType.ACCOUNT_TYPE_GENERAL ||
a.type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS ||
a.type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS
(a) => a.type === Schema.AccountType.ACCOUNT_TYPE_GENERAL
)
.map((a) => {
const isVega =
@@ -119,23 +115,14 @@ export const usePollForDelegations = () => {
subheading: isVega ? t('collateral') : a.asset.symbol,
symbol: a.asset.symbol,
decimals: a.asset.decimals,
assetId: a.asset.id,
balance: new BigNumber(
addDecimal(a.balance, a.asset.decimals)
),
image: isVega
? vegaBlack
: a.type ===
Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS ||
a.type ===
Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS
? vegaVesting
: noIcon,
image: isVega ? vegaBlack : noIcon,
border: isVega,
address: isAssetTypeERC20(a.asset)
? a.asset.source.contractAddress
: undefined,
type: a.type,
};
})
.sort((a, b) => {
@@ -1,18 +1,9 @@
import React, { useMemo } from 'react';
import React from 'react';
import { Link } from 'react-router-dom';
import { useAnimateValue } from '../../hooks/use-animate-value';
import type { BigNumber } from '../../lib/bignumber';
import { useNumberParts } from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { useTranslation } from 'react-i18next';
import { AnchorButton, Tooltip } from '@vegaprotocol/ui-toolkit';
import {
CONSOLE_TRANSFER_ASSET,
DApp,
useLinks,
} from '@vegaprotocol/environment';
import { useNetworkParam } from '@vegaprotocol/network-parameters';
interface WalletCardProps {
children: React.ReactNode;
@@ -109,10 +100,8 @@ export interface WalletCardAssetProps {
symbol: string;
balance: BigNumber;
decimals: number;
assetId?: string;
border?: boolean;
subheading?: string;
type?: Schema.AccountType;
}
export const WalletCardAsset = ({
@@ -121,37 +110,16 @@ export const WalletCardAsset = ({
symbol,
balance,
decimals,
assetId,
border,
subheading,
type,
}: WalletCardAssetProps) => {
const [integers, decimalsPlaces, separator] = useNumberParts(
balance,
decimals
);
const { t } = useTranslation();
const consoleLink = useLinks(DApp.Console);
const transferAssetLink = (assetId: string) =>
consoleLink(CONSOLE_TRANSFER_ASSET.replace(':assetId', assetId));
const { param: baseRate } = useNetworkParam('rewards_vesting_baseRate');
const isRedeemable =
type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS && assetId;
const accountTypeTooltip = useMemo(() => {
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS) {
return t('VestedRewardsTooltip');
}
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS && baseRate) {
return t('VestingRewardsTooltip', { baseRate });
}
return null;
}, [baseRate, t, type]);
return (
<div className="flex flex-nowrap gap-2 mt-2 mb-4">
<div className="flex flex-nowrap mt-2 mb-4">
<img
alt="Vega"
src={image}
@@ -161,37 +129,15 @@ export const WalletCardAsset = ({
/>
<div>
<div
className="flex align-center items-baseline text-base gap-2"
className="flex align-center text-base"
data-testid="currency-title"
>
<div className="mb-0 uppercase">{name}</div>
<div className="mb-0 px-2 uppercase">{name}</div>
<div className="mb-0 uppercase text-neutral-400">
{subheading || symbol}
</div>
</div>
{type ? (
<div className="mb-[2px] flex gap-2 items-baseline">
<Tooltip description={accountTypeTooltip}>
<span className="px-2 py-1 leading-none text-xs bg-vega-cdark-700 rounded">
{Schema.AccountTypeMapping[type]}
</span>
</Tooltip>
{isRedeemable ? (
<Tooltip description={t('RedeemRewardsTooltip')}>
<AnchorButton
variant="primary"
size="xs"
href={transferAssetLink(assetId)}
target="_blank"
className="px-2 py-1 leading-none text-xs bg-vega-yellow text-black rounded"
>
{t('Redeem')}
</AnchorButton>
</Tooltip>
) : null}
</div>
) : null}
<div className="basis-full font-mono" data-testid="currency-value">
<div className="px-2 basis-full font-mono" data-testid="currency-value">
<span>
{integers}
{separator}
@@ -953,8 +953,5 @@
"ACCOUNT_TYPE_REWARD_RELATIVE_RETURN": "Relative return reward account",
"ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY": "Return volatility reward account",
"ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING": "Validator ranking reward account",
"ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD": "Pending fee referral reward account",
"VestingRewardsTooltip": "Vesting rewards will be moved to vested account at a rate of {{baseRate}} per epoch.",
"VestedRewardsTooltip": "Vested rewards can be redeemed using Console",
"RedeemRewardsTooltip": "Click to redeem vested rewards in Console"
"ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD": "Pending fee referral reward account"
}
+6 -23
View File
@@ -1,28 +1,11 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import BigNumber from 'bignumber.js';
import {
AddressField,
TransferFee,
TransferForm,
type TransferFormProps,
} from './transfer-form';
import { AddressField, TransferFee, TransferForm } from './transfer-form';
import { AccountType } from '@vegaprotocol/types';
import { removeDecimal } from '@vegaprotocol/utils';
import { MockedProvider } from '@apollo/client/testing';
describe('TransferForm', () => {
const renderComponent = (props: TransferFormProps) => {
return render(
// Wrap with mock provider as the form will make queries to fetch the selected
// toVegaKey accounts. We don't test this for now but we need to wrap so that
// the component has access to the client
<MockedProvider>
<TransferForm {...props} />
</MockedProvider>
);
};
const submit = async () => {
await userEvent.click(
screen.getByRole('button', { name: 'Confirm transfer' })
@@ -83,7 +66,7 @@ describe('TransferForm', () => {
// 1003-TRAN-017
// 1003-TRAN-018
// 1003-TRAN-019
renderComponent(props);
render(<TransferForm {...props} />);
// Select a pubkey
await userEvent.selectOptions(
screen.getByLabelText('To Vega key'),
@@ -130,7 +113,7 @@ describe('TransferForm', () => {
// 1003-TRAN-012
// 1003-TRAN-013
// 1003-TRAN-004
renderComponent(props);
render(<TransferForm {...props} />);
await submit();
expect(await screen.findAllByText('Required')).toHaveLength(3); // pubkey is set as default value
const toggle = screen.getByText('Enter manually');
@@ -156,7 +139,7 @@ describe('TransferForm', () => {
// 1002-WITH-010
// 1003-TRAN-011
// 1003-TRAN-014
renderComponent(props);
render(<TransferForm {...props} />);
// check current pubkey not shown
const keySelect = screen.getByLabelText<HTMLSelectElement>('To Vega key');
@@ -223,7 +206,7 @@ describe('TransferForm', () => {
describe('IncludeFeesCheckbox', () => {
it('validates fields and submits when checkbox is checked', async () => {
const mockSubmit = jest.fn();
renderComponent({ ...props, submitTransfer: mockSubmit });
render(<TransferForm {...props} submitTransfer={mockSubmit} />);
// check current pubkey not shown
const keySelect = screen.getByLabelText<HTMLSelectElement>('To Vega key');
@@ -292,7 +275,7 @@ describe('TransferForm', () => {
});
it('validates fields when checkbox is not checked', async () => {
renderComponent(props);
render(<TransferForm {...props} />);
// check current pubkey not shown
const keySelect: HTMLSelectElement = screen.getByLabelText('To Vega key');
+14 -50
View File
@@ -27,8 +27,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { AssetOption, Balance } from '@vegaprotocol/assets';
import { AccountType, AccountTypeMapping } from '@vegaprotocol/types';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { accountsDataProvider } from './accounts-data-provider';
interface FormFields {
toVegaKey: string;
@@ -37,7 +35,7 @@ interface FormFields {
fromAccount: AccountType;
}
export interface TransferFormProps {
interface TransferFormProps {
pubKey: string | null;
pubKeys: string[] | null;
accounts: Array<{
@@ -74,28 +72,8 @@ export const TransferForm = ({
const assets = sortBy(
accounts
.filter(
(a) =>
a.type === AccountType.ACCOUNT_TYPE_GENERAL ||
a.type === AccountType.ACCOUNT_TYPE_VESTED_REWARDS
)
// Sum the general and vested account balances so the value shown in the asset
// dropdown is correct for all transferable accounts
.reduce((merged, account) => {
const existing = merged.findIndex(
(m) => m.asset.id === account.asset.id
);
if (existing > -1) {
const balance = new BigNumber(merged[existing].balance)
.plus(new BigNumber(account.balance))
.toString();
merged[existing] = { ...merged[existing], balance };
return merged;
}
return [...merged, account];
}, [] as typeof accounts)
.filter((a) => a.type === AccountType.ACCOUNT_TYPE_GENERAL)
.map((account) => ({
key: account.asset.id,
...account.asset,
balance: addDecimal(account.balance, account.asset.decimals),
})),
@@ -109,30 +87,18 @@ export const TransferForm = ({
const asset = assets.find((a) => a.id === assetId);
const { data: toAccounts } = useDataProvider({
dataProvider: accountsDataProvider,
variables: {
partyId: selectedPubKey,
},
skip: !selectedPubKey,
});
const account = accounts.find(
(a) => a.asset.id === assetId && a.type === fromAccount
);
const accountBalance =
account && addDecimal(account.balance, account.asset.decimals);
// The general account of the selected pubkey. You can only transfer
// to general accounts, either when redeeming vested rewards or just
// during normal general -> general transfers
const toGeneralAccount =
toAccounts &&
toAccounts.find((a) => {
return (
a.asset.id === assetId && a.type === AccountType.ACCOUNT_TYPE_GENERAL
);
});
// 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);
@@ -260,7 +226,7 @@ export const TransferForm = ({
>
{assets.map((a) => (
<AssetOption
key={a.key}
key={a.id}
asset={a}
balance={
<Balance
@@ -330,16 +296,14 @@ export const TransferForm = ({
defaultValue={AccountType.ACCOUNT_TYPE_GENERAL}
>
<option value={AccountType.ACCOUNT_TYPE_GENERAL}>
{toGeneralAccount
{generalAccount
? `${
AccountTypeMapping[AccountType.ACCOUNT_TYPE_GENERAL]
} (${addDecimalsFormatNumber(
toGeneralAccount.balance,
toGeneralAccount.asset.decimals
)} ${toGeneralAccount.asset.symbol})`
: `${AccountTypeMapping[AccountType.ACCOUNT_TYPE_GENERAL]} ${
asset ? `(0 ${asset.symbol})` : ''
}`}
generalAccount.balance,
generalAccount.asset.decimals
)} ${generalAccount.asset.symbol})`
: AccountTypeMapping[AccountType.ACCOUNT_TYPE_GENERAL]}
</option>
</TradingSelect>
</TradingFormGroup>
@@ -685,7 +685,7 @@ export const DealTicket = ({
subLabel={`${formatValue(
normalizedOrder.size,
market.positionDecimalPlaces
)} ${baseQuote || ''} @ ${
)} ${baseQuote} @ ${
type === Schema.OrderType.TYPE_MARKET
? 'market'
: `${formatValue(
-5
View File
@@ -126,11 +126,6 @@ export const useEtherscanLink = () => {
return link;
};
// Console pages
export const CONSOLE_TRANSFER = '#/portfolio/assets/transfer';
export const CONSOLE_TRANSFER_ASSET =
'#/portfolio/assets/transfer?assetId=:assetId';
// Governance pages
export const TOKEN_NEW_MARKET_PROPOSAL = '/proposals/propose/new-market';
export const TOKEN_NEW_NETWORK_PARAM_PROPOSAL =
@@ -12,7 +12,6 @@ export const NetworkParams = {
'rewards_marketCreationQuantumMultiple',
reward_staking_delegation_payoutDelay:
'reward_staking_delegation_payoutDelay',
rewards_vesting_baseRate: 'rewards_vesting_baseRate',
governance_proposal_market_minVoterBalance:
'governance_proposal_market_minVoterBalance',
governance_proposal_market_minClose: 'governance_proposal_market_minClose',
@@ -148,7 +148,6 @@ export const OrderListManager = ({
expiresAt: editOrder.expiresAt,
side: editOrder.side,
marketId: editOrder.market.id,
remaining: editOrder.remaining,
};
create({ orderAmendment }, originalOrder);
setEditOrder(null);
@@ -1,5 +1,4 @@
import {
MAXGOINT64,
addDecimalsFormatNumber,
getDateTimeFormat,
isNumeric,
@@ -145,22 +144,11 @@ export const OrderListTable = memo<
if (!data?.market || !isNumeric(data.size)) {
return '-';
}
const prefix = data
? data.side === Schema.Side.SIDE_BUY
? '+'
: '-'
: '';
if (
data.size === MAXGOINT64 &&
data.timeInForce ===
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC &&
data.reduceOnly
) {
return t('MAX');
}
return (
prefix +
addDecimalsFormatNumber(
@@ -65,7 +65,6 @@ fragment OrderTxUpdateFields on OrderUpdate {
expiresAt
side
marketId
remaining
}
subscription OrderTxUpdate($partyId: ID!) {
+2 -3
View File
@@ -21,14 +21,14 @@ export type WithdrawalBusEventSubscriptionVariables = Types.Exact<{
export type WithdrawalBusEventSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', event: { __typename?: 'Deposit' } | { __typename?: 'TimeUpdate' } | { __typename?: 'TransactionResult' } | { __typename?: 'Withdrawal', id: string, status: Types.WithdrawalStatus, amount: string, createdTimestamp: any, withdrawnTimestamp?: any | null, txHash?: string | null, pendingOnForeignChain: boolean, asset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, status: Types.AssetStatus, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } }, details?: { __typename?: 'Erc20WithdrawalDetails', receiverAddress: string } | null } }> | null };
export type OrderTxUpdateFieldsFragment = { __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string, remaining: string };
export type OrderTxUpdateFieldsFragment = { __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string };
export type OrderTxUpdateSubscriptionVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type OrderTxUpdateSubscription = { __typename?: 'Subscription', orders?: Array<{ __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string, remaining: string }> | null };
export type OrderTxUpdateSubscription = { __typename?: 'Subscription', orders?: Array<{ __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string }> | null };
export type DepositBusEventFieldsFragment = { __typename?: 'Deposit', id: string, status: Types.DepositStatus, amount: string, createdTimestamp: any, creditedTimestamp?: any | null, txHash?: string | null, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number } };
@@ -88,7 +88,6 @@ export const OrderTxUpdateFieldsFragmentDoc = gql`
expiresAt
side
marketId
remaining
}
`;
export const DepositBusEventFieldsFragmentDoc = gql`
@@ -56,7 +56,6 @@ describe('useVegaTransactionStore', () => {
side: Side.SIDE_BUY,
marketId:
'3aa2a828687cc3d59e92445d294891cbbd40e2165bbfb15674158ef5d4e8848d',
remaining: '12',
};
const processedTransactionUpdate = {
@@ -193,7 +193,6 @@ const submitOrder: VegaStoredTxState = {
createdAt: new Date(),
marketId: 'market-1',
status: OrderStatus.STATUS_ACTIVE,
remaining: '10',
},
};
@@ -250,7 +249,6 @@ const editOrder: VegaStoredTxState = {
createdAt: new Date(),
marketId: 'market-1',
status: OrderStatus.STATUS_ACTIVE,
remaining: '10',
},
};
@@ -491,7 +489,6 @@ describe('getRejectionReason', () => {
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
side: Types.Side.SIDE_BUY,
marketId: '',
remaining: '',
})
).toBe('Insufficient asset balance');
});
@@ -508,7 +505,6 @@ describe('getRejectionReason', () => {
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
side: Types.Side.SIDE_BUY,
marketId: '',
remaining: '',
})
).toBe(
'Your Fill or Kill (FOK) order was not filled and it has been stopped'
@@ -41,7 +41,6 @@ import {
toBigNum,
truncateByChars,
formatTrigger,
MAXGOINT64,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useAssetsMapProvider } from '@vegaprotocol/assets';
@@ -586,23 +585,6 @@ export const VegaTransactionDetails = ({ tx }: { tx: VegaStoredTxState }) => {
<Panel>
{t('Close position for')}{' '}
<strong>{market.tradableInstrument.instrument.code}</strong>
{tx.order?.remaining && (
<p>
{t('Filled')}{' '}
<SizeAtPrice
meta={{
positionDecimalPlaces: market.positionDecimalPlaces,
decimalPlaces: market.decimalPlaces,
asset: getAsset(market).symbol,
}}
side={tx.order.side}
size={(
BigInt(tx.order.size) - BigInt(tx.order.remaining)
).toString()}
price={tx.order.price}
/>
</p>
)}
</Panel>
);
}
@@ -971,19 +953,7 @@ export const getVegaTransactionContentIntent = (tx: VegaStoredTxState) => {
isWithdrawTransaction(tx.body) &&
Intent.Warning;
// Toast for an IOC should go green when it is stopped,
// because stopping an IOC once all available volume has filled is the correct behaviour (it is immediate or cancel),
// this behaviour should apply to all IOC, not just those created due to "Close position"
const intentForClosedPosition =
tx.order &&
tx.order.status === Schema.OrderStatus.STATUS_STOPPED &&
tx.order.timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_IOC &&
tx.order.size === MAXGOINT64 &&
// isClosePositionTransaction(tx) &&
Intent.Success;
const intent =
intentForClosedPosition ||
intentForRejectedOrder ||
intentForCompletedWithdrawal ||
intentMap[tx.status];
@@ -76,7 +76,6 @@ const orderUpdate: OrderTxUpdateFieldsFragment = {
createdAt: '2022-07-05T14:25:47.815283706Z',
expiresAt: '2022-07-05T14:25:47.815283706Z',
size: '10',
remaining: '10',
price: '300000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
side: Side.SIDE_BUY,