Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5eb25db969 |
@@ -19,7 +19,7 @@ jobs:
|
||||
create-docker-image:
|
||||
name: Create docker image for console-test
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
timeout-minutes: 20
|
||||
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: 45
|
||||
timeout-minutes: 20
|
||||
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 1 --dist loadfile --durations=45
|
||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
|
||||
working-directory: apps/trading/e2e
|
||||
#----------------------------------------------
|
||||
# upload traces
|
||||
@@ -227,46 +227,3 @@ jobs:
|
||||
name: worker-logs
|
||||
path: ./logs/
|
||||
retention-days: 15
|
||||
#----------------------------------------------
|
||||
# ----- upload market-sim logs -----
|
||||
#----------------------------------------------
|
||||
- name: Prepare and Zip market-sim-logs
|
||||
if: always()
|
||||
run: |
|
||||
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
|
||||
|
||||
echo "Moving vega-sim-* folders to $parent_dir"
|
||||
echo "$folders" | xargs -I {} mv {} "$parent_dir/"
|
||||
|
||||
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: always()
|
||||
with:
|
||||
name: market-sim-logs
|
||||
path: market-sim-logs.zip
|
||||
retention-days: 15
|
||||
|
||||
@@ -4,7 +4,7 @@ The front-end monorepo provides a toolkit for building apps that interact with V
|
||||
|
||||
This repository is managed using [Nx](https://nx.dev).
|
||||
|
||||
## 🔎 Applications in this repo
|
||||
# 🔎 Applications in this repo
|
||||
|
||||
### [Block explorer](./apps/explorer)
|
||||
|
||||
@@ -30,7 +30,7 @@ Hosting for static content being shared across apps, for example fonts.
|
||||
|
||||
The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract.
|
||||
|
||||
## 🧱 Libraries in this repo
|
||||
# 🧱 Libraries in this repo
|
||||
|
||||
### [UI toolkit](./libs/ui-toolkit)
|
||||
|
||||
@@ -53,7 +53,7 @@ A utility library for connecting to the Ethereum network and interacting with Ve
|
||||
|
||||
Generic react helpers that can be used across multiple applications, along with other utilities.
|
||||
|
||||
## 💻 Develop
|
||||
# 💻 Develop
|
||||
|
||||
### Set up
|
||||
|
||||
@@ -103,7 +103,7 @@ In CI linting, formatting and also run. These checks can be seen in the [CI work
|
||||
|
||||
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
|
||||
|
||||
## 🐋 Hosting a console
|
||||
# 🐋 Hosting a console
|
||||
|
||||
To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions).
|
||||
|
||||
@@ -226,6 +226,6 @@ Note: The script is only needed if capsule was built for first time or fresh. To
|
||||
vega wallet service run -n DV --load-tokens --tokens-passphrase-file passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet
|
||||
```
|
||||
|
||||
## 📑 License
|
||||
# 📑 License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
@@ -44,7 +44,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
cy.getByTestId('icon-cross').click();
|
||||
});
|
||||
|
||||
it('Proposal page displayed on mobile', function () {
|
||||
it.skip('Proposal page displayed on mobile', function () {
|
||||
const proposalTitle = 'Add Lorem Ipsum market';
|
||||
|
||||
cy.common_switch_to_mobile_and_click_toggle();
|
||||
@@ -55,7 +55,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('Able to view new asset proposal', function () {
|
||||
it('Able to view new asset proposal', function () {
|
||||
const proposalTitle = 'Test new asset proposal';
|
||||
const newAssetProposalBody = getNewAssetTxBody();
|
||||
cy.VegaWalletSubmitProposal(newAssetProposalBody);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { NestedDataList, sortNestedDataByChildren } from './nested-data-list';
|
||||
import {
|
||||
BORDER_COLOURS,
|
||||
NestedDataList,
|
||||
sortNestedDataByChildren,
|
||||
} from './nested-data-list';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
const mockData = {
|
||||
@@ -57,6 +61,38 @@ 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
@@ -1,54 +0,0 @@
|
||||
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>;
|
||||
@@ -1,11 +0,0 @@
|
||||
query ExplorerReferralCodeOwner($id: ID!) {
|
||||
referralSets(id: $id) {
|
||||
edges {
|
||||
node {
|
||||
createdAt
|
||||
updatedAt
|
||||
referrer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,93 +0,0 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -1,73 +0,0 @@
|
||||
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,8 +27,7 @@ export const sharedHeaderProps = {
|
||||
className: 'align-top',
|
||||
};
|
||||
|
||||
// The incoming type field is usually the right thing to show. Exceptions are listed here
|
||||
const LabelOverrides: Record<BlockExplorerTransactionResult['type'], string> = {
|
||||
const Labels: Record<BlockExplorerTransactionResult['type'], string> = {
|
||||
'Stop Orders Submission': 'Stop Order',
|
||||
'Stop Orders Cancellation': 'Cancel Stop Order',
|
||||
};
|
||||
@@ -51,7 +50,7 @@ export const TxDetailsShared = ({
|
||||
const time: string = blockData?.result.block.header.time || '';
|
||||
const height: string = blockData?.result.block.header.height || txData.block;
|
||||
|
||||
const type = LabelOverrides[txData.type] || txData.type;
|
||||
const type = Labels[txData.type] || txData.type;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,52 +0,0 @@
|
||||
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,10 +28,6 @@ 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;
|
||||
@@ -125,14 +121,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,54 +0,0 @@
|
||||
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,7 +28,6 @@ export type FilterOption =
|
||||
| 'Delegate'
|
||||
| 'Ethereum Key Rotate Submission'
|
||||
| 'Issue Signatures'
|
||||
| 'Join Team'
|
||||
| 'Key Rotate Submission'
|
||||
| 'Liquidity Provision Order'
|
||||
| 'Node Signature'
|
||||
@@ -48,46 +47,45 @@ export type FilterOption =
|
||||
| '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 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 AllFilterOptions: FilterOption[] =
|
||||
Object.values(filterOptions).flat();
|
||||
// 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 interface TxFilterProps {
|
||||
filters: Set<FilterOption>;
|
||||
@@ -124,33 +122,46 @@ export const TxsFilter = ({ filters, setFilters }: TxFilterProps) => {
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
|
||||
{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>
|
||||
{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>
|
||||
))}
|
||||
<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,8 +48,6 @@ 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,10 +31,6 @@ const Tx = () => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!data || !data?.transaction) {
|
||||
errorMessage = 'Transaction not found';
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<PageHeader
|
||||
@@ -53,7 +49,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 || !pubKey) {
|
||||
return <>{t('Transaction could not be found')}</>;
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
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.skip('Unable to submit proposal with public key', function () {
|
||||
it('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,10 +55,7 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
cy.getByTestId('dialog-content')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId('dialog-title').should(
|
||||
'have.text',
|
||||
'Transaction failed'
|
||||
);
|
||||
cy.get('h1').should('have.text', 'Transaction failed');
|
||||
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -112,7 +112,6 @@ 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',
|
||||
@@ -197,7 +196,6 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
|
||||
timeWindow: '3600',
|
||||
scalingFactor: 10,
|
||||
},
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
triggeringRatio: '0.7',
|
||||
auctionExtension: '1',
|
||||
},
|
||||
@@ -242,7 +240,6 @@ 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',
|
||||
@@ -337,7 +334,6 @@ export function createSuccessorMarketProposalTxBody(
|
||||
timeWindow: '3600',
|
||||
scalingFactor: 10,
|
||||
},
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
triggeringRatio: '0.7',
|
||||
auctionExtension: '1',
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from '@vegaprotocol/web3';
|
||||
import { Web3Provider } from '@vegaprotocol/web3';
|
||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||
import { VegaWalletProvider, useChainId } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
useVegaTransactionManager,
|
||||
useVegaTransactionUpdater,
|
||||
@@ -96,9 +96,7 @@ const cache: InMemoryCacheConfig = {
|
||||
const Web3Container = ({
|
||||
chainId,
|
||||
}: {
|
||||
/** Ethereum chain id */
|
||||
chainId: number;
|
||||
/** Ethereum provider url */
|
||||
providerUrl: string;
|
||||
}) => {
|
||||
const InitializeHandlers = () => {
|
||||
@@ -125,9 +123,6 @@ const Web3Container = ({
|
||||
MOZILLA_EXTENSION_URL,
|
||||
VEGA_WALLET_URL,
|
||||
} = useEnvironment();
|
||||
|
||||
const vegaChainId = useChainId(VEGA_URL);
|
||||
|
||||
useEffect(() => {
|
||||
if (chainId) {
|
||||
return initializeConnectors(
|
||||
@@ -162,8 +157,7 @@ const Web3Container = ({
|
||||
!VEGA_EXPLORER_URL ||
|
||||
!DocsLinks ||
|
||||
!CHROME_EXTENSION_URL ||
|
||||
!MOZILLA_EXTENSION_URL ||
|
||||
!vegaChainId
|
||||
!MOZILLA_EXTENSION_URL
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -175,7 +169,6 @@ const Web3Container = ({
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
chainId: vegaChainId,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
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,5 +1,3 @@
|
||||
import type { ApolloError } from '@apollo/client';
|
||||
|
||||
export const PARTY_NOT_FOUND = 'failed to get party for ID';
|
||||
|
||||
export const isPartyNotFoundError = (error: { message: string }) => {
|
||||
@@ -8,23 +6,3 @@ 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(
|
||||
'Update to market ID: MarketId'
|
||||
'Market change: MarketId'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
+4
-42
@@ -1,11 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
CopyWithTooltip,
|
||||
Lozenge,
|
||||
Tooltip,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Lozenge, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { shorten } from '@vegaprotocol/utils';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
@@ -18,12 +12,7 @@ import {
|
||||
useNewTransferProposalDetails,
|
||||
useSuccessorMarketProposalDetails,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import {
|
||||
CONSOLE_MARKET_PAGE,
|
||||
DApp,
|
||||
useFeatureFlags,
|
||||
useLinks,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import Routes from '../../../routes';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { VoteState } from '../vote-details/use-user-vote';
|
||||
@@ -43,8 +32,6 @@ export const ProposalHeader = ({
|
||||
const { t } = useTranslation();
|
||||
const change = proposal?.terms.change;
|
||||
|
||||
const consoleLink = useLinks(DApp.Console);
|
||||
|
||||
let details: ReactNode;
|
||||
let proposalType = '';
|
||||
let fallbackTitle = '';
|
||||
@@ -119,33 +106,8 @@ export const ProposalHeader = ({
|
||||
fallbackTitle = t('UpdateMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
<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>
|
||||
<span>{t('MarketChange')}:</span>{' '}
|
||||
<span>{truncateMiddle(change.marketId)}</span>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -48,7 +48,6 @@ const vegaWalletConfig: VegaWalletConfig = {
|
||||
chromeExtensionUrl: 'chrome',
|
||||
mozillaExtensionUrl: 'mozilla',
|
||||
},
|
||||
chainId: 'VEGA_CHAIN_ID',
|
||||
};
|
||||
|
||||
const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
||||
|
||||
@@ -18,7 +18,6 @@ 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;
|
||||
@@ -43,10 +42,8 @@ export const VoteButtonsContainer = (props: VoteButtonsContainerProps) => {
|
||||
skip: !pubKey,
|
||||
});
|
||||
|
||||
const filteredErrors = filterAcceptableGraphqlErrors(error);
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={filteredErrors} data={data}>
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
<VoteButtons
|
||||
{...props}
|
||||
currentStakeAvailable={toBigNum(
|
||||
|
||||
@@ -205,6 +205,7 @@ query Proposal(
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
@@ -212,6 +213,7 @@ query Proposal(
|
||||
}
|
||||
positionDecimalPlaces
|
||||
linearSlippageFactor
|
||||
quadraticSlippageFactor
|
||||
}
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
@@ -364,6 +366,7 @@ query Proposal(
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
|
||||
File diff suppressed because one or more lines are too long
+13
-23
@@ -10,7 +10,6 @@ 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;
|
||||
|
||||
@@ -100,24 +99,17 @@ 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={filteredErrors}
|
||||
data={filteredData}
|
||||
error={error}
|
||||
data={data}
|
||||
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
|
||||
@@ -126,19 +118,17 @@ export const EpochIndividualRewards = ({
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{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>
|
||||
)}
|
||||
<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: NodesFragmentFragment = {
|
||||
const defaultNode = {
|
||||
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('13.16%');
|
||||
).toHaveTextContent('10.07%');
|
||||
|
||||
expect(
|
||||
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
|
||||
|
||||
+2
-11
@@ -185,15 +185,6 @@ 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,
|
||||
@@ -222,11 +213,11 @@ export const ConsensusValidatorsTable = ({
|
||||
2
|
||||
),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
|
||||
overstakingPenalty,
|
||||
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||
totalPenalty,
|
||||
calculateOverallPenalty(id, allNodesInPreviousEpoch),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
|
||||
+2
-11
@@ -124,15 +124,6 @@ export const StandbyPendingValidatorsTable = ({
|
||||
}
|
||||
}
|
||||
|
||||
const overstakingPenalty = calculateOverallPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
const totalPenalty = calculateOverstakedPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
||||
@@ -163,11 +154,11 @@ export const StandbyPendingValidatorsTable = ({
|
||||
2
|
||||
),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
|
||||
overstakingPenalty,
|
||||
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||
totalPenalty,
|
||||
calculateOverallPenalty(id, allNodesInPreviousEpoch),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
|
||||
@@ -266,9 +266,7 @@ export const ValidatorTable = ({
|
||||
|
||||
<Tooltip description={t('OverstakedPenaltyDescription')}>
|
||||
<span data-testid="overstaking-penalty">
|
||||
{penalties.overstaked
|
||||
? formatNumberPercentage(penalties.overstaked, 2)
|
||||
: '-'}
|
||||
{formatNumberPercentage(penalties.overstaked, 2)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</KeyValueTableRow>
|
||||
@@ -287,9 +285,7 @@ export const ValidatorTable = ({
|
||||
</span>
|
||||
<span data-testid="total-penalties">
|
||||
<strong>
|
||||
{penalties.overall
|
||||
? formatNumberPercentage(penalties.overall, 2)
|
||||
: '-'}
|
||||
{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,6 +106,38 @@ 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));
|
||||
@@ -120,6 +152,17 @@ 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(
|
||||
@@ -139,107 +182,3 @@ 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,7 +5,6 @@ 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<
|
||||
@@ -22,10 +21,7 @@ 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[]
|
||||
): BigNumber | null => {
|
||||
const calculateTheoreticalStakeScore = (nodeId: string, nodes: Node[]) => {
|
||||
const node = nodes.find((n) => n.id === nodeId);
|
||||
if (!node) {
|
||||
return new BigNumber(0);
|
||||
@@ -46,25 +42,14 @@ const calculateTheoreticalStakeScore = (
|
||||
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
|
||||
* @returns %
|
||||
*/
|
||||
export const calculateOverallPenalty = (
|
||||
nodeId: string,
|
||||
nodes: Node[]
|
||||
): BigNumber | null => {
|
||||
export const calculateOverallPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
const node = nodes.find((n) => n.id === nodeId);
|
||||
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
|
||||
if (
|
||||
!node ||
|
||||
isNull(tts) ||
|
||||
!node.rewardScore ||
|
||||
(node.rewardScore && isNull(node.rewardScore.validatorScore))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (tts.isZero()) {
|
||||
if (!node || tts.isZero()) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const penalty = new BigNumber(1)
|
||||
.minus(new BigNumber(node.rewardScore.validatorScore).dividedBy(tts))
|
||||
.minus(new BigNumber(node.rewardScore?.validatorScore || 0).dividedBy(tts))
|
||||
.times(100);
|
||||
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
|
||||
};
|
||||
@@ -75,21 +60,10 @@ export const calculateOverallPenalty = (
|
||||
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
|
||||
* @returns %
|
||||
*/
|
||||
export const calculateOverstakedPenalty = (
|
||||
nodeId: string,
|
||||
nodes: Node[]
|
||||
): BigNumber | null => {
|
||||
export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
const node = nodes.find((n) => n.id === nodeId);
|
||||
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
|
||||
if (
|
||||
!node ||
|
||||
isNull(tts) ||
|
||||
isNull(node.rewardScore) ||
|
||||
(node.rewardScore && node.rewardScore.rawValidatorScore === null)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (tts.isZero()) {
|
||||
if (!node || tts.isZero()) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const penalty = new BigNumber(1)
|
||||
@@ -104,9 +78,7 @@ export const calculateOverstakedPenalty = (
|
||||
* Calculates performance penalty based on the given performance score.
|
||||
* @returns %
|
||||
*/
|
||||
export const calculatesPerformancePenalty = (
|
||||
performanceScore: string
|
||||
): BigNumber => {
|
||||
export const calculatesPerformancePenalty = (performanceScore: string) => {
|
||||
const penalty = new BigNumber(1)
|
||||
.minus(new BigNumber(performanceScore))
|
||||
.times(100);
|
||||
@@ -151,6 +123,60 @@ 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,50 +1,14 @@
|
||||
import {
|
||||
Intent,
|
||||
ToastsContainer,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
useToasts,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { ToastsContainer, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
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} />;
|
||||
|
||||
+2
-7
@@ -22,12 +22,7 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=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=
|
||||
NX_TEAM_COMPETITION=true
|
||||
|
||||
@@ -28,6 +28,3 @@ NX_REFERRALS=true
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
# NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
# NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -314,7 +314,7 @@ describe('Closed', () => {
|
||||
});
|
||||
|
||||
it('display market actions', async () => {
|
||||
// Use market with a successor Id as the actions dropdown will optionally
|
||||
// Use market with a succcessor Id as the actions dropdown will optionally
|
||||
// show a link to the successor market
|
||||
const marketsWithSuccessorAndParent = [
|
||||
{
|
||||
|
||||
@@ -137,8 +137,6 @@ const ClosedMarketsDataGrid = ({
|
||||
headerName: t('Market'),
|
||||
field: 'code',
|
||||
cellRenderer: 'MarketCodeCell',
|
||||
width: 150,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
headerName: t('Status'),
|
||||
@@ -282,7 +280,6 @@ const ClosedMarketsDataGrid = ({
|
||||
return (
|
||||
<AgGrid
|
||||
rowData={rowData}
|
||||
defaultColDef={COL_DEFS.default}
|
||||
columnDefs={colDefs}
|
||||
getRowId={({ data }) => data.id}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
||||
|
||||
@@ -17,7 +17,6 @@ const defaultColDef = {
|
||||
filter: true,
|
||||
resizable: true,
|
||||
filterParams: { buttons: ['reset'] },
|
||||
minWidth: 120,
|
||||
};
|
||||
|
||||
const components = {
|
||||
|
||||
@@ -53,7 +53,6 @@ export const MarketsPage = () => {
|
||||
size="extra-small"
|
||||
data-testid="propose-new-market"
|
||||
href={externalLink}
|
||||
target="_blank"
|
||||
>
|
||||
{t('Propose a new market')}
|
||||
</TradingAnchorButton>
|
||||
|
||||
@@ -29,7 +29,6 @@ export const useColumnDefs = () => {
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'tradableInstrument.instrument.code',
|
||||
pinned: true,
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
fragment TeamFields on Team {
|
||||
teamId
|
||||
referrer
|
||||
name
|
||||
teamUrl
|
||||
avatarUrl
|
||||
createdAt
|
||||
createdAtEpoch
|
||||
closed
|
||||
}
|
||||
|
||||
fragment TeamStatsFields on TeamStatistics {
|
||||
teamId
|
||||
totalQuantumVolume
|
||||
totalQuantumRewards
|
||||
totalGamesPlayed
|
||||
quantumRewards {
|
||||
epoch
|
||||
total_quantum_rewards
|
||||
}
|
||||
gamesPlayed
|
||||
}
|
||||
|
||||
fragment TeamRefereeFields on TeamReferee {
|
||||
teamId
|
||||
referee
|
||||
joinedAt
|
||||
joinedAtEpoch
|
||||
}
|
||||
|
||||
fragment TeamEntity on TeamGameEntity {
|
||||
rank
|
||||
volume
|
||||
rewardMetric
|
||||
rewardEarned
|
||||
totalRewardsEarned
|
||||
team {
|
||||
teamId
|
||||
}
|
||||
}
|
||||
|
||||
fragment TeamGameFields on Game {
|
||||
id
|
||||
epoch
|
||||
numberOfParticipants
|
||||
entities {
|
||||
... on TeamGameEntity {
|
||||
...TeamEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query Team($teamId: ID!, $partyId: ID) {
|
||||
teams(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamFields
|
||||
}
|
||||
}
|
||||
}
|
||||
partyTeams: teams(partyId: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamFields
|
||||
}
|
||||
}
|
||||
}
|
||||
teamsStatistics(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamStatsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
teamReferees(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamRefereeFields
|
||||
}
|
||||
}
|
||||
}
|
||||
games(entityScope: ENTITY_SCOPE_TEAMS) {
|
||||
edges {
|
||||
node {
|
||||
...TeamGameFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type TeamFieldsFragment = { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean };
|
||||
|
||||
export type TeamStatsFieldsFragment = { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, total_quantum_rewards: string }> };
|
||||
|
||||
export type TeamRefereeFieldsFragment = { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number };
|
||||
|
||||
export type TeamEntityFragment = { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } };
|
||||
|
||||
export type TeamGameFieldsFragment = { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> };
|
||||
|
||||
export type TeamQueryVariables = Types.Exact<{
|
||||
teamId: Types.Scalars['ID'];
|
||||
partyId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type TeamQuery = { __typename?: 'Query', teams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean } }> } | null, partyTeams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean } }> } | null, teamsStatistics?: { __typename?: 'TeamsStatisticsConnection', edges: Array<{ __typename?: 'TeamStatisticsEdge', node: { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, total_quantum_rewards: string }> } }> } | null, teamReferees?: { __typename?: 'TeamRefereeConnection', edges: Array<{ __typename?: 'TeamRefereeEdge', node: { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number } }> } | null, games: { __typename?: 'GamesConnection', edges?: Array<{ __typename?: 'GameEdge', node: { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> } } | null> | null } };
|
||||
|
||||
export const TeamFieldsFragmentDoc = gql`
|
||||
fragment TeamFields on Team {
|
||||
teamId
|
||||
referrer
|
||||
name
|
||||
teamUrl
|
||||
avatarUrl
|
||||
createdAt
|
||||
createdAtEpoch
|
||||
closed
|
||||
}
|
||||
`;
|
||||
export const TeamStatsFieldsFragmentDoc = gql`
|
||||
fragment TeamStatsFields on TeamStatistics {
|
||||
teamId
|
||||
totalQuantumVolume
|
||||
totalQuantumRewards
|
||||
totalGamesPlayed
|
||||
quantumRewards {
|
||||
epoch
|
||||
total_quantum_rewards
|
||||
}
|
||||
gamesPlayed
|
||||
}
|
||||
`;
|
||||
export const TeamRefereeFieldsFragmentDoc = gql`
|
||||
fragment TeamRefereeFields on TeamReferee {
|
||||
teamId
|
||||
referee
|
||||
joinedAt
|
||||
joinedAtEpoch
|
||||
}
|
||||
`;
|
||||
export const TeamEntityFragmentDoc = gql`
|
||||
fragment TeamEntity on TeamGameEntity {
|
||||
rank
|
||||
volume
|
||||
rewardMetric
|
||||
rewardEarned
|
||||
totalRewardsEarned
|
||||
team {
|
||||
teamId
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const TeamGameFieldsFragmentDoc = gql`
|
||||
fragment TeamGameFields on Game {
|
||||
id
|
||||
epoch
|
||||
numberOfParticipants
|
||||
entities {
|
||||
... on TeamGameEntity {
|
||||
...TeamEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
${TeamEntityFragmentDoc}`;
|
||||
export const TeamDocument = gql`
|
||||
query Team($teamId: ID!, $partyId: ID) {
|
||||
teams(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamFields
|
||||
}
|
||||
}
|
||||
}
|
||||
partyTeams: teams(partyId: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamFields
|
||||
}
|
||||
}
|
||||
}
|
||||
teamsStatistics(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamStatsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
teamReferees(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamRefereeFields
|
||||
}
|
||||
}
|
||||
}
|
||||
games(entityScope: ENTITY_SCOPE_TEAMS) {
|
||||
edges {
|
||||
node {
|
||||
...TeamGameFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${TeamFieldsFragmentDoc}
|
||||
${TeamStatsFieldsFragmentDoc}
|
||||
${TeamRefereeFieldsFragmentDoc}
|
||||
${TeamGameFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useTeamQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useTeamQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTeamQuery` 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 } = useTeamQuery({
|
||||
* variables: {
|
||||
* teamId: // value for 'teamId'
|
||||
* partyId: // value for 'partyId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTeamQuery(baseOptions: Apollo.QueryHookOptions<TeamQuery, TeamQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<TeamQuery, TeamQueryVariables>(TeamDocument, options);
|
||||
}
|
||||
export function useTeamLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<TeamQuery, TeamQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<TeamQuery, TeamQueryVariables>(TeamDocument, options);
|
||||
}
|
||||
export type TeamQueryHookResult = ReturnType<typeof useTeamQuery>;
|
||||
export type TeamLazyQueryHookResult = ReturnType<typeof useTeamLazyQuery>;
|
||||
export type TeamQueryResult = Apollo.QueryResult<TeamQuery, TeamQueryVariables>;
|
||||
@@ -0,0 +1 @@
|
||||
export { Team } from './team';
|
||||
@@ -0,0 +1,395 @@
|
||||
import { useState, type ReactNode, type ButtonHTMLAttributes } from 'react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import countBy from 'lodash/countBy';
|
||||
import {
|
||||
TradingButton as Button,
|
||||
Intent,
|
||||
Pill,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
Tooltip,
|
||||
Splash,
|
||||
truncateMiddle,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import classNames from 'classnames';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { Table } from '../../components/table';
|
||||
import { formatNumberRounded, getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import {
|
||||
useTeam,
|
||||
type Team as TeamType,
|
||||
type TeamStats,
|
||||
type Member,
|
||||
type TeamGame,
|
||||
} from './use-team';
|
||||
import { DApp, EXPLORER_PARTIES, useLinks } from '@vegaprotocol/environment';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export const Team = () => {
|
||||
const t = useT();
|
||||
const { teamId } = useParams<{ teamId: string }>();
|
||||
const { team, stats, partyInTeam, members, games } = useTeam(teamId);
|
||||
|
||||
if (!team) {
|
||||
return (
|
||||
<Splash>
|
||||
<p>{t('Page not found')}</p>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TeamPage
|
||||
team={team}
|
||||
stats={stats}
|
||||
partyInTeam={partyInTeam}
|
||||
members={members}
|
||||
games={games}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const TeamPage = ({
|
||||
team,
|
||||
stats,
|
||||
partyInTeam,
|
||||
members,
|
||||
games,
|
||||
}: {
|
||||
team: TeamType;
|
||||
stats?: TeamStats;
|
||||
partyInTeam: boolean;
|
||||
members?: Member[];
|
||||
games?: TeamGame[];
|
||||
}) => {
|
||||
const t = useT();
|
||||
const [showGames, setShowGames] = useState(true);
|
||||
|
||||
return (
|
||||
<div className="relative h-full overflow-y-auto">
|
||||
<div className="absolute top-0 left-0 w-full h-[40%] -z-10 bg-[40%_0px] bg-cover bg-no-repeat bg-local bg-[url(/cover.png)]">
|
||||
<div className="absolute top-o left-0 w-full h-full bg-gradient-to-t from-white dark:from-vega-cdark-900 to-transparent from-20% to-60%" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 lg:gap-6 container p-4 mx-auto">
|
||||
<header className="flex gap-3 lg:gap-4 pt-5 lg:pt-10">
|
||||
<TeamAvatar imgUrl={team.avatarUrl} />
|
||||
<div className="flex flex-col items-start gap-1 lg:gap-3">
|
||||
<h1 className="calt text-2xl lg:text-3xl xl:text-5xl">
|
||||
{team.name}
|
||||
</h1>
|
||||
<JoinButton joined={partyInTeam} />
|
||||
</div>
|
||||
</header>
|
||||
<StatSection>
|
||||
<StatList>
|
||||
<Stat value={members ? members.length : 0} label={t('Members')} />
|
||||
<Stat
|
||||
value={stats ? stats.totalGamesPlayed : 0}
|
||||
label={t('Total games')}
|
||||
tooltip={t('Total number of games this team has participated in')}
|
||||
/>
|
||||
<StatSectionSeparator />
|
||||
<Stat
|
||||
value={
|
||||
stats
|
||||
? formatNumberRounded(
|
||||
new BigNumber(stats.totalQuantumVolume),
|
||||
'1e3'
|
||||
)
|
||||
: 0
|
||||
}
|
||||
label={t('Total volume')}
|
||||
/>
|
||||
<Stat
|
||||
value={
|
||||
stats
|
||||
? formatNumberRounded(
|
||||
new BigNumber(stats.totalQuantumRewards),
|
||||
'1e3'
|
||||
)
|
||||
: 0
|
||||
}
|
||||
label={t('Rewards paid')}
|
||||
tooltip={'Total amount of rewards paid out to this team in qUSD'}
|
||||
/>
|
||||
</StatList>
|
||||
</StatSection>
|
||||
{games && games.length ? (
|
||||
<StatSection>
|
||||
<FavoriteGame games={games} />
|
||||
<StatSectionSeparator />
|
||||
<LatestResults games={games} />
|
||||
</StatSection>
|
||||
) : null}
|
||||
<section>
|
||||
<div className="flex gap-4 lg:gap-8 mb-4 border-b border-default">
|
||||
<ToggleButton active={showGames} onClick={() => setShowGames(true)}>
|
||||
{t('Games ({{count}})', { count: games ? games.length : 0 })}
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
active={!showGames}
|
||||
onClick={() => setShowGames(false)}
|
||||
>
|
||||
{t('Members ({{count}})', {
|
||||
count: members ? members.length : 0,
|
||||
})}
|
||||
</ToggleButton>
|
||||
</div>
|
||||
{showGames ? <Games games={games} /> : <Members members={members} />}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Games = ({ games }: { games?: TeamGame[] }) => {
|
||||
const t = useT();
|
||||
|
||||
if (!games?.length) {
|
||||
return <p>{t('No games')}</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Table
|
||||
columns={[
|
||||
{ name: 'rank', displayName: t('Rank') },
|
||||
{
|
||||
name: 'epoch',
|
||||
displayName: t('Epoch'),
|
||||
headerClassName: 'hidden md:block',
|
||||
className: 'hidden md:block',
|
||||
},
|
||||
{ name: 'type', displayName: t('Type') },
|
||||
{ name: 'amount', displayName: t('Amount earned') },
|
||||
{
|
||||
name: 'teams',
|
||||
displayName: t('No. of participating teams'),
|
||||
headerClassName: 'hidden md:block',
|
||||
className: 'hidden md:block',
|
||||
},
|
||||
{ name: 'status', displayName: t('Status') },
|
||||
]}
|
||||
data={games.map((game) => ({
|
||||
rank: game.team.rank,
|
||||
epoch: game.epoch,
|
||||
type: game.team.rewardMetric,
|
||||
amount: game.team.totalRewardsEarned,
|
||||
teams: game.numberOfParticipants,
|
||||
}))}
|
||||
noCollapse={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const TeamAvatar = ({ imgUrl }: { imgUrl: string }) => {
|
||||
// TODO: add fallback avatars
|
||||
return (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={imgUrl}
|
||||
alt="Team avatar"
|
||||
className="rounded-full w-20 h-20 lg:w-[112px] lg:h-[112px] bg-vega-clight-700 dark:bg-vega-cdark-700 shrink-0"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Members = ({ members }: { members?: Member[] }) => {
|
||||
const t = useT();
|
||||
|
||||
if (!members?.length) {
|
||||
return <p>{t('No members')}</p>;
|
||||
}
|
||||
|
||||
const data = orderBy(
|
||||
members.map((m) => ({
|
||||
referee: <RefereeCell pubkey={m.referee} />,
|
||||
joinedAt: getDateTimeFormat().format(new Date(m.joinedAt)),
|
||||
joinedAtEpoch: Number(m.joinedAtEpoch),
|
||||
explorerLink: <RefereeLink pubkey={m.referee} />,
|
||||
})),
|
||||
'joinedAtEpoch',
|
||||
'desc'
|
||||
);
|
||||
|
||||
return (
|
||||
<Table
|
||||
columns={[
|
||||
{ name: 'referee', displayName: t('Referee') },
|
||||
{
|
||||
name: 'joinedAt',
|
||||
displayName: t('Joined at'),
|
||||
},
|
||||
{
|
||||
name: 'joinedAtEpoch',
|
||||
displayName: t('Joined epoch'),
|
||||
headerClassName: 'text-right',
|
||||
className: 'text-right',
|
||||
},
|
||||
{
|
||||
name: 'explorerLink',
|
||||
displayName: '',
|
||||
headerClassName: 'hidden md:block',
|
||||
className: 'hidden md:block text-right',
|
||||
},
|
||||
]}
|
||||
data={data}
|
||||
noCollapse={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const RefereeCell = ({ pubkey }: { pubkey: string }) => {
|
||||
return <span title={pubkey}>{truncateMiddle(pubkey)}</span>;
|
||||
};
|
||||
|
||||
const RefereeLink = ({ pubkey }: { pubkey: string }) => {
|
||||
const t = useT();
|
||||
|
||||
const linkCreator = useLinks(DApp.Explorer);
|
||||
const link = linkCreator(EXPLORER_PARTIES.replace(':id', pubkey));
|
||||
|
||||
return (
|
||||
<Link to={link} className="underline underline-offset-4">
|
||||
{t('View on explorer')}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const JoinButton = ({ joined }: { joined: boolean }) => {
|
||||
const t = useT();
|
||||
|
||||
if (joined) {
|
||||
return (
|
||||
<Button intent={Intent.None} disabled={true}>
|
||||
<span className="flex items-center gap-2">
|
||||
{t('Joined')}{' '}
|
||||
<span className="text-vega-green-600 dark:text-vega-green">
|
||||
<VegaIcon name={VegaIconNames.TICK} />
|
||||
</span>
|
||||
</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return <Button intent={Intent.Primary}>{t('Join this team')}</Button>;
|
||||
};
|
||||
|
||||
const LatestResults = ({ games }: { games: TeamGame[] }) => {
|
||||
const t = useT();
|
||||
const latestGames = games.slice(0, 5);
|
||||
|
||||
return (
|
||||
<dl>
|
||||
<dt className="text-muted text-sm">
|
||||
{t('Last {{count}} game results', { count: latestGames.length })}
|
||||
</dt>
|
||||
<dd className="flex gap-1">
|
||||
{latestGames.map((game) => {
|
||||
return (
|
||||
<Pill key={game.id} className="text-sm">
|
||||
{t('place', { count: game.team.rank, ordinal: true })}
|
||||
</Pill>
|
||||
);
|
||||
})}
|
||||
</dd>
|
||||
</dl>
|
||||
);
|
||||
};
|
||||
|
||||
const FavoriteGame = ({ games }: { games: TeamGame[] }) => {
|
||||
const t = useT();
|
||||
|
||||
const rewardMetrics = games.map((game) => game.team.rewardMetric);
|
||||
const count = countBy(rewardMetrics);
|
||||
|
||||
let favoriteMetric = '';
|
||||
let mostOccurances = 0;
|
||||
|
||||
for (const key in count) {
|
||||
if (count[key] > mostOccurances) {
|
||||
favoriteMetric = key;
|
||||
mostOccurances = count[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!favoriteMetric) return null;
|
||||
|
||||
return (
|
||||
<dl>
|
||||
<dt className="text-muted text-sm">{t('Favorite game')}</dt>
|
||||
<dd>
|
||||
<Pill className="flex-inline items-center gap-2 bg-transparent text-sm">
|
||||
<VegaIcon
|
||||
name={VegaIconNames.STAR}
|
||||
className="text-vega-yellow-400"
|
||||
/>{' '}
|
||||
{favoriteMetric}
|
||||
</Pill>
|
||||
</dd>
|
||||
</dl>
|
||||
);
|
||||
};
|
||||
|
||||
const ToggleButton = ({
|
||||
active,
|
||||
...props
|
||||
}: ButtonHTMLAttributes<HTMLButtonElement> & { active: boolean }) => {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={classNames('relative top-px uppercase border-b-2 py-4', {
|
||||
'text-muted border-transparent': !active,
|
||||
'border-vega-yellow': active,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const StatSection = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<section className="flex flex-col lg:flex-row gap-2 lg:gap-8">
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const StatSectionSeparator = () => {
|
||||
return <div className="hidden md:block border-r border-default" />;
|
||||
};
|
||||
|
||||
const StatList = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<dl className="grid grid-cols-[min-content_min-content] md:flex gap-4 md:gap-6 lg:gap-8 whitespace-nowrap">
|
||||
{children}
|
||||
</dl>
|
||||
);
|
||||
};
|
||||
|
||||
const Stat = ({
|
||||
value,
|
||||
label,
|
||||
tooltip,
|
||||
}: {
|
||||
value: ReactNode;
|
||||
label: ReactNode;
|
||||
tooltip?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div>
|
||||
<dd className="text-3xl lg:text-4xl">{value}</dd>
|
||||
<dt className="text-sm text-muted">
|
||||
{tooltip ? (
|
||||
<Tooltip description={tooltip} underline={false}>
|
||||
<span className="flex items-center gap-2">
|
||||
{label}
|
||||
<VegaIcon name={VegaIconNames.INFO} size={12} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
label
|
||||
)}
|
||||
</dt>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import compact from 'lodash/compact';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import {
|
||||
useTeamQuery,
|
||||
type TeamFieldsFragment,
|
||||
type TeamStatsFieldsFragment,
|
||||
type TeamRefereeFieldsFragment,
|
||||
type TeamEntityFragment,
|
||||
} from './__generated__/Team';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
|
||||
export type Team = TeamFieldsFragment;
|
||||
export type TeamStats = TeamStatsFieldsFragment;
|
||||
export type Member = TeamRefereeFieldsFragment;
|
||||
export type TeamEntity = TeamEntityFragment;
|
||||
export type TeamGame = ReturnType<typeof useTeam>['games'][number];
|
||||
|
||||
export const useTeam = (teamId?: string) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
|
||||
const { data, loading, error } = useTeamQuery({
|
||||
variables: { teamId: teamId || '', partyId: pubKey },
|
||||
skip: !teamId,
|
||||
});
|
||||
|
||||
const teamEdge = data?.teams?.edges.find((e) => e.node.teamId === teamId);
|
||||
const partyTeamEdge = data?.partyTeams?.edges[0];
|
||||
const teamStatsEdge = data?.teamsStatistics?.edges.find(
|
||||
(e) => e.node.teamId === teamId
|
||||
);
|
||||
const members = data?.teamReferees?.edges
|
||||
.filter((e) => e.node.teamId === teamId)
|
||||
.map((e) => e.node);
|
||||
|
||||
// Find games where the current team participated in
|
||||
const gamesWithTeam = compact(data?.games.edges).map((edge) => {
|
||||
const team = edge.node.entities.find((e) => {
|
||||
if (e.__typename !== 'TeamGameEntity') return false;
|
||||
if (e.team.teamId !== teamId) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!team) return null;
|
||||
|
||||
return {
|
||||
id: edge.node.id,
|
||||
epoch: edge.node.epoch,
|
||||
numberOfParticipants: edge.node.numberOfParticipants,
|
||||
team: team as TeamEntity, // TS can't infer that all the game entities are teams
|
||||
};
|
||||
});
|
||||
const games = orderBy(compact(gamesWithTeam), 'epoch', 'desc');
|
||||
|
||||
return {
|
||||
data,
|
||||
loading,
|
||||
error,
|
||||
stats: teamStatsEdge?.node,
|
||||
team: teamEdge?.node,
|
||||
members,
|
||||
games,
|
||||
partyInTeam: Boolean(partyTeamEdge),
|
||||
};
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { Teams } from './teams';
|
||||
@@ -1,7 +0,0 @@
|
||||
export const Teams = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Teams</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -13,7 +13,6 @@ 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();
|
||||
@@ -27,16 +26,13 @@ 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 ||
|
||||
!chainId
|
||||
!DocsLinks
|
||||
) {
|
||||
return <AppLoader />;
|
||||
}
|
||||
@@ -76,7 +72,6 @@ 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,
|
||||
state,
|
||||
setState,
|
||||
setTradingViewStudies,
|
||||
} = 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) => {
|
||||
setState(data);
|
||||
onAutoSaveNeeded={(data: { studies: string[] }) => {
|
||||
setTradingViewStudies(data.studies);
|
||||
}}
|
||||
state={state}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,10 +27,10 @@ describe('ChartMenu', () => {
|
||||
|
||||
render(<ChartMenu />);
|
||||
|
||||
await userEvent.click(screen.getByTestId('chartlib-toggle-button'));
|
||||
await userEvent.click(screen.getByRole('button', { name: 'TradingView' }));
|
||||
expect(useChartSettingsStore.getState().chartlib).toEqual('tradingview');
|
||||
|
||||
await userEvent.click(screen.getByTestId('chartlib-toggle-button'));
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Vega chart' }));
|
||||
expect(useChartSettingsStore.getState().chartlib).toEqual('pennant');
|
||||
});
|
||||
|
||||
|
||||
@@ -68,7 +68,6 @@ export const ChartMenu = () => {
|
||||
setChartlib(isPennant ? 'tradingview' : 'pennant');
|
||||
}}
|
||||
size="extra-small"
|
||||
testId="chartlib-toggle-button"
|
||||
>
|
||||
{isPennant ? 'TradingView' : t('Vega chart')}
|
||||
</TradingButton>
|
||||
|
||||
@@ -9,7 +9,6 @@ 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
|
||||
@@ -18,6 +17,7 @@ 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;
|
||||
setState: (state: object) => void;
|
||||
setTradingViewStudies: (studies: string[]) => void;
|
||||
}
|
||||
>()(
|
||||
persist(
|
||||
@@ -95,8 +95,10 @@ export const useChartSettingsStore = create<
|
||||
state.chartlib = lib;
|
||||
});
|
||||
},
|
||||
setState: (state) => {
|
||||
set({ state });
|
||||
setTradingViewStudies: (studies: string[]) => {
|
||||
set((state) => {
|
||||
state.tradingViewStudies = studies;
|
||||
});
|
||||
},
|
||||
})),
|
||||
{
|
||||
@@ -145,13 +147,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,
|
||||
state: settings.state,
|
||||
setState: settings.setState,
|
||||
setTradingViewStudies: settings.setTradingViewStudies,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
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,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -42,9 +42,13 @@ export const LiquidityHeader = () => {
|
||||
const assetDecimalPlaces = asset?.decimals || 0;
|
||||
const symbol = asset?.symbol;
|
||||
|
||||
const triggeringRatio =
|
||||
market?.liquidityMonitoringParameters.triggeringRatio || '1';
|
||||
|
||||
const { percentage, status } = useCheckLiquidityStatus({
|
||||
suppliedStake: suppliedStake || 0,
|
||||
targetStake: targetStake || 0,
|
||||
triggeringRatio,
|
||||
});
|
||||
|
||||
const feesObject = feesPaidRes?.paidLiquidityFees?.edges?.find(
|
||||
|
||||
@@ -47,6 +47,9 @@ export const MarketLiquiditySupplied = ({
|
||||
]);
|
||||
|
||||
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
||||
const triggeringRatio = Number(
|
||||
params.market_liquidity_targetstake_triggering_ratio
|
||||
);
|
||||
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
@@ -91,6 +94,7 @@ export const MarketLiquiditySupplied = ({
|
||||
const { percentage, status } = useCheckLiquidityStatus({
|
||||
suppliedStake: market?.suppliedStake || 0,
|
||||
targetStake: market?.targetStake || 0,
|
||||
triggeringRatio,
|
||||
});
|
||||
|
||||
const showMessage =
|
||||
|
||||
@@ -162,8 +162,6 @@ 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">
|
||||
@@ -222,7 +220,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
|
||||
</NavbarLink>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<NavbarLinkExternal to={GOVERNANCE_LINK}>
|
||||
<NavbarLinkExternal to={useLinks(DApp.Governance)()}>
|
||||
{t('Governance')}
|
||||
</NavbarLinkExternal>
|
||||
</NavbarItem>
|
||||
@@ -230,13 +228,6 @@ 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,7 +98,6 @@ describe('ActiveRewards', () => {
|
||||
transferNode={mockTransferNode}
|
||||
currentEpoch={1}
|
||||
kind={mockRecurringTransfer}
|
||||
allMarkets={{}}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
import { useActiveRewardsQuery } from './__generated__/Rewards';
|
||||
import {
|
||||
useActiveRewardsQuery,
|
||||
useMarketForRewardsQuery,
|
||||
} from './__generated__/Rewards';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
type IconName,
|
||||
type VegaIconSize,
|
||||
Icon,
|
||||
type IconName,
|
||||
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 = {
|
||||
@@ -72,7 +74,7 @@ export const isActiveReward = (node: TransferNode, currentEpoch: number) => {
|
||||
export const applyFilter = (
|
||||
node: TransferNode & {
|
||||
asset?: AssetFieldsFragment | null;
|
||||
markets?: (MarketFieldsFragment | null)[];
|
||||
marketIds?: (MarketFieldsFragment | null)[];
|
||||
},
|
||||
filter: Filter
|
||||
) => {
|
||||
@@ -83,7 +85,6 @@ export const applyFilter = (
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
DispatchMetricLabels[transfer.kind.dispatchStrategy.dispatchMetric]
|
||||
.toLowerCase()
|
||||
@@ -97,7 +98,7 @@ export const applyFilter = (
|
||||
node.asset?.name
|
||||
.toLocaleLowerCase()
|
||||
.includes(filter.searchTerm.toLowerCase()) ||
|
||||
node.markets?.some((m) =>
|
||||
node.marketIds?.some((m) =>
|
||||
m?.tradableInstrument?.instrument?.name
|
||||
.toLocaleLowerCase()
|
||||
.includes(filter.searchTerm.toLowerCase())
|
||||
@@ -123,7 +124,7 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
||||
const { data: assets } = useAssetsMapProvider();
|
||||
const { data: markets } = useMarketsMapProvider();
|
||||
|
||||
const enrichedTransfers = activeRewardsData?.transfersConnection?.edges
|
||||
const transfers = activeRewardsData?.transfersConnection?.edges
|
||||
?.map((e) => e?.node as TransferNode)
|
||||
.filter((node) => isActiveReward(node, currentEpoch))
|
||||
.map((node) => {
|
||||
@@ -137,19 +138,19 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
||||
node.transfer.kind.dispatchStrategy?.dispatchMetricAssetId || ''
|
||||
];
|
||||
|
||||
const marketsInScope =
|
||||
const marketIds =
|
||||
node.transfer.kind.dispatchStrategy?.marketIdsInScope?.map(
|
||||
(id) => markets && markets[id]
|
||||
);
|
||||
|
||||
return { ...node, asset, markets: marketsInScope };
|
||||
return { ...node, asset, marketIds };
|
||||
});
|
||||
|
||||
if (!enrichedTransfers || !enrichedTransfers.length) return null;
|
||||
if (!transfers || !transfers.length) return null;
|
||||
|
||||
return (
|
||||
<Card title={t('Active rewards')} className="lg:col-span-full">
|
||||
{enrichedTransfers.length > 1 && (
|
||||
{transfers.length > 1 && (
|
||||
<TradingInput
|
||||
onChange={(e) =>
|
||||
setFilter((curr) => ({ ...curr, searchTerm: e.target.value }))
|
||||
@@ -165,7 +166,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">
|
||||
{enrichedTransfers
|
||||
{transfers
|
||||
.filter((n) => applyFilter(n, filter))
|
||||
.map((node, i) => {
|
||||
const { transfer } = node;
|
||||
@@ -183,7 +184,6 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
||||
transferNode={node}
|
||||
kind={transfer.kind}
|
||||
currentEpoch={currentEpoch}
|
||||
allMarkets={markets || {}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
@@ -207,8 +207,14 @@ 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 };
|
||||
}
|
||||
@@ -247,97 +253,49 @@ export const ActiveRewardCard = ({
|
||||
transferNode,
|
||||
currentEpoch,
|
||||
kind,
|
||||
allMarkets,
|
||||
}: {
|
||||
transferNode: TransferNode & {
|
||||
asset?: AssetFieldsFragment | null;
|
||||
markets?: (MarketFieldsFragment | null)[];
|
||||
};
|
||||
transferNode: TransferNode;
|
||||
currentEpoch: number;
|
||||
kind: RecurringTransfer;
|
||||
allMarkets?: Record<string, MarketFieldsFragment | null>;
|
||||
}) => {
|
||||
const t = useT();
|
||||
|
||||
const { transfer } = transferNode;
|
||||
const { dispatchStrategy } = kind;
|
||||
const marketIds = dispatchStrategy?.marketIdsInScope;
|
||||
|
||||
const marketIdsInScope = dispatchStrategy?.marketIdsInScope;
|
||||
const firstMarketData = transferNode.markets?.[0];
|
||||
const { data: marketNameData } = useMarketForRewardsQuery({
|
||||
variables: {
|
||||
marketId: marketIds ? marketIds[0] : '',
|
||||
},
|
||||
});
|
||||
|
||||
const specificMarkets = useMemo(() => {
|
||||
if (
|
||||
!firstMarketData ||
|
||||
!marketIdsInScope ||
|
||||
marketIdsInScope.length === 0
|
||||
const marketName = useMemo(() => {
|
||||
if (marketNameData && marketIds && marketIds.length > 1) {
|
||||
return 'Specific markets';
|
||||
} else if (
|
||||
marketNameData &&
|
||||
marketIds &&
|
||||
marketNameData &&
|
||||
marketIds.length === 1
|
||||
) {
|
||||
return null;
|
||||
return marketNameData?.market?.tradableInstrument?.instrument?.name || '';
|
||||
}
|
||||
if (marketIdsInScope.length > 1) {
|
||||
const marketNames =
|
||||
allMarkets &&
|
||||
marketIdsInScope
|
||||
.map((id) => allMarkets[id]?.tradableInstrument?.instrument?.name)
|
||||
.join(', ');
|
||||
return '';
|
||||
}, [marketIds, marketNameData]);
|
||||
|
||||
return (
|
||||
<Tooltip description={marketNames}>
|
||||
<span>Specific markets</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span>{firstMarketData?.tradableInstrument?.instrument?.name || ''}</span>
|
||||
);
|
||||
}, [firstMarketData, marketIdsInScope, allMarkets]);
|
||||
|
||||
const dispatchAsset = transferNode.asset;
|
||||
const { data: dispatchAsset } = useAssetDataProvider(
|
||||
dispatchStrategy?.dispatchMetricAssetId || ''
|
||||
);
|
||||
|
||||
if (!dispatchStrategy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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 { gradientClassName, mainClassName } = getGradientClasses(
|
||||
dispatchStrategy.dispatchMetric
|
||||
);
|
||||
|
||||
if (marketSettled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const assetInActiveMarket =
|
||||
allMarkets &&
|
||||
Object.values(allMarkets).some((m: MarketFieldsFragment | null) => {
|
||||
if (m && getAsset(m).id === dispatchStrategy.dispatchMetricAssetId) {
|
||||
return m?.state && MarketState.STATE_ACTIVE === m.state;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const marketSuspended = transferNode.markets?.some(
|
||||
(m) =>
|
||||
m?.state === MarketState.STATE_SUSPENDED ||
|
||||
m?.state === MarketState.STATE_SUSPENDED_VIA_GOVERNANCE
|
||||
);
|
||||
|
||||
// Gray out the cards that are related to suspended markets
|
||||
// Or settlement assets in markets that are not active and eligible for rewards
|
||||
const { gradientClassName, mainClassName } =
|
||||
marketSuspended || !assetInActiveMarket
|
||||
? {
|
||||
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>
|
||||
@@ -415,20 +373,8 @@ export const ActiveRewardCard = ({
|
||||
|
||||
<span className="border-[0.5px] border-gray-700" />
|
||||
<span>
|
||||
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]} •{' '}
|
||||
<Tooltip
|
||||
underline={marketSuspended}
|
||||
description={
|
||||
(marketSuspended || !assetInActiveMarket) &&
|
||||
(specificMarkets
|
||||
? t('Eligible market(s) currently suspended')
|
||||
: !assetInActiveMarket
|
||||
? t('Currently no markets eligible for reward')
|
||||
: '')
|
||||
}
|
||||
>
|
||||
<span>{specificMarkets || dispatchAsset?.name}</span>
|
||||
</Tooltip>
|
||||
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]}
|
||||
{marketName ? ` • ${marketName}` : ` • ${dispatchAsset?.name}`}
|
||||
</span>
|
||||
|
||||
<div className="flex items-center gap-8 flex-wrap">
|
||||
|
||||
@@ -81,7 +81,6 @@ export const Settings = () => {
|
||||
intent={Intent.Primary}
|
||||
onClick={() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
window.location.reload();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -11,6 +11,7 @@ type TableColumnDefinition = {
|
||||
name: string;
|
||||
tooltip?: string;
|
||||
className?: string;
|
||||
headerClassName?: string;
|
||||
testId?: string;
|
||||
};
|
||||
|
||||
@@ -41,13 +42,14 @@ export const Table = forwardRef<
|
||||
const header = (
|
||||
<thead className={classNames({ 'max-md:hidden': !noCollapse })}>
|
||||
<tr>
|
||||
{columns.map(({ displayName, name, tooltip }) => (
|
||||
{columns.map(({ displayName, name, tooltip, headerClassName }) => (
|
||||
<th
|
||||
key={name}
|
||||
col-id={name}
|
||||
className={classNames(
|
||||
'px-5 py-3 text-xs text-vega-clight-100 dark:text-vega-cdark-100 font-normal',
|
||||
INNER_BORDER_STYLE
|
||||
INNER_BORDER_STYLE,
|
||||
headerClassName
|
||||
)}
|
||||
>
|
||||
<span className="flex flex-row items-center gap-2">
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
||||
VEGA_VERSION=v0.74.0-preview.2
|
||||
LOCAL_SERVER=false
|
||||
VEGA_VERSION=v0.73.10
|
||||
LOCAL_SERVER=true
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
|
||||
VEGA_VERSION=v0.74.0-preview.2
|
||||
LOCAL_SERVER=false
|
||||
VEGA_VERSION=v0.73.10
|
||||
LOCAL_SERVER=false
|
||||
@@ -25,7 +25,7 @@ def setup_simple_market(
|
||||
|
||||
vega.mint(
|
||||
MM_WALLET.name,
|
||||
asset=vega.find_asset_id(symbol="VOTE", enabled=True),
|
||||
asset="VOTE",
|
||||
amount=mint_amount,
|
||||
)
|
||||
|
||||
@@ -207,7 +207,7 @@ def setup_perps_market(
|
||||
|
||||
vega.mint(
|
||||
MM_WALLET.name,
|
||||
asset=vega.find_asset_id(symbol="VOTE", enabled=True),
|
||||
asset="VOTE",
|
||||
amount=mint_amount,
|
||||
)
|
||||
|
||||
|
||||
Generated
+4
-4
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
@@ -1160,8 +1160,8 @@ profile = ["pytest-profiling", "snakeviz"]
|
||||
[package.source]
|
||||
type = "git"
|
||||
url = "https://github.com/vegaprotocol/vega-market-sim.git/"
|
||||
reference = "HEAD"
|
||||
resolved_reference = "2aed8c94b25d8fa2e376d3b63ca1f9193d28cdfd"
|
||||
reference = "fix/genesis_panic"
|
||||
resolved_reference = "de30d2d4c7a1b81a830527ca76473e23ef59de12"
|
||||
|
||||
[[package]]
|
||||
name = "websocket-client"
|
||||
@@ -1342,4 +1342,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<3.11"
|
||||
content-hash = "39ce8400de7bf060857447281ef27bd78c9b1d9639da063b051e3ae6e7887a67"
|
||||
content-hash = "68ed0de55290a3b929d47eb7f7b031fb7e172261c7bbeb4f554b7c27a4462754"
|
||||
|
||||
@@ -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/"}
|
||||
vega-sim = {git = "https://github.com/vegaprotocol/vega-market-sim.git/", branch = "fix/genesis_panic"}
|
||||
pytest-playwright = "^0.4.2"
|
||||
docker = "^6.1.3"
|
||||
pytest-xdist = "^3.3.1"
|
||||
|
||||
+23
-36
@@ -14,18 +14,15 @@ 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}")
|
||||
@@ -45,26 +42,31 @@ 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(5)).to_contain_text("10+10LimitFilled120.00GTT:")
|
||||
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10+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.wait_fn(2)
|
||||
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(6)).to_contain_text("10+10LimitFilled120.00GTC")
|
||||
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10+10LimitFilled120.00GTC"
|
||||
)
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
@@ -82,11 +84,13 @@ 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(7)).to_contain_text("10-10LimitFilled100.00GFN")
|
||||
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10-10LimitFilled100.00GFN"
|
||||
)
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
@@ -103,12 +107,14 @@ 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(8)).to_contain_text("10-10MarketFilled-IOC")
|
||||
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10-10MarketFilled-IOC"
|
||||
)
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_market_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
|
||||
@@ -118,32 +124,13 @@ 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(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")
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr10+10MarketFilled-FOK"
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
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()
|
||||
@@ -27,7 +27,7 @@ submit_stop_order = "place-order"
|
||||
stop_orders_tab = "Stop orders"
|
||||
row_table = "row"
|
||||
cancel = "cancel"
|
||||
market_name_col = '[data-testid="market-code"]'
|
||||
market_name_col = '[col-id="market.tradableInstrument.instrument.code"]'
|
||||
trigger_col = '[col-id="trigger"]'
|
||||
expiresAt_col = '[col-id="expiresAt"]'
|
||||
size_col = '[col-id="submission.size"]'
|
||||
@@ -41,6 +41,7 @@ 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
|
||||
|
||||
@@ -77,6 +78,7 @@ 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()
|
||||
@@ -267,6 +269,82 @@ 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 = '[data-testid="market-code"]'
|
||||
COL_INSTRUMENT_CODE = '[col-id="market.tradableInstrument.instrument.code"]'
|
||||
COL_CODE = '[col-id="code"]'
|
||||
COL_SIZE = '[col-id="size"]'
|
||||
COL_PRICE = '[col-id="price"]'
|
||||
@@ -563,6 +563,7 @@ 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)
|
||||
@@ -604,6 +605,7 @@ 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)
|
||||
@@ -681,4 +683,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=vega.find_asset_id(symbol="VOTE", enabled=True),
|
||||
asset="VOTE",
|
||||
amount=mint_amount,
|
||||
)
|
||||
|
||||
@@ -105,8 +105,6 @@ 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)
|
||||
|
||||
@@ -146,6 +144,29 @@ 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("/")
|
||||
@@ -156,3 +177,11 @@ 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,6 +35,7 @@ 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(
|
||||
@@ -50,6 +51,7 @@ 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()
|
||||
|
||||
@@ -82,6 +84,7 @@ 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,44 +16,34 @@ 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
|
||||
row = page.get_by_test_id(
|
||||
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
|
||||
expect(row).to_contain_text(
|
||||
"Active"
|
||||
)
|
||||
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",
|
||||
@@ -66,50 +56,41 @@ def test_liquidity_provision_amendment(
|
||||
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
|
||||
row = page.get_by_test_id(
|
||||
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
|
||||
expect(row).to_contain_text(
|
||||
"Updating next epoch"
|
||||
)
|
||||
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("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("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.skip("Waiting for the ability to cancel LP")
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_liquidity_provision_cancelled(
|
||||
continuous_market, vega: VegaServiceNull, page: Page
|
||||
):
|
||||
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
|
||||
row = page.get_by_test_id(
|
||||
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
|
||||
expect(row).to_contain_text(
|
||||
"Active"
|
||||
)
|
||||
expect(row).to_contain_text("Active")
|
||||
vega.cancel_liquidity(
|
||||
vega.submit_simple_liquidity(
|
||||
key_name="market_maker",
|
||||
market_id=continuous_market,
|
||||
commitment_amount=0,
|
||||
fee=0,
|
||||
is_amendment=False,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
page.reload()
|
||||
expect(page.get_by_test_id("supplied-stake")).to_have_text(
|
||||
"Supplied stake0.00 tDAI"
|
||||
)
|
||||
expect(page.get_by_test_id("liquidity-supplied")).to_have_text(
|
||||
"Liquidity supplied 0.00%"
|
||||
)
|
||||
expect(page.locator(".ag-overlay-panel")).to_have_text("No data")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
@@ -5,7 +5,6 @@ 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")
|
||||
@@ -22,7 +21,9 @@ def create_settled_market(vega: VegaServiceNull):
|
||||
settlement_price=110,
|
||||
market_id=market_id,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(10)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
|
||||
class TestSettledMarket:
|
||||
@@ -122,7 +123,9 @@ def test_terminated_market_no_settlement_date(page: Page, vega: VegaServiceNull)
|
||||
payload={"trading.terminated": "true"},
|
||||
key_name="FJMKnwfZdd48C8NqvYrG",
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(10)
|
||||
vega.wait_for_total_catchup()
|
||||
page.goto(f"/#/markets/all")
|
||||
page.get_by_test_id("Closed markets").click()
|
||||
row_selector = page.locator(
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
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,3 +25,65 @@ 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"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
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,6 +56,9 @@ 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,14 +4,9 @@ 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, change_keys
|
||||
from actions.utils import wait_for_toast_confirmation
|
||||
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):
|
||||
@@ -73,11 +68,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, 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)
|
||||
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)
|
||||
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
@@ -94,6 +89,7 @@ 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."
|
||||
)
|
||||
@@ -114,8 +110,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(4)).to_contain_text(
|
||||
"0+1LimitActive110.00GTC"
|
||||
expect(page.get_by_role("row").nth(2)).to_contain_text(
|
||||
"BTC:DAI_2023Futr0+1LimitActive110.00GTC"
|
||||
)
|
||||
|
||||
|
||||
@@ -129,6 +125,7 @@ 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."
|
||||
)
|
||||
@@ -138,29 +135,3 @@ 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,6 +73,9 @@ 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")
|
||||
@@ -115,7 +118,8 @@ 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.wait_fn(2)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
# check market state is now active and trading mode is continuous
|
||||
@@ -135,7 +139,9 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
.get_by_test_id(f"update-state-banner-{market_id}")
|
||||
).to_be_visible()
|
||||
|
||||
next_epoch(vega=vega)
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
expect(
|
||||
page.get_by_test_id("market-banner")
|
||||
@@ -149,7 +155,9 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
forward_time_to_enactment = False
|
||||
)
|
||||
|
||||
next_epoch(vega=vega)
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
expect(page.get_by_test_id("market-banner")).not_to_be_visible()
|
||||
|
||||
@@ -162,9 +170,11 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
payload={"trading.terminated": "true"},
|
||||
key_name=GOVERNANCE_WALLET.name,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
# market state should be changed to "No trading" because of the invalid oracle
|
||||
# market state should be changed to "Trading Terminated" because of the invalid oracle
|
||||
expect(trading_mode).to_have_text("No trading")
|
||||
expect(market_state).to_have_text("Trading Terminated")
|
||||
|
||||
@@ -174,7 +184,9 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
settlement_price=100,
|
||||
market_id=market_id,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
# 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
|
||||
|
||||
@@ -65,6 +65,7 @@ def test_limit_order_trade_open_order(
|
||||
expect(orderbook_trade).to_be_visible()
|
||||
|
||||
expected_open_order = [
|
||||
"BTC:DAI_2023",
|
||||
"+1",
|
||||
"Limit",
|
||||
"Active",
|
||||
@@ -86,6 +87,9 @@ 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",
|
||||
@@ -103,6 +107,12 @@ 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(secondary_id)).to_have_text(
|
||||
@@ -144,4 +154,5 @@ 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,63 +257,46 @@ def test_order_sorted(page: Page):
|
||||
|
||||
def test_order_status_active(page: Page):
|
||||
# 7002-SORD-041
|
||||
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"
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-2Futr" + "0" + "-10" + "Limit" + "Active" + "150.00" + "GTC"
|
||||
)
|
||||
|
||||
|
||||
def test_status_expired(page: Page):
|
||||
# 7002-SORD-042
|
||||
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:"
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-3Futr" + "0" + "-10" + "Limit" + "Expired" + "120.00" + "GTT:"
|
||||
)
|
||||
|
||||
|
||||
def test_order_status_Stopped(page: Page):
|
||||
# 7002-SORD-044
|
||||
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"
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-1Futr" + "0" + "-100" + "Limit" + "Stopped" + "130.00" + "IOC"
|
||||
)
|
||||
|
||||
|
||||
def test_order_status_partially_filled(page: Page):
|
||||
# 7002-SORD-045
|
||||
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"
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-2Futr" + "99" + "+100" + "Limit" + "Partially Filled" + "104.00" + "IOC"
|
||||
)
|
||||
|
||||
|
||||
def test_order_status_filled(page: Page):
|
||||
# 7002-SORD-046
|
||||
# 7003-MORD-020
|
||||
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"
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-1Futr" + "100" + "-100" + "Limit" + "Filled" + "88.00" + "GTC"
|
||||
)
|
||||
|
||||
|
||||
def test_order_status_rejected(page: Page):
|
||||
# 7002-SORD-047
|
||||
# 7003-MORD-018
|
||||
expect(page.locator('[row-index="9"]').first).to_contain_text(
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-1Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="9"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "0"
|
||||
+ "-10,000,000,000"
|
||||
+ "Limit"
|
||||
+ "Rejected: Margin check failed"
|
||||
@@ -325,11 +308,9 @@ def test_order_status_rejected(page: Page):
|
||||
def test_order_status_parked(page: Page):
|
||||
# 7002-SORD-048
|
||||
# 7003-MORD-016
|
||||
expect(page.locator('[row-index="3"]').first).to_contain_text(
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-5Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="3"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "0"
|
||||
+ "-60"
|
||||
+ "Ask + 15.00 Peg limit"
|
||||
+ "Parked"
|
||||
@@ -340,11 +321,9 @@ def test_order_status_parked(page: Page):
|
||||
|
||||
def test_order_status_pegged_ask(page: Page):
|
||||
# 7003-MORD-016
|
||||
expect(page.locator('[row-index="4"]').first).to_contain_text(
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-4Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="4"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "0"
|
||||
+ "-60"
|
||||
+ "Ask + 15.00 Peg limit"
|
||||
+ "Active"
|
||||
@@ -355,11 +334,9 @@ def test_order_status_pegged_ask(page: Page):
|
||||
|
||||
def test_order_status_pegged_bid(page: Page):
|
||||
# 7003-MORD-016
|
||||
expect(page.locator('[row-index="5"]').first).to_contain_text(
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-4Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="5"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "0"
|
||||
+ "+40"
|
||||
+ "Bid - 10.00 Peg limit"
|
||||
+ "Active"
|
||||
@@ -370,11 +347,9 @@ def test_order_status_pegged_bid(page: Page):
|
||||
|
||||
def test_order_status_pegged_mid(page: Page):
|
||||
# 7003-MORD-016
|
||||
expect(page.locator('[row-index="6"]').first).to_contain_text(
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-4Futr"
|
||||
)
|
||||
expect(page.locator('[row-index="6"]').nth(1)).to_contain_text(
|
||||
"0"
|
||||
+ "0"
|
||||
+ "+20"
|
||||
+ "Mid - 5.00 Peg limit"
|
||||
+ "Active"
|
||||
@@ -397,11 +372,9 @@ def test_order_amend_order(vega: VegaServiceNull, page: Page):
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
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"
|
||||
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-2Futr" + "0" + "-15" + "Limit" + "Active" + "170.00" + "GTC"
|
||||
)
|
||||
|
||||
|
||||
@@ -416,11 +389,9 @@ def test_order_cancel_single_order(vega: VegaServiceNull, page: Page):
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
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"
|
||||
|
||||
expect(page.get_by_test_id(order_tab)).to_contain_text(
|
||||
"market-3Futr" + "0" + "+10" + "Limit" + "Cancelled" + "60.00" + "GTC"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ def test_orderbook_grid_content(setup_market, page: Page):
|
||||
matching_order[1],
|
||||
)
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
@@ -232,6 +233,7 @@ 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()
|
||||
|
||||
@@ -252,6 +254,7 @@ 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,6 +34,7 @@ class TestPerpetuals:
|
||||
settlement_price=110,
|
||||
market_id=perps_market,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
submit_multiple_orders(
|
||||
@@ -47,7 +48,8 @@ class TestPerpetuals:
|
||||
settlement_price=110,
|
||||
market_id=perps_market,
|
||||
)
|
||||
vega.wait_fn(10)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
return perps_market
|
||||
|
||||
@@ -108,6 +110,7 @@ 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(
|
||||
@@ -132,6 +135,7 @@ 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})
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
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}"
|
||||
@@ -15,13 +15,14 @@ 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('[class="ag-body ag-layout-normal"]')
|
||||
table = tab.locator(".ag-center-cols-container")
|
||||
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()
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
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()
|
||||
@@ -0,0 +1,36 @@
|
||||
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,5 +149,7 @@ 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()
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import pytest
|
||||
from playwright.sync_api import expect, Page
|
||||
from vega_sim.null_service import VegaServiceNull
|
||||
from actions.utils import next_epoch
|
||||
from wallet_config import PARTY_A, PARTY_B, PARTY_C, PARTY_D
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_teams(vega: VegaServiceNull, page: Page):
|
||||
|
||||
# submit_transaction will fail with spam statistics error if block height is 0
|
||||
# so go forward at least one block
|
||||
vega.wait_fn(1)
|
||||
|
||||
for key in [PARTY_A, PARTY_B, PARTY_C, PARTY_D]:
|
||||
vega.wallet.create_key(key.name)
|
||||
|
||||
team_name = create_team(vega)
|
||||
|
||||
next_epoch(vega)
|
||||
|
||||
# would be better to derive the team id from the signature if possible but
|
||||
# vega.submit_transaction does not return anything
|
||||
teams = vega.list_teams()
|
||||
|
||||
# list_teams actually returns a dictionary {"team_id": Team}
|
||||
team_id = list(teams.keys())[0]
|
||||
|
||||
vega.apply_referral_code(PARTY_B.name, team_id)
|
||||
|
||||
# go to next epoch so we can check joinedAt and joinedAtEpoch appropriately
|
||||
next_epoch(vega)
|
||||
|
||||
vega.apply_referral_code(PARTY_C.name, team_id)
|
||||
|
||||
next_epoch(vega)
|
||||
|
||||
vega.apply_referral_code(PARTY_D.name, team_id)
|
||||
|
||||
vega.wait_fn(1)
|
||||
vega.forward("10s")
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
page.goto(f"/#/competitions/team/{team_id}")
|
||||
expect(page.get_by_role('heading', level=1)).to_have_text(team_name)
|
||||
expect(page.get_by_text('Members (3)')).to_be_visible()
|
||||
|
||||
def create_team(vega: VegaServiceNull):
|
||||
team_name = "Foobar"
|
||||
|
||||
vega.create_referral_set(
|
||||
key_name=PARTY_A.name,
|
||||
name=team_name,
|
||||
team_url="https://vega.xyz",
|
||||
avatar_url="http://placekitten.com/200/200",
|
||||
closed=False
|
||||
)
|
||||
|
||||
return team_name
|
||||
@@ -0,0 +1,78 @@
|
||||
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,6 +22,8 @@ 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()
|
||||
|
||||
@@ -32,6 +34,7 @@ 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()
|
||||
|
||||
@@ -42,6 +45,7 @@ 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()
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user