Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18f8d5e648 | ||
|
|
261f32aa5b | ||
|
|
053775bef6 | ||
|
|
0660eda334 | ||
|
|
f22a3bc2d2 | ||
|
|
fde77ebccb | ||
|
|
e309669736 | ||
|
|
b1621d1191 | ||
|
|
39907f07db | ||
|
|
3dab5f3d9b | ||
|
|
3cf9ae7582 | ||
|
|
557894e2ef | ||
|
|
baf9875c69 | ||
|
|
51199b02ce | ||
|
|
bb826c88f0 | ||
|
|
0da20b750f | ||
|
|
e16c447564 | ||
|
|
a2170d27d6 | ||
|
|
00d840b7b3 | ||
|
|
2a4a05630f | ||
|
|
97f1f40f2c | ||
|
|
a8e6963521 | ||
|
|
a2bffa1dfd | ||
|
|
9253e8067a | ||
|
|
58972f0a11 | ||
|
|
a3c55fd7c3 |
@@ -19,7 +19,7 @@ jobs:
|
||||
create-docker-image:
|
||||
name: Create docker image for console-test
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
#----------------------------------------------
|
||||
# check-out frontend-monorepo
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
name: run-tests
|
||||
runs-on: 8-cores
|
||||
needs: [create-docker-image, console-test-branch]
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
#----------------------------------------------
|
||||
# load docker image
|
||||
@@ -205,7 +205,7 @@ jobs:
|
||||
# run tests
|
||||
#----------------------------------------------
|
||||
- name: Run tests
|
||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
|
||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 1 --dist loadfile --durations=45
|
||||
working-directory: apps/trading/e2e
|
||||
#----------------------------------------------
|
||||
# upload traces
|
||||
@@ -230,28 +230,43 @@ jobs:
|
||||
#----------------------------------------------
|
||||
# ----- upload market-sim logs -----
|
||||
#----------------------------------------------
|
||||
- name: Find Directory
|
||||
id: find-dir
|
||||
- name: Prepare and Zip market-sim-logs
|
||||
if: always()
|
||||
run: |
|
||||
DIR=$(find /tmp -type d -name "vega-sim-*" -print -quit)
|
||||
if [[ -d "$DIR" ]]; then
|
||||
echo "Found directory: $DIR"
|
||||
echo "DIR=$DIR" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Directory not found."
|
||||
exit 1
|
||||
parent_dir="/tmp/market-sim-logs"
|
||||
echo "Creating parent directory at $parent_dir"
|
||||
mkdir -p "$parent_dir"
|
||||
|
||||
echo "Waiting for vega-sim-* folders to be created..."
|
||||
sleep 10 # Waits 10 seconds to ensure all folders are created
|
||||
|
||||
echo "Before searching for vega-sim-* folders in /tmp..."
|
||||
folders=$(find /tmp -mindepth 1 -type d -name 'vega-sim-*' -print) || echo "Find command failed with exit code $?"
|
||||
|
||||
echo "After searching for vega-sim-* folders in /tmp..."
|
||||
|
||||
if [ -z "$folders" ]; then
|
||||
echo "No vega-sim-* folders found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
- name: Compress Files
|
||||
if: env.DIR
|
||||
run: |
|
||||
tar -czvf ${{ github.workspace }}/market-sim-logs.tar.gz -C "$DIR" .
|
||||
echo "Compressed files at ${{ github.workspace }}/market-sim-logs.tar.gz"
|
||||
echo "Moving vega-sim-* folders to $parent_dir"
|
||||
echo "$folders" | xargs -I {} mv {} "$parent_dir/"
|
||||
|
||||
- name: Upload Compressed market-sim-logs
|
||||
echo "Checking if $parent_dir is not empty..."
|
||||
if [ "$(ls -A $parent_dir)" ]; then
|
||||
echo "Zipping the parent directory..."
|
||||
zip -r market-sim-logs.zip "$parent_dir" && echo "Zip file created successfully."
|
||||
else
|
||||
echo "$parent_dir is empty. No zip file created."
|
||||
exit 0
|
||||
fi
|
||||
shell: /usr/bin/bash -e {0}
|
||||
|
||||
- name: Upload market-sim-logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: env.DIR
|
||||
if: always()
|
||||
with:
|
||||
name: market-sim-logs
|
||||
path: ${{ github.workspace }}/market-sim-logs.tar.gz
|
||||
path: market-sim-logs.zip
|
||||
retention-days: 15
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import {
|
||||
BORDER_COLOURS,
|
||||
NestedDataList,
|
||||
sortNestedDataByChildren,
|
||||
} from './nested-data-list';
|
||||
import { NestedDataList, sortNestedDataByChildren } from './nested-data-list';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
const mockData = {
|
||||
@@ -61,38 +57,6 @@ describe('NestedDataList', () => {
|
||||
expect(parent[0].querySelector('li')).toHaveClass('pl-4 border-l-4 pt-2');
|
||||
});
|
||||
|
||||
it('should repeat the border colours in the correct order', () => {
|
||||
const colourMockData = {
|
||||
t0: {
|
||||
t1: {
|
||||
t2: {
|
||||
t3: {
|
||||
t4: {
|
||||
t5: {
|
||||
t6: {
|
||||
t7: {
|
||||
t8: {
|
||||
hello: 'world',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const tree = render(<NestedDataList data={colourMockData} />);
|
||||
const { getByTestId } = tree;
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const item = getByTestId(`T${i}`);
|
||||
const expected = BORDER_COLOURS.light[i % 5];
|
||||
expect(item.style.borderColor.toUpperCase()).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('should sort the data by values with children', () => {
|
||||
const mockData = {
|
||||
nonce: '5980890939790185837',
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerReferralCodeOwnerQueryVariables = Types.Exact<{
|
||||
id: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerReferralCodeOwnerQuery = { __typename?: 'Query', referralSets: { __typename?: 'ReferralSetConnection', edges: Array<{ __typename?: 'ReferralSetEdge', node: { __typename?: 'ReferralSet', createdAt: any, updatedAt: any, referrer: string } } | null> } };
|
||||
|
||||
|
||||
export const ExplorerReferralCodeOwnerDocument = gql`
|
||||
query ExplorerReferralCodeOwner($id: ID!) {
|
||||
referralSets(id: $id) {
|
||||
edges {
|
||||
node {
|
||||
createdAt
|
||||
updatedAt
|
||||
referrer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useExplorerReferralCodeOwnerQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerReferralCodeOwnerQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerReferralCodeOwnerQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useExplorerReferralCodeOwnerQuery({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerReferralCodeOwnerQuery(baseOptions: Apollo.QueryHookOptions<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>(ExplorerReferralCodeOwnerDocument, options);
|
||||
}
|
||||
export function useExplorerReferralCodeOwnerLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>(ExplorerReferralCodeOwnerDocument, options);
|
||||
}
|
||||
export type ExplorerReferralCodeOwnerQueryHookResult = ReturnType<typeof useExplorerReferralCodeOwnerQuery>;
|
||||
export type ExplorerReferralCodeOwnerLazyQueryHookResult = ReturnType<typeof useExplorerReferralCodeOwnerLazyQuery>;
|
||||
export type ExplorerReferralCodeOwnerQueryResult = Apollo.QueryResult<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>;
|
||||
@@ -0,0 +1,11 @@
|
||||
query ExplorerReferralCodeOwner($id: ID!) {
|
||||
referralSets(id: $id) {
|
||||
edges {
|
||||
node {
|
||||
createdAt
|
||||
updatedAt
|
||||
referrer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { ReferralCodeOwner } from './referral-code-owner';
|
||||
import type { ReferralCodeOwnerProps } from './referral-code-owner';
|
||||
import { ExplorerReferralCodeOwnerDocument } from './__generated__/code-owner';
|
||||
const renderComponent = (
|
||||
props: ReferralCodeOwnerProps,
|
||||
mocks: MockedResponse[]
|
||||
) => {
|
||||
return render(
|
||||
<MockedProvider mocks={mocks}>
|
||||
<MemoryRouter>
|
||||
<ReferralCodeOwner {...props} />
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('ReferralCodeOwner', () => {
|
||||
it('should render loading state', () => {
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: ExplorerReferralCodeOwnerDocument,
|
||||
variables: {
|
||||
id: 'ABC123',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const { getByText } = renderComponent({ code: 'ABC123' }, mocks);
|
||||
|
||||
expect(getByText('Loading...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render error state', async () => {
|
||||
const errorMessage = 'Error fetching referrer: ABC123';
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: ExplorerReferralCodeOwnerDocument,
|
||||
variables: {
|
||||
id: 'ABC123',
|
||||
},
|
||||
},
|
||||
error: new Error('nope'),
|
||||
},
|
||||
];
|
||||
const { getByText } = renderComponent({ code: 'ABC123' }, mocks);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText(errorMessage)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render link to referring party', async () => {
|
||||
const referrerId = 'DEF789';
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: ExplorerReferralCodeOwnerDocument,
|
||||
variables: {
|
||||
id: 'ABC123',
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
referralSets: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
__typename: 'ReferralSet',
|
||||
referrer: referrerId,
|
||||
createdAt: '123',
|
||||
updatedAt: '456',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const { getByText } = renderComponent({ code: 'ABC123' }, mocks);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText(referrerId)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { TableCell } from '../../../table';
|
||||
import { useExplorerReferralCodeOwnerQuery } from './__generated__/code-owner';
|
||||
import { PartyLink } from '../../../links';
|
||||
|
||||
export interface ReferralCodeOwnerProps {
|
||||
code: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the owner of a referral code
|
||||
*/
|
||||
export const ReferralCodeOwner = ({ code }: ReferralCodeOwnerProps) => {
|
||||
const { data, error, loading } = useExplorerReferralCodeOwnerQuery({
|
||||
variables: {
|
||||
id: code,
|
||||
},
|
||||
});
|
||||
const referrer = data?.referralSets.edges[0]?.node.referrer || '';
|
||||
return (
|
||||
<TableCell>
|
||||
{loading && 'Loading...'}
|
||||
{error && `Error fetching referrer: ${code}`}
|
||||
{referrer.length > 0 && <PartyLink id={referrer} />}
|
||||
</TableCell>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { ReferralTeam } from './team';
|
||||
import type { CreateReferralSet } from './team';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
|
||||
describe('ReferralTeam', () => {
|
||||
const team = {
|
||||
name: 'Test Team',
|
||||
teamUrl: 'https://example.com/team',
|
||||
avatarUrl: 'https://example.com/avatar',
|
||||
closed: false,
|
||||
};
|
||||
|
||||
const mockTx: CreateReferralSet = {
|
||||
team,
|
||||
};
|
||||
|
||||
const mockId = '123456';
|
||||
const mockCreator = 'JohnDoe';
|
||||
|
||||
it('should render the team name', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Test Team')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the team ID', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Id')).toBeInTheDocument();
|
||||
expect(getByText(mockId)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the creator', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Creator')).toBeInTheDocument();
|
||||
expect(getByText(mockCreator)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the team URL', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Team URL')).toBeInTheDocument();
|
||||
expect(getByText(team.teamUrl)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the avatar URL', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Avatar')).toBeInTheDocument();
|
||||
expect(getByText(team.avatarUrl)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the open status as a tick if closed is falsy', () => {
|
||||
const { getByTestId } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByTestId('open-yes')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the open status as a cross if it is truthy', () => {
|
||||
const m = {
|
||||
team: {
|
||||
closed: true,
|
||||
},
|
||||
};
|
||||
|
||||
const { getByTestId } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={m} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByTestId('open-no')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
VegaIcon,
|
||||
Icon,
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import Hash from '../../../links/hash';
|
||||
import { t } from 'i18next';
|
||||
import { PartyLink } from '../../../links';
|
||||
|
||||
export type CreateReferralSet = components['schemas']['v1CreateReferralSet'];
|
||||
export type ReferralTeam = CreateReferralSet['team'];
|
||||
|
||||
export interface ReferralTeamProps {
|
||||
tx: CreateReferralSet;
|
||||
id: string;
|
||||
creator: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the details for a team in a CreateReferralSet or UpdateReferralSet transaction.
|
||||
*
|
||||
* Intentionally does not render the avatar image or link to the team url.
|
||||
*/
|
||||
export const ReferralTeam = ({ tx, id, creator }: ReferralTeamProps) => {
|
||||
if (!tx.team) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="inline-block mr-2 leading-none">
|
||||
<VegaIcon name={VegaIconNames.TEAM} />
|
||||
</div>
|
||||
{tx.team.name && (
|
||||
<h3 className="inline-block leading-loose">{tx.team.name}</h3>
|
||||
)}
|
||||
|
||||
<div className="min-w-fit max-w-2xl block">
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
{t('Id')}
|
||||
<Hash text={id} truncate={false} />
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('Creator')}
|
||||
<PartyLink id={creator} truncate={false} />
|
||||
</KeyValueTableRow>
|
||||
{tx.team.teamUrl && (
|
||||
<KeyValueTableRow>
|
||||
{t('Team URL')}
|
||||
<Hash text={tx.team.teamUrl} truncate={false} />
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
{tx.team.avatarUrl && (
|
||||
<KeyValueTableRow>
|
||||
{t('Avatar')}
|
||||
<Hash text={tx.team.avatarUrl} truncate={false} />
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('Open')}
|
||||
<span data-testid={!tx.team.closed ? 'open-yes' : 'open-no'}>
|
||||
{!tx.team.closed ? <Icon name="tick" /> : <Icon name="cross" />}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -27,7 +27,8 @@ export const sharedHeaderProps = {
|
||||
className: 'align-top',
|
||||
};
|
||||
|
||||
const Labels: Record<BlockExplorerTransactionResult['type'], string> = {
|
||||
// The incoming type field is usually the right thing to show. Exceptions are listed here
|
||||
const LabelOverrides: Record<BlockExplorerTransactionResult['type'], string> = {
|
||||
'Stop Orders Submission': 'Stop Order',
|
||||
'Stop Orders Cancellation': 'Cancel Stop Order',
|
||||
};
|
||||
@@ -50,7 +51,7 @@ export const TxDetailsShared = ({
|
||||
const time: string = blockData?.result.block.header.time || '';
|
||||
const height: string = blockData?.result.block.header.height || txData.block;
|
||||
|
||||
const type = Labels[txData.type] || txData.type;
|
||||
const type = LabelOverrides[txData.type] || txData.type;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { ReferralCodeOwner } from './referrals/referral-code-owner';
|
||||
|
||||
interface TxDetailsApplyReferralCodeProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The signature can be turned in to an id with txSignatureToDeterministicId but
|
||||
*/
|
||||
export const TxDetailsApplyReferralCode = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsApplyReferralCodeProps) => {
|
||||
if (!txData || !txData.command.applyReferralCode || !txData.signature) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const referralCode = txData.command.applyReferralCode.id;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Applied Code')}</TableCell>
|
||||
<TableCell>{referralCode}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Referrer')}</TableCell>
|
||||
<ReferralCodeOwner code={referralCode} />
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
||||
import { ReferralTeam } from './referrals/team';
|
||||
|
||||
interface TxDetailsCreateReferralProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The signature can be turned in to an id with txSignatureToDeterministicId but
|
||||
*/
|
||||
export const TxDetailsCreateReferralSet = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsCreateReferralProps) => {
|
||||
if (!txData || !txData.command.createReferralSet || !txData.signature) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const id = txSignatureToDeterministicId(txData.signature.value);
|
||||
|
||||
const isTeam = txData.command.createReferralSet.isTeam || false;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{isTeam ? t('Team ID') : t('Referral code')}</TableCell>
|
||||
<TableCell>{id}</TableCell>
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
|
||||
<ReferralTeam
|
||||
tx={txData.command.createReferralSet}
|
||||
id={id}
|
||||
creator={txData.submitter}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -28,6 +28,10 @@ import { TxProposal } from './tx-proposal';
|
||||
import { TxDetailsTransfer } from './tx-transfer';
|
||||
import { TxDetailsStopOrderSubmission } from './tx-stop-order-submission';
|
||||
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
|
||||
import { TxDetailsCreateReferralSet } from './tx-create-referral-set';
|
||||
import { TxDetailsApplyReferralCode } from './tx-apply-referral-code';
|
||||
import { TxDetailsUpdateReferralSet } from './tx-update-referral-set';
|
||||
import { TxDetailsJoinTeam } from './tx-join-team';
|
||||
|
||||
interface TxDetailsWrapperProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -121,6 +125,14 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
||||
return TxDetailsStopOrderSubmission;
|
||||
case 'Transfer Funds':
|
||||
return TxDetailsTransfer;
|
||||
case 'Create Referral Set':
|
||||
return TxDetailsCreateReferralSet;
|
||||
case 'Update Referral Set':
|
||||
return TxDetailsUpdateReferralSet;
|
||||
case 'Apply Referral Code':
|
||||
return TxDetailsApplyReferralCode;
|
||||
case 'Join Team':
|
||||
return TxDetailsJoinTeam;
|
||||
default:
|
||||
return TxDetailsGeneric;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { ReferralCodeOwner } from './referrals/referral-code-owner';
|
||||
|
||||
interface TxDetailsJoinTeamProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The signature can be turned in to an id with txSignatureToDeterministicId but
|
||||
*/
|
||||
export const TxDetailsJoinTeam = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsJoinTeamProps) => {
|
||||
if (!txData || !txData.command.joinTeam || !txData.signature) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const team = txData.command.joinTeam.id;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Team')}</TableCell>
|
||||
<TableCell>{team}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Referrer')}</TableCell>
|
||||
<ReferralCodeOwner code={team} />
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
||||
import { ReferralTeam } from './referrals/team';
|
||||
|
||||
interface TxDetailsUpdateReferralProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A copy of create referral set, effectively.
|
||||
* Updating a referral set without a team doesn't make sense,
|
||||
* but is valid, so this component ignores sense.
|
||||
*/
|
||||
export const TxDetailsUpdateReferralSet = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsUpdateReferralProps) => {
|
||||
if (!txData || !txData.command.updateReferralSet || !txData.signature) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const id = txSignatureToDeterministicId(txData.signature.value);
|
||||
|
||||
const isTeam = txData.command.updateReferralSet.isTeam || false;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{isTeam ? t('Team ID') : t('Referral code')}</TableCell>
|
||||
<TableCell>{id}</TableCell>
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
|
||||
<ReferralTeam
|
||||
tx={txData.command.updateReferralSet}
|
||||
id={id}
|
||||
creator={txData.submitter}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -28,6 +28,7 @@ export type FilterOption =
|
||||
| 'Delegate'
|
||||
| 'Ethereum Key Rotate Submission'
|
||||
| 'Issue Signatures'
|
||||
| 'Join Team'
|
||||
| 'Key Rotate Submission'
|
||||
| 'Liquidity Provision Order'
|
||||
| 'Node Signature'
|
||||
@@ -47,45 +48,46 @@ export type FilterOption =
|
||||
| 'Vote on Proposal'
|
||||
| 'Withdraw';
|
||||
|
||||
// Alphabetised list of transaction types to appear at the top level
|
||||
export const PrimaryFilterOptions: FilterOption[] = [
|
||||
'Amend LiquidityProvision Order',
|
||||
'Amend Order',
|
||||
'Batch Market Instructions',
|
||||
'Cancel LiquidityProvision Order',
|
||||
'Cancel Order',
|
||||
'Cancel Transfer Funds',
|
||||
'Delegate',
|
||||
'Liquidity Provision Order',
|
||||
'Proposal',
|
||||
'Stop Orders Submission',
|
||||
'Stop Orders Cancellation',
|
||||
'Submit Oracle Data',
|
||||
'Submit Order',
|
||||
'Transfer Funds',
|
||||
'Undelegate',
|
||||
'Vote on Proposal',
|
||||
'Withdraw',
|
||||
];
|
||||
export const filterOptions: Record<string, FilterOption[]> = {
|
||||
'Market Instructions': [
|
||||
'Amend LiquidityProvision Order',
|
||||
'Amend Order',
|
||||
'Batch Market Instructions',
|
||||
'Cancel LiquidityProvision Order',
|
||||
'Cancel Order',
|
||||
'Liquidity Provision Order',
|
||||
'Stop Orders Submission',
|
||||
'Stop Orders Cancellation',
|
||||
'Submit Order',
|
||||
],
|
||||
'Transfers and Withdrawals': [
|
||||
'Transfer Funds',
|
||||
'Cancel Transfer Funds',
|
||||
'Withdraw',
|
||||
],
|
||||
Governance: ['Delegate', 'Undelegate', 'Vote on Proposal', 'Proposal'],
|
||||
Referrals: [
|
||||
'Apply Referral Code',
|
||||
'Create Referral Set',
|
||||
'Join Team',
|
||||
'Update Referral Set',
|
||||
],
|
||||
'External Data': ['Chain Event', 'Submit Oracle Data'],
|
||||
Validators: [
|
||||
'Ethereum Key Rotate Submission',
|
||||
'Issue Signatures',
|
||||
'Key Rotate Submission',
|
||||
'Node Signature',
|
||||
'Node Vote',
|
||||
'Protocol Upgrade',
|
||||
'Register new Node',
|
||||
'State Variable Proposal',
|
||||
'Validator Heartbeat',
|
||||
],
|
||||
};
|
||||
|
||||
// Alphabetised list of transaction types to nest under a 'More...' submenu
|
||||
export const SecondaryFilterOptions: FilterOption[] = [
|
||||
'Chain Event',
|
||||
'Ethereum Key Rotate Submission',
|
||||
'Issue Signatures',
|
||||
'Key Rotate Submission',
|
||||
'Node Signature',
|
||||
'Node Vote',
|
||||
'Protocol Upgrade',
|
||||
'Register new Node',
|
||||
'State Variable Proposal',
|
||||
'Validator Heartbeat',
|
||||
];
|
||||
|
||||
export const AllFilterOptions: FilterOption[] = [
|
||||
...PrimaryFilterOptions,
|
||||
...SecondaryFilterOptions,
|
||||
];
|
||||
export const AllFilterOptions: FilterOption[] =
|
||||
Object.values(filterOptions).flat();
|
||||
|
||||
export interface TxFilterProps {
|
||||
filters: Set<FilterOption>;
|
||||
@@ -122,46 +124,33 @@ export const TxsFilter = ({ filters, setFilters }: TxFilterProps) => {
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
{PrimaryFilterOptions.map((f) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={f}
|
||||
checked={filters.has(f)}
|
||||
onCheckedChange={() => {
|
||||
// NOTE: These act like radio buttons until the API supports multiple filters
|
||||
setFilters(new Set([f]));
|
||||
}}
|
||||
id={`radio-${f}`}
|
||||
>
|
||||
{f}
|
||||
<DropdownMenuItemIndicator>
|
||||
<Icon name="tick-circle" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</DropdownMenuCheckboxItem>
|
||||
|
||||
{Object.entries(filterOptions).map(([key, value]) => (
|
||||
<DropdownMenuSub key={key}>
|
||||
<DropdownMenuSubTrigger>
|
||||
{t(key)}
|
||||
<Icon name="chevron-right" />
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
{value.map((f) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={f}
|
||||
checked={filters.has(f)}
|
||||
onCheckedChange={(checked) => {
|
||||
// NOTE: These act like radio buttons until the API supports multiple filters
|
||||
setFilters(new Set([f]));
|
||||
}}
|
||||
id={`radio-${f}`}
|
||||
>
|
||||
{f}
|
||||
<DropdownMenuItemIndicator>
|
||||
<Icon name="tick-circle" className="inline" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
))}
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
{t('More Types')}
|
||||
<Icon name="chevron-right" />
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
{SecondaryFilterOptions.map((f) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={f}
|
||||
checked={filters.has(f)}
|
||||
onCheckedChange={(checked) => {
|
||||
// NOTE: These act like radio buttons until the API supports multiple filters
|
||||
setFilters(new Set([f]));
|
||||
}}
|
||||
id={`radio-${f}`}
|
||||
>
|
||||
{f}
|
||||
<DropdownMenuItemIndicator>
|
||||
<Icon name="tick-circle" className="inline" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
||||
@@ -48,6 +48,8 @@ const displayString: StringMap = {
|
||||
StopOrdersSubmission: 'Stop',
|
||||
StopOrdersCancellation: 'Cancel stop',
|
||||
'Stop Orders Cancellation': 'Cancel stop',
|
||||
'Apply Referral Code': 'Referral',
|
||||
'Create Referral Set': 'Create referral',
|
||||
};
|
||||
|
||||
export function getLabelForStopOrderType(
|
||||
|
||||
@@ -31,6 +31,10 @@ const Tx = () => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!data || !data?.transaction) {
|
||||
errorMessage = 'Transaction not found';
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<PageHeader
|
||||
@@ -49,7 +53,7 @@ const Tx = () => {
|
||||
<TxDetails
|
||||
className="mb-28"
|
||||
txData={data?.transaction}
|
||||
pubKey={data?.transaction.submitter}
|
||||
pubKey={data?.transaction?.submitter}
|
||||
/>
|
||||
</RenderFetched>
|
||||
</section>
|
||||
|
||||
@@ -11,8 +11,8 @@ interface TxDetailsProps {
|
||||
export const txDetailsTruncateLength = 30;
|
||||
|
||||
export const TxDetails = ({ txData, pubKey }: TxDetailsProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
if (!txData || !pubKey) {
|
||||
return <>{t('Transaction could not be found')}</>;
|
||||
}
|
||||
return (
|
||||
<section className="mb-10" key={txData.hash}>
|
||||
|
||||
+2
-2
@@ -8,13 +8,13 @@ type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
|
||||
type XOR<T, U> = T | U extends object
|
||||
? (Without<T, U> & U) | (Without<U, T> & T)
|
||||
: T | U;
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
type OneOf<T extends any[]> = T extends [infer Only]
|
||||
? Only
|
||||
: T extends [infer A, infer B, ...infer Rest]
|
||||
? OneOf<[XOR<A, B>, ...Rest]>
|
||||
: never;
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export interface paths {
|
||||
'/info': {
|
||||
|
||||
@@ -47,7 +47,7 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
.and('contain.text', 'USDC (fake)');
|
||||
});
|
||||
|
||||
it('Unable to submit proposal with public key', function () {
|
||||
it.skip('Unable to submit proposal with public key', function () {
|
||||
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
|
||||
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
@@ -55,7 +55,10 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
cy.getByTestId('dialog-content')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('h1').should('have.text', 'Transaction failed');
|
||||
cy.getByTestId('dialog-title').should(
|
||||
'have.text',
|
||||
'Transaction failed'
|
||||
);
|
||||
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -112,6 +112,7 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
|
||||
performanceHysteresisEpochs: 2,
|
||||
slaCompetitionFactor: '0.1',
|
||||
},
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
quadraticSlippageFactor: '0',
|
||||
instrument: {
|
||||
name: 'Token test market',
|
||||
@@ -241,6 +242,7 @@ export function createSuccessorMarketProposalTxBody(
|
||||
decimalPlaces: '5',
|
||||
positionDecimalPlaces: '5',
|
||||
linearSlippageFactor: '0.001',
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
quadraticSlippageFactor: '0',
|
||||
liquiditySlaParameters: {
|
||||
priceRange: '0.5',
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from '@vegaprotocol/web3';
|
||||
import { Web3Provider } from '@vegaprotocol/web3';
|
||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletProvider, useChainId } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
useVegaTransactionManager,
|
||||
useVegaTransactionUpdater,
|
||||
@@ -96,7 +96,9 @@ const cache: InMemoryCacheConfig = {
|
||||
const Web3Container = ({
|
||||
chainId,
|
||||
}: {
|
||||
/** Ethereum chain id */
|
||||
chainId: number;
|
||||
/** Ethereum provider url */
|
||||
providerUrl: string;
|
||||
}) => {
|
||||
const InitializeHandlers = () => {
|
||||
@@ -123,6 +125,9 @@ const Web3Container = ({
|
||||
MOZILLA_EXTENSION_URL,
|
||||
VEGA_WALLET_URL,
|
||||
} = useEnvironment();
|
||||
|
||||
const vegaChainId = useChainId(VEGA_URL);
|
||||
|
||||
useEffect(() => {
|
||||
if (chainId) {
|
||||
return initializeConnectors(
|
||||
@@ -157,7 +162,8 @@ const Web3Container = ({
|
||||
!VEGA_EXPLORER_URL ||
|
||||
!DocsLinks ||
|
||||
!CHROME_EXTENSION_URL ||
|
||||
!MOZILLA_EXTENSION_URL
|
||||
!MOZILLA_EXTENSION_URL ||
|
||||
!vegaChainId
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -169,6 +175,7 @@ const Web3Container = ({
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
chainId: vegaChainId,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { ApolloError } from '@apollo/client';
|
||||
import {
|
||||
PARTY_NOT_FOUND,
|
||||
filterAcceptableGraphqlErrors,
|
||||
isPartyNotFoundError,
|
||||
} from './party';
|
||||
import type { GraphQLError } from 'graphql';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param message
|
||||
* @returns GraphQLError
|
||||
*/
|
||||
function createMockApolloErrors(message: string): GraphQLError {
|
||||
return {
|
||||
message,
|
||||
extensions: {
|
||||
code: message.toUpperCase().replace(/ /g, '_'),
|
||||
},
|
||||
locations: [],
|
||||
originalError: new Error(message),
|
||||
path: [],
|
||||
nodes: [],
|
||||
positions: [1],
|
||||
name: message,
|
||||
source: {
|
||||
body: message,
|
||||
name: message,
|
||||
locationOffset: {
|
||||
line: 1,
|
||||
column: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('filterAcceptableGraphqlErrors', () => {
|
||||
it('should return undefined if the error is a party not found error', () => {
|
||||
const error: Partial<ApolloError> = {
|
||||
graphQLErrors: [createMockApolloErrors('failed to get party for ID')],
|
||||
};
|
||||
|
||||
const result = filterAcceptableGraphqlErrors(error as ApolloError);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return the error if it is not a party not found error', () => {
|
||||
const error: Partial<ApolloError> = {
|
||||
graphQLErrors: [createMockApolloErrors('Some other error')],
|
||||
};
|
||||
|
||||
const result = filterAcceptableGraphqlErrors(error as ApolloError);
|
||||
|
||||
expect(result).toEqual(error);
|
||||
});
|
||||
|
||||
it('should return the error if there are multiple errors', () => {
|
||||
const error: Partial<ApolloError> = {
|
||||
graphQLErrors: [
|
||||
createMockApolloErrors('failed to get party for ID'),
|
||||
createMockApolloErrors('Some other error'),
|
||||
],
|
||||
};
|
||||
|
||||
const result = filterAcceptableGraphqlErrors(error as ApolloError);
|
||||
|
||||
expect(result).toEqual(error);
|
||||
});
|
||||
|
||||
it('should return the error if there are no errors', () => {
|
||||
const error: Partial<ApolloError> = {
|
||||
graphQLErrors: [],
|
||||
};
|
||||
|
||||
const result = filterAcceptableGraphqlErrors(error as ApolloError);
|
||||
|
||||
expect(result).toEqual(error);
|
||||
});
|
||||
|
||||
it('should return undefined if the error is undefined', () => {
|
||||
const result = filterAcceptableGraphqlErrors(undefined);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPartyNotFoundError', () => {
|
||||
it('should return true if the error message includes PARTY_NOT_FOUND', () => {
|
||||
const error = { message: 'failed to get party for ID' };
|
||||
|
||||
const result = isPartyNotFoundError(error);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the error message does not include PARTY_NOT_FOUND', () => {
|
||||
const error = { message: 'Some other error' };
|
||||
|
||||
const result = isPartyNotFoundError(error);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
// Will trip if the error message changes, which should not be a problem, but there
|
||||
// might be logic that depends on it
|
||||
it('expects party not found error to remain consistent', () => {
|
||||
const error = 'failed to get party for ID';
|
||||
|
||||
expect(PARTY_NOT_FOUND).toStrictEqual(error);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { ApolloError } from '@apollo/client';
|
||||
|
||||
export const PARTY_NOT_FOUND = 'failed to get party for ID';
|
||||
|
||||
export const isPartyNotFoundError = (error: { message: string }) => {
|
||||
@@ -6,3 +8,23 @@ export const isPartyNotFoundError = (error: { message: string }) => {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* If a party has no accounts or data, then this GraphQL query believes it does not exist
|
||||
* Not having any rewards is a valid state, so in some cases we can filter this error out.
|
||||
*
|
||||
* @param error ApolloError | undefined
|
||||
* @returns ApolloError | undefined
|
||||
*/
|
||||
export function filterAcceptableGraphqlErrors(
|
||||
error?: ApolloError
|
||||
): ApolloError | undefined {
|
||||
// Currently the only error we expect is when a party has no accounts
|
||||
if (error && error.graphQLErrors.length === 1) {
|
||||
if (isPartyNotFoundError(error.graphQLErrors[0])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ describe('Proposal header', () => {
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Market change: MarketId'
|
||||
'Update to market ID: MarketId'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
+42
-4
@@ -1,5 +1,11 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Lozenge, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
CopyWithTooltip,
|
||||
Lozenge,
|
||||
Tooltip,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { shorten } from '@vegaprotocol/utils';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
@@ -12,7 +18,12 @@ import {
|
||||
useNewTransferProposalDetails,
|
||||
useSuccessorMarketProposalDetails,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import {
|
||||
CONSOLE_MARKET_PAGE,
|
||||
DApp,
|
||||
useFeatureFlags,
|
||||
useLinks,
|
||||
} from '@vegaprotocol/environment';
|
||||
import Routes from '../../../routes';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { VoteState } from '../vote-details/use-user-vote';
|
||||
@@ -32,6 +43,8 @@ export const ProposalHeader = ({
|
||||
const { t } = useTranslation();
|
||||
const change = proposal?.terms.change;
|
||||
|
||||
const consoleLink = useLinks(DApp.Console);
|
||||
|
||||
let details: ReactNode;
|
||||
let proposalType = '';
|
||||
let fallbackTitle = '';
|
||||
@@ -106,8 +119,33 @@ export const ProposalHeader = ({
|
||||
fallbackTitle = t('UpdateMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
<span>{t('MarketChange')}:</span>{' '}
|
||||
<span>{truncateMiddle(change.marketId)}</span>
|
||||
<span>{t('UpdateToMarket')}:</span>{' '}
|
||||
<span className="inline-flex items-start gap-2">
|
||||
<span className="break-all">{change.marketId} </span>
|
||||
<span className="inline-flex items-end gap-0">
|
||||
<CopyWithTooltip
|
||||
text={change.marketId}
|
||||
description={t('copyToClipboard')}
|
||||
>
|
||||
<button className="inline-block px-1">
|
||||
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
<Tooltip description={t('OpenInConsole')} align="center">
|
||||
<button
|
||||
className="inline-block px-1"
|
||||
onClick={() => {
|
||||
const marketPageLink = consoleLink(
|
||||
CONSOLE_MARKET_PAGE.replace(':marketId', change.marketId)
|
||||
);
|
||||
window.open(marketPageLink, '_blank');
|
||||
}}
|
||||
>
|
||||
<VegaIcon size={20} name={VegaIconNames.OPEN_EXTERNAL} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</span>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -48,6 +48,7 @@ const vegaWalletConfig: VegaWalletConfig = {
|
||||
chromeExtensionUrl: 'chrome',
|
||||
mozillaExtensionUrl: 'mozilla',
|
||||
},
|
||||
chainId: 'VEGA_CHAIN_ID',
|
||||
};
|
||||
|
||||
const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ProposalMinRequirements, ProposalUserAction } from '../shared';
|
||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||
import { useVoteButtonsQuery } from './__generated__/Stake';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
|
||||
import { filterAcceptableGraphqlErrors } from '../../../../lib/party';
|
||||
|
||||
interface VoteButtonsContainerProps {
|
||||
voteState: VoteState | null;
|
||||
@@ -42,8 +43,10 @@ export const VoteButtonsContainer = (props: VoteButtonsContainerProps) => {
|
||||
skip: !pubKey,
|
||||
});
|
||||
|
||||
const filteredErrors = filterAcceptableGraphqlErrors(error);
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
<AsyncRenderer loading={loading} error={filteredErrors} data={data}>
|
||||
<VoteButtons
|
||||
{...props}
|
||||
currentStakeAvailable={toBigNum(
|
||||
|
||||
@@ -212,7 +212,6 @@ query Proposal(
|
||||
}
|
||||
positionDecimalPlaces
|
||||
linearSlippageFactor
|
||||
quadraticSlippageFactor
|
||||
}
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
|
||||
File diff suppressed because one or more lines are too long
+23
-13
@@ -10,6 +10,7 @@ import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
import { useNetworkParam } from '@vegaprotocol/network-parameters';
|
||||
import { filterAcceptableGraphqlErrors } from '../../../lib/party';
|
||||
|
||||
const EPOCHS_PAGE_SIZE = 10;
|
||||
|
||||
@@ -99,17 +100,24 @@ export const EpochIndividualRewards = ({
|
||||
prevEpochIdRef.current = epochId;
|
||||
}, [epochId, refetchData]);
|
||||
|
||||
// Workarounds for the error handling of AsyncRenderer
|
||||
const filteredErrors = filterAcceptableGraphqlErrors(error);
|
||||
const filteredData = data || [];
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={data}
|
||||
error={filteredErrors}
|
||||
data={filteredData}
|
||||
render={() => (
|
||||
<div>
|
||||
<p data-testid="connected-vega-key" className="mb-10">
|
||||
{t('Connected Vega key')}:{' '}
|
||||
<span className="text-white">{pubKey}</span>
|
||||
</p>
|
||||
{epochIndividualRewardSummaries.length === 0 && (
|
||||
<p>{t('No rewards for key')}</p>
|
||||
)}
|
||||
{epochIndividualRewardSummaries.map(
|
||||
(epochIndividualRewardSummary) => (
|
||||
<EpochIndividualRewardsTable
|
||||
@@ -118,17 +126,19 @@ export const EpochIndividualRewards = ({
|
||||
/>
|
||||
)
|
||||
)}
|
||||
<Pagination
|
||||
isLoading={loading}
|
||||
hasPrevPage={page > 1}
|
||||
hasNextPage={page < totalPages}
|
||||
onBack={() => refetchData(page - 1)}
|
||||
onNext={() => refetchData(page + 1)}
|
||||
onFirst={() => refetchData(1)}
|
||||
onLast={() => refetchData(totalPages)}
|
||||
>
|
||||
{t('Page')} {page}
|
||||
</Pagination>
|
||||
{epochIndividualRewardSummaries.length > 0 && (
|
||||
<Pagination
|
||||
isLoading={loading}
|
||||
hasPrevPage={page > 1}
|
||||
hasNextPage={page < totalPages}
|
||||
onBack={() => refetchData(page - 1)}
|
||||
onNext={() => refetchData(page + 1)}
|
||||
onFirst={() => refetchData(1)}
|
||||
onLast={() => refetchData(totalPages)}
|
||||
>
|
||||
{t('Page')} {page}
|
||||
</Pagination>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ import type { ValidatorsView } from './validator-tables';
|
||||
const nodeFactory = (
|
||||
overrides?: PartialDeep<NodesFragmentFragment>
|
||||
): NodesFragmentFragment => {
|
||||
const defaultNode = {
|
||||
const defaultNode: NodesFragmentFragment = {
|
||||
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
|
||||
name: 'high',
|
||||
avatarUrl: 'https://upload.wikimedia.org/wikipedia/en/2/25/Marvin-TV-3.jpg',
|
||||
@@ -288,7 +288,7 @@ describe('Consensus validators table', () => {
|
||||
|
||||
expect(
|
||||
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
|
||||
).toHaveTextContent('10.07%');
|
||||
).toHaveTextContent('13.16%');
|
||||
|
||||
expect(
|
||||
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
|
||||
|
||||
+11
-2
@@ -185,6 +185,15 @@ export const ConsensusValidatorsTable = ({
|
||||
const { rawValidatorScore: previousEpochValidatorScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
|
||||
const overstakingPenalty = calculateOverallPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
const totalPenalty = calculateOverstakedPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
||||
@@ -213,11 +222,11 @@ export const ConsensusValidatorsTable = ({
|
||||
2
|
||||
),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
|
||||
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
|
||||
overstakingPenalty,
|
||||
2
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||
calculateOverallPenalty(id, allNodesInPreviousEpoch),
|
||||
totalPenalty,
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
|
||||
+11
-2
@@ -124,6 +124,15 @@ export const StandbyPendingValidatorsTable = ({
|
||||
}
|
||||
}
|
||||
|
||||
const overstakingPenalty = calculateOverallPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
const totalPenalty = calculateOverstakedPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
||||
@@ -154,11 +163,11 @@ export const StandbyPendingValidatorsTable = ({
|
||||
2
|
||||
),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
|
||||
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
|
||||
overstakingPenalty,
|
||||
2
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||
calculateOverallPenalty(id, allNodesInPreviousEpoch),
|
||||
totalPenalty,
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
|
||||
@@ -266,7 +266,9 @@ export const ValidatorTable = ({
|
||||
|
||||
<Tooltip description={t('OverstakedPenaltyDescription')}>
|
||||
<span data-testid="overstaking-penalty">
|
||||
{formatNumberPercentage(penalties.overstaked, 2)}
|
||||
{penalties.overstaked
|
||||
? formatNumberPercentage(penalties.overstaked, 2)
|
||||
: '-'}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</KeyValueTableRow>
|
||||
@@ -285,7 +287,9 @@ export const ValidatorTable = ({
|
||||
</span>
|
||||
<span data-testid="total-penalties">
|
||||
<strong>
|
||||
{formatNumberPercentage(penalties.overall, 2)}
|
||||
{penalties.overall
|
||||
? formatNumberPercentage(penalties.overall, 2)
|
||||
: '-'}
|
||||
</strong>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
getLastEpochScoreAndPerformance,
|
||||
getNormalisedVotingPower,
|
||||
getUnnormalisedVotingPower,
|
||||
getOverstakingPenalty,
|
||||
getFormattedPerformanceScore,
|
||||
getPerformancePenalty,
|
||||
getTotalPenalties,
|
||||
getStakePercentage,
|
||||
calculateOverallPenalty,
|
||||
calculateOverstakedPenalty,
|
||||
} from './shared';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
@@ -106,38 +106,6 @@ describe('getUnnormalisedVotingPower', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOverstakingPenalty', () => {
|
||||
it('returns "0%" when both arguments are null or undefined', () => {
|
||||
expect(getOverstakingPenalty(null, null)).toBe('0%');
|
||||
expect(getOverstakingPenalty(undefined, undefined)).toBe('0%');
|
||||
expect(getOverstakingPenalty(null, undefined)).toBe('0%');
|
||||
expect(getOverstakingPenalty(undefined, null)).toBe('0%');
|
||||
});
|
||||
|
||||
it('returns "0%" when one argument is null or undefined', () => {
|
||||
expect(getOverstakingPenalty('10', null)).toBe('0%');
|
||||
expect(getOverstakingPenalty(null, '20')).toBe('0%');
|
||||
expect(getOverstakingPenalty('10', undefined)).toBe('0%');
|
||||
expect(getOverstakingPenalty(undefined, '20')).toBe('0%');
|
||||
});
|
||||
|
||||
it('returns "0%" when validatorScore or stakeScore is zero', () => {
|
||||
expect(getOverstakingPenalty('0', '20')).toBe('0%');
|
||||
expect(getOverstakingPenalty('10', '0')).toBe('0%');
|
||||
});
|
||||
|
||||
it('returns the correct overstaking penalty', () => {
|
||||
expect(getOverstakingPenalty('0.18', '0.2')).toBe('10.00%');
|
||||
expect(getOverstakingPenalty('0.2', '0.2')).toBe('0.00%');
|
||||
expect(getOverstakingPenalty('0.04', '0.2')).toBe('80.00%');
|
||||
});
|
||||
|
||||
it('handles string numbers with decimals', () => {
|
||||
expect(getOverstakingPenalty('7.5', '15')).toBe('50.00%');
|
||||
expect(getOverstakingPenalty('12.5', '25')).toBe('50.00%');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFormattedPerformanceScore', () => {
|
||||
it('should return the formatted performance score', () => {
|
||||
expect(getFormattedPerformanceScore('0.25')).toEqual(new BigNumber(0.25));
|
||||
@@ -152,17 +120,6 @@ describe('getPerformancePenalty', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalPenalties', () => {
|
||||
it('should return the total penalties', () => {
|
||||
expect(getTotalPenalties('0.25', '1', '5000', '10000')).toEqual('50.00%');
|
||||
expect(getTotalPenalties('0.25', '0.5', '5000', '10000')).toEqual('75.00%');
|
||||
});
|
||||
|
||||
it('should return 0 if the total penalties is negative', () => {
|
||||
expect(getTotalPenalties('0.25', '0.5', '1000', '10000')).toEqual('0.00%');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStakePercentage', () => {
|
||||
it('should return the stake percentage', () => {
|
||||
expect(
|
||||
@@ -182,3 +139,107 @@ describe('getStakePercentage', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateOverallPenalty', () => {
|
||||
it('returns null if rewardScore is null', () => {
|
||||
const res = calculateOverallPenalty('1', [
|
||||
{
|
||||
id: '1',
|
||||
rewardScore: null,
|
||||
stakedTotal: '',
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
previousStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
rankingScore: '',
|
||||
votingPower: '',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null if rewardScore.rawValidatorScore is null (should not happen)', () => {
|
||||
const res = calculateOverallPenalty('1', [
|
||||
{
|
||||
id: '1',
|
||||
stakedTotal: '',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
multisigScore: '',
|
||||
validatorScore: null as unknown as string,
|
||||
normalisedScore: '',
|
||||
validatorStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
},
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
previousStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
rankingScore: '',
|
||||
votingPower: '',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateOverstakedPenalty', () => {
|
||||
it('returns null if rewardScore is null', () => {
|
||||
const res = calculateOverstakedPenalty('1', [
|
||||
{
|
||||
id: '1',
|
||||
rewardScore: null,
|
||||
stakedTotal: '',
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
previousStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
rankingScore: '',
|
||||
votingPower: '',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null if rewardScore.rawValidatorScore is null (should not happen)', () => {
|
||||
const res = calculateOverstakedPenalty('1', [
|
||||
{
|
||||
id: '1',
|
||||
stakedTotal: '',
|
||||
rewardScore: {
|
||||
rawValidatorScore: null as unknown as string,
|
||||
performanceScore: '0.75',
|
||||
multisigScore: '',
|
||||
validatorScore: '',
|
||||
normalisedScore: '',
|
||||
validatorStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
},
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
previousStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
rankingScore: '',
|
||||
votingPower: '',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
import type { PreviousEpochQuery } from './__generated__/PreviousEpoch';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import type { LastArrayElement } from 'type-fest';
|
||||
import isNull from 'lodash/isNull';
|
||||
|
||||
type Node = NonNullable<
|
||||
LastArrayElement<
|
||||
@@ -21,7 +22,10 @@ type Node = NonNullable<
|
||||
* @returns Theoretical stake score for given node based on the staked total
|
||||
* of all node of the same type (status)
|
||||
*/
|
||||
const calculateTheoreticalStakeScore = (nodeId: string, nodes: Node[]) => {
|
||||
const calculateTheoreticalStakeScore = (
|
||||
nodeId: string,
|
||||
nodes: Node[]
|
||||
): BigNumber | null => {
|
||||
const node = nodes.find((n) => n.id === nodeId);
|
||||
if (!node) {
|
||||
return new BigNumber(0);
|
||||
@@ -42,14 +46,25 @@ const calculateTheoreticalStakeScore = (nodeId: string, nodes: Node[]) => {
|
||||
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
|
||||
* @returns %
|
||||
*/
|
||||
export const calculateOverallPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
export const calculateOverallPenalty = (
|
||||
nodeId: string,
|
||||
nodes: Node[]
|
||||
): BigNumber | null => {
|
||||
const node = nodes.find((n) => n.id === nodeId);
|
||||
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
|
||||
if (!node || tts.isZero()) {
|
||||
if (
|
||||
!node ||
|
||||
isNull(tts) ||
|
||||
!node.rewardScore ||
|
||||
(node.rewardScore && isNull(node.rewardScore.validatorScore))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (tts.isZero()) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const penalty = new BigNumber(1)
|
||||
.minus(new BigNumber(node.rewardScore?.validatorScore || 0).dividedBy(tts))
|
||||
.minus(new BigNumber(node.rewardScore.validatorScore).dividedBy(tts))
|
||||
.times(100);
|
||||
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
|
||||
};
|
||||
@@ -60,10 +75,21 @@ export const calculateOverallPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
|
||||
* @returns %
|
||||
*/
|
||||
export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
export const calculateOverstakedPenalty = (
|
||||
nodeId: string,
|
||||
nodes: Node[]
|
||||
): BigNumber | null => {
|
||||
const node = nodes.find((n) => n.id === nodeId);
|
||||
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
|
||||
if (!node || tts.isZero()) {
|
||||
if (
|
||||
!node ||
|
||||
isNull(tts) ||
|
||||
isNull(node.rewardScore) ||
|
||||
(node.rewardScore && node.rewardScore.rawValidatorScore === null)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (tts.isZero()) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const penalty = new BigNumber(1)
|
||||
@@ -78,7 +104,9 @@ export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
* Calculates performance penalty based on the given performance score.
|
||||
* @returns %
|
||||
*/
|
||||
export const calculatesPerformancePenalty = (performanceScore: string) => {
|
||||
export const calculatesPerformancePenalty = (
|
||||
performanceScore: string
|
||||
): BigNumber => {
|
||||
const penalty = new BigNumber(1)
|
||||
.minus(new BigNumber(performanceScore))
|
||||
.times(100);
|
||||
@@ -123,60 +151,6 @@ export const getPerformancePenalty = (performanceScore?: string) =>
|
||||
2
|
||||
);
|
||||
|
||||
export const getOverstakingPenalty = (
|
||||
validatorScore: string | null | undefined,
|
||||
stakeScore: string | null | undefined
|
||||
) => {
|
||||
if (!validatorScore || !stakeScore) {
|
||||
return '0%';
|
||||
}
|
||||
|
||||
// avoid division by zero
|
||||
if (
|
||||
new BigNumber(validatorScore).isZero() ||
|
||||
new BigNumber(stakeScore).isZero()
|
||||
) {
|
||||
return '0%';
|
||||
}
|
||||
|
||||
return formatNumberPercentage(
|
||||
BigNumber.max(
|
||||
new BigNumber(1)
|
||||
.minus(
|
||||
new BigNumber(validatorScore).dividedBy(new BigNumber(stakeScore))
|
||||
)
|
||||
.times(100),
|
||||
new BigNumber(0)
|
||||
),
|
||||
2
|
||||
);
|
||||
};
|
||||
|
||||
export const getTotalPenalties = (
|
||||
rawValidatorScore: string | null | undefined,
|
||||
performanceScore: string | undefined,
|
||||
stakedOnNode: string,
|
||||
totalStake: string
|
||||
) => {
|
||||
const calc =
|
||||
rawValidatorScore &&
|
||||
performanceScore &&
|
||||
new BigNumber(totalStake).isGreaterThan(0)
|
||||
? new BigNumber(1).minus(
|
||||
new BigNumber(performanceScore)
|
||||
.times(new BigNumber(rawValidatorScore))
|
||||
.dividedBy(
|
||||
new BigNumber(stakedOnNode).dividedBy(new BigNumber(totalStake))
|
||||
)
|
||||
)
|
||||
: new BigNumber(0);
|
||||
|
||||
return formatNumberPercentage(
|
||||
calc.isPositive() ? calc.times(100) : new BigNumber(0),
|
||||
2
|
||||
);
|
||||
};
|
||||
|
||||
export const getStakePercentage = (total: BigNumber, stakedOnNode: BigNumber) =>
|
||||
total.isEqualTo(0) || stakedOnNode.isEqualTo(0)
|
||||
? '0%'
|
||||
|
||||
@@ -1,14 +1,50 @@
|
||||
import { ToastsContainer, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
Intent,
|
||||
ToastsContainer,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
useToasts,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
useEthereumTransactionToasts,
|
||||
useEthereumWithdrawApprovalsToasts,
|
||||
useVegaTransactionToasts,
|
||||
useWalletDisconnectToastActions,
|
||||
useWalletDisconnectedToasts,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const WalletDisconnectAdditionalContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const { hideToast } = useWalletDisconnectToastActions();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
return (
|
||||
<p className="mt-2">
|
||||
<TradingButton
|
||||
data-testid="connect-vega-wallet"
|
||||
onClick={() => {
|
||||
hideToast();
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
size="small"
|
||||
intent={Intent.Danger}
|
||||
icon={<VegaIcon name={VegaIconNames.ARROW_RIGHT} size={14} />}
|
||||
>
|
||||
<span className="whitespace-nowrap uppercase">{t('Connect')}</span>
|
||||
</TradingButton>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export const ToastsManager = () => {
|
||||
useVegaTransactionToasts();
|
||||
useEthereumTransactionToasts();
|
||||
useEthereumWithdrawApprovalsToasts();
|
||||
useWalletDisconnectedToasts(<WalletDisconnectAdditionalContent />);
|
||||
|
||||
const toasts = useToasts((store) => store.toasts);
|
||||
return <ToastsContainer order="desc" toasts={toasts} />;
|
||||
|
||||
+8
-2
@@ -21,8 +21,14 @@ 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
|
||||
NX_REFERRALS=true
|
||||
NX_TEAM_COMPETITION=true
|
||||
# NX_DISABLE_CLOSE_POSITION=false
|
||||
|
||||
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=
|
||||
|
||||
@@ -21,6 +21,7 @@ NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supp
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
NX_ISOLATED_MARGIN=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -20,6 +20,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
@@ -21,6 +21,7 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=false
|
||||
NX_ICEBERG_ORDERS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -21,6 +21,7 @@ NX_APP_VERSION=v0.20.19-core-0.71.6
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=false
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -21,6 +21,7 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
@@ -22,6 +22,7 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -22,6 +22,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ISOLATED_MARGIN=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { ReactNode } from 'react';
|
||||
import { Web3Provider } from './web3-provider';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { DataLoader } from './data-loader';
|
||||
import { useChainId } from '@vegaprotocol/wallet';
|
||||
|
||||
export const Bootstrapper = ({ children }: { children: ReactNode }) => {
|
||||
const t = useT();
|
||||
@@ -26,13 +27,16 @@ export const Bootstrapper = ({ children }: { children: ReactNode }) => {
|
||||
CHROME_EXTENSION_URL,
|
||||
} = useEnvironment();
|
||||
|
||||
const chainId = useChainId(VEGA_URL);
|
||||
|
||||
if (
|
||||
!VEGA_URL ||
|
||||
!VEGA_WALLET_URL ||
|
||||
!VEGA_EXPLORER_URL ||
|
||||
!CHROME_EXTENSION_URL ||
|
||||
!MOZILLA_EXTENSION_URL ||
|
||||
!DocsLinks
|
||||
!DocsLinks ||
|
||||
!chainId
|
||||
) {
|
||||
return <AppLoader />;
|
||||
}
|
||||
@@ -72,6 +76,7 @@ export const Bootstrapper = ({ children }: { children: ReactNode }) => {
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
chainId,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
|
||||
@@ -25,12 +25,12 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
|
||||
overlays,
|
||||
studies,
|
||||
studySizes,
|
||||
tradingViewStudies,
|
||||
setInterval,
|
||||
setStudies,
|
||||
setStudySizes,
|
||||
setOverlays,
|
||||
setTradingViewStudies,
|
||||
state,
|
||||
setState,
|
||||
} = useChartSettings();
|
||||
|
||||
const pennantChart = (
|
||||
@@ -64,13 +64,13 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
|
||||
libraryHash={CHARTING_LIBRARY_HASH}
|
||||
marketId={marketId}
|
||||
interval={toTradingViewResolution(interval)}
|
||||
studies={tradingViewStudies}
|
||||
onIntervalChange={(newInterval) => {
|
||||
setInterval(fromTradingViewResolution(newInterval));
|
||||
}}
|
||||
onAutoSaveNeeded={(data: { studies: string[] }) => {
|
||||
setTradingViewStudies(data.studies);
|
||||
onAutoSaveNeeded={(data) => {
|
||||
setState(data);
|
||||
}}
|
||||
state={state}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,10 +27,10 @@ describe('ChartMenu', () => {
|
||||
|
||||
render(<ChartMenu />);
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'TradingView' }));
|
||||
await userEvent.click(screen.getByTestId('chartlib-toggle-button'));
|
||||
expect(useChartSettingsStore.getState().chartlib).toEqual('tradingview');
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Vega chart' }));
|
||||
await userEvent.click(screen.getByTestId('chartlib-toggle-button'));
|
||||
expect(useChartSettingsStore.getState().chartlib).toEqual('pennant');
|
||||
});
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ export const ChartMenu = () => {
|
||||
setChartlib(isPennant ? 'tradingview' : 'pennant');
|
||||
}}
|
||||
size="extra-small"
|
||||
testId="chartlib-toggle-button"
|
||||
>
|
||||
{isPennant ? 'TradingView' : t('Vega chart')}
|
||||
</TradingButton>
|
||||
|
||||
@@ -9,6 +9,7 @@ type StudySizes = { [S in Study]?: number };
|
||||
export type Chartlib = 'pennant' | 'tradingview';
|
||||
|
||||
interface StoredSettings {
|
||||
state: object | undefined; // Don't see a better type provided from TradingView type definitions
|
||||
chartlib: Chartlib;
|
||||
// For interval we use the enum from @vegaprotocol/types, this is to make mapping between different
|
||||
// chart types easier and more consistent
|
||||
@@ -17,7 +18,6 @@ interface StoredSettings {
|
||||
overlays: Overlay[];
|
||||
studies: Study[];
|
||||
studySizes: StudySizes;
|
||||
tradingViewStudies: string[];
|
||||
}
|
||||
|
||||
export const STUDY_SIZE = 90;
|
||||
@@ -30,13 +30,13 @@ const STUDY_ORDER: Study[] = [
|
||||
];
|
||||
|
||||
export const DEFAULT_CHART_SETTINGS = {
|
||||
state: undefined,
|
||||
chartlib: 'pennant' as const,
|
||||
interval: Interval.INTERVAL_I15M,
|
||||
type: ChartType.CANDLE,
|
||||
overlays: [Overlay.MOVING_AVERAGE],
|
||||
studies: [Study.MACD, Study.VOLUME],
|
||||
studySizes: {},
|
||||
tradingViewStudies: ['Volume'],
|
||||
};
|
||||
|
||||
export const useChartSettingsStore = create<
|
||||
@@ -47,7 +47,7 @@ export const useChartSettingsStore = create<
|
||||
setStudies: (studies?: Study[]) => void;
|
||||
setStudySizes: (sizes: number[]) => void;
|
||||
setChartlib: (lib: Chartlib) => void;
|
||||
setTradingViewStudies: (studies: string[]) => void;
|
||||
setState: (state: object) => void;
|
||||
}
|
||||
>()(
|
||||
persist(
|
||||
@@ -95,10 +95,8 @@ export const useChartSettingsStore = create<
|
||||
state.chartlib = lib;
|
||||
});
|
||||
},
|
||||
setTradingViewStudies: (studies: string[]) => {
|
||||
set((state) => {
|
||||
state.tradingViewStudies = studies;
|
||||
});
|
||||
setState: (state) => {
|
||||
set({ state });
|
||||
},
|
||||
})),
|
||||
{
|
||||
@@ -147,13 +145,13 @@ export const useChartSettings = () => {
|
||||
overlays,
|
||||
studies,
|
||||
studySizes,
|
||||
tradingViewStudies: settings.tradingViewStudies,
|
||||
setInterval: settings.setInterval,
|
||||
setType: settings.setType,
|
||||
setStudies: settings.setStudies,
|
||||
setOverlays: settings.setOverlays,
|
||||
setStudySizes: settings.setStudySizes,
|
||||
setChartlib: settings.setChartlib,
|
||||
setTradingViewStudies: settings.setTradingViewStudies,
|
||||
state: settings.state,
|
||||
setState: settings.setState,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { GridSettings } from './grid-settings';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
describe('GridSettings', () => {
|
||||
it('calls updateGridStore with correct arguments on button click', async () => {
|
||||
const mockUpdateGridStore = jest.fn();
|
||||
const { getByText } = render(
|
||||
<GridSettings updateGridStore={mockUpdateGridStore} />
|
||||
);
|
||||
|
||||
await userEvent.click(getByText('Reset Columns'));
|
||||
|
||||
expect(mockUpdateGridStore).toHaveBeenCalledWith({
|
||||
columnState: undefined,
|
||||
filterModel: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -162,6 +162,8 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
|
||||
const envNameMapping = useEnvNameMapping();
|
||||
const { VEGA_ENV, VEGA_NETWORKS, GITHUB_FEEDBACK_URL } = useEnvironment();
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
const GOVERNANCE_LINK = useLinks(DApp.Governance)();
|
||||
const EXPLORER_LINK = useLinks(DApp.Explorer)();
|
||||
|
||||
return (
|
||||
<div className="gap-3 lg:flex lg:h-full">
|
||||
@@ -220,7 +222,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
|
||||
</NavbarLink>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<NavbarLinkExternal to={useLinks(DApp.Governance)()}>
|
||||
<NavbarLinkExternal to={GOVERNANCE_LINK}>
|
||||
{t('Governance')}
|
||||
</NavbarLinkExternal>
|
||||
</NavbarItem>
|
||||
@@ -228,6 +230,13 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
|
||||
<NavbarTrigger>{t('Resources')}</NavbarTrigger>
|
||||
<NavbarContent data-testid="navbar-content-resources">
|
||||
<ul className="lg:p-4">
|
||||
{EXPLORER_LINK && (
|
||||
<NavbarSubItem>
|
||||
<NavbarLinkExternal to={EXPLORER_LINK}>
|
||||
{t('Explorer')}
|
||||
</NavbarLinkExternal>
|
||||
</NavbarSubItem>
|
||||
)}
|
||||
{DocsLinks?.NEW_TO_VEGA && (
|
||||
<NavbarSubItem>
|
||||
<NavbarLinkExternal to={DocsLinks?.NEW_TO_VEGA}>
|
||||
|
||||
@@ -98,6 +98,7 @@ describe('ActiveRewards', () => {
|
||||
transferNode={mockTransferNode}
|
||||
currentEpoch={1}
|
||||
kind={mockRecurringTransfer}
|
||||
allMarkets={{}}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,48 +1,46 @@
|
||||
import {
|
||||
useActiveRewardsQuery,
|
||||
useMarketForRewardsQuery,
|
||||
} from './__generated__/Rewards';
|
||||
import { useActiveRewardsQuery } from './__generated__/Rewards';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
Icon,
|
||||
type IconName,
|
||||
type VegaIconSize,
|
||||
Icon,
|
||||
Intent,
|
||||
Tooltip,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
type VegaIconSize,
|
||||
TradingInput,
|
||||
TinyScroll,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import {
|
||||
type Maybe,
|
||||
type Transfer,
|
||||
type TransferNode,
|
||||
type RecurringTransfer,
|
||||
DistributionStrategyDescriptionMapping,
|
||||
DistributionStrategyMapping,
|
||||
EntityScope,
|
||||
EntityScopeMapping,
|
||||
type Maybe,
|
||||
type Transfer,
|
||||
type TransferNode,
|
||||
TransferStatus,
|
||||
TransferStatusMapping,
|
||||
DispatchMetric,
|
||||
DispatchMetricDescription,
|
||||
DispatchMetricLabels,
|
||||
type RecurringTransfer,
|
||||
EntityScopeLabelMapping,
|
||||
MarketState,
|
||||
} from '@vegaprotocol/types';
|
||||
import { Card } from '../card/card';
|
||||
import { useMemo, useState } from 'react';
|
||||
import {
|
||||
type AssetFieldsFragment,
|
||||
useAssetDataProvider,
|
||||
useAssetsMapProvider,
|
||||
} from '@vegaprotocol/assets';
|
||||
import {
|
||||
type MarketFieldsFragment,
|
||||
useMarketsMapProvider,
|
||||
getAsset,
|
||||
} from '@vegaprotocol/markets';
|
||||
|
||||
export type Filter = {
|
||||
@@ -74,7 +72,7 @@ export const isActiveReward = (node: TransferNode, currentEpoch: number) => {
|
||||
export const applyFilter = (
|
||||
node: TransferNode & {
|
||||
asset?: AssetFieldsFragment | null;
|
||||
marketIds?: (MarketFieldsFragment | null)[];
|
||||
markets?: (MarketFieldsFragment | null)[];
|
||||
},
|
||||
filter: Filter
|
||||
) => {
|
||||
@@ -85,6 +83,7 @@ export const applyFilter = (
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
DispatchMetricLabels[transfer.kind.dispatchStrategy.dispatchMetric]
|
||||
.toLowerCase()
|
||||
@@ -98,7 +97,7 @@ export const applyFilter = (
|
||||
node.asset?.name
|
||||
.toLocaleLowerCase()
|
||||
.includes(filter.searchTerm.toLowerCase()) ||
|
||||
node.marketIds?.some((m) =>
|
||||
node.markets?.some((m) =>
|
||||
m?.tradableInstrument?.instrument?.name
|
||||
.toLocaleLowerCase()
|
||||
.includes(filter.searchTerm.toLowerCase())
|
||||
@@ -124,7 +123,7 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
||||
const { data: assets } = useAssetsMapProvider();
|
||||
const { data: markets } = useMarketsMapProvider();
|
||||
|
||||
const transfers = activeRewardsData?.transfersConnection?.edges
|
||||
const enrichedTransfers = activeRewardsData?.transfersConnection?.edges
|
||||
?.map((e) => e?.node as TransferNode)
|
||||
.filter((node) => isActiveReward(node, currentEpoch))
|
||||
.map((node) => {
|
||||
@@ -138,19 +137,19 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
||||
node.transfer.kind.dispatchStrategy?.dispatchMetricAssetId || ''
|
||||
];
|
||||
|
||||
const marketIds =
|
||||
const marketsInScope =
|
||||
node.transfer.kind.dispatchStrategy?.marketIdsInScope?.map(
|
||||
(id) => markets && markets[id]
|
||||
);
|
||||
|
||||
return { ...node, asset, marketIds };
|
||||
return { ...node, asset, markets: marketsInScope };
|
||||
});
|
||||
|
||||
if (!transfers || !transfers.length) return null;
|
||||
if (!enrichedTransfers || !enrichedTransfers.length) return null;
|
||||
|
||||
return (
|
||||
<Card title={t('Active rewards')} className="lg:col-span-full">
|
||||
{transfers.length > 1 && (
|
||||
{enrichedTransfers.length > 1 && (
|
||||
<TradingInput
|
||||
onChange={(e) =>
|
||||
setFilter((curr) => ({ ...curr, searchTerm: e.target.value }))
|
||||
@@ -166,7 +165,7 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
||||
/>
|
||||
)}
|
||||
<TinyScroll className="grid gap-x-8 gap-y-10 h-fit grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] md:grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] lg:grid-cols-[repeat(auto-fill,_minmax(320px,_1fr))] xl:grid-cols-[repeat(auto-fill,_minmax(335px,_1fr))] max-h-[40rem] overflow-auto pr-2">
|
||||
{transfers
|
||||
{enrichedTransfers
|
||||
.filter((n) => applyFilter(n, filter))
|
||||
.map((node, i) => {
|
||||
const { transfer } = node;
|
||||
@@ -184,6 +183,7 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
||||
transferNode={node}
|
||||
kind={transfer.kind}
|
||||
currentEpoch={currentEpoch}
|
||||
allMarkets={markets || {}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
@@ -207,14 +207,8 @@ const StatusIndicator = ({
|
||||
switch (status) {
|
||||
case TransferStatus.STATUS_DONE:
|
||||
return { icon: IconNames.TICK_CIRCLE, intent: Intent.Success };
|
||||
case TransferStatus.STATUS_CANCELLED:
|
||||
return { icon: IconNames.MOON, intent: Intent.None };
|
||||
case TransferStatus.STATUS_PENDING:
|
||||
return { icon: IconNames.HELP, intent: Intent.Primary };
|
||||
case TransferStatus.STATUS_REJECTED:
|
||||
return { icon: IconNames.ERROR, intent: Intent.Danger };
|
||||
case TransferStatus.STATUS_STOPPED:
|
||||
return { icon: IconNames.ERROR, intent: Intent.Danger };
|
||||
default:
|
||||
return { icon: IconNames.HELP, intent: Intent.Primary };
|
||||
}
|
||||
@@ -253,49 +247,117 @@ export const ActiveRewardCard = ({
|
||||
transferNode,
|
||||
currentEpoch,
|
||||
kind,
|
||||
allMarkets,
|
||||
}: {
|
||||
transferNode: TransferNode;
|
||||
transferNode: TransferNode & {
|
||||
asset?: AssetFieldsFragment | null;
|
||||
markets?: (MarketFieldsFragment | null)[];
|
||||
};
|
||||
currentEpoch: number;
|
||||
kind: RecurringTransfer;
|
||||
allMarkets?: Record<string, MarketFieldsFragment | null>;
|
||||
}) => {
|
||||
const t = useT();
|
||||
|
||||
const { transfer } = transferNode;
|
||||
const { dispatchStrategy } = kind;
|
||||
const marketIds = dispatchStrategy?.marketIdsInScope;
|
||||
|
||||
const { data: marketNameData } = useMarketForRewardsQuery({
|
||||
variables: {
|
||||
marketId: marketIds ? marketIds[0] : '',
|
||||
},
|
||||
});
|
||||
const marketIdsInScope = dispatchStrategy?.marketIdsInScope;
|
||||
const firstMarketData = transferNode.markets?.[0];
|
||||
|
||||
const marketName = useMemo(() => {
|
||||
if (marketNameData && marketIds && marketIds.length > 1) {
|
||||
return 'Specific markets';
|
||||
} else if (
|
||||
marketNameData &&
|
||||
marketIds &&
|
||||
marketNameData &&
|
||||
marketIds.length === 1
|
||||
const specificMarkets = useMemo(() => {
|
||||
if (
|
||||
!firstMarketData ||
|
||||
!marketIdsInScope ||
|
||||
marketIdsInScope.length === 0
|
||||
) {
|
||||
return marketNameData?.market?.tradableInstrument?.instrument?.name || '';
|
||||
return null;
|
||||
}
|
||||
return '';
|
||||
}, [marketIds, marketNameData]);
|
||||
if (marketIdsInScope.length > 1) {
|
||||
const marketNames =
|
||||
allMarkets &&
|
||||
marketIdsInScope
|
||||
.map((id) => allMarkets[id]?.tradableInstrument?.instrument?.name)
|
||||
.join(', ');
|
||||
|
||||
const { data: dispatchAsset } = useAssetDataProvider(
|
||||
dispatchStrategy?.dispatchMetricAssetId || ''
|
||||
);
|
||||
return (
|
||||
<Tooltip description={marketNames}>
|
||||
<span>Specific markets</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span>{firstMarketData?.tradableInstrument?.instrument?.name || ''}</span>
|
||||
);
|
||||
}, [firstMarketData, marketIdsInScope, allMarkets]);
|
||||
|
||||
const dispatchAsset = transferNode.asset;
|
||||
|
||||
if (!dispatchStrategy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { gradientClassName, mainClassName } = getGradientClasses(
|
||||
dispatchStrategy.dispatchMetric
|
||||
// Gray out/hide the cards that are related to not trading markets
|
||||
const marketSettled = transferNode.markets?.some(
|
||||
(m) =>
|
||||
m?.state &&
|
||||
[
|
||||
MarketState.STATE_TRADING_TERMINATED,
|
||||
MarketState.STATE_SETTLED,
|
||||
MarketState.STATE_CANCELLED,
|
||||
MarketState.STATE_CLOSED,
|
||||
].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(
|
||||
(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
|
||||
const { gradientClassName, mainClassName } =
|
||||
suspended || assetInSuspendedMarket || assetInSettledMarket
|
||||
? {
|
||||
gradientClassName: 'from-vega-cdark-500 to-vega-clight-400',
|
||||
mainClassName: 'from-vega-cdark-400 dark:from-vega-cdark-600 to-20%',
|
||||
}
|
||||
: getGradientClasses(dispatchStrategy.dispatchMetric);
|
||||
|
||||
const entityScope = dispatchStrategy.entityScope;
|
||||
return (
|
||||
<div>
|
||||
@@ -373,8 +435,20 @@ export const ActiveRewardCard = ({
|
||||
|
||||
<span className="border-[0.5px] border-gray-700" />
|
||||
<span>
|
||||
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]}
|
||||
{marketName ? ` • ${marketName}` : ` • ${dispatchAsset?.name}`}
|
||||
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]} •{' '}
|
||||
<Tooltip
|
||||
underline={suspended}
|
||||
description={
|
||||
(suspended || assetInSuspendedMarket) &&
|
||||
(specificMarkets
|
||||
? t('Eligible market(s) currently suspended')
|
||||
: assetInSuspendedMarket
|
||||
? t('Currently no markets eligible for reward')
|
||||
: '')
|
||||
}
|
||||
>
|
||||
<span>{specificMarkets || dispatchAsset?.name}</span>
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
<div className="flex items-center gap-8 flex-wrap">
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
||||
VEGA_VERSION=v0.73.10
|
||||
VEGA_VERSION=v0.74.0-preview.2
|
||||
LOCAL_SERVER=false
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
|
||||
VEGA_VERSION=v0.73.10
|
||||
LOCAL_SERVER=false
|
||||
VEGA_VERSION=v0.74.0-preview.2
|
||||
LOCAL_SERVER=false
|
||||
|
||||
@@ -25,7 +25,7 @@ def setup_simple_market(
|
||||
|
||||
vega.mint(
|
||||
MM_WALLET.name,
|
||||
asset="VOTE",
|
||||
asset=vega.find_asset_id(symbol="VOTE", enabled=True),
|
||||
amount=mint_amount,
|
||||
)
|
||||
|
||||
@@ -207,7 +207,7 @@ def setup_perps_market(
|
||||
|
||||
vega.mint(
|
||||
MM_WALLET.name,
|
||||
asset="VOTE",
|
||||
asset=vega.find_asset_id(symbol="VOTE", enabled=True),
|
||||
amount=mint_amount,
|
||||
)
|
||||
|
||||
|
||||
Generated
+3
-3
@@ -1160,8 +1160,8 @@ profile = ["pytest-profiling", "snakeviz"]
|
||||
[package.source]
|
||||
type = "git"
|
||||
url = "https://github.com/vegaprotocol/vega-market-sim.git/"
|
||||
reference = "fix/genesis_panic"
|
||||
resolved_reference = "de30d2d4c7a1b81a830527ca76473e23ef59de12"
|
||||
reference = "HEAD"
|
||||
resolved_reference = "4440abbb6ce0d3e80beba5cd01f20cd21983cbf8"
|
||||
|
||||
[[package]]
|
||||
name = "websocket-client"
|
||||
@@ -1342,4 +1342,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<3.11"
|
||||
content-hash = "68ed0de55290a3b929d47eb7f7b031fb7e172261c7bbeb4f554b7c27a4462754"
|
||||
content-hash = "39ce8400de7bf060857447281ef27bd78c9b1d9639da063b051e3ae6e7887a67"
|
||||
|
||||
@@ -9,7 +9,7 @@ packages = [{include = "trading market-sim e2e"}]
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<3.11"
|
||||
psutil = "^5.9.5"
|
||||
vega-sim = {git = "https://github.com/vegaprotocol/vega-market-sim.git/", branch = "fix/genesis_panic"}
|
||||
vega-sim = {git = "https://github.com/vegaprotocol/vega-market-sim.git/"}
|
||||
pytest-playwright = "^0.4.2"
|
||||
docker = "^6.1.3"
|
||||
pytest-xdist = "^3.3.1"
|
||||
|
||||
+36
-23
@@ -14,15 +14,18 @@ market_order = "order-type-Market"
|
||||
tif = "order-tif"
|
||||
expire = "expire"
|
||||
|
||||
|
||||
@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.usefixtures("auth", "risk_accepted")
|
||||
def test_limit_buy_order_GTT(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
@@ -42,31 +45,26 @@ def test_limit_buy_order_GTT(continuous_market, vega: VegaServiceNull, page: Pag
|
||||
)
|
||||
page.get_by_test_id(place_order).click()
|
||||
wait_for_toast_confirmation(page)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.get_by_test_id("All").click()
|
||||
# 7002-SORD-017
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10+10LimitFilled120.00GTT:"
|
||||
)
|
||||
expect(page.get_by_role("row").nth(5)).to_contain_text("10+10LimitFilled120.00GTT:")
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_limit_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
|
||||
page.get_by_test_id(order_size).fill("10")
|
||||
page.get_by_test_id(order_price).fill("120")
|
||||
page.get_by_test_id(place_order).click()
|
||||
wait_for_toast_confirmation(page)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_fn(2)
|
||||
vega.wait_for_total_catchup()
|
||||
page.get_by_test_id("All").click()
|
||||
# 7002-SORD-017
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10+10LimitFilled120.00GTC"
|
||||
)
|
||||
expect(page.get_by_role("row").nth(6)).to_contain_text("10+10LimitFilled120.00GTC")
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
@@ -84,13 +82,11 @@ def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
)
|
||||
page.get_by_test_id(place_order).click()
|
||||
wait_for_toast_confirmation(page)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.get_by_test_id("All").click()
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10-10LimitFilled100.00GFN"
|
||||
)
|
||||
expect(page.get_by_role("row").nth(7)).to_contain_text("10-10LimitFilled100.00GFN")
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
@@ -107,14 +103,12 @@ def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page)
|
||||
)
|
||||
page.get_by_test_id(place_order).click()
|
||||
wait_for_toast_confirmation(page)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
|
||||
page.get_by_test_id("All").click()
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10-10MarketFilled-IOC"
|
||||
)
|
||||
expect(page.get_by_role("row").nth(8)).to_contain_text("10-10MarketFilled-IOC")
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_market_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
@@ -124,13 +118,32 @@ def test_market_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
page.get_by_test_id(tif).select_option("Fill or Kill (FOK)")
|
||||
page.get_by_test_id(place_order).click()
|
||||
wait_for_toast_confirmation(page)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.get_by_test_id("All").click()
|
||||
# 7002-SORD-010
|
||||
# 0003-WTXN-012
|
||||
# 0003-WTXN-003
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10+10MarketFilled-FOK"
|
||||
)
|
||||
expect(page.get_by_role("row").nth(9)).to_contain_text("10+10MarketFilled-FOK")
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_sidebar_should_be_open_after_reload(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
expect(page.get_by_test_id("deal-ticket-form")).to_be_visible()
|
||||
page.get_by_test_id("Order").click()
|
||||
expect(page.get_by_test_id("deal-ticket-form")).not_to_be_visible()
|
||||
page.reload()
|
||||
expect(page.get_by_test_id("deal-ticket-form")).to_be_visible()
|
||||
|
||||
@pytest.mark.skip("We currently can't approve wallet connection through Sim")
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_connect_vega_wallet(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id("order-price").fill("101")
|
||||
page.get_by_test_id("order-connect-wallet").click()
|
||||
expect(page.locator('[role="dialog"]')).to_be_visible()
|
||||
page.get_by_test_id("connector-jsonRpc").click()
|
||||
expect(page.get_by_test_id("wallet-dialog-title")).to_be_visible()
|
||||
# TODO: accept wallet connection and assert wallet is connected.
|
||||
expect(page.get_by_test_id("order-type-Limit")).to_be_checked()
|
||||
expect(page.get_by_test_id("order-price")).to_have_value("101")
|
||||
@@ -1,35 +0,0 @@
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
from conftest import init_vega
|
||||
from fixtures.market import setup_continuous_market
|
||||
|
||||
@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("We currently can't approve wallet connection through Sim")
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_connect_vega_wallet(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id("order-price").fill("101")
|
||||
page.get_by_test_id("order-connect-wallet").click()
|
||||
expect(page.locator('[role="dialog"]')).to_be_visible()
|
||||
page.get_by_test_id("connector-jsonRpc").click()
|
||||
expect(page.get_by_test_id("wallet-dialog-title")).to_be_visible()
|
||||
# TODO: accept wallet connection and assert wallet is connected.
|
||||
expect(page.get_by_test_id("order-type-Limit")).to_be_checked()
|
||||
expect(page.get_by_test_id("order-price")).to_have_value("101")
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_sidebar_should_be_open_after_reload(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
expect(page.get_by_test_id("deal-ticket-form")).to_be_visible()
|
||||
page.get_by_test_id("Order").click()
|
||||
expect(page.get_by_test_id("deal-ticket-form")).not_to_be_visible()
|
||||
page.reload()
|
||||
expect(page.get_by_test_id("deal-ticket-form")).to_be_visible()
|
||||
@@ -0,0 +1,53 @@
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
from vega_sim.null_service import VegaServiceNull
|
||||
from actions.vega import submit_order
|
||||
from actions.utils import next_epoch, wait_for_toast_confirmation
|
||||
|
||||
tooltip_content = "tooltip-content"
|
||||
leverage_input = "#leverage-input"
|
||||
tab_positions = "tab-positions"
|
||||
margin_row = '[col-id="margin"]'
|
||||
|
||||
|
||||
def create_position(vega: VegaServiceNull, market_id):
|
||||
submit_order(vega, "Key 1", market_id, "SIDE_SELL", 100, 110)
|
||||
submit_order(vega, "Key 1", market_id, "SIDE_BUY", 100, 110)
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_switch_cross_isolated_margin(
|
||||
continuous_market, vega: VegaServiceNull, page: Page):
|
||||
create_position(vega, continuous_market)
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
expect(page.locator(margin_row).nth(1)).to_have_text("874.21992Cross1.0x")
|
||||
# tbd - tooltip is not visible without this wait
|
||||
page.wait_for_timeout(1000)
|
||||
page.get_by_test_id(tab_positions).get_by_text("Cross").hover()
|
||||
expect(page.get_by_test_id(tooltip_content).nth(0)).to_have_text(
|
||||
"Liquidation: 582.81328Margin: 874.21992General account: 998,084.95183"
|
||||
)
|
||||
page.get_by_role("button", name="Isolated 10x").click()
|
||||
page.locator(leverage_input).clear()
|
||||
page.locator(leverage_input).type("1")
|
||||
page.get_by_role("button", name="Confirm").click()
|
||||
wait_for_toast_confirmation(page)
|
||||
next_epoch(vega=vega)
|
||||
expect(page.get_by_test_id("toast-content")).to_have_text(
|
||||
"ConfirmedYour transaction has been confirmedView in block explorerUpdate margin modeBTC:DAI_2023Isolated margin mode, leverage: 1.0x")
|
||||
expect(page.locator(margin_row).nth(1)
|
||||
).to_have_text("11,109.99996Isolated1.0x")
|
||||
# tbd - tooltip is not visible without this wait
|
||||
page.wait_for_timeout(1000)
|
||||
page.get_by_test_id(tab_positions).get_by_text("Isolated").hover()
|
||||
expect(page.get_by_test_id(tooltip_content).nth(0)).to_have_text(
|
||||
"Liquidation: 583.62409Margin: 11,109.99996Order: 11,000.00"
|
||||
)
|
||||
page.get_by_role("button", name="Cross").click()
|
||||
page.get_by_role("button", name="Confirm").click()
|
||||
wait_for_toast_confirmation(page)
|
||||
next_epoch(vega=vega)
|
||||
expect(page.locator(margin_row).nth(1)).to_have_text(
|
||||
"22,109.99996Cross1.0x")
|
||||
@@ -27,7 +27,7 @@ submit_stop_order = "place-order"
|
||||
stop_orders_tab = "Stop orders"
|
||||
row_table = "row"
|
||||
cancel = "cancel"
|
||||
market_name_col = '[col-id="market.tradableInstrument.instrument.code"]'
|
||||
market_name_col = '[data-testid="market-code"]'
|
||||
trigger_col = '[col-id="trigger"]'
|
||||
expiresAt_col = '[col-id="expiresAt"]'
|
||||
size_col = '[col-id="submission.size"]'
|
||||
@@ -41,7 +41,6 @@ close_toast = "toast-close"
|
||||
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.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup
|
||||
|
||||
@@ -78,7 +77,6 @@ def test_submit_stop_order_rejected(continuous_market, vega: VegaServiceNull, pa
|
||||
page.get_by_test_id(trigger_price).fill("103")
|
||||
page.get_by_test_id(order_size).fill("3")
|
||||
page.get_by_test_id(submit_stop_order).click()
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.get_by_test_id(close_toast).click()
|
||||
@@ -269,82 +267,6 @@ class TestStopOcoValidation:
|
||||
def continuous_market(self, vega):
|
||||
return setup_continuous_market(vega)
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_stop_market_order_form_validation(self, continuous_market, page: Page):
|
||||
# 7002-SORD-052
|
||||
# 7002-SORD-055
|
||||
# 7002-SORD-056
|
||||
# 7002-SORD-057
|
||||
# 7002-SORD-058
|
||||
# 7002-SORD-064
|
||||
# 7002-SORD-065
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id(stop_order_btn).click()
|
||||
page.get_by_test_id(stop_market_order_btn).is_visible()
|
||||
page.get_by_test_id(stop_market_order_btn).click()
|
||||
expect(
|
||||
page.get_by_test_id("sidebar-content").get_by_text("Trigger").first
|
||||
).to_be_visible()
|
||||
expect(page.locator('[for="triggerDirection-risesAbove"]')).to_have_text(
|
||||
"Rises above"
|
||||
)
|
||||
expect(page.locator('[for="triggerDirection-fallsBelow"]')).to_have_text(
|
||||
"Falls below"
|
||||
)
|
||||
page.get_by_test_id(trigger_price).click()
|
||||
expect(page.get_by_test_id(trigger_price)).to_be_empty
|
||||
expect(page.locator('[for="triggerType-price"]')).to_have_text("Price")
|
||||
expect(page.locator('[for="triggerType-trailingPercentOffset"]')).to_have_text(
|
||||
"Trailing Percent Offset"
|
||||
)
|
||||
expect(page.locator('[for="order-size"]')).to_have_text("Size")
|
||||
page.get_by_test_id(order_size).click()
|
||||
expect(page.get_by_test_id(order_size)).to_be_empty
|
||||
expect(page.get_by_test_id(order_price)).not_to_be_visible()
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_stop_limit_order_form_validation(self, continuous_market, page: Page):
|
||||
# 7002-SORD-020
|
||||
# 7002-SORD-021
|
||||
# 7002-SORD-022
|
||||
# 7002-SORD-033
|
||||
# 7002-SORD-034
|
||||
# 7002-SORD-035
|
||||
# 7002-SORD-036
|
||||
# 7002-SORD-037
|
||||
# 7002-SORD-038
|
||||
# 7002-SORD-049
|
||||
# 7002-SORD-050
|
||||
# 7002-SORD-051
|
||||
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id(stop_order_btn).click()
|
||||
page.get_by_test_id(stop_limit_order_btn).is_visible()
|
||||
page.get_by_test_id(stop_limit_order_btn).click()
|
||||
expect(
|
||||
page.get_by_test_id("sidebar-content").get_by_text("Trigger").first
|
||||
).to_be_visible()
|
||||
expect(page.locator('[for="triggerDirection-risesAbove"]')).to_have_text(
|
||||
"Rises above"
|
||||
)
|
||||
expect(page.locator('[for="triggerDirection-risesAbove"]')).to_be_checked
|
||||
expect(page.locator('[for="triggerDirection-fallsBelow"]')).to_have_text(
|
||||
"Falls below"
|
||||
)
|
||||
page.get_by_test_id(trigger_price).click()
|
||||
expect(page.get_by_test_id(trigger_price)).to_be_empty
|
||||
expect(page.locator('[for="triggerType-price"]')).to_have_text("Price")
|
||||
expect(page.locator('[for="triggerType-price"]')).to_be_checked
|
||||
expect(page.locator('[for="triggerType-trailingPercentOffset"]')).to_have_text(
|
||||
"Trailing Percent Offset"
|
||||
)
|
||||
expect(page.locator('[for="order-size"]').first).to_have_text("Size")
|
||||
expect(page.locator('[for="order-price"]').last).to_have_text("Price")
|
||||
page.get_by_test_id(order_size).click()
|
||||
expect(page.get_by_test_id(order_size)).to_be_empty
|
||||
page.get_by_test_id(order_price).click()
|
||||
expect(page.get_by_test_id(order_price)).to_be_empty()
|
||||
|
||||
@pytest.mark.skip("core issue")
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_maximum_number_of_active_stop_orders(
|
||||
|
||||
@@ -53,7 +53,7 @@ FEE_BREAKDOWN_TOOLTIP = "fee-breakdown-tooltip"
|
||||
PINNED_ROW_LOCATOR = ".ag-pinned-left-cols-container .ag-row"
|
||||
ROW_LOCATOR = ".ag-center-cols-container .ag-row"
|
||||
# Col-Ids:
|
||||
COL_INSTRUMENT_CODE = '[col-id="market.tradableInstrument.instrument.code"]'
|
||||
COL_INSTRUMENT_CODE = '[data-testid="market-code"]'
|
||||
COL_CODE = '[col-id="code"]'
|
||||
COL_SIZE = '[col-id="size"]'
|
||||
COL_PRICE = '[col-id="price"]'
|
||||
@@ -563,7 +563,6 @@ def test_fills_taker_discount_program(
|
||||
page.goto(f"/#/markets/{market_id}")
|
||||
page.get_by_test_id(FILLS).click()
|
||||
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
|
||||
expect(row.locator(COL_INSTRUMENT_CODE)).to_have_text("BTC:DAI_2023Futr")
|
||||
expect(row.locator(COL_SIZE)).to_have_text(size)
|
||||
expect(row.locator(COL_PRICE)).to_have_text("103.50 tDAI")
|
||||
expect(row.locator(COL_PRICE_1)).to_have_text(price_1)
|
||||
@@ -605,7 +604,6 @@ def test_fills_maker_discount_program(
|
||||
change_keys(page, vega_instance, MM_WALLET.name)
|
||||
page.get_by_test_id(FILLS).click()
|
||||
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
|
||||
expect(row.locator(COL_INSTRUMENT_CODE)).to_have_text("BTC:DAI_2023Futr")
|
||||
expect(row.locator(COL_SIZE)).to_have_text(size)
|
||||
expect(row.locator(COL_PRICE)).to_have_text("103.50 tDAI")
|
||||
expect(row.locator(COL_PRICE_1)).to_have_text(price_1)
|
||||
@@ -683,4 +681,4 @@ def test_fills_taker_fee_tooltip_discount_program(
|
||||
row.locator(COL_FEE).hover()
|
||||
expect(page.get_by_test_id(FEE_BREAKDOWN_TOOLTIP)).to_have_text(
|
||||
f"If the market was activeFees to be paid by the taker; discounts are already applied.Infrastructure fee{infra_fee} tDAILiquidity fee0.00 tDAIMaker fee{maker_fee} tDAITotal fees{total_fee} tDAI"
|
||||
)
|
||||
)
|
||||
@@ -77,7 +77,7 @@ class TestGetStarted:
|
||||
|
||||
vega.mint(
|
||||
MM_WALLET.name,
|
||||
asset="VOTE",
|
||||
asset=vega.find_asset_id(symbol="VOTE", enabled=True),
|
||||
amount=mint_amount,
|
||||
)
|
||||
|
||||
@@ -105,6 +105,8 @@ class TestGetStarted:
|
||||
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.reload()
|
||||
|
||||
# Assert step 2 complete
|
||||
expect(page.get_by_test_id("icon-tick")).to_have_count(2)
|
||||
|
||||
@@ -144,29 +146,6 @@ class TestGetStarted:
|
||||
# 0007-FUGS-007
|
||||
expect(page.get_by_test_id("dialog-content").nth(1)).to_be_visible()
|
||||
|
||||
def test_browser_wallet_installed(self, simple_market, page: Page):
|
||||
page.add_init_script("window.vega = {}")
|
||||
page.goto(f"/#/markets/{simple_market}")
|
||||
locator = page.get_by_test_id("connect-vega-wallet")
|
||||
page.wait_for_selector('[data-testid="connect-vega-wallet"]', state="attached")
|
||||
expect(locator).to_be_enabled
|
||||
expect(locator).to_be_visible
|
||||
expect(locator).to_have_text("Connect")
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_get_started_deal_ticket(self,simple_market, page: Page):
|
||||
page.goto(f"/#/markets/{simple_market}")
|
||||
expect(page.get_by_test_id("order-connect-wallet")).to_have_text("Connect wallet")
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_browser_wallet_installed_deal_ticket(simple_market, page: Page):
|
||||
page.add_init_script("window.vega = {}")
|
||||
page.goto(f"/#/markets/{simple_market}")
|
||||
# 0007-FUGS-013
|
||||
page.wait_for_selector('[data-testid="sidebar-content"]', state="visible")
|
||||
expect(page.get_by_test_id("get-started-banner")).not_to_be_visible()
|
||||
|
||||
@pytest.mark.skip("tbd-market-sim")
|
||||
def test_redirect_default_market(self, continuous_market, vega: VegaServiceNull, page: Page):
|
||||
page.goto("/")
|
||||
@@ -177,11 +156,3 @@ class TestGetStarted:
|
||||
page.get_by_test_id("icon-cross").click()
|
||||
# 0007-FUGS-018
|
||||
expect(page.get_by_test_id("welcome-dialog")).not_to_be_visible()
|
||||
|
||||
class TestBrowseAll:
|
||||
def test_get_started_browse_all(self, simple_market, vega: VegaServiceNull, page: Page):
|
||||
page.goto("/")
|
||||
print(simple_market)
|
||||
page.get_by_test_id("browse-markets-button").click()
|
||||
# 0007-FUGS-005
|
||||
expect(page).to_have_url(f"http://localhost:{vega.console_port}/#/markets/{simple_market}")
|
||||
|
||||
@@ -35,7 +35,6 @@ class TestIcebergOrdersValidations:
|
||||
"Awaiting confirmationPlease wait for your transaction to be confirmedView in block explorer"
|
||||
)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
expect(page.get_by_test_id("toast-content")).to_have_text(
|
||||
@@ -51,7 +50,6 @@ def test_iceberg_open_order(continuous_market, vega: VegaServiceNull, page: Page
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
|
||||
submit_order(vega, "Key 1", continuous_market, "SIDE_SELL", 102, 101, 2, 1)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
@@ -84,7 +82,6 @@ def test_iceberg_open_order(continuous_market, vega: VegaServiceNull, page: Page
|
||||
|
||||
submit_order(vega, MM_WALLET2.name, continuous_market, "SIDE_BUY", 103, 101)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
expect(
|
||||
|
||||
@@ -16,34 +16,44 @@ def vega(request):
|
||||
def continuous_market(vega):
|
||||
return setup_continuous_market(vega)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_liquidity_provision_amendment(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
def test_liquidity_provision_amendment(
|
||||
continuous_market, vega: VegaServiceNull, page: Page
|
||||
):
|
||||
# TODO Refactor asserting the grid
|
||||
page.goto(f"/#/liquidity/{continuous_market}")
|
||||
change_keys(page, vega, "market_maker")
|
||||
row = page.get_by_test_id(
|
||||
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
|
||||
expect(row).to_contain_text(
|
||||
"Active"
|
||||
row = (
|
||||
page.get_by_test_id("tab-myLP")
|
||||
.locator(".ag-center-cols-container .ag-row")
|
||||
.first
|
||||
)
|
||||
expect(row).to_contain_text("Active")
|
||||
# 5002-LIQP-006
|
||||
expect(page.get_by_test_id("target-stake")
|
||||
).to_have_text("Target stake5.82757 tDAI")
|
||||
expect(page.get_by_test_id("target-stake")).to_have_text("Target stake5.82757 tDAI")
|
||||
# 5002-LIQP-007
|
||||
expect(page.get_by_test_id("supplied-stake")
|
||||
).to_have_text("Supplied stake10,000.00 tDAI")
|
||||
expect(page.get_by_test_id("supplied-stake")).to_have_text(
|
||||
"Supplied stake10,000.00 tDAI"
|
||||
)
|
||||
# 5002-LIQP-008
|
||||
expect(page.get_by_test_id("liquidity-supplied")
|
||||
).to_have_text("Liquidity supplied 171,598.11%")
|
||||
expect(page.get_by_test_id("liquidity-supplied")).to_have_text(
|
||||
"Liquidity supplied 171,598.11%"
|
||||
)
|
||||
expect(page.get_by_test_id("fees-paid")).to_have_text("Fees paid-")
|
||||
# 5002-LIQP-009
|
||||
expect(page.get_by_test_id("liquidity-market-id")
|
||||
).to_have_text("Market ID" + truncate_middle(continuous_market))
|
||||
expect(page.get_by_test_id("liquidity-learn-more")
|
||||
).to_have_text("Learn moreProviding liquidity")
|
||||
expect(page.get_by_test_id("liquidity-market-id")).to_have_text(
|
||||
"Market ID" + truncate_middle(continuous_market)
|
||||
)
|
||||
expect(page.get_by_test_id("liquidity-learn-more")).to_have_text(
|
||||
"Learn moreProviding liquidity"
|
||||
)
|
||||
# 002-LIQP-010
|
||||
expect(page.get_by_test_id("liquidity-learn-more").get_by_test_id("external-link")
|
||||
).to_have_attribute("href", "https://docs.vega.xyz/testnet/concepts/liquidity/provision")
|
||||
expect(
|
||||
page.get_by_test_id("liquidity-learn-more").get_by_test_id("external-link")
|
||||
).to_have_attribute(
|
||||
"href", "https://docs.vega.xyz/testnet/concepts/liquidity/provision"
|
||||
)
|
||||
|
||||
vega.submit_simple_liquidity(
|
||||
key_name="market_maker",
|
||||
@@ -56,41 +66,50 @@ def test_liquidity_provision_amendment(continuous_market, vega: VegaServiceNull,
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.reload()
|
||||
row = page.get_by_test_id(
|
||||
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
|
||||
expect(row).to_contain_text(
|
||||
"Updating next epoch"
|
||||
row = (
|
||||
page.get_by_test_id("tab-myLP")
|
||||
.locator(".ag-center-cols-container .ag-row")
|
||||
.first
|
||||
)
|
||||
expect(row).to_contain_text("Updating next epoch")
|
||||
next_epoch(vega=vega)
|
||||
page.reload()
|
||||
expect(page.get_by_test_id("supplied-stake")).to_have_text(
|
||||
"Supplied stake1.00001 tDAI"
|
||||
)
|
||||
expect(page.get_by_test_id("liquidity-supplied")).to_have_text(
|
||||
"Liquidity supplied 17.16%"
|
||||
)
|
||||
row = (
|
||||
page.get_by_test_id("tab-myLP")
|
||||
.locator(".ag-center-cols-container .ag-row")
|
||||
.first
|
||||
)
|
||||
expect(row).to_contain_text("Active")
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_liquidity_provision_cancelled(
|
||||
continuous_market, vega: VegaServiceNull, page: Page
|
||||
):
|
||||
page.goto(f"/#/liquidity/{continuous_market}")
|
||||
change_keys(page, vega, "market_maker")
|
||||
row = (
|
||||
page.get_by_test_id("tab-myLP")
|
||||
.locator(".ag-center-cols-container .ag-row")
|
||||
.first
|
||||
)
|
||||
expect(row).to_contain_text("Active")
|
||||
vega.cancel_liquidity(
|
||||
key_name="market_maker",
|
||||
market_id=continuous_market,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
page.reload()
|
||||
expect(page.get_by_test_id("supplied-stake")
|
||||
).to_have_text("Supplied stake1.00001 tDAI")
|
||||
expect(page.get_by_test_id("liquidity-supplied")
|
||||
).to_have_text("Liquidity supplied 17.16%")
|
||||
row = page.get_by_test_id(
|
||||
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
|
||||
expect(row).to_contain_text(
|
||||
"Active"
|
||||
expect(page.get_by_test_id("supplied-stake")).to_have_text(
|
||||
"Supplied stake0.00 tDAI"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip("Waiting for the ability to cancel LP")
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_liquidity_provision_inactive(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
# TODO Refactor asserting the grid
|
||||
page.goto(f"/#/liquidity/{continuous_market}")
|
||||
change_keys(page, vega, "market_maker")
|
||||
row = page.get_by_test_id(
|
||||
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
|
||||
expect(row).to_contain_text(
|
||||
"Active"
|
||||
expect(page.get_by_test_id("liquidity-supplied")).to_have_text(
|
||||
"Liquidity supplied 0.00%"
|
||||
)
|
||||
vega.submit_simple_liquidity(
|
||||
key_name="market_maker",
|
||||
market_id=continuous_market,
|
||||
commitment_amount=0,
|
||||
fee=0,
|
||||
is_amendment=False,
|
||||
)
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
expect(page.locator(".ag-overlay-panel")).to_have_text("No data")
|
||||
|
||||
@@ -5,6 +5,7 @@ from vega_sim.null_service import VegaServiceNull
|
||||
from playwright.sync_api import Page, expect
|
||||
from fixtures.market import setup_continuous_market
|
||||
from conftest import init_vega
|
||||
from actions.utils import next_epoch
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@@ -21,9 +22,7 @@ def create_settled_market(vega: VegaServiceNull):
|
||||
settlement_price=110,
|
||||
market_id=market_id,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(10)
|
||||
vega.wait_for_total_catchup()
|
||||
next_epoch(vega=vega)
|
||||
|
||||
|
||||
class TestSettledMarket:
|
||||
@@ -123,15 +122,13 @@ def test_terminated_market_no_settlement_date(page: Page, vega: VegaServiceNull)
|
||||
payload={"trading.terminated": "true"},
|
||||
key_name="FJMKnwfZdd48C8NqvYrG",
|
||||
)
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(10)
|
||||
vega.wait_for_total_catchup()
|
||||
next_epoch(vega=vega)
|
||||
page.goto(f"/#/markets/all")
|
||||
page.get_by_test_id("Closed markets").click()
|
||||
row_selector = page.locator(
|
||||
'[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row'
|
||||
).first
|
||||
expect(row_selector.locator('[col-id="state"]')).to_have_text("No trading")
|
||||
expect(row_selector.locator('[col-id="state"]')).to_have_text("Trading Terminated")
|
||||
expect(row_selector.locator('[col-id="settlementDate"]')).to_have_text("Unknown")
|
||||
|
||||
# TODO Create test for terminated market with settlement date in future
|
||||
|
||||
@@ -1,222 +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 change_keys
|
||||
from wallet_config import MM_WALLET, MM_WALLET2
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
table_row_selector = (
|
||||
'[data-testid="tab-open-markets"] .ag-center-cols-container .ag-row'
|
||||
)
|
||||
trading_mode_col = '[col-id="tradingMode"]'
|
||||
state_col = '[col-id="state"]'
|
||||
item_value = "item-value"
|
||||
price_monitoring_bounds_row = "key-value-table-row"
|
||||
market_trading_mode = "market-trading-mode"
|
||||
market_state = "market-state"
|
||||
liquidity_supplied = "liquidity-supplied"
|
||||
item_value = "item-value"
|
||||
price_monitoring_bounds_row = "key-value-table-row"
|
||||
market_trading_mode = "market-trading-mode"
|
||||
market_state = "market-state"
|
||||
liquidity_supplied = "liquidity-supplied"
|
||||
|
||||
initial_commitment: float = 100
|
||||
initial_price: float = 1
|
||||
initial_volume: float = 1
|
||||
initial_spread: float = 0.1
|
||||
market_name = "BTC:DAI_2023"
|
||||
|
||||
@pytest.mark.skip("tbd")
|
||||
@pytest.mark.usefixtures("risk_accepted", "auth")
|
||||
def test_price_monitoring(simple_market, vega: VegaServiceNull, page: Page):
|
||||
page.goto(f"/#/markets/all")
|
||||
expect(page.locator(table_row_selector).locator(trading_mode_col)).to_have_text(
|
||||
"Opening auction"
|
||||
)
|
||||
expect(page.locator(table_row_selector).locator('[col-id="state"]')).to_have_text(
|
||||
"Pending"
|
||||
)
|
||||
result = page.get_by_text(market_name)
|
||||
result.first.click()
|
||||
page.get_by_test_id(market_trading_mode).get_by_text("Opening auction").hover()
|
||||
expect(page.get_by_test_id("opening-auction-sub-status").first).to_have_text(
|
||||
"Opening auction: Not enough liquidity to open"
|
||||
)
|
||||
logger.info(page.get_by_test_id("opening-auction-sub-status").inner_text)
|
||||
vega.submit_liquidity(
|
||||
key_name=MM_WALLET.name,
|
||||
market_id=simple_market,
|
||||
commitment_amount=initial_commitment,
|
||||
fee=0.002,
|
||||
is_amendment=False,
|
||||
)
|
||||
|
||||
vega.submit_order(
|
||||
market_id=simple_market,
|
||||
trading_key=MM_WALLET.name,
|
||||
side="SIDE_BUY",
|
||||
order_type="TYPE_LIMIT",
|
||||
price=initial_price - 0.0005,
|
||||
wait=False,
|
||||
time_in_force="TIME_IN_FORCE_GTC",
|
||||
volume=99,
|
||||
)
|
||||
vega.submit_order(
|
||||
market_id=simple_market,
|
||||
trading_key=MM_WALLET.name,
|
||||
side="SIDE_SELL",
|
||||
order_type="TYPE_LIMIT",
|
||||
price=initial_price + 0.0005,
|
||||
wait=False,
|
||||
time_in_force="TIME_IN_FORCE_GTC",
|
||||
volume=99,
|
||||
)
|
||||
# 6002-MDET-009
|
||||
expect(
|
||||
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
|
||||
).to_have_text("0.00 (0.00%)")
|
||||
|
||||
# add orders to provide liquidity
|
||||
submit_order(
|
||||
vega, MM_WALLET.name, simple_market, "SIDE_BUY", initial_volume, initial_price
|
||||
)
|
||||
submit_order(
|
||||
vega, MM_WALLET.name, simple_market, "SIDE_SELL", initial_volume, initial_price
|
||||
)
|
||||
submit_order(
|
||||
vega,
|
||||
MM_WALLET.name,
|
||||
simple_market,
|
||||
"SIDE_BUY",
|
||||
initial_volume,
|
||||
initial_price + initial_spread / 2,
|
||||
)
|
||||
submit_order(
|
||||
vega,
|
||||
MM_WALLET.name,
|
||||
simple_market,
|
||||
"SIDE_SELL",
|
||||
initial_volume,
|
||||
initial_price + initial_spread / 2,
|
||||
)
|
||||
submit_order(
|
||||
vega, MM_WALLET2.name, simple_market, "SIDE_SELL", initial_volume, initial_price
|
||||
)
|
||||
expect(
|
||||
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
|
||||
).to_have_text("100.00 (>100%)")
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(10)
|
||||
vega.wait_for_total_catchup()
|
||||
expect(
|
||||
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
|
||||
).to_have_text("50.00 (>100%)")
|
||||
|
||||
page.goto(f"/#/markets/all")
|
||||
# temporary skip
|
||||
# expect(page.locator(table_row_selector).locator(trading_mode_col)).to_have_text(
|
||||
# "Continuous"
|
||||
# )
|
||||
|
||||
# commented out because we have an issue #4233
|
||||
# expect(page.locator(row_selector).locator(state_col)
|
||||
# ).to_have_text("Pending")
|
||||
|
||||
page.goto(f"/#/markets/all")
|
||||
result = page.get_by_text(market_name)
|
||||
result.first.click()
|
||||
|
||||
page.get_by_test_id("Info").click()
|
||||
page.get_by_test_id("accordion-title").get_by_text(
|
||||
"Price monitoring bounds 1"
|
||||
).click()
|
||||
expect(
|
||||
page.get_by_test_id(price_monitoring_bounds_row).first.get_by_text(
|
||||
"1.32217 BTC"
|
||||
)
|
||||
).to_be_visible()
|
||||
expect(
|
||||
page.get_by_test_id(price_monitoring_bounds_row).last.get_by_text("0.79245 BTC")
|
||||
).to_be_visible()
|
||||
|
||||
# add orders that change the price so that it goes beyond the limits of price monitoring
|
||||
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 110)
|
||||
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 90)
|
||||
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 105)
|
||||
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 95)
|
||||
|
||||
# add order at the current price so that it is possible to change the status to price monitoring
|
||||
to_cancel = submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 1, 105)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
expect(
|
||||
page.get_by_test_id(price_monitoring_bounds_row).first.get_by_text(
|
||||
"135.44204 BTC"
|
||||
)
|
||||
).to_be_visible()
|
||||
expect(
|
||||
page.get_by_test_id(price_monitoring_bounds_row).last.get_by_text(
|
||||
"81.17758 BTC"
|
||||
)
|
||||
).to_be_visible()
|
||||
expect(
|
||||
page.get_by_test_id(market_trading_mode).get_by_test_id(item_value)
|
||||
).to_have_text("Monitoring auction - price")
|
||||
expect(page.get_by_test_id(market_state).get_by_test_id(item_value)).to_have_text(
|
||||
"Suspended"
|
||||
)
|
||||
expect(
|
||||
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
|
||||
).to_have_text("50.00 (8.78%)")
|
||||
|
||||
# cancel order to increase liquidity
|
||||
vega.cancel_order(MM_WALLET2.name, simple_market, to_cancel)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
expect(page.get_by_text(market_name).first).to_be_attached()
|
||||
expect(
|
||||
page.get_by_test_id(market_trading_mode).get_by_test_id(item_value)
|
||||
).to_have_text("Continuous")
|
||||
expect(page.get_by_test_id(market_state).get_by_test_id(item_value)).to_have_text(
|
||||
"Active"
|
||||
)
|
||||
# commented out because we have an issue #4233
|
||||
# expect(page.get_by_text("Opening auction")).to_be_hidden()
|
||||
|
||||
# 6002-MDET-009
|
||||
expect(
|
||||
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
|
||||
).to_have_text("50.00 (>100%)")
|
||||
|
||||
|
||||
COL_ID_FEE = ".ag-center-cols-container [col-id='fee'] .ag-cell-value"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted", "auth")
|
||||
def test_auction_uncross_fees(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id("Fills").click()
|
||||
expect(page.locator(COL_ID_FEE)).to_have_text("0.00 tDAI")
|
||||
# tbd - tooltip is not visible without this wait
|
||||
page.wait_for_timeout(1000)
|
||||
page.get_by_role("gridcell", name="0.00 tDAI").nth(0).hover()
|
||||
expect(page.get_by_test_id("fee-breakdown-tooltip")).to_have_text(
|
||||
"If the market was suspendedDuring auction, half the infrastructure and liquidity fees will be paid.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee0.00 tDAITotal fees0.00 tDAI"
|
||||
)
|
||||
change_keys(page, vega, "market_maker")
|
||||
expect(page.locator(COL_ID_FEE)).to_have_text("0.00 tDAI")
|
||||
# tbd - tooltip is not visible without this wait
|
||||
page.wait_for_timeout(1000)
|
||||
page.get_by_role("gridcell", name="0.00 tDAI").nth(0).hover()
|
||||
expect(page.get_by_test_id("fee-breakdown-tooltip")).to_have_text(
|
||||
"If the market was suspendedDuring auction, half the infrastructure and liquidity fees will be paid.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee0.00 tDAITotal fees0.00 tDAI"
|
||||
)
|
||||
@@ -25,65 +25,3 @@ def test_market_selector(continuous_market, page: Page):
|
||||
expect(btc_market.locator("span.rounded-md.leading-none")).to_be_visible()
|
||||
expect(btc_market.locator("span.rounded-md.leading-none")).to_have_text("Futr")
|
||||
expect(btc_market.locator('[data-testid="sparkline-svg"]')).not_to_be_visible
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("simple_market", "auth", "risk_accepted")
|
||||
@pytest.mark.parametrize(
|
||||
"simple_market",
|
||||
[
|
||||
{
|
||||
"custom_market_name": "APPL.MF21",
|
||||
"custom_asset_name": "tUSDC",
|
||||
"custom_asset_symbol": "tUSDC",
|
||||
}
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_market_selector_filter(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id("header-title").click()
|
||||
# 6001-MARK-027
|
||||
page.get_by_test_id("product-Spot").click()
|
||||
expect(page.get_by_test_id("market-selector-list")).to_contain_text(
|
||||
"Spot markets coming soon."
|
||||
)
|
||||
page.get_by_test_id("product-Perpetual").click()
|
||||
expect(page.get_by_test_id("market-selector-list")).to_contain_text(
|
||||
"No perpetual markets."
|
||||
)
|
||||
page.get_by_test_id("product-Future").click()
|
||||
expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(2)
|
||||
|
||||
# 6001-MARK-029
|
||||
page.get_by_test_id("search-term").fill("btc")
|
||||
expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(1)
|
||||
# tbd - 5465
|
||||
expect(page.locator('[data-testid="market-selector-list"] a').nth(0)).to_contain_text(
|
||||
"BTC:DAI_2023107.50 tDAI"
|
||||
)
|
||||
|
||||
page.get_by_test_id("search-term").clear()
|
||||
expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(2)
|
||||
|
||||
# 6001-MARK-030
|
||||
# 6001-MARK-031
|
||||
# 6001-MARK-032
|
||||
# 6001-MARK-033
|
||||
page.get_by_test_id("sort-trigger").click()
|
||||
|
||||
expect(page.get_by_test_id("sort-item-Gained")).to_have_text("Top gaining")
|
||||
expect(page.get_by_test_id("sort-item-Gained")).to_be_visible()
|
||||
expect(page.get_by_test_id("sort-item-Lost")).to_have_text("Top losing")
|
||||
expect(page.get_by_test_id("sort-item-Lost")).to_be_visible()
|
||||
expect(page.get_by_test_id("sort-item-New")).to_have_text("New markets")
|
||||
expect(page.get_by_test_id("sort-item-New")).to_be_visible()
|
||||
|
||||
# 6001-MARK-028
|
||||
page.get_by_test_id("sort-trigger").click(force=True)
|
||||
page.get_by_test_id("asset-trigger").click()
|
||||
page.get_by_role("menuitemcheckbox").nth(0).get_by_text("tDAI").click()
|
||||
expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(1)
|
||||
# tbd - 5465
|
||||
expect(page.locator('[data-testid="market-selector-list"] a').nth(0)).to_contain_text(
|
||||
"BTC:DAI_2023107.50 tDAI"
|
||||
)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
from conftest import init_page, init_vega, risk_accepted_setup
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vega(request):
|
||||
with init_vega(request) as vega:
|
||||
yield vega
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def page(vega, browser, request):
|
||||
with init_page(vega, browser, request) as page:
|
||||
risk_accepted_setup(page)
|
||||
page.goto("/#/markets/all")
|
||||
yield page
|
||||
|
||||
|
||||
def test_no_open_markets(page: Page):
|
||||
# 6001-MARK-034
|
||||
page.get_by_test_id("Open markets").click()
|
||||
expect(page.locator(".ag-overlay-wrapper")).to_have_text("No markets")
|
||||
|
||||
|
||||
def test_no_closed_markets(page: Page):
|
||||
page.get_by_test_id("Closed markets").click()
|
||||
expect(page.locator(".ag-overlay-wrapper")).to_have_text("No markets")
|
||||
|
||||
|
||||
def test_no_proposed_markets(page: Page):
|
||||
# 6001-MARK-061
|
||||
page.get_by_test_id("Proposed markets").click()
|
||||
expect(page.locator(".ag-overlay-wrapper")).to_have_text("No proposed markets")
|
||||
@@ -56,9 +56,6 @@ def test_renders_markets_correctly(proposed_market, page: Page):
|
||||
page.goto(f"/#/markets/all")
|
||||
page.click('[data-testid="Proposed markets"]')
|
||||
row = page.locator(row_selector)
|
||||
# 6001-MARK-049
|
||||
expect(row.locator(col_market_id)).to_have_text("BTC:DAI_2023")
|
||||
|
||||
# 6001-MARK-051
|
||||
expect(row.locator('[col-id="asset"]')).to_have_text("tDAI")
|
||||
|
||||
|
||||
@@ -4,9 +4,14 @@ from vega_sim.null_service import VegaServiceNull
|
||||
from actions.vega import submit_order
|
||||
from fixtures.market import setup_simple_market
|
||||
from conftest import init_vega
|
||||
from actions.utils import wait_for_toast_confirmation
|
||||
from actions.utils import wait_for_toast_confirmation, change_keys
|
||||
from wallet_config import MM_WALLET, MM_WALLET2
|
||||
|
||||
market_trading_mode = "market-trading-mode"
|
||||
market_state = "market-state"
|
||||
item_value = "item-value"
|
||||
|
||||
COL_ID_FEE = ".ag-center-cols-container [col-id='fee'] .ag-cell-value"
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vega(request):
|
||||
@@ -68,11 +73,11 @@ def setup_market_monitoring_auction(vega: VegaServiceNull, simple_market):
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
# add orders that change the price so that it goes beyond the limits of price monitoring
|
||||
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 110)
|
||||
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 90)
|
||||
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 105)
|
||||
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 95)
|
||||
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 1, 105)
|
||||
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 300)
|
||||
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 290)
|
||||
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 305)
|
||||
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 295)
|
||||
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 1, 305)
|
||||
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
@@ -89,7 +94,6 @@ def test_market_monitoring_auction_price_volatility_limit_order(
|
||||
page.get_by_test_id("order-price").type("110")
|
||||
page.get_by_test_id("order-tif").select_option("Fill or Kill (FOK)")
|
||||
page.get_by_test_id("place-order").click()
|
||||
|
||||
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text(
|
||||
"This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders."
|
||||
)
|
||||
@@ -110,8 +114,8 @@ def test_market_monitoring_auction_price_volatility_limit_order(
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.get_by_test_id("All").click()
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr0+1LimitActive110.00GTC"
|
||||
expect(page.get_by_role("row").nth(4)).to_contain_text(
|
||||
"0+1LimitActive110.00GTC"
|
||||
)
|
||||
|
||||
|
||||
@@ -125,7 +129,6 @@ def test_market_monitoring_auction_price_volatility_market_order(
|
||||
page.get_by_test_id("order-size").type("1")
|
||||
# 7002-SORD-060
|
||||
page.get_by_test_id("place-order").click()
|
||||
|
||||
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text(
|
||||
"This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders."
|
||||
)
|
||||
@@ -135,3 +138,29 @@ def test_market_monitoring_auction_price_volatility_market_order(
|
||||
"This market is in auction due to high price volatility. Only limit orders are permitted when market is in auction."
|
||||
)
|
||||
expect(page.get_by_test_id("deal-ticket-error-message-type")).to_be_visible()
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted", "auth", "setup_market_monitoring_auction")
|
||||
def test_market_price_volatility(
|
||||
page: Page, simple_market, vega: VegaServiceNull
|
||||
):
|
||||
page.goto(f"/#/markets/{simple_market}")
|
||||
expect(
|
||||
page.get_by_test_id(market_trading_mode).get_by_test_id(item_value)
|
||||
).to_have_text("Monitoring auction - price")
|
||||
expect(page.get_by_test_id(market_state).get_by_test_id(item_value)).to_have_text(
|
||||
"Suspended"
|
||||
)
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted", "auth", "setup_market_monitoring_auction")
|
||||
def test_auction_uncross_fees(simple_market, vega: VegaServiceNull, page: Page):
|
||||
page.goto(f"/#/markets/{simple_market}")
|
||||
change_keys(page, vega, "market_maker")
|
||||
page.get_by_test_id("Fills").click()
|
||||
row = page.locator('[row-index="3"]').nth(1)
|
||||
expect(row.locator('[col-id="fee"]')).to_have_text("0.00 tDAI")
|
||||
# tbd - tooltip is not visible without this wait
|
||||
page.wait_for_timeout(1000)
|
||||
row.locator('[col-id="fee"]').hover()
|
||||
expect(page.get_by_test_id("fee-breakdown-tooltip")).to_have_text(
|
||||
"If the market was suspendedDuring auction, half the infrastructure and liquidity fees will be paid.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee0.00 tDAITotal fees0.00 tDAI"
|
||||
)
|
||||
@@ -73,9 +73,6 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
)
|
||||
|
||||
# "wait" for market to be approved and enacted
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(10)
|
||||
vega.wait_for_total_catchup()
|
||||
next_epoch(vega=vega)
|
||||
# check that market is in pending state
|
||||
expect(trading_mode).to_have_text("Opening auction")
|
||||
@@ -118,8 +115,7 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
submit_order(vega, MM_WALLET.name, market_id, "SIDE_SELL", 1, 100)
|
||||
submit_order(vega, MM_WALLET2.name, market_id, "SIDE_BUY", 1, 100)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_fn(2)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
# check market state is now active and trading mode is continuous
|
||||
@@ -139,9 +135,7 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
.get_by_test_id(f"update-state-banner-{market_id}")
|
||||
).to_be_visible()
|
||||
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
next_epoch(vega=vega)
|
||||
|
||||
expect(
|
||||
page.get_by_test_id("market-banner")
|
||||
@@ -155,9 +149,7 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
forward_time_to_enactment = False
|
||||
)
|
||||
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
next_epoch(vega=vega)
|
||||
|
||||
expect(page.get_by_test_id("market-banner")).not_to_be_visible()
|
||||
|
||||
@@ -170,13 +162,11 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
payload={"trading.terminated": "true"},
|
||||
key_name=GOVERNANCE_WALLET.name,
|
||||
)
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
next_epoch(vega=vega)
|
||||
|
||||
# market state should be changed to "No trading" because of the invalid oracle
|
||||
expect(trading_mode).to_have_text("No trading")
|
||||
expect(market_state).to_have_text("No trading")
|
||||
expect(market_state).to_have_text("Trading Terminated")
|
||||
|
||||
# settle market
|
||||
vega.submit_termination_and_settlement_data(
|
||||
@@ -184,9 +174,7 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
settlement_price=100,
|
||||
market_id=market_id,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
next_epoch(vega=vega)
|
||||
|
||||
# check market state is now settled
|
||||
expect(trading_mode).to_have_text("No trading")
|
||||
|
||||
@@ -44,7 +44,7 @@ def verify_order_value(
|
||||
actual_text = element.text_content()
|
||||
|
||||
if actual_text is None:
|
||||
raise Exception(f"no text found for test_id {test_id}")
|
||||
raise Exception(f"no text found for test_id {test_id}")
|
||||
|
||||
assert re.match(
|
||||
expected_text, actual_text
|
||||
|
||||
@@ -29,13 +29,15 @@ def verify_data_grid(page: Page, data_test_id, expected_pattern):
|
||||
logger.info(f"Matched: {expected} == {actual}")
|
||||
else:
|
||||
logger.info(f"Not Matched: {expected} != {actual}")
|
||||
raise AssertionError(f"Pattern does not match: {expected} != {actual}")
|
||||
raise AssertionError(
|
||||
f"Pattern does not match: {expected} != {actual}")
|
||||
else: # it's not a regex, so we escape it
|
||||
if re.search(re.escape(expected), actual):
|
||||
logger.info(f"Matched: {expected} == {actual}")
|
||||
else:
|
||||
logger.info(f"Not Matched: {expected} != {actual}")
|
||||
raise AssertionError(f"Pattern does not match: {expected} != {actual}")
|
||||
raise AssertionError(
|
||||
f"Pattern does not match: {expected} != {actual}")
|
||||
|
||||
|
||||
def submit_order(vega: VegaServiceNull, wallet_name, market_id, side, volume, price):
|
||||
@@ -65,7 +67,6 @@ def test_limit_order_trade_open_order(
|
||||
expect(orderbook_trade).to_be_visible()
|
||||
|
||||
expected_open_order = [
|
||||
"BTC:DAI_2023",
|
||||
"+1",
|
||||
"Limit",
|
||||
"Active",
|
||||
@@ -87,15 +88,12 @@ def test_limit_order_trade_open_position(continuous_market, page: Page):
|
||||
secondary_id = "stack-cell-secondary"
|
||||
|
||||
position = {
|
||||
"market_code": "BTC:DAI_2023",
|
||||
"settlement_asset": "tDAI",
|
||||
"product_type": "Futr",
|
||||
"size": "+1",
|
||||
"notional": "107.50",
|
||||
"average_entry_price": "107.50",
|
||||
"mark_price": "107.50",
|
||||
"margin": "8.50269",
|
||||
"leverage": "1.0x",
|
||||
"leverage": "Cross1.0x",
|
||||
"liquidation": "0.00",
|
||||
"realised_pnl": "0.00",
|
||||
"unrealised_pnl": "0.00",
|
||||
@@ -107,14 +105,9 @@ def test_limit_order_trade_open_position(continuous_market, page: Page):
|
||||
# 7004-POSI-001
|
||||
# 7004-POSI-002
|
||||
|
||||
market = table.locator("[col-id='marketCode']")
|
||||
expect(market.get_by_test_id(primary_id)).to_have_text(position["market_code"])
|
||||
expect(market.get_by_test_id(secondary_id)).to_have_text(
|
||||
position["settlement_asset"] + position["product_type"]
|
||||
)
|
||||
|
||||
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"]
|
||||
)
|
||||
@@ -154,5 +147,4 @@ def test_limit_order_trade_order_trade_away(continuous_market, page: Page):
|
||||
page.get_by_test_id("Orderbook").click()
|
||||
price_element = page.get_by_test_id("price-11000000").nth(1)
|
||||
# 6003-ORDB-010
|
||||
print(price_element)
|
||||
expect(price_element).to_be_hidden()
|
||||
|
||||
@@ -257,46 +257,63 @@ def test_order_sorted(page: Page):
|
||||
|
||||
def test_order_status_active(page: Page):
|
||||
# 7002-SORD-041
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-2Futr" + "0" + "-10" + "Limit" + "Active" + "150.00" + "GTC"
|
||||
expect(page.locator('[row-index="2"]').first).to_contain_text(
|
||||
"market-2Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="2"]').nth(1)).to_contain_text(
|
||||
"0" + "-10" + "Limit" + "Active" + "150.00" + "GTC"
|
||||
)
|
||||
|
||||
|
||||
def test_status_expired(page: Page):
|
||||
# 7002-SORD-042
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-3Futr" + "0" + "-10" + "Limit" + "Expired" + "120.00" + "GTT:"
|
||||
expect(page.locator('[row-index="7"]').first).to_contain_text(
|
||||
"market-3Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="7"]').nth(1)).to_contain_text(
|
||||
"0" + "-10" + "Limit" + "Expired" + "120.00" + "GTT:"
|
||||
)
|
||||
|
||||
|
||||
def test_order_status_Stopped(page: Page):
|
||||
# 7002-SORD-044
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-1Futr" + "0" + "-100" + "Limit" + "Stopped" + "130.00" + "IOC"
|
||||
expect(page.locator('[row-index="12"]').first).to_contain_text(
|
||||
"market-1Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="12"]').nth(1)).to_contain_text(
|
||||
"0" + "-100" + "Limit" + "Stopped" + "130.00" + "IOC"
|
||||
)
|
||||
|
||||
|
||||
def test_order_status_partially_filled(page: Page):
|
||||
# 7002-SORD-045
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-2Futr" + "99" + "+100" + "Limit" + "Partially Filled" + "104.00" + "IOC"
|
||||
expect(page.locator('[row-index="8"]').first).to_contain_text(
|
||||
"market-2Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="8"]').nth(1)).to_contain_text(
|
||||
"99" + "+100" + "Limit" + "Partially Filled" + "104.00" + "IOC"
|
||||
)
|
||||
|
||||
|
||||
def test_order_status_filled(page: Page):
|
||||
# 7002-SORD-046
|
||||
# 7003-MORD-020
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-1Futr" + "100" + "-100" + "Limit" + "Filled" + "88.00" + "GTC"
|
||||
expect(page.locator('[row-index="11"]').first).to_contain_text(
|
||||
"market-1Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="11"]').nth(1)).to_contain_text(
|
||||
"100" + "-100" + "Limit" + "Filled" + "88.00" + "GTC"
|
||||
)
|
||||
|
||||
|
||||
def test_order_status_rejected(page: Page):
|
||||
# 7002-SORD-047
|
||||
# 7003-MORD-018
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
expect(page.locator('[row-index="9"]').first).to_contain_text(
|
||||
"market-1Futr"
|
||||
+ "0"
|
||||
)
|
||||
expect(page.locator('[row-index="9"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "-10,000,000,000"
|
||||
+ "Limit"
|
||||
+ "Rejected: Margin check failed"
|
||||
@@ -308,9 +325,11 @@ def test_order_status_rejected(page: Page):
|
||||
def test_order_status_parked(page: Page):
|
||||
# 7002-SORD-048
|
||||
# 7003-MORD-016
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
expect(page.locator('[row-index="3"]').first).to_contain_text(
|
||||
"market-5Futr"
|
||||
+ "0"
|
||||
)
|
||||
expect(page.locator('[row-index="3"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "-60"
|
||||
+ "Ask + 15.00 Peg limit"
|
||||
+ "Parked"
|
||||
@@ -321,9 +340,11 @@ def test_order_status_parked(page: Page):
|
||||
|
||||
def test_order_status_pegged_ask(page: Page):
|
||||
# 7003-MORD-016
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
expect(page.locator('[row-index="4"]').first).to_contain_text(
|
||||
"market-4Futr"
|
||||
+ "0"
|
||||
)
|
||||
expect(page.locator('[row-index="4"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "-60"
|
||||
+ "Ask + 15.00 Peg limit"
|
||||
+ "Active"
|
||||
@@ -334,9 +355,11 @@ def test_order_status_pegged_ask(page: Page):
|
||||
|
||||
def test_order_status_pegged_bid(page: Page):
|
||||
# 7003-MORD-016
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
expect(page.locator('[row-index="5"]').first).to_contain_text(
|
||||
"market-4Futr"
|
||||
+ "0"
|
||||
)
|
||||
expect(page.locator('[row-index="5"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "+40"
|
||||
+ "Bid - 10.00 Peg limit"
|
||||
+ "Active"
|
||||
@@ -347,9 +370,11 @@ def test_order_status_pegged_bid(page: Page):
|
||||
|
||||
def test_order_status_pegged_mid(page: Page):
|
||||
# 7003-MORD-016
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
expect(page.locator('[row-index="6"]').first).to_contain_text(
|
||||
"market-4Futr"
|
||||
+ "0"
|
||||
)
|
||||
expect(page.locator('[row-index="6"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "+20"
|
||||
+ "Mid - 5.00 Peg limit"
|
||||
+ "Active"
|
||||
@@ -372,9 +397,11 @@ def test_order_amend_order(vega: VegaServiceNull, page: Page):
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-2Futr" + "0" + "-15" + "Limit" + "Active" + "170.00" + "GTC"
|
||||
expect(page.locator('[row-index="1"]').first).to_contain_text(
|
||||
"market-2Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="1"]').nth(1)).to_contain_text(
|
||||
"0" + "-15" + "Limit" + "Active" + "170.00" + "GTC"
|
||||
)
|
||||
|
||||
|
||||
@@ -389,9 +416,11 @@ def test_order_cancel_single_order(vega: VegaServiceNull, page: Page):
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-3Futr" + "0" + "+10" + "Limit" + "Cancelled" + "60.00" + "GTC"
|
||||
expect(page.locator('[row-index="0"]').first).to_contain_text(
|
||||
"market-3Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="0"]').nth(1)).to_contain_text(
|
||||
"0" + "+10" + "Limit" + "Cancelled" + "60.00" + "GTC"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -106,7 +106,6 @@ def test_orderbook_grid_content(setup_market, page: Page):
|
||||
matching_order[1],
|
||||
)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
@@ -233,7 +232,6 @@ def test_orderbook_price_movement(setup_market, page: Page):
|
||||
matching_order_1[1],
|
||||
)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
@@ -254,7 +252,6 @@ def test_orderbook_price_movement(setup_market, page: Page):
|
||||
matching_order_2[1],
|
||||
)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ class TestPerpetuals:
|
||||
settlement_price=110,
|
||||
market_id=perps_market,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
submit_multiple_orders(
|
||||
@@ -48,8 +47,7 @@ class TestPerpetuals:
|
||||
settlement_price=110,
|
||||
market_id=perps_market,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_fn(10)
|
||||
vega.wait_for_total_catchup()
|
||||
return perps_market
|
||||
|
||||
@@ -110,7 +108,6 @@ def test_perps_market_termination_proposed(page: Page, vega: VegaServiceNull):
|
||||
forward_time_to_enactment=False,
|
||||
)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
banner_text = page.get_by_test_id(
|
||||
@@ -135,7 +132,6 @@ def test_perps_market_terminated(page: Page, vega: VegaServiceNull):
|
||||
approve_proposal=True,
|
||||
forward_time_to_enactment=True,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ def check_pnl_color_value(element, expected_color, expected_value):
|
||||
assert color == expected_color, f"Unexpected color: {color}"
|
||||
assert value == expected_value, f"Unexpected value: {value}"
|
||||
|
||||
|
||||
#TODO move this test to jest
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_pnl(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
page.set_viewport_size({"width": 1748, "height": 977})
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import os
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
#TODO migrate to jest
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted", "continuous_market")
|
||||
def test_ledger_entries_downloads(page: Page):
|
||||
page.goto("/#/portfolio")
|
||||
page.get_by_test_id("Ledger entries").click()
|
||||
expect(page.get_by_test_id("ledger-download-button")).to_be_enabled()
|
||||
# 7007-LEEN-001
|
||||
page.get_by_test_id("ledger-download-button").click()
|
||||
# 7007-LEEN-009
|
||||
expect(page.get_by_test_id("toast-content")).to_contain_text(("Your file is ready"))
|
||||
# Get the user's Downloads directory
|
||||
downloads_directory = os.path.expanduser("~") + "/Downloads/"
|
||||
# Start waiting for the download
|
||||
with page.expect_download() as download_info:
|
||||
# Perform the action that initiates download
|
||||
page.get_by_role("link", name="Get file here").click()
|
||||
|
||||
download = download_info.value
|
||||
# Wait for the download process to complete and save the downloaded file in the Downloads directory
|
||||
download.save_as(os.path.join(downloads_directory, download.suggested_filename))
|
||||
|
||||
# Verify the download by asserting that the file exists
|
||||
downloaded_file_path = os.path.join(
|
||||
downloads_directory, download.suggested_filename
|
||||
)
|
||||
assert os.path.exists(
|
||||
downloaded_file_path
|
||||
), f"Download failed! File not found at: {downloaded_file_path}"
|
||||
@@ -28,8 +28,9 @@ def test_usage_breakdown(continuous_market, page: Page):
|
||||
usage_breakdown = page.get_by_test_id("usage-breakdown")
|
||||
|
||||
# Verify headers
|
||||
headers = ["Market", "Account type", "Balance", "Margin health"]
|
||||
ag_headers = usage_breakdown.locator(".ag-header-cell-text").element_handles()
|
||||
headers = ["Market", "Account type", "Balance"]
|
||||
ag_headers = usage_breakdown.locator(
|
||||
".ag-header-cell-text").element_handles()
|
||||
for i, header_element in enumerate(ag_headers):
|
||||
header_text = header_element.text_content()
|
||||
assert header_text == headers[i]
|
||||
@@ -38,30 +39,10 @@ def test_usage_breakdown(continuous_market, page: Page):
|
||||
expect(usage_breakdown.locator('[class="mb-2 text-sm"]')).to_have_text(
|
||||
"You have 1,000,000.00 tDAI in total."
|
||||
)
|
||||
expect(usage_breakdown.locator(COL_ID_USED).first).to_have_text("8.50269 (0%)")
|
||||
expect(usage_breakdown.locator(
|
||||
COL_ID_USED).first).to_have_text("8.50269 (0%)")
|
||||
expect(usage_breakdown.locator(COL_ID_USED).nth(1)).to_have_text(
|
||||
"999,991.49731 (99%)"
|
||||
)
|
||||
|
||||
# Maintenance Level
|
||||
expect(
|
||||
usage_breakdown.locator(
|
||||
".ag-center-cols-container [col-id='market.id'] .ag-cell-value"
|
||||
).first
|
||||
).to_have_text("2.85556 above maintenance level")
|
||||
|
||||
# Margin health tooltip
|
||||
usage_breakdown.get_by_test_id("margin-health-chart-track").hover()
|
||||
tooltip_data = [
|
||||
("maintenance level", "5.64713"),
|
||||
("search level", "6.21184"),
|
||||
("initial level", "8.47069"),
|
||||
("balance", "8.50269"),
|
||||
("release level", "9.60012"),
|
||||
]
|
||||
|
||||
for index, (label, value) in enumerate(tooltip_data):
|
||||
expect(page.get_by_test_id(TOOLTIP_LABEL).nth(index)).to_have_text(label)
|
||||
expect(page.get_by_test_id(TOOLTIP_VALUE).nth(index)).to_have_text(value)
|
||||
|
||||
page.get_by_test_id("dialog-close").click()
|
||||
|
||||
@@ -15,14 +15,13 @@ def test_closed_market_position(vega: VegaServiceNull, page: Page):
|
||||
settlement_price=110,
|
||||
market_id=market_id,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.goto(f"/#/markets/{market_id}")
|
||||
expect(page.locator(".ag-overlay-panel")).to_have_text("No positions")
|
||||
page.get_by_test_id("open-transfer").click()
|
||||
tab = page.get_by_test_id("tab-positions")
|
||||
table = tab.locator(".ag-center-cols-container")
|
||||
table = tab.locator('[class="ag-body ag-layout-normal"]')
|
||||
market = table.locator("[col-id='marketCode']")
|
||||
expect(market.get_by_test_id("stack-cell-primary")).to_have_text("BTC:DAI_2023")
|
||||
page.get_by_test_id("open-transfer").click()
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import pytest
|
||||
import vega_sim.proto.vega as vega_protos
|
||||
from playwright.sync_api import Page, expect
|
||||
from vega_sim.null_service import VegaServiceNull
|
||||
from actions.utils import next_epoch
|
||||
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.usefixtures("risk_accepted", "auth")
|
||||
def test_filtered_cards(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
tDAI_asset_id = vega.find_asset_id(symbol="tDAI")
|
||||
vega.update_network_parameter(
|
||||
MM_WALLET.name, parameter="reward.asset", new_value=tDAI_asset_id
|
||||
)
|
||||
vega.mint(key_name=PARTY_B.name, asset=tDAI_asset_id, amount=100000)
|
||||
vega.mint(key_name=PARTY_A.name, asset=tDAI_asset_id, amount=100000)
|
||||
next_epoch(vega=vega)
|
||||
vega.recurring_transfer(
|
||||
from_key_name=PARTY_A.name,
|
||||
from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL,
|
||||
to_account_type=vega_protos.vega.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
asset=tDAI_asset_id,
|
||||
reference="reward",
|
||||
markets=[continuous_market],
|
||||
asset_for_metric=tDAI_asset_id,
|
||||
metric=vega_protos.vega.DISPATCH_METRIC_MAKER_FEES_PAID,
|
||||
lock_period=5,
|
||||
amount=100,
|
||||
factor=1.0,
|
||||
)
|
||||
vega.submit_order(
|
||||
trading_key=PARTY_B.name,
|
||||
market_id=continuous_market,
|
||||
order_type="TYPE_MARKET",
|
||||
time_in_force="TIME_IN_FORCE_IOC",
|
||||
side="SIDE_BUY",
|
||||
volume=1,
|
||||
)
|
||||
vega.submit_order(
|
||||
trading_key=PARTY_A.name,
|
||||
market_id=continuous_market,
|
||||
order_type="TYPE_MARKET",
|
||||
time_in_force="TIME_IN_FORCE_IOC",
|
||||
side="SIDE_BUY",
|
||||
volume=1,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
|
||||
vega.update_market_state(
|
||||
market_id=continuous_market,
|
||||
proposal_key=MM_WALLET.name,
|
||||
market_state=MarketStateUpdateType.Suspend,
|
||||
forward_time_to_enactment=True,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
page.goto("/#/rewards")
|
||||
expect(page.locator(".from-vega-cdark-400")).to_be_visible()
|
||||
governance.submit_oracle_data(
|
||||
wallet=vega.wallet,
|
||||
payload={"trading.terminated": "true"},
|
||||
key_name="FJMKnwfZdd48C8NqvYrG",
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
page.reload()
|
||||
expect(page.locator(".from-vega-cdark-400")).not_to_be_in_viewport()
|
||||
@@ -1,36 +0,0 @@
|
||||
import pytest
|
||||
from playwright.sync_api import expect, Page
|
||||
|
||||
settings_icon = "icon-cog"
|
||||
settings_column_btn = "popover-trigger"
|
||||
settings_close_btn = "settings-close"
|
||||
split_view_view = "split-view-view"
|
||||
|
||||
@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted")
|
||||
def test_column_settings_is_visible(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
expect(page.get_by_test_id(split_view_view).get_by_test_id(settings_column_btn)).to_be_visible()
|
||||
page.goto("/#/portfolio")
|
||||
expect(page.get_by_test_id(split_view_view).get_by_test_id(settings_column_btn).nth(0)).to_be_visible()
|
||||
expect(page.get_by_test_id(split_view_view).get_by_test_id(settings_column_btn).nth(1)).to_be_visible()
|
||||
page.goto(f"/#/markets/all")
|
||||
expect(page.get_by_test_id(settings_column_btn)).to_be_visible()
|
||||
page.click('[data-testid="Proposed markets"]')
|
||||
expect(page.get_by_test_id(settings_column_btn)).to_be_visible()
|
||||
page.click('[data-testid="Closed markets"]')
|
||||
expect(page.get_by_test_id(settings_column_btn)).to_be_visible()
|
||||
|
||||
@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted")
|
||||
def test_can_reset_columns_state(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/all")
|
||||
col_market = page.locator('[col-id="tradableInstrument.instrument.code"]').first
|
||||
col_settlement_asset = page.locator('[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"]').first
|
||||
col_market.drag_to(col_settlement_asset)
|
||||
|
||||
# Check the attribute of the dragged element
|
||||
attribute_value = col_market.get_attribute("aria-colindex")
|
||||
assert attribute_value != "1"
|
||||
page.get_by_test_id(settings_column_btn).click()
|
||||
page.get_by_role("button", name="Reset Columns").click()
|
||||
attribute_value_after_reset = col_market.get_attribute("aria-colindex")
|
||||
assert attribute_value_after_reset == "1"
|
||||
@@ -149,7 +149,5 @@ def provide_successor_liquidity(
|
||||
)
|
||||
|
||||
submit_order(vega, "Key 1", market_id, "SIDE_BUY", 1, 110)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
import pytest
|
||||
import re
|
||||
import logging
|
||||
from playwright.sync_api import expect
|
||||
from actions.vega import submit_order
|
||||
from conftest import init_vega
|
||||
from playwright.sync_api import Page
|
||||
from vega_sim.null_service import VegaServiceNull
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vega():
|
||||
with init_vega() as vega:
|
||||
yield vega
|
||||
|
||||
|
||||
# Could be turned into a helper function in the future.
|
||||
def verify_data_grid(page: Page, data_test_id, expected_pattern):
|
||||
page.get_by_test_id(data_test_id).click()
|
||||
# Required so that we can get liquidation price
|
||||
expect(
|
||||
page.locator(
|
||||
f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container .ag-row-first'
|
||||
)
|
||||
).to_be_visible()
|
||||
actual_text = page.locator(
|
||||
f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container'
|
||||
).text_content()
|
||||
lines = actual_text.strip().split("\n")
|
||||
for expected, actual in zip(expected_pattern, lines):
|
||||
# We are using regex so that we can run tests in different timezones.
|
||||
if re.match(r"^\\d", expected): # check if it's a regex
|
||||
if re.search(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}")
|
||||
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}")
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_limit_order_new_trade_top_of_list(
|
||||
continuous_market, vega: VegaServiceNull, page: Page
|
||||
):
|
||||
submit_order(vega, "Key 1", continuous_market, "SIDE_BUY", 1, 110)
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
expected_trade = [
|
||||
"103.50",
|
||||
"1",
|
||||
r"\d{1,2}/\d{1,2}/\d{4},\s*\d{1,2}:\d{2}:\d{2}\s*(?:AM|PM)" "107.50",
|
||||
"1",
|
||||
r"\d{1,2}/\d{1,2}/\d{4},\s*\d{1,2}:\d{2}:\d{2}\s*(?:AM|PM)",
|
||||
]
|
||||
# 6005-THIS-001
|
||||
# 6005-THIS-002
|
||||
# 6005-THIS-003
|
||||
# 6005-THIS-004
|
||||
# 6005-THIS-005
|
||||
# 6005-THIS-006
|
||||
verify_data_grid(page, "Trades", expected_trade)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_price_copied_to_deal_ticket(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id("Trades").click()
|
||||
page.locator("[col-id=price]").last.click()
|
||||
# 6005-THIS-007
|
||||
expect(page.get_by_test_id("order-price")).to_have_value("107.50000")
|
||||
@@ -22,8 +22,6 @@ def test_trade_match_table(opening_auction_market: str, vega: VegaServiceNull, p
|
||||
price=10e15,
|
||||
wait=False,
|
||||
)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
@@ -34,7 +32,6 @@ def test_trade_match_table(opening_auction_market: str, vega: VegaServiceNull, p
|
||||
"SIDE_BUY",
|
||||
[[5, 110], [5, 105], [1, 50]],
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
@@ -45,7 +42,6 @@ def test_trade_match_table(opening_auction_market: str, vega: VegaServiceNull, p
|
||||
"SIDE_SELL",
|
||||
[[5, 90], [5, 95], [1, 150]],
|
||||
)
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ def test_transfer_submit(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
|
||||
page.locator('[data-testid=transfer-form] [type="submit"]').click()
|
||||
wait_for_toast_confirmation(page)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
expected_confirmation_text = re.compile(
|
||||
@@ -142,14 +141,12 @@ def test_transfer_vesting_below_minimum(
|
||||
asset=asset_id,
|
||||
amount=24.999999,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(10)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
page.get_by_test_id("use-max-button").first.click()
|
||||
page.locator('[data-testid=transfer-form] [type="submit"]').click()
|
||||
wait_for_toast_confirmation(page)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
expected_confirmation_text = re.compile(
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
import pytest
|
||||
import re
|
||||
import json
|
||||
from playwright.sync_api import Page, expect, Route
|
||||
from conftest import init_vega
|
||||
from fixtures.market import setup_continuous_market
|
||||
|
||||
order_size = "order-size"
|
||||
order_price = "order-price"
|
||||
place_order = "place-order"
|
||||
order_side_sell = "order-side-SIDE_SELL"
|
||||
market_order = "order-type-Market"
|
||||
tif = "order-tif"
|
||||
expire = "expire"
|
||||
api_request_match = r"http://localhost:\d+/api/v2/requests"
|
||||
|
||||
|
||||
@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)
|
||||
|
||||
|
||||
def handle_route_connection_lost(route: Route, request):
|
||||
if request.method == "POST" and re.match(api_request_match, request.url):
|
||||
route.fulfill(
|
||||
status=200,
|
||||
headers={"Content-Type": "application/json"},
|
||||
body='{"jsonrpc": "2.0", "id": "1"}',
|
||||
)
|
||||
else:
|
||||
route.continue_()
|
||||
|
||||
|
||||
def handle_route_connection_rejected(route: Route, request):
|
||||
if request.method == "POST" and re.match(api_request_match, request.url):
|
||||
custom_response = {
|
||||
"jsonrpc": "2.0",
|
||||
"error": {
|
||||
"code": 3001,
|
||||
"data": "the user rejected the wallet connection",
|
||||
"message": "User error",
|
||||
},
|
||||
"id": "0",
|
||||
}
|
||||
route.fulfill(
|
||||
status=400,
|
||||
headers={"Content-Type": "application/json"},
|
||||
body=json.dumps(custom_response),
|
||||
)
|
||||
else:
|
||||
route.continue_()
|
||||
|
||||
|
||||
def assert_connection_approve(route: Route, request, page: Page):
|
||||
if request.method == "POST" and re.match(api_request_match, request.url):
|
||||
expect(page.get_by_test_id("toast-content")).to_have_text(
|
||||
"Please go to your Vega wallet application and approve or reject the transaction."
|
||||
)
|
||||
else:
|
||||
route.continue_()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_wallet_connection_error(continuous_market, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.route("**/*", handle_route_connection_lost)
|
||||
page.get_by_test_id("connect-vega-wallet").click()
|
||||
page.get_by_test_id("connector-jsonRpc").click()
|
||||
expect(page.get_by_test_id("wallet-dialog-title")).to_have_text(
|
||||
"Something went wrong"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_wallet_connection_rejected(continuous_market, page: Page):
|
||||
# 0002-WCON-002
|
||||
# 0002-WCON-005
|
||||
# 0002-WCON-007
|
||||
# 0002-WCON-015
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.route("**/*", handle_route_connection_rejected)
|
||||
page.get_by_test_id("connect-vega-wallet").click()
|
||||
page.get_by_test_id("connector-jsonRpc").click()
|
||||
expect(page.get_by_test_id("dialog-content").nth(1)).to_have_text(
|
||||
"User errorthe user rejected the wallet connectionTry againAbout the Vega wallet | Supported browsers "
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_wallet_connection_error_transaction(continuous_market, page: Page):
|
||||
# 0003-WTXN-009
|
||||
# 0003-WTXN-011
|
||||
# 0002-WCON-016
|
||||
# 0003-WTXN-008
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id(order_size).fill("10")
|
||||
page.get_by_test_id(order_price).fill("120")
|
||||
page.route("**/*", handle_route_connection_lost)
|
||||
page.get_by_test_id(place_order).click()
|
||||
expect(page.get_by_test_id("toast-content")).to_have_text(
|
||||
"Wallet disconnectedThe connection to your Vega Wallet has been lost.Connect vega wallet"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_wallet_transaction_rejected(continuous_market, page: Page):
|
||||
# 0003-WTXN-007
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id(order_size).fill("10")
|
||||
page.get_by_test_id(order_price).fill("120")
|
||||
page.route("**/*", handle_route_connection_rejected)
|
||||
page.get_by_test_id(place_order).click()
|
||||
expect(page.get_by_test_id("toast-content")).to_have_text(
|
||||
"Error occurredthe user rejected the wallet connection"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_wallet_connection_approve(continuous_market, page: Page):
|
||||
# 0002-WCON-005
|
||||
# 0002-WCON-007
|
||||
# 0002-WCON-009
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
page.get_by_test_id(order_size).fill("10")
|
||||
page.get_by_test_id(order_price).fill("120")
|
||||
page.route("**/*", assert_connection_approve)
|
||||
page.get_by_test_id(place_order).click()
|
||||
@@ -1,62 +0,0 @@
|
||||
import {
|
||||
Intent,
|
||||
useToasts,
|
||||
ToastHeading,
|
||||
CLOSE_AFTER,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useT } from '../use-t';
|
||||
import { VegaWalletConnectButton } from '../../components/vega-wallet-connect-button';
|
||||
|
||||
const WALLET_DISCONNECTED_TOAST_ID = 'WALLET_DISCONNECTED_TOAST_ID';
|
||||
|
||||
export const useWalletDisconnectedToasts = () => {
|
||||
const t = useT();
|
||||
const [hasToast, setToast, updateToast] = useToasts((state) => [
|
||||
state.hasToast,
|
||||
state.setToast,
|
||||
state.update,
|
||||
]);
|
||||
const { isAlive } = useVegaWallet();
|
||||
|
||||
const toast = useMemo(
|
||||
() => ({
|
||||
id: WALLET_DISCONNECTED_TOAST_ID,
|
||||
intent: Intent.Danger,
|
||||
content: (
|
||||
<>
|
||||
<ToastHeading>{t('Wallet connection lost')}</ToastHeading>
|
||||
<p>{t('The connection to the Vega wallet has been lost.')}</p>
|
||||
<p className="mt-2">
|
||||
<VegaWalletConnectButton
|
||||
intent={Intent.Danger}
|
||||
onClick={() => {
|
||||
updateToast(WALLET_DISCONNECTED_TOAST_ID, {
|
||||
hidden: true,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
onClose: () => {
|
||||
updateToast(WALLET_DISCONNECTED_TOAST_ID, {
|
||||
hidden: true,
|
||||
});
|
||||
},
|
||||
closeAfter: CLOSE_AFTER,
|
||||
}),
|
||||
[t, updateToast]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAlive === false) {
|
||||
if (hasToast(WALLET_DISCONNECTED_TOAST_ID)) {
|
||||
updateToast(WALLET_DISCONNECTED_TOAST_ID, { hidden: false });
|
||||
} else {
|
||||
setToast(toast);
|
||||
}
|
||||
}
|
||||
}, [hasToast, isAlive, setToast, t, toast, updateToast]);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ToastsContainer, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { Intent, ToastsContainer, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { useProposalToasts } from '@vegaprotocol/proposals';
|
||||
import { useVegaTransactionToasts } from '@vegaprotocol/web3';
|
||||
import { useEthereumTransactionToasts } from '@vegaprotocol/web3';
|
||||
@@ -6,7 +6,26 @@ import { useEthereumWithdrawApprovalsToasts } from '@vegaprotocol/web3';
|
||||
import { useReadyToWithdrawalToasts } from '@vegaprotocol/withdraws';
|
||||
import { Links } from '../lib/links';
|
||||
import { useReferralToasts } from '../client-pages/referrals/hooks/use-referral-toasts';
|
||||
import { useWalletDisconnectedToasts } from '../lib/hooks/use-wallet-disconnected-toasts';
|
||||
import {
|
||||
useWalletDisconnectToastActions,
|
||||
useWalletDisconnectedToasts,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { VegaWalletConnectButton } from '../components/vega-wallet-connect-button';
|
||||
|
||||
const WalletDisconnectAdditionalContent = () => {
|
||||
const { hideToast } = useWalletDisconnectToastActions();
|
||||
return (
|
||||
<p className="mt-2">
|
||||
<VegaWalletConnectButton
|
||||
intent={Intent.Danger}
|
||||
onClick={() => {
|
||||
// hide toast when clicked on `Connect`
|
||||
hideToast();
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export const ToastsManager = () => {
|
||||
useProposalToasts();
|
||||
@@ -17,7 +36,7 @@ export const ToastsManager = () => {
|
||||
withdrawalsLink: Links.PORTFOLIO(),
|
||||
});
|
||||
useReferralToasts();
|
||||
useWalletDisconnectedToasts();
|
||||
useWalletDisconnectedToasts(<WalletDisconnectAdditionalContent />);
|
||||
|
||||
const toasts = useToasts((store) => store.toasts);
|
||||
return <ToastsContainer order="desc" toasts={toasts} />;
|
||||
|
||||
@@ -3,6 +3,9 @@ fragment MarginFields on MarginLevels {
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
asset {
|
||||
id
|
||||
}
|
||||
@@ -33,6 +36,9 @@ subscription MarginsSubscription($partyId: ID!) {
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
|
||||
+9
-3
@@ -3,21 +3,21 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } };
|
||||
export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } };
|
||||
|
||||
export type MarginsQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarginsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, marginsConnection?: { __typename?: 'MarginConnection', edges?: Array<{ __typename?: 'MarginEdge', node: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
|
||||
export type MarginsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, marginsConnection?: { __typename?: 'MarginConnection', edges?: Array<{ __typename?: 'MarginEdge', node: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
|
||||
|
||||
export type MarginsSubscriptionSubscriptionVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarginsSubscriptionSubscription = { __typename?: 'Subscription', margins: { __typename?: 'MarginLevelsUpdate', marketId: string, asset: string, partyId: string, maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, timestamp: any } };
|
||||
export type MarginsSubscriptionSubscription = { __typename?: 'Subscription', margins: { __typename?: 'MarginLevelsUpdate', marketId: string, asset: string, partyId: string, maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: string, timestamp: any } };
|
||||
|
||||
export const MarginFieldsFragmentDoc = gql`
|
||||
fragment MarginFields on MarginLevels {
|
||||
@@ -25,6 +25,9 @@ export const MarginFieldsFragmentDoc = gql`
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
asset {
|
||||
id
|
||||
}
|
||||
@@ -85,6 +88,9 @@ export const MarginsSubscriptionDocument = gql`
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
marginFactor
|
||||
marginMode
|
||||
orderMarginLevel
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ export interface AccountFields extends Account {
|
||||
// The total balance of these accounts will be used for the 'used' column in the
|
||||
// collateral table
|
||||
const USE_ACCOUNT_TYPES = [
|
||||
AccountType.ACCOUNT_TYPE_ORDER_MARGIN,
|
||||
AccountType.ACCOUNT_TYPE_MARGIN,
|
||||
AccountType.ACCOUNT_TYPE_BOND,
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
|
||||
@@ -4,14 +4,6 @@ import * as Types from '@vegaprotocol/types';
|
||||
import type { AccountFields } from './accounts-data-provider';
|
||||
import { getAccountData } from './accounts-data-provider';
|
||||
|
||||
const marginHealthChartTestId = 'margin-health-chart';
|
||||
|
||||
jest.mock('./margin-health-chart', () => ({
|
||||
MarginHealthChart: () => {
|
||||
return <div data-testid={marginHealthChartTestId}></div>;
|
||||
},
|
||||
}));
|
||||
|
||||
const singleRow = {
|
||||
__typename: 'AccountBalance',
|
||||
type: Types.AccountType.ACCOUNT_TYPE_MARGIN,
|
||||
@@ -49,10 +41,10 @@ describe('BreakdownTable', () => {
|
||||
render(<BreakdownTable data={singleRowData} />);
|
||||
});
|
||||
const headers = await screen.findAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(4);
|
||||
expect(headers).toHaveLength(3);
|
||||
expect(
|
||||
headers.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim())
|
||||
).toEqual(['Market', 'Account type', 'Balance', 'Margin health']);
|
||||
).toEqual(['Market', 'Account type', 'Balance']);
|
||||
});
|
||||
|
||||
it('should apply correct formatting', async () => {
|
||||
@@ -70,24 +62,6 @@ describe('BreakdownTable', () => {
|
||||
cells.slice(0, -1).forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
expect(screen.getByTestId(marginHealthChartTestId)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays margin health chart only for margin account', async () => {
|
||||
await act(async () => {
|
||||
render(
|
||||
<BreakdownTable
|
||||
data={[
|
||||
{
|
||||
...singleRow,
|
||||
type: Types.AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
market: null,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
expect(screen.queryByTestId(marginHealthChartTestId)).toBeNull();
|
||||
});
|
||||
|
||||
it('should get correct account data', () => {
|
||||
|
||||
@@ -16,14 +16,13 @@ import { ProgressBarCell } from '@vegaprotocol/datagrid';
|
||||
import { AgGrid, PriceCell } from '@vegaprotocol/datagrid';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import { accountValuesComparator } from './accounts-table';
|
||||
import { MarginHealthChart } from './margin-health-chart';
|
||||
import { MarketNameCell } from '@vegaprotocol/datagrid';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
const defaultColDef = {
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
minWidth: 100,
|
||||
flex: 1,
|
||||
};
|
||||
|
||||
interface BreakdownTableProps extends AgGridReactProps {
|
||||
@@ -39,7 +38,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
width: 90,
|
||||
maxWidth: 150,
|
||||
pinned: true,
|
||||
sort: 'desc',
|
||||
cellRenderer: ({
|
||||
@@ -111,23 +110,6 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
},
|
||||
comparator: accountValuesComparator,
|
||||
},
|
||||
{
|
||||
headerName: t('Margin health'),
|
||||
field: 'market.id',
|
||||
maxWidth: 500,
|
||||
sortable: false,
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaICellRendererParams<AccountFields, 'market.id'>) =>
|
||||
data?.market?.id &&
|
||||
data.type === AccountType['ACCOUNT_TYPE_MARGIN'] &&
|
||||
data?.asset.id ? (
|
||||
<MarginHealthChart
|
||||
marketId={data.market.id}
|
||||
assetId={data.asset.id}
|
||||
/>
|
||||
) : null,
|
||||
},
|
||||
];
|
||||
return defs;
|
||||
}, [t]);
|
||||
|
||||
@@ -20,14 +20,20 @@ const update = (
|
||||
return produce(data || [], (draft) => {
|
||||
const { marketId } = delta;
|
||||
const index = draft.findIndex((node) => node.market.id === marketId);
|
||||
const deltaData = {
|
||||
maintenanceLevel: delta.maintenanceLevel,
|
||||
searchLevel: delta.searchLevel,
|
||||
initialLevel: delta.initialLevel,
|
||||
collateralReleaseLevel: delta.collateralReleaseLevel,
|
||||
marginFactor: delta.marginFactor,
|
||||
marginMode: delta.marginMode,
|
||||
orderMarginLevel: delta.orderMarginLevel,
|
||||
};
|
||||
if (index !== -1) {
|
||||
const currNode = draft[index];
|
||||
draft[index] = {
|
||||
...currNode,
|
||||
maintenanceLevel: delta.maintenanceLevel,
|
||||
searchLevel: delta.searchLevel,
|
||||
initialLevel: delta.initialLevel,
|
||||
collateralReleaseLevel: delta.collateralReleaseLevel,
|
||||
...deltaData,
|
||||
};
|
||||
} else {
|
||||
draft.unshift({
|
||||
@@ -36,10 +42,7 @@ const update = (
|
||||
__typename: 'Market',
|
||||
id: delta.marketId,
|
||||
},
|
||||
maintenanceLevel: delta.maintenanceLevel,
|
||||
searchLevel: delta.searchLevel,
|
||||
initialLevel: delta.initialLevel,
|
||||
collateralReleaseLevel: delta.collateralReleaseLevel,
|
||||
...deltaData,
|
||||
asset: {
|
||||
__typename: 'Asset',
|
||||
id: delta.asset,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import type { MarginFieldsFragment } from './__generated__/Margins';
|
||||
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
import { MarginMode } from '@vegaprotocol/types';
|
||||
|
||||
const asset: AssetFieldsFragment = {
|
||||
id: 'assetId',
|
||||
@@ -18,6 +19,9 @@ const margins: MarginFieldsFragment = {
|
||||
initialLevel: '800',
|
||||
searchLevel: '600',
|
||||
maintenanceLevel: '400',
|
||||
marginFactor: '',
|
||||
marginMode: MarginMode.MARGIN_MODE_CROSS_MARGIN,
|
||||
orderMarginLevel: '',
|
||||
market: {
|
||||
id: 'marketId',
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user