Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
536c493a52 | ||
|
|
bd371ac6bb | ||
|
|
f9b154119d | ||
|
|
747d75c223 | ||
|
|
14928d318d | ||
|
|
a19ea1c939 | ||
|
|
c65c296db2 | ||
|
|
6c5cd85d96 | ||
|
|
da0c8d3720 | ||
|
|
8efadda98c | ||
|
|
0872a14f44 | ||
|
|
13817e4d57 | ||
|
|
9da704c117 | ||
|
|
d262650258 | ||
|
|
30da1663eb | ||
|
|
79cbe62774 | ||
|
|
df11401b69 | ||
|
|
2f0be0bf34 | ||
|
|
5b5802104e | ||
|
|
3a149ddfb2 | ||
|
|
0e519f4d0e | ||
|
|
ff2e2574f6 | ||
|
|
9b52cecf21 | ||
|
|
478cc9e753 | ||
|
|
18f1fbd56d | ||
|
|
ccb22f02c3 | ||
|
|
015d8f51c0 | ||
|
|
e2a72cb395 | ||
|
|
0669696b6f | ||
|
|
56c662e5ba | ||
|
|
7fe269fad6 | ||
|
|
b761023069 | ||
|
|
7453acd632 | ||
|
|
1f507facba | ||
|
|
9d6b34963a | ||
|
|
7ac3a68ac9 | ||
|
|
2640ccb20a | ||
|
|
77c0894501 |
@@ -10,7 +10,6 @@ on:
|
||||
- opened
|
||||
- ready_for_review
|
||||
- reopened
|
||||
- edited
|
||||
- synchronize
|
||||
jobs:
|
||||
node-modules:
|
||||
@@ -44,13 +43,6 @@ jobs:
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: yarn install --pure-lockfile
|
||||
|
||||
lint-pr-title:
|
||||
needs: node-modules
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
name: Verify PR title
|
||||
uses: ./.github/workflows/lint-pr.yml
|
||||
secrets: inherit
|
||||
|
||||
lint-format:
|
||||
timeout-minutes: 20
|
||||
needs: node-modules
|
||||
|
||||
@@ -9,23 +9,37 @@ on:
|
||||
github-sha:
|
||||
required: true
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
console-test-branch:
|
||||
type: choice
|
||||
description: 'main: v0.72.14, develop: v0.73.0-preview7'
|
||||
options:
|
||||
- main
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
name: run-tests
|
||||
runs-on: console-test
|
||||
timeout-minutes: 40
|
||||
runs-on: 8-cores
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
#----------------------------------------------
|
||||
# check-out frontend-monorepo
|
||||
#----------------------------------------------
|
||||
- name: Checkout console test repo
|
||||
- name: Checkout frontend-monorepo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.github-sha }}
|
||||
ref: ${{ inputs.github-sha || github.sha }}
|
||||
#----------------------------------------------
|
||||
# cache node modules
|
||||
#----------------------------------------------
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
cache: yarn
|
||||
|
||||
- name: Cache node modules
|
||||
id: cache
|
||||
uses: actions/cache@v3
|
||||
@@ -68,7 +82,18 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: vegaprotocol/console-test
|
||||
ref: ${{ inputs.console-test-branch }}
|
||||
path: './console-test'
|
||||
|
||||
- name: Load console test envs
|
||||
id: console-test-env
|
||||
uses: falti/dotenv-action@v1.0.4
|
||||
with:
|
||||
path: './console-test/.env.${{ inputs.console-test-branch }}'
|
||||
export-variables: true
|
||||
keys-case: upper
|
||||
log-variables: true
|
||||
|
||||
#----------------------------------------------
|
||||
# install dependencies
|
||||
#----------------------------------------------
|
||||
@@ -96,8 +121,7 @@ jobs:
|
||||
#----------------------------------------------
|
||||
- name: Install vega binaries
|
||||
working-directory: ./console-test
|
||||
if: steps.vega_binaries_cache.outputs.cache-hit != 'true'
|
||||
run: poetry run python -m vega_sim.tools.load_binaries --force --version $VEGA_VERSION
|
||||
run: poetry run python -m vega_sim.tools.load_binaries --force --version ${{ env.VEGA_VERSION }}
|
||||
#----------------------------------------------
|
||||
# install playwright
|
||||
#----------------------------------------------
|
||||
@@ -109,7 +133,7 @@ jobs:
|
||||
#----------------------------------------------
|
||||
- name: Run tests
|
||||
working-directory: ./console-test
|
||||
run: poetry run pytest -v -s --numprocesses 2 --dist loadfile --durations=20
|
||||
run: poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
|
||||
- name: Check files
|
||||
run: |
|
||||
ls -al .
|
||||
@@ -124,3 +148,13 @@ jobs:
|
||||
name: playwright-trace
|
||||
path: ./traces/
|
||||
retention-days: 15
|
||||
#----------------------------------------------
|
||||
# ----- upload logs -----
|
||||
#----------------------------------------------
|
||||
- name: Upload worker logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: worker-logs
|
||||
path: ./logs/
|
||||
retention-days: 15
|
||||
|
||||
@@ -13,13 +13,35 @@ on:
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
runner-choice:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
runner: ${{ steps.step.outputs.runner }}
|
||||
steps:
|
||||
- name: Check branch
|
||||
id: step
|
||||
run: |
|
||||
if [ ${{ github.base_ref }} == 'main' ]; then
|
||||
echo "runner=mainnet-compatible-runner" >> $GITHUB_OUTPUT
|
||||
elif [ ${{ github.base_ref }} == 'develop' ] && [ ${{ github.ref_name }} == 'main' ]; then
|
||||
echo "runner=mainnet-compatible-runner" >> $GITHUB_OUTPUT
|
||||
elif [ ${{ github.event_name }} == 'push' ] && [ ${{ contains(github.ref_name, 'release/mainnet') }} ]; then
|
||||
echo "runner=mainnet-compatible-runner" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "runner=self-hosted-runner" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Print runner
|
||||
run: echo ${{ steps.step.outputs.runner }}
|
||||
|
||||
e2e:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
project: ${{ fromJSON(inputs.projects) }}
|
||||
name: ${{ matrix.project }}
|
||||
runs-on: self-hosted-runner
|
||||
needs: runner-choice
|
||||
runs-on: ${{ needs.runner-choice.outputs.runner }}
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
# Checks if skip cache was requested
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
name: Verify PR title
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- reopened
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
lint_pr:
|
||||
@@ -11,21 +16,16 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
cache: yarn
|
||||
node-version: 16
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm package.json
|
||||
npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx
|
||||
|
||||
- name: Check PR title
|
||||
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
|
||||
|
||||
@@ -30,12 +30,18 @@ jobs:
|
||||
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
|
||||
echo IS_S3_RELEASE=false >> $GITHUB_ENV
|
||||
echo IS_DEV_IMAGE=false >> $GITHUB_ENV
|
||||
echo IS_MAIN_IMAGE=false >> $GITHUB_ENV
|
||||
|
||||
- name: Is dev image
|
||||
if: ${{ contains(github.ref, 'develop') && github.event_name == 'push' && matrix.app == 'trading' }}
|
||||
if: ${{ github.ref_name == 'develop' && github.event_name == 'push' && matrix.app == 'trading' }}
|
||||
run: |
|
||||
echo IS_DEV_IMAGE=true >> $GITHUB_ENV
|
||||
|
||||
- name: Is main image
|
||||
if: ${{ github.ref_name == 'main' && github.event_name == 'push' && matrix.app == 'trading' }}
|
||||
run: |
|
||||
echo IS_MAIN_IMAGE=true >> $GITHUB_ENV
|
||||
|
||||
- name: Is PR
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
@@ -81,7 +87,7 @@ jobs:
|
||||
|
||||
- name: Log in to the Container registry (docker hub)
|
||||
uses: docker/login-action@v2
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' || env.IS_MAIN_IMAGE == 'true' }}
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -179,7 +185,7 @@ jobs:
|
||||
uses: docker/build-push-action@v3
|
||||
continue-on-error: true
|
||||
id: dockerhub-push
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' || env.IS_MAIN_IMAGE == 'true' }}
|
||||
with:
|
||||
context: .
|
||||
file: docker/node-outside-docker.Dockerfile
|
||||
@@ -189,7 +195,7 @@ jobs:
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
tags: |
|
||||
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
|
||||
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || '' }}
|
||||
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || env.IS_MAIN_IMAGE == 'true && main' || '' }}
|
||||
|
||||
- name: Publish dist as docker image (ghcr - retry)
|
||||
uses: docker/build-push-action@v3
|
||||
@@ -216,7 +222,7 @@ jobs:
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
tags: |
|
||||
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
|
||||
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
|
||||
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || env.IS_MAIN_IMAGE == 'true && main' || '' }}
|
||||
|
||||
# bucket creation in github.com/vegaprotocol/terraform//frontend
|
||||
- name: Publish dist to s3
|
||||
|
||||
@@ -87,7 +87,6 @@ context('Market page', { tags: '@regression' }, function () {
|
||||
// Liquidity
|
||||
cy.validate_element_from_table('Target Stake', '0.00 fUSDC');
|
||||
cy.validate_element_from_table('Supplied Stake', '0.00 fUSDC');
|
||||
cy.validate_element_from_table('Market Value Proxy', '0.00 fUSDC');
|
||||
// Liquidity price range
|
||||
cy.validate_element_from_table(
|
||||
'Liquidity Price Range',
|
||||
@@ -143,11 +142,8 @@ context('Market page', { tags: '@regression' }, function () {
|
||||
.as('successorMarketId');
|
||||
cy.contains('Token test market').click();
|
||||
cy.getByTestId(marketHeaders).should('have.text', 'Token test market');
|
||||
cy.validate_proposal_change_type('Triggering Ratio', 'Added');
|
||||
cy.validate_element_from_table('Triggering Ratio', '0.7');
|
||||
cy.validate_proposal_change_type('Time Window', 'Added');
|
||||
cy.validate_element_from_table('Time Window', '3,600');
|
||||
cy.validate_proposal_change_type('Scaling Factor', 'Added');
|
||||
cy.validate_element_from_table('Scaling Factor', '10');
|
||||
|
||||
cy.getByTestId(successionLineItem)
|
||||
|
||||
@@ -129,6 +129,12 @@ function getSuccessorTxBody(parentMarketId) {
|
||||
parentMarketId: parentMarketId,
|
||||
insurancePoolFraction: '0.75',
|
||||
},
|
||||
liquiditySlaParameters: {
|
||||
priceRange: '0.95',
|
||||
commitmentMinTimeFraction: '0.5',
|
||||
performanceHysteresisEpochs: 2,
|
||||
slaCompetitionFactor: '0.75',
|
||||
},
|
||||
},
|
||||
},
|
||||
closingTimestamp,
|
||||
|
||||
@@ -49,6 +49,37 @@ fragment ExplorerOracleDataSource on OracleSpec {
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on EthCallSpec {
|
||||
abi
|
||||
args
|
||||
method
|
||||
requiredConfirmations
|
||||
address
|
||||
normalisers {
|
||||
name
|
||||
expression
|
||||
}
|
||||
trigger {
|
||||
trigger {
|
||||
... on EthTimeTrigger {
|
||||
initial
|
||||
every
|
||||
until
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
|
||||
+34
-3
@@ -5,19 +5,19 @@ import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
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 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?: 'EthCallSpec' } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } } } }, 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 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?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | 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> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } } } }, 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, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec' } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } } } }, 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 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?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | 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> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } } } }, 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, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec' } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } } } }, 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 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?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | 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> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } } } }, 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 {
|
||||
@@ -72,6 +72,37 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on EthCallSpec {
|
||||
abi
|
||||
args
|
||||
method
|
||||
requiredConfirmations
|
||||
address
|
||||
normalisers {
|
||||
name
|
||||
expression
|
||||
}
|
||||
trigger {
|
||||
trigger {
|
||||
... on EthTimeTrigger {
|
||||
initial
|
||||
every
|
||||
until
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
|
||||
@@ -21,6 +21,8 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
|
||||
@@ -4,3 +4,5 @@ NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
|
||||
@@ -4,3 +4,5 @@ NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
NX_PRODUCT_PERPETUALS=false
|
||||
NX_UPDATE_MARKET_STATE=false
|
||||
|
||||
@@ -4,3 +4,5 @@ NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"rationale": {
|
||||
"title": "Add USDT Coin (USDT)",
|
||||
"description": "Proposal to add USDT Coin (USDT) as an asset"
|
||||
},
|
||||
"terms": {
|
||||
"newAsset": {
|
||||
"changes": {
|
||||
"name": "USDT Coin",
|
||||
"symbol": "USDT",
|
||||
"decimals": "18",
|
||||
"quantum": "1",
|
||||
"erc20": {
|
||||
"contractAddress": "0xb404c51bbc10dcbe948077f18a4b8e553d160084"
|
||||
}
|
||||
}
|
||||
},
|
||||
"closingTimestamp": 1662374250,
|
||||
"enactmentTimestamp": 1662460650
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
{
|
||||
"rationale": {
|
||||
"description": "## Summary\n\nThis proposal requests to list BTC PERPS Incentive as a market with USD-P as a settlement asset on the Vega Network as discussed in: https://community.vega.xyz/.\n\n## Rationale\n\n- BTC is the largest Crypto asset with the highest volume and Marketcap.\n- Given the price, 1 decimal places will be used for price due to the number of valid digits in asset price. \n- Position decimal places will be set to 4 considering the value per contract\n- USDT is chosen as settlement asset due to its stability.",
|
||||
"title": "perpetual market proposal"
|
||||
},
|
||||
"terms": {
|
||||
"closingTimestamp": 0,
|
||||
"enactmentTimestamp": 0,
|
||||
"newMarket": {
|
||||
"changes": {
|
||||
"instrument": {
|
||||
"name": "Token test market",
|
||||
"code": "TEST.24h",
|
||||
"perpetual": {
|
||||
"clampLowerBound": "0",
|
||||
"clampUpperBound": "0",
|
||||
"interestRate": "0",
|
||||
"marginFundingFactor": "0.1",
|
||||
"settlementAsset": "73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0",
|
||||
"quoteName": "fBTC",
|
||||
"dataSourceSpecForSettlementData": {
|
||||
"external": {
|
||||
"ethOracle": {
|
||||
"address": "0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43",
|
||||
"abi": "[{\"inputs\":[],\"name\":\"latestAnswer\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
|
||||
"method": "latestAnswer",
|
||||
"normalisers": [
|
||||
{
|
||||
"name": "btc.price",
|
||||
"expression": "$[0]"
|
||||
}
|
||||
],
|
||||
"requiredConfirmations": 3,
|
||||
"trigger": {
|
||||
"timeTrigger": {
|
||||
"every": 30
|
||||
}
|
||||
},
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "btc.price",
|
||||
"type": "TYPE_INTEGER",
|
||||
"numberDecimalPlaces": 8
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN_OR_EQUAL",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataSourceSpecForSettlementSchedule": {
|
||||
"internal": {
|
||||
"timeTrigger": {
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN_OR_EQUAL",
|
||||
"value": "0"
|
||||
}
|
||||
],
|
||||
"triggers": [
|
||||
{
|
||||
"every": 1800
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataSourceSpecBinding": {
|
||||
"settlementDataProperty": "btc.price",
|
||||
"settlementScheduleProperty": "vegaprotocol.builtin.timetrigger"
|
||||
}
|
||||
}
|
||||
},
|
||||
"metadata": [
|
||||
"base:BTC",
|
||||
"quote:USD-P",
|
||||
"class:fx/crypto",
|
||||
"quarterly",
|
||||
"sector:defi",
|
||||
"enactment:2023-06-15T14:00:00Z",
|
||||
"settlement:2023-09-30T08:00:00Z"
|
||||
],
|
||||
"priceMonitoringParameters": {
|
||||
"triggers": [
|
||||
{
|
||||
"horizon": "3600",
|
||||
"probability": "0.9999",
|
||||
"auctionExtension": "120"
|
||||
},
|
||||
{
|
||||
"horizon": "14400",
|
||||
"probability": "0.9999",
|
||||
"auctionExtension": "180"
|
||||
},
|
||||
{
|
||||
"horizon": "43200",
|
||||
"probability": "0.9999",
|
||||
"auctionExtension": "300"
|
||||
}
|
||||
]
|
||||
},
|
||||
"liquidityMonitoringParameters": {
|
||||
"targetStakeParameters": {
|
||||
"timeWindow": "3600",
|
||||
"scalingFactor": 1
|
||||
},
|
||||
"triggeringRatio": "0.7",
|
||||
"auctionExtension": "1"
|
||||
},
|
||||
|
||||
"liquiditySlaParameters": {
|
||||
"priceRange": "0.05",
|
||||
"commitmentMinTimeFraction": "0.95",
|
||||
"performanceHysteresisEpochs": 1,
|
||||
"slaCompetitionFactor": "0.95"
|
||||
},
|
||||
|
||||
"logNormal": {
|
||||
"riskAversionParameter": 0.000001,
|
||||
"tau": 0.0001140771161,
|
||||
"params": {
|
||||
"sigma": 1.5
|
||||
}
|
||||
},
|
||||
"decimalPlaces": "1",
|
||||
"positionDecimalPlaces": "4",
|
||||
"linearSlippageFactor": "0.001",
|
||||
"quadraticSlippageFactor": "0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"rationale": {
|
||||
"title": "Market resume test",
|
||||
"description": "E2E test for market resume proposal"
|
||||
},
|
||||
"terms": {
|
||||
"updateMarketState": {
|
||||
"changes": {
|
||||
"marketId": "b33bb4157e12355db22e41f277ddd0c10104dec29a4d6960bbcb96d186c40cbd",
|
||||
"updateType": "MARKET_STATE_UPDATE_TYPE_RESUME"
|
||||
}
|
||||
},
|
||||
"closingTimestamp": 0,
|
||||
"enactmentTimestamp": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"rationale": {
|
||||
"title": "Market suspended test",
|
||||
"description": "E2E test for market suspended proposal"
|
||||
},
|
||||
"terms": {
|
||||
"updateMarketState": {
|
||||
"changes": {
|
||||
"marketId": "",
|
||||
"updateType": "MARKET_STATE_UPDATE_TYPE_SUSPEND"
|
||||
}
|
||||
},
|
||||
"closingTimestamp": 0,
|
||||
"enactmentTimestamp": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"rationale": {
|
||||
"title": "Market terminate test",
|
||||
"description": "E2E test for market terminate proposal"
|
||||
},
|
||||
"terms": {
|
||||
"updateMarketState": {
|
||||
"changes": {
|
||||
"marketId": "",
|
||||
"updateType": "MARKET_STATE_UPDATE_TYPE_TERMINATE",
|
||||
"price": "100"
|
||||
}
|
||||
},
|
||||
"closingTimestamp": 0,
|
||||
"enactmentTimestamp": 0
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
{
|
||||
"instrument": {
|
||||
"code": "Token.24h",
|
||||
"future": {
|
||||
"quoteName": "fBTC",
|
||||
"dataSourceSpecForSettlementData": {
|
||||
"external": {
|
||||
"oracle": {
|
||||
"signers": [
|
||||
{
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "prices.BTC.value",
|
||||
"type": "TYPE_INTEGER"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataSourceSpecForTradingTermination": {
|
||||
"external": {
|
||||
"oracle": {
|
||||
"signers": [
|
||||
{
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "trading.terminated.ETH5",
|
||||
"type": "TYPE_BOOLEAN"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN_OR_EQUAL",
|
||||
"value": "1648684800000000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataSourceSpecBinding": {
|
||||
"settlementDataProperty": "prices.BTC.value",
|
||||
"tradingTerminationProperty": "trading.terminated.ETH5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"metadata": ["sector:energy", "sector:food", "source:docs.vega.xyz"],
|
||||
"priceMonitoringParameters": {
|
||||
"triggers": [
|
||||
{
|
||||
"horizon": "43200",
|
||||
"probability": "0.9999999",
|
||||
"auctionExtension": "600"
|
||||
}
|
||||
]
|
||||
},
|
||||
"logNormal": {
|
||||
"tau": 0.0001140771161,
|
||||
"riskAversionParameter": 0.001,
|
||||
"params": {
|
||||
"mu": 0,
|
||||
"r": 0.016,
|
||||
"sigma": 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
createTenDigitUnixTimeStampForSpecifiedDays,
|
||||
generateFreeFormProposalTitle,
|
||||
getDateFormatForSpecifiedDays,
|
||||
getProposalDetailsValue,
|
||||
getProposalFromTitle,
|
||||
getProposalInformationFromTable,
|
||||
goToMakeNewProposal,
|
||||
@@ -50,6 +51,7 @@ const openProposals = 'open-proposals';
|
||||
const viewProposalButton = 'view-proposal-btn';
|
||||
const proposalTermsToggle = 'proposal-json-toggle';
|
||||
const marketDataToggle = 'proposal-market-data-toggle';
|
||||
const marketProposalType = 'proposal-type';
|
||||
|
||||
describe(
|
||||
'Governance flow for proposal details',
|
||||
@@ -58,6 +60,17 @@ describe(
|
||||
before('connect wallets and set approval limit', function () {
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
cy.createMarket();
|
||||
navigateTo(navigation.proposals);
|
||||
cy.getByTestId('closed-proposals').within(() => {
|
||||
cy.contains('Add Lorem Ipsum market')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
});
|
||||
getProposalInformationFromTable('ID').invoke('text').as('parentMarketId');
|
||||
});
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
@@ -296,9 +309,6 @@ describe(
|
||||
});
|
||||
|
||||
it('Able to see successor market details with new and updated values', function () {
|
||||
cy.createMarket();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.getByTestId('closed-proposals').within(() => {
|
||||
cy.contains('Add Lorem Ipsum market')
|
||||
.parentsUntil(proposalListItem)
|
||||
@@ -307,14 +317,9 @@ describe(
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
});
|
||||
getProposalInformationFromTable('ID')
|
||||
.invoke('text')
|
||||
.as('parentMarketId')
|
||||
.then(() => {
|
||||
cy.VegaWalletSubmitProposal(
|
||||
createSuccessorMarketProposalTxBody(this.parentMarketId)
|
||||
);
|
||||
});
|
||||
cy.VegaWalletSubmitProposal(
|
||||
createSuccessorMarketProposalTxBody(this.parentMarketId)
|
||||
);
|
||||
navigateTo(navigation.proposals);
|
||||
cy.reload();
|
||||
getProposalFromTitle('Test successor market proposal details').within(
|
||||
@@ -372,19 +377,147 @@ describe(
|
||||
});
|
||||
|
||||
// 3003-PMAN-011
|
||||
cy.get('.underline').contains('Parent Market ID').realHover();
|
||||
cy.contains('Parent Market ID').realHover();
|
||||
cy.getByTestId('tooltip-content', { timeout: 8000 }).should(
|
||||
'contain.text',
|
||||
'The ID of the market this market succeeds.'
|
||||
);
|
||||
cy.get('.underline')
|
||||
.contains('Insurance Pool Fraction')
|
||||
.realMouseUp()
|
||||
.realHover();
|
||||
cy.contains('Insurance Pool Fraction').realMouseUp().realHover();
|
||||
cy.getByTestId('tooltip-content', { timeout: 8000 }).should(
|
||||
'contain.text',
|
||||
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
|
||||
);
|
||||
});
|
||||
|
||||
it('Able to see perpetual market', function () {
|
||||
const proposalPath =
|
||||
'src/fixtures/proposals/new-market-perpetual-raw.json';
|
||||
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
closingTimestamp: closingTimestamp,
|
||||
});
|
||||
getProposalFromTitle('perpetual market proposal').within(() => {
|
||||
cy.getByTestId(marketProposalType).should(
|
||||
'have.text',
|
||||
'New market - perpetual'
|
||||
);
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
cy.getByTestId(marketDataToggle).click();
|
||||
getProposalDetailsValue('Product Type').should(
|
||||
'contain.text',
|
||||
'Perpetual'
|
||||
);
|
||||
cy.getByTestId(marketProposalType).should(
|
||||
'have.text',
|
||||
'New market - perpetual'
|
||||
);
|
||||
// Liquidity SLA protocols
|
||||
getProposalDetailsValue('Performance Hysteresis Epochs').should(
|
||||
'contain.text',
|
||||
'1'
|
||||
);
|
||||
getProposalDetailsValue('SLA Competition Factor').should(
|
||||
'contain.text',
|
||||
'95.00%'
|
||||
);
|
||||
getProposalDetailsValue('Epoch Length').should('contain.text', '5s');
|
||||
getProposalDetailsValue('Non Performance Bond Penalty Max').should(
|
||||
'contain.text',
|
||||
'0.05'
|
||||
);
|
||||
getProposalDetailsValue('Stake To CCY Volume').should(
|
||||
'contain.text',
|
||||
'0.3'
|
||||
);
|
||||
getProposalDetailsValue(
|
||||
'Minimum Probability Of Trading LP Orders'
|
||||
).should('contain.text', '1e-8');
|
||||
});
|
||||
|
||||
it('Able to see suspended market proposal', function () {
|
||||
const proposalPath = 'src/fixtures/proposals/suspend-market-raw.json';
|
||||
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
updateMarketId: this.parentMarketId,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
closingTimestamp: closingTimestamp,
|
||||
});
|
||||
getProposalFromTitle('Market suspended test').within(() => {
|
||||
cy.getByTestId(marketProposalType).should(
|
||||
'have.text',
|
||||
'Suspend market'
|
||||
);
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
cy.getByTestId(marketProposalType).should('have.text', 'Suspend market');
|
||||
cy.getByTestId(marketDataToggle).click();
|
||||
cy.getByTestId('proposal-update-market-state').within(() => {
|
||||
getProposalInformationFromTable('Market ID')
|
||||
.invoke('text')
|
||||
.and('eq', this.parentMarketId);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to see resume market proposal', function () {
|
||||
const proposalPath = 'src/fixtures/proposals/resume-market-raw.json';
|
||||
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
updateMarketId: this.parentMarketId,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
closingTimestamp: closingTimestamp,
|
||||
});
|
||||
getProposalFromTitle('Market resume test').within(() => {
|
||||
cy.getByTestId(marketProposalType).should('have.text', 'Resume market');
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
cy.getByTestId(marketProposalType).should('have.text', 'Resume market');
|
||||
cy.getByTestId(marketDataToggle).click();
|
||||
cy.getByTestId('proposal-update-market-state').within(() => {
|
||||
getProposalInformationFromTable('Market ID')
|
||||
.invoke('text')
|
||||
.and('eq', this.parentMarketId);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to see terminate market proposal', function () {
|
||||
const proposalPath = 'src/fixtures/proposals/terminate-market-raw.json';
|
||||
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
updateMarketId: this.parentMarketId,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
closingTimestamp: closingTimestamp,
|
||||
});
|
||||
getProposalFromTitle('Market terminate test').within(() => {
|
||||
cy.getByTestId(marketProposalType).should(
|
||||
'have.text',
|
||||
'Terminate market'
|
||||
);
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
cy.getByTestId(marketProposalType).should(
|
||||
'have.text',
|
||||
'Terminate market'
|
||||
);
|
||||
cy.getByTestId(marketDataToggle).click();
|
||||
cy.getByTestId('proposal-update-market-state').within(() => {
|
||||
getProposalInformationFromTable('Market ID')
|
||||
.invoke('text')
|
||||
.and('eq', this.parentMarketId);
|
||||
getProposalDetailsValue('Termination Price').should(
|
||||
'contain.text',
|
||||
'0.001 fUSDC'
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -65,7 +65,10 @@ context(
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
});
|
||||
cy.getByTestId('proposal-type').should('have.text', 'New market');
|
||||
cy.getByTestId('proposal-type').should(
|
||||
'have.text',
|
||||
'New market - future'
|
||||
);
|
||||
cy.getByTestId(proposalStatus).should('have.text', 'Enacted');
|
||||
cy.getByTestId(votesTable).within(() => {
|
||||
cy.contains('Voting has ended.').should('be.visible');
|
||||
|
||||
@@ -89,11 +89,12 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
|
||||
it('Newly created proposals list - shows title and portion of summary', function () {
|
||||
const proposalPath = 'src/fixtures/proposals/new-market-raw.json';
|
||||
const proposalTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
enactmentTimestamp: proposalTimestamp,
|
||||
closingTimestamp: proposalTimestamp,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
closingTimestamp: closingTimestamp,
|
||||
}); // 3001-VOTE-052
|
||||
// 3001-VOTE-008
|
||||
// 3001-VOTE-034
|
||||
|
||||
@@ -54,6 +54,7 @@ export function submitUniqueRawProposal(proposalFields: {
|
||||
proposalBody?: string;
|
||||
proposalTitle?: string;
|
||||
proposalDescription?: string;
|
||||
updateMarketId?: string;
|
||||
closingTimestamp?: number;
|
||||
enactmentTimestamp?: number;
|
||||
submit?: boolean;
|
||||
@@ -71,6 +72,10 @@ export function submitUniqueRawProposal(proposalFields: {
|
||||
if (proposalFields.proposalDescription) {
|
||||
rawProposal.rationale.description = proposalFields.proposalDescription;
|
||||
}
|
||||
if (proposalFields.updateMarketId) {
|
||||
rawProposal.terms.updateMarketState.changes.marketId =
|
||||
proposalFields.updateMarketId;
|
||||
}
|
||||
if (proposalFields.closingTimestamp) {
|
||||
rawProposal.terms.closingTimestamp = proposalFields.closingTimestamp;
|
||||
} else if (
|
||||
@@ -232,21 +237,25 @@ export function getDownloadedProposalJsonPath(proposalType: string) {
|
||||
return filepath;
|
||||
}
|
||||
|
||||
export function getProposalDetailsValue(RowName: string) {
|
||||
return cy
|
||||
.contains(RowName)
|
||||
.parentsUntil(proposalInformationTableRows)
|
||||
.parent()
|
||||
.first();
|
||||
}
|
||||
|
||||
export function validateProposalDetailsDiff(
|
||||
RowName: string,
|
||||
changeType: proposalChangeType,
|
||||
newValue: string,
|
||||
oldValue?: string
|
||||
) {
|
||||
cy.contains(RowName)
|
||||
.parentsUntil(proposalInformationTableRows)
|
||||
.parent()
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.contains(changeType).should('be.visible');
|
||||
cy.contains(newValue).should('be.visible');
|
||||
if (oldValue) cy.contains(oldValue).should('have.class', 'line-through');
|
||||
});
|
||||
getProposalDetailsValue(RowName).within(() => {
|
||||
cy.contains(changeType).should('be.visible');
|
||||
cy.contains(newValue).should('be.visible');
|
||||
if (oldValue) cy.contains(oldValue).should('have.class', 'line-through');
|
||||
});
|
||||
}
|
||||
|
||||
function getFormattedTime() {
|
||||
|
||||
@@ -32,3 +32,5 @@ LC_ALL="en_US.UTF-8"
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_PRODUCT_PERPETUALS=false
|
||||
NX_UPDATE_MARKET_STATE=false
|
||||
|
||||
@@ -33,3 +33,5 @@ CYPRESS_FAIRGROUND=false
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
|
||||
@@ -25,3 +25,5 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
|
||||
@@ -24,3 +24,5 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_PRODUCT_PERPETUALS=false
|
||||
NX_UPDATE_MARKET_STATE=false
|
||||
|
||||
@@ -23,3 +23,5 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_PRODUCT_PERPETUALS=false
|
||||
NX_UPDATE_MARKET_STATE=false
|
||||
|
||||
@@ -20,3 +20,5 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
|
||||
@@ -25,3 +25,5 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
|
||||
@@ -22,3 +22,5 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_PRODUCT_PERPETUALS=false
|
||||
NX_UPDATE_MARKET_STATE=false
|
||||
|
||||
@@ -308,7 +308,7 @@ const AppContainer = () => {
|
||||
<Router>
|
||||
<ScrollToTop />
|
||||
<AppStateProvider>
|
||||
<div className="min-h-full text-white">
|
||||
<div className="min-h-full text-white grid">
|
||||
<NodeGuard
|
||||
skeleton={<div>{t('Loading')}</div>}
|
||||
failure={
|
||||
|
||||
@@ -613,6 +613,9 @@
|
||||
"proposalDetails": "Proposal details",
|
||||
"marketSpecification": "Market specification",
|
||||
"viewMarketJson": "View market JSON",
|
||||
"marketId": "Market ID",
|
||||
"marketName": "Market name",
|
||||
"marketCode": "Market code",
|
||||
"proposalDescription": "Description",
|
||||
"currentlySetTo": "Currently expected to ",
|
||||
"currently": "currently",
|
||||
@@ -705,10 +708,17 @@
|
||||
"parameter": "parameter",
|
||||
"NewMarketProposal": "New market proposal",
|
||||
"UpdateMarketProposal": "Update market proposal",
|
||||
"UpdateMarketStateProposal": "Update market state proposal",
|
||||
"MarketChange": "Market change",
|
||||
"MarketStateChange": "Market state change",
|
||||
"MarketDetails": "Market details",
|
||||
"NewAssetProposal": "New asset proposal",
|
||||
"UpdateAssetProposal": "Update asset proposal",
|
||||
"NewFreeformProposal": "New freeform proposal",
|
||||
"NewRawProposal": "New proposal",
|
||||
"MARKET_STATE_UPDATE_TYPE_RESUME": "Resume market",
|
||||
"MARKET_STATE_UPDATE_TYPE_SUSPEND": "Suspend market",
|
||||
"MARKET_STATE_UPDATE_TYPE_TERMINATE": "Terminate market",
|
||||
"MinProposalRequirements": "You must have at least {{value}} VEGA associated to make a proposal",
|
||||
"MinProposalVoteRequirements": "You must have at least {{value}} VEGA associated to vote on this proposal",
|
||||
"totalSupply": "Total Supply",
|
||||
@@ -717,7 +727,11 @@
|
||||
"ProposalDocsPrefix": "For guidance on how to make proposals, see",
|
||||
"NetworkParameter": "Network parameter",
|
||||
"NewMarket": "New market",
|
||||
"NewMarketPerpetualProduct": "New market - perpetual",
|
||||
"NewMarketFutureProduct": "New market - future",
|
||||
"NewMarketSpotProduct": "New market - spot",
|
||||
"UpdateMarket": "Update market",
|
||||
"UpdateMarketState": "Update market state",
|
||||
"NewAsset": "New asset",
|
||||
"UpdateAsset": "Update asset",
|
||||
"AssetID": "Asset ID",
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||
import Routes from '../routes';
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
import { ExternalLinks, FLAGS } from '@vegaprotocol/environment';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
import { Heading, SubHeading } from '../../components/heading';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { RouteChildProps } from '..';
|
||||
import type { ProposalFieldsFragment } from '../proposals/proposals/__generated__/Proposals';
|
||||
import type { NodesFragmentFragment } from '../staking/home/__generated__/Nodes';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
|
||||
@@ -32,6 +31,7 @@ import {
|
||||
orderByUpgradeBlockHeight,
|
||||
} from '../proposals/components/proposals-list/proposals-list';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import type { ProposalQuery } from '../proposals/proposal/__generated__/Proposal';
|
||||
|
||||
const nodesToShow = 6;
|
||||
|
||||
@@ -39,7 +39,7 @@ const HomeProposals = ({
|
||||
proposals,
|
||||
protocolUpgradeProposals,
|
||||
}: {
|
||||
proposals: ProposalFieldsFragment[];
|
||||
proposals: ProposalQuery['proposal'][];
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -60,9 +60,12 @@ const HomeProposals = ({
|
||||
<ProtocolUpgradeProposalsListItem key={index} proposal={proposal} />
|
||||
))}
|
||||
|
||||
{proposals.map((proposal) => (
|
||||
<ProposalsListItem key={proposal.id} proposal={proposal} />
|
||||
))}
|
||||
{proposals.map(
|
||||
(proposal) =>
|
||||
proposal?.id && (
|
||||
<ProposalsListItem key={proposal.id} proposal={proposal} />
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
|
||||
<div className="mt-6">
|
||||
@@ -182,6 +185,10 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -206,7 +213,9 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
const proposals = useMemo(
|
||||
() =>
|
||||
proposalsData
|
||||
? getNotRejectedProposals(proposalsData.proposalsConnection)
|
||||
? getNotRejectedProposals(
|
||||
removePaginationWrapper(proposalsData.proposalsConnection?.edges)
|
||||
)
|
||||
: [],
|
||||
[proposalsData]
|
||||
);
|
||||
|
||||
+25
-4
@@ -3,7 +3,6 @@ import { Lozenge, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { shorten } from '@vegaprotocol/utils';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
@@ -20,7 +19,7 @@ export const ProposalHeader = ({
|
||||
isListItem = true,
|
||||
voteState,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
proposal: ProposalQuery['proposal'];
|
||||
isListItem?: boolean;
|
||||
voteState?: VoteState | null;
|
||||
}) => {
|
||||
@@ -37,7 +36,10 @@ export const ProposalHeader = ({
|
||||
|
||||
switch (change?.__typename) {
|
||||
case 'NewMarket': {
|
||||
proposalType = 'NewMarket';
|
||||
proposalType =
|
||||
FLAGS.PRODUCT_PERPETUALS && change?.instrument?.product?.__typename
|
||||
? `NewMarket${change?.instrument?.product?.__typename}`
|
||||
: 'NewMarket';
|
||||
fallbackTitle = t('NewMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
@@ -61,12 +63,31 @@ export const ProposalHeader = ({
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'UpdateMarketState': {
|
||||
proposalType =
|
||||
FLAGS.UPDATE_MARKET_STATE && change?.updateType
|
||||
? t(change.updateType)
|
||||
: 'UpdateMarketState';
|
||||
fallbackTitle = t('UpdateMarketStateProposal');
|
||||
details = (
|
||||
<span>
|
||||
{FLAGS.UPDATE_MARKET_STATE &&
|
||||
change?.market?.id &&
|
||||
change.updateType ? (
|
||||
<>
|
||||
{t(change.updateType)}: {truncateMiddle(change.market.id)}
|
||||
</>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'UpdateMarket': {
|
||||
proposalType = 'UpdateMarket';
|
||||
fallbackTitle = t('UpdateMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
<span>{t('Market change')}:</span>{' '}
|
||||
<span>{t('MarketChange')}:</span>{' '}
|
||||
<span>{truncateMiddle(change.marketId)}</span>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './proposal-update-market-state';
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { ProposalUpdateMarketState } from './proposal-update-market-state';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { MarketUpdateType } from '@vegaprotocol/types';
|
||||
|
||||
describe('<ProposalUpdateMarketState />', () => {
|
||||
const suspendProposal = generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateMarketState',
|
||||
market: {
|
||||
id: '1',
|
||||
decimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'suspendProposal Name',
|
||||
code: 'suspendProposal Code',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: 'USD',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
updateType: MarketUpdateType.MARKET_STATE_UPDATE_TYPE_SUSPEND,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const resumeProposal = generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateMarketState',
|
||||
market: {
|
||||
id: '1',
|
||||
decimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'resumeProposal Name',
|
||||
code: 'resumeProposal Code',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: 'USD',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
updateType: MarketUpdateType.MARKET_STATE_UPDATE_TYPE_RESUME,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const terminateProposal = generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateMarketState',
|
||||
market: {
|
||||
id: '1',
|
||||
decimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'terminateProposal Name',
|
||||
code: 'terminateProposal Code',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: 'USD',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
updateType: MarketUpdateType.MARKET_STATE_UPDATE_TYPE_TERMINATE,
|
||||
price: '123',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
it('should render nothing if proposal is null', () => {
|
||||
render(<ProposalUpdateMarketState proposal={null} />);
|
||||
expect(screen.queryByTestId('proposal-update-market-state')).toBeNull();
|
||||
});
|
||||
|
||||
it('should toggle details when CollapsibleToggle is clicked', () => {
|
||||
render(<ProposalUpdateMarketState proposal={suspendProposal} />);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('proposal-update-market-state-table')
|
||||
).toBeNull();
|
||||
|
||||
fireEvent.click(screen.getByTestId('proposal-market-data-toggle'));
|
||||
|
||||
expect(
|
||||
screen.getByTestId('proposal-update-market-state-table')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display suspend market information when showDetails is true', () => {
|
||||
render(<ProposalUpdateMarketState proposal={suspendProposal} />);
|
||||
|
||||
fireEvent.click(screen.getByTestId('proposal-market-data-toggle'));
|
||||
|
||||
expect(screen.getByText('suspendProposal Name')).toBeInTheDocument();
|
||||
expect(screen.getByText('suspendProposal Code')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display resume market information when showDetails is true', () => {
|
||||
render(<ProposalUpdateMarketState proposal={resumeProposal} />);
|
||||
|
||||
fireEvent.click(screen.getByTestId('proposal-market-data-toggle'));
|
||||
|
||||
expect(screen.getByText('resumeProposal Name')).toBeInTheDocument();
|
||||
expect(screen.getByText('resumeProposal Code')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display terminate market information when showDetails is true', () => {
|
||||
render(<ProposalUpdateMarketState proposal={terminateProposal} />);
|
||||
|
||||
fireEvent.click(screen.getByTestId('proposal-market-data-toggle'));
|
||||
|
||||
expect(screen.getByText('terminateProposal Name')).toBeInTheDocument();
|
||||
expect(screen.getByText('terminateProposal Code')).toBeInTheDocument();
|
||||
expect(screen.getByText('123 USD')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Row } from '@vegaprotocol/markets';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { useState } from 'react';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
|
||||
interface ProposalUpdateMarketStateProps {
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
export const ProposalUpdateMarketState = ({
|
||||
proposal,
|
||||
}: ProposalUpdateMarketStateProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
let market;
|
||||
let isTerminate = false;
|
||||
|
||||
if (!proposal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (proposal?.terms.change.__typename === 'UpdateMarketState') {
|
||||
market = proposal?.terms?.change?.market;
|
||||
isTerminate =
|
||||
proposal?.terms?.change?.updateType ===
|
||||
'MARKET_STATE_UPDATE_TYPE_TERMINATE';
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="relative" data-testid="proposal-update-market-state">
|
||||
<CollapsibleToggle
|
||||
toggleState={showDetails}
|
||||
setToggleState={setShowDetails}
|
||||
dataTestId="proposal-market-data-toggle"
|
||||
>
|
||||
<SubHeading title={t('MarketDetails')} />
|
||||
</CollapsibleToggle>
|
||||
|
||||
{showDetails && (
|
||||
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
|
||||
{proposal?.terms.change.__typename === 'UpdateMarketState' && (
|
||||
<KeyValueTable data-testid="proposal-update-market-state-table">
|
||||
<KeyValueTableRow>
|
||||
{t('marketId')}
|
||||
{market?.id}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('marketName')}
|
||||
{market?.tradableInstrument?.instrument?.name}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={!isTerminate}>
|
||||
{t('marketCode')}
|
||||
{market?.tradableInstrument?.instrument?.code}
|
||||
</KeyValueTableRow>
|
||||
{isTerminate && (
|
||||
<Row
|
||||
field="termination-price"
|
||||
value={proposal?.terms?.change?.price}
|
||||
assetSymbol={
|
||||
market?.tradableInstrument?.instrument?.product
|
||||
?.__typename === 'Future' ||
|
||||
market?.tradableInstrument?.instrument?.product
|
||||
?.__typename === 'Perpetual'
|
||||
? market?.tradableInstrument?.instrument?.product
|
||||
?.quoteName
|
||||
: undefined
|
||||
}
|
||||
decimalPlaces={market?.decimalPlaces}
|
||||
/>
|
||||
)}
|
||||
</KeyValueTable>
|
||||
)}
|
||||
</RoundedWrapper>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -10,21 +10,21 @@ import { UserVote } from '../vote-details';
|
||||
import { ListAsset } from '../list-asset';
|
||||
import Routes from '../../../routes';
|
||||
import { ProposalMarketData } from '../proposal-market-data';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MarketInfo } from '@vegaprotocol/markets';
|
||||
import type { AssetQuery } from '@vegaprotocol/assets';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ProposalMarketChanges } from '../proposal-market-changes';
|
||||
import { ProposalUpdateMarketState } from '../proposal-update-market-state';
|
||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||
import { useVoteSubmit } from '@vegaprotocol/proposals';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
|
||||
export interface ProposalProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
proposal: ProposalQuery['proposal'];
|
||||
networkParams: Partial<NetworkParamsResult>;
|
||||
newMarketData?: MarketInfo | null;
|
||||
marketData?: MarketInfo | null;
|
||||
parentMarketData?: MarketInfo | null;
|
||||
assetData?: AssetQuery | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -39,7 +39,7 @@ export const Proposal = ({
|
||||
proposal,
|
||||
networkParams,
|
||||
restData,
|
||||
newMarketData,
|
||||
marketData,
|
||||
parentMarketData,
|
||||
assetData,
|
||||
originalMarketProposalRestData,
|
||||
@@ -74,14 +74,15 @@ export const Proposal = ({
|
||||
|
||||
if (networkParams) {
|
||||
switch (proposal.terms.change.__typename) {
|
||||
case 'UpdateMarket':
|
||||
case 'UpdateMarketState':
|
||||
minVoterBalance =
|
||||
networkParams.governance_proposal_updateMarket_minVoterBalance;
|
||||
break;
|
||||
case 'NewMarket':
|
||||
minVoterBalance =
|
||||
networkParams.governance_proposal_market_minVoterBalance;
|
||||
break;
|
||||
case 'UpdateMarket':
|
||||
minVoterBalance =
|
||||
networkParams.governance_proposal_updateMarket_minVoterBalance;
|
||||
break;
|
||||
case 'NewAsset':
|
||||
minVoterBalance =
|
||||
networkParams.governance_proposal_asset_minVoterBalance;
|
||||
@@ -145,15 +146,21 @@ export const Proposal = ({
|
||||
<ProposalDescription description={proposal.rationale.description} />
|
||||
</div>
|
||||
|
||||
{newMarketData && (
|
||||
{marketData && (
|
||||
<div className="mb-4">
|
||||
<ProposalMarketData
|
||||
marketData={newMarketData}
|
||||
marketData={marketData}
|
||||
parentMarketData={parentMarketData ? parentMarketData : undefined}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{proposal.terms.change.__typename === 'UpdateMarketState' && (
|
||||
<div className="mb-4">
|
||||
<ProposalUpdateMarketState proposal={proposal} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{proposal.terms.change.__typename === 'UpdateMarket' && (
|
||||
<div className="mb-4">
|
||||
<ProposalMarketChanges
|
||||
|
||||
+1
-2
@@ -2,11 +2,10 @@ import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
interface ProposalsListItemProps {
|
||||
proposal?: ProposalFieldsFragment | ProposalQuery['proposal'] | null;
|
||||
proposal?: ProposalQuery['proposal'] | null;
|
||||
}
|
||||
|
||||
export const ProposalsListItem = ({ proposal }: ProposalsListItemProps) => {
|
||||
|
||||
@@ -16,14 +16,14 @@ import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/propos
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
||||
proposals: Array<ProposalQuery['proposal']>;
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
lastBlockHeight?: string;
|
||||
}
|
||||
|
||||
interface SortedProposalsProps {
|
||||
open: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
||||
closed: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
||||
open: ProposalQuery['proposal'][];
|
||||
closed: ProposalQuery['proposal'][];
|
||||
}
|
||||
|
||||
interface SortedProtocolUpgradeProposalsProps {
|
||||
@@ -31,7 +31,7 @@ interface SortedProtocolUpgradeProposalsProps {
|
||||
closed: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}
|
||||
|
||||
export const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
export const orderByDate = (arr: ProposalQuery['proposal'][]) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[
|
||||
@@ -92,12 +92,12 @@ export const ProposalsList = ({
|
||||
return {
|
||||
open:
|
||||
initialSorting.open.length > 0
|
||||
? orderByDate(initialSorting.open as ProposalFieldsFragment[])
|
||||
? orderByDate(initialSorting.open as ProposalQuery['proposal'][])
|
||||
: [],
|
||||
closed:
|
||||
initialSorting.closed.length > 0
|
||||
? orderByDate(
|
||||
initialSorting.closed as ProposalFieldsFragment[]
|
||||
initialSorting.closed as ProposalQuery['proposal'][]
|
||||
).reverse()
|
||||
: [],
|
||||
};
|
||||
|
||||
+2
-5
@@ -3,20 +3,17 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { ProposalsListItem } from '../proposals-list-item';
|
||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Array<ProposalQuery['proposal'] | ProposalFieldsFragment>;
|
||||
proposals: ProposalQuery['proposal'][];
|
||||
}
|
||||
|
||||
export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [filterString, setFilterString] = useState('');
|
||||
|
||||
const filterPredicate = (
|
||||
p: ProposalFieldsFragment | ProposalQuery['proposal']
|
||||
) =>
|
||||
const filterPredicate = (p: ProposalQuery['proposal']) =>
|
||||
p?.id?.includes(filterString) ||
|
||||
p?.party?.id?.toString().includes(filterString);
|
||||
|
||||
|
||||
@@ -28,17 +28,20 @@ export const useProposalNetworkParams = ({
|
||||
NetworkParams.governance_proposal_freeform_requiredParticipation,
|
||||
]);
|
||||
|
||||
const fallback = {
|
||||
requiredMajority: new BigNumber(1),
|
||||
requiredMajorityLP: new BigNumber(0),
|
||||
requiredParticipation: new BigNumber(1),
|
||||
requiredParticipationLP: new BigNumber(0),
|
||||
};
|
||||
|
||||
if (!params) {
|
||||
return {
|
||||
requiredMajority: new BigNumber(1),
|
||||
requiredMajorityLP: new BigNumber(0),
|
||||
requiredParticipation: new BigNumber(1),
|
||||
requiredParticipationLP: new BigNumber(0),
|
||||
};
|
||||
return fallback;
|
||||
}
|
||||
|
||||
switch (proposal?.terms.change.__typename) {
|
||||
case 'UpdateMarket':
|
||||
case 'UpdateMarketState':
|
||||
return {
|
||||
requiredMajority:
|
||||
params.governance_proposal_updateMarket_requiredMajority,
|
||||
@@ -89,6 +92,6 @@ export const useProposalNetworkParams = ({
|
||||
),
|
||||
};
|
||||
default:
|
||||
throw new Error('Unknown proposal type');
|
||||
return fallback;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,53 @@
|
||||
query Proposal($proposalId: ID!) {
|
||||
fragment NewMarketProductField on Proposal {
|
||||
terms {
|
||||
change {
|
||||
... on NewMarket {
|
||||
instrument {
|
||||
product {
|
||||
__typename
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment UpdateMarketState on Proposal {
|
||||
terms {
|
||||
change {
|
||||
... on UpdateMarketState {
|
||||
updateType
|
||||
market {
|
||||
decimalPlaces
|
||||
id
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
product {
|
||||
__typename
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
name
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
updateType
|
||||
price
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query Proposal(
|
||||
$proposalId: ID!
|
||||
$includeNewMarketProductField: Boolean!
|
||||
$includeUpdateMarketState: Boolean!
|
||||
) {
|
||||
proposal(id: $proposalId) {
|
||||
id
|
||||
rationale {
|
||||
@@ -13,6 +62,8 @@ query Proposal($proposalId: ID!) {
|
||||
id
|
||||
}
|
||||
errorDetails
|
||||
...NewMarketProductField @include(if: $includeNewMarketProductField)
|
||||
...UpdateMarketState @include(if: $includeUpdateMarketState)
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -53,7 +53,11 @@ export const ProposalContainer = () => {
|
||||
const { data, loading, error, refetch } = useProposalQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: { proposalId: params.proposalId || '' },
|
||||
variables: {
|
||||
proposalId: params.proposalId || '',
|
||||
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
},
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
|
||||
@@ -91,9 +95,9 @@ export const ProposalContainer = () => {
|
||||
);
|
||||
|
||||
const {
|
||||
data: newMarketData,
|
||||
loading: newMarketLoading,
|
||||
error: newMarketError,
|
||||
data: marketData,
|
||||
loading: marketLoading,
|
||||
error: marketError,
|
||||
} = useDataProvider({
|
||||
dataProvider: marketInfoProvider,
|
||||
skipUpdates: true,
|
||||
@@ -109,9 +113,9 @@ export const ProposalContainer = () => {
|
||||
error: parentMarketIdError,
|
||||
} = useParentMarketIdQuery({
|
||||
variables: {
|
||||
marketId: newMarketData?.id || '',
|
||||
marketId: marketData?.id || '',
|
||||
},
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !newMarketData?.id,
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -191,7 +195,7 @@ export const ProposalContainer = () => {
|
||||
<AsyncRenderer
|
||||
loading={
|
||||
loading ||
|
||||
newMarketLoading ||
|
||||
marketLoading ||
|
||||
assetLoading ||
|
||||
networkParamsLoading ||
|
||||
parentMarketIdLoading ||
|
||||
@@ -206,7 +210,7 @@ export const ProposalContainer = () => {
|
||||
}
|
||||
error={
|
||||
error ||
|
||||
newMarketError ||
|
||||
marketError ||
|
||||
assetError ||
|
||||
networkParamsError ||
|
||||
parentMarketIdError ||
|
||||
@@ -218,7 +222,7 @@ export const ProposalContainer = () => {
|
||||
data={{
|
||||
...data,
|
||||
...networkParams,
|
||||
...(newMarketData ? { newMarketData } : {}),
|
||||
...(marketData ? { newMarketData: marketData } : {}),
|
||||
...(parentMarketData ? { parentMarketData } : {}),
|
||||
...(assetData ? { assetData } : {}),
|
||||
...(restData ? { restData } : {}),
|
||||
@@ -235,7 +239,7 @@ export const ProposalContainer = () => {
|
||||
proposal={data.proposal}
|
||||
networkParams={networkParams}
|
||||
restData={restData}
|
||||
newMarketData={newMarketData}
|
||||
marketData={marketData}
|
||||
parentMarketData={parentMarketData}
|
||||
assetData={assetData}
|
||||
originalMarketProposalRestData={originalMarketProposalRestData}
|
||||
|
||||
@@ -1,3 +1,48 @@
|
||||
fragment NewMarketProductFields on Proposal {
|
||||
terms {
|
||||
change {
|
||||
... on NewMarket {
|
||||
instrument {
|
||||
product {
|
||||
__typename
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment UpdateMarketStates on Proposal {
|
||||
terms {
|
||||
change {
|
||||
... on UpdateMarketState {
|
||||
updateType
|
||||
market {
|
||||
decimalPlaces
|
||||
id
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
product {
|
||||
__typename
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
name
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
updateType
|
||||
price
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment ProposalFields on Proposal {
|
||||
id
|
||||
rationale {
|
||||
@@ -79,11 +124,16 @@ fragment ProposalFields on Proposal {
|
||||
}
|
||||
}
|
||||
|
||||
query Proposals {
|
||||
query Proposals(
|
||||
$includeNewMarketProductFields: Boolean!
|
||||
$includeUpdateMarketStates: Boolean!
|
||||
) {
|
||||
proposalsConnection {
|
||||
edges {
|
||||
node {
|
||||
...ProposalFields
|
||||
...NewMarketProductFields @include(if: $includeNewMarketProductFields)
|
||||
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+65
-5
@@ -3,13 +3,67 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type NewMarketProductFieldsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
||||
|
||||
export type UpdateMarketStatesFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
||||
|
||||
export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
|
||||
|
||||
export type ProposalsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
export type ProposalsQueryVariables = Types.Exact<{
|
||||
includeNewMarketProductFields: Types.Scalars['Boolean'];
|
||||
includeUpdateMarketStates: Types.Scalars['Boolean'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
||||
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
||||
|
||||
export const NewMarketProductFieldsFragmentDoc = gql`
|
||||
fragment NewMarketProductFields on Proposal {
|
||||
terms {
|
||||
change {
|
||||
... on NewMarket {
|
||||
instrument {
|
||||
product {
|
||||
__typename
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const UpdateMarketStatesFragmentDoc = gql`
|
||||
fragment UpdateMarketStates on Proposal {
|
||||
terms {
|
||||
change {
|
||||
... on UpdateMarketState {
|
||||
updateType
|
||||
market {
|
||||
decimalPlaces
|
||||
id
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
product {
|
||||
__typename
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
name
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
updateType
|
||||
price
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const ProposalFieldsFragmentDoc = gql`
|
||||
fragment ProposalFields on Proposal {
|
||||
id
|
||||
@@ -93,16 +147,20 @@ export const ProposalFieldsFragmentDoc = gql`
|
||||
}
|
||||
`;
|
||||
export const ProposalsDocument = gql`
|
||||
query Proposals {
|
||||
query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!) {
|
||||
proposalsConnection {
|
||||
edges {
|
||||
node {
|
||||
...ProposalFields
|
||||
...NewMarketProductFields @include(if: $includeNewMarketProductFields)
|
||||
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${ProposalFieldsFragmentDoc}`;
|
||||
${ProposalFieldsFragmentDoc}
|
||||
${NewMarketProductFieldsFragmentDoc}
|
||||
${UpdateMarketStatesFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useProposalsQuery__
|
||||
@@ -116,10 +174,12 @@ export const ProposalsDocument = gql`
|
||||
* @example
|
||||
* const { data, loading, error } = useProposalsQuery({
|
||||
* variables: {
|
||||
* includeNewMarketProductFields: // value for 'includeNewMarketProductFields'
|
||||
* includeUpdateMarketStates: // value for 'includeUpdateMarketStates'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useProposalsQuery(baseOptions?: Apollo.QueryHookOptions<ProposalsQuery, ProposalsQueryVariables>) {
|
||||
export function useProposalsQuery(baseOptions: Apollo.QueryHookOptions<ProposalsQuery, ProposalsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ProposalsQuery, ProposalsQueryVariables>(ProposalsDocument, options);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { SplashLoader } from '../../../components/splash-loader';
|
||||
import { ProposalsList } from '../components/proposals-list';
|
||||
import { useProposalsQuery } from './__generated__/Proposals';
|
||||
import { getNodes } from '@vegaprotocol/utils';
|
||||
import { getNodes, removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import {
|
||||
ProposalState,
|
||||
ProtocolUpgradeProposalStatus,
|
||||
@@ -15,14 +15,13 @@ import type { NodeConnection, NodeEdge } from '@vegaprotocol/utils';
|
||||
import type { ProposalFieldsFragment } from './__generated__/Proposals';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
|
||||
export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
|
||||
data?: NodeConnection<NodeEdge<T>> | null
|
||||
): T[] {
|
||||
export function getNotRejectedProposals(data?: ProposalFieldsFragment[]) {
|
||||
return flow([
|
||||
(data) =>
|
||||
getNodes<ProposalFieldsFragment>(data, (p) =>
|
||||
p ? p.state !== ProposalState.STATE_REJECTED : false
|
||||
data.filter(
|
||||
(p: ProposalFieldsFragment) => p?.state !== ProposalState.STATE_REJECTED
|
||||
),
|
||||
])(data);
|
||||
}
|
||||
@@ -47,6 +46,10 @@ export const ProposalsContainer = () => {
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -60,7 +63,10 @@ export const ProposalsContainer = () => {
|
||||
});
|
||||
|
||||
const proposals = useMemo(
|
||||
() => getNotRejectedProposals(data?.proposalsConnection),
|
||||
() =>
|
||||
getNotRejectedProposals(
|
||||
removePaginationWrapper(data?.proposalsConnection?.edges)
|
||||
),
|
||||
[data]
|
||||
);
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ import { SplashLoader } from '../../../components/splash-loader';
|
||||
import { RejectedProposalsList } from '../components/proposals-list';
|
||||
import type { ProposalFieldsFragment } from '../proposals/__generated__/Proposals';
|
||||
import { useProposalsQuery } from '../proposals/__generated__/Proposals';
|
||||
import type { NodeConnection, NodeEdge } from '@vegaprotocol/utils';
|
||||
import { getNodes } from '@vegaprotocol/utils';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import flow from 'lodash/flow';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
|
||||
const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
orderBy(
|
||||
@@ -22,13 +22,11 @@ const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
['desc', 'desc']
|
||||
);
|
||||
|
||||
export function getRejectedProposals<T extends ProposalFieldsFragment>(
|
||||
data?: NodeConnection<NodeEdge<ProposalFieldsFragment>> | null
|
||||
): T[] {
|
||||
export function getRejectedProposals(data?: ProposalFieldsFragment[] | null) {
|
||||
return flow([
|
||||
(data) =>
|
||||
getNodes<ProposalFieldsFragment>(data, (p) =>
|
||||
p ? p?.state === ProposalState.STATE_REJECTED : false
|
||||
data.filter(
|
||||
(p: ProposalFieldsFragment) => p?.state === ProposalState.STATE_REJECTED
|
||||
),
|
||||
orderByDate,
|
||||
])(data);
|
||||
@@ -36,11 +34,21 @@ export function getRejectedProposals<T extends ProposalFieldsFragment>(
|
||||
|
||||
export const RejectedProposalsContainer = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data, loading, error } = useProposalsQuery();
|
||||
const { data, loading, error } = useProposalsQuery({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
},
|
||||
});
|
||||
|
||||
const proposals = useMemo(
|
||||
() =>
|
||||
getRejectedProposals<ProposalFieldsFragment>(data?.proposalsConnection),
|
||||
getRejectedProposals(
|
||||
removePaginationWrapper(data?.proposalsConnection?.edges)
|
||||
),
|
||||
[data]
|
||||
);
|
||||
|
||||
|
||||
@@ -64,48 +64,6 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId('Collateral').click({ force: true });
|
||||
});
|
||||
|
||||
it('should open asset details dialog when clicked on symbol', () => {
|
||||
// 7001-COLL-008
|
||||
// 6501-ASSE-001
|
||||
// 6501-ASSE-002
|
||||
// 6501-ASSE-003
|
||||
// 6501-ASSE-004
|
||||
// 6501-ASSE-005
|
||||
// 6501-ASSE-006
|
||||
// 6501-ASSE-007
|
||||
// 6501-ASSE-008
|
||||
// 6501-ASSE-009
|
||||
// 6501-ASSE-010
|
||||
// 6501-ASSE-011
|
||||
// 6501-ASSE-012
|
||||
// 6501-ASSE-013
|
||||
const titles = [
|
||||
'ID',
|
||||
'Type',
|
||||
'Name',
|
||||
'Symbol',
|
||||
'Decimals',
|
||||
'Quantum',
|
||||
'Status',
|
||||
'Contract address',
|
||||
'Withdrawal threshold',
|
||||
'Lifetime limit',
|
||||
'Infrastructure fee account balance',
|
||||
'Global reward pool account balance',
|
||||
'Maker paid fees account balance',
|
||||
'Maker received fees account balance',
|
||||
'Liquidity provision fee reward account balance',
|
||||
'Market proposer reward account balance',
|
||||
];
|
||||
cy.get('[col-id="asset.symbol"]').contains('tEURO').click();
|
||||
cy.get('[data-testid$="_label"]').should('have.length', 16);
|
||||
cy.get('[data-testid$="_label"]').each((element, index) => {
|
||||
cy.wrap(element).should('have.text', titles[index]);
|
||||
});
|
||||
cy.getByTestId(dialogClose).click();
|
||||
cy.getByTestId(dialogClose).should('not.exist');
|
||||
});
|
||||
|
||||
it('should open usage breakdown dialog when clicked on used', () => {
|
||||
// 7001-COLL-009
|
||||
cy.get('[col-id="used"]').contains('1.01').click();
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
import {
|
||||
orderPriceField,
|
||||
orderSizeField,
|
||||
orderTIFDropDown,
|
||||
placeOrderBtn,
|
||||
toggleLimit,
|
||||
toggleMarket,
|
||||
} from '../support/deal-ticket';
|
||||
|
||||
describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.getByTestId('deal-ticket-fee-margin-required').click();
|
||||
});
|
||||
|
||||
describe('limit order', () => {
|
||||
before(() => {
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
});
|
||||
|
||||
it('must see the price unit', function () {
|
||||
// 7002-SORD-018
|
||||
cy.getByTestId(orderPriceField).next().should('have.text', 'DAI');
|
||||
});
|
||||
|
||||
it('must see warning when placing an order with expiry date in past', () => {
|
||||
const expiresAt = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
const expiresAtInputValue = expiresAt.toISOString().substring(0, 16);
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
cy.getByTestId(orderPriceField).clear().type('0.1');
|
||||
cy.getByTestId(orderSizeField).clear().type('1');
|
||||
cy.getByTestId(orderTIFDropDown).select('TIME_IN_FORCE_GTT');
|
||||
|
||||
cy.log('choosing yesterday');
|
||||
cy.getByTestId('date-picker-field').type(expiresAtInputValue);
|
||||
|
||||
cy.getByTestId(placeOrderBtn).click();
|
||||
|
||||
cy.getByTestId('deal-ticket-error-message-expiry').should(
|
||||
'have.text',
|
||||
'The expiry date that you have entered appears to be in the past'
|
||||
);
|
||||
});
|
||||
|
||||
it('must see warning if price has too many digits after decimal place', function () {
|
||||
// 7002-SORD-059
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
cy.getByTestId(orderTIFDropDown).select('TIME_IN_FORCE_GTC');
|
||||
cy.getByTestId(orderSizeField).clear().type('1');
|
||||
cy.getByTestId(orderPriceField).clear().type('1.123456');
|
||||
cy.getByTestId(placeOrderBtn).click();
|
||||
cy.getByTestId('deal-ticket-error-message-price').should(
|
||||
'have.text',
|
||||
'Price accepts up to 5 decimal places'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('market order', () => {
|
||||
before(() => {
|
||||
cy.getByTestId(toggleMarket).click();
|
||||
cy.getByTestId(placeOrderBtn).click();
|
||||
});
|
||||
|
||||
it('must not see the price unit', function () {
|
||||
// 7002-SORD-019
|
||||
cy.getByTestId(orderPriceField).should('not.exist');
|
||||
});
|
||||
|
||||
it('must warn if order size input has too many digits after the decimal place', function () {
|
||||
// 7002-SORD-016
|
||||
cy.getByTestId(orderSizeField).clear().type('1.234');
|
||||
// 7002-SORD-060
|
||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
||||
cy.getByTestId('deal-ticket-error-message-size').should(
|
||||
'have.text',
|
||||
'Size must be whole numbers for this market'
|
||||
);
|
||||
});
|
||||
|
||||
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').should(
|
||||
'have.text',
|
||||
'Size cannot be lower than 1'
|
||||
);
|
||||
});
|
||||
|
||||
it('must have total margin available', () => {
|
||||
// 7001-COLL-011
|
||||
cy.getByTestId('deal-ticket-fee-total-margin-available').within(() => {
|
||||
cy.get('[data-state="closed"]').should(
|
||||
'have.text',
|
||||
'Total margin available100.01 tDAI'
|
||||
);
|
||||
});
|
||||
cy.getByTestId('deal-ticket-fee-margin-required').click();
|
||||
});
|
||||
|
||||
it('must have current margin allocation', () => {
|
||||
cy.getByTestId('deal-ticket-fee-current-margin-allocation').within(() => {
|
||||
cy.get('[data-state="closed"]:first').should(
|
||||
'have.text',
|
||||
'Current margin allocation'
|
||||
);
|
||||
});
|
||||
cy.getByTestId('deal-ticket-fee-margin-required').click();
|
||||
});
|
||||
|
||||
it('should open usage breakdown dialog when clicked on current margin allocation', () => {
|
||||
cy.getByTestId('deal-ticket-fee-current-margin-allocation').within(() => {
|
||||
cy.get('button').click();
|
||||
});
|
||||
cy.getByTestId('usage-breakdown').should('exist');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId('deal-ticket-fee-margin-required').click();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
testOrderAmendment,
|
||||
} from '../support/order-validation';
|
||||
|
||||
const orderSymbol = 'market.tradableInstrument.instrument.code';
|
||||
const orderSymbol = 'instrument-code';
|
||||
const orderSize = 'size';
|
||||
const orderType = 'type';
|
||||
const orderStatus = 'status';
|
||||
@@ -229,10 +229,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
|
||||
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'
|
||||
);
|
||||
cy.get(`[col-id="${orderSymbol}"]`).contains('[title="Future"]', 'Futr');
|
||||
});
|
||||
|
||||
it('must see a rejected order', () => {
|
||||
|
||||
@@ -15,6 +15,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import MarketPage from '../market';
|
||||
|
||||
export const ClosedMarketPage = () => {
|
||||
return <MarketPage closed />;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { ClosedMarketPage as default } from './closed-market';
|
||||
@@ -9,9 +9,10 @@ import { useGlobalStore, usePageTitleStore } from '../../stores';
|
||||
import { TradeGrid } from './trade-grid';
|
||||
import { TradePanels } from './trade-panels';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Links } from '../../lib/links';
|
||||
import { Links, Routes } from '../../lib/links';
|
||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
|
||||
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||
return markPrice && decimalPlaces
|
||||
@@ -56,7 +57,7 @@ const TitleUpdater = ({
|
||||
return null;
|
||||
};
|
||||
|
||||
export const MarketPage = () => {
|
||||
export const MarketPage = ({ closed }: { closed?: boolean }) => {
|
||||
const { marketId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
@@ -70,16 +71,33 @@ export const MarketPage = () => {
|
||||
const { data, error, loading } = useMarket(marketId);
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.id && data.id !== lastMarketId) {
|
||||
if (
|
||||
data?.state &&
|
||||
[
|
||||
MarketState.STATE_SETTLED,
|
||||
MarketState.STATE_TRADING_TERMINATED,
|
||||
].includes(data.state) &&
|
||||
currentRouteId !== Routes.CLOSED_MARKETS &&
|
||||
marketId
|
||||
) {
|
||||
navigate(Links.CLOSED_MARKETS(marketId));
|
||||
}
|
||||
}, [data?.state, currentRouteId, navigate, marketId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.id && data.id !== lastMarketId && !closed) {
|
||||
update({ marketId: data.id });
|
||||
}
|
||||
}, [update, lastMarketId, data?.id]);
|
||||
}, [update, lastMarketId, data?.id, closed]);
|
||||
|
||||
useEffect(() => {
|
||||
if (largeScreen && view === undefined) {
|
||||
setViews({ type: ViewType.Order }, currentRouteId);
|
||||
setViews(
|
||||
{ type: closed ? ViewType.Info : ViewType.Order },
|
||||
currentRouteId
|
||||
);
|
||||
}
|
||||
}, [setViews, view, currentRouteId, largeScreen]);
|
||||
}, [setViews, view, currentRouteId, largeScreen, closed]);
|
||||
|
||||
const pinnedAsset = data && getAsset(data);
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ import { SettlementPriceCell } from './settlement-price-cell';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { MarketCodeCell } from './market-code-cell';
|
||||
import { MarketActionsDropdown } from './market-table-actions';
|
||||
import type { CellClickedEvent } from 'ag-grid-community';
|
||||
import { useClosedMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
|
||||
type SettlementAsset = Pick<
|
||||
Asset,
|
||||
@@ -125,6 +127,7 @@ const ClosedMarketsDataGrid = ({
|
||||
rowData: Row[];
|
||||
error: Error | undefined;
|
||||
}) => {
|
||||
const handleOnSelect = useClosedMarketClickHandler();
|
||||
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
|
||||
|
||||
const colDefs = useMemo(() => {
|
||||
@@ -281,6 +284,27 @@ const ClosedMarketsDataGrid = ({
|
||||
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
||||
components={components}
|
||||
rowHeight={45}
|
||||
onCellClicked={({ data, column, event }: CellClickedEvent<Row>) => {
|
||||
if (!data) return;
|
||||
|
||||
// prevent navigating to the market page if any of the below cells are clicked
|
||||
// event.preventDefault or event.stopPropagation dont seem to apply for aggird
|
||||
const colId = column.getColId();
|
||||
|
||||
if (
|
||||
[
|
||||
'settlementDate',
|
||||
'settlementDataOracleId',
|
||||
'settlementAsset',
|
||||
'market-actions',
|
||||
].includes(colId)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore metaKey exists
|
||||
handleOnSelect(data.id, event ? event.metaKey : false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,6 +23,10 @@ export const LayoutWithSidebar = () => {
|
||||
<div className="col-span-full">
|
||||
<Routes>
|
||||
<Route path="markets/:marketId" element={<MarketHeader />} />
|
||||
<Route
|
||||
path="markets/all/closed/:marketId"
|
||||
element={<MarketHeader />}
|
||||
/>
|
||||
<Route path="liquidity/:marketId" element={<LiquidityHeader />} />
|
||||
</Routes>
|
||||
</div>
|
||||
|
||||
@@ -12,10 +12,6 @@ import {
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ExternalLink, Indicator } from '@vegaprotocol/ui-toolkit';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { useCheckLiquidityStatus } from '@vegaprotocol/liquidity';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
@@ -31,12 +27,8 @@ export const LiquidityHeader = () => {
|
||||
const assetDecimalPlaces = asset?.decimals || 0;
|
||||
const symbol = asset?.symbol;
|
||||
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.market_liquidity_stakeToCcyVolume,
|
||||
NetworkParams.market_liquidity_targetstake_triggering_ratio,
|
||||
]);
|
||||
const triggeringRatio =
|
||||
params.market_liquidity_targetstake_triggering_ratio || '1';
|
||||
market?.liquidityMonitoringParameters.triggeringRatio || '1';
|
||||
|
||||
const { percentage, status } = useCheckLiquidityStatus({
|
||||
suppliedStake: suppliedStake || 0,
|
||||
|
||||
@@ -61,7 +61,6 @@ describe('MarketSelectorItem', () => {
|
||||
indicativeVolume: '100',
|
||||
marketState: MarketState.STATE_ACTIVE,
|
||||
marketTradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
marketValueProxy: '100',
|
||||
markPrice: '50000',
|
||||
midPrice: '100',
|
||||
staticMidPrice: '100',
|
||||
|
||||
@@ -126,10 +126,12 @@ export const MarketSelector = ({
|
||||
onSelect={onSelect}
|
||||
noItems={
|
||||
filter.product === Product.Perpetual
|
||||
? t('Perpetual markets coming soon.')
|
||||
? t('No perpetual markets.')
|
||||
: filter.product === Product.Spot
|
||||
? t('Spot markets coming soon.')
|
||||
: t('No markets')
|
||||
: filter.product === Product.Future
|
||||
? t('No future markets.')
|
||||
: t('No markets.')
|
||||
}
|
||||
allProducts={allProducts}
|
||||
/>
|
||||
|
||||
@@ -7,6 +7,8 @@ import * as Schema from '@vegaprotocol/types';
|
||||
import { HeaderStat } from '../header';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import * as constants from '../constants';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const MarketState = ({ market }: { market: Market | null }) => {
|
||||
const [marketState, setMarketState] = useState<Schema.MarketState | null>(
|
||||
@@ -90,5 +92,20 @@ const getMarketStateTooltip = (state: Schema.MarketState | null) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (state === Schema.MarketState.STATE_SUSPENDED_VIA_GOVERNANCE) {
|
||||
return (
|
||||
<p>
|
||||
{t(
|
||||
`This market has been suspended via a governance vote and can be resumed or terminated by further votes.`
|
||||
)}
|
||||
{DocsLinks && (
|
||||
<ExternalLink href={DocsLinks.MARKET_LIFECYCLE} className="ml-1">
|
||||
{t('Find out more')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@@ -28,14 +28,20 @@ export interface OrderContainerProps {
|
||||
filter?: Filter;
|
||||
}
|
||||
|
||||
const AUTO_SIZE_COLUMNS = ['instrument-code'];
|
||||
|
||||
export const OrdersContainer = ({ filter }: OrderContainerProps) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
||||
const { gridState, updateGridState } = useOrderListGridState(filter);
|
||||
const gridStoreCallbacks = useDataGridEvents(gridState, (newState) => {
|
||||
updateGridState(filter, newState);
|
||||
});
|
||||
const gridStoreCallbacks = useDataGridEvents(
|
||||
gridState,
|
||||
(newState) => {
|
||||
updateGridState(filter, newState);
|
||||
},
|
||||
AUTO_SIZE_COLUMNS
|
||||
);
|
||||
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
|
||||
@@ -10,6 +10,8 @@ import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
|
||||
const AUTO_SIZE_COLUMNS = ['marketCode'];
|
||||
|
||||
export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
|
||||
@@ -17,7 +19,11 @@ export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
|
||||
const showClosed = usePositionsStore((store) => store.showClosedMarkets);
|
||||
const gridStore = usePositionsStore((store) => store.gridStore);
|
||||
const updateGridStore = usePositionsStore((store) => store.updateGridStore);
|
||||
const gridStoreCallbacks = useDataGridEvents(gridStore, updateGridStore);
|
||||
const gridStoreCallbacks = useDataGridEvents(
|
||||
gridStore,
|
||||
updateGridStore,
|
||||
AUTO_SIZE_COLUMNS
|
||||
);
|
||||
|
||||
if (!pubKey) {
|
||||
return (
|
||||
|
||||
@@ -135,6 +135,21 @@ export const Sidebar = () => {
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="markets/all/closed/:marketId"
|
||||
element={
|
||||
<>
|
||||
<AssetSidebarButtons />
|
||||
<SidebarDivider />
|
||||
<SidebarButton
|
||||
view={ViewType.Info}
|
||||
icon={VegaIconNames.BREAKDOWN}
|
||||
tooltip={t('Market specification')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</nav>
|
||||
<nav className={classNames(navClasses, 'ml-auto lg:mt-auto lg:ml-0')}>
|
||||
|
||||
@@ -20,3 +20,16 @@ export const useMarketLiquidityClickHandler = () => {
|
||||
window.open(`/#/liquidity/${selectedId}`, metaKey ? '_blank' : '_self');
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const useClosedMarketClickHandler = (replace = false) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (selectedId: string, metaKey?: boolean) => {
|
||||
const link = Links.CLOSED_MARKETS(selectedId);
|
||||
if (metaKey) {
|
||||
window.open(`/#${link}`, '_blank');
|
||||
} else {
|
||||
navigate(link, { replace });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import trimEnd from 'lodash/trimEnd';
|
||||
export const Routes = {
|
||||
HOME: '/',
|
||||
MARKETS: '/markets/all',
|
||||
CLOSED_MARKETS: '/markets/all/closed/:marketId',
|
||||
MARKET: '/markets/:marketId',
|
||||
LIQUIDITY: '/liquidity/:marketId',
|
||||
PORTFOLIO: '/portfolio',
|
||||
@@ -28,6 +29,8 @@ export const Links: ConsoleLinks = {
|
||||
MARKET: (marketId: string) =>
|
||||
trimEnd(Routes.MARKET.replace(':marketId', marketId)),
|
||||
MARKETS: () => Routes.MARKETS,
|
||||
CLOSED_MARKETS: (marketId: string) =>
|
||||
trimEnd(Routes.CLOSED_MARKETS.replace(':marketId', marketId)),
|
||||
PORTFOLIO: () => Routes.PORTFOLIO,
|
||||
LIQUIDITY: (marketId: string) =>
|
||||
trimEnd(Routes.LIQUIDITY.replace(':marketId', marketId)),
|
||||
|
||||
@@ -5,6 +5,7 @@ const MARKET_TEMPLATE = [
|
||||
MarketState.STATE_ACTIVE,
|
||||
MarketState.STATE_SUSPENDED,
|
||||
MarketState.STATE_PENDING,
|
||||
MarketState.STATE_SUSPENDED_VIA_GOVERNANCE,
|
||||
];
|
||||
|
||||
export const isMarketActive = (state: MarketState) => {
|
||||
|
||||
@@ -26,6 +26,7 @@ import { FLAGS } from '@vegaprotocol/environment';
|
||||
// These must remain dynamically imported as pennant cannot be compiled by nextjs due to ESM
|
||||
// Using dynamic imports is a workaround for this until pennant is published as ESM
|
||||
const MarketPage = lazy(() => import('../client-pages/market'));
|
||||
const ClosedMarketPage = lazy(() => import('../client-pages/closed-market'));
|
||||
const Portfolio = lazy(() => import('../client-pages/portfolio'));
|
||||
|
||||
const NotFound = () => (
|
||||
@@ -101,6 +102,11 @@ export const routerConfig: RouteObject[] = compact([
|
||||
element: <MarketPage />,
|
||||
id: Routes.MARKET,
|
||||
},
|
||||
{
|
||||
path: 'all/closed/:marketId',
|
||||
element: <ClosedMarketPage />,
|
||||
id: Routes.CLOSED_MARKETS,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -62,6 +62,20 @@ const WrappedAssetDetailsDialog = ({ assetId }: { assetId: string }) => (
|
||||
);
|
||||
|
||||
describe('AssetDetailsDialog', () => {
|
||||
// 7001-COLL-008
|
||||
// 6501-ASSE-001
|
||||
// 6501-ASSE-002
|
||||
// 6501-ASSE-003
|
||||
// 6501-ASSE-004
|
||||
// 6501-ASSE-005
|
||||
// 6501-ASSE-006
|
||||
// 6501-ASSE-007
|
||||
// 6501-ASSE-008
|
||||
// 6501-ASSE-009
|
||||
// 6501-ASSE-010
|
||||
// 6501-ASSE-011
|
||||
// 6501-ASSE-012
|
||||
// 6501-ASSE-013
|
||||
it('should show no data message given unknown asset symbol', async () => {
|
||||
render(<WrappedAssetDetailsDialog assetId={'UNKNOWN_FOR_SURE'} />);
|
||||
expect((await screen.findByTestId('splash')).textContent).toContain(
|
||||
|
||||
@@ -5,8 +5,6 @@ import type { MutableRefObject } from 'react';
|
||||
import { useRef } from 'react';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
|
||||
const GRID_EVENT_DEBOUNCE_TIME = 500;
|
||||
|
||||
const gridProps = {
|
||||
rowData: [{ id: 1 }],
|
||||
columnDefs: [
|
||||
@@ -18,25 +16,29 @@ const gridProps = {
|
||||
],
|
||||
style: { width: 500, height: 300 },
|
||||
};
|
||||
const GRID_EVENT_DEBOUNCE_TIME = 300;
|
||||
let gridRef: MutableRefObject<AgGridReact | null>;
|
||||
function TestComponent({
|
||||
hookParams,
|
||||
}: {
|
||||
hookParams: Parameters<typeof useDataGridEvents>;
|
||||
}) {
|
||||
const hookCallbacks = useDataGridEvents(...hookParams);
|
||||
gridRef = useRef<AgGridReact | null>(null);
|
||||
return <AgGridThemed gridRef={gridRef} {...gridProps} {...hookCallbacks} />;
|
||||
}
|
||||
|
||||
// Not using render hook so I can pass event callbacks
|
||||
// to a rendered grid
|
||||
function setup(...args: Parameters<typeof useDataGridEvents>) {
|
||||
let gridRef;
|
||||
|
||||
function TestComponent() {
|
||||
const hookCallbacks = useDataGridEvents(...args);
|
||||
gridRef = useRef<AgGridReact | null>(null);
|
||||
return <AgGridThemed gridRef={gridRef} {...gridProps} {...hookCallbacks} />;
|
||||
}
|
||||
render(<TestComponent />);
|
||||
return gridRef as unknown as MutableRefObject<AgGridReact>;
|
||||
return render(<TestComponent hookParams={args} />);
|
||||
}
|
||||
|
||||
describe('useDataGridEvents', () => {
|
||||
const originalWarn = console.warn;
|
||||
|
||||
beforeAll(() => {
|
||||
gridRef = undefined;
|
||||
jest.useFakeTimers();
|
||||
|
||||
// disabling some ag grid warnings that are caused by test setup only
|
||||
@@ -55,15 +57,15 @@ describe('useDataGridEvents', () => {
|
||||
columnState: undefined,
|
||||
};
|
||||
|
||||
const result = setup(initialState, callback);
|
||||
setup(initialState, callback);
|
||||
|
||||
// column state was not updated, so the default width provided by the
|
||||
// col def should be set
|
||||
expect(result.current.columnApi.getColumnState()[0].width).toEqual(
|
||||
expect(gridRef.current?.columnApi.getColumnState()[0].width).toEqual(
|
||||
gridProps.columnDefs[0].width
|
||||
);
|
||||
// no filters set
|
||||
expect(result.current.api.getFilterModel()).toEqual({});
|
||||
expect(gridRef.current?.api.getFilterModel()).toEqual({});
|
||||
|
||||
// Set filter
|
||||
const idFilter = {
|
||||
@@ -72,7 +74,7 @@ describe('useDataGridEvents', () => {
|
||||
type: 'equals',
|
||||
};
|
||||
await act(async () => {
|
||||
result.current.api.setFilterModel({
|
||||
gridRef.current?.api.setFilterModel({
|
||||
id: idFilter,
|
||||
});
|
||||
});
|
||||
@@ -88,7 +90,7 @@ describe('useDataGridEvents', () => {
|
||||
},
|
||||
});
|
||||
callback.mockClear();
|
||||
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter);
|
||||
expect(gridRef.current?.api.getFilterModel()['id']).toEqual(idFilter);
|
||||
});
|
||||
|
||||
it('applies grid state on ready', async () => {
|
||||
@@ -105,11 +107,11 @@ describe('useDataGridEvents', () => {
|
||||
columnState: [colState],
|
||||
};
|
||||
|
||||
const result = setup(initialState, jest.fn());
|
||||
setup(initialState, jest.fn());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter);
|
||||
expect(result.current.columnApi.getColumnState()[0]).toEqual(
|
||||
expect(gridRef.current?.api.getFilterModel()['id']).toEqual(idFilter);
|
||||
expect(gridRef.current?.columnApi.getColumnState()[0]).toEqual(
|
||||
expect.objectContaining(colState)
|
||||
);
|
||||
});
|
||||
@@ -122,13 +124,13 @@ describe('useDataGridEvents', () => {
|
||||
columnState: undefined,
|
||||
};
|
||||
|
||||
const result = setup(initialState, callback);
|
||||
setup(initialState, callback);
|
||||
|
||||
const newWidth = 400;
|
||||
|
||||
// Set col width multiple times
|
||||
await act(async () => {
|
||||
result.current.columnApi.setColumnWidth('id', newWidth);
|
||||
gridRef.current?.columnApi.setColumnWidth('id', newWidth);
|
||||
});
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
@@ -139,4 +141,23 @@ describe('useDataGridEvents', () => {
|
||||
|
||||
expect(callback).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('columns for autosizing should be handle', () => {
|
||||
const callback = jest.fn();
|
||||
const initialState = {
|
||||
filterModel: undefined,
|
||||
columnState: undefined,
|
||||
};
|
||||
|
||||
const { rerender } = setup(initialState, callback, ['id']);
|
||||
jest.spyOn(gridRef.current?.columnApi, 'autoSizeColumns');
|
||||
rerender(<TestComponent hookParams={[initialState, callback, ['id']]} />);
|
||||
act(() => {
|
||||
gridRef.current?.api.setRowData([{ id: 'test-id' }]);
|
||||
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
|
||||
});
|
||||
expect(gridRef.current?.columnApi.autoSizeColumns).toHaveBeenCalledWith([
|
||||
'id',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
FilterChangedEvent,
|
||||
FirstDataRenderedEvent,
|
||||
SortChangedEvent,
|
||||
GridReadyEvent,
|
||||
} from 'ag-grid-community';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
@@ -17,7 +18,8 @@ type State = {
|
||||
|
||||
export const useDataGridEvents = (
|
||||
state: State,
|
||||
callback: (data: State) => void
|
||||
callback: (data: State) => void,
|
||||
autoSizeColumns?: string[]
|
||||
) => {
|
||||
/**
|
||||
* Callback for filter events
|
||||
@@ -78,7 +80,7 @@ export const useDataGridEvents = (
|
||||
* State only applied if found, otherwise columns sized to fit available space
|
||||
*/
|
||||
const onGridReady = useCallback(
|
||||
({ api, columnApi }: FirstDataRenderedEvent) => {
|
||||
({ api, columnApi }: GridReadyEvent) => {
|
||||
if (!api || !columnApi) return;
|
||||
|
||||
if (state.columnState) {
|
||||
@@ -97,6 +99,16 @@ export const useDataGridEvents = (
|
||||
[state]
|
||||
);
|
||||
|
||||
const onFirstDataRendered = useCallback(
|
||||
({ columnApi }: FirstDataRenderedEvent) => {
|
||||
if (!columnApi) return;
|
||||
if (!state?.columnState && autoSizeColumns?.length) {
|
||||
columnApi.autoSizeColumns(autoSizeColumns);
|
||||
}
|
||||
},
|
||||
[state, autoSizeColumns]
|
||||
);
|
||||
|
||||
return {
|
||||
onGridReady,
|
||||
// these events don't use the 'finished' flag
|
||||
@@ -106,5 +118,6 @@ export const useDataGridEvents = (
|
||||
// these trigger a lot so this callback uses the 'finished' flag
|
||||
onColumnMoved: onDebouncedColumnChange,
|
||||
onColumnResized: onDebouncedColumnChange,
|
||||
onFirstDataRendered,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -156,6 +156,44 @@ describe('DealTicket', () => {
|
||||
expect(screen.getByTestId('order-tif')).toHaveValue(
|
||||
Schema.OrderTimeInForce.TIME_IN_FORCE_GTC
|
||||
);
|
||||
// 7002-SORD-018
|
||||
expect(screen.getByTestId('order-price').nextSibling).toHaveTextContent(
|
||||
'BTC'
|
||||
);
|
||||
});
|
||||
|
||||
it('market order should not display price', async () => {
|
||||
render(generateJsx());
|
||||
await userEvent.click(screen.getByTestId('order-type-Market'));
|
||||
// 7002-SORD-018ß
|
||||
expect(screen.queryByTestId('order-price')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('market order must warn for whole numbers', async () => {
|
||||
const marketOverrides = { positionDecimalPlaces: 0 };
|
||||
render(generateJsx([], marketOverrides));
|
||||
await userEvent.click(screen.getByTestId('order-type-Market'));
|
||||
await userEvent.click(screen.getByTestId('place-order'));
|
||||
await userEvent.type(screen.getByTestId('order-size'), '1.231');
|
||||
// 7002-SORD-060
|
||||
expect(screen.queryByTestId('place-order')).toBeEnabled();
|
||||
// 7002-SORD-016
|
||||
expect(
|
||||
screen.queryByTestId('deal-ticket-error-message-size')
|
||||
).toHaveTextContent('Size must be whole numbers for this market');
|
||||
});
|
||||
|
||||
it('market order must warn if order size set to 0', async () => {
|
||||
render(generateJsx());
|
||||
await userEvent.click(screen.getByTestId('order-type-Market'));
|
||||
await userEvent.click(screen.getByTestId('place-order'));
|
||||
await userEvent.type(screen.getByTestId('order-size'), '0');
|
||||
// 7002-SORD-060
|
||||
expect(screen.queryByTestId('place-order')).toBeEnabled();
|
||||
// 7002-SORD-016
|
||||
expect(
|
||||
screen.queryByTestId('deal-ticket-error-message-size')
|
||||
).toHaveTextContent('Size cannot be lower than 0.1');
|
||||
});
|
||||
|
||||
it('should use local storage state for initial values', () => {
|
||||
@@ -686,7 +724,6 @@ describe('DealTicket', () => {
|
||||
new Date(screen.getByTestId<HTMLInputElement>(datePicker).value).getTime()
|
||||
).toEqual(now);
|
||||
});
|
||||
|
||||
describe('market states not accepting orders', () => {
|
||||
const states = [
|
||||
Schema.MarketState.STATE_REJECTED,
|
||||
@@ -715,4 +752,80 @@ describe('DealTicket', () => {
|
||||
expect(screen.getByTestId('place-order')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
it('must see warning if price has too many digits after decimal place', async () => {
|
||||
// 7002-SORD-059
|
||||
// Render component
|
||||
render(generateJsx());
|
||||
|
||||
// Elements
|
||||
const toggleLimit = screen.getByTestId('order-type-Limit');
|
||||
const orderTIFDropDown = screen.getByTestId('order-tif');
|
||||
const orderSizeField = screen.getByTestId('order-price');
|
||||
const orderPriceField = screen.getByTestId('order-price');
|
||||
const placeOrderBtn = screen.getByTestId('place-order');
|
||||
|
||||
// Actions
|
||||
await userEvent.click(toggleLimit);
|
||||
await userEvent.selectOptions(orderTIFDropDown, 'TIME_IN_FORCE_GTC');
|
||||
await userEvent.clear(orderSizeField);
|
||||
await userEvent.type(orderSizeField, '1');
|
||||
await userEvent.clear(orderPriceField);
|
||||
await userEvent.type(orderPriceField, '1.123456');
|
||||
await userEvent.click(placeOrderBtn);
|
||||
|
||||
// Expectations
|
||||
await waitFor(() => {
|
||||
const errorMessage = screen.getByTestId(
|
||||
'deal-ticket-error-message-price'
|
||||
);
|
||||
expect(errorMessage).toHaveTextContent(
|
||||
'Price accepts up to 2 decimal places'
|
||||
);
|
||||
});
|
||||
});
|
||||
it('must see warning when placing an order with expiry date in past', async () => {
|
||||
// Render component
|
||||
render(generateJsx());
|
||||
|
||||
const now = Date.now();
|
||||
jest.spyOn(global.Date, 'now').mockImplementation(() => now);
|
||||
// Elements
|
||||
const toggleLimit = screen.getByTestId('order-type-Limit');
|
||||
const orderPriceField = screen.getByTestId('order-price');
|
||||
const orderSizeField = screen.getByTestId('order-price');
|
||||
const orderTIFDropDown = screen.getByTestId('order-tif');
|
||||
const datePicker = 'date-picker-field';
|
||||
const placeOrderBtn = screen.getByTestId('place-order');
|
||||
|
||||
// Actions
|
||||
userEvent.click(toggleLimit);
|
||||
userEvent.clear(orderPriceField);
|
||||
userEvent.type(orderPriceField, '0.1');
|
||||
userEvent.clear(orderSizeField);
|
||||
userEvent.type(orderSizeField, '1');
|
||||
await userEvent.selectOptions(
|
||||
orderTIFDropDown,
|
||||
Schema.OrderTimeInForce.TIME_IN_FORCE_GTT
|
||||
);
|
||||
|
||||
// Set date to past
|
||||
const expiresAt = new Date(now - 24 * 60 * 60 * 1000);
|
||||
const expiresAtInputValue = formatForInput(expiresAt);
|
||||
fireEvent.change(screen.getByTestId(datePicker), {
|
||||
target: { value: expiresAtInputValue },
|
||||
});
|
||||
|
||||
// Place order
|
||||
userEvent.click(placeOrderBtn);
|
||||
|
||||
// Expectations
|
||||
await waitFor(() => {
|
||||
const errorMessage = screen.getByTestId(
|
||||
'deal-ticket-error-message-expiry'
|
||||
);
|
||||
expect(errorMessage).toHaveTextContent(
|
||||
'The expiry date that you have entered appears to be in the past'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -264,7 +264,11 @@ export const DealTicket = ({
|
||||
orders,
|
||||
collateralAvailable:
|
||||
marginAccountBalance || generalAccountBalance ? balance : undefined,
|
||||
skip: !normalizedOrder,
|
||||
skip:
|
||||
!normalizedOrder ||
|
||||
(normalizedOrder.type !== Schema.OrderType.TYPE_MARKET &&
|
||||
(!normalizedOrder.price || normalizedOrder.price === '0')) ||
|
||||
normalizedOrder.size === '0',
|
||||
});
|
||||
|
||||
const assetSymbol = getAsset(market).symbol;
|
||||
|
||||
@@ -114,6 +114,20 @@ export const TradingModeTooltip = ({
|
||||
</section>
|
||||
);
|
||||
}
|
||||
case Schema.MarketTradingMode.TRADING_MODE_SUSPENDED_VIA_GOVERNANCE: {
|
||||
return (
|
||||
<section data-testid="trading-mode-suspended-via-governance">
|
||||
{t(
|
||||
`This market has been suspended via a governance vote and can be resumed or terminated by further votes.`
|
||||
)}
|
||||
{DocsLinks && (
|
||||
<ExternalLink href={DocsLinks.MARKET_LIFECYCLE} className="ml-1">
|
||||
{t('Find out more')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
case Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION: {
|
||||
switch (trigger) {
|
||||
case Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET: {
|
||||
|
||||
@@ -30,9 +30,11 @@ export const usePositionEstimate = ({
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
if (skip) {
|
||||
setEstimates(undefined);
|
||||
} else if (data) {
|
||||
setEstimates(data);
|
||||
}
|
||||
}, [data]);
|
||||
}, [data, skip]);
|
||||
return estimates;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,9 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
|
||||
positionDecimalPlaces: 1,
|
||||
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
state: Schema.MarketState.STATE_ACTIVE,
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
marketTimestamps: {
|
||||
__typename: 'MarketTimestamps',
|
||||
close: '',
|
||||
|
||||
@@ -417,6 +417,12 @@ function compileFeatureFlags(): FeatureFlags {
|
||||
REFERRALS: TRUTHY.includes(
|
||||
windowOrDefault('NX_REFERRALS', process.env['NX_REFERRALS']) as string
|
||||
),
|
||||
UPDATE_MARKET_STATE: TRUTHY.includes(
|
||||
windowOrDefault(
|
||||
'NX_UPDATE_MARKET_STATE',
|
||||
process.env['NX_UPDATE_MARKET_STATE']
|
||||
) as string
|
||||
),
|
||||
};
|
||||
const EXPLORER_FLAGS = {
|
||||
EXPLORER_ASSETS: TRUTHY.includes(
|
||||
|
||||
@@ -80,6 +80,7 @@ export const DocsLinks = VEGA_DOCS_URL
|
||||
LIQUIDITY: `${VEGA_DOCS_URL}/concepts/liquidity/provision`,
|
||||
WITHDRAWAL_LIMITS: `${VEGA_DOCS_URL}/concepts/assets/deposits-withdrawals#withdrawal-limits`,
|
||||
VALIDATOR_SCORES_REWARDS: `${VEGA_DOCS_URL}/concepts/vega-chain/validator-scores-and-rewards`,
|
||||
MARKET_LIFECYCLE: `${VEGA_DOCS_URL}/concepts/trading-on-vega/market-lifecycle`,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export type CosmicElevatorFlags = Pick<
|
||||
| 'PRODUCT_PERPETUALS'
|
||||
| 'METAMASK_SNAPS'
|
||||
| 'REFERRALS'
|
||||
| 'UPDATE_MARKET_STATE'
|
||||
>;
|
||||
export type Configuration = z.infer<typeof tomlConfigSchema>;
|
||||
export const CUSTOM_NODE_KEY = 'custom' as const;
|
||||
|
||||
@@ -79,6 +79,7 @@ const COSMIC_ELEVATOR_FLAGS = {
|
||||
PRODUCT_PERPETUALS: z.optional(z.boolean()),
|
||||
METAMASK_SNAPS: z.optional(z.boolean()),
|
||||
REFERRALS: z.optional(z.boolean()),
|
||||
UPDATE_MARKET_STATE: z.optional(z.boolean()),
|
||||
};
|
||||
|
||||
const EXPLORER_FLAGS = {
|
||||
|
||||
@@ -42,6 +42,9 @@ export const generateFill = (override?: PartialDeep<Trade>) => {
|
||||
decimalPlaces: 5,
|
||||
state: MarketState.STATE_ACTIVE,
|
||||
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
fees: {
|
||||
__typename: 'Fees',
|
||||
factors: {
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+4
-6
@@ -3,23 +3,23 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type MarketDataUpdateFieldsFragment = { __typename?: 'ObservableMarketData', marketId: string, auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, marketValueProxy: string, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, lastTradedPrice: string, productData?: { __typename?: 'PerpetualData', fundingRate?: string | null, fundingPayment?: string | null, externalTwap?: string | null, internalTwap?: string | null } | null, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null };
|
||||
export type MarketDataUpdateFieldsFragment = { __typename?: 'ObservableMarketData', marketId: string, auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, lastTradedPrice: string, productData?: { __typename?: 'PerpetualData', fundingRate?: string | null, fundingPayment?: string | null, externalTwap?: string | null, internalTwap?: string | null } | null, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null };
|
||||
|
||||
export type MarketDataUpdateSubscriptionVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarketDataUpdateSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', marketId: string, auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, marketValueProxy: string, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, lastTradedPrice: string, productData?: { __typename?: 'PerpetualData', fundingRate?: string | null, fundingPayment?: string | null, externalTwap?: string | null, internalTwap?: string | null } | null, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null }> };
|
||||
export type MarketDataUpdateSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', marketId: string, auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, lastTradedPrice: string, productData?: { __typename?: 'PerpetualData', fundingRate?: string | null, fundingPayment?: string | null, externalTwap?: string | null, internalTwap?: string | null } | null, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null }> };
|
||||
|
||||
export type MarketDataFieldsFragment = { __typename?: 'MarketData', auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, marketValueProxy: string, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, lastTradedPrice: string, market: { __typename?: 'Market', id: string }, productData?: { __typename?: 'PerpetualData', fundingRate?: string | null, fundingPayment?: string | null, externalTwap?: string | null, internalTwap?: string | null } | null, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null };
|
||||
export type MarketDataFieldsFragment = { __typename?: 'MarketData', auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, lastTradedPrice: string, market: { __typename?: 'Market', id: string }, productData?: { __typename?: 'PerpetualData', fundingRate?: string | null, fundingPayment?: string | null, externalTwap?: string | null, internalTwap?: string | null } | null, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null };
|
||||
|
||||
export type MarketDataQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarketDataQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', data?: { __typename?: 'MarketData', auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, marketValueProxy: string, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, lastTradedPrice: string, market: { __typename?: 'Market', id: string }, productData?: { __typename?: 'PerpetualData', fundingRate?: string | null, fundingPayment?: string | null, externalTwap?: string | null, internalTwap?: string | null } | null, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null } | null } }> } | null };
|
||||
export type MarketDataQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', data?: { __typename?: 'MarketData', auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, lastTradedPrice: string, market: { __typename?: 'Market', id: string }, productData?: { __typename?: 'PerpetualData', fundingRate?: string | null, fundingPayment?: string | null, externalTwap?: string | null, internalTwap?: string | null } | null, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null } | null } }> } | null };
|
||||
|
||||
export const MarketDataUpdateFieldsFragmentDoc = gql`
|
||||
fragment MarketDataUpdateFields on ObservableMarketData {
|
||||
@@ -46,7 +46,6 @@ export const MarketDataUpdateFieldsFragmentDoc = gql`
|
||||
indicativeVolume
|
||||
marketState
|
||||
marketTradingMode
|
||||
marketValueProxy
|
||||
markPrice
|
||||
midPrice
|
||||
openInterest
|
||||
@@ -94,7 +93,6 @@ export const MarketDataFieldsFragmentDoc = gql`
|
||||
indicativeVolume
|
||||
marketState
|
||||
marketTradingMode
|
||||
marketValueProxy
|
||||
markPrice
|
||||
midPrice
|
||||
openInterest
|
||||
|
||||
+5
-2
File diff suppressed because one or more lines are too long
@@ -4,6 +4,10 @@ fragment DataSourceFilter on Filter {
|
||||
type
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
|
||||
fragment DataSource on DataSourceSpec {
|
||||
@@ -12,6 +16,37 @@ fragment DataSource on DataSourceSpec {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on EthCallSpec {
|
||||
abi
|
||||
address
|
||||
args
|
||||
method
|
||||
requiredConfirmations
|
||||
normalisers {
|
||||
name
|
||||
expression
|
||||
}
|
||||
trigger {
|
||||
trigger {
|
||||
... on EthTimeTrigger {
|
||||
initial
|
||||
every
|
||||
until
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,226 +6,234 @@ import {
|
||||
import { DataSourceProof, SuccessionLineInfoPanel } from './market-info-panels';
|
||||
import { MockedProvider } from '@apollo/react-testing';
|
||||
import { SuccessorMarketIdsDocument } from '../../__generated__';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
|
||||
jest.mock('../../hooks/use-oracle-markets', () => ({
|
||||
useOracleMarkets: () => [],
|
||||
}));
|
||||
|
||||
describe('DataSourceProof', () => {
|
||||
const ORACLE_PUBKEY =
|
||||
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f';
|
||||
describe('MarketInfoPanels', () => {
|
||||
describe('DataSourceProof', () => {
|
||||
const ORACLE_PUBKEY =
|
||||
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f';
|
||||
|
||||
it('renders message if there are no providers', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionExternal' as const,
|
||||
it('renders message if there are no providers', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration' as const,
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer' as const,
|
||||
signer: {
|
||||
__typename: 'PubKey' as const,
|
||||
key: ORACLE_PUBKEY,
|
||||
__typename: 'DataSourceDefinitionExternal' as const,
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration' as const,
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer' as const,
|
||||
signer: {
|
||||
__typename: 'PubKey' as const,
|
||||
key: ORACLE_PUBKEY,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
render(<DataSourceProof dataSourceSpecId={''} {...props} />);
|
||||
expect(
|
||||
screen.getByText('No oracle proof for termination')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
providers: [],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
render(<DataSourceProof dataSourceSpecId={''} {...props} />);
|
||||
expect(
|
||||
screen.getByText('No oracle proof for termination')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders message if there are no matching proofs', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionExternal' as const,
|
||||
it('renders message if there are no matching proofs', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration' as const,
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer' as const,
|
||||
signer: {
|
||||
__typename: 'PubKey' as const,
|
||||
key: ORACLE_PUBKEY,
|
||||
__typename: 'DataSourceDefinitionExternal' as const,
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration' as const,
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer' as const,
|
||||
signer: {
|
||||
__typename: 'PubKey' as const,
|
||||
key: ORACLE_PUBKEY,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providers: [
|
||||
{
|
||||
name: 'Another oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: 'GOOD' as const,
|
||||
status_reason: '',
|
||||
first_verified: '2022-01-01T00:00:00.000Z',
|
||||
last_verified: '2022-12-31T00:00:00.000Z',
|
||||
type: 'public_key' as const,
|
||||
public_key: 'not-the-pubkey',
|
||||
},
|
||||
proofs: [
|
||||
{
|
||||
format: 'signed_message' as const,
|
||||
available: true,
|
||||
providers: [
|
||||
{
|
||||
name: 'Another oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: 'GOOD' as const,
|
||||
status_reason: '',
|
||||
first_verified: '2022-01-01T00:00:00.000Z',
|
||||
last_verified: '2022-12-31T00:00:00.000Z',
|
||||
type: 'public_key' as const,
|
||||
public_key: 'not-the-pubkey',
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/PubKey-${ORACLE_PUBKEY}.toml`,
|
||||
},
|
||||
],
|
||||
type: 'settlementData' as const,
|
||||
};
|
||||
render(<DataSourceProof dataSourceSpecId={''} {...props} />);
|
||||
expect(
|
||||
screen.getByText('No oracle proof for settlement data')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders message if no data source on market', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'Invalid',
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
// @ts-ignore types are invalid
|
||||
render(<DataSourceProof {...props} />);
|
||||
expect(screen.getByText('Invalid data source')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders conditions for internal data sources', () => {
|
||||
const condition = {
|
||||
__typename: 'Condition' as const,
|
||||
operator: ConditionOperator.OPERATOR_GREATER_THAN,
|
||||
value: '100',
|
||||
};
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionInternal' as const,
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfigurationTime' as const,
|
||||
conditions: [condition],
|
||||
},
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
render(<DataSourceProof dataSourceSpecId={''} {...props} />);
|
||||
expect(screen.getByText('Internal conditions')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
`${ConditionOperatorMapping[condition.operator]} ${condition.value}`
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SuccessionLineInfoPanel', () => {
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: SuccessorMarketIdsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
__typename: 'Query',
|
||||
marketsConnection: {
|
||||
__typename: 'MarketConnection',
|
||||
edges: [
|
||||
proofs: [
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'abc',
|
||||
successorMarketID: 'def',
|
||||
parentMarketID: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'def',
|
||||
successorMarketID: 'ghi',
|
||||
parentMarketID: 'abc',
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'ghi',
|
||||
successorMarketID: null,
|
||||
parentMarketID: 'def',
|
||||
},
|
||||
format: 'signed_message' as const,
|
||||
available: true,
|
||||
type: 'public_key' as const,
|
||||
public_key: 'not-the-pubkey',
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/PubKey-${ORACLE_PUBKEY}.toml`,
|
||||
},
|
||||
],
|
||||
type: 'settlementData' as const,
|
||||
};
|
||||
render(<DataSourceProof dataSourceSpecId={''} {...props} />);
|
||||
expect(
|
||||
screen.getByText('No oracle proof for settlement data')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders message if no data source on market', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'Invalid',
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
// @ts-ignore types are invalid
|
||||
render(<DataSourceProof {...props} />);
|
||||
expect(screen.getByText('Invalid data source')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders conditions for internal data sources', () => {
|
||||
const condition = {
|
||||
__typename: 'Condition' as const,
|
||||
operator: ConditionOperator.OPERATOR_GREATER_THAN,
|
||||
value: '1696238009',
|
||||
};
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionInternal' as const,
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfigurationTime' as const,
|
||||
conditions: [condition],
|
||||
},
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
render(<DataSourceProof dataSourceSpecId={''} {...props} />);
|
||||
expect(screen.getByText('Internal conditions')).toBeInTheDocument();
|
||||
const dateFromUnixTimestamp = condition.value
|
||||
? getDateTimeFormat().format(new Date(parseInt(condition.value)))
|
||||
: '-';
|
||||
expect(
|
||||
screen.getByText(
|
||||
`${
|
||||
ConditionOperatorMapping[condition.operator]
|
||||
} ${dateFromUnixTimestamp}`
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SuccessionLineInfoPanel', () => {
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: SuccessorMarketIdsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
__typename: 'Query',
|
||||
marketsConnection: {
|
||||
__typename: 'MarketConnection',
|
||||
edges: [
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'abc',
|
||||
successorMarketID: 'def',
|
||||
parentMarketID: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'def',
|
||||
successorMarketID: 'ghi',
|
||||
parentMarketID: 'abc',
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'ghi',
|
||||
successorMarketID: null,
|
||||
parentMarketID: 'def',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
];
|
||||
|
||||
it.each([
|
||||
['abc', 1],
|
||||
['def', 2],
|
||||
['ghi', 3],
|
||||
])(
|
||||
'renders succession line for %s (current position %d)',
|
||||
async (id, number) => {
|
||||
render(
|
||||
<MockedProvider mocks={mocks}>
|
||||
<SuccessionLineInfoPanel
|
||||
market={{
|
||||
id,
|
||||
}}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
it.each([
|
||||
['abc', 1],
|
||||
['def', 2],
|
||||
['ghi', 3],
|
||||
])(
|
||||
'renders succession line for %s (current position %d)',
|
||||
async (id, number) => {
|
||||
render(
|
||||
<MockedProvider mocks={mocks}>
|
||||
<SuccessionLineInfoPanel
|
||||
market={{
|
||||
id,
|
||||
}}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
const items = screen.getAllByTestId('succession-line-item');
|
||||
expect(items.length).toBe(3);
|
||||
expect(
|
||||
items[0].querySelector(
|
||||
'[data-testid="succession-line-item-market-id"]'
|
||||
)?.textContent
|
||||
).toBe('abc');
|
||||
expect(
|
||||
items[1].querySelector(
|
||||
'[data-testid="succession-line-item-market-id"]'
|
||||
)?.textContent
|
||||
).toBe('def');
|
||||
expect(
|
||||
items[2].querySelector(
|
||||
'[data-testid="succession-line-item-market-id"]'
|
||||
)?.textContent
|
||||
).toBe('ghi');
|
||||
await waitFor(() => {
|
||||
const items = screen.getAllByTestId('succession-line-item');
|
||||
expect(items.length).toBe(3);
|
||||
expect(
|
||||
items[0].querySelector(
|
||||
'[data-testid="succession-line-item-market-id"]'
|
||||
)?.textContent
|
||||
).toBe('abc');
|
||||
expect(
|
||||
items[1].querySelector(
|
||||
'[data-testid="succession-line-item-market-id"]'
|
||||
)?.textContent
|
||||
).toBe('def');
|
||||
expect(
|
||||
items[2].querySelector(
|
||||
'[data-testid="succession-line-item-market-id"]'
|
||||
)?.textContent
|
||||
).toBe('ghi');
|
||||
|
||||
expect(
|
||||
items[number - 1].querySelector('[data-testid="icon-bullet"]')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
}
|
||||
);
|
||||
expect(
|
||||
items[number - 1].querySelector('[data-testid="icon-bullet"]')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,13 +6,21 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { marketDataProvider } from '../../market-data-provider';
|
||||
import { totalFeesPercentage } from '../../market-utils';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionChevron,
|
||||
AccordionPanel,
|
||||
CopyWithTooltip,
|
||||
ExternalLink,
|
||||
Intent,
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
Lozenge,
|
||||
Splash,
|
||||
SyntaxHighlighter,
|
||||
Tooltip,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
truncateMiddle,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
@@ -31,6 +39,7 @@ import { Last24hVolume } from '../last-24h-volume';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type {
|
||||
DataSourceDefinition,
|
||||
EthCallSpec,
|
||||
MarketTradingMode,
|
||||
SignerKind,
|
||||
} from '@vegaprotocol/types';
|
||||
@@ -40,6 +49,7 @@ import {
|
||||
} from '@vegaprotocol/types';
|
||||
import {
|
||||
DApp,
|
||||
EtherscanLink,
|
||||
FLAGS,
|
||||
TOKEN_PROPOSAL,
|
||||
useEnvironment,
|
||||
@@ -65,6 +75,7 @@ import {
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import type { DataSourceFragment } from './__generated__/MarketInfo';
|
||||
import { formatDuration } from 'date-fns';
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
|
||||
type MarketInfoProps = {
|
||||
market: MarketInfo;
|
||||
@@ -659,6 +670,97 @@ export const LiquidityMonitoringParametersInfoPanel = ({
|
||||
return <MarketInfoTable data={marketData} parentData={parentMarketData} />;
|
||||
};
|
||||
|
||||
export const EthOraclePanel = ({ sourceType }: { sourceType: EthCallSpec }) => {
|
||||
const abis = sourceType.abi?.map((abi) => JSON.parse(abi));
|
||||
const header = 'uppercase my-1 text-left';
|
||||
return (
|
||||
<>
|
||||
<h3 className={header}>{t('Ethereum Oracle')}</h3>
|
||||
{sourceType.address && (
|
||||
<>
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow noBorder>
|
||||
<div>{t('Address')}</div>
|
||||
<CopyWithTooltip text={sourceType.address}>
|
||||
<button
|
||||
data-testid="copy-eth-oracle-address"
|
||||
className="uppercase text-right"
|
||||
>
|
||||
<span className="flex gap-1">
|
||||
{truncateMiddle(sourceType.address)}
|
||||
<VegaIcon name={VegaIconNames.COPY} size={16} />
|
||||
</span>
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
|
||||
<div className="my-2">
|
||||
<EtherscanLink address={sourceType.address}>
|
||||
{t('View on Etherscan')}
|
||||
</EtherscanLink>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<MarketInfoTable
|
||||
key="eth-call-spec"
|
||||
data={{
|
||||
method: sourceType.method,
|
||||
requiredConfirmations: sourceType.requiredConfirmations,
|
||||
}}
|
||||
/>
|
||||
<Accordion>
|
||||
<AccordionPanel
|
||||
itemId="abi"
|
||||
trigger={
|
||||
<AccordionPrimitive.Trigger
|
||||
data-testid="accordion-toggle"
|
||||
className={classNames(
|
||||
'w-full pt-2',
|
||||
'flex items-center gap-2',
|
||||
'group'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
data-testid={`abi-dropdown`}
|
||||
key={'value-dropdown'}
|
||||
className="flex items-center gap-2 w-full"
|
||||
>
|
||||
<div className="underline underline-offset-4 mb-1 uppercase">
|
||||
{t('ABI specification')}
|
||||
</div>
|
||||
<AccordionChevron size={14} />
|
||||
<div className="flex items-center gap-1"></div>
|
||||
</div>
|
||||
</AccordionPrimitive.Trigger>
|
||||
}
|
||||
>
|
||||
<SyntaxHighlighter data={abis} />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
|
||||
<h3 className={header}>{t('Normalisers')}</h3>
|
||||
{sourceType.normalisers?.map((normaliser, i) => (
|
||||
<MarketInfoTable key={i} data={normaliser} />
|
||||
))}
|
||||
<h3 className={header}>{t('Filters')}</h3>
|
||||
<h3 className={header}>{t('Key')}</h3>
|
||||
{sourceType.filters?.map((filter, i) => (
|
||||
<>
|
||||
<MarketInfoTable key={i} data={filter.key} />
|
||||
<h3 className={header}>{t('Conditions')}</h3>
|
||||
{filter.conditions?.map((condition, i) => (
|
||||
<span>
|
||||
{ConditionOperatorMapping[condition.operator]} {condition.value}
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const LiquidityPriceRangeInfoPanel = ({
|
||||
market,
|
||||
parentMarket,
|
||||
@@ -782,7 +884,7 @@ export const LiquiditySLAParametersInfoPanel = ({
|
||||
market.liquiditySLAParameters?.slaCompetitionFactor
|
||||
).times(100)
|
||||
),
|
||||
commitmentMinimumTimeFraction:
|
||||
commitmentMinTimeFraction:
|
||||
market.liquiditySLAParameters?.commitmentMinTimeFraction &&
|
||||
formatNumberPercentage(
|
||||
new BigNumber(
|
||||
@@ -797,7 +899,7 @@ export const LiquiditySLAParametersInfoPanel = ({
|
||||
parentMarket.liquiditySLAParameters?.performanceHysteresisEpochs,
|
||||
slaCompetitionFactor:
|
||||
parentMarket.liquiditySLAParameters?.slaCompetitionFactor,
|
||||
commitmentMinimumTimeFraction:
|
||||
commitmentMinTimeFraction:
|
||||
parentMarket.liquiditySLAParameters?.commitmentMinTimeFraction,
|
||||
}
|
||||
: undefined;
|
||||
@@ -823,13 +925,13 @@ export const LiquiditySLAParametersInfoPanel = ({
|
||||
networkParams['market_liquidity_nonPerformanceBondPenaltySlope'],
|
||||
nonPerformanceBondPenaltyMax:
|
||||
networkParams['market_liquidity_sla_nonPerformanceBondPenaltyMax'],
|
||||
maximumLiquidityFeeFactorLevel:
|
||||
maxLiquidityFeeFactorLevel:
|
||||
networkParams['market_liquidity_maximumLiquidityFeeFactorLevel'],
|
||||
stakeToCCYVolume: networkParams['market_liquidity_stakeToCcyVolume'],
|
||||
earlyExitPenalty: networkParams['market_liquidity_earlyExitPenalty'],
|
||||
probabilityOfTradingTauScaling:
|
||||
networkParams['market_liquidity_probabilityOfTrading_tau_scaling'],
|
||||
minimumProbabilityOfTradingLPOrders:
|
||||
minProbabilityOfTradingLPOrders:
|
||||
networkParams['market_liquidity_minimum_probabilityOfTrading_lpOrders'],
|
||||
feeCalculationTimeStep:
|
||||
networkParams['market_liquidity_feeCalculationTimeStep'] &&
|
||||
@@ -858,7 +960,6 @@ export const LiquidityInfoPanel = ({ market, children }: MarketInfoProps) => {
|
||||
data={{
|
||||
targetStake: data?.targetStake,
|
||||
suppliedStake: data?.suppliedStake,
|
||||
marketValueProxy: data?.marketValueProxy,
|
||||
}}
|
||||
decimalPlaces={asset.decimals}
|
||||
assetSymbol={asset.symbol}
|
||||
@@ -932,6 +1033,10 @@ export const OracleInfoPanel = ({
|
||||
</Lozenge>
|
||||
)}
|
||||
|
||||
{dataSourceSpec?.sourceType.sourceType.__typename === 'EthCallSpec' && (
|
||||
<EthOraclePanel sourceType={dataSourceSpec?.sourceType.sourceType} />
|
||||
)}
|
||||
|
||||
<div className={wrapperClasses}>
|
||||
{shouldShowParentData &&
|
||||
parentDataSourceSpec &&
|
||||
@@ -946,6 +1051,13 @@ export const OracleInfoPanel = ({
|
||||
dataSourceSpecId={parentDataSourceSpecId}
|
||||
/>
|
||||
|
||||
{parentDataSourceSpec?.sourceType.sourceType.__typename ===
|
||||
'EthCallSpec' && (
|
||||
<EthOraclePanel
|
||||
sourceType={parentDataSourceSpec?.sourceType.sourceType}
|
||||
/>
|
||||
)}
|
||||
|
||||
{dataSourceSpecId && (
|
||||
<ExternalLink
|
||||
data-testid="oracle-spec-links"
|
||||
@@ -1033,9 +1145,13 @@ export const DataSourceProof = ({
|
||||
<h3>{t('Internal conditions')}</h3>
|
||||
{data.sourceType.sourceType?.conditions?.map((condition, i) => {
|
||||
if (!condition) return null;
|
||||
const dateFromUnixTimestamp = condition.value
|
||||
? getDateTimeFormat().format(new Date(parseInt(condition.value)))
|
||||
: '-';
|
||||
return (
|
||||
<p key={i}>
|
||||
{ConditionOperatorMapping[condition.operator]} {condition.value}
|
||||
{ConditionOperatorMapping[condition.operator]}{' '}
|
||||
{dateFromUnixTimestamp}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -106,7 +106,7 @@ export const tooltipMapping: Record<string, ReactNode> = {
|
||||
insurancePoolFraction: t(
|
||||
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
|
||||
),
|
||||
commitmentMinimumTimeFraction: t(
|
||||
commitmentMinTimeFraction: t(
|
||||
`Specifies the minimum fraction of time LPs must spend 'on the book' providing their committed liquidity. This is a market parameter.`
|
||||
),
|
||||
feeCalculationTimeStep: t(
|
||||
@@ -127,7 +127,7 @@ export const tooltipMapping: Record<string, ReactNode> = {
|
||||
nonPerformanceBondPenaltyMax: t(
|
||||
`The maximum amount, as a fraction, that an LP's bond can be slashed by if they fail to reach the minimum SLA. This is a network parameter.`
|
||||
),
|
||||
maximumLiquidityFeeFactorLevel: t(
|
||||
maxLiquidityFeeFactorLevel: t(
|
||||
'Maximum value that a proposed fee amount can be, which is submitted as part of the LP commitment transaction. Note that a value of 0.05 = 5%. This is a network parameter.'
|
||||
),
|
||||
stakeToCCYVolume: t(
|
||||
@@ -137,12 +137,12 @@ export const tooltipMapping: Record<string, ReactNode> = {
|
||||
'How long an epoch is. LP rewards from liquidity fees are paid out once per epoch. How much they receive depends on whether they met the liquidity SLA and their previous performance in recent epochs. This is a network parameter.'
|
||||
),
|
||||
earlyExitPenalty: t(
|
||||
`How much an LP forfeits of their bond if they reduce their commitment while the market is below target stake, expressed as a factor. If set to 0 there is no penalty for early exit. If set to 1 an LP's entire bond is forfeited when an LP removes their full commitment. This is a network parameter.`
|
||||
`The percentage of their bond an LP forfeits if they reduce their commitment while the market is below target stake. If 100%, an LP's entire bond is forfeited when they cancel their full commitment. This is a network parameter.`
|
||||
),
|
||||
probabilityOfTradingTauScaling: t(
|
||||
`Determines how the probability of trading is scaled from the risk model, and is used to measure the relative competitiveness of an LP's supplied volume. This is a network parameter.`
|
||||
),
|
||||
minimumProbabilityOfTradingLPOrders: t(
|
||||
minProbabilityOfTradingLPOrders: t(
|
||||
'The lower bound for the probability of trading calculation, used to measure liquidity available on a market to determine if LPs are meeting their commitment. This is a network parameter.'
|
||||
),
|
||||
};
|
||||
|
||||
@@ -22,7 +22,6 @@ fragment MarketDataUpdateFields on ObservableMarketData {
|
||||
indicativeVolume
|
||||
marketState
|
||||
marketTradingMode
|
||||
marketValueProxy
|
||||
markPrice
|
||||
midPrice
|
||||
openInterest
|
||||
@@ -75,7 +74,6 @@ fragment MarketDataFields on MarketData {
|
||||
indicativeVolume
|
||||
marketState
|
||||
marketTradingMode
|
||||
marketValueProxy
|
||||
markPrice
|
||||
midPrice
|
||||
openInterest
|
||||
|
||||
@@ -58,7 +58,6 @@ const marketDataFields: MarketDataFieldsFragment = {
|
||||
indicativeVolume: '0',
|
||||
marketState: Schema.MarketState.STATE_ACTIVE,
|
||||
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
marketValueProxy: '2000000',
|
||||
markPrice: '4612690058',
|
||||
midPrice: '4612690000',
|
||||
openInterest: '0',
|
||||
@@ -97,7 +96,6 @@ const marketDataUpdateFields: MarketDataUpdateFieldsFragment = {
|
||||
marketId: 'market-0',
|
||||
marketState: Schema.MarketState.STATE_ACTIVE,
|
||||
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
marketValueProxy: '',
|
||||
markPrice: '4612690058',
|
||||
midPrice: '0',
|
||||
lastTradedPrice: '0',
|
||||
|
||||
@@ -35,6 +35,9 @@ fragment MarketFields on Market {
|
||||
open
|
||||
close
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
}
|
||||
}
|
||||
|
||||
query Markets {
|
||||
|
||||
@@ -53,6 +53,9 @@ export const createMarketFragment = (
|
||||
liquidityFee: '',
|
||||
},
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
id: '',
|
||||
|
||||
@@ -20,6 +20,9 @@ export const generateOrder = (partialOrder?: PartialDeep<Order>) => {
|
||||
makerFee: '0.1',
|
||||
},
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
marketTimestamps: {
|
||||
__typename: 'MarketTimestamps',
|
||||
close: '',
|
||||
|
||||
@@ -21,6 +21,9 @@ export const generateStopOrder = (
|
||||
__typename: 'Market',
|
||||
id: 'market-id',
|
||||
decimalPlaces: 1,
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '0.7',
|
||||
},
|
||||
fees: {
|
||||
__typename: 'Fees',
|
||||
factors: {
|
||||
|
||||
@@ -79,6 +79,7 @@ export const OrderListTable = memo<
|
||||
() => [
|
||||
{
|
||||
headerName: t('Market'),
|
||||
colId: 'instrument-code',
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
cellRenderer: 'MarketNameCell',
|
||||
cellRendererParams: { idPath: 'market.id', onMarketClick },
|
||||
|
||||
@@ -31,6 +31,9 @@ describe('OrderViewDialog', () => {
|
||||
liquidityFee: '0.001',
|
||||
},
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { PositionsManager } from './positions-manager';
|
||||
import { positionsMarketsProvider } from './positions-data-providers';
|
||||
import { singleRow } from './positions.mock';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MAXGOINT64 } from '@vegaprotocol/utils';
|
||||
|
||||
const mockCreate = jest.fn();
|
||||
jest.mock('@vegaprotocol/wallet', () => ({
|
||||
...jest.requireActual('@vegaprotocol/wallet'),
|
||||
useVegaWallet: jest.fn(() => ({ pubKey: 'partyId' })),
|
||||
useVegaTransactionStore: jest.fn(() => mockCreate),
|
||||
}));
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockUseDataProvider = (args: any) => {
|
||||
if (args.dataProvider === positionsMarketsProvider) {
|
||||
return { data: ['market-1', 'market-2'] };
|
||||
}
|
||||
return { data: [singleRow] };
|
||||
};
|
||||
jest.mock('@vegaprotocol/data-provider', () => ({
|
||||
...jest.requireActual('@vegaprotocol/data-provider'),
|
||||
useDataProvider: jest.fn((args) => mockUseDataProvider(args)),
|
||||
}));
|
||||
|
||||
describe('PositionsManager', () => {
|
||||
it('should close position with max uint64', async () => {
|
||||
render(<PositionsManager partyIds={['partyId']} isReadOnly={false} />, {
|
||||
wrapper: MockedProvider,
|
||||
});
|
||||
expect(await screen.getByTestId('close-position')).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByTestId('close-position'));
|
||||
|
||||
expect(
|
||||
mockCreate.mock.lastCall[0].batchMarketInstructions.submissions[0].size
|
||||
).toEqual(MAXGOINT64);
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user