Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
513b415a02 | ||
|
|
a7f95aa953 | ||
|
|
3583f22067 | ||
|
|
2ac5471710 | ||
|
|
98ce1d0a25 |
@@ -4,7 +4,7 @@ The front-end monorepo provides a toolkit for building apps that interact with V
|
||||
|
||||
This repository is managed using [Nx](https://nx.dev).
|
||||
|
||||
# 🔎 Applications in this repo
|
||||
## 🔎 Applications in this repo
|
||||
|
||||
### [Block explorer](./apps/explorer)
|
||||
|
||||
@@ -30,7 +30,7 @@ Hosting for static content being shared across apps, for example fonts.
|
||||
|
||||
The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract.
|
||||
|
||||
# 🧱 Libraries in this repo
|
||||
## 🧱 Libraries in this repo
|
||||
|
||||
### [UI toolkit](./libs/ui-toolkit)
|
||||
|
||||
@@ -53,7 +53,7 @@ A utility library for connecting to the Ethereum network and interacting with Ve
|
||||
|
||||
Generic react helpers that can be used across multiple applications, along with other utilities.
|
||||
|
||||
# 💻 Develop
|
||||
## 💻 Develop
|
||||
|
||||
### Set up
|
||||
|
||||
@@ -103,7 +103,7 @@ In CI linting, formatting and also run. These checks can be seen in the [CI work
|
||||
|
||||
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
|
||||
|
||||
# 🐋 Hosting a console
|
||||
## 🐋 Hosting a console
|
||||
|
||||
To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions).
|
||||
|
||||
@@ -226,6 +226,6 @@ Note: The script is only needed if capsule was built for first time or fresh. To
|
||||
vega wallet service run -n DV --load-tokens --tokens-passphrase-file passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet
|
||||
```
|
||||
|
||||
# 📑 License
|
||||
## 📑 License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
query ExplorerProposal($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
... on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-7
@@ -8,18 +8,16 @@ export type ExplorerProposalQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null };
|
||||
export type ExplorerProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null };
|
||||
|
||||
|
||||
export const ExplorerProposalDocument = gql`
|
||||
query ExplorerProposal($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
... on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,7 @@ import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { render } from '@testing-library/react';
|
||||
import ProposalLink from './proposal-link';
|
||||
import {
|
||||
ExplorerProposalDocument,
|
||||
type ExplorerProposalQuery,
|
||||
type ExplorerProposalQueryVariables,
|
||||
} from './__generated__/Proposal';
|
||||
import { ExplorerProposalDocument } from './__generated__/Proposal';
|
||||
import { GraphQLError } from 'graphql';
|
||||
|
||||
function renderComponent(id: string, mocks: MockedResponse[]) {
|
||||
@@ -27,10 +23,7 @@ describe('Proposal link component', () => {
|
||||
});
|
||||
|
||||
it('Renders the ID on error', async () => {
|
||||
const mock: MockedResponse<
|
||||
ExplorerProposalQuery,
|
||||
ExplorerProposalQueryVariables
|
||||
> = {
|
||||
const mock = {
|
||||
request: {
|
||||
query: ExplorerProposalDocument,
|
||||
variables: {
|
||||
@@ -47,22 +40,17 @@ describe('Proposal link component', () => {
|
||||
});
|
||||
|
||||
it('Renders the proposal title when the query returns a result', async () => {
|
||||
const proposalId = '123';
|
||||
const mock: MockedResponse<
|
||||
ExplorerProposalQuery,
|
||||
ExplorerProposalQueryVariables
|
||||
> = {
|
||||
const mock = {
|
||||
request: {
|
||||
query: ExplorerProposalDocument,
|
||||
variables: {
|
||||
id: proposalId,
|
||||
id: '123',
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
id: proposalId,
|
||||
id: '123',
|
||||
rationale: {
|
||||
title: 'test-title',
|
||||
description: 'test description',
|
||||
@@ -72,16 +60,13 @@ describe('Proposal link component', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const res = render(renderComponent(proposalId, [mock]));
|
||||
expect(res.getByText(proposalId)).toBeInTheDocument();
|
||||
const res = render(renderComponent('123', [mock]));
|
||||
expect(res.getByText('123')).toBeInTheDocument();
|
||||
expect(await res.findByText('test-title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Leaves the proposal id when the market is not found', async () => {
|
||||
const mock: MockedResponse<
|
||||
ExplorerProposalQuery,
|
||||
ExplorerProposalQueryVariables
|
||||
> = {
|
||||
const mock = {
|
||||
request: {
|
||||
query: ExplorerProposalDocument,
|
||||
variables: {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import {
|
||||
useExplorerProposalQuery,
|
||||
type ExplorerProposalQuery,
|
||||
} from './__generated__/Proposal';
|
||||
import { useExplorerProposalQuery } from './__generated__/Proposal';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { ENV } from '../../../config/env';
|
||||
import Hash from '../hash';
|
||||
|
||||
export type ProposalLinkProps = {
|
||||
id: string;
|
||||
text?: string;
|
||||
@@ -20,13 +16,8 @@ const ProposalLink = ({ id, text }: ProposalLinkProps) => {
|
||||
variables: { id },
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as Extract<
|
||||
ExplorerProposalQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
|
||||
const base = ENV.dataSources.governanceUrl;
|
||||
const label = proposal?.rationale.title || id;
|
||||
const label = data?.proposal?.rationale.title || id;
|
||||
|
||||
return (
|
||||
<ExternalLink href={`${base}/proposals/${id}`}>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import {
|
||||
LiquidationStrategyInfoPanel,
|
||||
LiquidityPriceRangeInfoPanel,
|
||||
LiquiditySLAParametersInfoPanel,
|
||||
MarginScalingFactorsPanel,
|
||||
@@ -95,8 +94,6 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
</>
|
||||
)
|
||||
)}
|
||||
<h2 className={headerClassName}>{t('Liquidation strategy')}</h2>
|
||||
<LiquidationStrategyInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Liquidity monitoring')}</h2>
|
||||
<LiquidityMonitoringParametersInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Liquidity price range')}</h2>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
query ExplorerProposalStatus($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
... on Proposal {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -8,17 +8,15 @@ export type ExplorerProposalStatusQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
|
||||
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
|
||||
|
||||
|
||||
export const ExplorerProposalStatusDocument = gql`
|
||||
query ExplorerProposalStatus($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
... on Proposal {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -14,18 +14,16 @@ export function format(date: string | undefined, def: string) {
|
||||
return new Date().toLocaleDateString() || def;
|
||||
}
|
||||
|
||||
type Proposal = Extract<
|
||||
ExplorerProposalStatusQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
|
||||
export function getDate(proposal: Proposal | undefined, terms: Terms): string {
|
||||
export function getDate(
|
||||
data: ExplorerProposalStatusQuery | undefined,
|
||||
terms: Terms
|
||||
): string {
|
||||
const DEFAULT = t('Unknown');
|
||||
if (!proposal?.state) {
|
||||
if (!data?.proposal?.state) {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
switch (proposal.state) {
|
||||
switch (data.proposal.state) {
|
||||
case 'STATE_DECLINED':
|
||||
return `${t('Rejected on')}: ${format(terms.closingTimestamp, DEFAULT)}`;
|
||||
case 'STATE_ENACTED':
|
||||
@@ -64,11 +62,9 @@ export const ProposalDate = ({ terms, id }: ProposalDateProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as Proposal;
|
||||
|
||||
return (
|
||||
<Lozenge className="font-sans text-xs float-right">
|
||||
{getDate(proposal, terms)}
|
||||
{getDate(data, terms)}
|
||||
</Lozenge>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,8 +2,17 @@ import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import type { IconProps } from '@vegaprotocol/ui-toolkit';
|
||||
import { useExplorerProposalStatusQuery } from './__generated__/Proposal';
|
||||
import type { ExplorerProposalStatusQuery } from './__generated__/Proposal';
|
||||
import type * as Apollo from '@apollo/client';
|
||||
import type * as Types from '@vegaprotocol/types';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
type ProposalQueryResult = Apollo.QueryResult<
|
||||
ExplorerProposalStatusQuery,
|
||||
Types.Exact<{
|
||||
id: string;
|
||||
}>
|
||||
>;
|
||||
|
||||
interface ProposalStatusIconProps {
|
||||
id: string;
|
||||
}
|
||||
@@ -20,38 +29,29 @@ type IconAndLabel = {
|
||||
* @param data a data result from useExplorerProposalStatusQuery
|
||||
* @returns Icon name
|
||||
*/
|
||||
export function useIconAndLabelForStatus(id: string): IconAndLabel {
|
||||
const { data, loading, error } = useExplorerProposalStatusQuery({
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as Extract<
|
||||
ExplorerProposalStatusQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
|
||||
export function getIconAndLabelForStatus(
|
||||
res: ProposalQueryResult
|
||||
): IconAndLabel {
|
||||
const DEFAULT: IconAndLabel = {
|
||||
icon: 'error',
|
||||
label: t('Proposal state unknown'),
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
if (res.loading) {
|
||||
return {
|
||||
icon: 'more',
|
||||
label: t('Loading data'),
|
||||
};
|
||||
}
|
||||
|
||||
if (!data?.proposal || error) {
|
||||
if (!res?.data?.proposal || res.error) {
|
||||
return {
|
||||
icon: 'error',
|
||||
label: error?.message || DEFAULT.label,
|
||||
label: res.error?.message || DEFAULT.label,
|
||||
};
|
||||
}
|
||||
|
||||
switch (proposal.state) {
|
||||
switch (res.data.proposal.state) {
|
||||
case 'STATE_DECLINED':
|
||||
return {
|
||||
icon: 'stop',
|
||||
@@ -99,7 +99,13 @@ export function useIconAndLabelForStatus(id: string): IconAndLabel {
|
||||
/**
|
||||
*/
|
||||
export const ProposalStatusIcon = ({ id }: ProposalStatusIconProps) => {
|
||||
const { icon, label } = useIconAndLabelForStatus(id);
|
||||
const { icon, label } = getIconAndLabelForStatus(
|
||||
useExplorerProposalStatusQuery({
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="float-left mr-3">
|
||||
|
||||
@@ -61,4 +61,53 @@ describe('TxsListNavigation', () => {
|
||||
|
||||
expect(nextPageMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('disables "Older" button if hasMoreTxs is false', () => {
|
||||
render(
|
||||
<TxsListNavigation
|
||||
refreshTxs={NOOP}
|
||||
nextPage={NOOP}
|
||||
previousPage={NOOP}
|
||||
hasMoreTxs={false}
|
||||
hasPreviousPage={false}
|
||||
>
|
||||
<span></span>
|
||||
</TxsListNavigation>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Older')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('disables "Newer" button if hasPreviousPage is false', () => {
|
||||
render(
|
||||
<TxsListNavigation
|
||||
refreshTxs={NOOP}
|
||||
nextPage={NOOP}
|
||||
previousPage={NOOP}
|
||||
hasMoreTxs={true}
|
||||
hasPreviousPage={false}
|
||||
>
|
||||
<span></span>
|
||||
</TxsListNavigation>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Newer')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('disables both buttons when more and previous are false', () => {
|
||||
render(
|
||||
<TxsListNavigation
|
||||
refreshTxs={NOOP}
|
||||
nextPage={NOOP}
|
||||
previousPage={NOOP}
|
||||
hasMoreTxs={false}
|
||||
hasPreviousPage={false}
|
||||
>
|
||||
<span></span>
|
||||
</TxsListNavigation>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Newer')).toBeDisabled();
|
||||
expect(screen.getByText('Older')).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,8 +10,7 @@ export interface TxListNavigationProps {
|
||||
loading?: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
hasMoreTxs: boolean;
|
||||
children?: React.ReactNode;
|
||||
isEmpty?: boolean;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
/**
|
||||
* Displays a list of transactions with filters and controls to navigate through the list.
|
||||
@@ -22,8 +21,9 @@ export const TxsListNavigation = ({
|
||||
refreshTxs,
|
||||
nextPage,
|
||||
previousPage,
|
||||
hasMoreTxs,
|
||||
hasPreviousPage,
|
||||
children,
|
||||
isEmpty,
|
||||
loading = false,
|
||||
}: TxListNavigationProps) => {
|
||||
return (
|
||||
@@ -35,6 +35,7 @@ export const TxsListNavigation = ({
|
||||
<Button
|
||||
className="mr-2"
|
||||
size="xs"
|
||||
disabled={!hasPreviousPage || loading}
|
||||
onClick={() => {
|
||||
previousPage();
|
||||
}}
|
||||
@@ -43,7 +44,7 @@ export const TxsListNavigation = ({
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
disabled={isEmpty}
|
||||
disabled={!hasMoreTxs}
|
||||
onClick={() => {
|
||||
nextPage();
|
||||
}}
|
||||
|
||||
@@ -43,7 +43,7 @@ export const getTxsDataUrl = (params: IGetTxsDataUrl) => {
|
||||
url.searchParams.append('first', count);
|
||||
url.searchParams.append('after', params.after);
|
||||
} else {
|
||||
url.searchParams.append('first', count);
|
||||
url.searchParams.append('last', count);
|
||||
}
|
||||
|
||||
// Hacky fix for param as array
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('getTxsDataUrl', () => {
|
||||
count: 10,
|
||||
baseUrl: 'https://example.com/transactions',
|
||||
};
|
||||
const expectedUrl = 'https://example.com/transactions?first=10';
|
||||
const expectedUrl = 'https://example.com/transactions?last=10';
|
||||
|
||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||
});
|
||||
@@ -41,7 +41,7 @@ describe('getTxsDataUrl', () => {
|
||||
baseUrl: 'https://example.com/transactions',
|
||||
};
|
||||
const expectedUrl =
|
||||
'https://example.com/transactions?first=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
|
||||
'https://example.com/transactions?last=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
|
||||
|
||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||
});
|
||||
|
||||
@@ -31,14 +31,14 @@ export interface IUseTxsData {
|
||||
}
|
||||
|
||||
export const useTxsData = ({
|
||||
count = 50,
|
||||
count = 25,
|
||||
before,
|
||||
after,
|
||||
filters,
|
||||
party,
|
||||
}: IUseTxsData) => {
|
||||
const [, setSearchParams] = useSearchParams();
|
||||
let hasMoreTxs = false;
|
||||
let hasMoreTxs = true;
|
||||
let txsData: BlockExplorerTransactionResult[] = [];
|
||||
|
||||
const url = getTxsDataUrl({
|
||||
@@ -60,8 +60,8 @@ export const useTxsData = ({
|
||||
}
|
||||
|
||||
const nextPage = useCallback(() => {
|
||||
const before = data?.transactions.at(-1)?.cursor || '';
|
||||
const params: URLSearchParamsInit = { before };
|
||||
const after = data?.transactions.at(-1)?.cursor || '';
|
||||
const params: URLSearchParamsInit = { after };
|
||||
if (filters) {
|
||||
params.filters = Array.from(filters).join(',');
|
||||
}
|
||||
@@ -69,8 +69,8 @@ export const useTxsData = ({
|
||||
}, [filters, data, setSearchParams]);
|
||||
|
||||
const previousPage = useCallback(() => {
|
||||
const after = data?.transactions[0]?.cursor || '';
|
||||
const params: URLSearchParamsInit = { after };
|
||||
const before = data?.transactions[0]?.cursor || '';
|
||||
const params: URLSearchParamsInit = { before };
|
||||
if (filters && filters.size > 0 && filters.size === 1) {
|
||||
params.filters = Array.from(filters)[0];
|
||||
}
|
||||
|
||||
@@ -51,10 +51,9 @@ export const TxsListFiltered = () => {
|
||||
refreshTxs={refreshTxs}
|
||||
nextPage={nextPage}
|
||||
previousPage={previousPage}
|
||||
hasPreviousPage={hasMoreTxs}
|
||||
hasPreviousPage={true}
|
||||
loading={loading}
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
isEmpty={txsData.length === 0}
|
||||
>
|
||||
<TxsFilter
|
||||
filters={filters}
|
||||
@@ -71,16 +70,7 @@ export const TxsListFiltered = () => {
|
||||
txs={txsData}
|
||||
loadMoreTxs={nextPage}
|
||||
error={error}
|
||||
className="mb-4 w-full min-w-[400px]"
|
||||
/>
|
||||
<TxsListNavigation
|
||||
refreshTxs={refreshTxs}
|
||||
nextPage={nextPage}
|
||||
previousPage={previousPage}
|
||||
hasPreviousPage={hasMoreTxs}
|
||||
loading={loading}
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
isEmpty={txsData.length === 0}
|
||||
className="mb-28 w-full min-w-[400px]"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -215,7 +215,7 @@ context(
|
||||
});
|
||||
|
||||
// 3003-PMAN-001
|
||||
it.skip(
|
||||
it(
|
||||
'Able to submit valid new market proposal',
|
||||
// @ts-ignore clash between jest and cypress
|
||||
{ tags: '@smoke' },
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
orderByUpgradeBlockHeight,
|
||||
} from '../proposals/components/proposals-list/proposals-list';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { type Proposal } from '../proposals/types';
|
||||
import type { ProposalQuery } from '../proposals/proposal/__generated__/Proposal';
|
||||
|
||||
const nodesToShow = 6;
|
||||
|
||||
@@ -39,7 +39,7 @@ const HomeProposals = ({
|
||||
proposals,
|
||||
protocolUpgradeProposals,
|
||||
}: {
|
||||
proposals: Proposal[];
|
||||
proposals: ProposalQuery['proposal'][];
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
+9
-4
@@ -1,11 +1,16 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
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';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalInfoLabelVariant } from '../proposal-info-label';
|
||||
|
||||
export const CurrentProposalState = ({ proposal }: { proposal: Proposal }) => {
|
||||
export const CurrentProposalState = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
let proposalStatus: ReactNode;
|
||||
let variant = 'tertiary' as ProposalInfoLabelVariant;
|
||||
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ProposalRejectionReason, ProposalState } from '@vegaprotocol/types';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { CurrentProposalStatus } from './current-proposal-status';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
const networkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
request: {
|
||||
query: NetworkParamsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
networkParametersConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'governance.proposal.updateNetParam.requiredMajority',
|
||||
value: '0.00000001',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'governance.proposal.updateNetParam.requiredParticipation',
|
||||
value: '0.000000001',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}) => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider mocks={[networkParamsQueryMock]}>
|
||||
<CurrentProposalStatus proposal={proposal} />
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(60 * 60 * 1000);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('Proposal open - renders will fail state if the proposal will fail', async () => {
|
||||
const failedProposal = generateProposal({
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
no: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
renderComponent({ proposal: failedProposal });
|
||||
expect(await screen.findByText('Currently expected to')).toBeInTheDocument();
|
||||
expect(await screen.findByText('fail.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal open - renders will pass state if the proposal will pass', async () => {
|
||||
const proposal = generateProposal();
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(await screen.findByText('Currently expected to')).toBeInTheDocument();
|
||||
expect(await screen.findByText('pass.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal enacted - renders vote passed and time since enactment', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
terms: {
|
||||
enactmentDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(await screen.findByText('Vote passed.')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal passed - renders vote passed and time since vote closed', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_PASSED,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(await screen.findByText('Vote passed.')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal waiting for node vote - will pass - renders if the vote will pass and status', async () => {
|
||||
const failedProposal = generateProposal({
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
no: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
renderComponent({ proposal: failedProposal });
|
||||
expect(
|
||||
await screen.findByText('Waiting for nodes to validate asset.')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('Currently expected to')).toBeInTheDocument();
|
||||
expect(await screen.findByText('fail.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal waiting for node vote - will fail - renders if the vote will pass and status', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Waiting for nodes to validate asset.')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('Currently expected to')).toBeInTheDocument();
|
||||
expect(await screen.findByText('pass.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders vote failed reason and vote closed ago', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
errorDetails: 'foo',
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('foo')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders rejection reason there are no error details', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
rejectionReason: ProposalRejectionReason.PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText('PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders unknown reason if there are no error details or rejection reason', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('unknown reason')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders participation not met if participation is not met', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
no: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('Participation not met')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders majority not met if majority is not met', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
no: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '1',
|
||||
totalTokens: '25242474195500835440000',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('Majority not met')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
export const StatusPass = ({ children }: { children: ReactNode }) => (
|
||||
<span className="text-vega-green">{children}</span>
|
||||
);
|
||||
|
||||
export const StatusFail = ({ children }: { children: ReactNode }) => (
|
||||
<span className="text-danger">{children}</span>
|
||||
);
|
||||
|
||||
const WillPass = ({
|
||||
willPass,
|
||||
children,
|
||||
}: {
|
||||
willPass: boolean;
|
||||
children?: ReactNode;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
if (willPass) {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<StatusPass>{t('pass')}.</StatusPass>
|
||||
<span className="ml-2">{t('finalOutcomeMayDiffer')}</span>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<StatusFail>{t('fail')}.</StatusFail>
|
||||
<span className="ml-2">{t('finalOutcomeMayDiffer')}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const CurrentProposalStatus = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { willPassByTokenVote, majorityMet, participationMet } =
|
||||
useVoteInformation({
|
||||
proposal,
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
const daysClosedAgo = formatDistanceToNow(
|
||||
new Date(proposal?.terms.closingDatetime),
|
||||
{ addSuffix: true }
|
||||
);
|
||||
|
||||
const daysEnactedAgo =
|
||||
proposal?.terms.enactmentDatetime &&
|
||||
formatDistanceToNow(new Date(proposal.terms.enactmentDatetime), {
|
||||
addSuffix: true,
|
||||
});
|
||||
|
||||
if (proposal?.state === ProposalState.STATE_OPEN) {
|
||||
return (
|
||||
<WillPass willPass={willPassByTokenVote}>{t('currentlySetTo')}</WillPass>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
proposal?.state === ProposalState.STATE_FAILED ||
|
||||
proposal?.state === ProposalState.STATE_DECLINED ||
|
||||
proposal?.state === ProposalState.STATE_REJECTED
|
||||
) {
|
||||
if (!participationMet) {
|
||||
return (
|
||||
<>
|
||||
<span>{t('voteFailedReason')}</span>
|
||||
<StatusFail>{t('participationNotMet')}</StatusFail>
|
||||
<span> {daysClosedAgo}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (!majorityMet) {
|
||||
return (
|
||||
<>
|
||||
<span>{t('voteFailedReason')}</span>
|
||||
<StatusFail>{t('majorityNotMet')}</StatusFail>
|
||||
<span> {daysClosedAgo}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>{t('voteFailedReason')}</span>
|
||||
<StatusFail>
|
||||
{proposal?.errorDetails ||
|
||||
proposal?.rejectionReason ||
|
||||
t('unknownReason')}
|
||||
</StatusFail>
|
||||
<span> {daysClosedAgo}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (
|
||||
proposal?.state === ProposalState.STATE_ENACTED ||
|
||||
proposal?.state === ProposalState.STATE_PASSED
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<span>{t('votePassed')}</span>
|
||||
<StatusPass>
|
||||
|
||||
{proposal?.state === ProposalState.STATE_ENACTED
|
||||
? t('Enacted')
|
||||
: t('Passed')}
|
||||
</StatusPass>
|
||||
<span>
|
||||
|
||||
{proposal?.state === ProposalState.STATE_ENACTED
|
||||
? daysEnactedAgo
|
||||
: daysClosedAgo}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (proposal?.state === ProposalState.STATE_WAITING_FOR_NODE_VOTE) {
|
||||
return (
|
||||
<WillPass willPass={willPassByTokenVote}>
|
||||
<span>{t('WaitingForNodeVote')}</span>{' '}
|
||||
<span>{t('currentlySetTo')}</span>
|
||||
</WillPass>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { CurrentProposalStatus } from './current-proposal-status';
|
||||
+3
-2
@@ -6,10 +6,11 @@ import {
|
||||
KeyValueTableRow,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
interface ProposalChangeTableProps {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
|
||||
+3
-17
@@ -23,8 +23,8 @@ import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { VoteState } from '../vote-details/use-user-vote';
|
||||
import { useNewTransferProposalDetails } from '@vegaprotocol/proposals';
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
|
||||
jest.mock('@vegaprotocol/proposals', () => ({
|
||||
...jest.requireActual('@vegaprotocol/proposals'),
|
||||
@@ -36,7 +36,7 @@ jest.mock('@vegaprotocol/proposals', () => ({
|
||||
}));
|
||||
|
||||
const renderComponent = (
|
||||
proposal: Proposal,
|
||||
proposal: ProposalQuery['proposal'],
|
||||
isListItem = true,
|
||||
mocks: MockedResponse[] = [],
|
||||
voteState?: VoteState
|
||||
@@ -64,7 +64,6 @@ describe('Proposal header', () => {
|
||||
it('Renders New market proposal', () => {
|
||||
useFeatureFlags.setState({ flags: { SUCCESSOR_MARKETS: true } });
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New some market',
|
||||
@@ -103,7 +102,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Update market proposal', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New market id',
|
||||
@@ -132,7 +130,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders New asset proposal - ERC20', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New asset: Fake currency',
|
||||
@@ -162,7 +159,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders New asset proposal - BuiltInAsset', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
@@ -188,7 +184,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Update network', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'Network parameter',
|
||||
@@ -218,7 +213,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Freeform proposal - short rationale', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
id: 'short',
|
||||
rationale: {
|
||||
@@ -240,7 +234,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Freeform proposal - long rationale (105 chars) - listing', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
rationale: {
|
||||
@@ -266,7 +259,6 @@ describe('Proposal header', () => {
|
||||
// Remove once proposals have rationale and re-enable above tests
|
||||
it('Renders Freeform proposal - id for title', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
id: 'freeform id',
|
||||
rationale: {
|
||||
@@ -288,7 +280,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders asset change proposal header', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
@@ -306,7 +297,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it("Renders unknown proposal if it's a different proposal type", () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
@@ -323,7 +313,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Enacted', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
terms: {
|
||||
@@ -336,7 +325,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Passed', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_PASSED,
|
||||
terms: {
|
||||
@@ -350,7 +338,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Waiting for node vote', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
terms: {
|
||||
@@ -365,7 +352,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Open', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_OPEN,
|
||||
votes: {
|
||||
|
||||
+5
-5
@@ -8,7 +8,8 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { shorten } from '@vegaprotocol/utils';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import { type ReactNode } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
@@ -25,17 +26,16 @@ import {
|
||||
} from '@vegaprotocol/environment';
|
||||
import Routes from '../../../routes';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { type VoteState } from '../vote-details/use-user-vote';
|
||||
import type { VoteState } from '../vote-details/use-user-vote';
|
||||
import { VoteBreakdown } from '../vote-breakdown';
|
||||
import { GovernanceTransferKindMapping } from '@vegaprotocol/types';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const ProposalHeader = ({
|
||||
proposal,
|
||||
isListItem = true,
|
||||
voteState,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
isListItem?: boolean;
|
||||
voteState?: VoteState | null;
|
||||
}) => {
|
||||
@@ -53,7 +53,7 @@ export const ProposalHeader = ({
|
||||
|
||||
const titleContent = shorten(title ?? '', 100);
|
||||
|
||||
const getAsset = (proposal: Proposal) => {
|
||||
const getAsset = (proposal: ProposalQuery['proposal']) => {
|
||||
const terms = proposal?.terms;
|
||||
if (
|
||||
terms?.change.__typename === 'NewMarket' &&
|
||||
|
||||
+1
@@ -266,6 +266,7 @@ export const ProposalMarketData = ({
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>
|
||||
{t('Liquidity monitoring parameters')}
|
||||
</h2>
|
||||
|
||||
+2
-2
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
@@ -13,10 +14,9 @@ import {
|
||||
} from '@vegaprotocol/utils';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalReferralProgramDetailsProps {
|
||||
proposal: Proposal | null;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
export const formatEndOfProgramTimestamp = (value: string) => {
|
||||
|
||||
+3
-2
@@ -1,4 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import { useCancelTransferProposalDetails } from '@vegaprotocol/proposals';
|
||||
import {
|
||||
KeyValueTable,
|
||||
@@ -6,12 +8,11 @@ import {
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const ProposalCancelTransferDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const details = useCancelTransferProposalDetails(proposal?.id);
|
||||
|
||||
+3
-2
@@ -1,4 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -19,12 +21,11 @@ import {
|
||||
addDecimalsFormatNumberQuantum,
|
||||
formatDateWithLocalTimezone,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const ProposalTransferDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
+2
-2
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
@@ -11,7 +12,6 @@ import {
|
||||
} from '../proposal-referral-program-details';
|
||||
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
// These types are not generated as it's not known how dynamic these are
|
||||
type VestingBenefitTier = {
|
||||
@@ -43,7 +43,7 @@ export const formatVolumeDiscountFactor = (value: string) => {
|
||||
};
|
||||
|
||||
interface ProposalReferralProgramDetailsProps {
|
||||
proposal: Proposal | null;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -5,13 +5,13 @@ import {
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Row } from '@vegaprotocol/markets';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { useState } from 'react';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalUpdateMarketStateProps {
|
||||
proposal: Proposal | null;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
export const ProposalUpdateMarketState = ({
|
||||
|
||||
+2
-2
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
@@ -11,10 +12,9 @@ import {
|
||||
} from '../proposal-referral-program-details';
|
||||
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalReferralProgramDetailsProps {
|
||||
proposal: Proposal | null;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
export const formatVolumeDiscountFactor = (value: string) => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import { type VegaWalletConfig } from '@vegaprotocol/wallet';
|
||||
import type { VegaWalletConfig } from '@vegaprotocol/wallet';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { Proposal } from './proposal';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { mockNetworkParams } from '../../test-helpers/mocks';
|
||||
import { type Proposal as IProposal } from '../../types';
|
||||
|
||||
jest.mock('@vegaprotocol/network-parameters', () => ({
|
||||
...jest.requireActual('@vegaprotocol/network-parameters'),
|
||||
@@ -51,14 +51,14 @@ const vegaWalletConfig: VegaWalletConfig = {
|
||||
chainId: 'VEGA_CHAIN_ID',
|
||||
};
|
||||
|
||||
const renderComponent = (proposal: IProposal) => {
|
||||
const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<VegaWalletProvider config={vegaWalletConfig}>
|
||||
<Proposal
|
||||
restData={{}}
|
||||
proposal={proposal}
|
||||
proposal={proposal as ProposalQuery['proposal']}
|
||||
networkParams={mockNetworkParams}
|
||||
/>
|
||||
</VegaWalletProvider>
|
||||
|
||||
@@ -12,13 +12,14 @@ import { UserVote } from '../vote-details';
|
||||
import { ListAsset } from '../list-asset';
|
||||
import Routes from '../../../routes';
|
||||
import { ProposalMarketData } from '../proposal-market-data';
|
||||
import { type MarketInfo } from '@vegaprotocol/markets';
|
||||
import { type AssetQuery } from '@vegaprotocol/assets';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MarketInfo } from '@vegaprotocol/markets';
|
||||
import type { AssetQuery } from '@vegaprotocol/assets';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ProposalMarketChanges } from '../proposal-market-changes';
|
||||
import { ProposalUpdateMarketState } from '../proposal-update-market-state';
|
||||
import { type NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||
import { useVoteSubmit } from '@vegaprotocol/proposals';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
import {
|
||||
@@ -27,10 +28,9 @@ import {
|
||||
} from '../proposal-transfer';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
|
||||
import { type Proposal as IProposal } from '../../types';
|
||||
|
||||
export interface ProposalProps {
|
||||
proposal: IProposal;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
networkParams: Partial<NetworkParamsResult>;
|
||||
marketData?: MarketInfo | null;
|
||||
parentMarketData?: MarketInfo | null;
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { format } from 'date-fns';
|
||||
@@ -18,10 +18,10 @@ import {
|
||||
lastWeek,
|
||||
nextWeek,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
const renderComponent = (
|
||||
proposal: Proposal,
|
||||
proposal: ProposalQuery['proposal'],
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
mocks: MockedResponse<any>[] = [networkParamsQueryMock]
|
||||
) =>
|
||||
|
||||
+4
-3
@@ -1,20 +1,21 @@
|
||||
import { type ReactNode } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
import type { ReactNode } from 'react';
|
||||
import {
|
||||
ProposalRejectionReasonMapping,
|
||||
ProposalState,
|
||||
} from '@vegaprotocol/types';
|
||||
import Routes from '../../../routes';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
export const ProposalsListItemDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const state = proposal?.state;
|
||||
|
||||
+2
-2
@@ -2,10 +2,10 @@ import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
interface ProposalsListItemProps {
|
||||
proposal?: Proposal | null;
|
||||
proposal?: ProposalQuery['proposal'] | null;
|
||||
}
|
||||
|
||||
export const ProposalsListItem = ({ proposal }: ProposalsListItemProps) => {
|
||||
|
||||
+3
-3
@@ -17,8 +17,8 @@ import {
|
||||
lastMonth,
|
||||
nextMonth,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
|
||||
const openProposalClosesNextMonth = generateProposal({
|
||||
id: 'proposal1',
|
||||
@@ -63,7 +63,7 @@ const closedProtocolUpgradeProposal = generateProtocolUpgradeProposal({
|
||||
});
|
||||
|
||||
const renderComponent = (
|
||||
proposals: Proposal[],
|
||||
proposals: ProposalQuery['proposal'][],
|
||||
protocolUpgradeProposals?: ProtocolUpgradeProposalFieldsFragment[]
|
||||
) => (
|
||||
<Router>
|
||||
|
||||
@@ -10,20 +10,20 @@ import Routes from '../../../routes';
|
||||
import { Button, Toggle } from '@vegaprotocol/ui-toolkit';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
import { type ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Proposal[];
|
||||
proposals: Array<ProposalQuery['proposal']>;
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
lastBlockHeight?: string;
|
||||
}
|
||||
|
||||
interface SortedProposalsProps {
|
||||
open: Proposal[];
|
||||
closed: Proposal[];
|
||||
open: ProposalQuery['proposal'][];
|
||||
closed: ProposalQuery['proposal'][];
|
||||
}
|
||||
|
||||
interface SortedProtocolUpgradeProposalsProps {
|
||||
@@ -31,7 +31,7 @@ interface SortedProtocolUpgradeProposalsProps {
|
||||
closed: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}
|
||||
|
||||
export const orderByDate = (arr: Proposal[]) =>
|
||||
export const orderByDate = (arr: ProposalQuery['proposal'][]) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[
|
||||
@@ -91,10 +91,14 @@ export const ProposalsList = ({
|
||||
);
|
||||
return {
|
||||
open:
|
||||
initialSorting.open.length > 0 ? orderByDate(initialSorting.open) : [],
|
||||
initialSorting.open.length > 0
|
||||
? orderByDate(initialSorting.open as ProposalQuery['proposal'][])
|
||||
: [],
|
||||
closed:
|
||||
initialSorting.closed.length > 0
|
||||
? orderByDate(initialSorting.closed).reverse()
|
||||
? orderByDate(
|
||||
initialSorting.closed as ProposalQuery['proposal'][]
|
||||
).reverse()
|
||||
: [],
|
||||
};
|
||||
}, [proposals]);
|
||||
@@ -121,7 +125,9 @@ export const ProposalsList = ({
|
||||
};
|
||||
}, [protocolUpgradeProposals, lastBlockHeight]);
|
||||
|
||||
const filterPredicate = (p: ProposalFieldsFragment | Proposal) =>
|
||||
const filterPredicate = (
|
||||
p: ProposalFieldsFragment | ProposalQuery['proposal']
|
||||
) =>
|
||||
p?.id?.includes(filterString) ||
|
||||
p?.party?.id?.toString().includes(filterString);
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ import {
|
||||
nextWeek,
|
||||
lastMonth,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
const rejectedProposalClosesNextWeek = generateProposal({
|
||||
id: 'rejected1',
|
||||
@@ -35,7 +35,7 @@ const rejectedProposalClosedLastMonth = generateProposal({
|
||||
},
|
||||
});
|
||||
|
||||
const renderComponent = (proposals: Proposal[]) => (
|
||||
const renderComponent = (proposals: ProposalQuery['proposal'][]) => (
|
||||
<Router>
|
||||
<MockedProvider mocks={[networkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
|
||||
+3
-3
@@ -3,17 +3,17 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { ProposalsListItem } from '../proposals-list-item';
|
||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Proposal[];
|
||||
proposals: ProposalQuery['proposal'][];
|
||||
}
|
||||
|
||||
export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [filterString, setFilterString] = useState('');
|
||||
|
||||
const filterPredicate = (p: Proposal) =>
|
||||
const filterPredicate = (p: ProposalQuery['proposal']) =>
|
||||
p?.id?.includes(filterString) ||
|
||||
p?.party?.id?.toString().includes(filterString);
|
||||
|
||||
|
||||
+4
-4
@@ -9,7 +9,8 @@ import {
|
||||
nextWeek,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { CompactVotes, VoteBreakdown } from './vote-breakdown';
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import {
|
||||
generateNoVotes,
|
||||
generateProposal,
|
||||
@@ -17,8 +18,7 @@ import {
|
||||
} from '../../test-helpers/generate-proposals';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { BigNumber } from '../../../../lib/bignumber';
|
||||
import { type AppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { AppState } from '../../../../contexts/app-state/app-state-context';
|
||||
|
||||
const mockTotalSupply = new BigNumber(100);
|
||||
// Note - giving a fixedTokenValue of 1 means a ratio of 1:1 votes to tokens, making sums easier :)
|
||||
@@ -41,7 +41,7 @@ jest.mock('../../../../contexts/app-state/app-state-context', () => ({
|
||||
}));
|
||||
|
||||
const renderComponent = (
|
||||
proposal: Proposal,
|
||||
proposal: ProposalQuery['proposal'],
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
mocks: MockedResponse<any>[] = [networkParamsQueryMock]
|
||||
) =>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { type ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -6,8 +5,10 @@ import { useVoteInformation } from '../../hooks';
|
||||
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { formatNumber } from '@vegaprotocol/utils';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { CompactNumber } from '@vegaprotocol/react-helpers';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||
<CompactNumber
|
||||
@@ -19,7 +20,7 @@ export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||
);
|
||||
|
||||
interface VoteBreakdownProps {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
interface VoteProgressProps {
|
||||
|
||||
@@ -5,13 +5,14 @@ import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ConnectToVega } from '../../../../components/connect-to-vega';
|
||||
import { VoteButtonsContainer } from './vote-buttons';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { type VoteValue } from '@vegaprotocol/types';
|
||||
import { type DialogProps, type VegaTxState } from '@vegaprotocol/proposals';
|
||||
import { type VoteState } from './use-user-vote';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { VoteValue } from '@vegaprotocol/types';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { VoteState } from './use-user-vote';
|
||||
|
||||
interface UserVoteProps {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
minVoterBalance: string | null | undefined;
|
||||
spamProtectionMinTokens: string | null | undefined;
|
||||
transaction: VegaTxState | null;
|
||||
|
||||
@@ -3,12 +3,13 @@ import {
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import { type Proposal } from '../types';
|
||||
import type { ProposalFieldsFragment } from '../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
|
||||
export const useProposalNetworkParams = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.governance_proposal_updateMarket_requiredMajority,
|
||||
|
||||
@@ -2,10 +2,15 @@ import { useMemo } from 'react';
|
||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import { useProposalNetworkParams } from './use-proposal-network-params';
|
||||
import type { ProposalFieldsFragment } from '../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
import { addDecimal } from '@vegaprotocol/utils';
|
||||
import { type Proposal } from '../types';
|
||||
|
||||
export const useVoteInformation = ({ proposal }: { proposal: Proposal }) => {
|
||||
export const useVoteInformation = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const {
|
||||
appState: { totalSupply, decimals },
|
||||
} = useAppState();
|
||||
|
||||
@@ -86,65 +86,141 @@ query Proposal(
|
||||
$includeUpdateReferralProgram: Boolean!
|
||||
) {
|
||||
proposal(id: $proposalId) {
|
||||
... on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
party {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
party {
|
||||
id
|
||||
}
|
||||
errorDetails
|
||||
...NewMarketProductField @include(if: $includeNewMarketProductField)
|
||||
...UpdateMarketState @include(if: $includeUpdateMarketState)
|
||||
...UpdateReferralProgram @include(if: $includeUpdateReferralProgram)
|
||||
...UpdateVolumeDiscountProgram
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
... on NewMarket {
|
||||
decimalPlaces
|
||||
metadata
|
||||
riskParameters {
|
||||
... on LogNormalRiskModel {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
mu
|
||||
r
|
||||
sigma
|
||||
}
|
||||
}
|
||||
... on SimpleRiskModel {
|
||||
params {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
errorDetails
|
||||
...NewMarketProductField @include(if: $includeNewMarketProductField)
|
||||
...UpdateMarketState @include(if: $includeUpdateMarketState)
|
||||
...UpdateReferralProgram @include(if: $includeUpdateReferralProgram)
|
||||
...UpdateVolumeDiscountProgram
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
... on NewMarket {
|
||||
decimalPlaces
|
||||
metadata
|
||||
riskParameters {
|
||||
... on LogNormalRiskModel {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
mu
|
||||
r
|
||||
sigma
|
||||
}
|
||||
}
|
||||
... on SimpleRiskModel {
|
||||
params {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
}
|
||||
instrument {
|
||||
name
|
||||
code
|
||||
product {
|
||||
... on FutureProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
}
|
||||
quoteName
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on PerpetualProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
}
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
positionDecimalPlaces
|
||||
linearSlippageFactor
|
||||
}
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
updateMarketConfiguration {
|
||||
instrument {
|
||||
name
|
||||
code
|
||||
product {
|
||||
... on FutureProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
}
|
||||
... on UpdateFutureProduct {
|
||||
quoteName
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
@@ -185,19 +261,101 @@ query Proposal(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on PerpetualProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
# dataSourceSpecForTradingTermination {
|
||||
# sourceType {
|
||||
# ... on DataSourceDefinitionInternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfigurationTime {
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ... on DataSourceDefinitionExternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfiguration {
|
||||
# signers {
|
||||
# signer {
|
||||
# ... on PubKey {
|
||||
# key
|
||||
# }
|
||||
# ... on ETHAddress {
|
||||
# address
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# filters {
|
||||
# key {
|
||||
# name
|
||||
# type
|
||||
# }
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
}
|
||||
... on UpdatePerpetualProduct {
|
||||
quoteName
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
settlementScheduleProperty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
metadata
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
@@ -211,231 +369,71 @@ query Proposal(
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
positionDecimalPlaces
|
||||
linearSlippageFactor
|
||||
}
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
updateMarketConfiguration {
|
||||
instrument {
|
||||
code
|
||||
product {
|
||||
... on UpdateFutureProduct {
|
||||
quoteName
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# dataSourceSpecForTradingTermination {
|
||||
# sourceType {
|
||||
# ... on DataSourceDefinitionInternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfigurationTime {
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ... on DataSourceDefinitionExternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfiguration {
|
||||
# signers {
|
||||
# signer {
|
||||
# ... on PubKey {
|
||||
# key
|
||||
# }
|
||||
# ... on ETHAddress {
|
||||
# address
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# filters {
|
||||
# key {
|
||||
# name
|
||||
# type
|
||||
# }
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
}
|
||||
... on UpdatePerpetualProduct {
|
||||
quoteName
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
settlementScheduleProperty
|
||||
}
|
||||
}
|
||||
riskParameters {
|
||||
... on UpdateMarketSimpleRiskModel {
|
||||
simple {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
metadata
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
riskParameters {
|
||||
... on UpdateMarketSimpleRiskModel {
|
||||
simple {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
... on UpdateMarketLogNormalRiskModel {
|
||||
logNormal {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
r
|
||||
sigma
|
||||
mu
|
||||
}
|
||||
... on UpdateMarketLogNormalRiskModel {
|
||||
logNormal {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
r
|
||||
sigma
|
||||
mu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on NewAsset {
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
source {
|
||||
... on BuiltinAsset {
|
||||
maxFaucetAmountMint
|
||||
}
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
... on NewAsset {
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
source {
|
||||
... on BuiltinAsset {
|
||||
maxFaucetAmountMint
|
||||
}
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
... on UpdateNetworkParameter {
|
||||
networkParameter {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
... on UpdateNetworkParameter {
|
||||
networkParameter {
|
||||
key
|
||||
value
|
||||
}
|
||||
... on UpdateAsset {
|
||||
quantum
|
||||
assetId
|
||||
source {
|
||||
... on UpdateERC20 {
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
... on UpdateAsset {
|
||||
quantum
|
||||
assetId
|
||||
source {
|
||||
... on UpdateERC20 {
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
votes {
|
||||
yes {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
no {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
}
|
||||
votes {
|
||||
yes {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
no {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+230
-232
File diff suppressed because one or more lines are too long
@@ -17,7 +17,6 @@ import {
|
||||
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
||||
import { type Proposal as IProposal } from '../types';
|
||||
|
||||
export const ProposalContainer = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
@@ -68,8 +67,6 @@ export const ProposalContainer = () => {
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as IProposal;
|
||||
|
||||
const successor = useSuccessorMarketProposalDetails(params.proposalId);
|
||||
|
||||
const isSuccessor = !!successor?.parentMarketId || !!successor.code;
|
||||
@@ -82,12 +79,12 @@ export const ProposalContainer = () => {
|
||||
},
|
||||
} = useFetch(
|
||||
`${ENV.rest}governance?proposalId=${
|
||||
proposal?.terms.change.__typename === 'UpdateMarket' &&
|
||||
proposal.terms.change.marketId
|
||||
data?.proposal?.terms.change.__typename === 'UpdateMarket' &&
|
||||
data?.proposal.terms.change.marketId
|
||||
}`,
|
||||
undefined,
|
||||
true,
|
||||
proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -100,7 +97,7 @@ export const ProposalContainer = () => {
|
||||
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
|
||||
undefined,
|
||||
true,
|
||||
proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -111,8 +108,8 @@ export const ProposalContainer = () => {
|
||||
dataProvider: marketInfoProvider,
|
||||
skipUpdates: true,
|
||||
variables: {
|
||||
marketId: proposal?.id || '',
|
||||
skip: !proposal?.id,
|
||||
marketId: data?.proposal?.id || '',
|
||||
skip: !data?.proposal?.id,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -151,22 +148,23 @@ export const ProposalContainer = () => {
|
||||
fetchPolicy: 'network-only',
|
||||
variables: {
|
||||
assetId:
|
||||
(proposal?.terms.change.__typename === 'NewAsset' && proposal?.id) ||
|
||||
(proposal?.terms.change.__typename === 'UpdateAsset' &&
|
||||
proposal.terms.change.assetId) ||
|
||||
(data?.proposal?.terms.change.__typename === 'NewAsset' &&
|
||||
data?.proposal?.id) ||
|
||||
(data?.proposal?.terms.change.__typename === 'UpdateAsset' &&
|
||||
data.proposal.terms.change.assetId) ||
|
||||
'',
|
||||
},
|
||||
skip: !['NewAsset', 'UpdateAsset'].includes(
|
||||
proposal?.terms?.change?.__typename || ''
|
||||
data?.proposal?.terms?.change?.__typename || ''
|
||||
),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
previouslyEnactedMarketProposalsRestData &&
|
||||
proposal?.terms.change.__typename === 'UpdateMarket'
|
||||
data?.proposal?.terms.change.__typename === 'UpdateMarket'
|
||||
) {
|
||||
const change = proposal?.terms?.change as { marketId: string };
|
||||
const change = data?.proposal?.terms?.change as { marketId: string };
|
||||
|
||||
const filteredProposals =
|
||||
// @ts-ignore rest data is not typed
|
||||
@@ -190,8 +188,8 @@ export const ProposalContainer = () => {
|
||||
}, [
|
||||
previouslyEnactedMarketProposalsRestData,
|
||||
params.proposalId,
|
||||
proposal?.terms.change.__typename,
|
||||
proposal?.terms.change,
|
||||
data?.proposal?.terms.change.__typename,
|
||||
data?.proposal?.terms.change,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -244,7 +242,7 @@ export const ProposalContainer = () => {
|
||||
>
|
||||
{data?.proposal ? (
|
||||
<Proposal
|
||||
proposal={proposal}
|
||||
proposal={data.proposal}
|
||||
networkParams={networkParams}
|
||||
restData={restData}
|
||||
marketData={marketData}
|
||||
|
||||
@@ -8,7 +8,6 @@ import mergeWith from 'lodash/mergeWith';
|
||||
import { type PartialDeep } from 'type-fest';
|
||||
import { type ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type Proposal } from '../types';
|
||||
|
||||
export function generateProtocolUpgradeProposal(
|
||||
override: PartialDeep<ProtocolUpgradeProposalFieldsFragment> = {}
|
||||
@@ -44,8 +43,8 @@ export function generateProtocolUpgradeProposal(
|
||||
}
|
||||
|
||||
export function generateProposal(
|
||||
override: PartialDeep<Proposal> = {}
|
||||
): Proposal {
|
||||
override: PartialDeep<ProposalQuery['proposal']> = {}
|
||||
): ProposalQuery['proposal'] {
|
||||
const defaultProposal: ProposalQuery['proposal'] = {
|
||||
__typename: 'Proposal',
|
||||
id: faker.datatype.uuid(),
|
||||
@@ -93,16 +92,15 @@ export function generateProposal(
|
||||
},
|
||||
};
|
||||
|
||||
return mergeWith<Proposal, PartialDeep<Proposal>>(
|
||||
defaultProposal,
|
||||
override,
|
||||
(objValue, srcValue) => {
|
||||
if (!isArray(objValue)) {
|
||||
return;
|
||||
}
|
||||
return srcValue;
|
||||
return mergeWith<
|
||||
ProposalQuery['proposal'],
|
||||
PartialDeep<ProposalQuery['proposal']>
|
||||
>(defaultProposal, override, (objValue, srcValue) => {
|
||||
if (!isArray(objValue)) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
return srcValue;
|
||||
});
|
||||
}
|
||||
|
||||
type Vote = Pick<Schema.Vote, '__typename' | 'value' | 'party' | 'datetime'>;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { ProposalQuery } from './proposal/__generated__/Proposal';
|
||||
|
||||
/**
|
||||
* The default Proposal type needs extracting from the ProposalNode union type
|
||||
* as lots of fields on the original type don't exist on BatchProposal. Eventually
|
||||
* we will support BatchProposal but for now we don't
|
||||
*/
|
||||
export type Proposal = Extract<
|
||||
ProposalQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
@@ -21,7 +21,6 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -21,7 +21,6 @@ NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supp
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
NX_ISOLATED_MARGIN=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -20,7 +20,6 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
@@ -21,7 +21,6 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=false
|
||||
NX_ICEBERG_ORDERS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
@@ -30,5 +29,5 @@ NX_REFERRALS=true
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
# NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
# NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -21,7 +21,6 @@ NX_APP_VERSION=v0.20.19-core-0.71.6
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=false
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -21,7 +21,6 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
@@ -22,7 +22,6 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -22,7 +22,6 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
} from '@vegaprotocol/candles-chart';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useChartSettings, STUDY_SIZE } from './use-chart-settings';
|
||||
import { SUPPORTED_INTERVALS, type SupportedInterval } from './constants';
|
||||
|
||||
/**
|
||||
* Renders either the pennant chart or the tradingview chart
|
||||
@@ -37,7 +36,7 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
|
||||
const pennantChart = (
|
||||
<CandlesChartContainer
|
||||
marketId={marketId}
|
||||
interval={toPennantInterval(interval as SupportedInterval)}
|
||||
interval={toPennantInterval(interval)}
|
||||
chartType={chartType}
|
||||
overlays={overlays}
|
||||
studies={studies}
|
||||
@@ -64,7 +63,7 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
|
||||
libraryPath={CHARTING_LIBRARY_PATH}
|
||||
libraryHash={CHARTING_LIBRARY_HASH}
|
||||
marketId={marketId}
|
||||
interval={toTradingViewResolution(interval as SupportedInterval)}
|
||||
interval={toTradingViewResolution(interval)}
|
||||
onIntervalChange={(newInterval) => {
|
||||
setInterval(fromTradingViewResolution(newInterval));
|
||||
}}
|
||||
@@ -84,11 +83,7 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const toTradingViewResolution = (interval: SupportedInterval) => {
|
||||
if (!SUPPORTED_INTERVALS.includes(interval)) {
|
||||
throw new Error(`interval ${interval} is not supported`);
|
||||
}
|
||||
|
||||
const toTradingViewResolution = (interval: Interval) => {
|
||||
const resolution = TRADINGVIEW_INTERVAL_MAP[interval];
|
||||
|
||||
if (!resolution) {
|
||||
@@ -112,11 +107,7 @@ const fromTradingViewResolution = (resolution: string) => {
|
||||
return interval as Interval;
|
||||
};
|
||||
|
||||
const toPennantInterval = (interval: SupportedInterval) => {
|
||||
if (!SUPPORTED_INTERVALS.includes(interval)) {
|
||||
throw new Error(`interval ${interval} is not supported`);
|
||||
}
|
||||
|
||||
const toPennantInterval = (interval: Interval) => {
|
||||
const pennantInterval = PENNANT_INTERVAL_MAP[interval];
|
||||
|
||||
if (!pennantInterval) {
|
||||
|
||||
@@ -18,13 +18,21 @@ import {
|
||||
TradingDropdownTrigger,
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { type Interval } from '@vegaprotocol/types';
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ALLOWED_TRADINGVIEW_HOSTNAMES } from '@vegaprotocol/trading-view';
|
||||
import { IconNames, type IconName } from '@blueprintjs/icons';
|
||||
import { useChartSettings } from './use-chart-settings';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { SUPPORTED_INTERVALS } from './constants';
|
||||
|
||||
const INTERVALS = [
|
||||
Interval.INTERVAL_I1M,
|
||||
Interval.INTERVAL_I5M,
|
||||
Interval.INTERVAL_I15M,
|
||||
Interval.INTERVAL_I1H,
|
||||
Interval.INTERVAL_I6H,
|
||||
Interval.INTERVAL_I1D,
|
||||
];
|
||||
|
||||
const chartTypeIcon = new Map<ChartType, IconName>([
|
||||
[ChartType.AREA, IconNames.TIMELINE_AREA_CHART],
|
||||
@@ -86,7 +94,7 @@ export const ChartMenu = () => {
|
||||
setInterval(value as Interval);
|
||||
}}
|
||||
>
|
||||
{SUPPORTED_INTERVALS.map((timeInterval) => (
|
||||
{INTERVALS.map((timeInterval) => (
|
||||
<TradingDropdownRadioItem
|
||||
key={timeInterval}
|
||||
inset
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
|
||||
export type SupportedInterval = typeof SUPPORTED_INTERVALS[number];
|
||||
|
||||
export const SUPPORTED_INTERVALS = [
|
||||
Interval.INTERVAL_I1M,
|
||||
Interval.INTERVAL_I5M,
|
||||
Interval.INTERVAL_I15M,
|
||||
Interval.INTERVAL_I1H,
|
||||
Interval.INTERVAL_I6H,
|
||||
Interval.INTERVAL_I1D,
|
||||
] as const;
|
||||
@@ -309,49 +309,29 @@ export const ActiveRewardCard = ({
|
||||
].includes(m.state)
|
||||
);
|
||||
|
||||
const assetInSettledMarket =
|
||||
allMarkets &&
|
||||
Object.values(allMarkets).some((m: MarketFieldsFragment | null) => {
|
||||
if (m && getAsset(m).id === dispatchStrategy.dispatchMetricAssetId) {
|
||||
return (
|
||||
m?.state &&
|
||||
[
|
||||
MarketState.STATE_TRADING_TERMINATED,
|
||||
MarketState.STATE_SETTLED,
|
||||
MarketState.STATE_CANCELLED,
|
||||
MarketState.STATE_CLOSED,
|
||||
].includes(m.state)
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (marketSettled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Gray out the cards that are related to suspended markets
|
||||
const suspended = transferNode.markets?.some(
|
||||
const assetInActiveMarket =
|
||||
allMarkets &&
|
||||
Object.values(allMarkets).some((m: MarketFieldsFragment | null) => {
|
||||
if (m && getAsset(m).id === dispatchStrategy.dispatchMetricAssetId) {
|
||||
return m?.state && MarketState.STATE_ACTIVE === m.state;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const marketSuspended = transferNode.markets?.some(
|
||||
(m) =>
|
||||
m?.state === MarketState.STATE_SUSPENDED ||
|
||||
m?.state === MarketState.STATE_SUSPENDED_VIA_GOVERNANCE
|
||||
);
|
||||
|
||||
const assetInSuspendedMarket =
|
||||
allMarkets &&
|
||||
Object.values(allMarkets).some((m: MarketFieldsFragment | null) => {
|
||||
if (m && getAsset(m).id === dispatchStrategy.dispatchMetricAssetId) {
|
||||
return (
|
||||
m?.state === MarketState.STATE_SUSPENDED ||
|
||||
m?.state === MarketState.STATE_SUSPENDED_VIA_GOVERNANCE
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Gray out the cards that are related to suspended markets
|
||||
// Or settlement assets in markets that are not active and eligible for rewards
|
||||
const { gradientClassName, mainClassName } =
|
||||
suspended || assetInSuspendedMarket || assetInSettledMarket
|
||||
marketSuspended || !assetInActiveMarket
|
||||
? {
|
||||
gradientClassName: 'from-vega-cdark-500 to-vega-clight-400',
|
||||
mainClassName: 'from-vega-cdark-400 dark:from-vega-cdark-600 to-20%',
|
||||
@@ -437,12 +417,12 @@ export const ActiveRewardCard = ({
|
||||
<span>
|
||||
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]} •{' '}
|
||||
<Tooltip
|
||||
underline={suspended}
|
||||
underline={marketSuspended}
|
||||
description={
|
||||
(suspended || assetInSuspendedMarket) &&
|
||||
(marketSuspended || !assetInActiveMarket) &&
|
||||
(specificMarkets
|
||||
? t('Eligible market(s) currently suspended')
|
||||
: assetInSuspendedMarket
|
||||
: !assetInActiveMarket
|
||||
? t('Currently no markets eligible for reward')
|
||||
: '')
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ export const Settings = () => {
|
||||
intent={Intent.Primary}
|
||||
onClick={() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
window.location.reload();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
||||
VEGA_VERSION=v0.74.0-preview.6
|
||||
VEGA_VERSION=v0.74.0-preview.2
|
||||
LOCAL_SERVER=false
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
|
||||
VEGA_VERSION=v0.74.0-preview.6
|
||||
VEGA_VERSION=v0.74.0-preview.2
|
||||
LOCAL_SERVER=false
|
||||
|
||||
Generated
+1
-1
@@ -1161,7 +1161,7 @@ profile = ["pytest-profiling", "snakeviz"]
|
||||
type = "git"
|
||||
url = "https://github.com/vegaprotocol/vega-market-sim.git/"
|
||||
reference = "HEAD"
|
||||
resolved_reference = "026976549c21e59f6f9c48f06ab15a210c5a5bf3"
|
||||
resolved_reference = "2aed8c94b25d8fa2e376d3b63ca1f9193d28cdfd"
|
||||
|
||||
[[package]]
|
||||
name = "websocket-client"
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
from vega_sim.null_service import VegaServiceNull
|
||||
from actions.vega import submit_order
|
||||
from actions.utils import next_epoch, wait_for_toast_confirmation
|
||||
|
||||
tooltip_content = "tooltip-content"
|
||||
leverage_input = "#leverage-input"
|
||||
tab_positions = "tab-positions"
|
||||
margin_row = '[col-id="margin"]'
|
||||
|
||||
|
||||
def create_position(vega: VegaServiceNull, market_id):
|
||||
submit_order(vega, "Key 1", market_id, "SIDE_SELL", 100, 110)
|
||||
submit_order(vega, "Key 1", market_id, "SIDE_BUY", 100, 110)
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_switch_cross_isolated_margin(
|
||||
continuous_market, vega: VegaServiceNull, page: Page):
|
||||
create_position(vega, continuous_market)
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
expect(page.locator(margin_row).nth(1)).to_have_text("874.21992Cross1.0x")
|
||||
# tbd - tooltip is not visible without this wait
|
||||
page.wait_for_timeout(1000)
|
||||
page.get_by_test_id(tab_positions).get_by_text("Cross").hover()
|
||||
expect(page.get_by_test_id(tooltip_content).nth(0)).to_have_text(
|
||||
"Liquidation: 582.81328Margin: 874.21992General account: 998,084.95183"
|
||||
)
|
||||
page.get_by_role("button", name="Isolated 10x").click()
|
||||
page.locator(leverage_input).clear()
|
||||
page.locator(leverage_input).type("1")
|
||||
page.get_by_role("button", name="Confirm").click()
|
||||
wait_for_toast_confirmation(page)
|
||||
next_epoch(vega=vega)
|
||||
expect(page.get_by_test_id("toast-content")).to_have_text(
|
||||
"ConfirmedYour transaction has been confirmedView in block explorerUpdate margin modeBTC:DAI_2023Isolated margin mode, leverage: 1.0x")
|
||||
expect(page.locator(margin_row).nth(1)
|
||||
).to_have_text("11,109.99996Isolated1.0x")
|
||||
# tbd - tooltip is not visible without this wait
|
||||
page.wait_for_timeout(1000)
|
||||
page.get_by_test_id(tab_positions).get_by_text("Isolated").hover()
|
||||
expect(page.get_by_test_id(tooltip_content).nth(0)).to_have_text(
|
||||
"Liquidation: 583.62409Margin: 11,109.99996Order: 11,000.00"
|
||||
)
|
||||
page.get_by_role("button", name="Cross").click()
|
||||
page.get_by_role("button", name="Confirm").click()
|
||||
wait_for_toast_confirmation(page)
|
||||
next_epoch(vega=vega)
|
||||
expect(page.locator(margin_row).nth(1)).to_have_text(
|
||||
"22,109.99996Cross1.0x")
|
||||
@@ -11,35 +11,27 @@ place_order = "place-order"
|
||||
deal_ticket_warning_margin = "deal-ticket-warning-margin"
|
||||
deal_ticket_deposit_dialog_button = "deal-ticket-deposit-dialog-button"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vega(request):
|
||||
with init_vega(request) as vega:
|
||||
yield vega
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def continuous_market(vega):
|
||||
return setup_continuous_market(vega)
|
||||
|
||||
|
||||
@pytest.mark.skip("marked id issue #5681")
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_should_display_info_and_button_for_deposit(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id(order_size).fill("200000")
|
||||
page.get_by_test_id(order_price).fill("20")
|
||||
# 7002-SORD-060
|
||||
expect(page.get_by_test_id(deal_ticket_warning_margin)).to_have_text(
|
||||
"You may not have enough margin available to open this position.")
|
||||
expect(page.get_by_test_id(deal_ticket_warning_margin)).to_have_text("You may not have enough margin available to open this position.")
|
||||
page.get_by_test_id(deal_ticket_warning_margin).hover()
|
||||
expect(page.get_by_test_id("tooltip-content").nth(0)).to_have_text(
|
||||
"1,661,896.6317 tDAI is currently required.You have only 1,000,000.00.Deposit tDAI")
|
||||
expect(page.get_by_test_id("tooltip-content").nth(0)).to_have_text("1,661,896.6317 tDAI is currently required.You have only 1,000,000.00.Deposit tDAI")
|
||||
page.get_by_test_id(deal_ticket_deposit_dialog_button).nth(0).click()
|
||||
expect(page.get_by_test_id("sidebar-content")
|
||||
).to_contain_text("DepositFrom")
|
||||
|
||||
|
||||
expect(page.get_by_test_id("sidebar-content")).to_contain_text("DepositFrom")
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_should_show_an_error_if_your_balance_is_zero(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
@@ -50,6 +42,5 @@ def test_should_show_an_error_if_your_balance_is_zero(continuous_market, vega: V
|
||||
# 7002-SORD-060
|
||||
expect(page.get_by_test_id(place_order)).to_be_enabled()
|
||||
# 7002-SORD-003
|
||||
expect(page.get_by_test_id("deal-ticket-error-message-zero-balance")
|
||||
).to_have_text("You need tDAI in your wallet to trade in this market.Make a deposit")
|
||||
expect(page.get_by_test_id(deal_ticket_deposit_dialog_button)).to_be_visible()
|
||||
expect(page.get_by_test_id("deal-ticket-error-message-zero-balance")).to_have_text("You need tDAI in your wallet to trade in this market.Make a deposit")
|
||||
expect(page.get_by_test_id(deal_ticket_deposit_dialog_button)).to_be_visible()
|
||||
|
||||
@@ -10,18 +10,15 @@ import logging
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def vega():
|
||||
with init_vega() as vega:
|
||||
yield vega
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def simple_market(vega: VegaServiceNull):
|
||||
return setup_simple_market(vega)
|
||||
|
||||
|
||||
class TestGetStarted:
|
||||
def test_get_started_interactive(self, vega: VegaServiceNull, page: Page):
|
||||
page.goto("/")
|
||||
@@ -33,8 +30,7 @@ class TestGetStarted:
|
||||
expect(page.locator(".list-none")).to_contain_text(
|
||||
"1.Connect2.Deposit funds3.Open a position"
|
||||
)
|
||||
# This is the default wallet name within VegaServiceNull and CANNOT be changed
|
||||
DEFAULT_WALLET_NAME = "MarketSim"
|
||||
DEFAULT_WALLET_NAME = "MarketSim" # This is the default wallet name within VegaServiceNull and CANNOT be changed
|
||||
|
||||
# Calling get_keypairs will internally call _load_tokens for the given wallet
|
||||
keypairs = vega.wallet.get_keypairs(DEFAULT_WALLET_NAME)
|
||||
@@ -141,8 +137,7 @@ class TestGetStarted:
|
||||
def test_get_started_seen_already(self, simple_market, page: Page):
|
||||
page.goto(f"/#/markets/{simple_market}")
|
||||
get_started_locator = page.get_by_test_id("connect-vega-wallet")
|
||||
page.wait_for_selector(
|
||||
'[data-testid="connect-vega-wallet"]', state="attached")
|
||||
page.wait_for_selector('[data-testid="connect-vega-wallet"]', state="attached")
|
||||
expect(get_started_locator).to_be_enabled
|
||||
expect(get_started_locator).to_be_visible
|
||||
# 0007-FUGS-015
|
||||
|
||||
@@ -36,19 +36,16 @@ def validate_info_section(page: Page, fields: [[str, str]]):
|
||||
for rowNumber, field in enumerate(fields):
|
||||
name, value = field
|
||||
expect(
|
||||
page.get_by_test_id(
|
||||
"key-value-table-row").nth(rowNumber).locator("dt")
|
||||
page.get_by_test_id("key-value-table-row").nth(rowNumber).locator("dt")
|
||||
).to_contain_text(name)
|
||||
expect(
|
||||
page.get_by_test_id(
|
||||
"key-value-table-row").nth(rowNumber).locator("dd")
|
||||
page.get_by_test_id("key-value-table-row").nth(rowNumber).locator("dd")
|
||||
).to_contain_text(value)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_current_fees(page: Page):
|
||||
# 6002-MDET-101
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Current fees").click()
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Current fees").click()
|
||||
fields = [
|
||||
["Maker Fee", "10%"],
|
||||
["Infrastructure Fee", "0.05%"],
|
||||
@@ -57,11 +54,10 @@ def test_market_info_current_fees(page: Page):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_market_price(page: Page):
|
||||
# 6002-MDET-102
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Market price").click()
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Market price").click()
|
||||
fields = [
|
||||
["Mark Price", "107.50"],
|
||||
["Best Bid Price", "101.50"],
|
||||
@@ -70,11 +66,10 @@ def test_market_info_market_price(page: Page):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_market_volume(page: Page):
|
||||
# 6002-MDET-103
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Market volume").click()
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Market volume").click()
|
||||
fields = [
|
||||
["24 Hour Volume", "-"],
|
||||
["Open Interest", "1"],
|
||||
@@ -85,32 +80,17 @@ def test_market_info_market_volume(page: Page):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
def test_market_info_liquidation_strategy(page: Page):
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Liquidation strategy").click()
|
||||
fields = [
|
||||
["Disposal Fraction", "1"],
|
||||
["Disposal Time Step", "1"],
|
||||
["Full Disposal Size", "1,000,000,000"],
|
||||
["Max Fraction Consumed", "0.5"],
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
def test_market_info_liquidation(page: Page):
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_insurance_pool(page: Page):
|
||||
# 6002-MDET-104
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Liquidations").click()
|
||||
fields = [["Insurance Pool Balance", "0.00 tDAI"]]
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Insurance pool").click()
|
||||
fields = [["Balance", "0.00 tDAI"]]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("core issue #5681")
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_key_details(page: Page, vega: VegaServiceNull):
|
||||
# 6002-MDET-201
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Key details").click()
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Key details").click()
|
||||
market_id = vega.find_market_id("BTC:DAI_2023")
|
||||
short_market_id = market_id[:6] + "…" + market_id[-4:]
|
||||
fields = [
|
||||
@@ -126,7 +106,7 @@ def test_market_info_key_details(page: Page, vega: VegaServiceNull):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_instrument(page: Page):
|
||||
# 6002-MDET-202
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Instrument").click()
|
||||
@@ -141,7 +121,7 @@ def test_market_info_instrument(page: Page):
|
||||
|
||||
# @pytest.mark.skip("oracle test to be fixed")
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_oracle(page: Page):
|
||||
# 6002-MDET-203
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Oracle").click()
|
||||
@@ -155,11 +135,10 @@ def test_market_info_oracle(page: Page):
|
||||
# "href", re.compile(rf'(\/oracles\/{vega.find_market_id("BTC:DAI_2023")})')
|
||||
# )
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_settlement_asset(page: Page, vega: VegaServiceNull):
|
||||
# 6002-MDET-206
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Settlement asset").click()
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Settlement asset").click()
|
||||
tdai_id = vega.find_asset_id("tDAI")
|
||||
tdai_id_short = tdai_id[:6] + "…" + tdai_id[-4:]
|
||||
fields = [
|
||||
@@ -176,7 +155,7 @@ def test_market_info_settlement_asset(page: Page, vega: VegaServiceNull):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_metadata(page: Page):
|
||||
# 6002-MDET-207
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Metadata").click()
|
||||
@@ -185,7 +164,7 @@ def test_market_info_metadata(page: Page):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_risk_model(page: Page):
|
||||
# 6002-MDET-208
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Risk model").click()
|
||||
@@ -196,7 +175,7 @@ def test_market_info_risk_model(page: Page):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_margin_scaling_factors(page: Page):
|
||||
# 6002-MDET-209
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
@@ -204,17 +183,17 @@ def test_market_info_margin_scaling_factors(page: Page):
|
||||
).click()
|
||||
fields = [
|
||||
["Linear Slippage Factor", "0.001"],
|
||||
["Quadratic Slippage Factor", "0"],
|
||||
["Search Level", "1.1"],
|
||||
["Initial Margin", "1.5"],
|
||||
["Collateral Release", "1.7"],
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_risk_factors(page: Page):
|
||||
# 6002-MDET-210
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Risk factors").click()
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Risk factors").click()
|
||||
fields = [
|
||||
["Long", "0.05153"],
|
||||
["Short", "0.05422"],
|
||||
@@ -225,7 +204,7 @@ def test_market_info_risk_factors(page: Page):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_price_monitoring_bounds(page: Page):
|
||||
# 6002-MDET-211
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
@@ -234,27 +213,27 @@ def test_market_info_price_monitoring_bounds(page: Page):
|
||||
expect(page.locator("p.col-span-1").nth(0)).to_contain_text(
|
||||
"99.9999% probability price bounds"
|
||||
)
|
||||
expect(page.locator("p.col-span-1").nth(1)
|
||||
).to_contain_text("Within 86,400 seconds")
|
||||
expect(page.locator("p.col-span-1").nth(1)).to_contain_text("Within 86,400 seconds")
|
||||
fields = [
|
||||
["Highest Price", "138.66685 BTC"],
|
||||
["Lowest Price", "83.11038 BTC"],
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_liquidity_monitoring_parameters(page: Page):
|
||||
# 6002-MDET-212
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Liquidity monitoring parameters"
|
||||
).click()
|
||||
fields = [
|
||||
["Triggering Ratio", "0.7"],
|
||||
["Time Window", "3,600"],
|
||||
["Scaling Factor", "1"],
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
# Liquidity resolves to 3 results
|
||||
def test_market_info_liquidit(page: Page):
|
||||
# 6002-MDET-213
|
||||
@@ -267,7 +246,7 @@ def test_market_info_liquidit(page: Page):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_liquidity_price_range(page: Page):
|
||||
# 6002-MDET-214
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
@@ -280,22 +259,19 @@ def test_market_info_liquidity_price_range(page: Page):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_proposal(page: Page, vega: VegaServiceNull):
|
||||
# 6002-MDET-301
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Proposal").click()
|
||||
first_link = (
|
||||
page.get_by_test_id(
|
||||
"accordion-content").get_by_test_id("external-link").first
|
||||
page.get_by_test_id("accordion-content").get_by_test_id("external-link").first
|
||||
)
|
||||
second_link = (
|
||||
page.get_by_test_id(
|
||||
"accordion-content").get_by_test_id("external-link").nth(1)
|
||||
page.get_by_test_id("accordion-content").get_by_test_id("external-link").nth(1)
|
||||
)
|
||||
expect(first_link).to_have_text("View governance proposal")
|
||||
expect(first_link).to_have_attribute(
|
||||
"href", re.compile(
|
||||
rf'(\/proposals\/{vega.find_market_id("BTC:DAI_2023")})')
|
||||
"href", re.compile(rf'(\/proposals\/{vega.find_market_id("BTC:DAI_2023")})')
|
||||
)
|
||||
expect(second_link).to_have_text("Propose a change to market")
|
||||
|
||||
@@ -304,14 +280,13 @@ def test_market_info_proposal(page: Page, vega: VegaServiceNull):
|
||||
"href", re.compile(r"(\/proposals\/propose\/update-market)")
|
||||
)
|
||||
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_market_info_succession_line(page: Page, vega: VegaServiceNull):
|
||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||
"Succession line").click()
|
||||
page.get_by_test_id(market_title_test_id).get_by_text("Succession line").click()
|
||||
market_id = vega.find_market_id("BTC:DAI_2023")
|
||||
succession_line = page.get_by_test_id("succession-line-item")
|
||||
expect(succession_line.get_by_test_id(
|
||||
"external-link")).to_have_text("BTC:DAI_2023")
|
||||
expect(succession_line.get_by_test_id("external-link")).to_have_text("BTC:DAI_2023")
|
||||
expect(succession_line.get_by_test_id("external-link")).to_have_attribute(
|
||||
"href", re.compile(rf"(\/proposals\/{market_id})")
|
||||
)
|
||||
|
||||
@@ -29,15 +29,13 @@ def verify_data_grid(page: Page, data_test_id, expected_pattern):
|
||||
logger.info(f"Matched: {expected} == {actual}")
|
||||
else:
|
||||
logger.info(f"Not Matched: {expected} != {actual}")
|
||||
raise AssertionError(
|
||||
f"Pattern does not match: {expected} != {actual}")
|
||||
raise AssertionError(f"Pattern does not match: {expected} != {actual}")
|
||||
else: # it's not a regex, so we escape it
|
||||
if re.search(re.escape(expected), actual):
|
||||
logger.info(f"Matched: {expected} == {actual}")
|
||||
else:
|
||||
logger.info(f"Not Matched: {expected} != {actual}")
|
||||
raise AssertionError(
|
||||
f"Pattern does not match: {expected} != {actual}")
|
||||
raise AssertionError(f"Pattern does not match: {expected} != {actual}")
|
||||
|
||||
|
||||
def submit_order(vega: VegaServiceNull, wallet_name, market_id, side, volume, price):
|
||||
@@ -93,7 +91,7 @@ def test_limit_order_trade_open_position(continuous_market, page: Page):
|
||||
"average_entry_price": "107.50",
|
||||
"mark_price": "107.50",
|
||||
"margin": "8.50269",
|
||||
"leverage": "Cross1.0x",
|
||||
"leverage": "1.0x",
|
||||
"liquidation": "0.00",
|
||||
"realised_pnl": "0.00",
|
||||
"unrealised_pnl": "0.00",
|
||||
@@ -106,8 +104,7 @@ def test_limit_order_trade_open_position(continuous_market, page: Page):
|
||||
# 7004-POSI-002
|
||||
|
||||
size_and_notional = table.locator("[col-id='openVolume']")
|
||||
expect(size_and_notional.get_by_test_id(
|
||||
primary_id)).to_have_text(position["size"])
|
||||
expect(size_and_notional.get_by_test_id(primary_id)).to_have_text(position["size"])
|
||||
expect(size_and_notional.get_by_test_id(secondary_id)).to_have_text(
|
||||
position["notional"]
|
||||
)
|
||||
@@ -128,11 +125,10 @@ def test_limit_order_trade_open_position(continuous_market, page: Page):
|
||||
position["leverage"]
|
||||
)
|
||||
|
||||
# need to ne check why it is not visible
|
||||
# liquidation = table.locator("[col-id='liquidationPrice']")
|
||||
# expect(liquidation.get_by_test_id("liquidation-price")).to_have_text(
|
||||
# position["liquidation"]
|
||||
# )
|
||||
liquidation = table.locator("[col-id='liquidationPrice']")
|
||||
expect(liquidation.get_by_test_id("liquidation-price")).to_have_text(
|
||||
position["liquidation"]
|
||||
)
|
||||
|
||||
realisedPNL = table.locator("[col-id='realisedPNL']")
|
||||
expect(realisedPNL).to_have_text(position["realised_pnl"])
|
||||
|
||||
@@ -28,9 +28,8 @@ def test_usage_breakdown(continuous_market, page: Page):
|
||||
usage_breakdown = page.get_by_test_id("usage-breakdown")
|
||||
|
||||
# Verify headers
|
||||
headers = ["Market", "Account type", "Balance"]
|
||||
ag_headers = usage_breakdown.locator(
|
||||
".ag-header-cell-text").element_handles()
|
||||
headers = ["Market", "Account type", "Balance", "Margin health"]
|
||||
ag_headers = usage_breakdown.locator(".ag-header-cell-text").element_handles()
|
||||
for i, header_element in enumerate(ag_headers):
|
||||
header_text = header_element.text_content()
|
||||
assert header_text == headers[i]
|
||||
@@ -39,10 +38,30 @@ def test_usage_breakdown(continuous_market, page: Page):
|
||||
expect(usage_breakdown.locator('[class="mb-2 text-sm"]')).to_have_text(
|
||||
"You have 1,000,000.00 tDAI in total."
|
||||
)
|
||||
expect(usage_breakdown.locator(
|
||||
COL_ID_USED).first).to_have_text("8.50269 (0%)")
|
||||
expect(usage_breakdown.locator(COL_ID_USED).first).to_have_text("8.50269 (0%)")
|
||||
expect(usage_breakdown.locator(COL_ID_USED).nth(1)).to_have_text(
|
||||
"999,991.49731 (99%)"
|
||||
)
|
||||
|
||||
# Maintenance Level
|
||||
expect(
|
||||
usage_breakdown.locator(
|
||||
".ag-center-cols-container [col-id='market.id'] .ag-cell-value"
|
||||
).first
|
||||
).to_have_text("2.85556 above maintenance level")
|
||||
|
||||
# Margin health tooltip
|
||||
usage_breakdown.get_by_test_id("margin-health-chart-track").hover()
|
||||
tooltip_data = [
|
||||
("maintenance level", "5.64713"),
|
||||
("search level", "6.21184"),
|
||||
("initial level", "8.47069"),
|
||||
("balance", "8.50269"),
|
||||
("release level", "9.60012"),
|
||||
]
|
||||
|
||||
for index, (label, value) in enumerate(tooltip_data):
|
||||
expect(page.get_by_test_id(TOOLTIP_LABEL).nth(index)).to_have_text(label)
|
||||
expect(page.get_by_test_id(TOOLTIP_VALUE).nth(index)).to_have_text(value)
|
||||
|
||||
page.get_by_test_id("dialog-close").click()
|
||||
|
||||
@@ -7,7 +7,7 @@ from wallet_config import MM_WALLET, PARTY_A, PARTY_B
|
||||
from vega_sim.service import MarketStateUpdateType
|
||||
import vega_sim.api.governance as governance
|
||||
|
||||
@pytest.mark.skip("Skipping to unblock CI, working on fix")
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted", "auth")
|
||||
def test_filtered_cards(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
tDAI_asset_id = vega.find_asset_id(symbol="tDAI")
|
||||
@@ -46,9 +46,7 @@ def test_filtered_cards(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
side="SIDE_BUY",
|
||||
volume=1,
|
||||
)
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.goto("/#/rewards")
|
||||
next_epoch(vega=vega)
|
||||
|
||||
vega.update_market_state(
|
||||
market_id=continuous_market,
|
||||
@@ -57,9 +55,8 @@ def test_filtered_cards(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
forward_time_to_enactment=True,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
|
||||
page.reload()
|
||||
expect(page.locator(".from-vega-cdark-400")).to_be_visible(timeout=15000)
|
||||
page.goto("/#/rewards")
|
||||
expect(page.locator(".from-vega-cdark-400")).to_be_visible()
|
||||
governance.submit_oracle_data(
|
||||
wallet=vega.wallet,
|
||||
payload={"trading.terminated": "true"},
|
||||
|
||||
@@ -8,7 +8,7 @@ from actions.utils import next_epoch
|
||||
|
||||
market_banner = "market-banner"
|
||||
|
||||
|
||||
@pytest.mark.skip("tbd")
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_succession_line(vega: VegaServiceNull, page: Page):
|
||||
parent_market_id = setup_continuous_market(vega)
|
||||
@@ -20,14 +20,12 @@ def test_succession_line(vega: VegaServiceNull, page: Page):
|
||||
expect(page.get_by_test_id(market_banner)).not_to_be_attached()
|
||||
|
||||
successor_name = "successor market name"
|
||||
successor_id = propose_successor(
|
||||
vega, parent_market_id, tdai_id, successor_name)
|
||||
successor_id = propose_successor(vega, parent_market_id, tdai_id, successor_name)
|
||||
|
||||
# Check that the banner notifying about the successor proposal is shown
|
||||
banner = page.get_by_test_id(market_banner)
|
||||
expect(banner).to_be_attached()
|
||||
expect(banner.get_by_text(
|
||||
"A successor to this market has been proposed")).to_be_visible()
|
||||
expect(banner.get_by_text("A successor to this market has been proposed")).to_be_visible()
|
||||
|
||||
next_epoch(vega)
|
||||
|
||||
@@ -47,6 +45,7 @@ def test_succession_line(vega: VegaServiceNull, page: Page):
|
||||
# the succession line
|
||||
page.reload()
|
||||
|
||||
#tbd issue - 5546
|
||||
page.get_by_test_id("Info").click()
|
||||
|
||||
page.get_by_role("button", name="Succession line").click()
|
||||
@@ -79,7 +78,6 @@ def test_succession_line(vega: VegaServiceNull, page: Page):
|
||||
page.wait_for_selector('[data-testid="market-banner"]', state="attached")
|
||||
expect(banner.get_by_text("This market has been succeeded")).to_be_visible()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_banners(vega: VegaServiceNull, page: Page):
|
||||
|
||||
@@ -93,9 +91,9 @@ def test_banners(vega: VegaServiceNull, page: Page):
|
||||
expect(page.get_by_test_id(market_banner)).not_to_be_attached()
|
||||
|
||||
vega.submit_termination_and_settlement_data(
|
||||
settlement_key=GOVERNANCE_WALLET.name,
|
||||
settlement_price=100,
|
||||
market_id=parent_market_id,
|
||||
settlement_key=GOVERNANCE_WALLET.name,
|
||||
settlement_price=100,
|
||||
market_id=parent_market_id,
|
||||
)
|
||||
|
||||
successor_name = "successor market name"
|
||||
@@ -110,7 +108,7 @@ def test_banners(vega: VegaServiceNull, page: Page):
|
||||
# Check that the banner notifying about the successor proposal and market has been settled are shown still after reload
|
||||
page.reload()
|
||||
expect(banner.get_by_text(banner_successor_text)).to_be_visible()
|
||||
expect(banner.get_by_text("1/2")).to_be_visible()
|
||||
expect(banner.get_by_text("1/2")).to_be_visible()
|
||||
# Check that the banner notifying about the successor proposal is not visible after close those banners
|
||||
banner.get_by_test_id("icon-cross").click()
|
||||
expect(banner.get_by_text("This market has been settled")).to_be_visible()
|
||||
@@ -121,8 +119,7 @@ def test_banners(vega: VegaServiceNull, page: Page):
|
||||
expect(page.get_by_test_id(market_banner)).not_to_be_attached()
|
||||
page.reload()
|
||||
expect(banner).to_be_attached()
|
||||
expect(banner.get_by_text(banner_successor_text)).to_be_visible()
|
||||
|
||||
expect(banner.get_by_text(banner_successor_text)).to_be_visible()
|
||||
|
||||
def propose_successor(
|
||||
vega: VegaServiceNull, parent_market_id, tdai_id, market_name
|
||||
@@ -140,7 +137,6 @@ def propose_successor(
|
||||
)
|
||||
return market_id
|
||||
|
||||
|
||||
def provide_successor_liquidity(
|
||||
vega: VegaServiceNull, market_id
|
||||
):
|
||||
|
||||
@@ -3,9 +3,6 @@ fragment MarginFields on MarginLevels {
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
asset {
|
||||
id
|
||||
}
|
||||
@@ -36,9 +33,6 @@ subscription MarginsSubscription($partyId: ID!) {
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
|
||||
+3
-9
@@ -3,21 +3,21 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } };
|
||||
export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } };
|
||||
|
||||
export type MarginsQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarginsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, marginsConnection?: { __typename?: 'MarginConnection', edges?: Array<{ __typename?: 'MarginEdge', node: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
|
||||
export type MarginsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, marginsConnection?: { __typename?: 'MarginConnection', edges?: Array<{ __typename?: 'MarginEdge', node: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
|
||||
|
||||
export type MarginsSubscriptionSubscriptionVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarginsSubscriptionSubscription = { __typename?: 'Subscription', margins: { __typename?: 'MarginLevelsUpdate', marketId: string, asset: string, partyId: string, maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: string, timestamp: any } };
|
||||
export type MarginsSubscriptionSubscription = { __typename?: 'Subscription', margins: { __typename?: 'MarginLevelsUpdate', marketId: string, asset: string, partyId: string, maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, timestamp: any } };
|
||||
|
||||
export const MarginFieldsFragmentDoc = gql`
|
||||
fragment MarginFields on MarginLevels {
|
||||
@@ -25,9 +25,6 @@ export const MarginFieldsFragmentDoc = gql`
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
asset {
|
||||
id
|
||||
}
|
||||
@@ -88,9 +85,6 @@ export const MarginsSubscriptionDocument = gql`
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,6 @@ export interface AccountFields extends Account {
|
||||
// The total balance of these accounts will be used for the 'used' column in the
|
||||
// collateral table
|
||||
const USE_ACCOUNT_TYPES = [
|
||||
AccountType.ACCOUNT_TYPE_ORDER_MARGIN,
|
||||
AccountType.ACCOUNT_TYPE_MARGIN,
|
||||
AccountType.ACCOUNT_TYPE_BOND,
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
|
||||
@@ -4,6 +4,14 @@ import * as Types from '@vegaprotocol/types';
|
||||
import type { AccountFields } from './accounts-data-provider';
|
||||
import { getAccountData } from './accounts-data-provider';
|
||||
|
||||
const marginHealthChartTestId = 'margin-health-chart';
|
||||
|
||||
jest.mock('./margin-health-chart', () => ({
|
||||
MarginHealthChart: () => {
|
||||
return <div data-testid={marginHealthChartTestId}></div>;
|
||||
},
|
||||
}));
|
||||
|
||||
const singleRow = {
|
||||
__typename: 'AccountBalance',
|
||||
type: Types.AccountType.ACCOUNT_TYPE_MARGIN,
|
||||
@@ -41,10 +49,10 @@ describe('BreakdownTable', () => {
|
||||
render(<BreakdownTable data={singleRowData} />);
|
||||
});
|
||||
const headers = await screen.findAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(3);
|
||||
expect(headers).toHaveLength(4);
|
||||
expect(
|
||||
headers.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim())
|
||||
).toEqual(['Market', 'Account type', 'Balance']);
|
||||
).toEqual(['Market', 'Account type', 'Balance', 'Margin health']);
|
||||
});
|
||||
|
||||
it('should apply correct formatting', async () => {
|
||||
@@ -62,6 +70,24 @@ describe('BreakdownTable', () => {
|
||||
cells.slice(0, -1).forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
expect(screen.getByTestId(marginHealthChartTestId)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays margin health chart only for margin account', async () => {
|
||||
await act(async () => {
|
||||
render(
|
||||
<BreakdownTable
|
||||
data={[
|
||||
{
|
||||
...singleRow,
|
||||
type: Types.AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
market: null,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
expect(screen.queryByTestId(marginHealthChartTestId)).toBeNull();
|
||||
});
|
||||
|
||||
it('should get correct account data', () => {
|
||||
|
||||
@@ -16,13 +16,14 @@ import { ProgressBarCell } from '@vegaprotocol/datagrid';
|
||||
import { AgGrid, PriceCell } from '@vegaprotocol/datagrid';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import { accountValuesComparator } from './accounts-table';
|
||||
import { MarginHealthChart } from './margin-health-chart';
|
||||
import { MarketNameCell } from '@vegaprotocol/datagrid';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
const defaultColDef = {
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
minWidth: 100,
|
||||
flex: 1,
|
||||
};
|
||||
|
||||
interface BreakdownTableProps extends AgGridReactProps {
|
||||
@@ -38,7 +39,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
maxWidth: 150,
|
||||
width: 90,
|
||||
pinned: true,
|
||||
sort: 'desc',
|
||||
cellRenderer: ({
|
||||
@@ -110,6 +111,23 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
},
|
||||
comparator: accountValuesComparator,
|
||||
},
|
||||
{
|
||||
headerName: t('Margin health'),
|
||||
field: 'market.id',
|
||||
maxWidth: 500,
|
||||
sortable: false,
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaICellRendererParams<AccountFields, 'market.id'>) =>
|
||||
data?.market?.id &&
|
||||
data.type === AccountType['ACCOUNT_TYPE_MARGIN'] &&
|
||||
data?.asset.id ? (
|
||||
<MarginHealthChart
|
||||
marketId={data.market.id}
|
||||
assetId={data.asset.id}
|
||||
/>
|
||||
) : null,
|
||||
},
|
||||
];
|
||||
return defs;
|
||||
}, [t]);
|
||||
|
||||
@@ -20,20 +20,14 @@ const update = (
|
||||
return produce(data || [], (draft) => {
|
||||
const { marketId } = delta;
|
||||
const index = draft.findIndex((node) => node.market.id === marketId);
|
||||
const deltaData = {
|
||||
maintenanceLevel: delta.maintenanceLevel,
|
||||
searchLevel: delta.searchLevel,
|
||||
initialLevel: delta.initialLevel,
|
||||
collateralReleaseLevel: delta.collateralReleaseLevel,
|
||||
marginFactor: delta.marginFactor,
|
||||
marginMode: delta.marginMode,
|
||||
orderMarginLevel: delta.orderMarginLevel,
|
||||
};
|
||||
if (index !== -1) {
|
||||
const currNode = draft[index];
|
||||
draft[index] = {
|
||||
...currNode,
|
||||
...deltaData,
|
||||
maintenanceLevel: delta.maintenanceLevel,
|
||||
searchLevel: delta.searchLevel,
|
||||
initialLevel: delta.initialLevel,
|
||||
collateralReleaseLevel: delta.collateralReleaseLevel,
|
||||
};
|
||||
} else {
|
||||
draft.unshift({
|
||||
@@ -42,7 +36,10 @@ const update = (
|
||||
__typename: 'Market',
|
||||
id: delta.marketId,
|
||||
},
|
||||
...deltaData,
|
||||
maintenanceLevel: delta.maintenanceLevel,
|
||||
searchLevel: delta.searchLevel,
|
||||
initialLevel: delta.initialLevel,
|
||||
collateralReleaseLevel: delta.collateralReleaseLevel,
|
||||
asset: {
|
||||
__typename: 'Asset',
|
||||
id: delta.asset,
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import type { MarginFieldsFragment } from './__generated__/Margins';
|
||||
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
import { MarginMode } from '@vegaprotocol/types';
|
||||
|
||||
const asset: AssetFieldsFragment = {
|
||||
id: 'assetId',
|
||||
@@ -19,9 +18,6 @@ const margins: MarginFieldsFragment = {
|
||||
initialLevel: '800',
|
||||
searchLevel: '600',
|
||||
maintenanceLevel: '400',
|
||||
marginFactor: '',
|
||||
marginMode: MarginMode.MARGIN_MODE_CROSS_MARGIN,
|
||||
orderMarginLevel: '',
|
||||
market: {
|
||||
id: 'marketId',
|
||||
},
|
||||
|
||||
@@ -96,11 +96,6 @@ describe('TransferForm', () => {
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
targetText: 'Include transfer fee',
|
||||
tooltipText:
|
||||
'The fee will be taken from the amount you are transferring.',
|
||||
},
|
||||
{
|
||||
targetText: 'Transfer fee',
|
||||
tooltipText: /transfer\.fee\.factor/,
|
||||
@@ -276,9 +271,6 @@ describe('TransferForm', () => {
|
||||
|
||||
const amountInput = screen.getByLabelText('Amount');
|
||||
|
||||
const checkbox = screen.getByTestId('include-transfer-fee');
|
||||
expect(checkbox).not.toBeChecked();
|
||||
|
||||
await userEvent.clear(amountInput);
|
||||
await userEvent.type(amountInput, '50');
|
||||
|
||||
@@ -288,10 +280,7 @@ describe('TransferForm', () => {
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Use max' }));
|
||||
expect(amountInput).toHaveValue('100.00');
|
||||
|
||||
// If transfering from a vested account 'include fees' checkbox should
|
||||
// be disabled and fees should be 0
|
||||
expect(checkbox).not.toBeChecked();
|
||||
expect(checkbox).toBeDisabled();
|
||||
// If transfering from a vested account fees should be 0
|
||||
const expectedFee = '0';
|
||||
const total = new BigNumber(amount).plus(expectedFee).toFixed();
|
||||
|
||||
@@ -396,78 +385,7 @@ describe('TransferForm', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('IncludeFeesCheckbox', () => {
|
||||
it('validates fields and submits when checkbox is checked', async () => {
|
||||
const mockSubmit = jest.fn();
|
||||
renderComponent({ ...props, submitTransfer: mockSubmit });
|
||||
|
||||
// check current pubkey not shown
|
||||
const keySelect = screen.getByLabelText<HTMLSelectElement>('To Vega key');
|
||||
const pubKeyOptions = ['', pubKey, props.pubKeys[1]];
|
||||
expect(keySelect.children).toHaveLength(pubKeyOptions.length);
|
||||
expect(Array.from(keySelect.options).map((o) => o.value)).toEqual(
|
||||
pubKeyOptions
|
||||
);
|
||||
|
||||
await submit();
|
||||
expect(await screen.findAllByText('Required')).toHaveLength(2); // pubkey set as default value
|
||||
|
||||
// Select a pubkey
|
||||
await userEvent.selectOptions(
|
||||
screen.getByLabelText('To Vega key'),
|
||||
props.pubKeys[1]
|
||||
);
|
||||
|
||||
// Select asset
|
||||
await selectAsset(asset);
|
||||
|
||||
await userEvent.selectOptions(
|
||||
screen.getByLabelText('From account'),
|
||||
`${AccountType.ACCOUNT_TYPE_GENERAL}-${asset.id}`
|
||||
);
|
||||
|
||||
const amountInput = screen.getByLabelText('Amount');
|
||||
const checkbox = screen.getByTestId('include-transfer-fee');
|
||||
|
||||
// 1003-TRAN-022
|
||||
expect(checkbox).not.toBeChecked();
|
||||
|
||||
await userEvent.clear(amountInput);
|
||||
await userEvent.type(amountInput, amount);
|
||||
await userEvent.click(checkbox);
|
||||
|
||||
expect(checkbox).toBeChecked();
|
||||
const expectedFee = new BigNumber(amount)
|
||||
.times(props.feeFactor)
|
||||
.toFixed();
|
||||
const expectedAmount = new BigNumber(amount).minus(expectedFee).toFixed();
|
||||
|
||||
// 1003-TRAN-020
|
||||
expect(screen.getByTestId('transfer-fee')).toHaveTextContent(expectedFee);
|
||||
expect(screen.getByTestId('transfer-amount')).toHaveTextContent(
|
||||
expectedAmount
|
||||
);
|
||||
expect(screen.getByTestId('total-transfer-fee')).toHaveTextContent(
|
||||
amount
|
||||
);
|
||||
|
||||
await submit();
|
||||
|
||||
await waitFor(() => {
|
||||
// 1003-TRAN-023
|
||||
expect(mockSubmit).toHaveBeenCalledTimes(1);
|
||||
expect(mockSubmit).toHaveBeenCalledWith({
|
||||
fromAccountType: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
toAccountType: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
to: props.pubKeys[1],
|
||||
asset: asset.id,
|
||||
amount: removeDecimal(expectedAmount, asset.decimals),
|
||||
oneOff: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('validates fields when checkbox is not checked', async () => {
|
||||
renderComponent(props);
|
||||
|
||||
@@ -497,11 +415,8 @@ describe('TransferForm', () => {
|
||||
);
|
||||
|
||||
const amountInput = screen.getByLabelText('Amount');
|
||||
const checkbox = screen.getByTestId('include-transfer-fee');
|
||||
expect(checkbox).not.toBeChecked();
|
||||
|
||||
await userEvent.type(amountInput, amount);
|
||||
expect(checkbox).not.toBeChecked();
|
||||
const expectedFee = new BigNumber(amount)
|
||||
.times(props.feeFactor)
|
||||
.toFixed();
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
TradingRichSelect,
|
||||
TradingSelect,
|
||||
Tooltip,
|
||||
TradingCheckbox,
|
||||
TradingButton,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { Transfer } from '@vegaprotocol/wallet';
|
||||
@@ -135,32 +134,17 @@ export const TransferForm = ({
|
||||
const accountBalance =
|
||||
account && addDecimal(account.balance, account.asset.decimals);
|
||||
|
||||
const [includeFee, setIncludeFee] = useState(false);
|
||||
|
||||
// Max amount given selected asset and from account
|
||||
const max = accountBalance ? new BigNumber(accountBalance) : new BigNumber(0);
|
||||
|
||||
const transferAmount = useMemo(() => {
|
||||
if (!amount) return undefined;
|
||||
if (includeFee && feeFactor) {
|
||||
return new BigNumber(1).minus(feeFactor).times(amount).toString();
|
||||
}
|
||||
return amount;
|
||||
}, [amount, includeFee, feeFactor]);
|
||||
|
||||
const fee = useMemo(() => {
|
||||
if (!transferAmount) return undefined;
|
||||
if (includeFee) {
|
||||
return new BigNumber(amount).minus(transferAmount).toString();
|
||||
}
|
||||
return (
|
||||
feeFactor && new BigNumber(feeFactor).times(transferAmount).toString()
|
||||
);
|
||||
}, [amount, includeFee, transferAmount, feeFactor]);
|
||||
const fee = useMemo(
|
||||
() => feeFactor && new BigNumber(feeFactor).times(amount).toString(),
|
||||
[amount, feeFactor]
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(fields: FormFields) => {
|
||||
if (!transferAmount) {
|
||||
if (!amount) {
|
||||
throw new Error('Submitted transfer with no amount selected');
|
||||
}
|
||||
|
||||
@@ -173,7 +157,7 @@ export const TransferForm = ({
|
||||
|
||||
const transfer = normalizeTransfer(
|
||||
fields.toVegaKey,
|
||||
transferAmount,
|
||||
amount,
|
||||
type,
|
||||
AccountType.ACCOUNT_TYPE_GENERAL, // field is readonly in the form
|
||||
{
|
||||
@@ -183,7 +167,7 @@ export const TransferForm = ({
|
||||
);
|
||||
submitTransfer(transfer);
|
||||
},
|
||||
[submitTransfer, transferAmount, assets]
|
||||
[submitTransfer, amount, assets]
|
||||
);
|
||||
|
||||
// reset for placeholder workaround https://github.com/radix-ui/primitives/issues/1569
|
||||
@@ -279,7 +263,6 @@ export const TransferForm = ({
|
||||
) {
|
||||
setValue('toVegaKey', pubKey);
|
||||
setToVegaKeyMode('select');
|
||||
setIncludeFee(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -449,27 +432,9 @@ export const TransferForm = ({
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<div className="mb-4">
|
||||
<Tooltip
|
||||
description={t(
|
||||
`The fee will be taken from the amount you are transferring.`
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<TradingCheckbox
|
||||
name="include-transfer-fee"
|
||||
disabled={!transferAmount || fromVested}
|
||||
label={t('Include transfer fee')}
|
||||
checked={includeFee}
|
||||
onCheckedChange={() => setIncludeFee((x) => !x)}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{transferAmount && fee && (
|
||||
{amount && fee && (
|
||||
<TransferFee
|
||||
amount={transferAmount}
|
||||
transferAmount={transferAmount}
|
||||
amount={amount}
|
||||
feeFactor={feeFactor}
|
||||
fee={fromVested ? '0' : fee}
|
||||
decimals={asset?.decimals}
|
||||
@@ -484,29 +449,22 @@ export const TransferForm = ({
|
||||
|
||||
export const TransferFee = ({
|
||||
amount,
|
||||
transferAmount,
|
||||
feeFactor,
|
||||
fee,
|
||||
decimals,
|
||||
}: {
|
||||
amount: string;
|
||||
transferAmount: string;
|
||||
feeFactor: string | null;
|
||||
fee?: string;
|
||||
decimals?: number;
|
||||
}) => {
|
||||
const t = useT();
|
||||
if (!feeFactor || !amount || !transferAmount || !fee) return null;
|
||||
if (
|
||||
isNaN(Number(feeFactor)) ||
|
||||
isNaN(Number(amount)) ||
|
||||
isNaN(Number(transferAmount)) ||
|
||||
isNaN(Number(fee))
|
||||
) {
|
||||
if (!feeFactor || !amount || !fee) return null;
|
||||
if (isNaN(Number(feeFactor)) || isNaN(Number(amount)) || isNaN(Number(fee))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const totalValue = new BigNumber(transferAmount).plus(fee).toString();
|
||||
const totalValue = new BigNumber(amount).plus(fee).toString();
|
||||
|
||||
return (
|
||||
<div className="mb-4 flex flex-col gap-2 text-xs">
|
||||
|
||||
@@ -29,7 +29,6 @@ export const assetsProvider = makeDataProvider<
|
||||
>({
|
||||
query: AssetsDocument,
|
||||
getData,
|
||||
errorPolicy: 'all',
|
||||
});
|
||||
|
||||
export const assetsMapProvider = makeDerivedDataProvider<
|
||||
|
||||
@@ -20,9 +20,6 @@ query Candles($marketId: ID!, $interval: Interval!, $since: String!) {
|
||||
code
|
||||
}
|
||||
}
|
||||
marketTimestamps {
|
||||
open
|
||||
}
|
||||
candlesConnection(
|
||||
interval: $interval
|
||||
since: $since
|
||||
|
||||
+1
-4
@@ -12,7 +12,7 @@ export type CandlesQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CandlesQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any }, candlesConnection?: { __typename?: 'CandleDataConnection', edges?: Array<{ __typename?: 'CandleEdge', node: { __typename?: 'Candle', periodStart: any, lastUpdateInPeriod: any, high: string, low: string, open: string, close: string, volume: string } } | null> | null } | null } | null };
|
||||
export type CandlesQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string } }, candlesConnection?: { __typename?: 'CandleDataConnection', edges?: Array<{ __typename?: 'CandleEdge', node: { __typename?: 'Candle', periodStart: any, lastUpdateInPeriod: any, high: string, low: string, open: string, close: string, volume: string } } | null> | null } | null } | null };
|
||||
|
||||
export type CandlesEventsSubscriptionVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
@@ -46,9 +46,6 @@ export const CandlesDocument = gql`
|
||||
code
|
||||
}
|
||||
}
|
||||
marketTimestamps {
|
||||
open
|
||||
}
|
||||
candlesConnection(interval: $interval, since: $since, pagination: {last: 5000}) {
|
||||
edges {
|
||||
node {
|
||||
|
||||
@@ -15,10 +15,6 @@ export const candlesQuery = (
|
||||
id: 'market-0',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 0,
|
||||
marketTimestamps: {
|
||||
__typename: 'MarketTimestamps',
|
||||
open: '2022-04-06T09:15:00Z',
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
id: '',
|
||||
|
||||
@@ -13,9 +13,6 @@ const returnDataMocks = (nodes: CandleFieldsFragment[]): CandlesQuery => {
|
||||
market: {
|
||||
decimalPlaces: 1,
|
||||
positionDecimalPlaces: 1,
|
||||
marketTimestamps: {
|
||||
open: '2022-05-10T11:00:00Z',
|
||||
},
|
||||
candlesConnection: {
|
||||
edges: nodes.map((node) => ({ node })),
|
||||
},
|
||||
@@ -24,107 +21,106 @@ const returnDataMocks = (nodes: CandleFieldsFragment[]): CandlesQuery => {
|
||||
} as CandlesQuery;
|
||||
};
|
||||
|
||||
const dataMocks: {
|
||||
[key in Schema.Interval]?: Partial<CandleFieldsFragment>[];
|
||||
} = {
|
||||
[Schema.Interval.INTERVAL_I1M]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:05:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I5M]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:25:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I15M]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T13:15:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I1H]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T17:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I6H]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-11T18:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I1D]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T00:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-15T00:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_BLOCK]: [],
|
||||
};
|
||||
const dataMocks: { [key in Schema.Interval]: Partial<CandleFieldsFragment>[] } =
|
||||
{
|
||||
[Schema.Interval.INTERVAL_I1M]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:05:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I5M]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:25:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I15M]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T13:15:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I1H]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T17:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I6H]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-11T18:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I1D]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T00:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-15T00:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_BLOCK]: [],
|
||||
};
|
||||
|
||||
describe('VegaDataSource', () => {
|
||||
const marketId = 'marketId';
|
||||
|
||||
@@ -172,30 +172,13 @@ export class VegaDataSource implements DataSource {
|
||||
},
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
|
||||
if (data?.market?.candlesConnection?.edges) {
|
||||
const decimalPlaces = data.market.decimalPlaces;
|
||||
const positionDecimalPlaces = data.market.positionDecimalPlaces;
|
||||
|
||||
const openSince =
|
||||
typeof data.market.marketTimestamps.open === 'string' &&
|
||||
data.market.marketTimestamps.open.length > 0
|
||||
? new Date(data.market.marketTimestamps.open)
|
||||
: // this should never happen, but just in case let's have it as
|
||||
// Date(0) if the market data is incomplete for some reason
|
||||
new Date(0);
|
||||
|
||||
if (this.from < openSince) {
|
||||
// overwrite `from` if requested value is before the market's open date
|
||||
this.from = openSince;
|
||||
}
|
||||
|
||||
const candles = data.market.candlesConnection.edges
|
||||
.map((edge) => edge?.node)
|
||||
.filter((node): node is CandleFieldsFragment => !!node)
|
||||
.filter(
|
||||
(node) => sinceMarketOpen(node, openSince) && !emptyCandle(node)
|
||||
)
|
||||
.map((node) =>
|
||||
parseCandle(node, decimalPlaces, positionDecimalPlaces)
|
||||
)
|
||||
@@ -343,9 +326,3 @@ function parseCandle(
|
||||
volume: Number(addDecimal(candle.volume, positionDecimalPlaces)),
|
||||
};
|
||||
}
|
||||
|
||||
const sinceMarketOpen = (candle: CandleFieldsFragment, openSince: Date) =>
|
||||
new Date(candle.periodStart) >= openSince;
|
||||
|
||||
const emptyCandle = (candle: CandleFieldsFragment) =>
|
||||
candle.high === '' && candle.low === '';
|
||||
|
||||
@@ -22,9 +22,11 @@ import {
|
||||
type QueryOptions,
|
||||
type ApolloClient,
|
||||
} from '@apollo/client';
|
||||
import { type ApolloError } from '@apollo/client';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import type { GraphQLErrors } from '@apollo/client/errors';
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { type Subscription, type Observable } from 'zen-observable-ts';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
type Item = {
|
||||
cursor: string;
|
||||
@@ -115,6 +117,24 @@ const paginatedSubscribe = makeDataProvider<
|
||||
},
|
||||
});
|
||||
|
||||
const mockErrorPolicyGuard: (errors: GraphQLErrors) => boolean = jest
|
||||
.fn()
|
||||
.mockImplementation(() => true);
|
||||
const errorGuardedSubscribe = makeDataProvider<
|
||||
QueryData,
|
||||
Data,
|
||||
SubscriptionData,
|
||||
Delta,
|
||||
Variables
|
||||
>({
|
||||
query,
|
||||
subscriptionQuery,
|
||||
update,
|
||||
getData,
|
||||
getDelta,
|
||||
errorPolicyGuard: mockErrorPolicyGuard,
|
||||
});
|
||||
|
||||
const derivedSubscribe = makeDerivedDataProvider(
|
||||
[paginatedSubscribe, subscribe],
|
||||
combineData,
|
||||
@@ -384,6 +404,34 @@ describe('data provider', () => {
|
||||
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
|
||||
it('should retry with ignore error policy if errorPolicyGuard returns true', async () => {
|
||||
const subscription = errorGuardedSubscribe(callback, client, variables);
|
||||
const graphQLError = new GraphQLError(
|
||||
'',
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
['market', 'data'],
|
||||
undefined,
|
||||
{
|
||||
type: 'Internal',
|
||||
}
|
||||
);
|
||||
const graphQLErrors = [graphQLError];
|
||||
const error = new ApolloError({ graphQLErrors });
|
||||
|
||||
await rejectQuery(error);
|
||||
const data = generateData(0, 5);
|
||||
await resolveQuery({
|
||||
data,
|
||||
});
|
||||
expect(mockErrorPolicyGuard).toHaveBeenNthCalledWith(1, graphQLErrors);
|
||||
await waitFor(() =>
|
||||
expect(getData).toHaveBeenCalledWith({ data }, variables)
|
||||
);
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
});
|
||||
|
||||
describe('derived data provider', () => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
ApolloQueryResult,
|
||||
QueryOptions,
|
||||
} from '@apollo/client';
|
||||
import type { GraphQLErrors } from '@apollo/client/errors';
|
||||
import type { Subscription } from 'zen-observable-ts';
|
||||
import isEqualWith from 'lodash/isEqualWith';
|
||||
import { isNotFoundGraphQLError } from './helpers';
|
||||
@@ -160,7 +161,7 @@ interface DataProviderParams<
|
||||
resetDelay?: number;
|
||||
pollInterval?: number;
|
||||
additionalContext?: Record<string, unknown>;
|
||||
errorPolicy?: ErrorPolicy;
|
||||
errorPolicyGuard?: (graphqlErrors: GraphQLErrors) => boolean;
|
||||
getQueryVariables?: (variables: Variables) => QueryVariables;
|
||||
getSubscriptionVariables?: (
|
||||
variables: Variables
|
||||
@@ -175,7 +176,7 @@ interface DataProviderParams<
|
||||
* @param fetchPolicy
|
||||
* @param resetDelay
|
||||
* @param additionalContext add property to the context of the query, ie. 'isEnlargedTimeout'
|
||||
* @param errorPolicy Apollos error policy, will be used when querying
|
||||
* @param errorPolicyGuard indicate which gql errors can be tolerate
|
||||
* @returns subscribe function
|
||||
*/
|
||||
function makeDataProviderInternal<
|
||||
@@ -196,7 +197,7 @@ function makeDataProviderInternal<
|
||||
fetchPolicy,
|
||||
resetDelay,
|
||||
additionalContext,
|
||||
errorPolicy = 'none',
|
||||
errorPolicyGuard,
|
||||
getQueryVariables,
|
||||
getSubscriptionVariables,
|
||||
pollInterval,
|
||||
@@ -330,10 +331,20 @@ function makeDataProviderInternal<
|
||||
const callQuery = (
|
||||
pagination?: Pagination,
|
||||
policy?: ErrorPolicy
|
||||
): Promise<ApolloQueryResult<QueryData>> => {
|
||||
const options = getQueryOptions(pagination, policy);
|
||||
return client.query<QueryData>(options);
|
||||
};
|
||||
): Promise<ApolloQueryResult<QueryData>> =>
|
||||
client
|
||||
.query<QueryData>(getQueryOptions(pagination, policy))
|
||||
.catch((err) => {
|
||||
if (
|
||||
err.graphQLErrors &&
|
||||
errorPolicyGuard &&
|
||||
errorPolicyGuard(err.graphQLErrors)
|
||||
) {
|
||||
return callQuery(pagination, 'ignore');
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
const load = async () => {
|
||||
if (!pagination) {
|
||||
@@ -353,7 +364,7 @@ function makeDataProviderInternal<
|
||||
}
|
||||
}
|
||||
|
||||
const res = await callQuery(paginationVariables, errorPolicy);
|
||||
const res = await callQuery(paginationVariables);
|
||||
|
||||
const insertionData = getData(res.data, variables);
|
||||
const insertionPageInfo = pagination.getPageInfo(res.data);
|
||||
@@ -406,14 +417,12 @@ function makeDataProviderInternal<
|
||||
const paginationVariables = pagination
|
||||
? { first: pagination.first }
|
||||
: undefined;
|
||||
|
||||
if (pollInterval) {
|
||||
callWatchQuery(paginationVariables, errorPolicy);
|
||||
callWatchQuery();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
onNext(await callQuery(paginationVariables, errorPolicy));
|
||||
onNext(await callQuery(paginationVariables));
|
||||
} catch (e) {
|
||||
onError(e as Error);
|
||||
} finally {
|
||||
|
||||
@@ -27,3 +27,10 @@ const hasNotFoundGraphQLErrors = (errors: GraphQLErrors, path?: string[]) => {
|
||||
(!path || path.every((item, i) => item === e?.path?.[i]))
|
||||
);
|
||||
};
|
||||
|
||||
export const marketDataErrorPolicyGuard = (errors: GraphQLErrors) =>
|
||||
errors.every(
|
||||
(e) =>
|
||||
e.message.match(/no market data for market:/i) ||
|
||||
e.message.match(/Conditions list is empty/)
|
||||
);
|
||||
|
||||
@@ -13,7 +13,6 @@ import { AsyncRendererInline } from '@vegaprotocol/ui-toolkit';
|
||||
import { DealTicket } from './deal-ticket';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useT } from '../../use-t';
|
||||
import { MarginModeSelector } from './margin-mode-selector';
|
||||
|
||||
interface DealTicketContainerProps {
|
||||
marketId: string;
|
||||
@@ -52,31 +51,21 @@ export const DealTicketContainer = ({
|
||||
reload={reload}
|
||||
>
|
||||
{market && marketData ? (
|
||||
<>
|
||||
{featureFlags.ISOLATED_MARGIN && (
|
||||
<>
|
||||
<MarginModeSelector marketId={marketId} />
|
||||
<hr className="border-vega-clight-500 dark:border-vega-cdark-500 mb-4" />
|
||||
</>
|
||||
)}
|
||||
{featureFlags.STOP_ORDERS && showStopOrder ? (
|
||||
<StopOrder
|
||||
market={market}
|
||||
marketPrice={marketPrice}
|
||||
submit={(stopOrdersSubmission) =>
|
||||
create({ stopOrdersSubmission })
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<DealTicket
|
||||
{...props}
|
||||
market={market}
|
||||
marketPrice={marketPrice}
|
||||
marketData={marketData}
|
||||
submit={(orderSubmission) => create({ orderSubmission })}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
featureFlags.STOP_ORDERS && showStopOrder ? (
|
||||
<StopOrder
|
||||
market={market}
|
||||
marketPrice={marketPrice}
|
||||
submit={(stopOrdersSubmission) => create({ stopOrdersSubmission })}
|
||||
/>
|
||||
) : (
|
||||
<DealTicket
|
||||
{...props}
|
||||
market={market}
|
||||
marketPrice={marketPrice}
|
||||
marketData={marketData}
|
||||
submit={(orderSubmission) => create({ orderSubmission })}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<p>{t('Could not load market')}</p>
|
||||
)}
|
||||
|
||||
@@ -263,11 +263,8 @@ export const DealTicket = ({
|
||||
marketId: market.id,
|
||||
openVolume,
|
||||
orders,
|
||||
marginAccountBalance: marginAccountBalance,
|
||||
generalAccountBalance: generalAccountBalance,
|
||||
orderMarginAccountBalance: '0', // TODO: Get real balance
|
||||
marginMode: Schema.MarginMode.MARGIN_MODE_CROSS_MARGIN, // TODO: unhardcode this and get users margin mode for the market
|
||||
averageEntryPrice: marketPrice || '0', // TODO: This assumes the order will be entirely filled at the current market price
|
||||
collateralAvailable:
|
||||
marginAccountBalance || generalAccountBalance ? balance : undefined,
|
||||
skip:
|
||||
!normalizedOrder ||
|
||||
(normalizedOrder.type !== Schema.OrderType.TYPE_MARKET &&
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import {
|
||||
TradingButton as Button,
|
||||
TradingInput as Input,
|
||||
FormGroup,
|
||||
LeverageSlider,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { MarginMode, useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
import {
|
||||
type VegaTransactionStore,
|
||||
useVegaTransactionStore,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useT } from '../../use-t';
|
||||
import classnames from 'classnames';
|
||||
import { marketMarginDataProvider } from '@vegaprotocol/accounts';
|
||||
import { useMaxLeverage } from '@vegaprotocol/positions';
|
||||
|
||||
const defaultLeverage = 10;
|
||||
interface MarginDialogProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
marketId: string;
|
||||
partyId: string;
|
||||
create: VegaTransactionStore['create'];
|
||||
}
|
||||
|
||||
const CrossMarginModeDialog = ({
|
||||
open,
|
||||
onClose,
|
||||
marketId,
|
||||
create,
|
||||
}: MarginDialogProps) => {
|
||||
const t = useT();
|
||||
return (
|
||||
<Dialog
|
||||
title={t('Cross margin')}
|
||||
size="small"
|
||||
open={open}
|
||||
onChange={(isOpen) => {
|
||||
if (!isOpen) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="text-sm mb-4">
|
||||
<p className="mb-1">
|
||||
{t('You are setting this market to cross-margin mode.')}
|
||||
</p>
|
||||
<p className="mb-1">
|
||||
{t(
|
||||
'Your max leverage on each position will be determined by the risk model of the market.'
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{t(
|
||||
'All available funds in your general account will be used to finance your margin if the market moves against you.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
create({
|
||||
updateMarginMode: {
|
||||
marketId,
|
||||
mode: MarginMode.MARGIN_MODE_CROSS_MARGIN,
|
||||
},
|
||||
});
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('Confirm')}
|
||||
</Button>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
const IsolatedMarginModeDialog = ({
|
||||
open,
|
||||
onClose,
|
||||
marketId,
|
||||
partyId,
|
||||
marginFactor,
|
||||
create,
|
||||
}: MarginDialogProps & { marginFactor: string }) => {
|
||||
const [leverage, setLeverage] = useState(
|
||||
Number((1 / Number(marginFactor)).toFixed(1))
|
||||
);
|
||||
const { data: maxLeverage } = useMaxLeverage(marketId, partyId);
|
||||
const max = Math.floor((maxLeverage || 1) * 10) / 10;
|
||||
useEffect(() => {
|
||||
setLeverage(Number((1 / Number(marginFactor)).toFixed(1)));
|
||||
}, [marginFactor]);
|
||||
useEffect(() => {
|
||||
if (maxLeverage && leverage > max) {
|
||||
setLeverage(max);
|
||||
}
|
||||
}, [max, maxLeverage, leverage]);
|
||||
|
||||
const t = useT();
|
||||
return (
|
||||
<Dialog
|
||||
title={t('Isolated margin')}
|
||||
size="small"
|
||||
open={open}
|
||||
onChange={(isOpen) => {
|
||||
if (!isOpen) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="text-sm mb-4">
|
||||
<p className="mb-1">
|
||||
{t('You are setting this market to isolated margin mode.')}
|
||||
</p>
|
||||
<p className="mb-1">
|
||||
{t(
|
||||
'Set the leverage you want below. The maximum leverage you can take is determined by the risk model of the market.'
|
||||
)}
|
||||
</p>
|
||||
<p className="mb-1">
|
||||
{t(
|
||||
'Only your allocated margin will be used to fund this position, and if the maintenance margin is breached you will be closed out.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={() => {
|
||||
create({
|
||||
updateMarginMode: {
|
||||
marketId,
|
||||
mode: MarginMode.MARGIN_MODE_ISOLATED_MARGIN,
|
||||
marginFactor: `${1 / leverage}`,
|
||||
},
|
||||
});
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<FormGroup label={t('Leverage')} labelFor="leverage-input" compact>
|
||||
<div className="mb-2">
|
||||
<LeverageSlider
|
||||
max={max}
|
||||
step={0.1}
|
||||
value={[leverage]}
|
||||
onValueChange={([value]) => setLeverage(value)}
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
type="number"
|
||||
id="leverage-input"
|
||||
min={1}
|
||||
max={max}
|
||||
step={0.1}
|
||||
value={leverage}
|
||||
onChange={(e) => setLeverage(Number(e.target.value))}
|
||||
/>
|
||||
</FormGroup>
|
||||
<Button className="w-full" type="submit">
|
||||
{t('Confirm')}
|
||||
</Button>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export const MarginModeSelector = ({ marketId }: { marketId: string }) => {
|
||||
const t = useT();
|
||||
const [dialog, setDialog] = useState<'cross' | 'isolated' | ''>();
|
||||
const { pubKey: partyId, isReadOnly } = useVegaWallet();
|
||||
const { data: margin } = useDataProvider({
|
||||
dataProvider: marketMarginDataProvider,
|
||||
variables: {
|
||||
partyId: partyId || '',
|
||||
marketId,
|
||||
},
|
||||
skip: !partyId,
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!partyId) {
|
||||
setDialog('');
|
||||
}
|
||||
}, [partyId]);
|
||||
const create = useVegaTransactionStore((state) => state.create);
|
||||
const marginMode = margin?.marginMode;
|
||||
const marginFactor =
|
||||
margin?.marginFactor && margin?.marginFactor !== '0'
|
||||
? margin?.marginFactor
|
||||
: undefined;
|
||||
const disabled = isReadOnly;
|
||||
const onClose = () => setDialog(undefined);
|
||||
const enabledModeClassName = 'bg-vega-clight-500 dark:bg-vega-cdark-500';
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-4 grid h-8 leading-8 font-alpha text-xs grid-cols-2">
|
||||
<button
|
||||
disabled={disabled}
|
||||
onClick={() => partyId && setDialog('cross')}
|
||||
className={classnames('rounded', {
|
||||
[enabledModeClassName]:
|
||||
!marginMode ||
|
||||
marginMode === Types.MarginMode.MARGIN_MODE_CROSS_MARGIN,
|
||||
})}
|
||||
>
|
||||
{t('Cross')}
|
||||
</button>
|
||||
<button
|
||||
disabled={disabled}
|
||||
onClick={() => partyId && setDialog('isolated')}
|
||||
className={classnames('rounded', {
|
||||
[enabledModeClassName]:
|
||||
marginMode === Types.MarginMode.MARGIN_MODE_ISOLATED_MARGIN,
|
||||
})}
|
||||
>
|
||||
{t('Isolated {{leverage}}x', {
|
||||
leverage: marginFactor
|
||||
? (1 / Number(marginFactor)).toFixed(1)
|
||||
: defaultLeverage,
|
||||
})}
|
||||
</button>
|
||||
</div>
|
||||
{partyId && (
|
||||
<CrossMarginModeDialog
|
||||
partyId={partyId}
|
||||
open={dialog === 'cross'}
|
||||
onClose={onClose}
|
||||
marketId={marketId}
|
||||
create={create}
|
||||
/>
|
||||
)}
|
||||
{partyId && (
|
||||
<IsolatedMarginModeDialog
|
||||
partyId={partyId}
|
||||
open={dialog === 'isolated'}
|
||||
onClose={onClose}
|
||||
marketId={marketId}
|
||||
create={create}
|
||||
marginFactor={marginFactor || `${1 / defaultLeverage}`}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
import { parseISO, isValid, isAfter } from 'date-fns';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
useProposalOfMarketQuery,
|
||||
type ProposalOfMarketQuery,
|
||||
type SingleProposal,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useProposalOfMarketQuery } from '@vegaprotocol/proposals';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
@@ -40,15 +36,12 @@ export const TradingModeTooltip = ({
|
||||
marketTradingMode,
|
||||
});
|
||||
|
||||
// We only fetch Proposals (and not BatchProposals)
|
||||
const proposal = proposalData?.proposal as SingleProposal<
|
||||
ProposalOfMarketQuery['proposal']
|
||||
>;
|
||||
|
||||
if (!market || !marketData) {
|
||||
return null;
|
||||
}
|
||||
const enactmentDate = parseISO(proposal?.terms.enactmentDatetime);
|
||||
const enactmentDate = parseISO(
|
||||
proposalData?.proposal?.terms.enactmentDatetime
|
||||
);
|
||||
|
||||
const compiledGrid =
|
||||
!skipGrid && compileGridData(t, market, marketData, onSelect);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { usePositionEstimate } from './use-position-estimate';
|
||||
import * as positionsModule from '@vegaprotocol/positions';
|
||||
import type {
|
||||
EstimatePositionQuery,
|
||||
EstimatePositionQueryVariables,
|
||||
} from '@vegaprotocol/positions';
|
||||
import type { QueryResult } from '@apollo/client';
|
||||
|
||||
let mockData: object | undefined = {};
|
||||
|
||||
describe('usePositionEstimate', () => {
|
||||
const args = {
|
||||
marketId: 'marketId',
|
||||
openVolume: '10',
|
||||
orders: [],
|
||||
collateralAvailable: '200',
|
||||
skip: false,
|
||||
};
|
||||
it('should return proper data', () => {
|
||||
jest
|
||||
.spyOn(positionsModule, 'useEstimatePositionQuery')
|
||||
.mockReturnValue({ data: mockData } as unknown as QueryResult<
|
||||
EstimatePositionQuery,
|
||||
EstimatePositionQueryVariables
|
||||
>);
|
||||
const { result, rerender } = renderHook(() => usePositionEstimate(args));
|
||||
expect(result.current).toEqual(mockData);
|
||||
mockData = undefined;
|
||||
rerender(true);
|
||||
expect(result.current).toEqual({});
|
||||
});
|
||||
});
|
||||
@@ -13,12 +13,7 @@ export const usePositionEstimate = ({
|
||||
marketId,
|
||||
openVolume,
|
||||
orders,
|
||||
generalAccountBalance,
|
||||
marginAccountBalance,
|
||||
orderMarginAccountBalance,
|
||||
averageEntryPrice,
|
||||
marginMode,
|
||||
marginFactor,
|
||||
collateralAvailable,
|
||||
skip,
|
||||
}: PositionEstimateProps) => {
|
||||
const [estimates, setEstimates] = useState<EstimatePositionQuery | undefined>(
|
||||
@@ -29,12 +24,7 @@ export const usePositionEstimate = ({
|
||||
marketId,
|
||||
openVolume,
|
||||
orders,
|
||||
generalAccountBalance,
|
||||
marginAccountBalance,
|
||||
orderMarginAccountBalance,
|
||||
averageEntryPrice,
|
||||
marginMode,
|
||||
marginFactor,
|
||||
collateralAvailable,
|
||||
},
|
||||
skip,
|
||||
fetchPolicy: 'no-cache',
|
||||
|
||||
@@ -323,9 +323,6 @@ export const compileFeatureFlags = (refresh = false): FeatureFlags => {
|
||||
STOP_ORDERS: TRUTHY.includes(
|
||||
windowOrDefault('NX_STOP_ORDERS', process.env['NX_STOP_ORDERS']) as string
|
||||
),
|
||||
ISOLATED_MARGIN: TRUTHY.includes(
|
||||
windowOrDefault('NX_STOP_ORDERS', process.env['NX_STOP_ORDERS']) as string
|
||||
),
|
||||
SUCCESSOR_MARKETS: TRUTHY.includes(
|
||||
windowOrDefault(
|
||||
'NX_SUCCESSOR_MARKETS',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user