Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b998c8d1a7 | ||
|
|
7c1a4f2fde | ||
|
|
123c0e001f | ||
|
|
6f2ec4e7cd | ||
|
|
da1ebf5cac | ||
|
|
6b7bbc9c94 | ||
|
|
9153677a33 | ||
|
|
cff1818940 | ||
|
|
d05dd6e4cb | ||
|
|
4ef789e00a | ||
|
|
0bd13a5f7b | ||
|
|
67d38ff03e | ||
|
|
6aea10c27b | ||
|
|
27a9d5f247 | ||
|
|
261f32aa5b |
@@ -1,9 +1,11 @@
|
||||
query ExplorerProposal($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
... on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -8,16 +8,18 @@ export type ExplorerProposalQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null };
|
||||
export type ExplorerProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null };
|
||||
|
||||
|
||||
export const ExplorerProposalDocument = gql`
|
||||
query ExplorerProposal($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
... on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@ 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 } from './__generated__/Proposal';
|
||||
import {
|
||||
ExplorerProposalDocument,
|
||||
type ExplorerProposalQuery,
|
||||
type ExplorerProposalQueryVariables,
|
||||
} from './__generated__/Proposal';
|
||||
import { GraphQLError } from 'graphql';
|
||||
|
||||
function renderComponent(id: string, mocks: MockedResponse[]) {
|
||||
@@ -23,7 +27,10 @@ describe('Proposal link component', () => {
|
||||
});
|
||||
|
||||
it('Renders the ID on error', async () => {
|
||||
const mock = {
|
||||
const mock: MockedResponse<
|
||||
ExplorerProposalQuery,
|
||||
ExplorerProposalQueryVariables
|
||||
> = {
|
||||
request: {
|
||||
query: ExplorerProposalDocument,
|
||||
variables: {
|
||||
@@ -40,17 +47,22 @@ describe('Proposal link component', () => {
|
||||
});
|
||||
|
||||
it('Renders the proposal title when the query returns a result', async () => {
|
||||
const mock = {
|
||||
const proposalId = '123';
|
||||
const mock: MockedResponse<
|
||||
ExplorerProposalQuery,
|
||||
ExplorerProposalQueryVariables
|
||||
> = {
|
||||
request: {
|
||||
query: ExplorerProposalDocument,
|
||||
variables: {
|
||||
id: '123',
|
||||
id: proposalId,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
proposal: {
|
||||
id: '123',
|
||||
__typename: 'Proposal',
|
||||
id: proposalId,
|
||||
rationale: {
|
||||
title: 'test-title',
|
||||
description: 'test description',
|
||||
@@ -60,13 +72,16 @@ describe('Proposal link component', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const res = render(renderComponent('123', [mock]));
|
||||
expect(res.getByText('123')).toBeInTheDocument();
|
||||
const res = render(renderComponent(proposalId, [mock]));
|
||||
expect(res.getByText(proposalId)).toBeInTheDocument();
|
||||
expect(await res.findByText('test-title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Leaves the proposal id when the market is not found', async () => {
|
||||
const mock = {
|
||||
const mock: MockedResponse<
|
||||
ExplorerProposalQuery,
|
||||
ExplorerProposalQueryVariables
|
||||
> = {
|
||||
request: {
|
||||
query: ExplorerProposalDocument,
|
||||
variables: {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { useExplorerProposalQuery } from './__generated__/Proposal';
|
||||
import {
|
||||
useExplorerProposalQuery,
|
||||
type ExplorerProposalQuery,
|
||||
} 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;
|
||||
@@ -16,8 +20,13 @@ const ProposalLink = ({ id, text }: ProposalLinkProps) => {
|
||||
variables: { id },
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as Extract<
|
||||
ExplorerProposalQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
|
||||
const base = ENV.dataSources.governanceUrl;
|
||||
const label = data?.proposal?.rationale.title || id;
|
||||
const label = proposal?.rationale.title || id;
|
||||
|
||||
return (
|
||||
<ExternalLink href={`${base}/proposals/${id}`}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import {
|
||||
LiquidationStrategyInfoPanel,
|
||||
LiquidityPriceRangeInfoPanel,
|
||||
LiquiditySLAParametersInfoPanel,
|
||||
MarginScalingFactorsPanel,
|
||||
@@ -94,6 +95,8 @@ 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,7 +1,9 @@
|
||||
query ExplorerProposalStatus($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
... on Proposal {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -8,15 +8,17 @@ export type ExplorerProposalStatusQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
|
||||
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
|
||||
|
||||
|
||||
export const ExplorerProposalStatusDocument = gql`
|
||||
query ExplorerProposalStatus($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
... on Proposal {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -14,16 +14,18 @@ export function format(date: string | undefined, def: string) {
|
||||
return new Date().toLocaleDateString() || def;
|
||||
}
|
||||
|
||||
export function getDate(
|
||||
data: ExplorerProposalStatusQuery | undefined,
|
||||
terms: Terms
|
||||
): string {
|
||||
type Proposal = Extract<
|
||||
ExplorerProposalStatusQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
|
||||
export function getDate(proposal: Proposal | undefined, terms: Terms): string {
|
||||
const DEFAULT = t('Unknown');
|
||||
if (!data?.proposal?.state) {
|
||||
if (!proposal?.state) {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
switch (data.proposal.state) {
|
||||
switch (proposal.state) {
|
||||
case 'STATE_DECLINED':
|
||||
return `${t('Rejected on')}: ${format(terms.closingTimestamp, DEFAULT)}`;
|
||||
case 'STATE_ENACTED':
|
||||
@@ -62,9 +64,11 @@ export const ProposalDate = ({ terms, id }: ProposalDateProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as Proposal;
|
||||
|
||||
return (
|
||||
<Lozenge className="font-sans text-xs float-right">
|
||||
{getDate(data, terms)}
|
||||
{getDate(proposal, terms)}
|
||||
</Lozenge>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,17 +2,8 @@ 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;
|
||||
}
|
||||
@@ -29,29 +20,38 @@ type IconAndLabel = {
|
||||
* @param data a data result from useExplorerProposalStatusQuery
|
||||
* @returns Icon name
|
||||
*/
|
||||
export function getIconAndLabelForStatus(
|
||||
res: ProposalQueryResult
|
||||
): IconAndLabel {
|
||||
export function useIconAndLabelForStatus(id: string): IconAndLabel {
|
||||
const { data, loading, error } = useExplorerProposalStatusQuery({
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as Extract<
|
||||
ExplorerProposalStatusQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
|
||||
const DEFAULT: IconAndLabel = {
|
||||
icon: 'error',
|
||||
label: t('Proposal state unknown'),
|
||||
};
|
||||
|
||||
if (res.loading) {
|
||||
if (loading) {
|
||||
return {
|
||||
icon: 'more',
|
||||
label: t('Loading data'),
|
||||
};
|
||||
}
|
||||
|
||||
if (!res?.data?.proposal || res.error) {
|
||||
if (!data?.proposal || error) {
|
||||
return {
|
||||
icon: 'error',
|
||||
label: res.error?.message || DEFAULT.label,
|
||||
label: error?.message || DEFAULT.label,
|
||||
};
|
||||
}
|
||||
|
||||
switch (res.data.proposal.state) {
|
||||
switch (proposal.state) {
|
||||
case 'STATE_DECLINED':
|
||||
return {
|
||||
icon: 'stop',
|
||||
@@ -99,13 +99,7 @@ export function getIconAndLabelForStatus(
|
||||
/**
|
||||
*/
|
||||
export const ProposalStatusIcon = ({ id }: ProposalStatusIconProps) => {
|
||||
const { icon, label } = getIconAndLabelForStatus(
|
||||
useExplorerProposalStatusQuery({
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
);
|
||||
const { icon, label } = useIconAndLabelForStatus(id);
|
||||
|
||||
return (
|
||||
<div className="float-left mr-3">
|
||||
|
||||
@@ -61,53 +61,4 @@ 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,7 +10,8 @@ export interface TxListNavigationProps {
|
||||
loading?: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
hasMoreTxs: boolean;
|
||||
children: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
isEmpty?: boolean;
|
||||
}
|
||||
/**
|
||||
* Displays a list of transactions with filters and controls to navigate through the list.
|
||||
@@ -21,9 +22,8 @@ export const TxsListNavigation = ({
|
||||
refreshTxs,
|
||||
nextPage,
|
||||
previousPage,
|
||||
hasMoreTxs,
|
||||
hasPreviousPage,
|
||||
children,
|
||||
isEmpty,
|
||||
loading = false,
|
||||
}: TxListNavigationProps) => {
|
||||
return (
|
||||
@@ -35,7 +35,6 @@ export const TxsListNavigation = ({
|
||||
<Button
|
||||
className="mr-2"
|
||||
size="xs"
|
||||
disabled={!hasPreviousPage || loading}
|
||||
onClick={() => {
|
||||
previousPage();
|
||||
}}
|
||||
@@ -44,7 +43,7 @@ export const TxsListNavigation = ({
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
disabled={!hasMoreTxs}
|
||||
disabled={isEmpty}
|
||||
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('last', count);
|
||||
url.searchParams.append('first', 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?last=10';
|
||||
const expectedUrl = 'https://example.com/transactions?first=10';
|
||||
|
||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||
});
|
||||
@@ -41,7 +41,7 @@ describe('getTxsDataUrl', () => {
|
||||
baseUrl: 'https://example.com/transactions',
|
||||
};
|
||||
const expectedUrl =
|
||||
'https://example.com/transactions?last=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
|
||||
'https://example.com/transactions?first=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 = 25,
|
||||
count = 50,
|
||||
before,
|
||||
after,
|
||||
filters,
|
||||
party,
|
||||
}: IUseTxsData) => {
|
||||
const [, setSearchParams] = useSearchParams();
|
||||
let hasMoreTxs = true;
|
||||
let hasMoreTxs = false;
|
||||
let txsData: BlockExplorerTransactionResult[] = [];
|
||||
|
||||
const url = getTxsDataUrl({
|
||||
@@ -60,8 +60,8 @@ export const useTxsData = ({
|
||||
}
|
||||
|
||||
const nextPage = useCallback(() => {
|
||||
const after = data?.transactions.at(-1)?.cursor || '';
|
||||
const params: URLSearchParamsInit = { after };
|
||||
const before = data?.transactions.at(-1)?.cursor || '';
|
||||
const params: URLSearchParamsInit = { before };
|
||||
if (filters) {
|
||||
params.filters = Array.from(filters).join(',');
|
||||
}
|
||||
@@ -69,8 +69,8 @@ export const useTxsData = ({
|
||||
}, [filters, data, setSearchParams]);
|
||||
|
||||
const previousPage = useCallback(() => {
|
||||
const before = data?.transactions[0]?.cursor || '';
|
||||
const params: URLSearchParamsInit = { before };
|
||||
const after = data?.transactions[0]?.cursor || '';
|
||||
const params: URLSearchParamsInit = { after };
|
||||
if (filters && filters.size > 0 && filters.size === 1) {
|
||||
params.filters = Array.from(filters)[0];
|
||||
}
|
||||
|
||||
@@ -51,9 +51,10 @@ export const TxsListFiltered = () => {
|
||||
refreshTxs={refreshTxs}
|
||||
nextPage={nextPage}
|
||||
previousPage={previousPage}
|
||||
hasPreviousPage={true}
|
||||
hasPreviousPage={hasMoreTxs}
|
||||
loading={loading}
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
isEmpty={txsData.length === 0}
|
||||
>
|
||||
<TxsFilter
|
||||
filters={filters}
|
||||
@@ -70,7 +71,16 @@ export const TxsListFiltered = () => {
|
||||
txs={txsData}
|
||||
loadMoreTxs={nextPage}
|
||||
error={error}
|
||||
className="mb-28 w-full min-w-[400px]"
|
||||
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}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -215,7 +215,7 @@ context(
|
||||
});
|
||||
|
||||
// 3003-PMAN-001
|
||||
it(
|
||||
it.skip(
|
||||
'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 { ProposalQuery } from '../proposals/proposal/__generated__/Proposal';
|
||||
import { type Proposal } from '../proposals/types';
|
||||
|
||||
const nodesToShow = 6;
|
||||
|
||||
@@ -39,7 +39,7 @@ const HomeProposals = ({
|
||||
proposals,
|
||||
protocolUpgradeProposals,
|
||||
}: {
|
||||
proposals: ProposalQuery['proposal'][];
|
||||
proposals: Proposal[];
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
+4
-9
@@ -1,16 +1,11 @@
|
||||
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 ReactNode } from 'react';
|
||||
import { type ProposalInfoLabelVariant } from '../proposal-info-label';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const CurrentProposalState = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
export const CurrentProposalState = ({ proposal }: { proposal: Proposal }) => {
|
||||
const { t } = useTranslation();
|
||||
let proposalStatus: ReactNode;
|
||||
let variant = 'tertiary' as ProposalInfoLabelVariant;
|
||||
|
||||
-272
@@ -1,272 +0,0 @@
|
||||
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
@@ -1,143 +0,0 @@
|
||||
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;
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { CurrentProposalStatus } from './current-proposal-status';
|
||||
+2
-3
@@ -6,11 +6,10 @@ import {
|
||||
KeyValueTableRow,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalChangeTableProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
proposal: Proposal;
|
||||
}
|
||||
|
||||
export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
|
||||
+17
-3
@@ -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 { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
jest.mock('@vegaprotocol/proposals', () => ({
|
||||
...jest.requireActual('@vegaprotocol/proposals'),
|
||||
@@ -36,7 +36,7 @@ jest.mock('@vegaprotocol/proposals', () => ({
|
||||
}));
|
||||
|
||||
const renderComponent = (
|
||||
proposal: ProposalQuery['proposal'],
|
||||
proposal: Proposal,
|
||||
isListItem = true,
|
||||
mocks: MockedResponse[] = [],
|
||||
voteState?: VoteState
|
||||
@@ -64,6 +64,7 @@ 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',
|
||||
@@ -102,6 +103,7 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Update market proposal', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New market id',
|
||||
@@ -130,6 +132,7 @@ 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',
|
||||
@@ -159,6 +162,7 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders New asset proposal - BuiltInAsset', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
@@ -184,6 +188,7 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Update network', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'Network parameter',
|
||||
@@ -213,6 +218,7 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Freeform proposal - short rationale', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
id: 'short',
|
||||
rationale: {
|
||||
@@ -234,6 +240,7 @@ 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: {
|
||||
@@ -259,6 +266,7 @@ 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: {
|
||||
@@ -280,6 +288,7 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders asset change proposal header', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
@@ -297,6 +306,7 @@ 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: {
|
||||
@@ -313,6 +323,7 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Enacted', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
terms: {
|
||||
@@ -325,6 +336,7 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Passed', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_PASSED,
|
||||
terms: {
|
||||
@@ -338,6 +350,7 @@ 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: {
|
||||
@@ -352,6 +365,7 @@ 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,8 +8,7 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { shorten } from '@vegaprotocol/utils';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { type ReactNode } from 'react';
|
||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
@@ -26,16 +25,17 @@ 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: ProposalQuery['proposal'];
|
||||
proposal: Proposal;
|
||||
isListItem?: boolean;
|
||||
voteState?: VoteState | null;
|
||||
}) => {
|
||||
@@ -53,7 +53,7 @@ export const ProposalHeader = ({
|
||||
|
||||
const titleContent = shorten(title ?? '', 100);
|
||||
|
||||
const getAsset = (proposal: ProposalQuery['proposal']) => {
|
||||
const getAsset = (proposal: Proposal) => {
|
||||
const terms = proposal?.terms;
|
||||
if (
|
||||
terms?.change.__typename === 'NewMarket' &&
|
||||
|
||||
-1
@@ -266,7 +266,6 @@ export const ProposalMarketData = ({
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>
|
||||
{t('Liquidity monitoring parameters')}
|
||||
</h2>
|
||||
|
||||
+2
-2
@@ -1,5 +1,4 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
@@ -14,9 +13,10 @@ 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: ProposalQuery['proposal'];
|
||||
proposal: Proposal | null;
|
||||
}
|
||||
|
||||
export const formatEndOfProgramTimestamp = (value: string) => {
|
||||
|
||||
+2
-3
@@ -1,6 +1,4 @@
|
||||
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,
|
||||
@@ -8,11 +6,12 @@ import {
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const ProposalCancelTransferDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
proposal: Proposal;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const details = useCancelTransferProposalDetails(proposal?.id);
|
||||
|
||||
+2
-3
@@ -1,6 +1,4 @@
|
||||
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';
|
||||
@@ -21,11 +19,12 @@ import {
|
||||
addDecimalsFormatNumberQuantum,
|
||||
formatDateWithLocalTimezone,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const ProposalTransferDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
proposal: Proposal;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
+2
-2
@@ -1,5 +1,4 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
@@ -12,6 +11,7 @@ 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: ProposalQuery['proposal'];
|
||||
proposal: Proposal | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+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: ProposalQuery['proposal'];
|
||||
proposal: Proposal | null;
|
||||
}
|
||||
|
||||
export const ProposalUpdateMarketState = ({
|
||||
|
||||
+2
-2
@@ -1,5 +1,4 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
@@ -12,9 +11,10 @@ import {
|
||||
} from '../proposal-referral-program-details';
|
||||
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalReferralProgramDetailsProps {
|
||||
proposal: ProposalQuery['proposal'];
|
||||
proposal: Proposal | null;
|
||||
}
|
||||
|
||||
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: ProposalQuery['proposal']) => {
|
||||
const renderComponent = (proposal: IProposal) => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<VegaWalletProvider config={vegaWalletConfig}>
|
||||
<Proposal
|
||||
restData={{}}
|
||||
proposal={proposal as ProposalQuery['proposal']}
|
||||
proposal={proposal}
|
||||
networkParams={mockNetworkParams}
|
||||
/>
|
||||
</VegaWalletProvider>
|
||||
|
||||
@@ -12,14 +12,13 @@ import { UserVote } from '../vote-details';
|
||||
import { ListAsset } from '../list-asset';
|
||||
import Routes from '../../../routes';
|
||||
import { ProposalMarketData } from '../proposal-market-data';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MarketInfo } from '@vegaprotocol/markets';
|
||||
import type { AssetQuery } from '@vegaprotocol/assets';
|
||||
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 {
|
||||
@@ -28,9 +27,10 @@ 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: ProposalQuery['proposal'];
|
||||
proposal: IProposal;
|
||||
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 { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
const renderComponent = (
|
||||
proposal: ProposalQuery['proposal'],
|
||||
proposal: Proposal,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
mocks: MockedResponse<any>[] = [networkParamsQueryMock]
|
||||
) =>
|
||||
|
||||
+3
-4
@@ -1,21 +1,20 @@
|
||||
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 { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const ProposalsListItemDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
proposal: 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 { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalsListItemProps {
|
||||
proposal?: ProposalQuery['proposal'] | null;
|
||||
proposal?: Proposal | null;
|
||||
}
|
||||
|
||||
export const ProposalsListItem = ({ proposal }: ProposalsListItemProps) => {
|
||||
|
||||
+3
-3
@@ -17,8 +17,8 @@ import {
|
||||
lastMonth,
|
||||
nextMonth,
|
||||
} from '../../test-helpers/mocks';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
const openProposalClosesNextMonth = generateProposal({
|
||||
id: 'proposal1',
|
||||
@@ -63,7 +63,7 @@ const closedProtocolUpgradeProposal = generateProtocolUpgradeProposal({
|
||||
});
|
||||
|
||||
const renderComponent = (
|
||||
proposals: ProposalQuery['proposal'][],
|
||||
proposals: 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: Array<ProposalQuery['proposal']>;
|
||||
proposals: Proposal[];
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
lastBlockHeight?: string;
|
||||
}
|
||||
|
||||
interface SortedProposalsProps {
|
||||
open: ProposalQuery['proposal'][];
|
||||
closed: ProposalQuery['proposal'][];
|
||||
open: Proposal[];
|
||||
closed: Proposal[];
|
||||
}
|
||||
|
||||
interface SortedProtocolUpgradeProposalsProps {
|
||||
@@ -31,7 +31,7 @@ interface SortedProtocolUpgradeProposalsProps {
|
||||
closed: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}
|
||||
|
||||
export const orderByDate = (arr: ProposalQuery['proposal'][]) =>
|
||||
export const orderByDate = (arr: Proposal[]) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[
|
||||
@@ -91,14 +91,10 @@ export const ProposalsList = ({
|
||||
);
|
||||
return {
|
||||
open:
|
||||
initialSorting.open.length > 0
|
||||
? orderByDate(initialSorting.open as ProposalQuery['proposal'][])
|
||||
: [],
|
||||
initialSorting.open.length > 0 ? orderByDate(initialSorting.open) : [],
|
||||
closed:
|
||||
initialSorting.closed.length > 0
|
||||
? orderByDate(
|
||||
initialSorting.closed as ProposalQuery['proposal'][]
|
||||
).reverse()
|
||||
? orderByDate(initialSorting.closed).reverse()
|
||||
: [],
|
||||
};
|
||||
}, [proposals]);
|
||||
@@ -125,9 +121,7 @@ export const ProposalsList = ({
|
||||
};
|
||||
}, [protocolUpgradeProposals, lastBlockHeight]);
|
||||
|
||||
const filterPredicate = (
|
||||
p: ProposalFieldsFragment | ProposalQuery['proposal']
|
||||
) =>
|
||||
const filterPredicate = (p: ProposalFieldsFragment | 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 { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
const rejectedProposalClosesNextWeek = generateProposal({
|
||||
id: 'rejected1',
|
||||
@@ -35,7 +35,7 @@ const rejectedProposalClosedLastMonth = generateProposal({
|
||||
},
|
||||
});
|
||||
|
||||
const renderComponent = (proposals: ProposalQuery['proposal'][]) => (
|
||||
const renderComponent = (proposals: 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 { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: ProposalQuery['proposal'][];
|
||||
proposals: Proposal[];
|
||||
}
|
||||
|
||||
export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [filterString, setFilterString] = useState('');
|
||||
|
||||
const filterPredicate = (p: ProposalQuery['proposal']) =>
|
||||
const filterPredicate = (p: Proposal) =>
|
||||
p?.id?.includes(filterString) ||
|
||||
p?.party?.id?.toString().includes(filterString);
|
||||
|
||||
|
||||
+4
-4
@@ -9,8 +9,7 @@ import {
|
||||
nextWeek,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { CompactVotes, VoteBreakdown } from './vote-breakdown';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import {
|
||||
generateNoVotes,
|
||||
generateProposal,
|
||||
@@ -18,7 +17,8 @@ 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 AppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
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: ProposalQuery['proposal'],
|
||||
proposal: Proposal,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
mocks: MockedResponse<any>[] = [networkParamsQueryMock]
|
||||
) =>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -5,10 +6,8 @@ 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
|
||||
@@ -20,7 +19,7 @@ export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||
);
|
||||
|
||||
interface VoteBreakdownProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
proposal: Proposal;
|
||||
}
|
||||
|
||||
interface VoteProgressProps {
|
||||
|
||||
@@ -5,14 +5,13 @@ 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, 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';
|
||||
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';
|
||||
|
||||
interface UserVoteProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
proposal: Proposal;
|
||||
minVoterBalance: string | null | undefined;
|
||||
spamProtectionMinTokens: string | null | undefined;
|
||||
transaction: VegaTxState | null;
|
||||
|
||||
@@ -3,13 +3,12 @@ import {
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import type { ProposalFieldsFragment } from '../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
import { type Proposal } from '../types';
|
||||
|
||||
export const useProposalNetworkParams = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
proposal: Proposal;
|
||||
}) => {
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.governance_proposal_updateMarket_requiredMajority,
|
||||
|
||||
@@ -2,15 +2,10 @@ 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: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
export const useVoteInformation = ({ proposal }: { proposal: Proposal }) => {
|
||||
const {
|
||||
appState: { totalSupply, decimals },
|
||||
} = useAppState();
|
||||
|
||||
@@ -86,228 +86,65 @@ query Proposal(
|
||||
$includeUpdateReferralProgram: Boolean!
|
||||
) {
|
||||
proposal(id: $proposalId) {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
party {
|
||||
... on Proposal {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 PerpetualProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
... on SimpleRiskModel {
|
||||
params {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
positionDecimalPlaces
|
||||
linearSlippageFactor
|
||||
}
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
updateMarketConfiguration {
|
||||
instrument {
|
||||
name
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on FutureProduct {
|
||||
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
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
quoteName
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
}
|
||||
... on UpdatePerpetualProduct {
|
||||
quoteName
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
@@ -348,14 +185,19 @@ query Proposal(
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
settlementScheduleProperty
|
||||
}
|
||||
... on PerpetualProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
}
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
metadata
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
@@ -369,71 +211,231 @@ query Proposal(
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
riskParameters {
|
||||
... on UpdateMarketSimpleRiskModel {
|
||||
simple {
|
||||
factorLong
|
||||
factorShort
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on UpdateMarketLogNormalRiskModel {
|
||||
logNormal {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
r
|
||||
sigma
|
||||
mu
|
||||
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 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+226
-224
File diff suppressed because one or more lines are too long
@@ -17,6 +17,7 @@ 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);
|
||||
@@ -67,6 +68,8 @@ export const ProposalContainer = () => {
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as IProposal;
|
||||
|
||||
const successor = useSuccessorMarketProposalDetails(params.proposalId);
|
||||
|
||||
const isSuccessor = !!successor?.parentMarketId || !!successor.code;
|
||||
@@ -79,12 +82,12 @@ export const ProposalContainer = () => {
|
||||
},
|
||||
} = useFetch(
|
||||
`${ENV.rest}governance?proposalId=${
|
||||
data?.proposal?.terms.change.__typename === 'UpdateMarket' &&
|
||||
data?.proposal.terms.change.marketId
|
||||
proposal?.terms.change.__typename === 'UpdateMarket' &&
|
||||
proposal.terms.change.marketId
|
||||
}`,
|
||||
undefined,
|
||||
true,
|
||||
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -97,7 +100,7 @@ export const ProposalContainer = () => {
|
||||
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
|
||||
undefined,
|
||||
true,
|
||||
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -108,8 +111,8 @@ export const ProposalContainer = () => {
|
||||
dataProvider: marketInfoProvider,
|
||||
skipUpdates: true,
|
||||
variables: {
|
||||
marketId: data?.proposal?.id || '',
|
||||
skip: !data?.proposal?.id,
|
||||
marketId: proposal?.id || '',
|
||||
skip: !proposal?.id,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -148,23 +151,22 @@ export const ProposalContainer = () => {
|
||||
fetchPolicy: 'network-only',
|
||||
variables: {
|
||||
assetId:
|
||||
(data?.proposal?.terms.change.__typename === 'NewAsset' &&
|
||||
data?.proposal?.id) ||
|
||||
(data?.proposal?.terms.change.__typename === 'UpdateAsset' &&
|
||||
data.proposal.terms.change.assetId) ||
|
||||
(proposal?.terms.change.__typename === 'NewAsset' && proposal?.id) ||
|
||||
(proposal?.terms.change.__typename === 'UpdateAsset' &&
|
||||
proposal.terms.change.assetId) ||
|
||||
'',
|
||||
},
|
||||
skip: !['NewAsset', 'UpdateAsset'].includes(
|
||||
data?.proposal?.terms?.change?.__typename || ''
|
||||
proposal?.terms?.change?.__typename || ''
|
||||
),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
previouslyEnactedMarketProposalsRestData &&
|
||||
data?.proposal?.terms.change.__typename === 'UpdateMarket'
|
||||
proposal?.terms.change.__typename === 'UpdateMarket'
|
||||
) {
|
||||
const change = data?.proposal?.terms?.change as { marketId: string };
|
||||
const change = proposal?.terms?.change as { marketId: string };
|
||||
|
||||
const filteredProposals =
|
||||
// @ts-ignore rest data is not typed
|
||||
@@ -188,8 +190,8 @@ export const ProposalContainer = () => {
|
||||
}, [
|
||||
previouslyEnactedMarketProposalsRestData,
|
||||
params.proposalId,
|
||||
data?.proposal?.terms.change.__typename,
|
||||
data?.proposal?.terms.change,
|
||||
proposal?.terms.change.__typename,
|
||||
proposal?.terms.change,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -242,7 +244,7 @@ export const ProposalContainer = () => {
|
||||
>
|
||||
{data?.proposal ? (
|
||||
<Proposal
|
||||
proposal={data.proposal}
|
||||
proposal={proposal}
|
||||
networkParams={networkParams}
|
||||
restData={restData}
|
||||
marketData={marketData}
|
||||
|
||||
@@ -8,6 +8,7 @@ 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> = {}
|
||||
@@ -43,8 +44,8 @@ export function generateProtocolUpgradeProposal(
|
||||
}
|
||||
|
||||
export function generateProposal(
|
||||
override: PartialDeep<ProposalQuery['proposal']> = {}
|
||||
): ProposalQuery['proposal'] {
|
||||
override: PartialDeep<Proposal> = {}
|
||||
): Proposal {
|
||||
const defaultProposal: ProposalQuery['proposal'] = {
|
||||
__typename: 'Proposal',
|
||||
id: faker.datatype.uuid(),
|
||||
@@ -92,15 +93,16 @@ export function generateProposal(
|
||||
},
|
||||
};
|
||||
|
||||
return mergeWith<
|
||||
ProposalQuery['proposal'],
|
||||
PartialDeep<ProposalQuery['proposal']>
|
||||
>(defaultProposal, override, (objValue, srcValue) => {
|
||||
if (!isArray(objValue)) {
|
||||
return;
|
||||
return mergeWith<Proposal, PartialDeep<Proposal>>(
|
||||
defaultProposal,
|
||||
override,
|
||||
(objValue, srcValue) => {
|
||||
if (!isArray(objValue)) {
|
||||
return;
|
||||
}
|
||||
return srcValue;
|
||||
}
|
||||
return srcValue;
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
type Vote = Pick<Schema.Vote, '__typename' | 'value' | 'party' | 'datetime'>;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
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,6 +21,7 @@ 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,6 +21,7 @@ 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,6 +20,7 @@ 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,6 +21,7 @@ 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
|
||||
|
||||
@@ -21,6 +21,7 @@ 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,6 +21,7 @@ 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,6 +22,7 @@ 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,6 +22,7 @@ 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,6 +11,7 @@ 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
|
||||
@@ -36,7 +37,7 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
|
||||
const pennantChart = (
|
||||
<CandlesChartContainer
|
||||
marketId={marketId}
|
||||
interval={toPennantInterval(interval)}
|
||||
interval={toPennantInterval(interval as SupportedInterval)}
|
||||
chartType={chartType}
|
||||
overlays={overlays}
|
||||
studies={studies}
|
||||
@@ -63,7 +64,7 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
|
||||
libraryPath={CHARTING_LIBRARY_PATH}
|
||||
libraryHash={CHARTING_LIBRARY_HASH}
|
||||
marketId={marketId}
|
||||
interval={toTradingViewResolution(interval)}
|
||||
interval={toTradingViewResolution(interval as SupportedInterval)}
|
||||
onIntervalChange={(newInterval) => {
|
||||
setInterval(fromTradingViewResolution(newInterval));
|
||||
}}
|
||||
@@ -83,7 +84,11 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const toTradingViewResolution = (interval: Interval) => {
|
||||
const toTradingViewResolution = (interval: SupportedInterval) => {
|
||||
if (!SUPPORTED_INTERVALS.includes(interval)) {
|
||||
throw new Error(`interval ${interval} is not supported`);
|
||||
}
|
||||
|
||||
const resolution = TRADINGVIEW_INTERVAL_MAP[interval];
|
||||
|
||||
if (!resolution) {
|
||||
@@ -107,7 +112,11 @@ const fromTradingViewResolution = (resolution: string) => {
|
||||
return interval as Interval;
|
||||
};
|
||||
|
||||
const toPennantInterval = (interval: Interval) => {
|
||||
const toPennantInterval = (interval: SupportedInterval) => {
|
||||
if (!SUPPORTED_INTERVALS.includes(interval)) {
|
||||
throw new Error(`interval ${interval} is not supported`);
|
||||
}
|
||||
|
||||
const pennantInterval = PENNANT_INTERVAL_MAP[interval];
|
||||
|
||||
if (!pennantInterval) {
|
||||
|
||||
@@ -18,21 +18,13 @@ import {
|
||||
TradingDropdownTrigger,
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
import { type 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';
|
||||
|
||||
const INTERVALS = [
|
||||
Interval.INTERVAL_I1M,
|
||||
Interval.INTERVAL_I5M,
|
||||
Interval.INTERVAL_I15M,
|
||||
Interval.INTERVAL_I1H,
|
||||
Interval.INTERVAL_I6H,
|
||||
Interval.INTERVAL_I1D,
|
||||
];
|
||||
import { SUPPORTED_INTERVALS } from './constants';
|
||||
|
||||
const chartTypeIcon = new Map<ChartType, IconName>([
|
||||
[ChartType.AREA, IconNames.TIMELINE_AREA_CHART],
|
||||
@@ -94,7 +86,7 @@ export const ChartMenu = () => {
|
||||
setInterval(value as Interval);
|
||||
}}
|
||||
>
|
||||
{INTERVALS.map((timeInterval) => (
|
||||
{SUPPORTED_INTERVALS.map((timeInterval) => (
|
||||
<TradingDropdownRadioItem
|
||||
key={timeInterval}
|
||||
inset
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
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;
|
||||
@@ -1,3 +1,3 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
||||
VEGA_VERSION=v0.74.0-preview.2
|
||||
VEGA_VERSION=v0.74.0-preview.6
|
||||
LOCAL_SERVER=false
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
|
||||
VEGA_VERSION=v0.74.0-preview.2
|
||||
VEGA_VERSION=v0.74.0-preview.6
|
||||
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 = "2aed8c94b25d8fa2e376d3b63ca1f9193d28cdfd"
|
||||
resolved_reference = "026976549c21e59f6f9c48f06ab15a210c5a5bf3"
|
||||
|
||||
[[package]]
|
||||
name = "websocket-client"
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
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,27 +11,35 @@ 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}")
|
||||
@@ -42,5 +50,6 @@ 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,15 +10,18 @@ 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("/")
|
||||
@@ -30,7 +33,8 @@ class TestGetStarted:
|
||||
expect(page.locator(".list-none")).to_contain_text(
|
||||
"1.Connect2.Deposit funds3.Open a position"
|
||||
)
|
||||
DEFAULT_WALLET_NAME = "MarketSim" # This is the default wallet name within VegaServiceNull and CANNOT be changed
|
||||
# This is the default wallet name within VegaServiceNull and CANNOT be changed
|
||||
DEFAULT_WALLET_NAME = "MarketSim"
|
||||
|
||||
# Calling get_keypairs will internally call _load_tokens for the given wallet
|
||||
keypairs = vega.wallet.get_keypairs(DEFAULT_WALLET_NAME)
|
||||
@@ -137,7 +141,8 @@ 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,16 +36,19 @@ 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%"],
|
||||
@@ -54,10 +57,11 @@ 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"],
|
||||
@@ -66,10 +70,11 @@ 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"],
|
||||
@@ -80,17 +85,32 @@ def test_market_info_market_volume(page: Page):
|
||||
]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
@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("Insurance pool").click()
|
||||
fields = [["Balance", "0.00 tDAI"]]
|
||||
|
||||
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)
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
|
||||
def test_market_info_liquidation(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"]]
|
||||
validate_info_section(page, fields)
|
||||
|
||||
|
||||
@pytest.mark.skip("core issue #5681")
|
||||
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 = [
|
||||
@@ -106,7 +126,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()
|
||||
@@ -121,7 +141,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()
|
||||
@@ -135,10 +155,11 @@ 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 = [
|
||||
@@ -155,7 +176,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()
|
||||
@@ -164,7 +185,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()
|
||||
@@ -175,7 +196,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(
|
||||
@@ -183,17 +204,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"],
|
||||
@@ -204,7 +225,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(
|
||||
@@ -213,27 +234,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
|
||||
@@ -246,7 +267,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(
|
||||
@@ -259,19 +280,22 @@ 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")
|
||||
|
||||
@@ -280,13 +304,14 @@ 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,13 +29,15 @@ 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):
|
||||
@@ -91,7 +93,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": "1.0x",
|
||||
"leverage": "Cross1.0x",
|
||||
"liquidation": "0.00",
|
||||
"realised_pnl": "0.00",
|
||||
"unrealised_pnl": "0.00",
|
||||
@@ -104,7 +106,8 @@ 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"]
|
||||
)
|
||||
@@ -125,10 +128,11 @@ def test_limit_order_trade_open_position(continuous_market, page: Page):
|
||||
position["leverage"]
|
||||
)
|
||||
|
||||
liquidation = table.locator("[col-id='liquidationPrice']")
|
||||
expect(liquidation.get_by_test_id("liquidation-price")).to_have_text(
|
||||
position["liquidation"]
|
||||
)
|
||||
# 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"]
|
||||
# )
|
||||
|
||||
realisedPNL = table.locator("[col-id='realisedPNL']")
|
||||
expect(realisedPNL).to_have_text(position["realised_pnl"])
|
||||
|
||||
@@ -28,8 +28,9 @@ def test_usage_breakdown(continuous_market, page: Page):
|
||||
usage_breakdown = page.get_by_test_id("usage-breakdown")
|
||||
|
||||
# Verify headers
|
||||
headers = ["Market", "Account type", "Balance", "Margin health"]
|
||||
ag_headers = usage_breakdown.locator(".ag-header-cell-text").element_handles()
|
||||
headers = ["Market", "Account type", "Balance"]
|
||||
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]
|
||||
@@ -38,30 +39,10 @@ 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,7 +46,9 @@ def test_filtered_cards(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
side="SIDE_BUY",
|
||||
volume=1,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.goto("/#/rewards")
|
||||
|
||||
vega.update_market_state(
|
||||
market_id=continuous_market,
|
||||
@@ -55,8 +57,9 @@ def test_filtered_cards(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
forward_time_to_enactment=True,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
page.goto("/#/rewards")
|
||||
expect(page.locator(".from-vega-cdark-400")).to_be_visible()
|
||||
|
||||
page.reload()
|
||||
expect(page.locator(".from-vega-cdark-400")).to_be_visible(timeout=15000)
|
||||
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,12 +20,14 @@ 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)
|
||||
|
||||
@@ -45,7 +47,6 @@ 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()
|
||||
@@ -78,6 +79,7 @@ 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):
|
||||
|
||||
@@ -91,9 +93,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"
|
||||
@@ -108,7 +110,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()
|
||||
@@ -119,7 +121,8 @@ 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
|
||||
@@ -137,6 +140,7 @@ def propose_successor(
|
||||
)
|
||||
return market_id
|
||||
|
||||
|
||||
def provide_successor_liquidity(
|
||||
vega: VegaServiceNull, market_id
|
||||
):
|
||||
|
||||
@@ -3,6 +3,9 @@ fragment MarginFields on MarginLevels {
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
asset {
|
||||
id
|
||||
}
|
||||
@@ -33,6 +36,9 @@ subscription MarginsSubscription($partyId: ID!) {
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
|
||||
+9
-3
@@ -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, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } };
|
||||
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 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, 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, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: 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, timestamp: any } };
|
||||
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 const MarginFieldsFragmentDoc = gql`
|
||||
fragment MarginFields on MarginLevels {
|
||||
@@ -25,6 +25,9 @@ export const MarginFieldsFragmentDoc = gql`
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
asset {
|
||||
id
|
||||
}
|
||||
@@ -85,6 +88,9 @@ export const MarginsSubscriptionDocument = gql`
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ 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,14 +4,6 @@ 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,
|
||||
@@ -49,10 +41,10 @@ describe('BreakdownTable', () => {
|
||||
render(<BreakdownTable data={singleRowData} />);
|
||||
});
|
||||
const headers = await screen.findAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(4);
|
||||
expect(headers).toHaveLength(3);
|
||||
expect(
|
||||
headers.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim())
|
||||
).toEqual(['Market', 'Account type', 'Balance', 'Margin health']);
|
||||
).toEqual(['Market', 'Account type', 'Balance']);
|
||||
});
|
||||
|
||||
it('should apply correct formatting', async () => {
|
||||
@@ -70,24 +62,6 @@ 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,14 +16,13 @@ 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 {
|
||||
@@ -39,7 +38,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
width: 90,
|
||||
maxWidth: 150,
|
||||
pinned: true,
|
||||
sort: 'desc',
|
||||
cellRenderer: ({
|
||||
@@ -111,23 +110,6 @@ 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,14 +20,20 @@ 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,
|
||||
maintenanceLevel: delta.maintenanceLevel,
|
||||
searchLevel: delta.searchLevel,
|
||||
initialLevel: delta.initialLevel,
|
||||
collateralReleaseLevel: delta.collateralReleaseLevel,
|
||||
...deltaData,
|
||||
};
|
||||
} else {
|
||||
draft.unshift({
|
||||
@@ -36,10 +42,7 @@ const update = (
|
||||
__typename: 'Market',
|
||||
id: delta.marketId,
|
||||
},
|
||||
maintenanceLevel: delta.maintenanceLevel,
|
||||
searchLevel: delta.searchLevel,
|
||||
initialLevel: delta.initialLevel,
|
||||
collateralReleaseLevel: delta.collateralReleaseLevel,
|
||||
...deltaData,
|
||||
asset: {
|
||||
__typename: 'Asset',
|
||||
id: delta.asset,
|
||||
|
||||
@@ -5,6 +5,7 @@ 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',
|
||||
@@ -18,6 +19,9 @@ const margins: MarginFieldsFragment = {
|
||||
initialLevel: '800',
|
||||
searchLevel: '600',
|
||||
maintenanceLevel: '400',
|
||||
marginFactor: '',
|
||||
marginMode: MarginMode.MARGIN_MODE_CROSS_MARGIN,
|
||||
orderMarginLevel: '',
|
||||
market: {
|
||||
id: 'marketId',
|
||||
},
|
||||
|
||||
@@ -29,6 +29,7 @@ export const assetsProvider = makeDataProvider<
|
||||
>({
|
||||
query: AssetsDocument,
|
||||
getData,
|
||||
errorPolicy: 'all',
|
||||
});
|
||||
|
||||
export const assetsMapProvider = makeDerivedDataProvider<
|
||||
|
||||
@@ -21,106 +21,107 @@ 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';
|
||||
|
||||
@@ -22,11 +22,9 @@ import {
|
||||
type QueryOptions,
|
||||
type ApolloClient,
|
||||
} from '@apollo/client';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import type { GraphQLErrors } from '@apollo/client/errors';
|
||||
import { type ApolloError } from '@apollo/client';
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { type Subscription, type Observable } from 'zen-observable-ts';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
type Item = {
|
||||
cursor: string;
|
||||
@@ -117,24 +115,6 @@ 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,
|
||||
@@ -404,34 +384,6 @@ 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,7 +9,6 @@ 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';
|
||||
@@ -161,7 +160,7 @@ interface DataProviderParams<
|
||||
resetDelay?: number;
|
||||
pollInterval?: number;
|
||||
additionalContext?: Record<string, unknown>;
|
||||
errorPolicyGuard?: (graphqlErrors: GraphQLErrors) => boolean;
|
||||
errorPolicy?: ErrorPolicy;
|
||||
getQueryVariables?: (variables: Variables) => QueryVariables;
|
||||
getSubscriptionVariables?: (
|
||||
variables: Variables
|
||||
@@ -176,7 +175,7 @@ interface DataProviderParams<
|
||||
* @param fetchPolicy
|
||||
* @param resetDelay
|
||||
* @param additionalContext add property to the context of the query, ie. 'isEnlargedTimeout'
|
||||
* @param errorPolicyGuard indicate which gql errors can be tolerate
|
||||
* @param errorPolicy Apollos error policy, will be used when querying
|
||||
* @returns subscribe function
|
||||
*/
|
||||
function makeDataProviderInternal<
|
||||
@@ -197,7 +196,7 @@ function makeDataProviderInternal<
|
||||
fetchPolicy,
|
||||
resetDelay,
|
||||
additionalContext,
|
||||
errorPolicyGuard,
|
||||
errorPolicy = 'none',
|
||||
getQueryVariables,
|
||||
getSubscriptionVariables,
|
||||
pollInterval,
|
||||
@@ -331,20 +330,10 @@ function makeDataProviderInternal<
|
||||
const callQuery = (
|
||||
pagination?: Pagination,
|
||||
policy?: ErrorPolicy
|
||||
): 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;
|
||||
}
|
||||
});
|
||||
): Promise<ApolloQueryResult<QueryData>> => {
|
||||
const options = getQueryOptions(pagination, policy);
|
||||
return client.query<QueryData>(options);
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
if (!pagination) {
|
||||
@@ -364,7 +353,7 @@ function makeDataProviderInternal<
|
||||
}
|
||||
}
|
||||
|
||||
const res = await callQuery(paginationVariables);
|
||||
const res = await callQuery(paginationVariables, errorPolicy);
|
||||
|
||||
const insertionData = getData(res.data, variables);
|
||||
const insertionPageInfo = pagination.getPageInfo(res.data);
|
||||
@@ -417,12 +406,14 @@ function makeDataProviderInternal<
|
||||
const paginationVariables = pagination
|
||||
? { first: pagination.first }
|
||||
: undefined;
|
||||
|
||||
if (pollInterval) {
|
||||
callWatchQuery();
|
||||
callWatchQuery(paginationVariables, errorPolicy);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
onNext(await callQuery(paginationVariables));
|
||||
onNext(await callQuery(paginationVariables, errorPolicy));
|
||||
} catch (e) {
|
||||
onError(e as Error);
|
||||
} finally {
|
||||
|
||||
@@ -27,10 +27,3 @@ 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,6 +13,7 @@ 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;
|
||||
@@ -51,21 +52,31 @@ export const DealTicketContainer = ({
|
||||
reload={reload}
|
||||
>
|
||||
{market && marketData ? (
|
||||
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.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 })}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<p>{t('Could not load market')}</p>
|
||||
)}
|
||||
|
||||
@@ -263,8 +263,11 @@ export const DealTicket = ({
|
||||
marketId: market.id,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable:
|
||||
marginAccountBalance || generalAccountBalance ? balance : undefined,
|
||||
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
|
||||
skip:
|
||||
!normalizedOrder ||
|
||||
(normalizedOrder.type !== Schema.OrderType.TYPE_MARKET &&
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
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,7 +1,11 @@
|
||||
import { useMemo } from 'react';
|
||||
import { parseISO, isValid, isAfter } from 'date-fns';
|
||||
import classNames from 'classnames';
|
||||
import { useProposalOfMarketQuery } from '@vegaprotocol/proposals';
|
||||
import {
|
||||
useProposalOfMarketQuery,
|
||||
type ProposalOfMarketQuery,
|
||||
type SingleProposal,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
@@ -36,12 +40,15 @@ 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(
|
||||
proposalData?.proposal?.terms.enactmentDatetime
|
||||
);
|
||||
const enactmentDate = parseISO(proposal?.terms.enactmentDatetime);
|
||||
|
||||
const compiledGrid =
|
||||
!skipGrid && compileGridData(t, market, marketData, onSelect);
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
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,7 +13,12 @@ export const usePositionEstimate = ({
|
||||
marketId,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable,
|
||||
generalAccountBalance,
|
||||
marginAccountBalance,
|
||||
orderMarginAccountBalance,
|
||||
averageEntryPrice,
|
||||
marginMode,
|
||||
marginFactor,
|
||||
skip,
|
||||
}: PositionEstimateProps) => {
|
||||
const [estimates, setEstimates] = useState<EstimatePositionQuery | undefined>(
|
||||
@@ -24,7 +29,12 @@ export const usePositionEstimate = ({
|
||||
marketId,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable,
|
||||
generalAccountBalance,
|
||||
marginAccountBalance,
|
||||
orderMarginAccountBalance,
|
||||
averageEntryPrice,
|
||||
marginMode,
|
||||
marginFactor,
|
||||
},
|
||||
skip,
|
||||
fetchPolicy: 'no-cache',
|
||||
|
||||
@@ -323,6 +323,9 @@ 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',
|
||||
|
||||
@@ -19,6 +19,7 @@ export type FeatureFlags = z.infer<typeof featureFlagsSchema>;
|
||||
export type CosmicElevatorFlags = Pick<
|
||||
FeatureFlags,
|
||||
| 'ICEBERG_ORDERS'
|
||||
| 'ISOLATED_MARGIN'
|
||||
| 'STOP_ORDERS'
|
||||
| 'SUCCESSOR_MARKETS'
|
||||
| 'PRODUCT_PERPETUALS'
|
||||
|
||||
@@ -76,6 +76,7 @@ export const envSchema = z
|
||||
const COSMIC_ELEVATOR_FLAGS = {
|
||||
SUCCESSOR_MARKETS: z.optional(z.boolean()),
|
||||
STOP_ORDERS: z.optional(z.boolean()),
|
||||
ISOLATED_MARGIN: z.optional(z.boolean()),
|
||||
ICEBERG_ORDERS: z.optional(z.boolean()),
|
||||
PRODUCT_PERPETUALS: z.optional(z.boolean()),
|
||||
METAMASK_SNAPS: z.optional(z.boolean()),
|
||||
|
||||
@@ -8,13 +8,17 @@
|
||||
"A release candidate for the staging environment": "A release candidate for the staging environment",
|
||||
"above": "above",
|
||||
"Advanced": "Advanced",
|
||||
"All available funds in your general account will be used to finance your margin if the market moves against you.": "All available funds in your general account will be used to finance your margin if the market moves against you.",
|
||||
"An estimate of the most you would be expected to pay in fees, in the market's settlement asset {{assetSymbol}}. Fees estimated are \"taker\" fees and will only be payable if the order trades aggressively. Rebate equal to the maker portion will be paid to the trader if the order trades passively.": "An estimate of the most you would be expected to pay in fees, in the market's settlement asset {{assetSymbol}}. Fees estimated are \"taker\" fees and will only be payable if the order trades aggressively. Rebate equal to the maker portion will be paid to the trader if the order trades passively.",
|
||||
"Any orders placed now will not trade until the auction ends": "Any orders placed now will not trade until the auction ends",
|
||||
"below": "below",
|
||||
"Cancel": "Cancel",
|
||||
"Closed": "Closed",
|
||||
"Closing on {{time}}": "Closing on {{time}}",
|
||||
"Confirm": "Confirm",
|
||||
"Could not load market": "Could not load market",
|
||||
"Cross": "Cross",
|
||||
"Cross margin": "Cross margin",
|
||||
"Current margin allocation": "Current margin allocation",
|
||||
"Custom": "Custom",
|
||||
"Deduction from collateral": "Deduction from collateral",
|
||||
@@ -35,6 +39,9 @@
|
||||
"Iceberg": "Iceberg",
|
||||
"ICEBERG_TOOLTIP": "Trade only a fraction of the order size at once. After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away. For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each. Note that the full volume of the order is not hidden and is still reflected in the order book.",
|
||||
"Infrastructure fee": "Infrastructure fee",
|
||||
"Isolated {{leverage}}x": "Isolated {{leverage}}x",
|
||||
"Isolated margin": "Isolated margin",
|
||||
"Leverage": "Leverage",
|
||||
"Limit": "Limit",
|
||||
"Liquidation": "Liquidation",
|
||||
"LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT": "This is an approximation for the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.",
|
||||
@@ -59,6 +66,7 @@
|
||||
"OCO": "OCO",
|
||||
"One cancels another": "One cancels another",
|
||||
"Only limit orders are permitted when market is in auction": "Only limit orders are permitted when market is in auction",
|
||||
"Only your allocated margin will be used to fund this position, and if the maintenance margin is breached you will be closed out.": "Only your allocated margin will be used to fund this position, and if the maintenance margin is breached you will be closed out.",
|
||||
"Peak size": "Peak size",
|
||||
"Peak size cannot be greater than the size ({{size}})": "Peak size cannot be greater than the size ({{size}})",
|
||||
"Peak size cannot be lower than {{stepSize}}": "Peak size cannot be lower than {{stepSize}}",
|
||||
@@ -75,6 +83,7 @@
|
||||
"Public testnet run by the Vega team, often used for incentives": "Public testnet run by the Vega team, often used for incentives",
|
||||
"Reduce only": "Reduce only",
|
||||
"Referral discount": "Referral discount",
|
||||
"Set the leverage you want below. The maximum leverage you can take is determined by the risk model of the market.": "Set the leverage you want below. The maximum leverage you can take is determined by the risk model of the market.",
|
||||
"Short": "Short",
|
||||
"Size": "Size",
|
||||
"Size cannot be lower than {{sizeStep}}": "Size cannot be lower than {{sizeStep}}",
|
||||
@@ -128,6 +137,8 @@
|
||||
"VALIDATOR_TESTNET": "VALIDATOR_TESTNET",
|
||||
"Volume discount": "Volume discount",
|
||||
"When the order trades and its size falls below this threshold, it will be reset to the peak size and moved to the back of the priority order. Must be less than or equal to peak size, and greater than 0.": "When the order trades and its size falls below this threshold, it will be reset to the peak size and moved to the back of the priority order. Must be less than or equal to peak size, and greater than 0.",
|
||||
"You are setting this market to cross-margin mode.": "You are setting this market to cross-margin mode.",
|
||||
"You are setting this market to isolated margin mode.": "You are setting this market to isolated margin mode.",
|
||||
"You have only {{amount}}.": "You have only {{amount}}.",
|
||||
"You may not have enough margin available to open this position.": "You may not have enough margin available to open this position.",
|
||||
"You need {{symbol}} in your wallet to trade in this market.": "You need {{symbol}} in your wallet to trade in this market.",
|
||||
@@ -137,5 +148,6 @@
|
||||
"You need to connect your own wallet to start trading on this market": "You need to connect your own wallet to start trading on this market",
|
||||
"You need to provide a minimum visible size": "You need to provide a minimum visible size",
|
||||
"You need to provide a peak size": "You need to provide a peak size",
|
||||
"You need to provide a size": "You need to provide a size"
|
||||
"You need to provide a size": "You need to provide a size",
|
||||
"Your max leverage on each position will be determined by the risk model of the market.": "Your max leverage on each position will be determined by the risk model of the market."
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"Key": "Key",
|
||||
"Key details": "Key details",
|
||||
"Liquidity": "Liquidity",
|
||||
"Liquidations": "Liquidations",
|
||||
"Liquidity monitoring parameters": "Liquidity monitoring parameters",
|
||||
"Liquidity portion of the fee is paid to liquidity providers, and is transferred to the liquidity fee pool for the market.": "Liquidity portion of the fee is paid to liquidity providers, and is transferred to the liquidity fee pool for the market.",
|
||||
"Liquidity price range": "Liquidity price range",
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
{
|
||||
"Best case": "Best case",
|
||||
"Cross": "Cross",
|
||||
"Close position": "Close position",
|
||||
"Entry / Mark": "Entry / Mark",
|
||||
"General account: {{balance}}": "General account: {{balance}}",
|
||||
"Isolated": "Isolated",
|
||||
"Lifetime loss socialisation deductions: {{losses}}": "Lifetime loss socialisation deductions: {{losses}}",
|
||||
"Liquidation: {{maintenanceLevel}}": "Liquidation: {{maintenanceLevel}}",
|
||||
"Maintained by network": "Maintained by network",
|
||||
"Margin / Leverage": "Margin / Leverage",
|
||||
"Margin: {{balance}}": "Margin: {{balance}}",
|
||||
"Market": "Market",
|
||||
"Order: {{balance}}": "Order: {{balance}}",
|
||||
"No positions": "No positions",
|
||||
"Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.": "Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.",
|
||||
"Read more about loss socialisation": "Read more about loss socialisation",
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"Go to your Ethereum wallet and connect to the network {{networkName}}": "Go to your Ethereum wallet and connect to the network {{networkName}}",
|
||||
"If the network is reset or has an outage, records of your withdrawal may be lost. It is recommended that you save these details in a safe place so you can still complete your withdrawal.": "If the network is reset or has an outage, records of your withdrawal may be lost. It is recommended that you save these details in a safe place so you can still complete your withdrawal.",
|
||||
"Invalid asset source: {{source}}": "Invalid asset source: {{source}}",
|
||||
"Isolated margin mode, leverage: {{leverage}}x": "Isolated margin mode, leverage: {{leverage}}x",
|
||||
"Loading": "Loading",
|
||||
"MetaMask": "MetaMask",
|
||||
"MetaMask, Brave or other injected web wallet": "MetaMask, Brave or other injected web wallet",
|
||||
@@ -79,6 +80,7 @@
|
||||
"Transfer": "Transfer",
|
||||
"Transfer complete": "Transfer complete",
|
||||
"Unknown": "Unknown",
|
||||
"Update margin mode": "Update margin mode",
|
||||
"Vega confirmation": "Vega confirmation",
|
||||
"Vega is confirming your transaction...": "Vega is confirming your transaction...",
|
||||
"Verifying withdrawal approval": "Verifying withdrawal approval",
|
||||
|
||||
@@ -198,6 +198,12 @@ query MarketInfo($marketId: ID!) {
|
||||
performanceHysteresisEpochs
|
||||
slaCompetitionFactor
|
||||
}
|
||||
liquidationStrategy {
|
||||
disposalTimeStep
|
||||
disposalFraction
|
||||
fullDisposalSize
|
||||
maxFractionConsumed
|
||||
}
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
id
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -27,6 +27,7 @@ import {
|
||||
InstrumentInfoPanel,
|
||||
InsurancePoolInfoPanel,
|
||||
KeyDetailsInfoPanel,
|
||||
LiquidationStrategyInfoPanel,
|
||||
LiquidityInfoPanel,
|
||||
LiquidityMonitoringParametersInfoPanel,
|
||||
LiquidityPriceRangeInfoPanel,
|
||||
@@ -151,7 +152,7 @@ export const MarketInfoAccordion = ({
|
||||
<AccordionItem
|
||||
key={id}
|
||||
itemId={id}
|
||||
title={t('Insurance pool')}
|
||||
title={t('Liquidations')}
|
||||
content={
|
||||
<InsurancePoolInfoPanel market={market} account={a} />
|
||||
}
|
||||
@@ -269,6 +270,11 @@ export const MarketInfoAccordion = ({
|
||||
);
|
||||
}
|
||||
)}
|
||||
<AccordionItem
|
||||
itemId="liquidation-strategy"
|
||||
title={t('Liquidation strategy')}
|
||||
content={<LiquidationStrategyInfoPanel market={market} />}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="liquidity-monitoring-parameters"
|
||||
title={t('Liquidity monitoring parameters')}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user