Compare commits

...
Author SHA1 Message Date
Joe 1267713a87 test: base for transaction tests 2023-09-15 09:43:51 +01:00
Mikołaj Młodzikowski 9ea8c839db feat(ci): use ipfs daemon in dockerfiles (#4786) 2023-09-15 08:48:25 +02:00
Matthew Russell d7e2da7a54 fix(trading): missing styles for tooltip in positions table (#4780) 2023-09-14 14:00:59 -07:00
m.ray b29c268257 chore(trading): add oracle proofs url to all envs (#4793) 2023-09-14 13:57:28 -07:00
m.rayandMatthew Russell 01ca05a313 chore(trading): update governance variables (#4766)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-09-14 13:57:15 -07:00
Matthew Russell e3c6dd41c9 fix(trading): simplify onboarding and explore button to use top traded (#4781) 2023-09-14 12:10:33 -07:00
Bartłomiej GłowniaandMatthew Russell fbd01dc1bd fix(trading): use asset decimals to format liquidation price in posions table (#4787)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-09-14 11:18:58 -07:00
m.rayandMatthew Russell af6719cc9d chore(trading): add governance link to top level nav (#4765)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-09-13 14:25:31 -07:00
Sam KeenandJoe c433a4ee06 feat(governance): more mobile view improvements (#4751)
Co-authored-by: Joe <joe@vega.xyz>
2023-09-13 19:01:33 +01:00
Matthew Russell e3a00ada05 chore(trading,governance,explorer): update pr template to ensure issues get linked 2023-09-13 08:30:06 -07:00
Joe Tsang aa38e0c001 test(governance): add assert for numerical sort (#4742) 2023-09-13 13:02:47 +01:00
Joe Tsang 86da521da5 test(governance): flaky test fixes (#4760) 2023-09-13 11:53:22 +01:00
Matthew Russell c6df34fe95 chore(trading): bump app version and enable snaps 2023-09-12 13:40:08 -07:00
Matthew Russell c7d0803164 chore(trading): make closed market table rows the correct height 2023-09-12 12:47:47 -07:00
m.ray 250a654544 feat(trading): margin required progressive disclosure (#4755) 2023-09-12 12:06:47 -07:00
Matthew Russell 7f5e8ebb15 chore(trading): replace successor cols with market code cell (#4722) 2023-09-12 11:52:03 -07:00
Art ccbe34e172 chore(trading): make deal ticket open on market change (#4749) 2023-09-12 11:26:54 -07:00
Maciek 27e8ce88bf chore(trading): adjust onboarding experience (#4754) 2023-09-12 11:10:17 -07:00
ArtandMatthew Russell 41804d9869 chore(trading): market list tweaks (#4756)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-09-12 10:41:45 -07:00
Maciek 293288286b chore(trading): adjust gap between candles (#4753) 2023-09-12 09:59:13 -07:00
Matthew Russell a77765b1e9 feat(trading): store study sizes, reduce candles gap (#4708) 2023-09-12 09:56:43 -07:00
MaciekandMatthew Russell 2453d7841a chore(trading): open connect dialog from onboarding dialog (#4744)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-09-12 08:07:37 -07:00
Sam KeenandJoe a0945be721 fix(governance,environment): adjust proposal user journey (#4679)
Co-authored-by: Joe <joe@vega.xyz>
2023-09-11 22:04:30 +01:00
Maciek 84e40ccb2b chore(deal-ticket): make margin warning more subtle (#4732) 2023-09-11 16:46:16 +02:00
daro-maj a868f6d2f5 test(trading): update acs for deal ticket (#4739) 2023-09-11 11:17:07 +02:00
m.rayandMatthew Russell 8b83c51014 feat(trading): improve connect wallet description (#4726)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-09-11 08:48:27 +00:00
Maciek bf7aacf984 chore(trading): initial number of candles depended on pane width (#4737) 2023-09-11 10:27:03 +02:00
Matthew Russell 5183f2591b chore(trading,governance,explorer): add frontend-qa to codeowners (#4735) 2023-09-08 17:00:18 -07:00
Maciek b3308439bd chore(candles-chart): show more candles by default (#4734) 2023-09-08 15:15:36 -07:00
Bartłomiej Głownia 719d210219 chore(trading): remove target build from libs (#4725) 2023-09-08 13:43:54 -07:00
Edd f810a3a0b1 chore(trading): maintenance on ipfs build process (#4736) 2023-09-08 10:50:19 -07:00
Sam Keen db19ee80ac feat(governance): improve the display of Governance app on mobile (#4724) 2023-09-08 09:48:09 +00:00
Sam Keen 45623f33c5 fix(governance): corrected sorting of upgrade proposals by blockheight (#4729) 2023-09-08 09:19:59 +00:00
Joe Tsang 08e204fe31 test(explorer): test for network param grouping (#4710) 2023-09-07 15:15:41 +01:00
Joe Tsang 81f7b0595e chore(governance): update tests with vote changes (#4706) 2023-09-07 15:14:37 +01:00
Radosław Szpiech 4ed0e3a056 chore(trading): add console-test runner (#4711) 2023-09-07 13:22:01 +02:00
47a84b4dac feat(deal-ticket): show maximum number of active stop orders limit warning (#4687)
Co-authored-by: Maciek <maciek@vegaprotocol.io>
Co-authored-by: Edd <edd@vega.xyz>
Co-authored-by: Joe Tsang <30622993+jtsang586@users.noreply.github.com>
Co-authored-by: Sam Keen <samuel.kleinmann@gmail.com>
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
Co-authored-by: m.ray <16125548+MadalinaRaicu@users.noreply.github.com>
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2023-09-07 09:25:26 +00:00
Matthew Russell c540d4b17f chore(utils): bump utils version 2023-09-06 22:13:24 -07:00
Matthew Russell f1fa3250a4 chore(utils): remove externals from utils 2023-09-06 22:12:21 -07:00
Matthew Russell 707c00def2 fix(trading,governance,explorer): peer deps for published packages (#4721) 2023-09-06 17:04:52 -07:00
250 changed files with 2891 additions and 3219 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Related issues 🔗
Issue: #[Issue number here]
Closes #[Issue number here]
# Description
+2 -37
View File
@@ -10,7 +10,7 @@ on:
jobs:
run-tests:
name: run-tests
runs-on: 8-cores
runs-on: console-test
timeout-minutes: 20
steps:
#----------------------------------------------
@@ -33,15 +33,6 @@ jobs:
restore-keys: |
${{ runner.os }}-cache-node-modules-
#----------------------------------------------
# setup node
#----------------------------------------------
- name: Setup node
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
#----------------------------------------------
# install deps if cache missing
#----------------------------------------------
- name: yarn install
@@ -76,32 +67,6 @@ jobs:
repository: vegaprotocol/console-test
path: './console-test'
#----------------------------------------------
# set-up python
#----------------------------------------------
- name: Set up python
id: setup-python
uses: actions/setup-python@v4
with:
python-version: '3.10.11'
#----------------------------------------------
# ----- install & configure poetry -----
#----------------------------------------------
- name: Install Poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
virtualenvs-path: console-test/.venv
#----------------------------------------------
# load cached venv if cache exists
#----------------------------------------------
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v3
with:
path: console-test/.venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
#----------------------------------------------
# install dependencies if cache does not exist
#----------------------------------------------
- name: Install dependencies
@@ -125,7 +90,7 @@ jobs:
#----------------------------------------------
- name: Run tests
working-directory: ./console-test
run: poetry run pytest -v -s --numprocesses auto --dist loadfile --durations=20
run: poetry run pytest -v -s --numprocesses 2 --dist loadfile --durations=20
- name: Check files
run: |
ls -al .
+1
View File
@@ -1,2 +1,3 @@
* @vegaprotocol/frontend
* @vegaprotocol/frontend-qa
*.graphql @vegaprotocol/core
+1 -1
View File
@@ -11,7 +11,7 @@ recalculate-ipfs:
echo "ipfs hash inside the image"
docker run --rm ${TAG} cat /ipfs-hash
echo "recalculating ipfs hash"
docker run --rm ${TAG} ipfs add -r /usr/share/nginx/html
docker run --rm ${TAG} ipfs add -rQ /usr/share/nginx/html
.PHONY: eject-ipfs-hash
unpack:
+39 -25
View File
@@ -113,13 +113,47 @@ In order to run a container on port 3000:
docker run -p 3000:80 [TAG]
```
On top of that there are two possible scenarios for running docker image - using nginx server (default) of ipfs daemon.
to run ipfs on port 3000:
```bash
docker run -p 3000:80 [TAG] ipfs
```
to run nginx on port 3000:
```bash
docker run -p 3000:80 [TAG]
```
## Build instructions
The [`docker`](./docker) subfolder has some docker configurations for easily setting up your own hosted version of console either for the web, or ready for pinning on IPFS
The [`docker`](./docker) subfolder has some docker configurations for easily setting up your own hosted version of Console either for the web, or ready for pinning on IPFS.
### nx build inside the docker
Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build). The multistage builds ensures consistent CPU architecture and build toolchains are used so that the result will be identical.
```bash
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=16.5.1 --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
```
### Computing ipfs-hash of the build
At the moment this feature is important only for Console releases.
Each docker build finishes with hash calculation for ` dist`` directory. Resulting hash is added to file named as `/ipfs-hash`. Once docker image is produced you can run following commad to display ipfs-hash:
```bash
make recalculate-ipfs TAG=vegaprotocol/trading:{YOUR_VERSION}
```
**updating hash:** recompiling dist directory (even if there are no changed to source code) results in different hash computed by ipfs command.
### nx build outside the docker
Packaging prepared dist into [`nginx`](https://hub.docker.com/_/nginx)([server configuration](./nginx/nginx.conf)) docker image involves building the application on docker host machine from source.
This Docker image packages a pre-built `dist` folder into an [`nginx`](https://hub.docker.com/_/nginx)([server configuration](./nginx/nginx.conf)) docker image. In this case, the application on docker host machine from source.
As a prerequisite you need to perform build of `dist` directory and move its content for specific application to `dist-result` directory. Use following script to do it with a single command:
@@ -130,41 +164,21 @@ As a prerequisite you need to perform build of `dist` directory and move its con
You can build any of the containers locally with the following command:
```bash
docker build --dockerfile docker/node-outside-docker.Dockerfile . --tag=[TAG]
docker build -f docker/node-outside-docker.Dockerfile . --tag=[TAG]
```
### nx build inside the docker
Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build) example.
```bash
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=$(cat .nvmrc) --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
```
### Computing ipfs-hash of the build
At the moment this feature is important only for `trading` (console) releases.
Each docker build finishes with hash calculation for dist directory. Resulting hash is added to file named as `/ipfs-hash`. Once docker image is produced you can run following commad to display ipfs-hash:
```bash
make recalculate-ipfs TAG=vegaprotocol/trading:{YOUR_VERSION}
```
**updating hash:** recompiling dist directory (even if there are no changed to source code) results in different hash computed by ipfs command.
### Verifying ipfs-hash of existing current application version
An IPFS CID will be attached to every [release](https://github.com/vegaprotocol/frontend-monorepo/releases). If you are intending to pin an application on IPFS, you can check that your build matches by running the following steps:
1. Show latest release by runnning: `make latest-release`. You need to configure [`gh`](https://cli.github.com/) for this step to work, otherwise please provide release manually from [github](https://github.com/vegaprotocol/frontend-monorepo/releases) or [dockerhub](https://hub.docker.com/r/vegaprotocol/trading)
1. Show latest release by running: `make latest-release`. You need to configure [`gh`](https://cli.github.com/) for this step to work, otherwise please provide release manually from [github](https://github.com/vegaprotocol/frontend-monorepo/releases) or [dockerhub](https://hub.docker.com/r/vegaprotocol/trading)
2. Set RELEASE environment variable to value that you want to validate: `export RELEASE=$(make latest-release)` or `export RELEASE=vXX.XX.XX`
3. Set TAG environment variable to image that you want to validate: `export TAG=vegaprotocol/trading:$RELEASE`
4. Download docker image with the desired release `docker pull $TAG`.
5. Recalculate hash: `make recalculate-ipfs`
6. You should see exactly same hash produced by ipfs command as one placed with the release notes: `make show-latest-release`
7. If you want to extract dist from docker image to your local filesystem you can run following command: `make unpack`
8. Now `dist` directory contains valid application build. **it is not possible to calculate same ipfs hash on files that are result of copy operation**
8. Now `dist` directory contains valid application build
## Config
+2
View File
@@ -4,6 +4,8 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
NX_VEGA_ENV=DEVNET
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocke
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_VEGA_ENV=TESTNET
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# App flags
NX_EXPLORER_ASSETS=1
@@ -0,0 +1,24 @@
{
"transaction": {
"block": "10277448",
"index": 13,
"hash": "C79AE0040AE7CFE3EACD715E569B043B214D8451405CB40C06EAA1C38F453A85",
"submitter": "86ff2c3b45be7c43202d1dc370779d070faaba1029094c46174857f1b445673f",
"type": "Cancel Order",
"code": 0,
"cursor": "10277448.13",
"command": {
"nonce": "18292162483463539777",
"blockHeight": "10277444",
"orderCancellation": {
"orderId": "",
"marketId": ""
}
},
"signature": {
"value": "22a24614c3b19b5c6a7a979738dbda5cdeea39f61aabb937fb795f975ddc56a9e4823659024c29f1b9400939a502c4cf7fa10b946400d2b4f18caed6ac9f7d07",
"algo": "vega/ed25519",
"version": 1
}
}
}
@@ -0,0 +1,30 @@
{
"transaction": {
"block": "9319120",
"index": 0,
"hash": "28B2C9082E853F83958E91E9CA12343034CE7268DAF05EB987E6DB236C240208",
"submitter": "a80cc31c783829674a0f340a765e401f089349c706cc6c99401962aa3f663471",
"type": "Amend Order",
"code": 70,
"cursor": "9319120.0",
"command": {
"nonce": "13856472833343542396",
"blockHeight": "9319118",
"orderAmendment": {
"orderId": "a4a385ef2e8d8d508d129c91680fdd771374a21bd6fc49628a7e79cf53af1fbb",
"marketId": "e6561f69c2a76858866aab2896eeb529b46040614566e0665602d67bc682c31f",
"price": "13984",
"sizeDelta": "0",
"timeInForce": "TIME_IN_FORCE_GTC",
"peggedOffset": "",
"peggedReference": "PEGGED_REFERENCE_UNSPECIFIED"
}
},
"signature": {
"value": "43d6f047c7c11f96b15ccd18acaf2889ec902bcdc816b029ddd103576e303abca71e4ef06c41e9072f7623230586d7d85425784f935857c3838f1b9828be1a01",
"algo": "vega/ed25519",
"version": 1
},
"error": "couldn't insert order in book"
}
}
@@ -1,12 +1,9 @@
context('Network parameters page', { tags: '@smoke' }, function () {
before('navigate to network parameter page', function () {
cy.fixture('net_parameter_format_lookup').as('networkParameterFormat');
cy.visit('/network-parameters');
});
describe('Verify elements on page', function () {
beforeEach(() => {
cy.visit('/network-parameters');
});
const networkParametersHeader = '[data-testid="network-param-header"]';
const tableRows = '[data-testid="key-value-table-row"]';
@@ -16,6 +13,7 @@ context('Network parameters page', { tags: '@smoke' }, function () {
.and('be.visible');
});
// 0006-NETW-021
it('should list each of the network parameters available', function () {
cy.get_network_parameters().then((network_parameters) => {
const numberOfNetworkParametersInSystem =
@@ -198,6 +196,19 @@ context('Network parameters page', { tags: '@smoke' }, function () {
});
});
// 0006-NETW-022 0006-NETW-023
it('governance assets should be correctly grouped', function () {
cy.getByTestId('governance')
.should('exist')
.parent()
.should('have.attr', 'href', '/network-parameters#governance');
cy.get('[id="governance-proposal-asset"]')
.parent()
.within(() => {
cy.getByTestId('key-value-table-row').should('have.length', 8);
});
});
it('should be able to see network parameters - on mobile', function () {
cy.switchToMobile();
cy.get_network_parameters().then((network_parameters) => {
@@ -0,0 +1,64 @@
context('Transaction ID page', { tags: '@smoke' }, function () {
describe('Verify elements on transaction page', function () {
it.only('Able to view order cancelation', function () {
const txId =
'C79AE0040AE7CFE3EACD715E569B043B214D8451405CB40C06EAA1C38F453A85';
cy.intercept('GET', `/rest/transactions/${txId}`, {
fixture: '/mocks/cancel_order_transaction.json',
});
cy.visit(`/txs/${txId}`);
cy.validate_transaction_fields(0, 'Type', 'Cancel Order');
cy.validate_transaction_fields(
1,
'Hash',
'c79ae0040ae7cfe3eacd715e569b043b214d8451405cb40c06eaa1c38f453a85'
);
cy.validate_transaction_fields(
2,
'Submitter',
'86ff2c3b45be7c43202d1dc370779d070faaba1029094c46174857f1b445673f',
'/parties/86ff2c3b45be7c43202d1dc370779d070faaba1029094c46174857f1b445673f'
);
cy.validate_transaction_fields(
3,
'Block',
'10277448',
'/blocks/10277448'
);
cy.validate_transaction_fields(5, 'Response code', 'Success');
cy.contains('Show raw transaction').click();
cy.get('textarea').should(
'have.text',
'{\n "nonce": "18292162483463539777",\n "blockHeight": "10277444",\n "orderCancellation": {\n "orderId": "",\n "marketId": ""\n }\n}'
);
});
it('Able to view order amendment', function () {
const txId =
'0x28B2C9082E853F83958E91E9CA12343034CE7268DAF05EB987E6DB236C240208';
cy.intercept('GET', `/rest/transactions/${txId}`, {
fixture: '/mocks/order_amendment_transaction.json',
});
cy.visit(`/txs/${txId}`);
cy.validate_transaction_fields(0, 'Type', 'Amend Order');
cy.validate_transaction_fields(
4,
'Response code',
"couldn't insert order in book"
);
cy.validate_transaction_fields(
7,
'Order',
'a4a385ef2e8d8d508d129c91680fdd771374a21bd6fc49628a7e79cf53af1fbb'
);
cy.validate_transaction_fields(
8,
'Market',
'ETHBTC Quarterly (Jul 2023)',
'/markets/e6561f69c2a76858866aab2896eeb529b46040614566e0665602d67bc682c31f'
);
});
});
});
@@ -134,3 +134,16 @@ Cypress.Commands.add(
cy.contains(tableRowName).siblings().should('have.text', changeType);
}
);
Cypress.Commands.add(
'validate_transaction_fields',
(rowNum, rowName, rowValue, hrefUrl = null) => {
cy.get('tr')
.eq(rowNum)
.within(() => {
cy.get('td').eq(0).should('have.text', rowName);
cy.get('td').eq(1).should('have.text', rowValue);
if (hrefUrl) cy.get('a').should('have.attr', 'href', hrefUrl);
});
}
);
+1
View File
@@ -3,6 +3,7 @@ NX_VEGA_ENV=CUSTOM
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_VEGA_EXPLORER_URL=/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# App flags
NX_EXPLORER_TXS_LIST=0
+1
View File
@@ -8,6 +8,7 @@ NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
+1
View File
@@ -9,6 +9,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
+1
View File
@@ -9,6 +9,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
+1
View File
@@ -1,2 +1,3 @@
# .env is stagnet1, so there are no overrides required
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET1":"https://stagnet1.explorer.vega.xyz"}'
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
+1
View File
@@ -9,6 +9,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
+1
View File
@@ -11,6 +11,7 @@ NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
+2 -1
View File
@@ -5,4 +5,5 @@ NX_VEGA_ENV=CUSTOM
NX_BLOCK_EXPLORER=
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_VEGA_EXPLORER_URL=/
NX_VEGA_EXPLORER_URL=/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
@@ -33,7 +33,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
const { params } = useNetworkParams([
NetworkParams.governance_proposal_market_requiredMajority,
]);
const tokenLink = useLinks(DApp.Token);
const tokenLink = useLinks(DApp.Governance);
const requiredMajorityPercentage = useMemo(() => {
const requiredMajority =
params?.governance_proposal_market_requiredMajority ?? 1;
@@ -35,7 +35,7 @@ export const BundleSigners = ({
tx,
id,
}: BundleSignersProps) => {
const tokenLink = useLinks(DApp.Token);
const tokenLink = useLinks(DApp.Governance);
const bridgeFunction: BridgeFunction =
tx?.changes?.erc20 && 'contractAddress' in tx.changes.erc20
@@ -139,7 +139,7 @@ export const ValidatorsPage = () => {
const [vegaDialog, setVegaDialog] = useState<boolean>(false);
const [tmDialog, setTmDialog] = useState<boolean>(false);
const tokenLink = useLinks(DApp.Token);
const tokenLink = useLinks(DApp.Governance);
return (
<>
+2
View File
@@ -14,6 +14,8 @@ NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
+1
View File
@@ -3,3 +3,4 @@ NX_VEGA_ENV=DEVNET
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
+1
View File
@@ -3,3 +3,4 @@ NX_VEGA_ENV=MAINNET
NX_VEGA_URL=https://api.vega.community/graphql
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
+1
View File
@@ -3,3 +3,4 @@ NX_VEGA_ENV=TESTNET
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
@@ -19,7 +19,22 @@ export const upgradeProposalsData = {
},
{
node: {
upgradeBlockHeight: '1955065',
upgradeBlockHeight: '10001',
vegaReleaseTag: 'v0.71.0+dev-12156-bca1d57e',
approvers: [
'02a6531716b7a6d82779b7793c3ad2fcb47290ea2ff0912c56f61219bd9675ff',
'121934387281812a2d5e6913e5d57c0f85a8f169e2752347ee2e23b52d46623c',
'65c80e2f5f84e2109eec30810f137ba04cbbecaba8f27706c146cc6c6f90db29',
'bd6339d2428c79ac3bc9011771236d17bac92bcb1806423388d52fb440043aef',
],
status: 'PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED',
__typename: 'ProtocolUpgradeProposal',
},
__typename: 'ProtocolUpgradeProposalEdge',
},
{
node: {
upgradeBlockHeight: '20',
vegaReleaseTag: 'v0.71.0+dev-12156-bca1d57e',
approvers: [
'02a6531716b7a6d82779b7793c3ad2fcb47290ea2ff0912c56f61219bd9675ff',
@@ -34,19 +34,20 @@ import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
import { createSuccessorMarketProposalTxBody } from '../../support/proposal.functions';
const proposalListItem = '[data-testid="proposals-list-item"]';
const proposalVoteProgressForPercentage =
'vote-progress-indicator-percentage-for';
const proposalVoteProgressAgainstPercentage =
'vote-progress-indicator-percentage-against';
const proposalVoteProgressForTokens = 'vote-progress-indicator-tokens-for';
const proposalVoteProgressAgainstTokens =
'vote-progress-indicator-tokens-against';
const participationNotMet = 'token-participation-not-met';
const voteStatus = 'vote-status';
const voteMajorityNotMet = 'token-majority-not-met';
const numberOfVotesFor = 'num-votes-for';
const votesForPercentage = 'votes-for-percentage';
const numberOfVotesAgainst = 'num-votes-against';
const votesAgainstPercentage = 'votes-against-percentage';
const totalVotedNumber = 'total-voted';
const totalVotedPercentage = 'total-voted-percentage';
const changeVoteButton = 'change-vote-button';
const proposalDetailsTitle = 'proposal-title';
const proposalDetailsDescription = 'proposal-description';
const openProposals = 'open-proposals';
const viewProposalButton = 'view-proposal-btn';
const voteBreakdownToggle = 'vote-breakdown-toggle';
const proposalTermsToggle = 'proposal-json-toggle';
const marketDataToggle = 'proposal-market-data-toggle';
@@ -150,29 +151,32 @@ describe(
// 3001-VOTE-037
// 3001-VOTE-040
// 3001-VOTE-067
// 3001-VOTE-023
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
);
});
cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should(
'be.visible'
cy.getByTestId(participationNotMet).should(
'have.text',
'0.000000000000000000000015% participation threshold not met'
);
cy.getByTestId(voteMajorityNotMet).should(
'have.text',
'66% majority threshold not met'
);
cy.getByTestId(voteStatus).should(
'have.text',
'Currently expected to fail'
);
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Expected to pass')
.contains('👎')
.should('be.visible');
// 3001-VOTE-062
// 3001-VOTE-040
// 3001-VOTE-070
getProposalInformationFromTable('Token majority met')
.contains('👎')
.should('be.visible');
// 3001-VOTE-068
getProposalInformationFromTable('Token participation met')
.contains('👎')
.should('be.visible');
cy.getByTestId(numberOfVotesFor).should('have.text', '0.0M');
cy.getByTestId(numberOfVotesAgainst).should('have.text', '0.0M');
cy.getByTestId(totalVotedNumber).should('have.text', '0.0M');
});
// 3001-VOTE-080 3001-VOTE-090 3001-VOTE-069 3001-VOTE-072 3001-VOTE-073
@@ -197,43 +201,16 @@ describe(
.contains(votedDate)
.should('be.visible');
});
cy.getByTestId(proposalVoteProgressForPercentage) // 3001-VOTE-072
.contains('100.00%')
.and('be.visible');
cy.getByTestId(proposalVoteProgressAgainstPercentage)
.contains('0.00%')
.and('be.visible');
cy.getByTestId(proposalVoteProgressForTokens)
.contains('1.00')
.and('be.visible');
cy.getByTestId(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
getProposalInformationFromTable('Tokens against proposal')
.should('have.text', '0.00')
.and('be.visible');
// 3001-VOTE-061
getProposalInformationFromTable('Participation required')
.contains('0.00%')
.should('be.visible');
// 3001-VOTE-066
getProposalInformationFromTable('Majority Required') // 3001-VOTE-073
.contains(`${(66).toFixed(2)}%`)
.should('be.visible');
getProposalInformationFromTable('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
cy.getByTestId(votesForPercentage) // 3001-VOTE-072
.should('have.text', '100%');
cy.getByTestId(votesAgainstPercentage).should('have.text', '0%');
cy.getByTestId('token-majority-progress')
.should('have.attr', 'style')
.and('eq', 'width: 100%;'); // 3001-VOTE-024
cy.getByTestId(changeVoteButton).should('be.visible').click();
voteForProposal('for');
// 3001-VOTE-064
cy.getByTestId('user-voted-yes').should('exist');
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
@@ -244,16 +221,7 @@ describe(
});
cy.getByTestId(changeVoteButton).should('be.visible').click();
voteForProposal('against');
cy.getByTestId(proposalVoteProgressAgainstPercentage)
.contains('100.00%')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Tokens against proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
getProposalInformationFromTable('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
cy.getByTestId(votesAgainstPercentage).should('have.text', '100%');
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
@@ -265,6 +233,7 @@ describe(
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
it('Newly created proposal details - ability to increase associated tokens - by voting again after association', function () {
ensureSpecifiedUnstakedTokensAreAssociated('1');
vegaWalletSetSpecifiedApprovalAmount('1000');
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
@@ -275,69 +244,28 @@ describe(
voteForProposal('for');
// 3001-VOTE-079
cy.contains('You voted: For').should('be.visible');
cy.getByTestId(proposalVoteProgressForTokens)
.contains('1')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Total Supply')
.invoke('text')
.then((totalSupply) => {
const tokensRequiredToAchieveResult = (
(Number(totalSupply.replace(/,/g, '')) * 0.001) /
100
).toFixed(2);
ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated(
tokensRequiredToAchieveResult
);
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
);
});
cy.getByTestId(proposalVoteProgressForPercentage)
.contains('100.00%')
.and('be.visible');
cy.getByTestId(proposalVoteProgressAgainstPercentage)
.contains('0.00%')
.and('be.visible');
// 3001-VOTE-065
cy.getByTestId(changeVoteButton).should('be.visible').click();
voteForProposal('for');
cy.getByTestId(proposalVoteProgressForTokens)
.contains(tokensRequiredToAchieveResult)
.and('be.visible');
cy.getByTestId(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Total tokens voted percentage')
.should('have.text', '0.00%')
.and('be.visible');
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', tokensRequiredToAchieveResult)
.and('be.visible');
getProposalInformationFromTable('Tokens against proposal')
.should('have.text', '0.00')
.and('be.visible');
getProposalInformationFromTable('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
getProposalInformationFromTable('Expected to pass')
.contains('👍')
.should('be.visible');
// 3001-VOTE-062
getProposalInformationFromTable('Token majority met')
.contains('👍')
.should('be.visible');
getProposalInformationFromTable('Token participation met')
.contains('👍')
.should('be.visible');
getProposalInformationFromTable('Tokens for proposal')
.contains(tokensRequiredToAchieveResult)
.and('be.visible');
});
cy.getByTestId(numberOfVotesFor).should('have.text', '0.0M');
cy.getByTestId(votesForPercentage).should('have.text', '100%');
cy.getByTestId(totalVotedNumber).should('have.text', '0.0M');
cy.getByTestId(totalVotedPercentage).should('have.text', '(0.00%)');
ethereumWalletConnect();
stakingPageAssociateTokens('1000000', { approve: true });
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
);
});
cy.getByTestId(votesForPercentage).should('have.text', '100%');
cy.getByTestId(numberOfVotesFor).should('have.text', '0.0M');
cy.getByTestId(totalVotedNumber).should('have.text', '0.0M');
cy.getByTestId(totalVotedPercentage).should('have.text', '(0.00%)');
// 3001-VOTE-065
cy.getByTestId(changeVoteButton).should('be.visible').click();
voteForProposal('for');
cy.getByTestId(numberOfVotesFor).should('have.text', '1.0M');
cy.getByTestId(totalVotedNumber).should('have.text', '1.0M');
cy.getByTestId(totalVotedPercentage).should('have.text', '(1.54%)');
});
it('Able to vote for proposal twice by switching public key', function () {
@@ -360,10 +288,6 @@ describe(
voteForProposal('against');
cy.contains('You voted: Against').should('be.visible');
switchVegaWalletPubKey();
cy.getByTestId(proposalVoteProgressForTokens).should(
'contain.text',
'1.00'
);
// Checking vote status for different public keys is displayed correctly
cy.contains('You voted: For').should('be.visible');
});
@@ -22,12 +22,10 @@ const proposalListItem = '[data-testid="proposals-list-item"]';
const closedProposals = 'closed-proposals';
const proposalStatus = 'proposal-status';
const viewProposalButton = 'view-proposal-btn';
const votesTable = 'votes-table';
const votesTable = 'user-vote';
const openProposals = 'open-proposals';
const proposalVoteProgressForPercentage =
'vote-progress-indicator-percentage-for';
const majorityVoteReached = 'majority-reached';
const minParticipationReached = 'participation-reached';
const majorityVoteReached = 'token-majority-met';
const minParticipationReached = 'token-participation-met';
const proposalTimeout = { timeout: 8000 };
context(
@@ -70,7 +68,6 @@ context(
cy.getByTestId('proposal-type').should('have.text', 'New market');
cy.getByTestId(proposalStatus).should('have.text', 'Enacted');
cy.getByTestId(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible');
});
});
@@ -92,7 +89,7 @@ context(
// 3001-VOTE-019 time to vote is highlighted red
cy.getByTestId('vote-details')
.find('span')
.should('have.class', 'text-vega-pink');
.should('have.class', 'text-vega-orange');
cy.getByTestId(viewProposalButton).click();
});
});
@@ -109,12 +106,9 @@ context(
);
});
cy.getByTestId(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible');
});
cy.getByTestId(proposalVoteProgressForPercentage)
.contains('100.00%')
.and('be.visible');
cy.getByTestId('votes-for-percentage').should('have.text', '100%');
navigateTo(navigation.proposals);
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
@@ -298,9 +298,6 @@ context(
getProposalFromTitle(proposalTitle).within(() =>
cy.getByTestId(viewProposalButton).click()
);
cy.contains('Vote breakdown').should('be.visible', {
timeout: 10000,
});
cy.getByTestId(voteButtons).should('not.exist');
cy.getByTestId('min-proposal-requirements').should(
'have.text',
@@ -52,8 +52,6 @@ const enactmentDeadlineError = 'enactment-before-voting-deadline';
const proposalDownloadBtn = 'proposal-download-json';
const feedbackError = '[data-testid="Error"]';
const viewProposalBtn = 'view-proposal-btn';
const liquidityVoteStatus = 'liquidity-votes-status';
const tokenVoteStatus = 'token-votes-status';
const proposalJsonToggle = 'proposal-json-toggle';
const proposalJsonSection = 'proposal-json';
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
@@ -421,27 +419,28 @@ context(
cy.getByTestId(viewProposalBtn).click();
});
});
cy.getByTestId(liquidityVoteStatus).should(
'contain.text',
'Currently expected to fail'
cy.getByTestId('lp-majority-not-met').should(
'have.text',
'66% majority threshold not met'
);
cy.getByTestId(tokenVoteStatus).should(
'contain.text',
'Currently expected to fail'
cy.getByTestId('token-majority-not-met').should(
'have.text',
'66% majority threshold not met'
);
voteForProposal('for');
cy.getByTestId(liquidityVoteStatus).should(
'contain.text',
'Currently expected to pass'
cy.getByTestId('lp-majority-met').should(
'have.text',
'66% majority threshold met'
);
cy.getByTestId(tokenVoteStatus).should(
'contain.text',
'Currently expected to pass'
cy.getByTestId('token-majority-met').should(
'have.text',
'66% majority threshold met'
);
cy.getByTestId('vote-status').should(
'have.text',
'Currently expected to pass by token vote'
);
cy.getByTestId('vote-breakdown-toggle').click();
getProposalInformationFromTable('Expected to pass')
.contains('👍 by token vote')
.should('be.visible');
});
// 3001-VOTE-026 3001-VOTE-027 3001-VOTE-028 3001-VOTE-095 3001-VOTE-096 3005-PASN-001
@@ -11,7 +11,6 @@ import {
enterRawProposalBody,
generateFreeFormProposalTitle,
getProposalFromTitle,
getProposalInformationFromTable,
goToMakeNewProposal,
governanceProposalType,
submitUniqueRawProposal,
@@ -30,7 +29,9 @@ const proposalType = 'proposal-type';
const proposalStatus = 'proposal-status';
const proposalClosingDate = 'vote-details';
const viewProposalButton = 'view-proposal-btn';
const voteBreakDownToggle = 'vote-breakdown-toggle';
const voteMajorityNotMet = 'token-majority-not-met';
const voteMajorityMet = 'token-majority-met';
const votesForPercentage = 'votes-for-percentage';
describe('Governance flow for proposal list', { tags: '@slow' }, function () {
before('connect wallets and set approval limit', function () {
@@ -121,10 +122,15 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
submitUniqueRawProposal({ proposalTitle: proposalTitle });
getProposalFromTitle(proposalTitle).within(() => {
// 3001-VOTE-039
cy.getByTestId('participation-not-reached').should(
cy.getByTestId(voteMajorityNotMet).should(
'have.text',
'Min. participation not reached'
'66% majority threshold not met'
);
cy.getByTestId('token-participation-not-met').should(
'have.text',
'0.000000000000000000000015% participation threshold not met'
);
cy.getByTestId(votesForPercentage).should('have.text', '0%');
cy.getByTestId(viewProposalButton).click();
});
voteForProposal('for');
@@ -134,16 +140,15 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
'have.text',
'Currently expected to pass'
);
cy.getByTestId('user-voted-yes').should('exist');
cy.getByTestId('participation-reached').should(
cy.getByTestId(voteMajorityMet).should(
'have.text',
'Min. participation reached'
'66% majority threshold met'
);
cy.getByTestId(votesForPercentage).should('have.text', '100%');
cy.getByTestId('token-participation-met').should(
'have.text',
'0.000000000000000000000015% participation threshold met'
);
cy.getByTestId(viewProposalButton).click();
});
cy.getByTestId(voteBreakDownToggle).click();
getProposalInformationFromTable('Token participation met')
.contains('👍')
.should('be.visible');
});
});
@@ -262,10 +262,10 @@ context(
'2'
);
waitForBeginningOfEpoch();
cy.getByTestId(stakeValidatorListStakePercentage).should(
'have.text',
'50.02%'
);
cy.getByTestId(
stakeValidatorListStakePercentage,
epochTimeout
).should('have.text', '50.02%');
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
}
@@ -15,6 +15,7 @@ const proposalDocumentationLink = 'proposal-documentation-link';
const connectToVegaWalletButton = 'connect-to-vega-wallet-btn';
const governanceDocsUrl = 'https://vega.xyz/governance';
const networkUpgradeProposalListItem = 'protocol-upgrade-proposals-list-item';
const proposalUpgradeBlockHeight = 'protocol-upgrade-proposal-block-height';
const closedProposals = 'closed-proposals';
const closedProposalToggle = 'closed-proposals-toggle-networkUpgrades';
const protocolUpgradeTime = 'protocol-upgrade-time';
@@ -37,11 +38,39 @@ context(
});
// 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003
it('should have button for link to more information on proposals', function () {
const proposalsUrl = 'https://docs.vega.xyz/mainnet/tutorials/proposals';
cy.getByTestId('new-proposal-link')
.find('a')
.should('have.attr', 'href', proposalsUrl);
it('new proposal page should have button for link to more information on proposals', function () {
cy.getByTestId('new-proposal-link').click();
cy.url().should('include', '/proposals/propose/raw');
cy.contains('To see Explorer data on proposals visit').within(() => {
cy.getByTestId('external-link').should(
'have.attr',
'href',
'https://explorer.fairground.wtf/governance'
);
});
cy.contains(
'1. Sense check your proposal with the community on the forum:'
).within(() => {
cy.getByTestId('external-link').should(
'have.attr',
'href',
'https://community.vega.xyz/c/governance/25'
);
});
cy.contains(
'2. Use the appropriate proposal template in the docs:'
).within(() => {
cy.getByTestId('external-link').should(
'have.attr',
'href',
'https://docs.vega.xyz/mainnet/tutorials/proposals'
);
});
cy.contains('Connect your wallet to submit a proposal').should(
'be.visible'
);
cy.getByTestId('connect-to-vega-wallet-btn').should('exist');
navigateTo(navigation.proposals);
});
it('should be able to see a working link for - find out more about Vega governance', function () {
@@ -156,7 +185,7 @@ context(
'have.text',
'Vega release tag: v1'
);
cy.getByTestId('protocol-upgrade-proposal-block-height').should(
cy.getByTestId(proposalUpgradeBlockHeight).should(
'have.text',
'Upgrade block height: 2015942'
);
@@ -171,7 +200,15 @@ context(
});
cy.getByTestId(closedProposalToggle).click();
cy.getByTestId(closedProposals).within(() => {
cy.getByTestId(networkUpgradeProposalListItem).should('have.length', 1);
cy.getByTestId(networkUpgradeProposalListItem).should('have.length', 2);
cy.getByTestId(networkUpgradeProposalListItem)
.first()
.within(() => {
cy.getByTestId(proposalUpgradeBlockHeight).should(
'contain.text',
'10001'
);
});
});
});
@@ -146,7 +146,7 @@ export function getProposalInformationFromTable(heading: string) {
}
export function voteForProposal(vote: string) {
cy.contains('Vote breakdown').should('be.visible', { timeout: 10000 });
cy.get(voteButtons).should('be.visible', { timeout: 10000 });
cy.get(voteButtons).contains(vote).click();
cy.get(dialogTitle, proposalTimeout).should(
'have.text',
@@ -170,21 +170,20 @@ export function clickOnValidatorFromList(
validatorName = null
) {
cy.contains('Loading...', epochTimeout).should('not.exist');
waitForBeginningOfEpoch();
// below is to ensure validator list is shown
cy.get(stakeValidatorListName, { timeout: 10000 }).should('exist');
cy.get(stakeValidatorListPendingStake, txTimeout).should(
'not.contain',
'2,000,000,000,000,000,000.00' // number due to bug #936
);
waitForBeginningOfEpoch();
if (validatorName) {
cy.contains(validatorName).click();
} else {
cy.get(`[row-id="${validatorNumber}"]`)
.should('be.visible')
.first()
.as('validatorOnList');
cy.get('@validatorOnList').click();
.click();
}
}
@@ -196,7 +195,7 @@ export function validateValidatorListTotalStakeAndShare(
cy.contains('Loading...', epochTimeout).should('not.exist');
waitForBeginningOfEpoch();
cy.get(`[row-id="${positionOnList}"]`)
.eq(1)
.first()
.within(() => {
cy.getByTestId(stakeValidatorListTotalStake, epochTimeout).should(
'have.text',
@@ -219,7 +218,9 @@ export function ensureSpecifiedUnstakedTokensAreAssociated(
.eq(1)
.invoke('text')
.then((unstakedBalance) => {
if (parseFloat(unstakedBalance) != parseFloat(tokenAmount)) {
const tokenFloat = parseFloat(tokenAmount);
const unstakedFloat = parseFloat(unstakedBalance.replace(/,/g, ''));
if (tokenFloat != unstakedFloat) {
vegaWalletTeardown();
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'0.00',
+2
View File
@@ -22,6 +22,8 @@ NX_VEGA_REST_URL=http://localhost:3008/api/v2/
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
+2
View File
@@ -19,6 +19,8 @@ NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fa
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
+1
View File
@@ -16,6 +16,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
+1
View File
@@ -15,6 +15,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
+1
View File
@@ -9,6 +9,7 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_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_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
+1
View File
@@ -14,6 +14,7 @@ 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_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
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_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
+1
View File
@@ -11,6 +11,7 @@ NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
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_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
+2 -2
View File
@@ -184,7 +184,7 @@ const Web3Container = ({
<TemplateSidebar sidebar={sideBar}>
<AppRouter />
</TemplateSidebar>
<footer className="p-4 border-t border-neutral-700">
<footer className="p-4 border-t border-neutral-700 break-all">
<NetworkInfo />
</footer>
</AppLayout>
@@ -308,7 +308,7 @@ const AppContainer = () => {
<Router>
<ScrollToTop />
<AppStateProvider>
<div className="min-h-full text-white grid">
<div className="min-h-full text-white">
<NodeGuard
skeleton={<div>{t('Loading')}</div>}
failure={
@@ -708,7 +708,7 @@
"NewAssetProposal": "New asset proposal",
"UpdateAssetProposal": "Update asset proposal",
"NewFreeformProposal": "New freeform proposal",
"NewRawProposal": "New raw proposal",
"NewRawProposal": "New proposal",
"MinProposalRequirements": "You must have at least {{value}} VEGA associated to make a proposal",
"MinProposalVoteRequirements": "You must have at least {{value}} VEGA associated to vote on this proposal",
"totalSupply": "Total Supply",
@@ -870,5 +870,9 @@
"Upgraded at": "Upgraded at",
"dataIsIdentical": "Data is identical",
"updatesToMarket": "Updates to market",
"viewAsParty": "View as party"
"viewAsParty": "View as party",
"HowToPropose": "How to make a proposal",
"HowToProposeRawStep1": "1. Sense check your proposal with the community on the forum:",
"HowToProposeRawStep2": "2. Use the appropriate proposal template in the docs:",
"HowToProposeRawStep3": "3. Submit on-chain below"
}
+4 -4
View File
@@ -45,7 +45,7 @@ const HomeProposals = ({
const { t } = useTranslation();
return (
<section className="mb-16" data-testid="home-proposals">
<section className="mb-16 break-all" data-testid="home-proposals">
<Heading title={t('vegaGovernance')} />
<h3 className="mb-6">{t('homeProposalsIntro')}</h3>
<div className="mb-8">
@@ -279,8 +279,8 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
trimmedActiveNodes={trimmedActiveNodes}
/>
<section className="grid grid-cols-2 gap-12 mb-16">
<div data-testid="home-rewards">
<section className="flex justify-between flex-wrap gap-12 mb-16">
<div className="min-w-[360px] flex-1" data-testid="home-rewards">
<Heading title={t('Rewards')} marginTop={false} />
<h3 className="mb-6">{t('homeRewardsIntro')}</h3>
<div className="flex items-center mb-8 gap-4">
@@ -290,7 +290,7 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
</div>
</div>
<div data-testid="home-vega-token">
<div className="min-w-[360px] flex-1" data-testid="home-vega-token">
<Heading title={t('vegaToken')} marginTop={false} />
<h3 className="mb-6">{t('homeVegaTokenIntro')}</h3>
<div className="flex items-center mb-8 gap-4">
@@ -159,7 +159,7 @@ export const ProposalHeader = ({
</div>
</div>
<div data-testid="proposal-title">
<div data-testid="proposal-title" className="break-all">
{isListItem ? (
<header>
<SubHeading
@@ -127,7 +127,7 @@ export const Proposal = ({
voteState={voteState}
/>
<div className="my-10">
<div className="my-10 break-all">
<ProposalChangeTable proposal={proposal} />
</div>
@@ -7,18 +7,13 @@ import { ProposalsListItem } from '../proposals-list-item';
import { ProtocolUpgradeProposalsListItem } from '../protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
import { ProposalsListFilter } from '../proposals-list-filter';
import Routes from '../../../routes';
import {
Button,
Toggle,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { Button, Toggle } from '@vegaprotocol/ui-toolkit';
import { Link } from 'react-router-dom';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { DocsLinks, ExternalLinks } from '@vegaprotocol/environment';
import { ExternalLinks } from '@vegaprotocol/environment';
interface ProposalsListProps {
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
@@ -55,7 +50,10 @@ export const orderByUpgradeBlockHeight = (
) =>
orderBy(
arr,
[(p) => p?.upgradeBlockHeight, (p) => p.vegaReleaseTag],
[
(p) => (p?.upgradeBlockHeight ? parseInt(p.upgradeBlockHeight, 10) : 0),
(p) => p.vegaReleaseTag,
],
['desc', 'desc']
);
@@ -142,18 +140,13 @@ export const ProposalsList = ({
title={t('pageTitleProposals')}
/>
{DocsLinks && (
<div className="xs:justify-self-end" data-testid="new-proposal-link">
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
<Button variant="primary" size="sm">
<div className="flex items-center gap-1">
{t('NewProposal')}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
</div>
</Button>
</ExternalLink>
</div>
)}
<div className="xs:justify-self-end" data-testid="new-proposal-link">
<Link to={`${Routes.PROPOSALS}/propose/raw`}>
<Button variant="primary" size="sm">
<div className="flex items-center gap-1">{t('NewProposal')}</div>
</Button>
</Link>
</div>
</div>
<p className="mb-8">
@@ -224,10 +217,10 @@ export const ProposalsList = ({
sortedProtocolUpgradeProposals.closed.length > 0 &&
filterString.length < 1 && (
<div
className="grid w-full justify-end xl:-mt-12 pb-6"
className="flex justify-end xl:-mt-12 pb-6"
data-testid="toggle-closed-proposals"
>
<div className="w-[440px]">
<div className="w-full max-w-[420px]">
<Toggle
name="closed-proposals-toggle"
toggles={[
@@ -130,7 +130,10 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
? t('byTokenVote')
: t('byLiquidityVote');
const sectionWrapperClasses = classNames('grid sm:grid-cols-2 gap-6');
const sectionWrapperClasses = classNames(
'flex justify-between flex-wrap gap-6'
);
const sectionClasses = classNames('min-w-[300px] flex-1 flex-grow');
const headingClasses = classNames('mb-2 text-vega-dark-400');
const progressDetailsClasses = classNames(
'flex justify-between flex-wrap mt-2 text-sm'
@@ -166,7 +169,10 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<div className="mb-4">
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
<div className={sectionWrapperClasses}>
<section data-testid="lp-majority-breakdown">
<section
className={sectionClasses}
data-testid="lp-majority-breakdown"
>
<VoteProgress
percentageFor={yesLPPercentage}
colourfulBg={true}
@@ -241,7 +247,10 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
</div>
</section>
<section data-testid="lp-participation-breakdown">
<section
className={sectionClasses}
data-testid="lp-participation-breakdown"
>
<VoteProgress
percentageFor={
lpParticipationThresholdProgress || new BigNumber(0)
@@ -288,7 +297,10 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
{isUpdateMarket && <h3 className={headingClasses}>{t('tokenVote')}</h3>}
<div className={sectionWrapperClasses}>
<section data-testid="token-majority-breakdown">
<section
className={sectionClasses}
data-testid="token-majority-breakdown"
>
<VoteProgress
percentageFor={yesPercentage}
colourfulBg={true}
@@ -308,7 +320,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<div className="flex items-center gap-1">
<span>{t('tokenVotesFor')}:</span>
<Tooltip description={formatNumber(yesTokens, defaultDP)}>
<button>
<button data-testid="num-votes-for">
{yesTokens.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
</button>
</Tooltip>
@@ -317,7 +329,9 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<Tooltip
description={<span>{yesPercentage.toFixed(defaultDP)}%</span>}
>
<button>{yesPercentage.toFixed(0)}%</button>
<button data-testid="votes-for-percentage">
{yesPercentage.toFixed(0)}%
</button>
</Tooltip>
)
</span>
@@ -326,7 +340,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<div className="flex items-center gap-1">
<span>{t('tokenVotesAgainst')}:</span>
<Tooltip description={formatNumber(noTokens, defaultDP)}>
<button>
<button data-testid="num-votes-against">
{noTokens.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
</button>
</Tooltip>
@@ -335,7 +349,9 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<Tooltip
description={<span>{noPercentage.toFixed(defaultDP)}%</span>}
>
<button>{noPercentage.toFixed(0)}%</button>
<button data-testid="votes-against-percentage">
{noPercentage.toFixed(0)}%
</button>
</Tooltip>
)
</span>
@@ -343,7 +359,10 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
</div>
</section>
<section data-testid="token-participation-breakdown">
<section
className={sectionClasses}
data-testid="token-participation-breakdown"
>
<VoteProgress
percentageFor={participationThresholdProgress}
testId="token-participation-progress"
@@ -364,11 +383,13 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<div className="flex items-center gap-1">
<span>{t('totalTokensVoted')}:</span>
<Tooltip description={formatNumber(totalTokensVoted, defaultDP)}>
<button>
<button data-testid="total-voted">
{totalTokensVoted.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
</button>
</Tooltip>
<span>({totalTokensPercentage.toFixed(defaultDP)}%)</span>
<span data-testid="total-voted-percentage">
({totalTokensPercentage.toFixed(defaultDP)}%)
</span>
</div>
</div>
</section>
@@ -1,12 +1,17 @@
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { Heading } from '../../../../components/heading';
import {
useEnvironment,
DocsLinks,
ExternalLinks,
} from '@vegaprotocol/environment';
import { Heading, SubHeading } from '../../../../components/heading';
import {
AsyncRenderer,
ExternalLink,
FormGroup,
InputError,
RoundedWrapper,
TextArea,
} from '@vegaprotocol/ui-toolkit';
import { validateJson } from '@vegaprotocol/utils';
@@ -79,15 +84,6 @@ export const ProposeRaw = () => {
spamProtectionMin={params.spam_protection_proposal_min_tokens}
/>
{DocsLinks && (
<p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE} target="_blank">
{DocsLinks.PROPOSALS_GUIDE}
</ExternalLink>
</p>
)}
{VEGA_EXPLORER_URL && (
<p className="text-sm">
{t('MoreProposalsInfo')}{' '}
@@ -98,12 +94,44 @@ export const ProposeRaw = () => {
</p>
)}
<section data-testid="how-to" className="my-10">
<RoundedWrapper paddingBottom={true}>
<SubHeading title={t('HowToPropose')} />
<ul>
<li className="p-1">
{t('HowToProposeRawStep1')}{' '}
{ExternalLinks && (
<span data-testid="proposal-docs-link">
<ExternalLink
href={ExternalLinks.PROPOSALS_FORUM}
target="_blank"
>
{ExternalLinks.PROPOSALS_FORUM}
</ExternalLink>
</span>
)}
</li>
<li className="p-1">
{t('HowToProposeRawStep2')}{' '}
{DocsLinks && (
<span data-testid="proposal-docs-link">
<ExternalLink
href={DocsLinks.PROPOSALS_GUIDE}
target="_blank"
>
{DocsLinks.PROPOSALS_GUIDE}
</ExternalLink>
</span>
)}
</li>
<li className="p-1">{t('HowToProposeRawStep3')}</li>
</ul>
</RoundedWrapper>
</section>
<div data-testid="raw-proposal-form">
<form onSubmit={handleSubmit(onSubmit)}>
<FormGroup
label="Make a proposal by submitting JSON"
labelFor="proposal-data"
>
<FormGroup label="Valid JSON required" labelFor="proposal-data">
<TextArea
id="proposal-data"
className="min-h-[200px]"
@@ -22,7 +22,6 @@ export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
const { param: marketCreationQuantumMultiple } = useNetworkParam(
'rewards_marketCreationQuantumMultiple'
);
console.log(marketCreationQuantumMultiple);
const [page, setPage] = useState(1);
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
notifyOnNetworkStatusChange: true,
@@ -133,24 +133,26 @@ export const RewardsPage = () => {
</p>
</div>
<div className="w-[360px]">
<Toggle
name="epoch-reward-view-toggle"
toggles={[
{
label: t('totalDistributed'),
value: 'total',
},
{
label: t('earnedByMe'),
value: 'individual',
},
]}
checkedValue={toggleRewardsView}
onChange={(e) =>
setToggleRewardsView(e.target.value as RewardsView)
}
/>
<div className="flex justify-end">
<div className="w-full max-w-[360px]">
<Toggle
name="epoch-reward-view-toggle"
toggles={[
{
label: t('totalDistributed'),
value: 'total',
},
{
label: t('earnedByMe'),
value: 'individual',
},
]}
checkedValue={toggleRewardsView}
onChange={(e) =>
setToggleRewardsView(e.target.value as RewardsView)
}
/>
</div>
</div>
</section>
@@ -352,7 +352,6 @@ export const ConsensusValidatorsTable = ({
field: ValidatorFields.RANKING_INDEX,
headerName: '#',
width: 60,
pinned: 'left',
},
{
field: ValidatorFields.VALIDATOR,
@@ -362,7 +361,6 @@ export const ConsensusValidatorsTable = ({
if (a === b) return 0;
return a > b ? 1 : -1;
},
pinned: 'left',
width: 260,
},
{
@@ -195,14 +195,12 @@ export const StandbyPendingValidatorsTable = ({
field: ValidatorFields.RANKING_INDEX,
headerName: '#',
width: 60,
pinned: 'left',
},
{
field: ValidatorFields.VALIDATOR,
headerName: t(ValidatorFields.VALIDATOR).toString(),
cellRenderer: ValidatorRenderer,
comparator: ({ name: a }, { name: b }) => Math.sign(a - b),
pinned: 'left',
width: 260,
},
{
@@ -122,38 +122,43 @@ export const ValidatorTable = ({
<div className="my-12" data-testid="validator-table">
<SubHeading title={t('profile')} />
<RoundedWrapper paddingBottom={true}>
<KeyValueTable data-testid="validator-table-profile">
<KeyValueTableRow>
<span>{t('id')}</span>
<ValidatorTableCell dataTestId="validator-id">
{node.id}
</ValidatorTableCell>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('ABOUT THIS VALIDATOR')}</span>
<div className="break-all">
<RoundedWrapper paddingBottom={true}>
<KeyValueTable data-testid="validator-table-profile">
<KeyValueTableRow>
<span>{t('id')}</span>
<ValidatorTableCell dataTestId="validator-id">
{node.id}
</ValidatorTableCell>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('ABOUT THIS VALIDATOR')}</span>
<Tooltip description={t('AboutThisValidatorDescription')}>
<a data-testid="validator-description-url" href={node.infoUrl}>
{node.infoUrl}
</a>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>
<strong>{t('STATUS')}</strong>
</span>
<Tooltip description={t('ValidatorStatusDescription')}>
<span data-testid="validator-status">
<strong>
{t(statusTranslationKey(node.rankingScore.status))}
</strong>
<Tooltip description={t('AboutThisValidatorDescription')}>
<a
data-testid="validator-description-url"
href={node.infoUrl}
>
{node.infoUrl}
</a>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>
<strong>{t('STATUS')}</strong>
</span>
</Tooltip>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
<Tooltip description={t('ValidatorStatusDescription')}>
<span data-testid="validator-status">
<strong>
{t(statusTranslationKey(node.rankingScore.status))}
</strong>
</span>
</Tooltip>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
</div>
<div className="mb-10">
{t('validatorTableIntro')}{' '}
@@ -167,144 +172,154 @@ export const ValidatorTable = ({
</div>
<SubHeading title={t('ADDRESS')} />
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
<KeyValueTable data-testid="validator-table-address">
<KeyValueTableRow>
<span>{t('VEGA ADDRESS / PUBLIC KEY')}</span>
<ValidatorTableCell dataTestId="validator-public-key">
{node.pubkey}
</ValidatorTableCell>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('SERVER LOCATION')}</span>
<ValidatorTableCell dataTestId="validator-server-location">
{countryData.find((c) => c.code === node.location)?.name ||
t('not available')}
</ValidatorTableCell>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>{t('ETHEREUM ADDRESS')}</span>
<span data-testid="validator-eth-address">
<Link
title={t('View on Etherscan (opens in a new tab)')}
href={`${ETHERSCAN_URL}/address/${node.ethereumAddress}`}
target="_blank"
>
{node.ethereumAddress}
</Link>
</span>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
<div className="break-all">
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
<KeyValueTable data-testid="validator-table-address">
<KeyValueTableRow>
<span>{t('VEGA ADDRESS / PUBLIC KEY')}</span>
<ValidatorTableCell dataTestId="validator-public-key">
{node.pubkey}
</ValidatorTableCell>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('SERVER LOCATION')}</span>
<ValidatorTableCell dataTestId="validator-server-location">
{countryData.find((c) => c.code === node.location)?.name ||
t('not available')}
</ValidatorTableCell>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>{t('ETHEREUM ADDRESS')}</span>
<span data-testid="validator-eth-address">
<Link
title={t('View on Etherscan (opens in a new tab)')}
href={`${ETHERSCAN_URL}/address/${node.ethereumAddress}`}
target="_blank"
>
{node.ethereumAddress}
</Link>
</span>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
</div>
<SubHeading title={t('STAKE')} />
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
<KeyValueTable data-testid="validator-table-stake">
<KeyValueTableRow>
<span>{t('STAKED BY OPERATOR')}</span>
<div className="break-all">
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
<KeyValueTable data-testid="validator-table-stake">
<KeyValueTableRow>
<span>{t('STAKED BY OPERATOR')}</span>
<Tooltip description={t('StakedByOperatorDescription')}>
<span data-testid="staked-by-operator">
{formatNumber(toBigNum(node.stakedByOperator, decimals))}
<Tooltip description={t('StakedByOperatorDescription')}>
<span data-testid="staked-by-operator">
{formatNumber(toBigNum(node.stakedByOperator, decimals))}
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('STAKED BY DELEGATES')}</span>
<Tooltip description={t('StakedByDelegatesDescription')}>
<span data-testid="staked-by-delegates">
{formatNumber(toBigNum(node.stakedByDelegates, decimals))}
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow>
<span>
<strong>{t('TOTAL STAKE')}</strong>
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('STAKED BY DELEGATES')}</span>
<Tooltip description={t('StakedByDelegatesDescription')}>
<span data-testid="staked-by-delegates">
{formatNumber(toBigNum(node.stakedByDelegates, decimals))}
<span data-testid="total-stake">
<strong>
{formatNumber(toBigNum(node.stakedTotal, decimals))}
</strong>
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow>
<span>
<strong>{t('TOTAL STAKE')}</strong>
</span>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('PENDING STAKE')}</span>
<span data-testid="total-stake">
<strong>
{formatNumber(toBigNum(node.stakedTotal, decimals))}
</strong>
</span>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('PENDING STAKE')}</span>
<Tooltip description={t('PendingStakeDescription')}>
<span data-testid="pending-stake">
{formatNumber(toBigNum(node.pendingStake, decimals))}
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>{t('STAKE SHARE')}</span>
<Tooltip description={t('PendingStakeDescription')}>
<span data-testid="pending-stake">
{formatNumber(toBigNum(node.pendingStake, decimals))}
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>{t('STAKE SHARE')}</span>
<Tooltip description={t('StakeShareDescription')}>
<span data-testid="stake-percentage">{stakePercentage}</span>
</Tooltip>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
<Tooltip description={t('StakeShareDescription')}>
<span data-testid="stake-percentage">{stakePercentage}</span>
</Tooltip>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
</div>
<SubHeading title={t('PENALTIES')} />
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
<KeyValueTable data-testid="validator-table-penalties">
<KeyValueTableRow>
<span>{t('OVERSTAKED PENALTY')}</span>
<div className="break-all">
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
<KeyValueTable data-testid="validator-table-penalties">
<KeyValueTableRow>
<span>{t('OVERSTAKED PENALTY')}</span>
<Tooltip description={t('OverstakedPenaltyDescription')}>
<span data-testid="overstaking-penalty">
{formatNumberPercentage(penalties.overstaked, 2)}
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('PERFORMANCE PENALTY')}</span>
<Tooltip description={t('OverstakedPenaltyDescription')}>
<span data-testid="overstaking-penalty">
{formatNumberPercentage(penalties.overstaked, 2)}
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow>
<span>{t('PERFORMANCE PENALTY')}</span>
<Tooltip description={t('PerformancePenaltyDescription')}>
<span data-testid="performance-penalty">
{formatNumberPercentage(penalties.performance, 2)}
<Tooltip description={t('PerformancePenaltyDescription')}>
<span data-testid="performance-penalty">
{formatNumberPercentage(penalties.performance, 2)}
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>
<strong>{t('TOTAL PENALTIES')}</strong>
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>
<strong>{t('TOTAL PENALTIES')}</strong>
</span>
<span data-testid="total-penalties">
<strong>{formatNumberPercentage(penalties.overall, 2)}</strong>
</span>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
<span data-testid="total-penalties">
<strong>
{formatNumberPercentage(penalties.overall, 2)}
</strong>
</span>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
</div>
<SubHeading title={t('VOTING POWER')} />
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
<KeyValueTable data-testid="validator-table-voting-power">
<KeyValueTableRow>
<span>{t('UNNORMALISED VOTING POWER')}</span>
<div className="break-all">
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
<KeyValueTable data-testid="validator-table-voting-power">
<KeyValueTableRow>
<span>{t('UNNORMALISED VOTING POWER')}</span>
<Tooltip description={t('UnnormalisedVotingPowerDescription')}>
<span data-testid="unnormalised-voting-power">
{getUnnormalisedVotingPower(rawValidatorScore)}
<Tooltip description={t('UnnormalisedVotingPowerDescription')}>
<span data-testid="unnormalised-voting-power">
{getUnnormalisedVotingPower(rawValidatorScore)}
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>
<strong>{t('NORMALISED VOTING POWER')}</strong>
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>
<strong>{t('NORMALISED VOTING POWER')}</strong>
</span>
<Tooltip description={t('NormalisedVotingPowerDescription')}>
<strong data-testid="normalised-voting-power">
{getNormalisedVotingPower(node.rankingScore.votingPower)}
</strong>
</Tooltip>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
<Tooltip description={t('NormalisedVotingPowerDescription')}>
<strong data-testid="normalised-voting-power">
{getNormalisedVotingPower(node.rankingScore.votingPower)}
</strong>
</Tooltip>
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
</div>
</div>
</>
);
+3 -3
View File
@@ -100,8 +100,8 @@ const Home = ({ name }: RouteChildProps) => {
</Link>
</p>
</HomeSection>
<div className="flex gap-12">
<div className="flex-1">
<div className="flex justify-between flex-wrap gap-x-12 gap-y-4">
<div className="flex-1 min-w-[360px]">
<HomeSection>
<SubHeading title={t('Staking')} />
<p>
@@ -118,7 +118,7 @@ const Home = ({ name }: RouteChildProps) => {
</p>
</HomeSection>
</div>
<div className="flex-1">
<div className="flex-1 min-w-[360px]">
<HomeSection>
<SubHeading title={t('Governance')} />
<p>
@@ -54,7 +54,7 @@ export const TokenDetails = ({
config.token_vesting_contract?.address || ENV.addresses.tokenVestingAddress;
return (
<div className="token-details">
<div className="token-details break-all">
<RoundedWrapper>
<KeyValueTable>
<KeyValueTableRow>
+6
View File
@@ -6,6 +6,12 @@
@tailwind utilities;
@layer base {
*,
*::before,
*::after {
box-sizing: border-box;
}
h1 {
@apply text-2xl text-white uppercase mb-4;
}
+2
View File
@@ -15,6 +15,8 @@ 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_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SENTRY_DSN=https://dummy@o999999.ingest.sentry.io/9999999
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Expose some env vars to cypress environment for market setup
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
+2
View File
@@ -12,6 +12,8 @@ NX_VEGA_URL=http://localhost:3008/graphql
NX_VEGA_WALLET_URL=http://localhost:1789
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_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Expose some env vars to cypress environment for market setup
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
+12 -11
View File
@@ -1,27 +1,28 @@
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
NX_VEGA_ENV=TESTNET
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
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_CONSOLE_URL=https://console.fairground.wtf
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
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
# NX_ICEBERG_ORDERS
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_METAMASK_SNAPS=false
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
+5 -2
View File
@@ -15,15 +15,18 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# TAG name of the current app version - TODO: bump to the latest upon release
NX_APP_VERSION=v0.21.0-core-0.72.14
NX_APP_VERSION=v0.21.1-core-0.72.14
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=false
NX_METAMASK_SNAPS=true
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
+2
View File
@@ -15,6 +15,8 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
+2 -1
View File
@@ -16,13 +16,14 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=false
NX_METAMASK_SNAPS=true
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
+1
View File
@@ -17,6 +17,7 @@ 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_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
+10 -15
View File
@@ -1,20 +1,16 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { marketsWithDataProvider } from '@vegaprotocol/markets';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { Links, Routes } from '../../pages/client-router';
import { useGlobalStore } from '../../stores';
import { useTopTradedMarkets } from '../../lib/hooks/use-top-traded-markets';
// The home pages only purpose is to redirect to the users last market,
// the top traded if they are new, or fall back to the list of markets.
// Thats why we just render a loader here
export const Home = () => {
const navigate = useNavigate();
// The default market selected in the platform behind the overlay
// should be the oldest market that is currently trading in continuous mode(i.e. not in auction).
const { data, error, loading } = useDataProvider({
dataProvider: marketsWithDataProvider,
variables: undefined,
});
const update = useGlobalStore((store) => store.update);
const { data } = useTopTradedMarkets();
const marketId = useGlobalStore((store) => store.marketId);
useEffect(() => {
@@ -32,12 +28,11 @@ export const Home = () => {
navigate(Links[Routes.MARKETS]());
}
}
}, [marketId, data, navigate, update]);
}, [marketId, data, navigate]);
return (
<AsyncRenderer data={data} loading={loading} error={error}>
{/* Render a loading and error state but we will redirect if markets are found */}
{null}
</AsyncRenderer>
<Splash>
<Loader />
</Splash>
);
};
+7 -7
View File
@@ -69,15 +69,15 @@ export const MarketPage = () => {
useEffect(() => {
if (data?.id && data.id !== lastMarketId) {
update({ marketId: data.id });
}
}, [update, lastMarketId, data?.id]);
// Make sidebar open on deal ticket by default
useEffect(() => {
if (init && view === null) {
// make sidebar open on market id change
setView({ type: ViewType.Order });
}
}, [init, view, setView]);
// make sidebar open on deal ticket by default
if (view === null) {
setView({ type: ViewType.Order });
}
}, [update, lastMarketId, data?.id, setView, init, view]);
const tradeView = useMemo(() => {
if (largeScreen) {
+21 -208
View File
@@ -1,4 +1,4 @@
import { act, render, screen, within, waitFor } from '@testing-library/react';
import { act, render, screen, within } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Closed } from './closed';
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
@@ -10,55 +10,21 @@ import type {
OracleSpecDataConnectionQuery,
MarketsDataQuery,
MarketsQuery,
SuccessorMarketIdsQuery,
} from '@vegaprotocol/markets';
import {
OracleSpecDataConnectionDocument,
MarketsDataDocument,
MarketsDocument,
SuccessorMarketIdsDocument,
} from '@vegaprotocol/markets';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { FLAGS } from '@vegaprotocol/environment';
import {
createMarketFragment,
marketsQuery,
marketsDataQuery,
createMarketsDataFragment,
} from '@vegaprotocol/mock';
import type { FeatureFlags } from '@vegaprotocol/environment';
jest.mock('@vegaprotocol/markets', () => ({
...jest.requireActual('@vegaprotocol/markets'),
useSuccessorMarket: (marketId: string) =>
marketId === 'include-0'
? {
data: {
id: 'successorMarketID',
state: 'STATE_ACTIVE',
tradableInstrument: {
instrument: {
name: 'Successor Market Name',
code: 'SuccessorCode',
},
},
},
}
: { data: undefined },
}));
jest.mock('@vegaprotocol/environment', () => {
const actual = jest.requireActual('@vegaprotocol/environment');
return {
...actual,
FLAGS: {
...actual.FLAGS,
SUCCESSOR_MARKETS: true,
} as FeatureFlags,
};
});
describe('Closed', () => {
let originalNow: typeof Date.now;
@@ -218,10 +184,8 @@ describe('Closed', () => {
const headers = screen.getAllByRole('columnheader');
const expectedHeaders = [
'Market',
'Description',
'Status',
'Settlement date',
'Successor market',
'Best bid',
'Best offer',
'Mark price',
@@ -235,10 +199,8 @@ describe('Closed', () => {
const cells = screen.getAllByRole('gridcell');
const expectedValues = [
market.tradableInstrument.instrument.code,
market.tradableInstrument.instrument.name,
MarketStateMapping[market.state],
'3 days ago',
'-',
/* eslint-disable @typescript-eslint/no-non-null-assertion */
addDecimalsFormatNumber(marketsData.bestBidPrice, market.decimalPlaces),
addDecimalsFormatNumber(
@@ -340,43 +302,26 @@ describe('Closed', () => {
.getAllByRole('gridcell')
.filter((cell) => cell.getAttribute('col-id') === 'code')
.map((cell) => {
const marketId = within(cell)
.getByTestId('market-code')
.getAttribute('data-market-id');
return marketId;
const marketCode = within(cell).getByTestId('stack-cell-primary');
return marketCode.textContent;
});
expect(cells).toEqual(expectedRows.map((m) => m.node.id));
expect(cells).toEqual(
expectedRows.map((m) => m.node.tradableInstrument.instrument.code)
);
});
it('successor marked should be visible', async () => {
const mixedMarkets = [
const marketsWithSuccessorID = [
{
__typename: 'MarketEdge' as const,
node: createMarketFragment({
id: 'include-0',
state: MarketState.STATE_SETTLED,
successorMarketID: 'successor',
}),
},
{
__typename: 'MarketEdge' as const,
node: {
...createMarketFragment({
id: 'successorMarketID',
state: MarketState.STATE_ACTIVE,
}),
tradableInstrument: {
...createMarketFragment().tradableInstrument,
instrument: {
...createMarketFragment().tradableInstrument.instrument,
id: 'successorAssset',
name: 'Successor Market Name',
code: 'SuccessorCode',
},
},
},
},
];
const mixedMarketsMock: MockedResponse<MarketsQuery> = {
const mockWithSuccessors: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
},
@@ -384,42 +329,17 @@ describe('Closed', () => {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: mixedMarkets,
edges: marketsWithSuccessorID,
},
},
},
};
const successorMarketsMock: MockedResponse<SuccessorMarketIdsQuery> = {
request: {
query: SuccessorMarketIdsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: [
{
node: {
id: 'include-0',
successorMarketID: 'successorMarketID',
parentMarketID: '',
},
},
],
},
},
},
};
await act(() => {
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[
mixedMarketsMock,
marketsDataMock,
oracleDataMock,
successorMarketsMock,
]}
mocks={[mockWithSuccessors, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
@@ -431,122 +351,15 @@ describe('Closed', () => {
);
});
await waitFor(() => {
expect(
screen.getByRole('button', { name: /^SuccessorCode/ })
).toBeInTheDocument();
});
expect(
screen.getByRole('columnheader', {
name: (_name, element) =>
element.getAttribute('col-id') === 'successorMarket',
})
).toBeInTheDocument();
screen
.getAllByRole('gridcell', {
name: (_name, element) =>
element.getAttribute('col-id') === 'successorMarket',
})
.forEach((element) => {
expect(element.querySelector('[title="Future"]')?.textContent).toEqual(
'Futr'
);
});
});
const container = within(
document.querySelector('.ag-center-cols-container') as HTMLElement
);
const cell = container.getAllByRole('gridcell', {
name: (_name, element) => element.getAttribute('col-id') === 'code',
})[0];
it('feature flag should hide successors', async () => {
const mockedFlags = jest.mocked(FLAGS);
mockedFlags.SUCCESSOR_MARKETS = false;
const mixedMarkets = [
{
__typename: 'MarketEdge' as const,
node: createMarketFragment({
id: 'include-0',
state: MarketState.STATE_SETTLED,
}),
},
{
__typename: 'MarketEdge' as const,
node: {
...createMarketFragment({
id: 'successorMarketID',
state: MarketState.STATE_ACTIVE,
}),
tradableInstrument: {
...createMarketFragment().tradableInstrument,
instrument: {
...createMarketFragment().tradableInstrument.instrument,
id: 'successorAssset',
name: 'Successor Market Name',
code: 'SuccessorCode',
},
},
},
},
];
const mixedMarketsMock: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: mixedMarkets,
},
},
},
};
const successorMarketsMock: MockedResponse<SuccessorMarketIdsQuery> = {
request: {
query: SuccessorMarketIdsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: [
{
node: {
id: 'include-0',
successorMarketID: 'successorMarketID',
parentMarketID: '',
},
},
],
},
},
},
};
render(
<MemoryRouter>
<MockedProvider
mocks={[
mixedMarketsMock,
marketsDataMock,
oracleDataMock,
successorMarketsMock,
]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
expect(within(cell).getByTestId('stack-cell-secondary')).toHaveTextContent(
'PRNT'
);
await waitFor(() => {
expect(
screen.getByRole('columnheader', {
name: (_name, element) =>
element.getAttribute('col-id') === 'settlementDate',
})
).toBeInTheDocument();
});
screen.getAllByRole('columnheader').forEach((element) => {
expect(element.getAttribute('col-id')).not.toEqual('successorMarket');
});
});
});
+24 -34
View File
@@ -4,13 +4,10 @@ import type {
VegaICellRendererParams,
VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import {
AgGridLazy as AgGrid,
COL_DEFS,
MarketNameCell,
} from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
import { useMemo } from 'react';
import { t } from '@vegaprotocol/i18n';
import type { ProductType } from '@vegaprotocol/types';
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
import {
addDecimalsFormatNumber,
@@ -20,17 +17,13 @@ import type {
DataSourceFilterFragment,
MarketMaybeWithData,
} from '@vegaprotocol/markets';
import {
MarketActionsDropdown,
closedMarketsWithDataProvider,
} from '@vegaprotocol/markets';
import { closedMarketsWithDataProvider } from '@vegaprotocol/markets';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import type { ColDef } from 'ag-grid-community';
import { FLAGS } from '@vegaprotocol/environment';
import { SettlementDateCell } from './settlement-date-cell';
import { SettlementPriceCell } from './settlement-price-cell';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { SuccessorMarketRenderer } from './successor-market-cell';
import { MarketActionsDropdown } from './market-table-actions';
import { MarketCodeCell } from './market-code-cell';
type SettlementAsset =
MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset'];
@@ -51,7 +44,9 @@ interface Row {
setlementDataSourceFilter: DataSourceFilterFragment | undefined;
tradingTerminationOracleId: string;
settlementAsset: SettlementAsset;
productType: string;
productType: ProductType | undefined;
successorMarketID: string | null | undefined;
parentMarketID: string | null | undefined;
}
export const Closed = () => {
@@ -95,16 +90,19 @@ export const Closed = () => {
tradingTerminationOracleId:
instrument.product.dataSourceSpecForTradingTermination.id,
settlementAsset: instrument.product.settlementAsset,
productType: instrument.product.__typename || '',
productType: instrument.product.__typename,
successorMarketID: market.successorMarketID,
parentMarketID: market.parentMarketID,
};
return row;
});
return (
<div className="h-full relative">
<ClosedMarketsDataGrid rowData={rowData} error={error} />
</div>
);
return <ClosedMarketsDataGrid rowData={rowData} error={error} />;
};
const components = {
MarketCodeCell,
};
const ClosedMarketsDataGrid = ({
@@ -117,15 +115,11 @@ const ClosedMarketsDataGrid = ({
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
const colDefs = useMemo(() => {
const cols: ColDef[] = compact([
return [
{
headerName: t('Market'),
field: 'code',
cellRenderer: 'MarketNameCell',
},
{
headerName: t('Description'),
field: 'name',
cellRenderer: 'MarketCodeCell',
},
{
headerName: t('Status'),
@@ -176,12 +170,6 @@ const ClosedMarketsDataGrid = ({
},
},
},
FLAGS.SUCCESSOR_MARKETS && {
headerName: t('Successor market'),
field: 'id',
colId: 'successorMarket',
cellRenderer: 'SuccessorMarketRenderer',
},
{
headerName: t('Best bid'),
field: 'bestBidPrice',
@@ -263,12 +251,13 @@ const ClosedMarketsDataGrid = ({
<MarketActionsDropdown
marketId={data.id}
assetId={data.settlementAsset.id}
successorMarketID={data.successorMarketID}
parentMarketID={data.parentMarketID}
/>
);
},
},
]);
return cols;
];
}, [openAssetDialog]);
return (
@@ -276,8 +265,9 @@ const ClosedMarketsDataGrid = ({
rowData={rowData}
columnDefs={colDefs}
getRowId={({ data }) => data.id}
components={{ SuccessorMarketRenderer, MarketNameCell }}
overlayNoRowsTemplate={error ? error.message : t('No markets')}
components={components}
rowHeight={45}
/>
);
};
@@ -0,0 +1,82 @@
import { render, screen } from '@testing-library/react';
import { ProductTypeShortName } from '@vegaprotocol/types';
import type { MarketCodeCellProps } from './market-code-cell';
import { MarketCodeCell } from './market-code-cell';
describe('MarketCodeCell', () => {
const renderComponent = (props: MarketCodeCellProps) => {
return render(<MarketCodeCell {...props} />);
};
it('renders SCCR if the market is a successor', () => {
const productType = 'Future';
const code = 'code';
const props = {
value: 'code',
data: {
productType,
parentMarketID: 'foo',
successorMarketID: undefined,
},
} as const;
renderComponent(props);
expect(screen.getByTestId('stack-cell-primary')).toHaveTextContent(code);
expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent(
ProductTypeShortName[productType]
);
expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent(
'SCCR'
);
expect(screen.getByTestId('stack-cell-secondary')).not.toHaveTextContent(
'PRNT'
);
});
it('renders PRNT if the market is a parent', () => {
const productType = 'Future';
const code = 'code';
const props = {
value: 'code',
data: {
productType,
parentMarketID: undefined,
successorMarketID: 'foo',
},
} as const;
renderComponent(props);
expect(screen.getByTestId('stack-cell-primary')).toHaveTextContent(code);
expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent(
ProductTypeShortName[productType]
);
expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent(
'PRNT'
);
expect(screen.getByTestId('stack-cell-secondary')).not.toHaveTextContent(
'SCCR'
);
});
it('renders both SCCR and PRNT if the market is both a parent and a successor', () => {
const productType = 'Future';
const code = 'code';
const props = {
value: 'code',
data: {
productType,
parentMarketID: 'foo',
successorMarketID: 'bar',
},
} as const;
renderComponent(props);
expect(screen.getByTestId('stack-cell-primary')).toHaveTextContent(code);
expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent(
ProductTypeShortName[productType]
);
expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent(
'PRNT'
);
expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent(
'SCCR'
);
});
});
@@ -0,0 +1,51 @@
import compact from 'lodash/compact';
import type { ProductType } from '@vegaprotocol/types';
import { ProductTypeMapping, ProductTypeShortName } from '@vegaprotocol/types';
import { StackedCell } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
export interface MarketCodeCellProps {
value: string | undefined; // market code
data: {
productType: ProductType | undefined;
parentMarketID: string | null | undefined;
successorMarketID: string | null | undefined;
};
}
export const MarketCodeCell = ({ value, data }: MarketCodeCellProps) => {
if (!value || !data || !data.productType) return null;
const infoSpanClasses =
'mr-1 pr-1 uppercase border-r last:pr-0 last:mr-0 last:border-r-0 border-vega-clight-200 dark:border-vega-cdark-200';
const info = compact([
<span
className={infoSpanClasses}
key="productType"
title={ProductTypeMapping[data.productType]}
>
{ProductTypeShortName[data.productType]}
</span>,
data.parentMarketID && (
<span
className={infoSpanClasses}
key="successor"
title={t('Successor of a market')}
>
{t('SCCR')}
</span>
),
data.successorMarketID && (
<span
className={infoSpanClasses}
key="parent"
title={t('Parent of a market')}
>
{t('PRNT')}
</span>
),
]);
return <StackedCell primary={value} secondary={info} />;
};
@@ -0,0 +1,35 @@
import type { TypedDataAgGrid } from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid, PriceFlashCell } from '@vegaprotocol/datagrid';
import type { MarketMaybeWithData } from '@vegaprotocol/markets';
import { useColumnDefs } from './use-column-defs';
export const getRowId = ({ data }: { data: { id: string } }) => data.id;
const defaultColDef = {
sortable: true,
filter: true,
filterParams: { buttons: ['reset'] },
};
const components = {
PriceFlashCell,
};
type Props = TypedDataAgGrid<MarketMaybeWithData>;
export const MarketListTable = (props: Props) => {
const columnDefs = useColumnDefs();
return (
<AgGrid
getRowId={getRowId}
defaultColDef={defaultColDef}
columnDefs={columnDefs}
components={components}
rowHeight={45}
{...props}
/>
);
};
export default MarketListTable;
@@ -9,14 +9,21 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { DApp, EXPLORER_MARKET, useLinks } from '@vegaprotocol/environment';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { useNavigate } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
export const MarketActionsDropdown = ({
marketId,
assetId,
successorMarketID,
parentMarketID,
}: {
marketId: string;
assetId: string;
successorMarketID: string | null | undefined;
parentMarketID: string | null | undefined;
}) => {
const navigate = useNavigate();
const open = useAssetDetailsDialogStore((store) => store.open);
const linkCreator = useLinks(DApp.Explorer);
@@ -42,6 +49,26 @@ export const MarketActionsDropdown = ({
<VegaIcon name={VegaIconNames.INFO} size={16} />
{t('View settlement asset details')}
</TradingDropdownItem>
{parentMarketID && (
<TradingDropdownItem
onClick={() => {
navigate(Links[Routes.MARKET](parentMarketID));
}}
>
<VegaIcon name={VegaIconNames.EYE} size={16} />
{t('View parent market')}
</TradingDropdownItem>
)}
{successorMarketID && (
<TradingDropdownItem
onClick={() => {
navigate(Links[Routes.MARKET](successorMarketID));
}}
>
<VegaIcon name={VegaIconNames.EYE} size={16} />
{t('View successor market')}
</TradingDropdownItem>
)}
</ActionsDropdown>
);
};
@@ -6,7 +6,7 @@ import {
Tab,
TradingAnchorButton,
} from '@vegaprotocol/ui-toolkit';
import { Markets } from './markets';
import { OpenMarkets } from './open-markets';
import { Proposed } from './proposed';
import { usePageTitleStore } from '../../stores';
import { Closed } from './closed';
@@ -21,8 +21,8 @@ export const MarketsPage = () => {
updateTitle: store.updateTitle,
}));
const tokenLink = useLinks(DApp.Token);
const externalLink = tokenLink(TOKEN_NEW_MARKET_PROPOSAL);
const governanceLink = useLinks(DApp.Governance);
const externalLink = governanceLink(TOKEN_NEW_MARKET_PROPOSAL);
useEffect(() => {
updateTitle(titlefy(['Markets']));
@@ -33,13 +33,17 @@ export const MarketsPage = () => {
<div className="h-full my-1 border rounded-sm border-default">
<Tabs storageKey="console-markets">
<Tab id="open-markets" name={t('Open markets')}>
<Markets />
<OpenMarkets />
</Tab>
<Tab
id="proposed-markets"
name={t('Proposed markets')}
menu={
<TradingAnchorButton size="extra-small" href={externalLink}>
<TradingAnchorButton
size="extra-small"
data-testid="propose-new-market"
href={externalLink}
>
{t('Propose a new market')}
</TradingAnchorButton>
}
@@ -1,13 +0,0 @@
import { MarketsContainer } from '@vegaprotocol/markets';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { SuccessorMarketRenderer } from './successor-market-cell';
export const Markets = () => {
const handleOnSelect = useMarketClickHandler();
return (
<MarketsContainer
onSelect={handleOnSelect}
SuccessorMarketRenderer={SuccessorMarketRenderer}
/>
);
};
@@ -0,0 +1,63 @@
import { useDataProvider } from '@vegaprotocol/data-provider';
import type { MarketMaybeWithData } from '@vegaprotocol/markets';
import { marketListProvider } from '@vegaprotocol/markets';
import { useEffect } from 'react';
import { t } from '@vegaprotocol/i18n';
import type { CellClickedEvent } from 'ag-grid-community';
import MarketListTable from './market-list-table';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { Interval } from '@vegaprotocol/types';
import { useYesterday } from '@vegaprotocol/react-helpers';
const POLLING_TIME = 2000;
export const OpenMarkets = () => {
const handleOnSelect = useMarketClickHandler();
const yesterday = useYesterday();
const { data, error, reload } = useDataProvider({
dataProvider: marketListProvider,
variables: {
since: new Date(yesterday).toISOString(),
interval: Interval.INTERVAL_I1H,
},
});
useEffect(() => {
const interval = setInterval(() => {
reload();
}, POLLING_TIME);
return () => {
clearInterval(interval);
};
}, [reload]);
return (
<MarketListTable
rowData={data}
onCellClicked={({
data,
column,
event,
}: CellClickedEvent<MarketMaybeWithData>) => {
if (!data) return;
// prevent navigating to the market page if any of the below cells are clicked
// event.preventDefault or event.stopPropagation dont seem to apply for aggird
const colId = column.getColId();
if (
[
'tradableInstrument.instrument.product.settlementAsset.symbol',
'market-actions',
].includes(colId)
) {
return;
}
// @ts-ignore metaKey exists
handleOnSelect(data.id, event ? event.metaKey : false);
}}
overlayNoRowsTemplate={error ? error.message : t('No markets')}
/>
);
};
@@ -0,0 +1,46 @@
import { useEnvironment } from '@vegaprotocol/environment';
import { Icon } from '@vegaprotocol/ui-toolkit';
import type { IconName } from '@blueprintjs/icons';
import type { Market } from '@vegaprotocol/markets';
import {
getMatchingOracleProvider,
getVerifiedStatusIcon,
useOracleProofs,
} from '@vegaprotocol/markets';
export const OracleStatus = ({
dataSourceSpecForSettlementData,
dataSourceSpecForTradingTermination,
}: Pick<
Market['tradableInstrument']['instrument']['product'],
'dataSourceSpecForSettlementData' | 'dataSourceSpecForTradingTermination'
>) => {
const { ORACLE_PROOFS_URL } = useEnvironment();
const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL);
if (providers) {
const settlementDataProvider = getMatchingOracleProvider(
dataSourceSpecForSettlementData.data,
providers
);
const tradingTerminationDataProvider = getMatchingOracleProvider(
dataSourceSpecForTradingTermination.data,
providers
);
let maliciousOracleProvider = null;
if (settlementDataProvider?.oracle.status !== 'GOOD') {
maliciousOracleProvider = settlementDataProvider;
} else if (tradingTerminationDataProvider?.oracle.status !== 'GOOD') {
maliciousOracleProvider = tradingTerminationDataProvider;
}
if (!maliciousOracleProvider) return null;
const { icon } = getVerifiedStatusIcon(maliciousOracleProvider);
return <Icon size={3} name={icon as IconName} className="ml-1" />;
}
return null;
};
@@ -0,0 +1,15 @@
import { useMarketsMapProvider } from '@vegaprotocol/markets';
export const ParentMarketCell = ({
value,
}: {
value: string; // parentMarketId
}) => {
const { data, loading } = useMarketsMapProvider();
if (loading) return null;
if (!data || !data[value]) return <span>-</span>;
return <div>{data[value].tradableInstrument.instrument.code}</div>;
};
@@ -1,6 +1,10 @@
import { ProposalsList } from '@vegaprotocol/proposals';
import { SuccessorMarketRenderer } from './successor-market-cell';
import { ParentMarketCell } from './parent-market-cell';
const cellRenderers = {
ParentMarketCell,
};
export const Proposed = () => {
return <ProposalsList SuccessorMarketRenderer={SuccessorMarketRenderer} />;
return <ProposalsList cellRenderers={cellRenderers} />;
};
@@ -1,190 +0,0 @@
import { render, screen, act, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import userEvent from '@testing-library/user-event';
import type { Market } from '@vegaprotocol/markets';
import { SuccessorMarketRenderer } from './successor-market-cell';
import {
MarketsDocument,
SuccessorMarketIdsDocument,
} from '@vegaprotocol/markets';
import { createMarketFragment } from '@vegaprotocol/mock';
const mockSuccessorsQuery = [
{
id: 'market1',
parentMarketID: 'parentMarket1',
successorMarketID: 'successorMarket1',
},
{ id: 'market2', parentMarketID: 'parentMarket2' },
{ id: 'market3', successorMarketID: 'successorMarket3' },
];
const parentMarket1 = {
id: 'parentMarket1',
tradableInstrument: {
instrument: { code: 'code parent 1', id: '1' },
},
} as unknown as Market;
const successorMarket1 = {
id: 'successorMarket1',
tradableInstrument: {
instrument: { code: 'code successor 1', id: '2' },
},
} as unknown as Market;
const parentMarket2 = {
id: 'parentMarket2',
tradableInstrument: {
instrument: { code: 'code parent 2', id: '3' },
},
} as unknown as Market;
const successorMarket3 = {
id: 'successorMarket3',
tradableInstrument: {
instrument: { code: 'code successor 3', id: '4' },
},
} as unknown as Market;
const mockMarkets = [
parentMarket1,
successorMarket1,
parentMarket2,
successorMarket3,
];
const mockClickHandler = jest.fn();
jest.mock('../../lib/hooks/use-market-click-handler', () => ({
useMarketClickHandler: jest.fn().mockImplementation(() => mockClickHandler),
}));
const marketMock = {
request: {
query: MarketsDocument,
variables: undefined,
},
result: {
data: {
marketsConnection: {
edges: mockMarkets.map((item) => ({
node: {
...createMarketFragment(item),
},
})),
},
},
},
};
const successorMock = {
request: {
query: SuccessorMarketIdsDocument,
},
result: {
data: {
marketsConnection: {
edges: mockSuccessorsQuery.map((item) => ({
node: {
...item,
},
})),
},
},
},
};
const mocks = [marketMock, successorMock];
describe('SuccessorMarketRenderer', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should properly rendered successor market', async () => {
const successorValue = 'market1';
render(
<MockedProvider mocks={[...mocks]}>
<SuccessorMarketRenderer value={successorValue} />
</MockedProvider>
);
await waitFor(() => {
expect(screen.getByTestId('market-code')).toBeInTheDocument();
});
expect(screen.getByText('code successor 1')).toBeInTheDocument();
expect(screen.getByText('Futr')).toBeInTheDocument();
await userEvent.click(screen.getByRole('button'));
await waitFor(() => {
expect(mockClickHandler).toHaveBeenCalledWith('successorMarket1', false);
});
});
it('should properly rendered parent market', async () => {
const successorValue = 'market1';
render(
<MockedProvider mocks={[...mocks]}>
<SuccessorMarketRenderer value={successorValue} parent />
</MockedProvider>
);
await waitFor(() => {
expect(screen.getByTestId('market-code')).toBeInTheDocument();
});
expect(screen.getByText('code parent 1')).toBeInTheDocument();
expect(screen.getByText('Futr')).toBeInTheDocument();
await userEvent.click(screen.getByRole('button'));
await waitFor(() => {
expect(mockClickHandler).toHaveBeenCalledWith('parentMarket1', false);
});
});
it('should properly rendered only parent market', async () => {
const successorValue = 'market2';
const { rerender } = render(
<MockedProvider mocks={[...mocks]}>
<SuccessorMarketRenderer value={successorValue} parent />
</MockedProvider>
);
await waitFor(() => {
expect(screen.getByTestId('market-code')).toBeInTheDocument();
});
expect(screen.getByText('code parent 2')).toBeInTheDocument();
expect(screen.getByText('Futr')).toBeInTheDocument();
await userEvent.click(screen.getByRole('button'));
await waitFor(() => {
expect(mockClickHandler).toHaveBeenCalledWith('parentMarket2', false);
});
rerender(
<MockedProvider mocks={[...mocks]}>
<SuccessorMarketRenderer value={successorValue} />
</MockedProvider>
);
expect(screen.getByText('-')).toBeInTheDocument();
});
it('should properly rendered only successor market', async () => {
const successorValue = 'market3';
const { rerender } = render(
<MockedProvider mocks={[...mocks]}>
<SuccessorMarketRenderer value={successorValue} />
</MockedProvider>
);
await waitFor(() => {
expect(screen.getByTestId('market-code')).toBeInTheDocument();
});
expect(screen.getByText('code successor 3')).toBeInTheDocument();
expect(screen.getByText('Futr')).toBeInTheDocument();
await userEvent.click(screen.getByRole('button'));
await waitFor(() => {
expect(mockClickHandler).toHaveBeenCalledWith('successorMarket3', false);
});
await act(() => {
rerender(
<MockedProvider mocks={[...mocks]}>
<SuccessorMarketRenderer value={successorValue} parent />
</MockedProvider>
);
});
expect(screen.getByText('-')).toBeInTheDocument();
});
});
@@ -1,41 +0,0 @@
import React from 'react';
import { MarketNameCell } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketProvider, useSuccessorMarketIds } from '@vegaprotocol/markets';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
export const SuccessorMarketRenderer = ({
value,
parent,
}: {
value: string;
parent?: boolean;
}) => {
const successors = useSuccessorMarketIds(value);
const onMarketClick = useMarketClickHandler();
const lookupValue = successors
? parent
? successors.parentMarketID
: successors.successorMarketID
: '';
const { data } = useDataProvider({
dataProvider: marketProvider,
variables: {
marketId: lookupValue || '',
},
skip: !lookupValue,
});
return data ? (
<MarketNameCell
value={data.tradableInstrument.instrument.code}
data={data}
onMarketClick={onMarketClick}
productType={data.tradableInstrument.instrument?.product.__typename}
/>
) : (
'-'
);
};
@@ -0,0 +1,226 @@
import { useMemo } from 'react';
import type { ColDef, ValueFormatterParams } from 'ag-grid-community';
import { t } from '@vegaprotocol/i18n';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
VegaValueGetterParams,
} from '@vegaprotocol/datagrid';
import { COL_DEFS, SetFilter } from '@vegaprotocol/datagrid';
import * as Schema from '@vegaprotocol/types';
import { addDecimalsFormatNumber, toBigNum } from '@vegaprotocol/utils';
import { ButtonLink, Tooltip } from '@vegaprotocol/ui-toolkit';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import type {
MarketMaybeWithData,
MarketMaybeWithDataAndCandles,
} from '@vegaprotocol/markets';
import { MarketActionsDropdown } from './market-table-actions';
import { calcCandleVolume } from '@vegaprotocol/markets';
import { MarketCodeCell } from './market-code-cell';
const { MarketTradingMode, AuctionTrigger } = Schema;
export const useColumnDefs = () => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
return useMemo<ColDef[]>(
() => [
{
headerName: t('Market'),
field: 'tradableInstrument.instrument.code',
flex: 2,
cellRenderer: ({
value,
data,
}: VegaICellRendererParams<
MarketMaybeWithData,
'tradableInstrument.instrument.code'
>) => (
<MarketCodeCell
value={value}
data={{
productType:
data?.tradableInstrument.instrument.product.__typename,
successorMarketID: data?.successorMarketID,
parentMarketID: data?.parentMarketID,
}}
/>
),
},
{
headerName: t('Description'),
field: 'tradableInstrument.instrument.name',
flex: 2,
},
{
headerName: t('Trading mode'),
field: 'tradingMode',
cellRenderer: ({
data,
}: VegaICellRendererParams<MarketMaybeWithData, 'data'>) => {
if (!data?.data) return '-';
const { trigger, marketTradingMode } = data.data;
const withTriggerInfo =
marketTradingMode ===
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
trigger &&
trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED;
if (withTriggerInfo) {
return (
<Tooltip
description={`${Schema.MarketTradingModeMapping[marketTradingMode]}
- ${Schema.AuctionTriggerMapping[trigger]}`}
>
<span>
{Schema.MarketTradingModeMapping[marketTradingMode]}
</span>
</Tooltip>
);
}
return Schema.MarketTradingModeMapping[marketTradingMode];
},
filter: SetFilter,
filterParams: {
set: Schema.MarketTradingModeMapping,
},
},
{
headerName: t('Status'),
field: 'state',
valueFormatter: ({
data,
}: VegaValueFormatterParams<MarketMaybeWithData, 'state'>) => {
return data?.state ? Schema.MarketStateMapping[data.state] : '-';
},
filter: SetFilter,
filterParams: {
set: Schema.MarketStateMapping,
},
},
{
headerName: t('Mark price'),
field: 'data.markPrice',
type: 'rightAligned',
cellRenderer: 'PriceFlashCell',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<MarketMaybeWithData>) => {
return data?.data?.markPrice === undefined
? undefined
: toBigNum(data?.data?.markPrice, data.decimalPlaces).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<MarketMaybeWithData, 'data.markPrice'>) =>
data?.data?.bestOfferPrice === undefined
? '-'
: addDecimalsFormatNumber(data.data.markPrice, data.decimalPlaces),
},
{
headerName: t('24h volume'),
type: 'rightAligned',
field: 'data.candles',
valueGetter: ({
data,
}: VegaValueGetterParams<MarketMaybeWithDataAndCandles>) => {
if (!data) return 0;
const candles = data?.candles;
const vol = candles ? calcCandleVolume(candles) : '0';
return Number(vol);
},
valueFormatter: ({
data,
}: ValueFormatterParams<MarketMaybeWithDataAndCandles, 'candles'>) => {
const candles = data?.candles;
const vol = candles ? calcCandleVolume(candles) : '0';
const volume =
data && vol && vol !== '0'
? addDecimalsFormatNumber(vol, data.positionDecimalPlaces)
: '0.00';
return volume;
},
},
{
headerName: t('Settlement asset'),
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
cellRenderer: ({
data,
}: VegaICellRendererParams<
MarketMaybeWithData,
'tradableInstrument.instrument.product.settlementAsset.symbol'
>) => {
const value =
data?.tradableInstrument.instrument.product.settlementAsset;
return value ? (
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value.id, e.target as HTMLElement);
}}
>
{value.symbol}
</ButtonLink>
) : (
''
);
},
},
{
headerName: t('Spread'),
field: 'data.bestBidPrice',
type: 'rightAligned',
filter: 'agNumberColumnFilter',
cellRenderer: 'PriceFlashCell',
valueGetter: ({ data }: VegaValueGetterParams<MarketMaybeWithData>) => {
if (!data || !data.data?.bestOfferPrice || !data.data?.bestBidPrice) {
return undefined;
}
const offer = toBigNum(data.data.bestOfferPrice, data.decimalPlaces);
const bid = toBigNum(data.data.bestBidPrice, data.decimalPlaces);
const spread = offer.minus(bid).toNumber();
// The calculation above can result in '-0' being rendered after formatting
// so return Math.abs to remove it and just render '0'
if (spread === 0) {
return Math.abs(spread);
}
return spread;
},
valueFormatter: ({
value,
}: VegaValueFormatterParams<
MarketMaybeWithData,
'data.bestBidPrice'
>) => {
if (!value) return '-';
return value.toString();
},
},
{
colId: 'market-actions',
field: 'id',
...COL_DEFS.actions,
cellRenderer: ({
data,
}: VegaICellRendererParams<MarketMaybeWithData>) => {
if (!data) return null;
return (
<MarketActionsDropdown
marketId={data.id}
assetId={
data.tradableInstrument.instrument.product.settlementAsset.id
}
successorMarketID={data.successorMarketID}
parentMarketID={data.parentMarketID}
/>
);
},
},
],
[openAssetDetailsDialog]
);
};
-1
View File
@@ -1,2 +1 @@
export const THROTTLE_UPDATE_TIME = 500;
export const ONBOARDING_VIEWED_KEY = 'vega_onboarding_viewed';
@@ -29,7 +29,7 @@ export const MarketSuccessorProposalBanner = ({
?.successorConfiguration?.parentMarketId === marketId
) ?? [];
const [visible, setVisible] = useState(true);
const tokenLink = useLinks(DApp.Token);
const tokenLink = useLinks(DApp.Governance);
if (visible && successors.length) {
return (
<NotificationBanner
@@ -10,7 +10,7 @@ import type { SortType } from './sort-dropdown';
import { SortTypeMapping } from './sort-dropdown';
import { Sort } from './sort-dropdown';
import { subDays } from 'date-fns';
import { isMarketActive } from './use-market-selector-list';
import { isMarketActive } from '../../lib/utils';
jest.mock('@vegaprotocol/markets');
const mockUseMarketList = useMarketList as jest.Mock;
@@ -1,15 +1,13 @@
import merge from 'lodash/merge';
import { renderHook } from '@testing-library/react';
import {
isMarketActive,
useMarketSelectorList,
} from './use-market-selector-list';
import { Product } from '../../components/market-selector/product-selector';
import { useMarketSelectorList } from './use-market-selector-list';
import { isMarketActive } from '../../lib/utils';
import { Product } from './product-selector';
import { Sort } from './sort-dropdown';
import { createMarketFragment } from '@vegaprotocol/mock';
import { MarketState } from '@vegaprotocol/types';
import { useMarketList } from '@vegaprotocol/markets';
import type { Filter } from '../../components/market-selector';
import type { Filter } from './market-selector';
import { subDays } from 'date-fns';
jest.mock('@vegaprotocol/markets', () => ({
@@ -1,18 +1,11 @@
import { useMemo } from 'react';
import orderBy from 'lodash/orderBy';
import { MarketState } from '@vegaprotocol/types';
import { calcTradedFactor, useMarketList } from '@vegaprotocol/markets';
import { priceChangePercentage } from '@vegaprotocol/utils';
import type { Filter } from '../../components/market-selector/market-selector';
import { Sort } from './sort-dropdown';
import { Product } from './product-selector';
// Used for sort order and filter
const MARKET_TEMPLATE = [
MarketState.STATE_ACTIVE,
MarketState.STATE_SUSPENDED,
MarketState.STATE_PENDING,
];
import { isMarketActive } from '../../lib/utils';
export const useMarketSelectorList = ({
product,
@@ -87,7 +80,3 @@ export const useMarketSelectorList = ({
return { markets, data, loading, error, reload };
};
export const isMarketActive = (state: MarketState) => {
return MARKET_TEMPLATE.includes(state);
};
@@ -57,6 +57,7 @@ describe('Navbar', () => {
['/markets/all', 'Markets'],
[`/markets/${marketId}`, 'Trading'],
['/portfolio', 'Portfolio'],
[expect.stringContaining('governance'), 'Governance'],
];
const links = screen.getAllByRole('link');
@@ -88,6 +89,7 @@ describe('Navbar', () => {
['/markets/all', 'Markets'],
[`/markets/${marketId}`, 'Trading'],
['/portfolio', 'Portfolio'],
[expect.stringContaining('governance'), 'Governance'],
];
const links = menu.getAllByRole('link');
links.forEach((link, i) => {
+20 -9
View File
@@ -1,6 +1,12 @@
import type { ButtonHTMLAttributes, LiHTMLAttributes, ReactNode } from 'react';
import { useState } from 'react';
import { useEnvironment, DocsLinks, Networks } from '@vegaprotocol/environment';
import {
useEnvironment,
DocsLinks,
Networks,
DApp,
useLinks,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { useGlobalStore } from '../../stores';
import { VegaWalletConnectButton } from '../vega-wallet-connect-button';
@@ -59,14 +65,14 @@ export const Navbar = ({
<VLogo className="w-4" />
</NavLink>
{/* Left section */}
<div className="lg:hidden flex items-center">{children}</div>
<div className="flex items-center lg:hidden">{children}</div>
{/* Used to show header in nav on mobile */}
<div className="hidden lg:block">
<NavbarMenu onClick={() => setMenu(null)} />
</div>
{/* Right section */}
<div className="ml-auto flex justify-end items-center gap-2">
<div className="flex items-center justify-end ml-auto gap-2">
<ProtocolUpgradeCountdown />
<NavbarMobileButton
onClick={() => {
@@ -100,7 +106,7 @@ export const Navbar = ({
onOpenChange={(open) => setMenu((x) => (open ? x : null))}
>
<D.Overlay
className="lg:hidden fixed inset-0 dark:bg-black/80 bg-black/50 z-20"
className="fixed inset-0 z-20 lg:hidden dark:bg-black/80 bg-black/50"
data-testid="navbar-menu-overlay"
/>
<D.Content
@@ -111,7 +117,7 @@ export const Navbar = ({
)}
data-testid="navbar-menu-content"
>
<div className="flex justify-end items-center h-10 p-1">
<div className="flex items-center justify-end h-10 p-1">
<NavbarMobileButton onClick={() => setMenu(null)}>
<span className="sr-only">{t('Close menu')}</span>
<VegaIcon name={VegaIconNames.CROSS} size={24} />
@@ -179,6 +185,11 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
{t('Portfolio')}
</NavbarLink>
</NavbarItem>
<NavbarItem>
<NavbarLinkExternal to={useLinks(DApp.Governance)()}>
{t('Governance')}
</NavbarLinkExternal>
</NavbarItem>
<NavbarItem>
<NavbarTrigger>{t('Resources')}</NavbarTrigger>
<NavbarContent data-testid="navbar-content-resources">
@@ -336,7 +347,7 @@ const NavbarLinkExternal = ({
<NavLink
to={to}
className={classNames(
'flex lg:inline-flex gap-2 justify-between items-center relative',
'flex gap-2 lg:h-full items-center',
'px-6 py-2 lg:p-0 text-lg lg:text-sm',
'hover:text-vega-clight-100 dark:hover:text-vega-cdark-100'
)}
@@ -344,7 +355,7 @@ const NavbarLinkExternal = ({
target="_blank"
>
<span>{children}</span>
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={14} />
</NavLink>
</N.Link>
);
@@ -364,8 +375,8 @@ const BurgerIcon = () => (
const NavbarListDivider = () => {
return (
<div className="py-2 px-6 lg:px-0" role="separator">
<div className="h-px lg:h-full w-full lg:w-px bg-vega-clight-500 dark:bg-vega-cdark-500" />
<div className="px-6 py-2 lg:px-0" role="separator">
<div className="w-full h-px lg:h-full lg:w-px bg-vega-clight-500 dark:bg-vega-cdark-500" />
</div>
);
};
@@ -0,0 +1 @@
export { Telemetry } from './telemetry';
@@ -0,0 +1,69 @@
import type { Toast } from '@vegaprotocol/ui-toolkit';
import { Intent, useToasts } from '@vegaprotocol/ui-toolkit';
import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
import { useCallback, useEffect } from 'react';
import { TelemetryApproval } from './telemetry-approval';
import { t } from '@vegaprotocol/i18n';
import { useOnboardingStore } from '../welcome-dialog/use-get-onboarding-step';
const TELEMETRY_APPROVAL_TOAST_ID = 'telemetry_tost_id';
export const Telemetry = () => {
const onboardingDissmissed = useOnboardingStore((store) => store.dismissed);
const [telemetryValue, setTelemetryValue, isTelemetryNeeded, closeTelemetry] =
useTelemetryApproval();
const [setToast, hasToast, removeToast] = useToasts((store) => [
store.setToast,
store.hasToast,
store.remove,
]);
const onApprovalClose = useCallback(() => {
closeTelemetry();
removeToast(TELEMETRY_APPROVAL_TOAST_ID);
}, [closeTelemetry, removeToast]);
const setTelemetryApprovalAndClose = useCallback(
(value: string) => {
setTelemetryValue(value);
onApprovalClose();
},
[onApprovalClose, setTelemetryValue]
);
useEffect(() => {
if (isTelemetryNeeded && onboardingDissmissed) {
const toast: Toast = {
id: TELEMETRY_APPROVAL_TOAST_ID,
intent: Intent.Primary,
content: (
<>
<h3 className="mb-1 text-sm uppercase">
{t('Improve vega console')}
</h3>
<TelemetryApproval
telemetryValue={telemetryValue}
setTelemetryValue={setTelemetryApprovalAndClose}
/>
</>
),
onClose: onApprovalClose,
};
if (!hasToast(TELEMETRY_APPROVAL_TOAST_ID)) {
setToast(toast);
}
return;
}
}, [
telemetryValue,
isTelemetryNeeded,
onboardingDissmissed,
setToast,
hasToast,
onApprovalClose,
setTelemetryApprovalAndClose,
]);
return null;
};
@@ -2,7 +2,8 @@ import { MemoryRouter } from 'react-router-dom';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { GetStarted } from './get-started';
import { render, screen } from '@testing-library/react';
import { render, screen, fireEvent } from '@testing-library/react';
import { useOnboardingStore } from './use-get-onboarding-step';
let mockStep = 1;
jest.mock('./use-get-onboarding-step', () => ({
@@ -22,7 +23,7 @@ describe('GetStarted', () => {
};
const checkTicks = (elements: Element[]) => {
elements.forEach((item, i) => {
if (i + 1 < mockStep) {
if (i + 1 < mockStep - 1) {
expect(item.querySelector('[data-testid="icon-tick"]')).toBeTruthy();
}
});
@@ -44,32 +45,24 @@ describe('GetStarted', () => {
globalThis.window.vega = undefined as unknown as Vega;
});
it('renders nothing if connected', () => {
it('renders nothing if dismissed', () => {
useOnboardingStore.setState({ dismissed: true });
mockStep = 0;
const { container } = renderComponent({ pubKey: 'my-pubkey' });
expect(container).toBeEmptyDOMElement();
});
it('steps should be ticked', () => {
useOnboardingStore.setState({ dismissed: false });
const navigatorGetter: jest.SpyInstance = jest.spyOn(
window.navigator,
'userAgent',
'get'
);
navigatorGetter.mockReturnValue('Chrome');
mockStep = 1;
const { rerender, container } = renderComponent();
expect(screen.queryByTestId('icon-tick')).not.toBeInTheDocument();
expect(screen.getByTestId('get-wallet-button')).toBeInTheDocument();
mockStep = 2;
rerender(
<MemoryRouter>
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
<GetStarted />
</VegaWalletContext.Provider>
</MemoryRouter>
);
const { rerender, container } = renderComponent();
checkTicks(screen.getAllByRole('listitem'));
expect(screen.getByRole('button', { name: 'Connect' })).toBeInTheDocument();
@@ -93,7 +86,11 @@ describe('GetStarted', () => {
</MemoryRouter>
);
checkTicks(screen.getAllByRole('listitem'));
expect(screen.getByRole('button', { name: 'Dismiss' })).toBeInTheDocument();
expect(
screen.getByRole('button', { name: 'Ready to trade' })
).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Ready to trade' }));
mockStep = 5;
rerender(
@@ -7,36 +7,26 @@ import {
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import {
GetWalletButton,
useVegaWallet,
useVegaWalletDialogStore,
} from '@vegaprotocol/wallet';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
import { useNavigate } from 'react-router-dom';
import {
OnboardingStep,
useGetOnboardingStep,
useOnboardingStore,
} from './use-get-onboarding-step';
import { Links, Routes } from '../../pages/client-router';
import { useGlobalStore } from '../../stores';
import { useSidebar, ViewType } from '../sidebar';
import * as constants from '../constants';
import { useOnboardingStore } from './welcome-dialog';
interface Props {
lead?: string;
}
const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
const { CHROME_EXTENSION_URL, MOZILLA_EXTENSION_URL } = useEnvironment();
const navigate = useNavigate();
const [, setOnboardingViewed] = useLocalStorage(
constants.ONBOARDING_VIEWED_KEY
);
const dismiss = useOnboardingStore((store) => store.dismiss);
const setDialogOpen = useOnboardingStore((store) => store.setDialogOpen);
const marketId = useGlobalStore((store) => store.marketId);
const link = marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]();
const openVegaWalletDialog = useVegaWalletDialogStore(
@@ -47,29 +37,21 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
let onClickHandle = () => {
openVegaWalletDialog();
};
if (step === OnboardingStep.ONBOARDING_WALLET_STEP) {
return (
<GetWalletButton
className="justify-between"
chromeExtensionUrl={CHROME_EXTENSION_URL}
mozillaExtensionUrl={MOZILLA_EXTENSION_URL}
/>
);
} else if (step === OnboardingStep.ONBOARDING_CONNECT_STEP) {
if (step <= OnboardingStep.ONBOARDING_CONNECT_STEP) {
buttonText = t('Connect');
} else if (step === OnboardingStep.ONBOARDING_DEPOSIT_STEP) {
buttonText = t('Deposit');
onClickHandle = () => {
navigate(link);
setView({ type: ViewType.Deposit });
dismiss();
setDialogOpen(false);
};
} else if (step === OnboardingStep.ONBOARDING_ORDER_STEP) {
buttonText = t('Dismiss');
} else if (step >= OnboardingStep.ONBOARDING_ORDER_STEP) {
buttonText = t('Ready to trade');
onClickHandle = () => {
navigate(link);
setView({ type: ViewType.Order });
setOnboardingViewed('true');
dismiss();
};
}
@@ -88,17 +70,12 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
export const GetStarted = ({ lead }: Props) => {
const { pubKey } = useVegaWallet();
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
const currentStep = useGetOnboardingStep();
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
const getStartedNeeded =
onBoardingViewed !== 'true' &&
currentStep &&
currentStep < OnboardingStep.ONBOARDING_COMPLETE_STEP;
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
const currentStep = useGetOnboardingStep();
const dismissed = useOnboardingStore((store) => store.dismissed);
const wrapperClasses = classNames(
'flex flex-col py-4 px-6 gap-4 rounded',
@@ -107,7 +84,7 @@ export const GetStarted = ({ lead }: Props) => {
{ 'mt-8': !lead }
);
if (getStartedNeeded) {
if (!dismissed) {
return (
<div className={wrapperClasses} data-testid="get-started-banner">
{lead && <h2>{lead}</h2>}
@@ -116,23 +93,18 @@ export const GetStarted = ({ lead }: Props) => {
<ul className="list-none">
<Step
step={1}
text={t('Get a Vega wallet')}
complete={currentStep > OnboardingStep.ONBOARDING_WALLET_STEP}
/>
<Step
step={2}
text={t('Connect')}
complete={Boolean(
currentStep > OnboardingStep.ONBOARDING_CONNECT_STEP || pubKey
)}
/>
<Step
step={3}
step={2}
text={t('Deposit funds')}
complete={currentStep > OnboardingStep.ONBOARDING_DEPOSIT_STEP}
/>
<Step
step={4}
step={3}
text={t('Open a position')}
complete={currentStep > OnboardingStep.ONBOARDING_ORDER_STEP}
/>
@@ -41,7 +41,7 @@ export const ProposedMarkets = () => {
proposal.terms.change.instrument.code,
}));
const tokenLink = useLinks(DApp.Token);
const tokenLink = useLinks(DApp.Governance);
return useMemo(
() => (
<div className="mt-7 pt-8 border-t border-default">
@@ -7,8 +7,6 @@ import {
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { depositsProvider } from '@vegaprotocol/deposits';
import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
import { ordersWithMarketProvider } from '@vegaprotocol/orders';
import { positionsDataProvider } from '@vegaprotocol/positions';
@@ -23,7 +21,7 @@ let mockContext: Partial<VegaWalletContextShape> = { pubKey: 'test-pubkey' };
describe('useGetOnboardingStep', () => {
beforeEach(() => {
jest.clearAllMocks();
mockData = [{ id: 'item-id' }];
mockData = [];
mockContext = { pubKey: 'test-pubkey' };
globalThis.window.vega = {} as Vega;
});
@@ -42,13 +40,6 @@ describe('useGetOnboardingStep', () => {
expect(result.current).toEqual(OnboardingStep.ONBOARDING_UNKNOWN_STEP);
});
it('should return properly ONBOARDING_WALLET_STEP', () => {
// @ts-ignore test only purpose
globalThis.window.vega = undefined;
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
expect(result.current).toEqual(OnboardingStep.ONBOARDING_WALLET_STEP);
});
it('should return properly ONBOARDING_CONNECT_STEP', () => {
mockContext = { pubKey: null };
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
@@ -56,15 +47,6 @@ describe('useGetOnboardingStep', () => {
});
it('should return properly ONBOARDING_DEPOSIT_STEP', async () => {
(useDataProvider as jest.Mock).mockImplementation((args) => {
if (
args.dataProvider === depositsProvider ||
args.dataProvider === aggregatedAccountsDataProvider
) {
return { data: [] };
}
return { data: mockData };
});
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
await expect(result.current).toEqual(
OnboardingStep.ONBOARDING_DEPOSIT_STEP
@@ -72,6 +54,7 @@ describe('useGetOnboardingStep', () => {
});
it('should return properly ONBOARDING_ORDER_STEP', async () => {
mockData = [{ id: 'item-id' }];
(useDataProvider as jest.Mock).mockImplementation((args) => {
if (
args.dataProvider === ordersWithMarketProvider ||
@@ -86,6 +69,7 @@ describe('useGetOnboardingStep', () => {
});
it('should return properly ONBOARDING_COMPLETE_STEP', async () => {
mockData = [{ id: 'item-id' }];
(useDataProvider as jest.Mock).mockImplementation(() => {
return { data: mockData };
});

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