Compare commits

..
Author SHA1 Message Date
Radosław Szpiech 5b42a6ad35 chore: update market.liquidity.stakeToCcyVolume in genesis file (#2545) 2023-01-06 15:21:35 +01:00
mattrussell36 870ae13b4c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-06 12:07:43 +00:00
mattrussell36 4c32a21f28 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-06 06:08:04 +00:00
mattrussell36 2ea603e59e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-06 00:10:00 +00:00
mattrussell36 3e485e3841 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-05 18:06:39 +00:00
daro-maj 7766b3f118 chore: update token e2e readme (#2504)
* chore: update readme for token-e2e tests setup

* chore: formatting fix

* chore: fix style

* chore: update readme for explorer-e2e tests

* chore: rework
2023-01-05 18:00:29 +01:00
Ciaran McGhie 8db3b01284 feat: add market close datetime to lp dashboard (#2529)
* feat: add proposal data from governance to market liquidity provider

We need data from this API to be able to tell when a market will close,
and when opening auctions will end.

* feat: add closing time to lp-dashboard market list page

Adds a 'closing time' column to the market list page on the dashboard.
2023-01-05 15:28:30 +00:00
Bartłomiej Głownia 4fbd2e5eba feat(#2323): don't allow cancel all if there are no cancellable orders (#2472) 2023-01-05 13:11:58 +00:00
Edd 53d3d5ac1c feat(explorer): improve empty states and loading indicators (#2496)
* feat(explorer): initial empty list component on block txs list

* feat(explorer): messages formatting

* chore(explorer): update generated types

* feat(explorer): empty state for markets and assets and governance and blocks and txs

* feat(explorer): use loader rather than the word loading

* feat(explorer): use loader rather than the word loading in more places

* feat(explorer): empty state appears below headers in more pages

* feat(explorer): txs per block update

* feat(explorer): update tests to match new messages

* test(explorer): add test for loading and empty states for assets

* test(explorer): add test for loading and empty states for more routes

* test(explorer): change loading detector
2023-01-05 13:10:42 +00:00
Matthew RussellandMadalina Raicu 35de756120 fix(#2515): use accounts nested under LP provision (#2523)
* fix: use accounts nested under lp provision

* fix: add getLiquidityProvision test

Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
2023-01-05 13:09:45 +00:00
mattrussell36 f89cf31982 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-05 12:06:22 +00:00
mattrussell36 8976a40dc8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-05 06:07:37 +00:00
mattrussell36 7669aeb218 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-05 00:10:03 +00:00
mattrussell36 6e996221b2 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-04 18:08:45 +00:00
botond 117c0c1306 fix: wallet connect save url to config (#2518) 2023-01-04 16:34:35 +00:00
daro-majandm.ray e56d7bc2d5 chore: cover ac 7003-MORD-016 by e2e test (#2514)
* chore: cover ac 7003-MORD-016 by e2e test

* chore: formatting fix

* Update apps/trading-e2e/src/integration/trading-orders.cy.ts

Co-authored-by: m.ray <16125548+MadalinaRaicu@users.noreply.github.com>

Co-authored-by: m.ray <16125548+MadalinaRaicu@users.noreply.github.com>
2023-01-04 16:04:28 +01:00
Dexter 85ec66a9e2 chore: update mirror URL 2023-01-04 14:31:28 +00:00
macqbat ba14ead8fc feat(1784): maintenance page (#2505)
* feat: maintance page

* feat: maintenance page - add styling and animations

* feat: maintenance page - add skip of node inits

* feat: maintenance page - add unit test

* feat: maintenance page - add unit test
2023-01-04 13:45:50 +01:00
Art 1211d27011 fix: error message consitency (2486) (#2507)
fix: error message consitency (2486)
2023-01-04 13:25:11 +01:00
mattrussell36 904a1b7805 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-04 12:08:46 +00:00
Dexter Edwards 66a8c659b4 chore: sentry dsn (#2499)
* chore: update readme to be correct

* chore: add sentry DSN to env files

* chore: use correct dsn
2023-01-04 09:16:37 +00:00
mattrussell36 f1a437500e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-04 06:08:37 +00:00
mattrussell36 29ced0b460 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-04 00:10:49 +00:00
Art e45265ac71 chore: chain id to name (714) (#2503) 2023-01-03 14:54:54 -08:00
Art cc3c339cc3 fix: asset dialog overflow (2488) (#2497) 2023-01-03 10:37:23 -08:00
mattrussell36 6f95989569 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-03 18:07:04 +00:00
Radosław Szpiech 14f0c5a5fb chore: change connect vega wallet to set vega wallet config in local storage (#2502) 2023-01-03 14:56:33 +01:00
mattrussell36 0167a42990 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-03 12:06:25 +00:00
Joe Tsang 1d65c4791c chore: skip flow test (#2500) 2023-01-03 11:32:39 +00:00
mattrussell36 8a1fe6e5b1 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-03 06:08:12 +00:00
mattrussell36 437f7ef807 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-03 00:12:00 +00:00
mattrussell36 2b3683d4a4 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-02 18:07:24 +00:00
macqbat 6c84153cdb chore: redirect to last visited market instead first from the list (#2489)
* chore: redirect to last visited market instead first from the list

* chore: redirect to last visited market instead first from the list - add int tests

* feat: redirect to last visited market instead first from the list - refactor solution

* feat: redirect to last visited market instead first from the list - fix failing int test

* feat: redirect to last visited market instead first from the list - fix failing int test

* feat: redirect to last visited market instead first from the list - fix failing int test

* feat: redirect to last visited market instead first from the list - use immer in globalStore

* chore: redirect to last visited market - improve use of zustand
2023-01-02 17:01:06 +01:00
mattrussell36 ccce5a2848 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-02 12:08:44 +00:00
mattrussell36 5673620fbd chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-02 06:08:34 +00:00
mattrussell36 41d6940b89 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-02 00:12:58 +00:00
mattrussell36 84150bec8c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-01 18:08:33 +00:00
mattrussell36 5a311d14c9 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-01 12:08:31 +00:00
mattrussell36 0f152a4b30 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-01 06:05:51 +00:00
mattrussell36 45d8b537a3 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-01 00:14:06 +00:00
mattrussell36 dc41be5715 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-31 18:08:31 +00:00
mattrussell36 a85cf66419 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-31 12:06:36 +00:00
mattrussell36 6e82e917ac chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-31 06:06:27 +00:00
mattrussell36 8dfdccd4aa chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-31 00:09:38 +00:00
Matthew Russell 82833cc06a chore: fix and improve types for market proposal (#2493)
* chore: fix and improve types for market proposal

* chore: remove unused type definition
2022-12-30 10:05:48 -08:00
botondandMatthew Russell aac3799b14 chore: add wallet client (#2462)
* chore: migrate to wallet client

* fix: bump client with new esm release version

* fix: add new no client error

* fix: bump wallet client to 0.1.2 and remove empty params from method calls

* fix: bump wallet client

* fix: format

* fix: reset client on url change

* fix: trading-deal-ticket tests after wallet client added

* fix: amend and cancel order tests, global connect tests

* chore: fix typescript error

* fix: connect wallet before mobile view test for console-lite

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2022-12-30 10:05:27 -08:00
mattrussell36 0d1c9ff0f1 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-30 12:07:23 +00:00
Edd f8eec8a056 fix(explorer): fix resizing callout (#2492)
* fix(explorer): prevent layout thrashing on stats page
2022-12-30 11:40:47 +00:00
Edd ee6f26d473 feat(explorer): use block height filter 2022-12-30 09:53:07 +00:00
mattrussell36 de62d2d972 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-30 06:08:01 +00:00
Matthew Russell 955e233c46 fix: dark class being removed on token due to theme switcher changes (#2484)
* fix: dark class being removed on token due to theme switcher changes

* chore: check for window before reading from document classlist
2022-12-29 21:01:37 -08:00
Matthew RussellandEdd 0bafa4c7d5 chore: add readmes for token and explorer e2e (#2485)
* chore: add readmes for token and explorer e2e

* fix: typo fix in explorer readme

* fix: typo fix in token readme

Co-authored-by: Edd <edd@vega.xyz>
2022-12-29 20:48:04 -08:00
mattrussell36 045529dc38 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-30 00:11:08 +00:00
mattrussell36 5ca3e4d90f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-29 18:08:27 +00:00
mattrussell36 9048d41f72 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-29 12:08:48 +00:00
mattrussell36 d731c4797c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-29 06:08:36 +00:00
mattrussell36 4a931ca134 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-29 00:11:03 +00:00
mattrussell36 3398b15d8a chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-28 18:08:02 +00:00
m.rayandMatthew Russell 1e98ecbd21 feat(#2367): liquidity supplied info in market header (#2481)
* feat(#2367): market liquidity supplied in trade market header

* feat(#2467): calculate LP status

* fix: add LP view link

* fix: add LP view link

* feat(#2367): show liquidity supplied percentage

* feat(#2367): show liquidity supplied percentage

* fix: liquidity-utils test needs big number

* feat(#2456): liquidity status marker

* feat(#2456): liquidity indicator

* feat(#2367): update props lp

* fix: use market data directly

* feat(#2367): move data grid in react-helpers

* feat(#2367): move data grid in react-helpers

* fix: indicator commented

* chore: remove unnecessary styles

* test: update test name

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2022-12-28 09:37:19 -08:00
daro-maj a9c7c2bb46 chore: cover7003 mord e2e tests (#2487)
* chore:  tests will be added after the proper implementation is prepared

* chore: formatting fix
2022-12-28 15:18:06 +01:00
mattrussell36 ba2c71de67 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-28 12:06:41 +00:00
mattrussell36 15d2aed343 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-28 06:08:30 +00:00
mattrussell36 0284eaf9a2 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-28 00:12:18 +00:00
126 changed files with 3229 additions and 1331 deletions
@@ -4,8 +4,8 @@ const marketName = 'ACTIVE MARKET';
describe('market selector', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.mockConsole();
cy.setVegaWallet();
cy.visit(`/trading/${marketId}`);
cy.connectVegaWallet();
cy.wait('@Markets');
});
@@ -67,6 +67,7 @@ describe('market selector', { tags: '@smoke' }, () => {
it('mobile view', () => {
cy.viewport('iphone-xr');
cy.visit(`/trading/${marketId}`);
cy.connectVegaWallet();
cy.get('[role="dialog"]').should('not.exist');
cy.getByTestId('arrow-button').click();
cy.get('[role="dialog"]').should('be.visible');
@@ -51,8 +51,8 @@ describe('Market trade', { tags: '@regression' }, () => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Market', marketQuery(marketOverride));
});
cy.setVegaWallet();
cy.visit(`/trading/${marketId}`);
cy.connectVegaWallet();
cy.wait('@Market');
});
@@ -34,11 +34,11 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Accounts', accountsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery());
});
cy.setVegaWallet();
});
it('certain tabs should exist', () => {
cy.visit('/portfolio');
cy.connectVegaWallet();
cy.getByTestId('assets').click();
cy.location('pathname').should('eq', '/portfolio/assets');
@@ -68,17 +68,13 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Accounts', accountsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery());
});
cy.setVegaWallet();
cy.visit('/portfolio/assets');
cy.connectVegaWallet();
});
it('data should be properly rendered', () => {
cy.get('.ag-center-cols-container .ag-row').should('have.length', 5);
cy.get(
'.ag-center-cols-container [row-id="ACCOUNT_TYPE_GENERAL-asset-id-null"]'
)
.find('button')
.click();
cy.get('[title="tEURO"] button').click();
cy.getByTestId('dialog-title').should(
'have.text',
'Asset details - tEURO'
@@ -99,8 +95,8 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Accounts', accountsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery());
});
cy.setVegaWallet();
cy.visit('/portfolio/positions');
cy.connectVegaWallet();
});
it('data should be properly rendered', () => {
@@ -116,8 +112,8 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Orders', ordersQuery());
aliasGQLQuery(req, 'Markets', marketsQuery());
});
cy.setVegaWallet();
cy.visit('/portfolio/orders');
cy.connectVegaWallet();
});
it('data should be properly rendered', () => {
@@ -134,8 +130,8 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Markets', marketsQuery());
aliasGQLQuery(req, 'Fills', fillsQuery());
});
cy.setVegaWallet();
cy.visit('/portfolio/fills');
cy.connectVegaWallet();
});
it('data should be properly rendered', () => {
@@ -161,8 +157,8 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Margins', marginsQuery());
aliasGQLQuery(req, 'MarketsData', marketsDataQuery());
});
cy.setVegaWallet();
cy.visit('/portfolio');
cy.connectVegaWallet();
});
it('"No data to display" should be always displayed', () => {
@@ -8,7 +8,6 @@ import {
useOrderMargin,
useMaximumPositionSize,
useCalculateSlippage,
validateAmount,
} from '@vegaprotocol/deal-ticket';
import { InputError } from '@vegaprotocol/ui-toolkit';
import { BigNumber } from 'bignumber.js';
@@ -23,6 +22,7 @@ import {
addDecimalsFormatNumber,
addDecimal,
formatNumber,
validateAmount,
} from '@vegaprotocol/react-helpers';
import {
useOrderSubmit,
@@ -1,13 +1,12 @@
import type { ReactNode } from 'react';
import type { FieldErrors } from 'react-hook-form';
import { useMemo } from 'react';
import { t, toDecimal } from '@vegaprotocol/react-helpers';
import { DataGrid, t, toDecimal } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
import * as Schema from '@vegaprotocol/types';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import {
MarketDataGrid,
compileGridData,
MarginWarning,
isMarketInAuction,
@@ -216,9 +215,7 @@ export const useOrderValidation = ({
<span>
{t('This market is in auction until it reaches')}{' '}
<Tooltip
description={
<MarketDataGrid grid={compileGridData(market)} />
}
description={<DataGrid grid={compileGridData(market)} />}
>
<span>{t('sufficient liquidity')}</span>
</Tooltip>
@@ -240,9 +237,7 @@ export const useOrderValidation = ({
<span>
{t('This market is in auction due to')}{' '}
<Tooltip
description={
<MarketDataGrid grid={compileGridData(market)} />
}
description={<DataGrid grid={compileGridData(market)} />}
>
<span>{t('high price volatility')}</span>
</Tooltip>
@@ -281,9 +276,7 @@ export const useOrderValidation = ({
<span>
{t('This market is in auction until it reaches')}{' '}
<Tooltip
description={
<MarketDataGrid grid={compileGridData(market)} />
}
description={<DataGrid grid={compileGridData(market)} />}
>
<span>{t('sufficient liquidity')}</span>
</Tooltip>
@@ -307,9 +300,7 @@ export const useOrderValidation = ({
<span>
{t('This market is in auction due to')}{' '}
<Tooltip
description={
<MarketDataGrid grid={compileGridData(market)} />
}
description={<DataGrid grid={compileGridData(market)} />}
>
<span>{t('high price volatility')}</span>
</Tooltip>
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
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
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://tm.n00.stagnet3.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n00.stagnet3.vega.xyz/websocket
NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
NX_VEGA_ENV=STAGNET3
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
NX_VEGA_ENV=TESTNET
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
# App flags
NX_EXPLORER_ASSETS=1
+8 -3
View File
@@ -21,7 +21,12 @@ The e2e tests run against a locally running instance of the Vega network, manage
- Install the required Vega smart contracts
- Set up DataNodes with a running GraphQL and REST APIs.
Refer to the [Vega Capsule readme](https://github.com/vegaprotocol/vegacapsule#readme) for setting up and running Capsule. You will need [Go 1.19 or later](https://go.dev/doc/install) and [Docker](https://docs.docker.com/get-docker/) installed.
1. Refer to the [Vega Capsule readme](https://github.com/vegaprotocol/vegacapsule#readme) for setting up and running Capsule - follow by Pre-start and Quick Start (points 1-2)
2. Bootstrap with auto-installed dependencies including wallet
```bash
vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
```
### Troubleshooting
@@ -29,6 +34,6 @@ Refer to the [Vega Capsule readme](https://github.com/vegaprotocol/vegacapsule#r
## Vega Wallet Setup
Start by [downloading the Vega wallet software here](https://github.com/vegaprotocol/vega/releases).
You can then refer to (or run) `frontend-monorepo/vegacapsule/setup-vegawallet.sh`. This will initialise and configure your wallet to have the correct public keys and network config to run against capsule.
You can then refer to (or run) `vegacapsule/setup-vegawallet.sh`. This will initialise and configure your wallet to have the correct public keys and network config to run against capsule.
Go to the .env file in `apps/explorer-e2e` and set the `CYPRESS_VEGA_WALLET_API_TOKEN` environment variable by pasting in your wallets long lived api token
@@ -52,7 +52,8 @@ context('Blocks page', { tags: '@regression' }, function () {
});
});
it('Previous button disabled on first block', function () {
// Skipping - see https://github.com/vegaprotocol/frontend-monorepo/issues/2494
it.skip('Previous button disabled on first block', function () {
cy.get('[data-testid="block-input"]').type('1');
cy.get('[data-testid="go-submit"]').click();
cy.get(previousBlockBtn).find('button').should('be.disabled');
@@ -115,7 +116,7 @@ context('Blocks page', { tags: '@regression' }, function () {
});
function waitForBlocksResponse() {
cy.contains('Loading...').should('not.exist', { timeout: 18000 });
cy.get('[data-testid="loader"]').should('not.exist', { timeout: 18000 });
}
function validateBlocksDisplayed() {
+19 -21
View File
@@ -20,19 +20,18 @@ context('Home Page', function () {
1: 'Height',
2: 'Uptime',
3: 'Total nodes',
4: 'Inactive nodes',
5: 'Total staked',
6: 'Backlog',
7: 'Trades / second',
8: 'Orders / block',
9: 'Orders / second',
10: 'Transactions / block',
11: 'Block time',
12: 'Time',
13: 'App',
14: 'Tendermint',
15: 'Up since',
16: 'Chain ID',
4: 'Total staked',
5: 'Backlog',
6: 'Trades / second',
7: 'Orders / block',
8: 'Orders / second',
9: 'Transactions / block',
10: 'Block time',
11: 'Time',
12: 'App',
13: 'Tendermint',
14: 'Up since',
15: 'Chain ID',
};
cy.get('[data-testid="stats-title"]')
@@ -40,7 +39,7 @@ context('Home Page', function () {
cy.wrap($list).should('have.text', statTitles[index]);
})
.then(($list) => {
cy.wrap($list).should('have.length', 17);
cy.wrap($list).should('have.length', 16);
});
cy.get(statsValue).eq(0).should('have.text', 'CONNECTED');
@@ -50,30 +49,29 @@ context('Home Page', function () {
.invoke('text')
.should('match', /\d+d \d+h \d+m \d+s/i);
cy.get(statsValue).eq(3).should('have.text', '2');
cy.get(statsValue).eq(4).should('have.text', '2');
cy.get(statsValue)
.eq(5)
.eq(4)
.invoke('text')
.should('match', /\d+\.\d\d(?!\d)/i);
cy.get(statsValue).eq(5).should('have.text', '0');
cy.get(statsValue).eq(6).should('have.text', '0');
cy.get(statsValue).eq(7).should('have.text', '0');
cy.get(statsValue).eq(8).should('have.text', '0');
cy.get(statsValue).eq(9).should('have.text', '0');
cy.get(statsValue).eq(9).should('not.be.empty');
cy.get(statsValue).eq(10).should('not.be.empty');
cy.get(statsValue).eq(11).should('not.be.empty');
cy.get(statsValue).eq(12).should('not.be.empty');
if (Cypress.env('NIGHTLY_RUN') != true) {
cy.get(statsValue)
.eq(13)
.eq(12)
.invoke('text')
.should('match', /v\d+\.\d+\.\d+/i);
}
cy.get(statsValue)
.eq(14)
.eq(13)
.invoke('text')
.should('match', /\d+\.\d+\.\d+/i);
cy.get(statsValue).eq(14).should('not.be.empty');
cy.get(statsValue).eq(15).should('not.be.empty');
cy.get(statsValue).eq(16).should('not.be.empty');
});
it('Block height should be updating', function () {
+1 -1
View File
@@ -70,7 +70,7 @@
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.62.1/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.65.1/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
]
}
},
@@ -59,7 +59,8 @@ describe('Blocks infinite list', () => {
error={undefined}
/>
);
expect(screen.getByText('No items')).toBeInTheDocument();
expect(screen.getByTestId('emptylist')).toBeInTheDocument();
expect(screen.getByText('This chain has 0 blocks')).toBeInTheDocument();
});
it('error is displayed at item level', () => {
@@ -4,6 +4,8 @@ import InfiniteLoader from 'react-window-infinite-loader';
import { t } from '@vegaprotocol/react-helpers';
import type { BlockMeta } from '../../routes/blocks/tendermint-blockchain-response';
import { BlockData } from './block-data';
import EmptyList from '../empty-list/empty-list';
import { Loader } from '@vegaprotocol/ui-toolkit';
interface BlocksInfiniteListProps {
hasMoreBlocks: boolean;
@@ -31,7 +33,16 @@ export const BlocksInfiniteList = ({
className,
}: BlocksInfiniteListProps) => {
if (!blocks) {
return <div>No items</div>;
if (!areBlocksLoading) {
return (
<EmptyList
heading={t('This chain has 0 blocks')}
label={t('Check back soon')}
/>
);
} else {
return <Loader />;
}
}
// If there are more items to be loaded then add an extra row to hold a loading indicator.
@@ -50,7 +61,7 @@ export const BlocksInfiniteList = ({
if (error) {
content = t(`${error}`);
} else if (!isItemLoaded(index)) {
content = t('Loading...');
content = <Loader />;
} else {
content = <BlockData block={blocks[index]} />;
}
@@ -0,0 +1,34 @@
export type EmptyListProps = {
heading?: string;
label?: string;
};
/**
* Renders the empty state from github ticket #1463
*/
const EmptyList = ({ heading, label }: EmptyListProps) => {
return (
<div
className="empty-list w-full items-center h-full align-center"
data-testid="emptylist"
>
<div className="skeleton-list border-dashed border-neutral-800 rounded p-5 w-full border-[1px] grid gap-4 grid-cols-9 grid-rows-1 place-content-around mb-4">
<div className="bg-neutral-900 mr-5 h-3 col-span-5"></div>
<div className="bg-neutral-900 h-3 col-span-1"></div>
</div>
<div className="mt-4">
{heading ? (
<h1 className="font-alpha text-xl uppercase text-center leading-relaxed">
{heading}
</h1>
) : null}
{label ? (
<p className="font-alpha text-gray-500 text-center">{label}</p>
) : null}
</div>
</div>
);
};
export default EmptyList;
@@ -14,8 +14,6 @@ export const methodText: Record<
METHOD_NOW: 'Immediate',
METHOD_UNSPECIFIED: 'Unspecified',
METHOD_AT_END_OF_EPOCH: 'End of epoch',
// This will be removed in a future release
METHOD_IN_ANGER: 'Immediate',
};
interface TxDetailsUndelegateProps {
@@ -2,4 +2,3 @@ export { TxList } from './tx-list';
export { TxOrderType } from './tx-order-type';
export { TxsInfiniteList } from './txs-infinite-list';
export { TxsInfiniteListItem } from './txs-infinite-list-item';
export { TxsStatsInfo } from './txs-stats-info';
@@ -46,7 +46,10 @@ describe('Txs infinite list', () => {
error={undefined}
/>
);
expect(screen.getByText('No items')).toBeInTheDocument();
expect(screen.getByTestId('emptylist')).toBeInTheDocument();
expect(
screen.getByText('This chain has 0 transactions')
).toBeInTheDocument();
});
it('error is displayed at item level', () => {
@@ -4,6 +4,8 @@ import InfiniteLoader from 'react-window-infinite-loader';
import { t, useScreenDimensions } from '@vegaprotocol/react-helpers';
import { TxsInfiniteListItem } from './txs-infinite-list-item';
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
import EmptyList from '../empty-list/empty-list';
import { Loader } from '@vegaprotocol/ui-toolkit';
interface TxsInfiniteListProps {
hasMoreTxs: boolean;
@@ -29,7 +31,7 @@ const Item = ({ index, style, isLoading, error }: ItemProps) => {
if (error) {
content = t(`Cannot fetch transaction: ${error}`);
} else if (isLoading) {
content = t('Loading...');
content = <Loader />;
} else {
const {
hash,
@@ -68,7 +70,16 @@ export const TxsInfiniteList = ({
const isStacked = ['xs', 'sm', 'md', 'lg'].includes(screenSize);
if (!txs) {
return <div>No items</div>;
if (!areTxsLoading) {
return (
<EmptyList
heading={t('This chain has 0 transactions')}
label={t('Check back soon')}
/>
);
} else {
return <Loader />;
}
}
// If there are more items to be loaded then add an extra row to hold a loading indicator.
@@ -1,5 +1,4 @@
import { Routes } from '../../routes/route-names';
import { DATA_SOURCES } from '../../config';
import { RenderFetched } from '../render-fetched';
import { TruncatedLink } from '../truncate/truncated-link';
import { TxOrderType } from './tx-order-type';
@@ -8,6 +7,9 @@ import { t, useFetch } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactions } from '../../routes/types/block-explorer-response';
import isNumber from 'lodash/isNumber';
import { ChainResponseCode } from './details/chain-response-code/chain-reponse.code';
import { getTxsDataUrl } from '../../hooks/use-txs-data';
import { Loader } from '@vegaprotocol/ui-toolkit';
import EmptyList from '../empty-list/empty-list';
interface TxsPerBlockProps {
blockHeight: string;
@@ -17,15 +19,11 @@ interface TxsPerBlockProps {
const truncateLength = 5;
export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
// TODO after https://github.com/vegaprotocol/vega/pull/6958/files is merged and deployed, use filter
// by block height instead
const filters = `filters[block.height]=${blockHeight}`;
const url = getTxsDataUrl({ limit: txCount.toString(), filters });
const {
state: { data, loading, error },
} = useFetch<BlockExplorerTransactions>(
`${
DATA_SOURCES.blockExplorerUrl
}/transactions?before=${blockHeight.toString()}.0&limit=${txCount}`
);
} = useFetch<BlockExplorerTransactions>(url);
return (
<RenderFetched error={error} loading={loading} className="text-body-large">
@@ -88,10 +86,13 @@ export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
</tbody>
</Table>
</div>
) : loading ? (
<Loader />
) : (
<div className="sr-only">
{t(`No transactions in block ${blockHeight}`)}
</div>
<EmptyList
heading={t('No transactions in this block')}
label={t('0 transactions')}
/>
)}
</RenderFetched>
);
@@ -1,77 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import { useEffect } from 'react';
import { InfoBlock } from '../../components/info-block';
import { Panel } from '../../components/panel';
import { useExplorerStatsQuery } from './__generated__/Explorer-stats';
import type { ExplorerStatsFieldsFragment } from './__generated__/Explorer-stats';
interface StatsMap {
field: keyof ExplorerStatsFieldsFragment;
label: string;
info: string;
}
export const TXS_STATS_MAP: StatsMap[] = [
{
field: 'averageOrdersPerBlock',
label: t('Orders per block'),
info: t(
'Number of new orders processed in the last block. All orders derived from pegged orders and liquidity commitments count as a single order'
),
},
{
field: 'txPerBlock',
label: t('Transactions per block'),
info: t('Number of transactions processed in the last block'),
},
{
field: 'tradesPerSecond',
label: t('Trades per second'),
info: t('Number of trades processed in the last second'),
},
{
field: 'ordersPerSecond',
label: t('Order per second'),
info: t(
'Number of orders processed in the last second. All orders derived from pegged orders and liquidity commitments count as a single order'
),
},
];
interface TxsStatsInfoProps {
className?: string;
}
export const TxsStatsInfo = ({ className }: TxsStatsInfoProps) => {
const { data, startPolling, stopPolling } = useExplorerStatsQuery();
useEffect(() => {
startPolling(1000);
return () => stopPolling();
});
const gridStyles =
'grid grid-rows-2 gap-4 grid-cols-2 xl:gap-8 xl:grid-rows-1 xl:grid-cols-4';
return (
<Panel className={className}>
<section className={gridStyles}>
{TXS_STATS_MAP.map((field) => {
if (!data?.statistics) {
return null;
}
// Workaround for awkward typing
const title = data.statistics[field.field] || '';
return (
<InfoBlock
subtitle={field.label}
tooltipInfo={field.info}
title={title}
/>
);
})}
</section>
</Panel>
);
};
@@ -0,0 +1,44 @@
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import Assets from './index';
import type { MockedResponse } from '@apollo/client/testing';
import { ExplorerAssetDocument } from '../../components/links/asset-link/__generated__/Asset';
function renderComponent(mock: MockedResponse[]) {
return (
<MemoryRouter>
<MockedProvider mocks={mock}>
<Assets />
</MockedProvider>
</MemoryRouter>
);
}
describe('Assets index', () => {
it('Renders loader when loading', async () => {
const mock = {
request: {
query: ExplorerAssetDocument,
},
result: {
data: {},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('loader')).toBeInTheDocument();
});
it('Renders EmptyList when loading completes and there are no results', async () => {
const mock = {
request: {
query: ExplorerAssetDocument,
},
result: {
data: {},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('emptylist')).toBeInTheDocument();
});
});
+16 -7
View File
@@ -2,14 +2,15 @@ import { getNodes, t } from '@vegaprotocol/react-helpers';
import React from 'react';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { useExplorerAssetsQuery } from './__generated__/Assets';
import type { AssetsFieldsFragment } from './__generated__/Assets';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import EmptyList from '../../components/empty-list/empty-list';
const Assets = () => {
const { data } = useExplorerAssetsQuery();
const { data, loading } = useExplorerAssetsQuery();
useDocumentTitle(['Assets']);
useScrollToLocation();
@@ -17,17 +18,25 @@ const Assets = () => {
const assets = getNodes<AssetsFieldsFragment>(data?.assetsConnection);
if (!assets || assets.length === 0) {
return <section></section>;
if (!loading) {
return (
<section>
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
<EmptyList
heading={t('This chain has no assets')}
label={t('0 assets')}
/>
</section>
);
} else {
return <Loader />;
}
}
return (
<section>
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
{assets.map((a) => {
if (!a) {
return null;
}
return (
<React.Fragment key={a.id}>
<SubHeading data-testid="asset-header" id={a.id}>
@@ -18,6 +18,7 @@ import { RenderFetched } from '../../../components/render-fetched';
import { t, useFetch } from '@vegaprotocol/react-helpers';
import { NodeLink } from '../../../components/links';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import EmptyList from '../../../components/empty-list/empty-list';
const Block = () => {
const { block } = useParams<{ block: string }>();
@@ -112,7 +113,12 @@ const Block = () => {
blockHeight={blockData.result.block.header.height}
txCount={blockData.result.block.data.txs.length}
/>
) : null}
) : (
<EmptyList
heading={t('This block is empty')}
label={t('0 transactions')}
/>
)}
</>
)}
</>
@@ -1,6 +1,6 @@
import { t, useFetch } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../components/route-title';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { DATA_SOURCES } from '../../config';
import type { TendermintGenesisResponse } from './tendermint-genesis-response';
import { useDocumentTitle } from '../../hooks/use-document-title';
@@ -9,11 +9,16 @@ const Genesis = () => {
useDocumentTitle(['Genesis']);
const {
state: { data: genesis },
state: { data: genesis, loading },
} = useFetch<TendermintGenesisResponse>(
`${DATA_SOURCES.tendermintUrl}/genesis`
);
if (!genesis?.result.genesis) return null;
if (!genesis?.result.genesis) {
if (loading) {
return <Loader />;
}
return null;
}
return (
<section>
<RouteTitle data-testid="genesis-header">{t('Genesis')}</RouteTitle>
@@ -2,19 +2,35 @@ import { t } from '@vegaprotocol/react-helpers';
import React from 'react';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { useExplorerProposalsQuery } from './__generated__/Proposals';
import { useDocumentTitle } from '../../hooks/use-document-title';
import EmptyList from '../../components/empty-list/empty-list';
const Governance = () => {
const { data } = useExplorerProposalsQuery({
const { data, loading } = useExplorerProposalsQuery({
errorPolicy: 'ignore',
});
useDocumentTitle();
if (!data || !data.proposalsConnection || !data.proposalsConnection.edges) {
return <section></section>;
if (!loading) {
return (
<section>
<RouteTitle data-testid="governance-header">
{t('Governance Proposals')}
</RouteTitle>
<EmptyList
heading={t('This chain has no proposals')}
label={t('0 proposals')}
/>
</section>
);
} else {
return <Loader />;
}
}
const proposals = data?.proposalsConnection?.edges.map((e) => {
@@ -0,0 +1,48 @@
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import Markets from './index';
import type { MockedResponse } from '@apollo/client/testing';
import { ExplorerMarketsDocument } from './__generated__/Markets';
function renderComponent(mock: MockedResponse[]) {
return (
<MemoryRouter>
<MockedProvider mocks={mock}>
<Markets />
</MockedProvider>
</MemoryRouter>
);
}
describe('Markets index', () => {
it('Renders loader when loading', async () => {
const mock = {
request: {
query: ExplorerMarketsDocument,
},
result: {
data: {
marketsConnection: [],
},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('loader')).toBeInTheDocument();
});
it('Renders EmptyList when loading completes and there are no results', async () => {
const mock = {
request: {
query: ExplorerMarketsDocument,
},
result: {
data: {
marketsConnection: [],
},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('emptylist')).toBeInTheDocument();
});
});
+20 -12
View File
@@ -1,14 +1,15 @@
import React from 'react';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
import { t } from '@vegaprotocol/react-helpers';
import { useExplorerMarketsQuery } from './__generated__/Markets';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import EmptyList from '../../components/empty-list/empty-list';
const Markets = () => {
const { data } = useExplorerMarketsQuery();
const { data, loading } = useExplorerMarketsQuery();
useScrollToLocation();
useDocumentTitle(['Markets']);
@@ -19,16 +20,23 @@ const Markets = () => {
<section key="markets">
<RouteTitle data-testid="markets-heading">{t('Markets')}</RouteTitle>
{m
? m.map((e) => (
<React.Fragment key={e.node.id}>
<SubHeading data-testid="markets-header" id={e.node.id}>
{e.node.tradableInstrument.instrument.name}
</SubHeading>
<SyntaxHighlighter data={e.node} />
</React.Fragment>
))
: null}
{m ? (
m.map((e) => (
<React.Fragment key={e.node.id}>
<SubHeading data-testid="markets-header" id={e.node.id}>
{e.node.tradableInstrument.instrument.name}
</SubHeading>
<SyntaxHighlighter data={e.node} />
</React.Fragment>
))
) : loading ? (
<Loader />
) : (
<EmptyList
heading={t('This chain has no markets')}
label={t('0 markets')}
/>
)}
</section>
);
};
@@ -1,7 +1,7 @@
import { t } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../../components/route-title';
import { BlocksRefetch } from '../../../components/blocks';
import { TxsInfiniteList, TxsStatsInfo } from '../../../components/txs';
import { TxsInfiniteList } from '../../../components/txs';
import { useTxsData } from '../../../hooks/use-txs-data';
import { useDocumentTitle } from '../../../hooks/use-document-title';
@@ -16,7 +16,6 @@ export const TxsList = () => {
<section className="md:p-2 lg:p-4 xl:p-6">
<RouteTitle>{t('Transactions')}</RouteTitle>
<BlocksRefetch refetch={refreshTxs} />
<TxsStatsInfo className="!my-12 py-8" />
<TxsInfiniteList
hasMoreTxs={hasMoreTxs}
areTxsLoading={loading}
@@ -1,8 +1,7 @@
import { t } from '@vegaprotocol/react-helpers';
import React from 'react';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { DATA_SOURCES } from '../../config';
import { useFetch } from '@vegaprotocol/react-helpers';
import type { TendermintValidatorsResponse } from './tendermint-validator-response';
@@ -28,7 +27,9 @@ const Validators = () => {
<SubHeading data-testid="vega-header">{t('Vega data')}</SubHeading>
<SyntaxHighlighter data-testid="vega-data" data={data} />
</>
) : null}
) : (
<Loader />
)}
{validators ? (
<>
<SubHeading data-testid="tendermint-header">
@@ -36,7 +37,9 @@ const Validators = () => {
</SubHeading>
<SyntaxHighlighter data-testid="tendermint-data" data={validators} />
</>
) : null}
) : (
<Loader />
)}
</section>
);
};
+73 -26
View File
@@ -14,9 +14,17 @@ type OneOf<T extends any[]> = T extends [infer Only]
: T extends [infer A, infer B, ...infer Rest]
? OneOf<[XOR<A, B>, ...Rest]>
: never;
/* typescript-eslint:enable no-explicit-any */
/* eslint-enable @typescript-eslint/no-explicit-any */
export interface paths {
'/info': {
/**
* Info
* @description Retrieves information about the block explorer.
* Response contains a semver formatted version of the data node and the commit hash, from which the block explorer was built,
*/
get: operations['BlockExplorer_Info'];
};
'/transactions': {
/**
* List transactions
@@ -100,8 +108,7 @@ export interface components {
readonly UndelegateSubmissionMethod:
| 'METHOD_UNSPECIFIED'
| 'METHOD_NOW'
| 'METHOD_AT_END_OF_EPOCH'
| 'METHOD_IN_ANGER';
| 'METHOD_AT_END_OF_EPOCH';
readonly blockexplorerapiv1Transaction: {
/**
* The height of the block the transaction was found in
@@ -117,6 +124,11 @@ export interface components {
readonly command?: components['schemas']['v1InputData'];
/** The cursor for this transaction (in the page, used for paginated results) */
readonly cursor?: string;
/**
* An optional error happening when processing / checking the transaction
* Should be set if error code is not 0
*/
readonly error?: string;
/** The hash of the transaction */
readonly hash?: string;
/**
@@ -124,6 +136,8 @@ export interface components {
* Format: int64
*/
readonly index?: number;
/** Submitter's signature of transaction */
readonly signature?: components['schemas']['v1Signature'];
/** The submitter of the transaction (Vega public key) */
readonly submitter?: string;
/** The type of transaction */
@@ -159,7 +173,7 @@ export interface components {
readonly '@type'?: string;
[key: string]: unknown | undefined;
};
/** Used announce a node as a new potential validator */
/** Used announce a node as a new pending validator */
readonly v1AnnounceNode: {
/** AvatarURL of the validator */
readonly avatarUrl?: string;
@@ -192,7 +206,7 @@ export interface components {
* Format: int64
*/
readonly vegaPubKeyIndex?: number;
/** Signature from the validator made using the vega wallet */
/** Signature from the validator made using the Vega wallet */
readonly vegaSignature?: components['schemas']['v1Signature'];
};
/**
@@ -287,6 +301,12 @@ export interface components {
/** The transaction corresponding to the hash */
readonly transaction?: components['schemas']['blockexplorerapiv1Transaction'];
};
readonly v1InfoResponse: {
/** The commit hash from which the data-node was built */
readonly commitHash?: string;
/** A semver formatted version of the data node */
readonly version?: string;
};
readonly v1InputData: {
readonly announceNode?: components['schemas']['v1AnnounceNode'];
readonly batchMarketInstructions?: components['schemas']['v1BatchMarketInstructions'];
@@ -351,7 +371,7 @@ export interface components {
/** The ID of the node that will be signed in or out of the smartcontract */
readonly validatorNodeId?: string;
};
/** A transaction to allow validator to rotate their vega keys */
/** A transaction to allow validator to rotate their Vega keys */
readonly v1KeyRotateSubmission: {
/** Hash of currently used public key */
readonly currentPubKeyHash?: string;
@@ -414,7 +434,7 @@ export interface components {
readonly sig?: string;
};
/**
* The kind of the signature created by a node, for example, allow-listing a new asset, withdrawal etc
* The kind of signature created by a node, for example, allow-listing a new asset, withdrawal etc
* @description - NODE_SIGNATURE_KIND_UNSPECIFIED: Represents an unspecified or missing value from the input
* - NODE_SIGNATURE_KIND_ASSET_NEW: Represents a signature for a new asset allow-listing
* - NODE_SIGNATURE_KIND_ASSET_WITHDRAWAL: Represents a signature for an asset withdrawal
@@ -432,7 +452,7 @@ export interface components {
| 'NODE_SIGNATURE_KIND_ERC20_MULTISIG_SIGNER_REMOVED'
| 'NODE_SIGNATURE_KIND_ASSET_UPDATE';
/**
* Used when a node votes for validating a given resource exists or is valid,
* Used when a node votes for validating that a given resource exists or is valid,
* for example, an ERC20 deposit is valid and exists on ethereum
*/
readonly v1NodeVote: {
@@ -553,6 +573,12 @@ export interface components {
readonly v1PropertyKey: {
/** @description name is the name of the property. */
readonly name?: string;
/**
* An optional decimal place to be be applied on the provided value
* valid only for PropertyType of type DECIMAL and INTEGER
* Format: uint64
*/
readonly numberDecimalPlaces?: string;
/** @description type is the type of the property. */
readonly type?: components['schemas']['v1PropertyKeyType'];
};
@@ -596,7 +622,7 @@ export interface components {
* Format: uint64
*/
readonly upgradeBlockHeight?: string;
/** the release tag for the vega binary */
/** the release tag for the Vega binary */
readonly vegaReleaseTag?: string;
};
/**
@@ -659,8 +685,8 @@ export interface components {
readonly nodeId?: string;
};
/**
* A message from a validator signaling they are still online and validating blocks
* or ready to validate block when they are till a potential validator
* A message from a validator signalling they are still online and validating blocks
* or ready to validate blocks when they are still a pending validator
*/
readonly v1ValidatorHeartbeat: {
/** Signature from the validator made using the ethereum wallet */
@@ -1061,11 +1087,6 @@ export interface components {
readonly quoteName?: string;
/** Asset ID for the product's settlement asset */
readonly settlementAsset?: string;
/**
* The number of decimal places implied by the settlement data (such as price) emitted by the settlement data source
* Format: int64
*/
readonly settlementDataDecimals?: number;
};
/** Instrument configuration */
readonly vegaInstrumentConfiguration: {
@@ -1111,17 +1132,17 @@ export interface components {
/** Risk model parameters for log normal */
readonly vegaLogNormalModelParams: {
/**
* Mu param
* Mu parameter, annualised growth rate of the underlying asset
* Format: double
*/
readonly mu?: number;
/**
* R param
* R parameter, annualised growth rate of the risk-free asset, used for discounting of future cash flows, can be any real number
* Format: double
*/
readonly r?: number;
/**
* Sigma param
* Sigma parameter, annualised volatility of the underlying asset, must be a strictly non-negative real number
* Format: double
*/
readonly sigma?: number;
@@ -1136,7 +1157,7 @@ export interface components {
*/
readonly riskAversionParameter?: number;
/**
* Tau
* Tau parameter of the risk model, projection horizon measured as a year fraction used in the expected shortfall calculation to obtain the maintenance margin, must be a strictly non-negative real number
* Format: double
*/
readonly tau?: number;
@@ -1180,6 +1201,11 @@ export interface components {
readonly liquidityMonitoringParameters?: components['schemas']['vegaLiquidityMonitoringParameters'];
/** Log normal risk model parameters, valid only if MODEL_LOG_NORMAL is selected */
readonly logNormal?: components['schemas']['vegaLogNormalRiskModel'];
/**
* Percentage move up and down from the mid price which specifies the range of
* price levels over which automated liquidity provision orders will be deployed
*/
readonly lpPriceRange?: string;
/** Optional new market meta data, tags */
readonly metadata?: readonly string[];
/**
@@ -1239,7 +1265,7 @@ export interface components {
readonly vegaPriceMonitoringTrigger: {
/**
* Price monitoring auction extension duration in seconds should the price
* breach it's theoretical level over the specified horizon at the specified
* breach its theoretical level over the specified horizon at the specified
* probability level
* Format: int64
*/
@@ -1436,11 +1462,6 @@ export interface components {
readonly dataSourceSpecForTradingTermination?: components['schemas']['vegaDataSourceDefinition'];
/** Human-readable name/abbreviation of the quote name */
readonly quoteName?: string;
/**
* The number of decimal places implied by the settlement data (such as price) emitted by the settlement external data source
* Format: int64
*/
readonly settlementDataDecimals?: number;
};
/** Instrument configuration */
readonly vegaUpdateInstrumentConfiguration: {
@@ -1464,6 +1485,11 @@ export interface components {
readonly liquidityMonitoringParameters?: components['schemas']['vegaLiquidityMonitoringParameters'];
/** Log normal risk model parameters, valid only if MODEL_LOG_NORMAL is selected */
readonly logNormal?: components['schemas']['vegaLogNormalRiskModel'];
/**
* Percentage move up and down from the mid price which specifies the range of
* price levels over which automated liquidity provision orders will be deployed
*/
readonly lpPriceRange?: string;
/** Optional market metadata, tags */
readonly metadata?: readonly string[];
/** Price monitoring parameters */
@@ -1504,6 +1530,27 @@ export interface components {
export type external = Record<string, never>;
export interface operations {
BlockExplorer_Info: {
/**
* Info
* @description Retrieves information about the block explorer.
* Response contains a semver formatted version of the data node and the commit hash, from which the block explorer was built,
*/
responses: {
/** @description A successful response. */
200: {
content: {
readonly 'application/json': components['schemas']['v1InfoResponse'];
};
};
/** @description An unexpected error response. */
default: {
content: {
readonly 'application/json': components['schemas']['googlerpcStatus'];
};
};
};
};
BlockExplorer_ListTransactions: {
/**
* List transactions
@@ -10,6 +10,7 @@ import {
formatNumberPercentage,
t,
toBigNum,
getDateTimeFormat,
} from '@vegaprotocol/react-helpers';
import type { VegaValueFormatterParams } from '@vegaprotocol/ui-toolkit';
import type * as Schema from '@vegaprotocol/types';
@@ -296,6 +297,21 @@ export const MarketList = () => {
return value ? formatDistanceToNow(new Date(value)) : '-';
}}
/>
<AgGridColumn
headerName={t('Closing Time')}
field="proposal.terms.closingDatetime"
headerTooltip={t('Closing time of the market')}
valueFormatter={({
value,
}: VegaValueFormatterParams<
Market,
'proposal.terms.closingDatetime'
>) => {
return value
? getDateTimeFormat().format(new Date(value).getTime())
: '-';
}}
/>
</Grid>
<HealthDialog
@@ -1,5 +1,5 @@
import { useEnvironment } from '@vegaprotocol/environment';
import { useEthereumConfig } from '@vegaprotocol/web3';
import { getChainName, useEthereumConfig } from '@vegaprotocol/web3';
import { Button, Splash, AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Web3ConnectDialog } from '@vegaprotocol/web3';
import { useWeb3React } from '@web3-react/core';
@@ -82,7 +82,7 @@ export const Web3Content = ({
<Splash>
<div className="flex flex-col items-center gap-12">
<p className="text-white">
This app only works on chain ID: {appChainId}
This app only works on {getChainName(appChainId)}
</p>
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
</div>
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,3 +1,3 @@
{
"hosts": ["https://api.n00.mainnet-mirror.vega.xyz/graphql"]
"hosts": ["https://api.n00.mainnet-mirror.vega.xyz/query"]
}
+8 -3
View File
@@ -21,7 +21,12 @@ The e2e tests run against a locally running instance of the Vega network, manage
- Install the required Vega smart contracts
- Set up DataNodes with a running GraphQL and REST APIs.
Refer to the [Vega Capsule readme](https://github.com/vegaprotocol/vegacapsule#readme) for setting up and running Capsule. You will need [Go 1.19 or later](https://go.dev/doc/install) and [Docker](https://docs.docker.com/get-docker/) installed.
1. Refer to the [Vega Capsule readme](https://github.com/vegaprotocol/vegacapsule#readme) for setting up and running Capsule - follow by Pre-start and Quick Start (points 1-2)
2. Bootstrap with auto-installed dependencies including wallet
```bash
vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
```
### Troubleshooting
@@ -29,6 +34,6 @@ Refer to the [Vega Capsule readme](https://github.com/vegaprotocol/vegacapsule#r
## Vega Wallet Setup
Start by [downloading the Vega wallet software here](https://github.com/vegaprotocol/vega/releases).
You can then refer to (or run) `frontend-monorepo/vegacapsule/setup-vegawallet.sh`. This will initialise and configure your wallet to have the correct public keys and network config to run against capsule.
You can then refer to (or run) `vegacapsule/setup-vegawallet.sh`. This will initialise and configure your wallet to have the correct public keys and network config to run against capsule.
Go to the .env file in `apps/token-e2e` and set the `CYPRESS_VEGA_WALLET_API_TOKEN` environment variable by pasting in your wallets long lived api token
@@ -728,7 +728,7 @@ context(
});
// 1005-PROP-009
it(
it.skip(
'Unable to vote on a freeform proposal - when some but not enough vega associated',
{ tags: '@smoke' },
function () {
+1
View File
@@ -8,3 +8,4 @@ NX_ETHERSCAN_URL=https://etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
+1
View File
@@ -6,3 +6,4 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
+1
View File
@@ -9,3 +9,4 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
+6 -6
View File
@@ -39,12 +39,12 @@ There are a few different configuration options offered for this app:
| **Flag** | **Purpose** |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NX_APP_SENTRY_DSN` | The sentry endpoint to report to. Should be off in dev but set in live. |
| `NX_APP_VEGA_URL` | The GraphQL query endpoint of a [Vega data node](https://github.com/vegaprotocol/networks#data-node) |
| `NX_APP_DEX_STAKING_DISABLED` | Disable the dex liquidity page an show a coming soon message |
| `NX_APP_FAIRGROUND` | Change styling to be themed as the fairground version of the website |
| `NX_APP_INFURA_ID` | Infura fallback for if the user does not have a web3 compatible browser |
| `NX_APP_ENV` | Change network to connect to. When set to CUSTOM use CUSTOM\_\* vars for network parameters |
| `NX_SENTRY_DSN` | The sentry endpoint to report to. Should be off in dev but set in live. |
| `NX_VEGA_URL` | The GraphQL query endpoint of a [Vega data node](https://github.com/vegaprotocol/networks#data-node) |
| `NX_DEX_STAKING_DISABLED` | Disable the dex liquidity page an show a coming soon message |
| `NX_FAIRGROUND` | Change styling to be themed as the fairground version of the website |
| `NX_INFURA_ID` | Infura fallback for if the user does not have a web3 compatible browser |
| `NX_ENV` | Change network to connect to. |
| `NX_ETH_URL_CONNECT` (optional) | If set to true the below two must also be set. This allows siging transactions in brower to allow to connect to a local ganache node through cypress |
| `NX_ETH_WALLET_MNEMONIC` (optional) | The mnemonic to be used to sign transactions with in browser |
| `NX_LOCAL_PROVIDER_URL` (optional) | The local node to use to send transaction to when signing in browser |
@@ -1,5 +1,5 @@
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
import { Web3ConnectDialog } from '@vegaprotocol/web3';
import { getChainName, Web3ConnectDialog } from '@vegaprotocol/web3';
import { useWeb3React } from '@web3-react/core';
import type { ReactElement } from 'react';
import { useCallback, useEffect } from 'react';
@@ -78,7 +78,7 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
<Splash>
<div className="flex flex-col items-center gap-12">
<p className="text-white">
This app only works on chain ID: {appChainId}
This app only works on {getChainName(appChainId)}
</p>
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
</div>
+14 -14
View File
@@ -6,43 +6,41 @@ const amountField = 'input[name="amount"]';
const formFieldError = 'input-error-text';
describe('deposit form validation', { tags: '@smoke' }, () => {
beforeEach(() => {
before(() => {
cy.mockWeb3Provider();
cy.mockSubscription();
cy.mockTradingPage();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Deposits').click();
cy.getByTestId('tab-deposits').contains('Connect your Vega wallet');
cy.connectVegaWallet();
cy.getByTestId('deposit-button').click();
cy.wait('@Assets');
connectEthereumWallet();
cy.getByTestId('deposit-submit').click();
});
it('handles empty fields', () => {
cy.getByTestId(formFieldError).should('contain.text', 'Required');
cy.getByTestId(formFieldError).should('have.length', 2);
});
it('unable to select assets not enabled', () => {
connectEthereumWallet();
cy.getByTestId('deposit-submit').click();
// Assets not enabled in mocks
cy.get(assetSelectField + ' option:contains(Asset 2)').should('not.exist');
cy.get(assetSelectField + ' option:contains(Asset 3)').should('not.exist');
cy.get(assetSelectField + ' option:contains(Asset 4)').should('not.exist');
});
it('handles empty fields', () => {
connectEthereumWallet();
// Submit form to trigger any empty validation messages
cy.getByTestId('deposit-submit').click();
cy.getByTestId(formFieldError).should('contain.text', 'Required');
cy.getByTestId(formFieldError).should('have.length', 2);
// Invalid public key
it('invalid public key', () => {
cy.get(toAddressField)
.clear()
.type('INVALID_DEPOSIT_TO_ADDRESS')
.next(`[data-testid="${formFieldError}"]`)
.should('have.text', 'Invalid Vega key');
});
it('invalid amount', () => {
// Deposit amount smaller than minimum viable for selected asset
// Select an amount so that we have a known decimal places value to work with
cy.get(assetSelectField).select('Euro');
@@ -51,7 +49,9 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
.type('0.00000000000000000000000000000000001')
.next(`[data-testid="${formFieldError}"]`)
.should('have.text', 'Value is below minimum');
});
it('insufficient funds', () => {
// Deposit amount is valid, but less than approved. This will always be the case because our
// CI wallet wont have approved any assets
cy.get(amountField)
@@ -133,6 +133,7 @@ describe('Navbar', { tags: '@smoke' }, () => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
cy.wait('@Market');
cy.getByTestId('dialog-close').click();
});
@@ -224,4 +224,34 @@ describe('home', { tags: '@regression' }, () => {
.should('exist');
});
});
describe('redirect should take last visited market into consideration', () => {
beforeEach(() => {
cy.window().then((window) => {
window.localStorage.removeItem('marketId');
});
});
it('marketId comes from existing market', () => {
cy.window().then((window) => {
window.localStorage.setItem('marketId', 'market-1');
cy.visit('/');
cy.wait('@Market');
cy.location('hash').should('equal', '#/markets/market-1');
cy.get('[role="dialog"]').should('not.exist');
});
});
it('marketId comes from not-existing market', () => {
cy.window().then((window) => {
window.localStorage.setItem('marketId', 'market-not-existing');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Market', null);
});
cy.visit('/');
cy.wait('@Market');
cy.location('hash').should('equal', '#/markets/market-not-existing');
cy.get('[role="dialog"]').should('not.exist');
});
});
});
});
@@ -235,9 +235,9 @@ describe('market states not accepting orders', { tags: '@smoke' }, function () {
beforeEach(function () {
cy.mockTradingPage(marketState);
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
});
it('must display that market is not accepting orders', function () {
cy.getByTestId('place-order').click();
@@ -2,6 +2,7 @@ beforeEach(() => {
cy.mockTradingPage();
cy.mockWeb3Provider();
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/markets/market-0');
});
@@ -10,8 +11,6 @@ describe('accounts', { tags: '@smoke' }, () => {
const tradingAccountRowId = '[row-id="asset-0"]';
cy.getByTestId('Collateral').click();
cy.connectVegaWallet();
cy.getByTestId('tab-accounts').should('be.visible');
cy.getByTestId('tab-accounts')
@@ -30,11 +30,11 @@ const displayTomorrow = () => {
describe('time in force default values', () => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
});
it('must have market order set up to IOC by default', function () {
@@ -59,20 +59,15 @@ describe('time in force default values', () => {
describe('must submit order', { tags: '@smoke' }, () => {
// 7002-SORD-039
before(() => {
cy.setVegaWallet();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
cy.window().then(function (window) {
cy.wrap(window.localStorage.getItem('vega_wallet_config')).as('cfg');
});
});
beforeEach(() => {
cy.window().then(function (window) {
window.localStorage.setItem('vega_wallet_config', this.cfg);
});
cy.setVegaWallet();
});
it('successfully places market buy order', () => {
@@ -158,6 +153,7 @@ describe(
{ tags: '@regression' },
() => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage(
Schema.MarketState.STATE_SUSPENDED,
Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
@@ -166,16 +162,10 @@ describe(
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
cy.window().then(function (window) {
cy.wrap(window.localStorage.getItem('vega_wallet_config')).as('cfg');
});
});
beforeEach(() => {
cy.window().then(function (window) {
window.localStorage.setItem('vega_wallet_config', this.cfg);
});
cy.setVegaWallet();
});
it('successfully places limit buy order', () => {
@@ -232,6 +222,7 @@ describe(
{ tags: '@regression' },
() => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage(
Schema.MarketState.STATE_SUSPENDED,
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
@@ -240,16 +231,10 @@ describe(
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
cy.window().then(function (window) {
cy.wrap(window.localStorage.getItem('vega_wallet_config')).as('cfg');
});
});
beforeEach(() => {
cy.window().then(function (window) {
window.localStorage.setItem('vega_wallet_config', this.cfg);
});
cy.setVegaWallet();
});
it('successfully places limit buy order', () => {
@@ -306,6 +291,7 @@ describe(
{ tags: '@regression' },
() => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage(
Schema.MarketState.STATE_SUSPENDED,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
@@ -314,16 +300,10 @@ describe(
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
cy.window().then(function (window) {
cy.wrap(window.localStorage.getItem('vega_wallet_config')).as('cfg');
});
});
beforeEach(() => {
cy.window().then(function (window) {
window.localStorage.setItem('vega_wallet_config', this.cfg);
});
cy.setVegaWallet();
});
it('successfully places limit buy order', () => {
@@ -421,10 +401,10 @@ describe('deal ticket validation', { tags: '@smoke' }, () => {
describe('deal ticket size validation', { tags: '@smoke' }, function () {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
});
it('must warn if order size input has too many digits after the decimal place', function () {
@@ -455,14 +435,18 @@ describe('deal ticket size validation', { tags: '@smoke' }, function () {
describe('limit order validations', { tags: '@smoke' }, () => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.connectVegaWallet();
cy.wait('@Market');
cy.getByTestId(toggleLimit).click();
});
beforeEach(() => {
cy.setVegaWallet();
});
it('must see the price unit', function () {
//7002-SORD-018
cy.getByTestId(orderPriceField)
@@ -544,11 +528,17 @@ describe('limit order validations', { tags: '@smoke' }, () => {
describe('market order validations', { tags: '@smoke' }, () => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.getByTestId(toggleMarket).click();
});
beforeEach(() => {
cy.setVegaWallet();
});
it('must not see the price unit', function () {
//7002-SORD-019
cy.getByTestId(orderPriceField).should('not.exist');
@@ -587,6 +577,7 @@ describe('market order validations', { tags: '@smoke' }, () => {
describe('suspended market validation', { tags: '@regression' }, () => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage(
Schema.MarketState.STATE_SUSPENDED,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
@@ -595,7 +586,10 @@ describe('suspended market validation', { tags: '@regression' }, () => {
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
});
beforeEach(() => {
cy.setVegaWallet();
});
it('should show warning for market order', function () {
@@ -608,6 +602,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
'This market is in auction until it reaches sufficient liquidity. Only limit orders are permitted when market is in auction'
);
});
it('should show info for allowed TIF', function () {
cy.getByTestId(toggleLimit).click();
cy.getByTestId(orderPriceField).clear().type('0.1');
@@ -635,6 +630,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
describe('account validation', { tags: '@regression' }, () => {
describe('zero balance error', () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
cy.mockGQL((req) => {
aliasGQLQuery(
@@ -663,7 +659,6 @@ describe('account validation', { tags: '@regression' }, () => {
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.connectVegaWallet();
cy.wait('@Market');
});
@@ -682,6 +677,7 @@ describe('account validation', { tags: '@regression' }, () => {
describe('not enough balance warning', () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
cy.mockGQL((req) => {
aliasGQLQuery(
@@ -699,9 +695,9 @@ describe('account validation', { tags: '@regression' }, () => {
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.connectVegaWallet();
cy.wait('@Market');
});
it('should display info and button for deposit', () => {
//7002-SORD-003
// warning should show immediately
@@ -12,6 +12,7 @@ describe('fills', { tags: '@regression' }, () => {
)
);
});
cy.setVegaWallet();
cy.mockTradingPage();
cy.mockGQL((req) => {
aliasGQLQuery(
@@ -27,20 +28,12 @@ describe('fills', { tags: '@regression' }, () => {
cy.visit('/#/portfolio');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Fills').click();
cy.getByTestId('tab-fills').contains('Connect your Vega wallet');
cy.connectVegaWallet();
validateFillsDisplayed();
});
it('renders fills on trading tab', () => {
cy.mockTradingPage();
cy.visit('/#/markets/market-0');
cy.getByTestId('Fills').click();
cy.getByTestId('tab-fills').should(
'contain.text',
'Connect your Vega wallet'
);
cy.connectVegaWallet();
validateFillsDisplayed();
});
@@ -27,11 +27,11 @@ describe('orders list', { tags: '@smoke' }, () => {
cy.spy(subscriptionMocks, 'OrdersUpdate');
cy.mockTradingPage();
cy.mockSubscription(subscriptionMocks);
cy.setVegaWallet();
cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click();
cy.connectVegaWallet();
cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledOnce;
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice;
});
cy.wait('@Markets');
});
@@ -125,11 +125,11 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
cy.spy(subscriptionMocks, 'OrdersUpdate');
cy.mockTradingPage();
cy.mockSubscription(subscriptionMocks);
cy.setVegaWallet();
cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click();
cy.connectVegaWallet();
cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledOnce;
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice;
});
});
const orderId = '1234567890';
@@ -341,11 +341,11 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
cy.spy(subscriptionMocks, 'OrdersUpdate');
cy.mockTradingPage();
cy.mockSubscription(subscriptionMocks);
cy.setVegaWallet();
cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click();
cy.connectVegaWallet();
cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledOnce;
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice;
});
cy.mockVegaWalletTransaction();
});
@@ -423,4 +423,30 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
testOrderCancellation(order);
});
});
it('must be warned (pre-submit) if the input price has too many digits after the decimal place for the market', () => {
// 7003-MORD-016
updateOrder({
id: orderId,
status: Schema.OrderStatus.STATUS_ACTIVE,
peggedOrder: null,
liquidityProvisionId: null,
});
cy.get(`[row-id=${orderId}]`)
.find('[data-testid="edit"]')
.should('have.text', 'Edit')
.then(($btn) => {
cy.wrap($btn).click();
cy.getByTestId('dialog-title').should('have.text', 'Edit order');
cy.get('#limitPrice').focus().clear().type('0.111111');
cy.getByTestId('edit-order').find('[type="submit"]').click();
cy.getByTestId('input-error-text').should(
'have.text',
'Price accepts up to 5 decimal places'
);
});
});
it.skip('tbd for 7003-MORD', () => {
// NOT COVERED: must see the reference, offset and direction for each part pegged order - waiting for clarification
// NOT COVERED: must see the reference, offset and direction for each part liquidity order order - waiting for clarification
});
});
@@ -13,11 +13,11 @@ describe('Portfolio page', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Markets', marketsQuery());
});
cy.mockSubscription();
cy.setVegaWallet();
});
describe('Ledger entries', () => {
it('List should be properly rendered', () => {
cy.visit('/#/portfolio');
cy.connectVegaWallet();
cy.getByTestId('"Ledger entries"').click();
const headers = [
'Sender',
@@ -1,25 +1,19 @@
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
});
describe('positions', { tags: '@smoke' }, () => {
it('renders positions on trading page', () => {
cy.visit('/#/markets/market-0');
cy.getByTestId('Positions').click();
cy.getByTestId('tab-positions').should(
'contain.text',
'Connect your Vega wallet'
);
cy.connectVegaWallet();
validatePositionsDisplayed();
});
it('renders positions on portfolio page', () => {
cy.visit('/#/portfolio');
cy.getByTestId('Positions').click();
cy.connectVegaWallet();
validatePositionsDisplayed();
});
@@ -15,13 +15,11 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
cy.mockWeb3Provider();
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.getByTestId('Withdrawals').click();
// Withdraw page requires vega wallet connection
cy.connectVegaWallet();
cy.getByTestId('withdraw-dialog-button').click();
// It also requires connection Ethereum wallet
@@ -72,13 +70,10 @@ describe('withdraw actions', { tags: '@regression' }, () => {
cy.mockWeb3Provider();
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.getByTestId('Withdrawals').click();
// Withdraw page requires vega wallet connection
cy.connectVegaWallet();
cy.getByTestId('withdraw-dialog-button').click();
connectEthereumWallet();
@@ -1,11 +0,0 @@
import { ethers } from 'ethers';
import type { Transaction } from '@vegaprotocol/wallet';
/**
* Base64 encode a transaction object
*/
export const encodeTransaction = (tx: Transaction): string => {
return ethers.utils.base64.encode(
ethers.utils.toUtf8Bytes(JSON.stringify(tx))
);
};
@@ -7,7 +7,6 @@ import type {
OrderSubmissionBody,
Transaction,
} from '@vegaprotocol/wallet';
import { encodeTransaction } from './encode-transaction';
export const testOrderSubmission = (
order: OrderSubmission,
@@ -18,9 +17,6 @@ export const testOrderSubmission = (
...expected,
};
expectedOrder.expiresAt = expectedOrder.expiresAt || undefined;
expectedOrder.price = expectedOrder.price || undefined;
const transaction: OrderSubmissionBody = {
orderSubmission: expectedOrder,
};
@@ -36,9 +32,6 @@ export const testOrderAmendment = (
...expected,
};
expectedOrder.expiresAt = expectedOrder.expiresAt || undefined;
expectedOrder.price = expectedOrder.price || undefined;
const transaction: OrderAmendmentBody = {
orderAmendment: expectedOrder,
};
@@ -70,7 +63,7 @@ const vegaWalletTransaction = (transaction: Transaction) => {
?.token,
publicKey: Cypress.env('VEGA_PUBLIC_KEY2'),
sendingMode: 'TYPE_SYNC',
encodedTransaction: encodeTransaction(transaction),
transaction,
});
cy.getByTestId(dialogTitle).should(
'have.text',
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+17 -35
View File
@@ -1,14 +1,10 @@
import { marketsWithDataProvider } from '@vegaprotocol/market-list';
import {
addDecimalsFormatNumber,
titlefy,
useDataProvider,
} from '@vegaprotocol/react-helpers';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Links, Routes } from '../../pages/client-router';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useGlobalStore, usePageTitleStore } from '../../stores';
import { marketsWithDataProvider } from '@vegaprotocol/market-list';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Links, Routes } from '../../pages/client-router';
import { useGlobalStore } from '../../stores';
export const Home = () => {
const navigate = useNavigate();
@@ -17,40 +13,26 @@ export const Home = () => {
const { data, error, loading } = useDataProvider({
dataProvider: marketsWithDataProvider,
});
const { update } = useGlobalStore((store) => ({
update: store.update,
}));
const { pageTitle, updateTitle } = usePageTitleStore((store) => ({
pageTitle: store.pageTitle,
updateTitle: store.updateTitle,
}));
const update = useGlobalStore((store) => store.update);
const marketId = useGlobalStore((store) => store.marketId);
useEffect(() => {
if (data) {
const marketId = data[0]?.id;
const marketName = data[0]?.tradableInstrument.instrument.name;
const marketPrice = data[0]?.data?.markPrice
? addDecimalsFormatNumber(
data[0]?.data?.markPrice,
data[0]?.decimalPlaces
)
: null;
const newPageTitle = titlefy([marketName, marketPrice]);
if (marketId) {
navigate(Links[Routes.MARKET](marketId), {
if (marketId) {
navigate(Links[Routes.MARKET](marketId), {
replace: true,
});
} else if (data) {
const marketDataId = data[0]?.id;
if (marketDataId) {
navigate(Links[Routes.MARKET](marketDataId), {
replace: true,
});
update({ marketId });
if (pageTitle !== newPageTitle) {
updateTitle(newPageTitle);
}
} else {
navigate(Links[Routes.MARKET]());
}
update({ shouldDisplayWelcomeDialog: true });
}
}, [data, navigate, update, pageTitle, updateTitle]);
}, [marketId, data, navigate, update]);
return (
<AsyncRenderer data={data} loading={loading} error={error}>
+20 -21
View File
@@ -32,29 +32,23 @@ export interface SingleMarketData extends SingleMarketFieldsFragment {
}
export const Market = () => {
const params = useParams();
const { marketId } = useParams();
const navigate = useNavigate();
const marketId = params.marketId;
const { w } = useWindowSize();
const { update } = useGlobalStore((store) => ({
update: store.update,
}));
const update = useGlobalStore((store) => store.update);
const lastMarketId = useGlobalStore((store) => store.marketId);
const { pageTitle, updateTitle } = usePageTitleStore((store) => ({
pageTitle: store.pageTitle,
updateTitle: store.updateTitle,
}));
const pageTitle = usePageTitleStore((store) => store.pageTitle);
const updateTitle = usePageTitleStore((store) => store.updateTitle);
const onSelect = useCallback(
(id: string) => {
if (id && id !== marketId) {
update({ marketId: id });
navigate(Links[Routes.MARKET](id));
}
},
[marketId, update, navigate]
[marketId, navigate]
);
const variables = useMemo(
@@ -64,12 +58,22 @@ export const Market = () => {
[marketId]
);
const updateMarketId = useCallback(
({ data }: { data: { id?: string } | null }) => {
if (data?.id && data.id !== lastMarketId) {
update({ marketId: data.id });
}
return true;
},
[update, lastMarketId]
);
const { data, error, loading } = useDataProvider<
SingleMarketFieldsFragment,
never
>({
dataProvider: marketProvider,
variables,
update: updateMarketId,
skip: !marketId,
});
@@ -104,11 +108,10 @@ export const Market = () => {
}
return <TradePanels market={data} onSelect={onSelect} />;
}, [w, data, onSelect]);
if (!data && marketId) {
return (
<Splash>
<p>{t('Not found')}</p>
<p>{t('Market not found')}</p>
</Splash>
);
}
@@ -119,13 +122,9 @@ export const Market = () => {
error={error}
data={data || undefined}
noDataCondition={(data) => false}
render={(data) => {
if (!data && marketId) {
return <Splash>{t('Market not found')}</Splash>;
}
return <>{tradeView}</>;
}}
/>
>
{tradeView}
</AsyncRenderer>
);
};
@@ -17,6 +17,7 @@ import { Last24hPriceChange } from '../../components/last-24h-price-change';
import { Last24hVolume } from '../../components/last-24h-volume';
import { MarketState } from '../../components/market-state';
import { MarketTradingMode } from '../../components/market-trading-mode';
import { MarketLiquiditySupplied } from '../../components/liquidity-supplied';
interface TradeMarketHeaderProps {
market: SingleMarketFieldsFragment | null;
@@ -96,6 +97,10 @@ export const TradeMarketHeader = ({
</HeaderStat>
) : null}
<MarketProposalNotification marketId={market?.id} />
<MarketLiquiditySupplied
marketId={market?.id}
assetDecimals={asset?.decimals || 0}
/>
</Header>
);
};
@@ -1,18 +1,15 @@
import { useCallback } from 'react';
import { MarketsContainer } from '@vegaprotocol/market-list';
import { useGlobalStore } from '../../stores';
import { useNavigate } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
export const Markets = () => {
const navigate = useNavigate();
const { update } = useGlobalStore((store) => ({ update: store.update }));
const handleOnSelect = useCallback(
(marketId: string) => {
update({ marketId });
navigate(Links[Routes.MARKET](marketId));
},
[update, navigate]
[navigate]
);
return <MarketsContainer onSelect={handleOnSelect} />;
@@ -0,0 +1 @@
export * from './liquidity-supplied';
@@ -0,0 +1,130 @@
import { useCallback, useMemo, useState } from 'react';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
NetworkParams,
t,
useDataProvider,
useNetworkParams,
} from '@vegaprotocol/react-helpers';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
SingleMarketFieldsFragment,
} from '@vegaprotocol/market-list';
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
import { HeaderStat } from '../header';
import { Link } from '@vegaprotocol/ui-toolkit';
import BigNumber from 'bignumber.js';
import { useCheckLiquidityStatus } from '@vegaprotocol/liquidity';
import { DataGrid } from '@vegaprotocol/react-helpers';
interface Props {
marketId?: string;
noUpdate?: boolean;
assetDecimals: number;
}
export const MarketLiquiditySupplied = ({
marketId,
assetDecimals,
noUpdate = false,
}: Props) => {
const [market, setMarket] = useState<MarketData>();
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcySiskas,
NetworkParams.market_liquidity_targetstake_triggering_ratio,
]);
const stakeToCcyVolume = Number(params.market_liquidity_stakeToCcySiskas);
const triggeringRatio = Number(
params.market_liquidity_targetstake_triggering_ratio
);
const variables = useMemo(
() => ({
marketId: marketId,
}),
[marketId]
);
const { data } = useDataProvider<SingleMarketFieldsFragment, never>({
dataProvider: marketProvider,
variables,
skip: !marketId,
});
const update = useCallback(
({ data: marketData }: { data: MarketData | null }) => {
if (!noUpdate && marketData) {
setMarket(marketData);
}
return true;
},
[noUpdate]
);
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
dataProvider: marketDataProvider,
update,
variables,
skip: noUpdate || !marketId || !data,
});
const supplied = market?.suppliedStake
? addDecimalsFormatNumber(
new BigNumber(market?.suppliedStake)
.multipliedBy(stakeToCcyVolume || 1)
.toString(),
assetDecimals
)
: '-';
const { percentage } = useCheckLiquidityStatus({
suppliedStake: market?.suppliedStake || 0,
targetStake: market?.targetStake || 0,
triggeringRatio,
});
const compiledGrid = [
{
label: t('Supplied stake'),
value: market?.suppliedStake
? addDecimalsFormatNumber(
new BigNumber(market?.suppliedStake).toString(),
assetDecimals
)
: '-',
},
{
label: t('Target stake'),
value: market?.targetStake
? addDecimalsFormatNumber(
new BigNumber(market?.targetStake).toString(),
assetDecimals
)
: '-',
},
];
const description = (
<section>
{compiledGrid && <DataGrid grid={compiledGrid} />}
<br />
<Link href={`/#/liquidity/${marketId}`} data-testid="view-liquidity-link">
{t('View liquidity provision table')}
</Link>
</section>
);
return (
<HeaderStat
heading={t('Liquidity supplied')}
description={description}
testId="liquidity-supplied"
>
{/* <Indicator variant={status} /> */}
{supplied} ({formatNumberPercentage(percentage, 2)})
</HeaderStat>
);
};
@@ -1,4 +1,4 @@
import React, { useMemo, useState, useCallback } from 'react';
import React, { useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import { Dialog } from '@vegaprotocol/ui-toolkit';
import {
@@ -12,59 +12,50 @@ import * as constants from '../constants';
import { RiskNoticeDialog } from './risk-notice-dialog';
import { WelcomeNoticeDialog } from './welcome-notice-dialog';
import { WelcomeLandingDialog } from './welcome-landing-dialog';
import { useGlobalStore } from '../../stores';
interface DialogConfig {
open?: boolean;
content: React.ReactNode;
title?: string;
size?: 'small' | 'medium';
onClose: () => void;
}
export const WelcomeDialog = () => {
const { pathname } = useLocation();
const { VEGA_ENV } = useEnvironment();
const [dialog, setDialog] = useState<DialogConfig | null>(null);
const onClose = useCallback(() => {
setDialog(null);
}, [setDialog]);
let dialogContent: React.ReactNode = null;
let title = '';
let size: 'small' | 'medium' = 'small';
const [riskAccepted] = useLocalStorage(constants.RISK_ACCEPTED_KEY);
const { data } = useDataProvider({
dataProvider: activeMarketsProvider,
});
useMemo(() => {
switch (true) {
case riskAccepted !== 'true' && VEGA_ENV === Networks.MAINNET:
setDialog({
content: <RiskNoticeDialog onClose={onClose} />,
title: t('WARNING'),
size: 'medium',
onClose,
});
break;
case pathname === '/' && data?.length === 0:
setDialog({
content: <WelcomeNoticeDialog />,
onClose,
});
break;
case pathname === '/' && (data?.length || 0) > 0:
setDialog({
content: <WelcomeLandingDialog onClose={onClose} />,
onClose,
});
break;
}
}, [onClose, data?.length, riskAccepted, pathname, VEGA_ENV, setDialog]);
return dialog ? (
const { update, shouldDisplayWelcomeDialog } = useGlobalStore((store) => ({
update: store.update,
shouldDisplayWelcomeDialog: store.shouldDisplayWelcomeDialog,
}));
const isRiskDialogNeeded =
riskAccepted !== 'true' && VEGA_ENV === Networks.MAINNET;
const isWelcomeDialogNeeded = pathname === '/' || shouldDisplayWelcomeDialog;
const onClose = useCallback(() => {
update({ shouldDisplayWelcomeDialog: isRiskDialogNeeded });
// eslint-disable-next-line react-hooks/exhaustive-deps
dialogContent = null;
}, [update, isRiskDialogNeeded]);
if (isRiskDialogNeeded) {
dialogContent = <RiskNoticeDialog onClose={onClose} />;
title = t('WARNING');
size = 'medium';
} else if (isWelcomeDialogNeeded && data?.length === 0) {
dialogContent = <WelcomeNoticeDialog />;
} else if (isWelcomeDialogNeeded && (data?.length || 0) > 0) {
dialogContent = <WelcomeLandingDialog onClose={onClose} />;
}
return (
<Dialog
open={Boolean(dialog.content)}
title={dialog.title}
size={dialog.size}
onChange={dialog.onClose}
open={Boolean(dialogContent)}
title={title}
size={size}
onChange={onClose}
>
{dialog.content}
{dialogContent}
</Dialog>
) : null;
);
};
@@ -12,7 +12,6 @@ import {
} from '../select-market';
import { WelcomeDialogHeader } from './welcome-dialog-header';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { useGlobalStore } from '../../stores';
import { ProposedMarkets } from './proposed-markets';
import { Links, Routes } from '../../pages/client-router';
@@ -27,18 +26,13 @@ export const SelectMarketLandingTable = ({
const navigate = useNavigate();
const marketId = params.marketId;
const { update } = useGlobalStore((store) => ({
update: store.update,
}));
const onSelect = useCallback(
(id: string) => {
if (id && id !== marketId) {
update({ marketId: id });
navigate(Links[Routes.MARKET](id));
}
},
[marketId, update, navigate]
[marketId, navigate]
);
const onSelectMarket = useCallback(
+2 -2
View File
@@ -3,7 +3,7 @@ import type { RouteObject } from 'react-router-dom';
import { useRoutes } from 'react-router-dom';
import dynamic from 'next/dynamic';
import { t } from '@vegaprotocol/react-helpers';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import trimEnd from 'lodash/trimEnd';
const LazyHome = dynamic(() => import('../client-pages/home'), {
@@ -91,7 +91,7 @@ export const ClientRouter = () => {
<Suspense
fallback={
<div className="w-full h-full flex justify-center items-center">
{t('Loading...')}
<Loader />
</div>
}
>
+11 -4
View File
@@ -1,10 +1,12 @@
import { LocalStorage } from '@vegaprotocol/react-helpers';
import create from 'zustand';
import produce from 'immer';
interface GlobalStore {
networkSwitcherDialog: boolean;
marketId: string | null;
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
shouldDisplayWelcomeDialog: boolean;
}
interface PageTitleStore {
@@ -15,10 +17,15 @@ interface PageTitleStore {
export const useGlobalStore = create<GlobalStore>((set) => ({
networkSwitcherDialog: false,
marketId: LocalStorage.getItem('marketId') || null,
update: (state) => {
set(state);
if (state.marketId) {
LocalStorage.setItem('marketId', state.marketId);
shouldDisplayWelcomeDialog: false,
update: (newState) => {
set(
produce((state: GlobalStore) => {
Object.assign(state, newState);
})
);
if (newState.marketId) {
LocalStorage.setItem('marketId', newState.marketId);
}
},
}));
+1 -1
View File
@@ -213,7 +213,7 @@ export const AssetDetailsTable = ({
}: AssetDetailsTableProps) => {
const longStringModifiers = (key: AssetDetail, value: string) =>
(value && key === AssetDetail.CONTRACT_ADDRESS) || key === AssetDetail.ID
? { className: 'truncate', title: value }
? { className: 'break-all', title: value }
: {};
const details = rows.map((r) => ({
+5 -1
View File
@@ -10,7 +10,10 @@ import { addVegaWalletReceiveFaucetedAsset } from './lib/commands/vega-wallet-re
import { addContainsExactly } from './lib/commands/contains-exactly';
import { addGetNetworkParameters } from './lib/commands/get-network-parameters';
import { addUpdateCapsuleMultiSig } from './lib/commands/add-validators-to-multisig';
import { addVegaWalletConnect } from './lib/commands/vega-wallet-connect';
import {
addVegaWalletConnect,
addSetVegaWallet,
} from './lib/commands/vega-wallet-connect';
import { addMockTransactionResponse } from './lib/commands/mock-transaction-response';
addGetTestIdcommand();
@@ -26,6 +29,7 @@ addContainsExactly();
addGetNetworkParameters();
addUpdateCapsuleMultiSig();
addVegaWalletConnect();
addSetVegaWallet();
addMockTransactionResponse();
export { mockConnectWallet } from './lib/commands/vega-wallet-connect';
@@ -7,6 +7,10 @@ declare global {
interface Chainable<Subject> {
connectVegaWallet(): void;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
setVegaWallet(): void;
}
}
}
@@ -21,7 +25,6 @@ export const mockConnectWallet = () => {
};
export function addVegaWalletConnect() {
// @ts-ignore - ignoring Cypress type error which gets resolved when Cypress uses the command
Cypress.Commands.add('connectVegaWallet', () => {
mockConnectWallet();
cy.highlight(`Connecting Vega Wallet`);
@@ -38,3 +41,18 @@ export function addVegaWalletConnect() {
cy.get('[data-testid=dialog-content]').should('not.exist');
});
}
export function addSetVegaWallet() {
Cypress.Commands.add('setVegaWallet', () => {
cy.window().then((win) => {
win.localStorage.setItem(
'vega_wallet_config',
JSON.stringify({
token: Cypress.env('VEGA_WALLET_API_TOKEN'),
connector: 'jsonRpc',
url: 'http://localhost:1789',
})
);
});
});
}
@@ -1,7 +1,6 @@
import { FormGroup, Input, InputError } from '@vegaprotocol/ui-toolkit';
import { t, toDecimal } from '@vegaprotocol/react-helpers';
import { t, toDecimal, validateAmount } from '@vegaprotocol/react-helpers';
import type { DealTicketAmountProps } from './deal-ticket-amount';
import { validateAmount } from '../../utils';
export type DealTicketLimitAmountProps = Omit<
DealTicketAmountProps,
@@ -2,9 +2,10 @@ import {
addDecimalsFormatNumber,
t,
toDecimal,
validateAmount,
} from '@vegaprotocol/react-helpers';
import { Input, InputError, Tooltip } from '@vegaprotocol/ui-toolkit';
import { isMarketInAuction, validateAmount } from '../../utils';
import { isMarketInAuction } from '../../utils';
import type { DealTicketAmountProps } from './deal-ticket-amount';
import { getMarketPrice } from '../../utils/get-price';
@@ -6,10 +6,10 @@ import {
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import * as Schema from '@vegaprotocol/types';
import { t } from '@vegaprotocol/react-helpers';
import { DataGrid, t } from '@vegaprotocol/react-helpers';
import { timeInForceLabel } from '@vegaprotocol/orders';
import type { MarketDealTicket } from '@vegaprotocol/market-list';
import { compileGridData, MarketDataGrid } from '../trading-mode-tooltip';
import { compileGridData } from '../trading-mode-tooltip';
import { MarketModeValidationType } from '../../constants';
interface TimeInForceSelectorProps {
@@ -78,9 +78,7 @@ export const TimeInForceSelector = ({
return (
<span>
{t('This market is in auction until it reaches')}{' '}
<Tooltip
description={<MarketDataGrid grid={compileGridData(market)} />}
>
<Tooltip description={<DataGrid grid={compileGridData(market)} />}>
<span>{t('sufficient liquidity')}</span>
</Tooltip>
{'. '}
@@ -95,9 +93,7 @@ export const TimeInForceSelector = ({
return (
<span>
{t('This market is in auction due to')}{' '}
<Tooltip
description={<MarketDataGrid grid={compileGridData(market)} />}
>
<Tooltip description={<DataGrid grid={compileGridData(market)} />}>
<span>{t('high price volatility')}</span>
</Tooltip>
{'. '}
@@ -1,9 +1,9 @@
import { FormGroup, InputError, Tooltip } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import { DataGrid, t } from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { Toggle } from '@vegaprotocol/ui-toolkit';
import type { MarketDealTicket } from '@vegaprotocol/market-list';
import { compileGridData, MarketDataGrid } from '../trading-mode-tooltip';
import { compileGridData } from '../trading-mode-tooltip';
import { MarketModeValidationType } from '../../constants';
interface TypeSelectorProps {
@@ -33,9 +33,7 @@ export const TypeSelector = ({
return (
<span>
{t('This market is in auction until it reaches')}{' '}
<Tooltip
description={<MarketDataGrid grid={compileGridData(market)} />}
>
<Tooltip description={<DataGrid grid={compileGridData(market)} />}>
<span>{t('sufficient liquidity')}</span>
</Tooltip>
{'. '}
@@ -48,9 +46,7 @@ export const TypeSelector = ({
return (
<span>
{t('This market is in auction due to')}{' '}
<Tooltip
description={<MarketDataGrid grid={compileGridData(market)} />}
>
<Tooltip description={<DataGrid grid={compileGridData(market)} />}>
<span>{t('high price volatility')}</span>
</Tooltip>
{'. '}
@@ -1,3 +1,4 @@
import type { DataGridProps } from '@vegaprotocol/react-helpers';
import {
t,
getDateTimeFormat,
@@ -6,7 +7,6 @@ import {
import * as Schema from '@vegaprotocol/types';
import { Link as UILink } from '@vegaprotocol/ui-toolkit';
import type { ReactNode } from 'react';
import type { MarketDataGridProps } from './market-data-grid';
import { Link } from 'react-router-dom';
import type { MarketDealTicket } from '@vegaprotocol/market-list';
@@ -14,7 +14,7 @@ export const compileGridData = (
market: MarketDealTicket,
onSelect?: (id: string) => void
): { label: ReactNode; value?: ReactNode }[] => {
const grid: MarketDataGridProps['grid'] = [];
const grid: DataGridProps['grid'] = [];
const isLiquidityMonitoringAuction =
market.data.marketTradingMode ===
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
@@ -1,3 +1,2 @@
export * from './market-data-grid';
export * from './trading-mode-tooltip';
export * from './compile-grid-data';
@@ -1,11 +1,10 @@
import type { ReactNode } from 'react';
import classNames from 'classnames';
import { useEnvironment } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/react-helpers';
import { DataGrid, t } from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { createDocsLinks } from '@vegaprotocol/react-helpers';
import { MarketDataGrid } from './market-data-grid';
type TradingModeTooltipProps = {
tradingMode: Schema.MarketTradingMode | null;
@@ -46,7 +45,7 @@ export const TradingModeTooltip = ({
</ExternalLink>
)}
</p>
{compiledGrid && <MarketDataGrid grid={compiledGrid} />}
{compiledGrid && <DataGrid grid={compiledGrid} />}
</section>
);
}
@@ -72,7 +71,7 @@ export const TradingModeTooltip = ({
</ExternalLink>
)}
</p>
{compiledGrid && <MarketDataGrid grid={compiledGrid} />}
{compiledGrid && <DataGrid grid={compiledGrid} />}
</section>
);
}
@@ -94,7 +93,7 @@ export const TradingModeTooltip = ({
</ExternalLink>
)}
</p>
{compiledGrid && <MarketDataGrid grid={compiledGrid} />}
{compiledGrid && <DataGrid grid={compiledGrid} />}
</section>
);
}
-1
View File
@@ -1,6 +1,5 @@
export * from './get-default-order';
export * from './is-market-in-auction';
export * from './validate-amount';
export * from './validate-expiration';
export * from './validate-market-state';
export * from './validate-market-trading-mode';
+2 -2
View File
@@ -29,7 +29,7 @@ import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import {
ETHEREUM_EAGER_CONNECT,
useWeb3ConnectStore,
ChainIdMap,
getChainName,
} from '@vegaprotocol/web3';
interface FormFields {
@@ -323,7 +323,7 @@ const FormButton = ({
);
} else if (chainId !== desiredChainId) {
console.log(chainId, desiredChainId);
const chainName = desiredChainId ? ChainIdMap[desiredChainId] : 'Unknown';
const chainName = getChainName(desiredChainId);
message = t(`This app only works on ${chainName}.`);
button = (
<Button
@@ -0,0 +1,22 @@
import { act, render, screen, waitFor } from '@testing-library/react';
import { Networks } from '../types';
import { EnvironmentProvider } from './use-environment';
describe('EnvironmentProvider', () => {
beforeAll(() => {
process.env['NX_MAINTENANCE_PAGE'] = 'true';
process.env['NX_VEGA_URL'] = 'https://vega.xyz';
process.env['NX_VEGA_ENV'] = Networks.TESTNET;
});
afterAll(() => {
process.env['NX_MAINTENANCE_PAGE'] = '';
});
it('EnvironmentProvider should return maintenance page', async () => {
await act(async () => {
render(<EnvironmentProvider />);
});
await waitFor(() => {
expect(screen.getByTestId('maintenance-page')).toBeInTheDocument();
});
});
});
@@ -6,7 +6,7 @@ import {
useContext,
useCallback,
} from 'react';
import { MaintenancePage } from '@vegaprotocol/ui-toolkit';
import { NodeSwitcherDialog } from '../components/node-switcher-dialog';
import { useConfig } from './use-config';
import { useNodes } from './use-nodes';
@@ -76,7 +76,10 @@ export const EnvironmentProvider = ({
}
}
);
const { state: nodes, clients } = useNodes(config);
const { state: nodes, clients } = useNodes(
config,
environment.MAINTENANCE_PAGE
);
const nodeKeys = Object.keys(nodes);
useEffect(() => {
@@ -128,6 +131,10 @@ export const EnvironmentProvider = ({
throw new Error(errorMessage);
}
if (environment.MAINTENANCE_PAGE) {
return <MaintenancePage />;
}
return (
<EnvironmentContext.Provider
value={{
+4 -6
View File
@@ -178,7 +178,7 @@ const reducer = (state: Record<string, NodeData>, action: Action) => {
}
};
export const useNodes = (config?: Configuration) => {
export const useNodes = (config?: Configuration, skip?: boolean) => {
const [clients, setClients] = useState<ClientCollection>({});
const [state, dispatch] = useReducer(reducer, getInitialState(config));
const configCacheKey = config?.hosts.join(';');
@@ -193,10 +193,8 @@ export const useNodes = (config?: Configuration) => {
}, []);
useEffect(() => {
const nodeUrlMap = (config?.hosts || []).reduce(
(acc, url) => ({ ...acc, [url]: url }),
{}
);
const hosts = skip ? [] : config?.hosts || [];
const nodeUrlMap = hosts.reduce((acc, url) => ({ ...acc, [url]: url }), {});
const { clients: newClients, subscriptions } = initializeNodes(
dispatch,
nodeUrlMap
@@ -208,7 +206,7 @@ export const useNodes = (config?: Configuration) => {
};
// use primitive cache key to prevent infinite rerender loop
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [configCacheKey]);
}, [configCacheKey, skip]);
useEffect(() => {
const allNodes = Object.keys(state);
@@ -38,6 +38,8 @@ const transformValue = (key: EnvKey, value?: string) => {
}
return {};
}
case 'MAINTENANCE_PAGE':
return ['true', '1', 'yes'].includes(value?.toLowerCase() || '');
default:
return value;
}
@@ -76,6 +78,10 @@ const getBundledEnvironmentValue = (key: EnvKey) => {
return process.env['NX_VEGA_DOCS_URL'];
case 'HOSTED_WALLET_URL':
return process.env['NX_HOSTED_WALLET_URL'];
case 'MAINTENANCE_PAGE':
return (
process.env['MAINTENANCE_PAGE'] || process.env['NX_MAINTENANCE_PAGE']
);
}
};
@@ -49,6 +49,7 @@ const schemaObject = {
message: 'The NX_ETHERSCAN_URL environment variable must be a valid url',
}),
HOSTED_WALLET_URL: z.optional(z.string()),
MAINTENANCE_PAGE: z.optional(z.boolean()),
};
export const ENV_KEYS = Object.keys(schemaObject) as Array<
@@ -0,0 +1,219 @@
import type { LiquidityProviderFeeShare } from '@vegaprotocol/types';
import { AccountType } from '@vegaprotocol/types';
import { getLiquidityProvision } from './liquidity-data-provider';
import type {
LiquidityProvisionFieldsFragment,
MarketLpQuery,
} from './__generated__/MarketLiquidity';
const input = {
liquidityProvisions: [
{
party: {
id: 'dde288688af2aeb5feb349dd72d3679a7a9be34c7375f6a4a48ef2f6140e7e59',
accountsConnection: {
edges: [
{
node: {
type: AccountType.ACCOUNT_TYPE_BOND,
balance: '18003328918633596575000',
__typename: 'AccountBalance',
},
__typename: 'AccountEdge',
},
],
__typename: 'AccountsConnection',
},
__typename: 'Party',
},
createdAt: '2022-12-16T09:28:29.071781Z',
updatedAt: '2023-01-04T22:13:27.761985Z',
commitmentAmount: '18003328918633596575000',
fee: '0.001',
status: 'STATUS_ACTIVE',
__typename: 'LiquidityProvision',
} as LiquidityProvisionFieldsFragment,
],
marketLiquidity: {
market: {
id: 'ccbd651b4a1167fd73c4a0340ac759fa0a31ca487ad46a13254b741ad71947ed',
decimalPlaces: 5,
positionDecimalPlaces: 3,
tradableInstrument: {
instrument: {
code: 'UNIDAI.MF21',
name: 'UNIDAI Monthly (Dec 2022)',
product: {
settlementAsset: {
id: '16ae5dbb1fd7aa2ddef725703bfe66b3647a4da7b844bfdd04e985756f53d9d6',
symbol: 'tDAI',
decimals: 18,
__typename: 'Asset',
},
__typename: 'Future',
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
data: {
market: {
id: 'ccbd651b4a1167fd73c4a0340ac759fa0a31ca487ad46a13254b741ad71947ed',
__typename: 'Market',
},
marketTradingMode: 'TRADING_MODE_CONTINUOUS',
suppliedStake: '18003328918633596575000',
openInterest: '89660',
targetStake: '70159269843504000000',
trigger: 'AUCTION_TRIGGER_UNSPECIFIED',
marketValueProxy: '18003328918633596575000',
__typename: 'MarketData',
},
__typename: 'Market',
},
} as MarketLpQuery,
liquidityFeeShare: [
{
party: {
id: 'dde288688af2aeb5feb349dd72d3679a7a9be34c7375f6a4a48ef2f6140e7e59',
__typename: 'Party',
},
equityLikeShare: '1',
averageEntryValuation: '12064118310408958216220.7224556301338111',
__typename: 'LiquidityProviderFeeShare',
} as LiquidityProviderFeeShare,
],
};
const result = [
{
__typename: 'LiquidityProvision',
assetDecimalPlaces: 18,
averageEntryValuation: '12064118310408958216220.7224556301338111',
balance: '1.8003328918633596575e+22',
commitmentAmount: '18003328918633596575000',
createdAt: '2022-12-16T09:28:29.071781Z',
equityLikeShare: '1',
fee: '0.001',
party: {
__typename: 'Party',
accountsConnection: {
__typename: 'AccountsConnection',
edges: [
{
__typename: 'AccountEdge',
node: {
__typename: 'AccountBalance',
balance: '18003328918633596575000',
type: 'ACCOUNT_TYPE_BOND',
},
},
],
},
id: 'dde288688af2aeb5feb349dd72d3679a7a9be34c7375f6a4a48ef2f6140e7e59',
},
status: 'STATUS_ACTIVE',
updatedAt: '2023-01-04T22:13:27.761985Z',
},
];
describe('getLiquidityProvision', () => {
it('should return an empty array when no data is provided', () => {
const data = getLiquidityProvision([], {}, []);
expect(data).toEqual([]);
});
it('should return correct array when correct liquidity provision parameters are provided', () => {
const data = getLiquidityProvision(
input.liquidityProvisions,
input.marketLiquidity,
input.liquidityFeeShare
);
expect(data).toStrictEqual(result);
});
it('should return empty array when no liquidity provision parameters are provided', () => {
const data = getLiquidityProvision(
[],
input.marketLiquidity,
input.liquidityFeeShare
);
expect(data).toStrictEqual([]);
});
it('should return empty array when no market lp query parameter is provided', () => {
const data = getLiquidityProvision(
input.liquidityProvisions,
{},
input.liquidityFeeShare
);
const result = [
{
__typename: 'LiquidityProvision',
assetDecimalPlaces: undefined,
averageEntryValuation: '12064118310408958216220.7224556301338111',
balance: '1.8003328918633596575e+22',
commitmentAmount: '18003328918633596575000',
createdAt: '2022-12-16T09:28:29.071781Z',
equityLikeShare: '1',
fee: '0.001',
party: {
__typename: 'Party',
accountsConnection: {
__typename: 'AccountsConnection',
edges: [
{
__typename: 'AccountEdge',
node: {
__typename: 'AccountBalance',
balance: '18003328918633596575000',
type: 'ACCOUNT_TYPE_BOND',
},
},
],
},
id: 'dde288688af2aeb5feb349dd72d3679a7a9be34c7375f6a4a48ef2f6140e7e59',
},
status: 'STATUS_ACTIVE',
updatedAt: '2023-01-04T22:13:27.761985Z',
},
];
expect(data).toStrictEqual(result);
});
it('should return empty array when no liquidity fee share param is provided', () => {
const data = getLiquidityProvision(
input.liquidityProvisions,
input.marketLiquidity,
[]
);
const result = [
{
__typename: 'LiquidityProvision',
commitmentAmount: '18003328918633596575000',
createdAt: '2022-12-16T09:28:29.071781Z',
fee: '0.001',
party: {
__typename: 'Party',
accountsConnection: {
__typename: 'AccountsConnection',
edges: [
{
__typename: 'AccountEdge',
node: {
__typename: 'AccountBalance',
balance: '18003328918633596575000',
type: 'ACCOUNT_TYPE_BOND',
},
},
],
},
id: 'dde288688af2aeb5feb349dd72d3679a7a9be34c7375f6a4a48ef2f6140e7e59',
},
status: 'STATUS_ACTIVE',
updatedAt: '2023-01-04T22:13:27.761985Z',
},
];
expect(data).toStrictEqual(result);
});
});
@@ -1,4 +1,4 @@
import { accountsDataProvider } from '@vegaprotocol/accounts';
import compact from 'lodash/compact';
import {
makeDataProvider,
makeDerivedDataProvider,
@@ -24,7 +24,6 @@ import type {
LiquidityProvisionsQuery,
LiquidityProvisionsUpdateSubscription,
} from './__generated__/MarketLiquidity';
import type { Account } from '@vegaprotocol/accounts';
import type { IterableElement } from 'type-fest';
export const liquidityProvisionsDataProvider = makeDataProvider<
@@ -157,22 +156,16 @@ export const lpAggregatedDataProvider = makeDerivedDataProvider(
liquidityProvisionsDataProvider(callback, client, {
marketId: variables?.marketId,
}),
(callback, client, variables) =>
accountsDataProvider(callback, client, {
partyId: variables?.partyId || '', // party Id can not be null
}),
marketLiquidityDataProvider,
liquidityFeeShareDataProvider,
],
([
liquidityProvisions,
accounts,
marketLiquidity,
liquidityFeeShare,
]): LiquidityProvisionData[] => {
return getLiquidityProvision(
liquidityProvisions,
accounts,
marketLiquidity,
liquidityFeeShare
);
@@ -181,7 +174,6 @@ export const lpAggregatedDataProvider = makeDerivedDataProvider(
export const getLiquidityProvision = (
liquidityProvisions: LiquidityProvisionFieldsFragment[],
accounts: Account[],
marketLiquidity: MarketLpQuery,
liquidityFeeShare: LiquidityProviderFeeShareFieldsFragment[]
): LiquidityProvisionData[] => {
@@ -189,26 +181,27 @@ export const getLiquidityProvision = (
const market = marketLiquidity?.market;
const feeShare = liquidityFeeShare.find((f) => f.party.id === lp.party.id);
if (!feeShare) return lp;
const bondAccounts = accounts?.filter(
(a) =>
a?.type === Schema.AccountType.ACCOUNT_TYPE_BOND &&
(!a.party?.id || a.party?.id === lp.party.id)
const accounts = compact(lp.party.accountsConnection?.edges).map(
(e) => e.node
);
const lpData: LiquidityProvisionData = {
const bondAccounts = accounts?.filter(
(a) => a?.type === Schema.AccountType.ACCOUNT_TYPE_BOND
);
const balance =
bondAccounts
?.reduce(
(acc, a) => acc.plus(new BigNumber(a.balance ?? 0)),
new BigNumber(0)
)
.toString() || '0';
return {
...lp,
averageEntryValuation: feeShare?.averageEntryValuation,
equityLikeShare: feeShare?.equityLikeShare,
assetDecimalPlaces:
market?.tradableInstrument.instrument.product.settlementAsset.decimals,
balance:
bondAccounts
?.reduce(
(acc, a) => acc.plus(new BigNumber(a.balance ?? 0)),
new BigNumber(0)
)
.toString() ?? '0',
balance,
};
return lpData;
});
};
@@ -31,6 +31,8 @@ import {
getTargetStake,
} from './utils/liquidity-utils';
import type { Provider, LiquidityProvisionMarket } from './utils';
import { proposalsListDataProvider } from '@vegaprotocol/governance';
import type { Proposal } from '@vegaprotocol/types';
export interface FeeLevels {
commitmentAmount: number;
@@ -44,6 +46,7 @@ export type Market = MarketWithData &
dayVolume: string;
liquidityCommitted: number;
volumeChange: string;
proposal?: Proposal;
};
export interface Markets {
@@ -63,7 +66,8 @@ const getData = (
export const addData = (
markets: (MarketWithData & MarketWithCandles)[],
marketsCandles24hAgo: MarketCandles[],
marketsLiquidity: LiquidityProvisionMarket[]
marketsLiquidity: LiquidityProvisionMarket[],
proposals: Proposal[]
) => {
return markets.map((market) => {
const dayVolume = calcDayVolume(market.candles);
@@ -76,6 +80,9 @@ export const addData = (
marketsLiquidity
) as Provider[];
const proposalForMarket =
proposals && proposals.find((p) => p.id === market.id);
return {
...market,
dayVolume,
@@ -83,6 +90,7 @@ export const addData = (
liquidityCommitted: sumLiquidityCommitted(liquidityProviders),
feeLevels: getFeeLevels(liquidityProviders) || [],
target: getTargetStake(market.id, marketsLiquidity),
proposal: proposalForMarket,
};
});
};
@@ -106,12 +114,14 @@ const liquidityProvisionProvider = makeDerivedDataProvider<Markets, never>(
interval: Schema.Interval.INTERVAL_I1D,
}),
liquidityMarketsProvider,
proposalsListDataProvider,
],
(parts) => {
const data = addData(
parts[0] as (MarketWithData & MarketWithCandles)[],
parts[1] as MarketCandles[],
parts[2] as LiquidityProvisionMarket[]
parts[2] as LiquidityProvisionMarket[],
parts[3] as Proposal[]
);
return { markets: data };
}
@@ -1,3 +1,6 @@
import { renderHook } from '@testing-library/react';
import { Intent } from '@vegaprotocol/ui-toolkit';
import BigNumber from 'bignumber.js';
import {
formatWithAsset,
sumLiquidityCommitted,
@@ -6,6 +9,7 @@ import {
getCandle24hAgo,
getChange,
EMPTY_VALUE,
useCheckLiquidityStatus,
} from './liquidity-utils';
const CANDLES_1 = [
@@ -118,3 +122,50 @@ describe('getChange', () => {
expect(result).toEqual(EMPTY_VALUE);
});
});
describe('useCheckLiquidityStatus', () => {
it('should return amber if liquidity is enough', () => {
const { result } = renderHook(() =>
useCheckLiquidityStatus({
suppliedStake: '60',
targetStake: '100',
triggeringRatio: '0.5',
})
);
expect(result.current).toEqual({
status: Intent.Warning,
percentage: new BigNumber('60'),
});
});
it('should return red if liquidity is not enough', () => {
const { result } = renderHook(() =>
useCheckLiquidityStatus({
suppliedStake: '60',
targetStake: '100',
triggeringRatio: '1',
})
);
expect(result.current).toEqual({
status: Intent.Danger,
percentage: new BigNumber('60'),
});
});
it('should return green if liquidity is enough', () => {
const { result } = renderHook(() =>
useCheckLiquidityStatus({
suppliedStake: '101',
targetStake: '100',
triggeringRatio: '1',
})
);
expect(result.current).toEqual({
status: Intent.Success,
percentage: new BigNumber('101'),
});
});
});
@@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js';
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
import type { MarketNodeFragment } from './../__generated__/MarketsLiquidity';
import { Intent } from '@vegaprotocol/ui-toolkit';
export type LiquidityProvisionMarket = MarketNodeFragment;
@@ -117,3 +118,46 @@ export const getTargetStake = (
) => {
return markets.find((m) => m.id === marketId)?.data?.targetStake || '0';
};
export const useCheckLiquidityStatus = ({
suppliedStake,
targetStake,
triggeringRatio,
}: {
suppliedStake: string | number;
targetStake: string | number;
triggeringRatio: string | number;
}): {
status: Intent;
percentage: BigNumber;
} => {
// percentage supplied
const percentage = new BigNumber(suppliedStake)
.dividedBy(targetStake)
.multipliedBy(100);
// IF supplied_stake >= target_stake THEN
if (new BigNumber(suppliedStake).gte(new BigNumber(targetStake))) {
// show a green status, e.g. "🟢 $13,666,999 liquidity supplied"
return {
status: Intent.Success,
percentage,
};
// ELSE IF supplied_stake > NETPARAM[market.liquidity.targetstake.triggering.ratio] * target_stake THEN
} else if (
new BigNumber(suppliedStake).gte(
new BigNumber(targetStake).multipliedBy(triggeringRatio)
)
) {
// show an amber status, e.g. "🟠 $3,456,123 liquidity supplied"
return {
status: Intent.Warning,
percentage,
};
// ELSE show a red status, e.g. "🔴 $600,002 liquidity supplied"
} else {
return {
status: Intent.Danger,
percentage,
};
}
};
+4 -2
View File
@@ -3,14 +3,14 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketDataUpdateFieldsFragment = { __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string };
export type MarketDataUpdateFieldsFragment = { __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null };
export type MarketDataUpdateSubscriptionVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketDataUpdateSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string }> };
export type MarketDataUpdateSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null }> };
export type MarketDataFieldsFragment = { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } };
@@ -35,6 +35,8 @@ export const MarketDataUpdateFieldsFragmentDoc = gql`
indicativePrice
bestStaticBidPrice
bestStaticOfferPrice
targetStake
suppliedStake
}
`;
export const MarketDataFieldsFragmentDoc = gql`
@@ -11,6 +11,8 @@ fragment MarketDataUpdateFields on ObservableMarketData {
indicativePrice
bestStaticBidPrice
bestStaticOfferPrice
targetStake
suppliedStake
}
subscription MarketDataUpdate($marketId: ID!) {
@@ -31,7 +31,7 @@ const compileData = (data?: StatsQuery) => {
value.forEach((x) => {
const stat = {
...x,
value: statData,
value: statData || '-',
};
stat.promoted ? acc.promoted.push(stat) : acc.table.push(stat);
@@ -75,7 +75,7 @@ export const StatsManager = ({ className }: StatsManagerProps) => {
return (
<PromotedStatsItem
title={stat.title}
value={stat.value}
value={stat.value || '-'}
formatter={stat.formatter}
goodThreshold={stat.goodThreshold}
description={stat.description}
@@ -92,7 +92,7 @@ export const StatsManager = ({ className }: StatsManagerProps) => {
return (
<TableRow
title={stat.title}
value={stat.value}
value={stat.value || '-'}
formatter={stat.formatter}
goodThreshold={stat.goodThreshold}
description={stat.description}
+6 -12
View File
@@ -48,13 +48,7 @@ export const statsFields: { [key in keyof Stats]: StatFields[] } = {
description: t('The total number of nodes registered on the network'),
},
],
inactiveNodes: [
{
title: t('Inactive nodes'),
goodThreshold: (totalInactive: number) => totalInactive < 1,
description: t('Nodes that are registered but not validating'),
},
],
inactiveNodes: [],
stakedTotal: [
{
title: t('Total staked'),
@@ -174,11 +168,11 @@ export const statsFields: { [key in keyof Stats]: StatFields[] } = {
title: t('Uptime'),
formatter: (t: string) => {
if (!t) {
return;
return '-';
}
const date = new Date(t);
if (!isValidDate(date)) {
return;
return '-';
}
const secSinceStart = (new Date().getTime() - date.getTime()) / 1000;
const days = Math.floor(secSinceStart / 60 / 60 / 24);
@@ -194,13 +188,13 @@ export const statsFields: { [key in keyof Stats]: StatFields[] } = {
title: t('Up since'),
formatter: (t: string) => {
if (!t) {
return;
return '-';
}
const date = new Date(t);
if (!isValidDate(date)) {
return;
return '-';
}
return getDateTimeFormat().format(date);
return getDateTimeFormat().format(date) || '-';
},
description: t('Genesis'),
},
@@ -5,6 +5,7 @@ import {
getDateTimeFormat,
addDecimal,
addDecimalsFormatNumber,
validateAmount,
} from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import {
@@ -50,6 +51,7 @@ export const OrderEditDialog = ({
const step = toDecimal(order.market?.decimalPlaces ?? 0);
const stepSize = toDecimal(order.market?.positionDecimalPlaces ?? 0);
return (
<Dialog
open={isOpen}
@@ -97,6 +99,7 @@ export const OrderEditDialog = ({
onSubmit={handleSubmit(onSubmit)}
data-testid="edit-order"
className="w-full mt-4"
noValidate
>
<div className="flex flex-col md:flex-row gap-4">
<FormGroup label={t('Price')} labelFor="limitPrice" className="grow">
@@ -110,6 +113,7 @@ export const OrderEditDialog = ({
Number(value) > 0
? true
: t('The price cannot be negative'),
validate: validateAmount(step, t('Price')),
},
})}
id="limitPrice"
@@ -129,6 +133,7 @@ export const OrderEditDialog = ({
validate: {
min: (value) =>
Number(value) > 0 ? true : t('The size cannot be negative'),
validate: validateAmount(stepSize, t('Size')),
},
})}
id="size"
@@ -16,6 +16,7 @@ import {
} from '../mocks/generate-orders';
const defaultProps: OrderListTableProps = {
hasActiveOrder: true,
rowData: [],
setEditOrder: jest.fn(),
cancel: jest.fn(),
@@ -17,6 +17,7 @@ const Template: Story = (args) => {
return (
<div style={{ height: 1000 }}>
<OrderListTable
hasActiveOrder
rowData={args.data}
cancel={cancel}
cancelAll={cancel}
@@ -46,6 +47,7 @@ const Template2: Story = (args) => {
<>
<div style={{ height: 1000 }}>
<OrderListTable
hasActiveOrder
rowData={args.data}
cancel={cancel}
cancelAll={cancel}
@@ -24,6 +24,7 @@ import BigNumber from 'bignumber.js';
import { forwardRef, useState } from 'react';
import type { TypedDataAgGrid } from '@vegaprotocol/ui-toolkit';
import { useOrderCancel } from '../../order-hooks/use-order-cancel';
import { useHasActiveOrder } from '../../order-hooks/use-has-active-order';
import { useOrderEdit } from '../../order-hooks/use-order-edit';
import { OrderFeedback } from '../order-feedback';
import { OrderEditDialog } from './order-edit-dialog';
@@ -78,11 +79,13 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
const [editOrder, setEditOrder] = useState<Order | null>(null);
const orderCancel = useOrderCancel();
const orderEdit = useOrderEdit(editOrder);
const hasActiveOrder = useHasActiveOrder(props.marketId);
return (
<>
<OrderListTable
{...props}
hasActiveOrder={hasActiveOrder}
cancelAll={() => {
orderCancel.cancel({
marketId: props.marketId,
@@ -144,11 +147,12 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
export type OrderListTableProps = OrderListProps & {
cancel: (order: Order) => void;
cancelAll: () => void;
hasActiveOrder: boolean;
setEditOrder: (order: Order) => void;
};
export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
({ cancel, cancelAll, setEditOrder, ...props }, ref) => {
({ cancel, cancelAll, setEditOrder, hasActiveOrder, ...props }, ref) => {
return (
<AgGrid
ref={ref}
@@ -365,15 +369,17 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
cellRenderer={({ data, node }: VegaICellRendererParams<Order>) => {
if (node?.rowPinned) {
return (
<div className="flex gap-2 items-center h-full justify-end">
<Button
size="xs"
data-testid="cancelAll"
onClick={() => cancelAll()}
>
{t('Cancel all')}
</Button>
</div>
hasActiveOrder && (
<div className="flex gap-2 items-center h-full justify-end">
<Button
size="xs"
data-testid="cancelAll"
onClick={() => cancelAll()}
>
{t('Cancel all')}
</Button>
</div>
)
);
}
if (isOrderAmendable(data)) {
+1
View File
@@ -1,4 +1,5 @@
export * from './__generated__/OrderEvent';
export * from './use-has-active-order';
export * from './use-order-cancel';
export * from './use-order-submit';
export * from './use-order-edit';
@@ -0,0 +1,47 @@
import { useState, useEffect, useMemo } from 'react';
import {
useOrdersUpdateSubscription,
useOrdersQuery,
} from '../components/order-data-provider/__generated__/Orders';
import { useVegaWallet } from '@vegaprotocol/wallet';
import * as Schema from '@vegaprotocol/types';
export const useHasActiveOrder = (marketId?: string) => {
const { pubKey } = useVegaWallet();
const skip = !pubKey;
const [hasActiveOrder, setHasActiveOrder] = useState(false);
const subscriptionVariables = useMemo(
() => ({
partyId: pubKey || '',
marketId,
}),
[pubKey, marketId]
);
const queryVariables = useMemo(
() => ({
...subscriptionVariables,
pagination: { first: 1 },
filter: { status: [Schema.OrderStatus.STATUS_ACTIVE] },
}),
[subscriptionVariables]
);
const { refetch, data, loading } = useOrdersQuery({
variables: queryVariables,
fetchPolicy: 'no-cache',
skip,
});
useEffect(() => {
if (!loading && data) {
setHasActiveOrder(!!data.party?.ordersConnection?.edges?.length);
}
}, [loading, data]);
useOrdersUpdateSubscription({
variables: subscriptionVariables,
onData: () => refetch(),
skip,
});
return hasActiveOrder;
};
@@ -102,6 +102,9 @@ export const NetworkParams = {
spam_protection_voting_min_tokens: 'spam_protection_voting_min_tokens',
spam_protection_proposal_min_tokens: 'spam_protection_proposal_min_tokens',
market_liquidity_stakeToCcySiskas: 'market_liquidity_stakeToCcySiskas',
market_liquidity_stakeToCcyVolume: 'market_liquidity_stakeToCcyVolume',
market_liquidity_targetstake_triggering_ratio:
'market_liquidity_targetstake_triggering_ratio',
} as const;
type Params = typeof NetworkParams;

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