Compare commits

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

Some files were not shown because too many files have changed in this diff Show More