Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d8176e9b4 | ||
|
|
d515186bd0 | ||
|
|
1b311a8b36 | ||
|
|
f04bfec1a9 | ||
|
|
2acabb3043 | ||
|
|
1296faab8d | ||
|
|
1e39248d63 | ||
|
|
22c893dc73 | ||
|
|
a1a70887ec | ||
|
|
70c3bc8128 | ||
|
|
7e49836875 | ||
|
|
d624957e6d | ||
|
|
069b57d4ae | ||
|
|
6894fe1264 | ||
|
|
93e4b5fdb9 | ||
|
|
1566fc3984 | ||
|
|
d537b2a510 | ||
|
|
9ff43f9630 | ||
|
|
b5996eae39 | ||
|
|
8b33e9a099 | ||
|
|
39962a0246 | ||
|
|
8d31510d5e | ||
|
|
26d5a67604 | ||
|
|
9cc8f5a377 | ||
|
|
2e11cf4dfa | ||
|
|
9e4ba9f275 | ||
|
|
aac0c25d09 | ||
|
|
d20c2a08ae | ||
|
|
4e6ba5fe3d | ||
|
|
276d1d7c7b | ||
|
|
4990c5808d | ||
|
|
ab1a2cb222 | ||
|
|
cdd91c24f2 | ||
|
|
e3eb13ca72 | ||
|
|
3f0ebbf33d | ||
|
|
e1a1ae0fa1 |
@@ -10,8 +10,8 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
run-tests:
|
run-tests:
|
||||||
name: run-tests
|
name: run-tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: 8-cores
|
||||||
timeout-minutes: 25
|
timeout-minutes: 20
|
||||||
steps:
|
steps:
|
||||||
#----------------------------------------------
|
#----------------------------------------------
|
||||||
# check-out frontend-monorepo
|
# check-out frontend-monorepo
|
||||||
@@ -125,7 +125,7 @@ jobs:
|
|||||||
#----------------------------------------------
|
#----------------------------------------------
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
working-directory: ./console-test
|
working-directory: ./console-test
|
||||||
run: poetry run pytest --numprocesses auto
|
run: poetry run pytest -s --numprocesses auto
|
||||||
- name: Check files
|
- name: Check files
|
||||||
run: |
|
run: |
|
||||||
ls -al .
|
ls -al .
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const Footer = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="grid grid-rows-2 lg:grid-cols-[1fr_auto] text-xs md:text-md lg:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
<footer className="grid grid-cols-[1fr_auto] items-center text-xs md:text-md lg:flex md:col-span-2 px-4 pt-2 pb-3 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
||||||
<div className="flex justify-between gap-2 align-middle">
|
<div className="flex justify-between gap-2 align-middle">
|
||||||
{GIT_COMMIT_HASH && (
|
{GIT_COMMIT_HASH && (
|
||||||
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||||
@@ -43,9 +43,12 @@ export const Footer = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
<div className="content-center flex pr-4 md:border-r border-neutral-700 dark:border-neutral-300">
|
||||||
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
|
<span className="pr-2">{VEGA_URL && <NodeUrl url={VEGA_URL} />}</span>
|
||||||
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
|
<Link
|
||||||
|
className="ml-2 underline-offset-4"
|
||||||
|
onClick={() => setNodeSwitcherOpen(true)}
|
||||||
|
>
|
||||||
{t('Change')}
|
{t('Change')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,7 +62,10 @@ export const Footer = () => {
|
|||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
|
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
|
||||||
<RouteLink to={`/${Routes.DISCLAIMER}`} className="underline">
|
<RouteLink
|
||||||
|
to={`/${Routes.DISCLAIMER}`}
|
||||||
|
className="underline underline-offset-4"
|
||||||
|
>
|
||||||
Disclaimer
|
Disclaimer
|
||||||
</RouteLink>
|
</RouteLink>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||||
import { PriceMonitoringBoundsInfoPanel } from '@vegaprotocol/markets';
|
import {
|
||||||
|
PriceMonitoringBoundsInfoPanel,
|
||||||
|
SuccessionLineInfoPanel,
|
||||||
|
} from '@vegaprotocol/markets';
|
||||||
import {
|
import {
|
||||||
LiquidityInfoPanel,
|
LiquidityInfoPanel,
|
||||||
LiquidityMonitoringParametersInfoPanel,
|
LiquidityMonitoringParametersInfoPanel,
|
||||||
@@ -103,6 +106,8 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
|||||||
<OracleInfoPanel market={market} type="settlementData" />
|
<OracleInfoPanel market={market} type="settlementData" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<h2 className={`${headerClassName} mb-4`}>{t('Succession line')}</h2>
|
||||||
|
<SuccessionLineInfoPanel market={market} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import Hash from '../../links/hash';
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new';
|
import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new';
|
||||||
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
|
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
|
||||||
|
import { MarketLink } from '../../links';
|
||||||
|
import { formatNumber } from '@vegaprotocol/utils';
|
||||||
|
|
||||||
export type Proposal = components['schemas']['v1ProposalSubmission'];
|
export type Proposal = components['schemas']['v1ProposalSubmission'];
|
||||||
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
|
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
|
||||||
@@ -53,7 +55,11 @@ export function proposalTypeLabel(terms?: ProposalTerms): string {
|
|||||||
} else if (has(terms, 'updateAsset')) {
|
} else if (has(terms, 'updateAsset')) {
|
||||||
return t('Update asset proposal');
|
return t('Update asset proposal');
|
||||||
} else if (has(terms, 'newMarket')) {
|
} 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')) {
|
} else if (has(terms, 'updateMarket')) {
|
||||||
return t('Update market proposal');
|
return t('Update market proposal');
|
||||||
} else if (has(terms, 'updateNetworkParameter')) {
|
} 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 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
|
// This component is not rendered if no bundle is required
|
||||||
const SignatureBundleComponent = proposal.terms?.newAsset
|
const SignatureBundleComponent = proposal.terms?.newAsset
|
||||||
@@ -111,6 +124,30 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
|
|||||||
<Hash text={deterministicId} />
|
<Hash text={deterministicId} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</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>
|
</TableWithTbody>
|
||||||
<ProposalSummary
|
<ProposalSummary
|
||||||
id={deterministicId}
|
id={deterministicId}
|
||||||
|
|||||||
@@ -78,7 +78,11 @@ export function getLabelForProposal(
|
|||||||
} else if (proposal.terms?.updateAsset) {
|
} else if (proposal.terms?.updateAsset) {
|
||||||
return t('Proposal: Update asset');
|
return t('Proposal: Update asset');
|
||||||
} else if (proposal.terms?.newMarket) {
|
} 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) {
|
} else if (proposal.terms?.updateMarket) {
|
||||||
return t('Proposal: Update market');
|
return t('Proposal: Update market');
|
||||||
} else if (proposal.terms?.updateNetworkParameter) {
|
} else if (proposal.terms?.updateNetworkParameter) {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 547 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"short_name": "Mainnet Stats",
|
||||||
|
"name": "Vega Mainnet statistics",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
@@ -1,23 +1,39 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Explorer</title>
|
|
||||||
<base href="/" />
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
|
||||||
as="font"
|
|
||||||
type="font/woff2"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta charset="utf-8" />
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/x-icon"
|
type="image/x-icon"
|
||||||
href="https://static.vega.xyz/favicon.ico"
|
href="https://static.vega.xyz/favicon.ico"
|
||||||
/>
|
/>
|
||||||
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description" content="Vega Protocol - VEGA Explorer" />
|
||||||
|
<meta name="og:type" content="website" />
|
||||||
|
<meta name="og:url" content="https://explorer.vega.xyz/" />
|
||||||
|
<meta name="og:title" content="Vega Protocol - Explorer" />
|
||||||
|
<meta name="og:site_name" content="Vega Protocol - Explorer" />
|
||||||
|
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content="Vega Protocol - Explorer" />
|
||||||
|
<meta name="twitter:description" content="Vega Protocol - Explorer" />
|
||||||
|
<meta name="twitter:image" content="https://static.vega.xyz/favicon.ico" />
|
||||||
|
<meta name="twitter:image:alt" content="VEGA logo" />
|
||||||
|
<meta name="twitter:site" content="@vegaprotocol" />
|
||||||
|
|
||||||
|
<meta name="description" content="Vega Protocol - Explorer" />
|
||||||
|
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png" />
|
||||||
|
<link rel="manifest" href="assets/manifest.json" />
|
||||||
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
||||||
|
as="font"
|
||||||
|
type="font/woff2"
|
||||||
|
/>
|
||||||
|
<title>VEGA Explorer</title>
|
||||||
<script src="./assets/env-config.js"></script>
|
<script src="./assets/env-config.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="dark:bg-black h-full w-full">
|
<body class="dark:bg-black h-full w-full">
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
|||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||||
|
NX_SUCCESSOR_MARKETS=true
|
||||||
|
|
||||||
#Test configuration variables
|
#Test configuration variables
|
||||||
CYPRESS_FAIRGROUND=false
|
CYPRESS_FAIRGROUND=false
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import {
|
|||||||
getDateFormatForSpecifiedDays,
|
getDateFormatForSpecifiedDays,
|
||||||
getProposalFromTitle,
|
getProposalFromTitle,
|
||||||
getProposalInformationFromTable,
|
getProposalInformationFromTable,
|
||||||
|
proposalChangeType,
|
||||||
submitUniqueRawProposal,
|
submitUniqueRawProposal,
|
||||||
|
validateProposalDetailsDiff,
|
||||||
voteForProposal,
|
voteForProposal,
|
||||||
} from '../../../../governance-e2e/src/support/governance.functions';
|
} from '../../../../governance-e2e/src/support/governance.functions';
|
||||||
import {
|
import {
|
||||||
@@ -26,7 +28,9 @@ import {
|
|||||||
} from '../../support/wallet-functions';
|
} from '../../support/wallet-functions';
|
||||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||||
|
import { createSuccessorMarketProposalTxBody } from '../../support/proposal.functions';
|
||||||
|
|
||||||
|
const proposalListItem = '[data-testid="proposals-list-item"]';
|
||||||
const proposalVoteProgressForPercentage =
|
const proposalVoteProgressForPercentage =
|
||||||
'vote-progress-indicator-percentage-for';
|
'vote-progress-indicator-percentage-for';
|
||||||
const proposalVoteProgressAgainstPercentage =
|
const proposalVoteProgressAgainstPercentage =
|
||||||
@@ -42,6 +46,7 @@ const viewProposalButton = 'view-proposal-btn';
|
|||||||
const proposalDescriptionToggle = 'proposal-description-toggle';
|
const proposalDescriptionToggle = 'proposal-description-toggle';
|
||||||
const voteBreakdownToggle = 'vote-breakdown-toggle';
|
const voteBreakdownToggle = 'vote-breakdown-toggle';
|
||||||
const proposalTermsToggle = 'proposal-json-toggle';
|
const proposalTermsToggle = 'proposal-json-toggle';
|
||||||
|
const marketDataToggle = 'proposal-market-data-toggle';
|
||||||
|
|
||||||
describe(
|
describe(
|
||||||
'Governance flow for proposal details',
|
'Governance flow for proposal details',
|
||||||
@@ -354,5 +359,98 @@ describe(
|
|||||||
switchVegaWalletPubKey();
|
switchVegaWalletPubKey();
|
||||||
stakingPageDisassociateAllTokens();
|
stakingPageDisassociateAllTokens();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Able to see successor market details with new and updated values', function () {
|
||||||
|
cy.createMarket();
|
||||||
|
cy.reload();
|
||||||
|
waitForSpinner();
|
||||||
|
cy.getByTestId('closed-proposals').within(() => {
|
||||||
|
cy.contains('Add Lorem Ipsum market')
|
||||||
|
.parentsUntil(proposalListItem)
|
||||||
|
.last()
|
||||||
|
.within(() => {
|
||||||
|
cy.getByTestId(viewProposalButton).click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
getProposalInformationFromTable('ID')
|
||||||
|
.invoke('text')
|
||||||
|
.as('parentMarketId')
|
||||||
|
.then(() => {
|
||||||
|
cy.VegaWalletSubmitProposal(
|
||||||
|
createSuccessorMarketProposalTxBody(this.parentMarketId)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
navigateTo(navigation.proposals);
|
||||||
|
cy.reload();
|
||||||
|
getProposalFromTitle('Test successor market proposal details').within(
|
||||||
|
() => cy.getByTestId(viewProposalButton).click()
|
||||||
|
);
|
||||||
|
// #3003-PMAN-010
|
||||||
|
cy.getByTestId(proposalTermsToggle).click();
|
||||||
|
cy.get('.language-json').within(() => {
|
||||||
|
cy.get('.hljs-attr').should('contain.text', 'parentMarketId');
|
||||||
|
cy.get('.hljs-string').should('contain.text', this.parentMarketId);
|
||||||
|
cy.get('.hljs-attr').should('contain.text', 'insurancePoolFraction');
|
||||||
|
cy.get('.hljs-string').should('contain.text', '0.75');
|
||||||
|
});
|
||||||
|
// 3003-PMAN-011 3003-PMAN-012
|
||||||
|
cy.getByTestId(marketDataToggle).click();
|
||||||
|
cy.getByTestId('proposal-market-data').within(() => {
|
||||||
|
cy.contains('Key details').click();
|
||||||
|
validateProposalDetailsDiff(
|
||||||
|
'Name',
|
||||||
|
proposalChangeType.UPDATED,
|
||||||
|
'Token test market',
|
||||||
|
'Test market 1'
|
||||||
|
);
|
||||||
|
validateProposalDetailsDiff(
|
||||||
|
'Parent Market ID',
|
||||||
|
proposalChangeType.ADDED,
|
||||||
|
this.parentMarketId
|
||||||
|
);
|
||||||
|
validateProposalDetailsDiff(
|
||||||
|
'Insurance Pool Fraction',
|
||||||
|
proposalChangeType.ADDED,
|
||||||
|
'0.75'
|
||||||
|
);
|
||||||
|
validateProposalDetailsDiff(
|
||||||
|
'Trading Mode',
|
||||||
|
proposalChangeType.UPDATED,
|
||||||
|
'No trading',
|
||||||
|
'Opening auction'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.contains('Instrument').click();
|
||||||
|
validateProposalDetailsDiff(
|
||||||
|
'Market Name',
|
||||||
|
proposalChangeType.UPDATED,
|
||||||
|
'Token test market',
|
||||||
|
'Test market 1'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.contains('Metadata').click();
|
||||||
|
validateProposalDetailsDiff(
|
||||||
|
'Sector',
|
||||||
|
proposalChangeType.UPDATED,
|
||||||
|
'materials',
|
||||||
|
'tech'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3003-PMAN-011
|
||||||
|
cy.get('.underline').contains('Parent Market ID').realHover();
|
||||||
|
cy.getByTestId('tooltip-content', { timeout: 8000 }).should(
|
||||||
|
'contain.text',
|
||||||
|
'The ID of the market this market succeeds.'
|
||||||
|
);
|
||||||
|
cy.get('.underline')
|
||||||
|
.contains('Insurance Pool Fraction')
|
||||||
|
.realMouseUp()
|
||||||
|
.realHover();
|
||||||
|
cy.getByTestId('tooltip-content', { timeout: 8000 }).should(
|
||||||
|
'contain.text',
|
||||||
|
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -654,44 +654,7 @@ context(
|
|||||||
.find('a')
|
.find('a')
|
||||||
.should('have.attr', 'href')
|
.should('have.attr', 'href')
|
||||||
.and('contain', this.parentMarketId);
|
.and('contain', this.parentMarketId);
|
||||||
cy.getByTestId('view-proposal-btn').click();
|
|
||||||
});
|
});
|
||||||
// #3003-PMAN-010
|
|
||||||
cy.getByTestId(proposalJsonToggle).click();
|
|
||||||
cy.get('.language-json').within(() => {
|
|
||||||
cy.get('.hljs-attr').should('contain.text', 'parentMarketId');
|
|
||||||
cy.get('.hljs-string').should('contain.text', this.parentMarketId);
|
|
||||||
cy.get('.hljs-attr').should('contain.text', 'insurancePoolFraction');
|
|
||||||
cy.get('.hljs-string').should('contain.text', '0.75');
|
|
||||||
});
|
|
||||||
cy.getByTestId('proposal-market-data').within(() => {
|
|
||||||
cy.getByTestId('proposal-market-data-toggle').click();
|
|
||||||
cy.contains('Key details').click();
|
|
||||||
// 3003-PMAN-009
|
|
||||||
getMarketProposalDetailsFromTable('Parent Market ID').should(
|
|
||||||
'have.text',
|
|
||||||
this.parentMarketId
|
|
||||||
);
|
|
||||||
getMarketProposalDetailsFromTable('Insurance Pool Fraction').should(
|
|
||||||
'have.text',
|
|
||||||
'0.75'
|
|
||||||
);
|
|
||||||
getMarketProposalDetailsFromTable('Trading Mode').should(
|
|
||||||
'have.text',
|
|
||||||
'No trading'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
// 3003-PMAN-011
|
|
||||||
cy.contains('Parent Market ID').realHover();
|
|
||||||
cy.getByTestId('tooltip-content').should(
|
|
||||||
'contain.text',
|
|
||||||
'The ID of the market this market succeeds.'
|
|
||||||
);
|
|
||||||
cy.contains('Insurance Pool Fraction').realHover();
|
|
||||||
cy.getByTestId('tooltip-content').should(
|
|
||||||
'contain.text',
|
|
||||||
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after('Disassociate from second wallet key if present', function () {
|
after('Disassociate from second wallet key if present', function () {
|
||||||
@@ -730,6 +693,7 @@ context(
|
|||||||
.getByTestId('key-value-table-row')
|
.getByTestId('key-value-table-row')
|
||||||
.contains(heading)
|
.contains(heading)
|
||||||
.parent()
|
.parent()
|
||||||
|
.parent()
|
||||||
.siblings();
|
.siblings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ import {
|
|||||||
vegaWalletSetSpecifiedApprovalAmount,
|
vegaWalletSetSpecifiedApprovalAmount,
|
||||||
vegaWalletTeardown,
|
vegaWalletTeardown,
|
||||||
} from '../../support/wallet-functions';
|
} from '../../support/wallet-functions';
|
||||||
|
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
|
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||||
|
|
||||||
const stakeValidatorListTotalStake = 'total-stake';
|
const stakeValidatorListTotalStake = 'total-stake';
|
||||||
const stakeValidatorListTotalShare = 'total-stake-share';
|
const stakeValidatorListTotalShare = 'total-stake-share';
|
||||||
const stakeValidatorListStakePercentage = 'stake-percentage';
|
const stakeValidatorListStakePercentage = 'stake-percentage';
|
||||||
@@ -55,6 +58,10 @@ context(
|
|||||||
function () {
|
function () {
|
||||||
// 1002-STKE-002, 1002-STKE-032
|
// 1002-STKE-002, 1002-STKE-032
|
||||||
before('visit staking tab and connect vega wallet', function () {
|
before('visit staking tab and connect vega wallet', function () {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||||
|
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||||
|
});
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
ethereumWalletConnect();
|
ethereumWalletConnect();
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
@@ -65,6 +72,10 @@ context(
|
|||||||
beforeEach(
|
beforeEach(
|
||||||
'teardown wallet & drill into a specific validator',
|
'teardown wallet & drill into a specific validator',
|
||||||
function () {
|
function () {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||||
|
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||||
|
});
|
||||||
cy.clearLocalStorage();
|
cy.clearLocalStorage();
|
||||||
turnTelemetryOff();
|
turnTelemetryOff();
|
||||||
// Go to homepage to allow wallet teardown without epoch timer refreshing page
|
// Go to homepage to allow wallet teardown without epoch timer refreshing page
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||||
import { depositAsset } from '../../support/wallet-functions';
|
import { depositAsset } from '../../support/wallet-functions';
|
||||||
|
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
|
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||||
|
|
||||||
const withdraw = 'withdraw';
|
const withdraw = 'withdraw';
|
||||||
const withdrawalForm = 'withdraw-form';
|
const withdrawalForm = 'withdraw-form';
|
||||||
@@ -42,12 +44,20 @@ context(
|
|||||||
{ tags: '@slow' },
|
{ tags: '@slow' },
|
||||||
function () {
|
function () {
|
||||||
before('visit withdrawals and connect vega wallet', function () {
|
before('visit withdrawals and connect vega wallet', function () {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||||
|
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||||
|
});
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
ethereumWalletConnect();
|
ethereumWalletConnect();
|
||||||
depositAsset(usdcEthAddress, '1000', 5);
|
depositAsset(usdcEthAddress, '1000', 5);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach('Navigate to withdrawal page', function () {
|
beforeEach('Navigate to withdrawal page', function () {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||||
|
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||||
|
});
|
||||||
cy.clearLocalStorage();
|
cy.clearLocalStorage();
|
||||||
turnTelemetryOff();
|
turnTelemetryOff();
|
||||||
cy.reload();
|
cy.reload();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
waitForBeginningOfEpoch,
|
waitForBeginningOfEpoch,
|
||||||
} from '../../support/staking.functions';
|
} from '../../support/staking.functions';
|
||||||
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
|
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
|
||||||
|
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||||
|
|
||||||
const guideLink = 'staking-guide-link';
|
const guideLink = 'staking-guide-link';
|
||||||
const validatorTitle = 'validator-node-title';
|
const validatorTitle = 'validator-node-title';
|
||||||
@@ -33,11 +34,22 @@ const overstakedPenaltyToolTip = 'overstaked-penalty-tooltip';
|
|||||||
const multisigPenaltyToolTip = 'multisig-error-tooltip';
|
const multisigPenaltyToolTip = 'multisig-error-tooltip';
|
||||||
const epochCountDown = 'epoch-countdown';
|
const epochCountDown = 'epoch-countdown';
|
||||||
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
|
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
|
||||||
|
const txTimeout = Cypress.env('txTimeout');
|
||||||
|
|
||||||
context('Validators Page - verify elements on page', function () {
|
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');
|
cy.visit('/validators');
|
||||||
});
|
});
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||||
|
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
||||||
it('Should have validators tab highlighted', function () {
|
it('Should have validators tab highlighted', function () {
|
||||||
@@ -177,6 +189,11 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
{ tags: '@smoke' },
|
{ tags: '@smoke' },
|
||||||
function () {
|
function () {
|
||||||
before('connect wallets and click on validator', 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();
|
cy.connectVegaWallet();
|
||||||
clickOnValidatorFromList(0);
|
clickOnValidatorFromList(0);
|
||||||
});
|
});
|
||||||
@@ -263,7 +280,7 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
|
|
||||||
cy.getByTestId(epochCountDown).within(() => {
|
cy.getByTestId(epochCountDown).within(() => {
|
||||||
cy.get(epochTitle).should('not.be.empty');
|
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,
|
vegaWalletFaucetAssetsWithoutCheck,
|
||||||
vegaWalletTeardown,
|
vegaWalletTeardown,
|
||||||
} from '../../support/wallet-functions';
|
} from '../../support/wallet-functions';
|
||||||
|
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
|
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||||
|
|
||||||
const walletContainer = 'aside [data-testid="vega-wallet"]';
|
const walletContainer = 'aside [data-testid="vega-wallet"]';
|
||||||
const walletHeader = '[data-testid="wallet-header"] h1';
|
const walletHeader = '[data-testid="wallet-header"] h1';
|
||||||
@@ -14,10 +16,6 @@ const dialogHeader = 'dialog-title';
|
|||||||
const walletDialogHeader = 'wallet-dialog-title';
|
const walletDialogHeader = 'wallet-dialog-title';
|
||||||
const connectorsList = 'connectors-list';
|
const connectorsList = 'connectors-list';
|
||||||
const dialogCloseBtn = 'dialog-close';
|
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 accountNo = 'vega-account-truncated';
|
||||||
const currencyTitle = 'currency-title';
|
const currencyTitle = 'currency-title';
|
||||||
const currencyValue = 'currency-value';
|
const currencyValue = 'currency-value';
|
||||||
@@ -69,7 +67,7 @@ context(
|
|||||||
cy.get(dialog).within(() => {
|
cy.get(dialog).within(() => {
|
||||||
cy.getByTestId(walletDialogHeader)
|
cy.getByTestId(walletDialogHeader)
|
||||||
.should('be.visible')
|
.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(connectorsList).within(() => {
|
||||||
cy.getByTestId('connector-jsonRpc')
|
cy.getByTestId('connector-jsonRpc')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.and('have.text', 'Connect Vega wallet');
|
.and('have.text', 'Use the Desktop App/CLI');
|
||||||
cy.getByTestId('connector-rest')
|
|
||||||
.should('be.visible')
|
|
||||||
.and('have.text', 'Hosted Fairground wallet');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -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 () {
|
describe('when vega wallet connected', function () {
|
||||||
before('connect vega wallet', function () {
|
before('connect vega wallet', function () {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||||
|
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||||
|
});
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
|
cy.wait('@ChainId');
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
vegaWalletTeardown();
|
vegaWalletTeardown();
|
||||||
});
|
});
|
||||||
@@ -315,6 +276,10 @@ context(
|
|||||||
];
|
];
|
||||||
|
|
||||||
before('faucet assets to connected vega wallet', function () {
|
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) {
|
for (const { id, amount } of assets) {
|
||||||
vegaWalletFaucetAssetsWithoutCheck(id, amount, vegaWalletPublicKey);
|
vegaWalletFaucetAssetsWithoutCheck(id, amount, vegaWalletPublicKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,23 @@ export function getDownloadedProposalJsonPath(proposalType: string) {
|
|||||||
return filepath;
|
return filepath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function validateProposalDetailsDiff(
|
||||||
|
RowName: string,
|
||||||
|
changeType: proposalChangeType,
|
||||||
|
newValue: string,
|
||||||
|
oldValue?: string
|
||||||
|
) {
|
||||||
|
cy.contains(RowName)
|
||||||
|
.parentsUntil(proposalInformationTableRows)
|
||||||
|
.parent()
|
||||||
|
.first()
|
||||||
|
.within(() => {
|
||||||
|
cy.contains(changeType).should('be.visible');
|
||||||
|
cy.contains(newValue).should('be.visible');
|
||||||
|
if (oldValue) cy.contains(oldValue).should('have.class', 'line-through');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getFormattedTime() {
|
function getFormattedTime() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const day = now.getDate().toString().padStart(2, '0');
|
const day = now.getDate().toString().padStart(2, '0');
|
||||||
@@ -252,3 +269,8 @@ export enum governanceProposalType {
|
|||||||
FREEFORM = 'Freeform',
|
FREEFORM = 'Freeform',
|
||||||
RAW = 'raw proposal',
|
RAW = 'raw proposal',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum proposalChangeType {
|
||||||
|
UPDATED = 'Updated',
|
||||||
|
ADDED = 'Added',
|
||||||
|
}
|
||||||
|
|||||||
@@ -211,6 +211,143 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Requires cy.createMarket() to be run to set up parent market
|
||||||
|
export function createSuccessorMarketProposalTxBody(
|
||||||
|
parentMarketId: string
|
||||||
|
): ProposalSubmissionBody {
|
||||||
|
const MIN_CLOSE_SEC = 10000;
|
||||||
|
const MIN_ENACT_SEC = 10000;
|
||||||
|
|
||||||
|
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
|
||||||
|
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
|
||||||
|
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
|
||||||
|
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
|
||||||
|
return {
|
||||||
|
proposalSubmission: {
|
||||||
|
rationale: {
|
||||||
|
title: 'Test successor market proposal details',
|
||||||
|
description: 'E2E test for successor market',
|
||||||
|
},
|
||||||
|
terms: {
|
||||||
|
newMarket: {
|
||||||
|
changes: {
|
||||||
|
decimalPlaces: '5',
|
||||||
|
positionDecimalPlaces: '5',
|
||||||
|
linearSlippageFactor: '0.001',
|
||||||
|
quadraticSlippageFactor: '0',
|
||||||
|
lpPriceRange: '10',
|
||||||
|
instrument: {
|
||||||
|
name: 'Token test market',
|
||||||
|
code: 'TEST.24h',
|
||||||
|
future: {
|
||||||
|
settlementAsset:
|
||||||
|
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc',
|
||||||
|
quoteName: 'fUSDC',
|
||||||
|
dataSourceSpecForSettlementData: {
|
||||||
|
external: {
|
||||||
|
oracle: {
|
||||||
|
signers: [
|
||||||
|
{
|
||||||
|
pubKey: {
|
||||||
|
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
name: 'prices.BTC.value',
|
||||||
|
type: 'TYPE_INTEGER' as const,
|
||||||
|
numberDecimalPlaces: '0',
|
||||||
|
},
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
operator: 'OPERATOR_GREATER_THAN' as const,
|
||||||
|
value: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataSourceSpecForTradingTermination: {
|
||||||
|
external: {
|
||||||
|
oracle: {
|
||||||
|
signers: [
|
||||||
|
{
|
||||||
|
pubKey: {
|
||||||
|
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
name: 'trading.terminated.ETH5',
|
||||||
|
type: 'TYPE_BOOLEAN' as const,
|
||||||
|
},
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
operator: 'OPERATOR_EQUALS' as const,
|
||||||
|
value: 'true',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataSourceSpecBinding: {
|
||||||
|
settlementDataProperty: 'prices.BTC.value',
|
||||||
|
tradingTerminationProperty: 'trading.terminated.ETH5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadata: [
|
||||||
|
'sector:food',
|
||||||
|
'sector:materials',
|
||||||
|
'source:docs.vega.xyz',
|
||||||
|
],
|
||||||
|
priceMonitoringParameters: {
|
||||||
|
triggers: [
|
||||||
|
{
|
||||||
|
horizon: '43200',
|
||||||
|
probability: '0.9999999',
|
||||||
|
auctionExtension: '600',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
liquidityMonitoringParameters: {
|
||||||
|
targetStakeParameters: {
|
||||||
|
timeWindow: '3600',
|
||||||
|
scalingFactor: 10,
|
||||||
|
},
|
||||||
|
triggeringRatio: '0.7',
|
||||||
|
auctionExtension: '1',
|
||||||
|
},
|
||||||
|
logNormal: {
|
||||||
|
tau: 0.0001140771161,
|
||||||
|
riskAversionParameter: 0.01,
|
||||||
|
params: {
|
||||||
|
mu: 0,
|
||||||
|
r: 0.016,
|
||||||
|
sigma: 0.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
successor: {
|
||||||
|
parentMarketId: parentMarketId,
|
||||||
|
insurancePoolFraction: '0.75',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
closingTimestamp,
|
||||||
|
enactmentTimestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function mockNetworkUpgradeProposal() {
|
export function mockNetworkUpgradeProposal() {
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasGQLQuery(req, 'Nodes', nodeData);
|
aliasGQLQuery(req, 'Nodes', nodeData);
|
||||||
|
|||||||
@@ -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_URL=https://tm.n01.stagnet1.vega.rocks
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
|
||||||
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||||
|
|
||||||
#Test configuration variables
|
#Test configuration variables
|
||||||
CYPRESS_FAIRGROUND=false
|
CYPRESS_FAIRGROUND=false
|
||||||
LC_ALL="en_US.UTF-8"
|
LC_ALL="en_US.UTF-8"
|
||||||
|
|
||||||
# Cosmic elevator flags
|
# Cosmic elevator flags
|
||||||
NX_SUCCESSOR_MARKETS=true
|
NX_SUCCESSOR_MARKETS=true
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { VegaConnectDialog, VegaManageDialog } from '@vegaprotocol/wallet';
|
import {
|
||||||
|
VegaConnectDialog,
|
||||||
|
VegaManageDialog,
|
||||||
|
ViewAsDialog,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
AppStateActionType,
|
AppStateActionType,
|
||||||
useAppState,
|
useAppState,
|
||||||
@@ -25,6 +29,8 @@ export const VegaWalletDialogs = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ViewAsDialog connector={Connectors.view} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
|
||||||
|
|
||||||
export const DownloadWalletPrompt = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
|
|
||||||
<p>
|
|
||||||
<Link className="text-neutral-500" href={ExternalLinks.VEGA_WALLET_URL}>
|
|
||||||
{t('getWalletLink')}
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||||
|
import { useViewAsDialog } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
|
export const VegaWalletPrompt = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const setViewAsDialog = useViewAsDialog((state) => state.setOpen);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
|
||||||
|
<div className="flex flex-row gap-4">
|
||||||
|
<Link className="text-neutral-500" href={ExternalLinks.VEGA_WALLET_URL}>
|
||||||
|
{t('getWalletLink')}
|
||||||
|
</Link>
|
||||||
|
<ButtonLink
|
||||||
|
className="text-neutral-500"
|
||||||
|
data-testid="view-as-user"
|
||||||
|
onClick={() => setViewAsDialog(true)}
|
||||||
|
>
|
||||||
|
{t('viewAsParty')}
|
||||||
|
</ButtonLink>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
WalletCardHeader,
|
WalletCardHeader,
|
||||||
WalletCardRow,
|
WalletCardRow,
|
||||||
} from '../wallet-card';
|
} from '../wallet-card';
|
||||||
import { DownloadWalletPrompt } from './download-wallet-prompt';
|
import { VegaWalletPrompt } from './vega-wallet-prompt';
|
||||||
import { usePollForDelegations } from './hooks';
|
import { usePollForDelegations } from './hooks';
|
||||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||||
@@ -85,7 +85,7 @@ const VegaWalletNotConnected = () => {
|
|||||||
>
|
>
|
||||||
{t('connectVegaWalletToUseAssociated')}
|
{t('connectVegaWalletToUseAssociated')}
|
||||||
</Button>
|
</Button>
|
||||||
<DownloadWalletPrompt />
|
<VegaWalletPrompt />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -855,5 +855,6 @@
|
|||||||
"Estimated time to upgrade": "Estimated time to upgrade",
|
"Estimated time to upgrade": "Estimated time to upgrade",
|
||||||
"Upgraded at": "Upgraded at",
|
"Upgraded at": "Upgraded at",
|
||||||
"dataIsIdentical": "Data is identical",
|
"dataIsIdentical": "Data is identical",
|
||||||
"updatesToMarket": "Updates to market"
|
"updatesToMarket": "Updates to market",
|
||||||
|
"viewAsParty": "View as party"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" class="dark bg-black w-full h-full">
|
<html lang="en" class="dark bg-black w-full h-full">
|
||||||
<head>
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="stylesheet" href="//static.vega.xyz/fonts.css" />
|
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/x-icon"
|
type="image/x-icon"
|
||||||
@@ -10,35 +10,42 @@
|
|||||||
/>
|
/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta name="description" content="Vega Protocol - VEGA Token Vesting" />
|
<meta name="description" content="Vega Protocol - VEGA Governance" />
|
||||||
|
<meta name="og:type" content="website" />
|
||||||
|
<meta name="og:url" content="https://governance.vega.xyz/" />
|
||||||
|
<meta name="og:title" content="Vega Protocol - Governance" />
|
||||||
|
<meta name="og:site_name" content="Vega Protocol - Governance" />
|
||||||
|
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content="Vega Protocol - Governance" />
|
||||||
|
<meta name="twitter:description" content="Vega Protocol - Governance" />
|
||||||
|
<meta name="twitter:image" content="https://static.vega.xyz/favicon.ico" />
|
||||||
|
<meta name="twitter:image:alt" content="VEGA logo" />
|
||||||
|
<meta name="twitter:site" content="@vegaprotocol" />
|
||||||
|
|
||||||
|
<meta name="description" content="Vega Protocol - Governance" />
|
||||||
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png" />
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="assets/manifest.json" />
|
<link rel="manifest" href="assets/manifest.json" />
|
||||||
<link
|
<link
|
||||||
rel="preload"
|
rel="preload"
|
||||||
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
||||||
as="font"
|
as="font"
|
||||||
type="font/woff2"
|
type="font/woff2"
|
||||||
crossorigin="anonymous"
|
|
||||||
/>
|
/>
|
||||||
<title>Vega Governance dApp</title>
|
<title>VEGA Governance</title>
|
||||||
<script src="./assets/env-config.js"></script>
|
<script src="./assets/env-config.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="h-full">
|
<body class="h-full">
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript> You need to enable JavaScript to run this app. </noscript>
|
||||||
<div id="root" class="h-full"></div>
|
<div id="root" class="h-full"></div>
|
||||||
<!--
|
<!-- This HTML file is a template.
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
To create a production bundle, use `npm run build` or `yarn build`. -->
|
||||||
-->
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
RestConnector,
|
|
||||||
JsonRpcConnector,
|
JsonRpcConnector,
|
||||||
ViewConnector,
|
ViewConnector,
|
||||||
InjectedConnector,
|
InjectedConnector,
|
||||||
@@ -8,13 +7,11 @@ import {
|
|||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
export const injected = new InjectedConnector();
|
export const injected = new InjectedConnector();
|
||||||
export const rest = new RestConnector();
|
|
||||||
export const jsonRpc = new JsonRpcConnector();
|
export const jsonRpc = new JsonRpcConnector();
|
||||||
export const view = new ViewConnector(urlParams.get('address'));
|
export const view = new ViewConnector(urlParams.get('address'));
|
||||||
|
|
||||||
export const Connectors = {
|
export const Connectors = {
|
||||||
injected,
|
injected,
|
||||||
rest,
|
|
||||||
jsonRpc,
|
jsonRpc,
|
||||||
view,
|
view,
|
||||||
};
|
};
|
||||||
|
|||||||
+83
-9
@@ -45,8 +45,10 @@ export const useMarketDataDialogStore = create<MarketDataDialogState>(
|
|||||||
|
|
||||||
export const ProposalMarketData = ({
|
export const ProposalMarketData = ({
|
||||||
marketData,
|
marketData,
|
||||||
|
parentMarketData,
|
||||||
}: {
|
}: {
|
||||||
marketData: MarketInfoWithData;
|
marketData: MarketInfoWithData;
|
||||||
|
parentMarketData?: MarketInfoWithData;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isOpen, open, close } = useMarketDataDialogStore();
|
const { isOpen, open, close } = useMarketDataDialogStore();
|
||||||
@@ -58,8 +60,21 @@ export const ProposalMarketData = ({
|
|||||||
|
|
||||||
const settlementData = marketData.tradableInstrument.instrument.product
|
const settlementData = marketData.tradableInstrument.instrument.product
|
||||||
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
|
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
|
||||||
|
const parentSettlementData =
|
||||||
|
parentMarketData?.tradableInstrument.instrument?.product
|
||||||
|
?.dataSourceSpecForSettlementData?.data;
|
||||||
const terminationData = marketData.tradableInstrument.instrument.product
|
const terminationData = marketData.tradableInstrument.instrument.product
|
||||||
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
|
.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) => {
|
const getSigners = (data: DataSourceDefinition) => {
|
||||||
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
|
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
|
||||||
@@ -97,12 +112,22 @@ export const ProposalMarketData = ({
|
|||||||
<AccordionItem
|
<AccordionItem
|
||||||
itemId="key-details"
|
itemId="key-details"
|
||||||
title={t('Key details')}
|
title={t('Key details')}
|
||||||
content={<KeyDetailsInfoPanel market={marketData} />}
|
content={
|
||||||
|
<KeyDetailsInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
parentMarket={parentMarketData}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
itemId="instrument"
|
itemId="instrument"
|
||||||
title={t('Instrument')}
|
title={t('Instrument')}
|
||||||
content={<InstrumentInfoPanel market={marketData} />}
|
content={
|
||||||
|
<InstrumentInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
parentMarket={parentMarketData}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{isEqual(
|
{isEqual(
|
||||||
getSigners(settlementData),
|
getSigners(settlementData),
|
||||||
@@ -115,6 +140,11 @@ export const ProposalMarketData = ({
|
|||||||
<OracleInfoPanel
|
<OracleInfoPanel
|
||||||
market={marketData}
|
market={marketData}
|
||||||
type="settlementData"
|
type="settlementData"
|
||||||
|
parentMarket={
|
||||||
|
isParentSettlementDataEqual
|
||||||
|
? undefined
|
||||||
|
: parentMarketData
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -127,6 +157,11 @@ export const ProposalMarketData = ({
|
|||||||
<OracleInfoPanel
|
<OracleInfoPanel
|
||||||
market={marketData}
|
market={marketData}
|
||||||
type="settlementData"
|
type="settlementData"
|
||||||
|
parentMarket={
|
||||||
|
isParentSettlementDataEqual
|
||||||
|
? undefined
|
||||||
|
: parentMarketData
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -135,11 +170,21 @@ export const ProposalMarketData = ({
|
|||||||
itemId="termination-oracle"
|
itemId="termination-oracle"
|
||||||
title={t('Termination Oracle')}
|
title={t('Termination Oracle')}
|
||||||
content={
|
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
|
<AccordionItem
|
||||||
itemId="settlement-asset"
|
itemId="settlement-asset"
|
||||||
title={t('Settlement asset')}
|
title={t('Settlement asset')}
|
||||||
@@ -148,22 +193,42 @@ export const ProposalMarketData = ({
|
|||||||
<AccordionItem
|
<AccordionItem
|
||||||
itemId="metadata"
|
itemId="metadata"
|
||||||
title={t('Metadata')}
|
title={t('Metadata')}
|
||||||
content={<MetadataInfoPanel market={marketData} />}
|
content={
|
||||||
|
<MetadataInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
parentMarket={parentMarketData}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
itemId="risk-model"
|
itemId="risk-model"
|
||||||
title={t('Risk model')}
|
title={t('Risk model')}
|
||||||
content={<RiskModelInfoPanel market={marketData} />}
|
content={
|
||||||
|
<RiskModelInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
parentMarket={parentMarketData}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
itemId="risk-parameters"
|
itemId="risk-parameters"
|
||||||
title={t('Risk parameters')}
|
title={t('Risk parameters')}
|
||||||
content={<RiskParametersInfoPanel market={marketData} />}
|
content={
|
||||||
|
<RiskParametersInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
parentMarket={parentMarketData}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
itemId="risk-factors"
|
itemId="risk-factors"
|
||||||
title={t('Risk factors')}
|
title={t('Risk factors')}
|
||||||
content={<RiskFactorsInfoPanel market={marketData} />}
|
content={
|
||||||
|
<RiskFactorsInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
parentMarket={parentMarketData}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{(
|
{(
|
||||||
marketData.priceMonitoringSettings?.parameters?.triggers || []
|
marketData.priceMonitoringSettings?.parameters?.triggers || []
|
||||||
@@ -174,6 +239,7 @@ export const ProposalMarketData = ({
|
|||||||
content={
|
content={
|
||||||
<PriceMonitoringBoundsInfoPanel
|
<PriceMonitoringBoundsInfoPanel
|
||||||
market={marketData}
|
market={marketData}
|
||||||
|
parentMarket={parentMarketData}
|
||||||
triggerIndex={triggerIndex}
|
triggerIndex={triggerIndex}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -183,13 +249,21 @@ export const ProposalMarketData = ({
|
|||||||
itemId="liqudity-monitoring-parameters"
|
itemId="liqudity-monitoring-parameters"
|
||||||
title={t('Liquidity monitoring parameters')}
|
title={t('Liquidity monitoring parameters')}
|
||||||
content={
|
content={
|
||||||
<LiquidityMonitoringParametersInfoPanel market={marketData} />
|
<LiquidityMonitoringParametersInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
parentMarket={parentMarketData}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
itemId="liquidity-price-range"
|
itemId="liquidity-price-range"
|
||||||
title={t('Liquidity price range')}
|
title={t('Liquidity price range')}
|
||||||
content={<LiquidityPriceRangeInfoPanel market={marketData} />}
|
content={
|
||||||
|
<LiquidityPriceRangeInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
parentMarket={parentMarketData}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export interface ProposalProps {
|
|||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
networkParams: Partial<NetworkParamsResult>;
|
networkParams: Partial<NetworkParamsResult>;
|
||||||
newMarketData?: MarketInfoWithData | null;
|
newMarketData?: MarketInfoWithData | null;
|
||||||
|
parentMarketData?: MarketInfoWithData | null;
|
||||||
assetData?: AssetQuery | null;
|
assetData?: AssetQuery | null;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
restData: any;
|
restData: any;
|
||||||
@@ -46,6 +47,7 @@ export const Proposal = ({
|
|||||||
networkParams,
|
networkParams,
|
||||||
restData,
|
restData,
|
||||||
newMarketData,
|
newMarketData,
|
||||||
|
parentMarketData,
|
||||||
assetData,
|
assetData,
|
||||||
originalMarketProposalRestData,
|
originalMarketProposalRestData,
|
||||||
mostRecentlyEnactedAssociatedMarketProposal,
|
mostRecentlyEnactedAssociatedMarketProposal,
|
||||||
@@ -157,7 +159,10 @@ export const Proposal = ({
|
|||||||
|
|
||||||
{newMarketData && (
|
{newMarketData && (
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<ProposalMarketData marketData={newMarketData} />
|
<ProposalMarketData
|
||||||
|
marketData={newMarketData}
|
||||||
|
parentMarketData={parentMarketData ? parentMarketData : undefined}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import {
|
|||||||
NetworkParams,
|
NetworkParams,
|
||||||
useNetworkParams,
|
useNetworkParams,
|
||||||
} from '@vegaprotocol/network-parameters';
|
} from '@vegaprotocol/network-parameters';
|
||||||
|
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
|
||||||
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
|
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
||||||
|
|
||||||
export const ProposalContainer = () => {
|
export const ProposalContainer = () => {
|
||||||
const [
|
const [
|
||||||
@@ -54,6 +57,10 @@ export const ProposalContainer = () => {
|
|||||||
skip: !params.proposalId,
|
skip: !params.proposalId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const successor = useSuccessorMarketProposalDetails(params.proposalId);
|
||||||
|
|
||||||
|
const isSuccessor = !!successor?.parentMarketId || !!successor.code;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: {
|
state: {
|
||||||
data: originalMarketProposalRestData,
|
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 {
|
const {
|
||||||
data: assetData,
|
data: assetData,
|
||||||
loading: assetLoading,
|
loading: assetLoading,
|
||||||
@@ -160,6 +197,8 @@ export const ProposalContainer = () => {
|
|||||||
newMarketLoading ||
|
newMarketLoading ||
|
||||||
assetLoading ||
|
assetLoading ||
|
||||||
networkParamsLoading ||
|
networkParamsLoading ||
|
||||||
|
parentMarketIdLoading ||
|
||||||
|
parentMarketLoading ||
|
||||||
(restLoading ? (restLoading as boolean) : false) ||
|
(restLoading ? (restLoading as boolean) : false) ||
|
||||||
(originalMarketProposalRestLoading
|
(originalMarketProposalRestLoading
|
||||||
? (originalMarketProposalRestLoading as boolean)
|
? (originalMarketProposalRestLoading as boolean)
|
||||||
@@ -172,15 +211,18 @@ export const ProposalContainer = () => {
|
|||||||
error ||
|
error ||
|
||||||
newMarketError ||
|
newMarketError ||
|
||||||
assetError ||
|
assetError ||
|
||||||
|
networkParamsError ||
|
||||||
|
parentMarketIdError ||
|
||||||
|
parentMarketError ||
|
||||||
restError ||
|
restError ||
|
||||||
originalMarketProposalRestError ||
|
originalMarketProposalRestError ||
|
||||||
previouslyEnactedMarketProposalsRestError ||
|
previouslyEnactedMarketProposalsRestError
|
||||||
networkParamsError
|
|
||||||
}
|
}
|
||||||
data={{
|
data={{
|
||||||
...data,
|
...data,
|
||||||
...networkParams,
|
...networkParams,
|
||||||
...(newMarketData ? { newMarketData } : {}),
|
...(newMarketData ? { newMarketData } : {}),
|
||||||
|
...(parentMarketData ? { parentMarketData } : {}),
|
||||||
...(assetData ? { assetData } : {}),
|
...(assetData ? { assetData } : {}),
|
||||||
...(restData ? { restData } : {}),
|
...(restData ? { restData } : {}),
|
||||||
...(originalMarketProposalRestData
|
...(originalMarketProposalRestData
|
||||||
@@ -197,6 +239,7 @@ export const ProposalContainer = () => {
|
|||||||
networkParams={networkParams}
|
networkParams={networkParams}
|
||||||
restData={restData}
|
restData={restData}
|
||||||
newMarketData={newMarketData}
|
newMarketData={newMarketData}
|
||||||
|
parentMarketData={parentMarketData}
|
||||||
assetData={assetData}
|
assetData={assetData}
|
||||||
originalMarketProposalRestData={originalMarketProposalRestData}
|
originalMarketProposalRestData={originalMarketProposalRestData}
|
||||||
mostRecentlyEnactedAssociatedMarketProposal={
|
mostRecentlyEnactedAssociatedMarketProposal={
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ module.exports = defineConfig({
|
|||||||
requestTimeout: 20000,
|
requestTimeout: 20000,
|
||||||
retries: 1,
|
retries: 1,
|
||||||
testIsolation: false,
|
testIsolation: false,
|
||||||
experimentalMemoryManagement: true,
|
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
ETHERSCAN_URL: 'https://sepolia.etherscan.io',
|
ETHERSCAN_URL: 'https://sepolia.etherscan.io',
|
||||||
|
|||||||
@@ -104,11 +104,11 @@ describe('deposit actions', { tags: '@smoke' }, () => {
|
|||||||
cy.visit('/#/markets/market-1');
|
cy.visit('/#/markets/market-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Deposit to trade is visble', () => {
|
it('Deposit to trade is visible', () => {
|
||||||
cy.getByTestId('Collateral').click();
|
cy.getByTestId('Collateral').click();
|
||||||
cy.contains('[data-testid="deposit"]', 'Deposit')
|
cy.get('[row-id="asset-id"]').contains('tEURO').should('be.visible');
|
||||||
.should('be.visible')
|
cy.contains('[data-testid="deposit"]', 'Deposit').should('be.visible');
|
||||||
.click();
|
cy.contains('[data-testid="deposit"]', 'Deposit').click();
|
||||||
cy.getByTestId('deposit-submit').should('be.visible');
|
cy.getByTestId('deposit-submit').should('be.visible');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -169,14 +169,13 @@ describe('home', { tags: '@regression' }, () => {
|
|||||||
|
|
||||||
it('click get started button should open connect dialog', () => {
|
it('click get started button should open connect dialog', () => {
|
||||||
cy.getByTestId('welcome-dialog').should('be.visible');
|
cy.getByTestId('welcome-dialog').should('be.visible');
|
||||||
cy.getByTestId('get-started-button').click();
|
|
||||||
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
|
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
|
||||||
cy.window().then((window) => {
|
cy.window().then((window) => {
|
||||||
expect(window.localStorage.getItem('vega_onboarding_viewed')).to.equal(
|
// @ts-ignore stub it out just for test case
|
||||||
'true'
|
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');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
|||||||
cy.getByTestId(orderTIFDropDown).select('TIME_IN_FORCE_GTC');
|
cy.getByTestId(orderTIFDropDown).select('TIME_IN_FORCE_GTC');
|
||||||
cy.getByTestId(orderSizeField).clear().type('1');
|
cy.getByTestId(orderSizeField).clear().type('1');
|
||||||
cy.getByTestId(orderPriceField).clear().type('1.123456');
|
cy.getByTestId(orderPriceField).clear().type('1.123456');
|
||||||
|
cy.getByTestId(placeOrderBtn).click();
|
||||||
cy.getByTestId('deal-ticket-error-message-price-limit').should(
|
cy.getByTestId('deal-ticket-error-message-price-limit').should(
|
||||||
'have.text',
|
'have.text',
|
||||||
'Price accepts up to 5 decimal places'
|
'Price accepts up to 5 decimal places'
|
||||||
@@ -73,6 +74,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
|||||||
describe('market order', () => {
|
describe('market order', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.getByTestId(toggleMarket).click();
|
cy.getByTestId(toggleMarket).click();
|
||||||
|
cy.getByTestId(placeOrderBtn).click();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('must not see the price unit', function () {
|
it('must not see the price unit', function () {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
|
|||||||
cy.getByTestId(orderPriceField).clear().type('0.1');
|
cy.getByTestId(orderPriceField).clear().type('0.1');
|
||||||
cy.getByTestId(orderSizeField).clear().type('1');
|
cy.getByTestId(orderSizeField).clear().type('1');
|
||||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
||||||
|
cy.getByTestId(placeOrderBtn).click();
|
||||||
cy.getByTestId('deal-ticket-warning-auction').should(
|
cy.getByTestId('deal-ticket-warning-auction').should(
|
||||||
'have.text',
|
'have.text',
|
||||||
'Any orders placed now will not trade until the auction ends'
|
'Any orders placed now will not trade until the auction ends'
|
||||||
@@ -60,6 +61,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
|
|||||||
TIFlist.filter((item) => item.code === 'FOK')[0].value
|
TIFlist.filter((item) => item.code === 'FOK')[0].value
|
||||||
);
|
);
|
||||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
||||||
|
cy.getByTestId(placeOrderBtn).click();
|
||||||
cy.getByTestId('deal-ticket-error-message-tif').should(
|
cy.getByTestId('deal-ticket-error-message-tif').should(
|
||||||
'have.text',
|
'have.text',
|
||||||
'This market is in auction until it reaches sufficient liquidity. Until the auction ends, you can only place GFA, GTT, or GTC limit orders'
|
'This market is in auction until it reaches sufficient liquidity. Until the auction ends, you can only place GFA, GTT, or GTC limit orders'
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ describe('fills', { tags: '@regression' }, () => {
|
|||||||
cy.getByTestId(tabFills).contains('Market');
|
cy.getByTestId(tabFills).contains('Market');
|
||||||
cy.getByTestId(tabFills)
|
cy.getByTestId(tabFills)
|
||||||
.get(
|
.get(
|
||||||
'[role="gridcell"][col-id="market.tradableInstrument.instrument.name"]'
|
'[role="gridcell"][col-id="market.tradableInstrument.instrument.code"]'
|
||||||
)
|
)
|
||||||
.each(($marketSymbol) => {
|
.each(($marketSymbol) => {
|
||||||
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
|
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
|
||||||
|
|||||||
@@ -142,22 +142,22 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
|||||||
it('sorting by Market', () => {
|
it('sorting by Market', () => {
|
||||||
visitAndClickPositions();
|
visitAndClickPositions();
|
||||||
const marketsSortedDefault = [
|
const marketsSortedDefault = [
|
||||||
'ACTIVE MARKET',
|
'AAPL.MF21',
|
||||||
'Apple Monthly (30 Jun 2022)',
|
'BTCUSD.MF21',
|
||||||
'ETHBTC Quarterly (30 Jun 2022)',
|
'ETHBTC.QM21',
|
||||||
'SUSPENDED MARKET',
|
'SOLUSD',
|
||||||
];
|
];
|
||||||
const marketsSortedAsc = [
|
const marketsSortedAsc = [
|
||||||
'ACTIVE MARKET',
|
'AAPL.MF21',
|
||||||
'Apple Monthly (30 Jun 2022)',
|
'BTCUSD.MF21',
|
||||||
'ETHBTC Quarterly (30 Jun 2022)',
|
'ETHBTC.QM21',
|
||||||
'SUSPENDED MARKET',
|
'SOLUSD',
|
||||||
];
|
];
|
||||||
const marketsSortedDesc = [
|
const marketsSortedDesc = [
|
||||||
'SUSPENDED MARKET',
|
'SOLUSD',
|
||||||
'ETHBTC Quarterly (30 Jun 2022)',
|
'ETHBTC.QM21',
|
||||||
'Apple Monthly (30 Jun 2022)',
|
'BTCUSD.MF21',
|
||||||
'ACTIVE MARKET',
|
'AAPL.MF21',
|
||||||
];
|
];
|
||||||
cy.getByTestId(positions).click();
|
cy.getByTestId(positions).click();
|
||||||
// 7004-POSI-003
|
// 7004-POSI-003
|
||||||
@@ -291,9 +291,11 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
|||||||
|
|
||||||
it('View settlement asset', () => {
|
it('View settlement asset', () => {
|
||||||
visitAndClickPositions();
|
visitAndClickPositions();
|
||||||
cy.get('[col-id="asset"]').within(() => {
|
cy.get('[col-id="asset"]')
|
||||||
cy.get('button[type="button"]').first().click();
|
.eq(3)
|
||||||
});
|
.within(() => {
|
||||||
|
cy.get('button[type="button"]').click();
|
||||||
|
});
|
||||||
// 7004-POSI-008
|
// 7004-POSI-008
|
||||||
cy.getByTestId(dialogContent).should('be.visible');
|
cy.getByTestId(dialogContent).should('be.visible');
|
||||||
cy.getByTestId(dialogCloseX).click();
|
cy.getByTestId(dialogCloseX).click();
|
||||||
@@ -308,7 +310,7 @@ function validatePositionsDisplayed(multiKey = false) {
|
|||||||
cy.getByTestId('tab-positions').should('be.visible');
|
cy.getByTestId('tab-positions').should('be.visible');
|
||||||
cy.getByTestId('tab-positions')
|
cy.getByTestId('tab-positions')
|
||||||
.get('.ag-center-cols-container .ag-row')
|
.get('.ag-center-cols-container .ag-row')
|
||||||
.first()
|
.eq(multiKey ? 3 : 1)
|
||||||
.within(() => {
|
.within(() => {
|
||||||
cy.get('[col-id="marketName"]')
|
cy.get('[col-id="marketName"]')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
|
|||||||
@@ -5,118 +5,8 @@ import {
|
|||||||
|
|
||||||
const connectVegaBtn = 'connect-vega-wallet';
|
const connectVegaBtn = 'connect-vega-wallet';
|
||||||
const manageVegaBtn = 'manage-vega-wallet';
|
const manageVegaBtn = 'manage-vega-wallet';
|
||||||
const form = 'rest-connector-form';
|
|
||||||
const dialogContent = 'dialog-content';
|
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 }, () => {
|
describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Using portfolio page as it requires vega wallet connection
|
// 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('connect-vega-wallet').should('exist');
|
||||||
cy.getByTestId('manage-vega-wallet').should('not.exist');
|
cy.getByTestId('manage-vega-wallet').should('not.exist');
|
||||||
cy.getByTestId('connect-vega-wallet').click();
|
cy.getByTestId('connect-vega-wallet').click();
|
||||||
cy.contains(
|
cy.contains('Enter a custom wallet location');
|
||||||
'Choose wallet app to connect, or to change port or server URL enter a custom wallet location first'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,100 +12,108 @@ const walletTransfer = 'wallet-transfer';
|
|||||||
|
|
||||||
const ASSET_SEPOLIA_TBTC = 2;
|
const ASSET_SEPOLIA_TBTC = 2;
|
||||||
|
|
||||||
describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
|
describe.skip(
|
||||||
beforeEach(() => {
|
'transfer fees',
|
||||||
cy.mockWeb3Provider();
|
{ tags: '@regression', testIsolation: true },
|
||||||
cy.mockTradingPage();
|
() => {
|
||||||
cy.mockSubscription();
|
beforeEach(() => {
|
||||||
cy.setVegaWallet();
|
cy.mockWeb3Provider();
|
||||||
|
cy.mockTradingPage();
|
||||||
|
cy.mockSubscription();
|
||||||
|
cy.setVegaWallet();
|
||||||
|
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.getByTestId(manageVegaWallet).click();
|
cy.getByTestId(manageVegaWallet).click();
|
||||||
cy.getByTestId(walletTransfer).click();
|
cy.getByTestId(walletTransfer).click();
|
||||||
|
|
||||||
cy.wait('@Assets');
|
cy.wait('@Assets');
|
||||||
cy.wait('@Accounts');
|
cy.wait('@Accounts');
|
||||||
|
|
||||||
cy.mockVegaWalletTransaction();
|
cy.mockVegaWalletTransaction();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('transfer fees tooltips', () => {
|
it('transfer fees tooltips', () => {
|
||||||
// 1003-TRAN-015
|
// 1003-TRAN-015
|
||||||
// 1003-TRAN-016
|
// 1003-TRAN-016
|
||||||
// 1003-TRAN-017
|
// 1003-TRAN-017
|
||||||
// 1003-TRAN-018
|
// 1003-TRAN-018
|
||||||
// 1003-TRAN-019
|
// 1003-TRAN-019
|
||||||
cy.getByTestId(transferForm);
|
cy.getByTestId(transferForm);
|
||||||
cy.contains('Enter manually').click();
|
cy.contains('Enter manually').click();
|
||||||
cy.getByTestId(transferForm)
|
cy.getByTestId(transferForm)
|
||||||
.find(toAddressField)
|
.find(toAddressField)
|
||||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
.type(
|
||||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
'7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535'
|
||||||
cy.getByTestId(transferForm)
|
);
|
||||||
.find(amountField)
|
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||||
.type('1', { delay: 100, force: true });
|
cy.getByTestId(transferForm)
|
||||||
|
.find(amountField)
|
||||||
|
.type('1', { delay: 100, force: true });
|
||||||
|
|
||||||
/// Check Include Transfer Fee tooltip
|
/// Check Include Transfer Fee tooltip
|
||||||
cy.get('label[for="include-transfer-fee"] div').realHover();
|
cy.get('label[for="include-transfer-fee"] div').realHover();
|
||||||
cy.get('[data-side="bottom"] div')
|
cy.get('[data-side="bottom"] div')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('not.be.empty');
|
.should('not.be.empty');
|
||||||
|
|
||||||
//Check Transfer Fee tooltip
|
//Check Transfer Fee tooltip
|
||||||
cy.contains('div', 'Transfer fee').realHover();
|
cy.contains('div', 'Transfer fee').realHover();
|
||||||
cy.get('[data-side="bottom"] div')
|
cy.get('[data-side="bottom"] div')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('not.be.empty');
|
.should('not.be.empty');
|
||||||
|
|
||||||
//Check Amount to be transferred tooltip
|
//Check Amount to be transferred tooltip
|
||||||
cy.contains('div', 'Amount to be transferred').realHover();
|
cy.contains('div', 'Amount to be transferred').realHover();
|
||||||
cy.get('[data-side="bottom"] div')
|
cy.get('[data-side="bottom"] div')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('not.be.empty');
|
.should('not.be.empty');
|
||||||
|
|
||||||
//Check Total amount (with fee) tooltip
|
//Check Total amount (with fee) tooltip
|
||||||
cy.contains('div', 'Total amount (with fee)').realHover();
|
cy.contains('div', 'Total amount (with fee)').realHover();
|
||||||
cy.get('[data-side="bottom"] div')
|
cy.get('[data-side="bottom"] div')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('not.be.empty');
|
.should('not.be.empty');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('transfer fees', () => {
|
it('transfer fees', () => {
|
||||||
// 1003-TRAN-020
|
// 1003-TRAN-020
|
||||||
// 1003-TRAN-021
|
// 1003-TRAN-021
|
||||||
// 1003-TRAN-022
|
// 1003-TRAN-022
|
||||||
// 1003-TRAN-023
|
// 1003-TRAN-023
|
||||||
cy.getByTestId(transferForm);
|
cy.getByTestId(transferForm);
|
||||||
cy.contains('Enter manually').click();
|
cy.contains('Enter manually').click();
|
||||||
|
|
||||||
cy.getByTestId(transferForm)
|
cy.getByTestId(transferForm)
|
||||||
.find(toAddressField)
|
.find(toAddressField)
|
||||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
.type(
|
||||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
'7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535'
|
||||||
cy.getByTestId(includeTransferFeeRadioBtn).should('be.disabled');
|
);
|
||||||
|
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||||
|
cy.getByTestId(includeTransferFeeRadioBtn).should('be.disabled');
|
||||||
|
|
||||||
cy.getByTestId(transferForm)
|
cy.getByTestId(transferForm)
|
||||||
.find(amountField)
|
.find(amountField)
|
||||||
.type('1', { delay: 100, force: true });
|
.type('1', { delay: 100, force: true });
|
||||||
|
|
||||||
cy.getByTestId(transferFee)
|
cy.getByTestId(transferFee)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('contain.text', '0.01');
|
.should('contain.text', '0.01');
|
||||||
cy.getByTestId(transferAmount)
|
cy.getByTestId(transferAmount)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('contain.text', '1.00');
|
.should('contain.text', '1.00');
|
||||||
cy.getByTestId(totalTransferfee)
|
cy.getByTestId(totalTransferfee)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('contain.text', '1.01');
|
.should('contain.text', '1.01');
|
||||||
cy.getByTestId(includeTransferFeeRadioBtn).click();
|
cy.getByTestId(includeTransferFeeRadioBtn).click();
|
||||||
cy.getByTestId(transferFee)
|
cy.getByTestId(transferFee)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('contain.text', '0.01');
|
.should('contain.text', '0.01');
|
||||||
cy.getByTestId(transferAmount)
|
cy.getByTestId(transferAmount)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('contain.text', '0.99');
|
.should('contain.text', '0.99');
|
||||||
cy.getByTestId(totalTransferfee)
|
cy.getByTestId(totalTransferfee)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.should('contain.text', '1.00');
|
.should('contain.text', '1.00');
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|||||||
+3
-2
@@ -1,4 +1,3 @@
|
|||||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||||
@@ -13,9 +12,11 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
|||||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
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
|
# Cosmic elevator flags
|
||||||
NX_SUCCESSOR_MARKETS=true
|
NX_SUCCESSOR_MARKETS=true
|
||||||
NX_STOP_ORDERS=true
|
NX_STOP_ORDERS=true
|
||||||
# NX_ICEBERG_ORDERS
|
# NX_ICEBERG_ORDERS
|
||||||
# NX_PRODUCT_PERPETUALS
|
# NX_PRODUCT_PERPETUALS
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1 @@
|
|||||||
|
window._env_ = {};
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 547 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"short_name": "Mainnet Stats",
|
||||||
|
"name": "Vega Mainnet statistics",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
@@ -10,7 +10,6 @@ import { TradeGrid } from './trade-grid';
|
|||||||
import { TradePanels } from './trade-panels';
|
import { TradePanels } from './trade-panels';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { Links, Routes } from '../../pages/client-router';
|
import { Links, Routes } from '../../pages/client-router';
|
||||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
|
||||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||||
|
|
||||||
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||||
@@ -65,8 +64,6 @@ export const MarketPage = () => {
|
|||||||
const update = useGlobalStore((store) => store.update);
|
const update = useGlobalStore((store) => store.update);
|
||||||
const lastMarketId = useGlobalStore((store) => store.marketId);
|
const lastMarketId = useGlobalStore((store) => store.marketId);
|
||||||
|
|
||||||
const onSelect = useMarketClickHandler();
|
|
||||||
|
|
||||||
const { data, error, loading } = useMarket(marketId);
|
const { data, error, loading } = useMarket(marketId);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -87,7 +84,6 @@ export const MarketPage = () => {
|
|||||||
return (
|
return (
|
||||||
<TradeGrid
|
<TradeGrid
|
||||||
market={data}
|
market={data}
|
||||||
onSelect={onSelect}
|
|
||||||
pinnedAsset={
|
pinnedAsset={
|
||||||
data?.tradableInstrument.instrument.product.settlementAsset
|
data?.tradableInstrument.instrument.product.settlementAsset
|
||||||
}
|
}
|
||||||
@@ -97,11 +93,12 @@ export const MarketPage = () => {
|
|||||||
return (
|
return (
|
||||||
<TradePanels
|
<TradePanels
|
||||||
market={data}
|
market={data}
|
||||||
onSelect={onSelect}
|
pinnedAsset={
|
||||||
onClickCollateral={() => navigate('/portfolio')}
|
data?.tradableInstrument.instrument.product.settlementAsset
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [largeScreen, data, onSelect, navigate]);
|
}, [largeScreen, data]);
|
||||||
|
|
||||||
if (!data && marketId) {
|
if (!data && marketId) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { OracleBanner } from '@vegaprotocol/markets';
|
|||||||
import type { Market } from '@vegaprotocol/markets';
|
import type { Market } from '@vegaprotocol/markets';
|
||||||
import { Filter } from '@vegaprotocol/orders';
|
import { Filter } from '@vegaprotocol/orders';
|
||||||
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
|
||||||
import {
|
import {
|
||||||
ResizableGrid,
|
ResizableGrid,
|
||||||
ResizableGridPanel,
|
ResizableGridPanel,
|
||||||
@@ -24,7 +23,6 @@ import { FLAGS } from '@vegaprotocol/environment';
|
|||||||
|
|
||||||
interface TradeGridProps {
|
interface TradeGridProps {
|
||||||
market: Market | null;
|
market: Market | null;
|
||||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,20 +38,19 @@ const MainGrid = memo(
|
|||||||
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
|
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
|
||||||
id: 'middle-1',
|
id: 'middle-1',
|
||||||
});
|
});
|
||||||
const onMarketClick = useMarketClickHandler(true);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
||||||
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
|
<ResizableGridPanel
|
||||||
<ResizableGrid
|
preferredSize={sizes[0]}
|
||||||
proportionalLayout={false}
|
priority={LayoutPriority.High}
|
||||||
minSize={200}
|
minSize={200}
|
||||||
onChange={handleOnMiddleLayoutChange}
|
>
|
||||||
>
|
<ResizableGrid onChange={handleOnMiddleLayoutChange}>
|
||||||
<ResizableGridPanel
|
<ResizableGridPanel
|
||||||
priority={LayoutPriority.High}
|
priority={LayoutPriority.High}
|
||||||
minSize={200}
|
minSize={200}
|
||||||
preferredSize={sizesMiddle[1] || '50%'}
|
preferredSize={sizesMiddle[0] || '75%'}
|
||||||
>
|
>
|
||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs storageKey="console-trade-grid-main-left">
|
<Tabs storageKey="console-trade-grid-main-left">
|
||||||
@@ -74,8 +71,8 @@ const MainGrid = memo(
|
|||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
</ResizableGridPanel>
|
</ResizableGridPanel>
|
||||||
<ResizableGridPanel
|
<ResizableGridPanel
|
||||||
preferredSize={sizesMiddle[2] || 300}
|
|
||||||
minSize={200}
|
minSize={200}
|
||||||
|
preferredSize={sizesMiddle[1] || 300}
|
||||||
>
|
>
|
||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs storageKey="console-trade-grid-main-right">
|
<Tabs storageKey="console-trade-grid-main-right">
|
||||||
@@ -91,37 +88,34 @@ const MainGrid = memo(
|
|||||||
</ResizableGrid>
|
</ResizableGrid>
|
||||||
</ResizableGridPanel>
|
</ResizableGridPanel>
|
||||||
<ResizableGridPanel
|
<ResizableGridPanel
|
||||||
priority={LayoutPriority.Low}
|
|
||||||
preferredSize={sizes[1] || '25%'}
|
preferredSize={sizes[1] || '25%'}
|
||||||
minSize={50}
|
minSize={50}
|
||||||
|
priority={LayoutPriority.Low}
|
||||||
>
|
>
|
||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs storageKey="console-trade-grid-bottom">
|
<Tabs storageKey="console-trade-grid-bottom">
|
||||||
<Tab id="positions" name={t('Positions')}>
|
<Tab id="positions" name={t('Positions')}>
|
||||||
<TradingViews.positions.component
|
<TradingViews.positions.component />
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
/>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="open-orders" name={t('Open')}>
|
<Tab
|
||||||
<TradingViews.orders.component
|
id="open-orders"
|
||||||
marketId={marketId}
|
name={t('Open')}
|
||||||
filter={Filter.Open}
|
menu={<TradingViews.activeOrders.menu marketId={marketId} />}
|
||||||
/>
|
>
|
||||||
|
<TradingViews.orders.component filter={Filter.Open} />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="closed-orders" name={t('Closed')}>
|
<Tab id="closed-orders" name={t('Closed')}>
|
||||||
<TradingViews.orders.component
|
<TradingViews.orders.component filter={Filter.Closed} />
|
||||||
marketId={marketId}
|
|
||||||
filter={Filter.Closed}
|
|
||||||
/>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="rejected-orders" name={t('Rejected')}>
|
<Tab id="rejected-orders" name={t('Rejected')}>
|
||||||
<TradingViews.orders.component
|
<TradingViews.orders.component filter={Filter.Rejected} />
|
||||||
marketId={marketId}
|
|
||||||
filter={Filter.Rejected}
|
|
||||||
/>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="orders" name={t('All')}>
|
<Tab
|
||||||
<TradingViews.orders.component marketId={marketId} />
|
id="orders"
|
||||||
|
name={t('All')}
|
||||||
|
menu={<TradingViews.orders.menu marketId={marketId} />}
|
||||||
|
>
|
||||||
|
<TradingViews.orders.component />
|
||||||
</Tab>
|
</Tab>
|
||||||
{FLAGS.STOP_ORDERS ? (
|
{FLAGS.STOP_ORDERS ? (
|
||||||
<Tab id="stop-orders" name={t('Stop orders')}>
|
<Tab id="stop-orders" name={t('Stop orders')}>
|
||||||
@@ -129,17 +123,14 @@ const MainGrid = memo(
|
|||||||
</Tab>
|
</Tab>
|
||||||
) : null}
|
) : null}
|
||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
<TradingViews.fills.component
|
<TradingViews.fills.component marketId={marketId} />
|
||||||
marketId={marketId}
|
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
/>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="accounts" name={t('Collateral')}>
|
<Tab
|
||||||
<TradingViews.collateral.component
|
id="accounts"
|
||||||
pinnedAsset={pinnedAsset}
|
name={t('Collateral')}
|
||||||
onMarketClick={onMarketClick}
|
menu={<TradingViews.collateral.menu />}
|
||||||
hideButtons
|
>
|
||||||
/>
|
<TradingViews.collateral.component pinnedAsset={pinnedAsset} />
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||||
import type { Market } from '@vegaprotocol/markets';
|
import type { Market } from '@vegaprotocol/markets';
|
||||||
import { OracleBanner } 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 type { TradingView } from './trade-views';
|
||||||
import { TradingViews } from './trade-views';
|
import { TradingViews } from './trade-views';
|
||||||
import { memo, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { NO_MARKET } from './constants';
|
import { NO_MARKET } from './constants';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
@@ -20,33 +16,14 @@ import { FLAGS } from '@vegaprotocol/environment';
|
|||||||
|
|
||||||
interface TradePanelsProps {
|
interface TradePanelsProps {
|
||||||
market: Market | null;
|
market: Market | null;
|
||||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
|
||||||
onMarketClick?: (marketId: string) => void;
|
|
||||||
onOrderTypeClick?: (marketId: string) => void;
|
|
||||||
onClickCollateral: () => void;
|
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TradePanels = ({
|
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||||
market,
|
|
||||||
onSelect,
|
|
||||||
onClickCollateral,
|
|
||||||
pinnedAsset,
|
|
||||||
}: TradePanelsProps) => {
|
|
||||||
const onMarketClick = useMarketClickHandler(true);
|
|
||||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
|
||||||
|
|
||||||
const [view, setView] = useState<TradingView>('candles');
|
const [view, setView] = useState<TradingView>('candles');
|
||||||
|
|
||||||
const renderView = () => {
|
const renderView = () => {
|
||||||
const Component = memo<{
|
const Component = TradingViews[view].component;
|
||||||
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) {
|
if (!Component) {
|
||||||
throw new Error(`No component for view: ${view}`);
|
throw new Error(`No component for view: ${view}`);
|
||||||
@@ -54,16 +31,7 @@ export const TradePanels = ({
|
|||||||
|
|
||||||
if (!market) return <Splash>{NO_MARKET}</Splash>;
|
if (!market) return <Splash>{NO_MARKET}</Splash>;
|
||||||
|
|
||||||
return (
|
return <Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
|
||||||
<Component
|
|
||||||
marketId={market?.id}
|
|
||||||
onSelect={onSelect}
|
|
||||||
onClickCollateral={onClickCollateral}
|
|
||||||
pinnedAsset={pinnedAsset}
|
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderMenu = () => {
|
const renderMenu = () => {
|
||||||
@@ -71,9 +39,10 @@ export const TradePanels = ({
|
|||||||
|
|
||||||
if ('menu' in viewCfg) {
|
if ('menu' in viewCfg) {
|
||||||
const Menu = viewCfg.menu;
|
const Menu = viewCfg.menu;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
|
<div className="flex gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
|
||||||
<Menu />
|
<Menu marketId={market?.id || ''} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
CandlesChartContainer,
|
CandlesChartContainer,
|
||||||
CandlesMenu,
|
CandlesMenu,
|
||||||
} from '@vegaprotocol/candles-chart';
|
} from '@vegaprotocol/candles-chart';
|
||||||
import { Filter } from '@vegaprotocol/orders';
|
import { Filter, OpenOrdersMenu } from '@vegaprotocol/orders';
|
||||||
import { NO_MARKET } from './constants';
|
import { NO_MARKET } from './constants';
|
||||||
import { OrderbookContainer } from '../../components/orderbook-container';
|
import { OrderbookContainer } from '../../components/orderbook-container';
|
||||||
import { FillsContainer } from '../../components/fills-container';
|
import { FillsContainer } from '../../components/fills-container';
|
||||||
@@ -16,6 +16,7 @@ import { LiquidityContainer } from '../../components/liquidity-container';
|
|||||||
import type { OrderContainerProps } from '../../components/orders-container';
|
import type { OrderContainerProps } from '../../components/orders-container';
|
||||||
import { OrdersContainer } from '../../components/orders-container';
|
import { OrdersContainer } from '../../components/orders-container';
|
||||||
import { StopOrdersContainer } from '../../components/stop-orders-container';
|
import { StopOrdersContainer } from '../../components/stop-orders-container';
|
||||||
|
import { AccountsMenu } from '../../components/accounts-menu';
|
||||||
|
|
||||||
type MarketDependantView =
|
type MarketDependantView =
|
||||||
| typeof CandlesChartContainer
|
| typeof CandlesChartContainer
|
||||||
@@ -62,6 +63,7 @@ export const TradingViews = {
|
|||||||
component: (props: OrderContainerProps) => (
|
component: (props: OrderContainerProps) => (
|
||||||
<OrdersContainer {...props} filter={Filter.Open} />
|
<OrdersContainer {...props} filter={Filter.Open} />
|
||||||
),
|
),
|
||||||
|
menu: OpenOrdersMenu,
|
||||||
},
|
},
|
||||||
closedOrders: {
|
closedOrders: {
|
||||||
label: 'Closed',
|
label: 'Closed',
|
||||||
@@ -78,11 +80,16 @@ export const TradingViews = {
|
|||||||
orders: {
|
orders: {
|
||||||
label: 'All',
|
label: 'All',
|
||||||
component: OrdersContainer,
|
component: OrdersContainer,
|
||||||
|
menu: OpenOrdersMenu,
|
||||||
},
|
},
|
||||||
stopOrders: {
|
stopOrders: {
|
||||||
label: 'Stop',
|
label: 'Stop',
|
||||||
component: StopOrdersContainer,
|
component: StopOrdersContainer,
|
||||||
},
|
},
|
||||||
collateral: { label: 'Collateral', component: AccountsContainer },
|
collateral: {
|
||||||
|
label: 'Collateral',
|
||||||
|
component: AccountsContainer,
|
||||||
|
menu: AccountsMenu,
|
||||||
|
},
|
||||||
fills: { label: 'Fills', component: FillsContainer },
|
fills: { label: 'Fills', component: FillsContainer },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -411,24 +411,26 @@ describe('Closed', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
render(
|
await act(() => {
|
||||||
<MemoryRouter>
|
render(
|
||||||
<MockedProvider
|
<MemoryRouter>
|
||||||
mocks={[
|
<MockedProvider
|
||||||
mixedMarketsMock,
|
mocks={[
|
||||||
marketsDataMock,
|
mixedMarketsMock,
|
||||||
oracleDataMock,
|
marketsDataMock,
|
||||||
successorMarketsMock,
|
oracleDataMock,
|
||||||
]}
|
successorMarketsMock,
|
||||||
>
|
]}
|
||||||
<VegaWalletContext.Provider
|
|
||||||
value={{ pubKey } as VegaWalletContextShape}
|
|
||||||
>
|
>
|
||||||
<Closed />
|
<VegaWalletContext.Provider
|
||||||
</VegaWalletContext.Provider>
|
value={{ pubKey } as VegaWalletContextShape}
|
||||||
</MockedProvider>
|
>
|
||||||
</MemoryRouter>
|
<Closed />
|
||||||
);
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
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,12 +6,11 @@ import { t } from '@vegaprotocol/i18n';
|
|||||||
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
|
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
|
||||||
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
||||||
import { usePageTitleStore } from '../../stores';
|
import { usePageTitleStore } from '../../stores';
|
||||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
|
||||||
import { AccountsContainer } from '../../components/accounts-container';
|
import { AccountsContainer } from '../../components/accounts-container';
|
||||||
import { DepositsContainer } from './deposits-container';
|
import { DepositsContainer } from '../../components/deposits-container';
|
||||||
import { FillsContainer } from '../../components/fills-container';
|
import { FillsContainer } from '../../components/fills-container';
|
||||||
import { PositionsContainer } from '../../components/positions-container';
|
import { PositionsContainer } from '../../components/positions-container';
|
||||||
import { WithdrawalsContainer } from './withdrawals-container';
|
import { WithdrawalsContainer } from '../../components/withdrawals-container';
|
||||||
import { OrdersContainer } from '../../components/orders-container';
|
import { OrdersContainer } from '../../components/orders-container';
|
||||||
import { LedgerContainer } from '../../components/ledger-container';
|
import { LedgerContainer } from '../../components/ledger-container';
|
||||||
import { AccountHistoryContainer } from './account-history-container';
|
import { AccountHistoryContainer } from './account-history-container';
|
||||||
@@ -21,6 +20,9 @@ import {
|
|||||||
usePaneLayout,
|
usePaneLayout,
|
||||||
} from '../../components/resizable-grid';
|
} from '../../components/resizable-grid';
|
||||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
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 WithdrawalsIndicator = () => {
|
||||||
const { ready } = useIncompleteWithdrawals();
|
const { ready } = useIncompleteWithdrawals();
|
||||||
@@ -51,7 +53,6 @@ export const Portfolio = () => {
|
|||||||
}
|
}
|
||||||
}, [init, view, setView]);
|
}, [init, view, setView]);
|
||||||
|
|
||||||
const onMarketClick = useMarketClickHandler(true);
|
|
||||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
||||||
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
|
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
|
||||||
return (
|
return (
|
||||||
@@ -64,13 +65,13 @@ export const Portfolio = () => {
|
|||||||
<AccountHistoryContainer />
|
<AccountHistoryContainer />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="positions" name={t('Positions')}>
|
<Tab id="positions" name={t('Positions')}>
|
||||||
<PositionsContainer onMarketClick={onMarketClick} allKeys />
|
<PositionsContainer allKeys />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="orders" name={t('Orders')}>
|
<Tab id="orders" name={t('Orders')}>
|
||||||
<OrdersContainer />
|
<OrdersContainer />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
<FillsContainer onMarketClick={onMarketClick} />
|
<FillsContainer />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
||||||
<LedgerContainer />
|
<LedgerContainer />
|
||||||
@@ -85,16 +86,21 @@ export const Portfolio = () => {
|
|||||||
>
|
>
|
||||||
<PortfolioGridChild>
|
<PortfolioGridChild>
|
||||||
<Tabs storageKey="console-portfolio-bottom">
|
<Tabs storageKey="console-portfolio-bottom">
|
||||||
<Tab id="collateral" name={t('Collateral')}>
|
<Tab
|
||||||
|
id="collateral"
|
||||||
|
name={t('Collateral')}
|
||||||
|
menu={<AccountsMenu />}
|
||||||
|
>
|
||||||
<AccountsContainer />
|
<AccountsContainer />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="deposits" name={t('Deposits')}>
|
<Tab id="deposits" name={t('Deposits')} menu={<DepositsMenu />}>
|
||||||
<DepositsContainer />
|
<DepositsContainer />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab
|
<Tab
|
||||||
id="withdrawals"
|
id="withdrawals"
|
||||||
name={t('Withdrawals')}
|
name={t('Withdrawals')}
|
||||||
indicator={<WithdrawalsIndicator />}
|
indicator={<WithdrawalsIndicator />}
|
||||||
|
menu={<WithdrawalsMenu />}
|
||||||
>
|
>
|
||||||
<WithdrawalsContainer />
|
<WithdrawalsContainer />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
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,5 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
@@ -12,16 +11,14 @@ import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
|||||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
import { ViewType, useSidebar } from '../sidebar';
|
import { ViewType, useSidebar } from '../sidebar';
|
||||||
|
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||||
|
|
||||||
export const AccountsContainer = ({
|
export const AccountsContainer = ({
|
||||||
pinnedAsset,
|
pinnedAsset,
|
||||||
hideButtons,
|
|
||||||
onMarketClick,
|
|
||||||
}: {
|
}: {
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
hideButtons?: boolean;
|
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
const { pubKey, isReadOnly } = useVegaWallet();
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||||
const setView = useSidebar((store) => store.setView);
|
const setView = useSidebar((store) => store.setView);
|
||||||
@@ -48,44 +45,23 @@ export const AccountsContainer = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full relative">
|
<AccountManager
|
||||||
<AccountManager
|
partyId={pubKey}
|
||||||
partyId={pubKey}
|
onClickAsset={onClickAsset}
|
||||||
onClickAsset={onClickAsset}
|
onClickWithdraw={(assetId) => {
|
||||||
onClickWithdraw={(assetId) => {
|
setView({ type: ViewType.Withdraw, assetId });
|
||||||
setView({ type: ViewType.Withdraw, assetId });
|
}}
|
||||||
}}
|
onClickDeposit={(assetId) => {
|
||||||
onClickDeposit={(assetId) => {
|
setView({ type: ViewType.Deposit, assetId });
|
||||||
setView({ type: ViewType.Deposit, assetId });
|
}}
|
||||||
}}
|
onClickTransfer={(assetId) => {
|
||||||
onClickTransfer={(assetId) => {
|
setView({ type: ViewType.Transfer, assetId });
|
||||||
setView({ type: ViewType.Transfer, assetId });
|
}}
|
||||||
}}
|
onMarketClick={onMarketClick}
|
||||||
onMarketClick={onMarketClick}
|
isReadOnly={isReadOnly}
|
||||||
isReadOnly={isReadOnly}
|
pinnedAsset={pinnedAsset}
|
||||||
pinnedAsset={pinnedAsset}
|
gridProps={gridStoreCallbacks}
|
||||||
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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './accounts-menu';
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './deposits-container';
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './deposits-menu';
|
||||||
@@ -7,14 +7,10 @@ import { t } from '@vegaprotocol/i18n';
|
|||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||||
|
|
||||||
export const FillsContainer = ({
|
export const FillsContainer = ({ marketId }: { marketId?: string }) => {
|
||||||
marketId,
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
onMarketClick,
|
|
||||||
}: {
|
|
||||||
marketId?: string;
|
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
|
||||||
}) => {
|
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
|
|
||||||
const gridStore = useFillsStore((store) => store.gridStore);
|
const gridStore = useFillsStore((store) => store.gridStore);
|
||||||
|
|||||||
@@ -134,6 +134,20 @@ describe('MarketSelector', () => {
|
|||||||
error: undefined,
|
error: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Button "All" should be selected by default', () => {
|
||||||
|
const buttons = ['All', 'Futures', 'Spot', 'Perpetuals'];
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<MarketSelector currentMarketId="market-0" onSelect={jest.fn()} />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
screen
|
||||||
|
.getAllByTestId(/^product-(All|Future|Spot|Perpetual)$/)
|
||||||
|
.forEach((elem, i) => {
|
||||||
|
expect(elem.textContent).toEqual(buttons[i]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('renders only active markets', () => {
|
it('renders only active markets', () => {
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
@@ -169,6 +183,12 @@ describe('MarketSelector', () => {
|
|||||||
activeMarkets.length
|
activeMarkets.length
|
||||||
);
|
);
|
||||||
expect(screen.queryByTestId('no-items')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('no-items')).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByTestId('product-All'));
|
||||||
|
expect(screen.queryAllByTestId(/market-\d/)).toHaveLength(
|
||||||
|
activeMarkets.length
|
||||||
|
);
|
||||||
|
expect(screen.queryByTestId('no-items')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('filters by search term', async () => {
|
it('filters by search term', async () => {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const MarketSelector = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [filter, setFilter] = useState<Filter>({
|
const [filter, setFilter] = useState<Filter>({
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
product: Product.Future,
|
product: Product.All,
|
||||||
sort: Sort.None,
|
sort: Sort.None,
|
||||||
assets: [],
|
assets: [],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
|||||||
|
|
||||||
// Make sure these match the available __typename properties on product
|
// Make sure these match the available __typename properties on product
|
||||||
export const Product = {
|
export const Product = {
|
||||||
|
All: 'All',
|
||||||
Future: 'Future',
|
Future: 'Future',
|
||||||
Spot: 'Spot',
|
Spot: 'Spot',
|
||||||
Perpetual: 'Perpetual',
|
Perpetual: 'Perpetual',
|
||||||
@@ -16,6 +17,7 @@ export type ProductType = keyof typeof Product;
|
|||||||
const ProductTypeMapping: {
|
const ProductTypeMapping: {
|
||||||
[key in ProductType]: string;
|
[key in ProductType]: string;
|
||||||
} = {
|
} = {
|
||||||
|
[Product.All]: 'All',
|
||||||
[Product.Future]: 'Futures',
|
[Product.Future]: 'Futures',
|
||||||
[Product.Spot]: 'Spot',
|
[Product.Spot]: 'Spot',
|
||||||
[Product.Perpetual]: 'Perpetuals',
|
[Product.Perpetual]: 'Perpetuals',
|
||||||
@@ -49,8 +51,12 @@ export const ProductSelector = ({
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<Link to={Routes.MARKETS} className="flex items-center gap-2 ml-auto">
|
<Link
|
||||||
<span className="underline underline-offset-4">{t('All markets')}</span>
|
to={Routes.MARKETS}
|
||||||
|
className="flex items-center gap-2 ml-auto"
|
||||||
|
title={t('See all markets')}
|
||||||
|
>
|
||||||
|
<span className="underline underline-offset-4">{t('Browse')}</span>
|
||||||
<VegaIcon name={VegaIconNames.ARROW_RIGHT} />
|
<VegaIcon name={VegaIconNames.ARROW_RIGHT} />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -120,6 +120,13 @@ describe('useMarketSelectorList', () => {
|
|||||||
assets: [],
|
assets: [],
|
||||||
});
|
});
|
||||||
expect(result.current.markets).toEqual([markets[2]]);
|
expect(result.current.markets).toEqual([markets[2]]);
|
||||||
|
rerender({
|
||||||
|
searchTerm: '',
|
||||||
|
product: Product.All,
|
||||||
|
sort: Sort.None,
|
||||||
|
assets: [],
|
||||||
|
});
|
||||||
|
expect(result.current.markets).toEqual(markets);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('filters by asset', () => {
|
it('filters by asset', () => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets';
|
|||||||
import { priceChangePercentage } from '@vegaprotocol/utils';
|
import { priceChangePercentage } from '@vegaprotocol/utils';
|
||||||
import type { Filter } from '../../components/market-selector/market-selector';
|
import type { Filter } from '../../components/market-selector/market-selector';
|
||||||
import { Sort } from './sort-dropdown';
|
import { Sort } from './sort-dropdown';
|
||||||
|
import { Product } from './product-selector';
|
||||||
|
|
||||||
// Used for sort order and filter
|
// Used for sort order and filter
|
||||||
const MARKET_TEMPLATE = [
|
const MARKET_TEMPLATE = [
|
||||||
@@ -28,7 +29,10 @@ export const useMarketSelectorList = ({
|
|||||||
.filter((m) => isMarketActive(m.state))
|
.filter((m) => isMarketActive(m.state))
|
||||||
// only selected product type
|
// only selected product type
|
||||||
.filter((m) => {
|
.filter((m) => {
|
||||||
if (m.tradableInstrument.instrument.product.__typename === product) {
|
if (
|
||||||
|
product === Product.All ||
|
||||||
|
m.tradableInstrument.instrument.product.__typename === product
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,27 +1,15 @@
|
|||||||
import { OrderbookManager } from '@vegaprotocol/market-depth';
|
import { OrderbookManager } from '@vegaprotocol/market-depth';
|
||||||
import { useCreateOrderStore } from '@vegaprotocol/orders';
|
|
||||||
import { ViewType, useSidebar } from '../sidebar';
|
import { ViewType, useSidebar } from '../sidebar';
|
||||||
import { useStopOrderFormValues } from '@vegaprotocol/deal-ticket';
|
import { useDealTicketFormValues } from '@vegaprotocol/deal-ticket';
|
||||||
|
|
||||||
export const OrderbookContainer = ({ marketId }: { marketId: string }) => {
|
export const OrderbookContainer = ({ marketId }: { marketId: string }) => {
|
||||||
const useOrderStoreRef = useCreateOrderStore();
|
const update = useDealTicketFormValues((state) => state.updateAll);
|
||||||
const updateOrder = useOrderStoreRef((store) => store.update);
|
|
||||||
const updateStoredFormValues = useStopOrderFormValues(
|
|
||||||
(state) => state.update
|
|
||||||
);
|
|
||||||
const setView = useSidebar((store) => store.setView);
|
const setView = useSidebar((store) => store.setView);
|
||||||
return (
|
return (
|
||||||
<OrderbookManager
|
<OrderbookManager
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onClick={({ price, size }) => {
|
onClick={(values) => {
|
||||||
if (price) {
|
update(marketId, values);
|
||||||
updateOrder(marketId, { price });
|
|
||||||
updateStoredFormValues(marketId, { price });
|
|
||||||
}
|
|
||||||
if (size) {
|
|
||||||
updateOrder(marketId, { size });
|
|
||||||
updateStoredFormValues(marketId, { size });
|
|
||||||
}
|
|
||||||
setView({ type: ViewType.Order });
|
setView({ type: ViewType.Order });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -25,11 +25,10 @@ export const FilterStatusValue = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface OrderContainerProps {
|
export interface OrderContainerProps {
|
||||||
marketId?: string;
|
|
||||||
filter?: Filter;
|
filter?: Filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrdersContainer = ({ marketId, filter }: OrderContainerProps) => {
|
export const OrdersContainer = ({ filter }: OrderContainerProps) => {
|
||||||
const { pubKey, isReadOnly } = useVegaWallet();
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
const onMarketClick = useMarketClickHandler(true);
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
||||||
@@ -45,7 +44,6 @@ export const OrdersContainer = ({ marketId, filter }: OrderContainerProps) => {
|
|||||||
return (
|
return (
|
||||||
<OrderListManager
|
<OrderListManager
|
||||||
partyId={pubKey}
|
partyId={pubKey}
|
||||||
marketId={marketId}
|
|
||||||
filter={filter}
|
filter={filter}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
|||||||
@@ -7,14 +7,10 @@ import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
|||||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
|
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||||
|
|
||||||
export const PositionsContainer = ({
|
export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
|
||||||
onMarketClick,
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
allKeys,
|
|
||||||
}: {
|
|
||||||
onMarketClick?: (marketId: string) => void;
|
|
||||||
allKeys?: boolean;
|
|
||||||
}) => {
|
|
||||||
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
|
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
|
||||||
|
|
||||||
const gridStore = usePositionsStore((store) => store.gridStore);
|
const gridStore = usePositionsStore((store) => store.gridStore);
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
|
import { GetStarted } from './get-started';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
|
||||||
|
describe('GetStarted', () => {
|
||||||
|
const renderComponent = (context: Partial<VegaWalletContextShape> = {}) => {
|
||||||
|
return render(
|
||||||
|
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||||
|
<GetStarted />
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('renders full get started content if not connected and no browser wallet detected', () => {
|
||||||
|
renderComponent();
|
||||||
|
expect(screen.getByTestId('get-started-banner')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders connect prompt if no pubKey but wallet installed', () => {
|
||||||
|
globalThis.window.vega = {} as Vega;
|
||||||
|
renderComponent();
|
||||||
|
expect(screen.getByTestId('order-connect-wallet')).toBeInTheDocument();
|
||||||
|
globalThis.window.vega = undefined as unknown as Vega;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders nothing if connected', () => {
|
||||||
|
const { container } = renderComponent({ pubKey: 'my-pubkey' });
|
||||||
|
expect(container).toBeEmptyDOMElement();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -16,7 +16,6 @@ interface Props {
|
|||||||
|
|
||||||
export const GetStarted = ({ lead }: Props) => {
|
export const GetStarted = ({ lead }: Props) => {
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
|
|
||||||
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||||
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
|
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ export const GetStarted = ({ lead }: Props) => {
|
|||||||
{ 'mt-8': !lead }
|
{ 'mt-8': !lead }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isBrowserWalletInstalled()) {
|
if (!pubKey && !isBrowserWalletInstalled()) {
|
||||||
return (
|
return (
|
||||||
<div className={wrapperClasses} data-testid="get-started-banner">
|
<div className={wrapperClasses} data-testid="get-started-banner">
|
||||||
{lead && <h2>{lead}</h2>}
|
{lead && <h2>{lead}</h2>}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './withdrawals-container';
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './withdrawals-menu';
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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 {
|
import {
|
||||||
RestConnector,
|
|
||||||
JsonRpcConnector,
|
JsonRpcConnector,
|
||||||
ViewConnector,
|
ViewConnector,
|
||||||
InjectedConnector,
|
InjectedConnector,
|
||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
export const rest = new RestConnector();
|
|
||||||
export const jsonRpc = new JsonRpcConnector();
|
export const jsonRpc = new JsonRpcConnector();
|
||||||
export const injected = new InjectedConnector();
|
export const injected = new InjectedConnector();
|
||||||
|
|
||||||
@@ -19,7 +17,6 @@ if (typeof window !== 'undefined') {
|
|||||||
|
|
||||||
export const Connectors = {
|
export const Connectors = {
|
||||||
injected,
|
injected,
|
||||||
rest,
|
|
||||||
jsonRpc,
|
jsonRpc,
|
||||||
view,
|
view,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Head from 'next/head';
|
||||||
import { ClientRouter } from './client-router';
|
import { ClientRouter } from './client-router';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6,5 +7,30 @@ import { ClientRouter } from './client-router';
|
|||||||
* have to serve a static site via next export
|
* have to serve a static site via next export
|
||||||
*/
|
*/
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
return <ClientRouter />;
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<meta charSet="utf-8" />
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/x-icon"
|
||||||
|
href="https://static.vega.xyz/favicon.ico"
|
||||||
|
/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description" content="Vega Protocol - Console" />
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png" />
|
||||||
|
<link rel="manifest" href="assets/manifest.json" />
|
||||||
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
||||||
|
as="font"
|
||||||
|
type="font/woff2"
|
||||||
|
/>
|
||||||
|
<title>VEGA Console dApp</title>
|
||||||
|
</Head>
|
||||||
|
<ClientRouter />
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ module.exports = {
|
|||||||
'scope-enum': async (ctx) => [
|
'scope-enum': async (ctx) => [
|
||||||
2,
|
2,
|
||||||
'always',
|
'always',
|
||||||
['ci', 'docs', ...(await utils.getProjects(ctx))],
|
['ci', 'docs', 'specs', ...(await utils.getProjects(ctx))],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ const singleRow = {
|
|||||||
instrument: {
|
instrument: {
|
||||||
__typename: 'Instrument',
|
__typename: 'Instrument',
|
||||||
name: 'BTCUSD Monthly (30 Jun 2022)',
|
name: 'BTCUSD Monthly (30 Jun 2022)',
|
||||||
|
code: 'BTCUSD.MF21',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
|
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
|
||||||
@@ -57,7 +58,7 @@ describe('BreakdownTable', () => {
|
|||||||
});
|
});
|
||||||
const cells = await screen.findAllByRole('gridcell');
|
const cells = await screen.findAllByRole('gridcell');
|
||||||
const expectedValues = [
|
const expectedValues = [
|
||||||
'BTCUSD Monthly (30 Jun 2022)',
|
'BTCUSD.MF21',
|
||||||
'Margin',
|
'Margin',
|
||||||
'1,256.00 (50%)',
|
'1,256.00 (50%)',
|
||||||
'1,256.00',
|
'1,256.00',
|
||||||
@@ -118,6 +119,7 @@ describe('BreakdownTable', () => {
|
|||||||
instrument: {
|
instrument: {
|
||||||
__typename: 'Instrument',
|
__typename: 'Instrument',
|
||||||
name: 'BTCUSD Monthly (30 Jun 2022)',
|
name: 'BTCUSD Monthly (30 Jun 2022)',
|
||||||
|
code: 'BTCUSD.MF21',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
|||||||
const defs: ColDef[] = [
|
const defs: ColDef[] = [
|
||||||
{
|
{
|
||||||
headerName: t('Market'),
|
headerName: t('Market'),
|
||||||
field: 'market.tradableInstrument.instrument.name',
|
field: 'market.tradableInstrument.instrument.code',
|
||||||
valueFormatter: ({
|
valueFormatter: ({
|
||||||
value,
|
value,
|
||||||
}: VegaValueFormatterParams<
|
}: VegaValueFormatterParams<
|
||||||
AccountFields,
|
AccountFields,
|
||||||
'market.tradableInstrument.instrument.name'
|
'market.tradableInstrument.instrument.code'
|
||||||
>) => {
|
>) => {
|
||||||
if (!value) return 'None';
|
if (!value) return 'None';
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ export const assetProvider = makeDataProvider<
|
|||||||
getData,
|
getData,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useAssetDataProvider = (assetId: string) => {
|
export const useAssetDataProvider = (assetId: string, skip?: boolean) => {
|
||||||
return useDataProvider({
|
return useDataProvider({
|
||||||
dataProvider: assetProvider,
|
dataProvider: assetProvider,
|
||||||
variables: { assetId: assetId || '' },
|
variables: { assetId: assetId || '' },
|
||||||
skip: !assetId,
|
skip: !assetId || skip,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,14 +10,11 @@ declare global {
|
|||||||
|
|
||||||
export const addConnectPublicKey = () => {
|
export const addConnectPublicKey = () => {
|
||||||
Cypress.Commands.add('connectPublicKey', (publicKey) => {
|
Cypress.Commands.add('connectPublicKey', (publicKey) => {
|
||||||
const connectVegaWaletBtn = Cypress.$(
|
const connectVegaWaletBtn = Cypress.$(`[data-testid="view-as-user"]`);
|
||||||
`[data-testid="connect-vega-wallet"]`
|
|
||||||
);
|
|
||||||
if (connectVegaWaletBtn.length > 0) {
|
if (connectVegaWaletBtn.length > 0) {
|
||||||
cy.get('aside [data-testid="connect-vega-wallet"]').click();
|
cy.get('aside button').contains('View as party').click();
|
||||||
cy.getByTestId('connector-view').should('be.visible').click();
|
cy.getByTestId('address').should('be.visible').focus();
|
||||||
cy.getByTestId('address').click();
|
cy.getByTestId('address').type(publicKey, { delay: 50 });
|
||||||
cy.getByTestId('address').type(publicKey);
|
|
||||||
cy.getByTestId('connect').click();
|
cy.getByTestId('connect').click();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const mockConnectWallet = () => {
|
|||||||
|
|
||||||
export const mockConnectWalletWithUserError = () => {
|
export const mockConnectWalletWithUserError = () => {
|
||||||
cy.mockWallet((req) => {
|
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 = (
|
export const aliasWalletConnectWithUserError = (
|
||||||
req: CyHttpMessages.IncomingHttpRequest
|
req: CyHttpMessages.IncomingHttpRequest,
|
||||||
|
token: string
|
||||||
) => {
|
) => {
|
||||||
if (hasMethod(req, 'client.connect_wallet')) {
|
if (hasMethod(req, 'client.connect_wallet')) {
|
||||||
req.alias = '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, Link } from '@vegaprotocol/ui-toolkit';
|
import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
interface ZeroBalanceErrorProps {
|
interface ZeroBalanceErrorProps {
|
||||||
@@ -6,13 +6,11 @@ interface ZeroBalanceErrorProps {
|
|||||||
id: string;
|
id: string;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
};
|
};
|
||||||
onClickCollateral?: () => void;
|
|
||||||
onDeposit: (assetId: string) => void;
|
onDeposit: (assetId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ZeroBalanceError = ({
|
export const ZeroBalanceError = ({
|
||||||
asset,
|
asset,
|
||||||
onClickCollateral,
|
|
||||||
onDeposit,
|
onDeposit,
|
||||||
}: ZeroBalanceErrorProps) => {
|
}: ZeroBalanceErrorProps) => {
|
||||||
return (
|
return (
|
||||||
@@ -25,12 +23,6 @@ export const ZeroBalanceError = ({
|
|||||||
'You need %s in your wallet to trade in this market. ',
|
'You need %s in your wallet to trade in this market. ',
|
||||||
asset.symbol
|
asset.symbol
|
||||||
)}
|
)}
|
||||||
{onClickCollateral && (
|
|
||||||
<>
|
|
||||||
{t('See all your')}{' '}
|
|
||||||
<Link onClick={onClickCollateral}>collateral</Link>.
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
buttonProps={{
|
buttonProps={{
|
||||||
|
|||||||
@@ -3,29 +3,25 @@ import type { Market, StaticMarketData } from '@vegaprotocol/markets';
|
|||||||
import { DealTicketMarketAmount } from './deal-ticket-market-amount';
|
import { DealTicketMarketAmount } from './deal-ticket-market-amount';
|
||||||
import { DealTicketLimitAmount } from './deal-ticket-limit-amount';
|
import { DealTicketLimitAmount } from './deal-ticket-limit-amount';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import type { OrderObj } from '@vegaprotocol/orders';
|
import type { OrderFormValues } from '../../hooks/use-form-values';
|
||||||
import type { OrderFormFields } from '../../hooks/use-order-form';
|
|
||||||
|
|
||||||
export interface DealTicketAmountProps {
|
export interface DealTicketAmountProps {
|
||||||
control: Control<OrderFormFields>;
|
control: Control<OrderFormValues>;
|
||||||
orderType: Schema.OrderType;
|
type: Schema.OrderType;
|
||||||
marketData: StaticMarketData;
|
marketData: StaticMarketData;
|
||||||
marketPrice?: string;
|
marketPrice?: string;
|
||||||
market: Market;
|
market: Market;
|
||||||
sizeError?: string;
|
sizeError?: string;
|
||||||
priceError?: string;
|
priceError?: string;
|
||||||
update: (obj: Partial<OrderObj>) => void;
|
|
||||||
size: string;
|
|
||||||
price?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DealTicketAmount = ({
|
export const DealTicketAmount = ({
|
||||||
orderType,
|
type,
|
||||||
marketData,
|
marketData,
|
||||||
marketPrice,
|
marketPrice,
|
||||||
...props
|
...props
|
||||||
}: DealTicketAmountProps) => {
|
}: DealTicketAmountProps) => {
|
||||||
switch (orderType) {
|
switch (type) {
|
||||||
case Schema.OrderType.TYPE_MARKET:
|
case Schema.OrderType.TYPE_MARKET:
|
||||||
return (
|
return (
|
||||||
<DealTicketMarketAmount
|
<DealTicketMarketAmount
|
||||||
@@ -37,7 +33,7 @@ export const DealTicketAmount = ({
|
|||||||
case Schema.OrderType.TYPE_LIMIT:
|
case Schema.OrderType.TYPE_LIMIT:
|
||||||
return <DealTicketLimitAmount {...props} />;
|
return <DealTicketLimitAmount {...props} />;
|
||||||
default: {
|
default: {
|
||||||
throw new Error('Invalid ticket type');
|
throw new Error('Invalid ticket type ' + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
|
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
DealTicketType,
|
isStopOrderType,
|
||||||
useDealTicketTypeStore,
|
useDealTicketFormValues,
|
||||||
} from '../../hooks/use-type-store';
|
} from '../../hooks/use-form-values';
|
||||||
import { StopOrder } from './deal-ticket-stop-order';
|
import { StopOrder } from './deal-ticket-stop-order';
|
||||||
import {
|
import {
|
||||||
useStaticMarketData,
|
useStaticMarketData,
|
||||||
@@ -17,7 +17,6 @@ import { FLAGS } from '@vegaprotocol/environment';
|
|||||||
interface DealTicketContainerProps {
|
interface DealTicketContainerProps {
|
||||||
marketId: string;
|
marketId: string;
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
onClickCollateral?: () => void;
|
|
||||||
onDeposit: (assetId: string) => void;
|
onDeposit: (assetId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +24,9 @@ export const DealTicketContainer = ({
|
|||||||
marketId,
|
marketId,
|
||||||
...props
|
...props
|
||||||
}: DealTicketContainerProps) => {
|
}: DealTicketContainerProps) => {
|
||||||
const type = useDealTicketTypeStore((state) => state.type[marketId]);
|
const showStopOrder = useDealTicketFormValues((state) =>
|
||||||
|
isStopOrderType(state.formValues[marketId]?.type)
|
||||||
|
);
|
||||||
const {
|
const {
|
||||||
data: market,
|
data: market,
|
||||||
error: marketError,
|
error: marketError,
|
||||||
@@ -48,9 +49,7 @@ export const DealTicketContainer = ({
|
|||||||
reload={reload}
|
reload={reload}
|
||||||
>
|
>
|
||||||
{market && marketData ? (
|
{market && marketData ? (
|
||||||
FLAGS.STOP_ORDERS &&
|
FLAGS.STOP_ORDERS && showStopOrder ? (
|
||||||
(type === DealTicketType.StopLimit ||
|
|
||||||
type === DealTicketType.StopMarket) ? (
|
|
||||||
<StopOrder
|
<StopOrder
|
||||||
market={market}
|
market={market}
|
||||||
marketPrice={marketPrice}
|
marketPrice={marketPrice}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import type { DealTicketAmountProps } from './deal-ticket-amount';
|
|||||||
import { Controller } from 'react-hook-form';
|
import { Controller } from 'react-hook-form';
|
||||||
|
|
||||||
export type DealTicketLimitAmountProps = Omit<
|
export type DealTicketLimitAmountProps = Omit<
|
||||||
Omit<DealTicketAmountProps, 'marketData'>,
|
DealTicketAmountProps,
|
||||||
'orderType'
|
'marketData' | 'type'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const DealTicketLimitAmount = ({
|
export const DealTicketLimitAmount = ({
|
||||||
@@ -14,9 +14,6 @@ export const DealTicketLimitAmount = ({
|
|||||||
market,
|
market,
|
||||||
sizeError,
|
sizeError,
|
||||||
priceError,
|
priceError,
|
||||||
update,
|
|
||||||
price,
|
|
||||||
size,
|
|
||||||
}: DealTicketLimitAmountProps) => {
|
}: DealTicketLimitAmountProps) => {
|
||||||
const priceStep = toDecimal(market?.decimalPlaces);
|
const priceStep = toDecimal(market?.decimalPlaces);
|
||||||
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
||||||
@@ -62,17 +59,16 @@ export const DealTicketLimitAmount = ({
|
|||||||
},
|
},
|
||||||
validate: validateAmount(sizeStep, 'Size'),
|
validate: validateAmount(sizeStep, 'Size'),
|
||||||
}}
|
}}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<Input
|
<Input
|
||||||
id="input-order-size-limit"
|
id="input-order-size-limit"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
type="number"
|
type="number"
|
||||||
value={size}
|
|
||||||
onChange={(e) => update({ size: e.target.value })}
|
|
||||||
step={sizeStep}
|
step={sizeStep}
|
||||||
min={sizeStep}
|
min={sizeStep}
|
||||||
data-testid="order-size"
|
data-testid="order-size"
|
||||||
onWheel={(e) => e.currentTarget.blur()}
|
onWheel={(e) => e.currentTarget.blur()}
|
||||||
|
{...field}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -95,19 +91,17 @@ export const DealTicketLimitAmount = ({
|
|||||||
value: priceStep,
|
value: priceStep,
|
||||||
message: t('Price cannot be lower than ' + priceStep),
|
message: t('Price cannot be lower than ' + priceStep),
|
||||||
},
|
},
|
||||||
// @ts-ignore this fulfills the interface but still errors
|
|
||||||
validate: validateAmount(priceStep, 'Price'),
|
validate: validateAmount(priceStep, 'Price'),
|
||||||
}}
|
}}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<Input
|
<Input
|
||||||
id="input-price-quote"
|
id="input-price-quote"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
type="number"
|
type="number"
|
||||||
value={price}
|
|
||||||
onChange={(e) => update({ price: e.target.value })}
|
|
||||||
step={priceStep}
|
step={priceStep}
|
||||||
data-testid="order-price"
|
data-testid="order-price"
|
||||||
onWheel={(e) => e.currentTarget.blur()}
|
onWheel={(e) => e.currentTarget.blur()}
|
||||||
|
{...field}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -10,10 +10,7 @@ import type { DealTicketAmountProps } from './deal-ticket-amount';
|
|||||||
import { Controller } from 'react-hook-form';
|
import { Controller } from 'react-hook-form';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export type DealTicketMarketAmountProps = Omit<
|
export type DealTicketMarketAmountProps = Omit<DealTicketAmountProps, 'type'>;
|
||||||
DealTicketAmountProps,
|
|
||||||
'orderType'
|
|
||||||
>;
|
|
||||||
|
|
||||||
export const DealTicketMarketAmount = ({
|
export const DealTicketMarketAmount = ({
|
||||||
control,
|
control,
|
||||||
@@ -21,8 +18,6 @@ export const DealTicketMarketAmount = ({
|
|||||||
marketData,
|
marketData,
|
||||||
marketPrice,
|
marketPrice,
|
||||||
sizeError,
|
sizeError,
|
||||||
update,
|
|
||||||
size,
|
|
||||||
}: DealTicketMarketAmountProps) => {
|
}: DealTicketMarketAmountProps) => {
|
||||||
const quoteName = market.tradableInstrument.instrument.product.quoteName;
|
const quoteName = market.tradableInstrument.instrument.product.quoteName;
|
||||||
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
||||||
@@ -50,17 +45,16 @@ export const DealTicketMarketAmount = ({
|
|||||||
},
|
},
|
||||||
validate: validateAmount(sizeStep, 'Size'),
|
validate: validateAmount(sizeStep, 'Size'),
|
||||||
}}
|
}}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<Input
|
<Input
|
||||||
id="input-order-size-market"
|
id="input-order-size-market"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
type="number"
|
type="number"
|
||||||
value={size}
|
|
||||||
onChange={(e) => update({ size: e.target.value })}
|
|
||||||
step={sizeStep}
|
step={sizeStep}
|
||||||
min={sizeStep}
|
min={sizeStep}
|
||||||
onWheel={(e) => e.currentTarget.blur()}
|
onWheel={(e) => e.currentTarget.blur()}
|
||||||
data-testid="order-size"
|
data-testid="order-size"
|
||||||
|
{...field}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Controller, type Control } from 'react-hook-form';
|
import { Controller, type Control } from 'react-hook-form';
|
||||||
import type { Market } from '@vegaprotocol/markets';
|
import type { Market } from '@vegaprotocol/markets';
|
||||||
import type { OrderObj } from '@vegaprotocol/orders';
|
import type { OrderFormValues } from '../../hooks/use-form-values';
|
||||||
import type { OrderFormFields } from '../../hooks/use-order-form';
|
|
||||||
import { toDecimal, validateAmount } from '@vegaprotocol/utils';
|
import { toDecimal, validateAmount } from '@vegaprotocol/utils';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import {
|
import {
|
||||||
@@ -12,25 +11,21 @@ import {
|
|||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
export interface DealTicketSizeIcebergProps {
|
export interface DealTicketSizeIcebergProps {
|
||||||
control: Control<OrderFormFields>;
|
control: Control<OrderFormValues>;
|
||||||
market: Market;
|
market: Market;
|
||||||
peakSizeError?: string;
|
peakSizeError?: string;
|
||||||
minimumVisibleSizeError?: string;
|
minimumVisibleSizeError?: string;
|
||||||
update: (obj: Partial<OrderObj>) => void;
|
|
||||||
peakSize: string;
|
|
||||||
minimumVisibleSize: string;
|
|
||||||
size: string;
|
size: string;
|
||||||
|
peakSize?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DealTicketSizeIceberg = ({
|
export const DealTicketSizeIceberg = ({
|
||||||
control,
|
control,
|
||||||
market,
|
market,
|
||||||
update,
|
|
||||||
peakSizeError,
|
peakSizeError,
|
||||||
minimumVisibleSizeError,
|
minimumVisibleSizeError,
|
||||||
peakSize,
|
|
||||||
minimumVisibleSize,
|
|
||||||
size,
|
size,
|
||||||
|
peakSize,
|
||||||
}: DealTicketSizeIcebergProps) => {
|
}: DealTicketSizeIcebergProps) => {
|
||||||
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
||||||
|
|
||||||
@@ -80,7 +75,7 @@ export const DealTicketSizeIceberg = ({
|
|||||||
className="!mb-1"
|
className="!mb-1"
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name="icebergOpts.peakSize"
|
name="peakSize"
|
||||||
control={control}
|
control={control}
|
||||||
rules={{
|
rules={{
|
||||||
required: t('You need to provide a peak size'),
|
required: t('You need to provide a peak size'),
|
||||||
@@ -97,25 +92,17 @@ export const DealTicketSizeIceberg = ({
|
|||||||
},
|
},
|
||||||
validate: validateAmount(sizeStep, 'peakSize'),
|
validate: validateAmount(sizeStep, 'peakSize'),
|
||||||
}}
|
}}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<Input
|
<Input
|
||||||
id="input-order-peak-size"
|
id="input-order-peak-size"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
type="number"
|
type="number"
|
||||||
value={peakSize}
|
|
||||||
onChange={(e) =>
|
|
||||||
update({
|
|
||||||
icebergOpts: {
|
|
||||||
peakSize: e.target.value,
|
|
||||||
minimumVisibleSize,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
step={sizeStep}
|
step={sizeStep}
|
||||||
min={sizeStep}
|
min={sizeStep}
|
||||||
max={size}
|
max={size}
|
||||||
data-testid="order-peak-size"
|
data-testid="order-peak-size"
|
||||||
onWheel={(e) => e.currentTarget.blur()}
|
onWheel={(e) => e.currentTarget.blur()}
|
||||||
|
{...field}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -144,7 +131,7 @@ export const DealTicketSizeIceberg = ({
|
|||||||
className="!mb-1"
|
className="!mb-1"
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name="icebergOpts.minimumVisibleSize"
|
name="minimumVisibleSize"
|
||||||
control={control}
|
control={control}
|
||||||
rules={{
|
rules={{
|
||||||
required: t('You need to provide a minimum visible size'),
|
required: t('You need to provide a minimum visible size'),
|
||||||
@@ -154,7 +141,7 @@ export const DealTicketSizeIceberg = ({
|
|||||||
'Minimum visible size cannot be lower than ' + sizeStep
|
'Minimum visible size cannot be lower than ' + sizeStep
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
max: {
|
max: peakSize && {
|
||||||
value: peakSize,
|
value: peakSize,
|
||||||
message: t(
|
message: t(
|
||||||
'Minimum visible size cannot be greater than the peak size (%s)',
|
'Minimum visible size cannot be greater than the peak size (%s)',
|
||||||
@@ -163,25 +150,17 @@ export const DealTicketSizeIceberg = ({
|
|||||||
},
|
},
|
||||||
validate: validateAmount(sizeStep, 'minimumVisibleSize'),
|
validate: validateAmount(sizeStep, 'minimumVisibleSize'),
|
||||||
}}
|
}}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<Input
|
<Input
|
||||||
id="input-order-minimum-size"
|
id="input-order-minimum-size"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
type="number"
|
type="number"
|
||||||
value={minimumVisibleSize}
|
|
||||||
onChange={(e) =>
|
|
||||||
update({
|
|
||||||
icebergOpts: {
|
|
||||||
peakSize,
|
|
||||||
minimumVisibleSize: e.target.value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
step={sizeStep}
|
step={sizeStep}
|
||||||
min={sizeStep}
|
min={sizeStep}
|
||||||
max={peakSize}
|
max={peakSize}
|
||||||
data-testid="order-minimum-size"
|
data-testid="order-minimum-size"
|
||||||
onWheel={(e) => e.currentTarget.blur()}
|
onWheel={(e) => e.currentTarget.blur()}
|
||||||
|
{...field}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ import { generateMarket } from '../../test-helpers';
|
|||||||
import { StopOrder } from './deal-ticket-stop-order';
|
import { StopOrder } from './deal-ticket-stop-order';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import type { StopOrderFormValues } from '../../hooks/use-stop-order-form-values';
|
import type { StopOrderFormValues } from '../../hooks/use-form-values';
|
||||||
import { useStopOrderFormValues } from '../../hooks/use-stop-order-form-values';
|
import {
|
||||||
|
DealTicketType,
|
||||||
|
useDealTicketFormValues,
|
||||||
|
} from '../../hooks/use-form-values';
|
||||||
import type { FeatureFlags } from '@vegaprotocol/environment';
|
import type { FeatureFlags } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
jest.mock('zustand');
|
jest.mock('zustand');
|
||||||
@@ -131,9 +134,11 @@ describe('StopOrder', () => {
|
|||||||
expiresAt: '2023-07-27T16:43:27.000',
|
expiresAt: '2023-07-27T16:43:27.000',
|
||||||
};
|
};
|
||||||
|
|
||||||
useStopOrderFormValues.setState({
|
useDealTicketFormValues.setState({
|
||||||
formValues: {
|
formValues: {
|
||||||
[market.id]: values,
|
[market.id]: {
|
||||||
|
[DealTicketType.StopLimit]: values,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -207,11 +212,13 @@ describe('StopOrder', () => {
|
|||||||
// switch to market order type error should disappear
|
// switch to market order type error should disappear
|
||||||
await userEvent.click(screen.getByTestId(orderTypeTrigger));
|
await userEvent.click(screen.getByTestId(orderTypeTrigger));
|
||||||
await userEvent.click(screen.getByTestId(orderTypeMarket));
|
await userEvent.click(screen.getByTestId(orderTypeMarket));
|
||||||
|
await userEvent.click(screen.getByTestId(submitButton));
|
||||||
expect(screen.queryByTestId(priceErrorMessage)).toBeNull();
|
expect(screen.queryByTestId(priceErrorMessage)).toBeNull();
|
||||||
|
|
||||||
// switch back to limit type
|
// switch back to limit type
|
||||||
await userEvent.click(screen.getByTestId(orderTypeTrigger));
|
await userEvent.click(screen.getByTestId(orderTypeTrigger));
|
||||||
await userEvent.click(screen.getByTestId(orderTypeLimit));
|
await userEvent.click(screen.getByTestId(orderTypeLimit));
|
||||||
|
await userEvent.click(screen.getByTestId(submitButton));
|
||||||
expect(screen.getByTestId(priceErrorMessage)).toBeInTheDocument();
|
expect(screen.getByTestId(priceErrorMessage)).toBeInTheDocument();
|
||||||
|
|
||||||
// to small value should be invalid
|
// to small value should be invalid
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { FormEventHandler } from 'react';
|
|
||||||
import { useRef, useCallback, useEffect } from 'react';
|
import { useRef, useCallback, useEffect } from 'react';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import type { StopOrdersSubmission } from '@vegaprotocol/wallet';
|
import type { StopOrdersSubmission } from '@vegaprotocol/wallet';
|
||||||
@@ -8,7 +7,7 @@ import {
|
|||||||
toDecimal,
|
toDecimal,
|
||||||
validateAmount,
|
validateAmount,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import { useForm, Controller } from 'react-hook-form';
|
import { useForm, Controller, useController } from 'react-hook-form';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import {
|
import {
|
||||||
Radio,
|
Radio,
|
||||||
@@ -24,22 +23,22 @@ import { getDerivedPrice, type Market } from '@vegaprotocol/markets';
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { ExpirySelector } from './expiry-selector';
|
import { ExpirySelector } from './expiry-selector';
|
||||||
import { SideSelector } from './side-selector';
|
import { SideSelector } from './side-selector';
|
||||||
import { timeInForceLabel, useOrder } from '@vegaprotocol/orders';
|
import { timeInForceLabel } from '@vegaprotocol/orders';
|
||||||
import {
|
import {
|
||||||
NoWalletWarning,
|
NoWalletWarning,
|
||||||
REDUCE_ONLY_TOOLTIP,
|
REDUCE_ONLY_TOOLTIP,
|
||||||
useNotionalSize,
|
stopSubmit,
|
||||||
|
getNotionalSize,
|
||||||
} from './deal-ticket';
|
} from './deal-ticket';
|
||||||
import { TypeToggle } from './type-selector';
|
import { TypeToggle } from './type-selector';
|
||||||
import {
|
import {
|
||||||
useStopOrderFormValues,
|
useDealTicketFormValues,
|
||||||
type StopOrderFormValues,
|
|
||||||
} from '../../hooks/use-stop-order-form-values';
|
|
||||||
import {
|
|
||||||
DealTicketType,
|
DealTicketType,
|
||||||
useDealTicketTypeStore,
|
type StopOrderFormValues,
|
||||||
} from '../../hooks/use-type-store';
|
dealTicketTypeToOrderType,
|
||||||
import { mapFormValuesToStopOrdersSubmission } from '../../utils/map-form-values-to-stop-order-submission';
|
isStopOrderType,
|
||||||
|
} from '../../hooks/use-form-values';
|
||||||
|
import { mapFormValuesToStopOrdersSubmission } from '../../utils/map-form-values-to-submission';
|
||||||
import { DealTicketButton } from './deal-ticket-button';
|
import { DealTicketButton } from './deal-ticket-button';
|
||||||
import { DealTicketFeeDetails } from './deal-ticket-fee-details';
|
import { DealTicketFeeDetails } from './deal-ticket-fee-details';
|
||||||
import { validateExpiration } from '../../utils';
|
import { validateExpiration } from '../../utils';
|
||||||
@@ -50,32 +49,36 @@ export interface StopOrderProps {
|
|||||||
submit: (order: StopOrdersSubmission) => void;
|
submit: (order: StopOrdersSubmission) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultValues: Partial<StopOrderFormValues> = {
|
const getDefaultValues = (
|
||||||
type: Schema.OrderType.TYPE_LIMIT,
|
type: Schema.OrderType,
|
||||||
|
storedValues?: Partial<StopOrderFormValues>
|
||||||
|
): StopOrderFormValues => ({
|
||||||
|
type,
|
||||||
side: Schema.Side.SIDE_BUY,
|
side: Schema.Side.SIDE_BUY,
|
||||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||||
triggerType: 'price',
|
triggerType: 'price',
|
||||||
triggerDirection:
|
triggerDirection:
|
||||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE,
|
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE,
|
||||||
|
expire: false,
|
||||||
expiryStrategy: Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_SUBMIT,
|
expiryStrategy: Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_SUBMIT,
|
||||||
size: '0',
|
size: '0',
|
||||||
};
|
...storedValues,
|
||||||
|
});
|
||||||
const stopSubmit: FormEventHandler = (e) => e.preventDefault();
|
|
||||||
|
|
||||||
export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||||
const { pubKey, isReadOnly } = useVegaWallet();
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
const setDealTicketType = useDealTicketTypeStore((state) => state.set);
|
const setType = useDealTicketFormValues((state) => state.setType);
|
||||||
const [, updateOrder] = useOrder(market.id);
|
const updateStoredFormValues = useDealTicketFormValues(
|
||||||
const updateStoredFormValues = useStopOrderFormValues(
|
(state) => state.updateStopOrder
|
||||||
(state) => state.update
|
|
||||||
);
|
);
|
||||||
const storedFormValues = useStopOrderFormValues(
|
const storedFormValues = useDealTicketFormValues(
|
||||||
(state) => state.formValues[market.id]
|
(state) => state.formValues[market.id]
|
||||||
);
|
);
|
||||||
const { handleSubmit, setValue, watch, control, formState } =
|
const dealTicketType = storedFormValues?.type ?? DealTicketType.StopLimit;
|
||||||
|
const type = dealTicketTypeToOrderType(dealTicketType);
|
||||||
|
const { handleSubmit, setValue, watch, control, formState, reset } =
|
||||||
useForm<StopOrderFormValues>({
|
useForm<StopOrderFormValues>({
|
||||||
defaultValues: { ...defaultValues, ...storedFormValues },
|
defaultValues: getDefaultValues(type, storedFormValues?.[dealTicketType]),
|
||||||
});
|
});
|
||||||
const { errors } = formState;
|
const { errors } = formState;
|
||||||
const lastSubmitTime = useRef(0);
|
const lastSubmitTime = useRef(0);
|
||||||
@@ -102,16 +105,22 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
|||||||
const triggerType = watch('triggerType');
|
const triggerType = watch('triggerType');
|
||||||
const triggerPrice = watch('triggerPrice');
|
const triggerPrice = watch('triggerPrice');
|
||||||
const timeInForce = watch('timeInForce');
|
const timeInForce = watch('timeInForce');
|
||||||
const type = watch('type');
|
|
||||||
const rawPrice = watch('price');
|
const rawPrice = watch('price');
|
||||||
const rawSize = watch('size');
|
const rawSize = watch('size');
|
||||||
|
|
||||||
if (storedFormValues?.size && rawSize !== storedFormValues?.size) {
|
useEffect(() => {
|
||||||
setValue('size', storedFormValues.size);
|
const size = storedFormValues?.[dealTicketType]?.size;
|
||||||
}
|
if (size && rawSize !== size) {
|
||||||
if (storedFormValues?.price && rawPrice !== storedFormValues?.price) {
|
setValue('size', size);
|
||||||
setValue('price', storedFormValues.price);
|
}
|
||||||
}
|
}, [storedFormValues, dealTicketType, rawSize, setValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const price = storedFormValues?.[dealTicketType]?.price;
|
||||||
|
if (price && rawPrice !== price) {
|
||||||
|
setValue('price', price);
|
||||||
|
}
|
||||||
|
}, [storedFormValues, dealTicketType, rawPrice, setValue]);
|
||||||
|
|
||||||
const isPriceTrigger = triggerType === 'price';
|
const isPriceTrigger = triggerType === 'price';
|
||||||
const size = removeDecimal(rawSize, market.positionDecimalPlaces);
|
const size = removeDecimal(rawSize, market.positionDecimalPlaces);
|
||||||
@@ -127,7 +136,7 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
|||||||
: marketPrice
|
: marketPrice
|
||||||
);
|
);
|
||||||
|
|
||||||
const notionalSize = useNotionalSize(
|
const notionalSize = getNotionalSize(
|
||||||
price,
|
price,
|
||||||
size,
|
size,
|
||||||
market.decimalPlaces,
|
market.decimalPlaces,
|
||||||
@@ -153,47 +162,28 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
|||||||
? formatNumber(triggerPrice, market.decimalPlaces)
|
? formatNumber(triggerPrice, market.decimalPlaces)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
useController({
|
||||||
|
name: 'type',
|
||||||
|
control,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={isReadOnly || !pubKey ? stopSubmit : handleSubmit(onSubmit)}
|
onSubmit={isReadOnly || !pubKey ? stopSubmit : handleSubmit(onSubmit)}
|
||||||
noValidate
|
noValidate
|
||||||
>
|
>
|
||||||
<Controller
|
<TypeToggle
|
||||||
name="type"
|
value={dealTicketType}
|
||||||
control={control}
|
onValueChange={(dealTicketType) => {
|
||||||
render={({ field }) => {
|
setType(market.id, dealTicketType);
|
||||||
const { value } = field;
|
if (isStopOrderType(dealTicketType)) {
|
||||||
return (
|
reset(
|
||||||
<TypeToggle
|
getDefaultValues(
|
||||||
value={
|
dealTicketTypeToOrderType(dealTicketType),
|
||||||
value === Schema.OrderType.TYPE_LIMIT
|
storedFormValues?.[dealTicketType]
|
||||||
? DealTicketType.StopLimit
|
)
|
||||||
: DealTicketType.StopMarket
|
);
|
||||||
}
|
}
|
||||||
onValueChange={(value) => {
|
|
||||||
const type = value as DealTicketType;
|
|
||||||
setDealTicketType(market.id, type);
|
|
||||||
if (
|
|
||||||
type === DealTicketType.Limit ||
|
|
||||||
type === DealTicketType.Market
|
|
||||||
) {
|
|
||||||
updateOrder({
|
|
||||||
type:
|
|
||||||
type === DealTicketType.Limit
|
|
||||||
? Schema.OrderType.TYPE_LIMIT
|
|
||||||
: Schema.OrderType.TYPE_MARKET,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setValue(
|
|
||||||
'type',
|
|
||||||
type === DealTicketType.StopLimit
|
|
||||||
? Schema.OrderType.TYPE_LIMIT
|
|
||||||
: Schema.OrderType.TYPE_MARKET
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{errors.type && (
|
{errors.type && (
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import {
|
import { act, render, screen, waitFor } from '@testing-library/react';
|
||||||
act,
|
|
||||||
render,
|
|
||||||
renderHook,
|
|
||||||
screen,
|
|
||||||
waitFor,
|
|
||||||
} from '@testing-library/react';
|
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { generateMarket, generateMarketData } from '../../test-helpers';
|
import { generateMarket, generateMarketData } from '../../test-helpers';
|
||||||
import { DealTicket } from './deal-ticket';
|
import { DealTicket } from './deal-ticket';
|
||||||
@@ -15,7 +9,10 @@ import type { MockedResponse } from '@apollo/client/testing';
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { addDecimal } from '@vegaprotocol/utils';
|
import { addDecimal } from '@vegaprotocol/utils';
|
||||||
import type { OrdersQuery } from '@vegaprotocol/orders';
|
import type { OrdersQuery } from '@vegaprotocol/orders';
|
||||||
import { useCreateOrderStore } from '@vegaprotocol/orders';
|
import {
|
||||||
|
DealTicketType,
|
||||||
|
useDealTicketFormValues,
|
||||||
|
} from '../../hooks/use-form-values';
|
||||||
import * as positionsTools from '@vegaprotocol/positions';
|
import * as positionsTools from '@vegaprotocol/positions';
|
||||||
import { OrdersDocument } from '@vegaprotocol/orders';
|
import { OrdersDocument } from '@vegaprotocol/orders';
|
||||||
|
|
||||||
@@ -50,9 +47,6 @@ function generateJsx(mocks: MockedResponse[] = []) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('DealTicket', () => {
|
describe('DealTicket', () => {
|
||||||
const { result } = renderHook(() => useCreateOrderStore());
|
|
||||||
const useOrderStore = result.current;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
@@ -166,9 +160,11 @@ describe('DealTicket', () => {
|
|||||||
persist: true,
|
persist: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
useOrderStore.setState({
|
useDealTicketFormValues.setState({
|
||||||
orders: {
|
formValues: {
|
||||||
[expectedOrder.marketId]: expectedOrder,
|
[expectedOrder.marketId]: {
|
||||||
|
[DealTicketType.Limit]: expectedOrder,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -204,9 +200,11 @@ describe('DealTicket', () => {
|
|||||||
reduceOnly: true,
|
reduceOnly: true,
|
||||||
postOnly: false,
|
postOnly: false,
|
||||||
};
|
};
|
||||||
useOrderStore.setState({
|
useDealTicketFormValues.setState({
|
||||||
orders: {
|
formValues: {
|
||||||
[expectedOrder.marketId]: expectedOrder,
|
[expectedOrder.marketId]: {
|
||||||
|
[DealTicketType.Limit]: expectedOrder,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -247,9 +245,11 @@ describe('DealTicket', () => {
|
|||||||
postOnly: true,
|
postOnly: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
useOrderStore.setState({
|
useDealTicketFormValues.setState({
|
||||||
orders: {
|
formValues: {
|
||||||
[expectedOrder.marketId]: expectedOrder,
|
[expectedOrder.marketId]: {
|
||||||
|
[DealTicketType.Limit]: expectedOrder,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -295,9 +295,11 @@ describe('DealTicket', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
useOrderStore.setState({
|
useDealTicketFormValues.setState({
|
||||||
orders: {
|
formValues: {
|
||||||
[expectedOrder.marketId]: expectedOrder,
|
[expectedOrder.marketId]: {
|
||||||
|
[DealTicketType.Limit]: expectedOrder,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -339,9 +341,11 @@ describe('DealTicket', () => {
|
|||||||
reduceOnly: false,
|
reduceOnly: false,
|
||||||
postOnly: false,
|
postOnly: false,
|
||||||
};
|
};
|
||||||
useOrderStore.setState({
|
useDealTicketFormValues.setState({
|
||||||
orders: {
|
formValues: {
|
||||||
[expectedOrder.marketId]: expectedOrder,
|
[expectedOrder.marketId]: {
|
||||||
|
[DealTicketType.Limit]: expectedOrder,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -370,6 +374,7 @@ describe('DealTicket', () => {
|
|||||||
expect(screen.getByTestId('iceberg')).not.toBeChecked();
|
expect(screen.getByTestId('iceberg')).not.toBeChecked();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line jest/no-disabled-tests
|
||||||
it('handles TIF select box dependent on order type', async () => {
|
it('handles TIF select box dependent on order type', async () => {
|
||||||
render(generateJsx());
|
render(generateJsx());
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import { memo, useCallback, useEffect, useState, useRef, useMemo } from 'react';
|
import type { FormEventHandler } from 'react';
|
||||||
import { Controller } from 'react-hook-form';
|
import { memo, useCallback, useEffect, useRef, useMemo } from 'react';
|
||||||
|
import { Controller, useController, useForm } from 'react-hook-form';
|
||||||
import { DealTicketAmount } from './deal-ticket-amount';
|
import { DealTicketAmount } from './deal-ticket-amount';
|
||||||
import { DealTicketButton } from './deal-ticket-button';
|
import { DealTicketButton } from './deal-ticket-button';
|
||||||
import {
|
import {
|
||||||
@@ -13,7 +14,8 @@ import { SideSelector } from './side-selector';
|
|||||||
import { TimeInForceSelector } from './time-in-force-selector';
|
import { TimeInForceSelector } from './time-in-force-selector';
|
||||||
import { TypeSelector } from './type-selector';
|
import { TypeSelector } from './type-selector';
|
||||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||||
import { normalizeOrderSubmission, useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
import { mapFormValuesToOrderSubmission } from '../../utils/map-form-values-to-submission';
|
||||||
import {
|
import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
InputError,
|
InputError,
|
||||||
@@ -51,14 +53,15 @@ import {
|
|||||||
useAccountBalance,
|
useAccountBalance,
|
||||||
} from '@vegaprotocol/accounts';
|
} from '@vegaprotocol/accounts';
|
||||||
|
|
||||||
import { OrderTimeInForce, OrderType } from '@vegaprotocol/types';
|
import { OrderType } from '@vegaprotocol/types';
|
||||||
import { useOrderForm } from '../../hooks/use-order-form';
|
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import {
|
import {
|
||||||
DealTicketType,
|
DealTicketType,
|
||||||
useDealTicketTypeStore,
|
dealTicketTypeToOrderType,
|
||||||
} from '../../hooks/use-type-store';
|
isStopOrderType,
|
||||||
import { useStopOrderFormValues } from '../../hooks/use-stop-order-form-values';
|
} from '../../hooks/use-form-values';
|
||||||
|
import type { OrderFormValues } from '../../hooks/use-form-values';
|
||||||
|
import { useDealTicketFormValues } from '../../hooks/use-form-values';
|
||||||
import { DealTicketSizeIceberg } from './deal-ticket-size-iceberg';
|
import { DealTicketSizeIceberg } from './deal-ticket-size-iceberg';
|
||||||
import noop from 'lodash/noop';
|
import noop from 'lodash/noop';
|
||||||
|
|
||||||
@@ -71,27 +74,45 @@ export interface DealTicketProps {
|
|||||||
marketPrice?: string | null;
|
marketPrice?: string | null;
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
submit: (order: OrderSubmission) => void;
|
submit: (order: OrderSubmission) => void;
|
||||||
onClickCollateral?: () => void;
|
|
||||||
onDeposit: (assetId: string) => void;
|
onDeposit: (assetId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useNotionalSize = (
|
export const getNotionalSize = (
|
||||||
price: string | null | undefined,
|
price: string | null | undefined,
|
||||||
size: string | undefined,
|
size: string | undefined,
|
||||||
decimalPlaces: number,
|
decimalPlaces: number,
|
||||||
positionDecimalPlaces: number
|
positionDecimalPlaces: number
|
||||||
) =>
|
) => {
|
||||||
useMemo(() => {
|
if (price && size) {
|
||||||
if (price && size) {
|
return removeDecimal(
|
||||||
return removeDecimal(
|
toBigNum(size, positionDecimalPlaces).multipliedBy(
|
||||||
toBigNum(size, positionDecimalPlaces).multipliedBy(
|
toBigNum(price, decimalPlaces)
|
||||||
toBigNum(price, decimalPlaces)
|
),
|
||||||
),
|
decimalPlaces
|
||||||
decimalPlaces
|
);
|
||||||
);
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
};
|
||||||
}, [price, size, decimalPlaces, positionDecimalPlaces]);
|
|
||||||
|
export const stopSubmit: FormEventHandler = (e) => e.preventDefault();
|
||||||
|
|
||||||
|
const getDefaultValues = (
|
||||||
|
type: Schema.OrderType,
|
||||||
|
storedValues?: Partial<OrderFormValues>
|
||||||
|
): OrderFormValues => ({
|
||||||
|
type,
|
||||||
|
side: Schema.Side.SIDE_BUY,
|
||||||
|
timeInForce:
|
||||||
|
type === Schema.OrderType.TYPE_LIMIT
|
||||||
|
? Schema.OrderTimeInForce.TIME_IN_FORCE_GTC
|
||||||
|
: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
|
||||||
|
size: '0',
|
||||||
|
price: '0',
|
||||||
|
expiresAt: undefined,
|
||||||
|
postOnly: false,
|
||||||
|
reduceOnly: false,
|
||||||
|
...storedValues,
|
||||||
|
});
|
||||||
|
|
||||||
export const DealTicket = ({
|
export const DealTicket = ({
|
||||||
market,
|
market,
|
||||||
@@ -99,36 +120,32 @@ export const DealTicket = ({
|
|||||||
marketData,
|
marketData,
|
||||||
marketPrice,
|
marketPrice,
|
||||||
submit,
|
submit,
|
||||||
onClickCollateral,
|
|
||||||
onDeposit,
|
onDeposit,
|
||||||
}: DealTicketProps) => {
|
}: DealTicketProps) => {
|
||||||
const { pubKey, isReadOnly } = useVegaWallet();
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
const setDealTicketType = useDealTicketTypeStore((state) => state.set);
|
const setType = useDealTicketFormValues((state) => state.setType);
|
||||||
const updateStopOrderFormValues = useStopOrderFormValues(
|
const storedFormValues = useDealTicketFormValues(
|
||||||
(state) => state.update
|
(state) => state.formValues[market.id]
|
||||||
);
|
);
|
||||||
// store last used tif for market so that when changing OrderType the previous TIF
|
const updateStoredFormValues = useDealTicketFormValues(
|
||||||
// selection for that type is used when switching back
|
(state) => state.updateOrder
|
||||||
|
);
|
||||||
const [lastTIF, setLastTIF] = useState({
|
const dealTicketType = storedFormValues?.type ?? DealTicketType.Limit;
|
||||||
[OrderType.TYPE_MARKET]: OrderTimeInForce.TIME_IN_FORCE_IOC,
|
const type = dealTicketTypeToOrderType(dealTicketType);
|
||||||
[OrderType.TYPE_LIMIT]: OrderTimeInForce.TIME_IN_FORCE_GTC,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
errors,
|
reset,
|
||||||
order,
|
formState: { errors },
|
||||||
setError,
|
|
||||||
clearErrors,
|
|
||||||
update,
|
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
} = useOrderForm(market.id);
|
setValue,
|
||||||
|
watch,
|
||||||
|
} = useForm<OrderFormValues>({
|
||||||
|
defaultValues: getDefaultValues(type, storedFormValues?.[dealTicketType]),
|
||||||
|
});
|
||||||
const lastSubmitTime = useRef(0);
|
const lastSubmitTime = useRef(0);
|
||||||
|
|
||||||
const asset = market.tradableInstrument.instrument.product.settlementAsset;
|
const asset = market.tradableInstrument.instrument.product.settlementAsset;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
accountBalance: marginAccountBalance,
|
accountBalance: marginAccountBalance,
|
||||||
loading: loadingMarginAccountBalance,
|
loading: loadingMarginAccountBalance,
|
||||||
@@ -144,24 +161,54 @@ export const DealTicket = ({
|
|||||||
).toString();
|
).toString();
|
||||||
|
|
||||||
const { marketState, marketTradingMode } = marketData;
|
const { marketState, marketTradingMode } = marketData;
|
||||||
|
const timeInForce = watch('timeInForce');
|
||||||
|
|
||||||
const normalizedOrder =
|
const side = watch('side');
|
||||||
order &&
|
const rawSize = watch('size');
|
||||||
normalizeOrderSubmission(
|
const rawPrice = watch('price');
|
||||||
order,
|
const iceberg = watch('iceberg');
|
||||||
market.decimalPlaces,
|
const peakSize = watch('peakSize');
|
||||||
market.positionDecimalPlaces
|
|
||||||
);
|
|
||||||
|
|
||||||
const price = useMemo(() => {
|
useEffect(() => {
|
||||||
return (
|
const size = storedFormValues?.[dealTicketType]?.size;
|
||||||
normalizedOrder &&
|
if (size && rawSize !== size) {
|
||||||
marketPrice &&
|
setValue('size', size);
|
||||||
getDerivedPrice(normalizedOrder, marketPrice)
|
}
|
||||||
);
|
}, [storedFormValues, dealTicketType, rawSize, setValue]);
|
||||||
}, [normalizedOrder, marketPrice]);
|
|
||||||
|
|
||||||
const notionalSize = useNotionalSize(
|
useEffect(() => {
|
||||||
|
const price = storedFormValues?.[dealTicketType]?.price;
|
||||||
|
if (price && rawPrice !== price) {
|
||||||
|
setValue('price', price);
|
||||||
|
}
|
||||||
|
}, [storedFormValues, dealTicketType, rawPrice, setValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const subscription = watch((value, { name, type }) => {
|
||||||
|
updateStoredFormValues(market.id, value);
|
||||||
|
});
|
||||||
|
return () => subscription.unsubscribe();
|
||||||
|
}, [watch, market.id, updateStoredFormValues]);
|
||||||
|
|
||||||
|
const normalizedOrder = mapFormValuesToOrderSubmission(
|
||||||
|
{
|
||||||
|
price: rawPrice || undefined,
|
||||||
|
side,
|
||||||
|
size: rawSize,
|
||||||
|
timeInForce,
|
||||||
|
type,
|
||||||
|
},
|
||||||
|
market.id,
|
||||||
|
market.decimalPlaces,
|
||||||
|
market.positionDecimalPlaces
|
||||||
|
);
|
||||||
|
|
||||||
|
const price =
|
||||||
|
normalizedOrder &&
|
||||||
|
marketPrice &&
|
||||||
|
getDerivedPrice(normalizedOrder, marketPrice);
|
||||||
|
|
||||||
|
const notionalSize = getNotionalSize(
|
||||||
price,
|
price,
|
||||||
normalizedOrder?.size,
|
normalizedOrder?.size,
|
||||||
market.decimalPlaces,
|
market.decimalPlaces,
|
||||||
@@ -205,22 +252,20 @@ export const DealTicket = ({
|
|||||||
const assetSymbol =
|
const assetSymbol =
|
||||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||||
|
|
||||||
useEffect(() => {
|
const summaryError = useMemo(() => {
|
||||||
if (!pubKey) {
|
if (!pubKey) {
|
||||||
setError('summary', {
|
return {
|
||||||
message: t('No public key selected'),
|
message: t('No public key selected'),
|
||||||
type: SummaryValidationType.NoPubKey,
|
type: SummaryValidationType.NoPubKey,
|
||||||
});
|
};
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const marketStateError = validateMarketState(marketState);
|
const marketStateError = validateMarketState(marketState);
|
||||||
if (marketStateError !== true) {
|
if (marketStateError !== true) {
|
||||||
setError('summary', {
|
return {
|
||||||
message: marketStateError,
|
message: marketStateError,
|
||||||
type: SummaryValidationType.MarketState,
|
type: SummaryValidationType.MarketState,
|
||||||
});
|
};
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasNoBalance =
|
const hasNoBalance =
|
||||||
@@ -229,24 +274,21 @@ export const DealTicket = ({
|
|||||||
hasNoBalance &&
|
hasNoBalance &&
|
||||||
!(loadingMarginAccountBalance || loadingGeneralAccountBalance)
|
!(loadingMarginAccountBalance || loadingGeneralAccountBalance)
|
||||||
) {
|
) {
|
||||||
setError('summary', {
|
return {
|
||||||
message: SummaryValidationType.NoCollateral,
|
message: SummaryValidationType.NoCollateral,
|
||||||
type: SummaryValidationType.NoCollateral,
|
type: SummaryValidationType.NoCollateral,
|
||||||
});
|
};
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const marketTradingModeError = validateMarketTradingMode(marketTradingMode);
|
const marketTradingModeError = validateMarketTradingMode(marketTradingMode);
|
||||||
if (marketTradingModeError !== true) {
|
if (marketTradingModeError !== true) {
|
||||||
setError('summary', {
|
return {
|
||||||
message: marketTradingModeError,
|
message: marketTradingModeError,
|
||||||
type: SummaryValidationType.TradingMode,
|
type: SummaryValidationType.TradingMode,
|
||||||
});
|
};
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No error found above clear the error in case it was active on a previous render
|
return undefined;
|
||||||
clearErrors('summary');
|
|
||||||
}, [
|
}, [
|
||||||
marketState,
|
marketState,
|
||||||
marketTradingMode,
|
marketTradingMode,
|
||||||
@@ -255,156 +297,83 @@ export const DealTicket = ({
|
|||||||
loadingMarginAccountBalance,
|
loadingMarginAccountBalance,
|
||||||
loadingGeneralAccountBalance,
|
loadingGeneralAccountBalance,
|
||||||
pubKey,
|
pubKey,
|
||||||
setError,
|
|
||||||
clearErrors,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const disablePostOnlyCheckbox = useMemo(() => {
|
const disablePostOnlyCheckbox = [
|
||||||
const disabled = order
|
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
|
||||||
? [
|
Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||||
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
|
].includes(timeInForce);
|
||||||
Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
||||||
].includes(order.timeInForce)
|
|
||||||
: true;
|
|
||||||
return disabled;
|
|
||||||
}, [order]);
|
|
||||||
|
|
||||||
const disableReduceOnlyCheckbox = useMemo(() => {
|
const disableReduceOnlyCheckbox = !disablePostOnlyCheckbox;
|
||||||
const disabled = order
|
|
||||||
? ![
|
|
||||||
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
|
|
||||||
Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
||||||
].includes(order.timeInForce)
|
|
||||||
: true;
|
|
||||||
return disabled;
|
|
||||||
}, [order]);
|
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
(order: OrderSubmission) => {
|
(formValues: OrderFormValues) => {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
if (lastSubmitTime.current && now - lastSubmitTime.current < 1000) {
|
if (lastSubmitTime.current && now - lastSubmitTime.current < 1000) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
submit(
|
submit(
|
||||||
normalizeOrderSubmission(
|
mapFormValuesToOrderSubmission(
|
||||||
order,
|
formValues,
|
||||||
|
market.id,
|
||||||
market.decimalPlaces,
|
market.decimalPlaces,
|
||||||
market.positionDecimalPlaces
|
market.positionDecimalPlaces
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
lastSubmitTime.current = now;
|
lastSubmitTime.current = now;
|
||||||
},
|
},
|
||||||
[submit, market.decimalPlaces, market.positionDecimalPlaces]
|
[submit, market.decimalPlaces, market.positionDecimalPlaces, market.id]
|
||||||
);
|
);
|
||||||
|
useController({
|
||||||
// if an order doesn't exist one will be created by the store immediately
|
name: 'type',
|
||||||
if (!order || !normalizedOrder) {
|
control,
|
||||||
return null;
|
rules: {
|
||||||
}
|
validate: validateType(marketData.marketTradingMode, marketData.trigger),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={isReadOnly ? noop : handleSubmit(onSubmit)}
|
onSubmit={
|
||||||
|
isReadOnly || !pubKey
|
||||||
|
? stopSubmit
|
||||||
|
: handleSubmit(summaryError ? noop : onSubmit)
|
||||||
|
}
|
||||||
noValidate
|
noValidate
|
||||||
data-testid="deal-ticket-form"
|
data-testid="deal-ticket-form"
|
||||||
>
|
>
|
||||||
<Controller
|
<TypeSelector
|
||||||
name="type"
|
value={dealTicketType}
|
||||||
control={control}
|
onValueChange={(dealTicketType) => {
|
||||||
rules={{
|
setType(market.id, dealTicketType);
|
||||||
validate: validateType(
|
if (!isStopOrderType(dealTicketType)) {
|
||||||
marketData.marketTradingMode,
|
reset(
|
||||||
marketData.trigger
|
getDefaultValues(
|
||||||
),
|
dealTicketTypeToOrderType(dealTicketType),
|
||||||
|
storedFormValues?.[dealTicketType]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
render={() => (
|
market={market}
|
||||||
<TypeSelector
|
marketData={marketData}
|
||||||
value={
|
errorMessage={errors.type?.message}
|
||||||
order.type === OrderType.TYPE_LIMIT
|
|
||||||
? DealTicketType.Limit
|
|
||||||
: DealTicketType.Market
|
|
||||||
}
|
|
||||||
onValueChange={(dealTicketType) => {
|
|
||||||
setDealTicketType(market.id, dealTicketType);
|
|
||||||
if (
|
|
||||||
dealTicketType !== DealTicketType.Limit &&
|
|
||||||
dealTicketType !== DealTicketType.Market
|
|
||||||
) {
|
|
||||||
updateStopOrderFormValues(market.id, {
|
|
||||||
type:
|
|
||||||
dealTicketType === DealTicketType.StopLimit
|
|
||||||
? OrderType.TYPE_LIMIT
|
|
||||||
: OrderType.TYPE_MARKET,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const type =
|
|
||||||
dealTicketType === DealTicketType.Limit
|
|
||||||
? OrderType.TYPE_LIMIT
|
|
||||||
: OrderType.TYPE_MARKET;
|
|
||||||
update({
|
|
||||||
type,
|
|
||||||
// when changing type also update the TIF to what was last used of new type
|
|
||||||
timeInForce: lastTIF[type] || order.timeInForce,
|
|
||||||
postOnly:
|
|
||||||
type === OrderType.TYPE_MARKET ? false : order.postOnly,
|
|
||||||
iceberg:
|
|
||||||
type === OrderType.TYPE_MARKET ||
|
|
||||||
[
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_IOC,
|
|
||||||
].includes(lastTIF[type] || order.timeInForce)
|
|
||||||
? false
|
|
||||||
: order.iceberg,
|
|
||||||
icebergOpts:
|
|
||||||
type === OrderType.TYPE_MARKET ||
|
|
||||||
[
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_IOC,
|
|
||||||
].includes(lastTIF[type] || order.timeInForce)
|
|
||||||
? undefined
|
|
||||||
: order.icebergOpts,
|
|
||||||
reduceOnly:
|
|
||||||
type === OrderType.TYPE_LIMIT &&
|
|
||||||
![
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_IOC,
|
|
||||||
].includes(lastTIF[type] || order.timeInForce)
|
|
||||||
? false
|
|
||||||
: order.postOnly,
|
|
||||||
expiresAt: undefined,
|
|
||||||
});
|
|
||||||
clearErrors(['expiresAt', 'price']);
|
|
||||||
}}
|
|
||||||
market={market}
|
|
||||||
marketData={marketData}
|
|
||||||
errorMessage={errors.type?.message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
name="side"
|
name="side"
|
||||||
control={control}
|
control={control}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<SideSelector
|
<SideSelector value={field.value} onValueChange={field.onChange} />
|
||||||
value={order.side}
|
|
||||||
onValueChange={(side) => {
|
|
||||||
update({ side });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<DealTicketAmount
|
<DealTicketAmount
|
||||||
|
type={type}
|
||||||
control={control}
|
control={control}
|
||||||
orderType={order.type}
|
|
||||||
market={market}
|
market={market}
|
||||||
marketData={marketData}
|
marketData={marketData}
|
||||||
marketPrice={marketPrice || undefined}
|
marketPrice={marketPrice || undefined}
|
||||||
sizeError={errors.size?.message}
|
sizeError={errors.size?.message}
|
||||||
priceError={errors.price?.message}
|
priceError={errors.price?.message}
|
||||||
update={update}
|
|
||||||
size={order.size}
|
|
||||||
price={order.price}
|
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
name="timeInForce"
|
name="timeInForce"
|
||||||
@@ -415,58 +384,29 @@ export const DealTicket = ({
|
|||||||
marketData.trigger
|
marketData.trigger
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<TimeInForceSelector
|
<TimeInForceSelector
|
||||||
value={order.timeInForce}
|
value={field.value}
|
||||||
orderType={order.type}
|
orderType={type}
|
||||||
onSelect={(timeInForce) => {
|
onSelect={field.onChange}
|
||||||
// Reset post only and reduce only when changing TIF
|
|
||||||
update({
|
|
||||||
timeInForce,
|
|
||||||
postOnly: [
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_IOC,
|
|
||||||
].includes(timeInForce)
|
|
||||||
? false
|
|
||||||
: order.postOnly,
|
|
||||||
reduceOnly: ![
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
||||||
OrderTimeInForce.TIME_IN_FORCE_IOC,
|
|
||||||
].includes(timeInForce)
|
|
||||||
? false
|
|
||||||
: order.reduceOnly,
|
|
||||||
});
|
|
||||||
// Set TIF value for the given order type, so that when switching
|
|
||||||
// types we know the last used TIF for the given order type
|
|
||||||
setLastTIF((curr) => ({
|
|
||||||
...curr,
|
|
||||||
[order.type]: timeInForce,
|
|
||||||
expiresAt: undefined,
|
|
||||||
}));
|
|
||||||
clearErrors('expiresAt');
|
|
||||||
}}
|
|
||||||
market={market}
|
market={market}
|
||||||
marketData={marketData}
|
marketData={marketData}
|
||||||
errorMessage={errors.timeInForce?.message}
|
errorMessage={errors.timeInForce?.message}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{order.type === Schema.OrderType.TYPE_LIMIT &&
|
{type === Schema.OrderType.TYPE_LIMIT &&
|
||||||
order.timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_GTT && (
|
timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_GTT && (
|
||||||
<Controller
|
<Controller
|
||||||
name="expiresAt"
|
name="expiresAt"
|
||||||
control={control}
|
control={control}
|
||||||
rules={{
|
rules={{
|
||||||
validate: validateExpiration,
|
validate: validateExpiration,
|
||||||
}}
|
}}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<ExpirySelector
|
<ExpirySelector
|
||||||
value={order.expiresAt}
|
value={field.value}
|
||||||
onSelect={(expiresAt) =>
|
onSelect={(expiresAt) => field.onChange(expiresAt)}
|
||||||
update({
|
|
||||||
expiresAt: expiresAt || undefined,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
errorMessage={errors.expiresAt?.message}
|
errorMessage={errors.expiresAt?.message}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -476,13 +416,14 @@ export const DealTicket = ({
|
|||||||
<Controller
|
<Controller
|
||||||
name="postOnly"
|
name="postOnly"
|
||||||
control={control}
|
control={control}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name="post-only"
|
name="post-only"
|
||||||
checked={order.postOnly}
|
checked={!disablePostOnlyCheckbox && field.value}
|
||||||
disabled={disablePostOnlyCheckbox}
|
disabled={disablePostOnlyCheckbox}
|
||||||
onCheckedChange={() => {
|
onCheckedChange={(postOnly) => {
|
||||||
update({ postOnly: !order.postOnly, reduceOnly: false });
|
field.onChange(postOnly);
|
||||||
|
setValue('reduceOnly', false);
|
||||||
}}
|
}}
|
||||||
label={
|
label={
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@@ -507,13 +448,14 @@ export const DealTicket = ({
|
|||||||
<Controller
|
<Controller
|
||||||
name="reduceOnly"
|
name="reduceOnly"
|
||||||
control={control}
|
control={control}
|
||||||
render={() => (
|
render={({ field }) => (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name="reduce-only"
|
name="reduce-only"
|
||||||
checked={order.reduceOnly}
|
checked={!disableReduceOnlyCheckbox && field.value}
|
||||||
disabled={disableReduceOnlyCheckbox}
|
disabled={disableReduceOnlyCheckbox}
|
||||||
onCheckedChange={() => {
|
onCheckedChange={(reduceOnly) => {
|
||||||
update({ postOnly: false, reduceOnly: !order.reduceOnly });
|
field.onChange(reduceOnly);
|
||||||
|
setValue('postOnly', false);
|
||||||
}}
|
}}
|
||||||
label={
|
label={
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@@ -534,53 +476,49 @@ export const DealTicket = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 pb-2 justify-between">
|
{type === Schema.OrderType.TYPE_LIMIT && (
|
||||||
{order.type === Schema.OrderType.TYPE_LIMIT && (
|
<>
|
||||||
<Controller
|
<div className="flex gap-2 pb-2 justify-between">
|
||||||
name="iceberg"
|
<Controller
|
||||||
control={control}
|
name="iceberg"
|
||||||
render={() => (
|
control={control}
|
||||||
<Checkbox
|
render={({ field }) => (
|
||||||
name="iceberg"
|
<Checkbox
|
||||||
checked={order.iceberg}
|
name="iceberg"
|
||||||
onCheckedChange={() => {
|
checked={field.value}
|
||||||
update({ iceberg: !order.iceberg, icebergOpts: undefined });
|
onCheckedChange={field.onChange}
|
||||||
}}
|
label={
|
||||||
label={
|
<Tooltip
|
||||||
<Tooltip
|
description={
|
||||||
description={
|
<p>
|
||||||
<p>
|
{t(`Trade only a fraction of the order size at once.
|
||||||
{t(`Trade only a fraction of the order size at once.
|
|
||||||
After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away.
|
After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away.
|
||||||
For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each.
|
For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each.
|
||||||
Note that the full volume of the order is not hidden and is still reflected in the order book.`)}
|
Note that the full volume of the order is not hidden and is still reflected in the order book.`)}
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="text-xs">{t('Iceberg')}</span>
|
<span className="text-xs">{t('Iceberg')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
</div>
|
||||||
</div>
|
{iceberg && (
|
||||||
{order.iceberg && (
|
<DealTicketSizeIceberg
|
||||||
<DealTicketSizeIceberg
|
market={market}
|
||||||
update={update}
|
peakSizeError={errors.peakSize?.message}
|
||||||
market={market}
|
minimumVisibleSizeError={errors.minimumVisibleSize?.message}
|
||||||
peakSizeError={errors.icebergOpts?.peakSize?.message}
|
control={control}
|
||||||
minimumVisibleSizeError={
|
size={rawSize}
|
||||||
errors.icebergOpts?.minimumVisibleSize?.message
|
peakSize={peakSize}
|
||||||
}
|
/>
|
||||||
control={control}
|
)}
|
||||||
size={order.size}
|
</>
|
||||||
peakSize={order.icebergOpts?.peakSize || ''}
|
|
||||||
minimumVisibleSize={order.icebergOpts?.minimumVisibleSize || ''}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<SummaryMessage
|
<SummaryMessage
|
||||||
errorMessage={errors.summary?.message}
|
error={summaryError}
|
||||||
asset={asset}
|
asset={asset}
|
||||||
marketTradingMode={marketData.marketTradingMode}
|
marketTradingMode={marketData.marketTradingMode}
|
||||||
balance={balance}
|
balance={balance}
|
||||||
@@ -590,10 +528,9 @@ export const DealTicket = ({
|
|||||||
}
|
}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
pubKey={pubKey}
|
pubKey={pubKey}
|
||||||
onClickCollateral={onClickCollateral}
|
|
||||||
onDeposit={onDeposit}
|
onDeposit={onDeposit}
|
||||||
/>
|
/>
|
||||||
<DealTicketButton side={order.side} />
|
<DealTicketButton side={side} />
|
||||||
<DealTicketFeeDetails
|
<DealTicketFeeDetails
|
||||||
order={
|
order={
|
||||||
normalizedOrder && { ...normalizedOrder, price: price || undefined }
|
normalizedOrder && { ...normalizedOrder, price: price || undefined }
|
||||||
@@ -619,14 +556,13 @@ export const DealTicket = ({
|
|||||||
* renders warnings about current state of the market
|
* renders warnings about current state of the market
|
||||||
*/
|
*/
|
||||||
interface SummaryMessageProps {
|
interface SummaryMessageProps {
|
||||||
errorMessage?: string;
|
error?: { message: string; type: string };
|
||||||
asset: { id: string; symbol: string; name: string; decimals: number };
|
asset: { id: string; symbol: string; name: string; decimals: number };
|
||||||
marketTradingMode: MarketData['marketTradingMode'];
|
marketTradingMode: MarketData['marketTradingMode'];
|
||||||
balance: string;
|
balance: string;
|
||||||
margin: string;
|
margin: string;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
pubKey: string | null;
|
pubKey: string | null;
|
||||||
onClickCollateral?: () => void;
|
|
||||||
onDeposit: (assetId: string) => void;
|
onDeposit: (assetId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,14 +585,13 @@ export const NoWalletWarning = ({
|
|||||||
|
|
||||||
const SummaryMessage = memo(
|
const SummaryMessage = memo(
|
||||||
({
|
({
|
||||||
errorMessage,
|
error,
|
||||||
asset,
|
asset,
|
||||||
marketTradingMode,
|
marketTradingMode,
|
||||||
balance,
|
balance,
|
||||||
margin,
|
margin,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
pubKey,
|
pubKey,
|
||||||
onClickCollateral,
|
|
||||||
onDeposit,
|
onDeposit,
|
||||||
}: SummaryMessageProps) => {
|
}: SummaryMessageProps) => {
|
||||||
// Specific error UI for if balance is so we can
|
// Specific error UI for if balance is so we can
|
||||||
@@ -665,25 +600,21 @@ const SummaryMessage = memo(
|
|||||||
return <NoWalletWarning isReadOnly={isReadOnly} />;
|
return <NoWalletWarning isReadOnly={isReadOnly} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMessage === SummaryValidationType.NoCollateral) {
|
if (error?.type === SummaryValidationType.NoCollateral) {
|
||||||
return (
|
return (
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<ZeroBalanceError
|
<ZeroBalanceError asset={asset} onDeposit={onDeposit} />
|
||||||
asset={asset}
|
|
||||||
onClickCollateral={onClickCollateral}
|
|
||||||
onDeposit={onDeposit}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have any other full error which prevents
|
// If we have any other full error which prevents
|
||||||
// submission render that first
|
// submission render that first
|
||||||
if (errorMessage) {
|
if (error?.message) {
|
||||||
return (
|
return (
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<InputError testId="deal-ticket-error-message-summary">
|
<InputError testId="deal-ticket-error-message-summary">
|
||||||
{errorMessage}
|
{error?.message}
|
||||||
</InputError>
|
</InputError>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,6 +21,13 @@ interface TimeInForceSelectorProps {
|
|||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const typeLimitOptions = Object.entries(Schema.OrderTimeInForce);
|
||||||
|
const typeMarketOptions = typeLimitOptions.filter(
|
||||||
|
([_, timeInForce]) =>
|
||||||
|
timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_FOK ||
|
||||||
|
timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_IOC
|
||||||
|
);
|
||||||
|
|
||||||
export const TimeInForceSelector = ({
|
export const TimeInForceSelector = ({
|
||||||
value,
|
value,
|
||||||
orderType,
|
orderType,
|
||||||
@@ -31,12 +38,8 @@ export const TimeInForceSelector = ({
|
|||||||
}: TimeInForceSelectorProps) => {
|
}: TimeInForceSelectorProps) => {
|
||||||
const options =
|
const options =
|
||||||
orderType === Schema.OrderType.TYPE_LIMIT
|
orderType === Schema.OrderType.TYPE_LIMIT
|
||||||
? Object.entries(Schema.OrderTimeInForce)
|
? typeLimitOptions
|
||||||
: Object.entries(Schema.OrderTimeInForce).filter(
|
: typeMarketOptions;
|
||||||
([_, timeInForce]) =>
|
|
||||||
timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_FOK ||
|
|
||||||
timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_IOC
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderError = (errorType: string) => {
|
const renderError = (errorType: string) => {
|
||||||
if (errorType === MarketModeValidationType.Auction) {
|
if (errorType === MarketModeValidationType.Auction) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { t } from '@vegaprotocol/i18n';
|
|||||||
import type { Market, StaticMarketData } from '@vegaprotocol/markets';
|
import type { Market, StaticMarketData } from '@vegaprotocol/markets';
|
||||||
import { compileGridData } from '../trading-mode-tooltip';
|
import { compileGridData } from '../trading-mode-tooltip';
|
||||||
import { MarketModeValidationType } from '../../constants';
|
import { MarketModeValidationType } from '../../constants';
|
||||||
import { DealTicketType } from '../../hooks/use-type-store';
|
import { DealTicketType } from '../../hooks/use-form-values';
|
||||||
import * as RadioGroup from '@radix-ui/react-radio-group';
|
import * as RadioGroup from '@radix-ui/react-radio-group';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FLAGS } from '@vegaprotocol/environment';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export * from './__generated__/EstimateOrder';
|
export * from './__generated__/EstimateOrder';
|
||||||
export * from './use-estimate-fees';
|
export * from './use-estimate-fees';
|
||||||
export * from './use-type-store';
|
export * from './use-form-values';
|
||||||
export * from './use-stop-order-form-values';
|
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist, subscribeWithSelector } from 'zustand/middleware';
|
||||||
|
import type { OrderTimeInForce, Side, OrderType } from '@vegaprotocol/types';
|
||||||
|
import * as Schema from '@vegaprotocol/types';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
|
||||||
|
export enum DealTicketType {
|
||||||
|
Limit = 'Limit',
|
||||||
|
Market = 'Market',
|
||||||
|
StopLimit = 'StopLimit',
|
||||||
|
StopMarket = 'StopMarket',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StopOrderFormValues {
|
||||||
|
side: Side;
|
||||||
|
|
||||||
|
triggerDirection: Schema.StopOrderTriggerDirection;
|
||||||
|
|
||||||
|
triggerType: 'price' | 'trailingPercentOffset';
|
||||||
|
triggerPrice?: string;
|
||||||
|
triggerTrailingPercentOffset?: string;
|
||||||
|
|
||||||
|
type: OrderType;
|
||||||
|
size: string;
|
||||||
|
timeInForce: OrderTimeInForce;
|
||||||
|
price?: string;
|
||||||
|
|
||||||
|
expire: boolean;
|
||||||
|
expiryStrategy?: Schema.StopOrderExpiryStrategy;
|
||||||
|
expiresAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OrderFormValues = {
|
||||||
|
type: OrderType;
|
||||||
|
side: Side;
|
||||||
|
size: string;
|
||||||
|
timeInForce: OrderTimeInForce;
|
||||||
|
price?: string;
|
||||||
|
expiresAt?: string | undefined;
|
||||||
|
postOnly?: boolean;
|
||||||
|
reduceOnly?: boolean;
|
||||||
|
iceberg?: boolean;
|
||||||
|
peakSize?: string;
|
||||||
|
minimumVisibleSize?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UpdateOrder = (marketId: string, values: Partial<OrderFormValues>) => void;
|
||||||
|
|
||||||
|
type UpdateStopOrder = (
|
||||||
|
marketId: string,
|
||||||
|
values: Partial<StopOrderFormValues>
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
type Store = {
|
||||||
|
updateOrder: UpdateOrder;
|
||||||
|
updateStopOrder: UpdateStopOrder;
|
||||||
|
setType: (marketId: string, value: DealTicketType) => void;
|
||||||
|
updateAll: (
|
||||||
|
marketId: string,
|
||||||
|
values: { size?: string; price?: string }
|
||||||
|
) => void;
|
||||||
|
formValues: Record<
|
||||||
|
string,
|
||||||
|
| {
|
||||||
|
[DealTicketType.Limit]?: Partial<OrderFormValues>;
|
||||||
|
[DealTicketType.Market]?: Partial<OrderFormValues>;
|
||||||
|
[DealTicketType.StopLimit]?: Partial<StopOrderFormValues>;
|
||||||
|
[DealTicketType.StopMarket]?: Partial<StopOrderFormValues>;
|
||||||
|
type?: DealTicketType;
|
||||||
|
}
|
||||||
|
| undefined
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dealTicketTypeToOrderType = (dealTicketType?: DealTicketType) =>
|
||||||
|
dealTicketType === DealTicketType.Limit ||
|
||||||
|
dealTicketType === DealTicketType.StopLimit
|
||||||
|
? Schema.OrderType.TYPE_LIMIT
|
||||||
|
: Schema.OrderType.TYPE_MARKET;
|
||||||
|
|
||||||
|
export const isStopOrderType = (dealTicketType?: DealTicketType) =>
|
||||||
|
dealTicketType === DealTicketType.StopLimit ||
|
||||||
|
dealTicketType === DealTicketType.StopMarket;
|
||||||
|
|
||||||
|
export const useDealTicketFormValues = create<Store>()(
|
||||||
|
immer(
|
||||||
|
persist(
|
||||||
|
subscribeWithSelector((set) => ({
|
||||||
|
formValues: {},
|
||||||
|
updateStopOrder: (marketId, formValues) => {
|
||||||
|
set((state) => {
|
||||||
|
const type =
|
||||||
|
formValues.type === Schema.OrderType.TYPE_LIMIT
|
||||||
|
? DealTicketType.StopLimit
|
||||||
|
: DealTicketType.StopMarket;
|
||||||
|
const market = state.formValues[marketId] || {};
|
||||||
|
if (!state.formValues[marketId]) {
|
||||||
|
state.formValues[marketId] = market;
|
||||||
|
}
|
||||||
|
market[type] = Object.assign(market[type] ?? {}, formValues);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateOrder: (marketId, formValues) => {
|
||||||
|
set((state) => {
|
||||||
|
const type =
|
||||||
|
formValues.type === Schema.OrderType.TYPE_LIMIT
|
||||||
|
? DealTicketType.Limit
|
||||||
|
: DealTicketType.Market;
|
||||||
|
const market = state.formValues[marketId] || {};
|
||||||
|
if (!state.formValues[marketId]) {
|
||||||
|
state.formValues[marketId] = market;
|
||||||
|
}
|
||||||
|
market[type] = Object.assign(market[type] ?? {}, formValues);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateAll: (
|
||||||
|
marketId: string,
|
||||||
|
formValues: { size?: string; price?: string }
|
||||||
|
) => {
|
||||||
|
set((state) => {
|
||||||
|
const market = state.formValues[marketId] || {};
|
||||||
|
if (!state.formValues[marketId]) {
|
||||||
|
state.formValues[marketId] = market;
|
||||||
|
}
|
||||||
|
for (const type of Object.values(DealTicketType)) {
|
||||||
|
market[type] = Object.assign(market[type] ?? {}, formValues);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setType: (marketId, type) => {
|
||||||
|
set((state) => {
|
||||||
|
state.formValues[marketId] = Object.assign(
|
||||||
|
state.formValues[marketId] ?? {},
|
||||||
|
{ type }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
name: 'vega_deal_ticket_store',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user