Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c5eb0f3eb | ||
|
|
d6a8134f97 | ||
|
|
ffdf4f85ae | ||
|
|
0d1a8a5d3f | ||
|
|
49d4046cc1 | ||
|
|
069b57d4ae | ||
|
|
6894fe1264 | ||
|
|
93e4b5fdb9 | ||
|
|
1566fc3984 | ||
|
|
39962a0246 | ||
|
|
8d31510d5e | ||
|
|
26d5a67604 | ||
|
|
9cc8f5a377 |
@@ -10,8 +10,8 @@ on:
|
||||
jobs:
|
||||
run-tests:
|
||||
name: run-tests
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
runs-on: 8-cores
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
#----------------------------------------------
|
||||
# check-out frontend-monorepo
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
#----------------------------------------------
|
||||
- name: Run tests
|
||||
working-directory: ./console-test
|
||||
run: poetry run pytest --numprocesses auto
|
||||
run: poetry run pytest -s --numprocesses auto
|
||||
- name: Check files
|
||||
run: |
|
||||
ls -al .
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
NX_VEGA_ENV=CUSTOM
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
@@ -8,3 +6,6 @@ NX_VEGA_EXPLORER_URL=/
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_TXS_LIST=0
|
||||
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||
NX_VEGA_ENV=DEVNET
|
||||
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
|
||||
@@ -10,3 +8,6 @@ NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
NX_SENTRY_DSN=https://b3a56b03eda842faad731f3ea9dfd1bc@o286262.ingest.sentry.io/6242427
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
@@ -11,3 +9,6 @@ NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
|
||||
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
|
||||
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
|
||||
@@ -10,4 +8,7 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/
|
||||
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
|
||||
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
@@ -11,3 +9,6 @@ NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
@@ -7,9 +7,10 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
|
||||
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import { PriceMonitoringBoundsInfoPanel } from '@vegaprotocol/markets';
|
||||
import {
|
||||
PriceMonitoringBoundsInfoPanel,
|
||||
SuccessionLineInfoPanel,
|
||||
} from '@vegaprotocol/markets';
|
||||
import {
|
||||
LiquidityInfoPanel,
|
||||
LiquidityMonitoringParametersInfoPanel,
|
||||
@@ -103,6 +106,8 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
<OracleInfoPanel market={market} type="settlementData" />
|
||||
</>
|
||||
)}
|
||||
<h2 className={`${headerClassName} mb-4`}>{t('Succession line')}</h2>
|
||||
<SuccessionLineInfoPanel market={market} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,6 +10,8 @@ import Hash from '../../links/hash';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new';
|
||||
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
|
||||
import { MarketLink } from '../../links';
|
||||
import { formatNumber } from '@vegaprotocol/utils';
|
||||
|
||||
export type Proposal = components['schemas']['v1ProposalSubmission'];
|
||||
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
|
||||
@@ -53,7 +55,11 @@ export function proposalTypeLabel(terms?: ProposalTerms): string {
|
||||
} else if (has(terms, 'updateAsset')) {
|
||||
return t('Update asset proposal');
|
||||
} else if (has(terms, 'newMarket')) {
|
||||
return t('New market proposal');
|
||||
if (terms?.newMarket?.changes?.successor) {
|
||||
return t('Successor market proposal');
|
||||
} else {
|
||||
return t('New market proposal');
|
||||
}
|
||||
} else if (has(terms, 'updateMarket')) {
|
||||
return t('Update market proposal');
|
||||
} else if (has(terms, 'updateNetworkParameter')) {
|
||||
@@ -85,6 +91,13 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
|
||||
}
|
||||
|
||||
const tx = proposal.terms?.newAsset || proposal.terms?.updateAsset;
|
||||
const isSuccessorMarketProposal =
|
||||
proposal?.terms?.newMarket?.changes?.successor;
|
||||
const parentMarketId =
|
||||
isSuccessorMarketProposal &&
|
||||
proposal?.terms.newMarket?.changes?.successor?.parentMarketId;
|
||||
const insurancePoolFraction =
|
||||
proposal?.terms?.newMarket?.changes?.successor?.insurancePoolFraction;
|
||||
|
||||
// This component is not rendered if no bundle is required
|
||||
const SignatureBundleComponent = proposal.terms?.newAsset
|
||||
@@ -111,6 +124,30 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
|
||||
<Hash text={deterministicId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{isSuccessorMarketProposal ? (
|
||||
<>
|
||||
{parentMarketId ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Previous market')}</TableCell>
|
||||
<TableCell>
|
||||
<MarketLink
|
||||
id={
|
||||
proposal.terms?.newMarket.changes.successor.parentMarketId
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{insurancePoolFraction ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Insurance pool fraction')}</TableCell>
|
||||
<TableCell>
|
||||
{formatNumber(Number(insurancePoolFraction) * 100, 0)}%
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
<ProposalSummary
|
||||
id={deterministicId}
|
||||
|
||||
@@ -78,7 +78,11 @@ export function getLabelForProposal(
|
||||
} else if (proposal.terms?.updateAsset) {
|
||||
return t('Proposal: Update asset');
|
||||
} else if (proposal.terms?.newMarket) {
|
||||
return t('Proposal: New market');
|
||||
if (proposal.terms?.newMarket.changes?.successor) {
|
||||
return t('Proposal: Successor market');
|
||||
} else {
|
||||
return t('Proposal: New market');
|
||||
}
|
||||
} else if (proposal.terms?.updateMarket) {
|
||||
return t('Proposal: Update market');
|
||||
} else if (proposal.terms?.updateNetworkParameter) {
|
||||
|
||||
@@ -22,6 +22,11 @@ import { Footer } from '../components/footer/footer';
|
||||
import { Header } from '../components/header';
|
||||
import { Routes } from './route-names';
|
||||
import { useExplorerNodeNamesLazyQuery } from './validators/__generated__/NodeNames';
|
||||
import {
|
||||
ProtocolUpgradeCountdownMode,
|
||||
ProtocolUpgradeInProgressNotification,
|
||||
ProtocolUpgradeProposalNotification,
|
||||
} from '@vegaprotocol/proposals';
|
||||
|
||||
const DialogsContainer = () => {
|
||||
const { isOpen, id, trigger, asJson, setOpen } = useAssetDetailsDialogStore();
|
||||
@@ -62,6 +67,10 @@ export const Layout = () => {
|
||||
/>
|
||||
)}
|
||||
<Header />
|
||||
<ProtocolUpgradeProposalNotification
|
||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||
/>
|
||||
<ProtocolUpgradeInProgressNotification />
|
||||
</div>
|
||||
<div className={fixedWidthClasses}>
|
||||
<main className="p-4">
|
||||
|
||||
@@ -730,6 +730,7 @@ context(
|
||||
.getByTestId('key-value-table-row')
|
||||
.contains(heading)
|
||||
.parent()
|
||||
.parent()
|
||||
.siblings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ import {
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-functions';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||
|
||||
const stakeValidatorListTotalStake = 'total-stake';
|
||||
const stakeValidatorListTotalShare = 'total-stake-share';
|
||||
const stakeValidatorListStakePercentage = 'stake-percentage';
|
||||
@@ -55,6 +58,10 @@ context(
|
||||
function () {
|
||||
// 1002-STKE-002, 1002-STKE-032
|
||||
before('visit staking tab and connect vega wallet', function () {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
cy.connectVegaWallet();
|
||||
@@ -65,6 +72,10 @@ context(
|
||||
beforeEach(
|
||||
'teardown wallet & drill into a specific validator',
|
||||
function () {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
// Go to homepage to allow wallet teardown without epoch timer refreshing page
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
} from '../../support/common.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { depositAsset } from '../../support/wallet-functions';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||
|
||||
const withdraw = 'withdraw';
|
||||
const withdrawalForm = 'withdraw-form';
|
||||
@@ -42,12 +44,20 @@ context(
|
||||
{ tags: '@slow' },
|
||||
function () {
|
||||
before('visit withdrawals and connect vega wallet', function () {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
depositAsset(usdcEthAddress, '1000', 5);
|
||||
});
|
||||
|
||||
beforeEach('Navigate to withdrawal page', function () {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
waitForBeginningOfEpoch,
|
||||
} from '../../support/staking.functions';
|
||||
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
|
||||
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||
|
||||
const guideLink = 'staking-guide-link';
|
||||
const validatorTitle = 'validator-node-title';
|
||||
@@ -33,11 +34,22 @@ const overstakedPenaltyToolTip = 'overstaked-penalty-tooltip';
|
||||
const multisigPenaltyToolTip = 'multisig-error-tooltip';
|
||||
const epochCountDown = 'epoch-countdown';
|
||||
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
|
||||
context('Validators Page - verify elements on page', function () {
|
||||
before('navigate to validators page', function () {
|
||||
before('navigate to validators page', () => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
cy.visit('/validators');
|
||||
});
|
||||
beforeEach(() => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
});
|
||||
|
||||
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
||||
it('Should have validators tab highlighted', function () {
|
||||
@@ -177,6 +189,11 @@ context('Validators Page - verify elements on page', function () {
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
before('connect wallets and click on validator', function () {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
cy.visit('/validators');
|
||||
cy.connectVegaWallet();
|
||||
clickOnValidatorFromList(0);
|
||||
});
|
||||
@@ -263,7 +280,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
|
||||
cy.getByTestId(epochCountDown).within(() => {
|
||||
cy.get(epochTitle).should('not.be.empty');
|
||||
cy.get(nextEpochInfo).should('contain.text', 'Next epoch');
|
||||
cy.get(nextEpochInfo, txTimeout).should('contain.text', 'Next epoch');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
vegaWalletFaucetAssetsWithoutCheck,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-functions';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||
|
||||
const walletContainer = 'aside [data-testid="vega-wallet"]';
|
||||
const walletHeader = '[data-testid="wallet-header"] h1';
|
||||
@@ -14,10 +16,6 @@ const dialogHeader = 'dialog-title';
|
||||
const walletDialogHeader = 'wallet-dialog-title';
|
||||
const connectorsList = 'connectors-list';
|
||||
const dialogCloseBtn = 'dialog-close';
|
||||
const restConnectorForm = 'rest-connector-form';
|
||||
const restWallet = '#wallet';
|
||||
const restPassphrase = '#passphrase';
|
||||
const restConnectBtn = '[type="submit"]';
|
||||
const accountNo = 'vega-account-truncated';
|
||||
const currencyTitle = 'currency-title';
|
||||
const currencyValue = 'currency-value';
|
||||
@@ -69,7 +67,7 @@ context(
|
||||
cy.get(dialog).within(() => {
|
||||
cy.getByTestId(walletDialogHeader)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect');
|
||||
.and('have.text', 'Get a Vega wallet');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -77,10 +75,7 @@ context(
|
||||
cy.getByTestId(connectorsList).within(() => {
|
||||
cy.getByTestId('connector-jsonRpc')
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet');
|
||||
cy.getByTestId('connector-rest')
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Hosted Fairground wallet');
|
||||
.and('have.text', 'Use the Desktop App/CLI');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,48 +86,14 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
describe('when rest connector form opened', function () {
|
||||
before('click hosted wallet app button', function () {
|
||||
cy.getByTestId(connectorsList).within(() => {
|
||||
cy.getByTestId('connector-rest').click();
|
||||
});
|
||||
});
|
||||
|
||||
// 0002-WCON-002
|
||||
it('should have wallet field visible', function () {
|
||||
cy.getByTestId(restConnectorForm).within(() => {
|
||||
cy.get(restWallet).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have password field visible', function () {
|
||||
cy.getByTestId(restConnectorForm).within(() => {
|
||||
cy.get(restPassphrase).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have connect button visible', function () {
|
||||
cy.getByTestId(restConnectorForm).within(() => {
|
||||
cy.get(restConnectBtn)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have close button visible', function () {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.getByTestId(dialogCloseBtn).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
after('close dialog', function () {
|
||||
cy.getByTestId(dialogCloseBtn).click().should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when vega wallet connected', function () {
|
||||
before('connect vega wallet', function () {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
cy.visit('/');
|
||||
cy.wait('@ChainId');
|
||||
cy.connectVegaWallet();
|
||||
vegaWalletTeardown();
|
||||
});
|
||||
@@ -315,6 +276,10 @@ context(
|
||||
];
|
||||
|
||||
before('faucet assets to connected vega wallet', function () {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
for (const { id, amount } of assets) {
|
||||
vegaWalletFaucetAssetsWithoutCheck(id, amount, vegaWalletPublicKey);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,12 @@ NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
LC_ALL="en_US.UTF-8"
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Nav } from '../nav';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
ProtocolUpgradeCountdownMode,
|
||||
ProtocolUpgradeInProgressNotification,
|
||||
ProtocolUpgradeProposalNotification,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||
@@ -49,6 +50,7 @@ const NotificationsContainer = () => {
|
||||
<ProtocolUpgradeProposalNotification
|
||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||
/>
|
||||
<ProtocolUpgradeInProgressNotification />
|
||||
{isReadOnly ? (
|
||||
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
||||
) : null}
|
||||
|
||||
@@ -15,6 +15,7 @@ export const VegaWalletPrompt = () => {
|
||||
</Link>
|
||||
<ButtonLink
|
||||
className="text-neutral-500"
|
||||
data-testid="view-as-user"
|
||||
onClick={() => setViewAsDialog(true)}
|
||||
>
|
||||
{t('viewAsParty')}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
RestConnector,
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
InjectedConnector,
|
||||
@@ -8,13 +7,11 @@ import {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
export const injected = new InjectedConnector();
|
||||
export const rest = new RestConnector();
|
||||
export const jsonRpc = new JsonRpcConnector();
|
||||
export const view = new ViewConnector(urlParams.get('address'));
|
||||
|
||||
export const Connectors = {
|
||||
injected,
|
||||
rest,
|
||||
jsonRpc,
|
||||
view,
|
||||
};
|
||||
|
||||
+83
-9
@@ -45,8 +45,10 @@ export const useMarketDataDialogStore = create<MarketDataDialogState>(
|
||||
|
||||
export const ProposalMarketData = ({
|
||||
marketData,
|
||||
parentMarketData,
|
||||
}: {
|
||||
marketData: MarketInfoWithData;
|
||||
parentMarketData?: MarketInfoWithData;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { isOpen, open, close } = useMarketDataDialogStore();
|
||||
@@ -58,8 +60,21 @@ export const ProposalMarketData = ({
|
||||
|
||||
const settlementData = marketData.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
|
||||
const parentSettlementData =
|
||||
parentMarketData?.tradableInstrument.instrument?.product
|
||||
?.dataSourceSpecForSettlementData?.data;
|
||||
const terminationData = marketData.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
|
||||
const parentTerminationData =
|
||||
parentMarketData?.tradableInstrument.instrument?.product
|
||||
?.dataSourceSpecForTradingTermination?.data;
|
||||
|
||||
const isParentSettlementDataEqual =
|
||||
parentSettlementData !== undefined &&
|
||||
isEqual(settlementData, parentSettlementData);
|
||||
const isParentTerminationDataEqual =
|
||||
parentTerminationData !== undefined &&
|
||||
isEqual(terminationData, parentTerminationData);
|
||||
|
||||
const getSigners = (data: DataSourceDefinition) => {
|
||||
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
|
||||
@@ -97,12 +112,22 @@ export const ProposalMarketData = ({
|
||||
<AccordionItem
|
||||
itemId="key-details"
|
||||
title={t('Key details')}
|
||||
content={<KeyDetailsInfoPanel market={marketData} />}
|
||||
content={
|
||||
<KeyDetailsInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="instrument"
|
||||
title={t('Instrument')}
|
||||
content={<InstrumentInfoPanel market={marketData} />}
|
||||
content={
|
||||
<InstrumentInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{isEqual(
|
||||
getSigners(settlementData),
|
||||
@@ -115,6 +140,11 @@ export const ProposalMarketData = ({
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="settlementData"
|
||||
parentMarket={
|
||||
isParentSettlementDataEqual
|
||||
? undefined
|
||||
: parentMarketData
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -127,6 +157,11 @@ export const ProposalMarketData = ({
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="settlementData"
|
||||
parentMarket={
|
||||
isParentSettlementDataEqual
|
||||
? undefined
|
||||
: parentMarketData
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -135,11 +170,21 @@ export const ProposalMarketData = ({
|
||||
itemId="termination-oracle"
|
||||
title={t('Termination Oracle')}
|
||||
content={
|
||||
<OracleInfoPanel market={marketData} type="termination" />
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="termination"
|
||||
parentMarket={
|
||||
isParentTerminationDataEqual
|
||||
? undefined
|
||||
: parentMarketData
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{/*Note: successor markets will not differ in their settlement*/}
|
||||
{/*assets, so no need to pass in parent market data for comparison.*/}
|
||||
<AccordionItem
|
||||
itemId="settlement-asset"
|
||||
title={t('Settlement asset')}
|
||||
@@ -148,22 +193,42 @@ export const ProposalMarketData = ({
|
||||
<AccordionItem
|
||||
itemId="metadata"
|
||||
title={t('Metadata')}
|
||||
content={<MetadataInfoPanel market={marketData} />}
|
||||
content={
|
||||
<MetadataInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="risk-model"
|
||||
title={t('Risk model')}
|
||||
content={<RiskModelInfoPanel market={marketData} />}
|
||||
content={
|
||||
<RiskModelInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="risk-parameters"
|
||||
title={t('Risk parameters')}
|
||||
content={<RiskParametersInfoPanel market={marketData} />}
|
||||
content={
|
||||
<RiskParametersInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="risk-factors"
|
||||
title={t('Risk factors')}
|
||||
content={<RiskFactorsInfoPanel market={marketData} />}
|
||||
content={
|
||||
<RiskFactorsInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{(
|
||||
marketData.priceMonitoringSettings?.parameters?.triggers || []
|
||||
@@ -174,6 +239,7 @@ export const ProposalMarketData = ({
|
||||
content={
|
||||
<PriceMonitoringBoundsInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
triggerIndex={triggerIndex}
|
||||
/>
|
||||
}
|
||||
@@ -183,13 +249,21 @@ export const ProposalMarketData = ({
|
||||
itemId="liqudity-monitoring-parameters"
|
||||
title={t('Liquidity monitoring parameters')}
|
||||
content={
|
||||
<LiquidityMonitoringParametersInfoPanel market={marketData} />
|
||||
<LiquidityMonitoringParametersInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="liquidity-price-range"
|
||||
title={t('Liquidity price range')}
|
||||
content={<LiquidityPriceRangeInfoPanel market={marketData} />}
|
||||
content={
|
||||
<LiquidityPriceRangeInfoPanel
|
||||
market={marketData}
|
||||
parentMarket={parentMarketData}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Accordion>
|
||||
</div>
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface ProposalProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
networkParams: Partial<NetworkParamsResult>;
|
||||
newMarketData?: MarketInfoWithData | null;
|
||||
parentMarketData?: MarketInfoWithData | null;
|
||||
assetData?: AssetQuery | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
restData: any;
|
||||
@@ -46,6 +47,7 @@ export const Proposal = ({
|
||||
networkParams,
|
||||
restData,
|
||||
newMarketData,
|
||||
parentMarketData,
|
||||
assetData,
|
||||
originalMarketProposalRestData,
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
@@ -157,7 +159,10 @@ export const Proposal = ({
|
||||
|
||||
{newMarketData && (
|
||||
<div className="mb-4">
|
||||
<ProposalMarketData marketData={newMarketData} />
|
||||
<ProposalMarketData
|
||||
marketData={newMarketData}
|
||||
parentMarketData={parentMarketData ? parentMarketData : undefined}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
||||
|
||||
export const ProposalContainer = () => {
|
||||
const [
|
||||
@@ -54,6 +57,10 @@ export const ProposalContainer = () => {
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
|
||||
const successor = useSuccessorMarketProposalDetails(params.proposalId);
|
||||
|
||||
const isSuccessor = !!successor?.parentMarketId || !!successor.code;
|
||||
|
||||
const {
|
||||
state: {
|
||||
data: originalMarketProposalRestData,
|
||||
@@ -96,6 +103,36 @@ export const ProposalContainer = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
data: parentMarketId,
|
||||
loading: parentMarketIdLoading,
|
||||
error: parentMarketIdError,
|
||||
} = useParentMarketIdQuery({
|
||||
variables: {
|
||||
marketId: newMarketData?.data?.market?.id || '',
|
||||
},
|
||||
skip:
|
||||
!FLAGS.SUCCESSOR_MARKETS ||
|
||||
!isSuccessor ||
|
||||
!newMarketData?.data?.market?.id,
|
||||
});
|
||||
|
||||
const {
|
||||
data: parentMarketData,
|
||||
loading: parentMarketLoading,
|
||||
error: parentMarketError,
|
||||
} = useDataProvider({
|
||||
dataProvider: marketInfoWithDataProvider,
|
||||
skipUpdates: true,
|
||||
variables: {
|
||||
marketId: parentMarketId?.market?.parentMarketID || '',
|
||||
skip:
|
||||
!FLAGS.SUCCESSOR_MARKETS ||
|
||||
!isSuccessor ||
|
||||
!parentMarketId?.market?.parentMarketID,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
data: assetData,
|
||||
loading: assetLoading,
|
||||
@@ -160,6 +197,8 @@ export const ProposalContainer = () => {
|
||||
newMarketLoading ||
|
||||
assetLoading ||
|
||||
networkParamsLoading ||
|
||||
parentMarketIdLoading ||
|
||||
parentMarketLoading ||
|
||||
(restLoading ? (restLoading as boolean) : false) ||
|
||||
(originalMarketProposalRestLoading
|
||||
? (originalMarketProposalRestLoading as boolean)
|
||||
@@ -172,15 +211,18 @@ export const ProposalContainer = () => {
|
||||
error ||
|
||||
newMarketError ||
|
||||
assetError ||
|
||||
networkParamsError ||
|
||||
parentMarketIdError ||
|
||||
parentMarketError ||
|
||||
restError ||
|
||||
originalMarketProposalRestError ||
|
||||
previouslyEnactedMarketProposalsRestError ||
|
||||
networkParamsError
|
||||
previouslyEnactedMarketProposalsRestError
|
||||
}
|
||||
data={{
|
||||
...data,
|
||||
...networkParams,
|
||||
...(newMarketData ? { newMarketData } : {}),
|
||||
...(parentMarketData ? { parentMarketData } : {}),
|
||||
...(assetData ? { assetData } : {}),
|
||||
...(restData ? { restData } : {}),
|
||||
...(originalMarketProposalRestData
|
||||
@@ -197,6 +239,7 @@ export const ProposalContainer = () => {
|
||||
networkParams={networkParams}
|
||||
restData={restData}
|
||||
newMarketData={newMarketData}
|
||||
parentMarketData={parentMarketData}
|
||||
assetData={assetData}
|
||||
originalMarketProposalRestData={originalMarketProposalRestData}
|
||||
mostRecentlyEnactedAssociatedMarketProposal={
|
||||
|
||||
@@ -169,14 +169,13 @@ describe('home', { tags: '@regression' }, () => {
|
||||
|
||||
it('click get started button should open connect dialog', () => {
|
||||
cy.getByTestId('welcome-dialog').should('be.visible');
|
||||
cy.getByTestId('get-started-button').click();
|
||||
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
|
||||
cy.window().then((window) => {
|
||||
expect(window.localStorage.getItem('vega_onboarding_viewed')).to.equal(
|
||||
'true'
|
||||
);
|
||||
// @ts-ignore stub it out just for test case
|
||||
window.vega = {};
|
||||
cy.getByTestId('get-started-button').click();
|
||||
cy.getByTestId('wallet-dialog-title').should('contain.text', 'Connect');
|
||||
});
|
||||
cy.getByTestId('wallet-dialog-title').should('contain.text', 'Connect');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('fills', { tags: '@regression' }, () => {
|
||||
cy.getByTestId(tabFills).contains('Market');
|
||||
cy.getByTestId(tabFills)
|
||||
.get(
|
||||
'[role="gridcell"][col-id="market.tradableInstrument.instrument.name"]'
|
||||
'[role="gridcell"][col-id="market.tradableInstrument.instrument.code"]'
|
||||
)
|
||||
.each(($marketSymbol) => {
|
||||
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
|
||||
|
||||
@@ -142,22 +142,22 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
||||
it('sorting by Market', () => {
|
||||
visitAndClickPositions();
|
||||
const marketsSortedDefault = [
|
||||
'ACTIVE MARKET',
|
||||
'Apple Monthly (30 Jun 2022)',
|
||||
'ETHBTC Quarterly (30 Jun 2022)',
|
||||
'SUSPENDED MARKET',
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'SOLUSD',
|
||||
];
|
||||
const marketsSortedAsc = [
|
||||
'ACTIVE MARKET',
|
||||
'Apple Monthly (30 Jun 2022)',
|
||||
'ETHBTC Quarterly (30 Jun 2022)',
|
||||
'SUSPENDED MARKET',
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'SOLUSD',
|
||||
];
|
||||
const marketsSortedDesc = [
|
||||
'SUSPENDED MARKET',
|
||||
'ETHBTC Quarterly (30 Jun 2022)',
|
||||
'Apple Monthly (30 Jun 2022)',
|
||||
'ACTIVE MARKET',
|
||||
'SOLUSD',
|
||||
'ETHBTC.QM21',
|
||||
'BTCUSD.MF21',
|
||||
'AAPL.MF21',
|
||||
];
|
||||
cy.getByTestId(positions).click();
|
||||
// 7004-POSI-003
|
||||
@@ -310,7 +310,7 @@ function validatePositionsDisplayed(multiKey = false) {
|
||||
cy.getByTestId('tab-positions').should('be.visible');
|
||||
cy.getByTestId('tab-positions')
|
||||
.get('.ag-center-cols-container .ag-row')
|
||||
.first()
|
||||
.eq(multiKey ? 3 : 1)
|
||||
.within(() => {
|
||||
cy.get('[col-id="marketName"]')
|
||||
.should('be.visible')
|
||||
|
||||
@@ -5,118 +5,8 @@ import {
|
||||
|
||||
const connectVegaBtn = 'connect-vega-wallet';
|
||||
const manageVegaBtn = 'manage-vega-wallet';
|
||||
const form = 'rest-connector-form';
|
||||
const dialogContent = 'dialog-content';
|
||||
|
||||
describe(
|
||||
'connect hosted wallet',
|
||||
{ tags: '@smoke', testIsolation: true },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
// Using portfolio page as it requires vega wallet connection
|
||||
cy.visit('/#/portfolio');
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
});
|
||||
|
||||
it('can connect', () => {
|
||||
// 0002-WCON-002
|
||||
// 0002-WCON-003
|
||||
// 0002-WCON-039
|
||||
// 0002-WCON-017
|
||||
// 0002-WCON-018
|
||||
// 0002-WCON-019
|
||||
|
||||
// Mock authentication
|
||||
cy.intercept(
|
||||
'POST',
|
||||
'https://wallet.testnet.vega.xyz/api/v1/auth/token',
|
||||
{
|
||||
body: {
|
||||
token: 'test-token',
|
||||
},
|
||||
}
|
||||
);
|
||||
// Mock getting keys from wallet
|
||||
cy.intercept('GET', 'https://wallet.testnet.vega.xyz/api/v1/keys', {
|
||||
body: {
|
||||
keys: [
|
||||
{
|
||||
algorithm: {
|
||||
name: 'algo',
|
||||
version: 1,
|
||||
},
|
||||
index: 0,
|
||||
meta: [],
|
||||
pub: 'HOSTED_PUBKEY',
|
||||
tainted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
cy.getByTestId(connectVegaBtn).click();
|
||||
cy.contains(
|
||||
'Choose wallet app to connect, or to change port or server URL enter a custom wallet location first'
|
||||
);
|
||||
cy.contains('Connect Vega wallet');
|
||||
cy.contains('Hosted Fairground wallet');
|
||||
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-rest"]')
|
||||
.click();
|
||||
cy.getByTestId(form).find('#wallet').click().type('user');
|
||||
cy.getByTestId(form).find('#passphrase').click().type('pass');
|
||||
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
|
||||
cy.getByTestId(manageVegaBtn).should('exist');
|
||||
cy.getByTestId('manage-vega-wallet').click();
|
||||
cy.getByTestId('keypair-list').should('exist');
|
||||
});
|
||||
|
||||
it('doesnt connect with invalid credentials', () => {
|
||||
// 0002-WCON-020
|
||||
|
||||
// Mock incorrect username/password
|
||||
cy.intercept(
|
||||
'POST',
|
||||
'https://wallet.testnet.vega.xyz/api/v1/auth/token',
|
||||
{
|
||||
body: {
|
||||
error: 'No wallet',
|
||||
},
|
||||
statusCode: 403, // 403 forbidden invalid crednetials
|
||||
}
|
||||
);
|
||||
cy.getByTestId(connectVegaBtn).click();
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-rest"]')
|
||||
.click();
|
||||
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');
|
||||
});
|
||||
|
||||
it('doesnt connect with empty fields', () => {
|
||||
cy.getByTestId(connectVegaBtn).click();
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-rest"]')
|
||||
.click();
|
||||
|
||||
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
|
||||
cy.getByTestId(form)
|
||||
.find('#wallet')
|
||||
.next('[data-testid="input-error-text"]')
|
||||
.should('have.text', 'Required');
|
||||
cy.getByTestId(form)
|
||||
.find('#passphrase')
|
||||
.next('[data-testid="input-error-text"]')
|
||||
.should('have.text', 'Required');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
// Using portfolio page as it requires vega wallet connection
|
||||
@@ -196,8 +86,6 @@ describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
cy.getByTestId('connect-vega-wallet').should('exist');
|
||||
cy.getByTestId('manage-vega-wallet').should('not.exist');
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.contains(
|
||||
'Choose wallet app to connect, or to change port or server URL enter a custom wallet location first'
|
||||
);
|
||||
cy.contains('Enter a custom wallet location');
|
||||
});
|
||||
});
|
||||
|
||||
+6
-1
@@ -13,9 +13,14 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
|
||||
@@ -20,4 +20,7 @@ NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supp
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
|
||||
@@ -18,4 +18,7 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
@@ -21,3 +21,6 @@ NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
@@ -20,4 +20,7 @@ NX_APP_VERSION=v0.20.19-core-0.71.6
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
@@ -19,4 +19,7 @@ NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
@@ -19,4 +19,7 @@ NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
|
||||
@@ -10,6 +10,7 @@ import { TradeGrid } from './trade-grid';
|
||||
import { TradePanels } from './trade-panels';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
|
||||
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||
@@ -64,6 +65,8 @@ export const MarketPage = () => {
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const lastMarketId = useGlobalStore((store) => store.marketId);
|
||||
|
||||
const onSelect = useMarketClickHandler();
|
||||
|
||||
const { data, error, loading } = useMarket(marketId);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -84,6 +87,7 @@ export const MarketPage = () => {
|
||||
return (
|
||||
<TradeGrid
|
||||
market={data}
|
||||
onSelect={onSelect}
|
||||
pinnedAsset={
|
||||
data?.tradableInstrument.instrument.product.settlementAsset
|
||||
}
|
||||
@@ -93,12 +97,11 @@ export const MarketPage = () => {
|
||||
return (
|
||||
<TradePanels
|
||||
market={data}
|
||||
pinnedAsset={
|
||||
data?.tradableInstrument.instrument.product.settlementAsset
|
||||
}
|
||||
onSelect={onSelect}
|
||||
onClickCollateral={() => navigate('/portfolio')}
|
||||
/>
|
||||
);
|
||||
}, [largeScreen, data]);
|
||||
}, [largeScreen, data, onSelect, navigate]);
|
||||
|
||||
if (!data && marketId) {
|
||||
return (
|
||||
|
||||
@@ -9,6 +9,7 @@ import { OracleBanner } from '@vegaprotocol/markets';
|
||||
import type { Market } from '@vegaprotocol/markets';
|
||||
import { Filter } from '@vegaprotocol/orders';
|
||||
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
import {
|
||||
ResizableGrid,
|
||||
ResizableGridPanel,
|
||||
@@ -23,6 +24,7 @@ import { FLAGS } from '@vegaprotocol/environment';
|
||||
|
||||
interface TradeGridProps {
|
||||
market: Market | null;
|
||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}
|
||||
|
||||
@@ -38,6 +40,7 @@ const MainGrid = memo(
|
||||
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
|
||||
id: 'middle-1',
|
||||
});
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
|
||||
return (
|
||||
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
||||
@@ -95,27 +98,30 @@ const MainGrid = memo(
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-bottom">
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<TradingViews.positions.component />
|
||||
<TradingViews.positions.component
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
id="open-orders"
|
||||
name={t('Open')}
|
||||
menu={<TradingViews.activeOrders.menu marketId={marketId} />}
|
||||
>
|
||||
<TradingViews.orders.component filter={Filter.Open} />
|
||||
<Tab id="open-orders" name={t('Open')}>
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Open}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab id="closed-orders" name={t('Closed')}>
|
||||
<TradingViews.orders.component filter={Filter.Closed} />
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Closed}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab id="rejected-orders" name={t('Rejected')}>
|
||||
<TradingViews.orders.component filter={Filter.Rejected} />
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Rejected}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
id="orders"
|
||||
name={t('All')}
|
||||
menu={<TradingViews.orders.menu marketId={marketId} />}
|
||||
>
|
||||
<TradingViews.orders.component />
|
||||
<Tab id="orders" name={t('All')}>
|
||||
<TradingViews.orders.component marketId={marketId} />
|
||||
</Tab>
|
||||
{FLAGS.STOP_ORDERS ? (
|
||||
<Tab id="stop-orders" name={t('Stop orders')}>
|
||||
@@ -123,14 +129,17 @@ const MainGrid = memo(
|
||||
</Tab>
|
||||
) : null}
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
<TradingViews.fills.component marketId={marketId} />
|
||||
<TradingViews.fills.component
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
id="accounts"
|
||||
name={t('Collateral')}
|
||||
menu={<TradingViews.collateral.menu />}
|
||||
>
|
||||
<TradingViews.collateral.component pinnedAsset={pinnedAsset} />
|
||||
<Tab id="accounts" name={t('Collateral')}>
|
||||
<TradingViews.collateral.component
|
||||
pinnedAsset={pinnedAsset}
|
||||
onMarketClick={onMarketClick}
|
||||
hideButtons
|
||||
/>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</TradeGridChild>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||
import type { Market } from '@vegaprotocol/markets';
|
||||
import { OracleBanner } from '@vegaprotocol/markets';
|
||||
import {
|
||||
useMarketClickHandler,
|
||||
useMarketLiquidityClickHandler,
|
||||
} from '../../lib/hooks/use-market-click-handler';
|
||||
import type { TradingView } from './trade-views';
|
||||
import { TradingViews } from './trade-views';
|
||||
import { useState } from 'react';
|
||||
import { memo, useState } from 'react';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { NO_MARKET } from './constants';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
@@ -16,14 +20,33 @@ import { FLAGS } from '@vegaprotocol/environment';
|
||||
|
||||
interface TradePanelsProps {
|
||||
market: Market | null;
|
||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
onOrderTypeClick?: (marketId: string) => void;
|
||||
onClickCollateral: () => void;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}
|
||||
|
||||
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
export const TradePanels = ({
|
||||
market,
|
||||
onSelect,
|
||||
onClickCollateral,
|
||||
pinnedAsset,
|
||||
}: TradePanelsProps) => {
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
||||
|
||||
const [view, setView] = useState<TradingView>('candles');
|
||||
|
||||
const renderView = () => {
|
||||
const Component = TradingViews[view].component;
|
||||
const Component = memo<{
|
||||
marketId: string;
|
||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
onOrderTypeClick?: (marketId: string) => void;
|
||||
onClickCollateral: () => void;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}>(TradingViews[view].component);
|
||||
|
||||
if (!Component) {
|
||||
throw new Error(`No component for view: ${view}`);
|
||||
@@ -31,7 +54,16 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
|
||||
if (!market) return <Splash>{NO_MARKET}</Splash>;
|
||||
|
||||
return <Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
|
||||
return (
|
||||
<Component
|
||||
marketId={market?.id}
|
||||
onSelect={onSelect}
|
||||
onClickCollateral={onClickCollateral}
|
||||
pinnedAsset={pinnedAsset}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderMenu = () => {
|
||||
@@ -39,10 +71,9 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
|
||||
if ('menu' in viewCfg) {
|
||||
const Menu = viewCfg.menu;
|
||||
|
||||
return (
|
||||
<div className="flex gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
|
||||
<Menu marketId={market?.id || ''} />
|
||||
<Menu />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
CandlesChartContainer,
|
||||
CandlesMenu,
|
||||
} from '@vegaprotocol/candles-chart';
|
||||
import { Filter, OpenOrdersMenu } from '@vegaprotocol/orders';
|
||||
import { Filter } from '@vegaprotocol/orders';
|
||||
import { NO_MARKET } from './constants';
|
||||
import { OrderbookContainer } from '../../components/orderbook-container';
|
||||
import { FillsContainer } from '../../components/fills-container';
|
||||
@@ -16,7 +16,6 @@ import { LiquidityContainer } from '../../components/liquidity-container';
|
||||
import type { OrderContainerProps } from '../../components/orders-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
import { StopOrdersContainer } from '../../components/stop-orders-container';
|
||||
import { AccountsMenu } from '../../components/accounts-menu';
|
||||
|
||||
type MarketDependantView =
|
||||
| typeof CandlesChartContainer
|
||||
@@ -63,7 +62,6 @@ export const TradingViews = {
|
||||
component: (props: OrderContainerProps) => (
|
||||
<OrdersContainer {...props} filter={Filter.Open} />
|
||||
),
|
||||
menu: OpenOrdersMenu,
|
||||
},
|
||||
closedOrders: {
|
||||
label: 'Closed',
|
||||
@@ -80,16 +78,11 @@ export const TradingViews = {
|
||||
orders: {
|
||||
label: 'All',
|
||||
component: OrdersContainer,
|
||||
menu: OpenOrdersMenu,
|
||||
},
|
||||
stopOrders: {
|
||||
label: 'Stop',
|
||||
component: StopOrdersContainer,
|
||||
},
|
||||
collateral: {
|
||||
label: 'Collateral',
|
||||
component: AccountsContainer,
|
||||
menu: AccountsMenu,
|
||||
},
|
||||
collateral: { label: 'Collateral', component: AccountsContainer },
|
||||
fills: { label: 'Fills', component: FillsContainer },
|
||||
};
|
||||
|
||||
@@ -411,24 +411,26 @@ describe('Closed', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider
|
||||
mocks={[
|
||||
mixedMarketsMock,
|
||||
marketsDataMock,
|
||||
oracleDataMock,
|
||||
successorMarketsMock,
|
||||
]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
await act(() => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider
|
||||
mocks={[
|
||||
mixedMarketsMock,
|
||||
marketsDataMock,
|
||||
oracleDataMock,
|
||||
successorMarketsMock,
|
||||
]}
|
||||
>
|
||||
<Closed />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
>
|
||||
<Closed />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { DepositsTable } from '@vegaprotocol/deposits';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useSidebar, ViewType } from '../../components/sidebar';
|
||||
|
||||
export const DepositsContainer = () => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: depositsProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
return (
|
||||
<div className="h-full">
|
||||
<DepositsTable
|
||||
rowData={data}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
|
||||
/>
|
||||
{!isReadOnly && (
|
||||
<div className="h-auto flex justify-end p-2 bottom-0 right-0 absolute dark:bg-black/75 bg-white/75 rounded">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => setView({ type: ViewType.Deposit })}
|
||||
data-testid="deposit-button"
|
||||
>
|
||||
{t('Deposit')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -6,11 +6,12 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
|
||||
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
||||
import { usePageTitleStore } from '../../stores';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
import { AccountsContainer } from '../../components/accounts-container';
|
||||
import { DepositsContainer } from '../../components/deposits-container';
|
||||
import { DepositsContainer } from './deposits-container';
|
||||
import { FillsContainer } from '../../components/fills-container';
|
||||
import { PositionsContainer } from '../../components/positions-container';
|
||||
import { WithdrawalsContainer } from '../../components/withdrawals-container';
|
||||
import { WithdrawalsContainer } from './withdrawals-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
import { LedgerContainer } from '../../components/ledger-container';
|
||||
import { AccountHistoryContainer } from './account-history-container';
|
||||
@@ -20,9 +21,6 @@ import {
|
||||
usePaneLayout,
|
||||
} from '../../components/resizable-grid';
|
||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
import { AccountsMenu } from '../../components/accounts-menu';
|
||||
import { DepositsMenu } from '../../components/deposits-menu';
|
||||
import { WithdrawalsMenu } from '../../components/withdrawals-menu';
|
||||
|
||||
const WithdrawalsIndicator = () => {
|
||||
const { ready } = useIncompleteWithdrawals();
|
||||
@@ -53,6 +51,7 @@ export const Portfolio = () => {
|
||||
}
|
||||
}, [init, view, setView]);
|
||||
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
||||
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
|
||||
return (
|
||||
@@ -65,13 +64,13 @@ export const Portfolio = () => {
|
||||
<AccountHistoryContainer />
|
||||
</Tab>
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<PositionsContainer allKeys />
|
||||
<PositionsContainer onMarketClick={onMarketClick} allKeys />
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('Orders')}>
|
||||
<OrdersContainer />
|
||||
</Tab>
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
<FillsContainer />
|
||||
<FillsContainer onMarketClick={onMarketClick} />
|
||||
</Tab>
|
||||
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
||||
<LedgerContainer />
|
||||
@@ -86,21 +85,16 @@ export const Portfolio = () => {
|
||||
>
|
||||
<PortfolioGridChild>
|
||||
<Tabs storageKey="console-portfolio-bottom">
|
||||
<Tab
|
||||
id="collateral"
|
||||
name={t('Collateral')}
|
||||
menu={<AccountsMenu />}
|
||||
>
|
||||
<Tab id="collateral" name={t('Collateral')}>
|
||||
<AccountsContainer />
|
||||
</Tab>
|
||||
<Tab id="deposits" name={t('Deposits')} menu={<DepositsMenu />}>
|
||||
<Tab id="deposits" name={t('Deposits')}>
|
||||
<DepositsContainer />
|
||||
</Tab>
|
||||
<Tab
|
||||
id="withdrawals"
|
||||
name={t('Withdrawals')}
|
||||
indicator={<WithdrawalsIndicator />}
|
||||
menu={<WithdrawalsMenu />}
|
||||
>
|
||||
<WithdrawalsContainer />
|
||||
</Tab>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
withdrawalProvider,
|
||||
WithdrawalsTable,
|
||||
useIncompleteWithdrawals,
|
||||
} from '@vegaprotocol/withdraws';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
|
||||
export const WithdrawalsContainer = () => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: withdrawalProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const { ready, delayed } = useIncompleteWithdrawals();
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="h-full relative">
|
||||
<WithdrawalsTable
|
||||
data-testid="withdrawals-history"
|
||||
rowData={data}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No withdrawals')}
|
||||
ready={ready}
|
||||
delayed={delayed}
|
||||
/>
|
||||
</div>
|
||||
{!isReadOnly && (
|
||||
<div className="h-auto flex justify-end p-2 bottom-0 right-0 absolute dark:bg-black/75 bg-white/75 rounded">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => setView({ type: ViewType.Withdraw })}
|
||||
data-testid="withdraw-dialog-button"
|
||||
>
|
||||
{t('Make withdrawal')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
@@ -11,14 +12,16 @@ import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
|
||||
export const AccountsContainer = ({
|
||||
pinnedAsset,
|
||||
hideButtons,
|
||||
onMarketClick,
|
||||
}: {
|
||||
pinnedAsset?: PinnedAsset;
|
||||
hideButtons?: boolean;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
}) => {
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
@@ -45,23 +48,44 @@ export const AccountsContainer = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<AccountManager
|
||||
partyId={pubKey}
|
||||
onClickAsset={onClickAsset}
|
||||
onClickWithdraw={(assetId) => {
|
||||
setView({ type: ViewType.Withdraw, assetId });
|
||||
}}
|
||||
onClickDeposit={(assetId) => {
|
||||
setView({ type: ViewType.Deposit, assetId });
|
||||
}}
|
||||
onClickTransfer={(assetId) => {
|
||||
setView({ type: ViewType.Transfer, assetId });
|
||||
}}
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
pinnedAsset={pinnedAsset}
|
||||
gridProps={gridStoreCallbacks}
|
||||
/>
|
||||
<div className="h-full relative">
|
||||
<AccountManager
|
||||
partyId={pubKey}
|
||||
onClickAsset={onClickAsset}
|
||||
onClickWithdraw={(assetId) => {
|
||||
setView({ type: ViewType.Withdraw, assetId });
|
||||
}}
|
||||
onClickDeposit={(assetId) => {
|
||||
setView({ type: ViewType.Deposit, assetId });
|
||||
}}
|
||||
onClickTransfer={(assetId) => {
|
||||
setView({ type: ViewType.Transfer, assetId });
|
||||
}}
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
pinnedAsset={pinnedAsset}
|
||||
gridProps={gridStoreCallbacks}
|
||||
/>
|
||||
{!isReadOnly && !hideButtons && (
|
||||
<div className="flex gap-2 justify-end p-2 absolute bottom-0 right-0 dark:bg-black/75 bg-white/75 rounded">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
data-testid="open-transfer"
|
||||
onClick={() => setView({ type: ViewType.Transfer })}
|
||||
>
|
||||
{t('Transfer')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => setView({ type: ViewType.Deposit })}
|
||||
>
|
||||
{t('Deposit')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
|
||||
export const AccountsMenu = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
size="extra-small"
|
||||
data-testid="open-transfer"
|
||||
onClick={() => setView({ type: ViewType.Transfer })}
|
||||
>
|
||||
{t('Transfer')}
|
||||
</TradingButton>
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
size="extra-small"
|
||||
onClick={() => setView({ type: ViewType.Deposit })}
|
||||
>
|
||||
{t('Deposit')}
|
||||
</TradingButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './accounts-menu';
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { DepositsTable } from '@vegaprotocol/deposits';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
|
||||
export const DepositsContainer = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: depositsProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
return (
|
||||
<DepositsTable
|
||||
rowData={data}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './deposits-container';
|
||||
@@ -1,18 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
|
||||
export const DepositsMenu = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
|
||||
return (
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
size="extra-small"
|
||||
onClick={() => setView({ type: ViewType.Deposit })}
|
||||
data-testid="deposit-button"
|
||||
>
|
||||
{t('Deposit')}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './deposits-menu';
|
||||
@@ -7,10 +7,14 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
|
||||
export const FillsContainer = ({ marketId }: { marketId?: string }) => {
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
export const FillsContainer = ({
|
||||
marketId,
|
||||
onMarketClick,
|
||||
}: {
|
||||
marketId?: string;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
}) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
|
||||
const gridStore = useFillsStore((store) => store.gridStore);
|
||||
|
||||
@@ -25,10 +25,11 @@ export const FilterStatusValue = {
|
||||
};
|
||||
|
||||
export interface OrderContainerProps {
|
||||
marketId?: string;
|
||||
filter?: Filter;
|
||||
}
|
||||
|
||||
export const OrdersContainer = ({ filter }: OrderContainerProps) => {
|
||||
export const OrdersContainer = ({ marketId, filter }: OrderContainerProps) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
||||
@@ -44,6 +45,7 @@ export const OrdersContainer = ({ filter }: OrderContainerProps) => {
|
||||
return (
|
||||
<OrderListManager
|
||||
partyId={pubKey}
|
||||
marketId={marketId}
|
||||
filter={filter}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
|
||||
@@ -7,10 +7,14 @@ import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
|
||||
export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
export const PositionsContainer = ({
|
||||
onMarketClick,
|
||||
allKeys,
|
||||
}: {
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
allKeys?: boolean;
|
||||
}) => {
|
||||
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
|
||||
|
||||
const gridStore = usePositionsStore((store) => store.gridStore);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './withdrawals-container';
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
withdrawalProvider,
|
||||
WithdrawalsTable,
|
||||
useIncompleteWithdrawals,
|
||||
} from '@vegaprotocol/withdraws';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
|
||||
export const WithdrawalsContainer = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: withdrawalProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const { ready, delayed } = useIncompleteWithdrawals();
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
return (
|
||||
<WithdrawalsTable
|
||||
data-testid="withdrawals-history"
|
||||
rowData={data}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No withdrawals')}
|
||||
ready={ready}
|
||||
delayed={delayed}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './withdrawals-menu';
|
||||
@@ -1,18 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
|
||||
export const WithdrawalsMenu = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
|
||||
return (
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
size="extra-small"
|
||||
onClick={() => setView({ type: ViewType.Withdraw })}
|
||||
data-testid="withdraw-dialog-button"
|
||||
>
|
||||
{t('Make withdrawal')}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,9 @@
|
||||
import {
|
||||
RestConnector,
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
InjectedConnector,
|
||||
} from '@vegaprotocol/wallet';
|
||||
|
||||
export const rest = new RestConnector();
|
||||
export const jsonRpc = new JsonRpcConnector();
|
||||
export const injected = new InjectedConnector();
|
||||
|
||||
@@ -19,7 +17,6 @@ if (typeof window !== 'undefined') {
|
||||
|
||||
export const Connectors = {
|
||||
injected,
|
||||
rest,
|
||||
jsonRpc,
|
||||
view,
|
||||
};
|
||||
|
||||
@@ -43,6 +43,7 @@ import { Navbar } from '../components/navbar';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
ProtocolUpgradeCountdownMode,
|
||||
ProtocolUpgradeInProgressNotification,
|
||||
ProtocolUpgradeProposalNotification,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { ViewingBanner } from '../components/viewing-banner';
|
||||
@@ -111,6 +112,7 @@ function AppBody({ Component }: AppProps) {
|
||||
<ProtocolUpgradeProposalNotification
|
||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||
/>
|
||||
<ProtocolUpgradeInProgressNotification />
|
||||
<ViewingBanner />
|
||||
<UpgradeBanner showVersionChange={true} />
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,7 @@ const singleRow = {
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'BTCUSD Monthly (30 Jun 2022)',
|
||||
code: 'BTCUSD.MF21',
|
||||
},
|
||||
},
|
||||
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
|
||||
@@ -57,7 +58,7 @@ describe('BreakdownTable', () => {
|
||||
});
|
||||
const cells = await screen.findAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
'BTCUSD Monthly (30 Jun 2022)',
|
||||
'BTCUSD.MF21',
|
||||
'Margin',
|
||||
'1,256.00 (50%)',
|
||||
'1,256.00',
|
||||
@@ -118,6 +119,7 @@ describe('BreakdownTable', () => {
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'BTCUSD Monthly (30 Jun 2022)',
|
||||
code: 'BTCUSD.MF21',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -31,12 +31,12 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
const defs: ColDef[] = [
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'market.tradableInstrument.instrument.name',
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
}: VegaValueFormatterParams<
|
||||
AccountFields,
|
||||
'market.tradableInstrument.instrument.name'
|
||||
'market.tradableInstrument.instrument.code'
|
||||
>) => {
|
||||
if (!value) return 'None';
|
||||
return value;
|
||||
|
||||
@@ -121,9 +121,12 @@ export function createClient({
|
||||
// called on startup, NodeCheck and NodeCheckTimeUpdate are called
|
||||
// by the NodeSwitcher component and the useNodeHealth hook
|
||||
if (
|
||||
['NodeGuard', 'NodeCheck', 'NodeCheckTimeUpdate'].includes(
|
||||
operation.operationName
|
||||
)
|
||||
[
|
||||
'NodeGuard',
|
||||
'NodeCheck',
|
||||
'NodeCheckTimeUpdate',
|
||||
'BlockStatistics',
|
||||
].includes(operation.operationName)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ export const assetProvider = makeDataProvider<
|
||||
getData,
|
||||
});
|
||||
|
||||
export const useAssetDataProvider = (assetId: string) => {
|
||||
export const useAssetDataProvider = (assetId: string, skip?: boolean) => {
|
||||
return useDataProvider({
|
||||
dataProvider: assetProvider,
|
||||
variables: { assetId: assetId || '' },
|
||||
skip: !assetId,
|
||||
skip: !assetId || skip,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -10,14 +10,11 @@ declare global {
|
||||
|
||||
export const addConnectPublicKey = () => {
|
||||
Cypress.Commands.add('connectPublicKey', (publicKey) => {
|
||||
const connectVegaWaletBtn = Cypress.$(
|
||||
`[data-testid="connect-vega-wallet"]`
|
||||
);
|
||||
const connectVegaWaletBtn = Cypress.$(`[data-testid="view-as-user"]`);
|
||||
if (connectVegaWaletBtn.length > 0) {
|
||||
cy.get('aside [data-testid="connect-vega-wallet"]').click();
|
||||
cy.getByTestId('connector-view').should('be.visible').click();
|
||||
cy.getByTestId('address').click();
|
||||
cy.getByTestId('address').type(publicKey);
|
||||
cy.get('aside button').contains('View as party').click();
|
||||
cy.getByTestId('address').should('be.visible').focus();
|
||||
cy.getByTestId('address').type(publicKey, { delay: 50 });
|
||||
cy.getByTestId('connect').click();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ export const mockConnectWallet = () => {
|
||||
|
||||
export const mockConnectWalletWithUserError = () => {
|
||||
cy.mockWallet((req) => {
|
||||
aliasWalletConnectWithUserError(req);
|
||||
aliasWalletConnectWithUserError(req, Cypress.env('VEGA_WALLET_API_TOKEN'));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -62,10 +62,27 @@ export const aliasWalletConnectQuery = (
|
||||
},
|
||||
});
|
||||
}
|
||||
if (hasMethod(req, 'client.get_chain_id')) {
|
||||
req.reply({
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'Access-Control-Expose-Headers': 'Authorization',
|
||||
Authorization: `VWT ${token}`,
|
||||
},
|
||||
body: {
|
||||
jsonrpc: '2.0',
|
||||
result: {
|
||||
chainID: 'test-id',
|
||||
},
|
||||
id: '1',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const aliasWalletConnectWithUserError = (
|
||||
req: CyHttpMessages.IncomingHttpRequest
|
||||
req: CyHttpMessages.IncomingHttpRequest,
|
||||
token: string
|
||||
) => {
|
||||
if (hasMethod(req, 'client.connect_wallet')) {
|
||||
req.alias = 'client.connect_wallet';
|
||||
@@ -82,4 +99,20 @@ export const aliasWalletConnectWithUserError = (
|
||||
},
|
||||
});
|
||||
}
|
||||
if (hasMethod(req, 'client.get_chain_id')) {
|
||||
req.reply({
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'Access-Control-Expose-Headers': 'Authorization',
|
||||
Authorization: `VWT ${token}`,
|
||||
},
|
||||
body: {
|
||||
jsonrpc: '2.0',
|
||||
result: {
|
||||
chainID: 'test-id',
|
||||
},
|
||||
id: '1',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
import { Intent, Notification, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
interface ZeroBalanceErrorProps {
|
||||
@@ -6,11 +6,13 @@ interface ZeroBalanceErrorProps {
|
||||
id: string;
|
||||
symbol: string;
|
||||
};
|
||||
onClickCollateral?: () => void;
|
||||
onDeposit: (assetId: string) => void;
|
||||
}
|
||||
|
||||
export const ZeroBalanceError = ({
|
||||
asset,
|
||||
onClickCollateral,
|
||||
onDeposit,
|
||||
}: ZeroBalanceErrorProps) => {
|
||||
return (
|
||||
@@ -23,6 +25,12 @@ export const ZeroBalanceError = ({
|
||||
'You need %s in your wallet to trade in this market. ',
|
||||
asset.symbol
|
||||
)}
|
||||
{onClickCollateral && (
|
||||
<>
|
||||
{t('See all your')}{' '}
|
||||
<Link onClick={onClickCollateral}>collateral</Link>.
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
buttonProps={{
|
||||
|
||||
@@ -17,6 +17,7 @@ import { FLAGS } from '@vegaprotocol/environment';
|
||||
interface DealTicketContainerProps {
|
||||
marketId: string;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
onClickCollateral?: () => void;
|
||||
onDeposit: (assetId: string) => void;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ export interface DealTicketProps {
|
||||
marketPrice?: string | null;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
submit: (order: OrderSubmission) => void;
|
||||
onClickCollateral?: () => void;
|
||||
onDeposit: (assetId: string) => void;
|
||||
}
|
||||
|
||||
@@ -120,6 +121,7 @@ export const DealTicket = ({
|
||||
marketData,
|
||||
marketPrice,
|
||||
submit,
|
||||
onClickCollateral,
|
||||
onDeposit,
|
||||
}: DealTicketProps) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
@@ -528,6 +530,7 @@ export const DealTicket = ({
|
||||
}
|
||||
isReadOnly={isReadOnly}
|
||||
pubKey={pubKey}
|
||||
onClickCollateral={onClickCollateral}
|
||||
onDeposit={onDeposit}
|
||||
/>
|
||||
<DealTicketButton side={side} />
|
||||
@@ -563,6 +566,7 @@ interface SummaryMessageProps {
|
||||
margin: string;
|
||||
isReadOnly: boolean;
|
||||
pubKey: string | null;
|
||||
onClickCollateral?: () => void;
|
||||
onDeposit: (assetId: string) => void;
|
||||
}
|
||||
|
||||
@@ -592,6 +596,7 @@ const SummaryMessage = memo(
|
||||
margin,
|
||||
isReadOnly,
|
||||
pubKey,
|
||||
onClickCollateral,
|
||||
onDeposit,
|
||||
}: SummaryMessageProps) => {
|
||||
// Specific error UI for if balance is so we can
|
||||
@@ -603,7 +608,11 @@ const SummaryMessage = memo(
|
||||
if (error?.type === SummaryValidationType.NoCollateral) {
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<ZeroBalanceError asset={asset} onDeposit={onDeposit} />
|
||||
<ZeroBalanceError
|
||||
asset={asset}
|
||||
onClickCollateral={onClickCollateral}
|
||||
onDeposit={onDeposit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -371,6 +371,14 @@ function compileEnvVars() {
|
||||
'NX_TENDERMINT_WEBSOCKET_URL',
|
||||
process.env['NX_TENDERMINT_WEBSOCKET_URL']
|
||||
),
|
||||
CHROME_EXTENSION_URL: windowOrDefault(
|
||||
'NX_CHROME_EXTENSION_URL',
|
||||
process.env['NX_CHROME_EXTENSION_URL']
|
||||
),
|
||||
MOZILLA_EXTENSION_URL: windowOrDefault(
|
||||
'NX_MOZILLA_EXTENSION_URL',
|
||||
process.env['NX_MOZILLA_EXTENSION_URL']
|
||||
),
|
||||
};
|
||||
|
||||
return env;
|
||||
|
||||
@@ -29,6 +29,7 @@ const EmptyLinks: DAppLinks = {
|
||||
|
||||
const ExplorerLinks = {
|
||||
...EmptyLinks,
|
||||
[Networks.STAGNET1]: 'https://explorer.stagnet1.vega.rocks',
|
||||
[Networks.TESTNET]: 'https://explorer.fairground.wtf',
|
||||
[Networks.VALIDATOR_TESTNET]:
|
||||
'https://explorer.validators-testnet.vega.rocks',
|
||||
@@ -40,17 +41,18 @@ const ConsoleLinks = {
|
||||
...EmptyLinks,
|
||||
[Networks.STAGNET1]: 'https://trading.stagnet1.vega.rocks',
|
||||
[Networks.TESTNET]: 'https://console.fairground.wtf',
|
||||
[Networks.MAINNET]: 'https://console.vega.xyz',
|
||||
[Networks.MAINNET_MIRROR]: 'https://console.mainnet-mirror.vega.rocks/',
|
||||
[Networks.MAINNET]: 'https://vega.trading',
|
||||
[Networks.MAINNET_MIRROR]: 'https://console.mainnet-mirror.vega.rocks',
|
||||
};
|
||||
|
||||
const TokenLinks = {
|
||||
...EmptyLinks,
|
||||
[Networks.DEVNET]: 'https://dev.governance.vega.xyz',
|
||||
[Networks.STAGNET1]: 'https://governance.stagnet1.vega.rocks',
|
||||
[Networks.TESTNET]: 'https://governance.fairground.wtf',
|
||||
[Networks.VALIDATOR_TESTNET]:
|
||||
'https://governance.validators-testnet.vega.rocks',
|
||||
[Networks.MAINNET_MIRROR]: 'https://governance.mainnet-mirror.vega.rocks/',
|
||||
[Networks.MAINNET_MIRROR]: 'https://governance.mainnet-mirror.vega.rocks',
|
||||
[Networks.MAINNET]: 'https://governance.vega.xyz',
|
||||
};
|
||||
|
||||
@@ -160,7 +162,9 @@ export const ExternalLinks = {
|
||||
MARGIN_CREDIT_RISK:
|
||||
'https://vega.xyz/papers/margins-and-credit-risk.pdf#page=7',
|
||||
VEGA_WALLET_URL: 'https://vega.xyz/wallet',
|
||||
VEGA_WALLET_URL_ABOUT: 'https://vega.xyz/wallet/#overview',
|
||||
VEGA_WALLET_HOSTED_URL: 'https://vega-hosted-wallet.on.fleek.co/',
|
||||
VEGA_WALLET_BROWSER_LIST: '',
|
||||
BLOG: 'https://blog.vega.xyz/',
|
||||
};
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ export const envSchema = z
|
||||
SENTRY_DSN: z.optional(z.string()),
|
||||
TENDERMINT_URL: z.optional(z.string()),
|
||||
TENDERMINT_WEBSOCKET_URL: z.optional(z.string()),
|
||||
CHROME_EXTENSION_URL: z.optional(z.string()),
|
||||
MOZILLA_EXTENSION_URL: z.optional(z.string()),
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('FillsTable', () => {
|
||||
positionDecimalPlaces: 5,
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'test market',
|
||||
code: 'test market',
|
||||
product: {
|
||||
settlementAsset: {
|
||||
decimals: 2,
|
||||
@@ -71,7 +71,7 @@ describe('FillsTable', () => {
|
||||
render(<FillsTable partyId={partyId} rowData={[{ ...buyerFill }]} />);
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market?.tradableInstrument.instrument.name || '',
|
||||
buyerFill.market?.tradableInstrument.instrument.code || '',
|
||||
'+3.00',
|
||||
'1.00 BTC',
|
||||
'3.00 BTC',
|
||||
@@ -106,7 +106,7 @@ describe('FillsTable', () => {
|
||||
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market?.tradableInstrument.instrument.name || '',
|
||||
buyerFill.market?.tradableInstrument.instrument.code || '',
|
||||
'-3.00',
|
||||
'1.00 BTC',
|
||||
'3.00 BTC',
|
||||
@@ -141,7 +141,7 @@ describe('FillsTable', () => {
|
||||
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market?.tradableInstrument.instrument.name || '',
|
||||
buyerFill.market?.tradableInstrument.instrument.code || '',
|
||||
'-3.00',
|
||||
'1.00 BTC',
|
||||
'3.00 BTC',
|
||||
|
||||
@@ -47,7 +47,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
|
||||
() => [
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'market.tradableInstrument.instrument.name',
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
cellRenderer: 'MarketNameCell',
|
||||
cellRendererParams: { idPath: 'market.id', onMarketClick },
|
||||
},
|
||||
|
||||
@@ -4,8 +4,10 @@ import {
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
Intent,
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
Lozenge,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import BigNumber from 'bignumber.js';
|
||||
@@ -22,9 +24,11 @@ interface RowProps {
|
||||
unformatted?: boolean;
|
||||
assetSymbol?: string;
|
||||
noBorder?: boolean;
|
||||
parentValue?: ReactNode;
|
||||
hasParentData?: boolean;
|
||||
}
|
||||
|
||||
const Row = ({
|
||||
export const Row = ({
|
||||
field,
|
||||
value,
|
||||
decimalPlaces,
|
||||
@@ -32,7 +36,14 @@ const Row = ({
|
||||
unformatted,
|
||||
assetSymbol = '',
|
||||
noBorder = true,
|
||||
parentValue,
|
||||
hasParentData,
|
||||
}: RowProps) => {
|
||||
// Note: we need both 'parentValue' and 'hasParentData' to do a conditional
|
||||
// check to differentiate between when parentData itself is missing and when
|
||||
// a specific parentValue is missing. These values are only used when we
|
||||
// have successor market parent data.
|
||||
|
||||
const className = 'text-sm';
|
||||
|
||||
const getFormattedValue = (value: ReactNode) => {
|
||||
@@ -55,6 +66,10 @@ const Row = ({
|
||||
const formattedValue = getFormattedValue(value);
|
||||
|
||||
if (!formattedValue) return null;
|
||||
|
||||
const newValueInSuccessorMarket = hasParentData && value && !parentValue;
|
||||
const valueDiffersFromParentMarket = parentValue && parentValue !== value;
|
||||
|
||||
return (
|
||||
<KeyValueTableRow
|
||||
key={field}
|
||||
@@ -63,10 +78,35 @@ const Row = ({
|
||||
dtClassName={className}
|
||||
ddClassName={className}
|
||||
>
|
||||
<Tooltip description={tooltipMapping[field]} align="start">
|
||||
<div tabIndex={-1}>{startCase(t(field))}</div>
|
||||
</Tooltip>
|
||||
<span style={{ wordBreak: 'break-word' }}>{formattedValue}</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<Tooltip description={tooltipMapping[field]} align="start">
|
||||
<div tabIndex={-1}>{startCase(t(field))}</div>
|
||||
</Tooltip>
|
||||
|
||||
{valueDiffersFromParentMarket && (
|
||||
<Lozenge className="py-0" variant={Intent.Primary}>
|
||||
{t('Updated')}
|
||||
</Lozenge>
|
||||
)}
|
||||
|
||||
{newValueInSuccessorMarket && (
|
||||
<Lozenge className="py-0" variant={Intent.Primary}>
|
||||
{t('Added')}
|
||||
</Lozenge>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ wordBreak: 'break-word' }}>
|
||||
{valueDiffersFromParentMarket ? (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="line-through">
|
||||
{getFormattedValue(parentValue)}
|
||||
</span>
|
||||
<span>{formattedValue}</span>
|
||||
</div>
|
||||
) : (
|
||||
formattedValue
|
||||
)}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
);
|
||||
};
|
||||
@@ -79,6 +119,7 @@ export interface MarketInfoTableProps {
|
||||
children?: ReactNode;
|
||||
assetSymbol?: string;
|
||||
noBorder?: boolean;
|
||||
parentData?: Record<string, ReactNode> | null | undefined;
|
||||
}
|
||||
|
||||
export const MarketInfoTable = ({
|
||||
@@ -89,25 +130,33 @@ export const MarketInfoTable = ({
|
||||
children,
|
||||
assetSymbol,
|
||||
noBorder,
|
||||
parentData,
|
||||
}: MarketInfoTableProps) => {
|
||||
if (!data || typeof data !== 'object') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasParentData = parentData !== undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
<KeyValueTable>
|
||||
{Object.entries(data).map(([key, value]) => (
|
||||
<Row
|
||||
key={key}
|
||||
field={key}
|
||||
value={value}
|
||||
decimalPlaces={decimalPlaces}
|
||||
assetSymbol={assetSymbol}
|
||||
asPercentage={asPercentage}
|
||||
unformatted={unformatted}
|
||||
noBorder={noBorder}
|
||||
/>
|
||||
))}
|
||||
<>
|
||||
{Object.entries(data).map(([key, value]) => (
|
||||
<Row
|
||||
key={key}
|
||||
field={key}
|
||||
value={value}
|
||||
decimalPlaces={decimalPlaces}
|
||||
assetSymbol={assetSymbol}
|
||||
asPercentage={asPercentage}
|
||||
unformatted={unformatted}
|
||||
noBorder={noBorder}
|
||||
parentValue={parentData?.[key]}
|
||||
hasParentData={hasParentData}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
</KeyValueTable>
|
||||
<div className="flex flex-col gap-2">{children}</div>
|
||||
</>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import type { ReactNode } from 'react';
|
||||
import { Fragment, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
@@ -47,11 +48,14 @@ import {
|
||||
useSuccessorMarketQuery,
|
||||
} from '../../__generated__';
|
||||
import { useSuccessorMarketProposalDetailsQuery } from '@vegaprotocol/proposals';
|
||||
import type { MarketTradingMode } from '@vegaprotocol/types';
|
||||
import type { Signer } from '@vegaprotocol/types';
|
||||
import classNames from 'classnames';
|
||||
import compact from 'lodash/compact';
|
||||
|
||||
type MarketInfoProps = {
|
||||
market: MarketInfo;
|
||||
parentMarket?: MarketInfo;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
@@ -156,21 +160,43 @@ export const InsurancePoolInfoPanel = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const KeyDetailsInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
const { data: parentData } = useParentMarketIdQuery({
|
||||
export const KeyDetailsInfoPanel = ({
|
||||
market,
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => {
|
||||
const { data: parentMarketIdData } = useParentMarketIdQuery({
|
||||
variables: {
|
||||
marketId: market.id,
|
||||
},
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS,
|
||||
});
|
||||
|
||||
const { data: successor } = useSuccessorMarketProposalDetailsQuery({
|
||||
const { data: successorProposalDetails } =
|
||||
useSuccessorMarketProposalDetailsQuery({
|
||||
variables: {
|
||||
proposalId: market.proposal?.id || '',
|
||||
},
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS || !market.proposal?.id,
|
||||
});
|
||||
|
||||
// The following queries are needed as the parent market could also have been a successor market.
|
||||
// Note: the parent market is only passed to this component if the successor markets flag is enabled,
|
||||
// so that check is not needed in the skip.
|
||||
const { data: grandparentMarketIdData } = useParentMarketIdQuery({
|
||||
variables: {
|
||||
proposalId: market.proposal?.id || '',
|
||||
marketId: parentMarket?.id || '',
|
||||
},
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS || !market.proposal?.id,
|
||||
skip: !parentMarket?.id,
|
||||
});
|
||||
|
||||
const { data: parentSuccessorProposalDetails } =
|
||||
useSuccessorMarketProposalDetailsQuery({
|
||||
variables: {
|
||||
proposalId: parentMarket?.proposal?.id || '',
|
||||
},
|
||||
skip: !parentMarket?.proposal?.id,
|
||||
});
|
||||
|
||||
const assetDecimals =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.decimals;
|
||||
|
||||
@@ -181,11 +207,12 @@ export const KeyDetailsInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
? {
|
||||
name: market.tradableInstrument.instrument.name,
|
||||
marketID: market.id,
|
||||
parentMarketID: parentData?.market?.parentMarketID || '-',
|
||||
parentMarketID: parentMarketIdData?.market?.parentMarketID || '-',
|
||||
insurancePoolFraction:
|
||||
(successor?.proposal?.terms.change.__typename === 'NewMarket' &&
|
||||
successor.proposal.terms.change.successorConfiguration
|
||||
?.insurancePoolFraction) ||
|
||||
(successorProposalDetails?.proposal?.terms.change.__typename ===
|
||||
'NewMarket' &&
|
||||
successorProposalDetails.proposal.terms.change
|
||||
.successorConfiguration?.insurancePoolFraction) ||
|
||||
'-',
|
||||
tradingMode:
|
||||
market.tradingMode &&
|
||||
@@ -205,6 +232,28 @@ export const KeyDetailsInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
settlementAssetDecimalPlaces: assetDecimals,
|
||||
}
|
||||
}
|
||||
parentData={
|
||||
parentMarket && {
|
||||
name: parentMarket?.tradableInstrument?.instrument?.name,
|
||||
marketID: parentMarket?.id,
|
||||
parentMarketID: grandparentMarketIdData?.market?.parentMarketID,
|
||||
insurancePoolFraction:
|
||||
parentSuccessorProposalDetails?.proposal?.terms.change
|
||||
.__typename === 'NewMarket' &&
|
||||
parentSuccessorProposalDetails.proposal.terms.change
|
||||
.successorConfiguration?.insurancePoolFraction,
|
||||
tradingMode:
|
||||
parentMarket?.tradingMode &&
|
||||
MarketTradingModeMapping[
|
||||
parentMarket.tradingMode as MarketTradingMode
|
||||
],
|
||||
marketDecimalPlaces: parentMarket?.decimalPlaces,
|
||||
positionDecimalPlaces: parentMarket?.positionDecimalPlaces,
|
||||
settlementAssetDecimalPlaces:
|
||||
parentMarket?.tradableInstrument?.instrument?.product
|
||||
?.settlementAsset?.decimals,
|
||||
}
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -329,7 +378,10 @@ export const SuccessionLineInfoPanel = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const InstrumentInfoPanel = ({ market }: MarketInfoProps) => (
|
||||
export const InstrumentInfoPanel = ({
|
||||
market,
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
marketName: market.tradableInstrument.instrument.name,
|
||||
@@ -337,6 +389,16 @@ export const InstrumentInfoPanel = ({ market }: MarketInfoProps) => (
|
||||
productType: market.tradableInstrument.instrument.product.__typename,
|
||||
quoteName: market.tradableInstrument.instrument.product.quoteName,
|
||||
}}
|
||||
parentData={
|
||||
parentMarket && {
|
||||
marketName: parentMarket?.tradableInstrument?.instrument?.name,
|
||||
code: parentMarket?.tradableInstrument?.instrument?.code,
|
||||
productType:
|
||||
parentMarket?.tradableInstrument?.instrument?.product?.__typename,
|
||||
quoteName:
|
||||
parentMarket?.tradableInstrument?.instrument?.product?.quoteName,
|
||||
}
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -349,6 +411,7 @@ export const SettlementAssetInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
() => market?.tradableInstrument.instrument.product?.settlementAsset.id,
|
||||
[market]
|
||||
);
|
||||
|
||||
const { data: asset } = useAssetDataProvider(assetId ?? '');
|
||||
return asset ? (
|
||||
<>
|
||||
@@ -371,54 +434,148 @@ export const SettlementAssetInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const MetadataInfoPanel = ({ market }: MarketInfoProps) => (
|
||||
const getMarketMetadata = (market: MarketInfo) =>
|
||||
market.tradableInstrument.instrument.metadata.tags
|
||||
?.map((tag) => {
|
||||
const [key, value] = tag.split(':');
|
||||
return { [key]: value };
|
||||
})
|
||||
.reduce((acc, curr) => ({ ...acc, ...curr }), {});
|
||||
|
||||
export const MetadataInfoPanel = ({
|
||||
market,
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
expiryDate: getMarketExpiryDateFormatted(
|
||||
market.tradableInstrument.instrument.metadata.tags
|
||||
),
|
||||
...market.tradableInstrument.instrument.metadata.tags
|
||||
?.map((tag) => {
|
||||
const [key, value] = tag.split(':');
|
||||
return { [key]: value };
|
||||
})
|
||||
.reduce((acc, curr) => ({ ...acc, ...curr }), {}),
|
||||
...(getMarketMetadata(market) || {}),
|
||||
}}
|
||||
parentData={
|
||||
parentMarket && {
|
||||
expiryDate: getMarketExpiryDateFormatted(
|
||||
parentMarket.tradableInstrument.instrument.metadata.tags
|
||||
),
|
||||
...(getMarketMetadata(parentMarket) || {}),
|
||||
}
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
export const RiskModelInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
export const RiskModelInfoPanel = ({
|
||||
market,
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => {
|
||||
if (market.tradableInstrument.riskModel.__typename !== 'LogNormalRiskModel') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { tau, riskAversionParameter } = market.tradableInstrument.riskModel;
|
||||
return <MarketInfoTable data={{ tau, riskAversionParameter }} unformatted />;
|
||||
|
||||
let parentData;
|
||||
|
||||
if (
|
||||
parentMarket?.tradableInstrument?.riskModel?.__typename ===
|
||||
'LogNormalRiskModel'
|
||||
) {
|
||||
const {
|
||||
tau: parentTau,
|
||||
riskAversionParameter: parentRiskAversionParameter,
|
||||
} = market.tradableInstrument.riskModel;
|
||||
|
||||
parentData = {
|
||||
tau: parentTau,
|
||||
riskAversionParameter: parentRiskAversionParameter,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<MarketInfoTable
|
||||
data={{ tau, riskAversionParameter }}
|
||||
parentData={parentData}
|
||||
unformatted
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const RiskParametersInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
if (market.tradableInstrument.riskModel.__typename === 'LogNormalRiskModel') {
|
||||
export const RiskParametersInfoPanel = ({
|
||||
market,
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => {
|
||||
const marketType = market.tradableInstrument.riskModel.__typename;
|
||||
|
||||
let data, parentData;
|
||||
|
||||
if (marketType === 'LogNormalRiskModel') {
|
||||
const { r, sigma, mu } = market.tradableInstrument.riskModel.params;
|
||||
return <MarketInfoTable data={{ r, sigma, mu }} unformatted />;
|
||||
}
|
||||
if (market.tradableInstrument.riskModel.__typename === 'SimpleRiskModel') {
|
||||
data = { r, sigma, mu };
|
||||
|
||||
if (
|
||||
parentMarket?.tradableInstrument?.riskModel.__typename ===
|
||||
'LogNormalRiskModel'
|
||||
) {
|
||||
const parentParams = parentMarket.tradableInstrument.riskModel.params;
|
||||
parentData = {
|
||||
r: parentParams.r,
|
||||
sigma: parentParams.sigma,
|
||||
mu: parentParams.mu,
|
||||
};
|
||||
}
|
||||
} else if (marketType === 'SimpleRiskModel') {
|
||||
const { factorLong, factorShort } =
|
||||
market.tradableInstrument.riskModel.params;
|
||||
return <MarketInfoTable data={{ factorLong, factorShort }} unformatted />;
|
||||
data = { factorLong, factorShort };
|
||||
|
||||
if (
|
||||
parentMarket?.tradableInstrument?.riskModel.__typename ===
|
||||
'SimpleRiskModel'
|
||||
) {
|
||||
const parentParams = parentMarket.tradableInstrument.riskModel.params;
|
||||
parentData = {
|
||||
factorLong: parentParams.factorLong,
|
||||
factorShort: parentParams.factorShort,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
return <MarketInfoTable data={data} parentData={parentData} unformatted />;
|
||||
};
|
||||
|
||||
export const RiskFactorsInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
export const RiskFactorsInfoPanel = ({
|
||||
market,
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => {
|
||||
if (!market.riskFactors) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { short, long } = market.riskFactors;
|
||||
return <MarketInfoTable data={{ short, long }} unformatted />;
|
||||
|
||||
let parentData;
|
||||
|
||||
if (parentMarket?.riskFactors) {
|
||||
const parentShort = parentMarket.riskFactors.short;
|
||||
const parentLong = parentMarket.riskFactors.long;
|
||||
parentData = { short: parentShort, long: parentLong };
|
||||
}
|
||||
|
||||
return (
|
||||
<MarketInfoTable
|
||||
data={{ short, long }}
|
||||
parentData={parentData}
|
||||
unformatted
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const PriceMonitoringBoundsInfoPanel = ({
|
||||
market,
|
||||
triggerIndex,
|
||||
parentMarket,
|
||||
}: MarketInfoProps & {
|
||||
triggerIndex: number;
|
||||
}) => {
|
||||
@@ -426,11 +583,35 @@ export const PriceMonitoringBoundsInfoPanel = ({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId: market.id },
|
||||
});
|
||||
|
||||
const { data: parentData } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId: parentMarket?.id || '' },
|
||||
skip:
|
||||
!parentMarket ||
|
||||
!parentMarket?.priceMonitoringSettings?.parameters?.triggers?.[
|
||||
triggerIndex
|
||||
],
|
||||
});
|
||||
|
||||
const quoteUnit =
|
||||
market?.tradableInstrument.instrument.product?.quoteName || '';
|
||||
const parentQuoteUnit =
|
||||
parentMarket?.tradableInstrument.instrument.product?.quoteName || '';
|
||||
const isParentQuoteUnitEqual = quoteUnit === parentQuoteUnit;
|
||||
|
||||
const trigger =
|
||||
market.priceMonitoringSettings?.parameters?.triggers?.[triggerIndex];
|
||||
const parentTrigger =
|
||||
parentMarket?.priceMonitoringSettings?.parameters?.triggers?.[triggerIndex];
|
||||
const isParentTriggerEqual = isEqual(trigger, parentTrigger);
|
||||
|
||||
const bounds = data?.priceMonitoringBounds?.[triggerIndex];
|
||||
const parentBounds = parentData?.priceMonitoringBounds?.[triggerIndex];
|
||||
|
||||
const shouldShowParentData =
|
||||
isParentQuoteUnitEqual && isParentTriggerEqual && !!parentBounds;
|
||||
|
||||
if (!trigger) {
|
||||
console.error(
|
||||
`Could not find data for trigger ${triggerIndex} (market id: ${market.id})`
|
||||
@@ -457,6 +638,14 @@ export const PriceMonitoringBoundsInfoPanel = ({
|
||||
highestPrice: bounds.maxValidPrice,
|
||||
lowestPrice: bounds.minValidPrice,
|
||||
}}
|
||||
parentData={
|
||||
shouldShowParentData
|
||||
? {
|
||||
highestPrice: parentBounds.maxValidPrice,
|
||||
lowestPrice: parentBounds.minValidPrice,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
decimalPlaces={market.decimalPlaces}
|
||||
assetSymbol={quoteUnit}
|
||||
/>
|
||||
@@ -472,18 +661,31 @@ export const PriceMonitoringBoundsInfoPanel = ({
|
||||
|
||||
export const LiquidityMonitoringParametersInfoPanel = ({
|
||||
market,
|
||||
}: MarketInfoProps) => (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
triggeringRatio: market.liquidityMonitoringParameters.triggeringRatio,
|
||||
timeWindow:
|
||||
market.liquidityMonitoringParameters.targetStakeParameters.timeWindow,
|
||||
scalingFactor:
|
||||
market.liquidityMonitoringParameters.targetStakeParameters
|
||||
.scalingFactor,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => {
|
||||
const marketData = {
|
||||
triggeringRatio: market.liquidityMonitoringParameters.triggeringRatio,
|
||||
timeWindow:
|
||||
market.liquidityMonitoringParameters.targetStakeParameters.timeWindow,
|
||||
scalingFactor:
|
||||
market.liquidityMonitoringParameters.targetStakeParameters.scalingFactor,
|
||||
};
|
||||
|
||||
const parentMarketData = parentMarket
|
||||
? {
|
||||
triggeringRatio:
|
||||
parentMarket.liquidityMonitoringParameters.triggeringRatio,
|
||||
timeWindow:
|
||||
parentMarket.liquidityMonitoringParameters.targetStakeParameters
|
||||
.timeWindow,
|
||||
scalingFactor:
|
||||
parentMarket.liquidityMonitoringParameters.targetStakeParameters
|
||||
.scalingFactor,
|
||||
}
|
||||
: {};
|
||||
|
||||
return <MarketInfoTable data={marketData} parentData={parentMarketData} />;
|
||||
};
|
||||
|
||||
export const LiquidityInfoPanel = ({ market, children }: MarketInfoProps) => {
|
||||
const assetDecimals =
|
||||
@@ -510,16 +712,61 @@ export const LiquidityInfoPanel = ({ market, children }: MarketInfoProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const LiquidityPriceRangeInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
export const LiquidityPriceRangeInfoPanel = ({
|
||||
market,
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => {
|
||||
const quoteUnit =
|
||||
market?.tradableInstrument.instrument.product?.quoteName || '';
|
||||
const parentQuoteUnit =
|
||||
parentMarket?.tradableInstrument.instrument.product?.quoteName || '';
|
||||
|
||||
const liquidityPriceRange = formatNumberPercentage(
|
||||
new BigNumber(market.lpPriceRange).times(100)
|
||||
);
|
||||
const parentLiquidityPriceRange = parentMarket
|
||||
? formatNumberPercentage(
|
||||
new BigNumber(parentMarket.lpPriceRange).times(100)
|
||||
)
|
||||
: null;
|
||||
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId: market.id },
|
||||
});
|
||||
|
||||
const { data: parentMarketData } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId: parentMarket?.id || '' },
|
||||
skip: !parentMarket,
|
||||
});
|
||||
|
||||
let parentData;
|
||||
|
||||
if (parentMarket && parentMarketData && quoteUnit === parentQuoteUnit) {
|
||||
parentData = {
|
||||
liquidityPriceRange: `${parentLiquidityPriceRange} of mid price`,
|
||||
lowestPrice:
|
||||
parentMarketData?.midPrice &&
|
||||
`${addDecimalsFormatNumber(
|
||||
new BigNumber(1)
|
||||
.minus(parentMarket.lpPriceRange)
|
||||
.times(parentMarketData.midPrice)
|
||||
.toString(),
|
||||
parentMarket.decimalPlaces
|
||||
)} ${quoteUnit}`,
|
||||
highestPrice:
|
||||
parentMarketData?.midPrice &&
|
||||
`${addDecimalsFormatNumber(
|
||||
new BigNumber(1)
|
||||
.plus(parentMarket.lpPriceRange)
|
||||
.times(parentMarketData.midPrice)
|
||||
.toString(),
|
||||
parentMarket.decimalPlaces
|
||||
)} ${quoteUnit}`,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="text-sm mb-2">
|
||||
@@ -552,6 +799,7 @@ export const LiquidityPriceRangeInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
market.decimalPlaces
|
||||
)} ${quoteUnit}`,
|
||||
}}
|
||||
parentData={parentData}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
@@ -560,8 +808,12 @@ export const LiquidityPriceRangeInfoPanel = ({ market }: MarketInfoProps) => {
|
||||
export const OracleInfoPanel = ({
|
||||
market,
|
||||
type,
|
||||
parentMarket,
|
||||
}: MarketInfoProps & { type: 'settlementData' | 'termination' }) => {
|
||||
// If this is a successor market, this component will only receive parent market
|
||||
// data if the termination or settlement data is different from the parent.
|
||||
const product = market.tradableInstrument.instrument.product;
|
||||
const parentProduct = parentMarket?.tradableInstrument?.instrument?.product;
|
||||
const { VEGA_EXPLORER_URL, ORACLE_PROOFS_URL } = useEnvironment();
|
||||
const { data } = useOracleProofs(ORACLE_PROOFS_URL);
|
||||
|
||||
@@ -570,12 +822,33 @@ export const OracleInfoPanel = ({
|
||||
? product.dataSourceSpecForSettlementData.id
|
||||
: product.dataSourceSpecForTradingTermination.id;
|
||||
|
||||
const parentDataSourceSpecId =
|
||||
type === 'settlementData'
|
||||
? parentProduct?.dataSourceSpecForSettlementData?.id
|
||||
: parentProduct?.dataSourceSpecForTradingTermination?.id;
|
||||
|
||||
const dataSourceSpec = (
|
||||
type === 'settlementData'
|
||||
? product.dataSourceSpecForSettlementData.data
|
||||
: product.dataSourceSpecForTradingTermination.data
|
||||
) as DataSourceDefinition;
|
||||
|
||||
const parentDataSourceSpec =
|
||||
type === 'settlementData'
|
||||
? parentProduct?.dataSourceSpecForSettlementData?.data
|
||||
: (parentProduct?.dataSourceSpecForTradingTermination
|
||||
?.data as DataSourceDefinition);
|
||||
|
||||
const isParentDataSourceSpecEqual =
|
||||
parentDataSourceSpec !== undefined &&
|
||||
dataSourceSpec === parentDataSourceSpec;
|
||||
const isParentDataSourceSpecIdEqual =
|
||||
parentDataSourceSpecId !== undefined &&
|
||||
dataSourceSpecId === parentDataSourceSpecId;
|
||||
|
||||
// We'll only provide successor parent data (if it differs) to the
|
||||
// DataSourceProof component. Having an old external link struck through
|
||||
// is unlikely to be useful.
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<DataSourceProof
|
||||
@@ -584,7 +857,14 @@ export const OracleInfoPanel = ({
|
||||
providers={data}
|
||||
type={type}
|
||||
dataSourceSpecId={dataSourceSpecId}
|
||||
parentData={
|
||||
isParentDataSourceSpecEqual ? undefined : parentDataSourceSpec
|
||||
}
|
||||
parentDataSourceSpecId={
|
||||
isParentDataSourceSpecIdEqual ? undefined : parentDataSourceSpecId
|
||||
}
|
||||
/>
|
||||
|
||||
<ExternalLink
|
||||
data-testid="oracle-spec-links"
|
||||
href={`${VEGA_EXPLORER_URL}/oracles/${
|
||||
@@ -606,14 +886,28 @@ export const DataSourceProof = ({
|
||||
providers,
|
||||
type,
|
||||
dataSourceSpecId,
|
||||
parentData,
|
||||
parentDataSourceSpecId,
|
||||
}: {
|
||||
data: DataSourceDefinition;
|
||||
providers: Provider[] | undefined;
|
||||
type: 'settlementData' | 'termination';
|
||||
dataSourceSpecId: string;
|
||||
parentData?: DataSourceDefinition;
|
||||
parentDataSourceSpecId?: string;
|
||||
}) => {
|
||||
// If this is a successor market, we'll only pass parent data to child
|
||||
// components for comparison if the data differs from the parent market.
|
||||
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
|
||||
const signers = data.sourceType.sourceType.signers || [];
|
||||
let parentSigners: Signer[];
|
||||
|
||||
if (
|
||||
parentData &&
|
||||
parentData.sourceType.__typename === 'DataSourceDefinitionExternal'
|
||||
) {
|
||||
parentSigners = parentData.sourceType.sourceType?.signers || [];
|
||||
}
|
||||
|
||||
if (!providers?.length) {
|
||||
return <NoOracleProof type={type} />;
|
||||
@@ -622,7 +916,14 @@ export const DataSourceProof = ({
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
{signers.map(({ signer }, i) => {
|
||||
return (
|
||||
const parentSigner = parentSigners?.find(
|
||||
({ signer: ParentSigner }) =>
|
||||
ParentSigner.__typename === signer.__typename
|
||||
)?.signer;
|
||||
|
||||
const isParentSignerEqual = isEqual(signer, parentSigner);
|
||||
|
||||
return isParentSignerEqual ? (
|
||||
<OracleLink
|
||||
key={i}
|
||||
providers={providers}
|
||||
@@ -630,6 +931,16 @@ export const DataSourceProof = ({
|
||||
type={type}
|
||||
dataSourceSpecId={dataSourceSpecId}
|
||||
/>
|
||||
) : (
|
||||
<OracleLink
|
||||
key={i}
|
||||
providers={providers}
|
||||
signer={signer}
|
||||
type={type}
|
||||
dataSourceSpecId={dataSourceSpecId}
|
||||
parentSigner={parentSigner}
|
||||
parentDataSourceSpecId={parentDataSourceSpecId}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
@@ -663,18 +974,8 @@ export const DataSourceProof = ({
|
||||
return <div>{t('Invalid data source')}</div>;
|
||||
};
|
||||
|
||||
const OracleLink = ({
|
||||
providers,
|
||||
signer,
|
||||
type,
|
||||
dataSourceSpecId,
|
||||
}: {
|
||||
providers: Provider[];
|
||||
signer: SignerKind;
|
||||
type: 'settlementData' | 'termination';
|
||||
dataSourceSpecId: string;
|
||||
}) => {
|
||||
const signerProviders = providers.filter((p) => {
|
||||
const getSignerProviders = (signer: SignerKind, providers: Provider[]) =>
|
||||
providers.filter((p) => {
|
||||
if (signer.__typename === 'PubKey') {
|
||||
if (
|
||||
p.oracle.type === 'public_key' &&
|
||||
@@ -696,19 +997,62 @@ const OracleLink = ({
|
||||
return false;
|
||||
});
|
||||
|
||||
const OracleLink = ({
|
||||
providers,
|
||||
signer,
|
||||
type,
|
||||
dataSourceSpecId,
|
||||
parentSigner,
|
||||
parentDataSourceSpecId,
|
||||
}: {
|
||||
providers: Provider[];
|
||||
signer: SignerKind;
|
||||
type: 'settlementData' | 'termination';
|
||||
dataSourceSpecId: string;
|
||||
parentSigner?: SignerKind;
|
||||
parentDataSourceSpecId?: string;
|
||||
}) => {
|
||||
// If this is a successor market, the parent market data will only have been passed
|
||||
// in if it differs from the current data.
|
||||
const signerProviders = getSignerProviders(signer, providers);
|
||||
const parentSignerProviders = parentSigner
|
||||
? getSignerProviders(parentSigner, providers)
|
||||
: [];
|
||||
|
||||
if (!signerProviders.length) {
|
||||
return <NoOracleProof type={type} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-2">
|
||||
{signerProviders.map((provider) => (
|
||||
<OracleProfile
|
||||
key={dataSourceSpecId}
|
||||
provider={provider}
|
||||
dataSourceSpecId={dataSourceSpecId}
|
||||
/>
|
||||
))}
|
||||
{signerProviders.map((provider) => {
|
||||
// Making the assumption here that if the provider name is the same,
|
||||
// that it is the same provider that the parent market used.
|
||||
const parentProvider = parentSignerProviders.find(
|
||||
(p) => p.name === provider.name
|
||||
);
|
||||
|
||||
const isParentProviderEqual =
|
||||
parentProvider !== undefined && isEqual(provider, parentProvider);
|
||||
|
||||
// We only want to pass the parent data to the child component if the
|
||||
// data differs from the parent market.
|
||||
return isParentProviderEqual ? (
|
||||
<OracleProfile
|
||||
key={dataSourceSpecId}
|
||||
provider={provider}
|
||||
dataSourceSpecId={dataSourceSpecId}
|
||||
/>
|
||||
) : (
|
||||
<OracleProfile
|
||||
key={dataSourceSpecId}
|
||||
provider={provider}
|
||||
dataSourceSpecId={dataSourceSpecId}
|
||||
parentProvider={parentProvider}
|
||||
parentDataSourceSpecId={parentDataSourceSpecId}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -731,13 +1075,18 @@ const NoOracleProof = ({
|
||||
const OracleProfile = (props: {
|
||||
provider: Provider;
|
||||
dataSourceSpecId: string;
|
||||
parentProvider?: Provider;
|
||||
parentDataSourceSpecId?: string;
|
||||
}) => {
|
||||
// If this is a successor market, the parent market data will only have been passed
|
||||
// in if it differs from the current data.
|
||||
const [open, onChange] = useState(false);
|
||||
return (
|
||||
<div key={props.provider.name}>
|
||||
<OracleBasicProfile
|
||||
provider={props.provider}
|
||||
onClick={() => onChange(!open)}
|
||||
parentProvider={props.parentProvider}
|
||||
/>
|
||||
<OracleDialog {...props} open={open} onChange={onChange} />
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
ExternalLink,
|
||||
Icon,
|
||||
Intent,
|
||||
Lozenge,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@@ -59,10 +60,12 @@ export const OracleBasicProfile = ({
|
||||
provider,
|
||||
onClick,
|
||||
markets: oracleMarkets,
|
||||
parentProvider,
|
||||
}: {
|
||||
provider: Provider;
|
||||
markets?: OracleMarketSpecFieldsFragment[] | undefined;
|
||||
onClick?: (value?: boolean) => void;
|
||||
parentProvider?: Provider;
|
||||
}) => {
|
||||
const { icon, message, intent } = getVerifiedStatusIcon(provider);
|
||||
|
||||
@@ -78,8 +81,14 @@ export const OracleBasicProfile = ({
|
||||
icon: getLinkIcon(proof.type),
|
||||
}));
|
||||
|
||||
// If this is a successor market and there's a different parent provider,
|
||||
// we'll just show that there's been a change, rather than add old data
|
||||
// in alongside the new provider.
|
||||
return (
|
||||
<>
|
||||
{parentProvider && (
|
||||
<Lozenge variant={Intent.Primary}>{t('Updated')}</Lozenge>
|
||||
)}
|
||||
<span className="flex gap-1">
|
||||
{provider.url && (
|
||||
<span className="flex align-items-bottom text-md gap-1">
|
||||
|
||||
@@ -11,16 +11,27 @@ export const OracleDialog = ({
|
||||
dataSourceSpecId,
|
||||
open,
|
||||
onChange,
|
||||
parentProvider,
|
||||
}: {
|
||||
dataSourceSpecId: string;
|
||||
provider: Provider;
|
||||
open: boolean;
|
||||
onChange?: (isOpen: boolean) => void;
|
||||
parentProvider?: Provider;
|
||||
}) => {
|
||||
// If this is a successor market, the parent market data will only have been passed
|
||||
// in if it differs from the current data. We'll pass this on to the title component
|
||||
// to show a change, but the full profile showing changes is unwieldy - it's enough
|
||||
// to know from the title that the oracle has changed.
|
||||
const oracleMarkets = useOracleMarkets(provider);
|
||||
return (
|
||||
<Dialog
|
||||
title={<OracleProfileTitle provider={provider} />}
|
||||
title={
|
||||
<OracleProfileTitle
|
||||
provider={provider}
|
||||
parentProvider={parentProvider}
|
||||
/>
|
||||
}
|
||||
aria-labelledby="oracle-proof-dialog"
|
||||
open={open}
|
||||
onChange={onChange}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ExternalLink,
|
||||
Icon,
|
||||
Intent,
|
||||
Lozenge,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@@ -18,15 +19,30 @@ import type { OracleMarketSpecFieldsFragment } from '../../__generated__/OracleM
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { useState } from 'react';
|
||||
|
||||
export const OracleProfileTitle = ({ provider }: { provider: Provider }) => {
|
||||
export const OracleProfileTitle = ({
|
||||
provider,
|
||||
parentProvider,
|
||||
}: {
|
||||
provider: Provider;
|
||||
parentProvider?: Provider;
|
||||
}) => {
|
||||
// If this is a successor market, the parent provider will only have been passed
|
||||
// in if it differs from the current provider. If it is different, we'll just
|
||||
// show the change in name, not icons and proofs.
|
||||
const { icon, intent } = getVerifiedStatusIcon(provider);
|
||||
const verifiedProofs = provider.proofs.filter(
|
||||
(proof) => proof.available === true
|
||||
);
|
||||
return (
|
||||
<span className="flex gap-1">
|
||||
{parentProvider && (
|
||||
<Lozenge variant={Intent.Primary}>{t('Updated')}</Lozenge>
|
||||
)}
|
||||
{provider.url && (
|
||||
<span>
|
||||
{parentProvider && parentProvider.name && (
|
||||
<span className="line-through">{parentProvider.name}</span>
|
||||
)}
|
||||
<span className="pr-1">{provider.name}</span>
|
||||
<span className="dark:text-vega-light-300 text-vega-dark-300">
|
||||
({verifiedProofs.length})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './open-orders-menu';
|
||||
export * from './order-data-provider';
|
||||
export * from './order-list';
|
||||
export * from './order-list-manager';
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './open-orders-menu';
|
||||
@@ -1,39 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaTransactionStore, useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useHasAmendableOrder } from '../../order-hooks';
|
||||
|
||||
export const OpenOrdersMenu = ({ marketId }: { marketId: string }) => {
|
||||
const { isReadOnly } = useVegaWallet();
|
||||
const create = useVegaTransactionStore((state) => state.create);
|
||||
const hasAmendableOrder = useHasAmendableOrder(marketId);
|
||||
|
||||
if (isReadOnly) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!hasAmendableOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<CancelAllOrdersButton
|
||||
onClick={() => {
|
||||
create({
|
||||
orderCancellation: {},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
size="extra-small"
|
||||
onClick={onClick}
|
||||
data-testid="cancelAll"
|
||||
>
|
||||
{t('Cancel all')}
|
||||
</TradingButton>
|
||||
);
|
||||
@@ -150,3 +150,9 @@ query StopOrders($partyId: ID!) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query StopOrderById($stopOrderId: ID!) {
|
||||
stopOrder(id: $stopOrderId) {
|
||||
...StopOrderFields
|
||||
}
|
||||
}
|
||||
|
||||
+44
-2
@@ -32,7 +32,7 @@ export type OrdersUpdateSubscriptionVariables = Types.Exact<{
|
||||
|
||||
export type OrdersUpdateSubscription = { __typename?: 'Subscription', orders?: Array<{ __typename?: 'OrderUpdate', id: string, marketId: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, liquidityProvisionId?: string | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null }> | null };
|
||||
|
||||
export type OrderSubmissionFieldsFragment = { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null};
|
||||
export type OrderSubmissionFieldsFragment = { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null };
|
||||
|
||||
export type StopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger?: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string } | null, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } };
|
||||
|
||||
@@ -43,6 +43,13 @@ export type StopOrdersQueryVariables = Types.Exact<{
|
||||
|
||||
export type StopOrdersQuery = { __typename?: 'Query', stopOrders?: { __typename?: 'StopOrderConnection', edges?: Array<{ __typename?: 'StopOrderEdge', node?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger?: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string } | null, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null }> | null } | null };
|
||||
|
||||
export type StopOrderByIdQueryVariables = Types.Exact<{
|
||||
stopOrderId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger?: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string } | null, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
|
||||
|
||||
export const OrderFieldsFragmentDoc = gql`
|
||||
fragment OrderFields on Order {
|
||||
id
|
||||
@@ -309,4 +316,39 @@ export function useStopOrdersLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions
|
||||
}
|
||||
export type StopOrdersQueryHookResult = ReturnType<typeof useStopOrdersQuery>;
|
||||
export type StopOrdersLazyQueryHookResult = ReturnType<typeof useStopOrdersLazyQuery>;
|
||||
export type StopOrdersQueryResult = Apollo.QueryResult<StopOrdersQuery, StopOrdersQueryVariables>;
|
||||
export type StopOrdersQueryResult = Apollo.QueryResult<StopOrdersQuery, StopOrdersQueryVariables>;
|
||||
export const StopOrderByIdDocument = gql`
|
||||
query StopOrderById($stopOrderId: ID!) {
|
||||
stopOrder(id: $stopOrderId) {
|
||||
...StopOrderFields
|
||||
}
|
||||
}
|
||||
${StopOrderFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useStopOrderByIdQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useStopOrderByIdQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useStopOrderByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useStopOrderByIdQuery({
|
||||
* variables: {
|
||||
* stopOrderId: // value for 'stopOrderId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useStopOrderByIdQuery(baseOptions: Apollo.QueryHookOptions<StopOrderByIdQuery, StopOrderByIdQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<StopOrderByIdQuery, StopOrderByIdQueryVariables>(StopOrderByIdDocument, options);
|
||||
}
|
||||
export function useStopOrderByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<StopOrderByIdQuery, StopOrderByIdQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<StopOrderByIdQuery, StopOrderByIdQueryVariables>(StopOrderByIdDocument, options);
|
||||
}
|
||||
export type StopOrderByIdQueryHookResult = ReturnType<typeof useStopOrderByIdQuery>;
|
||||
export type StopOrderByIdLazyQueryHookResult = ReturnType<typeof useStopOrderByIdLazyQuery>;
|
||||
export type StopOrderByIdQueryResult = Apollo.QueryResult<StopOrderByIdQuery, StopOrderByIdQueryVariables>;
|
||||
@@ -1,7 +1,9 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { OrderListTable } from '../order-list/order-list';
|
||||
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
|
||||
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider';
|
||||
@@ -22,6 +24,7 @@ export enum Filter {
|
||||
|
||||
export interface OrderListManagerProps {
|
||||
partyId: string;
|
||||
marketId?: string;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
isReadOnly: boolean;
|
||||
@@ -31,6 +34,7 @@ export interface OrderListManagerProps {
|
||||
|
||||
export const OrderListManager = ({
|
||||
partyId,
|
||||
marketId,
|
||||
onMarketClick,
|
||||
onOrderTypeClick,
|
||||
isReadOnly,
|
||||
@@ -41,6 +45,7 @@ export const OrderListManager = ({
|
||||
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
||||
const [viewOrder, setViewOrder] = useState<Order | null>(null);
|
||||
const create = useVegaTransactionStore((state) => state.create);
|
||||
const hasAmendableOrder = useHasAmendableOrder(marketId);
|
||||
const variables =
|
||||
filter === Filter.Open
|
||||
? { partyId, filter: { liveOnly: true } }
|
||||
@@ -71,6 +76,12 @@ export const OrderListManager = ({
|
||||
[create]
|
||||
);
|
||||
|
||||
const cancelAll = useCallback(() => {
|
||||
create({
|
||||
orderCancellation: {},
|
||||
});
|
||||
}, [create]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-full relative">
|
||||
@@ -89,6 +100,9 @@ export const OrderListManager = ({
|
||||
{...gridProps}
|
||||
/>
|
||||
</div>
|
||||
{!isReadOnly && hasAmendableOrder && (
|
||||
<CancelAllOrdersButton onClick={cancelAll} />
|
||||
)}
|
||||
{editOrder && (
|
||||
<OrderEditDialog
|
||||
isOpen={Boolean(editOrder)}
|
||||
@@ -134,3 +148,16 @@ export const OrderListManager = ({
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
||||
<div className="dark:bg-black/75 bg-white/75 h-auto flex justify-end p-2 absolute bottom-0 right-0 rounded">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={onClick}
|
||||
data-testid="cancelAll"
|
||||
>
|
||||
{t('Cancel all')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
getDateTimeFormat,
|
||||
isNumeric,
|
||||
toBigNum,
|
||||
formatTrigger,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
@@ -55,29 +56,8 @@ export const StopOrdersTable = memo<
|
||||
sortable: false,
|
||||
valueFormatter: ({
|
||||
data,
|
||||
value,
|
||||
}: VegaValueFormatterParams<StopOrder, 'trigger'>): string => {
|
||||
if (data && value?.__typename === 'StopOrderPrice') {
|
||||
return `${t('Mark')} ${
|
||||
data?.triggerDirection ===
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
|
||||
? '<'
|
||||
: '>'
|
||||
} ${addDecimalsFormatNumber(
|
||||
value.price,
|
||||
data.market.decimalPlaces
|
||||
)}`;
|
||||
}
|
||||
if (data && value?.__typename === 'StopOrderTrailingPercentOffset') {
|
||||
return `${t('Mark')} ${
|
||||
data?.triggerDirection ===
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
|
||||
? '+'
|
||||
: '-'
|
||||
}${(Number(value.trailingPercentOffset) * 100).toFixed(1)}%`;
|
||||
}
|
||||
return '-';
|
||||
},
|
||||
}: VegaValueFormatterParams<StopOrder, 'trigger'>): string =>
|
||||
data ? formatTrigger(data, data.market.decimalPlaces) : '',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -113,6 +113,7 @@ const marketsData = [
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'AAVEDAI Monthly (30 Jun 2022)',
|
||||
code: 'AAVEDAI.MF21',
|
||||
product: {
|
||||
settlementAsset: {
|
||||
symbol: 'tDAI',
|
||||
@@ -142,6 +143,7 @@ const marketsData = [
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'UNIDAI Monthly (30 Jun 2022)',
|
||||
code: 'UNIDAI.MF21',
|
||||
product: {
|
||||
settlementAsset: {
|
||||
symbol: 'tDAI',
|
||||
@@ -183,7 +185,7 @@ describe('getMetrics && rejoinPositionData', () => {
|
||||
expect(metrics[0].marketId).toEqual(
|
||||
'5e6035fe6a6df78c9ec44b333c231e63d357acef0a0620d2c243f5865d1dc0d8'
|
||||
);
|
||||
expect(metrics[0].marketName).toEqual('AAVEDAI Monthly (30 Jun 2022)');
|
||||
expect(metrics[0].marketName).toEqual('AAVEDAI.MF21');
|
||||
expect(metrics[0].marketTradingMode).toEqual(
|
||||
'TRADING_MODE_MONITORING_AUCTION'
|
||||
);
|
||||
@@ -208,7 +210,7 @@ describe('getMetrics && rejoinPositionData', () => {
|
||||
expect(metrics[1].marketId).toEqual(
|
||||
'10c4b1114d2f6fda239b73d018bca55888b6018f0ac70029972a17fea0a6a56e'
|
||||
);
|
||||
expect(metrics[1].marketName).toEqual('UNIDAI Monthly (30 Jun 2022)');
|
||||
expect(metrics[1].marketName).toEqual('UNIDAI.MF21');
|
||||
expect(metrics[1].marketTradingMode).toEqual('TRADING_MODE_CONTINUOUS');
|
||||
expect(metrics[1].notional).toEqual('86976200');
|
||||
expect(metrics[1].openVolume).toEqual('-100');
|
||||
|
||||
@@ -121,7 +121,7 @@ export const getMetrics = (
|
||||
marginAccountBalance: marginAccount?.balance ?? '0',
|
||||
marketDecimalPlaces,
|
||||
marketId: market.id,
|
||||
marketName: market.tradableInstrument.instrument.name,
|
||||
marketName: market.tradableInstrument.instrument.code,
|
||||
marketTradingMode: market.tradingMode,
|
||||
markPrice: marketData ? marketData.markPrice : undefined,
|
||||
notional: notional
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './asset-proposal-notification';
|
||||
export * from './market-proposal-notification';
|
||||
export * from './protocol-upgrade-countdown';
|
||||
export * from './protocol-upgrade-proposal-notification';
|
||||
export * from './proposals-list';
|
||||
export * from './protocol-upgrade-countdown';
|
||||
export * from './protocol-upgrade-in-progress-notification';
|
||||
export * from './protocol-upgrade-proposal-notification';
|
||||
export * from './voting-progress';
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { useProtocolUpgradeProposalLink } from '@vegaprotocol/environment';
|
||||
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
ExternalLink,
|
||||
Intent,
|
||||
NotificationBanner,
|
||||
SHORT,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { StoredNextProtocolUpgradeData } from '../lib';
|
||||
import {
|
||||
NEXT_PROTOCOL_UPGRADE_PROPOSAL_SNAPSHOT,
|
||||
useNextProtocolUpgradeProposal,
|
||||
} from '../lib';
|
||||
import { useLocalStorageSnapshot } from '@vegaprotocol/react-helpers';
|
||||
import { useBlockRising } from '../lib/protocol-upgrade-proposals/use-block-rising';
|
||||
|
||||
/**
|
||||
* A flag determining whether to get the upgrade proposal data from local
|
||||
* storage if `useNextProtocolUpgradeProposal` fails.
|
||||
*/
|
||||
const ALLOW_STORED_PROPOSAL_DATA = true;
|
||||
|
||||
export const ProtocolUpgradeInProgressNotification = () => {
|
||||
const { data, error } = useNextProtocolUpgradeProposal(undefined, true);
|
||||
const [nextUpgrade] = useLocalStorageSnapshot(
|
||||
NEXT_PROTOCOL_UPGRADE_PROPOSAL_SNAPSHOT
|
||||
);
|
||||
const { blocksRising, block } = useBlockRising();
|
||||
const detailsLink = useProtocolUpgradeProposalLink();
|
||||
|
||||
let vegaReleaseTag: string | undefined;
|
||||
let upgradeBlockHeight: string | undefined;
|
||||
|
||||
if (error && !data && nextUpgrade && ALLOW_STORED_PROPOSAL_DATA) {
|
||||
try {
|
||||
const stored = JSON.parse(nextUpgrade) as StoredNextProtocolUpgradeData;
|
||||
vegaReleaseTag = stored.vegaReleaseTag;
|
||||
upgradeBlockHeight = stored.upgradeBlockHeight;
|
||||
} catch {
|
||||
// no op
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If upgrade is in progress then none of the nodes should produce blocks,
|
||||
* same should be with the tendermint block info otherwise it's a network
|
||||
* issue and not an upgrade. The upgrade usually lasts for couple of minutes.
|
||||
*
|
||||
* Once the networks is back then the notification disappears.
|
||||
*/
|
||||
const upgradeInProgress =
|
||||
vegaReleaseTag &&
|
||||
upgradeBlockHeight &&
|
||||
!blocksRising &&
|
||||
block <= Number(upgradeBlockHeight);
|
||||
|
||||
if (!upgradeInProgress) return null;
|
||||
|
||||
return (
|
||||
<NotificationBanner intent={Intent.Danger} className={SHORT}>
|
||||
<div className="uppercase">
|
||||
{t('The network is being upgraded to %s', vegaReleaseTag)}
|
||||
</div>
|
||||
<div>
|
||||
{t(
|
||||
'Trading and other network activity has stopped until the upgrade is complete.'
|
||||
)}{' '}
|
||||
{vegaReleaseTag && (
|
||||
<ExternalLink href={detailsLink(vegaReleaseTag)}>
|
||||
{t('View details')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</div>
|
||||
</NotificationBanner>
|
||||
);
|
||||
};
|
||||
@@ -29,27 +29,32 @@ export const ProtocolUpgradeProposalNotification = ({
|
||||
|
||||
const { vegaReleaseTag, upgradeBlockHeight } = data;
|
||||
|
||||
let timeLeft = time;
|
||||
let blocksLeft = Number(upgradeBlockHeight) - Number(lastBlockHeight);
|
||||
if (blocksLeft < 0) {
|
||||
blocksLeft = 0;
|
||||
timeLeft = 0;
|
||||
}
|
||||
|
||||
let countdown;
|
||||
switch (mode) {
|
||||
case ProtocolUpgradeCountdownMode.IN_BLOCKS:
|
||||
countdown = (
|
||||
<>
|
||||
<span className="text-vega-orange-500">
|
||||
{Number(upgradeBlockHeight) - Number(lastBlockHeight)}
|
||||
</span>{' '}
|
||||
<span className="text-vega-orange-500">{blocksLeft}</span>{' '}
|
||||
{t('blocks')}
|
||||
</>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING:
|
||||
countdown =
|
||||
time !== undefined ? (
|
||||
timeLeft !== undefined ? (
|
||||
<span
|
||||
title={t('estimated time to protocol upgrade')}
|
||||
className="text-vega-orange-500"
|
||||
data-testid="upgrade-proposal-estimate"
|
||||
>
|
||||
{convertToCountdownString(time, '0:00:00:00')}
|
||||
{convertToCountdownString(timeLeft, '0:00:00:00')}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-vega-orange-600 lowercase italic">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './__generated__/ProtocolUpgradeProposals';
|
||||
export * from './use-block-rising';
|
||||
export * from './use-next-protocol-upgrade-proposals';
|
||||
export * from './use-time-to-upgrade';
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { createClient } from '@vegaprotocol/apollo-client';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useBlockInfo } from '@vegaprotocol/tendermint';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import type { BlockStatisticsQuery } from './__generated__/BlockStatistics';
|
||||
import { BlockStatisticsDocument } from './__generated__/BlockStatistics';
|
||||
import compact from 'lodash/compact';
|
||||
import max from 'lodash/max';
|
||||
|
||||
const CHECK_INTERVAL = 5000; // ms
|
||||
|
||||
/**
|
||||
* Allows stale block height for given number of times. If stale count is
|
||||
* greater than this number then it is marked as not rising (producing blocks).
|
||||
*/
|
||||
const ALLOW_STALE = 2; // times -> MAX(this, 1) * CHECK_INTERVAL ~> min check time
|
||||
|
||||
export const useBlockRising = () => {
|
||||
const [blocksRising, setBlocksRising] = useState(true);
|
||||
const [block, setBlock] = useState(0);
|
||||
const nodes = useEnvironment((state) => state.nodes);
|
||||
const clients = useMemo(() => {
|
||||
return nodes.map(
|
||||
(n) =>
|
||||
n &&
|
||||
n.length > 0 &&
|
||||
createClient({
|
||||
url: n,
|
||||
cacheConfig: undefined,
|
||||
retry: false,
|
||||
connectToDevTools: false,
|
||||
connectToHeaderStore: true,
|
||||
})
|
||||
);
|
||||
}, [nodes]);
|
||||
|
||||
const { refetch: fetchBlockInfo } = useBlockInfo();
|
||||
useEffect(() => {
|
||||
let stale = 0;
|
||||
let prev = 0;
|
||||
const check = async () => {
|
||||
const queries = clients.map((client, index) =>
|
||||
client
|
||||
? client
|
||||
.query<BlockStatisticsQuery>({
|
||||
query: BlockStatisticsDocument,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
})
|
||||
.catch((err) =>
|
||||
Promise.reject(
|
||||
`could not retrieve statistics from ${nodes[index]}`
|
||||
)
|
||||
)
|
||||
: undefined
|
||||
);
|
||||
|
||||
const blockInfo = await fetchBlockInfo();
|
||||
const results = (await Promise.allSettled(compact(queries))).map(
|
||||
(res) => {
|
||||
if (res && res.status === 'fulfilled') {
|
||||
return res.value.data.statistics;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const heights = compact([
|
||||
...results.map((r) => r?.blockHeight),
|
||||
blockInfo?.result.block.header.height,
|
||||
]);
|
||||
const current = max(heights);
|
||||
if (current && Number(current) > prev) {
|
||||
setBlock(Number(current));
|
||||
prev = Number(current);
|
||||
stale = 0;
|
||||
if (!blocksRising) setBlocksRising(true);
|
||||
} else {
|
||||
if (stale > ALLOW_STALE) setBlocksRising(false);
|
||||
stale++;
|
||||
}
|
||||
};
|
||||
const interval = setInterval(check, CHECK_INTERVAL);
|
||||
check();
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [clients, fetchBlockInfo, blocksRising, nodes]);
|
||||
|
||||
return { blocksRising, block };
|
||||
};
|
||||
+27
-2
@@ -2,6 +2,13 @@ import { useMemo, useEffect } from 'react';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { removePaginationWrapper, isTestEnv } from '@vegaprotocol/utils';
|
||||
import { useProtocolUpgradeProposalsQuery } from './__generated__/ProtocolUpgradeProposals';
|
||||
import { useLocalStorageSnapshot } from '@vegaprotocol/react-helpers';
|
||||
import pick from 'lodash/pick';
|
||||
|
||||
const POLL_INTERVAL = 5000; // ms
|
||||
|
||||
export const NEXT_PROTOCOL_UPGRADE_PROPOSAL_SNAPSHOT =
|
||||
'next_protocol_upgrade_proposal';
|
||||
|
||||
export const useNextProtocolUpgradeProposals = (since?: number) => {
|
||||
const { data, loading, error, startPolling, stopPolling } =
|
||||
@@ -22,7 +29,7 @@ export const useNextProtocolUpgradeProposals = (since?: number) => {
|
||||
}
|
||||
|
||||
if (!isTestEnv() && window.location.hostname !== 'localhost') {
|
||||
startPolling(5000);
|
||||
startPolling(POLL_INTERVAL);
|
||||
}
|
||||
}, [error, startPolling, stopPolling]);
|
||||
|
||||
@@ -51,10 +58,28 @@ export const useNextProtocolUpgradeProposals = (since?: number) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useNextProtocolUpgradeProposal = (since?: number) => {
|
||||
export type StoredNextProtocolUpgradeData = {
|
||||
vegaReleaseTag: string;
|
||||
upgradeBlockHeight: string;
|
||||
};
|
||||
export const useNextProtocolUpgradeProposal = (
|
||||
since?: number,
|
||||
persist = false
|
||||
) => {
|
||||
const { data, lastBlockHeight, loading, error } =
|
||||
useNextProtocolUpgradeProposals(since);
|
||||
|
||||
const [, setNextUpgrade] = useLocalStorageSnapshot(
|
||||
NEXT_PROTOCOL_UPGRADE_PROPOSAL_SNAPSHOT
|
||||
);
|
||||
useEffect(() => {
|
||||
if (data && persist) {
|
||||
setNextUpgrade(
|
||||
JSON.stringify(pick(data, 'upgradeBlockHeight', 'vegaReleaseTag'))
|
||||
);
|
||||
}
|
||||
}, [data, persist, setNextUpgrade]);
|
||||
|
||||
return {
|
||||
data: !data ? undefined : data[0],
|
||||
lastBlockHeight,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "tendermint",
|
||||
"name": "@vegaprotocol/tendermint",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { TendermintBlockResponse } from '../types';
|
||||
export const useBlockInfo = (blockHeight?: number, canFetch = true) => {
|
||||
const { TENDERMINT_URL } = useEnvironment();
|
||||
|
||||
const url = `${TENDERMINT_URL}/block?height=${blockHeight}`;
|
||||
const url = `${TENDERMINT_URL}/block?height=${blockHeight || ''}`;
|
||||
const canFetchData = Boolean(
|
||||
TENDERMINT_URL && blockHeight && !isNaN(blockHeight) && canFetch
|
||||
);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
export const IconArrowLeft = ({ size = 16 }: { size: number }) => {
|
||||
return (
|
||||
<svg width={size} height={size} viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M 3.63,7.47
|
||||
C 3.63,7.47 8.72,2.37 8.72,2.37
|
||||
8.72,2.37 7.98,1.63 7.98,1.63
|
||||
7.98,1.63 1.60,8.00 1.60,8.00
|
||||
1.60,8.00 7.98,14.37 7.98,14.37
|
||||
7.98,14.37 8.72,13.63 8.72,13.63
|
||||
8.72,13.63 3.63,8.53 3.63,8.53
|
||||
3.63,8.53 14.35,8.53 14.35,8.53
|
||||
14.35,8.53 14.35,7.47 14.35,7.47
|
||||
14.35,7.47 3.63,7.47 3.63,7.47 Z
|
||||
M 35.00,44.00"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
export const IconArrowTopRight = ({ size = 16 }: { size: number }) => {
|
||||
return (
|
||||
<svg width={size} height={size} viewBox="0 0 14 14">
|
||||
<path
|
||||
d="M 10.47,4.28
|
||||
C 10.47,4.28 10.47,11.49 10.47,11.49
|
||||
10.47,11.49 11.53,11.49 11.53,11.49
|
||||
11.53,11.49 11.53,2.47 11.53,2.47
|
||||
11.53,2.47 2.51,2.47 2.51,2.47
|
||||
2.51,2.47 2.51,3.53 2.51,3.53
|
||||
2.51,3.53 9.72,3.53 9.72,3.53
|
||||
9.72,3.53 2.14,11.12 2.14,11.12
|
||||
2.14,11.12 2.88,11.86 2.88,11.86
|
||||
2.88,11.86 10.47,4.28 10.47,4.28 Z
|
||||
M -37.55,0.64"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,8 @@
|
||||
import { IconArrowDown } from './svg-icons/icon-arrow-down';
|
||||
import { IconArrowLeft } from './svg-icons/icon-arrow-left';
|
||||
import { IconArrowUp } from './svg-icons/icon-arrow-up';
|
||||
import { IconArrowRight } from './svg-icons/icon-arrow-right';
|
||||
import { IconArrowTopRight } from './svg-icons/icon-arrow-top-right';
|
||||
import { IconBreakdown } from './svg-icons/icon-breakdown';
|
||||
import { IconBullet } from './svg-icons/icon-bullet';
|
||||
import { IconChevronDown } from './svg-icons/icon-chevron-down';
|
||||
@@ -36,8 +38,10 @@ import { IconSearch } from './svg-icons/icon-search';
|
||||
|
||||
export enum VegaIconNames {
|
||||
ARROW_DOWN = 'arrow-down',
|
||||
ARROW_LEFT = 'arrow-left',
|
||||
ARROW_UP = 'arrow-up',
|
||||
ARROW_RIGHT = 'arrow-right',
|
||||
ARROW_TOP_RIGHT = 'arrow-top-right',
|
||||
BREAKDOWN = 'breakdown',
|
||||
BULLET = 'bullet',
|
||||
CHEVRON_DOWN = 'chevron-down',
|
||||
@@ -77,8 +81,10 @@ export const VegaIconNameMap: Record<
|
||||
({ size }: { size: number }) => JSX.Element
|
||||
> = {
|
||||
'arrow-down': IconArrowDown,
|
||||
'arrow-left': IconArrowLeft,
|
||||
'arrow-up': IconArrowUp,
|
||||
'arrow-right': IconArrowRight,
|
||||
'arrow-top-right': IconArrowTopRight,
|
||||
breakdown: IconBreakdown,
|
||||
bullet: IconBullet,
|
||||
'chevron-down': IconChevronDown,
|
||||
|
||||
@@ -54,3 +54,4 @@ export * from './traffic-light';
|
||||
export * from './vega-icons';
|
||||
export * from './vega-logo';
|
||||
export * from './viewing-as-user';
|
||||
export * from './pill';
|
||||
|
||||
@@ -4,6 +4,8 @@ import { Intent } from '../../utils/intent';
|
||||
import { Icon } from '../icon';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
|
||||
export const SHORT = '!px-1 !py-1 min-h-fit';
|
||||
|
||||
interface NotificationBannerProps {
|
||||
intent?: Intent;
|
||||
children?: React.ReactNode;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user