Compare commits
60
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3f5c2fc32 | ||
|
|
a8c2f4e025 | ||
|
|
2cea73c567 | ||
|
|
b0a54be408 | ||
|
|
d1a036e53c | ||
|
|
9ac199f59a | ||
|
|
332cc302c3 | ||
|
|
4da0e9c368 | ||
|
|
13e1f99767 | ||
|
|
5c18c898b0 | ||
|
|
4fe81cc4aa | ||
|
|
0fb8ee3abb | ||
|
|
3422b99491 | ||
|
|
287e294281 | ||
|
|
dc959025c6 | ||
|
|
63bfcc8f65 | ||
|
|
952e906eac | ||
|
|
e765c247ef | ||
|
|
52dea6d0dc | ||
|
|
97f243e5f7 | ||
|
|
9992d9f053 | ||
|
|
3e26431e8f | ||
|
|
6a9f15f59e | ||
|
|
4684745382 | ||
|
|
927e21b045 | ||
|
|
570472b739 | ||
|
|
28f7bd36e7 | ||
|
|
0f3e5595ba | ||
|
|
d3dbdd2bd5 | ||
|
|
0767139712 | ||
|
|
250492a02c | ||
|
|
29f3374c61 | ||
|
|
ba7b574a07 | ||
|
|
afd8650657 | ||
|
|
78414b4429 | ||
|
|
e0a91b3850 | ||
|
|
80f7a08765 | ||
|
|
3e627ff849 | ||
|
|
d2854b6e90 | ||
|
|
6d130c9cfc | ||
|
|
ab4f4e9084 | ||
|
|
fa1825ca64 | ||
|
|
206c6c7207 | ||
|
|
5b0bd69710 | ||
|
|
546b710093 | ||
|
|
afbf62be84 | ||
|
|
ef26d03d36 | ||
|
|
7471116ebf | ||
|
|
7142fc956c | ||
|
|
611f8d7491 | ||
|
|
f3b72b894e | ||
|
|
140f16f637 | ||
|
|
cae1fc67a4 | ||
|
|
35e25d9a8f | ||
|
|
86e122389c | ||
|
|
6616aceebb | ||
|
|
c937a9df0c | ||
|
|
9fcf286e4b | ||
|
|
e3ed71bec4 | ||
|
|
809f149df9 |
@@ -1,6 +1,6 @@
|
||||
# Related issues 🔗
|
||||
|
||||
Closes #[Issue number here]
|
||||
Issue: #[Issue number here]
|
||||
|
||||
# Description ℹ️
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
#----------------------------------------------
|
||||
- name: Run tests
|
||||
working-directory: ./console-test
|
||||
run: poetry run pytest -s --numprocesses auto
|
||||
run: poetry run pytest -v -s --numprocesses auto --dist loadfile --durations=20
|
||||
- name: Check files
|
||||
run: |
|
||||
ls -al .
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
project: ${{ fromJSON(inputs.projects) }}
|
||||
name: ${{ matrix.project }}
|
||||
runs-on: self-hosted-runner
|
||||
timeout-minutes: 100
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
# Checks if skip cache was requested
|
||||
- name: Set skip-nx-cache flag
|
||||
|
||||
@@ -15,6 +15,7 @@ on:
|
||||
- types
|
||||
- utils
|
||||
- i18n
|
||||
- wallet
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"proposalSubmission": {
|
||||
"rationale": {
|
||||
"title": "Test new asset proposal",
|
||||
"description": "E2E test for proposals"
|
||||
},
|
||||
"terms": {
|
||||
"newAsset": {
|
||||
"changes": {
|
||||
"name": "USDT Coin",
|
||||
"symbol": "USDT",
|
||||
"decimals": "18",
|
||||
"quantum": "1",
|
||||
"erc20": {
|
||||
"contractAddress": "0xb404c51bbc10dcbe948077f18a4b8e553d160084",
|
||||
"withdrawThreshold": "10",
|
||||
"lifetimeLimit": "10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"closingTimestamp": 1724339572,
|
||||
"enactmentTimestamp": 1724339572,
|
||||
"validationTimestamp": 1692799617
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import { getNewAssetTxBody } from '../support/governance.functions';
|
||||
|
||||
context('Proposal page', { tags: '@smoke' }, function () {
|
||||
describe('Verify elements on page', function () {
|
||||
const proposalHeading = 'proposals-heading';
|
||||
const dateTimeRegex =
|
||||
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
||||
const proposalTitle = 'Add Lorem Ipsum market';
|
||||
|
||||
before('Create market proposal', function () {
|
||||
cy.visit('/');
|
||||
@@ -11,6 +12,8 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
|
||||
it('Able to view proposal', function () {
|
||||
const proposalTitle = 'Add Lorem Ipsum market';
|
||||
|
||||
cy.navigate_to('governanceProposals');
|
||||
cy.getByTestId(proposalHeading).should('be.visible');
|
||||
cy.contains(proposalTitle)
|
||||
@@ -22,6 +25,9 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
cy.get_element_by_col_id('type').should('have.text', 'NewMarket');
|
||||
cy.get_element_by_col_id('state').should('have.text', 'Enacted');
|
||||
cy.getByTestId('vote-progress').should('be.visible');
|
||||
cy.getByTestId('vote-progress-bar-for')
|
||||
.invoke('attr', 'style')
|
||||
.should('eq', 'width: 100%;');
|
||||
cy.get('[col-id="cDate"]')
|
||||
.invoke('text')
|
||||
.should('match', dateTimeRegex);
|
||||
@@ -35,9 +41,12 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
cy.getByTestId('dialog-title').should('have.text', proposalTitle);
|
||||
cy.get('.language-json').should('exist');
|
||||
cy.getByTestId('icon-cross').click();
|
||||
});
|
||||
|
||||
it.skip('Proposal page displayed on mobile', function () {
|
||||
const proposalTitle = 'Add Lorem Ipsum market';
|
||||
|
||||
cy.common_switch_to_mobile_and_click_toggle();
|
||||
cy.navigate_to('governanceProposals', true);
|
||||
cy.getByTestId(proposalHeading).should('be.visible');
|
||||
@@ -45,5 +54,40 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to view new asset proposal', function () {
|
||||
const proposalTitle = 'Test new asset proposal';
|
||||
const newAssetProposalBody = getNewAssetTxBody();
|
||||
cy.VegaWalletSubmitProposal(newAssetProposalBody);
|
||||
|
||||
cy.visit('/');
|
||||
cy.navigate_to('governanceProposals');
|
||||
cy.contains(proposalTitle)
|
||||
.parent()
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
|
||||
cy.get_element_by_col_id('type').should('have.text', 'NewAsset');
|
||||
cy.get_element_by_col_id('state').should(
|
||||
'have.text',
|
||||
'Waiting for Node Vote'
|
||||
);
|
||||
cy.getByTestId('vote-progress').should('be.visible');
|
||||
cy.getByTestId('vote-progress-bar-against')
|
||||
.invoke('attr', 'style')
|
||||
.should('eq', 'width: 100%;');
|
||||
cy.get('[col-id="cDate"]')
|
||||
.invoke('text')
|
||||
.should('match', dateTimeRegex);
|
||||
cy.get('[col-id="eDate"]')
|
||||
.invoke('text')
|
||||
.should('match', dateTimeRegex);
|
||||
cy.getByTestId('external-link')
|
||||
.should('have.attr', 'href')
|
||||
.and('contains', 'https://governance.fairground.wtf/proposals/');
|
||||
cy.contains('View terms').should('exist').click();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
import { addSeconds, millisecondsToSeconds } from 'date-fns';
|
||||
|
||||
export function createSuccessorMarketProposal(parentMarketId) {
|
||||
cy.VegaWalletSubmitProposal(getSuccessorTxBody(parentMarketId));
|
||||
}
|
||||
|
||||
function getSuccessorTxBody(parentMarketId) {
|
||||
const MIN_CLOSE_SEC = 500;
|
||||
const MIN_ENACT_SEC = 700;
|
||||
|
||||
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
|
||||
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
|
||||
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
|
||||
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
|
||||
|
||||
return {
|
||||
proposalSubmission: {
|
||||
rationale: {
|
||||
@@ -122,8 +132,49 @@ function getSuccessorTxBody(parentMarketId) {
|
||||
},
|
||||
},
|
||||
},
|
||||
closingTimestamp: 1695666618,
|
||||
enactmentTimestamp: 1695666618,
|
||||
closingTimestamp,
|
||||
enactmentTimestamp,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getNewAssetTxBody() {
|
||||
const MIN_CLOSE_SEC = 500;
|
||||
const MIN_ENACT_SEC = 700;
|
||||
const MIN_VALID_SEC = 60;
|
||||
|
||||
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
|
||||
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
|
||||
const validationDate = addSeconds(new Date(), MIN_VALID_SEC);
|
||||
|
||||
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
|
||||
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
|
||||
const validationTimestamp = millisecondsToSeconds(validationDate.getTime());
|
||||
|
||||
return {
|
||||
proposalSubmission: {
|
||||
rationale: {
|
||||
title: 'Test new asset proposal',
|
||||
description: 'E2E test for proposals',
|
||||
},
|
||||
terms: {
|
||||
newAsset: {
|
||||
changes: {
|
||||
name: 'USDT Coin',
|
||||
symbol: 'USDT',
|
||||
decimals: '18',
|
||||
quantum: '1',
|
||||
erc20: {
|
||||
contractAddress: '0xb404c51bbc10dcbe948077f18a4b8e553d160084',
|
||||
withdrawThreshold: '10',
|
||||
lifetimeLimit: '10',
|
||||
},
|
||||
},
|
||||
},
|
||||
closingTimestamp,
|
||||
enactmentTimestamp,
|
||||
validationTimestamp,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
NX_VEGA_ENV=CUSTOM
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
@@ -8,3 +6,6 @@ NX_VEGA_EXPLORER_URL=/
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_TXS_LIST=0
|
||||
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||
NX_VEGA_ENV=DEVNET
|
||||
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
|
||||
@@ -10,3 +8,6 @@ NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
NX_SENTRY_DSN=https://b3a56b03eda842faad731f3ea9dfd1bc@o286262.ingest.sentry.io/6242427
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
@@ -11,3 +9,6 @@ NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
|
||||
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
|
||||
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
|
||||
@@ -10,4 +8,7 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/
|
||||
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
|
||||
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
@@ -11,3 +9,6 @@ NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
@@ -7,9 +7,10 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
|
||||
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/spec-update-v0.72.0-preview.2/specs/v0.72.0-preview.2/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
|
||||
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.72.3/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
fragment ExplorerStopOrderFields on StopOrder {
|
||||
id
|
||||
status
|
||||
createdAt
|
||||
trigger {
|
||||
... on StopOrderPrice {
|
||||
price
|
||||
}
|
||||
... on StopOrderTrailingPercentOffset {
|
||||
trailingPercentOffset
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
ocoLinkId
|
||||
triggerDirection
|
||||
order {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
query ExplorerStopOrder($stopOrderId: ID!) {
|
||||
stopOrder(id: $stopOrderId) {
|
||||
...ExplorerStopOrderFields
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerStopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, status: Types.StopOrderStatus, createdAt: any, ocoLinkId?: string | null, triggerDirection: Types.StopOrderTriggerDirection, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, order?: { __typename?: 'Order', id: string } | null };
|
||||
|
||||
export type ExplorerStopOrderQueryVariables = Types.Exact<{
|
||||
stopOrderId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerStopOrderQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, status: Types.StopOrderStatus, createdAt: any, ocoLinkId?: string | null, triggerDirection: Types.StopOrderTriggerDirection, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, order?: { __typename?: 'Order', id: string } | null } | null };
|
||||
|
||||
export const ExplorerStopOrderFieldsFragmentDoc = gql`
|
||||
fragment ExplorerStopOrderFields on StopOrder {
|
||||
id
|
||||
status
|
||||
createdAt
|
||||
trigger {
|
||||
... on StopOrderPrice {
|
||||
price
|
||||
}
|
||||
... on StopOrderTrailingPercentOffset {
|
||||
trailingPercentOffset
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
ocoLinkId
|
||||
triggerDirection
|
||||
order {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const ExplorerStopOrderDocument = gql`
|
||||
query ExplorerStopOrder($stopOrderId: ID!) {
|
||||
stopOrder(id: $stopOrderId) {
|
||||
...ExplorerStopOrderFields
|
||||
}
|
||||
}
|
||||
${ExplorerStopOrderFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useExplorerStopOrderQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerStopOrderQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerStopOrderQuery` 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 } = useExplorerStopOrderQuery({
|
||||
* variables: {
|
||||
* stopOrderId: // value for 'stopOrderId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerStopOrderQuery(baseOptions: Apollo.QueryHookOptions<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>(ExplorerStopOrderDocument, options);
|
||||
}
|
||||
export function useExplorerStopOrderLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>(ExplorerStopOrderDocument, options);
|
||||
}
|
||||
export type ExplorerStopOrderQueryHookResult = ReturnType<typeof useExplorerStopOrderQuery>;
|
||||
export type ExplorerStopOrderLazyQueryHookResult = ReturnType<typeof useExplorerStopOrderLazyQuery>;
|
||||
export type ExplorerStopOrderQueryResult = Apollo.QueryResult<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>;
|
||||
@@ -33,7 +33,7 @@ describe('Order TX Summary component', () => {
|
||||
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders nothing if the order passed lacks a price', () => {
|
||||
it('Renders "Market Price" if the order passed lacks a price', () => {
|
||||
const o: Order = {
|
||||
marketId: '123',
|
||||
side: 'SIDE_BUY',
|
||||
@@ -41,7 +41,7 @@ describe('Order TX Summary component', () => {
|
||||
size: '10',
|
||||
};
|
||||
const res = renderComponent(o);
|
||||
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
|
||||
expect(res.queryByText('Market Price')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders nothing if the order has an unspecified side', () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { components } from '../../../types/explorer';
|
||||
import PriceInMarket from '../price-in-market/price-in-market';
|
||||
import { sideText } from '../order-details/lib/order-labels';
|
||||
import SizeInMarket from '../size-in-market/size-in-market';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export type OrderSummaryProps = {
|
||||
order: components['schemas']['v1OrderSubmission'];
|
||||
@@ -20,7 +21,6 @@ const OrderTxSummary = ({ order }: OrderSummaryProps) => {
|
||||
if (
|
||||
!order ||
|
||||
!order.marketId ||
|
||||
!order.price ||
|
||||
!order.side ||
|
||||
order.side === 'SIDE_UNSPECIFIED'
|
||||
) {
|
||||
@@ -36,10 +36,14 @@ const OrderTxSummary = ({ order }: OrderSummaryProps) => {
|
||||
'-'
|
||||
)}
|
||||
<i className="text-xs">@</i>
|
||||
<PriceInMarket
|
||||
marketId={order.marketId}
|
||||
price={order.price}
|
||||
></PriceInMarket>
|
||||
{order.price ? (
|
||||
<PriceInMarket
|
||||
marketId={order.marketId}
|
||||
price={order.price}
|
||||
></PriceInMarket>
|
||||
) : (
|
||||
t('Market Price')
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import PriceInMarket from '../../../price-in-market/price-in-market';
|
||||
import StopOrderTriggerSummary from './stop-order-trigger';
|
||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import fromUnixTime from 'date-fns/fromUnixTime';
|
||||
|
||||
const wrapperClasses =
|
||||
'flex-1 max-w-xs items-center border border-vega-light-200 dark:border-vega-dark-150 rounded-md pv-2 ph-5 mb-5';
|
||||
|
||||
export type StopOrderType = 'RisesAbove' | 'FallsBelow' | 'OCO';
|
||||
type V1OrderSetup = components['schemas']['v1StopOrderSetup'];
|
||||
|
||||
interface StopOrderSetupProps extends V1OrderSetup {
|
||||
type: StopOrderType;
|
||||
deterministicId: string;
|
||||
}
|
||||
|
||||
export function getExpiryTypeLabel(
|
||||
expiryStrategy: V1OrderSetup['expiryStrategy']
|
||||
): string {
|
||||
switch (expiryStrategy) {
|
||||
case 'EXPIRY_STRATEGY_CANCELS':
|
||||
return t('Cancels');
|
||||
case 'EXPIRY_STRATEGY_SUBMIT':
|
||||
return t('Submit');
|
||||
}
|
||||
|
||||
return expiryStrategy || t('Unknown');
|
||||
}
|
||||
|
||||
export interface ExpiryTriggerProps {
|
||||
trailingPercentOffset?: string;
|
||||
price?: string;
|
||||
marketId?: string;
|
||||
}
|
||||
|
||||
export function ExpiryTrigger({
|
||||
trailingPercentOffset,
|
||||
price,
|
||||
marketId,
|
||||
}: ExpiryTriggerProps) {
|
||||
if (price && marketId) {
|
||||
return <PriceInMarket price={price} marketId={marketId} />;
|
||||
}
|
||||
if (trailingPercentOffset) {
|
||||
return (
|
||||
<span>
|
||||
{formatNumberPercentage(new BigNumber(trailingPercentOffset))}{' '}
|
||||
(trailing)
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getMovePrefix(
|
||||
type: StopOrderType,
|
||||
trailingPercentOffset?: string
|
||||
): string {
|
||||
if (type === 'RisesAbove') {
|
||||
if (trailingPercentOffset) {
|
||||
return '+';
|
||||
} else {
|
||||
return '>';
|
||||
}
|
||||
} else {
|
||||
if (trailingPercentOffset) {
|
||||
return '-';
|
||||
} else {
|
||||
return '<';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const TypeLabel = {
|
||||
RisesAbove: t('Rises above ↗'),
|
||||
FallsBelow: t('Falls below ↘'),
|
||||
OCO: '',
|
||||
};
|
||||
|
||||
/**
|
||||
*/
|
||||
export const StopOrderSetup = ({
|
||||
type,
|
||||
price,
|
||||
orderSubmission,
|
||||
expiresAt,
|
||||
expiryStrategy,
|
||||
trailingPercentOffset,
|
||||
deterministicId,
|
||||
}: StopOrderSetupProps) => {
|
||||
let d = 'Unknown';
|
||||
try {
|
||||
d = expiresAt
|
||||
? fromUnixTime(parseInt(expiresAt) / 1000000000).toLocaleString()
|
||||
: t('Unknown');
|
||||
} catch (e) {
|
||||
d = t('Unknown');
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<div className="mb-12 lg:mb-0">
|
||||
<div className="bg-slate-100 text-slate-900 px-6 py-2 md:px-6 flex">
|
||||
<div className="flex-1">
|
||||
<strong className="font-bold mb-1">{TypeLabel[type]} </strong>
|
||||
<p className=" font-xs mb-0">
|
||||
{getMovePrefix(type, trailingPercentOffset)}
|
||||
<ExpiryTrigger
|
||||
trailingPercentOffset={trailingPercentOffset}
|
||||
price={price}
|
||||
marketId={orderSubmission?.marketId}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{expiresAt && expiryStrategy ? (
|
||||
<div className="flex-1">
|
||||
<strong className="font-bold mb-1">{t('Expiry Type')}</strong>
|
||||
<p className=" font-xs mb-0">
|
||||
<Tooltip description={<span>{d}</span>}>
|
||||
<span>{getExpiryTypeLabel(expiryStrategy)}</span>
|
||||
</Tooltip>
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<StopOrderTriggerSummary
|
||||
id={deterministicId}
|
||||
orderSubmission={orderSubmission}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
import { StopOrderStatus } from '@vegaprotocol/types';
|
||||
import { useExplorerStopOrderQuery } from '../../../order-details/__generated__/StopOrder';
|
||||
import type { ExplorerStopOrderQuery } from '../../../order-details/__generated__/StopOrder';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import type { IconName } from '@vegaprotocol/ui-toolkit';
|
||||
import OrderTxSummary from '../../../order-summary/order-tx-summary';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import type { ApolloError } from '@apollo/client';
|
||||
|
||||
export const StatusLabel: Record<StopOrderStatus, string> = {
|
||||
[StopOrderStatus.STATUS_CANCELLED]: t('Cancelled'),
|
||||
[StopOrderStatus.STATUS_EXPIRED]: t('Expired'),
|
||||
[StopOrderStatus.STATUS_PENDING]: t('Pending'),
|
||||
[StopOrderStatus.STATUS_REJECTED]: t('Rejected'),
|
||||
[StopOrderStatus.STATUS_STOPPED]: t('Stopped'),
|
||||
[StopOrderStatus.STATUS_TRIGGERED]: t('Triggered'),
|
||||
[StopOrderStatus.STATUS_UNSPECIFIED]: t('Status unknown'),
|
||||
};
|
||||
|
||||
export const StatusIcon: Record<StopOrderStatus, IconName> = {
|
||||
[StopOrderStatus.STATUS_CANCELLED]: 'disable',
|
||||
[StopOrderStatus.STATUS_EXPIRED]: 'outdated',
|
||||
[StopOrderStatus.STATUS_PENDING]: 'circle',
|
||||
[StopOrderStatus.STATUS_REJECTED]: 'cross',
|
||||
[StopOrderStatus.STATUS_STOPPED]: 'stop',
|
||||
[StopOrderStatus.STATUS_TRIGGERED]: 'tick',
|
||||
[StopOrderStatus.STATUS_UNSPECIFIED]: 'help',
|
||||
};
|
||||
|
||||
export const StatusMidColor: Record<StopOrderStatus, string> = {
|
||||
[StopOrderStatus.STATUS_CANCELLED]: 'bg-red-100 text-red-900',
|
||||
[StopOrderStatus.STATUS_EXPIRED]: 'bg-red-100 text-red-900',
|
||||
[StopOrderStatus.STATUS_PENDING]: 'bg-yellow-100 text-yellow-900',
|
||||
[StopOrderStatus.STATUS_REJECTED]: 'bg-red-100 text-red-900',
|
||||
[StopOrderStatus.STATUS_STOPPED]: 'bg-red-100 text-red-900',
|
||||
[StopOrderStatus.STATUS_TRIGGERED]: 'bg-green-100 text-green-900',
|
||||
[StopOrderStatus.STATUS_UNSPECIFIED]: 'bg-yellow-100 text-yellow-900',
|
||||
};
|
||||
|
||||
export const StatusBottomColor: Record<StopOrderStatus, string> = {
|
||||
[StopOrderStatus.STATUS_CANCELLED]: 'bg-red-50 text-red-900 line-through',
|
||||
[StopOrderStatus.STATUS_EXPIRED]: 'bg-red-50 text-red-900 line-through',
|
||||
[StopOrderStatus.STATUS_PENDING]: 'bg-yellow-50 text-yellow-900',
|
||||
[StopOrderStatus.STATUS_REJECTED]: 'bg-red-50 text-red-900 line-through',
|
||||
[StopOrderStatus.STATUS_STOPPED]: 'bg-red-50 text-red-900 line-through',
|
||||
[StopOrderStatus.STATUS_TRIGGERED]: 'bg-green-50 text-green-900',
|
||||
[StopOrderStatus.STATUS_UNSPECIFIED]:
|
||||
'bg-yellow-50 text-yellow-900 line-through',
|
||||
};
|
||||
|
||||
export function getStopOrderTriggerStatus(
|
||||
data?: ExplorerStopOrderQuery,
|
||||
error?: ApolloError
|
||||
) {
|
||||
if (data && data.stopOrder) {
|
||||
return data.stopOrder.status;
|
||||
}
|
||||
|
||||
return StopOrderStatus.STATUS_UNSPECIFIED;
|
||||
}
|
||||
|
||||
export interface StopOrderTriggerSummaryProps {
|
||||
id: string;
|
||||
orderSubmission?: components['schemas']['v1OrderSubmission'];
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
const StopOrderTriggerSummary = ({
|
||||
id,
|
||||
orderSubmission,
|
||||
}: StopOrderTriggerSummaryProps) => {
|
||||
const { data, error } = useExplorerStopOrderQuery({
|
||||
variables: { stopOrderId: id },
|
||||
});
|
||||
|
||||
const status = getStopOrderTriggerStatus(data, error);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`${StatusMidColor[status]} px-3 py-2 md:px-6 flex space-x-4`}
|
||||
>
|
||||
<p className="m-0 p-0 align-top">
|
||||
<Icon
|
||||
size={6}
|
||||
name={StatusIcon[status]}
|
||||
className="inline-block mr-2"
|
||||
/>
|
||||
<span className="align-top">{StatusLabel[status]}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`${StatusBottomColor[status]} px-3 py-2 md:px-6 flex space-x-4`}
|
||||
>
|
||||
{orderSubmission && (
|
||||
<p className="text-vega-grey-400 strike">
|
||||
<OrderTxSummary order={orderSubmission} />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default StopOrderTriggerSummary;
|
||||
@@ -1,10 +1,12 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import SizeInMarket from '../../../size-in-market/size-in-market';
|
||||
|
||||
export interface TxDetailsOrderIcebergDetailsProps {
|
||||
iceberg: components['schemas']['v1IcebergOpts'];
|
||||
size: components['schemas']['v1OrderSubmission']['size'];
|
||||
marketId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,6 +30,7 @@ export interface TxDetailsOrderIcebergDetailsProps {
|
||||
export const TxOrderIcebergDetails = ({
|
||||
iceberg,
|
||||
size,
|
||||
marketId,
|
||||
}: TxDetailsOrderIcebergDetailsProps) => {
|
||||
return (
|
||||
<div
|
||||
@@ -36,15 +39,28 @@ export const TxOrderIcebergDetails = ({
|
||||
>
|
||||
<Tooltip description={t('Iceberg: Minimum visible size')}>
|
||||
<span className="align-bottom text-vega-orange-650">
|
||||
{iceberg.minimumVisibleSize || '-'}
|
||||
{marketId ? (
|
||||
<SizeInMarket
|
||||
size={iceberg.minimumVisibleSize}
|
||||
marketId={marketId}
|
||||
/>
|
||||
) : (
|
||||
iceberg.minimumVisibleSize
|
||||
)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip description={t('Iceberg: Total size')}>
|
||||
<span className="text-sm text-vega-blue-600 mx-3">{size}</span>
|
||||
<span className="text-sm text-vega-blue-600 mx-3">
|
||||
{marketId ? <SizeInMarket size={size} marketId={marketId} /> : size}
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip description={t('Iceberg: Visible peak')}>
|
||||
<span className="align-top text-vega-yellow-600">
|
||||
{iceberg.peakSize || '-'}
|
||||
{marketId ? (
|
||||
<SizeInMarket size={iceberg.peakSize} marketId={marketId} />
|
||||
) : (
|
||||
iceberg.peakSize
|
||||
)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@@ -26,6 +26,11 @@ export const sharedHeaderProps = {
|
||||
className: 'align-top',
|
||||
};
|
||||
|
||||
const Labels: Record<BlockExplorerTransactionResult['type'], string> = {
|
||||
'Stop Orders Submission': 'Stop Order',
|
||||
'Stop Orders Cancellation': 'Cancel Stop Order',
|
||||
};
|
||||
|
||||
/**
|
||||
* These rows are shown for every transaction type, providing a consistent set of rows for the top
|
||||
* of a transaction details row. The order is relatively arbitrary but felt right - it might need to
|
||||
@@ -44,12 +49,14 @@ export const TxDetailsShared = ({
|
||||
const time: string = blockData?.result.block.header.time || '';
|
||||
const height: string = blockData?.result.block.header.height || txData.block;
|
||||
|
||||
const type = Labels[txData.type] || txData.type;
|
||||
|
||||
return (
|
||||
<>
|
||||
{hideTypeRow === false ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
|
||||
<TableCell>{txData.type}</TableCell>
|
||||
<TableCell>{type}</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
<TableRow modifier="bordered">
|
||||
|
||||
@@ -31,6 +31,8 @@ const AccountType: Record<AccountTypes, string> = {
|
||||
ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: 'LP Received Fees',
|
||||
ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: 'Market Proposers',
|
||||
ACCOUNT_TYPE_HOLDING: 'Holding',
|
||||
ACCOUNT_TYPE_LIQUIDITY_FEES_BONUS_DISTRIBUTION: 'Bonus Distribution',
|
||||
ACCOUNT_TYPE_LP_LIQUIDITY_FEES: 'LP Liquidity Fees',
|
||||
};
|
||||
|
||||
interface TransferParticipantsProps {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { TxDetailsBatch } from './tx-batch';
|
||||
import { TxDetailsChainEvent } from './tx-chain-event';
|
||||
import { TxDetailsNodeVote } from './tx-node-vote';
|
||||
import { TxDetailsOrderCancel } from './tx-order-cancel';
|
||||
import { TxDetailsStopOrderCancel } from './tx-stop-order-cancel';
|
||||
import { TxDetailsOrderAmend } from './tx-order-amend';
|
||||
import { TxDetailsWithdrawSubmission } from './tx-withdraw-submission';
|
||||
import { TxDetailsDelegate } from './tx-delegation';
|
||||
@@ -26,6 +27,7 @@ import { TxDetailsNodeAnnounce } from './tx-node-announce';
|
||||
import { TxDetailsStateVariable } from './tx-state-variable-proposal';
|
||||
import { TxProposal } from './tx-proposal';
|
||||
import { TxDetailsTransfer } from './tx-transfer';
|
||||
import { TxDetailsStopOrderSubmission } from './tx-stop-order-submission';
|
||||
|
||||
interface TxDetailsWrapperProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -85,6 +87,8 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
||||
return TxDetailsProtocolUpgrade;
|
||||
case 'Cancel Order':
|
||||
return TxDetailsOrderCancel;
|
||||
case 'Stop Orders Cancellation':
|
||||
return TxDetailsStopOrderCancel;
|
||||
case 'Amend Order':
|
||||
return TxDetailsOrderAmend;
|
||||
case 'Validator Heartbeat':
|
||||
@@ -113,6 +117,8 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
||||
return TxDetailsUndelegate;
|
||||
case 'State Variable Proposal':
|
||||
return TxDetailsStateVariable;
|
||||
case 'Stop Orders Submission':
|
||||
return TxDetailsStopOrderSubmission;
|
||||
case 'Transfer Funds':
|
||||
return TxDetailsTransfer;
|
||||
default:
|
||||
|
||||
@@ -41,7 +41,7 @@ export const TxDetailsIssueSignatures = ({
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const cmd: Command = txData.command;
|
||||
const cmd: Command = txData.command.issueSignatures;
|
||||
const k = cmd.kind ? kind[cmd.kind] : null;
|
||||
|
||||
return (
|
||||
|
||||
@@ -81,7 +81,11 @@ export const TxDetailsOrder = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Iceberg details')}</TableCell>
|
||||
<TableCell>
|
||||
<TxOrderIcebergDetails iceberg={iceberg} size={size} />
|
||||
<TxOrderIcebergDetails
|
||||
iceberg={iceberg}
|
||||
size={size}
|
||||
marketId={marketId}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import { MarketLink } from '../../links/';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { CancelSummary } from '../../order-summary/order-cancellation';
|
||||
import Hash from '../../links/hash';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
|
||||
export type StopOrderCancellationTransaction =
|
||||
components['schemas']['v1StopOrdersCancellation'];
|
||||
|
||||
interface TxDetailsStopOrderCancelProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Someone cancelled a stop order
|
||||
*/
|
||||
export const TxDetailsStopOrderCancel = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsStopOrderCancelProps) => {
|
||||
if (!txData || !txData.command) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const command: StopOrderCancellationTransaction =
|
||||
txData.command.stopOrdersCancellation;
|
||||
|
||||
const { marketId, stopOrderId } = command;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Cancel stop order')}</TableCell>
|
||||
<TableCell>
|
||||
{stopOrderId ? (
|
||||
<Hash text={stopOrderId} />
|
||||
) : (
|
||||
<CancelSummary orderId={stopOrderId} marketId={marketId} />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{marketId ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Market')}</TableCell>
|
||||
<TableCell>
|
||||
<MarketLink id={marketId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import { MarketLink } from '../../links/';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import {
|
||||
getStopOrderIds,
|
||||
stopOrdersSignatureToDeterministicId,
|
||||
} from '../lib/deterministic-ids';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import { StopOrderSetup } from './order/stop-order-setup';
|
||||
|
||||
type StopOrderSetup = components['schemas']['v1StopOrderSetup'];
|
||||
|
||||
interface TxDetailsOrderProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
export function getStopTypeLabel(
|
||||
risesAbove: StopOrderSetup | undefined,
|
||||
fallsBelow: StopOrderSetup | undefined
|
||||
): string {
|
||||
if (risesAbove && fallsBelow) {
|
||||
return t('OCO (One Cancels Other)');
|
||||
} else if (fallsBelow) {
|
||||
return t('Falls Below ↘');
|
||||
} else if (risesAbove) {
|
||||
return t('Rises Above ↗');
|
||||
} else {
|
||||
return t('Stop Order');
|
||||
}
|
||||
}
|
||||
|
||||
export interface StopMarketIdProps {
|
||||
risesAbove: StopOrderSetup | undefined;
|
||||
fallsBelow: StopOrderSetup | undefined;
|
||||
showMarketName?: boolean;
|
||||
}
|
||||
|
||||
export function StopMarketId({
|
||||
risesAbove,
|
||||
fallsBelow,
|
||||
showMarketName = false,
|
||||
}: StopMarketIdProps) {
|
||||
const raMarketId = risesAbove?.orderSubmission?.marketId;
|
||||
const fbMarketId = fallsBelow?.orderSubmission?.marketId;
|
||||
|
||||
if (raMarketId && fbMarketId) {
|
||||
if (raMarketId === fbMarketId) {
|
||||
return <MarketLink id={raMarketId} showMarketName={showMarketName} />;
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<MarketLink id={raMarketId} showMarketName={showMarketName} />,
|
||||
<MarketLink id={fbMarketId} showMarketName={showMarketName} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
} else if (raMarketId) {
|
||||
return <MarketLink id={raMarketId} showMarketName={showMarketName} />;
|
||||
} else if (fbMarketId) {
|
||||
return <MarketLink id={fbMarketId} showMarketName={showMarketName} />;
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
export const TxDetailsStopOrderSubmission = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsOrderProps) => {
|
||||
if (!txData || !txData.command.stopOrdersSubmission) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const tx: components['schemas']['v1StopOrdersSubmission'] =
|
||||
txData.command.stopOrdersSubmission;
|
||||
|
||||
const orderIds = stopOrdersSignatureToDeterministicId(
|
||||
txData?.signature?.value
|
||||
);
|
||||
|
||||
const { risesAboveId, fallsBelowId } = getStopOrderIds(
|
||||
orderIds,
|
||||
tx.risesAbove,
|
||||
tx.fallsBelow
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Market ID')}</TableCell>
|
||||
<TableCell>
|
||||
<StopMarketId
|
||||
risesAbove={tx.risesAbove}
|
||||
fallsBelow={tx.fallsBelow}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Market')}</TableCell>
|
||||
<TableCell>
|
||||
<StopMarketId
|
||||
risesAbove={tx.risesAbove}
|
||||
fallsBelow={tx.fallsBelow}
|
||||
showMarketName={true}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Trigger')}</TableCell>
|
||||
<TableCell>
|
||||
{getStopTypeLabel(tx.risesAbove, tx.fallsBelow)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
<div className="flex gap-2">
|
||||
{tx.fallsBelow && fallsBelowId && (
|
||||
<StopOrderSetup
|
||||
type={'FallsBelow'}
|
||||
{...tx.fallsBelow}
|
||||
deterministicId={fallsBelowId}
|
||||
/>
|
||||
)}
|
||||
|
||||
{tx.risesAbove && risesAboveId && (
|
||||
<StopOrderSetup
|
||||
type={'RisesAbove'}
|
||||
{...tx.risesAbove}
|
||||
deterministicId={risesAboveId}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,8 @@
|
||||
import { hexToString, txSignatureToDeterministicId } from './deterministic-ids';
|
||||
import {
|
||||
hexToString,
|
||||
txSignatureToDeterministicId,
|
||||
stopOrdersSignatureToDeterministicId,
|
||||
} from './deterministic-ids';
|
||||
|
||||
it('txSignatureToDeterministicId Turns a known signature in to a known deterministic ID', () => {
|
||||
const signature =
|
||||
@@ -20,3 +24,24 @@ it('hexToString encodes a known good value as bytes', () => {
|
||||
const res = hexToString(hex);
|
||||
expect(res).toEqual([14, 221]);
|
||||
});
|
||||
|
||||
describe('stopOrdersSignatureToDeterministicId', () => {
|
||||
it('should return empty object if no signature is provided', () => {
|
||||
const result = stopOrdersSignatureToDeterministicId();
|
||||
expect(result.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should return valid deterministic ids if a signature is provided', () => {
|
||||
const signature = 'deadb33f';
|
||||
const result = stopOrdersSignatureToDeterministicId(signature);
|
||||
|
||||
expect(result.length).toEqual(2);
|
||||
|
||||
expect(result[0]).toBe(
|
||||
'4c45b67a8c08cbf1982883a75beaf309bf172461d04bd427623d6cd3d9ab0e91'
|
||||
);
|
||||
expect(result[1]).toBe(
|
||||
'afe7509ff90d8f26339a0ab81e4d3e1fb6c4d44e94419aa5e13ae7659d894da1'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import { sha3_256 } from 'js-sha3';
|
||||
type StopOrderSetup = components['schemas']['v1StopOrderSetup'];
|
||||
|
||||
/**
|
||||
* Encodes a string as bytes
|
||||
@@ -37,3 +39,58 @@ export function txSignatureToDeterministicId(signature: string): string {
|
||||
|
||||
return hash.hex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a stop order signature string, returns the deterministic IDs of both potential
|
||||
* Stop Orders. A stop order is not an order per se, but a trigger for an order. The order
|
||||
* created by the stop order will have another ID based on the market event that hit the
|
||||
* trigger, and as such is not deterministic.
|
||||
*
|
||||
* @param signature
|
||||
* @returns string[]
|
||||
*/
|
||||
export function stopOrdersSignatureToDeterministicId(
|
||||
signature?: string
|
||||
): string[] {
|
||||
if (!signature) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const firstId = txSignatureToDeterministicId(signature);
|
||||
return [firstId, txSignatureToDeterministicId(firstId)];
|
||||
}
|
||||
|
||||
export type stopSignatures = {
|
||||
risesAboveId: string | undefined;
|
||||
fallsBelowId: string | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* In 0.72.10 the way stop order IDs are determined is a little tricky. It will be stabilised
|
||||
* in a future release.
|
||||
* @param deterministicIds Output of stopORdersSignatureToDeterministicId
|
||||
* @param risesAbove Stop order setup
|
||||
* @param fallsBelow Stop order setup
|
||||
* @returns Object containing the deterministic IDs of the stop orders
|
||||
*/
|
||||
export function getStopOrderIds(
|
||||
deterministicIds: string[],
|
||||
risesAbove: StopOrderSetup | undefined,
|
||||
fallsBelow: StopOrderSetup | undefined
|
||||
) {
|
||||
if (risesAbove && fallsBelow) {
|
||||
return {
|
||||
risesAboveId: deterministicIds[0],
|
||||
fallsBelowId: deterministicIds[1],
|
||||
};
|
||||
} else if (!fallsBelow && risesAbove) {
|
||||
return {
|
||||
risesAboveId: deterministicIds[0],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
fallsBelowId: deterministicIds[0] || undefined,
|
||||
risesAboveId: deterministicIds[1] || undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ export type FilterOption =
|
||||
| 'Protocol Upgrade'
|
||||
| 'Register new Node'
|
||||
| 'State Variable Proposal'
|
||||
| 'Stop Orders Submission'
|
||||
| 'Stop Orders Cancellation'
|
||||
| 'Submit Oracle Data'
|
||||
| 'Submit Order'
|
||||
| 'Transfer Funds'
|
||||
@@ -53,6 +55,8 @@ export const PrimaryFilterOptions: FilterOption[] = [
|
||||
'Delegate',
|
||||
'Liquidity Provision Order',
|
||||
'Proposal',
|
||||
'Stop Orders Submission',
|
||||
'Stop Orders Cancellation',
|
||||
'Submit Oracle Data',
|
||||
'Submit Order',
|
||||
'Transfer Funds',
|
||||
|
||||
@@ -50,6 +50,25 @@ const displayString: StringMap = {
|
||||
'Stop Orders Cancellation': 'Cancel stop',
|
||||
};
|
||||
|
||||
export function getLabelForStopOrderType(
|
||||
orderType: string,
|
||||
command: components['schemas']['v1InputData']
|
||||
): string {
|
||||
if (command.stopOrdersSubmission) {
|
||||
if (
|
||||
command.stopOrdersSubmission.risesAbove &&
|
||||
command.stopOrdersSubmission.fallsBelow
|
||||
) {
|
||||
return 'Stop ⇅';
|
||||
} else if (command.stopOrdersSubmission.risesAbove) {
|
||||
return 'Stop ↗';
|
||||
} else if (command.stopOrdersSubmission.fallsBelow) {
|
||||
return 'Stop ↘';
|
||||
}
|
||||
}
|
||||
return 'Stop';
|
||||
}
|
||||
|
||||
export function getLabelForOrderType(
|
||||
orderType: string,
|
||||
command: components['schemas']['v1InputData']
|
||||
@@ -185,6 +204,9 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
||||
} else if (type === 'Order' && command) {
|
||||
type = getLabelForOrderType(orderType, command);
|
||||
colours = 'text-white dark-text-white bg-vega-blue dark:bg-vega-blue';
|
||||
} else if (type === 'Stop' && command) {
|
||||
type = getLabelForStopOrderType(orderType, command);
|
||||
colours = 'text-white dark-text-white bg-vega-blue dark:bg-vega-blue';
|
||||
}
|
||||
|
||||
if (type === 'Vote on Proposal') {
|
||||
|
||||
@@ -55,7 +55,7 @@ export const TxsInfiniteList = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overflow-scroll">
|
||||
<div>
|
||||
<table className={className} data-testid="transactions-list">
|
||||
<thead>
|
||||
<tr className="w-full mb-3 text-vega-dark-300 uppercase text-left">
|
||||
|
||||
@@ -22,6 +22,11 @@ import { Footer } from '../components/footer/footer';
|
||||
import { Header } from '../components/header';
|
||||
import { Routes } from './route-names';
|
||||
import { useExplorerNodeNamesLazyQuery } from './validators/__generated__/NodeNames';
|
||||
import {
|
||||
ProtocolUpgradeCountdownMode,
|
||||
ProtocolUpgradeInProgressNotification,
|
||||
ProtocolUpgradeProposalNotification,
|
||||
} from '@vegaprotocol/proposals';
|
||||
|
||||
const DialogsContainer = () => {
|
||||
const { isOpen, id, trigger, asJson, setOpen } = useAssetDetailsDialogStore();
|
||||
@@ -62,6 +67,10 @@ export const Layout = () => {
|
||||
/>
|
||||
)}
|
||||
<Header />
|
||||
<ProtocolUpgradeProposalNotification
|
||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||
/>
|
||||
<ProtocolUpgradeInProgressNotification />
|
||||
</div>
|
||||
<div className={fixedWidthClasses}>
|
||||
<main className="p-4">
|
||||
|
||||
@@ -1,4 +1,34 @@
|
||||
fragment ExplorerOracleDataConnection on OracleSpec {
|
||||
dataConnection {
|
||||
edges {
|
||||
node {
|
||||
externalData {
|
||||
data {
|
||||
signers {
|
||||
signer {
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
data {
|
||||
name
|
||||
value
|
||||
}
|
||||
matchedSpecIds
|
||||
broadcastAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment ExplorerOracleDataSource on OracleSpec {
|
||||
...ExplorerOracleDataConnection
|
||||
dataSourceSpec {
|
||||
spec {
|
||||
id
|
||||
@@ -34,6 +64,7 @@ fragment ExplorerOracleDataSource on OracleSpec {
|
||||
key {
|
||||
name
|
||||
type
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
@@ -49,38 +80,6 @@ fragment ExplorerOracleDataSource on OracleSpec {
|
||||
}
|
||||
}
|
||||
|
||||
fragment ExplorerOracleDataConnection on OracleSpec {
|
||||
dataConnection {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
externalData {
|
||||
data {
|
||||
signers {
|
||||
signer {
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
data {
|
||||
name
|
||||
value
|
||||
}
|
||||
matchedSpecIds
|
||||
broadcastAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ExplorerOracleSpecs {
|
||||
oracleSpecsConnection(pagination: { first: 50 }) {
|
||||
pageInfo {
|
||||
@@ -97,6 +96,5 @@ query ExplorerOracleSpecs {
|
||||
query ExplorerOracleSpecById($id: ID!) {
|
||||
oracleSpec(oracleSpecId: $id) {
|
||||
...ExplorerOracleDataSource
|
||||
...ExplorerOracleDataConnection
|
||||
}
|
||||
}
|
||||
|
||||
+38
-41
@@ -3,24 +3,55 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } } };
|
||||
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
||||
|
||||
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
||||
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
||||
|
||||
export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } } } } | null> | null } | null };
|
||||
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
|
||||
|
||||
export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{
|
||||
id: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
|
||||
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
|
||||
|
||||
export const ExplorerOracleDataConnectionFragmentDoc = gql`
|
||||
fragment ExplorerOracleDataConnection on OracleSpec {
|
||||
dataConnection {
|
||||
edges {
|
||||
node {
|
||||
externalData {
|
||||
data {
|
||||
signers {
|
||||
signer {
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
data {
|
||||
name
|
||||
value
|
||||
}
|
||||
matchedSpecIds
|
||||
broadcastAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const ExplorerOracleDataSourceFragmentDoc = gql`
|
||||
fragment ExplorerOracleDataSource on OracleSpec {
|
||||
...ExplorerOracleDataConnection
|
||||
dataSourceSpec {
|
||||
spec {
|
||||
id
|
||||
@@ -56,6 +87,7 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
||||
key {
|
||||
name
|
||||
type
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
@@ -70,40 +102,7 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const ExplorerOracleDataConnectionFragmentDoc = gql`
|
||||
fragment ExplorerOracleDataConnection on OracleSpec {
|
||||
dataConnection {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
externalData {
|
||||
data {
|
||||
signers {
|
||||
signer {
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
data {
|
||||
name
|
||||
value
|
||||
}
|
||||
matchedSpecIds
|
||||
broadcastAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
${ExplorerOracleDataConnectionFragmentDoc}`;
|
||||
export const ExplorerOracleSpecsDocument = gql`
|
||||
query ExplorerOracleSpecs {
|
||||
oracleSpecsConnection(pagination: {first: 50}) {
|
||||
@@ -149,11 +148,9 @@ export const ExplorerOracleSpecByIdDocument = gql`
|
||||
query ExplorerOracleSpecById($id: ID!) {
|
||||
oracleSpec(oracleSpecId: $id) {
|
||||
...ExplorerOracleDataSource
|
||||
...ExplorerOracleDataConnection
|
||||
}
|
||||
}
|
||||
${ExplorerOracleDataSourceFragmentDoc}
|
||||
${ExplorerOracleDataConnectionFragmentDoc}`;
|
||||
${ExplorerOracleDataSourceFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useExplorerOracleSpecByIdQuery__
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { OracleData } from './oracle-data';
|
||||
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
|
||||
import { type ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
|
||||
|
||||
function renderComponent(data: ExplorerOracleDataConnectionFragment) {
|
||||
type DataConnection = ExplorerOracleDataConnectionFragment['dataConnection'];
|
||||
|
||||
function renderComponent(
|
||||
data: ExplorerOracleDataConnectionFragment['dataConnection']
|
||||
) {
|
||||
return <OracleData data={data} />;
|
||||
}
|
||||
|
||||
describe('Oracle Data view', () => {
|
||||
it('Renders nothing when data is null', () => {
|
||||
const res = render(
|
||||
renderComponent(null as unknown as ExplorerOracleDataConnectionFragment)
|
||||
);
|
||||
const res = render(renderComponent(null as unknown as DataConnection));
|
||||
expect(res.container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('Renders nothing when dataConnection is empty', () => {
|
||||
const res = render(
|
||||
renderComponent({} as ExplorerOracleDataConnectionFragment)
|
||||
);
|
||||
const res = render(renderComponent({} as DataConnection));
|
||||
expect(res.container).toBeEmptyDOMElement();
|
||||
});
|
||||
it('Renders nothing when dataConnection has no edges', () => {
|
||||
@@ -26,7 +26,7 @@ describe('Oracle Data view', () => {
|
||||
dataConnection: {
|
||||
edges: null,
|
||||
},
|
||||
} as ExplorerOracleDataConnectionFragment)
|
||||
} as DataConnection)
|
||||
);
|
||||
expect(res.container).toBeEmptyDOMElement();
|
||||
});
|
||||
@@ -37,7 +37,7 @@ describe('Oracle Data view', () => {
|
||||
dataConnection: {
|
||||
edges: [],
|
||||
},
|
||||
} as unknown as ExplorerOracleDataConnectionFragment)
|
||||
} as unknown as DataConnection)
|
||||
);
|
||||
expect(res.container).toBeEmptyDOMElement();
|
||||
});
|
||||
@@ -47,20 +47,18 @@ describe('Oracle Data view', () => {
|
||||
it('Renders details component when there is data', () => {
|
||||
const res = render(
|
||||
renderComponent({
|
||||
dataConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
externalData: {
|
||||
data: {
|
||||
broadcastAt: '2022-01-01',
|
||||
},
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
externalData: {
|
||||
data: {
|
||||
broadcastAt: '2022-01-01',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as ExplorerOracleDataConnectionFragment)
|
||||
},
|
||||
],
|
||||
} as DataConnection)
|
||||
);
|
||||
expect(res.getByText('Broadcast data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import filter from 'recursive-key-filter';
|
||||
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
|
||||
|
||||
interface OracleDataTypeProps {
|
||||
data: ExplorerOracleDataConnectionFragment;
|
||||
data: ExplorerOracleDataConnectionFragment['dataConnection'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -14,12 +14,7 @@ interface OracleDataTypeProps {
|
||||
* that Does The Job, rather than because it's good.
|
||||
*/
|
||||
export function OracleData({ data }: OracleDataTypeProps) {
|
||||
if (
|
||||
!data ||
|
||||
!data.dataConnection ||
|
||||
!data.dataConnection.edges?.length ||
|
||||
data.dataConnection.edges.length > 1
|
||||
) {
|
||||
if (!data || !data.edges?.length || data.edges.length > 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -27,7 +22,7 @@ export function OracleData({ data }: OracleDataTypeProps) {
|
||||
<details data-testid="oracle-data">
|
||||
<summary>{t('Broadcast data')}</summary>
|
||||
<ul>
|
||||
{data.dataConnection.edges.map((d) => {
|
||||
{data.edges.map((d) => {
|
||||
if (!d) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ export function OracleMarkets({ id }: OracleMarketsProps) {
|
||||
const m = markets.find((m) => {
|
||||
const p = m.tradableInstrument.instrument.product;
|
||||
if (
|
||||
p.dataSourceSpecForSettlementData.id === id ||
|
||||
p.dataSourceSpecForTradingTermination.id === id
|
||||
p?.dataSourceSpecForSettlementData?.id === id ||
|
||||
p?.dataSourceSpecForTradingTermination?.id === id
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -36,15 +36,9 @@ export function OracleMarkets({ id }: OracleMarketsProps) {
|
||||
});
|
||||
|
||||
if (m && m.id) {
|
||||
const type =
|
||||
id ===
|
||||
m.tradableInstrument.instrument.product.dataSourceSpecForSettlementData
|
||||
.id
|
||||
? 'Settlement for'
|
||||
: 'Termination for';
|
||||
return (
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">{type}</TableHeader>
|
||||
<TableHeader scope="row">{getLabel(id, m)}</TableHeader>
|
||||
<TableCell modifier="bordered" data-testid={`m-${m.id}`}>
|
||||
<MarketLink id={m.id} />
|
||||
</TableCell>
|
||||
@@ -61,3 +55,14 @@ export function OracleMarkets({ id }: OracleMarketsProps) {
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
export function getLabel(
|
||||
id: string,
|
||||
m: ExplorerOracleForMarketsMarketFragment | null
|
||||
): string {
|
||||
const settlementId =
|
||||
m?.tradableInstrument?.instrument?.product?.dataSourceSpecForSettlementData
|
||||
?.id || null;
|
||||
|
||||
return id === settlementId ? 'Settlement for' : 'Termination for';
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export type SourceType =
|
||||
interface OracleDetailsProps {
|
||||
id: string;
|
||||
dataSource: ExplorerOracleDataSourceFragment;
|
||||
dataConnection?: ExplorerOracleDataConnectionFragment;
|
||||
dataConnection: ExplorerOracleDataConnectionFragment['dataConnection'];
|
||||
// Defaults to false. Hides the count of 'broadcasts' this oracle has seen
|
||||
showBroadcasts?: boolean;
|
||||
}
|
||||
@@ -41,8 +41,7 @@ export const OracleDetails = ({
|
||||
showBroadcasts = false,
|
||||
}: OracleDetailsProps) => {
|
||||
const sourceType = dataSource.dataSourceSpec.spec.data.sourceType;
|
||||
const reportsCount: number =
|
||||
dataConnection?.dataConnection.edges?.length || 0;
|
||||
const reportsCount: number = dataConnection.edges?.length || 0;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -34,11 +34,15 @@ const Oracles = () => {
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dataConnection = o?.node.dataConnection;
|
||||
|
||||
return (
|
||||
<div id={id} key={id} className="mb-10">
|
||||
<OracleDetails
|
||||
id={id}
|
||||
dataSource={o?.node}
|
||||
dataConnection={dataConnection}
|
||||
showBroadcasts={false}
|
||||
/>
|
||||
<details>
|
||||
|
||||
@@ -39,7 +39,7 @@ export const Oracle = () => {
|
||||
<OracleDetails
|
||||
id={id || ''}
|
||||
dataSource={data?.oracleSpec}
|
||||
dataConnection={data?.oracleSpec}
|
||||
dataConnection={data?.oracleSpec.dataConnection}
|
||||
showBroadcasts={true}
|
||||
/>
|
||||
<details>
|
||||
|
||||
@@ -48,9 +48,11 @@ query ExplorerPartyAssets($partyId: ID!) {
|
||||
}
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
linkings(pagination: { first: 100 }) {
|
||||
linkings(pagination: { last: 100 }) {
|
||||
edges {
|
||||
node {
|
||||
type
|
||||
status
|
||||
amount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export type ExplorerPartyAssetsQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string, linkings: { __typename?: 'StakesConnection', edges?: Array<{ __typename?: 'StakeLinkingEdge', node: { __typename?: 'StakeLinking', amount: string } } | null> | null } }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null } } | null> | null } | null } }> } | null };
|
||||
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string, linkings: { __typename?: 'StakesConnection', edges?: Array<{ __typename?: 'StakeLinkingEdge', node: { __typename?: 'StakeLinking', type: Types.StakeLinkingType, status: Types.StakeLinkingStatus, amount: string } } | null> | null } }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null } } | null> | null } | null } }> } | null };
|
||||
|
||||
export const ExplorerPartyAssetsAccountsFragmentDoc = gql`
|
||||
fragment ExplorerPartyAssetsAccounts on AccountBalance {
|
||||
@@ -64,9 +64,11 @@ export const ExplorerPartyAssetsDocument = gql`
|
||||
}
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
linkings(pagination: {first: 100}) {
|
||||
linkings(pagination: {last: 100}) {
|
||||
edges {
|
||||
node {
|
||||
type
|
||||
status
|
||||
amount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,15 @@ export const PartyBlockStake = ({
|
||||
linkedLength && linkedLength > 0
|
||||
? p?.stakingSummary?.linkings?.edges
|
||||
?.reduce((total, e) => {
|
||||
return new BigNumber(total).plus(
|
||||
new BigNumber(e?.node.amount || 0)
|
||||
);
|
||||
const accumulator = new BigNumber(total);
|
||||
const diff = new BigNumber(e?.node.amount || 0);
|
||||
if (e?.node.type === 'TYPE_LINK') {
|
||||
return accumulator.plus(diff);
|
||||
} else if (e?.node.type === 'TYPE_UNLINK') {
|
||||
return accumulator.minus(diff);
|
||||
} else {
|
||||
return accumulator;
|
||||
}
|
||||
}, new BigNumber(0))
|
||||
.toString()
|
||||
: '0';
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<meta name="og:site_name" content="Vega Protocol - Explorer" />
|
||||
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:card" content="https://static.vega.xyz/favicon.ico" />
|
||||
<meta name="twitter:title" content="Vega Protocol - Explorer" />
|
||||
<meta name="twitter:description" content="Vega Protocol - Explorer" />
|
||||
<meta name="twitter:image" content="https://static.vega.xyz/favicon.ico" />
|
||||
|
||||
+5
-1
@@ -920,6 +920,8 @@ export interface components {
|
||||
* - ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: Per asset reward account for fees received by liquidity providers
|
||||
* - ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: Per asset reward account for market proposers when the market goes above some trading threshold
|
||||
* - ACCOUNT_TYPE_HOLDING: Per asset account for holding in-flight unfilled orders' funds
|
||||
* - ACCOUNT_TYPE_LP_LIQUIDITY_FEES: Network controlled liquidity provider's account, per market, to hold accrued liquidity fees.
|
||||
* - ACCOUNT_TYPE_LIQUIDITY_FEES_BONUS_DISTRIBUTION: Network controlled liquidity fees bonus distribution account, per market.
|
||||
* @default ACCOUNT_TYPE_UNSPECIFIED
|
||||
* @enum {string}
|
||||
*/
|
||||
@@ -941,7 +943,9 @@ export interface components {
|
||||
| 'ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES'
|
||||
| 'ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES'
|
||||
| 'ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS'
|
||||
| 'ACCOUNT_TYPE_HOLDING';
|
||||
| 'ACCOUNT_TYPE_HOLDING'
|
||||
| 'ACCOUNT_TYPE_LP_LIQUIDITY_FEES'
|
||||
| 'ACCOUNT_TYPE_LIQUIDITY_FEES_BONUS_DISTRIBUTION';
|
||||
/** Vega representation of an external asset */
|
||||
readonly vegaAssetDetails: {
|
||||
/** @description Vega built-in asset. */
|
||||
|
||||
@@ -20,6 +20,10 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||
|
||||
@@ -11,6 +11,9 @@ import {
|
||||
getDateFormatForSpecifiedDays,
|
||||
getProposalFromTitle,
|
||||
getProposalInformationFromTable,
|
||||
goToMakeNewProposal,
|
||||
governanceProposalType,
|
||||
longProposalDescription,
|
||||
proposalChangeType,
|
||||
submitUniqueRawProposal,
|
||||
validateProposalDetailsDiff,
|
||||
@@ -43,7 +46,6 @@ const proposalDetailsTitle = 'proposal-title';
|
||||
const proposalDetailsDescription = 'proposal-description';
|
||||
const openProposals = 'open-proposals';
|
||||
const viewProposalButton = 'view-proposal-btn';
|
||||
const proposalDescriptionToggle = 'proposal-description-toggle';
|
||||
const voteBreakdownToggle = 'vote-breakdown-toggle';
|
||||
const proposalTermsToggle = 'proposal-json-toggle';
|
||||
const marketDataToggle = 'proposal-market-data-toggle';
|
||||
@@ -71,10 +73,13 @@ describe(
|
||||
|
||||
// 3001-VOTE-050 3001-VOTE-054 3001-VOTE-055 3002-PROP-019
|
||||
it('Newly created raw proposal details - shows proposal title and full description', function () {
|
||||
const proposalDescription =
|
||||
'I propose that everyone evaluate the following IPFS document and vote Yes if they agree. bafybeigwwctpv37xdcwacqxvekr6e4kaemqsrv34em6glkbiceo3fcy4si';
|
||||
const proposalDetails = longProposalDescription;
|
||||
|
||||
createRawProposal();
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({
|
||||
proposalTitle: 'raw proposal with long description',
|
||||
proposalDescription: proposalDetails,
|
||||
});
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
cy.getByTestId(openProposals).within(() => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() => {
|
||||
@@ -85,12 +90,17 @@ describe(
|
||||
'contain.text',
|
||||
rawProposal.rationale.title
|
||||
);
|
||||
cy.getByTestId(proposalDescriptionToggle).click();
|
||||
cy.getByTestId('proposal-description-toggle');
|
||||
cy.getByTestId(proposalDetailsDescription)
|
||||
.find('p')
|
||||
.should('have.text', proposalDescription);
|
||||
});
|
||||
cy.getByTestId(proposalDetailsDescription).within(() => {
|
||||
cy.get('p').should('not.have.text', 'Hyperlink text');
|
||||
cy.getByTestId('show-more-btn').click();
|
||||
cy.get('p')
|
||||
.invoke('text')
|
||||
.should('have.have.length', 2194) // Full description is displayed
|
||||
.and('contain', 'Hyperlink text');
|
||||
cy.get('a').should('have.attr', 'href');
|
||||
});
|
||||
|
||||
// 3001-VOTE-008
|
||||
getProposalInformationFromTable('ID')
|
||||
.invoke('text')
|
||||
@@ -220,6 +230,7 @@ describe(
|
||||
cy.getByTestId(changeVoteButton).should('be.visible').click();
|
||||
voteForProposal('for');
|
||||
// 3001-VOTE-064
|
||||
cy.getByTestId('user-voted-yes').should('exist');
|
||||
getProposalInformationFromTable('Tokens for proposal')
|
||||
.should('have.text', (1).toFixed(2))
|
||||
.and('be.visible');
|
||||
@@ -396,7 +407,8 @@ describe(
|
||||
// 3003-PMAN-011 3003-PMAN-012
|
||||
cy.getByTestId(marketDataToggle).click();
|
||||
cy.getByTestId('proposal-market-data').within(() => {
|
||||
cy.contains('Key details').click();
|
||||
// Assert that all toggles are removed
|
||||
cy.getByTestId('accordion-toggle').should('not.exist');
|
||||
validateProposalDetailsDiff(
|
||||
'Name',
|
||||
proposalChangeType.UPDATED,
|
||||
@@ -420,7 +432,6 @@ describe(
|
||||
'Opening auction'
|
||||
);
|
||||
|
||||
cy.contains('Instrument').click();
|
||||
validateProposalDetailsDiff(
|
||||
'Market Name',
|
||||
proposalChangeType.UPDATED,
|
||||
@@ -428,7 +439,6 @@ describe(
|
||||
'Test market 1'
|
||||
);
|
||||
|
||||
cy.contains('Metadata').click();
|
||||
validateProposalDetailsDiff(
|
||||
'Sector',
|
||||
proposalChangeType.UPDATED,
|
||||
|
||||
@@ -29,6 +29,7 @@ const toastPanel = 'toast-panel';
|
||||
const toastClose = 'toast-close';
|
||||
const withdrawalDialogContent = 'dialog-content';
|
||||
const toastCompleteWithdrawal = 'toast-complete-withdrawal';
|
||||
const scrollBar = '.ag-body-horizontal-scroll-viewport';
|
||||
const usdtName = 'USDC (local)';
|
||||
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
|
||||
const usdcSymbol = 'tUSDC';
|
||||
@@ -199,20 +200,17 @@ context(
|
||||
);
|
||||
cy.getByTestId(toastClose).click();
|
||||
});
|
||||
cy.get(tableTxHash)
|
||||
.eq(1)
|
||||
.should('have.text', 'Complete withdrawal')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.get(tableAmount).should('have.text', '110.00');
|
||||
cy.get(tableReceiverAddress)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/address/');
|
||||
cy.get(tableCreatedTimeStamp).should('not.be.empty');
|
||||
});
|
||||
cy.get("[row-id='0']").within(() => {
|
||||
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.get(tableAmount).should('have.text', '110.00');
|
||||
cy.get(tableReceiverAddress)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/address/');
|
||||
cy.get(tableCreatedTimeStamp).should('not.be.empty');
|
||||
});
|
||||
ethereumWalletConnect();
|
||||
cy.get(scrollBar).scrollTo('right');
|
||||
cy.getByTestId(completeWithdrawalButton).first().click();
|
||||
cy.getByTestId(toast)
|
||||
.last(txTimeout)
|
||||
|
||||
@@ -119,6 +119,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 0006-NETW-001 0006-NETW-002
|
||||
it('should display network data', function () {
|
||||
cy.getByTestId('git-network-data')
|
||||
.should('contain.text', 'Reading network data from')
|
||||
@@ -130,6 +131,37 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020
|
||||
it('should have option to switch to different network node', function () {
|
||||
cy.getByTestId('git-network-data').within(() => {
|
||||
cy.getByTestId('link').click();
|
||||
});
|
||||
cy.getByTestId('node-row').within(() => {
|
||||
cy.getByTestId('node-url-0')
|
||||
.parent()
|
||||
.should('have.text', 'http://localhost:3008/graphql');
|
||||
cy.getByTestId('response-time-cell')
|
||||
.invoke('text')
|
||||
.should('not.be.empty')
|
||||
.and('not.eq', 'Checking');
|
||||
cy.getByTestId('block-height-cell')
|
||||
.invoke('text')
|
||||
.should('not.be.empty')
|
||||
.then((currentBlockHeight) => {
|
||||
// Check that block height updates automatically
|
||||
cy.getByTestId('block-height-cell')
|
||||
.invoke('text')
|
||||
.should('not.eq', currentBlockHeight);
|
||||
});
|
||||
cy.getByTestId('subscription-cell').should('have.text', 'Yes');
|
||||
});
|
||||
cy.getByTestId('connect').should('be.disabled');
|
||||
cy.getByTestId('node-url-custom').click();
|
||||
cy.get('input').should('exist');
|
||||
cy.getByTestId('connect').should('be.disabled');
|
||||
cy.getByTestId('icon-cross').click();
|
||||
});
|
||||
|
||||
it('should display eth data', function () {
|
||||
cy.getByTestId('git-eth-data')
|
||||
.should('contain.text', 'Reading Ethereum data from')
|
||||
@@ -138,6 +170,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 0006-NETW-011
|
||||
it('should contain link for known issues on Github', function () {
|
||||
cy.getByTestId('git-info').within(() => {
|
||||
cy.contains('Known issues and feedback on')
|
||||
|
||||
@@ -274,3 +274,6 @@ export enum proposalChangeType {
|
||||
UPDATED = 'Updated',
|
||||
ADDED = 'Added',
|
||||
}
|
||||
|
||||
export const longProposalDescription =
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam bibendum orci augue, vel imperdiet augue ultrices sed. In hac habitasse platea dictumst. Sed eget elit vitae nisl tincidunt faucibus. Donec pellentesque mauris nec viverra blandit. Aenean eros diam, tempor eu luctus nec, rhoncus non massa. Mauris libero diam, mattis et enim ut, lobortis pharetra elit. Phasellus vel metus accumsan, rhoncus tellus finibus, blandit mi. In sollicitudin ex ac tortor ornare interdum. Sed est ipsum, vestibulum eget dolor vel, porta luctus elit. Fusce justo nibh, placerat eget sollicitudin eleifend, rhoncus id lorem. Fusce vitae magna vel urna faucibus accumsan quis id purus.\nPraesent convallis dolor sed ante ultricies tempor. Proin sed risus ut libero euismod semper. Duis quis quam sed lacus viverra blandit vel scelerisque diam. Donec interdum, ipsum eget imperdiet ornare, risus augue faucibus lectus, ullamcorper scelerisque erat sapien in purus. Nunc molestie tincidunt felis dignissim vestibulum. Quisque quis ornare enim, non dignissim lectus. Mauris mollis, massa ut maximus consectetur, sem mi lobortis quam, vel malesuada eros tortor nec ex. Cras ac nunc sed erat malesuada varius a quis nulla. Curabitur cursus nec sem sit amet aliquet. Ut tristique tortor neque, a dignissim lectus dictum vel. Praesent sollicitudin bibendum vulputate.\nAenean bibendum tristique diam laoreet posuere. Curabitur ornare lectus ut diam ultricies, ut sodales eros lacinia. Maecenas mauris turpis, gravida non arcu ac, interdum auctor sapien. Vestibulum sed tortor quam. Interdum et malesuada fames ac ante ipsum primis in faucibus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec finibus pulvinar magna, non laoreet lectus molestie nec. Vestibulum tempus mattis vehicula. Praesent in orci lectus. In commodo sollicitudin lacus, et lobortis eros placerat vitae. Proin mi libero, feugiat id pretium posuere, rhoncus ut augue. Cras massa tortor, rutrum sed ex vitae, posuere pretium augue. Donec pellentesque suscipit dignissim. Vivamus convallis a odio vitae sodales. Nullam non eleifend mauris, sed iaculis lectus. Cras facilisis justo at ante.\n[Hyperlink text](https://dweb.link/ipfs/bafybeigwwctpv37xdcwacqxvekr6e4kaemqsrv34em6glkbiceo3fcy4si)';
|
||||
|
||||
@@ -84,9 +84,10 @@ export function stakingPageAssociateTokens(
|
||||
.length
|
||||
) {
|
||||
cy.get(tokenInputApprove, txTimeout).should('be.enabled').click();
|
||||
cy.contains('Approve $VEGA Tokens for staking on Vega').should(
|
||||
'be.visible'
|
||||
);
|
||||
cy.contains(
|
||||
'Approve $VEGA Tokens for staking on Vega',
|
||||
txTimeout
|
||||
).should('be.visible');
|
||||
cy.contains(
|
||||
'Approve $VEGA Tokens for staking on Vega',
|
||||
txTimeout
|
||||
@@ -181,7 +182,7 @@ export function clickOnValidatorFromList(
|
||||
} else {
|
||||
cy.get(`[row-id="${validatorNumber}"]`)
|
||||
.should('be.visible')
|
||||
.find(stakeValidatorListName)
|
||||
.first()
|
||||
.as('validatorOnList');
|
||||
cy.get('@validatorOnList').click();
|
||||
}
|
||||
|
||||
@@ -31,3 +31,4 @@ LC_ALL="en_US.UTF-8"
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
@@ -19,6 +19,9 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
|
||||
@@ -26,4 +29,5 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
CYPRESS_FAIRGROUND=false
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
@@ -14,8 +14,12 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
@@ -14,9 +14,12 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -13,9 +13,12 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -10,8 +10,12 @@ NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
@@ -15,8 +15,12 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
@@ -12,8 +12,12 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
AppFailure,
|
||||
NodeSwitcherDialog,
|
||||
useNodeSwitcherStore,
|
||||
DocsLinks,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ENV } from './config';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
@@ -109,8 +110,17 @@ const Web3Container = ({
|
||||
store.connectors,
|
||||
store.initialize,
|
||||
]);
|
||||
const { ETHEREUM_PROVIDER_URL, ETH_LOCAL_PROVIDER_URL, ETH_WALLET_MNEMONIC } =
|
||||
useEnvironment();
|
||||
const {
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
ETH_LOCAL_PROVIDER_URL,
|
||||
ETH_WALLET_MNEMONIC,
|
||||
VEGA_ENV,
|
||||
VEGA_URL,
|
||||
VEGA_EXPLORER_URL,
|
||||
CHROME_EXTENSION_URL,
|
||||
MOZILLA_EXTENSION_URL,
|
||||
VEGA_WALLET_URL,
|
||||
} = useEnvironment();
|
||||
useEffect(() => {
|
||||
if (chainId) {
|
||||
return initializeConnectors(
|
||||
@@ -139,10 +149,33 @@ const Web3Container = ({
|
||||
return <SplashLoader />;
|
||||
}
|
||||
|
||||
if (
|
||||
!VEGA_URL ||
|
||||
!VEGA_WALLET_URL ||
|
||||
!VEGA_EXPLORER_URL ||
|
||||
!DocsLinks ||
|
||||
!CHROME_EXTENSION_URL ||
|
||||
!MOZILLA_EXTENSION_URL
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Web3Provider connectors={connectors}>
|
||||
<Web3Connector connectors={connectors} chainId={Number(chainId)}>
|
||||
<VegaWalletProvider>
|
||||
<VegaWalletProvider
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
concepts: DocsLinks?.VEGA_WALLET_CONCEPTS_URL,
|
||||
chromeExtensionUrl: CHROME_EXTENSION_URL,
|
||||
mozillaExtensionUrl: MOZILLA_EXTENSION_URL,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ContractsProvider>
|
||||
<AppLoader>
|
||||
<BalanceManager>
|
||||
@@ -275,7 +308,7 @@ const AppContainer = () => {
|
||||
<Router>
|
||||
<ScrollToTop />
|
||||
<AppStateProvider>
|
||||
<div className="grid min-h-full text-white">
|
||||
<div className="min-h-full text-white grid">
|
||||
<NodeGuard
|
||||
skeleton={<div>{t('Loading')}</div>}
|
||||
failure={
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Nav } from '../nav';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
ProtocolUpgradeCountdownMode,
|
||||
ProtocolUpgradeInProgressNotification,
|
||||
ProtocolUpgradeProposalNotification,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||
@@ -49,6 +50,7 @@ const NotificationsContainer = () => {
|
||||
<ProtocolUpgradeProposalNotification
|
||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||
/>
|
||||
<ProtocolUpgradeInProgressNotification />
|
||||
{isReadOnly ? (
|
||||
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
||||
) : null}
|
||||
|
||||
@@ -626,7 +626,8 @@
|
||||
"status-tendermint": "Consensus",
|
||||
"status-ersatz": "Standby",
|
||||
"status-pending": "Candidate",
|
||||
"ersatzDescription": "To be promoted, a standby validator must have more than the lowest consensus stake, plus a bonus given to existing validators. This currently requires a minimum of {{stakeNeededForPromotion}} stake assuming no penalties. Only one validator per epoch can be promoted.",
|
||||
"ersatzDescription1": "To be promoted, a standby validator must have more than the lowest consensus stake, plus a bonus given to existing validators, and only one standby validator can be promoted per epoch. Currently this requires a minimum of",
|
||||
"ersatzDescription2": "stake assuming no performance penalty incurred.",
|
||||
"pendingDescription1": "Anyone can",
|
||||
"pendingDescriptionLinkText": "set up and run a node on Vega",
|
||||
"pendingDescription2": ". A node can move from being a candidate into standby based on how much nomination it attracts, assuming it has proven reliability by sending heartbeats to the network.",
|
||||
@@ -784,6 +785,7 @@
|
||||
"PerformancePenaltyDescription": "Performance score is a measure of how often a validator proposed blocks in the last epoch relative to how many they should be expected to propose based on their voting power. Performance penalty is applied for having a performance score of less than 1",
|
||||
"UnnormalisedVotingPowerDescription": "The voting power of the validator based on their final validator score after all penalties have been applied",
|
||||
"NormalisedVotingPowerDescription": "The voting power of the validator, adjusted to ensure all validator scores sum to 1, used for distribution of rewards",
|
||||
"NonConsensusVotingPowerDescription": "The voting power of the validator. Only consensus validators have voting power",
|
||||
"Score": "Score",
|
||||
"performancePenalty": "Performance penalty",
|
||||
"overstaked": "Overstaked",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<meta name="og:site_name" content="Vega Protocol - Governance" />
|
||||
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:card" content="https://static.vega.xyz/favicon.ico" />
|
||||
<meta name="twitter:title" content="Vega Protocol - Governance" />
|
||||
<meta name="twitter:description" content="Vega Protocol - Governance" />
|
||||
<meta name="twitter:image" content="https://static.vega.xyz/favicon.ico" />
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
InjectedConnector,
|
||||
SnapConnector,
|
||||
DEFAULT_SNAP_ID,
|
||||
} from '@vegaprotocol/wallet';
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
export const injected = new InjectedConnector();
|
||||
export const jsonRpc = new JsonRpcConnector();
|
||||
export const injected = new InjectedConnector();
|
||||
export const view = new ViewConnector(urlParams.get('address'));
|
||||
|
||||
export const snap = FLAGS.METAMASK_SNAPS
|
||||
? new SnapConnector(DEFAULT_SNAP_ID)
|
||||
: undefined;
|
||||
|
||||
export const Connectors = {
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap,
|
||||
};
|
||||
|
||||
+21
-38
@@ -1,44 +1,27 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import { RoundedWrapper, ShowMore } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const ProposalDescription = ({
|
||||
description,
|
||||
}: {
|
||||
description: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDescription, setShowDescription] = useState(false);
|
||||
|
||||
return (
|
||||
<section data-testid="proposal-description">
|
||||
<CollapsibleToggle
|
||||
toggleState={showDescription}
|
||||
setToggleState={setShowDescription}
|
||||
dataTestId={'proposal-description-toggle'}
|
||||
>
|
||||
<SubHeading title={t('proposalDescription')} />
|
||||
</CollapsibleToggle>
|
||||
|
||||
{showDescription && (
|
||||
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
|
||||
<div className="p-2">
|
||||
<ReactMarkdown
|
||||
className="react-markdown-container"
|
||||
/* Prevents HTML embedded in the description from rendering */
|
||||
skipHtml={true}
|
||||
/* Stops users embedding images which could be used for tracking */
|
||||
disallowedElements={['img']}
|
||||
linkTarget="_blank"
|
||||
>
|
||||
{description}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</RoundedWrapper>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
}) => (
|
||||
<section data-testid="proposal-description">
|
||||
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
|
||||
<div className="p-2">
|
||||
<ShowMore>
|
||||
<ReactMarkdown
|
||||
className="react-markdown-container"
|
||||
/* Prevents HTML embedded in the description from rendering */
|
||||
skipHtml={true}
|
||||
/* Stops users embedding images which could be used for tracking */
|
||||
disallowedElements={['img']}
|
||||
linkTarget="_blank"
|
||||
>
|
||||
{description}
|
||||
</ReactMarkdown>
|
||||
</ShowMore>
|
||||
</div>
|
||||
</RoundedWrapper>
|
||||
</section>
|
||||
);
|
||||
|
||||
+22
-9
@@ -24,6 +24,7 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { VoteState } from '../vote-details/use-user-vote';
|
||||
|
||||
jest.mock('@vegaprotocol/proposals', () => ({
|
||||
...jest.requireActual('@vegaprotocol/proposals'),
|
||||
@@ -36,7 +37,8 @@ jest.mock('@vegaprotocol/proposals', () => ({
|
||||
const renderComponent = (
|
||||
proposal: ProposalQuery['proposal'],
|
||||
isListItem = true,
|
||||
mocks: MockedResponse[] = []
|
||||
mocks: MockedResponse[] = [],
|
||||
voteState?: VoteState
|
||||
) =>
|
||||
render(
|
||||
<AppStateProvider>
|
||||
@@ -47,6 +49,7 @@ const renderComponent = (
|
||||
proposal={proposal}
|
||||
isListItem={isListItem}
|
||||
networkParams={mockNetworkParams}
|
||||
voteState={voteState}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -386,10 +389,15 @@ describe('Proposal header', () => {
|
||||
closingDatetime: nextWeek.toString(),
|
||||
},
|
||||
});
|
||||
renderComponent(proposal, true, [
|
||||
// @ts-ignore generateProposal always creates an id
|
||||
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_NO),
|
||||
]);
|
||||
renderComponent(
|
||||
proposal,
|
||||
true,
|
||||
[
|
||||
// @ts-ignore generateProposal always creates an id
|
||||
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_NO),
|
||||
],
|
||||
VoteState.No
|
||||
);
|
||||
expect(await screen.findByTestId('user-voted-no')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -400,10 +408,15 @@ describe('Proposal header', () => {
|
||||
closingDatetime: nextWeek.toString(),
|
||||
},
|
||||
});
|
||||
renderComponent(proposal, true, [
|
||||
// @ts-ignore generateProposal always creates an id
|
||||
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_YES),
|
||||
]);
|
||||
renderComponent(
|
||||
proposal,
|
||||
true,
|
||||
[
|
||||
// @ts-ignore generateProposal always creates an id
|
||||
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_YES),
|
||||
],
|
||||
VoteState.Yes
|
||||
);
|
||||
expect(await screen.findByTestId('user-voted-yes')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
+3
-2
@@ -8,25 +8,26 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
import { ProposalVotingStatus } from '../proposal-voting-status';
|
||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import Routes from '../../../routes';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { VoteState } from '../vote-details/use-user-vote';
|
||||
|
||||
export const ProposalHeader = ({
|
||||
proposal,
|
||||
networkParams,
|
||||
isListItem = true,
|
||||
voteState,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
networkParams: Partial<NetworkParamsResult>;
|
||||
isListItem?: boolean;
|
||||
voteState?: VoteState | null;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { voteState } = useUserVote(proposal?.id);
|
||||
const change = proposal?.terms.change;
|
||||
|
||||
let details: ReactNode;
|
||||
|
||||
+138
-152
@@ -15,8 +15,6 @@ import {
|
||||
SettlementAssetInfoPanel,
|
||||
} from '@vegaprotocol/markets';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
Button,
|
||||
CopyWithTooltip,
|
||||
Dialog,
|
||||
@@ -43,6 +41,9 @@ export const useMarketDataDialogStore = create<MarketDataDialogState>(
|
||||
})
|
||||
);
|
||||
|
||||
const marketDataHeaderStyles =
|
||||
'font-alpha calt text-base border-b border-vega-dark-200 mt-2 py-2';
|
||||
|
||||
export const ProposalMarketData = ({
|
||||
marketData,
|
||||
parentMarketData,
|
||||
@@ -76,6 +77,14 @@ export const ProposalMarketData = ({
|
||||
parentTerminationData !== undefined &&
|
||||
isEqual(terminationData, parentTerminationData);
|
||||
|
||||
const showParentPriceMonitoringBounds =
|
||||
parentMarketData?.priceMonitoringSettings?.parameters?.triggers !==
|
||||
undefined &&
|
||||
!isEqual(
|
||||
marketData.priceMonitoringSettings?.parameters?.triggers,
|
||||
parentMarketData?.priceMonitoringSettings?.parameters?.triggers
|
||||
);
|
||||
|
||||
const getSigners = (data: DataSourceDefinition) => {
|
||||
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
|
||||
const signers = data.sourceType.sourceType.signers || [];
|
||||
@@ -108,164 +117,141 @@ export const ProposalMarketData = ({
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mb-10">
|
||||
<Accordion>
|
||||
<AccordionItem
|
||||
itemId="key-details"
|
||||
title={t('Key details')}
|
||||
content={
|
||||
<KeyDetailsInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="instrument"
|
||||
title={t('Instrument')}
|
||||
content={
|
||||
<InstrumentInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{isEqual(
|
||||
getSigners(settlementData),
|
||||
getSigners(terminationData)
|
||||
) ? (
|
||||
<AccordionItem
|
||||
itemId="oracles"
|
||||
title={t('Oracle')}
|
||||
content={
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="settlementData"
|
||||
parentMarket={
|
||||
isParentSettlementDataEqual
|
||||
? undefined
|
||||
: parentMarketData
|
||||
}
|
||||
/>
|
||||
<h2 className={marketDataHeaderStyles}>{t('Key details')}</h2>
|
||||
<KeyDetailsInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>{t('Instrument')}</h2>
|
||||
<InstrumentInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
|
||||
{isEqual(
|
||||
getSigners(settlementData),
|
||||
getSigners(terminationData)
|
||||
) ? (
|
||||
<>
|
||||
<h2 className={marketDataHeaderStyles}>{t('Oracle')}</h2>
|
||||
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="settlementData"
|
||||
parentMarket={
|
||||
isParentSettlementDataEqual ? undefined : parentMarketData
|
||||
}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h2 className={marketDataHeaderStyles}>
|
||||
{t('Settlement Oracle')}
|
||||
</h2>
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="settlementData"
|
||||
parentMarket={
|
||||
isParentSettlementDataEqual ? undefined : parentMarketData
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<AccordionItem
|
||||
itemId="settlement-oracle"
|
||||
title={t('Settlement Oracle')}
|
||||
content={
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="settlementData"
|
||||
parentMarket={
|
||||
isParentSettlementDataEqual
|
||||
? undefined
|
||||
: parentMarketData
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<AccordionItem
|
||||
itemId="termination-oracle"
|
||||
title={t('Termination Oracle')}
|
||||
content={
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="termination"
|
||||
parentMarket={
|
||||
isParentTerminationDataEqual
|
||||
? undefined
|
||||
: parentMarketData
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{/*Note: successor markets will not differ in their settlement*/}
|
||||
{/*assets, so no need to pass in parent market data for comparison.*/}
|
||||
<AccordionItem
|
||||
itemId="settlement-asset"
|
||||
title={t('Settlement asset')}
|
||||
content={<SettlementAssetInfoPanel market={marketData} />}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="metadata"
|
||||
title={t('Metadata')}
|
||||
content={
|
||||
<MetadataInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="risk-model"
|
||||
title={t('Risk model')}
|
||||
content={
|
||||
<RiskModelInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="risk-parameters"
|
||||
title={t('Risk parameters')}
|
||||
content={
|
||||
<RiskParametersInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="risk-factors"
|
||||
title={t('Risk factors')}
|
||||
content={
|
||||
<RiskFactorsInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{(
|
||||
marketData.priceMonitoringSettings?.parameters?.triggers || []
|
||||
<h2 className={marketDataHeaderStyles}>
|
||||
{t('Termination Oracle')}
|
||||
</h2>
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="termination"
|
||||
parentMarket={
|
||||
isParentTerminationDataEqual ? undefined : parentMarketData
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/*Note: successor markets will not differ in their settlement*/}
|
||||
{/*assets, so no need to pass in parent market data for comparison.*/}
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>{t('Settlement assets')}</h2>
|
||||
<SettlementAssetInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>{t('Metadata')}</h2>
|
||||
<MetadataInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>{t('Risk model')}</h2>
|
||||
<RiskModelInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>{t('Risk parameters')}</h2>
|
||||
<RiskParametersInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>{t('Risk factors')}</h2>
|
||||
<RiskFactorsInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
|
||||
{showParentPriceMonitoringBounds &&
|
||||
(
|
||||
parentMarketData?.priceMonitoringSettings?.parameters
|
||||
?.triggers || []
|
||||
).map((_, triggerIndex) => (
|
||||
<AccordionItem
|
||||
itemId={`trigger-${triggerIndex}`}
|
||||
title={t(`Price monitoring bounds ${triggerIndex + 1}`)}
|
||||
content={
|
||||
<>
|
||||
<h2 className={marketDataHeaderStyles}>
|
||||
{t(`Parent price monitoring bounds ${triggerIndex + 1}`)}
|
||||
</h2>
|
||||
|
||||
<div className="text-vega-dark-300 line-through">
|
||||
<PriceMonitoringBoundsInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
market={parentMarketData}
|
||||
triggerIndex={triggerIndex}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
<AccordionItem
|
||||
itemId="liqudity-monitoring-parameters"
|
||||
title={t('Liquidity monitoring parameters')}
|
||||
content={
|
||||
<LiquidityMonitoringParametersInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="liquidity-price-range"
|
||||
title={t('Liquidity price range')}
|
||||
content={
|
||||
<LiquidityPriceRangeInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Accordion>
|
||||
|
||||
{(
|
||||
marketData.priceMonitoringSettings?.parameters?.triggers || []
|
||||
).map((_, triggerIndex) => (
|
||||
<>
|
||||
<h2 className={marketDataHeaderStyles}>
|
||||
{t(`Price monitoring bounds ${triggerIndex + 1}`)}
|
||||
</h2>
|
||||
|
||||
<PriceMonitoringBoundsInfoPanel
|
||||
market={marketData}
|
||||
triggerIndex={triggerIndex}
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>
|
||||
{t('Liquidity monitoring parameters')}
|
||||
</h2>
|
||||
<LiquidityMonitoringParametersInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
|
||||
<h2 className={marketDataHeaderStyles}>
|
||||
{t('Liquidity price range')}
|
||||
</h2>
|
||||
<LiquidityPriceRangeInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import type { VegaWalletConfig } from '@vegaprotocol/wallet';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { Proposal } from './proposal';
|
||||
@@ -41,14 +44,30 @@ jest.mock('../list-asset', () => ({
|
||||
ListAsset: () => <div data-testid="proposal-list-asset"></div>,
|
||||
}));
|
||||
|
||||
const vegaWalletConfig: VegaWalletConfig = {
|
||||
network: 'TESTNET',
|
||||
vegaUrl: 'https://vega.xyz',
|
||||
vegaWalletServiceUrl: 'https://wallet.vega.xyz',
|
||||
links: {
|
||||
explorer: 'explorer',
|
||||
concepts: 'concepts',
|
||||
chromeExtensionUrl: 'chrome',
|
||||
mozillaExtensionUrl: 'mozilla',
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<Proposal
|
||||
restData={{}}
|
||||
proposal={proposal as ProposalQuery['proposal']}
|
||||
networkParams={mockNetworkParams}
|
||||
/>
|
||||
<MockedProvider>
|
||||
<VegaWalletProvider config={vegaWalletConfig}>
|
||||
<Proposal
|
||||
restData={{}}
|
||||
proposal={proposal as ProposalQuery['proposal']}
|
||||
networkParams={mockNetworkParams}
|
||||
/>
|
||||
</VegaWalletProvider>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,6 +19,8 @@ import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ProposalMarketChanges } from '../proposal-market-changes';
|
||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||
import { useVoteSubmit } from '@vegaprotocol/proposals';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
|
||||
export enum ProposalType {
|
||||
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
||||
@@ -53,6 +55,8 @@ export const Proposal = ({
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
}: ProposalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { submit, Dialog, finalizedVote, transaction } = useVoteSubmit();
|
||||
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
||||
|
||||
if (!proposal) {
|
||||
return null;
|
||||
@@ -132,10 +136,12 @@ export const Proposal = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ProposalHeader
|
||||
proposal={proposal}
|
||||
isListItem={false}
|
||||
networkParams={networkParams}
|
||||
voteState={voteState}
|
||||
/>
|
||||
|
||||
<div id="details">
|
||||
@@ -207,6 +213,11 @@ export const Proposal = ({
|
||||
spamProtectionMinTokens={
|
||||
networkParams?.spam_protection_voting_min_tokens
|
||||
}
|
||||
submit={submit}
|
||||
dialog={Dialog}
|
||||
transaction={transaction}
|
||||
voteState={voteState}
|
||||
voteDatetime={voteDatetime}
|
||||
/>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
|
||||
+7
-1
@@ -1,6 +1,7 @@
|
||||
import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||
@@ -14,12 +15,17 @@ export const ProposalsListItem = ({
|
||||
proposal,
|
||||
networkParams,
|
||||
}: ProposalsListItemProps) => {
|
||||
const { voteState } = useUserVote(proposal?.id);
|
||||
if (!proposal || !proposal.id || !networkParams) return null;
|
||||
|
||||
return (
|
||||
<li id={proposal.id} data-testid="proposals-list-item">
|
||||
<RoundedWrapper paddingBottom={true} heightFull={true}>
|
||||
<ProposalHeader proposal={proposal} networkParams={networkParams} />
|
||||
<ProposalHeader
|
||||
proposal={proposal}
|
||||
networkParams={networkParams}
|
||||
voteState={voteState}
|
||||
/>
|
||||
<ProposalsListItemDetails proposal={proposal} />
|
||||
</RoundedWrapper>
|
||||
</li>
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||
import { VoteState } from './use-user-vote';
|
||||
import { VegaTxStatus } from '@vegaprotocol/wallet';
|
||||
|
||||
describe('VoteTransactionDialog', () => {
|
||||
const mockTransactionDialog = jest.fn(({ title, content }) => (
|
||||
<div>
|
||||
<div>{title}</div>
|
||||
<div>{content?.Complete}</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
it('renders without crashing', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Yes}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('vote-transaction-dialog')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with txRequested title when voteState is Requested', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Requested}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('txRequested')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with votePending title when voteState is Pending', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Pending}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('votePending')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with no title when voteState is neither Requested nor Pending', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Yes} // or any other state other than Requested or Pending
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryByText('txRequested')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('votePending')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders custom error message when voteState is Failed and error message exists', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Failed}
|
||||
transaction={{
|
||||
error: { message: 'Custom error test message', name: 'blah' },
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Error,
|
||||
dialogOpen: false,
|
||||
}}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Custom error test message')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders default error message when voteState is failed and no error message exists on the tx', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Failed}
|
||||
transaction={{
|
||||
error: null,
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Error,
|
||||
dialogOpen: false,
|
||||
}}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('voteError')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders default ui (i.e. not error) when not in a failed state', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Yes}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryByText('voteError')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,10 @@
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { VoteButtons } from './vote-buttons';
|
||||
import { VoteState } from './use-user-vote';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { MockedProvider } from '@apollo/react-testing';
|
||||
@@ -24,6 +25,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -47,6 +49,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -65,7 +68,7 @@ describe('Vote buttons', () => {
|
||||
disconnect: jest.fn(),
|
||||
selectPubKey: jest.fn(),
|
||||
connector: null,
|
||||
};
|
||||
} as unknown as VegaWalletContextShape;
|
||||
|
||||
render(
|
||||
<AppStateProvider>
|
||||
@@ -81,6 +84,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -105,6 +109,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(0)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -132,6 +137,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -159,6 +165,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(10)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
@@ -183,6 +190,7 @@ describe('Vote buttons', () => {
|
||||
currentStakeAvailable={new BigNumber(10)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
|
||||
@@ -17,7 +17,7 @@ import { VoteState } from './use-user-vote';
|
||||
import { ProposalMinRequirements, ProposalUserAction } from '../shared';
|
||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||
import { useVoteButtonsQuery } from './__generated__/Stake';
|
||||
import type { DialogProps } from '@vegaprotocol/wallet';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet';
|
||||
|
||||
interface VoteButtonsContainerProps {
|
||||
voteState: VoteState | null;
|
||||
@@ -27,6 +27,7 @@ interface VoteButtonsContainerProps {
|
||||
minVoterBalance: string | null | undefined;
|
||||
spamProtectionMinTokens: string | null | undefined;
|
||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||
transaction: VegaTxState | null;
|
||||
dialog: (props: DialogProps) => JSX.Element;
|
||||
className?: string;
|
||||
}
|
||||
@@ -67,6 +68,7 @@ export const VoteButtons = ({
|
||||
minVoterBalance,
|
||||
spamProtectionMinTokens,
|
||||
submit,
|
||||
transaction,
|
||||
dialog: Dialog,
|
||||
}: VoteButtonsProps) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -188,11 +190,7 @@ export const VoteButtons = ({
|
||||
(voteState === VoteState.Yes || voteState === VoteState.No) && (
|
||||
<p data-testid="you-voted">
|
||||
<span>{t('youVoted')}:</span>{' '}
|
||||
<span
|
||||
className={
|
||||
voteState === VoteState.Yes ? 'text-success' : 'text-danger'
|
||||
}
|
||||
>
|
||||
<span className="text-white font-bold">
|
||||
{t(`voteState_${voteState}`)}
|
||||
</span>{' '}
|
||||
{voteDatetime ? (
|
||||
@@ -212,7 +210,11 @@ export const VoteButtons = ({
|
||||
</p>
|
||||
)
|
||||
)}
|
||||
<VoteTransactionDialog voteState={voteState} TransactionDialog={Dialog} />
|
||||
<VoteTransactionDialog
|
||||
voteState={voteState}
|
||||
transaction={transaction}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,23 +3,30 @@ import { formatDistanceToNow } from 'date-fns';
|
||||
import { RoundedWrapper, Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
|
||||
import { VoteProgress } from '@vegaprotocol/proposals';
|
||||
import { formatNumber } from '../../../../lib/format-number';
|
||||
import { ConnectToVega } from '../../../../components/connect-to-vega';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import { useUserVote } from './use-user-vote';
|
||||
import { CurrentProposalStatus } from '../current-proposal-status';
|
||||
import { VoteButtonsContainer } from './vote-buttons';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { ProposalType } from '../proposal/proposal';
|
||||
import type { VoteValue } from '@vegaprotocol/types';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { VoteState } from './use-user-vote';
|
||||
|
||||
interface VoteDetailsProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
minVoterBalance: string | null | undefined;
|
||||
spamProtectionMinTokens: string | null | undefined;
|
||||
proposalType: ProposalType | null;
|
||||
transaction: VegaTxState | null;
|
||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||
dialog: (props: DialogProps) => JSX.Element;
|
||||
voteState: VoteState | null;
|
||||
voteDatetime: Date | null;
|
||||
}
|
||||
|
||||
export const VoteDetails = ({
|
||||
@@ -27,6 +34,11 @@ export const VoteDetails = ({
|
||||
minVoterBalance,
|
||||
spamProtectionMinTokens,
|
||||
proposalType,
|
||||
submit,
|
||||
transaction,
|
||||
dialog,
|
||||
voteState,
|
||||
voteDatetime,
|
||||
}: VoteDetailsProps) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const {
|
||||
@@ -48,8 +60,7 @@ export const VoteDetails = ({
|
||||
} = useVoteInformation({ proposal });
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { submit, Dialog, finalizedVote } = useVoteSubmit();
|
||||
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
||||
|
||||
const defaultDecimals = 2;
|
||||
const daysLeft = t('daysLeft', {
|
||||
daysLeft: formatDistanceToNow(new Date(proposal?.terms.closingDatetime)),
|
||||
@@ -219,7 +230,8 @@ export const VoteDetails = ({
|
||||
spamProtectionMinTokens={spamProtectionMinTokens}
|
||||
className="flex"
|
||||
submit={submit}
|
||||
dialog={Dialog}
|
||||
transaction={transaction}
|
||||
dialog={dialog}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
|
||||
+6
-2
@@ -1,9 +1,10 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { VoteState } from './use-user-vote';
|
||||
import type { DialogProps } from '@vegaprotocol/wallet';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet';
|
||||
|
||||
interface VoteTransactionDialogProps {
|
||||
voteState: VoteState;
|
||||
transaction: VegaTxState | null;
|
||||
TransactionDialog: (props: DialogProps) => JSX.Element;
|
||||
}
|
||||
|
||||
@@ -20,12 +21,15 @@ const dialogTitle = (voteState: VoteState): string | undefined => {
|
||||
|
||||
export const VoteTransactionDialog = ({
|
||||
voteState,
|
||||
transaction,
|
||||
TransactionDialog,
|
||||
}: VoteTransactionDialogProps) => {
|
||||
// Render a custom message if the voting fails otherwise
|
||||
// pass undefined so that the default vega transaction dialog UI gets used
|
||||
const customMessage =
|
||||
voteState === VoteState.Failed ? <p>{t('voteError')}</p> : undefined;
|
||||
voteState === VoteState.Failed ? (
|
||||
<p>{transaction?.error?.message || t('voteError')}</p>
|
||||
) : undefined;
|
||||
|
||||
return (
|
||||
<div data-testid="vote-transaction-dialog">
|
||||
|
||||
@@ -114,6 +114,7 @@ describe('Raw proposal form', () => {
|
||||
{
|
||||
pubKey,
|
||||
sendTx: mockSendTx,
|
||||
links: { explorer: 'explorer' },
|
||||
} as unknown as VegaWalletContextShape
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import type { PubKey } from '@vegaprotocol/wallet';
|
||||
import type { PubKey, VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import type { VoteValue } from '@vegaprotocol/types';
|
||||
import type { UserVoteQuery } from '../components/vote-details/__generated__/Vote';
|
||||
import { UserVoteDocument } from '../components/vote-details/__generated__/Vote';
|
||||
@@ -21,7 +21,7 @@ export const mockWalletContext = {
|
||||
disconnect: jest.fn(),
|
||||
selectPubKey: jest.fn(),
|
||||
connector: null,
|
||||
};
|
||||
} as unknown as VegaWalletContextShape;
|
||||
|
||||
const mockEthereumConfig = {
|
||||
network_id: '3',
|
||||
|
||||
+2
-2
@@ -389,10 +389,10 @@ export const ConsensusValidatorsTable = ({
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.NORMALISED_VOTING_POWER,
|
||||
headerName: t(ValidatorFields.NORMALISED_VOTING_POWER).toString(),
|
||||
headerName: t('votingPower').toString(),
|
||||
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
|
||||
cellRenderer: VotingPowerRenderer,
|
||||
width: 200,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.TOTAL_PENALTIES,
|
||||
|
||||
+11
-2
@@ -20,6 +20,7 @@ import {
|
||||
TotalStakeRenderer,
|
||||
StakeShareRenderer,
|
||||
PendingStakeRenderer,
|
||||
VotingPowerRenderer,
|
||||
} from './shared';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
@@ -39,7 +40,6 @@ interface StandbyPendingValidatorsTableProps extends ValidatorsTableProps {
|
||||
export const StandbyPendingValidatorsTable = ({
|
||||
data,
|
||||
previousEpochData,
|
||||
totalStake,
|
||||
stakeNeededForPromotion,
|
||||
stakeNeededForPromotionDescription,
|
||||
validatorsView,
|
||||
@@ -132,6 +132,8 @@ export const StandbyPendingValidatorsTable = ({
|
||||
name,
|
||||
},
|
||||
[ValidatorFields.STAKE]: stakedTotal,
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]: '0%',
|
||||
[ValidatorFields.UNNORMALISED_VOTING_POWER]: '0%',
|
||||
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION]:
|
||||
individualStakeNeededForPromotion || null,
|
||||
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION_DESCRIPTION]:
|
||||
@@ -223,7 +225,14 @@ export const StandbyPendingValidatorsTable = ({
|
||||
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
|
||||
headerTooltip: t('StakeShareDescription').toString(),
|
||||
cellRenderer: StakeShareRenderer,
|
||||
width: 100,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.NORMALISED_VOTING_POWER,
|
||||
headerName: t('votingPower').toString(),
|
||||
headerTooltip: t('NonConsensusVotingPowerDescription').toString(),
|
||||
cellRenderer: VotingPowerRenderer,
|
||||
width: 120,
|
||||
},
|
||||
// {
|
||||
// field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
|
||||
|
||||
@@ -17,6 +17,10 @@ import type { PreviousEpochQuery } from '../../__generated__/PreviousEpoch';
|
||||
import type { StakingQuery } from '../../__generated__/Staking';
|
||||
import type { StakingDelegationFieldsFragment } from '../../__generated__/Staking';
|
||||
import type { ValidatorWithUserData } from './shared';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
|
||||
export interface ValidatorsTableProps {
|
||||
nodesData: NodesQuery | undefined;
|
||||
@@ -41,6 +45,9 @@ export const ValidatorTables = ({
|
||||
const {
|
||||
appState: { decimals },
|
||||
} = useAppState();
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.network_validators_incumbentBonus,
|
||||
]);
|
||||
|
||||
const [validatorsView, setValidatorsView] = useState<ValidatorsView>('all');
|
||||
const totalStake = useMemo(
|
||||
@@ -52,6 +59,10 @@ export const ValidatorTables = ({
|
||||
() => userStakingData?.party?.stakingSummary.currentStakeAvailable || '0',
|
||||
[userStakingData?.party?.stakingSummary.currentStakeAvailable]
|
||||
);
|
||||
const incumbentBonus = useMemo(
|
||||
() => new BigNumber(params?.network_validators_incumbentBonus),
|
||||
[params?.network_validators_incumbentBonus]
|
||||
);
|
||||
|
||||
let stakeNeededForPromotion = undefined;
|
||||
let delegations: StakingDelegationFieldsFragment[] | undefined = undefined;
|
||||
@@ -126,28 +137,23 @@ export const ValidatorTables = ({
|
||||
consensusValidators.length &&
|
||||
(standbyValidators.length || pendingValidators.length)
|
||||
) {
|
||||
const lowestRankingConsensusScore = consensusValidators.reduce(
|
||||
const lowestConsensusStake = consensusValidators.reduce(
|
||||
(lowest: ValidatorWithUserData, validator: ValidatorWithUserData) => {
|
||||
if (
|
||||
Number(validator.rankingScore.rankingScore) <
|
||||
Number(lowest.rankingScore.rankingScore)
|
||||
) {
|
||||
if (Number(validator.stakedTotal) < Number(lowest.stakedTotal)) {
|
||||
lowest = validator;
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
).rankingScore.rankingScore;
|
||||
).stakedTotal;
|
||||
|
||||
const lowestRankingBigNum = toBigNum(lowestRankingConsensusScore, 0);
|
||||
const consensusStakedTotal = consensusValidators.reduce((acc, cur) => {
|
||||
return acc.plus(toBigNum(cur.stakedTotal, decimals));
|
||||
}, new BigNumber(0));
|
||||
const lowestRankingBigNum = toBigNum(lowestConsensusStake, decimals);
|
||||
|
||||
stakeNeededForPromotion = formatNumber(
|
||||
lowestRankingBigNum.times(consensusStakedTotal),
|
||||
lowestRankingBigNum.multipliedBy(incumbentBonus.plus(1)),
|
||||
2
|
||||
).toString();
|
||||
}
|
||||
|
||||
return (
|
||||
<section data-testid="validator-tables">
|
||||
<div className="grid w-full justify-end">
|
||||
@@ -187,12 +193,18 @@ export const ValidatorTables = ({
|
||||
<div className="mb-10">
|
||||
<SubHeading title={t('status-ersatz')} />
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="ersatzDescription"
|
||||
values={{
|
||||
stakeNeededForPromotion,
|
||||
}}
|
||||
/>
|
||||
<span>
|
||||
<Trans i18nKey="ersatzDescription1" />
|
||||
</span>
|
||||
|
||||
<span className="text-white font-bold">
|
||||
{' '}
|
||||
{stakeNeededForPromotion}{' '}
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<Trans i18nKey="ersatzDescription2" />
|
||||
</span>
|
||||
</p>
|
||||
<StandbyPendingValidatorsTable
|
||||
data={standbyValidators}
|
||||
|
||||
@@ -68,7 +68,7 @@ export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
}
|
||||
const penalty = new BigNumber(1)
|
||||
.minus(
|
||||
new BigNumber(node.rewardScore?.rawValidatorScore || 0).dividedBy(tts)
|
||||
new BigNumber(node.rewardScore?.rawValidatorScore || 1).dividedBy(tts)
|
||||
)
|
||||
.times(100);
|
||||
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
|
||||
|
||||
@@ -36,4 +36,4 @@ CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
|
||||
# Cosmic elevator flags (MUST be doubled with CYPRESS_ prefix)
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
describe('charts', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
it('can see market depth chart', () => {
|
||||
// 6006-DEPC-001
|
||||
cy.getByTestId('Depth').click();
|
||||
cy.getByTestId('tab-depth').should('be.visible');
|
||||
});
|
||||
});
|
||||
@@ -256,8 +256,15 @@ describe('Closed markets', { tags: '@smoke' }, () => {
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="code"]')
|
||||
.find('[data-testid="market-code"]')
|
||||
.should('have.text', settledMarket.tradableInstrument.instrument.code);
|
||||
|
||||
// 6001-MARK-071
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[title="Future"]')
|
||||
.should('have.text', 'Futr');
|
||||
|
||||
// 6001-MARK-002
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import type { MarketsQuery } from '@vegaprotocol/markets';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const rowSelector =
|
||||
'[data-testid="tab-open-markets"] .ag-center-cols-container .ag-row';
|
||||
const colInstrumentCode = '[col-id="tradableInstrument.instrument.code"]';
|
||||
|
||||
describe('markets all table', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage().then(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/all');
|
||||
});
|
||||
});
|
||||
|
||||
it('can see table headers', () => {
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
const headers = [
|
||||
'Market',
|
||||
'Description',
|
||||
'Trading mode',
|
||||
'Status',
|
||||
'Successor market',
|
||||
'Best bid',
|
||||
'Best offer',
|
||||
'Mark price',
|
||||
'Settlement asset',
|
||||
'',
|
||||
];
|
||||
cy.getByTestId('tab-open-markets').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('markets tab should be rendered properly', () => {
|
||||
cy.get('[data-testid="Open markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'active'
|
||||
);
|
||||
cy.get('[data-testid="Proposed markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'inactive'
|
||||
);
|
||||
cy.get('[data-testid="Closed markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'inactive'
|
||||
);
|
||||
});
|
||||
it('renders markets correctly', () => {
|
||||
// 6001-MARK-035
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(colInstrumentCode)
|
||||
.should('have.text', 'SOLUSD');
|
||||
|
||||
// 6001-MARK-036
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="tradableInstrument.instrument.name"]')
|
||||
.should('have.text', 'SUSPENDED MARKET');
|
||||
|
||||
// 6001-MARK-037
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="tradingMode"]')
|
||||
.should('have.text', 'Continuous');
|
||||
|
||||
// 6001-MARK-038
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="state"]')
|
||||
.should('have.text', 'Active');
|
||||
|
||||
// 6001-MARK-039
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="data.bestBidPrice"]')
|
||||
.should('have.text', '0.00');
|
||||
|
||||
// 6001-MARK-040
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="data.bestOfferPrice"]')
|
||||
.should('have.text', '0.00');
|
||||
|
||||
// 6001-MARK-041
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="data.markPrice"]')
|
||||
.should('have.text', '84.41');
|
||||
|
||||
// 6001-MARK-042
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(
|
||||
'[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"]'
|
||||
)
|
||||
.should('have.text', 'XYZalpha');
|
||||
|
||||
// 6001-MARK-043
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(
|
||||
'[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"] button'
|
||||
)
|
||||
.click();
|
||||
cy.getByTestId('dialog-title').should('have.text', 'Asset details - tEURO');
|
||||
cy.getByTestId('close-asset-details-dialog').click();
|
||||
});
|
||||
|
||||
it('can open row actions', () => {
|
||||
// 6001-MARK-044
|
||||
cy.get('.ag-pinned-right-cols-container')
|
||||
.find('[col-id="market-actions"]')
|
||||
.first()
|
||||
.find('button')
|
||||
.click();
|
||||
|
||||
// 6001-MARK-045
|
||||
const dropdownContent = '[data-testid="market-actions-content"]';
|
||||
const dropdownContentItem = '[role="menuitem"]';
|
||||
cy.get(dropdownContent)
|
||||
.find(dropdownContentItem)
|
||||
.eq(0)
|
||||
// Cannot click the copy button as it falls back to window.prompt, blocking the test.
|
||||
.should('have.text', 'Copy Market ID');
|
||||
|
||||
// 6001-MARK-046
|
||||
cy.get(dropdownContent)
|
||||
.find(dropdownContentItem)
|
||||
.eq(1)
|
||||
.find('a')
|
||||
.then(($el) => {
|
||||
const href = $el.attr('href');
|
||||
expect(/\/markets\/market-1/.test(href || '')).to.equal(true);
|
||||
})
|
||||
.should('have.text', 'View on Explorer');
|
||||
|
||||
// 6001-MARK-047
|
||||
cy.get(dropdownContent)
|
||||
.find(dropdownContentItem)
|
||||
.eq(2)
|
||||
.should('have.text', 'View settlement asset details');
|
||||
cy.getByTestId('market-actions-content').click();
|
||||
});
|
||||
|
||||
it('able to open and sort full market list - market page', () => {
|
||||
// 6001-MARK-064
|
||||
const ExpectedSortedMarkets = [
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'SOLUSD',
|
||||
];
|
||||
cy.get('[data-testid="Open markets"]').click({ force: true });
|
||||
cy.url().should('eq', Cypress.config('baseUrl') + '/#/markets/all');
|
||||
cy.contains('AAPL.MF21').should('be.visible');
|
||||
cy.get('.ag-header-cell-label').contains('Market').click(); // sort by market name
|
||||
for (let i = 0; i < ExpectedSortedMarkets.length; i++) {
|
||||
cy.get(`[row-index=${i}]`)
|
||||
.find(colInstrumentCode)
|
||||
.should('have.text', ExpectedSortedMarkets[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it('can drag and drop columns', () => {
|
||||
// 6001-MARK-065
|
||||
cy.get('.ag-overlay-loading-wrapper').should('not.be.visible');
|
||||
cy.get(colInstrumentCode)
|
||||
.realMouseDown()
|
||||
.realMouseMove(700, 15)
|
||||
.realMouseUp();
|
||||
cy.get(colInstrumentCode).should(($element) => {
|
||||
const attributeValue = $element.attr('aria-colindex');
|
||||
expect(attributeValue).not.to.equal('1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('no open markets', { tags: '@smoke', testIsolation: true }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
const markets: MarketsQuery = {};
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Markets', markets);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/all');
|
||||
});
|
||||
|
||||
it.skip('can see no markets message', () => {
|
||||
// 6001-MARK-048
|
||||
cy.getByTestId('tab-open-markets').should('contain.text', 'No markets');
|
||||
});
|
||||
});
|
||||
@@ -1,130 +0,0 @@
|
||||
import {
|
||||
AuctionTrigger,
|
||||
MarketState,
|
||||
MarketTradingMode,
|
||||
} from '@vegaprotocol/types';
|
||||
|
||||
describe('markets selector', { tags: '@smoke' }, () => {
|
||||
const list = 'market-selector-list';
|
||||
const searchInput = 'search-term';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.window().then((window) => {
|
||||
window.localStorage.setItem('marketId', 'market-1');
|
||||
});
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage(
|
||||
MarketState.STATE_ACTIVE,
|
||||
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/');
|
||||
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
});
|
||||
|
||||
// 6001-MARK-066
|
||||
it('can open popover to view markets', () => {
|
||||
cy.getByTestId('market-selector').should('not.exist');
|
||||
cy.getByTestId('header-title').should('be.visible').click();
|
||||
cy.getByTestId('market-selector').should('be.visible');
|
||||
});
|
||||
|
||||
// need function keyword as we need 'this' to access market data
|
||||
it('displays data as expected', () => {
|
||||
// TODO: load data from mocks in. Using alias and wrap intermittently fails
|
||||
const data = [
|
||||
{
|
||||
code: 'SOLUSD',
|
||||
markPrice: '84.41',
|
||||
vol: '0.00',
|
||||
},
|
||||
{
|
||||
code: 'ETHBTC.QM21',
|
||||
markPrice: '46,126.90058',
|
||||
vol: '0.00',
|
||||
},
|
||||
{
|
||||
code: 'BTCUSD.MF21',
|
||||
markPrice: '46,126.90058',
|
||||
vol: '0.00',
|
||||
},
|
||||
{
|
||||
code: 'AAPL.MF21',
|
||||
markPrice: '46,126.90058',
|
||||
vol: '0.00',
|
||||
},
|
||||
];
|
||||
cy.getByTestId('header-title').should('be.visible').click();
|
||||
cy.getByTestId(list)
|
||||
.find('a')
|
||||
.each((item, i) => {
|
||||
const market = data[i];
|
||||
// 6001-MARK-021
|
||||
// 6001-MARK-022
|
||||
expect(item.find('h3').text()).equals(market.code);
|
||||
expect(
|
||||
item.find('[data-testid="market-selector-volume"]').text()
|
||||
).contains(market.vol);
|
||||
// 6001-MARK-024
|
||||
expect(
|
||||
item.find('[data-testid="market-selector-price"]').text()
|
||||
).contains(market.markPrice);
|
||||
// 6001-MARK-025
|
||||
expect(item.find('[data-testid="sparkline-svg"]')).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
it('can use the filter options', () => {
|
||||
cy.getByTestId('header-title').should('be.visible').click();
|
||||
|
||||
// 6001-MARK-027
|
||||
// product type
|
||||
cy.getByTestId('product-Spot').click();
|
||||
cy.getByTestId(list).contains('Spot markets coming soon.');
|
||||
cy.getByTestId('product-Perpetual').click();
|
||||
cy.getByTestId(list).contains('Perpetual markets coming soon.');
|
||||
cy.getByTestId('product-Future').click();
|
||||
cy.getByTestId(list).find('a').should('have.length', 4);
|
||||
|
||||
// 6001-MARK-029
|
||||
cy.getByTestId(searchInput).clear().type('btc');
|
||||
cy.getByTestId(list).find('a').should('have.length', 2);
|
||||
cy.getByTestId(list).find('a').eq(1).contains('BTCUSD.MF21');
|
||||
cy.getByTestId(list).find('a').eq(0).contains('ETHBTC.QM21');
|
||||
|
||||
cy.getByTestId(searchInput).clear();
|
||||
cy.getByTestId(list).find('a').should('have.length', 4);
|
||||
});
|
||||
|
||||
it('can sort by by top gaining and top losing market', () => {
|
||||
cy.getByTestId('header-title').should('be.visible').click();
|
||||
|
||||
// 6001-MARK-030
|
||||
// 6001-MARK-031
|
||||
// 6001-MARK-032
|
||||
// 6001-MARK-033
|
||||
cy.getByTestId(' sort-trigger').click();
|
||||
cy.getByTestId('sort-item-Gained')
|
||||
.contains('Top gaining')
|
||||
.should('be.visible');
|
||||
cy.getByTestId('sort-item-Lost')
|
||||
.contains('Top losing')
|
||||
.should('be.visible');
|
||||
cy.getByTestId('sort-item-New')
|
||||
.contains('New markets')
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('can filter by settlement asset', () => {
|
||||
cy.getByTestId('header-title').should('be.visible').click();
|
||||
|
||||
// 6001-MARK-028
|
||||
cy.getByTestId('asset-trigger').click();
|
||||
cy.getByTestId('asset-id-asset-3').contains('tBTC').click();
|
||||
cy.getByTestId(list).find('a').should('have.length', 1);
|
||||
cy.getByTestId(list).find('a').eq(0).contains('ETHBTC.QM21');
|
||||
});
|
||||
});
|
||||
@@ -1,227 +0,0 @@
|
||||
import { aliasGQLQuery, checkSorting } from '@vegaprotocol/cypress';
|
||||
import type { ProposalsListQuery } from '@vegaprotocol/proposals';
|
||||
|
||||
const rowSelector =
|
||||
'[data-testid="tab-proposed-markets"] .ag-center-cols-container .ag-row';
|
||||
const colMarketId = '[col-id="market"]';
|
||||
|
||||
describe('markets proposed table', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
});
|
||||
|
||||
it('can see table headers', () => {
|
||||
const headers = [
|
||||
'Market',
|
||||
'Description',
|
||||
'Settlement asset',
|
||||
'State',
|
||||
'Parent market',
|
||||
'Voting',
|
||||
'Closing date',
|
||||
'Enactment date',
|
||||
'',
|
||||
];
|
||||
cy.getByTestId('tab-proposed-markets').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
// 6001-MARK-049
|
||||
cy.get(rowSelector).first().find(colMarketId).should('have.text', 'ETHUSD');
|
||||
|
||||
// 6001-MARK-050
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="description"]')
|
||||
.should('have.text', 'ETHUSD');
|
||||
|
||||
// 6001-MARK-051
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="asset"]')
|
||||
.should('have.text', 'tDAI TEST');
|
||||
|
||||
// 6001-MARK-052
|
||||
// 6001-MARK-053
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="state"]')
|
||||
.should('have.text', 'Open');
|
||||
|
||||
// 6001-MARK-054
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="voting"]')
|
||||
.should('have.text', '');
|
||||
|
||||
// 6001-MARK-056
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="closing-date"]')
|
||||
.should('not.be.empty');
|
||||
|
||||
// 6001-MARK-057
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="enactment-date"]')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('can open row actions', () => {
|
||||
// 6001-MARK-058
|
||||
cy.get('.ag-pinned-right-cols-container')
|
||||
.find('[col-id="proposal-actions"]')
|
||||
.first()
|
||||
.find('button')
|
||||
.click();
|
||||
|
||||
const dropdownContent = '[data-testid="proposal-actions-content"]';
|
||||
const dropdownContentItem = '[role="menuitem"]';
|
||||
|
||||
// 6001-MARK-059
|
||||
cy.get(dropdownContent)
|
||||
.find(dropdownContentItem)
|
||||
.eq(0)
|
||||
.find('a')
|
||||
.should('have.text', 'View proposal')
|
||||
.and(
|
||||
'have.attr',
|
||||
'href',
|
||||
`${Cypress.env(
|
||||
'VEGA_TOKEN_URL'
|
||||
)}/proposals/e9ec6d5c46a7e7bcabf9ba7a893fa5a5eeeec08b731f06f7a6eb7bf0e605b829`
|
||||
);
|
||||
});
|
||||
|
||||
// 6001-MARK-060
|
||||
it('can see proposed market link', () => {
|
||||
cy.getByTestId('tab-proposed-markets')
|
||||
.find('[data-testid="external-link"]')
|
||||
.should('have.length', 11)
|
||||
.last()
|
||||
.should('have.text', 'Propose a new market')
|
||||
.and(
|
||||
'have.attr',
|
||||
'href',
|
||||
`${Cypress.env('VEGA_TOKEN_URL')}/proposals/propose/new-market`
|
||||
);
|
||||
});
|
||||
it('proposed markets tab should be sorted properly', () => {
|
||||
// 6001-MARK-062
|
||||
cy.get('[data-testid="Proposed markets"]').click({ force: true });
|
||||
const marketColDefault = [
|
||||
'ETHUSD',
|
||||
'LINKUSD',
|
||||
'ETHUSD',
|
||||
'ETHDAI.MF21',
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'TSLA.QM21',
|
||||
'AAVEDAI.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'UNIDAI.MF21',
|
||||
];
|
||||
const marketColAsc = [
|
||||
'AAPL.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'ETHDAI.MF21',
|
||||
'ETHUSD',
|
||||
'ETHUSD',
|
||||
'LINKUSD',
|
||||
'TSLA.QM21',
|
||||
'UNIDAI.MF21',
|
||||
];
|
||||
const marketColDesc = [
|
||||
'UNIDAI.MF21',
|
||||
'TSLA.QM21',
|
||||
'LINKUSD',
|
||||
'ETHUSD',
|
||||
'ETHUSD',
|
||||
'ETHDAI.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'BTCUSD.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'AAPL.MF21',
|
||||
];
|
||||
checkSorting('market', marketColDefault, marketColAsc, marketColDesc);
|
||||
|
||||
const stateColDefault = [
|
||||
'Open',
|
||||
'Passed',
|
||||
'Waiting for Node Vote',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Waiting for Node Vote',
|
||||
'Open',
|
||||
];
|
||||
const stateColAsc = [
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Waiting for Node Vote',
|
||||
'Waiting for Node Vote',
|
||||
];
|
||||
const stateColDesc = [
|
||||
'Waiting for Node Vote',
|
||||
'Waiting for Node Vote',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
];
|
||||
checkSorting('state', stateColDefault, stateColAsc, stateColDesc);
|
||||
});
|
||||
|
||||
it('can drag and drop columns', () => {
|
||||
// 6001-MARK-063
|
||||
cy.get(colMarketId).realMouseDown().realMouseMove(700, 15).realMouseUp();
|
||||
cy.get(colMarketId).should(($element) => {
|
||||
const attributeValue = $element.attr('aria-colindex');
|
||||
expect(attributeValue).not.to.equal('1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('no markets proposed', { tags: '@smoke', testIsolation: true }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
const proposal: ProposalsListQuery = {};
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ProposalsList', proposal);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
});
|
||||
|
||||
it('can see no markets message', () => {
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
|
||||
// 6001-MARK-061
|
||||
cy.getByTestId('tab-proposed-markets').should('contain.text', 'No markets');
|
||||
});
|
||||
});
|
||||
@@ -1,30 +0,0 @@
|
||||
describe('Settings page', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/');
|
||||
|
||||
// Only click if not already active otherwise sidebar will close
|
||||
cy.get('[data-testid="sidebar-content"]').then(($sidebarContent) => {
|
||||
if ($sidebarContent.find('h2').text() !== 'Settings') {
|
||||
cy.get('[data-testid="sidebar"] [data-testid="Settings"]').click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('telemetry checkbox should work well', () => {
|
||||
const telemetrySwitch = '#switch-settings-telemetry-switch';
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked');
|
||||
cy.get(telemetrySwitch).click();
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'checked');
|
||||
cy.reload();
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'checked');
|
||||
cy.get(telemetrySwitch).click();
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked');
|
||||
cy.reload();
|
||||
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked');
|
||||
});
|
||||
});
|
||||
@@ -1,186 +0,0 @@
|
||||
interface ItemInfoType {
|
||||
name: string;
|
||||
infoText: string;
|
||||
}
|
||||
|
||||
type CheckMenuItemsFnType = (
|
||||
triggerSelector: string,
|
||||
validTexts: string[],
|
||||
clickItem?: string
|
||||
) => void;
|
||||
type CheckMenuItemCheckboxFnType = (
|
||||
buttonText: string,
|
||||
items: ItemInfoType[]
|
||||
) => void;
|
||||
|
||||
const menuItemRadio = 'div[role="menuitemradio"]';
|
||||
const menuItemCheckbox = 'div[role="menuitemcheckbox"]';
|
||||
const button = 'button';
|
||||
const indicatorInfo = '.indicator-info-wrapper';
|
||||
|
||||
const checkMenuItems: CheckMenuItemsFnType = (
|
||||
triggerSelector,
|
||||
validTexts,
|
||||
clickItem
|
||||
) => {
|
||||
cy.get(triggerSelector).click();
|
||||
|
||||
cy.get(menuItemRadio)
|
||||
.should('have.length', validTexts.length)
|
||||
.each(($el, index) => {
|
||||
const text = $el.text().trim();
|
||||
expect(text).to.equal(validTexts[index]);
|
||||
});
|
||||
|
||||
if (clickItem) {
|
||||
cy.contains(menuItemRadio, clickItem).click();
|
||||
cy.get(triggerSelector).click();
|
||||
cy.get(`${menuItemRadio}[data-state="checked"]`)
|
||||
.invoke('text')
|
||||
.then((text: string) => {
|
||||
expect(text.trim()).to.equal(clickItem);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const checkMenuItemCheckbox: CheckMenuItemCheckboxFnType = (
|
||||
buttonText,
|
||||
items
|
||||
) => {
|
||||
items.forEach((item) => {
|
||||
cy.contains(button, buttonText).click();
|
||||
cy.contains(menuItemCheckbox, item.name).click();
|
||||
});
|
||||
|
||||
cy.contains(button, buttonText).click();
|
||||
cy.get(menuItemCheckbox)
|
||||
.should('have.length', items.length)
|
||||
.each(($el, index) => {
|
||||
const text = $el.text();
|
||||
expect(text).to.equal(items[index].name);
|
||||
});
|
||||
|
||||
items.forEach((item, index) => {
|
||||
cy.get(indicatorInfo)
|
||||
.eq(index + 1)
|
||||
.invoke('text')
|
||||
.should('eq', item.infoText);
|
||||
});
|
||||
|
||||
cy.contains(button, buttonText).click({ force: true });
|
||||
};
|
||||
|
||||
function getButtonSelectorByText(text: string): string {
|
||||
return `${button}[aria-haspopup="menu"]:contains(${text})`;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
describe(
|
||||
'chart display options',
|
||||
{ tags: '@smoke', testIsolation: true },
|
||||
() => {
|
||||
it('change time interval', () => {
|
||||
// 6004-CHAR-001
|
||||
checkMenuItems(
|
||||
getButtonSelectorByText('Interval:'),
|
||||
['1m', '5m', '15m', '1H', '6H', '1D'],
|
||||
'1m'
|
||||
);
|
||||
});
|
||||
|
||||
it('change display type', () => {
|
||||
// 6004-CHAR-002
|
||||
// 6004-CHAR-003
|
||||
checkMenuItems(
|
||||
'[aria-label$="chart icon"]',
|
||||
['Mountain', 'Candlestick', 'Line', 'OHLC'],
|
||||
'Mountain'
|
||||
);
|
||||
});
|
||||
|
||||
it('Overlays', () => {
|
||||
// 6004-CHAR-004
|
||||
// 6004-CHAR-008
|
||||
// 6004-CHAR-009
|
||||
// 6004-CHAR-034
|
||||
// 6004-CHAR-037
|
||||
// 6004-CHAR-039
|
||||
// 6004-CHAR-041
|
||||
|
||||
const overlayInfo: ItemInfoType[] = [
|
||||
{
|
||||
name: 'Bollinger bands',
|
||||
infoText: 'Bollinger: Upper 174.78590Lower 173.38014',
|
||||
},
|
||||
{
|
||||
name: 'Envelope',
|
||||
infoText: 'Envelope: Upper 191.29000Lower 156.51000',
|
||||
},
|
||||
{ name: 'EMA', infoText: 'EMA: 174.06793' },
|
||||
{ name: 'Moving average', infoText: 'Moving average: 174.08302' },
|
||||
{
|
||||
name: 'Price monitoring bounds',
|
||||
infoText:
|
||||
'Price Monitoring Bounds 1: Min 162.56291Max 182.96869Reference 172.47489',
|
||||
},
|
||||
];
|
||||
|
||||
checkMenuItemCheckbox('Overlays', overlayInfo);
|
||||
});
|
||||
|
||||
it('Studies', () => {
|
||||
// 6004-CHAR-005
|
||||
// 6004-CHAR-006
|
||||
// 6004-CHAR-007
|
||||
// 6004-CHAR-042
|
||||
// 6004-CHAR-045
|
||||
// 6004-CHAR-047
|
||||
// 6004-CHAR-049
|
||||
// 6004-CHAR-051
|
||||
const studyInfo: ItemInfoType[] = [
|
||||
{
|
||||
name: 'Eldar-ray',
|
||||
infoText: 'Eldar-ray: Bull -0.08376Bear -0.58376',
|
||||
},
|
||||
{ name: 'Force index', infoText: 'Force index: 987.48858' },
|
||||
{ name: 'MACD', infoText: 'MACD: S -0.06420D 0.00359MACD -0.06062' },
|
||||
{ name: 'RSI', infoText: 'RSI: 47.08648' },
|
||||
{ name: 'Volume', infoText: 'Volume: 55,000' },
|
||||
];
|
||||
cy.get(indicatorInfo).eq(1).realHover();
|
||||
cy.get('.chart__wrapper [data-testid="split-view-view"]')
|
||||
.last()
|
||||
.find('[role="button"][title="Close"]')
|
||||
.click({ force: true });
|
||||
cy.get(indicatorInfo).should('have.length', 1);
|
||||
|
||||
checkMenuItemCheckbox('Studies', studyInfo);
|
||||
});
|
||||
|
||||
it('price details', () => {
|
||||
// 6004-CHAR-010
|
||||
const expectedDateRegex = new RegExp(
|
||||
/^\d{2}:\d{2} \d{2} [A-Za-z]{3} \d{4}$/
|
||||
);
|
||||
const expectedOhlc = `O 173.60000H 174.00000L 173.50000C 173.90000Change −0.60000(−0.34%)`;
|
||||
cy.get(indicatorInfo)
|
||||
.eq(0)
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
const actualDate = text.slice(0, -67);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(actualDate);
|
||||
const actualOhlc = text.slice(-67);
|
||||
assert.isTrue(expectedDateRegex.test(actualDate));
|
||||
assert.strictEqual(actualOhlc, expectedOhlc);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,110 +0,0 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { testOrderSubmission } from '../support/order-validation';
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { createOrder } from '../support/create-order';
|
||||
|
||||
describe('must submit order', { tags: '@smoke' }, () => {
|
||||
// 7002-SORD-039
|
||||
before(() => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
|
||||
it('successfully places market buy order', () => {
|
||||
// 7002-SORD-010
|
||||
// 0003-WTXN-012
|
||||
// 0003-WTXN-003
|
||||
cy.mockVegaWalletTransaction();
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_MARKET,
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||
postOnly: false,
|
||||
reduceOnly: false,
|
||||
size: '100',
|
||||
};
|
||||
createOrder(order);
|
||||
testOrderSubmission(order);
|
||||
});
|
||||
|
||||
it('successfully places market sell order', () => {
|
||||
cy.mockVegaWalletTransaction();
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_MARKET,
|
||||
side: Schema.Side.SIDE_SELL,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
|
||||
size: '100',
|
||||
postOnly: false,
|
||||
reduceOnly: false,
|
||||
};
|
||||
createOrder(order);
|
||||
testOrderSubmission(order);
|
||||
});
|
||||
|
||||
it('successfully places limit buy order', () => {
|
||||
// 7002-SORD-017
|
||||
cy.mockVegaWalletTransaction();
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_LIMIT,
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
|
||||
size: '100',
|
||||
postOnly: false,
|
||||
reduceOnly: false,
|
||||
price: '200',
|
||||
};
|
||||
createOrder(order);
|
||||
testOrderSubmission(order, { price: '20000000' });
|
||||
});
|
||||
|
||||
it('successfully places limit sell order', () => {
|
||||
cy.mockVegaWalletTransaction();
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_LIMIT,
|
||||
side: Schema.Side.SIDE_SELL,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GFN,
|
||||
size: '100',
|
||||
postOnly: false,
|
||||
reduceOnly: false,
|
||||
price: '50000',
|
||||
};
|
||||
createOrder(order);
|
||||
testOrderSubmission(order, { price: '5000000000' });
|
||||
});
|
||||
|
||||
it('successfully places GTT limit buy order', () => {
|
||||
cy.mockVegaWalletTransaction();
|
||||
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000);
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_LIMIT,
|
||||
side: Schema.Side.SIDE_SELL,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTT,
|
||||
size: '100',
|
||||
price: '1.00',
|
||||
expiresAt: expiresAt.toISOString().substring(0, 16),
|
||||
postOnly: false,
|
||||
reduceOnly: false,
|
||||
};
|
||||
|
||||
createOrder(order);
|
||||
testOrderSubmission(order, {
|
||||
price: '100000',
|
||||
expiresAt:
|
||||
new Date(order.expiresAt as string).getTime().toString() + '000000',
|
||||
postOnly: false,
|
||||
reduceOnly: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -23,7 +23,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
// 0003-WTXN-001
|
||||
cy.getByTestId('connect-vega-wallet'); // Not connected
|
||||
cy.getByTestId(placeOrderBtn).should('exist');
|
||||
cy.getByTestId('get-started-button').should('exist');
|
||||
cy.getByTestId('order-connect-wallet').should('exist');
|
||||
});
|
||||
|
||||
it('must be able to select order direction - long/short', function () {
|
||||
@@ -44,7 +44,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
mockConnectWallet();
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
cy.getByTestId(orderPriceField).clear().type('101');
|
||||
cy.getByTestId('get-started-button').click();
|
||||
cy.getByTestId('order-connect-wallet').click();
|
||||
cy.getByTestId('dialog-content').should('be.visible');
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-jsonRpc"]')
|
||||
@@ -54,6 +54,15 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId(toggleLimit).next('input').should('be.checked');
|
||||
cy.getByTestId(orderPriceField).should('have.value', '101');
|
||||
});
|
||||
|
||||
it('sidebar should be open after reload', () => {
|
||||
cy.mockTradingPage();
|
||||
cy.getByTestId('deal-ticket-form').should('be.visible');
|
||||
cy.getByTestId('Order').click();
|
||||
cy.getByTestId('deal-ticket-form').should('not.exist');
|
||||
cy.reload();
|
||||
cy.getByTestId('deal-ticket-form').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe(
|
||||
|
||||
@@ -33,9 +33,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
|
||||
it('must see the price unit', function () {
|
||||
// 7002-SORD-018
|
||||
cy.getByTestId(orderPriceField)
|
||||
.siblings('label')
|
||||
.should('have.text', 'Price (DAI)');
|
||||
cy.getByTestId(orderPriceField).next().should('have.text', 'DAI');
|
||||
});
|
||||
|
||||
it('must see warning when placing an order with expiry date in past', () => {
|
||||
@@ -64,7 +62,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId(orderSizeField).clear().type('1');
|
||||
cy.getByTestId(orderPriceField).clear().type('1.123456');
|
||||
cy.getByTestId(placeOrderBtn).click();
|
||||
cy.getByTestId('deal-ticket-error-message-price-limit').should(
|
||||
cy.getByTestId('deal-ticket-error-message-price').should(
|
||||
'have.text',
|
||||
'Price accepts up to 5 decimal places'
|
||||
);
|
||||
@@ -87,7 +85,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId(orderSizeField).clear().type('1.234');
|
||||
// 7002-SORD-060
|
||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
||||
cy.getByTestId('deal-ticket-error-message-size-market').should(
|
||||
cy.getByTestId('deal-ticket-error-message-size').should(
|
||||
'have.text',
|
||||
'Size must be whole numbers for this market'
|
||||
);
|
||||
@@ -96,7 +94,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
it('must warn if order size is set to 0', function () {
|
||||
cy.getByTestId(orderSizeField).clear().type('0');
|
||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
||||
cy.getByTestId('deal-ticket-error-message-size-market').should(
|
||||
cy.getByTestId('deal-ticket-error-message-size').should(
|
||||
'have.text',
|
||||
'Size cannot be lower than 1'
|
||||
);
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import {
|
||||
accountsQuery,
|
||||
amendGeneralAccountBalance,
|
||||
amendMarginAccountBalance,
|
||||
} from '@vegaprotocol/mock';
|
||||
|
||||
describe.skip(
|
||||
'account validation',
|
||||
{ tags: '@regression', testIsolation: true },
|
||||
() => {
|
||||
describe('zero balance error', () => {
|
||||
beforeEach(() => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
let accounts = accountsQuery();
|
||||
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
|
||||
accounts = amendGeneralAccountBalance(accounts, 'market-0', '0');
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Accounts', accounts);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
it('should show an error if your balance is zero', () => {
|
||||
const accounts = accountsQuery();
|
||||
amendMarginAccountBalance(accounts, 'market-0', '0');
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Accounts', accounts);
|
||||
});
|
||||
// 7002-SORD-060
|
||||
cy.getByTestId('place-order').should('be.enabled');
|
||||
// 7002-SORD-003
|
||||
cy.getByTestId('deal-ticket-error-message-zero-balance').should(
|
||||
'have.text',
|
||||
'You need ' +
|
||||
'tDAI' +
|
||||
' in your wallet to trade in this market. See all your collateral.Make a deposit'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('not enough balance warning', () => {
|
||||
beforeEach(() => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
let accounts = accountsQuery();
|
||||
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
|
||||
accounts = amendGeneralAccountBalance(accounts, 'market-0', '1');
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Accounts', accounts);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
|
||||
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
|
||||
if (!$form.length) {
|
||||
cy.getByTestId('Order').click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should display info and button for deposit', () => {
|
||||
// 7002-SORD-003
|
||||
|
||||
// warning should show immediately
|
||||
cy.getByTestId('deal-ticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'You may not have enough margin available to open this position'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
|
||||
cy.getByTestId('sidebar-content')
|
||||
.find('h2')
|
||||
.eq(0)
|
||||
.should('have.text', 'Deposit');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { createOrder } from '../support/create-order';
|
||||
|
||||
describe(
|
||||
describe.skip(
|
||||
'account validation',
|
||||
{ tags: '@regression', testIsolation: true },
|
||||
() => {
|
||||
|
||||
@@ -120,7 +120,9 @@ describe('orders list', { tags: '@smoke', testIsolation: true }, () => {
|
||||
cy.getByTestId('All').click();
|
||||
|
||||
cy.getByTestId('tab-orders')
|
||||
.get(`.ag-center-cols-container [col-id='${orderSymbol}']`)
|
||||
.get(
|
||||
`.ag-center-cols-container [col-id='${orderSymbol}'] [data-testid="market-code"]`
|
||||
)
|
||||
.should('have.length.at.least', expectedOrderList.length)
|
||||
.then(($symbols) => {
|
||||
const symbolNames: string[] = [];
|
||||
@@ -220,12 +222,17 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
|
||||
|
||||
it('must see a filled order', () => {
|
||||
// 7002-SORD-046
|
||||
// 7003-MORD-020
|
||||
// NOT COVERED: Must be able to see/link to all trades that were created from this order
|
||||
updateOrder({
|
||||
id: orderId,
|
||||
status: Schema.OrderStatus.STATUS_FILLED,
|
||||
});
|
||||
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Filled');
|
||||
cy.get('[col-id="market.tradableInstrument.instrument.code"]').contains(
|
||||
'[title="Future"]',
|
||||
'Futr'
|
||||
);
|
||||
});
|
||||
|
||||
it('must see a rejected order', () => {
|
||||
@@ -445,8 +452,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
|
||||
liquidityProvisionId: null,
|
||||
});
|
||||
cy.get(`[row-id=${orderId}]`)
|
||||
.find('[data-testid="edit"]')
|
||||
.should('have.text', 'Edit')
|
||||
.find('[data-testid="icon-edit"]')
|
||||
.then(($btn) => {
|
||||
cy.wrap($btn).click();
|
||||
cy.getByTestId('dialog-title').should('have.text', 'Edit order');
|
||||
@@ -474,8 +480,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
|
||||
liquidityProvisionId: null,
|
||||
});
|
||||
cy.get(`[row-id=${orderId}]`)
|
||||
.find(`[data-testid="cancel"]`)
|
||||
.should('have.text', 'Cancel')
|
||||
.find(`[data-testid="icon-cross"]`)
|
||||
.then(($btn) => {
|
||||
cy.wrap($btn).click({ force: true });
|
||||
const order: OrderCancellation = {
|
||||
@@ -512,8 +517,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
|
||||
liquidityProvisionId: null,
|
||||
});
|
||||
cy.get(`[row-id=${orderId}]`)
|
||||
.find('[data-testid="edit"]')
|
||||
.should('have.text', 'Edit')
|
||||
.find('[data-testid="icon-edit"]')
|
||||
.then(($btn) => {
|
||||
cy.wrap($btn).click({ force: true });
|
||||
cy.getByTestId('dialog-title').should('have.text', 'Edit order');
|
||||
|
||||
@@ -1,59 +1,25 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { ledgerEntriesQuery } from '@vegaprotocol/mock';
|
||||
import { partyAssetsQuery } from '@vegaprotocol/mock';
|
||||
|
||||
describe('Portfolio page', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'LedgerEntries', ledgerEntriesQuery());
|
||||
aliasGQLQuery(req, 'PartyAssets', partyAssetsQuery());
|
||||
});
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
describe('Ledger entries', () => {
|
||||
it('List should be properly rendered', () => {
|
||||
it('Download form should be properly rendered', () => {
|
||||
// 7007-LEEN-001
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId('"Ledger entries"').click();
|
||||
const headers = [
|
||||
'Sender',
|
||||
'Account type',
|
||||
'Market',
|
||||
'Receiver',
|
||||
'Account type',
|
||||
'Market',
|
||||
'Transfer type',
|
||||
'Quantity',
|
||||
'Asset',
|
||||
'Sender account balance',
|
||||
'Receiver account balance',
|
||||
'Vega time',
|
||||
];
|
||||
cy.getByTestId('tab-ledger-entries').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
.getByTestId('ledger-download-button')
|
||||
.should('be.visible');
|
||||
});
|
||||
cy.get(
|
||||
'[data-testid="tab-ledger-entries"] .ag-center-cols-container .ag-row'
|
||||
).should('have.length', ledgerEntriesQuery().ledgerEntries.edges.length);
|
||||
});
|
||||
|
||||
it('account filters should be callable', () => {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId('"Ledger entries"').click();
|
||||
cy.get('[role="columnheader"][col-id="fromAccountType"]').realHover();
|
||||
cy.get(
|
||||
'[role="columnheader"][col-id="fromAccountType"] .ag-header-cell-menu-button'
|
||||
).click();
|
||||
cy.get('fieldset.ag-simple-filter-body-wrapper')
|
||||
.should('be.visible')
|
||||
.within((fields) => {
|
||||
cy.wrap(fields).find('label').should('have.length', 18);
|
||||
});
|
||||
cy.getByTestId('"Ledger entries"').click();
|
||||
cy.get('fieldset.ag-simple-filter-body-wrapper').should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
import { checkSorting, aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { marketsDataQuery } from '@vegaprotocol/mock';
|
||||
import { positionsQuery } from '@vegaprotocol/mock';
|
||||
|
||||
// #region consts
|
||||
const closePosition = 'close-position';
|
||||
const dialogCloseX = 'dialog-close';
|
||||
const dialogContent = 'dialog-content';
|
||||
const dropDownMenu = 'dropdown-menu';
|
||||
const marketActionsContent = 'position-actions-content';
|
||||
const positions = 'Positions';
|
||||
const tabPositions = 'tab-positions';
|
||||
const toastContent = 'toast-content';
|
||||
const tooltipContent = 'tooltip-content';
|
||||
// #endregion
|
||||
|
||||
describe('positions', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
it('renders positions on trading page', () => {
|
||||
visitAndClickPositions();
|
||||
// 7004-POSI-001
|
||||
// 7004-POSI-002
|
||||
validatePositionsDisplayed();
|
||||
});
|
||||
|
||||
// TODO: move this to sim, its flakey
|
||||
it.skip('renders positions on portfolio page', () => {
|
||||
cy.mockGQL((req) => {
|
||||
const positions = positionsQuery();
|
||||
if (positions.positions?.edges) {
|
||||
positions.positions.edges.push(
|
||||
...positions.positions.edges.map((edge) => ({
|
||||
...edge,
|
||||
node: {
|
||||
...edge.node,
|
||||
party: {
|
||||
...edge.node.party,
|
||||
id: 'vega-1',
|
||||
},
|
||||
},
|
||||
}))
|
||||
);
|
||||
}
|
||||
aliasGQLQuery(req, 'Positions', positions);
|
||||
});
|
||||
visitAndClickPositions();
|
||||
// 7004-POSI-001
|
||||
// 7004-POSI-002
|
||||
validatePositionsDisplayed(true);
|
||||
});
|
||||
|
||||
it('Close my position', () => {
|
||||
visitAndClickPositions();
|
||||
cy.getByTestId(closePosition).first().click();
|
||||
// 7004-POSI-010
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Awaiting confirmation'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
|
||||
it('rows should be displayed despite errors', () => {
|
||||
const errors = [
|
||||
{
|
||||
message:
|
||||
'no market data for market: 9c55fb644c6f7de5422d40d691a62bffd5898384c70135bab29ba1e3e2e5280a',
|
||||
path: ['marketsConnection', 'edges'],
|
||||
extensions: {
|
||||
code: 13,
|
||||
type: 'Internal',
|
||||
},
|
||||
},
|
||||
];
|
||||
const marketData = marketsDataQuery();
|
||||
const edges = marketData.marketsConnection?.edges.map((market) => {
|
||||
const replace =
|
||||
market.node.data?.market.id === 'market-2' ? null : market.node.data;
|
||||
return { ...market, node: { ...market.node, data: replace } };
|
||||
});
|
||||
const overrides = {
|
||||
...marketData,
|
||||
marketsConnection: { ...marketData.marketsConnection, edges },
|
||||
};
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'MarketsData', overrides, errors);
|
||||
});
|
||||
cy.visit('/#/markets/market-0');
|
||||
const emptyCells = [
|
||||
'notional',
|
||||
'markPrice',
|
||||
'currentLeverage',
|
||||
'averageEntryPrice',
|
||||
];
|
||||
cy.getByTestId(tabPositions)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(
|
||||
'[row-id="02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65-market-2"]'
|
||||
)
|
||||
.eq(0)
|
||||
.within(() => {
|
||||
emptyCells.forEach((cell) => {
|
||||
cy.get(`[col-id="${cell}"]`).should('contain.text', '-');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('error message should be displayed', () => {
|
||||
const errors = [
|
||||
{
|
||||
message:
|
||||
'no market data for asset: 9c55fb644c6f7de5422d40d691a62bffd5898384c70135bab29ba1e3e2e5280a',
|
||||
path: ['assets', 'edges'],
|
||||
extensions: {
|
||||
code: 13,
|
||||
type: 'Internal',
|
||||
},
|
||||
},
|
||||
];
|
||||
const overrides = {
|
||||
marketsConnection: { edges: [] },
|
||||
};
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'MarketsData', overrides, errors);
|
||||
});
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.getByTestId(tabPositions).contains('no market data');
|
||||
});
|
||||
|
||||
it('sorting by Market', () => {
|
||||
visitAndClickPositions();
|
||||
const marketsSortedDefault = [
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'SOLUSD',
|
||||
];
|
||||
const marketsSortedAsc = [
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'SOLUSD',
|
||||
];
|
||||
const marketsSortedDesc = [
|
||||
'SOLUSD',
|
||||
'ETHBTC.QM21',
|
||||
'BTCUSD.MF21',
|
||||
'AAPL.MF21',
|
||||
];
|
||||
cy.getByTestId(positions).click();
|
||||
// 7004-POSI-003
|
||||
checkSorting(
|
||||
'marketName',
|
||||
marketsSortedDefault,
|
||||
marketsSortedAsc,
|
||||
marketsSortedDesc
|
||||
);
|
||||
});
|
||||
|
||||
// let elementWidth: number;
|
||||
|
||||
it('Resize column', () => {
|
||||
visitAndClickPositions();
|
||||
cy.get('.ag-overlay-loading-wrapper').should('not.be.visible');
|
||||
cy.get('.ag-header-container').within(() => {
|
||||
cy.get(`[col-id="marketName"]`)
|
||||
.find('.ag-header-cell-resize')
|
||||
.realMouseDown()
|
||||
.realMouseMove(250, 0)
|
||||
.realMouseUp();
|
||||
});
|
||||
|
||||
// 7004-POSI-006
|
||||
cy.get(`[col-id="marketName"]`)
|
||||
.invoke('width')
|
||||
.should('be.greaterThan', 250);
|
||||
});
|
||||
|
||||
// This test depends on the previous one
|
||||
it('Has persisted column widths', () => {
|
||||
const width = 400;
|
||||
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem(
|
||||
'vega_positions_store',
|
||||
JSON.stringify({
|
||||
state: {
|
||||
gridStore: {
|
||||
columnState: [{ colId: 'marketName', width }],
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
visitAndClickPositions();
|
||||
|
||||
// 7004-POSI-012
|
||||
cy.get('.ag-center-cols-container .ag-row')
|
||||
.first()
|
||||
.find('[col-id="marketName"]')
|
||||
.invoke('outerWidth')
|
||||
.should('equal', width);
|
||||
});
|
||||
|
||||
it('Scroll horizontally', () => {
|
||||
visitAndClickPositions();
|
||||
|
||||
cy.get('.ag-header-container').within(() => {
|
||||
cy.get(`[col-id="marketName"]`)
|
||||
.find('.ag-header-cell-resize')
|
||||
.realMouseDown()
|
||||
.realMouseMove(400, 0)
|
||||
.realMouseUp();
|
||||
});
|
||||
cy.get('[col-id="marketName"]').should('be.visible');
|
||||
cy.get('.ag-body-horizontal-scroll-viewport').realMouseWheel({
|
||||
deltaX: 500,
|
||||
});
|
||||
// 7004-POSI-004
|
||||
cy.get('[col-id="unrealisedPNL"]').should('be.visible');
|
||||
});
|
||||
|
||||
it('Drag and drop columns', () => {
|
||||
visitAndClickPositions();
|
||||
cy.get('.ag-overlay-loading-wrapper').should('not.be.visible');
|
||||
cy.get('[col-id="marketName"]')
|
||||
.realMouseDown()
|
||||
.realMouseMove(700, 15)
|
||||
.realMouseUp();
|
||||
|
||||
// 7004-POSI-005
|
||||
cy.get('[col-id="marketName"]').should(($element) => {
|
||||
const attributeValue = $element.attr('aria-colindex');
|
||||
expect(attributeValue).not.to.equal('1');
|
||||
});
|
||||
});
|
||||
|
||||
it('I can see warnings', () => {
|
||||
visitAndClickPositions();
|
||||
|
||||
cy.get('[col-id="openVolume"]')
|
||||
.eq(3)
|
||||
.within(() => {
|
||||
cy.get('[aria-label="warning-sign icon"]')
|
||||
.should('be.visible')
|
||||
.realHover();
|
||||
});
|
||||
// 7004-POSI-011
|
||||
cy.getByTestId(tooltipContent).should('be.visible');
|
||||
});
|
||||
|
||||
it('Positive and Negative color change', () => {
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.getByTestId(positions).click();
|
||||
// 7004-POSI-007
|
||||
cy.get('.ag-center-cols-container').within(() => {
|
||||
assertPNLColor(
|
||||
'[col-id="realisedPNL"]',
|
||||
'text-market-green-600',
|
||||
'text-market-red'
|
||||
);
|
||||
});
|
||||
cy.get('.ag-center-cols-container').within(() => {
|
||||
assertPNLColor(
|
||||
'[col-id="unrealisedPNL"]',
|
||||
'text-market-green-600',
|
||||
'text-market-red'
|
||||
);
|
||||
});
|
||||
cy.get('.ag-center-cols-container').within(() => {
|
||||
assertPNLColor(
|
||||
'[col-id="openVolume"]',
|
||||
'text-market-green-600',
|
||||
'text-market-red'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('View settlement asset', () => {
|
||||
visitAndClickPositions();
|
||||
cy.get('[col-id="asset"]')
|
||||
.eq(3)
|
||||
.within(() => {
|
||||
cy.get('button[type="button"]').click();
|
||||
});
|
||||
// 7004-POSI-008
|
||||
cy.getByTestId(dialogContent).should('be.visible');
|
||||
cy.getByTestId(dialogCloseX).click();
|
||||
cy.getByTestId(dropDownMenu).first().click();
|
||||
cy.getByTestId(marketActionsContent).click();
|
||||
// 7004-POSI-009
|
||||
cy.getByTestId(dialogContent).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
function validatePositionsDisplayed(multiKey = false) {
|
||||
cy.getByTestId('tab-positions').should('be.visible');
|
||||
cy.getByTestId('tab-positions')
|
||||
.get('.ag-center-cols-container .ag-row')
|
||||
.eq(multiKey ? 3 : 1)
|
||||
.within(() => {
|
||||
cy.get('[col-id="marketName"]')
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
|
||||
cy.get('[col-id="openVolume"]').should('not.be.empty');
|
||||
|
||||
// includes average entry price, mark price, realised PNL & leverage
|
||||
cy.getByTestId('flash-cell').should('not.be.empty');
|
||||
|
||||
if (!multiKey) {
|
||||
cy.get('[col-id="currentLeverage"]').should('contain.text', '2,767.3');
|
||||
cy.get('[col-id="marginAccountBalance"]') // margin allocated
|
||||
.should('contain.text', '0.01');
|
||||
}
|
||||
|
||||
cy.get('[col-id="unrealisedPNL"]').should('not.be.empty');
|
||||
cy.get('[col-id="notional"]').should('contain.text', '276,761.40348'); // Total tDAI position
|
||||
cy.get('[col-id="realisedPNL"]').should('contain.text', '2.30'); // Total Realised PNL
|
||||
cy.get('[col-id="unrealisedPNL"]').should('contain.text', '8.95'); // Total Unrealised PNL
|
||||
});
|
||||
|
||||
cy.get('.ag-header-row [col-id="notional"]')
|
||||
.should('contain.text', 'Notional')
|
||||
.realHover();
|
||||
cy.get('.ag-popup').should('contain.text', 'Mark price x open volume');
|
||||
|
||||
cy.getByTestId('close-position').should('be.visible').and('have.length', 3);
|
||||
}
|
||||
|
||||
function assertPNLColor(
|
||||
pnlSelector: string,
|
||||
positiveClass: string,
|
||||
negativeClass: string
|
||||
) {
|
||||
cy.get(pnlSelector).each(($el) => {
|
||||
const value = parseFloat($el.text());
|
||||
|
||||
if (value > 0) {
|
||||
cy.wrap($el).invoke('attr', 'class').should('contain', positiveClass);
|
||||
} else if (value < 0) {
|
||||
cy.wrap($el).invoke('attr', 'class').should('contain', negativeClass);
|
||||
} else if (value == 0) {
|
||||
cy.wrap($el)
|
||||
.invoke('attr', 'class')
|
||||
.should('not.contain', negativeClass, positiveClass);
|
||||
} else {
|
||||
throw new Error('Unexpected value');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function visitAndClickPositions() {
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.getByTestId(positions).click();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user