Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb953bf62e | ||
|
|
a7d6c68515 | ||
|
|
660552c565 | ||
|
|
ccfcdd4d52 | ||
|
|
3ab0c77866 | ||
|
|
a673811d20 |
@@ -2,7 +2,7 @@
|
||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
||||
NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_URL=https://api.vega.xyz/query
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
"governance.proposal.updateMarket.minVoterBalance",
|
||||
"governance.proposal.updateNetParam.minProposerBalance",
|
||||
"governance.proposal.updateNetParam.minVoterBalance",
|
||||
"governance.proposal.updateAsset.minProposerBalance",
|
||||
"governance.proposal.updateAsset.minVoterBalance",
|
||||
"reward.staking.delegation.maxPayoutPerEpoch",
|
||||
"reward.staking.delegation.maxPayoutPerParticipant",
|
||||
"reward.staking.delegation.minimumValidatorStake",
|
||||
@@ -21,6 +19,9 @@
|
||||
"validators.delegation.minAmount"
|
||||
],
|
||||
"fiveDecimal": [
|
||||
"governance.proposal.updateAsset.minProposerBalance",
|
||||
"governance.proposal.updateAsset.minVoterBalance",
|
||||
"governance.proposal.updateAsset.requiredParticipation",
|
||||
"market.fee.factors.infrastructureFee",
|
||||
"market.fee.factors.makerFee",
|
||||
"market.liquidity.bondPenaltyParameter",
|
||||
@@ -76,7 +77,6 @@
|
||||
"governance.proposal.updateNetParam.requiredMajority",
|
||||
"governance.proposal.updateNetParam.requiredParticipation",
|
||||
"governance.proposal.updateMarket.minProposerEquityLikeShare",
|
||||
"governance.proposal.updateAsset.requiredParticipation",
|
||||
"validators.vote.required"
|
||||
],
|
||||
"duration": [
|
||||
|
||||
@@ -138,7 +138,7 @@ context('Network parameters page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should list each network parameter displayed as a currency value with four decimals - in the correct format', function () {
|
||||
it('should list each network parameter displayed as a currency value with four decimals - in the correct format', function () {
|
||||
cy.get_network_parameters().then((network_parameters) => {
|
||||
network_parameters = Object.entries(network_parameters);
|
||||
network_parameters.forEach((network_parameter) => {
|
||||
|
||||
@@ -4,7 +4,6 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
NX_VEGA_ENV=CUSTOM
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_TXS_LIST=0
|
||||
|
||||
@@ -8,5 +8,4 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_GOVERNANCE_URL=https://dev.token.vega.xyz
|
||||
NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
@@ -1,10 +1,9 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
NX_TENDERMINT_URL=https://be.explorer.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.explorer.vega.xyz/websocket
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_BLOCK_EXPLORER=https://be.vega.community/rest/
|
||||
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest/
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
NX_VEGA_GOVERNANCE_URL=https://token.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
@@ -8,5 +8,4 @@ NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_GOVERNANCE_URL=https://token.fairground.wtf
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
@@ -11,4 +11,3 @@ NX_VEGA_GOVERNANCE_URL=https://validator-testnet.governance.vega.xyz
|
||||
NX_TENDERMINT_URL=https://tm-be.validators-testnet.vega.rocks
|
||||
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
NX_VEGA_EXPLORER_URL=https://validator-testnet.explorer.vega.xyz/
|
||||
@@ -5,4 +5,3 @@ NX_VEGA_ENV=CUSTOM
|
||||
NX_BLOCK_EXPLORER=
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
@@ -6,7 +6,6 @@ export type AssetBalanceProps = {
|
||||
assetId: string;
|
||||
price: string;
|
||||
showAssetLink?: boolean;
|
||||
showAssetSymbol?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -17,21 +16,18 @@ const AssetBalance = ({
|
||||
assetId,
|
||||
price,
|
||||
showAssetLink = true,
|
||||
showAssetSymbol = false,
|
||||
}: AssetBalanceProps) => {
|
||||
const { data: asset, loading } = useAssetDataProvider(assetId);
|
||||
const { data: asset } = useAssetDataProvider(assetId);
|
||||
|
||||
const label =
|
||||
!loading && asset && asset.decimals
|
||||
asset && asset.decimals
|
||||
? addDecimalsFormatNumber(price, asset.decimals)
|
||||
: price;
|
||||
|
||||
return (
|
||||
<div className="inline-block">
|
||||
<span>{label}</span>{' '}
|
||||
{showAssetLink && asset?.id ? (
|
||||
<AssetLink showAssetSymbol={showAssetSymbol} assetId={assetId} />
|
||||
) : null}
|
||||
{showAssetLink && asset?.id ? <AssetLink assetId={assetId} /> : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,17 +13,11 @@ const DEFAULT_DECIMALS = 18;
|
||||
* the governance asset first, which is set by a network parameter
|
||||
*/
|
||||
const GovernanceAssetBalance = ({ price }: GovernanceAssetBalanceProps) => {
|
||||
const { data, loading } = useExplorerGovernanceAssetQuery();
|
||||
const { data } = useExplorerGovernanceAssetQuery();
|
||||
|
||||
if (!loading && data && data.networkParameter?.value) {
|
||||
if (data && data.networkParameter?.value) {
|
||||
const governanceAssetId = data.networkParameter.value;
|
||||
return (
|
||||
<AssetBalance
|
||||
price={price}
|
||||
showAssetSymbol={true}
|
||||
assetId={governanceAssetId}
|
||||
/>
|
||||
);
|
||||
return <AssetBalance price={price} assetId={governanceAssetId} />;
|
||||
} else {
|
||||
return (
|
||||
<div className="inline-block">
|
||||
|
||||
@@ -20,7 +20,7 @@ export const PageHeader = ({
|
||||
copy = false,
|
||||
className,
|
||||
}: PageHeaderProps) => {
|
||||
const titleClasses = 'text-xl uppercase font-alpha calt';
|
||||
const titleClasses = 'text-4xl xl:text-5xl uppercase font-alpha calt';
|
||||
return (
|
||||
<header className={className}>
|
||||
<span className={`${titleClasses} block`}>{prefix}</span>
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ export const BundleExists = ({
|
||||
// Note if this is wrong, the wrong decoder will be used which will give incorrect data
|
||||
|
||||
return (
|
||||
<div className="w-auto h-10 max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5 mt-5">
|
||||
<div className="w-auto max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5 mt-5">
|
||||
<IconForBundleStatus status={status} />
|
||||
<h1 className="text-xl pb-1">
|
||||
{status === 'STATUS_ENABLED'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { TxList } from './tx-list';
|
||||
export { TxOrderType } from './tx-order-type';
|
||||
export { TxsInfiniteList } from './txs-infinite-list';
|
||||
export { TxsInfiniteListItem } from './txs-infinite-list-item';
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { TendermintUnconfirmedTransactionsResponse } from '../../routes/txs/tendermint-unconfirmed-transactions-response.d';
|
||||
|
||||
interface TxsProps {
|
||||
data: TendermintUnconfirmedTransactionsResponse | undefined;
|
||||
}
|
||||
|
||||
export const TxList = ({ data }: TxsProps) => {
|
||||
if (!data) {
|
||||
return <div>{t('Awaiting transactions')}</div>;
|
||||
}
|
||||
|
||||
return <div>{JSON.stringify(data, null, ' ')}</div>;
|
||||
};
|
||||
@@ -7,7 +7,7 @@ import { toHex } from '../search/detect-search';
|
||||
import { ChainResponseCode } from './details/chain-response-code/chain-reponse.code';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
|
||||
const TRUNCATE_LENGTH = 10;
|
||||
const TRUNCATE_LENGTH = 5;
|
||||
|
||||
export const TxsInfiniteListItem = ({
|
||||
hash,
|
||||
@@ -34,10 +34,10 @@ export const TxsInfiniteListItem = ({
|
||||
className="flex items-center h-full border-t border-neutral-600 dark:border-neutral-800 txs-infinite-list-item grid grid-cols-10 py-2"
|
||||
>
|
||||
<div
|
||||
className="text-sm col-span-10 md:col-span-3 leading-none"
|
||||
className="text-sm col-span-10 xl:col-span-3 leading-none"
|
||||
data-testid="tx-hash"
|
||||
>
|
||||
<span className="md:hidden uppercase text-vega-dark-300">
|
||||
<span className="xl:hidden uppercase text-vega-dark-300">
|
||||
ID:
|
||||
</span>
|
||||
<TruncatedLink
|
||||
@@ -48,10 +48,10 @@ export const TxsInfiniteListItem = ({
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="text-sm col-span-10 md:col-span-3 leading-none"
|
||||
className="text-sm col-span-10 xl:col-span-3 leading-none"
|
||||
data-testid="pub-key"
|
||||
>
|
||||
<span className="md:hidden uppercase text-vega-dark-300">
|
||||
<span className="xl:hidden uppercase text-vega-dark-300">
|
||||
By:
|
||||
</span>
|
||||
<TruncatedLink
|
||||
@@ -61,14 +61,14 @@ export const TxsInfiniteListItem = ({
|
||||
endChars={TRUNCATE_LENGTH}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm col-span-5 md:col-span-2 leading-none flex items-center">
|
||||
<div className="text-sm col-span-5 xl:col-span-2 leading-none flex items-center">
|
||||
<TxOrderType orderType={type} command={command} />
|
||||
</div>
|
||||
<div
|
||||
className="text-sm col-span-3 md:col-span-1 leading-none flex items-center"
|
||||
className="text-sm col-span-3 xl:col-span-1 leading-none flex items-center"
|
||||
data-testid="tx-block"
|
||||
>
|
||||
<span className="md:hidden uppercase text-vega-dark-300">
|
||||
<span className="xl:hidden uppercase text-vega-dark-300">
|
||||
Block:
|
||||
</span>
|
||||
<TruncatedLink
|
||||
@@ -79,10 +79,10 @@ export const TxsInfiniteListItem = ({
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="text-sm col-span-2 md:col-span-1 leading-none flex items-center"
|
||||
className="text-sm col-span-2 xl:col-span-1 leading-none flex items-center"
|
||||
data-testid="tx-success"
|
||||
>
|
||||
<span className="md:hidden uppercase text-vega-dark-300">
|
||||
<span className="xl:hidden uppercase text-vega-dark-300">
|
||||
Success:
|
||||
</span>
|
||||
{isNumber(code) ? (
|
||||
|
||||
@@ -64,7 +64,9 @@ describe('Txs infinite list', () => {
|
||||
error={Error('test error!')}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('Cannot fetch transaction')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Cannot fetch transaction: Error: test error!')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('item renders data of n length into list of n length', () => {
|
||||
|
||||
@@ -30,7 +30,7 @@ const NOOP = () => {};
|
||||
const Item = ({ index, style, isLoading, error }: ItemProps) => {
|
||||
let content;
|
||||
if (error) {
|
||||
content = t(`Cannot fetch transaction`);
|
||||
content = t(`Cannot fetch transaction: ${error}`);
|
||||
} else if (isLoading) {
|
||||
content = <Loader />;
|
||||
} else {
|
||||
@@ -68,7 +68,7 @@ export const TxsInfiniteList = ({
|
||||
className,
|
||||
}: TxsInfiniteListProps) => {
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const isStacked = ['xs', 'sm'].includes(screenSize);
|
||||
const isStacked = ['xs', 'sm', 'md', 'lg'].includes(screenSize);
|
||||
|
||||
if (!txs) {
|
||||
if (!areTxsLoading) {
|
||||
@@ -95,15 +95,15 @@ export const TxsInfiniteList = ({
|
||||
|
||||
return (
|
||||
<div className={className} data-testid="transactions-list">
|
||||
<div className="lg:grid grid-cols-10 w-full mb-3 hidden text-vega-dark-300 uppercase">
|
||||
<div className="xl:grid grid-cols-10 w-full mb-3 hidden text-vega-dark-300 uppercase">
|
||||
<div className="col-span-3">
|
||||
<span className="hidden xl:inline">{t('Transaction')} </span>
|
||||
<span className="hidden xl:inline">Transaction </span>
|
||||
<span>ID</span>
|
||||
</div>
|
||||
<div className="col-span-3">{t('Submitted By')}</div>
|
||||
<div className="col-span-2">{t('Type')}</div>
|
||||
<div className="col-span-1">{t('Block')}</div>
|
||||
<div className="col-span-1">{t('Success')}</div>
|
||||
<div className="col-span-3">Submitted By</div>
|
||||
<div className="col-span-2">Type</div>
|
||||
<div className="col-span-1">Block</div>
|
||||
<div className="col-span-1">Success</div>
|
||||
</div>
|
||||
<div data-testid="infinite-scroll-wrapper">
|
||||
<InfiniteLoader
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Routes } from '../../routes/route-names';
|
||||
import { RenderFetched } from '../render-fetched';
|
||||
import { TruncatedLink } from '../truncate/truncated-link';
|
||||
import { TxOrderType } from './tx-order-type';
|
||||
import { Table, TableRow, TableCell } from '../table';
|
||||
@@ -8,7 +9,7 @@ import type { BlockExplorerTransactions } from '../../routes/types/block-explore
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import { ChainResponseCode } from './details/chain-response-code/chain-reponse.code';
|
||||
import { getTxsDataUrl } from '../../hooks/use-txs-data';
|
||||
import { AsyncRenderer, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import EmptyList from '../empty-list/empty-list';
|
||||
|
||||
interface TxsPerBlockProps {
|
||||
@@ -26,7 +27,7 @@ export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
|
||||
} = useFetch<BlockExplorerTransactions>(url);
|
||||
|
||||
return (
|
||||
<AsyncRenderer data={data} error={error} loading={!!loading}>
|
||||
<RenderFetched error={error} loading={loading} className="text-body-large">
|
||||
{data && data.transactions.length > 0 ? (
|
||||
<div className="overflow-x-auto whitespace-nowrap mb-28">
|
||||
<Table>
|
||||
@@ -94,6 +95,6 @@ export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
|
||||
label={t('0 transactions')}
|
||||
/>
|
||||
)}
|
||||
</AsyncRenderer>
|
||||
</RenderFetched>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -144,12 +144,12 @@ afterEach(() => {
|
||||
describe('Block', () => {
|
||||
it('renders error state if error is present', async () => {
|
||||
(useFetch as jest.Mock).mockReturnValue({
|
||||
state: { data: null, loading: false, error: new Error('asd') },
|
||||
state: { data: null, loading: false, error: 'asd' },
|
||||
});
|
||||
render(renderComponent());
|
||||
|
||||
expect(screen.getByText(`BLOCK ${blockId}`)).toBeInTheDocument();
|
||||
expect(screen.getByText('Something went wrong: asd')).toBeInTheDocument();
|
||||
expect(screen.getByText('Error retrieving data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders loading state if present', async () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import React from 'react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { DATA_SOURCES } from '../../../config';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
@@ -11,8 +12,9 @@ import {
|
||||
TableCell,
|
||||
} from '../../../components/table';
|
||||
import { TxsPerBlock } from '../../../components/txs/txs-per-block';
|
||||
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Routes } from '../../route-names';
|
||||
import { RenderFetched } from '../../../components/render-fetched';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { NodeLink } from '../../../components/links';
|
||||
@@ -32,7 +34,7 @@ const Block = () => {
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="block-header">{t(`BLOCK ${block}`)}</RouteTitle>
|
||||
<AsyncRenderer data={blockData} error={error} loading={!!loading}>
|
||||
<RenderFetched error={error} loading={loading}>
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-2 mb-8">
|
||||
<Link
|
||||
@@ -121,7 +123,7 @@ const Block = () => {
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</AsyncRenderer>
|
||||
</RenderFetched>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { RouteTitle } from '../../components/route-title';
|
||||
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { DATA_SOURCES } from '../../config';
|
||||
import type { TendermintGenesisResponse } from './tendermint-genesis-response';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
@@ -10,26 +10,21 @@ const Genesis = () => {
|
||||
useDocumentTitle(['Genesis']);
|
||||
|
||||
const {
|
||||
state: { data, loading, error },
|
||||
state: { data: genesis, loading },
|
||||
} = useFetch<TendermintGenesisResponse>(
|
||||
`${DATA_SOURCES.tendermintUrl}/genesis`
|
||||
);
|
||||
|
||||
if (!genesis?.result.genesis) {
|
||||
if (loading) {
|
||||
return <Loader />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<RouteTitle data-testid="genesis-header">{t('Genesis')}</RouteTitle>
|
||||
<AsyncRenderer
|
||||
data={data}
|
||||
error={error}
|
||||
loading={!!loading}
|
||||
loadingMessage={t('Loading genesis information...')}
|
||||
errorMessage={t('Could not fetch genesis data')}
|
||||
>
|
||||
<section>
|
||||
<SyntaxHighlighter data={data?.result.genesis} />
|
||||
</section>
|
||||
</AsyncRenderer>
|
||||
</>
|
||||
<SyntaxHighlighter data={genesis?.result.genesis} />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ export const MarketPage = () => {
|
||||
/>
|
||||
<AsyncRenderer
|
||||
noDataMessage={t('This chain has no markets')}
|
||||
errorMessage={t('Could not fetch market') + ' ' + marketId}
|
||||
data={data}
|
||||
loading={loading}
|
||||
error={error}
|
||||
|
||||
@@ -21,7 +21,6 @@ import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
const PERCENTAGE_PARAMS = [
|
||||
'governance.proposal.asset.requiredMajority',
|
||||
'governance.proposal.asset.requiredParticipation',
|
||||
'governance.proposal.updateAsset.requiredParticipation',
|
||||
'governance.proposal.freeform.requiredMajority',
|
||||
'governance.proposal.freeform.requiredParticipation',
|
||||
'governance.proposal.market.requiredMajority',
|
||||
@@ -54,8 +53,6 @@ const BIG_NUMBER_PARAMS = [
|
||||
'governance.proposal.asset.minProposerBalance',
|
||||
'governance.proposal.market.minProposerBalance',
|
||||
'governance.proposal.market.minVoterBalance',
|
||||
'governance.proposal.updateAsset.minProposerBalance',
|
||||
'governance.proposal.updateAsset.minVoterBalance',
|
||||
];
|
||||
|
||||
export const NetworkParameterRow = ({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { RouteTitle } from '../../../components/route-title';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useExplorerOracleSpecsQuery } from '../__generated__/Oracles';
|
||||
@@ -8,7 +8,7 @@ import { useScrollToLocation } from '../../../hooks/scroll-to-location';
|
||||
import filter from 'recursive-key-filter';
|
||||
|
||||
const Oracles = () => {
|
||||
const { data, loading, error } = useExplorerOracleSpecsQuery();
|
||||
const { data, loading } = useExplorerOracleSpecsQuery();
|
||||
|
||||
useDocumentTitle(['Oracles']);
|
||||
useScrollToLocation();
|
||||
@@ -16,40 +16,28 @@ const Oracles = () => {
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
|
||||
<AsyncRenderer
|
||||
data={data}
|
||||
loading={loading}
|
||||
error={error}
|
||||
loadingMessage={t('Loading oracle data...')}
|
||||
errorMessage={t('Oracle data could not be loaded')}
|
||||
noDataMessage={t('No oracles found')}
|
||||
noDataCondition={(data) =>
|
||||
!data?.oracleSpecsConnection?.edges ||
|
||||
data.oracleSpecsConnection.edges?.length === 0
|
||||
}
|
||||
>
|
||||
{data?.oracleSpecsConnection?.edges
|
||||
? data.oracleSpecsConnection.edges.map((o) => {
|
||||
const id = o?.node.dataSourceSpec.spec.id;
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div id={id} key={id} className="mb-10">
|
||||
<OracleDetails
|
||||
id={id}
|
||||
dataSource={o?.node}
|
||||
showBroadcasts={false}
|
||||
/>
|
||||
<details>
|
||||
<summary className="pointer">JSON</summary>
|
||||
<SyntaxHighlighter data={filter(o, ['__typename'])} />
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</AsyncRenderer>
|
||||
{loading ? <Loader /> : null}
|
||||
{data?.oracleSpecsConnection?.edges
|
||||
? data.oracleSpecsConnection.edges.map((o) => {
|
||||
const id = o?.node.dataSourceSpec.spec.id;
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div id={id} key={id} className="mb-10">
|
||||
<OracleDetails
|
||||
id={id}
|
||||
dataSource={o?.node}
|
||||
showBroadcasts={false}
|
||||
/>
|
||||
<details>
|
||||
<summary className="pointer">JSON</summary>
|
||||
<SyntaxHighlighter data={filter(o, ['__typename'])} />
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { RouteTitle } from '../../../components/route-title';
|
||||
import { RenderFetched } from '../../../components/render-fetched';
|
||||
import { truncateByChars } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useExplorerOracleSpecByIdQuery } from '../__generated__/Oracles';
|
||||
import { OracleDetails } from '../components/oracle';
|
||||
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import filter from 'recursive-key-filter';
|
||||
import { TruncateInline } from '../../../components/truncate/truncate';
|
||||
|
||||
@@ -26,14 +27,7 @@ export const Oracle = () => {
|
||||
{t(`Oracle `)}
|
||||
<TruncateInline startChars={5} endChars={5} text={id || '1'} />
|
||||
</RouteTitle>
|
||||
<AsyncRenderer
|
||||
data={data}
|
||||
error={error}
|
||||
loading={loading}
|
||||
noDataCondition={(data) => !data?.oracleSpec}
|
||||
errorMessage={t('Could not load oracle data')}
|
||||
loadingMessage={t('Loading oracle data...')}
|
||||
>
|
||||
<RenderFetched error={error} loading={loading}>
|
||||
{data?.oracleSpec ? (
|
||||
<div id={id} key={id} className="mb-10">
|
||||
<OracleDetails
|
||||
@@ -50,7 +44,7 @@ export const Oracle = () => {
|
||||
) : (
|
||||
<span></span>
|
||||
)}
|
||||
</AsyncRenderer>
|
||||
</RenderFetched>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -48,13 +48,6 @@ query ExplorerPartyAssets($partyId: ID!) {
|
||||
}
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
linkings(pagination: { first: 100 }) {
|
||||
edges {
|
||||
node {
|
||||
amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
accountsConnection {
|
||||
edges {
|
||||
|
||||
@@ -10,7 +10,7 @@ export type ExplorerPartyAssetsQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string, linkings: { __typename?: 'StakesConnection', edges?: Array<{ __typename?: 'StakeLinkingEdge', node: { __typename?: 'StakeLinking', amount: string } } | null> | null } }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null } } | null> | null } | null } }> } | null };
|
||||
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null } } | null> | null } | null } }> } | null };
|
||||
|
||||
export const ExplorerPartyAssetsAccountsFragmentDoc = gql`
|
||||
fragment ExplorerPartyAssetsAccounts on AccountBalance {
|
||||
@@ -64,13 +64,6 @@ export const ExplorerPartyAssetsDocument = gql`
|
||||
}
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
linkings(pagination: {first: 100}) {
|
||||
edges {
|
||||
node {
|
||||
amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
accountsConnection {
|
||||
edges {
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { toNonHex } from '../../../../components/search/detect-search';
|
||||
import { PageHeader } from '../../../../components/page-header';
|
||||
import { useDocumentTitle } from '../../../../hooks/use-document-title';
|
||||
|
||||
import { PartyAccounts } from '../components/party-accounts';
|
||||
|
||||
const PartyAccountsByAsset = () => {
|
||||
const { party } = useParams<{ party: string }>();
|
||||
|
||||
useDocumentTitle(['Public keys', party || '-']);
|
||||
const partyId = toNonHex(party ? party : '');
|
||||
|
||||
return (
|
||||
<section>
|
||||
<PageHeader title={t('Balances by asset')} />
|
||||
|
||||
<PartyAccounts partyId={partyId} />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export { PartyAccountsByAsset };
|
||||
@@ -1,9 +1,32 @@
|
||||
import { AccountManager } from '@vegaprotocol/accounts';
|
||||
import { useCallback } from 'react';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import get from 'lodash/get';
|
||||
import AssetBalance from '../../../../components/asset-balance/asset-balance';
|
||||
import { AssetLink, MarketLink } from '../../../../components/links';
|
||||
import { Table, TableRow } from '../../../../components/table';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import type { ExplorerPartyAssetsAccountsFragment } from '../__generated__/Party-assets';
|
||||
|
||||
const accountTypeString: Record<Schema.AccountType, string> = {
|
||||
ACCOUNT_TYPE_BOND: t('Bond'),
|
||||
ACCOUNT_TYPE_EXTERNAL: t('External'),
|
||||
ACCOUNT_TYPE_FEES_INFRASTRUCTURE: t('Fees (Infrastructure)'),
|
||||
ACCOUNT_TYPE_FEES_LIQUIDITY: t('Fees (Liquidity)'),
|
||||
ACCOUNT_TYPE_FEES_MAKER: t('Fees (Maker)'),
|
||||
ACCOUNT_TYPE_GENERAL: t('General'),
|
||||
ACCOUNT_TYPE_GLOBAL_INSURANCE: t('Global Insurance Pool'),
|
||||
ACCOUNT_TYPE_GLOBAL_REWARD: t('Global Reward Pool'),
|
||||
ACCOUNT_TYPE_INSURANCE: t('Insurance'),
|
||||
ACCOUNT_TYPE_MARGIN: t('Margin'),
|
||||
ACCOUNT_TYPE_PENDING_TRANSFERS: t('Pending Transfers'),
|
||||
ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: t('Reward - LP Fees received'),
|
||||
ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES: t('Reward - Maker fees paid'),
|
||||
ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES: t('Reward - Maker fees received'),
|
||||
ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: t('Reward - Market proposers'),
|
||||
ACCOUNT_TYPE_SETTLEMENT: t('Settlement'),
|
||||
};
|
||||
|
||||
interface PartyAccountsProps {
|
||||
partyId: string;
|
||||
accounts: ExplorerPartyAssetsAccountsFragment[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -11,22 +34,49 @@ interface PartyAccountsProps {
|
||||
* probably do with sorting by asset, and then within asset, by type with general
|
||||
* appearing first and... tbd
|
||||
*/
|
||||
export const PartyAccounts = ({ partyId }: PartyAccountsProps) => {
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
const onClickAsset = useCallback(
|
||||
(assetId?: string) => {
|
||||
assetId && openAssetDetailsDialog(assetId);
|
||||
},
|
||||
[openAssetDetailsDialog]
|
||||
);
|
||||
|
||||
export const PartyAccounts = ({ accounts }: PartyAccountsProps) => {
|
||||
return (
|
||||
<div className="block min-h-44 h-60 4 w-full border-red-800 relative">
|
||||
<AccountManager
|
||||
partyId={partyId}
|
||||
onClickAsset={onClickAsset}
|
||||
isReadOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<Table className="max-w-5xl min-w-fit">
|
||||
<thead>
|
||||
<TableRow modifier="bordered" className="font-mono">
|
||||
<td>{t('Type')}</td>
|
||||
<td>{t('Market')}</td>
|
||||
<td className="text-right pr-2">{t('Balance')}</td>
|
||||
<td>{t('Asset')}</td>
|
||||
</TableRow>
|
||||
</thead>
|
||||
<tbody>
|
||||
{accounts.map((account) => {
|
||||
const m = get(account, 'market.tradableInstrument.instrument.name');
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
key={`pa-${account.asset.id}-${account.type}`}
|
||||
title={account.asset.name}
|
||||
id={`${accountTypeString[account.type]} ${m ? ` - ${m}` : ''}`}
|
||||
>
|
||||
<td className="text-md">{accountTypeString[account.type]}</td>
|
||||
<td className="text-md">
|
||||
{account.market?.id ? (
|
||||
<MarketLink id={account.market?.id} />
|
||||
) : (
|
||||
<p>-</p>
|
||||
)}
|
||||
</td>
|
||||
<td className="text-md text-right pr-2">
|
||||
<AssetBalance
|
||||
assetId={account.asset.id}
|
||||
price={account.balance}
|
||||
showAssetLink={false}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-md">
|
||||
<AssetLink assetId={account.asset.id} asDialog={true} />
|
||||
</td>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Routes } from '../../../../routes/route-names';
|
||||
import { Button, Icon, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { PartyBlock } from './party-block';
|
||||
import type { AccountFields } from '@vegaprotocol/accounts';
|
||||
|
||||
export interface PartyBlockAccountProps {
|
||||
partyId: string;
|
||||
accountData: AccountFields[] | null;
|
||||
accountLoading: boolean;
|
||||
accountError?: Error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an overview of a party's assets. This uses existing data
|
||||
* providers to structure the details by asset, rather than looking at
|
||||
* it by account. The assumption is that this is a more natural way to
|
||||
* get an idea of the assets and activity of a party.
|
||||
*/
|
||||
export const PartyBlockAccounts = ({
|
||||
partyId,
|
||||
accountData,
|
||||
accountLoading,
|
||||
accountError,
|
||||
}: PartyBlockAccountProps) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const shouldShowActionButton =
|
||||
accountData && accountData.length > 0 && !accountLoading && !accountError;
|
||||
|
||||
const action = shouldShowActionButton ? (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => navigate(`/${Routes.PARTIES}/${partyId}/assets`)}
|
||||
>
|
||||
{t('Show all')}
|
||||
</Button>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<PartyBlock title={t('Assets')} action={action}>
|
||||
{accountData && accountData.length > 0 ? (
|
||||
<p>
|
||||
{accountData.length} {t('assets, including')}{' '}
|
||||
{accountData
|
||||
.map((a) => a.asset.symbol)
|
||||
.slice(0, 3)
|
||||
.join(', ')}
|
||||
</p>
|
||||
) : accountLoading && !accountError ? (
|
||||
<Loader size="small" />
|
||||
) : accountData && accountData.length === 0 ? (
|
||||
<p>{t('No accounts found')}</p>
|
||||
) : (
|
||||
<p>
|
||||
<Icon className="mr-1" name="error" />
|
||||
<span className="text-sm">{t('Could not load assets')}</span>
|
||||
</p>
|
||||
)}
|
||||
</PartyBlock>
|
||||
);
|
||||
};
|
||||
@@ -1,83 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useExplorerPartyAssetsQuery } from '../__generated__/Party-assets';
|
||||
import GovernanceAssetBalance from '../../../../components/asset-balance/governance-asset-balance';
|
||||
import {
|
||||
Icon,
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
Loader,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { PartyBlock } from './party-block';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export interface PartyBlockStakeProps {
|
||||
partyId: string;
|
||||
accountLoading: boolean;
|
||||
accountError?: Error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an overview of a single party's staking balance, importantly maintaining'
|
||||
* the same height before and after the details are loaded in.
|
||||
*
|
||||
* Unlike PartyBlockAccounts there is not action button in the title of this block as
|
||||
* there is no page for it to link to currently. That's a future task.
|
||||
*/
|
||||
export const PartyBlockStake = ({
|
||||
partyId,
|
||||
accountLoading,
|
||||
accountError,
|
||||
}: PartyBlockStakeProps) => {
|
||||
const partyRes = useExplorerPartyAssetsQuery({
|
||||
// Don't cache data for this query, party information can move quite quickly
|
||||
fetchPolicy: 'network-only',
|
||||
variables: { partyId: partyId },
|
||||
skip: !partyId,
|
||||
});
|
||||
|
||||
const p = partyRes.data?.partiesConnection?.edges[0].node;
|
||||
|
||||
const linkedLength = p?.stakingSummary?.linkings?.edges?.length;
|
||||
const linkedStake =
|
||||
linkedLength && linkedLength > 0
|
||||
? p?.stakingSummary?.linkings?.edges
|
||||
?.reduce((total, e) => {
|
||||
return new BigNumber(total).plus(
|
||||
new BigNumber(e?.node.amount || 0)
|
||||
);
|
||||
}, new BigNumber(0))
|
||||
.toString()
|
||||
: '0';
|
||||
|
||||
return (
|
||||
<PartyBlock title={t('Staking')}>
|
||||
{p?.stakingSummary.currentStakeAvailable ? (
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
<div>{t('Available stake')}</div>
|
||||
<div>
|
||||
<GovernanceAssetBalance
|
||||
price={p.stakingSummary.currentStakeAvailable}
|
||||
/>
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
<div>{t('Active stake')}</div>
|
||||
<div>
|
||||
<GovernanceAssetBalance price={linkedStake || '0'} />
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
) : accountLoading && !accountError ? (
|
||||
<Loader size="small" />
|
||||
) : !accountError ? (
|
||||
<p>{t('No staking balance')}</p>
|
||||
) : (
|
||||
<p>
|
||||
<Icon className="mr-1" name="error" />
|
||||
<span className="text-sm">{t('Could not load stake details')}</span>
|
||||
</p>
|
||||
)}
|
||||
</PartyBlock>
|
||||
);
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export interface PartyBlockProps {
|
||||
children: ReactNode;
|
||||
title: string;
|
||||
action?: ReactNode;
|
||||
}
|
||||
|
||||
export function PartyBlock({ children, title, action }: PartyBlockProps) {
|
||||
return (
|
||||
<div className="border-2 min-h-[138px] border-solid border-vega-light-100 dark:border-vega-dark-200 p-5 mt-5">
|
||||
<div
|
||||
className="flex flex-col md:flex-row gap-1 justify-between content-start mb-2"
|
||||
data-testid="page-title"
|
||||
>
|
||||
<h3 className="font-semibold text-lg">{title}</h3>
|
||||
|
||||
{action ? action : null}
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,26 +1,24 @@
|
||||
import { getNodes } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import { useMemo } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { SubHeading } from '../../../components/sub-heading';
|
||||
import { Panel } from '../../../components/panel';
|
||||
import { toNonHex } from '../../../components/search/detect-search';
|
||||
import { useTxsData } from '../../../hooks/use-txs-data';
|
||||
import { TxsInfiniteList } from '../../../components/txs';
|
||||
import { PageHeader } from '../../../components/page-header';
|
||||
import { useExplorerPartyAssetsQuery } from './__generated__/Party-assets';
|
||||
import type { ExplorerPartyAssetsAccountsFragment } from './__generated__/Party-assets';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
import { Icon, Intent, Notification, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
|
||||
import { PartyBlockStake } from './components/party-block-stake';
|
||||
import { PartyBlockAccounts } from './components/party-block-accounts';
|
||||
import { isValidPartyId } from './components/party-id-error';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import GovernanceAssetBalance from '../../../components/asset-balance/governance-asset-balance';
|
||||
import { PartyAccounts } from './components/party-accounts';
|
||||
|
||||
const Party = () => {
|
||||
const { party } = useParams<{ party: string }>();
|
||||
|
||||
useDocumentTitle(['Public keys', party || '-']);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const partyId = toNonHex(party ? party : '');
|
||||
const { isMobile } = useScreenDimensions();
|
||||
const visibleChars = useMemo(() => (isMobile ? 10 : 14), [isMobile]);
|
||||
@@ -30,72 +28,71 @@ const Party = () => {
|
||||
filters,
|
||||
});
|
||||
|
||||
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||
const {
|
||||
data: AccountData,
|
||||
loading: AccountLoading,
|
||||
error: AccountError,
|
||||
} = useDataProvider({
|
||||
dataProvider: aggregatedAccountsDataProvider,
|
||||
variables,
|
||||
const partyRes = useExplorerPartyAssetsQuery({
|
||||
// Don't cache data for this query, party information can move quite quickly
|
||||
fetchPolicy: 'network-only',
|
||||
variables: { partyId: partyId },
|
||||
skip: !party,
|
||||
});
|
||||
|
||||
if (!isValidPartyId(partyId)) {
|
||||
return (
|
||||
<div className="max-w-sm mx-auto">
|
||||
<Notification
|
||||
message={t('Invalid party ID')}
|
||||
intent={Intent.Danger}
|
||||
buttonProps={{
|
||||
text: t('Go back'),
|
||||
action: () => navigate(-1),
|
||||
className: 'py-1',
|
||||
size: 'sm',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const p = partyRes.data?.partiesConnection?.edges[0].node;
|
||||
|
||||
const header = p?.id ? (
|
||||
<PageHeader
|
||||
title={p.id}
|
||||
copy
|
||||
truncateStart={visibleChars}
|
||||
truncateEnd={visibleChars}
|
||||
/>
|
||||
) : (
|
||||
<Panel>
|
||||
<p>No data found for public key {party}</p>
|
||||
</Panel>
|
||||
);
|
||||
|
||||
const staking = (
|
||||
<section>
|
||||
{p?.stakingSummary?.currentStakeAvailable ? (
|
||||
<div className="mt-4 leading-3">
|
||||
<strong className="font-semibold">{t('Staking Balance: ')}</strong>
|
||||
<GovernanceAssetBalance
|
||||
price={p.stakingSummary.currentStakeAvailable}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
|
||||
const accounts = getNodes<ExplorerPartyAssetsAccountsFragment>(
|
||||
p?.accountsConnection
|
||||
);
|
||||
|
||||
return (
|
||||
<section>
|
||||
<PageHeader
|
||||
title={partyId}
|
||||
copy
|
||||
truncateStart={visibleChars}
|
||||
truncateEnd={visibleChars}
|
||||
/>
|
||||
<h1
|
||||
className="font-alpha calt uppercase font-xl mb-4 text-vega-dark-100 dark:text-vega-light-100"
|
||||
data-testid="parties-header"
|
||||
>
|
||||
{t('Public key')}
|
||||
</h1>
|
||||
{partyRes.data ? (
|
||||
<>
|
||||
{header}
|
||||
<SubHeading>{t('Asset data')}</SubHeading>
|
||||
{accounts ? <PartyAccounts accounts={accounts} /> : null}
|
||||
{staking}
|
||||
|
||||
<div className="grid md:grid-flow-col grid-flow-row md:space-x-4 grid-cols-1 md:grid-cols-2 w-full">
|
||||
<PartyBlockAccounts
|
||||
accountError={AccountError}
|
||||
accountLoading={AccountLoading}
|
||||
accountData={AccountData}
|
||||
partyId={partyId}
|
||||
/>
|
||||
<PartyBlockStake
|
||||
accountError={AccountError}
|
||||
accountLoading={AccountLoading}
|
||||
partyId={partyId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SubHeading>{t('Transactions')}</SubHeading>
|
||||
{!error && txsData ? (
|
||||
<TxsInfiniteList
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
areTxsLoading={loading}
|
||||
txs={txsData}
|
||||
loadMoreTxs={loadTxs}
|
||||
error={error}
|
||||
className="mb-28"
|
||||
/>
|
||||
) : (
|
||||
<Splash>
|
||||
<Icon name="error" className="mr-1" />
|
||||
{t('Could not load transaction list for party')}
|
||||
</Splash>
|
||||
)}
|
||||
<SubHeading>{t('Transactions')}</SubHeading>
|
||||
<TxsInfiniteList
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
areTxsLoading={loading}
|
||||
txs={txsData}
|
||||
loadMoreTxs={loadTxs}
|
||||
error={error}
|
||||
className="mb-28"
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
const PartiesSubPage = () => {
|
||||
return <Outlet />;
|
||||
};
|
||||
|
||||
export default PartiesSubPage;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { DATA_SOURCES } from '../../config';
|
||||
import type { TendermintUnconfirmedTransactionsResponse } from '../txs/tendermint-unconfirmed-transactions-response.d';
|
||||
import { TxList } from '../../components/txs';
|
||||
import { RouteTitle } from '../../components/route-title';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
|
||||
const PendingTxs = () => {
|
||||
const {
|
||||
state: { data: unconfirmedTransactions },
|
||||
} = useFetch<TendermintUnconfirmedTransactionsResponse>(
|
||||
`${DATA_SOURCES.tendermintUrl}/unconfirmed_txs`
|
||||
);
|
||||
|
||||
useDocumentTitle(['Pending transactions']);
|
||||
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="unconfirmed-transactions-header">
|
||||
{t('Unconfirmed transactions')}
|
||||
</RouteTitle>
|
||||
<br />
|
||||
<div>{t(`Number: ${unconfirmedTransactions?.result?.n_txs || 0}`)}</div>
|
||||
<br />
|
||||
<div>
|
||||
<br />
|
||||
<TxList data={unconfirmedTransactions} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export { PendingTxs };
|
||||
@@ -14,6 +14,7 @@ import { Block } from './blocks/id';
|
||||
import { Blocks } from './blocks/home';
|
||||
import { Tx } from './txs/id';
|
||||
import { TxsList } from './txs/home';
|
||||
import { PendingTxs } from './pending';
|
||||
import flags from '../config/flags';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Routes } from './route-names';
|
||||
@@ -28,7 +29,6 @@ import compact from 'lodash/compact';
|
||||
import { AssetLink, MarketLink } from '../components/links';
|
||||
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
|
||||
import { remove0x } from '@vegaprotocol/utils';
|
||||
import { PartyAccountsByAsset } from './parties/id/accounts';
|
||||
|
||||
export type Navigable = {
|
||||
path: string;
|
||||
@@ -75,43 +75,14 @@ const partiesRoutes: Route[] = flags.parties
|
||||
},
|
||||
{
|
||||
path: ':party',
|
||||
element: <Party />,
|
||||
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartySingle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
element: <Party />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartyAccountsByAsset />,
|
||||
handle: {
|
||||
breadcrumb: () => {
|
||||
return t('Assets');
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
element: <PartySingle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -266,6 +237,17 @@ export const routerConfig: Route[] = [
|
||||
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'pending',
|
||||
element: <PendingTxs />,
|
||||
handle: {
|
||||
breadcrumb: () => (
|
||||
<Link to={linkTo(Routes.TX, 'pending')}>
|
||||
{t('Pending transactions')}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':txHash',
|
||||
element: <Tx />,
|
||||
|
||||
@@ -10,7 +10,7 @@ interface TxDetailsProps {
|
||||
|
||||
export const txDetailsTruncateLength = 30;
|
||||
|
||||
export const TxDetails = ({ txData, pubKey }: TxDetailsProps) => {
|
||||
export const TxDetails = ({ txData, pubKey, className }: TxDetailsProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import {
|
||||
} from '@vegaprotocol/environment';
|
||||
import classNames from 'classnames';
|
||||
import { NodeStatus, NodeStatusMapping } from '@vegaprotocol/types';
|
||||
import { PartyLink } from '../../components/links';
|
||||
|
||||
type RateProps = {
|
||||
value: BigNumber | number | undefined;
|
||||
@@ -192,9 +191,7 @@ export const ValidatorsPage = () => {
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
<div>{t('ID')}</div>
|
||||
<div className="break-all text-xs font-mono">
|
||||
{v.id}
|
||||
</div>
|
||||
<div className="break-all text-xs">{v.id}</div>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<div>{t('Status')}</div>
|
||||
@@ -221,14 +218,12 @@ export const ValidatorsPage = () => {
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<div>{t('Key')}</div>
|
||||
<div className="break-all text-xs">
|
||||
<PartyLink id={v.pubkey} />
|
||||
</div>
|
||||
<div>{t('Public key')}</div>
|
||||
<div className="break-all text-xs">{v.pubkey}</div>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<div>{t('Ethereum address')}</div>
|
||||
<div className="break-all text-xs font-mono">
|
||||
<div className="break-all text-xs">
|
||||
<EtherscanLink address={v.ethereumAddress} />{' '}
|
||||
<CopyWithTooltip text={v.ethereumAddress}>
|
||||
<button title={t('Copy address to clipboard')}>
|
||||
@@ -239,9 +234,7 @@ export const ValidatorsPage = () => {
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<div>{t('Tendermint public key')}</div>
|
||||
<div className="break-all text-xs font-mono">
|
||||
{v.tmPubkey}
|
||||
</div>
|
||||
<div className="break-all text-xs">{v.tmPubkey}</div>
|
||||
</KeyValueTableRow>
|
||||
|
||||
<KeyValueTableRow>
|
||||
|
||||
@@ -32,11 +32,6 @@
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-row {
|
||||
border-width: 1px 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
.ag-theme-balham {
|
||||
--ag-background-color: theme(colors.white);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_URL=https://api.vega.xyz/query
|
||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
|
||||
@@ -4,11 +4,12 @@ import {
|
||||
navigation,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
convertUnixTimestampToDateformat,
|
||||
createRawProposal,
|
||||
createTenDigitUnixTimeStampForSpecifiedDays,
|
||||
enterUniqueFreeFormProposalBody,
|
||||
generateFreeFormProposalTitle,
|
||||
getDateFormatForSpecifiedDays,
|
||||
getGovernanceProposalDateFormatForSpecifiedDays,
|
||||
getProposalIdFromList,
|
||||
getProposalInformationFromTable,
|
||||
getSubmittedProposalFromProposalList,
|
||||
@@ -22,7 +23,6 @@ import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../../../governan
|
||||
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../../../governance-e2e/src/support/wallet-teardown.functions';
|
||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||
|
||||
const proposalVoteProgressForPercentage =
|
||||
'[data-testid="vote-progress-indicator-percentage-for"]';
|
||||
@@ -99,22 +99,20 @@ describe(
|
||||
getSubmittedProposalFromProposalList(proposalTitle).within(() =>
|
||||
cy.get(viewProposalButton).click()
|
||||
);
|
||||
cy.wrap(
|
||||
formatDateWithLocalTimezone(new Date(proposalTimeStamp * 1000))
|
||||
).then((closingDate) => {
|
||||
getProposalInformationFromTable('Closes on')
|
||||
.contains(closingDate)
|
||||
.should('be.visible');
|
||||
});
|
||||
cy.wrap(
|
||||
formatDateWithLocalTimezone(
|
||||
new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
|
||||
)
|
||||
).then((proposalDate) => {
|
||||
getProposalInformationFromTable('Proposed on')
|
||||
.contains(proposalDate)
|
||||
.should('be.visible');
|
||||
});
|
||||
convertUnixTimestampToDateformat(proposalTimeStamp).then(
|
||||
(closingDate) => {
|
||||
getProposalInformationFromTable('Closes on')
|
||||
.contains(closingDate)
|
||||
.should('be.visible');
|
||||
}
|
||||
);
|
||||
getGovernanceProposalDateFormatForSpecifiedDays(0).then(
|
||||
(proposalDate) => {
|
||||
getProposalInformationFromTable('Proposed on')
|
||||
.contains(proposalDate)
|
||||
.should('be.visible');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Newly created proposal details - shows default status set to fail', function () {
|
||||
@@ -157,16 +155,18 @@ describe(
|
||||
cy.getByTestId('vote-buttons').contains('against').should('be.visible');
|
||||
cy.getByTestId('vote-buttons').contains('for').should('be.visible');
|
||||
voteForProposal('for');
|
||||
getDateFormatForSpecifiedDays(0).then((votedDate) => {
|
||||
// 3001-VOTE-051
|
||||
// 3001-VOTE-093
|
||||
cy.contains('You voted:')
|
||||
.siblings()
|
||||
.contains('For')
|
||||
.siblings()
|
||||
.contains(votedDate)
|
||||
.should('be.visible');
|
||||
});
|
||||
getGovernanceProposalDateFormatForSpecifiedDays(0, 'shortMonth').then(
|
||||
(votedDate) => {
|
||||
// 3001-VOTE-051
|
||||
// 3001-VOTE-093
|
||||
cy.contains('You voted:')
|
||||
.siblings()
|
||||
.contains('For')
|
||||
.siblings()
|
||||
.contains(votedDate)
|
||||
.should('be.visible');
|
||||
}
|
||||
);
|
||||
cy.get(proposalVoteProgressForPercentage) // 3001-VOTE-072
|
||||
.contains('100.00%')
|
||||
.and('be.visible');
|
||||
|
||||
@@ -29,8 +29,6 @@ const tableReceiverAddress = '[col-id="details.receiverAddress"]';
|
||||
const tableWithdrawnTimeStamp = '[col-id="withdrawnTimestamp"]';
|
||||
const tableWithdrawnStatus = '[col-id="status"]';
|
||||
const tableCreatedTimeStamp = '[col-id="createdTimestamp"]';
|
||||
const toastContent = 'toast-content';
|
||||
const toastPanel = 'toast-panel';
|
||||
const usdtName = 'USDC (local)';
|
||||
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
|
||||
const usdcSymbol = 'tUSDC';
|
||||
@@ -151,6 +149,7 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
// Skipping because of bug #1857
|
||||
it('Able to withdraw asset: -eth wallet not connected', function () {
|
||||
const ethWalletAddress = Cypress.env('ethWalletPublicKey');
|
||||
cy.reload();
|
||||
@@ -188,26 +187,8 @@ context(
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/address/');
|
||||
cy.get(tableCreatedTimeStamp).should('not.be.empty');
|
||||
});
|
||||
ethereumWalletConnect();
|
||||
cy.getByTestId(completeWithdrawalButton).click();
|
||||
cy.getByTestId(toastContent)
|
||||
.last()
|
||||
.should('contain.text', 'Awaiting confirmation')
|
||||
.within(() => {
|
||||
cy.getByTestId('external-link').should('exist');
|
||||
});
|
||||
cy.getByTestId(toastContent)
|
||||
.first()
|
||||
.should('contain.text', 'The withdrawal has been approved.')
|
||||
.within(() => {
|
||||
cy.getByTestId(toastPanel).should('contain.text', '110.00', 'tUSDC');
|
||||
});
|
||||
cy.getByTestId(toastContent)
|
||||
.last()
|
||||
.should('contain.text', 'Transaction confirmed')
|
||||
.within(() => {
|
||||
cy.getByTestId('external-link').should('exist');
|
||||
cy.getByTestId(completeWithdrawalButton).click();
|
||||
// Unable to complete withdrawal in Capsule
|
||||
});
|
||||
});
|
||||
|
||||
@@ -237,10 +218,7 @@ context(
|
||||
});
|
||||
|
||||
function waitForAssetsDisplayed(expectedAsset: string) {
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'contain.text',
|
||||
expectedAsset
|
||||
);
|
||||
cy.getByTestId('currency-title').should('contain.text', expectedAsset);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
cy.getByTestId('app-announcement').should('not.exist');
|
||||
});
|
||||
|
||||
it('should show open or enacted proposals without proposal summary', function () {
|
||||
it('should show open or enacted proposals with proposal summary', function () {
|
||||
cy.get('body').then(($body) => {
|
||||
if (!$body.find('[data-testid="proposals-list-item"]').length) {
|
||||
cy.createMarket();
|
||||
@@ -43,6 +43,12 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('proposal-type').invoke('text').should('not.be.empty');
|
||||
cy.getByTestId('proposal-description')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('proposal-details')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('proposal-status')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
|
||||
@@ -101,15 +101,15 @@ context(
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-release-tag').should(
|
||||
'have.text',
|
||||
'Vega release tag: v1'
|
||||
'Vega release tagv1'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-block-height').should(
|
||||
'have.text',
|
||||
'Upgrade block height: 2015942'
|
||||
'Upgrade block height2015942'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-status').should(
|
||||
'have.text',
|
||||
'Approved '
|
||||
'Approved'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { format } from 'date-fns';
|
||||
import { closeDialog, navigateTo, navigation } from './common.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from './staking.functions';
|
||||
|
||||
@@ -16,6 +15,35 @@ const newProposalSubmitButton = '[data-testid="proposal-submit"]';
|
||||
const epochTimeout = Cypress.env('epochTimeout');
|
||||
const proposalTimeout = { timeout: 14000 };
|
||||
|
||||
export function convertUnixTimestampToDateformat(
|
||||
unixTimestamp: number,
|
||||
monthTextLength = 'longMonth'
|
||||
) {
|
||||
const dateSupplied = new Date(unixTimestamp * 1000);
|
||||
const year = dateSupplied.getFullYear();
|
||||
const months = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
];
|
||||
const month = months[dateSupplied.getMonth()];
|
||||
const shortMonth = months[dateSupplied.getMonth()].substring(0, 3),
|
||||
date = dateSupplied.getDate();
|
||||
|
||||
if (monthTextLength === 'longMonth') {
|
||||
return cy.wrap(`${date} ${month} ${year}`);
|
||||
} else return cy.wrap(`${date} ${shortMonth} ${year}`);
|
||||
}
|
||||
|
||||
export function createTenDigitUnixTimeStampForSpecifiedDays(
|
||||
durationDays: number
|
||||
) {
|
||||
@@ -24,13 +52,6 @@ export function createTenDigitUnixTimeStampForSpecifiedDays(
|
||||
return (timestamp = Math.floor(timestamp / 1000));
|
||||
}
|
||||
|
||||
export function getDateFormatForSpecifiedDays(days: number) {
|
||||
const date = new Date(
|
||||
createTenDigitUnixTimeStampForSpecifiedDays(days) * 1000
|
||||
);
|
||||
return cy.wrap(format(date, 'dd MMM yyyy'));
|
||||
}
|
||||
|
||||
export function enterRawProposalBody(timestamp: number) {
|
||||
cy.fixture('/proposals/raw.json').then((rawProposal) => {
|
||||
rawProposal.terms.closingTimestamp = timestamp;
|
||||
@@ -83,6 +104,16 @@ export function getProposalIdFromList(proposalTitle: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export function getGovernanceProposalDateFormatForSpecifiedDays(
|
||||
days: number,
|
||||
shortOrLong?: string
|
||||
) {
|
||||
return convertUnixTimestampToDateformat(
|
||||
createTenDigitUnixTimeStampForSpecifiedDays(days),
|
||||
shortOrLong
|
||||
);
|
||||
}
|
||||
|
||||
export function getProposalInformationFromTable(heading: string) {
|
||||
return cy.get(proposalInformationTableRows).contains(heading).siblings();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_URL=https://api.vega.xyz/query
|
||||
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","TESTNET":"https://token.fairground.wtf","MAINNET":"https://token.vega.xyz"}'
|
||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
function ReactMarkdown({ children }) {
|
||||
return <div>{children}</div>;
|
||||
}
|
||||
|
||||
export default ReactMarkdown;
|
||||
@@ -23,7 +23,6 @@ export const ConnectToVega = () => {
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
data-testid="connect-to-vega-wallet-btn"
|
||||
variant="primary"
|
||||
>
|
||||
{t('connectVegaWallet')}
|
||||
</Button>
|
||||
|
||||
@@ -23,7 +23,7 @@ export const Heading = ({
|
||||
})}
|
||||
>
|
||||
<h1
|
||||
className={classNames('font-alpha calt text-5xl break-words', {
|
||||
className={classNames('font-alpha calt text-5xl', {
|
||||
'mt-0': !marginTop,
|
||||
'mb-0': !marginBottom,
|
||||
})}
|
||||
|
||||
@@ -21,10 +21,10 @@ export const useGetUserBalances = (account: string | undefined) => {
|
||||
token.allowance(account, config.staking_bridge_contract.address),
|
||||
]);
|
||||
|
||||
const balance = toBigNum(b.toString(), decimals);
|
||||
const walletBalance = toBigNum(w.toString(), decimals);
|
||||
const lien = toBigNum(stats.lien.toString(), decimals);
|
||||
const allowance = toBigNum(a.toString(), decimals);
|
||||
const balance = toBigNum(b, decimals);
|
||||
const walletBalance = toBigNum(w, decimals);
|
||||
const lien = toBigNum(stats.lien, decimals);
|
||||
const allowance = toBigNum(a, decimals);
|
||||
|
||||
return {
|
||||
balanceFormatted: balance,
|
||||
|
||||
@@ -21,14 +21,8 @@ export function useRefreshAssociatedBalances() {
|
||||
]);
|
||||
|
||||
updateBalances({
|
||||
walletAssociatedBalance: toBigNum(
|
||||
walletAssociatedBalance.toString(),
|
||||
decimals
|
||||
),
|
||||
vestingAssociatedBalance: toBigNum(
|
||||
vestingAssociatedBalance.toString(),
|
||||
decimals
|
||||
),
|
||||
walletAssociatedBalance: toBigNum(walletAssociatedBalance, decimals),
|
||||
vestingAssociatedBalance: toBigNum(vestingAssociatedBalance, decimals),
|
||||
});
|
||||
},
|
||||
[staking, vesting, updateBalances, decimals]
|
||||
|
||||
@@ -31,18 +31,12 @@ export const useRefreshBalances = (address: string) => {
|
||||
pubKey ? vesting.stake_balance(address, pubKey) : null,
|
||||
]);
|
||||
|
||||
const balance = toBigNum(b.toString(), decimals);
|
||||
const walletBalance = toBigNum(w.toString(), decimals);
|
||||
const lien = toBigNum(stats.lien.toString(), decimals);
|
||||
const allowance = toBigNum(a.toString(), decimals);
|
||||
const walletAssociatedBalance = toBigNum(
|
||||
walletStakeBalance ? walletStakeBalance.toString() : 0,
|
||||
decimals
|
||||
);
|
||||
const vestingAssociatedBalance = toBigNum(
|
||||
vestingStakeBalance ? vestingStakeBalance.toString() : 0,
|
||||
decimals
|
||||
);
|
||||
const balance = toBigNum(b, decimals);
|
||||
const walletBalance = toBigNum(w, decimals);
|
||||
const lien = toBigNum(stats.lien, decimals);
|
||||
const allowance = toBigNum(a, decimals);
|
||||
const walletAssociatedBalance = toBigNum(walletStakeBalance, decimals);
|
||||
const vestingAssociatedBalance = toBigNum(vestingStakeBalance, decimals);
|
||||
|
||||
updateBalances({
|
||||
balanceFormatted: balance,
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
"NewFreeform": "Freeform",
|
||||
"tokenVotes": "Token votes",
|
||||
"liquidityVotes": "Liquidity votes",
|
||||
"castYourVote": "Cast your vote",
|
||||
"yourVote": "Your vote",
|
||||
"for": "For",
|
||||
"against": "Against",
|
||||
"majorityRequired": "Majority Required",
|
||||
@@ -587,7 +587,7 @@
|
||||
"tokensAgainstProposal": "Tokens against proposal",
|
||||
"participationRequired": "Participation required",
|
||||
"numberOfVotingParties": "Number of voting parties",
|
||||
"totalTokensVotes": "Total tokens voted",
|
||||
"totalTokensVotes": "Total yes tokens",
|
||||
"totalTokenVotedPercentage": "Total tokens voted percentage",
|
||||
"numberOfForVotes": "Number of votes for",
|
||||
"numberOfAgainstVotes": "Number of votes against",
|
||||
@@ -631,10 +631,7 @@
|
||||
"New market": "New market",
|
||||
"Market change": "Market change",
|
||||
"Network parameter": "Network parameter",
|
||||
"Change": "Change",
|
||||
"Unknown proposal": "Unknown proposal",
|
||||
"ERC20ContractAddress": "ERC20 contract address",
|
||||
"MaxFaucetAmountMint": "Max faucet amount mint",
|
||||
"Code": "Code",
|
||||
"settled future": "settled future",
|
||||
"Symbol": "Symbol",
|
||||
@@ -680,7 +677,6 @@
|
||||
"NewProposal": "New proposal",
|
||||
"ProposalTypeQuestion": "What type of proposal would you like to make?",
|
||||
"NetworkParameterProposal": "Update network parameter proposal",
|
||||
"parameter": "parameter",
|
||||
"NewMarketProposal": "New market proposal",
|
||||
"UpdateMarketProposal": "Update market proposal",
|
||||
"NewAssetProposal": "New asset proposal",
|
||||
@@ -698,7 +694,6 @@
|
||||
"UpdateMarket": "Update market",
|
||||
"NewAsset": "New asset",
|
||||
"UpdateAsset": "Update asset",
|
||||
"AssetID": "Asset ID",
|
||||
"Freeform": "Freeform",
|
||||
"RawProposal": "Let me choose (raw proposal)",
|
||||
"UseMin": "Use minimum",
|
||||
@@ -742,7 +737,6 @@
|
||||
"ProposalNotFound": "Proposal not found",
|
||||
"ProposalNotFoundDetails": "The proposal you are looking for is not here, it may have been enacted before the last chain restore. You could check the Vega forums/discord instead for information about it.",
|
||||
"FreeformProposal": "Freeform proposal",
|
||||
"Id": "ID",
|
||||
"unknownReason": "unknown reason",
|
||||
"votingEnded": "Voting has ended.",
|
||||
"STATUS": "STATUS",
|
||||
@@ -797,7 +791,5 @@
|
||||
"67% voting power required": "67% voting power required",
|
||||
"Token": "Token",
|
||||
"associateVegaNow": "Associate $VEGA now",
|
||||
"disconnectedNotice": "You have been disconnected. Connect your ETH wallet to the {{correctNetwork}} network to use this app.",
|
||||
"connectAVegaWalletToVote": "Connect a Vega wallet with $VEGA tokens to vote on a proposal.",
|
||||
"findOutMoreAboutHowToVote": "Find out more about how to vote on Vega"
|
||||
"disconnectedNotice": "You have been disconnected. Connect your ETH wallet to the {{correctNetwork}} network to use this app."
|
||||
}
|
||||
|
||||
+14
-62
@@ -1,11 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalInfoLabelVariant } from '../proposal-info-label';
|
||||
|
||||
export const CurrentProposalState = ({
|
||||
proposal,
|
||||
@@ -13,63 +9,19 @@ export const CurrentProposalState = ({
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
let proposalStatus: ReactNode;
|
||||
let variant = 'tertiary' as ProposalInfoLabelVariant;
|
||||
let className = 'text-white';
|
||||
|
||||
switch (proposal?.state) {
|
||||
case ProposalState.STATE_ENACTED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_Enacted')}</span>
|
||||
<Icon name={'tick'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_PASSED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_Passed')}</span>
|
||||
<Icon name={'tick'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_WaitingForNodeVote')}</span>
|
||||
<Icon name={'time'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_OPEN: {
|
||||
variant = 'primary' as ProposalInfoLabelVariant;
|
||||
proposalStatus = <>{t('voteState_Open')}</>;
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_DECLINED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_Declined')}</span>
|
||||
<Icon name={'cross'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_REJECTED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_Rejected')}</span>
|
||||
<Icon name={'warning-sign'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
if (
|
||||
proposal?.state === Schema.ProposalState.STATE_DECLINED ||
|
||||
proposal?.state === Schema.ProposalState.STATE_FAILED ||
|
||||
proposal?.state === Schema.ProposalState.STATE_REJECTED
|
||||
) {
|
||||
className = 'text-danger';
|
||||
} else if (
|
||||
proposal?.state === Schema.ProposalState.STATE_ENACTED ||
|
||||
proposal?.state === Schema.ProposalState.STATE_PASSED
|
||||
) {
|
||||
className = 'text-white';
|
||||
}
|
||||
|
||||
return (
|
||||
<ProposalInfoLabel variant={variant}>{proposalStatus}</ProposalInfoLabel>
|
||||
);
|
||||
return <span className={className}>{t(`${proposal?.state}`)}</span>;
|
||||
};
|
||||
|
||||
+16
@@ -16,12 +16,17 @@ it('Renders all data for table', () => {
|
||||
render(<ProposalChangeTable proposal={proposal} />);
|
||||
expect(screen.getByText('ID')).toBeInTheDocument();
|
||||
expect(screen.getByText(proposal?.id as string)).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('State')).toBeInTheDocument();
|
||||
expect(screen.getByText('Open')).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Closes on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
formatDateWithLocalTimezone(new Date(proposal?.terms.closingDatetime))
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Proposed enactment')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
@@ -30,12 +35,17 @@ it('Renders all data for table', () => {
|
||||
)
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Proposed by')).toBeInTheDocument();
|
||||
expect(screen.getByText(proposal?.party.id ?? '')).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Proposed on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(formatDateWithLocalTimezone(new Date(proposal?.datetime)))
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Type')).toBeInTheDocument();
|
||||
expect(screen.getByText('Network parameter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Changes data based on if data is in future or past', () => {
|
||||
@@ -43,12 +53,17 @@ it('Changes data based on if data is in future or past', () => {
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
});
|
||||
render(<ProposalChangeTable proposal={proposal} />);
|
||||
|
||||
expect(screen.getByText('State')).toBeInTheDocument();
|
||||
expect(screen.getByText('Enacted')).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Closed on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
formatDateWithLocalTimezone(new Date(proposal?.terms.closingDatetime))
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Enacted on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
@@ -89,6 +104,7 @@ it('Renders error details and rejection reason if present', () => {
|
||||
render(<ProposalChangeTable proposal={proposal} />);
|
||||
expect(screen.getByText('Error details')).toBeInTheDocument();
|
||||
expect(screen.getByText(errorDetails)).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Rejection reason')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(ProposalRejectionReason.PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE)
|
||||
|
||||
+13
-6
@@ -6,6 +6,7 @@ import {
|
||||
KeyValueTableRow,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
@@ -19,12 +20,16 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
const terms = proposal?.terms;
|
||||
|
||||
return (
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<RoundedWrapper>
|
||||
<KeyValueTable data-testid="proposal-change-table">
|
||||
<KeyValueTableRow>
|
||||
{t('id')}
|
||||
{proposal?.id}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('state')}
|
||||
<CurrentProposalState proposal={proposal} />
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{isFuture(new Date(terms?.closingDatetime))
|
||||
? t('closesOn')
|
||||
@@ -45,24 +50,26 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
{t('proposedBy')}
|
||||
<span style={{ wordBreak: 'break-word' }}>{proposal?.party.id}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow
|
||||
noBorder={!proposal?.rejectionReason && !proposal?.errorDetails}
|
||||
>
|
||||
<KeyValueTableRow>
|
||||
{t('proposedOn')}
|
||||
{formatDateWithLocalTimezone(new Date(proposal?.datetime))}
|
||||
</KeyValueTableRow>
|
||||
{proposal?.rejectionReason ? (
|
||||
<KeyValueTableRow noBorder={!proposal?.errorDetails}>
|
||||
<KeyValueTableRow>
|
||||
{t('rejectionReason')}
|
||||
{proposal.rejectionReason}
|
||||
</KeyValueTableRow>
|
||||
) : null}
|
||||
{proposal?.errorDetails ? (
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
<KeyValueTableRow>
|
||||
{t('errorDetails')}
|
||||
{proposal.errorDetails}
|
||||
</KeyValueTableRow>
|
||||
) : null}
|
||||
<KeyValueTableRow>
|
||||
{t('type')}
|
||||
{t(`${proposal?.terms.change.__typename}`)}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</RoundedWrapper>
|
||||
);
|
||||
|
||||
+165
-248
@@ -1,68 +1,68 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import {
|
||||
generateNoVotes,
|
||||
generateProposal,
|
||||
generateYesVotes,
|
||||
} from '../../test-helpers/generate-proposals';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { ProposalHeader } from './proposal-header';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalRejectionReason, ProposalState } from '@vegaprotocol/types';
|
||||
import { lastWeek, nextWeek } from '../../test-helpers/mocks';
|
||||
|
||||
const renderComponent = (
|
||||
proposal: ProposalQuery['proposal'],
|
||||
isListItem = true
|
||||
) => render(<ProposalHeader proposal={proposal} isListItem={isListItem} />);
|
||||
const renderComponent = (proposal: ProposalQuery['proposal']) => (
|
||||
<ProposalHeader proposal={proposal} />
|
||||
);
|
||||
|
||||
describe('Proposal header', () => {
|
||||
it('Renders New market proposal', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New some market',
|
||||
description: 'A new some market',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewMarket',
|
||||
instrument: {
|
||||
__typename: 'InstrumentConfiguration',
|
||||
name: 'Some market',
|
||||
code: 'FX:BTCUSD/DEC99',
|
||||
futureProduct: {
|
||||
__typename: 'FutureProduct',
|
||||
settlementAsset: {
|
||||
__typename: 'Asset',
|
||||
symbol: 'tGBP',
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New some market',
|
||||
description: 'A new some market',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewMarket',
|
||||
instrument: {
|
||||
__typename: 'InstrumentConfiguration',
|
||||
name: 'Some market',
|
||||
code: 'FX:BTCUSD/DEC99',
|
||||
futureProduct: {
|
||||
__typename: 'FutureProduct',
|
||||
settlementAsset: {
|
||||
__typename: 'Asset',
|
||||
symbol: 'tGBP',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'New some market'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('New market');
|
||||
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
|
||||
'A new some market'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'tGBP settled future.'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders Update market proposal', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New market id',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateMarket',
|
||||
marketId: 'MarketId',
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New market id',
|
||||
},
|
||||
},
|
||||
})
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateMarket',
|
||||
marketId: 'MarketId',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'New market id'
|
||||
@@ -79,49 +79,53 @@ describe('Proposal header', () => {
|
||||
});
|
||||
|
||||
it('Renders New asset proposal - ERC20', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New asset: Fake currency',
|
||||
description: '',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewAsset',
|
||||
name: 'Fake currency',
|
||||
symbol: 'FAKE',
|
||||
source: {
|
||||
__typename: 'ERC20',
|
||||
contractAddress: '0x0',
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New asset: Fake currency',
|
||||
description: '',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewAsset',
|
||||
name: 'Fake currency',
|
||||
symbol: 'FAKE',
|
||||
source: {
|
||||
__typename: 'ERC20',
|
||||
contractAddress: '0x0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'New asset: Fake currency'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset');
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Symbol: FAKE. ERC20 contract address: 0x0'
|
||||
'Symbol: FAKE. ERC20 0x0'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders New asset proposal - BuiltInAsset', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewAsset',
|
||||
name: 'Fake currency',
|
||||
symbol: 'BIA',
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
maxFaucetAmountMint: '300',
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewAsset',
|
||||
name: 'Fake currency',
|
||||
symbol: 'BIA',
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
maxFaucetAmountMint: '300',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Unknown proposal'
|
||||
@@ -133,22 +137,24 @@ describe('Proposal header', () => {
|
||||
});
|
||||
|
||||
it('Renders Update network', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'Network parameter',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateNetworkParameter',
|
||||
networkParameter: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'Network key',
|
||||
value: 'Network value',
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'Network parameter',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateNetworkParameter',
|
||||
networkParameter: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'Network key',
|
||||
value: 'Network value',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Network parameter'
|
||||
@@ -161,211 +167,122 @@ describe('Proposal header', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders Freeform proposal - short rationale', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'short',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
it('Renders Freeform network - short rationale', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'short',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
},
|
||||
},
|
||||
})
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0');
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('Freeform');
|
||||
expect(
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent('short');
|
||||
});
|
||||
|
||||
it('Renders Freeform proposal - long rationale (105 chars) - listing', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
description:
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
it('Renders Freeform proposal - long rationale (105 chars)', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
description:
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||
},
|
||||
},
|
||||
})
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
// For a rationale over 100 chars, we expect the header to be truncated at
|
||||
// 100 chars with ellipsis and the details-one element to contain the rest.
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0');
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('Freeform');
|
||||
// Rationale in list view is not rendered
|
||||
expect(
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders Freeform proposal - long rationale (105 chars) - details', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
description:
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
},
|
||||
}),
|
||||
false
|
||||
);
|
||||
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
|
||||
/Class aptent/
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent('long');
|
||||
});
|
||||
|
||||
// Remove once proposals have rationale and re-enable above tests
|
||||
it('Renders Freeform proposal - id for title', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'freeform id',
|
||||
rationale: {
|
||||
title: 'freeform',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'freeform id',
|
||||
rationale: {
|
||||
title: 'freeform',
|
||||
},
|
||||
},
|
||||
})
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('freeform');
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('Freeform');
|
||||
expect(
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('proposal-details')).toHaveTextContent(
|
||||
'freeform id'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders asset change proposal header', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateAsset',
|
||||
assetId: 'foo',
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateAsset',
|
||||
assetId: 'foo',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent(
|
||||
'Update asset'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Update asset'
|
||||
);
|
||||
expect(screen.getByText('foo')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("Renders unknown proposal if it's a different proposal type", () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
// @ts-ignore unknown proposal
|
||||
__typename: 'Foo',
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
// @ts-ignore unknown proposal
|
||||
__typename: 'Foo',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Unknown proposal'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders proposal state: Enacted', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
terms: {
|
||||
enactmentDatetime: lastWeek.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Enacted');
|
||||
});
|
||||
|
||||
it('Renders proposal state: Passed', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_PASSED,
|
||||
terms: {
|
||||
closingDatetime: lastWeek.toString(),
|
||||
enactmentDatetime: nextWeek.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Passed');
|
||||
});
|
||||
|
||||
it('Renders proposal state: Waiting for node vote', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
terms: {
|
||||
enactmentDatetime: nextWeek.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent(
|
||||
'Waiting for node vote'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders proposal state: Open', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_OPEN,
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: generateYesVotes(3000, 1000000000000000000),
|
||||
no: generateNoVotes(0),
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: nextWeek.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
});
|
||||
|
||||
it('Renders proposal state: Declined - majority not reached', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_DECLINED,
|
||||
terms: {
|
||||
enactmentDatetime: lastWeek.toString(),
|
||||
},
|
||||
votes: {
|
||||
no: generateNoVotes(1, 1000000000000000000),
|
||||
yes: generateYesVotes(1, 1000000000000000000),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Declined');
|
||||
});
|
||||
|
||||
it('Renders proposal state: Rejected', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_REJECTED,
|
||||
terms: {
|
||||
enactmentDatetime: lastWeek.toString(),
|
||||
},
|
||||
rejectionReason:
|
||||
ProposalRejectionReason.PROPOSAL_ERROR_INVALID_FUTURE_PRODUCT,
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Rejected');
|
||||
});
|
||||
});
|
||||
|
||||
+43
-77
@@ -1,27 +1,23 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
import { Intent, Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
import { shorten } from '@vegaprotocol/utils';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
|
||||
export const ProposalHeader = ({
|
||||
proposal,
|
||||
isListItem = true,
|
||||
useSubHeading = true,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
isListItem?: boolean;
|
||||
useSubHeading?: boolean;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const change = proposal?.terms.change;
|
||||
|
||||
let details: ReactNode;
|
||||
let proposalType = '';
|
||||
let proposalType: ReactNode;
|
||||
|
||||
let title = proposal?.rationale.title.trim();
|
||||
let description = proposal?.rationale.description.trim();
|
||||
@@ -34,12 +30,10 @@ export const ProposalHeader = ({
|
||||
|
||||
switch (change?.__typename) {
|
||||
case 'NewMarket': {
|
||||
proposalType = 'NewMarket';
|
||||
proposalType = t('NewMarket');
|
||||
details = (
|
||||
<>
|
||||
<span>
|
||||
{t('Code')}: {change.instrument.code}.
|
||||
</span>{' '}
|
||||
{t('Code')}: {change.instrument.code}.{' '}
|
||||
{change.instrument.futureProduct?.settlementAsset.symbol ? (
|
||||
<>
|
||||
<span className="font-semibold">
|
||||
@@ -55,61 +49,54 @@ export const ProposalHeader = ({
|
||||
break;
|
||||
}
|
||||
case 'UpdateMarket': {
|
||||
proposalType = 'UpdateMarket';
|
||||
details = (
|
||||
<>
|
||||
<span>{t('Market change')}:</span>{' '}
|
||||
<span>{truncateMiddle(change.marketId)}</span>
|
||||
</>
|
||||
);
|
||||
proposalType = t('UpdateMarket');
|
||||
details = `${t('Market change')}: ${change.marketId}`;
|
||||
break;
|
||||
}
|
||||
case 'NewAsset': {
|
||||
proposalType = 'NewAsset';
|
||||
proposalType = t('NewAsset');
|
||||
details = (
|
||||
<>
|
||||
<span>{t('Symbol')}:</span> <Lozenge>{change.symbol}.</Lozenge>{' '}
|
||||
{change.source.__typename === 'ERC20' && (
|
||||
<>
|
||||
<span>{t('ERC20ContractAddress')}:</span>{' '}
|
||||
<Lozenge>{change.source.contractAddress}</Lozenge>
|
||||
</>
|
||||
)}{' '}
|
||||
{change.source.__typename === 'BuiltinAsset' && (
|
||||
<>
|
||||
<span>{t('MaxFaucetAmountMint')}:</span>{' '}
|
||||
<Lozenge>{change.source.maxFaucetAmountMint}</Lozenge>
|
||||
</>
|
||||
)}
|
||||
{t('Symbol')}: {change.symbol}.{' '}
|
||||
<Lozenge>
|
||||
{change.source.__typename === 'ERC20' &&
|
||||
`ERC20 ${change.source.contractAddress}`}
|
||||
{change.source.__typename === 'BuiltinAsset' &&
|
||||
`${t('Max faucet amount mint')}: ${
|
||||
change.source.maxFaucetAmountMint
|
||||
}`}
|
||||
</Lozenge>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'UpdateNetworkParameter': {
|
||||
proposalType = 'NetworkParameter';
|
||||
proposalType = t('NetworkParameter');
|
||||
const parametersClasses = 'font-mono leading-none';
|
||||
details = (
|
||||
<>
|
||||
<span>{t('Change')}:</span>{' '}
|
||||
<Lozenge>{change.networkParameter.key}</Lozenge>{' '}
|
||||
<span>{t('to')}</span>{' '}
|
||||
<span className="whitespace-nowrap">
|
||||
<Lozenge>{change.networkParameter.value}</Lozenge>
|
||||
<span className={`${parametersClasses} mr-2`}>
|
||||
{change.networkParameter.key}
|
||||
</span>{' '}
|
||||
{t('to')}{' '}
|
||||
<span className={`${parametersClasses} ml-2`}>
|
||||
{change.networkParameter.value}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'NewFreeform': {
|
||||
proposalType = 'Freeform';
|
||||
details = <span />;
|
||||
proposalType = t('Freeform');
|
||||
details = `${t('FreeformProposal')}: ${proposal?.id}`;
|
||||
break;
|
||||
}
|
||||
case 'UpdateAsset': {
|
||||
proposalType = 'UpdateAsset';
|
||||
proposalType = t('UpdateAsset');
|
||||
details = (
|
||||
<>
|
||||
<span>{t('AssetID')}:</span>{' '}
|
||||
<Lozenge>{truncateMiddle(change.assetId)}</Lozenge>
|
||||
`${t('Update asset')}`;
|
||||
<Lozenge>{change.assetId}</Lozenge>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
@@ -117,9 +104,9 @@ export const ProposalHeader = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="text-sm mb-2">
|
||||
<div data-testid="proposal-title">
|
||||
{isListItem ? (
|
||||
{useSubHeading ? (
|
||||
<header>
|
||||
<SubHeading title={titleContent || t('Unknown proposal')} />
|
||||
</header>
|
||||
@@ -129,39 +116,18 @@ export const ProposalHeader = ({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<div data-testid="proposal-type">
|
||||
<ProposalInfoLabel variant="secondary">
|
||||
{t(`${proposalType}`)}
|
||||
</ProposalInfoLabel>
|
||||
</div>
|
||||
{proposalType && (
|
||||
<div data-testid="proposal-type">
|
||||
<Lozenge variant={Intent.None}>{proposalType}</Lozenge>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div data-testid="proposal-status">
|
||||
<CurrentProposalState proposal={proposal} />
|
||||
</div>
|
||||
{description && (
|
||||
<div data-testid="proposal-description">{description}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{details && (
|
||||
<div data-testid="proposal-details" className="break-words my-10">
|
||||
{details}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{description && !isListItem && (
|
||||
<div data-testid="proposal-description">
|
||||
{/*<div className="uppercase mr-2">{t('ProposalDescription')}:</div>*/}
|
||||
<SubHeading title={t('ProposalDescription')} />
|
||||
<ReactMarkdown
|
||||
className="react-markdown-container"
|
||||
/* Prevents HTML embedded in the description from rendering */
|
||||
skipHtml={true}
|
||||
/* Stops users embedding images which could be used for tracking */
|
||||
disallowedElements={['img']}
|
||||
linkTarget="_blank"
|
||||
>
|
||||
{description}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
{details && <div data-testid="proposal-details">{details}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './proposal-info-label';
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export type ProposalInfoLabelVariant =
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'tertiary'
|
||||
| 'highlight';
|
||||
|
||||
const base = 'rounded-md px-2 py-1 font-alpha';
|
||||
const primary = 'bg-vega-light-150 text-black';
|
||||
const secondary = 'bg-vega-dark-200 text-white';
|
||||
const tertiary = 'bg-vega-dark-150 text-white';
|
||||
const highlight = 'bg-vega-yellow text-black';
|
||||
|
||||
const getClassname = (variant: ProposalInfoLabelVariant) => {
|
||||
return classNames(base, {
|
||||
[primary]: variant === 'primary',
|
||||
[secondary]: variant === 'secondary',
|
||||
[tertiary]: variant === 'tertiary',
|
||||
[highlight]: variant === 'highlight',
|
||||
});
|
||||
};
|
||||
|
||||
interface ProposalInfoLabelProps {
|
||||
children: ReactNode;
|
||||
variant?: ProposalInfoLabelVariant;
|
||||
}
|
||||
|
||||
export const ProposalInfoLabel = ({
|
||||
children,
|
||||
variant = 'primary',
|
||||
}: ProposalInfoLabelProps) => {
|
||||
return <div className={getClassname(variant)}>{children}</div>;
|
||||
};
|
||||
+3
-21
@@ -1,10 +1,8 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import { useState } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
export const ProposalTermsJson = ({
|
||||
terms,
|
||||
@@ -12,26 +10,10 @@ export const ProposalTermsJson = ({
|
||||
terms: PartialDeep<Schema.ProposalTerms>;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const showDetailsIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDetails,
|
||||
});
|
||||
|
||||
return (
|
||||
<section>
|
||||
<button
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
data-testid="proposal-terms-toggle"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('proposalTerms')} />
|
||||
<div className={showDetailsIconClasses}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{showDetails && <SyntaxHighlighter data={terms} />}
|
||||
<SubHeading title={t('proposalTerms')} />
|
||||
<SyntaxHighlighter data={terms} />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
+3
-7
@@ -1,4 +1,4 @@
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { ProposalVotesTable } from './proposal-votes-table';
|
||||
@@ -46,7 +46,6 @@ describe('Proposal Votes Table', () => {
|
||||
|
||||
it('should show vote breakdown fields, excluding custom update market fields', () => {
|
||||
renderComponent();
|
||||
fireEvent.click(screen.getByTestId('vote-breakdown-toggle'));
|
||||
expect(screen.getByText('Expected to pass')).toBeInTheDocument();
|
||||
expect(screen.getByText('Token majority met')).toBeInTheDocument();
|
||||
expect(screen.getByText('Token participation met')).toBeInTheDocument();
|
||||
@@ -56,7 +55,7 @@ describe('Proposal Votes Table', () => {
|
||||
expect(screen.getByText('Participation required')).toBeInTheDocument();
|
||||
expect(screen.getByText('Majority Required')).toBeInTheDocument();
|
||||
expect(screen.getByText('Number of voting parties')).toBeInTheDocument();
|
||||
expect(screen.getByText('Total tokens voted')).toBeInTheDocument();
|
||||
expect(screen.getByText('Total yes tokens')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Total tokens voted percentage')
|
||||
).toBeInTheDocument();
|
||||
@@ -71,14 +70,13 @@ describe('Proposal Votes Table', () => {
|
||||
|
||||
it('displays different breakdown fields for update market proposal', () => {
|
||||
renderComponent(updateMarketProposal, updateMarketProposalType);
|
||||
fireEvent.click(screen.getByTestId('vote-breakdown-toggle'));
|
||||
expect(screen.getByText('Liquidity majority met')).toBeInTheDocument();
|
||||
expect(screen.getByText('Liquidity participation met')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Liquidity shares for proposal')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Number of voting parties')).toBeNull();
|
||||
expect(screen.queryByText('Total tokens voted')).toBeNull();
|
||||
expect(screen.queryByText('Total yes tokens')).toBeNull();
|
||||
expect(screen.queryByText('Total tokens voted percentage')).toBeNull();
|
||||
expect(screen.queryByText('Number of votes for')).toBeNull();
|
||||
expect(screen.queryByText('Number of votes against')).toBeNull();
|
||||
@@ -88,7 +86,6 @@ describe('Proposal Votes Table', () => {
|
||||
|
||||
it('displays if an update market proposal will pass by token vote', () => {
|
||||
renderComponent(updateMarketProposal, updateMarketProposalType);
|
||||
fireEvent.click(screen.getByTestId('vote-breakdown-toggle'));
|
||||
expect(screen.getByText('👍 by token vote')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -113,7 +110,6 @@ describe('Proposal Votes Table', () => {
|
||||
}),
|
||||
updateMarketProposalType
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('vote-breakdown-toggle'));
|
||||
expect(screen.getByText('👍 by liquidity vote')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
+91
-112
@@ -1,12 +1,9 @@
|
||||
import classnames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
Thumbs,
|
||||
RoundedWrapper,
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { formatNumber, formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
@@ -29,7 +26,6 @@ export const ProposalVotesTable = ({
|
||||
const {
|
||||
appState: { totalSupply },
|
||||
} = useAppState();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const {
|
||||
willPassByTokenVote,
|
||||
willPassByLPVote,
|
||||
@@ -57,130 +53,113 @@ export const ProposalVotesTable = ({
|
||||
? t('byTokenVote')
|
||||
: t('byLiquidityVote');
|
||||
|
||||
const showDetailsIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDetails,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
data-testid="vote-breakdown-toggle"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('voteBreakdown')} />
|
||||
<div className={showDetailsIconClasses}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{showDetails && (
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
<KeyValueTable
|
||||
data-testid="proposal-votes-table"
|
||||
numerical={true}
|
||||
headingLevel={4}
|
||||
>
|
||||
<SubHeading title={t('voteBreakdown')} />
|
||||
<RoundedWrapper>
|
||||
<KeyValueTable
|
||||
data-testid="proposal-votes-table"
|
||||
numerical={true}
|
||||
headingLevel={4}
|
||||
>
|
||||
<KeyValueTableRow>
|
||||
{t('expectedToPass')}
|
||||
{isUpdateMarket ? (
|
||||
updateMarketWillPass ? (
|
||||
<Thumbs up={true} text={updateMarketVotePassMethod} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)
|
||||
) : willPassByTokenVote ? (
|
||||
<Thumbs up={true} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('majorityMet')}
|
||||
{majorityMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('expectedToPass')}
|
||||
{isUpdateMarket ? (
|
||||
updateMarketWillPass ? (
|
||||
<Thumbs up={true} text={updateMarketVotePassMethod} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)
|
||||
) : willPassByTokenVote ? (
|
||||
{t('majorityLPMet')}
|
||||
{majorityLPMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('participationMet')}
|
||||
{participationMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('participationLPMet')}
|
||||
{participationLPMet ? (
|
||||
<Thumbs up={true} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('tokenForProposal')}
|
||||
{formatNumber(yesTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('majorityMet')}
|
||||
{majorityMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
{t('tokenLPForProposal')}
|
||||
{formatNumber(yesEquityLikeShareWeight, 2)}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('totalSupply')}
|
||||
{formatNumber(totalSupply, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('tokensAgainstProposal')}
|
||||
{formatNumber(noTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('participationRequired')}
|
||||
{formatNumberPercentage(requiredParticipation)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('majorityRequired')}
|
||||
{formatNumberPercentage(requiredMajorityPercentage)}
|
||||
</KeyValueTableRow>
|
||||
{!isUpdateMarket && (
|
||||
<>
|
||||
<KeyValueTableRow>
|
||||
{t('majorityLPMet')}
|
||||
{majorityLPMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
{t('numberOfVotingParties')}
|
||||
{formatNumber(totalVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('participationMet')}
|
||||
{participationMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('participationLPMet')}
|
||||
{participationLPMet ? (
|
||||
<Thumbs up={true} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)}
|
||||
{t('totalTokensVotes')}
|
||||
{formatNumber(totalTokensVoted, 2)}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('tokenForProposal')}
|
||||
{formatNumber(yesTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('tokenLPForProposal')}
|
||||
{formatNumber(yesEquityLikeShareWeight, 2)}
|
||||
{t('totalTokenVotedPercentage')}
|
||||
{formatNumberPercentage(totalTokensPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('totalSupply')}
|
||||
{formatNumber(totalSupply, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('tokensAgainstProposal')}
|
||||
{formatNumber(noTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('participationRequired')}
|
||||
{formatNumberPercentage(requiredParticipation)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('majorityRequired')}
|
||||
{formatNumberPercentage(requiredMajorityPercentage)}
|
||||
</KeyValueTableRow>
|
||||
{!isUpdateMarket && (
|
||||
<>
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfVotingParties')}
|
||||
{formatNumber(totalVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('totalTokensVotes')}
|
||||
{formatNumber(totalTokensVoted, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('totalTokenVotedPercentage')}
|
||||
{formatNumberPercentage(totalTokensPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfForVotes')}
|
||||
{formatNumber(yesVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfAgainstVotes')}
|
||||
{formatNumber(noVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('yesPercentage')}
|
||||
{formatNumberPercentage(yesPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
{t('noPercentage')}
|
||||
{formatNumberPercentage(noPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
</>
|
||||
)}
|
||||
</KeyValueTable>
|
||||
</RoundedWrapper>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfForVotes')}
|
||||
{formatNumber(yesVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfAgainstVotes')}
|
||||
{formatNumber(noVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('yesPercentage')}
|
||||
{formatNumberPercentage(yesPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
{t('noPercentage')}
|
||||
{formatNumberPercentage(noPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
</>
|
||||
)}
|
||||
</KeyValueTable>
|
||||
</RoundedWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { AsyncRenderer, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
@@ -77,8 +77,8 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
||||
return (
|
||||
<AsyncRenderer data={params} loading={loading} error={error}>
|
||||
<section data-testid="proposal">
|
||||
<ProposalHeader proposal={proposal} isListItem={false} />
|
||||
<div className="my-10">
|
||||
<ProposalHeader proposal={proposal} useSubHeading={false} />
|
||||
<div className="mb-10">
|
||||
<ProposalChangeTable proposal={proposal} />
|
||||
</div>
|
||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||
@@ -91,18 +91,14 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
||||
/>
|
||||
) : null}
|
||||
<div className="mb-12">
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<VoteDetails
|
||||
proposal={proposal}
|
||||
proposalType={proposalType}
|
||||
minVoterBalance={minVoterBalance}
|
||||
spamProtectionMinTokens={
|
||||
params?.spam_protection_voting_min_tokens
|
||||
}
|
||||
/>
|
||||
</RoundedWrapper>
|
||||
<VoteDetails
|
||||
proposal={proposal}
|
||||
proposalType={proposalType}
|
||||
minVoterBalance={minVoterBalance}
|
||||
spamProtectionMinTokens={params?.spam_protection_voting_min_tokens}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<div className="mb-10">
|
||||
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
|
||||
</div>
|
||||
<ProposalTermsJson terms={proposal.terms} />
|
||||
|
||||
+21
-2
@@ -97,6 +97,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Enacted');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
format(lastWeek, DATE_FORMAT_DETAILED)
|
||||
);
|
||||
@@ -112,6 +113,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Passed');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
`Enacts on ${format(nextWeek, DATE_FORMAT_DETAILED)}`
|
||||
);
|
||||
@@ -126,6 +128,9 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent(
|
||||
'Waiting for node vote'
|
||||
);
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
`Enacts on ${format(nextWeek, DATE_FORMAT_DETAILED)}`
|
||||
);
|
||||
@@ -216,6 +221,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
'5 minutes left to vote'
|
||||
);
|
||||
@@ -230,6 +236,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
'5 hours left to vote'
|
||||
);
|
||||
@@ -244,6 +251,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
'5 days left to vote'
|
||||
);
|
||||
@@ -260,7 +268,10 @@ describe('Proposals list item details', () => {
|
||||
networkParamsQueryMock,
|
||||
createUserVoteQueryMock(proposal?.id, VoteValue.VALUE_YES),
|
||||
]);
|
||||
expect(await screen.findByText('You voted For')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
|
||||
expect(await screen.findByText('You voted')).toBeInTheDocument();
|
||||
expect(await screen.findByText('For')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders proposal state: Open - user voted against', async () => {
|
||||
@@ -274,7 +285,9 @@ describe('Proposals list item details', () => {
|
||||
networkParamsQueryMock,
|
||||
createUserVoteQueryMock(proposal?.id, VoteValue.VALUE_NO),
|
||||
]);
|
||||
expect(await screen.findByText('You voted Against')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(await screen.findByText('You voted')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Against')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders proposal state: Open - participation not reached', () => {
|
||||
@@ -290,6 +303,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Participation not reached'
|
||||
);
|
||||
@@ -308,6 +322,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Majority not reached'
|
||||
);
|
||||
@@ -327,6 +342,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent('Set to pass');
|
||||
});
|
||||
|
||||
@@ -343,6 +359,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Declined');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Participation not reached'
|
||||
);
|
||||
@@ -361,6 +378,7 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Declined');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Majority not reached'
|
||||
);
|
||||
@@ -377,6 +395,7 @@ describe('Proposals list item details', () => {
|
||||
ProposalRejectionReason.PROPOSAL_ERROR_INVALID_FUTURE_PRODUCT,
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Rejected');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Invalid future product'
|
||||
);
|
||||
|
||||
+55
-12
@@ -1,8 +1,11 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
import { StatusPass } from '../current-proposal-status/current-proposal-status';
|
||||
import {
|
||||
StatusPass,
|
||||
StatusFail,
|
||||
} from '../current-proposal-status/current-proposal-status';
|
||||
import { format, formatDistanceToNowStrict } from 'date-fns';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
@@ -19,7 +22,7 @@ const MajorityNotReached = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
{t('Majority')} {t('not reached')}
|
||||
{t('Majority')} <StatusFail>{t('not reached')}</StatusFail>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -27,7 +30,7 @@ const ParticipationNotReached = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
{t('Participation')} {t('not reached')}
|
||||
{t('Participation')} <StatusFail>{t('not reached')}</StatusFail>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -54,11 +57,17 @@ export const ProposalsListItemDetails = ({
|
||||
? t('byTokenVote')
|
||||
: t('byLPVote');
|
||||
|
||||
let proposalStatus: ReactNode;
|
||||
let voteDetails: ReactNode;
|
||||
let voteStatus: ReactNode;
|
||||
|
||||
switch (state) {
|
||||
case ProposalState.STATE_ENACTED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_Enacted')} <Icon name={'tick'} />
|
||||
</>
|
||||
);
|
||||
voteDetails = proposal?.terms.enactmentDatetime && (
|
||||
<>
|
||||
{format(
|
||||
@@ -70,6 +79,11 @@ export const ProposalsListItemDetails = ({
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_PASSED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_Passed')} <Icon name={'tick'} />
|
||||
</>
|
||||
);
|
||||
voteDetails = proposal?.terms.change.__typename !== 'NewFreeform' && (
|
||||
<>
|
||||
{t('toEnactOn')}{' '}
|
||||
@@ -83,6 +97,11 @@ export const ProposalsListItemDetails = ({
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_WaitingForNodeVote')} <Icon name={'time'} />
|
||||
</>
|
||||
);
|
||||
voteDetails = proposal?.terms.change.__typename !== 'NewFreeform' && (
|
||||
<>
|
||||
{t('toEnactOn')}{' '}
|
||||
@@ -96,14 +115,19 @@ export const ProposalsListItemDetails = ({
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_OPEN: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_Open')} <Icon name={'hand'} />
|
||||
</>
|
||||
);
|
||||
voteDetails = (voteState === 'Yes' && (
|
||||
<>
|
||||
{t('youVoted')} {t('voteState_Yes')}
|
||||
{t('youVoted')} <StatusPass>{t('voteState_Yes')}</StatusPass>
|
||||
</>
|
||||
)) ||
|
||||
(voteState === 'No' && (
|
||||
<>
|
||||
{t('youVoted')} {t('voteState_No')}
|
||||
{t('youVoted')} <StatusFail>{t('voteState_No')}</StatusFail>
|
||||
</>
|
||||
)) || (
|
||||
<>
|
||||
@@ -124,29 +148,40 @@ export const ProposalsListItemDetails = ({
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{t('Set to')} {t('fail')}
|
||||
{t('Set to')} <StatusFail>{t('fail')}</StatusFail>
|
||||
</>
|
||||
))) ||
|
||||
(!participationMet && <ParticipationNotReached />) ||
|
||||
(!majorityMet && <MajorityNotReached />) ||
|
||||
(willPassByTokenVote ? (
|
||||
<>
|
||||
{t('Set to')} {t('pass')}
|
||||
{t('Set to')} <StatusPass>{t('pass')}</StatusPass>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{t('Set to')} {t('fail')}
|
||||
{t('Set to')} <StatusFail>{t('fail')}</StatusFail>
|
||||
</>
|
||||
));
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_DECLINED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_Declined')} <Icon name={'cross'} />
|
||||
</>
|
||||
);
|
||||
voteStatus =
|
||||
(!participationMet && <ParticipationNotReached />) ||
|
||||
(!majorityMet && <MajorityNotReached />);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_REJECTED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<StatusFail>{t('voteState_Rejected')}</StatusFail>{' '}
|
||||
<Icon name={'warning-sign'} />
|
||||
</>
|
||||
);
|
||||
voteStatus = proposal?.rejectionReason && (
|
||||
<>{t(ProposalRejectionReasonMapping[proposal.rejectionReason])}</>
|
||||
);
|
||||
@@ -155,10 +190,16 @@ export const ProposalsListItemDetails = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-[1fr_auto] mt-4 items-start gap-2 text-sm">
|
||||
<div className="grid grid-cols-[1fr_auto] mt-2 items-start gap-2 text-sm">
|
||||
<div
|
||||
className="col-start-1 row-start-1 flex items-center gap-2 text-white"
|
||||
data-testid="proposal-status"
|
||||
>
|
||||
{proposalStatus}
|
||||
</div>
|
||||
{voteDetails && (
|
||||
<div
|
||||
className="col-start-1 row-start-2 text-vega-light-300"
|
||||
className="col-start-1 row-start-2 text-neutral-500"
|
||||
data-testid="vote-details"
|
||||
>
|
||||
{voteDetails}
|
||||
@@ -175,7 +216,9 @@ export const ProposalsListItemDetails = ({
|
||||
{proposal?.id && (
|
||||
<div className="col-start-2 row-start-2 justify-self-end">
|
||||
<Link to={`${Routes.PROPOSALS}/${proposal.id}`}>
|
||||
<Button data-testid="view-proposal-btn">{t('View')}</Button>
|
||||
<Button data-testid="view-proposal-btn" size="sm">
|
||||
{t('View')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -106,7 +106,7 @@ export const ProposalsList = ({
|
||||
{proposals.length > 0 && (
|
||||
<ProposalsListFilter setFilterString={setFilterString} />
|
||||
)}
|
||||
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
|
||||
<section className="-mx-4 p-4 mb-8 bg-neutral-800">
|
||||
<SubHeading title={t('openProposals')} />
|
||||
{sortedProposals.open.length > 0 ||
|
||||
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
||||
|
||||
+32
-30
@@ -3,15 +3,15 @@ import { Link } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Intent,
|
||||
Lozenge,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { stripFullStops } from '@vegaprotocol/utils';
|
||||
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
import Routes from '../../../routes';
|
||||
import type { ReactNode } from 'react';
|
||||
import Routes from '../../../routes';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
|
||||
interface ProtocolProposalsListItemProps {
|
||||
@@ -29,30 +29,30 @@ export const ProtocolUpgradeProposalsListItem = ({
|
||||
switch (proposal.status) {
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED:
|
||||
proposalStatusIcon = (
|
||||
<span data-testid="protocol-upgrade-proposal-status-icon-rejected">
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-rejected">
|
||||
<Icon name={'cross'} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING:
|
||||
proposalStatusIcon = (
|
||||
<span data-testid="protocol-upgrade-proposal-status-icon-pending">
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-pending">
|
||||
<Icon name={'time'} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED:
|
||||
proposalStatusIcon = (
|
||||
<span data-testid="protocol-upgrade-proposal-status-icon-approved">
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-approved">
|
||||
<Icon name={'tick'} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED:
|
||||
proposalStatusIcon = (
|
||||
<span data-testid="protocol-upgrade-proposal-status-icon-unspecified">
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-unspecified">
|
||||
<Icon name={'disable'} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -71,28 +71,18 @@ export const ProtocolUpgradeProposalsListItem = ({
|
||||
</div>
|
||||
|
||||
<div className="text-sm">
|
||||
<div className="flex gap-2">
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-type"
|
||||
className="flex items-center gap-2 mb-4"
|
||||
>
|
||||
<ProposalInfoLabel variant="highlight">
|
||||
{t('networkUpgrade')}
|
||||
</ProposalInfoLabel>
|
||||
</div>
|
||||
|
||||
<div data-testid="protocol-upgrade-proposal-status">
|
||||
<ProposalInfoLabel>
|
||||
{t(`${proposal.status}`)} {proposalStatusIcon}
|
||||
</ProposalInfoLabel>
|
||||
</div>
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-type"
|
||||
className="flex items-center gap-2 mb-4"
|
||||
>
|
||||
<Lozenge variant={Intent.Success}>{t('networkUpgrade')}</Lozenge>
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-release-tag"
|
||||
className="mb-2"
|
||||
>
|
||||
<span>{t('vegaReleaseTag')}:</span>{' '}
|
||||
<span className="pr-2">{t('vegaReleaseTag')}</span>
|
||||
<Lozenge>{proposal.vegaReleaseTag}</Lozenge>
|
||||
</div>
|
||||
|
||||
@@ -100,18 +90,30 @@ export const ProtocolUpgradeProposalsListItem = ({
|
||||
data-testid="protocol-upgrade-proposal-block-height"
|
||||
className="mb-2"
|
||||
>
|
||||
<span>{t('upgradeBlockHeight')}:</span>{' '}
|
||||
<span className="pr-2">{t('upgradeBlockHeight')}</span>
|
||||
<Lozenge>{proposal.upgradeBlockHeight}</Lozenge>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 mt-3">
|
||||
<div className="justify-self-end">
|
||||
<div className="grid grid-cols-[1fr_auto] mt-3 items-start gap-2">
|
||||
<div className="col-start-1 row-start-1 text-white">
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-status"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<span>{t(`${proposal.status}`)}</span>
|
||||
<span>{proposalStatusIcon}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-start-2 row-start-2 justify-self-end">
|
||||
<Link
|
||||
to={`${Routes.PROPOSALS}/protocol-upgrade/${stripFullStops(
|
||||
proposal.vegaReleaseTag
|
||||
)}`}
|
||||
>
|
||||
<Button data-testid="view-proposal-btn">{t('View')}</Button>
|
||||
<Button data-testid="view-proposal-btn" size="sm">
|
||||
{t('View')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -107,6 +107,10 @@ export const VoteButtons = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (currentStakeAvailable.isLessThanOrEqualTo(0)) {
|
||||
return t('noGovernanceTokens');
|
||||
}
|
||||
|
||||
if (minVoterBalance && spamProtectionMinTokens) {
|
||||
const formattedMinVoterBalance = new BigNumber(
|
||||
addDecimal(minVoterBalance, 18)
|
||||
@@ -159,30 +163,24 @@ export const VoteButtons = ({
|
||||
return (
|
||||
<>
|
||||
{changeVote || (voteState === VoteState.NotCast && proposalVotable) ? (
|
||||
<>
|
||||
{currentStakeAvailable.isLessThanOrEqualTo(0) && (
|
||||
<p data-testid="no-stake-available">{t('noGovernanceTokens')}</p>
|
||||
)}
|
||||
|
||||
<div className="flex gap-4" data-testid="vote-buttons">
|
||||
<div className="flex gap-4" data-testid="vote-buttons">
|
||||
<div className="flex-1">
|
||||
<Button
|
||||
data-testid="vote-for"
|
||||
onClick={() => submitVote(VoteValue.VALUE_YES)}
|
||||
variant="primary"
|
||||
disabled={currentStakeAvailable.isLessThanOrEqualTo(0)}
|
||||
>
|
||||
{t('voteFor')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Button
|
||||
data-testid="vote-against"
|
||||
onClick={() => submitVote(VoteValue.VALUE_NO)}
|
||||
variant="primary"
|
||||
disabled={currentStakeAvailable.isLessThanOrEqualTo(0)}
|
||||
>
|
||||
{t('voteAgainst')}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
) : (
|
||||
(voteState === VoteState.Yes || voteState === VoteState.No) && (
|
||||
<p data-testid="you-voted">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { RoundedWrapper, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
|
||||
@@ -200,11 +199,10 @@ export const VoteDetails = ({
|
||||
{proposalType === ProposalType.PROPOSAL_UPDATE_MARKET && (
|
||||
<p>{t('votingThresholdInfo')}</p>
|
||||
)}
|
||||
|
||||
<section className="mt-10">
|
||||
<SubHeading title={t('castYourVote')} />
|
||||
{pubKey ? (
|
||||
proposal && (
|
||||
{pubKey ? (
|
||||
<section className="mt-10">
|
||||
<SubHeading title={t('yourVote')} />
|
||||
{proposal && (
|
||||
<VoteButtonsContainer
|
||||
voteState={voteState}
|
||||
voteDatetime={voteDatetime}
|
||||
@@ -216,19 +214,11 @@ export const VoteDetails = ({
|
||||
submit={submit}
|
||||
dialog={Dialog}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Icon name={'info-sign'} />
|
||||
<div>{t('connectAVegaWalletToVote')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<ConnectToVega />
|
||||
</RoundedWrapper>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
</section>
|
||||
) : (
|
||||
<ConnectToVega />
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -34,9 +34,8 @@ export const useUserTrancheBalances = (address: string | undefined) => {
|
||||
vesting.get_vested_for_tranche(address, tId),
|
||||
]);
|
||||
|
||||
// Convert t and v EthersBigNumbers to regular BigNumbers
|
||||
const total = toBigNum(t.toString(), decimals);
|
||||
const vested = toBigNum(v.toString(), decimals);
|
||||
const total = toBigNum(t, decimals);
|
||||
const vested = toBigNum(v, decimals);
|
||||
|
||||
return {
|
||||
id: tId,
|
||||
|
||||
@@ -54,7 +54,7 @@ export const WalletAssociate = ({
|
||||
address,
|
||||
ethereumConfig.staking_bridge_contract.address
|
||||
);
|
||||
const allowance = toBigNum(a.toString(), decimals);
|
||||
const allowance = toBigNum(a, decimals);
|
||||
setAllowance(allowance);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@ import '@testing-library/jest-dom';
|
||||
import dev from './i18n/translations/dev.json';
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
// Set up i18n instance so that components have the correct default
|
||||
// en translations
|
||||
@@ -23,5 +22,3 @@ i18n.use(initReactI18next).init({
|
||||
ns: ['translations'],
|
||||
defaultNS: 'translations',
|
||||
});
|
||||
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
||||
@@ -40,11 +40,6 @@
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-row {
|
||||
border-width: 1px 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* Dark variables */
|
||||
.ag-theme-balham-dark {
|
||||
--ag-background-color: theme(colors.black);
|
||||
@@ -63,39 +58,3 @@
|
||||
.validators-table .ag-theme-balham-dark *:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Styles required to (effectively) un-override the
|
||||
* reset styles so that the Proposal description fields
|
||||
* render as you'd expect them to.
|
||||
*
|
||||
* Notes:
|
||||
* - image embeds are disabled, so no styles are required
|
||||
* - strong may not be required
|
||||
* - skipHTML is enabled, so no nested HTML will be rendered. Only
|
||||
* . valid markdown
|
||||
*/
|
||||
|
||||
.dark .react-markdown-container,
|
||||
.dark .react-markdown-container li,
|
||||
.dark .react-markdown-container p {
|
||||
color: #fff;
|
||||
}
|
||||
.react-markdown-container strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.react-markdown-container ol {
|
||||
margin-left: 1em;
|
||||
}
|
||||
.react-markdown-container li {
|
||||
margin-left: 1em;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.react-markdown-container ol li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.react-markdown-container ul li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_URL=https://api.vega.xyz/query
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\"}
|
||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_URL=https://api.vega.xyz/query
|
||||
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
|
||||
NX_VEGA_ENV=MAINNET
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
"tranche_start": "2023-04-20T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-20T00:00:00.000Z",
|
||||
"total_added": "19242.125",
|
||||
"total_removed": "1523.8177488329475",
|
||||
"locked_amount": "6891.9471601080244395475",
|
||||
"total_removed": "1214.9959502191275",
|
||||
"locked_amount": "9295.5944273726850283975",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "188",
|
||||
@@ -223,11 +223,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "308.82179861382",
|
||||
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
|
||||
"tx": "0x2b3571c143ecebddf91fb62f402d516d51110edfe37b13200a6e5cf682dc5bb0"
|
||||
},
|
||||
{
|
||||
"amount": "202.093666077975",
|
||||
"user": "0x1447Efc62d5077d7AbB20492dF831Dd6c9Eb1756",
|
||||
@@ -356,12 +351,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "308.82179861382",
|
||||
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
|
||||
"tranche_id": 56,
|
||||
"tx": "0x2b3571c143ecebddf91fb62f402d516d51110edfe37b13200a6e5cf682dc5bb0"
|
||||
},
|
||||
{
|
||||
"amount": "195.89040769089",
|
||||
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
|
||||
@@ -370,8 +359,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "914.25",
|
||||
"withdrawn_tokens": "504.71220630471",
|
||||
"remaining_tokens": "409.53779369529"
|
||||
"withdrawn_tokens": "195.89040769089",
|
||||
"remaining_tokens": "718.35959230911"
|
||||
},
|
||||
{
|
||||
"address": "0x9573BDF7FfC5519912d293e4D1f750eab2E471E7",
|
||||
@@ -876,15 +865,10 @@
|
||||
"tranche_id": 54,
|
||||
"tranche_start": "2023-04-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-06T00:00:00.000Z",
|
||||
"total_added": "14610",
|
||||
"total_removed": "6141.45090157707",
|
||||
"locked_amount": "0",
|
||||
"total_added": "14520",
|
||||
"total_removed": "5079.76864115505",
|
||||
"locked_amount": "238.4036111111108718",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "90",
|
||||
"user": "0x05659B08a8079E003eE37F26c29e52532728c034",
|
||||
"tx": "0x83cb92910e0725b82b3459e025260c448f931224f7cb00a376351dbd75ae2733"
|
||||
},
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0x90Cf7B9958C3BbBCddAb52Ecc408535D4f3C4241",
|
||||
@@ -1037,46 +1021,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "191.85071991228",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
"tx": "0x1438ad7d4c51ec6cb9b8f9d67b05583915099427035179baf996fd208fd76603"
|
||||
},
|
||||
{
|
||||
"amount": "309",
|
||||
"user": "0x21770cCAb229Bd1509609fFFa35bEB16b73A657c",
|
||||
"tx": "0x264f4b3c5dc43cd5afaa5954b50ba7885ce3091b11ae51ba19b8a5851a5a4fc7"
|
||||
},
|
||||
{
|
||||
"amount": "116.83154050974",
|
||||
"user": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
"tx": "0x130c439ea764588c13cd825ae89767bea04064bd3d76b762b7148e5373857b9b"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
"tx": "0x9be84231ff156bc8b8de9a99250f03b8ebf9d59c252e3b778f10051489e5758a"
|
||||
},
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0x90Cf7B9958C3BbBCddAb52Ecc408535D4f3C4241",
|
||||
"tx": "0xdfae5add3e12eed919c0481d9289aa33688884db68ca90332d1df31107e4fbc6"
|
||||
},
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0xAc1c2783d64c29CDB79608f5B66a37Fd1ea35dD6",
|
||||
"tx": "0x7195dc5808870b0f00824c6593eda1e994976189cf3373cf84c5dae7a60d8b62"
|
||||
},
|
||||
{
|
||||
"amount": "90",
|
||||
"user": "0x05659B08a8079E003eE37F26c29e52532728c034",
|
||||
"tx": "0x016970cfdaaab7a663a5c8b897b44c5d302e5cd110638de231073c3b5e23ddf8"
|
||||
},
|
||||
{
|
||||
"amount": "132",
|
||||
"user": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
|
||||
"tx": "0x1dbcf713b48965a82aa2e17cb3e7db9a491668d859d408a39c8a74b0ea860b6b"
|
||||
},
|
||||
{
|
||||
"amount": "106.53500000286",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
@@ -1124,28 +1068,6 @@
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"address": "0x05659B08a8079E003eE37F26c29e52532728c034",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "90",
|
||||
"user": "0x05659B08a8079E003eE37F26c29e52532728c034",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x83cb92910e0725b82b3459e025260c448f931224f7cb00a376351dbd75ae2733"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "90",
|
||||
"user": "0x05659B08a8079E003eE37F26c29e52532728c034",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x016970cfdaaab7a663a5c8b897b44c5d302e5cd110638de231073c3b5e23ddf8"
|
||||
}
|
||||
],
|
||||
"total_tokens": "90",
|
||||
"withdrawn_tokens": "90",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x90Cf7B9958C3BbBCddAb52Ecc408535D4f3C4241",
|
||||
"deposits": [
|
||||
@@ -1156,17 +1078,10 @@
|
||||
"tx": "0x1229e077f48b796678436bfa8143cea3ae71df58b589ac922be205c4482be235"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0x90Cf7B9958C3BbBCddAb52Ecc408535D4f3C4241",
|
||||
"tranche_id": 54,
|
||||
"tx": "0xdfae5add3e12eed919c0481d9289aa33688884db68ca90332d1df31107e4fbc6"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "111",
|
||||
"withdrawn_tokens": "111",
|
||||
"remaining_tokens": "0"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "111"
|
||||
},
|
||||
{
|
||||
"address": "0x83D7eD53E7CB97b542F1F71f40561d51F8019C8B",
|
||||
@@ -1411,12 +1326,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "191.85071991228",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x1438ad7d4c51ec6cb9b8f9d67b05583915099427035179baf996fd208fd76603"
|
||||
},
|
||||
{
|
||||
"amount": "106.53500000286",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
@@ -1455,8 +1364,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "858",
|
||||
"withdrawn_tokens": "858",
|
||||
"remaining_tokens": "0"
|
||||
"withdrawn_tokens": "666.14928008772",
|
||||
"remaining_tokens": "191.85071991228"
|
||||
},
|
||||
{
|
||||
"address": "0x0bBf7580e036eA5D69ABe679CC90117EeC2e3dc1",
|
||||
@@ -1484,18 +1393,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "116.83154050974",
|
||||
"user": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x130c439ea764588c13cd825ae89767bea04064bd3d76b762b7148e5373857b9b"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x9be84231ff156bc8b8de9a99250f03b8ebf9d59c252e3b778f10051489e5758a"
|
||||
},
|
||||
{
|
||||
"amount": "60.16845949026",
|
||||
"user": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
@@ -1504,8 +1401,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "177",
|
||||
"withdrawn_tokens": "177",
|
||||
"remaining_tokens": "0"
|
||||
"withdrawn_tokens": "60.16845949026",
|
||||
"remaining_tokens": "116.83154050974"
|
||||
},
|
||||
{
|
||||
"address": "0xAc1c2783d64c29CDB79608f5B66a37Fd1ea35dD6",
|
||||
@@ -1517,17 +1414,10 @@
|
||||
"tx": "0x99aeaedef27b5fb693485f4e21d434e343b30fa100945a4ff65386de8801c87e"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0xAc1c2783d64c29CDB79608f5B66a37Fd1ea35dD6",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x7195dc5808870b0f00824c6593eda1e994976189cf3373cf84c5dae7a60d8b62"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "111",
|
||||
"withdrawn_tokens": "111",
|
||||
"remaining_tokens": "0"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "111"
|
||||
},
|
||||
{
|
||||
"address": "0x8D416A61bCccf4E6aF5598302BC4e41f97401652",
|
||||
@@ -1554,17 +1444,10 @@
|
||||
"tx": "0xcb0f255003872ac506798efa97744868e11ceab2f2f03d96da605d8674f783f6"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "132",
|
||||
"user": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x1dbcf713b48965a82aa2e17cb3e7db9a491668d859d408a39c8a74b0ea860b6b"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "132",
|
||||
"withdrawn_tokens": "132",
|
||||
"remaining_tokens": "0"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "132"
|
||||
},
|
||||
{
|
||||
"address": "0xC0f02AA8bA8b509D223C9De6108AFA1C03f03341",
|
||||
@@ -1591,17 +1474,10 @@
|
||||
"tx": "0xf6467d54c2de5c5d21fd3ac174ac6bf51b6fc675cf81dedcb143679f28caa46a"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "309",
|
||||
"user": "0x21770cCAb229Bd1509609fFFa35bEB16b73A657c",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x264f4b3c5dc43cd5afaa5954b50ba7885ce3091b11ae51ba19b8a5851a5a4fc7"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "309",
|
||||
"withdrawn_tokens": "309",
|
||||
"remaining_tokens": "0"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "309"
|
||||
},
|
||||
{
|
||||
"address": "0x0629D3C608e1E8e9B88a1B00e1422e4B33276a98",
|
||||
@@ -4861,7 +4737,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "49802.2753121154213651374",
|
||||
"locked_amount": "50692.0833719598561288655",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "86666.297",
|
||||
@@ -4927,7 +4803,7 @@
|
||||
"tranche_end": "2023-06-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "312.4325905575906",
|
||||
"locked_amount": "363.908920940171075",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
@@ -4960,7 +4836,7 @@
|
||||
"tranche_end": "2023-11-01T00:00:00.000Z",
|
||||
"total_added": "15000.000000000000015",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "14327.0455917874395143270455917874395",
|
||||
"locked_amount": "14632.5464221014495146325464221014495",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1.5e-14",
|
||||
@@ -5048,7 +4924,7 @@
|
||||
"tranche_end": "2023-09-01T00:00:00.000Z",
|
||||
"total_added": "17500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "10913.25608896940375",
|
||||
"locked_amount": "11269.67372433574875",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
@@ -5315,7 +5191,7 @@
|
||||
"tranche_end": "2023-08-01T00:00:00.000Z",
|
||||
"total_added": "37500",
|
||||
"total_removed": "18077.0118744",
|
||||
"locked_amount": "17350.5026089625535",
|
||||
"locked_amount": "18126.91355893186125",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -5734,7 +5610,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "49756.837002255673748325",
|
||||
"locked_amount": "50645.8332242054391795015",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -5767,7 +5643,7 @@
|
||||
"tranche_end": "2024-04-01T00:00:00.000Z",
|
||||
"total_added": "54144.7663",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "48485.46842731635829207292",
|
||||
"locked_amount": "49039.85711135509392504979",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "54144.7663",
|
||||
@@ -5800,7 +5676,7 @@
|
||||
"tranche_end": "2023-09-03T00:00:00.000Z",
|
||||
"total_added": "62600",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "20022.5830035514954",
|
||||
"locked_amount": "20665.30095129376038",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10000",
|
||||
@@ -5993,7 +5869,7 @@
|
||||
"tranche_end": "2023-09-17T00:00:00.000Z",
|
||||
"total_added": "5000",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1791.028665651953",
|
||||
"locked_amount": "1842.3639649923895",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5000",
|
||||
@@ -7062,7 +6938,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "1709370.7872515768348",
|
||||
"locked_amount": "126202.1342632826843379468",
|
||||
"locked_amount": "146119.4950805440611341152",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -40934,7 +40810,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "715655.108029600523393",
|
||||
"locked_amount": "218430.066175628697078272948",
|
||||
"locked_amount": "249036.115263241476836903328",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -42326,8 +42202,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15870102.715470999700000001",
|
||||
"total_removed": "871680.07831804700259352",
|
||||
"locked_amount": "6074226.5757489411253544394273819693295485",
|
||||
"total_removed": "870712.39693462448509352",
|
||||
"locked_amount": "6182753.66071744239145030977234379973803727",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -42831,11 +42707,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "967.6813834225175",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13"
|
||||
},
|
||||
{
|
||||
"amount": "10150.87581603206683",
|
||||
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
|
||||
@@ -45004,12 +44875,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "967.6813834225175",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13"
|
||||
},
|
||||
{
|
||||
"amount": "913.910324501590625",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -46566,8 +46431,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "259998.8875",
|
||||
"withdrawn_tokens": "159590.522104386423875",
|
||||
"remaining_tokens": "100408.365395613576125"
|
||||
"withdrawn_tokens": "158622.840720963906375",
|
||||
"remaining_tokens": "101376.046779036093625"
|
||||
},
|
||||
{
|
||||
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
|
||||
@@ -59069,8 +58934,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "44479.0535455583416",
|
||||
"locked_amount": "34611.49258501772999518148756976",
|
||||
"total_removed": "44110.3307271943416",
|
||||
"locked_amount": "39461.19601458522069398354033484",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
@@ -65689,11 +65554,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "368.722818364",
|
||||
"user": "0x0436E2E4EA467f9958403dc56635404EE1bb1E94",
|
||||
"tx": "0x97f5b5ec5ac839a8d182b9267b5d052cb70f9a18b648874337e3d5360b21a5fb"
|
||||
},
|
||||
{
|
||||
"amount": "182.662252664",
|
||||
"user": "0xb1169C6daAc76bAcaf0D8f87641Fc38fbabe569F",
|
||||
@@ -81903,17 +81763,10 @@
|
||||
"tx": "0xb1425d9b0d5f10c5b06236d6f9d61ebddd21c4027e0f41deb5ec227a1753c715"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "368.722818364",
|
||||
"user": "0x0436E2E4EA467f9958403dc56635404EE1bb1E94",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x97f5b5ec5ac839a8d182b9267b5d052cb70f9a18b648874337e3d5360b21a5fb"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "400",
|
||||
"withdrawn_tokens": "368.722818364",
|
||||
"remaining_tokens": "31.277181636"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "400"
|
||||
},
|
||||
{
|
||||
"address": "0x6672F8789C54b49C596E6e7Ca3A72726Fc4C400E",
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
import { isBefore, isAfter, addSeconds, subSeconds } from 'date-fns';
|
||||
import { createOrder } from '../support/create-order';
|
||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
||||
import { selectAsset } from '../support/helpers';
|
||||
|
||||
const orderSize = 'size';
|
||||
const orderType = 'type';
|
||||
@@ -23,13 +22,15 @@ const assetSelectField = 'select[name="asset"]';
|
||||
const amountField = 'input[name="amount"]';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
const sepoliaUrl = Cypress.env('ETHERSCAN_URL');
|
||||
const btcName = 0;
|
||||
const vegaName = 4;
|
||||
const btcName =
|
||||
'BTC (local)5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c - tBTC';
|
||||
const vegaName =
|
||||
'Vegab4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b - VEGA';
|
||||
const btcSymbol = 'tBTC';
|
||||
const vegaSymbol = 'VEGA';
|
||||
const usdcSymbol = 'fUSDC';
|
||||
const toastContent = 'toast-content';
|
||||
const openOrdersTab = 'Open';
|
||||
const ordersTab = 'Orders';
|
||||
const depositsTab = 'Deposits';
|
||||
const collateralTab = 'Collateral';
|
||||
const toastCloseBtn = 'toast-close';
|
||||
@@ -49,7 +50,6 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
cy.get('@markets').then((markets) => {
|
||||
cy.wrap(markets[0]).as('market');
|
||||
});
|
||||
cy.visit('/#/portfolio');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -73,7 +73,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
selectAsset(btcName);
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
cy.getByTestId('approve-default').should(
|
||||
'contain.text',
|
||||
`Before you can make a deposit of your chosen asset, ${btcSymbol}, you need to approve its use in your Ethereum wallet`
|
||||
@@ -81,7 +81,6 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
cy.getByTestId(approveSubmit).click();
|
||||
cy.getByTestId('approve-pending').should('exist');
|
||||
cy.getByTestId('approve-confirmed').should('exist');
|
||||
cy.get(amountField).focus();
|
||||
cy.get(amountField).clear().type('10');
|
||||
cy.getByTestId(depositSubmit).click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
@@ -116,6 +115,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
});
|
||||
|
||||
it('can key to key transfers', function () {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
|
||||
cy.getByTestId(collateralTab).click();
|
||||
@@ -147,8 +147,8 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
selectAsset(0);
|
||||
cy.get(amountField).focus();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
@@ -170,7 +170,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Error occurredcannot estimate gas'
|
||||
'Error occurredprocessing response error'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId(completeWithdrawalBtn).should(
|
||||
@@ -180,7 +180,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
describe('capsule', { tags: '@slow' }, () => {
|
||||
before(() => {
|
||||
cy.updateCapsuleMultiSig();
|
||||
});
|
||||
@@ -231,7 +231,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
`Order submittedYour transaction has been confirmed View in block explorerSubmit order - activeTEST.24h+${order.size} @ ${order.price}.00 ${usdcSymbol}`,
|
||||
`ConfirmedYour transaction has been confirmed View in block explorerSubmit order - activeTEST.24h+${order.size} @ ${order.price}.00 ${usdcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
@@ -242,9 +242,9 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
.get(`[data-testid="bid-vol-${rawPrice}"]`)
|
||||
.should('contain.text', order.size);
|
||||
|
||||
cy.getByTestId(openOrdersTab).click();
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.getByTestId('edit', txTimeout).should('contain.text', 'Edit');
|
||||
cy.getByTestId('tab-open-orders').within(() => {
|
||||
cy.getByTestId('tab-orders').within(() => {
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
.first()
|
||||
@@ -283,9 +283,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
});
|
||||
});
|
||||
it('can edit order', function () {
|
||||
const market = this.market;
|
||||
cy.visit(`/#/markets/${market.id}`);
|
||||
cy.getByTestId(openOrdersTab).click();
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.getByTestId('edit').first().should('be.visible').click();
|
||||
cy.getByTestId('dialog-title').should('contain.text', 'Edit order');
|
||||
cy.get('#limitPrice').focus().clear().type(newPrice);
|
||||
@@ -293,12 +291,12 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
`Order submittedYour transaction has been confirmed View in block explorerEdit order - activeTEST.24h+${size} @ ${price}.00 ${usdcSymbol}+${size} @ ${newPrice}.00 ${usdcSymbol}`,
|
||||
`ConfirmedYour transaction has been confirmed View in block explorerEdit order - activeTEST.24h+${size} @ ${price}.00 ${usdcSymbol}+${size} @ ${newPrice}.00 ${usdcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId(openOrdersTab).click();
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
.first()
|
||||
@@ -309,20 +307,18 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderUpdatedAt);
|
||||
});
|
||||
});
|
||||
|
||||
it('can cancel order', function () {
|
||||
const market = this.market;
|
||||
cy.visit(`/#/markets/${market.id}`);
|
||||
cy.getByTestId(openOrdersTab).click();
|
||||
// comment because of bug #2695
|
||||
it.skip('can cancel order', function () {
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.getByTestId('cancel').first().click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
`Order cancelledYour transaction has been confirmed View in block explorerCancel order - cancelledTEST.24h+${size} @ ${newPrice}.00 ${usdcSymbol}`,
|
||||
`ConfirmedYour transaction has been confirmed View in block explorerCancel order - cancelledTEST.24h+${size} @ ${newPrice}.00 ${usdcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId('Closed').click();
|
||||
cy.getByTestId('tab-closed-orders')
|
||||
|
||||
cy.getByTestId('tab-orders')
|
||||
.get('.ag-center-cols-container')
|
||||
.children()
|
||||
.first()
|
||||
@@ -352,7 +348,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
selectAsset(btcName);
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
@@ -375,15 +371,19 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
'contain.text',
|
||||
'Transaction confirmed'
|
||||
);
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Funds unlockedYour funds have been unlocked for withdrawalView in block explorerWithdraw 1.00 tBTCComplete withdrawal'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
|
||||
cy.get('.ag-center-cols-container')
|
||||
.find('[col-id="status"]')
|
||||
.eq(0, txTimeout)
|
||||
.should('contain.text', 'Completed');
|
||||
cy.wrap(null).then(() => {
|
||||
try {
|
||||
cy.getByTestId(completeWithdrawalBtn)
|
||||
.eq(0, txTimeout)
|
||||
.should('not.exist');
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'Assertion failed, but we are continuing because this is our wait to complete transaction'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
cy.get('[col-id="txHash"]', txTimeout)
|
||||
.should('have.length.above', 1)
|
||||
@@ -404,20 +404,17 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
|
||||
cy.getByTestId('withdraw-dialog-button').click({ force: true });
|
||||
cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '7.999');
|
||||
// comment because of bug #2819
|
||||
// cy.getByTestId('withdraw-dialog-button').click();
|
||||
// cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '0')
|
||||
});
|
||||
|
||||
it('approved amount is less than deposit', function () {
|
||||
// 1001-DEPO-006
|
||||
// 1001-DEPO-007
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
selectAsset(btcName);
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
cy.contains('Deposits of tBTC not approved').should('not.exist');
|
||||
cy.contains('Use maximum').should('be.visible');
|
||||
cy.get(amountField).clear().type('20000000');
|
||||
@@ -425,7 +422,11 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
cy.getByTestId(depositSubmit).click();
|
||||
cy.getByTestId('input-error-text').should(
|
||||
'contain.text',
|
||||
`You can't deposit more than you have in your Ethereum wallet`
|
||||
'Amount is above approved amount'
|
||||
);
|
||||
cy.getByTestId('reapprove-default').should(
|
||||
'contain.text',
|
||||
'Approve again to deposit more than'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -439,11 +440,11 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
selectAsset(vegaName);
|
||||
cy.get(assetSelectField, txTimeout).select(vegaName, { force: true });
|
||||
cy.getByTestId('approve-submit').click();
|
||||
cy.getByTestId('approve-confirmed').should(
|
||||
'contain.text',
|
||||
'You approved deposits of up to VEGA'
|
||||
'You can now make deposits in VEGA, up to a maximum of'
|
||||
);
|
||||
cy.get(amountField).clear().type('10000');
|
||||
cy.getByTestId('deposit-submit').click();
|
||||
@@ -477,9 +478,9 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
|
||||
cy.getByTestId('Withdrawals').click(txTimeout);
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
selectAsset(1);
|
||||
cy.get(assetSelectField, txTimeout).select(vegaName, { force: true });
|
||||
cy.get(amountField).clear().type('10000');
|
||||
cy.getByTestId('DELAY_TIME_value').should('have.text', '5 days');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
|
||||
@@ -14,226 +14,210 @@ const itemHeader = 'item-header';
|
||||
const itemValue = 'item-value';
|
||||
const marketListContent = 'popover-content';
|
||||
|
||||
describe(
|
||||
'Console - market list - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
describe('Console - market list - live env', { tags: '@live' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('shows the market list page', () => {
|
||||
cy.get('main', { timeout: 20000 });
|
||||
|
||||
// Overlay should be shown
|
||||
cy.getByTestId(selectMarketOverlay).should('exist');
|
||||
cy.contains('Select a market to get started').should('be.visible');
|
||||
|
||||
// I expect the market overlay table to contain at least one row
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table tr')
|
||||
.should('have.length.greaterThan', 1);
|
||||
|
||||
// each market shown in overlay table contains content under the last price and change fields
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table tr')
|
||||
.getByTestId('price')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('redirects to a default market', () => {
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(selectMarketOverlay).should('not.exist');
|
||||
|
||||
// the choose market overlay is no longer showing
|
||||
cy.contains('Select a market to get started').should('not.exist');
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
cy.getByTestId('popover-trigger').should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Console - market info - live env', { tags: '@live' }, () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.getByTestId('link').should('be.visible');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
});
|
||||
const titles = ['Market data', 'Market specification', 'Market governance'];
|
||||
const subtitles = [
|
||||
'Current fees',
|
||||
'Market price',
|
||||
'Market volume',
|
||||
'Insurance pool',
|
||||
'Key details',
|
||||
'Instrument',
|
||||
'Settlement asset',
|
||||
'Metadata',
|
||||
'Risk model',
|
||||
'Risk parameters',
|
||||
'Risk factors',
|
||||
'Price monitoring bounds 1',
|
||||
'Liquidity monitoring parameters',
|
||||
'Liquidity',
|
||||
'Liquidity price range',
|
||||
'Oracle',
|
||||
'Proposal',
|
||||
];
|
||||
|
||||
it('market info titles are displayed', () => {
|
||||
cy.getByTestId('split-view-view')
|
||||
.find('.text-lg')
|
||||
.each((element, index) => {
|
||||
cy.wrap(element).should('have.text', titles[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('market info subtitles are displayed', () => {
|
||||
cy.getByTestId('popover-trigger').click();
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.contains('[data-testid="link"]', 'AAVEDAI.MF21').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
cy.getByTestId(marketInfoSubtitle).each((element, index) => {
|
||||
cy.wrap(element).should('have.text', subtitles[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the market list page', () => {
|
||||
cy.get('main', { timeout: 20000 });
|
||||
|
||||
// Overlay should be shown
|
||||
cy.getByTestId(selectMarketOverlay).should('exist');
|
||||
cy.contains('Select a market to get started').should('be.visible');
|
||||
|
||||
// I expect the market overlay table to contain at least one row
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table tr')
|
||||
.should('have.length.greaterThan', 1);
|
||||
|
||||
// each market shown in overlay table contains content under the last price and change fields
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table tr')
|
||||
.getByTestId('price')
|
||||
.should('not.be.empty');
|
||||
it('renders correctly liquidity in trading tab', () => {
|
||||
cy.getByTestId('Liquidity').click();
|
||||
cy.contains('Loading').should('not.exist');
|
||||
cy.contains('Something went wrong').should('not.exist');
|
||||
cy.contains('Application error').should('not.exist');
|
||||
cy.getByTestId('tab-liquidity').within(() => {
|
||||
cy.get('[col-id="party.id"]').eq(1).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('redirects to a default market', () => {
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(selectMarketOverlay).should('not.exist');
|
||||
describe('Console - market summary - live env', { tags: '@live' }, () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
});
|
||||
|
||||
// the choose market overlay is no longer showing
|
||||
cy.contains('Select a market to get started').should('not.exist');
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
cy.getByTestId('popover-trigger').should('not.be.empty');
|
||||
});
|
||||
}
|
||||
);
|
||||
it('must display market name', () => {
|
||||
cy.getByTestId('popover-trigger').should('not.be.empty');
|
||||
});
|
||||
|
||||
describe(
|
||||
'Console - market info - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.getByTestId('link').should('be.visible');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
});
|
||||
const titles = ['Market data', 'Market specification', 'Market governance'];
|
||||
const subtitles = [
|
||||
'Current fees',
|
||||
'Market price',
|
||||
'Market volume',
|
||||
'Insurance pool',
|
||||
'Key details',
|
||||
'Instrument',
|
||||
'Settlement asset',
|
||||
'Metadata',
|
||||
'Risk model',
|
||||
'Risk parameters',
|
||||
'Risk factors',
|
||||
'Price monitoring bounds 1',
|
||||
'Liquidity monitoring parameters',
|
||||
'Liquidity',
|
||||
'Liquidity price range',
|
||||
'Oracle',
|
||||
'Proposal',
|
||||
];
|
||||
|
||||
it('market info titles are displayed', () => {
|
||||
cy.getByTestId('split-view-view')
|
||||
.find('.text-lg')
|
||||
.each((element, index) => {
|
||||
cy.wrap(element).should('have.text', titles[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('market info subtitles are displayed', () => {
|
||||
cy.getByTestId('popover-trigger').click();
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.contains('[data-testid="link"]', 'AAVEDAI.MF21').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
cy.getByTestId(marketInfoSubtitle).each((element, index) => {
|
||||
cy.wrap(element).should('have.text', subtitles[index]);
|
||||
it('must see market expiry', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketExpiry).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Expiry');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly liquidity in trading tab', () => {
|
||||
cy.getByTestId('Liquidity').click();
|
||||
cy.contains('Loading').should('not.exist');
|
||||
cy.contains('Something went wrong').should('not.exist');
|
||||
cy.contains('Application error').should('not.exist');
|
||||
cy.getByTestId('tab-liquidity').within(() => {
|
||||
cy.get('[col-id="party.id"]').eq(1).should('not.be.empty');
|
||||
it('must see market price', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketPrice).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Price');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe(
|
||||
'Console - market summary - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
});
|
||||
|
||||
it('must display market name', () => {
|
||||
cy.getByTestId('popover-trigger').should('not.be.empty');
|
||||
});
|
||||
|
||||
it('must see market expiry', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketExpiry).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Expiry');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
it('must see market change', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketChange).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Change (24h)');
|
||||
cy.getByTestId(percentageValue).should('not.be.empty');
|
||||
cy.getByTestId(priceChangeValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market price', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketPrice).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Price');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
it('must see market volume', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketVolume).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market change', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketChange).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Change (24h)');
|
||||
cy.getByTestId(percentageValue).should('not.be.empty');
|
||||
cy.getByTestId(priceChangeValue).should('not.be.empty');
|
||||
});
|
||||
it('must see market mode', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketMode).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Trading mode');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market volume', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketVolume).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
it('must see market settlement', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketSettlement).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Settlement asset');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market mode', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketMode).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Trading mode');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
describe('Console - markets table - live env', { tags: '@live' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
cy.get('[data-testid^="market-link-"]').should('not.be.empty');
|
||||
cy.getByTestId('price').invoke('text').should('not.be.empty');
|
||||
cy.getByTestId('settlement-asset').should('not.be.empty');
|
||||
cy.getByTestId('price-change-percentage').should('not.be.empty');
|
||||
cy.getByTestId('price-change').should('not.be.empty');
|
||||
cy.getByTestId('sparkline-svg').should('be.visible');
|
||||
});
|
||||
|
||||
it('renders market list drop down', () => {
|
||||
openMarketDropDown();
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="price"]')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="trading-mode-col"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="taker-fee"]')
|
||||
.should('contain.text', '%');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-volume"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-name"]')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('Able to select market from dropdown', () => {
|
||||
cy.getByTestId('popover-trigger')
|
||||
.invoke('text')
|
||||
.then((marketName) => {
|
||||
openMarketDropDown();
|
||||
cy.get('[data-testid^=market-link]').eq(1).click();
|
||||
cy.getByTestId('popover-trigger').should('not.be.equal', marketName);
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market settlement', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketSettlement).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Settlement asset');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe(
|
||||
'Console - markets table - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
cy.get('[data-testid^="market-link-"]').should('not.be.empty');
|
||||
cy.getByTestId('price').invoke('text').should('not.be.empty');
|
||||
cy.getByTestId('settlement-asset').should('not.be.empty');
|
||||
cy.getByTestId('price-change-percentage').should('not.be.empty');
|
||||
cy.getByTestId('price-change').should('not.be.empty');
|
||||
cy.getByTestId('sparkline-svg').should('be.visible');
|
||||
});
|
||||
|
||||
it('renders market list drop down', () => {
|
||||
openMarketDropDown();
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="price"]')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="trading-mode-col"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="taker-fee"]')
|
||||
.should('contain.text', '%');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-volume"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-name"]')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('Able to select market from dropdown', () => {
|
||||
cy.getByTestId('popover-trigger')
|
||||
.invoke('text')
|
||||
.then((marketName) => {
|
||||
openMarketDropDown();
|
||||
cy.get('[data-testid^=market-link]').eq(1).click();
|
||||
cy.getByTestId('popover-trigger').should('not.be.equal', marketName);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function openMarketDropDown() {
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
formatNumberPercentage,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { updateGridData } from '@vegaprotocol/datagrid';
|
||||
import { updateGridData } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
|
||||
@@ -155,7 +155,6 @@ const MarketBottomPanel = memo(
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketOpenOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -167,7 +166,6 @@ const MarketBottomPanel = memo(
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketClosedOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -179,7 +177,6 @@ const MarketBottomPanel = memo(
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketRejectOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -190,7 +187,6 @@ const MarketBottomPanel = memo(
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketAllOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -199,7 +195,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.fills.component
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
storeKey="marketFills"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -218,7 +213,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.positions.component
|
||||
onMarketClick={onMarketClick}
|
||||
noBottomPlaceholder
|
||||
storeKey="marketPositions"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -228,7 +222,6 @@ const MarketBottomPanel = memo(
|
||||
pinnedAsset={pinnedAsset}
|
||||
noBottomPlaceholder
|
||||
hideButtons
|
||||
storeKey="marketCollateral"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -241,10 +234,7 @@ const MarketBottomPanel = memo(
|
||||
<Tabs storageKey="console-trade-grid-bottom">
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.positions.component
|
||||
onMarketClick={onMarketClick}
|
||||
storeKey="marketPositions"
|
||||
/>
|
||||
<TradingViews.positions.component onMarketClick={onMarketClick} />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="open-orders" name={t('Open')}>
|
||||
@@ -255,7 +245,6 @@ const MarketBottomPanel = memo(
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketOpenOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -267,7 +256,6 @@ const MarketBottomPanel = memo(
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketClosedOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -279,7 +267,6 @@ const MarketBottomPanel = memo(
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketRejectedOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -290,7 +277,6 @@ const MarketBottomPanel = memo(
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketAllOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -299,7 +285,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.fills.component
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
storeKey="marketFills"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -308,7 +293,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.collateral.component
|
||||
pinnedAsset={pinnedAsset}
|
||||
hideButtons
|
||||
storeKey="marketCollateral"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
|
||||
@@ -264,7 +264,6 @@ const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
|
||||
{
|
||||
headerName: t('Market ID'),
|
||||
field: 'id',
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
return cols;
|
||||
@@ -277,10 +276,10 @@ const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
|
||||
columnDefs={colDefs}
|
||||
getRowId={({ data }) => data.id}
|
||||
defaultColDef={{
|
||||
flex: 1,
|
||||
resizable: true,
|
||||
}}
|
||||
overlayNoRowsTemplate="No data"
|
||||
storeKey="closedMarkets"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useRef } from 'react';
|
||||
|
||||
@@ -53,7 +53,6 @@ export const Portfolio = () => {
|
||||
<PositionsContainer
|
||||
onMarketClick={onMarketClick}
|
||||
noBottomPlaceholder
|
||||
storeKey="portfolioPositions"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -62,16 +61,12 @@ export const Portfolio = () => {
|
||||
<OrderListContainer
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
storeKey="portfolioOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
<VegaWalletContainer>
|
||||
<FillsContainer
|
||||
onMarketClick={onMarketClick}
|
||||
storeKey="portfolioFills"
|
||||
/>
|
||||
<FillsContainer onMarketClick={onMarketClick} />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
||||
@@ -91,7 +86,7 @@ export const Portfolio = () => {
|
||||
<Tabs storageKey="console-portfolio-bottom">
|
||||
<Tab id="collateral" name={t('Collateral')}>
|
||||
<VegaWalletContainer>
|
||||
<AccountsContainer storeKey="portfolioCollateral" />
|
||||
<AccountsContainer />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="deposits" name={t('Deposits')}>
|
||||
|
||||
@@ -13,12 +13,10 @@ export const AccountsContainer = ({
|
||||
pinnedAsset,
|
||||
hideButtons,
|
||||
noBottomPlaceholder,
|
||||
storeKey,
|
||||
}: {
|
||||
pinnedAsset?: PinnedAsset;
|
||||
hideButtons?: boolean;
|
||||
noBottomPlaceholder?: boolean;
|
||||
storeKey?: string;
|
||||
}) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
@@ -51,7 +49,6 @@ export const AccountsContainer = ({
|
||||
isReadOnly={isReadOnly}
|
||||
pinnedAsset={pinnedAsset}
|
||||
noBottomPlaceholder={noBottomPlaceholder}
|
||||
storeKey={storeKey}
|
||||
/>
|
||||
{!isReadOnly && !hideButtons && (
|
||||
<div className="flex gap-2 justify-end p-2 px-[11px] absolute lg:fixed bottom-0 right-3 dark:bg-black/75 bg-white/75 rounded">
|
||||
|
||||
@@ -93,11 +93,6 @@ html [data-theme='light'] {
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-row {
|
||||
border-width: 1px 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
.ag-theme-balham {
|
||||
--ag-background-color: theme(colors.white);
|
||||
@@ -121,9 +116,3 @@ html [data-theme='light'] {
|
||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-row.no-hover,
|
||||
.ag-theme-balham-dark .ag-row.no-hover:hover,
|
||||
.ag-theme-balham .ag-row.no-hover,
|
||||
.ag-theme-balham .ag-row.no-hover:hover {
|
||||
background: var(--ag-background-color);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useRef, useMemo, memo, useCallback } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
@@ -17,7 +17,6 @@ interface AccountManagerProps {
|
||||
isReadOnly: boolean;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
noBottomPlaceholder?: boolean;
|
||||
storeKey?: string;
|
||||
}
|
||||
|
||||
export const AccountManager = ({
|
||||
@@ -28,7 +27,6 @@ export const AccountManager = ({
|
||||
isReadOnly,
|
||||
pinnedAsset,
|
||||
noBottomPlaceholder,
|
||||
storeKey,
|
||||
}: AccountManagerProps) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||
@@ -61,7 +59,6 @@ export const AccountManager = ({
|
||||
suppressLoadingOverlay
|
||||
suppressNoRowsOverlay
|
||||
pinnedAsset={pinnedAsset}
|
||||
storeKey={storeKey}
|
||||
{...bottomPlaceholderProps}
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
|
||||
@@ -100,7 +100,6 @@ export interface AccountTableProps extends AgGridReactProps {
|
||||
onClickDeposit?: (assetId: string) => void;
|
||||
isReadOnly: boolean;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
storeKey?: string;
|
||||
}
|
||||
|
||||
export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
||||
@@ -156,6 +155,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
||||
(data) => data.asset.id !== pinnedAssetId
|
||||
)}
|
||||
defaultColDef={{
|
||||
flex: 1,
|
||||
resizable: true,
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
sortable: true,
|
||||
@@ -187,6 +187,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
||||
</ButtonLink>
|
||||
);
|
||||
}}
|
||||
maxWidth={300}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Used')}
|
||||
@@ -361,7 +362,6 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
||||
);
|
||||
}
|
||||
}}
|
||||
flex={1}
|
||||
/>
|
||||
}
|
||||
</AgGrid>
|
||||
|
||||
@@ -9,8 +9,9 @@ declare global {
|
||||
}
|
||||
|
||||
export function addGetNetworkParameters() {
|
||||
// @ts-ignore - ignoring Cypress type error which gets resolved when Cypress uses the command
|
||||
Cypress.Commands.add('get_network_parameters', () => {
|
||||
const query = `
|
||||
const mutation = `
|
||||
{
|
||||
networkParametersConnection {
|
||||
edges {
|
||||
@@ -25,17 +26,17 @@ export function addGetNetworkParameters() {
|
||||
method: 'POST',
|
||||
url: `http://localhost:3008/graphql`,
|
||||
body: {
|
||||
query,
|
||||
query: mutation,
|
||||
},
|
||||
headers: { 'content-type': 'application/json' },
|
||||
})
|
||||
.its('body.data.networkParametersConnection.edges')
|
||||
.then(function (response) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const object = response.reduce(function (obj: any, edge: any) {
|
||||
const { value, key } = edge.node;
|
||||
obj[key] = value;
|
||||
return obj;
|
||||
// @ts-ignore - ignoring Cypress type error which gets resolved when Cypress uses the command
|
||||
const object = response.reduce(function (r, e) {
|
||||
const { value, key } = e.node;
|
||||
r[key] = value;
|
||||
return r;
|
||||
}, {});
|
||||
return cy.wrap(object);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from './lib/ag-grid/ag-grid-lazy';
|
||||
export * from './lib/ag-grid/use-column-sizes';
|
||||
|
||||
export * from './lib/cells/cumulative-vol-cell';
|
||||
export * from './lib/cells/flash-cell';
|
||||
@@ -11,7 +10,6 @@ export * from './lib/cells/vol-cell';
|
||||
export * from './lib/cells/centered-grid-cell';
|
||||
export * from './lib/cells/market-name-cell';
|
||||
export * from './lib/cells/order-type-cell';
|
||||
export * from './lib/cells/size';
|
||||
|
||||
export * from './lib/filters/date-range-filter';
|
||||
export * from './lib/filters/set-filter';
|
||||
@@ -20,6 +18,3 @@ export * from './lib/cell-class-rules';
|
||||
export * from './lib/type-helpers';
|
||||
|
||||
export * from './lib/cells/grid-progress-bar';
|
||||
|
||||
export * from './lib/ag-grid-update';
|
||||
export * from './lib/use-bottom-placeholder';
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { useColumnSizes } from './use-column-sizes';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export const AgGridThemed = ({
|
||||
style,
|
||||
gridRef,
|
||||
storeKey,
|
||||
...props
|
||||
}: (AgGridReactProps | AgReactUiProps) & {
|
||||
style?: React.CSSProperties;
|
||||
gridRef?: React.ForwardedRef<AgGridReact>;
|
||||
storeKey?: string;
|
||||
}) => {
|
||||
const commonColumnCallbacks = useColumnSizes({
|
||||
storeKey,
|
||||
props,
|
||||
});
|
||||
const { theme } = useThemeSwitcher();
|
||||
const defaultProps = {
|
||||
rowHeight: 22,
|
||||
headerHeight: 22,
|
||||
enableCellTextSelection: true,
|
||||
};
|
||||
|
||||
const wrapperClasses = classNames('vega-ag-grid', {
|
||||
'ag-theme-balham': theme === 'light',
|
||||
'ag-theme-balham-dark': theme === 'dark',
|
||||
@@ -32,12 +24,7 @@ export const AgGridThemed = ({
|
||||
|
||||
return (
|
||||
<div className={wrapperClasses} style={style}>
|
||||
<AgGridReact
|
||||
{...defaultProps}
|
||||
{...props}
|
||||
{...commonColumnCallbacks}
|
||||
ref={gridRef}
|
||||
/>
|
||||
<AgGridReact {...defaultProps} {...props} ref={gridRef} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import type { AgGridReactProps, AgGridReact } from 'ag-grid-react';
|
||||
type Props = AgGridReactProps & {
|
||||
style?: React.CSSProperties;
|
||||
gridRef?: React.Ref<AgGridReact>;
|
||||
storeKey?: string;
|
||||
};
|
||||
|
||||
export const AgGridLazyInternal = lazy(() =>
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
import type {
|
||||
Column,
|
||||
ColumnResizedEvent,
|
||||
GridSizeChangedEvent,
|
||||
GridReadyEvent,
|
||||
} from 'ag-grid-community';
|
||||
import { renderHook, act, waitFor } from '@testing-library/react';
|
||||
import { useColumnSizes } from './use-column-sizes';
|
||||
|
||||
const mockApis = {
|
||||
api: {
|
||||
sizeColumnsToFit: jest.fn(),
|
||||
},
|
||||
columnApi: {
|
||||
setColumnWidths: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
const mockValueSetter = jest.fn();
|
||||
const mockStore = {
|
||||
sizes: { testid: { col1: 100 } },
|
||||
valueSetter: mockValueSetter,
|
||||
};
|
||||
jest.mock('zustand', () => ({
|
||||
...jest.requireActual('zustand'),
|
||||
create: () =>
|
||||
jest.fn(() =>
|
||||
jest.fn().mockImplementation((creator) => {
|
||||
return creator(mockStore);
|
||||
})
|
||||
),
|
||||
}));
|
||||
describe('UseColumnSizes hook', () => {
|
||||
const storeKey = 'testid';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('should return proper methods', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useColumnSizes({ storeKey, props: {} })
|
||||
);
|
||||
expect(Object.keys(result.current)).toHaveLength(3);
|
||||
expect(result.current).toStrictEqual({
|
||||
onColumnResized: expect.any(Function),
|
||||
onGridReady: expect.any(Function),
|
||||
onGridSizeChanged: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
it('onGridSizeChanged should call setSize', async () => {
|
||||
const { result } = renderHook(() =>
|
||||
useColumnSizes({ storeKey, props: {} })
|
||||
);
|
||||
await act(() => {
|
||||
result.current.onGridSizeChanged?.({
|
||||
clientWidth: 1000,
|
||||
...mockApis,
|
||||
} as GridSizeChangedEvent);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockApis.columnApi.setColumnWidths).toHaveBeenCalledWith([
|
||||
{ key: 'col1', newWidth: 100 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('onColumnResized should fill up store', async () => {
|
||||
const columns: Column[] = [
|
||||
{ getColId: () => 'col1', getActualWidth: () => 100 },
|
||||
{ getColId: () => 'col2', getActualWidth: () => 200 },
|
||||
] as Column[];
|
||||
const sizeObj = { col1: 100, col2: 200, clientWidth: 1000 };
|
||||
const { result } = renderHook(() =>
|
||||
useColumnSizes({ storeKey, props: {} })
|
||||
);
|
||||
await act(() => {
|
||||
result.current.onGridSizeChanged?.({
|
||||
clientWidth: 1000,
|
||||
...mockApis,
|
||||
} as GridSizeChangedEvent);
|
||||
});
|
||||
await act(() => {
|
||||
result.current.onColumnResized?.({
|
||||
columns,
|
||||
finished: true,
|
||||
source: 'uiColumnDragged',
|
||||
...mockApis,
|
||||
} as ColumnResizedEvent);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockValueSetter).toHaveBeenCalledWith(storeKey, sizeObj);
|
||||
});
|
||||
});
|
||||
|
||||
it('onGridReady should call setSizes', async () => {
|
||||
const props = { onGridReady: jest.fn() };
|
||||
const { result } = renderHook(() => useColumnSizes({ storeKey, props }));
|
||||
const obTest = { cool: 1, ...mockApis };
|
||||
await act(() => {
|
||||
result.current.onGridReady?.(obTest as GridReadyEvent);
|
||||
});
|
||||
expect(props.onGridReady).toHaveBeenCalledWith(obTest);
|
||||
expect(mockApis.api.sizeColumnsToFit).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('if no storeKey should be transparent', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useColumnSizes({ storeKey: '', props: {} })
|
||||
);
|
||||
expect(result.current).toStrictEqual({
|
||||
onColumnResized: undefined,
|
||||
onGridReady: undefined,
|
||||
onGridSizeChanged: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,143 +0,0 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import type {
|
||||
GridSizeChangedEvent,
|
||||
GridReadyEvent,
|
||||
ColumnResizedEvent,
|
||||
} from 'ag-grid-community';
|
||||
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
|
||||
const STORAGE_KEY = 'vega_columns_sizes_store';
|
||||
|
||||
export const useColumnSizesStore = create<{
|
||||
sizes: Record<string, Record<string, number>>;
|
||||
valueSetter: (storeKey: string, value: Record<string, number>) => void;
|
||||
}>()(
|
||||
persist(
|
||||
immer((set) => ({
|
||||
sizes: {},
|
||||
valueSetter: (storeKey, value) =>
|
||||
set((state) => {
|
||||
state.sizes[storeKey] = {
|
||||
...(state.sizes[storeKey] || {}),
|
||||
...value,
|
||||
};
|
||||
return state;
|
||||
}),
|
||||
})),
|
||||
{ name: STORAGE_KEY }
|
||||
)
|
||||
);
|
||||
|
||||
interface UseColumnSizesProps {
|
||||
props: AgGridReactProps | AgReactUiProps;
|
||||
storeKey?: string;
|
||||
}
|
||||
|
||||
export const useColumnSizes = ({
|
||||
storeKey = '',
|
||||
props,
|
||||
}: UseColumnSizesProps): {
|
||||
onColumnResized?: (event: ColumnResizedEvent) => void;
|
||||
onGridReady?: (event: GridReadyEvent) => void;
|
||||
onGridSizeChanged?: (event: GridSizeChangedEvent) => void;
|
||||
} => {
|
||||
const sizes = useColumnSizesStore((store) => store.sizes[storeKey] || {});
|
||||
const valueSetter = useColumnSizesStore((store) => store.valueSetter);
|
||||
const widthRef = useRef(sizes['clientWidth'] || 0);
|
||||
const {
|
||||
onColumnResized: parentOnColumnResized,
|
||||
onGridReady: parentOnGridReady,
|
||||
onGridSizeChanged: parentOnGridSizeChanged,
|
||||
} = props;
|
||||
const recalculateSizes = useCallback((sizes: Record<string, number>) => {
|
||||
if (
|
||||
widthRef.current &&
|
||||
sizes['clientWidth'] &&
|
||||
widthRef.current !== sizes['clientWidth']
|
||||
) {
|
||||
const oldWidth = sizes['clientWidth'];
|
||||
const ratio = widthRef.current / oldWidth;
|
||||
return {
|
||||
...Object.entries(sizes).reduce((agg, [key, value]) => {
|
||||
agg[key] = value * ratio;
|
||||
return agg;
|
||||
}, {} as Record<string, number>),
|
||||
width: widthRef.current,
|
||||
} as Record<string, number>;
|
||||
}
|
||||
return sizes;
|
||||
}, []);
|
||||
|
||||
const onColumnResized = useCallback(
|
||||
(event: ColumnResizedEvent) => {
|
||||
parentOnColumnResized?.(event);
|
||||
if (
|
||||
storeKey &&
|
||||
event.source === 'uiColumnDragged' &&
|
||||
event.finished &&
|
||||
widthRef.current
|
||||
) {
|
||||
const { columns } = event;
|
||||
if (columns?.length) {
|
||||
const sizesObj = columns.reduce((aggr, column) => {
|
||||
aggr[column.getColId()] = column.getActualWidth();
|
||||
return aggr;
|
||||
}, {} as Record<string, number>);
|
||||
sizesObj['clientWidth'] = widthRef.current;
|
||||
valueSetter(storeKey, sizesObj);
|
||||
}
|
||||
}
|
||||
},
|
||||
[valueSetter, storeKey, parentOnColumnResized]
|
||||
);
|
||||
|
||||
const setSizes = useCallback(
|
||||
(apiEvent: GridReadyEvent | GridSizeChangedEvent) => {
|
||||
if (!storeKey || !Object.keys(sizes).length || !widthRef.current) {
|
||||
apiEvent.api.sizeColumnsToFit();
|
||||
} else {
|
||||
const recalculatedSizes = recalculateSizes(sizes);
|
||||
const newSizes = Object.entries(recalculatedSizes).map(
|
||||
([key, size]) => ({
|
||||
key,
|
||||
newWidth: size,
|
||||
})
|
||||
);
|
||||
apiEvent.columnApi.setColumnWidths(newSizes);
|
||||
}
|
||||
},
|
||||
[storeKey, recalculateSizes, sizes]
|
||||
);
|
||||
|
||||
const onGridReady = useCallback(
|
||||
(event: GridReadyEvent) => {
|
||||
parentOnGridReady?.(event);
|
||||
setSizes(event);
|
||||
},
|
||||
[setSizes, parentOnGridReady]
|
||||
);
|
||||
|
||||
const onGridSizeChanged = useCallback(
|
||||
(event: GridSizeChangedEvent) => {
|
||||
parentOnGridSizeChanged?.(event);
|
||||
widthRef.current = event.clientWidth;
|
||||
setSizes(event);
|
||||
},
|
||||
[parentOnGridSizeChanged, setSizes]
|
||||
);
|
||||
if (storeKey) {
|
||||
return {
|
||||
onGridReady,
|
||||
onGridSizeChanged,
|
||||
onColumnResized,
|
||||
};
|
||||
}
|
||||
return {
|
||||
onGridReady: parentOnGridReady,
|
||||
onGridSizeChanged: parentOnGridSizeChanged,
|
||||
onColumnResized: parentOnColumnResized,
|
||||
};
|
||||
};
|
||||
@@ -1,4 +1 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { generateMarket, generateMarketData } from '../../test-helpers';
|
||||
import { DealTicket } from './deal-ticket';
|
||||
@@ -40,21 +40,12 @@ describe('DealTicket', () => {
|
||||
});
|
||||
|
||||
it('should display ticket defaults', () => {
|
||||
const { container } = render(generateJsx());
|
||||
render(generateJsx());
|
||||
|
||||
// Assert defaults are used
|
||||
expect(
|
||||
screen.getByTestId(`order-type-${Schema.OrderType.TYPE_MARKET}`)
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId(`order-type-${Schema.OrderType.TYPE_LIMIT}`)
|
||||
).toBeInTheDocument();
|
||||
|
||||
const oderTypeLimitToggle = container.querySelector(
|
||||
`[data-testid="order-type-${Schema.OrderType.TYPE_LIMIT}"] input[type="radio"]`
|
||||
);
|
||||
expect(oderTypeLimitToggle).toBeChecked();
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('order-side-SIDE_BUY')?.querySelector('input')
|
||||
).toBeChecked();
|
||||
@@ -63,15 +54,8 @@ describe('DealTicket', () => {
|
||||
).not.toBeChecked();
|
||||
expect(screen.getByTestId('order-size')).toHaveDisplayValue('0');
|
||||
expect(screen.getByTestId('order-tif')).toHaveValue(
|
||||
Schema.OrderTimeInForce.TIME_IN_FORCE_GTC
|
||||
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC
|
||||
);
|
||||
});
|
||||
|
||||
it('should display last price for market type order', () => {
|
||||
render(generateJsx());
|
||||
act(() => {
|
||||
screen.getByTestId(`order-type-${Schema.OrderType.TYPE_MARKET}`).click();
|
||||
});
|
||||
// Assert last price is shown
|
||||
expect(screen.getByTestId('last-price')).toHaveTextContent(
|
||||
// eslint-disable-next-line
|
||||
@@ -222,11 +206,7 @@ describe('DealTicket', () => {
|
||||
it('handles TIF select box dependent on order type', async () => {
|
||||
render(generateJsx());
|
||||
|
||||
act(() => {
|
||||
screen.getByTestId(`order-type-${Schema.OrderType.TYPE_MARKET}`).click();
|
||||
});
|
||||
|
||||
// Only FOK and IOC should be present for type market order
|
||||
// Only FOK and IOC should be present by default (type market order)
|
||||
expect(
|
||||
Array.from(screen.getByTestId('order-tif').children).map(
|
||||
(o) => o.textContent
|
||||
|
||||
@@ -20,8 +20,8 @@ interface TypeSelectorProps {
|
||||
}
|
||||
|
||||
const toggles = [
|
||||
{ label: t('Limit'), value: Schema.OrderType.TYPE_LIMIT },
|
||||
{ label: t('Market'), value: Schema.OrderType.TYPE_MARKET },
|
||||
{ label: t('Limit'), value: Schema.OrderType.TYPE_LIMIT },
|
||||
];
|
||||
|
||||
export const TypeSelector = ({
|
||||
|
||||
@@ -26,10 +26,9 @@ export const DepositsTable = forwardRef<
|
||||
<AgGrid
|
||||
ref={ref}
|
||||
overlayNoRowsTemplate={t('No deposits')}
|
||||
defaultColDef={{ resizable: true }}
|
||||
defaultColDef={{ flex: 1, resizable: true }}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
suppressCellFocus={true}
|
||||
storeKey="depositTable"
|
||||
{...props}
|
||||
>
|
||||
<AgGridColumn headerName="Asset" field="asset.symbol" />
|
||||
@@ -81,7 +80,6 @@ export const DepositsTable = forwardRef<
|
||||
</EtherscanLink>
|
||||
);
|
||||
}}
|
||||
flex={1}
|
||||
/>
|
||||
</AgGrid>
|
||||
);
|
||||
|
||||
@@ -34,22 +34,25 @@ fragment FillEdge on TradeEdge {
|
||||
cursor
|
||||
}
|
||||
|
||||
query Fills($filter: TradesFilter, $pagination: Pagination) {
|
||||
trades(filter: $filter, pagination: $pagination) {
|
||||
edges {
|
||||
...FillEdge
|
||||
}
|
||||
pageInfo {
|
||||
startCursor
|
||||
endCursor
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
query Fills($partyId: ID!, $marketId: ID, $pagination: Pagination) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
tradesConnection(marketId: $marketId, pagination: $pagination) {
|
||||
edges {
|
||||
...FillEdge
|
||||
}
|
||||
pageInfo {
|
||||
startCursor
|
||||
endCursor
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscription FillsEvent($filter: TradesSubscriptionFilter!) {
|
||||
tradesStream(filter: $filter) {
|
||||
subscription FillsEvent($partyId: ID!) {
|
||||
trades(partyId: $partyId) {
|
||||
id
|
||||
marketId
|
||||
buyOrder
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user