Compare commits

...
Author SHA1 Message Date
Matthew Russell 05b39e2c08 fix(trading): usdt approvals (#5939) 2024-03-07 14:21:31 +00:00
Matthew Russell 654dd1e7b0 fix(trading): stored state causing wrong chart data (#5928) 2024-03-05 18:17:18 +00:00
Matthew Russellandbwallacee 7ea7362a7d fix(trading): market into tab 24hr vol and price fix (#5910)
Co-authored-by: bwallacee <ben@vega.xyz>
2024-03-04 19:08:42 +00:00
Art bbfe42ddb1 fix(governance): rewards moved to console notification (#5904) 2024-03-04 18:09:52 +00:00
Matthew Russell b163af3e8a fix(trading): 24hr price and volume in header (#5908) 2024-03-04 18:00:54 +00:00
Matthew Russell 82df401611 fix(governance): incorrect penalty information in validators table and page (#5896) 2024-03-01 22:22:17 +00:00
Matthew Russell 4733bc169c fix(governance): rewards pagination and columns (#5879) 2024-02-29 14:57:07 +00:00
m.ray 2b91aebc2a fix(trading): fees by market fix order (#5877) 2024-02-29 10:25:56 +00:00
m.rayandbwallacee 375f4da541 fix(trading): fix reward card asssets (#5888)
Co-authored-by: bwallacee <ben@vega.xyz>
2024-02-29 09:21:35 +00:00
Matthew Russell bc413ed314 chore(trading): revert reuse vega work (#5883) 2024-02-28 16:57:42 +00:00
BenandMatthew Russell 2fab3daebd chore(trading): update vega binaries (#5873)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2024-02-27 23:24:28 +00:00
Art 2e07ada966 fix(governance): network paramater change in batch proposals (#5869) 2024-02-27 11:47:10 +01:00
ArtandDariusz Majcherczyk b81c4bc948 fix(governance): add multisig score to the validator page (#5845)
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2024-02-27 11:46:48 +01:00
4f8d6bd876 fix(trading): banner for resume trading (#5849)
Co-authored-by: Art <artur@vegaprotocol.io>
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2024-02-26 16:17:08 +00:00
Ben 52e5a37da3 chore(trading): fix skipped tests (#5868) 2024-02-26 15:52:22 +00:00
Ben 22599673ec chore(trading): remove fixture usage (#5865) 2024-02-26 14:24:56 +00:00
Ben 3c6a806ad3 chore(trading): reuse vega (#5850) 2024-02-26 12:57:16 +00:00
Dexter Edwards 7101d49d1d chore(governance, trading): update links for new beta extension (#5858) 2024-02-26 12:57:09 +00:00
Art 72e0cb76aa feat(governance): batch proposal indicators, vote breakdown ui changes (#5857) 2024-02-26 09:48:29 +00:00
m.ray ca64516a52 fix(trading): lp data provider reload - updating book (#5846) 2024-02-23 17:08:22 +00:00
daro-majanddaniel1302 55d692ea6f test(governance): revert tests (#5808)
Co-authored-by: daniel1302 <daniel.1302@gmail.com>
2024-02-23 10:27:35 +01:00
Ben f235c03abe chore(trading): update e2e test readme (#5844) 2024-02-22 21:50:55 +01:00
Matthew Russell 546deb0e1c fix(trading): use same epochs for all games/stats (#5834) 2024-02-22 13:27:13 +00:00
Art 042919eca9 fix(governance): update market proposal diff (#5842) 2024-02-22 13:37:48 +01:00
Ben c4a56e0de3 chore(trading): fix-order-status-test (#5840) 2024-02-21 15:36:55 +00:00
Ben 3c3bfb7dac chore(trading): update e2e dependencies (#5831) 2024-02-21 13:51:30 +00:00
113 changed files with 1866 additions and 1903 deletions
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
rm package.json rm package.json
npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx npm install --no-save @commitlint/cli@16.3.0 @commitlint/config-conventional@18.6.1 @commitlint/config-nx-scopes@18.6.1 nx@17.1.2
- name: Check PR title - name: Check PR title
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js run: echo "${{ github.event.pull_request.title }}" | npx @commitlint/cli@16.3.0 --config ./commitlint.config-ci.js
@@ -1,7 +1,7 @@
import { getNewAssetTxBody } from '../support/governance.functions'; import { getNewAssetTxBody } from '../support/governance.functions';
context('Proposal page', { tags: '@smoke' }, function () { context('Proposal page', { tags: '@smoke' }, function () {
describe('Verify elements on page', function () { describe.skip('Verify elements on page', function () {
const proposalHeading = 'proposals-heading'; const proposalHeading = 'proposals-heading';
const dateTimeRegex = const dateTimeRegex =
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm; /(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
@@ -7,7 +7,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
}); });
describe('Links and buttons', function () { describe('Links and buttons', function () {
it.skip('should have link for proposal page', function () { it('should have link for proposal page', function () {
cy.getByTestId('home-proposals').within(() => { cy.getByTestId('home-proposals').within(() => {
cy.get('[href="/proposals"]') cy.get('[href="/proposals"]')
.should('exist') .should('exist')
@@ -51,7 +51,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
}); });
}); });
it.skip('should have external link for governance', function () { it('should have external link for governance', function () {
cy.getByTestId('home-proposals').within(() => { cy.getByTestId('home-proposals').within(() => {
cy.getByTestId('external-link') cy.getByTestId('external-link')
.should('have.attr', 'href') .should('have.attr', 'href')
@@ -59,7 +59,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
}); });
}); });
it.skip('should have link for validator page', function () { it('should have link for validator page', function () {
cy.getByTestId('home-validators').within(() => { cy.getByTestId('home-validators').within(() => {
cy.get('[href="/validators"]') cy.get('[href="/validators"]')
.first() .first()
@@ -68,7 +68,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
}); });
}); });
it.skip('should have external link for validators', function () { it('should have external link for validators', function () {
cy.getByTestId('home-validators').within(() => { cy.getByTestId('home-validators').within(() => {
cy.getByTestId('external-link') cy.getByTestId('external-link')
.should('have.attr', 'href') .should('have.attr', 'href')
@@ -79,29 +79,29 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
}); });
}); });
it.skip('should have information on active nodes', function () { it('should have information on active nodes', function () {
cy.getByTestId('node-information') cy.getByTestId('node-information')
.first() .first()
.should('contain.text', '2') .should('contain.text', '1')
.and('contain.text', 'active nodes'); .and('contain.text', 'active nodes');
}); });
it.skip('should have information on consensus nodes', function () { it('should have information on consensus nodes', function () {
cy.getByTestId('node-information') cy.getByTestId('node-information')
.last() .last()
.should('contain.text', '2') .should('contain.text', '1')
.and('contain.text', 'consensus nodes'); .and('contain.text', 'consensus nodes');
}); });
it.skip('should contain link to specific validators', function () { it('should contain link to specific validators', function () {
cy.getByTestId('validators') cy.getByTestId('validators')
.should('have.length', '2') .should('have.length', '1')
.each(($validator) => { .each(($validator) => {
cy.wrap($validator).find('a').should('have.attr', 'href'); cy.wrap($validator).find('a').should('have.attr', 'href');
}); });
}); });
it.skip('should have link for rewards page', function () { it('should have link for rewards page', function () {
cy.getByTestId('home-rewards').within(() => { cy.getByTestId('home-rewards').within(() => {
cy.get('[href="/rewards"]') cy.get('[href="/rewards"]')
.first() .first()
@@ -110,7 +110,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
}); });
}); });
it.skip('should have link for withdrawal page', function () { it('should have link for withdrawal page', function () {
cy.getByTestId('home-vega-token').within(() => { cy.getByTestId('home-vega-token').within(() => {
cy.get('[href="/token/withdraw"]') cy.get('[href="/token/withdraw"]')
.first() .first()
@@ -132,7 +132,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
}); });
// 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020 // 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020
it.skip('should have option to switch to different network node', function () { it('should have option to switch to different network node', function () {
cy.getByTestId('git-network-data').within(() => { cy.getByTestId('git-network-data').within(() => {
cy.getByTestId('link').click(); cy.getByTestId('link').click();
}); });
@@ -159,7 +159,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.getByTestId('node-url-custom').click({ force: true }); cy.getByTestId('node-url-custom').click({ force: true });
cy.get('input').should('exist'); cy.get('input').should('exist');
cy.getByTestId('connect').should('be.disabled'); cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('icon-cross').click(); cy.getByTestId('dialog-close').click();
}); });
it('should display eth data', function () { it('should display eth data', function () {
@@ -33,12 +33,12 @@ context(
verifyTabHighlighted(navigation.proposals); verifyTabHighlighted(navigation.proposals);
}); });
it.skip('should have GOVERNANCE header visible', function () { it('should have GOVERNANCE header visible', function () {
verifyPageHeader('Proposals'); verifyPageHeader('Proposals');
}); });
// 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003 // 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003
it.skip('new proposal page should have button for link to more information on proposals', function () { it('new proposal page should have button for link to more information on proposals', function () {
cy.getByTestId('new-proposal-link').click(); cy.getByTestId('new-proposal-link').click();
cy.url().should('include', '/proposals/propose/raw'); cy.url().should('include', '/proposals/propose/raw');
cy.contains('To see Explorer data on proposals visit').within(() => { cy.contains('To see Explorer data on proposals visit').within(() => {
@@ -73,7 +73,7 @@ context(
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
}); });
it.skip('should be able to see a working link for - find out more about Vega governance', function () { it('should be able to see a working link for - find out more about Vega governance', function () {
// 3001-VOTE-001 // 3002-PROP-001 // 3001-VOTE-001 // 3002-PROP-001
cy.getByTestId(proposalDocumentationLink) cy.getByTestId(proposalDocumentationLink)
.should('be.visible') .should('be.visible')
@@ -40,10 +40,10 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
verifyConnectedToPubKey(); verifyConnectedToPubKey();
}); });
it('Able to connect public key via wallet and view assets in wallet', function () { it.skip('Able to connect public key via wallet and view assets in wallet', function () {
verifyConnectedToPubKey(); verifyConnectedToPubKey();
cy.getByTestId('currency-title', { timeout: 10000 }) cy.getByTestId('currency-title', { timeout: 10000 })
.should('have.length.at.least', 4) .should('have.length.at.least', 2)
.and('contain.text', 'USDC (fake)'); .and('contain.text', 'USDC (fake)');
}); });
@@ -46,11 +46,11 @@ context('Validators Page - verify elements on page', function () {
// @ts-ignore clash between jest and cypress // @ts-ignore clash between jest and cypress
describe('with wallets disconnected', { tags: '@smoke' }, function () { describe('with wallets disconnected', { tags: '@smoke' }, function () {
it.skip('Should have validators tab highlighted', function () { it('Should have validators tab highlighted', function () {
verifyTabHighlighted(navigation.validators); verifyTabHighlighted(navigation.validators);
}); });
it.skip('Should have validators ON VEGA header visible', function () { it('Should have validators ON VEGA header visible', function () {
verifyPageHeader('Validators'); verifyPageHeader('Validators');
}); });
@@ -192,7 +192,7 @@ context('Validators Page - verify elements on page', function () {
}); });
// 1002-STKE-006 // 1002-STKE-006
it.skip('Should be able to see validator name', function () { it('Should be able to see validator name', function () {
cy.getByTestId(validatorTitle).should('not.be.empty'); cy.getByTestId(validatorTitle).should('not.be.empty');
}); });
+4 -5
View File
@@ -14,7 +14,6 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
@@ -24,7 +23,7 @@ NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
#Test configuration variables #Test configuration variables
CYPRESS_FAIRGROUND=false CYPRESS_FAIRGROUND=false
@@ -33,7 +32,7 @@ LC_ALL="en_US.UTF-8"
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
NX_METAMASK_SNAPS=true NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=false NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=false NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true NX_REFERRALS=true
NX_GOVERNANCE_TRANSFERS=false NX_GOVERNANCE_TRANSFERS=true
+1 -2
View File
@@ -15,12 +15,11 @@ NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit suppl
NX_LOCAL_PROVIDER_URL=http://localhost:8545/ NX_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_VEGA_WALLET_URL=http://localhost:1789 NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_VEGA_REST_URL=http://localhost:3008/api/v2/ NX_VEGA_REST_URL=http://localhost:3008/api/v2/
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
+1 -2
View File
@@ -8,14 +8,13 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=# NX_VEGA_EXPLORER_URL=#
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/ NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/ NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
-1
View File
@@ -10,7 +10,6 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.vega.community/api/v2/ NX_VEGA_REST_URL=https://api.vega.community/api/v2/
-1
View File
@@ -9,7 +9,6 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/ NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
+1 -2
View File
@@ -5,7 +5,6 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https:
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/ NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
@@ -13,7 +12,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
+1 -2
View File
@@ -9,7 +9,6 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/ NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
@@ -18,7 +17,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
+1 -1
View File
@@ -14,7 +14,7 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega. NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
@@ -15,19 +15,16 @@ export const CollapsibleToggle = ({
dataTestId, dataTestId,
children, children,
}: CollapsibleToggleProps) => { }: CollapsibleToggleProps) => {
const classes = classnames( const classes = classnames('transition-transform ease-in-out duration-300', {
'mb-4 transition-transform ease-in-out duration-300', 'rotate-180': toggleState,
{ });
'rotate-180': toggleState,
}
);
return ( return (
<button <button
onClick={() => setToggleState(!toggleState)} onClick={() => setToggleState(!toggleState)}
data-testid={dataTestId} data-testid={dataTestId}
> >
<div className="flex items-center gap-3"> <div className="flex items-baseline gap-3">
{children} {children}
<div className={classes} data-testid="toggle-icon-wrapper"> <div className={classes} data-testid="toggle-icon-wrapper">
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} /> <VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
@@ -1,7 +1,8 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { type ReactNode } from 'react';
interface HeadingProps { interface HeadingProps {
title?: string; title?: ReactNode;
centerContent?: boolean; centerContent?: boolean;
marginTop?: boolean; marginTop?: boolean;
marginBottom?: boolean; marginBottom?: boolean;
@@ -0,0 +1,44 @@
import {
useLinks,
DApp,
CONSOLE_REWARDS_PAGE,
} from '@vegaprotocol/environment';
import {
ExternalLink,
Intent,
NotificationBanner,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { Trans } from 'react-i18next';
import { useMatch } from 'react-router-dom';
import Routes from '../../routes/routes';
import { type ReactNode } from 'react';
const ConsoleRewardsLink = ({ children }: { children: ReactNode }) => {
const consoleLink = useLinks(DApp.Console);
return (
<ExternalLink
href={consoleLink(CONSOLE_REWARDS_PAGE)}
className="underline inline-flex gap-1 items-center"
title="Rewards in Console"
>
<span>{children}</span>
<VegaIcon size={12} name={VegaIconNames.OPEN_EXTERNAL} />
</ExternalLink>
);
};
export const RewardsMovedNotification = () => {
const onRewardsPage = useMatch(Routes.REWARDS);
if (!onRewardsPage) return null;
return (
<NotificationBanner intent={Intent.Warning}>
<Trans
i18nKey="rewardsMovedNotification"
components={[<ConsoleRewardsLink>Console</ConsoleRewardsLink>]}
/>
</NotificationBanner>
);
};
@@ -10,6 +10,7 @@ import {
ProtocolUpgradeProposalNotification, ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals'; } from '@vegaprotocol/proposals';
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit'; import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import { RewardsMovedNotification } from '../notifications/rewards-moved-notification';
interface AppLayoutProps { interface AppLayoutProps {
children: ReactNode; children: ReactNode;
@@ -45,8 +46,10 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
const NotificationsContainer = () => { const NotificationsContainer = () => {
const { isReadOnly, pubKey, disconnect } = useVegaWallet(); const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return ( return (
<div data-testid="banners"> <div data-testid="banners">
<RewardsMovedNotification />
<ProtocolUpgradeProposalNotification <ProtocolUpgradeProposalNotification
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING} mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/> />
@@ -4,7 +4,6 @@ import keyBy from 'lodash/keyBy';
import uniq from 'lodash/uniq'; import uniq from 'lodash/uniq';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ENV } from '../../config';
import noIcon from '../../images/token-no-icon.png'; import noIcon from '../../images/token-no-icon.png';
import vegaBlack from '../../images/vega_black.png'; import vegaBlack from '../../images/vega_black.png';
@@ -37,7 +36,6 @@ export const usePollForDelegations = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
const client = useApolloClient(); const client = useApolloClient();
const { delegationsPagination } = ENV;
const [delegations, setDelegations] = React.useState< const [delegations, setDelegations] = React.useState<
WalletDelegationFieldsFragment[] WalletDelegationFieldsFragment[]
>([]); >([]);
@@ -68,11 +66,9 @@ export const usePollForDelegations = () => {
query: DelegationsDocument, query: DelegationsDocument,
variables: { variables: {
partyId: pubKey, partyId: pubKey,
delegationsPagination: delegationsPagination delegationsPagination: {
? { first: 50,
first: Number(delegationsPagination), },
}
: undefined,
}, },
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
}) })
@@ -236,14 +232,14 @@ export const usePollForDelegations = () => {
// will just continue to fail // will just continue to fail
clearInterval(interval); clearInterval(interval);
}); });
}, 1000); }, 20000);
} }
return () => { return () => {
clearInterval(interval); clearInterval(interval);
mounted = false; mounted = false;
}; };
}, [delegationsPagination, client, decimals, pubKey, t, vegaToken.address]); }, [client, decimals, pubKey, t, vegaToken.address]);
return { delegations, currentStakeAvailable, delegatedNodes, accounts }; return { delegations, currentStakeAvailable, delegatedNodes, accounts };
}; };
-1
View File
@@ -65,7 +65,6 @@ export const ENV = {
docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'), docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'),
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'), ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'), localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
rest: windowOrDefault('NX_VEGA_REST_URL'), rest: windowOrDefault('NX_VEGA_REST_URL'),
addresses: addresses:
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks], ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
@@ -20,6 +20,7 @@ describe('getMultisigStatus', () => {
expect(result).toEqual({ expect(result).toEqual({
multisigStatus: MultisigStatus.noNodes, multisigStatus: MultisigStatus.noNodes,
showMultisigStatusError: true, showMultisigStatusError: true,
zeroScoreNodes: [],
}); });
}); });
@@ -35,6 +36,7 @@ describe('getMultisigStatus', () => {
expect(result).toEqual({ expect(result).toEqual({
multisigStatus: MultisigStatus.correct, multisigStatus: MultisigStatus.correct,
showMultisigStatusError: false, showMultisigStatusError: false,
zeroScoreNodes: [],
}); });
}); });
@@ -50,6 +52,22 @@ describe('getMultisigStatus', () => {
expect(result).toEqual({ expect(result).toEqual({
multisigStatus: MultisigStatus.nodeNeedsRemoving, multisigStatus: MultisigStatus.nodeNeedsRemoving,
showMultisigStatusError: true, showMultisigStatusError: true,
zeroScoreNodes: [
{
id: '1',
rewardScore: {
multisigScore: '0',
},
stakedTotal: '1000',
},
{
id: '2',
rewardScore: {
multisigScore: '0',
},
stakedTotal: '1000',
},
],
}); });
}); });
@@ -65,6 +83,15 @@ describe('getMultisigStatus', () => {
expect(result).toEqual({ expect(result).toEqual({
multisigStatus: MultisigStatus.nodeNeedsAdding, multisigStatus: MultisigStatus.nodeNeedsAdding,
showMultisigStatusError: true, showMultisigStatusError: true,
zeroScoreNodes: [
{
id: '1',
rewardScore: {
multisigScore: '0',
},
stakedTotal: '1000',
},
],
}); });
}); });
}); });
@@ -1,5 +1,8 @@
import { removePaginationWrapper } from '@vegaprotocol/utils'; import { removePaginationWrapper } from '@vegaprotocol/utils';
import type { PreviousEpochQuery } from '../routes/staking/__generated__/PreviousEpoch'; import type {
PreviousEpochQuery,
ValidatorNodeFragment,
} from '../routes/staking/__generated__/PreviousEpoch';
export enum MultisigStatus { export enum MultisigStatus {
'correct' = 'correct', 'correct' = 'correct',
@@ -17,12 +20,15 @@ export const getMultisigStatusInfo = (
previousEpochData?.epoch.validatorsConnection?.edges previousEpochData?.epoch.validatorsConnection?.edges
); );
const hasZero = allNodesInPreviousEpoch.some( const zeroScore = (node: ValidatorNodeFragment) =>
(node) => Number(node?.rewardScore?.multisigScore) === 0 Number(node.rewardScore?.multisigScore) === 0;
); const oneScore = (node: ValidatorNodeFragment) =>
const hasOne = allNodesInPreviousEpoch.some( Number(node.rewardScore?.multisigScore) === 1;
(node) => Number(node?.rewardScore?.multisigScore) === 1
); const hasZero = allNodesInPreviousEpoch.some(zeroScore);
const hasOne = allNodesInPreviousEpoch.some(oneScore);
const zeroScoreNodes = allNodesInPreviousEpoch.filter(zeroScore);
if (hasZero && hasOne) { if (hasZero && hasOne) {
// If any individual node has 0 it means that node is missing from the multisig and needs to be added // If any individual node has 0 it means that node is missing from the multisig and needs to be added
@@ -38,5 +44,6 @@ export const getMultisigStatusInfo = (
return { return {
showMultisigStatusError: status !== MultisigStatus.correct, showMultisigStatusError: status !== MultisigStatus.correct,
multisigStatus: status, multisigStatus: status,
zeroScoreNodes,
}; };
}; };
@@ -36,8 +36,8 @@ import { type Proposal, type BatchProposal } from '../../types';
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals'; import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns'; import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats'; import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
import { getIndicatorStyle } from '../proposal/colours';
import { MarketName } from '../proposal/market-name'; import { MarketName } from '../proposal/market-name';
import { Indicator } from '../proposal/indicator';
const ProposalTypeTags = ({ const ProposalTypeTags = ({
proposal, proposal,
@@ -54,11 +54,8 @@ const ProposalTypeTags = ({
if (proposal.__typename === 'BatchProposal') { if (proposal.__typename === 'BatchProposal') {
return ( return (
<div data-testid="proposal-type" className="flex gap-1"> <div data-testid="proposal-type">
{proposal.subProposals?.map((subProposal, i) => { <ProposalInfoLabel variant="secondary">BatchProposal</ProposalInfoLabel>
if (!subProposal?.terms) return null;
return <ProposalTypeTag key={i} terms={subProposal.terms} />;
})}
</div> </div>
); );
} }
@@ -253,7 +250,7 @@ const ProposalDetails = ({
}} }}
components={{ components={{
// @ts-ignore children passed by i18next // @ts-ignore children passed by i18next
lozenge: <Lozenge />, lozenge: <Lozenge className="text-xs" />,
}} }}
/> />
); );
@@ -315,8 +312,11 @@ const ProposalDetails = ({
{proposal.subProposals.map((p, i) => { {proposal.subProposals.map((p, i) => {
if (!p?.terms) return null; if (!p?.terms) return null;
return ( return (
<li key={i} className="flex gap-3"> <li
<span className={getIndicatorStyle(i + 1)}>{i + 1}</span> key={i}
className="grid grid-cols-[40px_minmax(0,1fr)] grid-rows-1 gap-3 items-center"
>
<Indicator indicator={i + 1} />
<span> <span>
<div>{renderDetails(p.terms)}</div> <div>{renderDetails(p.terms)}</div>
<SubProposalStateText <SubProposalStateText
@@ -3,16 +3,8 @@ import { useTranslation } from 'react-i18next';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit'; import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading'; import { SubHeading } from '../../../../components/heading';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle'; import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import {
type BatchProposalFieldsFragment,
type ProposalFieldsFragment,
} from '../../__generated__/Proposals';
export const ProposalJson = ({ export const ProposalJson = ({ proposal }: { proposal?: unknown }) => {
proposal,
}: {
proposal: ProposalFieldsFragment | BatchProposalFieldsFragment;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [showDetails, setShowDetails] = useState(false); const [showDetails, setShowDetails] = useState(false);
@@ -26,7 +18,7 @@ export const ProposalJson = ({
<SubHeading title={t('proposalJson')} /> <SubHeading title={t('proposalJson')} />
</CollapsibleToggle> </CollapsibleToggle>
{showDetails && <SyntaxHighlighter data={proposal} />} {showDetails && <SyntaxHighlighter size="smaller" data={proposal} />}
</section> </section>
); );
}; };
@@ -5,6 +5,11 @@ import {
} from './proposal-market-changes'; } from './proposal-market-changes';
import type { JsonValue } from '../../../../components/json-diff'; import type { JsonValue } from '../../../../components/json-diff';
jest.mock('../proposal/market-name.tsx', () => ({
...jest.requireActual('../proposal/market-name.tsx'),
MarketName: jest.fn(),
}));
describe('applyImmutableKeysFromEarlierVersion', () => { describe('applyImmutableKeysFromEarlierVersion', () => {
it('returns an empty object if any argument is not an object or null', () => { it('returns an empty object if any argument is not an object or null', () => {
const earlierVersion: JsonValue = null; const earlierVersion: JsonValue = null;
@@ -57,21 +62,21 @@ describe('applyImmutableKeysFromEarlierVersion', () => {
describe('ProposalMarketChanges', () => { describe('ProposalMarketChanges', () => {
it('renders correctly', () => { it('renders correctly', () => {
const { getByTestId } = render( const { getByTestId } = render(
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} /> <ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
); );
expect(getByTestId('proposal-market-changes')).toBeInTheDocument(); expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
}); });
it('JsonDiff is not visible when showChanges is false', () => { it('JsonDiff is not visible when showChanges is false', () => {
const { queryByTestId } = render( const { queryByTestId } = render(
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} /> <ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
); );
expect(queryByTestId('json-diff')).not.toBeInTheDocument(); expect(queryByTestId('json-diff')).not.toBeInTheDocument();
}); });
it('JsonDiff is visible when showChanges is true', async () => { it('JsonDiff is visible when showChanges is true', async () => {
const { getByTestId } = render( const { getByTestId } = render(
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} /> <ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
); );
fireEvent.click(getByTestId('proposal-market-changes-toggle')); fireEvent.click(getByTestId('proposal-market-changes-toggle'));
expect(getByTestId('json-diff')).toBeInTheDocument(); expect(getByTestId('json-diff')).toBeInTheDocument();
@@ -1,14 +1,24 @@
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import set from 'lodash/set'; import set from 'lodash/set';
import get from 'lodash/get'; import get from 'lodash/get';
import { JsonDiff } from '../../../../components/json-diff'; import compact from 'lodash/compact';
import orderBy from 'lodash/orderBy';
import { JsonDiff, type JsonValue } from '../../../../components/json-diff';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useState } from 'react'; import { useState } from 'react';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle'; import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { SubHeading } from '../../../../components/heading'; import { SubHeading } from '../../../../components/heading';
import type { JsonValue } from '../../../../components/json-diff'; import {
import { useFetch } from '@vegaprotocol/react-helpers'; useFetchProposal,
import { ENV } from '../../../../config'; useFetchProposals,
flatten,
isBatchProposalNode,
isSingleProposalNode,
type ProposalNode,
type SingleProposalData,
type SubProposalData,
} from '../proposal/proposal-utils';
import { MarketName } from '../proposal/market-name';
const immutableKeys = [ const immutableKeys = [
'decimalPlaces', 'decimalPlaces',
@@ -18,8 +28,8 @@ const immutableKeys = [
]; ];
export const applyImmutableKeysFromEarlierVersion = ( export const applyImmutableKeysFromEarlierVersion = (
earlierVersion: JsonValue, earlierVersion: unknown,
updatedVersion: JsonValue updatedVersion: unknown
) => { ) => {
if ( if (
typeof earlierVersion !== 'object' || typeof earlierVersion !== 'object' ||
@@ -35,7 +45,8 @@ export const applyImmutableKeysFromEarlierVersion = (
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values // Overwrite the immutable keys in the updatedVersionCopy with the earlier values
immutableKeys.forEach((key) => { immutableKeys.forEach((key) => {
set(updatedVersionCopy, key, get(earlierVersion, key)); const earlier = get(earlierVersion, key);
if (earlier) set(updatedVersionCopy, key, earlier);
}); });
return updatedVersionCopy; return updatedVersionCopy;
@@ -43,49 +54,84 @@ export const applyImmutableKeysFromEarlierVersion = (
interface ProposalMarketChangesProps { interface ProposalMarketChangesProps {
marketId: string; marketId: string;
updatedProposal: JsonValue; /** This are the changes from proposal */
updateProposalNode: ProposalNode | null;
indicator?: number;
} }
export const ProposalMarketChanges = ({ export const ProposalMarketChanges = ({
marketId, marketId,
updatedProposal, updateProposalNode,
indicator,
}: ProposalMarketChangesProps) => { }: ProposalMarketChangesProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [showChanges, setShowChanges] = useState(false); const [showChanges, setShowChanges] = useState(false);
const { const { data: originalProposalData } = useFetchProposal({
state: { data }, proposalId: marketId,
} = useFetch(`${ENV.rest}governance?proposalId=${marketId}`, undefined, true); });
const { const { data: enactedProposalsData } = useFetchProposals({
state: { data: enactedProposalData }, proposalState: 'STATE_ENACTED',
} = useFetch( proposalType: 'TYPE_UPDATE_MARKET',
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`, });
undefined,
true let updateProposal: SingleProposalData | SubProposalData | undefined;
if (isBatchProposalNode(updateProposalNode)) {
updateProposal = updateProposalNode.proposals.find(
(p, i) =>
p.terms.updateMarket?.marketId === marketId &&
(indicator != null ? i === indicator - 1 : true)
);
}
if (isSingleProposalNode(updateProposalNode)) {
updateProposal = updateProposalNode.proposal;
}
// this should get the proposal before the current one
const enactedUpdateMarketProposals = orderBy(
compact(
flatten(enactedProposalsData).filter((enacted) => {
const related = enacted.terms.updateMarket?.marketId === marketId;
const notCurrent =
enacted.id !== updateProposal?.id ||
('batchId' in enacted && enacted.batchId !== updateProposal.id);
const beforeCurrent =
Number(enacted.terms.enactmentTimestamp) <
Number(updateProposal?.terms.enactmentTimestamp);
return related && notCurrent && beforeCurrent;
})
),
[(proposal) => Number(proposal.terms.enactmentTimestamp)],
'desc'
); );
// @ts-ignore no types here :-/ const latestEnactedProposal =
const enacted = enactedProposalData?.connection?.edges enactedUpdateMarketProposals.length > 0
.filter( ? enactedUpdateMarketProposals[0]
// @ts-ignore no type here : undefined;
({ node }) => node?.proposal?.terms?.updateMarket?.marketId === marketId
)
// @ts-ignore no type here
.sort((a, b) => {
return (
new Date(a?.node?.terms?.enactmentTimestamp).getTime() -
new Date(b?.node?.terms?.enactmentTimestamp).getTime()
);
});
const latestEnactedProposal = enacted?.length let originalProposal;
? enacted[enacted.length - 1] if (isBatchProposalNode(originalProposalData)) {
: undefined; originalProposal = originalProposalData.proposals.find(
(proposal) => proposal.id === marketId && proposal.terms.newMarket != null
);
}
if (isSingleProposalNode(originalProposalData)) {
originalProposal = originalProposalData.proposal;
}
const originalProposal = // LEFT SIDE: update market proposal enacted just before this one
// @ts-ignore no types with useFetch TODO: check this is good // or original new market proposal
data?.data?.proposal?.terms?.newMarket?.changes; const left =
latestEnactedProposal?.terms.updateMarket?.changes ||
originalProposal?.terms.newMarket?.changes;
// RIGHT SIDE: this update market proposal
const right = applyImmutableKeysFromEarlierVersion(
left,
updateProposal?.terms.updateMarket?.changes
);
return ( return (
<section data-testid="proposal-market-changes"> <section data-testid="proposal-market-changes">
@@ -94,25 +140,18 @@ export const ProposalMarketChanges = ({
setToggleState={setShowChanges} setToggleState={setShowChanges}
dataTestId={'proposal-market-changes-toggle'} dataTestId={'proposal-market-changes-toggle'}
> >
<SubHeading title={t('updatesToMarket')} /> <SubHeading
title={
<>
{t('UpdateToMarket')}: <MarketName marketId={marketId} truncate />
</>
}
/>
</CollapsibleToggle> </CollapsibleToggle>
{showChanges && ( {showChanges && (
<div className="mb-6"> <div className="mb-6 bg-vega-cdark-900 p-2 rounded-lg">
<JsonDiff <JsonDiff left={left as JsonValue} right={right as JsonValue} />
left={latestEnactedProposal || originalProposal}
right={
latestEnactedProposal
? applyImmutableKeysFromEarlierVersion(
latestEnactedProposal,
updatedProposal
)
: applyImmutableKeysFromEarlierVersion(
originalProposal,
updatedProposal
)
}
/>
</div> </div>
)} )}
</section> </section>
@@ -2,6 +2,11 @@ import { fireEvent, render, screen } from '@testing-library/react';
import { ProposalUpdateMarketState } from './proposal-update-market-state'; import { ProposalUpdateMarketState } from './proposal-update-market-state';
import { MarketUpdateType } from '@vegaprotocol/types'; import { MarketUpdateType } from '@vegaprotocol/types';
jest.mock('../proposal/market-name.tsx', () => ({
...jest.requireActual('../proposal/market-name.tsx'),
MarketName: jest.fn(),
}));
describe('<ProposalUpdateMarketState />', () => { describe('<ProposalUpdateMarketState />', () => {
const suspendProposal = { const suspendProposal = {
__typename: 'UpdateMarketState' as const, __typename: 'UpdateMarketState' as const,
@@ -10,6 +10,7 @@ import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { SubHeading } from '../../../../components/heading'; import { SubHeading } from '../../../../components/heading';
import { type UpdateMarketStatesFragment } from '../../__generated__/Proposals'; import { type UpdateMarketStatesFragment } from '../../__generated__/Proposals';
import { MarketUpdateTypeMapping } from '@vegaprotocol/types'; import { MarketUpdateTypeMapping } from '@vegaprotocol/types';
import { MarketName } from '../proposal/market-name';
interface ProposalUpdateMarketStateProps { interface ProposalUpdateMarketStateProps {
change: UpdateMarketStatesFragment | null; change: UpdateMarketStatesFragment | null;
@@ -20,16 +21,18 @@ export const ProposalUpdateMarketState = ({
}: ProposalUpdateMarketStateProps) => { }: ProposalUpdateMarketStateProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [showDetails, setShowDetails] = useState(false); const [showDetails, setShowDetails] = useState(false);
let market;
let isTerminate = false;
if (!change) { if (!change || change.__typename !== 'UpdateMarketState') {
return null; return null;
} }
if (change.__typename === 'UpdateMarketState') { const market = change?.market;
market = change?.market; const isTerminate =
isTerminate = change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE'; change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
let toggleTitle = t(change.updateType);
if (toggleTitle.length === 0) {
toggleTitle = t('MarketDetails');
} }
return ( return (
@@ -39,7 +42,13 @@ export const ProposalUpdateMarketState = ({
setToggleState={setShowDetails} setToggleState={setShowDetails}
dataTestId="proposal-market-data-toggle" dataTestId="proposal-market-data-toggle"
> >
<SubHeading title={t('MarketDetails')} /> <SubHeading
title={
<>
{toggleTitle}: <MarketName marketId={market?.id} />
</>
}
/>
</CollapsibleToggle> </CollapsibleToggle>
{showDetails && ( {showDetails && (
@@ -16,18 +16,31 @@ const getColour = (indicator: number, max = COLOURS.length) => {
export const getStyle = (indicator: number, max = COLOURS.length) => export const getStyle = (indicator: number, max = COLOURS.length) =>
classNames({ classNames({
'bg-vega-yellow-400': 'yellow' === getColour(indicator, max), 'bg-vega-yellow-400 before:bg-vega-yellow-400':
'bg-vega-green-400': 'green' === getColour(indicator, max), 'yellow' === getColour(indicator, max),
'bg-vega-blue-400': 'blue' === getColour(indicator, max), 'bg-vega-green-400 before:bg-vega-green-400':
'bg-vega-purple-400': 'purple' === getColour(indicator, max), 'green' === getColour(indicator, max),
'bg-vega-pink-400': 'pink' === getColour(indicator, max), 'bg-vega-blue-400 before:bg-vega-blue-400':
'bg-vega-orange-400': 'orange' === getColour(indicator, max), 'blue' === getColour(indicator, max),
'bg-vega-red-400': 'red' === getColour(indicator, max), 'bg-vega-purple-400 before:bg-vega-purple-400':
'bg-vega-clight-600': 'none' === getColour(indicator, max), 'purple' === getColour(indicator, max),
'bg-vega-pink-400 before:bg-vega-pink-400':
'pink' === getColour(indicator, max),
'bg-vega-orange-400 before:bg-vega-orange-400':
'orange' === getColour(indicator, max),
'bg-vega-red-400 before:bg-vega-red-400':
'red' === getColour(indicator, max),
'bg-vega-clight-600 before:bg-vega-clight-600':
'none' === getColour(indicator, max),
}); });
export const getIndicatorStyle = (indicator: number) => export const getIndicatorStyle = (indicator: number) =>
classNames( classNames(
'rounded-sm text-black inline-block px-1 py-1 font-alpha calt h-8', 'rounded-sm text-black inline-block px-1 py-1 font-alpha calt h-8 w-7 text-center',
getStyle(indicator) 'text-border-1',
getStyle(indicator),
// Comment below if you want to remove the "chevron"
'relative mr-[11px] z-1',
'before:absolute before:z-0 before:top-1 before:right-[-11px] before:rounded-sm',
"before:w-[22.62px] before:h-[22.62px] before:rotate-45 before:content-['']"
); );
@@ -0,0 +1,9 @@
import { getIndicatorStyle } from './colours';
export const Indicator = ({ indicator }: { indicator: number }) => (
<div className={getIndicatorStyle(indicator)}>
<span className="absolute top-0 left-0 p-1 w-full text-center z-1">
{indicator}
</span>
</div>
);
@@ -1,6 +1,13 @@
import { useMarketInfoQuery } from '@vegaprotocol/markets'; import { useMarketInfoQuery } from '@vegaprotocol/markets';
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
export const MarketName = ({ marketId }: { marketId?: string }) => { export const MarketName = ({
marketId,
truncate,
}: {
marketId?: string;
truncate?: boolean;
}) => {
const { data } = useMarketInfoQuery({ const { data } = useMarketInfoQuery({
variables: { variables: {
marketId: marketId || '', marketId: marketId || '',
@@ -8,7 +15,7 @@ export const MarketName = ({ marketId }: { marketId?: string }) => {
skip: !marketId, skip: !marketId,
}); });
return ( const id = truncate ? truncateMiddle(marketId || '') : marketId;
<span>{data?.market?.tradableInstrument.instrument.code || marketId}</span>
); return <span>{data?.market?.tradableInstrument.instrument.code || id}</span>;
}; };
@@ -1,3 +1,4 @@
import { Trans, useTranslation } from 'react-i18next';
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals'; import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
import { type Proposal, type BatchProposal } from '../../types'; import { type Proposal, type BatchProposal } from '../../types';
import { ListAsset } from '../list-asset'; import { ListAsset } from '../list-asset';
@@ -12,7 +13,10 @@ import {
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers'; import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
import { ProposalUpdateMarketState } from '../proposal-update-market-state'; import { ProposalUpdateMarketState } from '../proposal-update-market-state';
import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details'; import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details';
import { getIndicatorStyle } from './colours'; import { type ProposalNode } from './proposal-utils';
import { Lozenge } from '@vegaprotocol/ui-toolkit';
import { Indicator } from './indicator';
import { SubHeading } from '../../../../components/heading';
export const ProposalChangeDetails = ({ export const ProposalChangeDetails = ({
proposal, proposal,
@@ -22,10 +26,10 @@ export const ProposalChangeDetails = ({
}: { }: {
proposal: Proposal | BatchProposal; proposal: Proposal | BatchProposal;
terms: ProposalTermsFieldsFragment; terms: ProposalTermsFieldsFragment;
// eslint-disable-next-line restData: ProposalNode | null;
restData: any;
indicator?: number; indicator?: number;
}) => { }) => {
const { t } = useTranslation();
let details = null; let details = null;
switch (terms.change.__typename) { switch (terms.change.__typename) {
@@ -63,30 +67,13 @@ export const ProposalChangeDetails = ({
} }
case 'UpdateMarket': { case 'UpdateMarket': {
if (proposal.id) { if (proposal.id) {
const marketId = terms.change.marketId;
const proposalData = restData?.data?.proposal;
let updatedProposal = null;
// single proposal
if ('terms' in proposalData) {
updatedProposal = proposalData?.terms?.updateMarket?.changes;
}
// batch proposal - need to fish for the actual changes
if (
'batchTerms' in proposalData &&
Array.isArray(proposalData.batchTerms?.changes)
) {
updatedProposal = proposalData?.batchTerms?.changes.find(
(ch: { updateMarket?: { marketId: string } }) =>
ch?.updateMarket?.marketId === marketId
)?.updateMarket?.changes;
}
details = ( details = (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<ProposalMarketData proposalId={proposal.id} /> <ProposalMarketData proposalId={proposal.id} />
<ProposalMarketChanges <ProposalMarketChanges
indicator={indicator}
marketId={terms.change.marketId} marketId={terms.change.marketId}
updatedProposal={updatedProposal} updateProposalNode={restData}
/> />
</div> </div>
); );
@@ -125,6 +112,25 @@ export const ProposalChangeDetails = ({
'rewards.activityStreak.benefitTiers' 'rewards.activityStreak.benefitTiers'
) { ) {
details = <ProposalUpdateBenefitTiers change={terms.change} />; details = <ProposalUpdateBenefitTiers change={terms.change} />;
} else {
details = (
<div className="mb-4">
<SubHeading title={t(terms.change.__typename as string)} />
<span>
<Trans
i18nKey="Change <lozenge>{{key}}</lozenge> to <lozenge>{{value}}</lozenge>"
values={{
key: terms.change.networkParameter.key,
value: terms.change.networkParameter.value,
}}
components={{
// @ts-ignore children passed by i18next
lozenge: <Lozenge />,
}}
/>
</span>
</div>
);
} }
break; break;
@@ -137,10 +143,12 @@ export const ProposalChangeDetails = ({
} }
} }
if (indicator != null) { if (indicator != null && details != null) {
details = ( details = (
<div className="flex gap-3 mb-3"> <div className="grid grid-cols-[40px_minmax(0,1fr)] grid-rows-1 gap-3 mb-3">
<div className={getIndicatorStyle(indicator)}>{indicator}</div> <div className="w-10">
<Indicator indicator={indicator} />
</div>
<div>{details}</div> <div>{details}</div>
</div> </div>
); );
@@ -0,0 +1,261 @@
import compact from 'lodash/compact';
import { ENV } from '../../../../config';
import { useEffect, useState } from 'react';
type Maybe<T> = T | null | undefined;
type ProposalState =
| 'STATE_UNSPECIFIED'
| 'STATE_FAILED'
| 'STATE_OPEN'
| 'STATE_PASSED'
| 'STATE_REJECTED'
| 'STATE_DECLINED'
| 'STATE_ENACTED'
| 'STATE_WAITING_FOR_NODE_VOTE';
type ProposalType =
| 'TYPE_UNSPECIFIED'
| 'TYPE_ALL'
| 'TYPE_NEW_MARKET'
| 'TYPE_UPDATE_MARKET'
| 'TYPE_NETWORK_PARAMETERS'
| 'TYPE_NEW_ASSET'
| 'TYPE_NEW_FREE_FORM'
| 'TYPE_UPDATE_ASSET'
| 'TYPE_NEW_SPOT_MARKET'
| 'TYPE_UPDATE_SPOT_MARKET'
| 'TYPE_NEW_TRANSFER'
| 'TYPE_CANCEL_TRANSFER'
| 'TYPE_UPDATE_MARKET_STATE'
| 'TYPE_UPDATE_REFERRAL_PROGRAM'
| 'TYPE_UPDATE_VOLUME_DISCOUNT_PROGRAM';
type ProposalNodeType = 'TYPE_SINGLE_OR_UNSPECIFIED' | 'TYPE_BATCH';
type ProposalData = {
id: string;
rationale: {
description: string;
title: string;
};
state: ProposalState;
timestamp: string;
};
type Terms = {
cancelTransfer?: { changes: unknown };
enactmentTimestamp: string;
newAsset?: { changes: unknown };
newFreeform: object;
newMarket?: { changes: unknown };
newSpotMarket?: { changes: unknown };
newTransfer?: { changes: unknown };
updateAsset?: { assetId: string; changes: unknown };
updateMarket?: { marketId: string; changes: unknown };
updateMarketState?: {
changes: {
marketId: string;
price: string;
updateType:
| 'MARKET_STATE_UPDATE_TYPE_UNSPECIFIED'
| 'MARKET_STATE_UPDATE_TYPE_TERMINATE'
| 'MARKET_STATE_UPDATE_TYPE_SUSPEND'
| 'MARKET_STATE_UPDATE_TYPE_RESUME';
};
};
updateNetworkParameter?: { changes: unknown };
updateReferralProgram?: { changes: unknown };
updateSpotMarket?: { marketId: string; changes: unknown };
updateVolumeDiscountProgram?: { changes: unknown };
};
export type SingleProposalData = ProposalData & {
terms: Terms & {
closingTimestamp: string;
validationTimestamp: string;
};
};
type BatchProposalData = ProposalData & {
batchTerms: {
changes: Terms[];
};
};
export type SubProposalData = SingleProposalData & {
batchId: string;
};
export type ProposalNode = {
proposal: ProposalData;
proposalType: ProposalNodeType;
proposals: SubProposalData[];
};
type SingleProposalNode = ProposalNode & {
proposal: SingleProposalData;
proposalType: 'TYPE_SINGLE_OR_UNSPECIFIED';
proposals: [];
};
type BatchProposalNode = ProposalNode & {
proposal: BatchProposalData;
proposalType: 'TYPE_BATCH';
};
export const isProposalNode = (node: unknown): node is ProposalNode =>
Boolean(
typeof node === 'object' &&
node &&
'proposal' in node &&
typeof node.proposal === 'object' &&
node?.proposal &&
'id' in node.proposal &&
node?.proposal?.id
);
export const isSingleProposalNode = (
node: Maybe<ProposalNode>
): node is SingleProposalNode =>
Boolean(
node &&
node?.proposalType === 'TYPE_SINGLE_OR_UNSPECIFIED' &&
node?.proposal
);
export const isBatchProposalNode = (
node: Maybe<ProposalNode>
): node is BatchProposalNode =>
Boolean(
node &&
node?.proposalType === 'TYPE_BATCH' &&
node?.proposal &&
'batchTerms' in node.proposal &&
node?.proposals?.length > 0
);
// this includes also batch proposals with `updateMarket`s 👍
const PROPOSALS_ENDPOINT = `${ENV.rest}governances?proposalState=:proposalState&proposalType=:proposalType`;
// this can be queried also by sub proposal id as `proposalId` and it will
// return full batch proposal data with all of its sub proposals including
// the requested one inside `proposals` array.
const PROPOSAL_ENDPOINT = `${ENV.rest}governance?proposalId=:proposalId`;
export const getProposals = async ({
proposalState,
proposalType,
}: {
proposalState: ProposalState;
proposalType: ProposalType;
}) => {
try {
const response = await fetch(
PROPOSALS_ENDPOINT.replace(':proposalState', proposalState).replace(
':proposalType',
proposalType
)
);
if (response.ok) {
const data = await response.json();
if (
data &&
'connection' in data &&
data.connection &&
'edges' in data.connection &&
data.connection.edges?.length > 0
) {
const nodes = compact(
data.connection.edges.map((e: { node?: object }) => e?.node)
).filter(isProposalNode);
return nodes;
}
}
} catch {
// NOOP - ignore errors
}
return [];
};
export const getProposal = async ({ proposalId }: { proposalId: string }) => {
try {
const response = await fetch(
PROPOSAL_ENDPOINT.replace(':proposalId', proposalId)
);
if (response.ok) {
const data = await response.json();
if (data && 'data' in data && isProposalNode(data.data)) {
return data.data as ProposalNode;
}
}
} catch (err) {
// NOOP - ignore errors
}
return null;
};
export const useFetchProposal = ({ proposalId }: { proposalId?: string }) => {
const [data, setData] = useState<ProposalNode | null>(null);
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
const cb = async () => {
if (!proposalId) return;
setLoading(true);
const data = await getProposal({ proposalId });
setLoading(false);
if (data) {
setData(data);
}
};
cb();
}, [proposalId]);
return { data, loading };
};
export const useFetchProposals = ({
proposalState,
proposalType,
}: {
proposalState: ProposalState;
proposalType: ProposalType;
}) => {
const [data, setData] = useState<ProposalNode[]>([]);
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
const cb = async () => {
setLoading(true);
const data = await getProposals({ proposalState, proposalType });
setLoading(false);
if (data) {
setData(data);
}
};
cb();
}, [proposalState, proposalType]);
return { data, loading };
};
export const flatten = (
nodes: ProposalNode[]
): (SingleProposalData | SubProposalData)[] => {
const flattenNodes = [];
for (const node of nodes) {
if (isSingleProposalNode(node)) {
flattenNodes.push(node.proposal);
}
if (isBatchProposalNode(node)) {
for (const sub of node.proposals) {
flattenNodes.push(sub);
}
}
}
return flattenNodes;
};
@@ -61,7 +61,7 @@ const renderComponent = (proposal: IProposal) => {
<MemoryRouter> <MemoryRouter>
<MockedProvider> <MockedProvider>
<VegaWalletProvider config={vegaWalletConfig}> <VegaWalletProvider config={vegaWalletConfig}>
<Proposal restData={{}} proposal={proposal} /> <Proposal restData={null} proposal={proposal} />
</VegaWalletProvider> </VegaWalletProvider>
</MockedProvider> </MockedProvider>
</MemoryRouter> </MemoryRouter>
@@ -8,15 +8,17 @@ import { ProposalJson } from '../proposal-json';
import { UserVote } from '../vote-details'; import { UserVote } from '../vote-details';
import Routes from '../../../routes'; import Routes from '../../../routes';
import { ProposalState } from '@vegaprotocol/types'; import { ProposalState } from '@vegaprotocol/types';
import { type ProposalNode } from './proposal-utils';
import { useVoteSubmit } from '@vegaprotocol/proposals'; import { useVoteSubmit } from '@vegaprotocol/proposals';
import { useUserVote } from '../vote-details/use-user-vote'; import { useUserVote } from '../vote-details/use-user-vote';
import { type Proposal as IProposal, type BatchProposal } from '../../types'; import { type Proposal as IProposal, type BatchProposal } from '../../types';
import { ProposalChangeDetails } from './proposal-change-details'; import { ProposalChangeDetails } from './proposal-change-details';
import { type JsonValue } from 'type-fest';
export interface ProposalProps { export interface ProposalProps {
proposal: IProposal | BatchProposal; proposal: IProposal | BatchProposal;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any; restData: ProposalNode | null;
} }
export const Proposal = ({ proposal, restData }: ProposalProps) => { export const Proposal = ({ proposal, restData }: ProposalProps) => {
@@ -58,7 +60,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
<ProposalDescription description={proposal.rationale.description} /> <ProposalDescription description={proposal.rationale.description} />
</div> </div>
<div className="mb-4"> <div className="mb-4 flex flex-col gap-0">
{proposal.__typename === 'Proposal' ? ( {proposal.__typename === 'Proposal' ? (
<ProposalChangeDetails <ProposalChangeDetails
proposal={proposal} proposal={proposal}
@@ -95,7 +97,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
</div> </div>
<div className="mb-6"> <div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} /> <ProposalJson proposal={restData?.proposal as unknown as JsonValue} />
</div> </div>
</section> </section>
); );
@@ -206,7 +206,7 @@ export const ProposalsList = ({
/> />
)} )}
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100"> <section className="-mx-4 p-4 mb-8 bg-vega-cdark-900 rounded-l-sm">
<SubHeading title={t('openProposals')} /> <SubHeading title={t('openProposals')} />
{sortedProposals.open.length > 0 || {sortedProposals.open.length > 0 ||
@@ -15,7 +15,8 @@ import {
type VoteFieldsFragment, type VoteFieldsFragment,
} from '../../__generated__/Proposals'; } from '../../__generated__/Proposals';
import { useBatchVoteInformation } from '../../hooks/use-vote-information'; import { useBatchVoteInformation } from '../../hooks/use-vote-information';
import { getIndicatorStyle } from '../proposal/colours'; import { MarketName } from '../proposal/market-name';
import { Indicator } from '../proposal/indicator';
export const CompactVotes = ({ number }: { number: BigNumber }) => ( export const CompactVotes = ({ number }: { number: BigNumber }) => (
<CompactNumber <CompactNumber
@@ -40,8 +41,9 @@ const VoteProgress = ({
children, children,
}: VoteProgressProps) => { }: VoteProgressProps) => {
const containerClasses = classNames( const containerClasses = classNames(
'relative h-10 rounded-md border border-vega-dark-300 overflow-hidden', 'relative h-2 rounded-md overflow-hidden',
colourfulBg ? 'bg-vega-pink' : 'bg-vega-dark-400' // 'border border-vega-dark-300',
colourfulBg ? 'bg-vega-red' : 'bg-vega-dark-200'
); );
const progressClasses = classNames( const progressClasses = classNames(
@@ -50,17 +52,19 @@ const VoteProgress = ({
); );
const textClasses = classNames( const textClasses = classNames(
'absolute top-0 left-0 w-full h-full flex items-center justify-start px-3 text-black' 'w-full flex items-center justify-start text-white text-sm pb-1'
); );
return ( return (
<div className={containerClasses}> <div>
<div
className={progressClasses}
style={{ width: `${percentageFor}%` }}
data-testid={testId}
/>
<div className={textClasses}>{children}</div> <div className={textClasses}>{children}</div>
<div className={containerClasses}>
<div
className={progressClasses}
style={{ width: `${percentageFor}%` }}
data-testid={testId}
/>
</div>
</div> </div>
); );
}; };
@@ -79,14 +83,22 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
<div data-testid={testId}> <div data-testid={testId}>
{reached ? ( {reached ? (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<VegaIcon name={VegaIconNames.TICK} size={20} /> <VegaIcon
name={VegaIconNames.TICK}
className="text-vega-green"
size={20}
/>
<span> <span>
{threshold.toString()}% {text} {t('met')} {threshold.toString()}% {text} {t('met')}
</span> </span>
</div> </div>
) : ( ) : (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<VegaIcon name={VegaIconNames.CROSS} size={20} /> <VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-red"
size={20}
/>
<span> <span>
{threshold.toString()}% {text} {t('not met')} {threshold.toString()}% {text} {t('not met')}
</span> </span>
@@ -152,7 +164,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
<p className="flex gap-2 m-0 items-center"> <p className="flex gap-2 m-0 items-center">
<VegaIcon <VegaIcon
name={VegaIconNames.CROSS} name={VegaIconNames.CROSS}
className="text-vega-pink" className="text-vega-red"
size={20} size={20}
/> />
{t( {t(
@@ -215,7 +227,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
<p className="flex gap-2 m-0 items-center"> <p className="flex gap-2 m-0 items-center">
<VegaIcon <VegaIcon
name={VegaIconNames.CROSS} name={VegaIconNames.CROSS}
className="text-vega-pink" className="text-vega-red"
size={20} size={20}
/> />
{t('Proposal failed: {{count}} of {{total}} proposals passed', { {t('Proposal failed: {{count}} of {{total}} proposals passed', {
@@ -237,6 +249,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
if (!p?.terms) return null; if (!p?.terms) return null;
return ( return (
<VoteBreakdownBatchSubProposal <VoteBreakdownBatchSubProposal
indicator={i + 1}
key={i} key={i}
proposal={proposal} proposal={proposal}
votes={proposal.votes} votes={proposal.votes}
@@ -273,21 +286,37 @@ const VoteBreakdownBatchSubProposal = ({
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN; const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket'; const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
const indicatorElement = indicator && ( let marketId = undefined;
<span className={getIndicatorStyle(indicator)}>{indicator}</span> if (terms?.change?.__typename === 'UpdateMarket') {
); marketId = terms.change.marketId;
}
if (terms?.change?.__typename === 'UpdateMarketState') {
marketId = terms.change.market.id;
}
const marketName = marketId ? (
<>
: <MarketName marketId={marketId} />
</>
) : null;
const indicatorElement = indicator && <Indicator indicator={indicator} />;
return ( return (
<div> <div className="mb-6">
<div className="flex items-baseline gap-3"> <div className="flex items-center gap-3 mb-3">
{indicatorElement} {indicatorElement}
<h4>{t(terms.change.__typename)}</h4> <h4>
{t(terms.change.__typename)} {marketName}
</h4>
</div>
<div className="rounded-sm bg-vega-dark-100 p-3">
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
</div> </div>
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
</div> </div>
); );
}; };
@@ -302,11 +331,13 @@ const VoteBreakdownNormal = ({ proposal }: { proposal: Proposal }) => {
const isUpdateMarket = proposal?.terms?.change?.__typename === 'UpdateMarket'; const isUpdateMarket = proposal?.terms?.change?.__typename === 'UpdateMarket';
return ( return (
<VoteBreakDownUI <div className="mb-6">
voteInfo={voteInfo} <VoteBreakDownUI
isProposalOpen={isProposalOpen} voteInfo={voteInfo}
isUpdateMarket={isUpdateMarket} isProposalOpen={isProposalOpen}
/> isUpdateMarket={isUpdateMarket}
/>
</div>
); );
}; };
@@ -370,13 +401,13 @@ const VoteBreakDownUI = ({
'flex justify-between flex-wrap gap-6' 'flex justify-between flex-wrap gap-6'
); );
const sectionClasses = classNames('min-w-[300px] flex-1 flex-grow'); const sectionClasses = classNames('min-w-[300px] flex-1 flex-grow');
const headingClasses = classNames('mb-2 text-vega-dark-400'); const headingClasses = classNames('mb-2 text-sm text-white font-bold');
const progressDetailsClasses = classNames( const progressDetailsClasses = classNames(
'flex justify-between flex-wrap mt-2 text-sm' 'flex justify-between flex-wrap mt-2 text-sm'
); );
return ( return (
<div className="mb-6"> <div>
{isProposalOpen && ( {isProposalOpen && (
<div <div
data-testid="vote-status" data-testid="vote-status"
@@ -393,7 +424,7 @@ const VoteBreakDownUI = ({
<VegaIcon <VegaIcon
name={VegaIconNames.CROSS} name={VegaIconNames.CROSS}
size={20} size={20}
className="text-vega-pink" className="text-vega-red"
/> />
)} )}
</span> </span>
@@ -409,103 +440,13 @@ const VoteBreakDownUI = ({
<p className="m-0"> <p className="m-0">
<Trans <Trans
i18nKey={'Currently expected to <0>fail</0>'} i18nKey={'Currently expected to <0>fail</0>'}
components={[<span className="text-vega-pink" />]} components={[<span className="text-vega-red" />]}
/> />
</p> </p>
)} )}
</div> </div>
)} )}
{isUpdateMarket && (
<div className="mb-4">
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
<div className={sectionWrapperClasses}>
<section
className={sectionClasses}
data-testid="lp-majority-breakdown"
>
<VoteProgress
percentageFor={lpVoteWeight}
colourfulBg={true}
testId="lp-majority-progress"
>
<Status
reached={majorityLPMet}
threshold={requiredMajorityLPPercentage}
text={t('majorityThreshold')}
testId={
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
}
/>
</VoteProgress>
<div className={progressDetailsClasses}>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesFor')}:</span>
<Tooltip
description={
<span>{lpVoteWeight.toFixed(defaultDP)}%</span>
}
>
<button>{lpVoteWeight.toFixed(1)}%</button>
</Tooltip>
</div>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesAgainst')}:</span>
<span>
<Tooltip
description={
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
}
>
<button>{noLPPercentage.toFixed(1)}%</button>
</Tooltip>
</span>
</div>
</div>
</section>
<section
className={sectionClasses}
data-testid="lp-participation-breakdown"
>
<VoteProgress
percentageFor={
lpParticipationThresholdProgress || new BigNumber(0)
}
testId="lp-participation-progress"
>
<Status
reached={participationLPMet}
threshold={requiredParticipationLP || new BigNumber(1)}
text={t('participationThreshold')}
testId={
participationLPMet
? 'lp-participation-met'
: 'lp-participation-not-met'
}
/>
</VoteProgress>
<div className="flex mt-2 text-sm">
<div className="flex items-center gap-1">
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
<Tooltip
description={formatNumber(
totalEquityLikeShareWeight,
defaultDP
)}
>
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
</Tooltip>
</div>
</div>
</section>
</div>
</div>
)}
{isUpdateMarket && <h3 className={headingClasses}>{t('tokenVote')}</h3>} {isUpdateMarket && <h3 className={headingClasses}>{t('tokenVote')}</h3>}
<div className={sectionWrapperClasses}> <div className={sectionWrapperClasses}>
<section <section
@@ -605,6 +546,97 @@ const VoteBreakDownUI = ({
</div> </div>
</section> </section>
</div> </div>
{/** Liquidity provider vote */}
{isUpdateMarket && (
<div className="mt-3">
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
<div className={sectionWrapperClasses}>
<section
className={sectionClasses}
data-testid="lp-majority-breakdown"
>
<VoteProgress
percentageFor={lpVoteWeight}
colourfulBg={true}
testId="lp-majority-progress"
>
<Status
reached={majorityLPMet}
threshold={requiredMajorityLPPercentage}
text={t('majorityThreshold')}
testId={
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
}
/>
</VoteProgress>
<div className={progressDetailsClasses}>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesFor')}:</span>
<Tooltip
description={
<span>{lpVoteWeight.toFixed(defaultDP)}%</span>
}
>
<button>{lpVoteWeight.toFixed(1)}%</button>
</Tooltip>
</div>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesAgainst')}:</span>
<span>
<Tooltip
description={
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
}
>
<button>{noLPPercentage.toFixed(1)}%</button>
</Tooltip>
</span>
</div>
</div>
</section>
<section
className={sectionClasses}
data-testid="lp-participation-breakdown"
>
<VoteProgress
percentageFor={
lpParticipationThresholdProgress || new BigNumber(0)
}
testId="lp-participation-progress"
>
<Status
reached={participationLPMet}
threshold={requiredParticipationLP || new BigNumber(1)}
text={t('participationThreshold')}
testId={
participationLPMet
? 'lp-participation-met'
: 'lp-participation-not-met'
}
/>
</VoteProgress>
<div className="flex mt-2 text-sm">
<div className="flex items-center gap-1">
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
<Tooltip
description={formatNumber(
totalEquityLikeShareWeight,
defaultDP
)}
>
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
</Tooltip>
</div>
</div>
</section>
</div>
</div>
)}
</div> </div>
); );
}; };
@@ -1,17 +1,16 @@
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../config';
import { Proposal } from '../components/proposal'; import { Proposal } from '../components/proposal';
import { ProposalNotFound } from '../components/proposal-not-found'; import { ProposalNotFound } from '../components/proposal-not-found';
import { useProposalQuery } from '../__generated__/Proposals'; import { useProposalQuery } from '../__generated__/Proposals';
import { useFetchProposal } from '../components/proposal/proposal-utils';
export const ProposalContainer = () => { export const ProposalContainer = () => {
const params = useParams<{ proposalId: string }>(); const params = useParams<{ proposalId: string }>();
const { const { data: restData, loading: restLoading } = useFetchProposal({
state: { data: restData, loading: restLoading, error: restError }, proposalId: params.proposalId,
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`); });
const { data, loading, error } = useProposalQuery({ const { data, loading, error } = useProposalQuery({
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
@@ -26,7 +25,7 @@ export const ProposalContainer = () => {
return ( return (
<AsyncRenderer <AsyncRenderer
loading={Boolean(loading || restLoading)} loading={Boolean(loading || restLoading)}
error={error || restError} error={error}
data={{ data={{
...data, ...data,
...(restData ? { restData } : {}), ...(restData ? { restData } : {}),
@@ -1,10 +1,9 @@
import { useMemo, useEffect, useState, useCallback, useRef } from 'react'; import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
import { removePaginationWrapper } from '@vegaprotocol/utils'; import { removePaginationWrapper } from '@vegaprotocol/utils';
import type { EpochFieldsFragment } from '../home/__generated__/Rewards'; import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
import { useRewardsQuery } from '../home/__generated__/Rewards'; import { useRewardsQuery } from '../home/__generated__/Rewards';
import { ENV } from '../../../config';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table'; import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list'; import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
@@ -27,22 +26,16 @@ export const EpochIndividualRewards = ({
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const { t } = useTranslation(); const { t } = useTranslation();
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
const { delegationsPagination } = ENV;
const { param: marketCreationQuantumMultiple } = useNetworkParam( const { param: marketCreationQuantumMultiple } = useNetworkParam(
'rewards_marketCreationQuantumMultiple' 'rewards_marketCreationQuantumMultiple'
); );
const { data, loading, error, refetch } = useRewardsQuery({ const { data, loading, error } = useRewardsQuery({
notifyOnNetworkStatusChange: true, notifyOnNetworkStatusChange: true,
variables: { variables: {
partyId: pubKey || '', partyId: pubKey || '',
fromEpoch: epochId - EPOCHS_PAGE_SIZE, ...calculateEpochOffset({ epochId, page, size: EPOCHS_PAGE_SIZE }),
toEpoch: epochId, delegationsPagination: { first: 50 },
delegationsPagination: delegationsPagination
? {
first: Number(delegationsPagination),
}
: undefined,
}, },
skip: !pubKey, skip: !pubKey,
}); });
@@ -70,36 +63,6 @@ export const EpochIndividualRewards = ({
}); });
}, [data?.party, epochId, epochRewardSummaries, page, rewards]); }, [data?.party, epochId, epochRewardSummaries, page, rewards]);
const refetchData = useCallback(
async (toPage?: number) => {
const targetPage = toPage ?? page;
await refetch({
partyId: pubKey || '',
...calculateEpochOffset({ epochId, page, size: EPOCHS_PAGE_SIZE }),
delegationsPagination: delegationsPagination
? {
first: Number(delegationsPagination),
}
: undefined,
});
setPage(targetPage);
},
[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
refetchData();
}
prevEpochIdRef.current = epochId;
}, [epochId, refetchData]);
// Workarounds for the error handling of AsyncRenderer // Workarounds for the error handling of AsyncRenderer
const filteredErrors = filterAcceptableGraphqlErrors(error); const filteredErrors = filterAcceptableGraphqlErrors(error);
const filteredData = data || []; const filteredData = data || [];
@@ -131,10 +94,10 @@ export const EpochIndividualRewards = ({
isLoading={loading} isLoading={loading}
hasPrevPage={page > 1} hasPrevPage={page > 1}
hasNextPage={page < totalPages} hasNextPage={page < totalPages}
onBack={() => refetchData(page - 1)} onBack={() => setPage((x) => x - 1)}
onNext={() => refetchData(page + 1)} onNext={() => setPage((x) => x + 1)}
onFirst={() => refetchData(1)} onFirst={() => setPage(1)}
onLast={() => refetchData(totalPages)} onLast={() => setPage(totalPages)}
> >
{t('Page')} {page} {t('Page')} {page}
</Pagination> </Pagination>
@@ -23,36 +23,6 @@ describe('generateEpochIndividualRewardsList', () => {
epoch: { id: '2' }, epoch: { id: '2' },
}; };
const reward3: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '200',
percentageOfTotal: '0.2',
receivedAt: new Date(),
asset: { id: 'gbp', symbol: 'GBP', name: 'GBP', decimals: 7 },
party: { id: 'blah' },
epoch: { id: '2' },
};
const reward4: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '100',
percentageOfTotal: '0.1',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
party: { id: 'blah' },
epoch: { id: '1' },
};
const reward5: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '150',
percentageOfTotal: '0.15',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
party: { id: 'blah' },
epoch: { id: '3' },
};
const rewardWrongType: RewardFieldsFragment = { const rewardWrongType: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE, rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
amount: '50', amount: '50',
@@ -120,26 +90,10 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: { [AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '100', amount: '100',
percentageOfTotal: '0.1', percentageOfTotal: '0.1',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
}, },
}, },
], ],
@@ -147,7 +101,7 @@ describe('generateEpochIndividualRewardsList', () => {
}); });
it('should return an array sorted by epoch descending', () => { it('should return an array sorted by epoch descending', () => {
const rewards = [reward1, reward2, reward3, reward4]; const rewards = [reward1, reward2];
const result1 = generateEpochIndividualRewardsList({ const result1 = generateEpochIndividualRewardsList({
rewards, rewards,
epochId: 2, epochId: 2,
@@ -157,7 +111,7 @@ describe('generateEpochIndividualRewardsList', () => {
expect(result1[0].epoch).toEqual(2); expect(result1[0].epoch).toEqual(2);
expect(result1[1].epoch).toEqual(1); expect(result1[1].epoch).toEqual(1);
const reorderedRewards = [reward4, reward3, reward2, reward1]; const reorderedRewards = [reward2, reward1];
const result2 = generateEpochIndividualRewardsList({ const result2 = generateEpochIndividualRewardsList({
rewards: reorderedRewards, rewards: reorderedRewards,
epochId: 2, epochId: 2,
@@ -170,7 +124,7 @@ describe('generateEpochIndividualRewardsList', () => {
it('returns data in the expected shape', () => { it('returns data in the expected shape', () => {
// Just sanity checking the whole structure here // Just sanity checking the whole structure here
const rewards = [reward1, reward2, reward3, reward4]; const rewards = [reward1, reward2];
const result = generateEpochIndividualRewardsList({ const result = generateEpochIndividualRewardsList({
rewards, rewards,
epochId: 2, epochId: 2,
@@ -181,37 +135,6 @@ describe('generateEpochIndividualRewardsList', () => {
{ {
epoch: 2, epoch: 2,
rewards: [ rewards: [
{
asset: 'GBP',
totalAmount: '200',
decimals: 7,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '200',
percentageOfTotal: '0.2',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
},
},
{ {
asset: 'EUR', asset: 'EUR',
totalAmount: '50', totalAmount: '50',
@@ -221,26 +144,10 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: { [AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '50', amount: '50',
percentageOfTotal: '0.05', percentageOfTotal: '0.05',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
}, },
}, },
], ],
@@ -250,33 +157,17 @@ describe('generateEpochIndividualRewardsList', () => {
rewards: [ rewards: [
{ {
asset: 'USD', asset: 'USD',
totalAmount: '200', totalAmount: '100',
decimals: 6, decimals: 6,
rewardTypes: { rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: { [AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '100',
percentageOfTotal: '0.1',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: { [AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '100', amount: '100',
percentageOfTotal: '0.1', percentageOfTotal: '0.1',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
}, },
}, },
], ],
@@ -285,7 +176,7 @@ describe('generateEpochIndividualRewardsList', () => {
}); });
it('returns data correctly for the requested epoch range', () => { it('returns data correctly for the requested epoch range', () => {
const rewards = [reward1, reward2, reward3, reward4, reward5]; const rewards = [reward1, reward2];
const resultPageOne = generateEpochIndividualRewardsList({ const resultPageOne = generateEpochIndividualRewardsList({
rewards, rewards,
epochId: 3, epochId: 3,
@@ -297,74 +188,11 @@ describe('generateEpochIndividualRewardsList', () => {
expect(resultPageOne).toEqual([ expect(resultPageOne).toEqual([
{ {
epoch: 3, epoch: 3,
rewards: [ rewards: [],
{
asset: 'USD',
decimals: 6,
totalAmount: '150',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '150',
percentageOfTotal: '0.15',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
},
},
],
}, },
{ {
epoch: 2, epoch: 2,
rewards: [ rewards: [
{
asset: 'GBP',
totalAmount: '200',
decimals: 7,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '200',
percentageOfTotal: '0.2',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
},
},
{ {
asset: 'EUR', asset: 'EUR',
totalAmount: '50', totalAmount: '50',
@@ -374,26 +202,10 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: { [AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '50', amount: '50',
percentageOfTotal: '0.05', percentageOfTotal: '0.05',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
}, },
}, },
], ],
@@ -414,33 +226,17 @@ describe('generateEpochIndividualRewardsList', () => {
rewards: [ rewards: [
{ {
asset: 'USD', asset: 'USD',
totalAmount: '200', totalAmount: '100',
decimals: 6, decimals: 6,
rewardTypes: { rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: { [AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '100',
percentageOfTotal: '0.1',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: { [AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '100', amount: '100',
percentageOfTotal: '0.1', percentageOfTotal: '0.1',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
}, },
}, },
], ],
@@ -1,4 +1,4 @@
import { useState, useCallback, useEffect } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
import type { EpochFieldsFragment } from '../home/__generated__/Rewards'; import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
@@ -23,37 +23,17 @@ export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
'rewards_marketCreationQuantumMultiple' 'rewards_marketCreationQuantumMultiple'
); );
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({ const { data, loading, error } = useEpochAssetsRewardsQuery({
notifyOnNetworkStatusChange: true, notifyOnNetworkStatusChange: true,
variables: { variables: {
epochRewardSummariesFilter: { epochRewardSummariesFilter: calculateEpochOffset({
fromEpoch: epochId - EPOCHS_PAGE_SIZE, epochId,
}, page: page,
size: EPOCHS_PAGE_SIZE,
}),
}, },
}); });
const refetchData = useCallback(
async (toPage?: number) => {
const targetPage = toPage ?? page;
await refetch({
epochRewardSummariesFilter: calculateEpochOffset({
epochId,
page: targetPage,
size: EPOCHS_PAGE_SIZE,
}),
});
setPage(targetPage);
},
[epochId, page, refetch]
);
useEffect(() => {
// when the epoch changes, we want to refetch the data to update the current page
if (data) {
refetchData();
}
}, [epochId, data, refetchData]);
const epochTotalRewardSummaries = const epochTotalRewardSummaries =
generateEpochTotalRewardsList({ generateEpochTotalRewardsList({
data, data,
@@ -85,10 +65,10 @@ export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
isLoading={loading} isLoading={loading}
hasPrevPage={page > 1} hasPrevPage={page > 1}
hasNextPage={page < totalPages} hasNextPage={page < totalPages}
onBack={() => refetchData(page - 1)} onBack={() => setPage((x) => x - 1)}
onNext={() => refetchData(page + 1)} onNext={() => setPage((x) => x + 1)}
onFirst={() => refetchData(1)} onFirst={() => setPage(1)}
onLast={() => refetchData(totalPages)} onLast={() => setPage(totalPages)}
> >
{t('Page')} {page} {t('Page')} {page}
</Pagination> </Pagination>
@@ -149,38 +149,6 @@ describe('generateEpochAssetRewardsList', () => {
amount: '0', amount: '0',
}, },
], ],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
]), ]),
totalAmount: '123', totalAmount: '123',
}, },
@@ -291,40 +259,8 @@ describe('generateEpochAssetRewardsList', () => {
amount: '100', amount: '100',
}, },
], ],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '123',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
]), ]),
totalAmount: '223', totalAmount: '100',
}, },
], ],
]), ]),
@@ -334,66 +270,7 @@ describe('generateEpochAssetRewardsList', () => {
'2', '2',
{ {
epoch: 2, epoch: 2,
assetRewards: new Map([ assetRewards: new Map(),
[
'1',
{
assetId: '1',
decimals: 18,
name: 'Asset 1',
rewards: new Map([
[
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
{
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
{
rewardType:
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '5',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
]),
totalAmount: '5',
},
],
]),
}, },
], ],
]) ])
@@ -489,66 +366,7 @@ describe('generateEpochAssetRewardsList', () => {
'2', '2',
{ {
epoch: 2, epoch: 2,
assetRewards: new Map([ assetRewards: new Map(),
[
'1',
{
assetId: '1',
name: 'Asset 1',
decimals: 18,
rewards: new Map([
[
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
{
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
{
rewardType:
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '33',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
]),
totalAmount: '33',
},
],
]),
}, },
], ],
[ [
@@ -578,38 +396,6 @@ describe('generateEpochAssetRewardsList', () => {
amount: '15', amount: '15',
}, },
], ],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
]), ]),
totalAmount: '15', totalAmount: '15',
}, },
@@ -656,40 +442,8 @@ describe('generateEpochAssetRewardsList', () => {
amount: '100', amount: '100',
}, },
], ],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '123',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '0',
},
],
[
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
{
rewardType:
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
]), ]),
totalAmount: '223', totalAmount: '100',
}, },
], ],
]), ]),
@@ -18,22 +18,6 @@ export const RowAccountTypes = {
columnTitle: 'rewardsColInfraHeader', columnTitle: 'rewardsColInfraHeader',
description: 'rewardsColInfraTooltip', description: 'rewardsColInfraTooltip',
}, },
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
columnTitle: 'rewardsColPriceTakingHeader',
description: 'rewardsColPriceTakingTooltip',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
columnTitle: 'rewardsColPriceMakingHeader',
description: 'rewardsColPriceMakingTooltip',
},
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
columnTitle: 'rewardsColLiquidityProvisionHeader',
description: 'rewardsColLiquidityProvisionTooltip',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
columnTitle: 'rewardsColMarketCreationHeader',
description: 'rewardsColMarketCreationTooltip',
},
}; };
interface ColumnHeaderProps { interface ColumnHeaderProps {
@@ -55,7 +39,7 @@ export const rowGridItemStyles = (last = false) =>
}); });
const gridStyles = classNames( const gridStyles = classNames(
'grid grid-cols-[repeat(8,minmax(100px,auto))] max-w-full overflow-auto', 'grid grid-cols-[repeat(4,minmax(100px,auto))] max-w-full overflow-auto',
`border-t border-vega-dark-200`, `border-t border-vega-dark-200`,
'text-sm' 'text-sm'
); );
@@ -1,27 +1,31 @@
fragment ValidatorNode on Node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
}
query PreviousEpoch($epochId: ID) { query PreviousEpoch($epochId: ID) {
epoch(id: $epochId) { epoch(id: $epochId) {
id id
validatorsConnection { validatorsConnection {
edges { edges {
node { node {
id ...ValidatorNode
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
} }
} }
} }
@@ -3,6 +3,8 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client'; import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; const defaultOptions = {} as const;
export type ValidatorNodeFragment = { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } };
export type PreviousEpochQueryVariables = Types.Exact<{ export type PreviousEpochQueryVariables = Types.Exact<{
epochId?: Types.InputMaybe<Types.Scalars['ID']>; epochId?: Types.InputMaybe<Types.Scalars['ID']>;
}>; }>;
@@ -10,7 +12,28 @@ export type PreviousEpochQueryVariables = Types.Exact<{
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } } } | null> | null } | null } }; export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } } } | null> | null } | null } };
export const ValidatorNodeFragmentDoc = gql`
fragment ValidatorNode on Node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
}
`;
export const PreviousEpochDocument = gql` export const PreviousEpochDocument = gql`
query PreviousEpoch($epochId: ID) { query PreviousEpoch($epochId: ID) {
epoch(id: $epochId) { epoch(id: $epochId) {
@@ -18,30 +41,13 @@ export const PreviousEpochDocument = gql`
validatorsConnection { validatorsConnection {
edges { edges {
node { node {
id ...ValidatorNode
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
} }
} }
} }
} }
} }
`; ${ValidatorNodeFragmentDoc}`;
/** /**
* __usePreviousEpochQuery__ * __usePreviousEpochQuery__
@@ -6,7 +6,6 @@ import { usePreviousEpochQuery } from '../__generated__/PreviousEpoch';
import { ValidatorTables } from './validator-tables'; import { ValidatorTables } from './validator-tables';
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch'; import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { ENV } from '../../../config';
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info'; import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
import { MultisigIncorrectNotice } from '../../../components/multisig-incorrect-notice'; import { MultisigIncorrectNotice } from '../../../components/multisig-incorrect-notice';
@@ -20,7 +19,6 @@ export const EpochData = () => {
refetch: nodesRefetch, refetch: nodesRefetch,
} = useNodesQuery(); } = useNodesQuery();
const { delegationsPagination } = ENV;
const { const {
data: userStakingData, data: userStakingData,
error: userStakingError, error: userStakingError,
@@ -29,11 +27,9 @@ export const EpochData = () => {
} = useStakingQuery({ } = useStakingQuery({
variables: { variables: {
partyId: pubKey || '', partyId: pubKey || '',
delegationsPagination: delegationsPagination delegationsPagination: {
? { first: 50,
first: Number(delegationsPagination), },
}
: undefined,
}, },
}); });
const { data: previousEpochData } = usePreviousEpochQuery({ const { data: previousEpochData } = usePreviousEpochQuery({
@@ -288,7 +288,7 @@ describe('Consensus validators table', () => {
expect( expect(
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]') grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
).toHaveTextContent('13.16%'); ).toHaveTextContent('10.07%');
expect( expect(
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]') grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
@@ -185,15 +185,19 @@ export const ConsensusValidatorsTable = ({
const { rawValidatorScore: previousEpochValidatorScore } = const { rawValidatorScore: previousEpochValidatorScore } =
getLastEpochScoreAndPerformance(previousEpochData, id); getLastEpochScoreAndPerformance(previousEpochData, id);
const overstakingPenalty = calculateOverallPenalty( const overstakingPenalty = calculateOverstakedPenalty(
id, id,
allNodesInPreviousEpoch allNodesInPreviousEpoch
); );
const totalPenalty = calculateOverstakedPenalty( const totalPenalty = calculateOverallPenalty(
id, id,
allNodesInPreviousEpoch allNodesInPreviousEpoch
); );
const lastEpochDataForNode = allNodesInPreviousEpoch.find(
(node) => node.id === id
);
return { return {
id, id,
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking, [ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
@@ -239,6 +243,12 @@ export const ConsensusValidatorsTable = ({
: undefined, : undefined,
[ValidatorFields.MULTISIG_ERROR]: [ValidatorFields.MULTISIG_ERROR]:
multisigStatus?.showMultisigStatusError, multisigStatus?.showMultisigStatusError,
[ValidatorFields.MULTISIG_PENALTY]: formatNumberPercentage(
new BigNumber(1)
.minus(lastEpochDataForNode?.rewardScore?.multisigScore ?? 1)
.times(100),
2
),
}; };
} }
); );
@@ -378,7 +388,6 @@ export const ConsensusValidatorsTable = ({
headerTooltip: t('StakeDescription').toString(), headerTooltip: t('StakeDescription').toString(),
cellRenderer: TotalStakeRenderer, cellRenderer: TotalStakeRenderer,
width: 120, width: 120,
sort: 'desc',
}, },
{ {
field: ValidatorFields.PENDING_STAKE, field: ValidatorFields.PENDING_STAKE,
@@ -400,6 +409,7 @@ export const ConsensusValidatorsTable = ({
headerTooltip: t('NormalisedVotingPowerDescription').toString(), headerTooltip: t('NormalisedVotingPowerDescription').toString(),
cellRenderer: VotingPowerRenderer, cellRenderer: VotingPowerRenderer,
width: 120, width: 120,
sort: 'desc',
}, },
{ {
field: ValidatorFields.TOTAL_PENALTIES, field: ValidatorFields.TOTAL_PENALTIES,
@@ -40,6 +40,7 @@ export enum ValidatorFields {
PENDING_USER_STAKE = 'pendingUserStake', PENDING_USER_STAKE = 'pendingUserStake',
USER_STAKE_SHARE = 'userStakeShare', USER_STAKE_SHARE = 'userStakeShare',
MULTISIG_ERROR = 'multisigError', MULTISIG_ERROR = 'multisigError',
MULTISIG_PENALTY = 'multisigPenalty',
} }
export const addUserDataToValidator = ( export const addUserDataToValidator = (
@@ -327,7 +328,7 @@ interface TotalPenaltiesRendererProps {
overstakedAmount: string; overstakedAmount: string;
overstakingPenalty: string; overstakingPenalty: string;
totalPenalties: string; totalPenalties: string;
multisigError?: boolean; multisigPenalty: string;
}; };
} }
@@ -346,11 +347,9 @@ export const TotalPenaltiesRenderer = ({
<div data-testid="overstaked-penalty-tooltip"> <div data-testid="overstaked-penalty-tooltip">
{t('overstakedPenalty')}: {data.overstakingPenalty} {t('overstakedPenalty')}: {data.overstakingPenalty}
</div> </div>
{data.multisigError && ( <div data-testid="multisig-error-tooltip">
<div data-testid="multisig-error-tooltip"> {t('multisigPenalty')}: {data.multisigPenalty}
{t('multisigPenalty')}: 100% </div>
</div>
)}
</> </>
} }
> >
@@ -1,4 +1,3 @@
import { ENV } from '../../../config';
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit'; import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -28,16 +27,13 @@ export const NodeContainer = ({
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
const { delegationsPagination } = ENV;
const { data, loading, error, refetch } = useStakingQuery({ const { data, loading, error, refetch } = useStakingQuery({
variables: { variables: {
partyId: pubKey || '', partyId: pubKey || '',
delegationsPagination: delegationsPagination delegationsPagination: {
? { first: 50,
first: Number(delegationsPagination), },
}
: undefined,
}, },
errorPolicy: 'all', errorPolicy: 'all',
}); });
@@ -3,7 +3,6 @@ import * as Sentry from '@sentry/react';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ENV } from '../../../config';
import { usePartyDelegationsLazyQuery } from './__generated__/PartyDelegations'; import { usePartyDelegationsLazyQuery } from './__generated__/PartyDelegations';
import { TokenInput } from '../../../components/token-input'; import { TokenInput } from '../../../components/token-input';
import { useAppState } from '../../../contexts/app-state/app-state-context'; import { useAppState } from '../../../contexts/app-state/app-state-context';
@@ -79,7 +78,6 @@ export const StakingForm = ({
const [error, setError] = useState<Error | null>(null); const [error, setError] = useState<Error | null>(null);
const [isDialogVisible, setIsDialogVisible] = useState(false); const [isDialogVisible, setIsDialogVisible] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
const { delegationsPagination } = ENV;
const [action, setAction] = React.useState<StakeAction>( const [action, setAction] = React.useState<StakeAction>(
params.action as StakeAction params.action as StakeAction
); );
@@ -154,11 +152,9 @@ export const StakingForm = ({
const [delegationSearch, { data }] = usePartyDelegationsLazyQuery({ const [delegationSearch, { data }] = usePartyDelegationsLazyQuery({
variables: { variables: {
partyId: pubKey, partyId: pubKey,
delegationsPagination: delegationsPagination delegationsPagination: {
? { first: 50,
first: Number(delegationsPagination), },
}
: undefined,
}, },
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
}); });
@@ -104,6 +104,11 @@ export const ValidatorTable = ({
}; };
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]); }, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
const previousNodeData =
previousEpochData?.epoch.validatorsConnection?.edges?.find(
(e) => e?.node.id === node.id
);
return ( return (
<> <>
<p className="mb-12"> <p className="mb-12">
@@ -281,6 +286,28 @@ export const ValidatorTable = ({
</span> </span>
</Tooltip> </Tooltip>
</KeyValueTableRow> </KeyValueTableRow>
<KeyValueTableRow>
<span className="uppercase">{t('multisigPenalty')}</span>
<span
data-testid="multisig-penalty"
className="flex gap-2 items-baseline"
>
<Tooltip description={t('multisigPenaltyDescription')}>
<span>
{formatNumberPercentage(
new BigNumber(1)
.minus(
previousNodeData?.node.rewardScore?.multisigScore ??
1
)
.times(100),
2
)}
</span>
</Tooltip>
</span>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}> <KeyValueTableRow noBorder={true}>
<span> <span>
<strong>{t('TOTAL PENALTIES')}</strong> <strong>{t('TOTAL PENALTIES')}</strong>
+1 -1
View File
@@ -14,7 +14,7 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
+1 -1
View File
@@ -13,7 +13,7 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/ NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session" NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
+1 -1
View File
@@ -14,7 +14,7 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Cosmic elevator flags # Cosmic elevator flags
+1 -1
View File
@@ -14,7 +14,7 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
+1 -1
View File
@@ -15,7 +15,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
+1 -1
View File
@@ -16,7 +16,7 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Cosmic elevator flags # Cosmic elevator flags
@@ -30,8 +30,6 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
setStudies, setStudies,
setStudySizes, setStudySizes,
setOverlays, setOverlays,
state,
setState,
} = useChartSettings(); } = useChartSettings();
const pennantChart = ( const pennantChart = (
@@ -68,10 +66,6 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
onIntervalChange={(newInterval) => { onIntervalChange={(newInterval) => {
setInterval(fromTradingViewResolution(newInterval)); setInterval(fromTradingViewResolution(newInterval));
}} }}
onAutoSaveNeeded={(data) => {
setState(data);
}}
state={state}
/> />
); );
} }
@@ -9,7 +9,6 @@ type StudySizes = { [S in Study]?: number };
export type Chartlib = 'pennant' | 'tradingview'; export type Chartlib = 'pennant' | 'tradingview';
interface StoredSettings { interface StoredSettings {
state: object | undefined; // Don't see a better type provided from TradingView type definitions
chartlib: Chartlib; chartlib: Chartlib;
// For interval we use the enum from @vegaprotocol/types, this is to make mapping between different // For interval we use the enum from @vegaprotocol/types, this is to make mapping between different
// chart types easier and more consistent // chart types easier and more consistent
@@ -30,7 +29,6 @@ const STUDY_ORDER: Study[] = [
]; ];
export const DEFAULT_CHART_SETTINGS = { export const DEFAULT_CHART_SETTINGS = {
state: undefined,
chartlib: 'pennant' as const, chartlib: 'pennant' as const,
interval: Interval.INTERVAL_I15M, interval: Interval.INTERVAL_I15M,
type: ChartType.CANDLE, type: ChartType.CANDLE,
@@ -47,7 +45,6 @@ export const useChartSettingsStore = create<
setStudies: (studies?: Study[]) => void; setStudies: (studies?: Study[]) => void;
setStudySizes: (sizes: number[]) => void; setStudySizes: (sizes: number[]) => void;
setChartlib: (lib: Chartlib) => void; setChartlib: (lib: Chartlib) => void;
setState: (state: object) => void;
} }
>()( >()(
persist( persist(
@@ -95,9 +92,6 @@ export const useChartSettingsStore = create<
state.chartlib = lib; state.chartlib = lib;
}); });
}, },
setState: (state) => {
set({ state });
},
})), })),
{ {
name: 'vega_candles_chart_store', name: 'vega_candles_chart_store',
@@ -151,7 +145,5 @@ export const useChartSettings = () => {
setOverlays: settings.setOverlays, setOverlays: settings.setOverlays,
setStudySizes: settings.setStudySizes, setStudySizes: settings.setStudySizes,
setChartlib: settings.setChartlib, setChartlib: settings.setChartlib,
state: settings.state,
setState: settings.setState,
}; };
}; };
@@ -21,18 +21,13 @@ const useFeesTableColumnDefs = (): ColDef[] => {
pinned: 'left', pinned: 'left',
width: 150, width: 150,
}, },
{
field: 'liquidityFee',
valueFormatter: ({ value }: { value: number }) => value + '%',
},
{ {
field: 'feeAfterDiscount', field: 'feeAfterDiscount',
headerName: t('Total fee after discount'), headerName: t('Total fee after discount'),
valueFormatter: ({ value }: { value: number }) => value + '%', valueFormatter: ({ value }: { value: number }) => value + '%',
}, },
{ {
field: 'totalFee', field: 'liquidityFee',
headerName: t('Total fee before discount'),
valueFormatter: ({ value }: { value: number }) => value + '%', valueFormatter: ({ value }: { value: number }) => value + '%',
}, },
{ {
@@ -43,6 +38,11 @@ const useFeesTableColumnDefs = (): ColDef[] => {
field: 'makerFee', field: 'makerFee',
valueFormatter: ({ value }: { value: number }) => value + '%', valueFormatter: ({ value }: { value: number }) => value + '%',
}, },
{
field: 'totalFee',
headerName: t('Total fee before discount'),
valueFormatter: ({ value }: { value: number }) => value + '%',
},
] as ColDef[], ] as ColDef[],
[t] [t]
); );
@@ -4,7 +4,6 @@ import {
lpAggregatedDataProvider, lpAggregatedDataProvider,
type Filter, type Filter,
LiquidityTable, LiquidityTable,
liquidityProvisionsDataProvider,
} from '@vegaprotocol/liquidity'; } from '@vegaprotocol/liquidity';
import { getAsset, useMarket } from '@vegaprotocol/markets'; import { getAsset, useMarket } from '@vegaprotocol/markets';
import { import {
@@ -71,7 +70,7 @@ export const LiquidityContainer = ({
const useReloadLiquidityData = (marketId: string | undefined) => { const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({ const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider, dataProvider: lpAggregatedDataProvider,
variables: { marketId: marketId || '' }, variables: { marketId: marketId || '' },
update: () => true, update: () => true,
skip: !marketId, skip: !marketId,
@@ -7,7 +7,7 @@ import {
useMaliciousOracle, useMaliciousOracle,
} from '@vegaprotocol/markets'; } from '@vegaprotocol/markets';
import { useState } from 'react'; import { useState } from 'react';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals'; import { type ProposalFragment } from '@vegaprotocol/proposals';
import { MarketSuspendedBanner } from './market-suspended-banner'; import { MarketSuspendedBanner } from './market-suspended-banner';
import { MarketUpdateBanner } from './market-update-banner'; import { MarketUpdateBanner } from './market-update-banner';
import { MarketUpdateStateBanner } from './market-update-state-banner'; import { MarketUpdateStateBanner } from './market-update-state-banner';
@@ -22,17 +22,17 @@ import {
type UpdateMarketBanner = { type UpdateMarketBanner = {
kind: 'UpdateMarket'; kind: 'UpdateMarket';
proposals: MarketViewProposalFieldsFragment[]; proposals: ProposalFragment[];
}; };
type UpdateMarketStateBanner = { type UpdateMarketStateBanner = {
kind: 'UpdateMarketState'; kind: 'UpdateMarketState';
proposals: MarketViewProposalFieldsFragment[]; proposals: ProposalFragment[];
}; };
type NewMarketBanner = { type NewMarketBanner = {
kind: 'NewMarket'; // aka a proposal of NewMarket which succeeds the current market kind: 'NewMarket'; // aka a proposal of NewMarket which succeeds the current market
proposals: MarketViewProposalFieldsFragment[]; proposals: ProposalFragment[];
}; };
type SettledBanner = { type SettledBanner = {
@@ -1,5 +1,5 @@
import { Fragment } from 'react'; import { Fragment } from 'react';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals'; import { type ProposalFragment } from '@vegaprotocol/proposals';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment'; import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
@@ -7,7 +7,7 @@ import { useT } from '../../lib/use-t';
export const MarketSuccessorProposalBanner = ({ export const MarketSuccessorProposalBanner = ({
proposals, proposals,
}: { }: {
proposals: MarketViewProposalFieldsFragment[]; proposals: ProposalFragment[];
}) => { }) => {
const t = useT(); const t = useT();
const tokenLink = useLinks(DApp.Governance); const tokenLink = useLinks(DApp.Governance);
@@ -28,7 +28,7 @@ export const MarketSuccessorProposalBanner = ({
} }
)}{' '} )}{' '}
{proposals.map((item, i) => { {proposals.map((item, i) => {
if (item.terms.change.__typename !== 'NewMarket') { if (item.terms?.change.__typename !== 'NewMarket') {
return null; return null;
} }
@@ -1,7 +1,7 @@
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { ProposalState } from '@vegaprotocol/types'; import { ProposalState } from '@vegaprotocol/types';
import { MarketUpdateBanner } from './market-update-banner'; import { MarketUpdateBanner } from './market-update-banner';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals'; import { type ProposalFragment } from '@vegaprotocol/proposals';
describe('MarketUpdateBanner', () => { describe('MarketUpdateBanner', () => {
const change = { const change = {
@@ -34,9 +34,7 @@ describe('MarketUpdateBanner', () => {
it('renders content for a single open proposal', () => { it('renders content for a single open proposal', () => {
render( render(
<MarketUpdateBanner <MarketUpdateBanner proposals={[openProposal as ProposalFragment]} />
proposals={[openProposal as MarketViewProposalFieldsFragment]}
/>
); );
expect( expect(
@@ -50,9 +48,7 @@ describe('MarketUpdateBanner', () => {
it('renders content for a single passed proposal', () => { it('renders content for a single passed proposal', () => {
render( render(
<MarketUpdateBanner <MarketUpdateBanner proposals={[passedProposal as ProposalFragment]} />
proposals={[passedProposal as MarketViewProposalFieldsFragment]}
/>
); );
expect( expect(
@@ -65,10 +61,7 @@ describe('MarketUpdateBanner', () => {
}); });
it('renders content for multiple passed proposals', () => { it('renders content for multiple passed proposals', () => {
const proposals = [ const proposals = [openProposal, openProposal] as ProposalFragment[];
openProposal,
openProposal,
] as MarketViewProposalFieldsFragment[];
render(<MarketUpdateBanner proposals={proposals} />); render(<MarketUpdateBanner proposals={proposals} />);
@@ -6,7 +6,7 @@ import {
TOKEN_PROPOSALS, TOKEN_PROPOSALS,
useLinks, useLinks,
} from '@vegaprotocol/environment'; } from '@vegaprotocol/environment';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals'; import { type ProposalFragment } from '@vegaprotocol/proposals';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { ProposalState } from '@vegaprotocol/types'; import { ProposalState } from '@vegaprotocol/types';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
@@ -14,17 +14,17 @@ import { useT } from '../../lib/use-t';
export const MarketUpdateBanner = ({ export const MarketUpdateBanner = ({
proposals, proposals,
}: { }: {
proposals: MarketViewProposalFieldsFragment[]; proposals: ProposalFragment[];
}) => { }) => {
const governanceLink = useLinks(DApp.Governance); const governanceLink = useLinks(DApp.Governance);
const t = useT(); const t = useT();
const openProposals = sortBy( const openProposals = sortBy(
proposals.filter((p) => p.state === ProposalState.STATE_OPEN), proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
(p) => p.terms.enactmentDatetime (p) => p.terms?.enactmentDatetime
); );
const passedProposals = sortBy( const passedProposals = sortBy(
proposals.filter((p) => p.state === ProposalState.STATE_PASSED), proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
(p) => p.terms.enactmentDatetime (p) => p.terms?.enactmentDatetime
); );
let content = null; let content = null;
@@ -49,7 +49,7 @@ export const MarketUpdateBanner = ({
content = ( content = (
<p> <p>
{t('Proposal set to change market on {{date}}.', { {t('Proposal set to change market on {{date}}.', {
date: format(new Date(proposal.terms.enactmentDatetime), 'dd MMMM'), date: format(new Date(proposal.terms?.enactmentDatetime), 'dd MMMM'),
})} })}
<ExternalLink href={proposalLink}>{t('View proposal')}</ExternalLink>, <ExternalLink href={proposalLink}>{t('View proposal')}</ExternalLink>,
</p> </p>
@@ -1,8 +1,8 @@
import { type ReactNode } from 'react'; import { type ReactNode } from 'react';
import sortBy from 'lodash/sortBy'; import sortBy from 'lodash/sortBy';
import { format, formatDuration, intervalToDuration } from 'date-fns'; import { format, formatDuration, intervalToDuration } from 'date-fns';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals'; import { type ProposalFragment } from '@vegaprotocol/proposals';
import { ProposalState } from '@vegaprotocol/types'; import { MarketUpdateType, ProposalState } from '@vegaprotocol/types';
import { import {
DApp, DApp,
TOKEN_PROPOSAL, TOKEN_PROPOSAL,
@@ -19,18 +19,29 @@ export const MarketUpdateStateBanner = ({
proposals, proposals,
}: { }: {
market: Market; market: Market;
proposals: MarketViewProposalFieldsFragment[]; proposals: ProposalFragment[];
}) => { }) => {
const t = useT(); const t = useT();
const governanceLink = useLinks(DApp.Governance); const governanceLink = useLinks(DApp.Governance);
const openTradingProposals = sortBy(
proposals.filter(
(p) =>
p.terms &&
p.terms.change.__typename === 'UpdateMarketState' &&
p.terms.change.updateType ===
MarketUpdateType.MARKET_STATE_UPDATE_TYPE_RESUME
),
(p) => p.terms?.enactmentDatetime
);
const openProposals = sortBy( const openProposals = sortBy(
proposals.filter((p) => p.state === ProposalState.STATE_OPEN), proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
(p) => p.terms.enactmentDatetime (p) => p.terms?.enactmentDatetime
); );
const passedProposals = sortBy( const passedProposals = sortBy(
proposals.filter((p) => p.state === ProposalState.STATE_PASSED), proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
(p) => p.terms.enactmentDatetime (p) => p.terms?.enactmentDatetime
); );
if (!passedProposals.length && !openProposals.length) { if (!passedProposals.length && !openProposals.length) {
@@ -45,12 +56,41 @@ export const MarketUpdateStateBanner = ({
? governanceLink(TOKEN_PROPOSAL.replace(':id', openProposals[0]?.id)) ? governanceLink(TOKEN_PROPOSAL.replace(':id', openProposals[0]?.id))
: undefined; : undefined;
const openTradingProposalsLink =
openTradingProposals[0]?.__typename === 'Proposal' &&
openTradingProposals[0]?.id
? governanceLink(
TOKEN_PROPOSAL.replace(':id', openTradingProposals[0].id)
)
: openTradingProposals[0]?.__typename === 'ProposalDetail' &&
openTradingProposals[0]?.batchId
? governanceLink(
TOKEN_PROPOSAL.replace(':id', openTradingProposals[0].batchId)
)
: undefined;
const proposalsLink = const proposalsLink =
openProposals.length > 1 ? governanceLink(TOKEN_PROPOSALS) : undefined; openProposals.length > 1 ? governanceLink(TOKEN_PROPOSALS) : undefined;
let content: ReactNode; let content: ReactNode;
if (passedProposals.length) { if (openTradingProposals.length >= 1) {
content = (
<>
<p className="mb-1">
{t(
'Trading on market {{name}} was suspended by governance. There are open proposals to resume trading on this market.',
{ name }
)}
</p>
<p>
<ExternalLink href={openTradingProposalsLink}>
{t('View proposals')}
</ExternalLink>
</p>
</>
);
} else if (passedProposals.length) {
const { date, duration, price } = getMessageVariables(passedProposals[0]); const { date, duration, price } = getMessageVariables(passedProposals[0]);
content = ( content = (
<> <>
@@ -116,20 +156,23 @@ export const MarketUpdateStateBanner = ({
return <div data-testid={`update-state-banner-${market.id}`}>{content}</div>; return <div data-testid={`update-state-banner-${market.id}`}>{content}</div>;
}; };
const getMessageVariables = (proposal: MarketViewProposalFieldsFragment) => { const getMessageVariables = (proposal: ProposalFragment) => {
const enactmentDatetime = new Date(proposal.terms.enactmentDatetime); const enactmentDatetime =
const date = format(enactmentDatetime, 'dd MMMM'); proposal.terms && new Date(proposal.terms.enactmentDatetime);
const duration = formatDuration( const date = enactmentDatetime && format(enactmentDatetime, 'dd MMMM');
intervalToDuration({ const duration =
start: new Date(), enactmentDatetime &&
end: enactmentDatetime, formatDuration(
}), intervalToDuration({
{ start: new Date(),
format: ['days', 'hours'], end: enactmentDatetime,
} }),
); {
format: ['days', 'hours'],
}
);
const price = const price =
proposal.terms.change.__typename === 'UpdateMarketState' proposal.terms?.change.__typename === 'UpdateMarketState'
? proposal.terms.change.price ? proposal.terms.change.price
: ''; : '';
return { return {
@@ -1,10 +1,10 @@
import { import {
useMarketProposals, useMarketProposals,
type MarketViewProposalFieldsFragment, type ProposalFragment,
} from '@vegaprotocol/proposals'; } from '@vegaprotocol/proposals';
import { ProposalState, ProposalType } from '@vegaprotocol/types'; import { ProposalState, ProposalType } from '@vegaprotocol/types';
const isPending = (p: MarketViewProposalFieldsFragment) => { const isPending = (p: ProposalFragment) => {
return [ProposalState.STATE_OPEN, ProposalState.STATE_PASSED].includes( return [ProposalState.STATE_OPEN, ProposalState.STATE_PASSED].includes(
p.state p.state
); );
@@ -21,9 +21,10 @@ export const useUpdateMarketStateProposals = (
const proposals = data const proposals = data
? data.filter(isPending).filter((p) => { ? data.filter(isPending).filter((p) => {
const change = p.terms.change; const change = p.terms?.change;
if ( if (
change &&
change.__typename === 'UpdateMarketState' && change.__typename === 'UpdateMarketState' &&
change.market.id === marketId change.market.id === marketId
) { ) {
@@ -48,8 +49,9 @@ export const useUpdateMarketProposals = (
const proposals = data const proposals = data
? data.filter(isPending).filter((p) => { ? data.filter(isPending).filter((p) => {
const change = p.terms.change; const change = p.terms?.change;
if ( if (
change &&
change.__typename === 'UpdateMarket' && change.__typename === 'UpdateMarket' &&
change.marketId === marketId change.marketId === marketId
) { ) {
@@ -73,8 +75,9 @@ export const useSuccessorMarketProposals = (
const proposals = data const proposals = data
? data.filter(isPending).filter((p) => { ? data.filter(isPending).filter((p) => {
const change = p.terms.change; const change = p.terms?.change;
if ( if (
change &&
change.__typename === 'NewMarket' && change.__typename === 'NewMarket' &&
change.successorConfiguration?.parentMarketId === marketId change.successorConfiguration?.parentMarketId === marketId
) { ) {
@@ -12,6 +12,7 @@ import { useT } from '../../lib/use-t';
import classNames from 'classnames'; import classNames from 'classnames';
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats'; import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
import { MarketMarkPrice } from '../market-mark-price'; import { MarketMarkPrice } from '../market-mark-price';
import { MarketBanner } from '../market-banner';
/** /**
* This is only rendered for the mobile navigation * This is only rendered for the mobile navigation
*/ */
@@ -108,8 +109,11 @@ export const MobileMarketHeader = () => {
} }
> >
{data && ( {data && (
<div className="px-3 py-6 text-sm grid grid-cols-2 items-center gap-x-4 gap-y-6"> <div>
<MarketHeaderStats market={data} /> <MarketBanner market={data} />
<div className="px-3 py-6 text-sm grid grid-cols-2 items-center gap-x-4 gap-y-6">
<MarketHeaderStats market={data} />
</div>
</div> </div>
)} )}
</FullScreenPopover> </FullScreenPopover>
@@ -24,6 +24,7 @@ import {
type DispatchStrategy, type DispatchStrategy,
IndividualScopeMapping, IndividualScopeMapping,
IndividualScopeDescriptionMapping, IndividualScopeDescriptionMapping,
type Asset,
} from '@vegaprotocol/types'; } from '@vegaprotocol/types';
import { Card } from '../card/card'; import { Card } from '../card/card';
import { type ReactNode, useState } from 'react'; import { type ReactNode, useState } from 'react';
@@ -200,7 +201,8 @@ export const ActiveRewardCard = ({
transferNode.transfer.asset?.decimals || 0, transferNode.transfer.asset?.decimals || 0,
6 6
)} )}
rewardAsset={transferNode.asset} rewardAsset={transferNode.dispatchAsset}
transferAsset={transferNode.transfer.asset || undefined}
endsIn={ endsIn={
transferNode.transfer.kind.endEpoch != null transferNode.transfer.kind.endEpoch != null
? transferNode.transfer.kind.endEpoch - currentEpoch ? transferNode.transfer.kind.endEpoch - currentEpoch
@@ -216,6 +218,8 @@ const RewardCard = ({
colour, colour,
rewardAmount, rewardAmount,
rewardAsset, rewardAsset,
transferAsset,
vegaAsset,
dispatchStrategy, dispatchStrategy,
endsIn, endsIn,
dispatchMetricInfo, dispatchMetricInfo,
@@ -224,12 +228,14 @@ const RewardCard = ({
rewardAmount: string; rewardAmount: string;
/** The asset linked to the dispatch strategy via `dispatchMetricAssetId` property. */ /** The asset linked to the dispatch strategy via `dispatchMetricAssetId` property. */
rewardAsset?: BasicAssetDetails; rewardAsset?: BasicAssetDetails;
/** The VEGA asset details, required to format the min staking amount. */
transferAsset?: Asset | undefined;
/** The VEGA asset details, required to format the min staking amount. */
vegaAsset?: BasicAssetDetails;
/** The transfer's dispatch strategy. */ /** The transfer's dispatch strategy. */
dispatchStrategy: DispatchStrategy; dispatchStrategy: DispatchStrategy;
/** The number of epochs until the transfer stops. */ /** The number of epochs until the transfer stops. */
endsIn?: number; endsIn?: number;
/** The VEGA asset details, required to format the min staking amount. */
vegaAsset?: BasicAssetDetails;
dispatchMetricInfo?: ReactNode; dispatchMetricInfo?: ReactNode;
}) => { }) => {
const t = useT(); const t = useT();
@@ -269,7 +275,9 @@ const RewardCard = ({
{rewardAmount} {rewardAmount}
</span> </span>
<span className="font-alpha">{rewardAsset?.symbol || ''}</span> <span className="font-alpha" data-testid="reward-asset">
{transferAsset?.symbol || ''}
</span>
</h3> </h3>
{/** DISTRIBUTION STRATEGY */} {/** DISTRIBUTION STRATEGY */}
@@ -357,6 +365,7 @@ const RewardCard = ({
<RewardRequirements <RewardRequirements
dispatchStrategy={dispatchStrategy} dispatchStrategy={dispatchStrategy}
rewardAsset={rewardAsset} rewardAsset={rewardAsset}
vegaAsset={vegaAsset}
/> />
)} )}
</div> </div>
@@ -379,8 +388,8 @@ export const DispatchMetricInfo = ({
let additionalDispatchMetricInfo = null; let additionalDispatchMetricInfo = null;
// if asset found then display asset symbol // if asset found then display asset symbol
if (reward.asset) { if (reward.dispatchAsset) {
additionalDispatchMetricInfo = <span>{reward.asset.symbol}</span>; additionalDispatchMetricInfo = <span>{reward.dispatchAsset.symbol}</span>;
} }
// but if scoped to only one market then display market name // but if scoped to only one market then display market name
if (marketNames.length === 1) { if (marketNames.length === 1) {
+1 -1
View File
@@ -1,3 +1,3 @@
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
VEGA_VERSION=v0.74.3 VEGA_VERSION=v0.74.6
LOCAL_SERVER=false LOCAL_SERVER=false
+1 -1
View File
@@ -1,3 +1,3 @@
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
VEGA_VERSION=v0.74.3 VEGA_VERSION=v0.74.6
LOCAL_SERVER=false LOCAL_SERVER=false
+63 -119
View File
@@ -1,156 +1,100 @@
# Trading Market-Sim End-To-End Tests # Trading Market-Sim End-To-End Tests
This direcotry contains end-to-end tests for the trading application using vega-market-sim. This README will guide you through setting up your environment and running the tests. This directory contains end-to-end tests for the Trading application using Vega-market-sim. This guide will help you set up your environment and run the tests efficiently.
## Prerequisites ## Prerequisites
- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) Ensure you have the following installed:
- [Docker](https://www.docker.com/)
- [Python versions ">=3.9,<3.11"](https://www.python.org/)
## Getting Started - [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) for dependency management.
- [Docker](https://www.docker.com/) for running isolated application containers.
- Python, versions ">=3.9,<3.11". Install from the [official Python website](https://www.python.org/).
1. **Install Poetry**: Follow the instructions on the [official Poetry website](https://python-poetry.org/docs/#installing-with-the-official-installer). ## Setup
2. **Install Docker**: Follow the instructions on the [official Docker website](https://docs.docker.com/desktop/).
3. **Install Python**: Follow the instructions on the [official Python website](https://www.python.org/)
**ensure you install a version between 3.9 and 3.11.**
4. **Start up a Poetry environment**: Execute the commands below to configure the Poetry environment.
### Ensure you are in the tests folder before running commands ### 1. Install Dependencies
- **Poetry**: Follow the installation guide on the [official Poetry website](https://python-poetry.org/docs/#installing-with-the-official-installer).
- **Docker**: Installation instructions are available on the [official Docker website](https://docs.docker.com/desktop/).
- **Python**: Install a version between 3.9 and 3.11, as detailed on the [official Python website](https://www.python.org/).
### 2. Configure Your Environment
Ensure you're in the tests folder before executing commands.
```bash ```bash
poetry shell poetry shell
``` poetry update vega-sim # Updates to the latest version of the market-sim branch
5. **Install python dependencies**
To make sure you are on the latest version of our market-sim branch.
```bash
poetry update vega-sim
```
```bash
poetry install poetry install
playwright install chromium # Installs necessary browsers for Playwright
``` ```
6. **Install Playwright Browsers**: Execute the command below to browsers for Playwright. ### 3. Prepare Binaries and Docker Images
```bash Download necessary binaries for the desired Vega version:
playwright install chromium
```
7. **Download necessary binaries**:
Use the following command within your Python environment. The `--force` flag ensures the binaries are overwritten, and the `--version` specifies the desired version. e.g. `v0.73.4`
```bash ```bash
python -m vega_sim.tools.load_binaries --force --version $VEGA_VERSION python -m vega_sim.tools.load_binaries --force --version $VEGA_VERSION
``` ```
8. **Pull the desired Docker image** Pull Docker images for your environment:
```bash - **Development**: `docker pull vegaprotocol/trading:develop`
docker pull vegaprotocol/trading:develop - **Production**: `docker pull vegaprotocol/trading:main`
```
9. **Run tests**: Poetry/Python will serve the app from docker ### 4. Build a Docker Image of Your Locally Built Trading App
### Update the .env file with the correct trading image.
```bash
poetry run pytest
```
### Docker images
Pull the desired image:
**Testnet**
```bash
docker pull vegaprotocol/trading:develop
```
**Mainnet**
```bash
docker pull vegaprotocol/trading:main
```
Find all available images on [Docker Hub](https://hub.docker.com/r/vegaprotocol/trading/tags).
#### Create a Docker Image of Your Locally Built Trading App
To build your Docker image, use the following commands:
```bash
yarn nx build trading ./docker/prepare-dist.sh
```
```bash ```bash
./docker/prepare-dist.sh
docker build -f docker/node-outside-docker.Dockerfile --build-arg APP=trading --build-arg ENV_NAME=stagnet1 -t vegaprotocol/trading:latest . docker build -f docker/node-outside-docker.Dockerfile --build-arg APP=trading --build-arg ENV_NAME=stagnet1 -t vegaprotocol/trading:latest .
``` ```
## Running Tests 🧪 ## Running Tests
Before running make sure the docker daemon is running. Ensure the Docker daemon is running. Update the `.env` file with the correct trading image before proceeding.
To run a specific test, use the `-k` option followed by the name of the test. - **Run all tests**: `poetry run pytest`
Run all tests: - **Run a specific test**: `poetry run pytest -k "test_name" -s --headed`
- **Run tests using your locally served console**:
```bash In one terminal window, build and serve the trading console:
poetry run pytest
```bash
yarn nx build trading
yarn nx serve trading
```
Once the console is served, update the `.env` file to set `local_server=true`. You can then run your tests using the same commands as above.
NOTE: Parallel running of tests will not work against locally served console.
## Test Strategy and Container Cleanup
### Strategy
We aim for each test file to use a single Vega instance to ensure test isolation and manage resources efficiently. This approach helps in maintaining test performance and reliability.
### Cleanup Procedure
To ensure proper cleanup of containers after each test, use the following fixture pattern:
```python
@pytest.fixture
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
``` ```
Run a targeted test: ## Running Tests in Parallel
```bash For running tests in parallel:
poetry run pytest -k "test_name" -s --headed
```
Run from anywhere: - **Within the e2e folder**: `poetry run pytest -s --numprocesses auto --dist loadfile`
- **From anywhere**: `yarn trading:test:all`
```bash ## Troubleshooting
yarn trading:test -- "test_name" -s --headed
```
Run using your locally served console: If IntelliSense is not working in VSCode, follow these steps:
Within one terminal 1. Find the Poetry environment's Python binary: `poetry run which python`
2. In VSCode, open the command menu (`cmd + shift + p`), search for `Python: Select Interpreter`, select `Enter interpreter path`, and paste the path from step 1.
```bash
yarn nx build trading
```
```bash
yarn nx serve trading
```
Once console is served you can update the .env file to have local_server to true.
## Running Tests in Parallel 🔢
To run tests in parallel, use the `--numprocesses auto` option. The `--dist loadfile` setting ensures that multiple runners are not assigned to a single test file.
### From within the e2e folder:
```bash
poetry run pytest -s --numprocesses auto --dist loadfile
```
### From anywhere:
```bash
yarn trading:test:all
```
# Things to know
If you "intellisense" isn't working follow these steps:
1. ```bash
poetry run which python
```
2. Then open the command menu in vscode (cmd + shift + p) and type `select interpreter` , press enter, select enter interpreter path press enter then paste in the output from that above command you should get the right python again
+396 -391
View File
File diff suppressed because it is too large Load Diff
@@ -235,7 +235,7 @@ def test_submit_stop_oco_market_order_pending(
"PendingOCO" "PendingOCO"
) )
@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted") @pytest.mark.usefixtures("auth", "risk_accepted")
def test_submit_stop_oco_limit_order_pending( def test_submit_stop_oco_limit_order_pending(
continuous_market, vega: VegaServiceNull, page: Page continuous_market, vega: VegaServiceNull, page: Page
): ):
@@ -15,8 +15,7 @@ deal_ticket_deposit_dialog_button = "deal-ticket-deposit-dialog-button"
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def vega(request): def vega(request):
with init_vega(request) as vega_instance: with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container( request.addfinalizer(lambda: cleanup_container(vega_instance))
vega_instance)) # Register the cleanup function
yield vega_instance yield vega_instance
@@ -25,7 +24,6 @@ def continuous_market(vega):
return setup_continuous_market(vega) return setup_continuous_market(vega)
@pytest.mark.skip("tbd - issue only on the sim, should work in vega 0.74.0")
@pytest.mark.usefixtures("auth", "risk_accepted") @pytest.mark.usefixtures("auth", "risk_accepted")
def test_should_display_info_and_button_for_deposit(continuous_market, page: Page): def test_should_display_info_and_button_for_deposit(continuous_market, page: Page):
page.goto(f"/#/markets/{continuous_market}") page.goto(f"/#/markets/{continuous_market}")
@@ -33,17 +31,20 @@ def test_should_display_info_and_button_for_deposit(continuous_market, page: Pag
page.get_by_test_id(order_price).fill("20") page.get_by_test_id(order_price).fill("20")
# 7002-SORD-060 # 7002-SORD-060
expect(page.get_by_test_id(deal_ticket_warning_margin)).to_have_text( expect(page.get_by_test_id(deal_ticket_warning_margin)).to_have_text(
"You may not have enough margin available to open this position.") "You may not have enough margin available to open this position."
)
page.get_by_test_id(deal_ticket_warning_margin).hover() page.get_by_test_id(deal_ticket_warning_margin).hover()
expect(page.get_by_test_id("tooltip-content").nth(0)).to_have_text( expect(page.get_by_test_id("tooltip-content").nth(0)).to_have_text(
"1,661,888.12901 tDAI is currently required.You have only 999,991.49731.Deposit tDAI") "1,661,888.12901 tDAI is currently required.You have only 999,991.49731.Deposit tDAI"
)
page.get_by_test_id(deal_ticket_deposit_dialog_button).nth(0).click() page.get_by_test_id(deal_ticket_deposit_dialog_button).nth(0).click()
expect(page.get_by_test_id("sidebar-content") expect(page.get_by_test_id("sidebar-content")).to_contain_text("DepositFrom")
).to_contain_text("DepositFrom")
@pytest.mark.usefixtures("auth", "risk_accepted") @pytest.mark.usefixtures("auth", "risk_accepted")
def test_should_show_an_error_if_your_balance_is_zero(continuous_market, vega: VegaServiceNull, page: Page): def test_should_show_an_error_if_your_balance_is_zero(
continuous_market, vega: VegaServiceNull, page: Page
):
page.goto(f"/#/markets/{continuous_market}") page.goto(f"/#/markets/{continuous_market}")
vega.create_key("key_empty") vega.create_key("key_empty")
change_keys(page, vega, "key_empty") change_keys(page, vega, "key_empty")
@@ -52,6 +53,7 @@ def test_should_show_an_error_if_your_balance_is_zero(continuous_market, vega: V
# 7002-SORD-060 # 7002-SORD-060
expect(page.get_by_test_id(place_order)).to_be_enabled() expect(page.get_by_test_id(place_order)).to_be_enabled()
# 7002-SORD-003 # 7002-SORD-003
expect(page.get_by_test_id("deal-ticket-error-message-zero-balance") expect(page.get_by_test_id("deal-ticket-error-message-zero-balance")).to_have_text(
).to_have_text("You need tDAI in your wallet to trade in this market.Make a deposit") "You need tDAI in your wallet to trade in this market.Make a deposit"
)
expect(page.get_by_test_id(deal_ticket_deposit_dialog_button)).to_be_visible() expect(page.get_by_test_id(deal_ticket_deposit_dialog_button)).to_be_visible()
@@ -77,7 +77,7 @@ def test_market_info_market_volume(page: Page):
page.get_by_test_id(market_title_test_id).get_by_text( page.get_by_test_id(market_title_test_id).get_by_text(
"Market volume").click() "Market volume").click()
fields = [ fields = [
["24 Hour Volume", "-"], ["24 Hour Volume", "0 (0 )"],
["Open Interest", "1"], ["Open Interest", "1"],
["Best Bid Volume", "99"], ["Best Bid Volume", "99"],
["Best Offer Volume", "99"], ["Best Offer Volume", "99"],
@@ -107,7 +107,6 @@ def test_market_info_liquidation(page: Page):
validate_info_section(page, fields) validate_info_section(page, fields)
@pytest.mark.skip("core issue #5681")
def test_market_info_key_details(page: Page, vega: VegaServiceNull): def test_market_info_key_details(page: Page, vega: VegaServiceNull):
# 6002-MDET-201 # 6002-MDET-201
page.get_by_test_id(market_title_test_id).get_by_text( page.get_by_test_id(market_title_test_id).get_by_text(
@@ -117,8 +116,6 @@ def test_market_info_key_details(page: Page, vega: VegaServiceNull):
fields = [ fields = [
["Market ID", short_market_id], ["Market ID", short_market_id],
["Name", "BTC:DAI_2023"], ["Name", "BTC:DAI_2023"],
["Parent Market ID", "-"],
["Insurance Pool Fraction", "-"],
["Status", "Active"], ["Status", "Active"],
["Trading Mode", "Continuous"], ["Trading Mode", "Continuous"],
["Market Decimal Places", "5"], ["Market Decimal Places", "5"],
@@ -9,6 +9,7 @@ from actions.utils import next_epoch
from wallet_config import MM_WALLET, MM_WALLET2, GOVERNANCE_WALLET from wallet_config import MM_WALLET, MM_WALLET2, GOVERNANCE_WALLET
from vega_sim.api import governance from vega_sim.api import governance
@pytest.mark.usefixtures("risk_accepted") @pytest.mark.usefixtures("risk_accepted")
def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page): def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
# 7002-SORD-001 # 7002-SORD-001
@@ -16,15 +17,18 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
trading_mode = page.get_by_test_id("market-trading-mode").get_by_test_id( trading_mode = page.get_by_test_id("market-trading-mode").get_by_test_id(
"item-value" "item-value"
) )
market_state = page.get_by_test_id("market-state").get_by_test_id("item-value") market_state = page.get_by_test_id(
"market-state").get_by_test_id("item-value")
# setup market in proposed step, without liquidity provided # setup market in proposed step, without liquidity provided
market_id = proposed_market market_id = proposed_market
page.goto(f"/#/markets/{market_id}") page.goto(f"/#/markets/{market_id}")
# 6002-MDET-001 # 6002-MDET-001
expect(page.get_by_test_id("header-title")).to_have_text("BTC:DAI_2023Futr") expect(page.get_by_test_id("header-title")
).to_have_text("BTC:DAI_2023Futr")
# 6002-MDET-002 # 6002-MDET-002
expect(page.get_by_test_id("market-expiry")).to_have_text("ExpiryNot time-based") expect(page.get_by_test_id("market-expiry")
).to_have_text("ExpiryNot time-based")
page.get_by_test_id("market-expiry").hover() page.get_by_test_id("market-expiry").hover()
expect(page.get_by_test_id("expiry-tooltip").first).to_have_text( expect(page.get_by_test_id("expiry-tooltip").first).to_have_text(
"This market expires when triggered by its oracle, not on a set date.View oracle specification" "This market expires when triggered by its oracle, not on a set date.View oracle specification"
@@ -123,16 +127,16 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
expect(market_state).to_have_text("Active") expect(market_state).to_have_text("Active")
vega.update_market_state( vega.update_market_state(
market_id = market_id, market_id=market_id,
proposal_key = MM_WALLET.name, proposal_key=MM_WALLET.name,
market_state = MarketStateUpdateType.Suspend, market_state=MarketStateUpdateType.Suspend,
forward_time_to_enactment = False forward_time_to_enactment=False
) )
expect( expect(
page page
.get_by_test_id("market-banner") .get_by_test_id("market-banner")
.get_by_test_id(f"update-state-banner-{market_id}") .get_by_test_id(f"update-state-banner-{market_id}")
).to_be_visible() ).to_be_visible()
next_epoch(vega=vega) next_epoch(vega=vega)
@@ -143,12 +147,14 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
# banner should not show after resume # banner should not show after resume
vega.update_market_state( vega.update_market_state(
market_id = market_id, market_id=market_id,
proposal_key = MM_WALLET.name, proposal_key=MM_WALLET.name,
market_state = MarketStateUpdateType.Resume, market_state=MarketStateUpdateType.Resume,
forward_time_to_enactment = False forward_time_to_enactment=False
) )
expect(page.get_by_test_id("market-banner")
).to_have_text(" Trading on market BTC:DAI_2023 was suspended by governance. There are open proposals to resume trading on this market.View proposals1/2")
next_epoch(vega=vega) next_epoch(vega=vega)
expect(page.get_by_test_id("market-banner")).not_to_be_visible() expect(page.get_by_test_id("market-banner")).not_to_be_visible()
@@ -1,4 +1,4 @@
""" import pytest import pytest
from playwright.sync_api import Page, expect from playwright.sync_api import Page, expect
from vega_sim.service import PeggedOrder from vega_sim.service import PeggedOrder
from vega_sim.null_service import VegaServiceNull from vega_sim.null_service import VegaServiceNull
@@ -49,7 +49,7 @@ def markets(vega: VegaServiceNull):
price=130, price=130,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -63,7 +63,7 @@ def markets(vega: VegaServiceNull):
price=88, price=88,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -77,7 +77,7 @@ def markets(vega: VegaServiceNull):
price=88, price=88,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -92,7 +92,7 @@ def markets(vega: VegaServiceNull):
wait=False, wait=False,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -105,7 +105,7 @@ def markets(vega: VegaServiceNull):
volume=100, volume=100,
price=104, price=104,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -120,7 +120,7 @@ def markets(vega: VegaServiceNull):
expires_at=vega.get_blockchain_time() + 5 * 1e9, expires_at=vega.get_blockchain_time() + 5 * 1e9,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -134,7 +134,7 @@ def markets(vega: VegaServiceNull):
volume=20, volume=20,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -148,7 +148,7 @@ def markets(vega: VegaServiceNull):
volume=40, volume=40,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -162,7 +162,7 @@ def markets(vega: VegaServiceNull):
volume=60, volume=60,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -177,7 +177,7 @@ def markets(vega: VegaServiceNull):
volume=60, volume=60,
) )
vega.forward("10s") vega.forward("2s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -191,7 +191,6 @@ def markets(vega: VegaServiceNull):
price=150, price=150,
) )
vega.forward("10s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -205,7 +204,6 @@ def markets(vega: VegaServiceNull):
price=160, price=160,
) )
vega.forward("10s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -219,7 +217,6 @@ def markets(vega: VegaServiceNull):
price=60, price=60,
) )
vega.forward("10s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -395,7 +392,6 @@ def test_order_amend_order(vega: VegaServiceNull, page: Page):
page.get_by_role("button", name="Update").click() page.get_by_role("button", name="Update").click()
wait_for_toast_confirmation(page, timeout=5000) wait_for_toast_confirmation(page, timeout=5000)
vega.forward("10s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
expect(page.locator('[row-index="1"]').first).to_contain_text( expect(page.locator('[row-index="1"]').first).to_contain_text(
@@ -414,7 +410,6 @@ def test_order_cancel_single_order(vega: VegaServiceNull, page: Page):
page.get_by_test_id("cancel").first.click() page.get_by_test_id("cancel").first.click()
wait_for_toast_confirmation(page, timeout=5000) wait_for_toast_confirmation(page, timeout=5000)
vega.forward("10s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
expect(page.locator('[row-index="0"]').first).to_contain_text( expect(page.locator('[row-index="0"]').first).to_contain_text(
@@ -434,7 +429,6 @@ def test_order_cancel_all_orders(vega: VegaServiceNull, page: Page):
page.get_by_test_id("cancelAll").click() page.get_by_test_id("cancelAll").click()
wait_for_toast_confirmation(page, timeout=5000) wait_for_toast_confirmation(page, timeout=5000)
vega.forward("10s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -443,4 +437,3 @@ def test_order_cancel_all_orders(vega: VegaServiceNull, page: Page):
expect( expect(
page.locator('.ag-cell[col-id="status"]', has_text="Cancelled") page.locator('.ag-cell[col-id="status"]', has_text="Cancelled")
).to_have_count(7) ).to_have_count(7)
"""
@@ -1,10 +1,12 @@
import pytest import pytest
from playwright.sync_api import Page, expect from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from typing import List from typing import List
from actions.vega import submit_order, submit_liquidity, submit_multiple_orders from actions.vega import submit_order, submit_liquidity, submit_multiple_orders
from conftest import init_vega, cleanup_container from conftest import init_vega, cleanup_container
from fixtures.market import setup_simple_market from fixtures.market import setup_simple_market
from wallet_config import MM_WALLET, MM_WALLET2 from wallet_config import MM_WALLET, MM_WALLET2
from actions.utils import next_epoch
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
@@ -15,7 +17,7 @@ def vega(request):
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def setup_market(vega): def setup_market(vega: VegaServiceNull):
market_id = setup_simple_market(vega) market_id = setup_simple_market(vega)
submit_liquidity(vega, MM_WALLET.name, market_id) submit_liquidity(vega, MM_WALLET.name, market_id)
submit_multiple_orders( submit_multiple_orders(
@@ -33,7 +35,6 @@ def setup_market(vega):
[[10, 69.995], [5, 70], [5, 85], [3, 90], [3, 95]], [[10, 69.995], [5, 70], [5, 85], [3, 90], [3, 95]],
) )
vega.forward("10s")
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@@ -46,19 +47,19 @@ def setup_market(vega):
# these values don't align with the multiple orders above as # these values don't align with the multiple orders above as
# creating a trade triggers the liquidity provision # creating a trade triggers the liquidity provision
orderbook_content = [ orderbook_content = [
[130.00500, 10, 94], [130.00500, 10, 126],
[130.00000, 3, 84], [130.00000, 3, 116],
[120.00000, 7, 81], [120.00000, 7, 113],
[110.00000, 5, 74], [110.00000, 5, 106],
[105.00000, 2, 69], [105.00000, 2, 101],
[101.00000, 67, 67], [101.00000, 99, 99],
# mid # mid
[99.00000, 102, 102], [99.00000, 99, 99],
[95.00000, 3, 105], [95.00000, 3, 102],
[90.00000, 3, 108], [90.00000, 3, 105],
[85.00000, 5, 113], [85.00000, 5, 110],
[70.00000, 5, 118], [70.00000, 5, 115],
[69.99500, 10, 128], [69.99500, 10, 125],
] ]
@@ -81,7 +82,6 @@ def verify_prices_descending(page: Page):
assert prices == sorted(prices, reverse=True) assert prices == sorted(prices, reverse=True)
@pytest.mark.skip("tbd")
@pytest.mark.usefixtures("risk_accepted") @pytest.mark.usefixtures("risk_accepted")
def test_orderbook_grid_content(setup_market, page: Page): def test_orderbook_grid_content(setup_market, page: Page):
vega = setup_market[0] vega = setup_market[0]
@@ -109,6 +109,7 @@ def test_orderbook_grid_content(setup_market, page: Page):
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
next_epoch(vega)
# 6003-ORDB-001 # 6003-ORDB-001
# 6003-ORDB-002 # 6003-ORDB-002
@@ -120,7 +121,6 @@ def test_orderbook_grid_content(setup_market, page: Page):
page.goto(f"/#/markets/{market_id}") page.goto(f"/#/markets/{market_id}")
page.locator("[data-testid=Orderbook]").click() page.locator("[data-testid=Orderbook]").click()
# 6003-ORDB-013 # 6003-ORDB-013
assert ( assert (
float(page.locator("[data-testid*=last-traded]").text_content()) float(page.locator("[data-testid*=last-traded]").text_content())
@@ -144,50 +144,46 @@ def test_orderbook_grid_content(setup_market, page: Page):
def test_orderbook_resolution_change(setup_market, page: Page): def test_orderbook_resolution_change(setup_market, page: Page):
market_id = setup_market[1] market_id = setup_market[1]
# 6003-ORDB-008 # 6003-ORDB-008
orderbook_content_0_00 = [ orderbook_content_0_01 = [
[130.01, 10, 94], [130.01, 10, 126],
[130.00, 3, 84], [130.00, 3, 116],
[120.00, 7, 81], [120.00, 7, 113],
[110.00, 5, 74], [110.00, 5, 106],
[105.00, 2, 69], [105.00, 2, 101],
[101.00, 67, 67], [101.00, 99, 99],
# mid # mid
[99.00, 102, 102], [99.00, 99, 99],
[95.00, 3, 105], [95.00, 3, 102],
[90.00, 3, 108], [90.00, 3, 105],
[85.00, 5, 113], [85.00, 5, 110],
[70.00, 15, 128], [70.00, 15, 125],
] ]
orderbook_content_10 = [ orderbook_content_1 = [
[130, 13, 94], [130, 13, 126],
[120, 7, 81], [120, 7, 113],
[110, 7, 74], [110, 5, 106],
[100, 67, 67], [105, 2, 101],
[101, 99, 99],
# mid # mid
[100, 105, 105], [99, 99, 99],
[90, 8, 113], [95, 3, 102],
[70, 15, 128], [90, 3, 105],
[85, 5, 110],
[70, 15, 125],
] ]
orderbook_content_100 = [
[100, 94, 94],
# mid
[100, 128, 128],
]
resolutions = [ resolutions = [
["0.00", orderbook_content_0_00], ["0.01", orderbook_content_0_01],
["10", orderbook_content_10], ["1", orderbook_content_1],
["100", orderbook_content_100],
] ]
page.goto(f"/#/markets/{market_id}") page.goto(f"/#/markets/{market_id}")
# temporary skip for resolution in resolutions:
# for resolution in resolutions: page.get_by_test_id("resolution").click()
# page.get_by_test_id("resolution").click() page.get_by_role("menuitem").get_by_text(resolution[0], exact=True).click()
# page.get_by_role("menu").get_by_text(resolution[0], exact=True).click() verify_orderbook_grid(page, resolution[1])
# verify_orderbook_grid(page, resolution[1])
@pytest.mark.usefixtures("risk_accepted") @pytest.mark.usefixtures("risk_accepted")
@@ -209,7 +205,6 @@ def test_orderbook_price_size_copy(setup_market, page: Page):
expect(page.get_by_test_id("order-size")).to_have_value(volume.text_content()) expect(page.get_by_test_id("order-size")).to_have_value(volume.text_content())
@pytest.mark.skip("tbd")
@pytest.mark.usefixtures("risk_accepted") @pytest.mark.usefixtures("risk_accepted")
def test_orderbook_price_movement(setup_market, page: Page): def test_orderbook_price_movement(setup_market, page: Page):
vega = setup_market[0] vega = setup_market[0]
@@ -119,7 +119,7 @@ def setup_market_and_referral_scheme(vega: VegaServiceNull, continuous_market: s
forward_time(vega) forward_time(vega)
@pytest.mark.usefixtures("page", "auth", "risk_accepted") @pytest.mark.usefixtures("auth", "risk_accepted")
def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaServiceNull, page: Page): def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaServiceNull, page: Page):
setup_market_and_referral_scheme(vega, continuous_market, page) setup_market_and_referral_scheme(vega, continuous_market, page)
change_keys(page, vega, PARTY_B.name) change_keys(page, vega, PARTY_B.name)
@@ -163,7 +163,7 @@ def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaSer
"1%", "1", "1%", "0", "1", "1")) "1%", "1", "1%", "0", "1", "1"))
@pytest.mark.usefixtures("page", "auth", "risk_accepted") @pytest.mark.usefixtures("auth", "risk_accepted")
def test_does_not_move_up_tiers_when_not_enough_epochs(continuous_market, vega: VegaServiceNull, page: Page): def test_does_not_move_up_tiers_when_not_enough_epochs(continuous_market, vega: VegaServiceNull, page: Page):
setup_market_and_referral_scheme(vega, continuous_market, page) setup_market_and_referral_scheme(vega, continuous_market, page)
change_keys(page, vega, PARTY_B.name) change_keys(page, vega, PARTY_B.name)
+20 -2
View File
@@ -53,7 +53,24 @@ def setup_teams_and_games(vega: VegaServiceNull):
MM_WALLET.name, parameter="reward.asset", new_value=tDAI_asset_id MM_WALLET.name, parameter="reward.asset", new_value=tDAI_asset_id
) )
next_epoch(vega=vega) next_epoch(vega=vega)
vega.create_asset(
MM_WALLET.name,
name="VEGA",
symbol="VEGA",
decimals=5,
max_faucet_amount=1e10,
quantum=100000,
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
VEGA_asset_id = vega.find_asset_id(symbol="VEGA")
vega.mint(PARTY_A.name, VEGA_asset_id, 1e5)
vega.mint(PARTY_B.name, VEGA_asset_id, 1e5)
vega.wait_fn(1)
vega.wait_for_total_catchup()
team_name = create_team(vega) team_name = create_team(vega)
next_epoch(vega) next_epoch(vega)
@@ -101,7 +118,7 @@ def setup_teams_and_games(vega: VegaServiceNull):
from_key_name=PARTY_A.name, from_key_name=PARTY_A.name,
from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL, from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL,
to_account_type=vega_protos.vega.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES, to_account_type=vega_protos.vega.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
asset=tDAI_asset_id, asset=VEGA_asset_id,
reference="reward", reference="reward",
asset_for_metric=tDAI_asset_id, asset_for_metric=tDAI_asset_id,
metric=vega_protos.vega.DISPATCH_METRIC_MAKER_FEES_PAID, metric=vega_protos.vega.DISPATCH_METRIC_MAKER_FEES_PAID,
@@ -119,7 +136,7 @@ def setup_teams_and_games(vega: VegaServiceNull):
from_key_name=PARTY_B.name, from_key_name=PARTY_B.name,
from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL, from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL,
to_account_type=vega_protos.vega.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES, to_account_type=vega_protos.vega.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
asset=tDAI_asset_id, asset=VEGA_asset_id,
reference="reward", reference="reward",
asset_for_metric=tDAI_asset_id, asset_for_metric=tDAI_asset_id,
metric=vega_protos.vega.DISPATCH_METRIC_MAKER_FEES_PAID, metric=vega_protos.vega.DISPATCH_METRIC_MAKER_FEES_PAID,
@@ -288,6 +305,7 @@ def test_game_card(competitions_page: Page):
expect(game_1.get_by_test_id("entity-scope")).to_have_text("Individual") expect(game_1.get_by_test_id("entity-scope")).to_have_text("Individual")
expect(game_1.get_by_test_id("locked-for")).to_have_text("1 epoch") expect(game_1.get_by_test_id("locked-for")).to_have_text("1 epoch")
expect(game_1.get_by_test_id("reward-value")).to_have_text("100.00") expect(game_1.get_by_test_id("reward-value")).to_have_text("100.00")
expect(game_1.get_by_test_id("reward-asset")).to_have_text("VEGA")
expect(game_1.get_by_test_id("distribution-strategy")).to_have_text("Pro rata") expect(game_1.get_by_test_id("distribution-strategy")).to_have_text("Pro rata")
expect(game_1.get_by_test_id("dispatch-metric-info")).to_have_text( expect(game_1.get_by_test_id("dispatch-metric-info")).to_have_text(
"Price maker fees paid • tDAI" "Price maker fees paid • tDAI"
+8
View File
@@ -0,0 +1,8 @@
import { ENV, Networks } from '@vegaprotocol/environment';
const TEAMS_STATS_EPOCHS_MAINNET = 30;
const TEAMS_STATS_EPOCHS_TESTNET = 192;
export const TEAMS_STATS_EPOCHS =
ENV.VEGA_ENV === Networks.MAINNET
? TEAMS_STATS_EPOCHS_MAINNET
: TEAMS_STATS_EPOCHS_TESTNET;
+2 -3
View File
@@ -7,8 +7,7 @@ import orderBy from 'lodash/orderBy';
import { removePaginationWrapper } from '@vegaprotocol/utils'; import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useEpochInfoQuery } from './__generated__/Epoch'; import { useEpochInfoQuery } from './__generated__/Epoch';
import { type ApolloError } from '@apollo/client'; import { type ApolloError } from '@apollo/client';
import { TEAMS_STATS_EPOCHS } from './constants';
const TAKE_EPOCHS = 30; // TODO: should this be DEFAULT_AGGREGATION_EPOCHS?
const findTeam = (entities: GameFieldsFragment['entities'], teamId: string) => { const findTeam = (entities: GameFieldsFragment['entities'], teamId: string) => {
const team = entities.find( const team = entities.find(
@@ -45,7 +44,7 @@ export const useGames = (teamId?: string, epochFrom?: number): GamesData => {
let from = epochFrom; let from = epochFrom;
if (!from && epochData) { if (!from && epochData) {
from = Number(epochData.epoch.id) - TAKE_EPOCHS; from = Number(epochData.epoch.id) - TEAMS_STATS_EPOCHS;
if (from < 1) from = 1; // make sure it's not negative if (from < 1) from = 1; // make sure it's not negative
} }
+6 -5
View File
@@ -31,7 +31,7 @@ export type RewardTransfer = TransferNode & {
export type EnrichedRewardTransfer = RewardTransfer & { export type EnrichedRewardTransfer = RewardTransfer & {
/** Dispatch metric asset (reward asset) */ /** Dispatch metric asset (reward asset) */
asset?: AssetFieldsFragment; dispatchAsset?: AssetFieldsFragment;
/** A flag determining whether a reward asset is being traded on any of the active markets */ /** A flag determining whether a reward asset is being traded on any of the active markets */
isAssetTraded?: boolean; isAssetTraded?: boolean;
/** A list of markets in scope */ /** A list of markets in scope */
@@ -142,9 +142,10 @@ export const useRewards = ({
.filter((node) => (scopeToTeams ? isScopedToTeams(node) : true)) .filter((node) => (scopeToTeams ? isScopedToTeams(node) : true))
// enrich with dispatch asset and markets in scope details // enrich with dispatch asset and markets in scope details
.map((node) => { .map((node) => {
const asset = const dispatchAsset =
assets && (assets &&
assets[node.transfer.kind.dispatchStrategy.dispatchMetricAssetId]; assets[node.transfer.kind.dispatchStrategy.dispatchMetricAssetId]) ||
undefined;
const marketsInScope = compact( const marketsInScope = compact(
node.transfer.kind.dispatchStrategy.marketIdsInScope?.map( node.transfer.kind.dispatchStrategy.marketIdsInScope?.map(
(id) => markets && markets[id] (id) => markets && markets[id]
@@ -167,7 +168,7 @@ export const useRewards = ({
}); });
return { return {
...node, ...node,
asset: asset ? asset : undefined, dispatchAsset,
isAssetTraded: isAssetTraded != null ? isAssetTraded : undefined, isAssetTraded: isAssetTraded != null ? isAssetTraded : undefined,
markets: marketsInScope.length > 0 ? marketsInScope : undefined, markets: marketsInScope.length > 0 ? marketsInScope : undefined,
}; };
+2 -2
View File
@@ -5,7 +5,7 @@ import {
type TeamRefereeFieldsFragment, type TeamRefereeFieldsFragment,
type TeamMemberStatsFieldsFragment, type TeamMemberStatsFieldsFragment,
} from './__generated__/Team'; } from './__generated__/Team';
import { DEFAULT_AGGREGATION_EPOCHS } from './use-teams'; import { TEAMS_STATS_EPOCHS } from './constants';
export type Team = TeamFieldsFragment; export type Team = TeamFieldsFragment;
export type TeamStats = TeamStatsFieldsFragment; export type TeamStats = TeamStatsFieldsFragment;
@@ -22,7 +22,7 @@ export const useTeam = (teamId?: string, partyId?: string) => {
variables: { variables: {
teamId: teamId || '', teamId: teamId || '',
partyId, partyId,
aggregationEpochs: DEFAULT_AGGREGATION_EPOCHS, aggregationEpochs: TEAMS_STATS_EPOCHS,
}, },
skip: !teamId, skip: !teamId,
fetchPolicy: 'cache-and-network', fetchPolicy: 'cache-and-network',
+2 -4
View File
@@ -4,9 +4,7 @@ import { useTeamsQuery } from './__generated__/Teams';
import { useTeamsStatisticsQuery } from './__generated__/TeamsStatistics'; import { useTeamsStatisticsQuery } from './__generated__/TeamsStatistics';
import compact from 'lodash/compact'; import compact from 'lodash/compact';
import { type TeamStatsFieldsFragment } from './__generated__/Team'; import { type TeamStatsFieldsFragment } from './__generated__/Team';
import { TEAMS_STATS_EPOCHS } from './constants';
// 192
export const DEFAULT_AGGREGATION_EPOCHS = 192;
const EMPTY_STATS: Partial<TeamStatsFieldsFragment> = { const EMPTY_STATS: Partial<TeamStatsFieldsFragment> = {
totalQuantumVolume: '0', totalQuantumVolume: '0',
@@ -16,7 +14,7 @@ const EMPTY_STATS: Partial<TeamStatsFieldsFragment> = {
quantumRewards: [], quantumRewards: [],
}; };
export const useTeams = (aggregationEpochs = DEFAULT_AGGREGATION_EPOCHS) => { export const useTeams = (aggregationEpochs = TEAMS_STATS_EPOCHS) => {
const { const {
data: teamsData, data: teamsData,
loading: teamsLoading, loading: teamsLoading,
+7
View File
@@ -62,3 +62,10 @@ export const DENY_LIST: Record<string, string[]> = {
'fdf0ec118d98393a7702cf72e46fc87ad680b152f64b2aac59e093ac2d688fbb', 'fdf0ec118d98393a7702cf72e46fc87ad680b152f64b2aac59e093ac2d688fbb',
], ],
}; };
// We need a record of USDT on mainnet as it needs special handling for
// deposits and approvals due to the contract not conforming exactly
// to ERC20
export const USDT_ID = {
MAINNET: 'bf1e88d19db4b3ca0d1d5bdb73718a01686b18cf731ca26adedf3c8b83802bba',
} as const;
@@ -71,7 +71,7 @@ function waitForStake(vegaPublicKey: string) {
let tick = 1; let tick = 1;
const interval = setInterval(async () => { const interval = setInterval(async () => {
log(`confirming stake (attempt: ${tick})`); log(`confirming stake (attempt: ${tick})`);
if (tick >= 30) { if (tick >= 90) {
clearInterval(interval); clearInterval(interval);
reject(new Error('stake link never seen')); reject(new Error('stake link never seen'));
} }
@@ -25,7 +25,7 @@ export const addCreateMarket = () => {
cy.highlight('creating market on capsule environment'); cy.highlight('creating market on capsule environment');
cy.wrap(createMarket(config), { cy.wrap(createMarket(config), {
timeout: 5 * 60 * 1000, timeout: 9 * 60 * 1000,
}) })
// register market list result so it can be retrieved in tests later // register market list result so it can be retrieved in tests later
.as('markets'); .as('markets');
@@ -111,7 +111,7 @@ export const addValidatorsSelfDelegate = () => {
String(node0ApiToken), String(node0ApiToken),
String(node0Id) String(node0Id)
), ),
{ timeout: 60000 } { timeout: 90000 }
); );
// Self delegating Node 1 wallet // Self delegating Node 1 wallet
cy.get('@node1PubKey').then((node1PubKey) => { cy.get('@node1PubKey').then((node1PubKey) => {
@@ -124,7 +124,7 @@ export const addValidatorsSelfDelegate = () => {
String(node1ApiToken), String(node1ApiToken),
String(node1Id) String(node1Id)
), ),
{ timeout: 60000 } { timeout: 90000 }
); );
}); });
}); });
+37 -15
View File
@@ -1,5 +1,9 @@
import type { Asset } from '@vegaprotocol/assets'; import { USDT_ID, type Asset } from '@vegaprotocol/assets';
import { EtherscanLink } from '@vegaprotocol/environment'; import {
EtherscanLink,
Networks,
useEnvironment,
} from '@vegaprotocol/environment';
import { Intent, Notification } from '@vegaprotocol/ui-toolkit'; import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
import { import {
formatNumber, formatNumber,
@@ -15,7 +19,7 @@ import { useT } from './use-t';
interface ApproveNotificationProps { interface ApproveNotificationProps {
isActive: boolean; isActive: boolean;
selectedAsset?: Asset; selectedAsset?: Asset;
onApprove: () => void; onApprove: (amount?: string) => void;
approved: boolean; approved: boolean;
balances: DepositBalances | null; balances: DepositBalances | null;
amount: string; amount: string;
@@ -33,6 +37,7 @@ export const ApproveNotification = ({
approveTxId, approveTxId,
intent = Intent.Warning, intent = Intent.Warning,
}: ApproveNotificationProps) => { }: ApproveNotificationProps) => {
const { VEGA_ENV } = useEnvironment();
const t = useT(); const t = useT();
const tx = useEthTransactionStore((state) => { const tx = useEthTransactionStore((state) => {
return state.transactions.find((t) => t?.id === approveTxId); return state.transactions.find((t) => t?.id === approveTxId);
@@ -64,28 +69,45 @@ export const ApproveNotification = ({
text: t('Approve {{assetSymbol}}', { text: t('Approve {{assetSymbol}}', {
assetSymbol: selectedAsset?.symbol, assetSymbol: selectedAsset?.symbol,
}), }),
action: onApprove, action: () => onApprove(),
dataTestId: 'approve-submit', dataTestId: 'approve-submit',
}} }}
/> />
</div> </div>
); );
let message = t('Approve again to deposit more than {{allowance}}', {
allowance: formatNumber(balances.allowance.toString()),
});
const buttonProps = {
size: 'small' as const,
text: t('Approve {{assetSymbol}}', {
assetSymbol: selectedAsset?.symbol,
}),
action: () => onApprove(),
dataTestId: 'reapprove-submit',
};
if (VEGA_ENV === Networks.MAINNET && selectedAsset.id === USDT_ID[VEGA_ENV]) {
message = t(
'USDT approved amount cannot be changed, only revoked. Revoke and reapprove to deposit more than {{allowance}}.',
{
allowance: formatNumber(balances.allowance.toString()),
}
);
buttonProps.text = t('Revoke {{assetSymbol}} approval', {
assetSymbol: selectedAsset?.symbol,
});
buttonProps.action = () => onApprove('0');
}
const reApprovePrompt = ( const reApprovePrompt = (
<div className="mb-4"> <div className="mb-4">
<Notification <Notification
intent={intent} intent={intent}
testId="reapprove-default" testId="reapprove-default"
message={t('Approve again to deposit more than {{allowance}}', { message={message}
allowance: formatNumber(balances.allowance.toString()), buttonProps={buttonProps}
})}
buttonProps={{
size: 'small',
text: t('Approve {{assetSymbol}}', {
assetSymbol: selectedAsset?.symbol,
}),
action: onApprove,
dataTestId: 'reapprove-submit',
}}
/> />
</div> </div>
); );
+3 -3
View File
@@ -58,7 +58,7 @@ export interface DepositFormProps {
onSelectAsset: (assetId: string) => void; onSelectAsset: (assetId: string) => void;
handleAmountChange: (amount: string) => void; handleAmountChange: (amount: string) => void;
onDisconnect: () => void; onDisconnect: () => void;
submitApprove: () => void; submitApprove: (amount?: string) => void;
approveTxId: number | null; approveTxId: number | null;
submitFaucet: () => void; submitFaucet: () => void;
faucetTxId: number | null; faucetTxId: number | null;
@@ -423,8 +423,8 @@ export const DepositForm = ({
isActive={isActive} isActive={isActive}
approveTxId={approveTxId} approveTxId={approveTxId}
selectedAsset={selectedAsset} selectedAsset={selectedAsset}
onApprove={() => { onApprove={(amount) => {
submitApprove(); submitApprove(amount);
setApproveNotificationIntent(Intent.Warning); setApproveNotificationIntent(Intent.Warning);
}} }}
balances={balances} balances={balances}
+2 -2
View File
@@ -35,11 +35,11 @@ export const useSubmitApproval = (
reset: () => { reset: () => {
setId(null); setId(null);
}, },
perform: () => { perform: (amount?: string) => {
if (!asset || !config) return; if (!asset || !config) return;
const id = createEthTransaction(contract, 'approve', [ const id = createEthTransaction(contract, 'approve', [
config?.collateral_bridge_contract.address, config?.collateral_bridge_contract.address,
MaxUint256.toString(), amount ? amount : MaxUint256.toString(),
]); ]);
setId(id); setId(id);
}, },
+1
View File
@@ -134,6 +134,7 @@ export const CONSOLE_TRANSFER = '#/portfolio/assets/transfer';
export const CONSOLE_TRANSFER_ASSET = export const CONSOLE_TRANSFER_ASSET =
'#/portfolio/assets/transfer?assetId=:assetId'; '#/portfolio/assets/transfer?assetId=:assetId';
export const CONSOLE_MARKET_PAGE = '#/markets/:marketId'; export const CONSOLE_MARKET_PAGE = '#/markets/:marketId';
export const CONSOLE_REWARDS_PAGE = '#/rewards';
// Governance pages // Governance pages
export const TOKEN_NEW_MARKET_PROPOSAL = '/proposals/propose/new-market'; export const TOKEN_NEW_MARKET_PROPOSAL = '/proposals/propose/new-market';
+2
View File
@@ -4,6 +4,7 @@
"Approval failed": "Approval failed", "Approval failed": "Approval failed",
"Approve {{assetSymbol}}": "Approve {{assetSymbol}}", "Approve {{assetSymbol}}": "Approve {{assetSymbol}}",
"Approve again to deposit more than {{allowance}}": "Approve again to deposit more than {{allowance}}", "Approve again to deposit more than {{allowance}}": "Approve again to deposit more than {{allowance}}",
"USDT approved amount cannot be changed, only revoked. Revoke and reapprove to deposit more than {{allowance}}.": "USDT approved amount cannot be changed, only revoked. Revoke and reapprove to deposit more than {{allowance}}.",
"Asset": "Asset", "Asset": "Asset",
"Balance available": "Balance available", "Balance available": "Balance available",
"Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet": "Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet", "Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet": "Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet",
@@ -23,6 +24,7 @@
"Please select": "Please select", "Please select": "Please select",
"Please select an asset": "Please select an asset", "Please select an asset": "Please select an asset",
"Remaining deposit allowance": "Remaining deposit allowance", "Remaining deposit allowance": "Remaining deposit allowance",
"Revoke {{assetSymbol}} approval": "Revoke {{assetSymbol}} approval",
"Select from wallet": "Select from wallet", "Select from wallet": "Select from wallet",
"The {{symbol}} faucet is not available at this time": "The {{symbol}} faucet is not available at this time", "The {{symbol}} faucet is not available at this time": "The {{symbol}} faucet is not available at this time",
"The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.": "The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.", "The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.": "The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.",
+4 -1
View File
@@ -391,6 +391,8 @@
"multisigContractIncorrect": "was incorrectly configured as at the end of the last epoch so rewards were penalised. Validator and delegator rewards will continue to be penalised until this is resolved.", "multisigContractIncorrect": "was incorrectly configured as at the end of the last epoch so rewards were penalised. Validator and delegator rewards will continue to be penalised until this is resolved.",
"multisigContractLink": "Ethereum Multisig Contract", "multisigContractLink": "Ethereum Multisig Contract",
"multisigPenalty": "Multisig penalty", "multisigPenalty": "Multisig penalty",
"multisigPenaltyThisNodeIndicator": "The multisig score for this node is equal to zero.",
"multisigPenaltyDescription": "The multisig score is used in the calculation of rewards. For each validator that gets a multisig score of zero, no staking rewards are paid to that consensus validators and their nominators until the epoch following the one in which the configuration issue is resolved.",
"myPendingStake": "My pending stake", "myPendingStake": "My pending stake",
"myStake": "My stake", "myStake": "My stake",
"n/a": "N/A", "n/a": "N/A",
@@ -967,5 +969,6 @@
"YourIdentityAnonymous": "Your identity is always anonymous on Vega", "YourIdentityAnonymous": "Your identity is always anonymous on Vega",
"yourStake": "Your stake", "yourStake": "Your stake",
"yourVote": "Your vote", "yourVote": "Your vote",
"youVoted": "You voted" "youVoted": "You voted",
"rewardsMovedNotification": "Trading and liquidity rewards have moved. Visit <0>Console</0> to view your rewards."
} }
-5
View File
@@ -1,8 +1,6 @@
{ {
"{{liquidityPriceRange}} of mid price": "{{liquidityPriceRange}} of mid price", "{{liquidityPriceRange}} of mid price": "{{liquidityPriceRange}} of mid price",
"{{probability}} probability price bounds": "{{probability}} probability price bounds", "{{probability}} probability price bounds": "{{probability}} probability price bounds",
"24 hour change is unavailable at this time. The price change in the last 120 hours is:": "24 hour change is unavailable at this time. The price change in the last 120 hours is:",
"24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}": "24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}",
"A concept derived from traditional markets. It is a calculated value for the current market price on a market.": "A concept derived from traditional markets. It is a calculated value for the current market price on a market.", "A concept derived from traditional markets. It is a calculated value for the current market price on a market.": "A concept derived from traditional markets. It is a calculated value for the current market price on a market.",
"A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.": "A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.", "A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.": "A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.",
"A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.": "A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.", "A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.": "A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.",
@@ -51,9 +49,6 @@
"Market": "Market", "Market": "Market",
"Market data": "Market data", "Market data": "Market data",
"Market governance": "Market governance", "Market governance": "Market governance",
"Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:": "Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:",
"Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is:": "Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is:",
"Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}": "Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}",
"Market ID": "Market ID", "Market ID": "Market ID",
"Market price": "Market price", "Market price": "Market price",
"Market specification": "Market specification", "Market specification": "Market specification",
@@ -2,16 +2,14 @@ import { type ReactNode } from 'react';
import { import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
formatNumberPercentage, formatNumberPercentage,
getDateTimeFormat,
priceChange, priceChange,
priceChangePercentage, priceChangePercentage,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { PriceChangeCell, signedNumberCssClass } from '@vegaprotocol/datagrid'; import { signedNumberCssClass } from '@vegaprotocol/datagrid';
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit'; import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { useCandles } from '../../hooks/use-candles'; import { useCandles } from '../../hooks/use-candles';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import classNames from 'classnames'; import classNames from 'classnames';
import { useT } from '../../use-t';
interface Props { interface Props {
marketId?: string; marketId?: string;
@@ -24,7 +22,6 @@ export const Last24hPriceChange = ({
decimalPlaces, decimalPlaces,
fallback, fallback,
}: Props) => { }: Props) => {
const t = useT();
const { oneDayCandles, fiveDaysCandles, error } = useCandles({ const { oneDayCandles, fiveDaysCandles, error } = useCandles({
marketId, marketId,
}); });
@@ -35,56 +32,6 @@ export const Last24hPriceChange = ({
return nonIdeal; return nonIdeal;
} }
if (fiveDaysCandles.length < 24) {
return (
<Tooltip
description={
<span className="justify-start">
{t(
'Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:',
{
start: getDateTimeFormat().format(
new Date(fiveDaysCandles[0].periodStart)
),
end: getDateTimeFormat().format(
new Date(
fiveDaysCandles[fiveDaysCandles.length - 1].periodStart
)
),
}
)}
<PriceChangeCell
candles={fiveDaysCandles.map((c) => c.close) || []}
decimalPlaces={decimalPlaces}
/>
</span>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
if (oneDayCandles.length < 24) {
return (
<Tooltip
description={
<span className="justify-start">
{t(
'24 hour change is unavailable at this time. The price change in the last 120 hours is:'
)}{' '}
<PriceChangeCell
candles={fiveDaysCandles.map((c) => c.close) || []}
decimalPlaces={decimalPlaces}
/>
</span>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
const candles = oneDayCandles?.map((c) => c.close) || []; const candles = oneDayCandles?.map((c) => c.close) || [];
const change = priceChange(candles); const change = priceChange(candles);
const changePercentage = priceChangePercentage(candles); const changePercentage = priceChangePercentage(candles);
@@ -2,7 +2,6 @@ import { calcCandleVolume, calcCandleVolumePrice } from '../../market-utils';
import { import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
formatNumber, formatNumber,
getDateTimeFormat,
isNumeric, isNumeric,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { Tooltip } from '@vegaprotocol/ui-toolkit'; import { Tooltip } from '@vegaprotocol/ui-toolkit';
@@ -37,83 +36,6 @@ export const Last24hVolume = ({
return nonIdeal; return nonIdeal;
} }
if (fiveDaysCandles.length < 24) {
const candleVolume = calcCandleVolume(fiveDaysCandles);
const candleVolumePrice = calcCandleVolumePrice(
fiveDaysCandles,
marketDecimals,
positionDecimalPlaces
);
const candleVolumeValue =
candleVolume && isNumeric(positionDecimalPlaces)
? addDecimalsFormatNumber(
candleVolume,
positionDecimalPlaces,
formatDecimals
)
: '-';
return (
<Tooltip
description={
<div>
<span className="flex flex-col">
{t(
'Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}',
{
start: getDateTimeFormat().format(
new Date(fiveDaysCandles[0].periodStart)
),
end: getDateTimeFormat().format(
new Date(
fiveDaysCandles[fiveDaysCandles.length - 1].periodStart
)
),
candleVolumeValue,
candleVolumePrice,
quoteUnit,
}
)}
</span>
</div>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
if (oneDayCandles.length < 24) {
const candleVolume = calcCandleVolume(fiveDaysCandles);
const candleVolumePrice = calcCandleVolumePrice(
fiveDaysCandles,
marketDecimals,
positionDecimalPlaces
);
const candleVolumeValue =
candleVolume && isNumeric(positionDecimalPlaces)
? addDecimalsFormatNumber(
candleVolume,
positionDecimalPlaces,
formatDecimals
)
: '-';
return (
<Tooltip
description={
<div>
<span className="flex flex-col">
{t(
'24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of ({{candleVolumePrice}} {{quoteUnit}})',
{ candleVolumeValue, candleVolumePrice, quoteUnit }
)}
</span>
</div>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
const candleVolume = oneDayCandles const candleVolume = oneDayCandles
? calcCandleVolume(oneDayCandles) ? calcCandleVolume(oneDayCandles)
: initialValue; : initialValue;

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