Compare commits

..
134 changed files with 1011 additions and 2382 deletions
+8
View File
@@ -10,6 +10,7 @@ on:
- opened
- ready_for_review
- reopened
- edited
- synchronize
jobs:
node-modules:
@@ -43,6 +44,13 @@ 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
+7 -41
View File
@@ -9,37 +9,23 @@ 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: 8-cores
timeout-minutes: 20
runs-on: console-test
timeout-minutes: 40
steps:
#----------------------------------------------
# check-out frontend-monorepo
#----------------------------------------------
- name: Checkout frontend-monorepo
- name: Checkout console test repo
uses: actions/checkout@v3
with:
ref: ${{ inputs.github-sha || github.sha }}
ref: ${{ inputs.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
@@ -82,18 +68,7 @@ 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
#----------------------------------------------
@@ -121,7 +96,8 @@ jobs:
#----------------------------------------------
- name: Install vega binaries
working-directory: ./console-test
run: poetry run python -m vega_sim.tools.load_binaries --force --version ${{ env.VEGA_VERSION }}
if: steps.vega_binaries_cache.outputs.cache-hit != 'true'
run: poetry run python -m vega_sim.tools.load_binaries --force --version $VEGA_VERSION
#----------------------------------------------
# install playwright
#----------------------------------------------
@@ -133,7 +109,7 @@ jobs:
#----------------------------------------------
- name: Run tests
working-directory: ./console-test
run: poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
run: poetry run pytest -v -s --numprocesses 2 --dist loadfile --durations=20
- name: Check files
run: |
ls -al .
@@ -148,13 +124,3 @@ 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
+1 -23
View File
@@ -13,35 +13,13 @@ 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 }}
needs: runner-choice
runs-on: ${{ needs.runner-choice.outputs.runner }}
runs-on: self-hosted-runner
timeout-minutes: 120
steps:
# Checks if skip cache was requested
+11 -11
View File
@@ -2,12 +2,7 @@
name: Verify PR title
on:
pull_request:
types:
- opened
- edited
- reopened
- synchronize
workflow_call:
jobs:
lint_pr:
@@ -16,16 +11,21 @@ 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: 16
node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
- name: Install dependencies
run: |
rm package.json
npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx
- name: Cache node modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
- name: Check PR title
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
+5 -11
View File
@@ -30,18 +30,12 @@ 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: ${{ github.ref_name == 'develop' && github.event_name == 'push' && matrix.app == 'trading' }}
if: ${{ contains(github.ref, '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: |
@@ -87,7 +81,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' || env.IS_MAIN_IMAGE == 'true' }}
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -185,7 +179,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' || env.IS_MAIN_IMAGE == 'true' }}
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
@@ -195,7 +189,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' || env.IS_MAIN_IMAGE == 'true && main' || '' }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || '' }}
- name: Publish dist as docker image (ghcr - retry)
uses: docker/build-push-action@v3
@@ -222,7 +216,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' || env.IS_MAIN_IMAGE == 'true && main' || '' }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
# bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3
@@ -87,6 +87,7 @@ 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',
@@ -94,6 +95,7 @@ context('Market page', { tags: '@regression' }, function () {
);
cy.validate_element_from_table('Lowest Price', '0.00 fUSDC');
cy.validate_element_from_table('Highest Price', '0.00 fUSDC');
cy.getByTestId('oracle-spec-links')
.should('have.attr', 'href')
.and(
@@ -142,8 +144,11 @@ 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,12 +129,6 @@ function getSuccessorTxBody(parentMarketId) {
parentMarketId: parentMarketId,
insurancePoolFraction: '0.75',
},
liquiditySlaParameters: {
priceRange: '0.95',
commitmentMinTimeFraction: '0.5',
performanceHysteresisEpochs: 2,
slaCompetitionFactor: '0.75',
},
},
},
closingTimestamp,
@@ -1,8 +1,6 @@
import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import {
LiquidityPriceRangeInfoPanel,
LiquiditySLAParametersInfoPanel,
PriceMonitoringBoundsInfoPanel,
SuccessionLineInfoPanel,
getDataSourceSpecForSettlementData,
@@ -96,10 +94,6 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
)}
<h2 className={headerClassName}>{t('Liquidity monitoring')}</h2>
<LiquidityMonitoringParametersInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity price range')}</h2>
<LiquidityPriceRangeInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity SLA protocol')}</h2>
<LiquiditySLAParametersInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity')}</h2>
<LiquidityInfoPanel market={market} />
{showTwoOracles ? (
-1
View File
@@ -21,7 +21,6 @@ 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_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
-2
View File
@@ -4,5 +4,3 @@ 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
-2
View File
@@ -4,5 +4,3 @@ 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
-2
View File
@@ -4,5 +4,3 @@ 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,138 +0,0 @@
{
"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"
}
}
}
}
@@ -9,7 +9,6 @@ import {
createTenDigitUnixTimeStampForSpecifiedDays,
generateFreeFormProposalTitle,
getDateFormatForSpecifiedDays,
getProposalDetailsValue,
getProposalFromTitle,
getProposalInformationFromTable,
goToMakeNewProposal,
@@ -51,7 +50,6 @@ 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',
@@ -374,65 +372,19 @@ describe(
});
// 3003-PMAN-011
cy.contains('Parent Market ID').realHover();
cy.get('.underline').contains('Parent Market ID').realHover();
cy.getByTestId('tooltip-content', { timeout: 8000 }).should(
'contain.text',
'The ID of the market this market succeeds.'
);
cy.contains('Insurance Pool Fraction').realMouseUp().realHover();
cy.get('.underline')
.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');
});
}
);
@@ -65,10 +65,7 @@ context(
cy.getByTestId(viewProposalButton).click();
});
});
cy.getByTestId('proposal-type').should(
'have.text',
'New market - future'
);
cy.getByTestId('proposal-type').should('have.text', 'New market');
cy.getByTestId(proposalStatus).should('have.text', 'Enacted');
cy.getByTestId(votesTable).within(() => {
cy.contains('Voting has ended.').should('be.visible');
@@ -89,12 +89,11 @@ 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 enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
const proposalTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
submitUniqueRawProposal({
proposalBody: proposalPath,
enactmentTimestamp: enactmentTimestamp,
closingTimestamp: closingTimestamp,
enactmentTimestamp: proposalTimestamp,
closingTimestamp: proposalTimestamp,
}); // 3001-VOTE-052
// 3001-VOTE-008
// 3001-VOTE-034
@@ -232,25 +232,21 @@ 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
) {
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');
});
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');
});
}
function getFormattedTime() {
@@ -108,7 +108,7 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
liquiditySlaParameters: {
priceRange: '0.5',
commitmentMinTimeFraction: '0.1',
performanceHysteresisEpochs: 2,
performanceHysteresisEpochs: 0,
slaCompetitionFactor: '0.1',
},
quadraticSlippageFactor: '0',
@@ -243,7 +243,7 @@ export function createSuccessorMarketProposalTxBody(
liquiditySlaParameters: {
priceRange: '0.5',
commitmentMinTimeFraction: '0.1',
performanceHysteresisEpochs: 2,
performanceHysteresisEpochs: 0,
slaCompetitionFactor: '0.1',
},
instrument: {
-2
View File
@@ -32,5 +32,3 @@ 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
-2
View File
@@ -33,5 +33,3 @@ CYPRESS_FAIRGROUND=false
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false
NX_METAMASK_SNAPS=false
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
-2
View File
@@ -25,5 +25,3 @@ 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
-2
View File
@@ -24,5 +24,3 @@ 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
-2
View File
@@ -23,5 +23,3 @@ 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
-2
View File
@@ -20,5 +20,3 @@ 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
-2
View File
@@ -25,5 +25,3 @@ 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
-2
View File
@@ -22,5 +22,3 @@ 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
+1 -1
View File
@@ -308,7 +308,7 @@ const AppContainer = () => {
<Router>
<ScrollToTop />
<AppStateProvider>
<div className="min-h-full text-white grid">
<div className="min-h-full text-white">
<NodeGuard
skeleton={<div>{t('Loading')}</div>}
failure={
@@ -613,9 +613,6 @@
"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",
@@ -708,17 +705,10 @@
"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",
@@ -727,11 +717,7 @@
"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",
+7 -16
View File
@@ -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, FLAGS } from '@vegaprotocol/environment';
import { ExternalLinks } from '@vegaprotocol/environment';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
@@ -23,6 +23,7 @@ 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';
@@ -31,7 +32,6 @@ 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: ProposalQuery['proposal'][];
proposals: ProposalFieldsFragment[];
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
}) => {
const { t } = useTranslation();
@@ -60,12 +60,9 @@ const HomeProposals = ({
<ProtocolUpgradeProposalsListItem key={index} proposal={proposal} />
))}
{proposals.map(
(proposal) =>
proposal?.id && (
<ProposalsListItem key={proposal.id} proposal={proposal} />
)
)}
{proposals.map((proposal) => (
<ProposalsListItem key={proposal.id} proposal={proposal} />
))}
</ul>
<div className="mt-6">
@@ -185,10 +182,6 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
pollInterval: 5000,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
},
});
const {
@@ -213,9 +206,7 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
const proposals = useMemo(
() =>
proposalsData
? getNotRejectedProposals(
removePaginationWrapper(proposalsData.proposalsConnection?.edges)
)
? getNotRejectedProposals(proposalsData.proposalsConnection)
: [],
[proposalsData]
);
@@ -3,6 +3,7 @@ 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';
@@ -19,7 +20,7 @@ export const ProposalHeader = ({
isListItem = true,
voteState,
}: {
proposal: ProposalQuery['proposal'];
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
isListItem?: boolean;
voteState?: VoteState | null;
}) => {
@@ -36,10 +37,7 @@ export const ProposalHeader = ({
switch (change?.__typename) {
case 'NewMarket': {
proposalType =
FLAGS.PRODUCT_PERPETUALS && change?.instrument?.product?.__typename
? `NewMarket${change?.instrument?.product?.__typename}`
: 'NewMarket';
proposalType = 'NewMarket';
fallbackTitle = t('NewMarketProposal');
details = (
<>
@@ -63,31 +61,12 @@ 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('MarketChange')}:</span>{' '}
<span>{t('Market change')}:</span>{' '}
<span>{truncateMiddle(change.marketId)}</span>
</>
);
@@ -5,8 +5,6 @@ import {
InstrumentInfoPanel,
KeyDetailsInfoPanel,
LiquidityMonitoringParametersInfoPanel,
LiquidityPriceRangeInfoPanel,
LiquiditySLAParametersInfoPanel,
MetadataInfoPanel,
OracleInfoPanel,
PriceMonitoringBoundsInfoPanel,
@@ -272,21 +270,6 @@ export const ProposalMarketData = ({
market={marketData}
parentMarket={parentMarketData}
/>
<h2 className={marketDataHeaderStyles}>
{t('Liquidity price range')}
</h2>
<LiquidityPriceRangeInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
<h2 className={marketDataHeaderStyles}>
{t('Liquidity SLA protocol')}
</h2>
<LiquiditySLAParametersInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
</div>
</>
)}
@@ -1 +0,0 @@
export * from './proposal-update-market-state';
@@ -1,123 +0,0 @@
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();
});
});
@@ -1,84 +0,0 @@
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: ProposalQuery['proposal'];
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
networkParams: Partial<NetworkParamsResult>;
marketData?: MarketInfo | null;
newMarketData?: 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,
marketData,
newMarketData,
parentMarketData,
assetData,
originalMarketProposalRestData,
@@ -74,15 +74,14 @@ 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;
@@ -146,21 +145,15 @@ export const Proposal = ({
<ProposalDescription description={proposal.rationale.description} />
</div>
{marketData && (
{newMarketData && (
<div className="mb-4">
<ProposalMarketData
marketData={marketData}
marketData={newMarketData}
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
@@ -2,10 +2,11 @@ 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?: ProposalQuery['proposal'] | null;
proposal?: ProposalFieldsFragment | 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<ProposalQuery['proposal']>;
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
lastBlockHeight?: string;
}
interface SortedProposalsProps {
open: ProposalQuery['proposal'][];
closed: ProposalQuery['proposal'][];
open: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
closed: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
}
interface SortedProtocolUpgradeProposalsProps {
@@ -31,7 +31,7 @@ interface SortedProtocolUpgradeProposalsProps {
closed: ProtocolUpgradeProposalFieldsFragment[];
}
export const orderByDate = (arr: ProposalQuery['proposal'][]) =>
export const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
@@ -92,12 +92,12 @@ export const ProposalsList = ({
return {
open:
initialSorting.open.length > 0
? orderByDate(initialSorting.open as ProposalQuery['proposal'][])
? orderByDate(initialSorting.open as ProposalFieldsFragment[])
: [],
closed:
initialSorting.closed.length > 0
? orderByDate(
initialSorting.closed as ProposalQuery['proposal'][]
initialSorting.closed as ProposalFieldsFragment[]
).reverse()
: [],
};
@@ -3,17 +3,20 @@ 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: ProposalQuery['proposal'][];
proposals: Array<ProposalQuery['proposal'] | ProposalFieldsFragment>;
}
export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
const { t } = useTranslation();
const [filterString, setFilterString] = useState('');
const filterPredicate = (p: ProposalQuery['proposal']) =>
const filterPredicate = (
p: ProposalFieldsFragment | ProposalQuery['proposal']
) =>
p?.id?.includes(filterString) ||
p?.party?.id?.toString().includes(filterString);
@@ -28,20 +28,17 @@ 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 fallback;
return {
requiredMajority: new BigNumber(1),
requiredMajorityLP: new BigNumber(0),
requiredParticipation: new BigNumber(1),
requiredParticipationLP: new BigNumber(0),
};
}
switch (proposal?.terms.change.__typename) {
case 'UpdateMarket':
case 'UpdateMarketState':
return {
requiredMajority:
params.governance_proposal_updateMarket_requiredMajority,
@@ -92,6 +89,6 @@ export const useProposalNetworkParams = ({
),
};
default:
return fallback;
throw new Error('Unknown proposal type');
}
};
@@ -1,53 +1,4 @@
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!
) {
query Proposal($proposalId: ID!) {
proposal(id: $proposalId) {
id
rationale {
@@ -62,8 +13,6 @@ query Proposal(
id
}
errorDetails
...NewMarketProductField @include(if: $includeNewMarketProductField)
...UpdateMarketState @include(if: $includeUpdateMarketState)
terms {
closingDatetime
enactmentDatetime
File diff suppressed because one or more lines are too long
@@ -6,11 +6,6 @@ import { MockedProvider } from '@apollo/client/testing';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { ProposalDocument } from './__generated__/Proposal';
jest.mock('@vegaprotocol/data-provider', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useDataProvider: jest.fn(() => ({ data: [], loading: false })),
}));
jest.mock('../components/proposal', () => ({
Proposal: () => <div data-testid="proposal" />,
}));
@@ -49,9 +44,7 @@ const renderComponent = (
);
};
// These tests are broken due to schema changes. NewMarket.futureProduct -> NewMarket.product union
// eslint-disable-next-line jest/no-disabled-tests
describe.skip('Proposal container', () => {
describe('Proposal container', () => {
it('Renders not found if the proposal is not found', async () => {
render(renderComponent(null, 'foo'));
await waitFor(() => {
@@ -53,11 +53,7 @@ export const ProposalContainer = () => {
const { data, loading, error, refetch } = useProposalQuery({
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
proposalId: params.proposalId || '',
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
},
variables: { proposalId: params.proposalId || '' },
skip: !params.proposalId,
});
@@ -95,9 +91,9 @@ export const ProposalContainer = () => {
);
const {
data: marketData,
loading: marketLoading,
error: marketError,
data: newMarketData,
loading: newMarketLoading,
error: newMarketError,
} = useDataProvider({
dataProvider: marketInfoProvider,
skipUpdates: true,
@@ -113,9 +109,9 @@ export const ProposalContainer = () => {
error: parentMarketIdError,
} = useParentMarketIdQuery({
variables: {
marketId: marketData?.id || '',
marketId: newMarketData?.id || '',
},
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !newMarketData?.id,
});
const {
@@ -195,7 +191,7 @@ export const ProposalContainer = () => {
<AsyncRenderer
loading={
loading ||
marketLoading ||
newMarketLoading ||
assetLoading ||
networkParamsLoading ||
parentMarketIdLoading ||
@@ -210,7 +206,7 @@ export const ProposalContainer = () => {
}
error={
error ||
marketError ||
newMarketError ||
assetError ||
networkParamsError ||
parentMarketIdError ||
@@ -222,7 +218,7 @@ export const ProposalContainer = () => {
data={{
...data,
...networkParams,
...(marketData ? { newMarketData: marketData } : {}),
...(newMarketData ? { newMarketData } : {}),
...(parentMarketData ? { parentMarketData } : {}),
...(assetData ? { assetData } : {}),
...(restData ? { restData } : {}),
@@ -239,7 +235,7 @@ export const ProposalContainer = () => {
proposal={data.proposal}
networkParams={networkParams}
restData={restData}
marketData={marketData}
newMarketData={newMarketData}
parentMarketData={parentMarketData}
assetData={assetData}
originalMarketProposalRestData={originalMarketProposalRestData}
@@ -1,48 +1,3 @@
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 {
@@ -124,16 +79,11 @@ fragment ProposalFields on Proposal {
}
}
query Proposals(
$includeNewMarketProductFields: Boolean!
$includeUpdateMarketStates: Boolean!
) {
query Proposals {
proposalsConnection {
edges {
node {
...ProposalFields
...NewMarketProductFields @include(if: $includeNewMarketProductFields)
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
}
}
}
@@ -3,67 +3,13 @@ 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<{
includeNewMarketProductFields: Types.Scalars['Boolean'];
includeUpdateMarketStates: Types.Scalars['Boolean'];
}>;
export type ProposalsQueryVariables = Types.Exact<{ [key: string]: never; }>;
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 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 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
@@ -147,20 +93,16 @@ export const ProposalFieldsFragmentDoc = gql`
}
`;
export const ProposalsDocument = gql`
query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!) {
query Proposals {
proposalsConnection {
edges {
node {
...ProposalFields
...NewMarketProductFields @include(if: $includeNewMarketProductFields)
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
}
}
}
}
${ProposalFieldsFragmentDoc}
${NewMarketProductFieldsFragmentDoc}
${UpdateMarketStatesFragmentDoc}`;
${ProposalFieldsFragmentDoc}`;
/**
* __useProposalsQuery__
@@ -174,12 +116,10 @@ ${UpdateMarketStatesFragmentDoc}`;
* @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, removePaginationWrapper } from '@vegaprotocol/utils';
import { getNodes } from '@vegaprotocol/utils';
import {
ProposalState,
ProtocolUpgradeProposalStatus,
@@ -15,13 +15,14 @@ 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(data?: ProposalFieldsFragment[]) {
export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
data?: NodeConnection<NodeEdge<T>> | null
): T[] {
return flow([
(data) =>
data.filter(
(p: ProposalFieldsFragment) => p?.state !== ProposalState.STATE_REJECTED
getNodes<ProposalFieldsFragment>(data, (p) =>
p ? p.state !== ProposalState.STATE_REJECTED : false
),
])(data);
}
@@ -46,10 +47,6 @@ export const ProposalsContainer = () => {
pollInterval: 5000,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
},
});
const {
@@ -63,10 +60,7 @@ export const ProposalsContainer = () => {
});
const proposals = useMemo(
() =>
getNotRejectedProposals(
removePaginationWrapper(data?.proposalsConnection?.edges)
),
() => getNotRejectedProposals(data?.proposalsConnection),
[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 { removePaginationWrapper } from '@vegaprotocol/utils';
import type { NodeConnection, NodeEdge } from '@vegaprotocol/utils';
import { getNodes } 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,11 +22,13 @@ const orderByDate = (arr: ProposalFieldsFragment[]) =>
['desc', 'desc']
);
export function getRejectedProposals(data?: ProposalFieldsFragment[] | null) {
export function getRejectedProposals<T extends ProposalFieldsFragment>(
data?: NodeConnection<NodeEdge<ProposalFieldsFragment>> | null
): T[] {
return flow([
(data) =>
data.filter(
(p: ProposalFieldsFragment) => p?.state === ProposalState.STATE_REJECTED
getNodes<ProposalFieldsFragment>(data, (p) =>
p ? p?.state === ProposalState.STATE_REJECTED : false
),
orderByDate,
])(data);
@@ -34,21 +36,11 @@ export function getRejectedProposals(data?: ProposalFieldsFragment[] | null) {
export const RejectedProposalsContainer = () => {
const { t } = useTranslation();
const { data, loading, error } = useProposalsQuery({
pollInterval: 5000,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
},
});
const { data, loading, error } = useProposalsQuery();
const proposals = useMemo(
() =>
getRejectedProposals(
removePaginationWrapper(data?.proposalsConnection?.edges)
),
getRejectedProposals<ProposalFieldsFragment>(data?.proposalsConnection),
[data]
);
@@ -1,4 +1,4 @@
import { useMemo, useEffect, useState, useCallback, useRef } from 'react';
import { useMemo, useEffect, useState, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
import { removePaginationWrapper } from '@vegaprotocol/utils';
@@ -86,18 +86,12 @@ export const EpochIndividualRewards = ({
[epochId, page, refetch, delegationsPagination, pubKey]
);
const prevEpochIdRef = useRef<number | null>(null);
useEffect(() => {
if (prevEpochIdRef.current === null) {
prevEpochIdRef.current = epochId;
} else if (epochId !== prevEpochIdRef.current) {
// When the epoch changes, we want to refetch the data to update the current page
// when the epoch changes, we want to refetch the data to update the current page
if (data) {
refetchData();
}
prevEpochIdRef.current = epochId;
}, [epochId, refetchData]);
}, [epochId, data, refetchData]);
return (
<AsyncRenderer
@@ -0,0 +1,106 @@
import * as Schema from '@vegaprotocol/types';
import { mockConnectWallet } from '@vegaprotocol/cypress';
import {
orderPriceField,
placeOrderBtn,
toggleLimit,
toggleLong,
toggleMarket,
toggleShort,
} from '../support/deal-ticket';
describe('deal ticket basics', { tags: '@smoke' }, () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.clearAllLocalStorage();
cy.setOnBoardingViewed();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
});
it('must show place order button and connect wallet if wallet is not connected', () => {
// 0003-WTXN-001
cy.getByTestId('connect-vega-wallet'); // Not connected
cy.getByTestId(placeOrderBtn).should('exist');
cy.getByTestId('order-connect-wallet').should('exist');
});
it('must be able to select order direction - long/short', function () {
// 7002-SORD-004
cy.getByTestId(toggleShort).click().next('input').should('be.checked');
cy.getByTestId(toggleLong).click().next('input').should('be.checked');
});
it('must be able to select order type - limit/market', function () {
// 7002-SORD-005
// 7002-SORD-006
// 7002-SORD-007
cy.getByTestId(toggleLimit).click().next('input').should('be.checked');
cy.getByTestId(toggleMarket).click().next('input').should('be.checked');
});
it('order connect vega wallet button should connect', () => {
mockConnectWallet();
cy.getByTestId(toggleLimit).click();
cy.getByTestId(orderPriceField).clear().type('101');
cy.getByTestId('order-connect-wallet').click();
cy.getByTestId('dialog-content').should('be.visible');
cy.getByTestId('connectors-list')
.find('[data-testid="connector-jsonRpc"]')
.click();
cy.wait('@walletReq');
cy.getByTestId(placeOrderBtn).should('be.visible');
cy.getByTestId(toggleLimit).next('input').should('be.checked');
cy.getByTestId(orderPriceField).should('have.value', '101');
});
it('sidebar should be open after reload', () => {
cy.mockTradingPage();
cy.getByTestId('deal-ticket-form').should('be.visible');
cy.getByTestId('Order').click();
cy.getByTestId('deal-ticket-form').should('not.exist');
cy.reload();
cy.getByTestId('deal-ticket-form').should('be.visible');
});
});
describe(
'market states not accepting orders',
{ tags: '@smoke', testIsolation: true },
function () {
//7002-SORD-062
//7002-SORD-063
//7002-SORD-066
const states = [
Schema.MarketState.STATE_REJECTED,
Schema.MarketState.STATE_CANCELLED,
Schema.MarketState.STATE_CLOSED,
Schema.MarketState.STATE_SETTLED,
Schema.MarketState.STATE_TRADING_TERMINATED,
];
states.forEach((marketState) => {
describe(marketState, function () {
beforeEach(function () {
cy.mockTradingPage(marketState);
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/markets/market-0');
});
it('must display that market is not accepting orders', function () {
cy.getByTestId('deal-ticket-error-message-summary').should(
'have.text',
`This market is ${marketState
.split('_')
.pop()
?.toLowerCase()} and not accepting orders`
);
// 7002-SORD-060
cy.getByTestId('place-order').should('be.enabled');
});
});
});
}
);
@@ -0,0 +1,134 @@
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 = 'instrument-code';
const orderSymbol = 'market.tradableInstrument.instrument.code';
const orderSize = 'size';
const orderType = 'type';
const orderStatus = 'status';
@@ -229,7 +229,10 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
status: Schema.OrderStatus.STATUS_FILLED,
});
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Filled');
cy.get(`[col-id="${orderSymbol}"]`).contains('[title="Future"]', 'Futr');
cy.get('[col-id="market.tradableInstrument.instrument.code"]').contains(
'[title="Future"]',
'Futr'
);
});
it('must see a rejected order', () => {
+4
View File
@@ -17,6 +17,10 @@ NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-ma
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# TAG name of the current app version - TODO: bump to the latest upon release
NX_APP_VERSION=v0.21.2-core-0.72.14
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
@@ -126,20 +126,32 @@ export const FundingRate = ({ marketId }: { marketId: string }) => {
);
};
const useNow = () => {
const padStart = (n: number) => n.toString().padStart(2, '0');
export const FundingCountdown = ({ marketId }: { marketId: string }) => {
const { data: fundingPeriods } = useFundingPeriodsQuery({
variables: {
marketId: marketId,
pagination: { first: 1 },
},
});
const { data: marketInfo } = useDataProvider({
dataProvider: marketInfoProvider,
variables: { marketId },
});
const [now, setNow] = useState(Date.now());
useEffect(() => {
const interval = setInterval(() => setNow(Date.now()), 1000);
return () => clearInterval(interval);
}, []);
return now;
};
const useEvery = (marketId: string) => {
const { data: marketInfo } = useDataProvider({
dataProvider: marketInfoProvider,
variables: { marketId },
});
const node = fundingPeriods?.fundingPeriods.edges?.[0]?.node;
let startTime: number | undefined = undefined;
if (node && node.startTime && !node.endTime) {
startTime = fromNanoSeconds(node.startTime).getTime();
}
let diffFormatted = t('Unknown');
let every: number | undefined = undefined;
const sourceType =
marketInfo &&
@@ -153,51 +165,14 @@ const useEvery = (marketId: string) => {
every *= 1000;
}
}
return every;
};
const useStartTime = (marketId: string) => {
const { data: fundingPeriods } = useFundingPeriodsQuery({
variables: {
marketId: marketId,
pagination: { first: 1 },
},
});
const node = fundingPeriods?.fundingPeriods.edges?.[0]?.node;
let startTime: number | undefined = undefined;
if (node && node.startTime && !node.endTime) {
startTime = fromNanoSeconds(node.startTime).getTime();
}
return startTime;
};
const padStart = (n: number) => n.toString().padStart(2, '0');
const useFormatCountdown = (
now: number,
startTime?: number,
every?: number
) => {
if (startTime && every) {
const diff = every - ((now - startTime) % every);
const hours = (diff / 3.6e6) | 0;
const mins = ((diff % 3.6e6) / 6e4) | 0;
const secs = Math.round((diff % 6e4) / 1e3);
return `${padStart(hours)}:${padStart(mins)}:${padStart(secs)}`;
diffFormatted = `${padStart(hours)}:${padStart(mins)}:${padStart(secs)}`;
}
return t('Unknown');
};
export const FundingCountdown = ({ marketId }: { marketId: string }) => {
const now = useNow();
const startTime = useStartTime(marketId);
const every = useEvery(marketId);
return (
<div data-testid="funding-countdown">
{useFormatCountdown(now, startTime, every)}
</div>
);
return <div data-testid="funding-countdown">{diffFormatted}</div>;
};
const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
@@ -8,7 +8,6 @@ export const getRowId = ({ data }: { data: { id: string } }) => data.id;
const defaultColDef = {
sortable: true,
filter: true,
resizable: true,
filterParams: { buttons: ['reset'] },
};
@@ -7,21 +7,13 @@ import {
import type { FieldValues } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import classNames from 'classnames';
import { Navigate, useSearchParams } from 'react-router-dom';
import { useEffect, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { Button } from './buttons';
import {
useTransactionEventSubscription,
useVegaWallet,
} from '@vegaprotocol/wallet';
import { useReferral } from './hooks/use-referral';
import { Routes } from '../../lib/links';
import { useVegaWallet } from '@vegaprotocol/wallet';
export const ApplyCodeForm = () => {
const [status, setStatus] = useState<
'requested' | 'failed' | 'successful' | null
>(null);
const txHash = useRef<string | null>(null);
const [finalized, setFinalized] = useState<boolean>(false);
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
const {
register,
@@ -32,9 +24,6 @@ export const ApplyCodeForm = () => {
} = useForm();
const [params] = useSearchParams();
const { data: referee } = useReferral(pubKey, 'referee');
const { data: referrer } = useReferral(pubKey, 'referrer');
useEffect(() => {
const code = params.get('code');
if (code) setValue('code', code);
@@ -45,67 +34,23 @@ export const ApplyCodeForm = () => {
return;
}
setStatus('requested');
sendTx(pubKey, {
applyReferralCode: {
id: code as string,
},
})
.then((res) => {
if (!res) {
setError('code', {
type: 'required',
message: 'The transaction could not be sent',
});
}
if (res) {
txHash.current = res.transactionHash.toLowerCase();
}
setFinalized(true);
})
.catch((err) => {
if (err.message.includes('user rejected')) {
setStatus(null);
} else {
setError('code', {
type: 'required',
message: 'Your code has been rejected',
});
}
setError('code', {
type: 'required',
message: 'Your code has been rejected',
});
});
};
useTransactionEventSubscription({
variables: { partyId: pubKey || '' },
skip: !pubKey,
fetchPolicy: 'no-cache',
onData: ({ data: result }) =>
result.data?.busEvents?.forEach((event) => {
if (event.event.__typename === 'TransactionResult') {
const hash = event.event.hash.toLowerCase();
if (txHash.current && txHash.current === hash) {
const err = event.event.error;
const status = event.event.status;
if (err) {
setStatus(null);
setError('code', {
type: 'required',
message: err,
});
}
if (status && !err) {
setStatus('successful');
}
}
}
}),
});
if (referee || referrer) {
return <Navigate to={Routes.REFERRALS} />;
}
if (status === 'successful') {
if (finalized) {
return (
<div className="w-1/2 mx-auto">
<h3 className="mb-5 text-xl text-center uppercase calt flex flex-row gap-2 justify-center items-center">
@@ -118,27 +63,6 @@ export const ApplyCodeForm = () => {
);
}
const getButtonProps = () => {
if (isReadOnly || !pubKey) {
return {
disabled: true,
children: 'Apply',
};
}
if (status === 'requested') {
return {
disabled: true,
children: 'Confirm in wallet...',
};
}
return {
disabled: false,
children: 'Apply',
};
};
return (
<div className="w-1/2 mx-auto">
<h3 className="mb-5 text-xl text-center uppercase calt">
@@ -162,7 +86,13 @@ export const ApplyCodeForm = () => {
})}
/>
</label>
<Button className="w-full" type="submit" {...getButtonProps()} />
<Button
disabled={isReadOnly || !pubKey}
className="w-full"
type="submit"
>
Apply
</Button>
</form>
{errors.code && (
<InputError>{errors.code.message?.toString()}</InputError>
@@ -38,36 +38,18 @@ export const RainbowButton = ({
const RAINBOW_TAB_STYLE = classNames(
'inline-block',
'bg-vega-clight-500 dark:bg-vega-cdark-500',
'hover:bg-vega-clight-400 dark:hover:bg-vega-cdark-400',
'data-[state="active"]:text-white data-[state="active"]:bg-rainbow',
'data-[state="active"]:hover:bg-none data-[state="active"]:hover:bg-vega-pink-500 dark:data-[state="active"]:hover:bg-vega-pink-500',
'[&.active]:text-white [&.active]:bg-rainbow',
'[&.active]:hover:bg-none [&.active]:hover:bg-vega-pink-500 dark:[&.active]:hover:bg-vega-pink-500',
'bg-vega-clight-500 dark:bg-vega-cdark-500 hover:bg-vega-clight-400 dark:hover:bg-vega-cdark-400',
'data-[state="active"]:text-white data-[state="active"]:bg-rainbow data-[state="active"]:hover:bg-none data-[state="active"]:hover:bg-vega-pink-500 dark:data-[state="active"]:hover:bg-vega-pink-500',
'[&.active]:text-white [&.active]:bg-rainbow [&.active]:hover:bg-none [&.active]:hover:bg-vega-pink-500 dark:[&.active]:hover:bg-vega-pink-500',
'px-5 py-3',
'first:rounded-tl-lg last:rounded-tr-lg'
);
const DISABLED_RAINBOW_TAB_STYLE = classNames(
'pointer-events-none',
'text-vega-clight-100 dark:text-vega-cdark-100',
'data-[state="active"]:text-white',
'[&.active]:text-white'
);
export const RainbowTabButton = forwardRef<
HTMLButtonElement,
{ disabled?: boolean } & ButtonHTMLAttributes<HTMLButtonElement>
>(({ children, className, disabled = false, ...props }, ref) => (
<button
ref={ref}
className={classNames(
RAINBOW_TAB_STYLE,
{ 'pointer-events-none': disabled },
className
)}
{...props}
>
ButtonHTMLAttributes<HTMLButtonElement>
>(({ children, ...props }, ref) => (
<button ref={ref} className={RAINBOW_TAB_STYLE} {...props}>
{children}
</button>
));
@@ -76,19 +58,9 @@ RainbowTabButton.displayName = 'RainbowTabButton';
export const RainbowTabLink = ({
to,
children,
className,
disabled = false,
...props
}: { disabled?: boolean } & ComponentProps<typeof NavLink>) => (
<NavLink
to={to}
className={classNames(
RAINBOW_TAB_STYLE,
disabled && DISABLED_RAINBOW_TAB_STYLE,
typeof className === 'string' ? className : undefined
)}
{...props}
>
}: ComponentProps<typeof NavLink>) => (
<NavLink to={to} className={RAINBOW_TAB_STYLE} {...props}>
{children}
</NavLink>
);
@@ -9,14 +9,8 @@ import { TiersContainer } from './tiers';
import { RainbowTabLink } from './buttons';
import { Outlet } from 'react-router-dom';
import { Routes } from '../../lib/links';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useReferral } from './hooks/use-referral';
export const Referrals = () => {
const { pubKey } = useVegaWallet();
const { data: referee } = useReferral(pubKey, 'referee');
const { data: referrer } = useReferral(pubKey, 'referrer');
return (
<>
<LandingBanner />
@@ -25,10 +19,7 @@ export const Referrals = () => {
<RainbowTabLink end to={Routes.REFERRALS}>
Your referrals
</RainbowTabLink>
<RainbowTabLink
disabled={Boolean(referee || referrer)}
to={Routes.REFERRALS_APPLY_CODE}
>
<RainbowTabLink to={Routes.REFERRALS_APPLY_CODE}>
Apply a code
</RainbowTabLink>
</div>
@@ -9,11 +9,5 @@ export const AnnouncementBanner = () => {
return null;
}
return (
<Banner
app="console"
configUrl={ANNOUNCEMENTS_CONFIG_URL}
background="url('/banner-bg.jpg')"
/>
);
return <Banner app="console" configUrl={ANNOUNCEMENTS_CONFIG_URL} />;
};
+1
View File
@@ -1 +1,2 @@
export * from './announcement-banner';
export * from './upgrade-banner';
@@ -0,0 +1,84 @@
import { useMemo, useState } from 'react';
import { gt, prerelease } from 'semver';
import {
ReleasesFeed,
useEnvironment,
useReleases,
Networks,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import {
CopyWithTooltip,
ExternalLink,
Intent,
NotificationBanner,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
// v0.20.12-core-0.71.4 -> v0.20.12
// we need to strip the "core" suffix in order to determine whether a release
// is a pre-release (candidate); example: v.0.21.0-beta.1-core-0.71.4
const parseTagName = (tagName: string) => tagName.replace(/-core-[\d.]+$/i, '');
type UpgradeBannerProps = {
showVersionChange: boolean;
};
export const UpgradeBanner = ({ showVersionChange }: UpgradeBannerProps) => {
const [visible, setVisible] = useState(true);
const { data } = useReleases(ReleasesFeed.FrontEnd);
const { APP_VERSION, VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
const newest = useMemo(() => {
if (!APP_VERSION || !data) return undefined;
const newer = data.filter((r) => gt(r.tagName, APP_VERSION));
const valid =
// filter pre-releases on mainnet
VEGA_ENV === Networks.MAINNET
? newer?.filter((r) => !prerelease(parseTagName(r.tagName)))
: newer;
return valid.sort((a, b) => (gt(a.tagName, b.tagName) ? -1 : 1))[0];
}, [APP_VERSION, VEGA_ENV, data]);
if (!visible || !newest) {
return null;
}
return (
<NotificationBanner
intent={Intent.Warning}
onClose={() => {
setVisible(false);
}}
>
<div className="uppercase mb-1">
<ExternalLink href={CANONICAL_URL}>
{t('Upgrade to the latest version of Console')}
</ExternalLink>
</div>
<div data-testid="bookmark-message">
<a
className="underline"
href={newest.htmlUrl}
rel="noreferrer nofollow noopener"
target="_blank"
>
{t("View what's changed")}
</a>{' '}
{t(' or bookmark')}{' '}
<a className="underline" href={CANONICAL_URL}>
{t('console.vega.xyz')}
</a>{' '}
<CopyWithTooltip text={CANONICAL_URL}>
<button title={t('Copy %s', CANONICAL_URL)}>
<span className="sr-only">{t('Copy %s', CANONICAL_URL)}</span>
<VegaIcon size={14} name={VegaIconNames.COPY} />
</button>
</CopyWithTooltip>{' '}
{'to always see the latest version.'}
</div>
</NotificationBanner>
);
};
@@ -38,7 +38,7 @@ export const FundingContainer = ({ marketId }: { marketId: string }) => {
if (edge.node.endTime) {
acc?.push({
endTime: fromNanoSeconds(edge.node.endTime),
fundingRate: Number(edge.node.fundingRate) * 100,
fundingRate: Number(edge.node.fundingRate),
});
}
return acc;
@@ -51,11 +51,5 @@ export const FundingContainer = ({ marketId }: { marketId: string }) => {
if (!data || !values?.rows.length) {
return <Splash> {t('No funding history data')}</Splash>;
}
return (
<LineChart
data={values}
theme={theme}
priceFormat={(fundingRate) => `${fundingRate.toFixed(4)}%`}
/>
);
return <LineChart data={values} theme={theme} />;
};
+1 -1
View File
@@ -53,7 +53,7 @@ export const HeaderStat = ({
<div data-testid="item-header" id={id}>
{heading}
</div>
<Tooltip description={description} underline>
<Tooltip description={description}>
<div
data-testid="item-value"
aria-labelledby={id}
@@ -12,6 +12,10 @@ 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';
@@ -27,8 +31,12 @@ 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 =
market?.liquidityMonitoringParameters.triggeringRatio || '1';
params.market_liquidity_targetstake_triggering_ratio || '1';
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0,
@@ -5,7 +5,7 @@ import { MarketSelector } from '../../components/market-selector/market-selector
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
import { useMarket, useMarketList } from '@vegaprotocol/markets';
import { useState } from 'react';
import { ProductTypeShortName } from '@vegaprotocol/types';
import { MarketProductPill } from '@vegaprotocol/datagrid';
export const MarketHeader = () => {
const { marketId } = useParams();
@@ -28,13 +28,11 @@ export const MarketHeader = () => {
<HeaderTitle>
<span>
{data.tradableInstrument.instrument.code}
<span className="ml-2 text-xs uppercase text-muted">
{' '}
{data.tradableInstrument.instrument.product.__typename &&
ProductTypeShortName[
data.tradableInstrument.instrument.product.__typename
]}{' '}
</span>
<MarketProductPill
productType={
data.tradableInstrument.instrument.product.__typename
}
/>
</span>
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
</HeaderTitle>
@@ -61,6 +61,7 @@ describe('MarketSelectorItem', () => {
indicativeVolume: '100',
marketState: MarketState.STATE_ACTIVE,
marketTradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketValueProxy: '100',
markPrice: '50000',
midPrice: '100',
staticMidPrice: '100',
@@ -126,12 +126,10 @@ export const MarketSelector = ({
onSelect={onSelect}
noItems={
filter.product === Product.Perpetual
? t('No perpetual markets.')
? t('Perpetual markets coming soon.')
: filter.product === Product.Spot
? t('Spot markets coming soon.')
: filter.product === Product.Future
? t('No future markets.')
: t('No markets.')
: t('No markets')
}
allProducts={allProducts}
/>
@@ -28,20 +28,14 @@ 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);
},
AUTO_SIZE_COLUMNS
);
const gridStoreCallbacks = useDataGridEvents(gridState, (newState) => {
updateGridState(filter, newState);
});
if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>;
@@ -10,8 +10,6 @@ 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();
@@ -19,11 +17,7 @@ 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,
AUTO_SIZE_COLUMNS
);
const gridStoreCallbacks = useDataGridEvents(gridStore, updateGridStore);
if (!pubKey) {
return (
+31 -50
View File
@@ -55,6 +55,7 @@ const MarketSidebarButtons = () => {
return (
<>
<SidebarDivider />
<SidebarButton
view={ViewType.Order}
icon={VegaIconNames.TICKET}
@@ -71,33 +72,6 @@ const MarketSidebarButtons = () => {
);
};
const AssetSidebarButtons = () => {
const currentRouteId = useGetCurrentRouteId();
return (
<>
<SidebarButton
view={ViewType.Deposit}
icon={VegaIconNames.DEPOSIT}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Withdraw}
icon={VegaIconNames.WITHDRAW}
tooltip={t('Withdraw')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Transfer}
icon={VegaIconNames.TRANSFER}
tooltip={t('Transfer')}
routeId={currentRouteId}
/>
</>
);
};
export const Sidebar = () => {
const currentRouteId = useGetCurrentRouteId();
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
@@ -106,34 +80,41 @@ export const Sidebar = () => {
return (
<div className="flex h-full p-1 lg:flex-col gap-2" data-testid="sidebar">
<nav className={navClasses}>
{/* sidebar options that always show */}
<SidebarButton
view={ViewType.Deposit}
icon={VegaIconNames.DEPOSIT}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Withdraw}
icon={VegaIconNames.WITHDRAW}
tooltip={t('Withdraw')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Transfer}
icon={VegaIconNames.TRANSFER}
tooltip={t('Transfer')}
routeId={currentRouteId}
/>
{/* buttons for specific routes */}
<Routes>
<Route path="markets/all" element={<AssetSidebarButtons />} />
<Route path="portfolio">
<Route
// Show deposit/withdraw/transfer sidebar options only on portflio dashboard (index)
index={true}
element={<AssetSidebarButtons />}
/>
</Route>
<Route
path="markets/:marketId"
element={
<>
<AssetSidebarButtons />
<SidebarDivider />
<MarketSidebarButtons />
</>
}
path="markets/all"
// render nothing for markets/all, otherwise markets/:marketId will match with markets/all
element={null}
/>
<Route
// render nothing for portfolio
path="portfolio"
element={null}
/>
<Route path="markets/:marketId" element={<MarketSidebarButtons />} />
<Route
path="liquidity/:marketId"
element={
<>
<AssetSidebarButtons />
<SidebarDivider />
<MarketSidebarButtons />
</>
}
element={<MarketSidebarButtons />}
/>
</Routes>
</nav>
@@ -27,8 +27,8 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
const dismiss = useOnboardingStore((store) => store.dismiss);
const setDialogOpen = useOnboardingStore((store) => store.setDialogOpen);
const marketId = useGlobalStore((store) => store.marketId);
const setWalletDialogOpen = useOnboardingStore(
(store) => store.setWalletDialogOpen
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
const setViews = useSidebar((store) => store.setViews);
@@ -40,7 +40,7 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
if (step <= OnboardingStep.ONBOARDING_CONNECT_STEP) {
return (
<TradingButton {...buttonProps} onClick={() => setWalletDialogOpen(true)}>
<TradingButton {...buttonProps} onClick={() => openVegaWalletDialog()}>
{t('Connect')}
</TradingButton>
);
@@ -71,7 +71,7 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
}
return (
<TradingButton {...buttonProps} onClick={() => setWalletDialogOpen(true)}>
<TradingButton {...buttonProps} onClick={() => openVegaWalletDialog()}>
{t('Get started')}
</TradingButton>
);
@@ -12,20 +12,16 @@ import { useGlobalStore } from '../../stores';
const ONBOARDING_STORAGE_KEY = 'vega_onboarding';
export const useOnboardingStore = create<{
dialogOpen: boolean;
walletDialogOpen: boolean;
dismissed: boolean;
dismiss: () => void;
setDialogOpen: (isOpen: boolean) => void;
setWalletDialogOpen: (isOpen: boolean) => void;
}>()(
persist(
(set) => ({
dialogOpen: true,
walletDialogOpen: false,
dismissed: false,
dismiss: () => set({ dismissed: true }),
setDialogOpen: (isOpen) => set({ dialogOpen: isOpen }),
setWalletDialogOpen: (isOpen) => set({ walletDialogOpen: isOpen }),
}),
{
name: ONBOARDING_STORAGE_KEY,
@@ -3,54 +3,30 @@ import { t } from '@vegaprotocol/i18n';
import { useEnvironment } from '@vegaprotocol/environment';
import { WelcomeDialogContent } from './welcome-dialog-content';
import { useOnboardingStore } from './use-get-onboarding-step';
import { VegaConnectDialog } from '@vegaprotocol/wallet';
import { Connectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
export const WelcomeDialog = () => {
const { VEGA_ENV } = useEnvironment();
const dismissed = useOnboardingStore((store) => store.dismissed);
const dialogOpen = useOnboardingStore((store) => store.dialogOpen);
const dismiss = useOnboardingStore((store) => store.dismiss);
const walletDialogOpen = useOnboardingStore(
(store) => store.walletDialogOpen
);
const setWalletDialogOpen = useOnboardingStore(
(store) => store.setWalletDialogOpen
);
const content = walletDialogOpen ? (
<VegaConnectDialog
connectors={Connectors}
riskMessage={<RiskMessage />}
onClose={() => setWalletDialogOpen(false)}
contentOnly
/>
) : (
<WelcomeDialogContent />
);
const onClose = walletDialogOpen ? () => setWalletDialogOpen(false) : dismiss;
const title = walletDialogOpen ? null : (
<span className="font-alpha calt" data-testid="welcome-title">
{t('Console')}{' '}
<span className="text-vega-clight-100 dark:text-vega-cdark-100">
{VEGA_ENV}
</span>
</span>
);
return (
<Dialog
open={dismissed ? false : dialogOpen}
title={title}
title={
<span className="font-alpha calt" data-testid="welcome-title">
{t('Console')}{' '}
<span className="text-vega-clight-100 dark:text-vega-cdark-100">
{VEGA_ENV}
</span>
</span>
}
size="medium"
onChange={onClose}
onChange={() => dismiss()}
intent={Intent.None}
dataTestId="welcome-dialog"
>
{content}
<WelcomeDialogContent />
</Dialog>
);
};
+2 -1
View File
@@ -38,7 +38,7 @@ import { AppLoader, DynamicLoader } from '../components/app-loader';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { activeOrdersProvider } from '@vegaprotocol/orders';
import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
import { AnnouncementBanner } from '../components/banner';
import { AnnouncementBanner, UpgradeBanner } from '../components/banner';
import { Navbar } from '../components/navbar';
import classNames from 'classnames';
import {
@@ -115,6 +115,7 @@ function AppBody({ Component }: AppProps) {
/>
<ProtocolUpgradeInProgressNotification />
<ViewingBanner />
<UpgradeBanner showVersionChange={true} />
</div>
<div data-testid={`pathname-${location.pathname}`}>
<Component />
+17 -8
View File
@@ -4,24 +4,33 @@ export default function Document() {
return (
<>
<Head>
{/*
{/*
meta tags
- next advised against using _document for this, so they exist in our
- next advised against using _document for this, so they exist in our
- single page index.page.tsx
*/}
{/* preload fonts */}
{/* icons */}
<link
rel="icon"
type="image/x-icon"
href="https://static.vega.xyz/favicon.ico"
/>
<link
rel="apple-touch-icon"
content="https://static.vega.xyz/favicon.ico"
/>
{/* fonts */}
<link
rel="preload"
href="/AlphaLyrae-Medium.woff2"
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
as="font"
type="font/woff2"
/>
{/* icons */}
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="apple-touch-icon" content="/favicon.ico" />
{/* styles */}
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
{/* eslint-disable-next-line @next/next/no-css-tags */}
<link rel="stylesheet" href="/preloader.css" media="all" />
+9 -3
View File
@@ -19,11 +19,17 @@ export default function Index() {
<meta name="og:url" content="https://console.vega.xyz/" />
<meta name="og:title" content="Vega Protocol - Console" />
<meta name="og:site_name" content="Vega Protocol - Console" />
<meta name="og:image" content="./favicon.ico" />
<meta name="twitter:card" content="./favicon.ico" />
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
<meta
name="twitter:card"
content="https://static.vega.xyz/favicon.ico"
/>
<meta name="twitter:title" content="Vega Protocol - Console" />
<meta name="twitter:description" content="Vega Protocol - Console" />
<meta name="twitter:image" content="./favicon.ico" />
<meta
name="twitter:image"
content="https://static.vega.xyz/favicon.ico"
/>
<meta name="twitter:image:alt" content="VEGA logo" />
<meta name="twitter:site" content="@vegaprotocol" />
</Head>
+3 -10
View File
@@ -1,13 +1,6 @@
@import 'ag-grid-community/styles/ag-grid.css';
@import 'ag-grid-community/styles/ag-theme-balham.css';
/** Load AlphaLyrae font */
@font-face {
font-family: AlphaLyrae;
src: url('/AlphaLyrae-Medium.woff2') format('woff2'),
url('/AlphaLyrae-Medium.woff') format('woff');
}
@tailwind base;
@tailwind components;
@tailwind utilities;
@@ -110,8 +103,8 @@ html [data-theme='light'] {
--pennant-color-depth-sell-fill: theme(colors.market.red.DEFAULT);
--pennant-color-depth-sell-stroke: theme(colors.market.red.650);
--pennant-color-volume-buy: theme(colors.market.green.DEFAULT);
--pennant-color-volume-sell: theme(colors.market.red.DEFAULT);
--pennant-color-volume-buy: theme(colors.market.green.300);
--pennant-color-volume-sell: theme(colors.market.red.300);
}
html [data-theme='dark'] {
@@ -132,7 +125,7 @@ html [data-theme='dark'] {
--pennant-color-depth-sell-stroke: theme(colors.market.red.DEFAULT);
--pennant-color-volume-buy: theme(colors.market.green.600);
--pennant-color-volume-sell: theme(colors.market.red.DEFAULT);
--pennant-color-volume-sell: theme(colors.market.red.650);
}
/**
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
entrypoint="${1:-nginx}"
if [[ "$entrypoint" = "nginx" ]]; then
nginx -g 'daemon off;'
elif [[ "$entrypoint" = "ipfs" ]]; then
ipfs config profile apply server
ipfs config --json Addresses.Gateway '"/ip4/127.0.0.1/tcp/80"'
ipfs daemon
elif [[ "-c" ]]; then
shift
/bin/sh -c "$@"
fi
+3 -5
View File
@@ -14,7 +14,6 @@ import {
export type AnnouncementBannerProps = {
app: AppNameType;
configUrl: string;
background?: string;
};
// run only if below the allowed maximum delay ~24.8 days (https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value)
@@ -37,7 +36,6 @@ const doesEndInTheFuture = (now: Date, data: Announcement) => {
export const AnnouncementBanner = ({
app,
configUrl,
background,
}: AnnouncementBannerProps) => {
const [isVisible, setVisible] = useState(false);
const { data, reload } = useAnnouncement(app, configUrl);
@@ -81,10 +79,10 @@ export const AnnouncementBanner = ({
}
return (
<Banner className="relative px-10" background={background}>
<Banner className="relative px-10">
<div
data-testid="app-announcement"
className="relative flex justify-center text-lg text-center text-white font-alpha gap-2"
className="relative font-alpha flex gap-2 justify-center text-center text-lg text-white"
>
<span>{data.text}</span>{' '}
{data.urlText && data.url && (
@@ -92,7 +90,7 @@ export const AnnouncementBanner = ({
)}
</div>
<button
className="absolute top-0 right-0 flex items-center justify-center w-10 h-full p-4 text-white"
className="absolute right-0 top-0 p-4 w-10 h-full flex items-center justify-center text-white"
data-testid="app-announcement-close"
onClick={() => {
setVisible(false);
@@ -13,7 +13,7 @@ import { createLog } from './logging';
import { getMarkets } from './get-markets';
import { createWalletClient } from './wallet-client';
import { createEthereumWallet } from './ethereum-wallet';
import { ASSET_ID_FOR_MARKET } from './constants';
import { ASSET_ID_FOR_MARKET } from './contants';
const log = createLog('create-market');
@@ -4,7 +4,7 @@ import { createLog } from './logging';
import type { ProposalSubmissionBody } from '@vegaprotocol/wallet';
import { getProposal } from './get-proposal';
import { sendVegaTx } from './wallet-client';
import { ASSET_ID_FOR_MARKET, ASSET_SYMBOL } from './constants';
import { ASSET_ID_FOR_MARKET, ASSET_SYMBOL } from './contants';
const log = createLog('propose-market');
@@ -5,6 +5,8 @@ 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: [
@@ -16,29 +18,25 @@ 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>) {
return render(<TestComponent hookParams={args} />);
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>;
}
describe('useDataGridEvents', () => {
const originalWarn = console.warn;
beforeAll(() => {
gridRef = undefined;
jest.useFakeTimers();
// disabling some ag grid warnings that are caused by test setup only
@@ -57,15 +55,15 @@ describe('useDataGridEvents', () => {
columnState: undefined,
};
setup(initialState, callback);
const result = setup(initialState, callback);
// column state was not updated, so the default width provided by the
// col def should be set
expect(gridRef.current?.columnApi.getColumnState()[0].width).toEqual(
expect(result.current.columnApi.getColumnState()[0].width).toEqual(
gridProps.columnDefs[0].width
);
// no filters set
expect(gridRef.current?.api.getFilterModel()).toEqual({});
expect(result.current.api.getFilterModel()).toEqual({});
// Set filter
const idFilter = {
@@ -74,7 +72,7 @@ describe('useDataGridEvents', () => {
type: 'equals',
};
await act(async () => {
gridRef.current?.api.setFilterModel({
result.current.api.setFilterModel({
id: idFilter,
});
});
@@ -90,7 +88,7 @@ describe('useDataGridEvents', () => {
},
});
callback.mockClear();
expect(gridRef.current?.api.getFilterModel()['id']).toEqual(idFilter);
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter);
});
it('applies grid state on ready', async () => {
@@ -107,11 +105,11 @@ describe('useDataGridEvents', () => {
columnState: [colState],
};
setup(initialState, jest.fn());
const result = setup(initialState, jest.fn());
await waitFor(() => {
expect(gridRef.current?.api.getFilterModel()['id']).toEqual(idFilter);
expect(gridRef.current?.columnApi.getColumnState()[0]).toEqual(
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter);
expect(result.current.columnApi.getColumnState()[0]).toEqual(
expect.objectContaining(colState)
);
});
@@ -124,13 +122,13 @@ describe('useDataGridEvents', () => {
columnState: undefined,
};
setup(initialState, callback);
const result = setup(initialState, callback);
const newWidth = 400;
// Set col width multiple times
await act(async () => {
gridRef.current?.columnApi.setColumnWidth('id', newWidth);
result.current.columnApi.setColumnWidth('id', newWidth);
});
expect(callback).not.toHaveBeenCalled();
@@ -141,23 +139,4 @@ 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',
]);
});
});
+2 -15
View File
@@ -6,7 +6,6 @@ import type {
FilterChangedEvent,
FirstDataRenderedEvent,
SortChangedEvent,
GridReadyEvent,
} from 'ag-grid-community';
import { useCallback } from 'react';
@@ -18,8 +17,7 @@ type State = {
export const useDataGridEvents = (
state: State,
callback: (data: State) => void,
autoSizeColumns?: string[]
callback: (data: State) => void
) => {
/**
* Callback for filter events
@@ -80,7 +78,7 @@ export const useDataGridEvents = (
* State only applied if found, otherwise columns sized to fit available space
*/
const onGridReady = useCallback(
({ api, columnApi }: GridReadyEvent) => {
({ api, columnApi }: FirstDataRenderedEvent) => {
if (!api || !columnApi) return;
if (state.columnState) {
@@ -99,16 +97,6 @@ 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
@@ -118,6 +106,5 @@ export const useDataGridEvents = (
// these trigger a lot so this callback uses the 'finished' flag
onColumnMoved: onDebouncedColumnChange,
onColumnResized: onDebouncedColumnChange,
onFirstDataRendered,
};
};
@@ -12,7 +12,6 @@ import { formatRange, formatValue } from '@vegaprotocol/utils';
import { marketMarginDataProvider } from '@vegaprotocol/accounts';
import { useDataProvider } from '@vegaprotocol/data-provider';
import * as AccordionPrimitive from '@radix-ui/react-accordion';
import * as Schema from '@vegaprotocol/types';
import {
MARGIN_DIFF_TOOLTIP_TEXT,
@@ -38,16 +37,14 @@ export interface DealTicketFeeDetailsProps {
assetSymbol: string;
order: OrderSubmissionBody['orderSubmission'];
market: Market;
isMarketInAuction?: boolean;
}
export const DealTicketFeeDetails = ({
assetSymbol,
order,
market,
isMarketInAuction,
}: DealTicketFeeDetailsProps) => {
const feeEstimate = useEstimateFees(order, isMarketInAuction);
const feeEstimate = useEstimateFees(order);
const asset = getAsset(market);
const { decimals: assetDecimals, quantum } = asset;
@@ -66,7 +63,7 @@ export const DealTicketFeeDetails = ({
<>
<span>
{t(
`An estimate of the most you would be expected to pay in fees, in the market's settlement asset ${assetSymbol}. Fees estimated are "taker" fees and will only be payable if the order trades aggressively. Rebate equal to the maker portion will be paid to the trader if the order trades passively.`
`An estimate of the most you would be expected to pay in fees, in the market's settlement asset ${assetSymbol}.`
)}
</span>
<FeesBreakdown
@@ -89,7 +86,6 @@ export interface DealTicketMarginDetailsProps {
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
assetSymbol: string;
positionEstimate: EstimatePositionQuery['estimatePosition'];
side: Schema.Side;
}
export const DealTicketMarginDetails = ({
@@ -99,7 +95,6 @@ export const DealTicketMarginDetails = ({
market,
onMarketClick,
positionEstimate,
side,
}: DealTicketMarginDetailsProps) => {
const [breakdownDialog, setBreakdownDialog] = useState(false);
const { pubKey: partyId } = useVegaWallet();
@@ -168,7 +163,10 @@ export const DealTicketMarginDetails = ({
: '0',
assetDecimals
)}
formattedValue={formatValue(
formattedValue={formatRange(
deductionFromCollateralBestCase > 0
? deductionFromCollateralBestCase.toString()
: '0',
deductionFromCollateralWorstCase > 0
? deductionFromCollateralWorstCase.toString()
: '0',
@@ -187,7 +185,8 @@ export const DealTicketMarginDetails = ({
marginEstimate?.worstCase.initialLevel,
assetDecimals
)}
formattedValue={formatValue(
formattedValue={formatRange(
marginEstimate?.bestCase.initialLevel,
marginEstimate?.worstCase.initialLevel,
assetDecimals,
quantum
@@ -199,7 +198,6 @@ export const DealTicketMarginDetails = ({
}
let liquidationPriceEstimate = emptyValue;
let liquidationPriceEstimateRange = emptyValue;
if (liquidationEstimate) {
const liquidationEstimateBestCaseIncludingBuyOrders = BigInt(
@@ -209,7 +207,8 @@ export const DealTicketMarginDetails = ({
liquidationEstimate.bestCase.including_sell_orders.replace(/\..*/, '')
);
const liquidationEstimateBestCase =
side === Schema.Side.SIDE_BUY
liquidationEstimateBestCaseIncludingBuyOrders >
liquidationEstimateBestCaseIncludingSellOrders
? liquidationEstimateBestCaseIncludingBuyOrders
: liquidationEstimateBestCaseIncludingSellOrders;
@@ -220,19 +219,14 @@ export const DealTicketMarginDetails = ({
liquidationEstimate.worstCase.including_sell_orders.replace(/\..*/, '')
);
const liquidationEstimateWorstCase =
side === Schema.Side.SIDE_BUY
liquidationEstimateWorstCaseIncludingBuyOrders >
liquidationEstimateWorstCaseIncludingSellOrders
? liquidationEstimateWorstCaseIncludingBuyOrders
: liquidationEstimateWorstCaseIncludingSellOrders;
// The estimate order query API gives us the liquidation price in formatted by asset decimals.
// We need to calculate it with asset decimals, but display it with market decimals precision until the API changes.
liquidationPriceEstimate = formatValue(
liquidationEstimateWorstCase.toString(),
assetDecimals,
undefined,
market.decimalPlaces
);
liquidationPriceEstimateRange = formatRange(
liquidationPriceEstimate = formatRange(
(liquidationEstimateBestCase < liquidationEstimateWorstCase
? liquidationEstimateBestCase
: liquidationEstimateWorstCase
@@ -255,7 +249,7 @@ export const DealTicketMarginDetails = ({
const quoteName = getQuoteName(market);
return (
<div className="flex flex-col w-full gap-2">
<div className="flex flex-col gap-2 w-full">
<Accordion>
<AccordionPanel
itemId="margin"
@@ -271,7 +265,7 @@ export const DealTicketMarginDetails = ({
<div
data-testid={`deal-ticket-fee-margin-required`}
key={'value-dropdown'}
className="flex items-center justify-between w-full gap-2"
className="flex items-center gap-2 justify-between w-full"
>
<div className="flex items-center gap-1">
<Tooltip description={MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol)}>
@@ -288,9 +282,11 @@ export const DealTicketMarginDetails = ({
assetDecimals
) ?? '-'
}
noUnderline
>
<div className="font-mono text-right">
{formatValue(
{formatRange(
marginRequiredBestCase,
marginRequiredWorstCase,
assetDecimals,
quantum
@@ -302,7 +298,7 @@ export const DealTicketMarginDetails = ({
</AccordionPrimitive.Trigger>
}
>
<div className="flex flex-col w-full gap-2">
<div className="flex flex-col gap-2 w-full">
<KeyValue
label={t('Total margin available')}
indent
@@ -348,7 +344,7 @@ export const DealTicketMarginDetails = ({
{projectedMargin}
<KeyValue
label={t('Liquidation')}
value={liquidationPriceEstimateRange}
value={liquidationPriceEstimate}
formattedValue={liquidationPriceEstimate}
symbol={quoteName}
labelDescription={LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT}
@@ -23,7 +23,6 @@ import { OrdersDocument } from '@vegaprotocol/orders';
import { formatForInput } from '@vegaprotocol/utils';
import type { PartialDeep } from 'type-fest';
import type { Market } from '@vegaprotocol/markets';
import type { MarketData } from '@vegaprotocol/markets';
jest.mock('zustand');
jest.mock('./deal-ticket-fee-details', () => ({
@@ -41,25 +40,18 @@ const submit = jest.fn();
function generateJsx(
mocks: MockedResponse[] = [],
marketOverrides: PartialDeep<Market> = {},
marketDataOverrides: Partial<MarketData> = {}
marketOverrides: PartialDeep<Market> = {}
) {
const joinedMarket: Market = {
...market,
...marketOverrides,
} as Market;
const joinedMarketData: MarketData = {
...marketData,
...marketDataOverrides,
} as MarketData;
return (
<MockedProvider mocks={[...mocks]}>
<VegaWalletContext.Provider value={{ pubKey, isReadOnly: false } as any}>
<DealTicket
market={joinedMarket}
marketData={joinedMarketData}
marketData={marketData}
marketPrice={marketPrice}
submit={submit}
onDeposit={jest.fn()}
@@ -156,44 +148,6 @@ 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', () => {
@@ -508,10 +462,6 @@ describe('DealTicket', () => {
});
it('can edit deal ticket', async () => {
// 7002-SORD-004
// 7002-SORD-005
// 7002-SORD-006
// 7002-SORD-007
render(generateJsx());
// BUY is selected by default
@@ -533,31 +483,11 @@ describe('DealTicket', () => {
// Switch to limit order
await userEvent.click(screen.getByTestId('order-type-Limit'));
expect(screen.getByTestId('order-type-Limit').dataset.state).toEqual(
'checked'
);
// Check all TIF options shown
expect(screen.getByTestId('order-tif').children).toHaveLength(
Object.keys(Schema.OrderTimeInForce).length
);
// Switch to market order
await userEvent.click(screen.getByTestId('order-type-Market'));
expect(screen.getByTestId('order-type-Market').dataset.state).toEqual(
'checked'
);
// Switch to short order
await userEvent.click(screen.getByTestId('order-side-SIDE_SELL'));
expect(screen.getByTestId('order-side-SIDE_SELL').dataset.state).toEqual(
'checked'
);
// Switch to long order
await userEvent.click(screen.getByTestId('order-side-SIDE_BUY'));
expect(screen.getByTestId('order-side-SIDE_BUY').dataset.state).toEqual(
'checked'
);
});
it('validates size field', async () => {
@@ -724,108 +654,4 @@ describe('DealTicket', () => {
new Date(screen.getByTestId<HTMLInputElement>(datePicker).value).getTime()
).toEqual(now);
});
describe('market states not accepting orders', () => {
const states = [
Schema.MarketState.STATE_REJECTED,
Schema.MarketState.STATE_CANCELLED,
Schema.MarketState.STATE_CLOSED,
Schema.MarketState.STATE_SETTLED,
Schema.MarketState.STATE_TRADING_TERMINATED,
];
it.each(states)('handles state %s correctly', async (marketState) => {
const marketOverrides = { state: marketState };
const marketDataOverrides = { marketState: marketState };
render(generateJsx([], marketOverrides, marketDataOverrides));
const text = `This market is ${marketState
.split('_')
.pop()
?.toLowerCase()} and not accepting orders`;
await waitFor(() => {
expect(
screen.getByTestId('deal-ticket-error-message-summary')
).toHaveTextContent(text);
});
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'
);
});
});
});
@@ -36,12 +36,7 @@ import {
formatValue,
} from '@vegaprotocol/utils';
import { activeOrdersProvider } from '@vegaprotocol/orders';
import {
getAsset,
getDerivedPrice,
getQuoteName,
isMarketInAuction,
} from '@vegaprotocol/markets';
import { getAsset, getDerivedPrice, getQuoteName } from '@vegaprotocol/markets';
import {
validateExpiration,
validateMarketState,
@@ -187,7 +182,6 @@ export const DealTicket = ({
const iceberg = watch('iceberg');
const peakSize = watch('peakSize');
const expiresAt = watch('expiresAt');
const postOnly = watch('postOnly');
useEffect(() => {
const size = storedFormValues?.[dealTicketType]?.size;
@@ -204,7 +198,7 @@ export const DealTicket = ({
}, [storedFormValues, dealTicketType, rawPrice, setValue]);
useEffect(() => {
const subscription = watch((value) => {
const subscription = watch((value, { name, type }) => {
updateStoredFormValues(market.id, value);
});
return () => subscription.unsubscribe();
@@ -217,7 +211,6 @@ export const DealTicket = ({
size: rawSize,
timeInForce,
type,
postOnly,
},
market.id,
market.decimalPlaces,
@@ -226,7 +219,8 @@ export const DealTicket = ({
const price =
normalizedOrder &&
getDerivedPrice(normalizedOrder, marketPrice ?? undefined);
marketPrice &&
getDerivedPrice(normalizedOrder, marketPrice);
const notionalSize = getNotionalSize(
price,
@@ -465,7 +459,7 @@ export const DealTicket = ({
)}
/>
)}
<div className="flex flex-col w-full mb-4 gap-2">
<div className="mb-4 flex flex-col gap-2 w-full">
<KeyValue
label={t('Notional')}
value={formatValue(notionalSize, market.decimalPlaces)}
@@ -479,7 +473,6 @@ export const DealTicket = ({
}
assetSymbol={assetSymbol}
market={market}
isMarketInAuction={isMarketInAuction(marketData.marketTradingMode)}
/>
</div>
<Controller
@@ -682,7 +675,6 @@ export const DealTicket = ({
)}
</Button>
<DealTicketMarginDetails
side={normalizedOrder.side}
onMarketClick={onMarketClick}
assetSymbol={asset.symbol}
marginAccountBalance={marginAccountBalance}
@@ -47,7 +47,7 @@ export const KeyValue = ({
<Tooltip description={labelDescription}>
<div className="text-muted">{label}</div>
</Tooltip>
<Tooltip description={`${value ?? '-'} ${symbol || ''}`}>
<Tooltip description={`${value ?? '-'} ${symbol || ''}`} noUnderline>
{valueElement}
</Tooltip>
</div>
@@ -1,78 +0,0 @@
import { renderHook } from '@testing-library/react';
import { useEstimateFees } from './use-estimate-fees';
import { Side, OrderTimeInForce, OrderType } from '@vegaprotocol/types';
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
const data: EstimateFeesQuery = {
estimateFees: {
totalFeeAmount: '12',
fees: {
infrastructureFee: '2',
liquidityFee: '4',
makerFee: '6',
},
},
};
const mockUseEstimateFeesQuery = jest.fn((...args) => ({
data,
}));
jest.mock('./__generated__/EstimateOrder', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useEstimateFeesQuery: jest.fn((...args) => mockUseEstimateFeesQuery(...args)),
}));
jest.mock('@vegaprotocol/wallet', () => ({
useVegaWallet: () => ({ pubKey: 'pubKey' }),
}));
describe('useEstimateFees', () => {
it('returns 0 as estimated values if order is postOnly', () => {
const { result } = renderHook(() =>
useEstimateFees({
marketId: 'marketId',
side: Side.SIDE_BUY,
size: '1',
price: '1',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
type: OrderType.TYPE_LIMIT,
postOnly: true,
})
);
expect(result.current).toEqual({
totalFeeAmount: '0',
fees: {
infrastructureFee: '0',
liquidityFee: '0',
makerFee: '0',
},
});
expect(mockUseEstimateFeesQuery.mock.lastCall?.[0].skip).toBeTruthy();
});
it('divide values by 2 if market is in auction', () => {
const { result } = renderHook(() =>
useEstimateFees(
{
marketId: 'marketId',
side: Side.SIDE_BUY,
size: '1',
price: '1',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
type: OrderType.TYPE_LIMIT,
},
true
)
);
expect(result.current).toEqual({
totalFeeAmount: '6',
fees: {
infrastructureFee: '1',
liquidityFee: '2',
makerFee: '3',
},
});
});
});
@@ -1,16 +1,13 @@
import { useVegaWallet } from '@vegaprotocol/wallet';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
import { useEstimateFeesQuery } from './__generated__/EstimateOrder';
const divideByTwo = (n: string) => (BigInt(n) / BigInt(2)).toString();
export const useEstimateFees = (
order?: OrderSubmissionBody['orderSubmission'],
isMarketInAuction?: boolean
): EstimateFeesQuery['estimateFees'] | undefined => {
order?: OrderSubmissionBody['orderSubmission']
) => {
const { pubKey } = useVegaWallet();
const { data } = useEstimateFeesQuery({
variables: order && {
marketId: order.marketId,
@@ -22,28 +19,7 @@ export const useEstimateFees = (
type: order.type,
},
fetchPolicy: 'no-cache',
skip: !pubKey || !order?.size || !order?.price || order.postOnly,
skip: !pubKey || !order?.size || !order?.price,
});
if (order?.postOnly) {
return {
totalFeeAmount: '0',
fees: {
infrastructureFee: '0',
liquidityFee: '0',
makerFee: '0',
},
};
}
return isMarketInAuction && data?.estimateFees
? {
totalFeeAmount: divideByTwo(data.estimateFees.totalFeeAmount),
fees: {
infrastructureFee: divideByTwo(
data.estimateFees.fees.infrastructureFee
),
liquidityFee: divideByTwo(data.estimateFees.fees.liquidityFee),
makerFee: divideByTwo(data.estimateFees.fees.makerFee),
},
}
: data?.estimateFees;
return data?.estimateFees;
};
-3
View File
@@ -11,9 +11,6 @@ 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,12 +417,6 @@ 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(
-1
View File
@@ -24,7 +24,6 @@ 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,7 +79,6 @@ 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 = {
-3
View File
@@ -42,9 +42,6 @@ export const generateFill = (override?: PartialDeep<Trade>) => {
decimalPlaces: 5,
state: MarketState.STATE_ACTIVE,
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
fees: {
__typename: 'Fees',
factors: {
+1 -1
View File
@@ -175,7 +175,7 @@ export const Orderbook = ({
);
return (
<div className="h-full text-xs grid grid-rows-[1fr_min-content] overflow-hidden">
<div className="h-full text-xs grid grid-rows-[1fr_min-content]">
<div>
<ReactVirtualizedAutoSizer>
{({ width, height }) => {
+6 -4
View File
@@ -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, 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, 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 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, 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, 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 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 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 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, 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, 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 const MarketDataUpdateFieldsFragmentDoc = gql`
fragment MarketDataUpdateFields on ObservableMarketData {
@@ -46,6 +46,7 @@ export const MarketDataUpdateFieldsFragmentDoc = gql`
indicativeVolume
marketState
marketTradingMode
marketValueProxy
markPrice
midPrice
openInterest
@@ -93,6 +94,7 @@ export const MarketDataFieldsFragmentDoc = gql`
indicativeVolume
marketState
marketTradingMode
marketValueProxy
markPrice
midPrice
openInterest
File diff suppressed because one or more lines are too long

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