Compare commits
88
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
996c4ac4d5 | ||
|
|
560e4a4040 | ||
|
|
d98ce18f9e | ||
|
|
0fd1739ce5 | ||
|
|
cf43566bde | ||
|
|
24a9712e91 | ||
|
|
03f5b5e78b | ||
|
|
9552fbc033 | ||
|
|
74a4a60195 | ||
|
|
ef35999091 | ||
|
|
cba44d7eb1 | ||
|
|
c5b72e5a64 | ||
|
|
863c288e0d | ||
|
|
a97512cc7b | ||
|
|
0884654c1e | ||
|
|
5b4440f767 | ||
|
|
cfbea6e894 | ||
|
|
774a7d697f | ||
|
|
dbb08acba3 | ||
|
|
6db201c1de | ||
|
|
54cd8324ed | ||
|
|
562e2b5cb2 | ||
|
|
ca2647d4d9 | ||
|
|
bbdc64582a | ||
|
|
dabae31bfd | ||
|
|
5b42a6ad35 | ||
|
|
870ae13b4c | ||
|
|
4c32a21f28 | ||
|
|
2ea603e59e | ||
|
|
3e485e3841 | ||
|
|
7766b3f118 | ||
|
|
8db3b01284 | ||
|
|
4fbd2e5eba | ||
|
|
53d3d5ac1c | ||
|
|
35de756120 | ||
|
|
f89cf31982 | ||
|
|
8976a40dc8 | ||
|
|
7669aeb218 | ||
|
|
6e996221b2 | ||
|
|
117c0c1306 | ||
|
|
e56d7bc2d5 | ||
|
|
85ec66a9e2 | ||
|
|
ba14ead8fc | ||
|
|
1211d27011 | ||
|
|
904a1b7805 | ||
|
|
66a8c659b4 | ||
|
|
f1a437500e | ||
|
|
29ced0b460 | ||
|
|
e45265ac71 | ||
|
|
cc3c339cc3 | ||
|
|
6f95989569 | ||
|
|
14f0c5a5fb | ||
|
|
0167a42990 | ||
|
|
1d65c4791c | ||
|
|
8a1fe6e5b1 | ||
|
|
437f7ef807 | ||
|
|
2b3683d4a4 | ||
|
|
6c84153cdb | ||
|
|
ccce5a2848 | ||
|
|
5673620fbd | ||
|
|
41d6940b89 | ||
|
|
84150bec8c | ||
|
|
5a311d14c9 | ||
|
|
0f152a4b30 | ||
|
|
45d8b537a3 | ||
|
|
dc41be5715 | ||
|
|
a85cf66419 | ||
|
|
6e82e917ac | ||
|
|
8dfdccd4aa | ||
|
|
82833cc06a | ||
|
|
aac3799b14 | ||
|
|
0d1c9ff0f1 | ||
|
|
f8eec8a056 | ||
|
|
ee6f26d473 | ||
|
|
de62d2d972 | ||
|
|
955e233c46 | ||
|
|
0bafa4c7d5 | ||
|
|
045529dc38 | ||
|
|
5ca3e4d90f | ||
|
|
9048d41f72 | ||
|
|
d731c4797c | ||
|
|
4a931ca134 | ||
|
|
3398b15d8a | ||
|
|
1e98ecbd21 | ||
|
|
a9c7c2bb46 | ||
|
|
ba2c71de67 | ||
|
|
15d2aed343 | ||
|
|
0284eaf9a2 |
@@ -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');
|
||||
|
||||
@@ -30,7 +30,6 @@ describe('Market trade with wallet disconnected', { tags: '@smoke' }, () => {
|
||||
cy.mockConsole();
|
||||
cy.visit(`/trading/${marketId}`);
|
||||
cy.wait('@Market');
|
||||
console.log('marketId', marketId);
|
||||
});
|
||||
it('should not display steps', () => {
|
||||
cy.getByTestId('trading-connect-wallet')
|
||||
@@ -51,8 +50,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');
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
|
||||
describe('Portfolio page - wallet', { tags: '@smoke' }, () => {
|
||||
it('button for wallet connect should work', () => {
|
||||
cy.mockConsole();
|
||||
cy.visit('/');
|
||||
cy.get('[href="/portfolio"]').eq(0).click();
|
||||
cy.getByTestId('trading-connect-wallet').should('be.visible');
|
||||
@@ -34,11 +35,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');
|
||||
@@ -57,28 +58,15 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
describe('Assets view', () => {
|
||||
before(() => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
aliasGQLQuery(req, 'Positions', positionsQuery());
|
||||
aliasGQLQuery(req, 'Margins', marginsQuery());
|
||||
aliasGQLQuery(req, 'Markets', marketsQuery());
|
||||
aliasGQLQuery(req, 'MarketsData', marketsDataQuery());
|
||||
aliasGQLQuery(req, 'Accounts', accountsQuery());
|
||||
aliasGQLQuery(req, 'Assets', assetsQuery());
|
||||
});
|
||||
beforeEach(() => {
|
||||
cy.mockConsole();
|
||||
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 +87,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 +104,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 +122,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 +149,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', () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
marketsCandlesQuery,
|
||||
marketsDataQuery,
|
||||
marketsQuery,
|
||||
networkParamsQuery,
|
||||
ordersQuery,
|
||||
positionsQuery,
|
||||
statisticsQuery,
|
||||
@@ -47,6 +48,7 @@ const mockPage = (req: CyHttpMessages.IncomingHttpRequest) => {
|
||||
aliasGQLQuery(req, 'SimpleMarkets', marketsQuery());
|
||||
aliasGQLQuery(req, 'Orders', ordersQuery());
|
||||
aliasGQLQuery(req, 'Fills', fillsQuery());
|
||||
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
||||
};
|
||||
|
||||
export const addMockConsole = () => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -45,7 +45,6 @@ const AccountsManager = () => {
|
||||
successCallback(rowsThisBlock, lastRow);
|
||||
};
|
||||
const { columnDefs, defaultColDef } = useAccountColumnDefinitions();
|
||||
console.log(data, loading);
|
||||
return (
|
||||
<>
|
||||
<AsyncRenderer
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"market.liquidity.maximumLiquidityFeeFactorLevel",
|
||||
"market.liquidity.minimum.probabilityOfTrading.lpOrders",
|
||||
"market.liquidity.probabilityOfTrading.tau.scaling",
|
||||
"market.liquidity.stakeToCcySiskas",
|
||||
"market.liquidity.stakeToCcyVolume",
|
||||
"market.liquidity.targetstake.triggering.ratio",
|
||||
"market.liquidityProvision.minLpStakeQuantumMultiple",
|
||||
"market.liquidityProvision.shapes.maxSize",
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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.66.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';
|
||||
|
||||
@@ -137,8 +137,7 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
||||
// This will get unwieldy and should probably produce a different colour of tag
|
||||
if (type === 'Chain Event' && !!command?.chainEvent) {
|
||||
type = getLabelForChainEvent(command.chainEvent);
|
||||
colours =
|
||||
'text-white dark-text-white bg-vega-pink-dark dark:bg-vega-pink-dark';
|
||||
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
||||
} else if (type === 'Proposal' || type === 'Governance Proposal') {
|
||||
if (command && !!command.proposalSubmission) {
|
||||
type = getLabelForProposal(command.proposalSubmission);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -84,13 +84,13 @@ export const WithdrawalProgress = ({ id, txStatus }: TxsStatsInfoProps) => {
|
||||
|
||||
const classes = {
|
||||
indicatorFailed:
|
||||
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-red-600 bg-red-600 text-center text-white font-bold leading-5',
|
||||
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-pink-600 bg-vega-pink-600 text-center text-white font-bold leading-5',
|
||||
textFailed:
|
||||
'absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase text-vega-red',
|
||||
'absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase text-vega-pink',
|
||||
indicatorComplete:
|
||||
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-vega-green-dark bg-vega-green-dark text-center text-white leading-5',
|
||||
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-vega-green bg-vega-green text-center text-white leading-5',
|
||||
textComplete:
|
||||
'absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase text-vega-green-dark',
|
||||
'absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase text-vega-green',
|
||||
indicatorIncomplete:
|
||||
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-gray-300 text-center leading-5',
|
||||
textIncomplete:
|
||||
@@ -140,7 +140,7 @@ export function WithdrawalProgressSeparator({
|
||||
return (
|
||||
<div
|
||||
className={`flex-auto border-t-2 transition duration-500 ease-in-out ${
|
||||
isComplete ? 'border-vega-green-dark' : 'border-gray-300'
|
||||
isComplete ? 'border-vega-green' : 'border-gray-300'
|
||||
}`}
|
||||
></div>
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
|
||||
export type ExplorerMarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ExplorerMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, tradingMode: Types.MarketTradingMode, state: Types.MarketState, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', settlementAsset: { __typename?: 'Asset', id: string, name: string, decimals: number, globalRewardPoolAccount?: { __typename?: 'AccountBalance', balance: string } | null } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: number, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, proposal?: { __typename?: 'Proposal', id?: string | null } | null, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', balance: string, type: Types.AccountType, asset: { __typename?: 'Asset', id: string, name: string } } } | null> | null } | null, data?: { __typename?: 'MarketData', markPrice: string, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, midPrice: string, staticMidPrice: string, timestamp: any, openInterest: string, auctionEnd?: string | null, auctionStart?: string | null, indicativePrice: string, indicativeVolume: string, trigger: Types.AuctionTrigger, extensionTrigger: Types.AuctionTrigger, targetStake?: string | null, suppliedStake?: string | null, marketValueProxy: string, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', auctionExtensionSecs: number, probability: number } }> | null, liquidityProviderFeeShare?: Array<{ __typename?: 'LiquidityProviderFeeShare', equityLikeShare: string, averageEntryValuation: string, party: { __typename?: 'Party', id: string } }> | null } | null } }> } | null };
|
||||
export type ExplorerMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, tradingMode: Types.MarketTradingMode, state: Types.MarketState, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', settlementAsset: { __typename?: 'Asset', id: string, name: string, decimals: number, globalRewardPoolAccount?: { __typename?: 'AccountBalance', balance: string } | null } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, proposal?: { __typename?: 'Proposal', id?: string | null } | null, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', balance: string, type: Types.AccountType, asset: { __typename?: 'Asset', id: string, name: string } } } | null> | null } | null, data?: { __typename?: 'MarketData', markPrice: string, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, midPrice: string, staticMidPrice: string, timestamp: any, openInterest: string, auctionEnd?: string | null, auctionStart?: string | null, indicativePrice: string, indicativeVolume: string, trigger: Types.AuctionTrigger, extensionTrigger: Types.AuctionTrigger, targetStake?: string | null, suppliedStake?: string | null, marketValueProxy: string, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', auctionExtensionSecs: number, probability: number } }> | null, liquidityProviderFeeShare?: Array<{ __typename?: 'LiquidityProviderFeeShare', equityLikeShare: string, averageEntryValuation: string, party: { __typename?: 'Party', id: string } }> | null } | null } }> } | null };
|
||||
|
||||
|
||||
export const ExplorerMarketsDocument = gql`
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+110
-32
@@ -14,9 +14,16 @@ 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
|
||||
@@ -33,6 +40,8 @@ export interface paths {
|
||||
};
|
||||
}
|
||||
|
||||
export type webhooks = Record<string, never>;
|
||||
|
||||
export interface components {
|
||||
schemas: {
|
||||
/**
|
||||
@@ -100,8 +109,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 +125,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 +137,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 +174,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 +207,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 +302,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 +372,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 +435,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,13 +453,46 @@ 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: {
|
||||
/** Reference, required field */
|
||||
readonly reference?: string;
|
||||
/** type of NodeVote, also required */
|
||||
readonly type?: components['schemas']['v1NodeVoteType'];
|
||||
};
|
||||
/**
|
||||
* - TYPE_UNSPECIFIED: Represents an unspecified or missing value from the input
|
||||
* - TYPE_STAKE_DEPOSITED: A node vote a new stake deposit
|
||||
* - TYPE_STAKE_REMOVED: A node vote for a new stake removed event
|
||||
* - TYPE_FUNDS_DEPOSITED: A node vote for new collateral deposited
|
||||
* - TYPE_SIGNER_ADDED: A node vote for a new signer added to the erc20 bridge
|
||||
* - TYPE_SIGNER_REMOVED: A node vote for a signer removed from the erc20 bridge
|
||||
* - TYPE_BRIDGE_STOPPED: A node vote for a bridge stopped event
|
||||
* - TYPE_BRIDGE_RESUMED: A node vote for a bridge resumed event
|
||||
* - TYPE_ASSET_LISTED: A node vote for a newly listed asset
|
||||
* - TYPE_LIMITS_UPDATED: A node vote for an asset limits update
|
||||
* - TYPE_STAKE_TOTAL_SUPPLY: A node vote to share the total supply of the staking token
|
||||
* - TYPE_SIGNER_THRESHOLD_SET: A node vote to update the threshold of the signer set for the multisig contract
|
||||
* - TYPE_GOVERNANCE_VALIDATE_ASSET: A node vote to validate a new assert governance proposal
|
||||
* @default TYPE_UNSPECIFIED
|
||||
* @enum {string}
|
||||
*/
|
||||
readonly v1NodeVoteType:
|
||||
| 'TYPE_UNSPECIFIED'
|
||||
| 'TYPE_STAKE_DEPOSITED'
|
||||
| 'TYPE_STAKE_REMOVED'
|
||||
| 'TYPE_FUNDS_DEPOSITED'
|
||||
| 'TYPE_SIGNER_ADDED'
|
||||
| 'TYPE_SIGNER_REMOVED'
|
||||
| 'TYPE_BRIDGE_STOPPED'
|
||||
| 'TYPE_BRIDGE_RESUMED'
|
||||
| 'TYPE_ASSET_LISTED'
|
||||
| 'TYPE_LIMITS_UPDATED'
|
||||
| 'TYPE_STAKE_TOTAL_SUPPLY'
|
||||
| 'TYPE_SIGNER_THRESHOLD_SET'
|
||||
| 'TYPE_GOVERNANCE_VALIDATE_ASSET';
|
||||
/** Specific details for a one off transfer */
|
||||
readonly v1OneOffTransfer: {
|
||||
/**
|
||||
@@ -553,6 +607,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 +656,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 +719,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 +1121,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: {
|
||||
@@ -1090,11 +1145,8 @@ export interface components {
|
||||
readonly auctionExtension?: string;
|
||||
/** Specifies parameters related to target stake calculation */
|
||||
readonly targetStakeParameters?: components['schemas']['vegaTargetStakeParameters'];
|
||||
/**
|
||||
* Specifies the triggering ratio for entering liquidity auction
|
||||
* Format: double
|
||||
*/
|
||||
readonly triggeringRatio?: number;
|
||||
/** Specifies the triggering ratio for entering liquidity auction */
|
||||
readonly triggeringRatio?: string;
|
||||
};
|
||||
/** Represents a liquidity order */
|
||||
readonly vegaLiquidityOrder: {
|
||||
@@ -1111,17 +1163,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 +1188,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 +1232,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 +1296,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 +1493,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 +1516,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 +1561,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
|
||||
|
||||
+16
@@ -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,3 +1,3 @@
|
||||
{
|
||||
"hosts": ["https://api.n00.mainnet-mirror.vega.xyz/graphql"]
|
||||
"hosts": ["https://api.n00.mainnet-mirror.vega.xyz/query"]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
"timeWindow": "3600",
|
||||
"scalingFactor": 10
|
||||
},
|
||||
"triggeringRatio": 0.7,
|
||||
"triggeringRatio": "0.7",
|
||||
"auctionExtension": "1"
|
||||
},
|
||||
"logNormal": {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -30,9 +30,9 @@ export function useAnimateValue(
|
||||
) {
|
||||
elRef.current?.animate(
|
||||
[
|
||||
{ backgroundColor: customColors.vega.red, color: colors.white },
|
||||
{ backgroundColor: customColors.vega.pink, color: colors.white },
|
||||
{
|
||||
backgroundColor: customColors.vega.red,
|
||||
backgroundColor: customColors.vega.pink,
|
||||
color: colors.white,
|
||||
offset: 0.8,
|
||||
},
|
||||
|
||||
@@ -159,8 +159,10 @@
|
||||
"Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked.": "Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked.",
|
||||
"proposals": "Proposals",
|
||||
"proposal": "Proposal",
|
||||
"submitting": "Submitting",
|
||||
"submittingProposal": "Submitting proposal",
|
||||
"submit": "Submit",
|
||||
"submitProposal": "Submit proposal",
|
||||
"connectWalletToSubmitProposal": "Connect your wallet to submit a proposal",
|
||||
"proposedEnactment": "Proposed enactment",
|
||||
"Enacted": "Enacted",
|
||||
"enactedOn": "Enacted on",
|
||||
@@ -729,5 +731,6 @@
|
||||
"homeRewardsIntro": "Track rewards you've earned for trading, liquidity provision, market creation, and staking.",
|
||||
"homeRewardsButtonText": "See rewards",
|
||||
"homeVegaTokenIntro": "VEGA Token is a governance asset used to make and vote on proposals, and nominate validators.",
|
||||
"homeVegaTokenButtonText": "Manage tokens"
|
||||
"homeVegaTokenButtonText": "Manage tokens",
|
||||
"downloadProposalJson": "Download proposal as JSON"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
export const downloadJson = (jsonString: string, proposalTitle: string) => {
|
||||
try {
|
||||
const now = new Date();
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const month = now.toLocaleString('en-US', { month: 'short' });
|
||||
const year = now.getFullYear().toString();
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = now.getSeconds().toString().padStart(2, '0');
|
||||
// e.g. "2023-Jan-03-23-59-59"
|
||||
const formattedDateTime = `${day}-${month}-${year}-${hours}-${minutes}-${seconds}`;
|
||||
|
||||
const blob = new Blob([jsonString], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = url;
|
||||
a.download = `${proposalTitle}-${formattedDateTime}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
@@ -25,7 +25,7 @@ export const TargetAddressMismatch = ({
|
||||
}}
|
||||
components={{
|
||||
bold: <strong />,
|
||||
red: <span className={'text-vega-red'} />,
|
||||
red: <span className={'text-vega-pink'} />,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
|
||||
@@ -5,3 +5,4 @@ export * from './proposal-form-terms';
|
||||
export * from './proposal-form-submit';
|
||||
export * from './proposal-form-transaction-dialog';
|
||||
export * from './proposal-form-vote-and-enactment-deadline';
|
||||
export * from './proposal-form-download-json';
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { ProposalFormDownloadJson } from './proposal-form-download-json';
|
||||
|
||||
describe('ProposalFormDownloadJson', () => {
|
||||
it('calls the downloadJson method when the button is clicked', () => {
|
||||
const downloadJson = jest.fn();
|
||||
render(<ProposalFormDownloadJson downloadJson={downloadJson} />);
|
||||
const button = screen.getByTestId('proposal-download-json');
|
||||
fireEvent.click(button);
|
||||
expect(downloadJson).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface ProposalFormDownloadJsonProps {
|
||||
downloadJson: () => void;
|
||||
}
|
||||
|
||||
export const ProposalFormDownloadJson = ({
|
||||
downloadJson,
|
||||
}: ProposalFormDownloadJsonProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<Button data-testid="proposal-download-json" onClick={downloadJson}>
|
||||
{t('downloadProposalJson')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { ProposalFormSubmit } from './proposal-form-submit';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
|
||||
const renderComponent = (
|
||||
context: VegaWalletContextShape,
|
||||
isSubmitting: boolean
|
||||
) => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={context}>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('Proposal Form Submit', () => {
|
||||
it('should display connection message and button if wallet not connected', () => {
|
||||
renderComponent({ pubKey: null } as VegaWalletContextShape, false);
|
||||
|
||||
expect(
|
||||
screen.getByText('Connect your wallet to submit a proposal')
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId('connect-to-vega-wallet-btn')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display submit button if wallet is connected', () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
renderComponent(
|
||||
{
|
||||
pubKey: pubKey.publicKey,
|
||||
pubKeys: [pubKey],
|
||||
} as VegaWalletContextShape,
|
||||
false
|
||||
);
|
||||
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
||||
'Submit proposal'
|
||||
);
|
||||
});
|
||||
|
||||
it('should display submitting button text if wallet is connected and submitting', () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
renderComponent(
|
||||
{
|
||||
pubKey: pubKey.publicKey,
|
||||
pubKeys: [pubKey],
|
||||
} as VegaWalletContextShape,
|
||||
true
|
||||
);
|
||||
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
||||
'Submitting proposal'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
|
||||
interface ProposalFormSubmitProps {
|
||||
isSubmitting: boolean;
|
||||
@@ -8,17 +10,25 @@ interface ProposalFormSubmitProps {
|
||||
export const ProposalFormSubmit = ({
|
||||
isSubmitting,
|
||||
}: ProposalFormSubmitProps) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mt-10 my-20">
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
data-testid="proposal-submit"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? t('Submitting') : t('Submit')} {t('Proposal')}
|
||||
</Button>
|
||||
<div className="mb-6">
|
||||
<div className="mb-4 font-bold uppercase text-vega-orange">
|
||||
{!pubKey && t('connectWalletToSubmitProposal')}
|
||||
</div>
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
data-testid="proposal-submit"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? t('submittingProposal') : t('submitProposal')}
|
||||
</Button>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+3
-3
@@ -231,7 +231,7 @@ const EnactmentForm = ({
|
||||
{deadlines.enactment && deadlines.enactment < deadlines.vote && (
|
||||
<span
|
||||
data-testid="enactment-before-voting-deadline"
|
||||
className="block mt-4 text-vega-red-dark"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline')}
|
||||
</span>
|
||||
@@ -437,7 +437,7 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<div className="mb-10">
|
||||
<ProposalFormSubheader>
|
||||
{enactmentRegister && enactmentMinClose && enactmentMaxClose
|
||||
? t('ProposalVoteAndEnactmentTitle')
|
||||
@@ -535,6 +535,6 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
maxEnactmentHours={maxEnactmentHours}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -109,6 +109,7 @@ describe('Propose Freeform', () => {
|
||||
expect(screen.getByTestId('proposal-description')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,18 +12,19 @@ import {
|
||||
ProposalFormSubmit,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import {
|
||||
createDocsLinks,
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface FreeformProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@@ -50,10 +51,11 @@ export const ProposeFreeform = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<FreeformProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: FreeformProposalFormFields) => {
|
||||
const assembleProposal = (fields: FreeformProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
@@ -66,7 +68,7 @@ export const ProposeFreeform = () => {
|
||||
params.governance_proposal_freeform_maxClose
|
||||
).toString();
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@@ -79,86 +81,101 @@ export const ProposeFreeform = () => {
|
||||
isVoteDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: FreeformProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-freeform-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={params}>
|
||||
<Heading title={t('NewFreeformProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_freeform_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={params}
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NewFreeformProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_freeform_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreProposalsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/governance`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreProposalsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/governance`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="freeform-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="freeform-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_freeform_minClose}
|
||||
voteMaxClose={params.governance_proposal_freeform_maxClose}
|
||||
/>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_freeform_minClose}
|
||||
voteMaxClose={params.governance_proposal_freeform_maxClose}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+1
@@ -112,6 +112,7 @@ describe('Propose Network Parameter', () => {
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
+165
-153
@@ -20,6 +20,7 @@ import {
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
ProposalFormDownloadJson,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import {
|
||||
@@ -33,8 +34,8 @@ import {
|
||||
TextArea,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
interface SelectedNetworkParamCurrentValueProps {
|
||||
value: string;
|
||||
@@ -92,6 +93,7 @@ export const ProposeNetworkParameter = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<NetworkParameterProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@@ -99,8 +101,8 @@ export const ProposeNetworkParameter = () => {
|
||||
? Object.entries(params).find(([key]) => key === selectedNetworkParam)
|
||||
: null;
|
||||
|
||||
const onSubmit = async (fields: NetworkParameterProposalFormFields) => {
|
||||
const acutalNetworkParamKey = fields.proposalNetworkParameterKey
|
||||
const assembleProposal = (fields: NetworkParameterProposalFormFields) => {
|
||||
const actualNetworkParamKey = fields.proposalNetworkParameterKey
|
||||
.split('_')
|
||||
.join('.');
|
||||
|
||||
@@ -121,7 +123,7 @@ export const ProposeNetworkParameter = () => {
|
||||
params.governance_proposal_updateNetParam_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@@ -129,7 +131,7 @@ export const ProposeNetworkParameter = () => {
|
||||
terms: {
|
||||
updateNetworkParameter: {
|
||||
changes: {
|
||||
key: acutalNetworkParamKey,
|
||||
key: actualNetworkParamKey,
|
||||
value: fields.proposalNetworkParameterValue,
|
||||
},
|
||||
},
|
||||
@@ -144,7 +146,19 @@ export const ProposeNetworkParameter = () => {
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: NetworkParameterProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-network-param-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -152,163 +166,161 @@ export const ProposeNetworkParameter = () => {
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('NetworkParameterProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateNetParam_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NetworkParameterProposal')} />
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateNetParam_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreNetParamsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/network-parameters`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/network-parameters`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreNetParamsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/network-parameters`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/network-parameters`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="network-parameter-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="network-parameter-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('SelectAParameterToChange')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<FormGroup
|
||||
label={t('SelectAParameterToChange')}
|
||||
labelFor="proposal-parameter-key"
|
||||
hideLabel={true}
|
||||
>
|
||||
<Select
|
||||
data-testid="proposal-parameter-select"
|
||||
id="proposal-parameter-key"
|
||||
{...register('proposalNetworkParameterKey', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('SelectAParameterToChange')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<FormGroup
|
||||
label={t('SelectAParameterToChange')}
|
||||
labelFor="proposal-parameter-key"
|
||||
hideLabel={true}
|
||||
onChange={(e) => setSelectedNetworkParam(e.target.value)}
|
||||
value={selectedNetworkParam}
|
||||
>
|
||||
<Select
|
||||
data-testid="proposal-parameter-select"
|
||||
id="proposal-parameter-key"
|
||||
{...register('proposalNetworkParameterKey', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
onChange={(e) => setSelectedNetworkParam(e.target.value)}
|
||||
value={selectedNetworkParam}
|
||||
>
|
||||
<option value="">{t('SelectParameter')}</option>
|
||||
{Object.keys(params).map((key) => {
|
||||
const actualKey = key.split('_').join('.');
|
||||
return (
|
||||
<option key={key} value={key}>
|
||||
{actualKey}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
{errors?.proposalNetworkParameterKey?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalNetworkParameterKey?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
|
||||
{selectedNetworkParam && (
|
||||
<div className="mt-[-10px]">
|
||||
{selectedParamEntry && (
|
||||
<SelectedNetworkParamCurrentValue
|
||||
value={selectedParamEntry[1]}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormGroup
|
||||
label={t('NewProposedValue')}
|
||||
labelFor="proposal-parameter-new-value"
|
||||
>
|
||||
<TextArea
|
||||
data-testid="selected-proposal-param-new-value"
|
||||
id="proposal-parameter-new-value"
|
||||
{...register('proposalNetworkParameterValue', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
/>
|
||||
{errors?.proposalNetworkParameterValue?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalNetworkParameterValue?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</div>
|
||||
<option value="">{t('SelectParameter')}</option>
|
||||
{Object.keys(params).map((key) => {
|
||||
const actualKey = key.split('_').join('.');
|
||||
return (
|
||||
<option key={key} value={key}>
|
||||
{actualKey}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
{errors?.proposalNetworkParameterKey?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalNetworkParameterKey?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={
|
||||
params.governance_proposal_updateNetParam_minClose
|
||||
}
|
||||
voteMaxClose={
|
||||
params.governance_proposal_updateNetParam_maxClose
|
||||
}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateNetParam_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateNetParam_maxEnact
|
||||
}
|
||||
/>
|
||||
{selectedNetworkParam && (
|
||||
<div className="mt-[-10px]">
|
||||
{selectedParamEntry && (
|
||||
<SelectedNetworkParamCurrentValue
|
||||
value={selectedParamEntry[1]}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<FormGroup
|
||||
label={t('NewProposedValue')}
|
||||
labelFor="proposal-parameter-new-value"
|
||||
>
|
||||
<TextArea
|
||||
data-testid="selected-proposal-param-new-value"
|
||||
id="proposal-parameter-new-value"
|
||||
{...register('proposalNetworkParameterValue', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
/>
|
||||
{errors?.proposalNetworkParameterValue?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalNetworkParameterValue?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={
|
||||
params.governance_proposal_updateNetParam_minClose
|
||||
}
|
||||
voteMaxClose={
|
||||
params.governance_proposal_updateNetParam_maxClose
|
||||
}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateNetParam_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateNetParam_maxEnact
|
||||
}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -110,6 +110,7 @@ describe('Propose New Asset', () => {
|
||||
expect(screen.getByTestId('proposal-validation-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,13 +21,14 @@ import {
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface NewAssetProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@@ -62,10 +63,11 @@ export const ProposeNewAsset = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<NewAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: NewAssetProposalFormFields) => {
|
||||
const assembleProposal = (fields: NewAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_asset_minClose
|
||||
@@ -87,7 +89,7 @@ export const ProposeNewAsset = () => {
|
||||
params.governance_proposal_asset_maxClose
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@@ -111,7 +113,19 @@ export const ProposeNewAsset = () => {
|
||||
isValidationDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: NewAssetProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-new-asset-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -119,112 +133,111 @@ export const ProposeNewAsset = () => {
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('NewAssetProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_asset_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NewAssetProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_asset_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreAssetsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/assets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreAssetsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/assets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="new-asset-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="new-asset-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>{t('NewAsset')}</ProposalFormSubheader>
|
||||
<ProposalFormSubheader>{t('NewAsset')}</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.newAsset (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.newAsset (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_asset_minClose}
|
||||
voteMaxClose={params.governance_proposal_asset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={params.governance_proposal_asset_minEnact}
|
||||
enactmentMaxClose={params.governance_proposal_asset_maxEnact}
|
||||
validationRequired={true}
|
||||
onValidationMinMax={setValue}
|
||||
validationRegister={register('proposalValidationDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
validationErrorMessage={
|
||||
errors?.proposalValidationDeadline?.message
|
||||
}
|
||||
/>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_asset_minClose}
|
||||
voteMaxClose={params.governance_proposal_asset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={params.governance_proposal_asset_minEnact}
|
||||
enactmentMaxClose={params.governance_proposal_asset_maxEnact}
|
||||
validationRequired={true}
|
||||
onValidationMinMax={setValue}
|
||||
validationRegister={register('proposalValidationDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
validationErrorMessage={
|
||||
errors?.proposalValidationDeadline?.message
|
||||
}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -104,6 +104,7 @@ describe('Propose New Market', () => {
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,13 +20,14 @@ import {
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface NewMarketProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@@ -60,10 +61,11 @@ export const ProposeNewMarket = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<NewMarketProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: NewMarketProposalFormFields) => {
|
||||
const assembleProposal = (fields: NewMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_market_minClose
|
||||
@@ -81,7 +83,7 @@ export const ProposeNewMarket = () => {
|
||||
params.governance_proposal_market_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@@ -101,7 +103,19 @@ export const ProposeNewMarket = () => {
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: NewMarketProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-new-market-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -109,104 +123,103 @@ export const ProposeNewMarket = () => {
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('NewMarketProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_market_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NewMarketProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_market_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreMarketsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/markets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreMarketsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/markets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="new-market-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="new-market-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>{t('NewMarket')}</ProposalFormSubheader>
|
||||
<ProposalFormSubheader>{t('NewMarket')}</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.newMarket (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.newMarket (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_market_minClose}
|
||||
voteMaxClose={params.governance_proposal_market_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={params.governance_proposal_market_minEnact}
|
||||
enactmentMaxClose={params.governance_proposal_market_maxEnact}
|
||||
/>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_market_minClose}
|
||||
voteMaxClose={params.governance_proposal_market_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={params.governance_proposal_market_minEnact}
|
||||
enactmentMaxClose={params.governance_proposal_market_maxEnact}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
ExternalLink,
|
||||
@@ -20,8 +19,10 @@ import { useProposalSubmit } from '@vegaprotocol/governance';
|
||||
import {
|
||||
ProposalFormSubmit,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
} from '../../components/propose';
|
||||
import { ProposalRawMinRequirements } from './proposal-raw-min-requirements';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface RawProposalFormFields {
|
||||
rawProposalData: string;
|
||||
@@ -48,6 +49,7 @@ export const ProposeRaw = () => {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
watch,
|
||||
} = useForm<RawProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@@ -57,88 +59,90 @@ export const ProposeRaw = () => {
|
||||
await submit(JSON.parse(fields.rawProposalData));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(JSON.stringify(formData), 'vega-raw-proposal');
|
||||
};
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('NewRawProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalRawMinRequirements
|
||||
assetMin={params.governance_proposal_asset_minProposerBalance}
|
||||
updateAssetMin={
|
||||
params.governance_proposal_updateAsset_minProposerBalance
|
||||
}
|
||||
marketMin={params.governance_proposal_market_minProposerBalance}
|
||||
updateMarketMin={
|
||||
params.governance_proposal_updateMarket_minProposerBalance
|
||||
}
|
||||
updateNetParamMin={
|
||||
params.governance_proposal_updateNetParam_minProposerBalance
|
||||
}
|
||||
freeformMin={
|
||||
params.governance_proposal_freeform_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NewRawProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
target="_blank"
|
||||
>
|
||||
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
<ProposalRawMinRequirements
|
||||
assetMin={params.governance_proposal_asset_minProposerBalance}
|
||||
updateAssetMin={
|
||||
params.governance_proposal_updateAsset_minProposerBalance
|
||||
}
|
||||
marketMin={params.governance_proposal_market_minProposerBalance}
|
||||
updateMarketMin={
|
||||
params.governance_proposal_updateMarket_minProposerBalance
|
||||
}
|
||||
updateNetParamMin={
|
||||
params.governance_proposal_updateNetParam_minProposerBalance
|
||||
}
|
||||
freeformMin={params.governance_proposal_freeform_minProposerBalance}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
/>
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreProposalsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/governance`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
target="_blank"
|
||||
>
|
||||
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="raw-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<FormGroup
|
||||
label="Make a proposal by submitting JSON"
|
||||
labelFor="proposal-data"
|
||||
>
|
||||
<TextArea
|
||||
id="proposal-data"
|
||||
className="min-h-[200px]"
|
||||
hasError={hasError}
|
||||
data-testid="proposal-data"
|
||||
{...register('rawProposalData', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
/>
|
||||
{errors.rawProposalData?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors.rawProposalData?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreProposalsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/governance`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="raw-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<FormGroup
|
||||
label="Make a proposal by submitting JSON"
|
||||
labelFor="proposal-data"
|
||||
>
|
||||
<TextArea
|
||||
id="proposal-data"
|
||||
className="min-h-[200px]"
|
||||
hasError={hasError}
|
||||
data-testid="proposal-data"
|
||||
{...register('rawProposalData', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
{errors.rawProposalData?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors.rawProposalData?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -111,6 +111,7 @@ describe('Propose Update Asset', () => {
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,13 +20,14 @@ import {
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface UpdateAssetProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@@ -60,10 +61,11 @@ export const ProposeUpdateAsset = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<UpdateAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: UpdateAssetProposalFormFields) => {
|
||||
const assembleProposal = (fields: UpdateAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateAsset_minClose
|
||||
@@ -81,7 +83,7 @@ export const ProposeUpdateAsset = () => {
|
||||
params.governance_proposal_updateAsset_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@@ -101,7 +103,19 @@ export const ProposeUpdateAsset = () => {
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: UpdateAssetProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-update-asset-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -109,110 +123,107 @@ export const ProposeUpdateAsset = () => {
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('UpdateAssetProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateAsset_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('UpdateAssetProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateAsset_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreAssetsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/assets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreAssetsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/assets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="update-asset-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="update-asset-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('UpdateAsset')}
|
||||
</ProposalFormSubheader>
|
||||
<ProposalFormSubheader>{t('UpdateAsset')}</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.updateAsset (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.updateAsset (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_updateAsset_minClose}
|
||||
voteMaxClose={params.governance_proposal_updateAsset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateAsset_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateAsset_maxEnact
|
||||
}
|
||||
/>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_updateAsset_minClose}
|
||||
voteMaxClose={params.governance_proposal_updateAsset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateAsset_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateAsset_maxEnact
|
||||
}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -187,6 +187,7 @@ describe('Propose Update Market', () => {
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
@@ -34,9 +35,9 @@ import {
|
||||
Select,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { useProposalMarketsQueryQuery } from './__generated___/UpdateMarket';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface UpdateMarketProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@@ -101,10 +102,11 @@ export const ProposeUpdateMarket = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<UpdateMarketProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: UpdateMarketProposalFormFields) => {
|
||||
const assembleProposal = (fields: UpdateMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateMarket_minClose
|
||||
@@ -122,7 +124,7 @@ export const ProposeUpdateMarket = () => {
|
||||
params.governance_proposal_updateMarket_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@@ -145,176 +147,182 @@ export const ProposeUpdateMarket = () => {
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: UpdateMarketProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-update-market-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={networkParamsLoading && marketsLoading}
|
||||
error={networkParamsError && marketsError}
|
||||
data={params && marketsData}
|
||||
>
|
||||
<Heading title={t('UpdateMarketProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateMarket_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
data={{ ...params, ...marketsData }}
|
||||
render={(data) => (
|
||||
<>
|
||||
<Heading title={t('UpdateMarketProposal')} />
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
data.governance_proposal_updateMarket_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={data.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreMarketsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/markets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreMarketsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/markets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="update-market-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="update-market-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('SelectAMarketToChange')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<FormGroup
|
||||
label={t('SelectAMarketToChange')}
|
||||
labelFor="proposal-market"
|
||||
hideLabel={true}
|
||||
>
|
||||
<Select
|
||||
data-testid="proposal-market-select"
|
||||
id="proposal-market"
|
||||
{...register('proposalMarketId', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('SelectAMarketToChange')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<FormGroup
|
||||
label={t('SelectAMarketToChange')}
|
||||
labelFor="proposal-market"
|
||||
hideLabel={true}
|
||||
onChange={(e) => setSelectedMarket(e.target.value)}
|
||||
>
|
||||
<Select
|
||||
data-testid="proposal-market-select"
|
||||
id="proposal-market"
|
||||
{...register('proposalMarketId', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
onChange={(e) => setSelectedMarket(e.target.value)}
|
||||
>
|
||||
<option value="">{t('SelectMarket')}</option>
|
||||
{sortedMarkets.map((market) => (
|
||||
<option value={market.id} key={market.id}>
|
||||
{market.tradableInstrument.instrument.name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
{errors?.proposalMarketId?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalMarketId?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
|
||||
{selectedMarket && (
|
||||
<div className="mt-[-20px] mb-6">
|
||||
<KeyValueTable data-testid="update-market-details">
|
||||
<KeyValueTableRow>
|
||||
{t('MarketName')}
|
||||
{
|
||||
marketsData?.marketsConnection?.edges?.find(
|
||||
({ node: market }) => market.id === selectedMarket
|
||||
)?.node.tradableInstrument.instrument.name
|
||||
}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('MarketCode')}
|
||||
{
|
||||
marketsData?.marketsConnection?.edges?.find(
|
||||
({ node: market }) => market.id === selectedMarket
|
||||
)?.node.tradableInstrument.instrument.code
|
||||
}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('MarketId')}
|
||||
{selectedMarket}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
<option value="">{t('SelectMarket')}</option>
|
||||
{sortedMarkets.map((market) => (
|
||||
<option value={market.id} key={market.id}>
|
||||
{market.tradableInstrument.instrument.name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
{errors?.proposalMarketId?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalMarketId?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={t('ProposeUpdateMarketTerms')}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
{selectedMarket && (
|
||||
<div className="mt-[-20px] mb-6">
|
||||
<KeyValueTable data-testid="update-market-details">
|
||||
<KeyValueTableRow>
|
||||
{t('MarketName')}
|
||||
{
|
||||
data.marketsConnection?.edges?.find(
|
||||
({ node: market }) => market.id === selectedMarket
|
||||
)?.node.tradableInstrument.instrument.name
|
||||
}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('MarketCode')}
|
||||
{
|
||||
data.marketsConnection?.edges?.find(
|
||||
({ node: market }) => market.id === selectedMarket
|
||||
)?.node.tradableInstrument.instrument.code
|
||||
}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('MarketId')}
|
||||
{selectedMarket}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={
|
||||
params.governance_proposal_updateMarket_minClose
|
||||
}
|
||||
voteMaxClose={
|
||||
params.governance_proposal_updateMarket_maxClose
|
||||
}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateMarket_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateMarket_maxEnact
|
||||
}
|
||||
/>
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={t('ProposeUpdateMarketTerms')}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={data.governance_proposal_updateMarket_minClose}
|
||||
voteMaxClose={data.governance_proposal_updateMarket_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
data.governance_proposal_updateMarket_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
data.governance_proposal_updateMarket_maxEnact
|
||||
}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+1
-1
@@ -169,7 +169,7 @@ export const DisassociatePage = ({
|
||||
)}
|
||||
</p>
|
||||
<p data-testid="disassociation-warning">
|
||||
<span className="text-vega-red">{t('Warning')}:</span>{' '}
|
||||
<span className="text-vega-pink">{t('Warning')}:</span>{' '}
|
||||
{t(
|
||||
'Any tokens that have been nominated to a node will sacrifice rewards they are due for the current epoch. If you do not wish to sacrifice these, you should remove stake from a node at the end of an epoch before disassociation.'
|
||||
)}
|
||||
|
||||
@@ -96,7 +96,7 @@ export const StakingNode = ({ data, previousEpochData }: StakingNodeProps) => {
|
||||
|
||||
if (!nodeInfo) {
|
||||
return (
|
||||
<span data-testid="staking-node-not-found" className={'text-vega-red'}>
|
||||
<span data-testid="staking-node-not-found" className={'text-vega-pink'}>
|
||||
{t('stakingNodeNotFound', { node })}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -192,7 +192,7 @@ export const StakingForm = ({
|
||||
availableStakeToRemove.isEqualTo(0) && (
|
||||
<div className="mb-4">
|
||||
{lien.isGreaterThan(0) ? (
|
||||
<span className="text-vega-red">
|
||||
<span className="text-vega-pink">
|
||||
{t('stakeNodeWrongVegaKey')}
|
||||
</span>
|
||||
) : (
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('vega wallet v1', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId(form).find('#wallet').click().type('invalid name');
|
||||
cy.getByTestId(form).find('#passphrase').click().type('invalid password');
|
||||
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
|
||||
cy.getByTestId('form-error').should('have.text', 'Invalid credentials');
|
||||
cy.getByTestId('form-error').should('have.text', 'No wallet detected');
|
||||
});
|
||||
|
||||
it('doesnt connect with invalid fields', () => {
|
||||
@@ -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';
|
||||
@@ -216,12 +216,14 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
|
||||
|
||||
it('must see a parked order', () => {
|
||||
// 7002-SORD-048
|
||||
// NOT COVERED: must see an explanation of why parked orders happen
|
||||
updateOrder({
|
||||
id: orderId,
|
||||
status: Schema.OrderStatus.STATUS_PARKED,
|
||||
});
|
||||
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Parked');
|
||||
cy.getByTestId(`order-status-${orderId}`).should(
|
||||
'have.text',
|
||||
'Parked: Internal error'
|
||||
);
|
||||
});
|
||||
|
||||
it('must see the size of the order and direction/side -', () => {
|
||||
@@ -341,11 +343,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 +425,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
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import {
|
||||
assetsQuery,
|
||||
ledgerEntriesQuery,
|
||||
marketsQuery,
|
||||
} from '@vegaprotocol/mock';
|
||||
import { ledgerEntriesQuery } from '@vegaprotocol/mock';
|
||||
|
||||
describe('Portfolio page', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'LedgerEntries', ledgerEntriesQuery());
|
||||
aliasGQLQuery(req, 'Assets', assetsQuery());
|
||||
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 |
@@ -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}>
|
||||
|
||||
@@ -2,15 +2,16 @@ import {
|
||||
liquidityProvisionsDataProvider,
|
||||
LiquidityTable,
|
||||
lpAggregatedDataProvider,
|
||||
marketLiquidityDataProvider,
|
||||
useCheckLiquidityStatus,
|
||||
} from '@vegaprotocol/liquidity';
|
||||
import { tooltipMapping } from '@vegaprotocol/market-info';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
formatNumberPercentage,
|
||||
NetworkParams,
|
||||
t,
|
||||
useDataProvider,
|
||||
useNetworkParam,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
@@ -18,9 +19,10 @@ import {
|
||||
Tab,
|
||||
Tabs,
|
||||
Link as UiToolkitLink,
|
||||
Indicator,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { Header, HeaderStat } from '../../components/header';
|
||||
|
||||
@@ -28,6 +30,13 @@ import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { LiquidityProvisionData } from '@vegaprotocol/liquidity';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import type {
|
||||
MarketData,
|
||||
MarketDataUpdateFieldsFragment,
|
||||
MarketDealTicket,
|
||||
SingleMarketFieldsFragment,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { marketProvider, marketDataProvider } from '@vegaprotocol/market-list';
|
||||
|
||||
export const Liquidity = () => {
|
||||
const params = useParams();
|
||||
@@ -41,16 +50,47 @@ export const LiquidityContainer = ({
|
||||
marketId: string | undefined;
|
||||
}) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const { data: marketProvision } = useDataProvider({
|
||||
dataProvider: marketLiquidityDataProvider,
|
||||
skipUpdates: true,
|
||||
variables: useMemo(() => ({ marketId }), [marketId]),
|
||||
const [market, setMarket] = useState<MarketDealTicket | null>(null);
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId,
|
||||
}),
|
||||
[marketId]
|
||||
);
|
||||
|
||||
const { data: marketProvision } = useDataProvider<
|
||||
SingleMarketFieldsFragment,
|
||||
never
|
||||
>({
|
||||
dataProvider: marketProvider,
|
||||
variables,
|
||||
skip: !marketId,
|
||||
});
|
||||
|
||||
const updateMarket = useCallback(
|
||||
({ data: marketData }: { data: MarketData | null }) => {
|
||||
if (marketData) {
|
||||
setMarket({
|
||||
...marketProvision,
|
||||
data: marketData,
|
||||
} as MarketDealTicket);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[marketProvision]
|
||||
);
|
||||
|
||||
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
|
||||
dataProvider: marketDataProvider,
|
||||
update: updateMarket,
|
||||
variables,
|
||||
skip: !marketId || !marketProvision,
|
||||
});
|
||||
const dataRef = useRef<LiquidityProvisionData[] | null>(null);
|
||||
|
||||
const { reload } = useDataProvider({
|
||||
dataProvider: liquidityProvisionsDataProvider,
|
||||
variables: useMemo(() => ({ marketId }), [marketId]),
|
||||
variables,
|
||||
});
|
||||
|
||||
const update = useCallback(
|
||||
@@ -85,17 +125,14 @@ export const LiquidityContainer = ({
|
||||
}, [reload]);
|
||||
|
||||
const assetDecimalPlaces =
|
||||
marketProvision?.market?.tradableInstrument.instrument.product
|
||||
.settlementAsset.decimals || 0;
|
||||
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
|
||||
const symbol =
|
||||
marketProvision?.market?.tradableInstrument.instrument.product
|
||||
.settlementAsset.symbol;
|
||||
|
||||
const { param: stakeToCcySiskas } = useNetworkParam(
|
||||
NetworkParams.market_liquidity_stakeToCcySiskas
|
||||
);
|
||||
const stakeToCcySiska = stakeToCcySiskas && stakeToCcySiskas[0];
|
||||
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.market_liquidity_stakeToCcyVolume,
|
||||
]);
|
||||
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
||||
const filteredEdges = useMemo(
|
||||
() =>
|
||||
liquidityProviders?.filter((e) =>
|
||||
@@ -115,7 +152,7 @@ export const LiquidityContainer = ({
|
||||
data={filteredEdges}
|
||||
symbol={symbol}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
stakeToCcySiskas={stakeToCcySiska}
|
||||
stakeToCcyVolume={stakeToCcyVolume}
|
||||
/>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
@@ -128,10 +165,41 @@ export const LiquidityViewContainer = ({
|
||||
}) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const { data: marketProvision } = useDataProvider({
|
||||
dataProvider: marketLiquidityDataProvider,
|
||||
skipUpdates: true,
|
||||
variables: useMemo(() => ({ marketId }), [marketId]),
|
||||
const [market, setMarket] = useState<MarketDealTicket | null>(null);
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId,
|
||||
}),
|
||||
[marketId]
|
||||
);
|
||||
|
||||
const { data: marketProvision } = useDataProvider<
|
||||
SingleMarketFieldsFragment,
|
||||
never
|
||||
>({
|
||||
dataProvider: marketProvider,
|
||||
variables,
|
||||
skip: !marketId,
|
||||
});
|
||||
|
||||
const updateMarket = useCallback(
|
||||
({ data: marketData }: { data: MarketData | null }) => {
|
||||
if (marketData) {
|
||||
setMarket({
|
||||
...marketProvision,
|
||||
data: marketData,
|
||||
} as MarketDealTicket);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[marketProvision]
|
||||
);
|
||||
|
||||
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
|
||||
dataProvider: marketDataProvider,
|
||||
update: updateMarket,
|
||||
variables,
|
||||
skip: !marketId || !marketProvision,
|
||||
});
|
||||
const dataRef = useRef<LiquidityProvisionData[] | null>(null);
|
||||
|
||||
@@ -171,20 +239,20 @@ export const LiquidityViewContainer = ({
|
||||
return () => clearInterval(interval);
|
||||
}, [reload]);
|
||||
|
||||
const targetStake = marketProvision?.market?.data?.targetStake;
|
||||
const suppliedStake = marketProvision?.market?.data?.suppliedStake;
|
||||
const targetStake = market?.data?.targetStake;
|
||||
const suppliedStake = market?.data?.suppliedStake;
|
||||
const assetDecimalPlaces =
|
||||
marketProvision?.market?.tradableInstrument.instrument.product
|
||||
.settlementAsset.decimals || 0;
|
||||
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
|
||||
const symbol =
|
||||
marketProvision?.market?.tradableInstrument.instrument.product
|
||||
.settlementAsset.symbol;
|
||||
|
||||
const { param: stakeToCcySiskas } = useNetworkParam(
|
||||
NetworkParams.market_liquidity_stakeToCcySiskas
|
||||
);
|
||||
const stakeToCcySiska = stakeToCcySiskas && stakeToCcySiskas[0];
|
||||
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.market_liquidity_stakeToCcyVolume,
|
||||
NetworkParams.market_liquidity_targetstake_triggering_ratio,
|
||||
]);
|
||||
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
||||
const triggeringRatio =
|
||||
params.market_liquidity_targetstake_triggering_ratio || '1';
|
||||
const myLpEdges = useMemo(
|
||||
() => liquidityProviders?.filter((e) => e.party.id === pubKey),
|
||||
[liquidityProviders, pubKey]
|
||||
@@ -221,18 +289,24 @@ export const LiquidityViewContainer = ({
|
||||
return LiquidityTabs.Active;
|
||||
};
|
||||
|
||||
const { percentage, status } = useCheckLiquidityStatus({
|
||||
suppliedStake: suppliedStake || 0,
|
||||
targetStake: targetStake || 0,
|
||||
triggeringRatio,
|
||||
});
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={liquidityProviders}>
|
||||
<div className="h-full grid grid-rows-[min-content_1fr]">
|
||||
<Header
|
||||
title={
|
||||
marketProvision?.market?.tradableInstrument.instrument.name &&
|
||||
market?.tradableInstrument.instrument.name &&
|
||||
marketId && (
|
||||
<Link to={Links[Routes.MARKET](marketId)}>
|
||||
<UiToolkitLink className="sm:text-lg md:text-xl lg:text-2xl flex items-center gap-2 whitespace-nowrap hover:text-neutral-500 dark:hover:text-neutral-300">
|
||||
{`${
|
||||
marketProvision?.market?.tradableInstrument.instrument.name
|
||||
} ${t('liquidity provision')}`}
|
||||
{`${market?.tradableInstrument.instrument.name} ${t(
|
||||
'liquidity provision'
|
||||
)}`}
|
||||
</UiToolkitLink>
|
||||
</Link>
|
||||
)
|
||||
@@ -264,6 +338,14 @@ export const LiquidityViewContainer = ({
|
||||
: '-'}
|
||||
</div>
|
||||
</HeaderStat>
|
||||
<HeaderStat
|
||||
heading={t('Liquidity supplied')}
|
||||
testId="liquidity-supplied"
|
||||
>
|
||||
<Indicator variant={status} />
|
||||
|
||||
{formatNumberPercentage(percentage, 2)}
|
||||
</HeaderStat>
|
||||
<HeaderStat heading={t('Market ID')}>
|
||||
<div className="break-word">{marketId}</div>
|
||||
</HeaderStat>
|
||||
@@ -279,7 +361,7 @@ export const LiquidityViewContainer = ({
|
||||
ref={gridRef}
|
||||
data={myLpEdges}
|
||||
symbol={symbol}
|
||||
stakeToCcySiskas={stakeToCcySiska}
|
||||
stakeToCcyVolume={stakeToCcyVolume}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
/>
|
||||
)}
|
||||
@@ -291,7 +373,7 @@ export const LiquidityViewContainer = ({
|
||||
data={activeEdges}
|
||||
symbol={symbol}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
stakeToCcySiskas={stakeToCcySiska}
|
||||
stakeToCcyVolume={stakeToCcyVolume}
|
||||
/>
|
||||
)}
|
||||
</Tab>
|
||||
@@ -303,7 +385,7 @@ export const LiquidityViewContainer = ({
|
||||
data={inactiveEdges}
|
||||
symbol={symbol}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
stakeToCcySiskas={stakeToCcySiska}
|
||||
stakeToCcyVolume={stakeToCcyVolume}
|
||||
/>
|
||||
)}
|
||||
</Tab>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ const MainGrid = ({
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs>
|
||||
<Tab id="candles" name={t('Candles')}>
|
||||
<Tab id="chart" name={t('Chart')}>
|
||||
<TradingViews.Candles marketId={marketId} />
|
||||
</Tab>
|
||||
<Tab id="depth" name={t('Depth')}>
|
||||
|
||||
@@ -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} />;
|
||||
|
||||
@@ -151,7 +151,11 @@ const AccountHistoryManager = ({
|
||||
: t('Select account type')}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{Object.keys(Schema.AccountType).map((type) => (
|
||||
{[
|
||||
Schema.AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
Schema.AccountType.ACCOUNT_TYPE_BOND,
|
||||
Schema.AccountType.ACCOUNT_TYPE_MARGIN,
|
||||
].map((type) => (
|
||||
<DropdownMenuItem
|
||||
key={type}
|
||||
onClick={() => setAccountType(type as Schema.AccountType)}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './liquidity-supplied';
|
||||
@@ -0,0 +1,145 @@
|
||||
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 { Indicator, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useCheckLiquidityStatus } from '@vegaprotocol/liquidity';
|
||||
import { DataGrid } from '@vegaprotocol/react-helpers';
|
||||
import { AuctionTrigger, MarketTradingMode } from '@vegaprotocol/types';
|
||||
|
||||
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_stakeToCcyVolume,
|
||||
NetworkParams.market_liquidity_targetstake_triggering_ratio,
|
||||
]);
|
||||
|
||||
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
||||
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, status } = 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 showMessage =
|
||||
percentage.gte(100) &&
|
||||
market?.marketTradingMode ===
|
||||
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||
market.trigger === AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY;
|
||||
|
||||
const description = (
|
||||
<section>
|
||||
{compiledGrid && <DataGrid grid={compiledGrid} />}
|
||||
<br />
|
||||
<Link href={`/#/liquidity/${marketId}`} data-testid="view-liquidity-link">
|
||||
{t('View liquidity provision table')}
|
||||
</Link>
|
||||
{showMessage && (
|
||||
<p className="mt-4">
|
||||
{t(
|
||||
'The market is in an auction because there are no priced limit orders, which are required to deploy liquidity commitment pegged orders. This means the order book is empty on one or both sides.'
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
|
||||
return (
|
||||
<HeaderStat
|
||||
heading={t('Liquidity supplied')}
|
||||
description={description}
|
||||
testId="liquidity-supplied"
|
||||
>
|
||||
<Indicator variant={status} />
|
||||
{supplied} (
|
||||
{percentage.gt(100) ? '>100%' : 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(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user