Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6430c3930 | ||
|
|
f37a8720a2 | ||
|
|
f5fe3cc18d | ||
|
|
288f075cac | ||
|
|
ca46861fd8 | ||
|
|
2cb6ede492 | ||
|
|
eefcc96149 | ||
|
|
7a99ded8e9 | ||
|
|
4a4cdaa2b8 | ||
|
|
cd969549ab | ||
|
|
5a73b7e769 | ||
|
|
a4f41eabb6 | ||
|
|
351a20abad | ||
|
|
8d42481130 | ||
|
|
8a59722f09 | ||
|
|
de48ffa563 | ||
|
|
00c5252aea | ||
|
|
9acc083c11 | ||
|
|
77264e80f1 | ||
|
|
50378bc8d2 | ||
|
|
b88da51c68 | ||
|
|
2ae56bb113 | ||
|
|
752af9a983 | ||
|
|
a01df081eb | ||
|
|
c2d14d1abe | ||
|
|
a76ff44ffd | ||
|
|
ac2e262bf3 | ||
|
|
ab180875b1 | ||
|
|
66d3e89b9e | ||
|
|
c9cce87606 | ||
|
|
50232ab089 | ||
|
|
e86fb6d0b4 | ||
|
|
473ef3ae83 | ||
|
|
77ab719b37 | ||
|
|
a10f67d7ab | ||
|
|
66d2b83513 | ||
|
|
e4fd040933 | ||
|
|
30c1dbd019 | ||
|
|
5e558c4cd3 | ||
|
|
85060a0421 | ||
|
|
b336291b49 |
@@ -50,7 +50,7 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
lint-test-build:
|
||||
timeout-minutes: 35
|
||||
timeout-minutes: 60
|
||||
needs: node-modules
|
||||
runs-on: ubuntu-22.04
|
||||
name: '(CI) lint + unit test + build'
|
||||
|
||||
@@ -9,21 +9,22 @@ context('Home Page', function () {
|
||||
it('should show connected environment stats', function () {
|
||||
const statTitles = {
|
||||
0: 'Status',
|
||||
1: 'Block height',
|
||||
2: 'Uptime',
|
||||
3: 'Total nodes',
|
||||
4: 'Total staked',
|
||||
5: 'Backlog',
|
||||
6: 'Trades / second',
|
||||
7: 'Orders / block',
|
||||
8: 'Orders / second',
|
||||
9: 'Transactions / block',
|
||||
10: 'Block time',
|
||||
11: 'Time',
|
||||
12: 'App',
|
||||
13: 'Tendermint',
|
||||
14: 'Up since',
|
||||
15: 'Chain ID',
|
||||
1: 'Epoch',
|
||||
2: 'Block height',
|
||||
3: 'Uptime',
|
||||
4: 'Total nodes',
|
||||
5: 'Total staked',
|
||||
6: 'Backlog',
|
||||
7: 'Trades / second',
|
||||
8: 'Orders / block',
|
||||
9: 'Orders / second',
|
||||
10: 'Transactions / block',
|
||||
11: 'Block time',
|
||||
12: 'Time',
|
||||
13: 'App',
|
||||
14: 'Tendermint',
|
||||
15: 'Up since',
|
||||
16: 'Chain ID',
|
||||
};
|
||||
|
||||
cy.get('[data-testid="stats-title"]')
|
||||
@@ -31,42 +32,13 @@ context('Home Page', function () {
|
||||
cy.wrap($list).should('contain.text', statTitles[index]);
|
||||
})
|
||||
.then(($list) => {
|
||||
cy.wrap($list).should('have.length', 16);
|
||||
cy.wrap($list).should('have.length', 17);
|
||||
});
|
||||
|
||||
cy.get(statsValue).eq(0).should('contain.text', 'CONNECTED');
|
||||
cy.get(statsValue).eq(1).should('not.be.empty');
|
||||
cy.get(statsValue)
|
||||
.eq(2)
|
||||
.invoke('text')
|
||||
.should('match', /\d+d \d+h \d+m \d+s/i);
|
||||
cy.get(statsValue).eq(3).should('contain.text', '2');
|
||||
cy.get(statsValue)
|
||||
.eq(4)
|
||||
.invoke('text')
|
||||
.should('match', /\d+\.\d\d(?!\d)/i);
|
||||
cy.get(statsValue).eq(5).should('contain.text', '0');
|
||||
cy.get(statsValue).eq(6).should('contain.text', '0');
|
||||
cy.get(statsValue).eq(7).should('contain.text', '0');
|
||||
cy.get(statsValue).eq(8).should('contain.text', '0');
|
||||
cy.get(statsValue).eq(9).should('not.be.empty');
|
||||
cy.get(statsValue).eq(10).should('not.be.empty');
|
||||
cy.get(statsValue).eq(11).should('not.be.empty');
|
||||
cy.get(statsValue)
|
||||
.eq(12)
|
||||
.invoke('text')
|
||||
.should('match', /v\d+\.\d+\.\d+/i);
|
||||
cy.get(statsValue)
|
||||
.eq(13)
|
||||
.invoke('text')
|
||||
.should('match', /\d+\.\d+\.\d+/i);
|
||||
cy.get(statsValue).eq(14).should('not.be.empty');
|
||||
cy.get(statsValue).eq(15).should('not.be.empty');
|
||||
});
|
||||
|
||||
it('Block height should be updating', function () {
|
||||
cy.get(statsValue)
|
||||
.eq(1)
|
||||
.eq(2)
|
||||
.invoke('text')
|
||||
.then((blockHeightTxt) => {
|
||||
cy.get(statsValue)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
|
||||
import { useRef, useLayoutEffect } from 'react';
|
||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||
|
||||
@@ -43,7 +43,7 @@ export const AssetLink = ({
|
||||
if (asDialog) {
|
||||
open(assetId, e.target as HTMLElement);
|
||||
} else {
|
||||
navigate(`${Routes.ASSETS}/${asset?.id}`);
|
||||
navigate(`/${Routes.ASSETS}/${asset?.id}`);
|
||||
}
|
||||
}}
|
||||
{...props}
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
SettlementAssetInfoPanel,
|
||||
} from '@vegaprotocol/market-info';
|
||||
import { MarketInfoTable } from '@vegaprotocol/market-info';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
if (!market) return null;
|
||||
@@ -97,22 +96,19 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Oracle'),
|
||||
title: t('Settlement Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel noBorder={false} market={market}>
|
||||
<Link
|
||||
className="text-xs hover:underline"
|
||||
to={`/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForSettlementData.id}`}
|
||||
>
|
||||
{t('View settlement data oracle specification')}
|
||||
</Link>
|
||||
<Link
|
||||
className="text-xs hover:underline"
|
||||
to={`/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForTradingTermination.id}`}
|
||||
>
|
||||
{t('View termination oracle specification')}
|
||||
</Link>
|
||||
</OracleInfoPanel>
|
||||
<OracleInfoPanel
|
||||
noBorder={false}
|
||||
market={market}
|
||||
type="settlementData"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Termination Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel noBorder={false} market={market} type="termination" />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -3,7 +3,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueGetterParams,
|
||||
|
||||
@@ -9,7 +9,7 @@ import SizeInMarket from '../size-in-market/size-in-market';
|
||||
export interface DeterministicOrderDetailsProps {
|
||||
id: string;
|
||||
// Version to fetch, with 0 being 'latest' and 1 being 'first'. Defaults to 0
|
||||
version?: number;
|
||||
version?: number | null;
|
||||
}
|
||||
|
||||
export const wrapperClasses =
|
||||
@@ -28,7 +28,7 @@ export const wrapperClasses =
|
||||
*/
|
||||
const DeterministicOrderDetails = ({
|
||||
id,
|
||||
version = 0,
|
||||
version = null,
|
||||
}: DeterministicOrderDetailsProps) => {
|
||||
const { data, error } = useExplorerDeterministicOrderQuery({
|
||||
variables: { orderId: id, version },
|
||||
|
||||
@@ -3,7 +3,7 @@ import { VoteProgress } from '@vegaprotocol/proposals';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
|
||||
@@ -71,7 +71,7 @@ export const PartyAccounts = ({ accounts }: PartyAccountsProps) => {
|
||||
/>
|
||||
</td>
|
||||
<td className="text-md">
|
||||
<AssetLink assetId={account.asset.id} />
|
||||
<AssetLink assetId={account.asset.id} asDialog={true} />
|
||||
</td>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
|
||||
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@@ -11,3 +15,43 @@
|
||||
.react-markdown-container a:before {
|
||||
content: '🔗 ';
|
||||
}
|
||||
|
||||
/* AG GRID - Do not edit without updating other global stylesheets for each app */
|
||||
|
||||
.vega-ag-grid .ag-root-wrapper {
|
||||
border: solid 0px;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-react-container {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-cell,
|
||||
.vega-ag-grid .ag-full-width-row .ag-cell-wrapper.ag-row-group {
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
.ag-theme-balham {
|
||||
--ag-background-color: theme(colors.white);
|
||||
--ag-border-color: theme(colors.neutral[300]);
|
||||
--ag-header-background-color: theme(colors.white);
|
||||
--ag-odd-row-background-color: theme(colors.white);
|
||||
--ag-header-column-separator-color: theme(colors.neutral[300]);
|
||||
--ag-row-border-color: theme(colors.white);
|
||||
--ag-row-hover-color: theme(colors.neutral[100]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
/* Dark variables */
|
||||
.ag-theme-balham-dark {
|
||||
--ag-background-color: theme(colors.black);
|
||||
--ag-border-color: theme(colors.neutral[700]);
|
||||
--ag-header-background-color: theme(colors.black);
|
||||
--ag-odd-row-background-color: theme(colors.black);
|
||||
--ag-header-column-separator-color: theme(colors.neutral[600]);
|
||||
--ag-row-border-color: theme(colors.black);
|
||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"name": "Token test market",
|
||||
"code": "Token.24h",
|
||||
"future": {
|
||||
"settlementAsset": "fBTC",
|
||||
"settlementAsset": "73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0",
|
||||
"quoteName": "fBTC",
|
||||
"dataSourceSpecForSettlementData": {
|
||||
"external": {
|
||||
|
||||
@@ -49,6 +49,7 @@ describe(
|
||||
});
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -217,9 +218,9 @@ describe(
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getSubmittedProposalFromProposalList(rawProposal.rationale.title)
|
||||
.as('submittedProposal')
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
getSubmittedProposalFromProposalList(
|
||||
rawProposal.rationale.title
|
||||
).within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
voteForProposal('for');
|
||||
// 3001-VOTE-079
|
||||
@@ -237,9 +238,9 @@ describe(
|
||||
);
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getSubmittedProposalFromProposalList(rawProposal.rationale.title)
|
||||
.as('submittedProposal')
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
getSubmittedProposalFromProposalList(
|
||||
rawProposal.rationale.title
|
||||
).within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
cy.get(proposalVoteProgressForPercentage)
|
||||
.contains('100.00%')
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||
|
||||
const proposalListItem = '[data-testid="proposals-list-item"]';
|
||||
const closedProposals = '[data-testid="closed-proposals"]';
|
||||
const proposalStatus = '[data-testid="proposal-status"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
@@ -35,6 +36,7 @@ context(
|
||||
});
|
||||
|
||||
beforeEach('visit proposals', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -51,7 +53,8 @@ context(
|
||||
waitForSpinner();
|
||||
cy.get(closedProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(proposalStatus).should('have.text', 'Enacted ');
|
||||
cy.get(viewProposalButton).click();
|
||||
@@ -78,7 +81,8 @@ context(
|
||||
waitForSpinner();
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
getProposalInformationFromTable('State')
|
||||
@@ -111,7 +115,8 @@ context(
|
||||
waitForSpinner();
|
||||
cy.get(openProposals, { timeout: 6000 }).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
getProposalInformationFromTable('State')
|
||||
@@ -133,7 +138,8 @@ context(
|
||||
waitForSpinner();
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
getProposalInformationFromTable('State')
|
||||
|
||||
@@ -84,6 +84,7 @@ context(
|
||||
});
|
||||
|
||||
beforeEach('visit governance tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
||||
@@ -76,6 +76,7 @@ context(
|
||||
});
|
||||
|
||||
beforeEach('visit governance tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -304,7 +305,10 @@ context(
|
||||
cy.get('dd').eq(0).should('have.text', 'Test market 1');
|
||||
cy.get('dd').eq(1).should('have.text', 'TEST.24h');
|
||||
cy.get('dd').eq(2).should('not.be.empty');
|
||||
cy.get('dd').eq(2).invoke('text').as('EnactedMarketId');
|
||||
cy.get('dd')
|
||||
.eq(2)
|
||||
.invoke('text')
|
||||
.as('EnactedMarketId', { type: 'static' });
|
||||
});
|
||||
cy.get('@EnactedMarketId').then((marketId) => {
|
||||
cy.VegaWalletSubmitLiquidityProvision(String(marketId), '1');
|
||||
@@ -322,6 +326,7 @@ context(
|
||||
cy.get('@EnactedMarketId').then((marketId) => {
|
||||
cy.contains(String(marketId))
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
@@ -375,16 +380,19 @@ context(
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
// cannot submit a proposal with ERC20 address already in use
|
||||
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
|
||||
cy.getByTestId('dialog-content').within(() => {
|
||||
cy.get('p').should(
|
||||
'have.text',
|
||||
'PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE'
|
||||
);
|
||||
});
|
||||
cy.getByTestId('dialog-content')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get('p').should(
|
||||
'have.text',
|
||||
'PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE'
|
||||
);
|
||||
});
|
||||
closeDialog();
|
||||
navigateTo(navigation.proposals);
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
@@ -422,6 +430,7 @@ context(
|
||||
cy.get(proposalType)
|
||||
.contains('Update asset')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(proposalDetails).should('contain.text', assetId); // 3001-VOTE-029
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
|
||||
@@ -35,6 +35,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
});
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -59,7 +60,10 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.get(proposalClosingDate).first().should('contain.text', 'year');
|
||||
cy.get(proposalClosingDate).should('contain.text', 'months');
|
||||
cy.get(proposalClosingDate).last().should('contain.text', 'days');
|
||||
cy.get(proposalClosingDate)
|
||||
.last()
|
||||
.invoke('text')
|
||||
.should('match', /days|minutes/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -25,9 +25,10 @@ const rewardsTimeOut = { timeout: 60000 };
|
||||
|
||||
context('rewards - flow', { tags: '@slow' }, function () {
|
||||
before('set up environment to allow rewards', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
waitForSpinner();
|
||||
depositAsset(vegaAssetAddress, '1000');
|
||||
depositAsset(vegaAssetAddress, '1000', 18);
|
||||
cy.validatorsSelfDelegate();
|
||||
ethereumWalletConnect();
|
||||
cy.connectVegaWallet();
|
||||
@@ -56,7 +57,7 @@ context('rewards - flow', { tags: '@slow' }, function () {
|
||||
cy.getByTestId(rewardsTable)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId('asset').should('have.text', 'Vega');
|
||||
cy.getByTestId('asset', rewardsTimeOut).should('have.text', 'Vega');
|
||||
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD').should('have.text', '1');
|
||||
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE').should(
|
||||
'have.text',
|
||||
@@ -93,13 +94,13 @@ context('rewards - flow', { tags: '@slow' }, function () {
|
||||
.within(() => {
|
||||
cy.get('h2').first().should('contain.text', 'EPOCH');
|
||||
cy.getByTestId('individual-rewards-asset').should('have.text', 'Vega');
|
||||
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD')
|
||||
.should('contain.text', '0.1177')
|
||||
.and('contain.text', '(11.7733%)');
|
||||
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD', rewardsTimeOut)
|
||||
.should('contain.text', '0.4415')
|
||||
.and('contain.text', '(44.15%)');
|
||||
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE')
|
||||
.should('contain.text', '0.0001')
|
||||
.and('contain.text', '(11.7733%)');
|
||||
cy.getByTestId('total').should('have.text', '0.1179');
|
||||
.should('contain.text', '0.0004')
|
||||
.and('contain.text', '(44.15%)');
|
||||
cy.getByTestId('total').should('have.text', '0.4419');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ context(
|
||||
beforeEach(
|
||||
'teardown wallet & drill into a specific validator',
|
||||
function () {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
||||
@@ -53,6 +53,7 @@ context(
|
||||
beforeEach(
|
||||
'teardown wallet & drill into a specific validator',
|
||||
function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -90,9 +91,14 @@ context(
|
||||
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
@@ -128,26 +134,38 @@ context(
|
||||
stakingPageAssociateTokens('1001', { approve: true });
|
||||
verifyEthWalletAssociatedBalance('1,001.00');
|
||||
verifyEthWalletTotalAssociatedBalance('1,001.00');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'1,001.00'
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'1,001.00'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to disassociate a partial amount of tokens currently associated', function () {
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
stakingPageDisassociateTokens('1');
|
||||
verifyEthWalletAssociatedBalance('1.0');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 1.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
1.0
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to disassociate all tokens - using max', function () {
|
||||
@@ -155,26 +173,40 @@ context(
|
||||
const warningText =
|
||||
'Warning: Any tokens that have been nominated to a node will sacrifice rewards they are due for the current epoch. If you do not wish to sacrifice these, you should remove stake from a node at the end of an epoch before disassociation.';
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.get(ethWalletDissociateButton).click();
|
||||
cy.get(disassociationWarning).should('contain', warningText);
|
||||
stakingPageDisassociateAllTokens();
|
||||
cy.get(ethWalletContainer).within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
cy.get(ethWalletContainer).within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 0.0);
|
||||
});
|
||||
cy.get(ethWalletContainer)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
cy.get(ethWalletContainer)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
0.0
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to associate and disassociate vesting contract tokens', function () {
|
||||
@@ -200,9 +232,14 @@ context(
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
stakingPageDisassociateTokens('1', {
|
||||
type: 'contract',
|
||||
@@ -228,38 +265,61 @@ context(
|
||||
stakingPageAssociateTokens('21', { type: 'wallet' });
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
stakingPageAssociateTokens('37', { type: 'contract' });
|
||||
cy.get(vestingContractSection).within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 37);
|
||||
});
|
||||
cy.get(vegaInWalletSection).within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 21);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 58);
|
||||
});
|
||||
cy.get(vestingContractSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 37);
|
||||
});
|
||||
cy.get(vegaInWalletSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 21);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
58
|
||||
);
|
||||
});
|
||||
stakingPageDisassociateTokens('6', { type: 'contract' });
|
||||
cy.get(vestingContractSection).within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 31);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52);
|
||||
});
|
||||
cy.get(vestingContractSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 31);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
52
|
||||
);
|
||||
});
|
||||
navigateTo(navigation.validators);
|
||||
stakingPageDisassociateTokens('9', { type: 'wallet' });
|
||||
cy.get(vegaInWalletSection).within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 12);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 43);
|
||||
});
|
||||
cy.get(vegaInWalletSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 12);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
43
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Not able to associate more tokens than owned', function () {
|
||||
@@ -318,9 +378,14 @@ context(
|
||||
Cypress.env('vegaWalletPublicKey2')
|
||||
);
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(associateCompleteText).should(
|
||||
'have.text',
|
||||
`Vega key ${Cypress.env(
|
||||
|
||||
@@ -4,10 +4,7 @@ import {
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
depositAsset,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
import { depositAsset } from '../../support/wallet-teardown.functions';
|
||||
|
||||
const withdraw = 'withdraw';
|
||||
const withdrawalForm = 'withdraw-form';
|
||||
@@ -25,6 +22,13 @@ const withdrawalAmount = 'withdrawal-amount';
|
||||
const withdrawalRecipient = 'withdrawal-recipient';
|
||||
const withdrawFundsButton = 'withdraw-funds';
|
||||
const completeWithdrawalButton = 'complete-withdrawal';
|
||||
const tableTxHash = '[col-id="txHash"]';
|
||||
const tableAssetSymbol = '[col-id="asset.symbol"]';
|
||||
const tableAmount = '[col-id="amount"]';
|
||||
const tableReceiverAddress = '[col-id="details.receiverAddress"]';
|
||||
const tableWithdrawnTimeStamp = '[col-id="withdrawnTimestamp"]';
|
||||
const tableWithdrawnStatus = '[col-id="status"]';
|
||||
const tableCreatedTimeStamp = '[col-id="createdTimestamp"]';
|
||||
const usdtName = 'USDC (local)';
|
||||
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
|
||||
const usdcSymbol = 'tUSDC';
|
||||
@@ -42,26 +46,23 @@ context(
|
||||
cy.visit('/');
|
||||
// When running tests locally, will fail if run without restarting capsule
|
||||
cy.updateCapsuleMultiSig().then(() => {
|
||||
depositAsset(usdcEthAddress, '100');
|
||||
ethereumWalletConnect();
|
||||
depositAsset(usdcEthAddress, '1000', 5);
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach('Navigate to withdrawal page', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
navigateTo(navigation.withdraw);
|
||||
cy.connectVegaWallet();
|
||||
ethereumWalletConnect();
|
||||
vegaWalletTeardown();
|
||||
});
|
||||
|
||||
it('Able to open withdrawal form with vega wallet connected', function () {
|
||||
// needs to reload page for withdrawal form to be displayed in ci - not reproducible outside of ci
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
ethereumWalletConnect();
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').find('option').should('have.length.at.least', 2);
|
||||
cy.getByTestId(ethAddressInput).should('be.visible');
|
||||
cy.getByTestId(amountInput).should('be.visible');
|
||||
@@ -70,7 +71,7 @@ context(
|
||||
|
||||
it('Unable to submit withdrawal with invalid fields', function () {
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
@@ -94,7 +95,7 @@ context(
|
||||
it('Able to withdraw asset: -eth wallet connected -withdraw funds button', function () {
|
||||
// fill in withdrawal form
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
|
||||
cy.getByTestId(withdrawalThreshold).should(
|
||||
@@ -102,7 +103,7 @@ context(
|
||||
'100,000.00000T'
|
||||
);
|
||||
cy.getByTestId(delayTime).should('have.text', 'None');
|
||||
cy.getByTestId(amountInput).click().type('100');
|
||||
cy.getByTestId(amountInput).click().type('120');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
});
|
||||
|
||||
@@ -116,7 +117,7 @@ context(
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/txs/');
|
||||
cy.getByTestId(withdrawalAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.getByTestId(withdrawalAmount).should('have.text', '100.00');
|
||||
cy.getByTestId(withdrawalAmount).should('have.text', '120.00');
|
||||
cy.getByTestId(withdrawalRecipient)
|
||||
.should('have.text', truncatedWithdrawalEthAddress)
|
||||
.and('have.attr', 'href')
|
||||
@@ -128,26 +129,20 @@ context(
|
||||
'Withdraw asset complete'
|
||||
);
|
||||
cy.getByTestId(dialogClose).click();
|
||||
|
||||
// need to reload page to see withdrawal history complete
|
||||
cy.reload();
|
||||
waitForAssetsDisplayed(usdtName);
|
||||
|
||||
// withdrawal history for complete withdrawal displayed
|
||||
cy.get('[col-id="txHash"]', txTimeout)
|
||||
.should('have.length.above', 1)
|
||||
.eq(1)
|
||||
cy.get(tableWithdrawnStatus)
|
||||
.eq(1, txTimeout)
|
||||
.should('have.text', 'Completed')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', usdcSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '100.00');
|
||||
cy.get('[col-id="details.receiverAddress"]')
|
||||
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.get(tableAmount).should('have.text', '120.00');
|
||||
cy.get(tableReceiverAddress)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/address/');
|
||||
cy.get('[col-id="withdrawnTimestamp"]').should('not.be.empty');
|
||||
cy.get('[col-id="status"]').should('have.text', 'Completed');
|
||||
cy.get('[col-id="txHash"]')
|
||||
cy.get(tableWithdrawnTimeStamp).should('not.be.empty');
|
||||
cy.get(tableTxHash)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/tx/');
|
||||
@@ -155,16 +150,16 @@ context(
|
||||
});
|
||||
|
||||
// Skipping because of bug #1857
|
||||
it.skip('Able to withdraw asset: -eth wallet not connected', function () {
|
||||
it('Able to withdraw asset: -eth wallet not connected', function () {
|
||||
const ethWalletAddress = Cypress.env('ethWalletPublicKey');
|
||||
cy.reload();
|
||||
waitForAssetsDisplayed(usdtName);
|
||||
// fill in withdrawal form
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId(ethAddressInput).should('be.empty');
|
||||
cy.getByTestId(amountInput).click().type('100');
|
||||
cy.getByTestId(amountInput).click().type('110');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
|
||||
// Need eth address to submit withdrawal
|
||||
@@ -180,20 +175,20 @@ context(
|
||||
'Transaction complete'
|
||||
);
|
||||
cy.getByTestId(dialogClose).click();
|
||||
|
||||
cy.getByTestId(completeWithdrawalButton)
|
||||
.eq(0)
|
||||
.parent()
|
||||
cy.get(tableTxHash)
|
||||
.eq(1)
|
||||
.should('have.text', 'Complete withdrawal')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', usdcSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '100.00');
|
||||
cy.get('[col-id="details.receiverAddress"]')
|
||||
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.get(tableAmount).should('have.text', '110.00');
|
||||
cy.get(tableReceiverAddress)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/address/');
|
||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
||||
cy.get(tableCreatedTimeStamp).should('not.be.empty');
|
||||
cy.getByTestId(completeWithdrawalButton).click();
|
||||
// Unable to complete withdrawal in Capsule
|
||||
});
|
||||
});
|
||||
|
||||
@@ -202,26 +197,28 @@ context(
|
||||
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
|
||||
|
||||
// Disconnect vega wallet
|
||||
cy.getByTestId('manage-vega-wallet').click();
|
||||
cy.getByTestId('manage-vega-wallet').last().click();
|
||||
cy.getByTestId('disconnect').click();
|
||||
|
||||
cy.connectPublicKey(vegaWalletPubKey);
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
|
||||
cy.getByTestId(amountInput).click().type('100');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
});
|
||||
|
||||
cy.getByTestId('dialog-content').within(() => {
|
||||
cy.get('h1').should('have.text', 'Transaction failed');
|
||||
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
|
||||
});
|
||||
cy.getByTestId('dialog-content')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get('h1').should('have.text', 'Transaction failed');
|
||||
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
|
||||
});
|
||||
});
|
||||
|
||||
function waitForAssetsDisplayed(expectedAsset: string) {
|
||||
cy.contains(expectedAsset, txTimeout).should('be.visible');
|
||||
cy.getByTestId('currency-title').should('contain.text', expectedAsset);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -90,6 +90,7 @@ export function getSubmittedProposalFromProposalList(proposalTitle: string) {
|
||||
export function getProposalIdFromList(proposalTitle: string) {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(proposalDetails)
|
||||
.invoke('text')
|
||||
@@ -133,7 +134,7 @@ export function waitForProposalSync() {
|
||||
// before proposal appears in the list - so rather than hard coded wait - we just wait on the
|
||||
// delegation checks that are performed on the governance page.
|
||||
|
||||
cy.intercept('POST', '/query', (req) => {
|
||||
cy.intercept('POST', '/graphql', (req) => {
|
||||
if (req.body.operationName === 'Delegations') {
|
||||
req.alias = 'proposalDelegationsCompletion';
|
||||
}
|
||||
@@ -143,7 +144,7 @@ export function waitForProposalSync() {
|
||||
cy.wait(['@proposalDelegationsCompletion', '@proposalDelegationsCompletion']);
|
||||
|
||||
// Turn off this intercept from here on in
|
||||
cy.intercept('POST', '/query', (req) => {
|
||||
cy.intercept('POST', '/graphql', (req) => {
|
||||
if (req.body.operationName === 'Delegations') {
|
||||
req.continue();
|
||||
}
|
||||
|
||||
@@ -35,18 +35,25 @@ const stakingBridgeContract = new StakingBridge(
|
||||
);
|
||||
const vestingContract = new TokenVesting(vegaTokenContractAddress, signer);
|
||||
|
||||
export async function depositAsset(assetEthAddress: string, amount: string) {
|
||||
export async function depositAsset(
|
||||
assetEthAddress: string,
|
||||
amount: string,
|
||||
decimalPlaces: number
|
||||
) {
|
||||
// Approve asset
|
||||
const faucet = new TokenFaucetable(assetEthAddress, signer);
|
||||
cy.wrap(faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(19)), {
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}).then(() => {
|
||||
cy.wrap(
|
||||
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
|
||||
{
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}
|
||||
).then(() => {
|
||||
const collateralBridge = new CollateralBridge(Erc20BridgeAddress, signer);
|
||||
cy.wrap(
|
||||
collateralBridge.deposit_asset(
|
||||
assetEthAddress,
|
||||
amount + '0'.repeat(18),
|
||||
amount + '0'.repeat(decimalPlaces),
|
||||
'0x' + vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
@@ -102,27 +109,49 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
cy.highlight('Tearing down staking tokens from vega wallet if present');
|
||||
cy.wrap(
|
||||
stakingBridgeContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
{ timeout: transactionTimeout }
|
||||
).then((stakeBalance) => {
|
||||
if (Number(stakeBalance) != 0) {
|
||||
cy.get('[data-testid="vega-wallet-balance-unstaked"]:visible').within(
|
||||
() => {
|
||||
cy.get(associatedAmountInWallet)
|
||||
.invoke('text')
|
||||
.then(($walletAmount) => {
|
||||
cy.wrap(
|
||||
stakingBridgeContract.remove_stake(
|
||||
String(stakeBalance),
|
||||
vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
);
|
||||
cy.get(associatedAmountInWallet, {
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
cy.getByTestId('currency-value')
|
||||
.first()
|
||||
.invoke('text')
|
||||
.then(($associatedAmount) => {
|
||||
cy.wrap(
|
||||
stakingBridgeContract.remove_stake(
|
||||
String(stakeBalance),
|
||||
vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout }
|
||||
);
|
||||
cy.wrap(
|
||||
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
|
||||
{
|
||||
timeout: transactionTimeout,
|
||||
}).should('not.have.text', $walletAmount);
|
||||
log: false,
|
||||
}
|
||||
).then((vestingAmount) => {
|
||||
if (Number(vestingAmount) != 0) {
|
||||
cy.contains('Associated', {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.getByTestId('currency-value', {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.should('have.length', 1)
|
||||
.invoke('text')
|
||||
.as('displayedAmount');
|
||||
cy.get('@displayedAmount', {
|
||||
timeout: transactionTimeout,
|
||||
}).should('not.eq', $associatedAmount);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -136,7 +165,7 @@ async function vegaWalletTeardownVesting(vestingContract: TokenVesting) {
|
||||
if (Number(vestingAmount) != 0) {
|
||||
cy.wrap(
|
||||
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
{ timeout: transactionTimeout }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { DisconnectedNotice } from './disconnected-notice';
|
||||
|
||||
describe('DisconnectedNotice', () => {
|
||||
it('renders Notification when isDisconnected is true and correctNetworkChainId is valid', () => {
|
||||
render(
|
||||
<DisconnectedNotice isDisconnected={true} correctNetworkChainId={'1'} />
|
||||
);
|
||||
const disconnectedNotice = screen.getByTestId('disconnected-notice');
|
||||
expect(disconnectedNotice).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't render Notification when isDisconnected is false", () => {
|
||||
render(
|
||||
<DisconnectedNotice isDisconnected={false} correctNetworkChainId={'1'} />
|
||||
);
|
||||
const disconnectedNotice = screen.queryByTestId('disconnected-notice');
|
||||
expect(disconnectedNotice).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't render Notification when correctNetworkChainId is undefined", () => {
|
||||
render(
|
||||
<DisconnectedNotice
|
||||
isDisconnected={true}
|
||||
correctNetworkChainId={undefined}
|
||||
/>
|
||||
);
|
||||
const disconnectedNotice = screen.queryByTestId('disconnected-notice');
|
||||
expect(disconnectedNotice).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't render Notification when correctNetworkChainId is null", () => {
|
||||
render(
|
||||
<DisconnectedNotice isDisconnected={true} correctNetworkChainId={null} />
|
||||
);
|
||||
const disconnectedNotice = screen.queryByTestId('disconnected-notice');
|
||||
expect(disconnectedNotice).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface DisconnectedNoticeProps {
|
||||
isDisconnected: boolean;
|
||||
correctNetworkChainId?: string | null;
|
||||
}
|
||||
|
||||
export const DisconnectedNotice = ({
|
||||
isDisconnected,
|
||||
correctNetworkChainId,
|
||||
}: DisconnectedNoticeProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (
|
||||
!isDisconnected ||
|
||||
correctNetworkChainId === undefined ||
|
||||
correctNetworkChainId === null
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="col-span-full" data-testid="disconnected-notice">
|
||||
<Notification
|
||||
message={t('disconnectedNotice', {
|
||||
correctNetwork: correctNetworkChainId,
|
||||
})}
|
||||
intent={Intent.Danger}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './disconnected-notice';
|
||||
@@ -3,6 +3,7 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { DisconnectedNotice } from '../disconnected-notice';
|
||||
|
||||
import {
|
||||
AppStateActionType,
|
||||
@@ -26,7 +27,8 @@ import {
|
||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import colors from 'tailwindcss/colors';
|
||||
import { useBalances } from '../../lib/balances/balances-store';
|
||||
import { useWeb3Disconnect } from '@vegaprotocol/web3';
|
||||
import { useEthereumConfig, useWeb3Disconnect } from '@vegaprotocol/web3';
|
||||
import { getChainName } from '@vegaprotocol/web3';
|
||||
|
||||
const removeLeadingAddressSymbol = (key: string) => {
|
||||
if (key && key.length > 2 && key.slice(0, 2) === '0x') {
|
||||
@@ -182,16 +184,21 @@ const ConnectedKey = () => {
|
||||
|
||||
export const EthWallet = () => {
|
||||
const { t } = useTranslation();
|
||||
const { appDispatch } = useAppState();
|
||||
const { appDispatch, appState } = useAppState();
|
||||
const { account, connector } = useWeb3React();
|
||||
const pendingTxs = usePendingTransactions();
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
const { config } = useEthereumConfig();
|
||||
|
||||
return (
|
||||
<WalletCard>
|
||||
<section data-testid="ethereum-wallet">
|
||||
<WalletCardHeader>
|
||||
<h1 className="m-0 uppercase">{t('ethereumKey')}</h1>
|
||||
<DisconnectedNotice
|
||||
isDisconnected={appState.disconnectNotice}
|
||||
correctNetworkChainId={getChainName(Number(config?.chain_id))}
|
||||
/>
|
||||
{account && (
|
||||
<div className="place-self-end font-mono">
|
||||
<div
|
||||
|
||||
@@ -14,7 +14,7 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
||||
const { isReadOnly } = useVegaWallet();
|
||||
const AppLayoutClasses = classNames(
|
||||
'app w-full max-w-[1500px] mx-auto grid',
|
||||
'lg:text-body-large',
|
||||
'font-alpha lg:text-body-large',
|
||||
{
|
||||
'grid-rows-[repeat(2,min-content)_1fr_min-content]': !isReadOnly,
|
||||
'grid-rows-[repeat(3,min-content)_1fr_min-content]': isReadOnly,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
getChainName,
|
||||
useWeb3ConnectStore,
|
||||
useWeb3Disconnect,
|
||||
Web3ConnectDialog,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
@@ -36,9 +35,7 @@ export function Web3Connector({
|
||||
const appChainId = Number(chainId);
|
||||
return (
|
||||
<>
|
||||
<Web3Content appChainId={appChainId} setDialogOpen={setDialogOpen}>
|
||||
{children}
|
||||
</Web3Content>
|
||||
<Web3Content appChainId={appChainId}>{children}</Web3Content>
|
||||
<Web3ConnectDialog
|
||||
connectors={connectors}
|
||||
dialogOpen={appState.ethConnectOverlay}
|
||||
@@ -52,23 +49,59 @@ export function Web3Connector({
|
||||
interface Web3ContentProps {
|
||||
children: ReactElement;
|
||||
appChainId: number;
|
||||
setDialogOpen: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
|
||||
const { appState, appDispatch } = useAppState();
|
||||
const { connector, chainId } = useWeb3React();
|
||||
const [previousChainId, setPreviousChainId] = useState(chainId);
|
||||
const error = useWeb3ConnectStore((store) => store.error);
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
|
||||
const showDisconnectNotice = useCallback(
|
||||
(isVisible: boolean) =>
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_DISCONNECT_NOTICE,
|
||||
isVisible,
|
||||
}),
|
||||
[appDispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (connector?.connectEagerly) {
|
||||
connector.connectEagerly();
|
||||
}
|
||||
// wallet connect doesnt handle connectEagerly being called when connector is also in the
|
||||
// wallet connect doesn't handle connectEagerly being called when connector is also in the
|
||||
// deps array.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (chainId !== undefined) {
|
||||
// We use this to detect when the user switches networks.
|
||||
setPreviousChainId(chainId);
|
||||
|
||||
if (chainId !== appChainId) {
|
||||
disconnect();
|
||||
|
||||
// If the user was previously connected, show the disconnect explanation notice.
|
||||
if (previousChainId !== undefined && !appState.disconnectNotice) {
|
||||
showDisconnectNotice(true);
|
||||
}
|
||||
} else if (appState.disconnectNotice) {
|
||||
showDisconnectNotice(false);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
appChainId,
|
||||
appDispatch,
|
||||
appState.disconnectNotice,
|
||||
chainId,
|
||||
disconnect,
|
||||
previousChainId,
|
||||
showDisconnectNotice,
|
||||
]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Splash>
|
||||
@@ -80,18 +113,5 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (chainId !== undefined && chainId !== appChainId) {
|
||||
return (
|
||||
<Splash>
|
||||
<div className="flex flex-col items-center gap-12">
|
||||
<p className="text-white">
|
||||
This app only works on {getChainName(appChainId)}
|
||||
</p>
|
||||
<Button onClick={() => disconnect()}>Disconnect</Button>
|
||||
</div>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
|
||||
@@ -43,6 +43,11 @@ export interface AppState {
|
||||
* Message to display in a banner at the top of the screen, currently always shown as a warning/error
|
||||
*/
|
||||
bannerMessage: string;
|
||||
/**
|
||||
* Displays a notice to the user that they have been disconnected because they've changed their
|
||||
* ethereum network to an incompatible one.
|
||||
*/
|
||||
disconnectNotice: boolean;
|
||||
}
|
||||
|
||||
export enum AppStateActionType {
|
||||
@@ -58,6 +63,7 @@ export enum AppStateActionType {
|
||||
SET_ASSOCIATION_BREAKDOWN,
|
||||
SET_TRANSACTION_OVERLAY,
|
||||
SET_BANNER_MESSAGE,
|
||||
SET_DISCONNECT_NOTICE,
|
||||
}
|
||||
|
||||
export type AppStateAction =
|
||||
@@ -90,6 +96,10 @@ export type AppStateAction =
|
||||
| {
|
||||
type: AppStateActionType.SET_BANNER_MESSAGE;
|
||||
message: string;
|
||||
}
|
||||
| {
|
||||
type: AppStateActionType.SET_DISCONNECT_NOTICE;
|
||||
isVisible: boolean;
|
||||
};
|
||||
|
||||
type AppStateContextShape = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { AppStateActionType, AppStateContext } from './app-state-context';
|
||||
import type { AppState, AppStateAction } from './app-state-context';
|
||||
import { AppStateActionType, AppStateContext } from './app-state-context';
|
||||
|
||||
interface AppStateProviderProps {
|
||||
children: React.ReactNode;
|
||||
@@ -19,6 +19,7 @@ const initialAppState: AppState = {
|
||||
ethConnectOverlay: false,
|
||||
transactionOverlay: false,
|
||||
bannerMessage: '',
|
||||
disconnectNotice: false,
|
||||
};
|
||||
|
||||
function appStateReducer(state: AppState, action: AppStateAction): AppState {
|
||||
@@ -68,6 +69,12 @@ function appStateReducer(state: AppState, action: AppStateAction): AppState {
|
||||
bannerMessage: action.message,
|
||||
};
|
||||
}
|
||||
case AppStateActionType.SET_DISCONNECT_NOTICE: {
|
||||
return {
|
||||
...state,
|
||||
disconnectNotice: action.isVisible,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,17 @@ import { ContractsContext } from './contracts-context';
|
||||
import { createDefaultProvider } from '../../lib/web3-connectors';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ENV } from '../../config/env';
|
||||
import { ENV } from '../../config';
|
||||
|
||||
/**
|
||||
* Provides Vega Ethereum contract instances to its children.
|
||||
*/
|
||||
export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
const { provider: activeProvider, account } = useWeb3React();
|
||||
const {
|
||||
provider: activeProvider,
|
||||
account,
|
||||
chainId: activeChainId,
|
||||
} = useWeb3React();
|
||||
const { config } = useEthereumConfig();
|
||||
const { VEGA_ENV, ETHEREUM_PROVIDER_URL } = useEnvironment();
|
||||
const [contracts, setContracts] =
|
||||
@@ -39,7 +43,11 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
Number(config.chain_id)
|
||||
);
|
||||
const provider = activeProvider ? activeProvider : defaultProvider;
|
||||
|
||||
const provider =
|
||||
activeProvider && activeChainId === Number(config.chain_id)
|
||||
? activeProvider
|
||||
: defaultProvider;
|
||||
|
||||
if (
|
||||
account &&
|
||||
@@ -84,7 +92,14 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
// TODO: hacky quick fix for release to prevent race condition, find a better fix for this.
|
||||
cancelled = true;
|
||||
};
|
||||
}, [activeProvider, account, config, VEGA_ENV, ETHEREUM_PROVIDER_URL]);
|
||||
}, [
|
||||
activeProvider,
|
||||
activeChainId,
|
||||
account,
|
||||
config,
|
||||
VEGA_ENV,
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
]);
|
||||
|
||||
if (!contracts) {
|
||||
return (
|
||||
|
||||
@@ -790,5 +790,6 @@
|
||||
"approval (% validator voting power)": "approval (% validator voting power)",
|
||||
"67% voting power required": "67% voting power required",
|
||||
"Token": "Token",
|
||||
"associateVegaNow": "Associate $VEGA now"
|
||||
"associateVegaNow": "Associate $VEGA now",
|
||||
"disconnectedNotice": "You have been disconnected. Connect your ETH wallet to the {{correctNetwork}} network to use this app."
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ const mockAppState: AppState = {
|
||||
ethConnectOverlay: false,
|
||||
transactionOverlay: false,
|
||||
bannerMessage: '',
|
||||
disconnectNotice: false,
|
||||
};
|
||||
|
||||
jest.mock('../../../contexts/app-state/app-state-context', () => ({
|
||||
|
||||
+10
-14
@@ -1,8 +1,7 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ProposalRejectionReason, ProposalState } from '@vegaprotocol/types';
|
||||
import { format } from 'date-fns';
|
||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { ProposalChangeTable } from './proposal-change-table';
|
||||
|
||||
@@ -24,16 +23,15 @@ it('Renders all data for table', () => {
|
||||
expect(screen.getByText('Closes on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
format(new Date(proposal?.terms.closingDatetime), DATE_FORMAT_DETAILED)
|
||||
formatDateWithLocalTimezone(new Date(proposal?.terms.closingDatetime))
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Proposed enactment')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
format(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0),
|
||||
DATE_FORMAT_DETAILED
|
||||
formatDateWithLocalTimezone(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0)
|
||||
)
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
@@ -43,7 +41,7 @@ it('Renders all data for table', () => {
|
||||
|
||||
expect(screen.getByText('Proposed on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(format(new Date(proposal?.datetime), DATE_FORMAT_DETAILED))
|
||||
screen.getByText(formatDateWithLocalTimezone(new Date(proposal?.datetime)))
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Type')).toBeInTheDocument();
|
||||
@@ -62,16 +60,15 @@ it('Changes data based on if data is in future or past', () => {
|
||||
expect(screen.getByText('Closed on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
format(new Date(proposal?.terms.closingDatetime), DATE_FORMAT_DETAILED)
|
||||
formatDateWithLocalTimezone(new Date(proposal?.terms.closingDatetime))
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Enacted on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
format(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0),
|
||||
DATE_FORMAT_DETAILED
|
||||
formatDateWithLocalTimezone(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0)
|
||||
)
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
@@ -91,9 +88,8 @@ it('Does not render enactment time for freeform proposal', () => {
|
||||
expect(screen.queryByText('Enacted on')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
format(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0),
|
||||
DATE_FORMAT_DETAILED
|
||||
formatDateWithLocalTimezone(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0)
|
||||
)
|
||||
)
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
+6
-8
@@ -1,12 +1,11 @@
|
||||
import { format, isFuture } from 'date-fns';
|
||||
import { isFuture } from 'date-fns';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
@@ -35,16 +34,15 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
{isFuture(new Date(terms?.closingDatetime))
|
||||
? t('closesOn')
|
||||
: t('closedOn')}
|
||||
{format(new Date(terms?.closingDatetime), DATE_FORMAT_DETAILED)}
|
||||
{formatDateWithLocalTimezone(new Date(terms?.closingDatetime))}
|
||||
</KeyValueTableRow>
|
||||
{terms?.change.__typename !== 'NewFreeform' ? (
|
||||
<KeyValueTableRow>
|
||||
{isFuture(new Date(terms?.enactmentDatetime || 0))
|
||||
? t('proposedEnactment')
|
||||
: t('enactedOn')}
|
||||
{format(
|
||||
new Date(terms?.enactmentDatetime || 0),
|
||||
DATE_FORMAT_DETAILED
|
||||
{formatDateWithLocalTimezone(
|
||||
new Date(terms?.enactmentDatetime || 0)
|
||||
)}
|
||||
</KeyValueTableRow>
|
||||
) : null}
|
||||
@@ -54,7 +52,7 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('proposedOn')}
|
||||
{format(new Date(proposal?.datetime), DATE_FORMAT_DETAILED)}
|
||||
{formatDateWithLocalTimezone(new Date(proposal?.datetime))}
|
||||
</KeyValueTableRow>
|
||||
{proposal?.rejectionReason ? (
|
||||
<KeyValueTableRow>
|
||||
|
||||
@@ -19,6 +19,7 @@ const mockAppState: AppState = {
|
||||
ethConnectOverlay: false,
|
||||
transactionOverlay: false,
|
||||
bannerMessage: '',
|
||||
disconnectNotice: false,
|
||||
};
|
||||
|
||||
jest.mock('../../../contexts/app-state/app-state-context', () => ({
|
||||
|
||||
@@ -9,6 +9,9 @@ query ProposalMarketsQuery {
|
||||
code
|
||||
}
|
||||
}
|
||||
proposal {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
|
||||
export type ProposalMarketsQueryQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ProposalMarketsQueryQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string } } } }> } | null };
|
||||
export type ProposalMarketsQueryQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string } }, proposal?: { __typename?: 'Proposal', state: Types.ProposalState } | null } }> } | null };
|
||||
|
||||
|
||||
export const ProposalMarketsQueryDocument = gql`
|
||||
@@ -21,6 +21,9 @@ export const ProposalMarketsQueryDocument = gql`
|
||||
code
|
||||
}
|
||||
}
|
||||
proposal {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ProposalMarketsQueryQueryVariables = Types.Exact<{
|
||||
[key: string]: never;
|
||||
}>;
|
||||
|
||||
export type ProposalMarketsQueryQuery = {
|
||||
__typename?: 'Query';
|
||||
marketsConnection?: {
|
||||
__typename?: 'MarketConnection';
|
||||
edges: Array<{
|
||||
__typename?: 'MarketEdge';
|
||||
node: {
|
||||
__typename?: 'Market';
|
||||
id: string;
|
||||
tradableInstrument: {
|
||||
__typename?: 'TradableInstrument';
|
||||
instrument: { __typename?: 'Instrument'; name: string; code: string };
|
||||
};
|
||||
};
|
||||
}>;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export const ProposalMarketsQueryDocument = gql`
|
||||
query ProposalMarketsQuery {
|
||||
marketsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
name
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useProposalMarketsQueryQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useProposalMarketsQueryQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useProposalMarketsQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useProposalMarketsQueryQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useProposalMarketsQueryQuery(
|
||||
baseOptions?: Apollo.QueryHookOptions<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useQuery<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>(ProposalMarketsQueryDocument, options);
|
||||
}
|
||||
export function useProposalMarketsQueryLazyQuery(
|
||||
baseOptions?: Apollo.LazyQueryHookOptions<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useLazyQuery<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>(ProposalMarketsQueryDocument, options);
|
||||
}
|
||||
export type ProposalMarketsQueryQueryHookResult = ReturnType<
|
||||
typeof useProposalMarketsQueryQuery
|
||||
>;
|
||||
export type ProposalMarketsQueryLazyQueryHookResult = ReturnType<
|
||||
typeof useProposalMarketsQueryLazyQuery
|
||||
>;
|
||||
export type ProposalMarketsQueryQueryResult = Apollo.QueryResult<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>;
|
||||
+104
-8
@@ -1,15 +1,16 @@
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter as Router } from 'react-router-dom';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { ProposeUpdateMarket } from './propose-update-market';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/react-helpers';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/react-helpers';
|
||||
import type { ProposalMarketsQueryQuery } from './__generated___/UpdateMarket';
|
||||
import { ProposalMarketsQueryDocument } from './__generated___/UpdateMarket';
|
||||
import type { ProposalMarketsQueryQuery } from './__generated__/UpdateMarket';
|
||||
import { ProposalMarketsQueryDocument } from './__generated__/UpdateMarket';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
|
||||
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
request: {
|
||||
@@ -89,6 +90,10 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
||||
code: 'ETHiUSDT',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -101,11 +106,16 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'Las Vegas nuggets',
|
||||
code: 'Nuggets2',
|
||||
code: 'Nuggets',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_OPEN,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
@@ -115,10 +125,14 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'Nuggets',
|
||||
code: 'Nuggets',
|
||||
name: 'California Nuggets',
|
||||
code: 'Nuggets2',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -130,10 +144,71 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'CELUSD (June 2022)',
|
||||
code: 'CELUSD',
|
||||
name: 'Nevada Nuggets',
|
||||
code: 'Nugetts3',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_REJECTED,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'asdfe1755208914b6f258a28babd19ae8dfbaf4084d8867d8a120c50dca1e17f',
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'NZ Nuggets',
|
||||
code: 'Nugetts4',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_DECLINED,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'asdfe1755208914b6f258a28babd19ae8dfbaf4084d8867d8a120c50dca1e17f',
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'Aussie Nuggets',
|
||||
code: 'Nugetts5',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_PASSED,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'xcvbe1755208914b6f258a28babd19ae8dfbaf4084d8867d8a120c50dca1e17f',
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'Tasmanian Nuggets',
|
||||
code: 'Nugetts6',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_FAILED,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -199,6 +274,27 @@ describe('Propose Update Market', () => {
|
||||
expect(screen.getByTestId('proposal-market-select')).toHaveValue('');
|
||||
});
|
||||
|
||||
it('should only render enacted markets', async () => {
|
||||
renderComponent();
|
||||
expect(
|
||||
await screen.findByText('Update market proposal')
|
||||
).toBeInTheDocument();
|
||||
// (state enacted)
|
||||
expect(screen.getByText('ETHUSD (September Market)')).toBeInTheDocument();
|
||||
// (state open)
|
||||
expect(screen.queryByText('Las Vegas nuggets')).not.toBeInTheDocument();
|
||||
// (state waiting for node vote)
|
||||
expect(screen.queryByText('California nuggets')).not.toBeInTheDocument();
|
||||
// (state rejected)
|
||||
expect(screen.queryByText('Nevada nuggets')).not.toBeInTheDocument();
|
||||
// (state declined)
|
||||
expect(screen.queryByText('NZ nuggets')).not.toBeInTheDocument();
|
||||
// (state passed)
|
||||
expect(screen.queryByText('Aussie nuggets')).not.toBeInTheDocument();
|
||||
// (state failed)
|
||||
expect(screen.queryByText('Tasmanian nuggets')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the correct market details when the market select is used', async () => {
|
||||
renderComponent();
|
||||
expect(
|
||||
|
||||
+11
-5
@@ -2,25 +2,28 @@ import { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import {
|
||||
doesValueEquateToParam,
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||
import { NetworkParams, useNetworkParams } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
ProposalFormDescription,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormSubheader,
|
||||
ProposalFormSubmit,
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import {
|
||||
ProposalMinRequirements,
|
||||
ProposalUserAction,
|
||||
} from '../../components/shared';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
ExternalLink,
|
||||
@@ -31,9 +34,9 @@ import {
|
||||
Select,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { useProposalMarketsQueryQuery } from './__generated___/UpdateMarket';
|
||||
import { useProposalMarketsQueryQuery } from './__generated__/UpdateMarket';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
|
||||
export interface UpdateMarketProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@@ -73,6 +76,9 @@ export const ProposeUpdateMarket = () => {
|
||||
|
||||
return marketsData.marketsConnection.edges
|
||||
.map((edge) => edge.node)
|
||||
.filter(
|
||||
(market) => market.proposal?.state === ProposalState.STATE_ENACTED
|
||||
)
|
||||
.sort((a, b) => {
|
||||
const aName = a.tradableInstrument.instrument.name;
|
||||
const bName = b.tradableInstrument.instrument.name;
|
||||
|
||||
+5
-4
@@ -3,7 +3,7 @@ import { forwardRef, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../../../lib/bignumber';
|
||||
import {
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
} from '../../shared';
|
||||
import {
|
||||
defaultColDef,
|
||||
NODE_LIST_GRID_STYLES,
|
||||
PendingStakeRenderer,
|
||||
stakedTotalPercentage,
|
||||
StakeShareRenderer,
|
||||
@@ -382,12 +381,14 @@ export const ConsensusValidatorsTable = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div data-testid="consensus-validators-table">
|
||||
<div
|
||||
data-testid="consensus-validators-table"
|
||||
className="validators-table"
|
||||
>
|
||||
{nodes.length > 0 && (
|
||||
<AgGrid
|
||||
domLayout="autoHeight"
|
||||
style={{ width: '100%' }}
|
||||
customThemeParams={NODE_LIST_GRID_STYLES}
|
||||
getRowHeight={(params: RowHeightParams) => getRowHeight(params)}
|
||||
defaultColDef={defaultColDef}
|
||||
tooltipShowDelay={0}
|
||||
|
||||
@@ -79,17 +79,6 @@ export interface ValidatorsTableProps {
|
||||
validatorsView: ValidatorsView;
|
||||
}
|
||||
|
||||
// Custom styling to account for the scrollbar. This is needed because the
|
||||
// AG Grid places the scrollbar over the bottom validator, which obstructs
|
||||
export const NODE_LIST_GRID_STYLES = `
|
||||
.ag-theme-balham-dark .ag-body-horizontal-scroll {
|
||||
opacity: 0.75;
|
||||
}
|
||||
.ag-theme-balham-dark *:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const stakedTotalPercentage = (stakeScore: string) =>
|
||||
formatNumberPercentage(toBigNum(stakeScore, 0).times(100), 2);
|
||||
|
||||
|
||||
+5
-4
@@ -1,7 +1,7 @@
|
||||
import { forwardRef, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../../../lib/bignumber';
|
||||
import {
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
} from '../../shared';
|
||||
import {
|
||||
defaultColDef,
|
||||
NODE_LIST_GRID_STYLES,
|
||||
StakeNeededForPromotionRenderer,
|
||||
stakedTotalPercentage,
|
||||
ValidatorFields,
|
||||
@@ -248,11 +247,13 @@ export const StandbyPendingValidatorsTable = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div data-testid="standby-pending-validators-table">
|
||||
<div
|
||||
data-testid="standby-pending-validators-table"
|
||||
className="validators-table"
|
||||
>
|
||||
<AgGrid
|
||||
domLayout="autoHeight"
|
||||
style={{ width: '100%' }}
|
||||
customThemeParams={NODE_LIST_GRID_STYLES}
|
||||
rowHeight={68}
|
||||
defaultColDef={defaultColDef}
|
||||
tooltipShowDelay={0}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -19,3 +23,38 @@
|
||||
@apply border-vega-dark-200;
|
||||
}
|
||||
}
|
||||
|
||||
/* AG GRID - Do not edit without updating other global stylesheets for each app */
|
||||
|
||||
.vega-ag-grid .ag-root-wrapper {
|
||||
border: solid 0px;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-react-container {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-cell,
|
||||
.vega-ag-grid .ag-full-width-row .ag-cell-wrapper.ag-row-group {
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
/* Dark variables */
|
||||
.ag-theme-balham-dark {
|
||||
--ag-background-color: theme(colors.black);
|
||||
--ag-border-color: theme(colors.neutral[700]);
|
||||
--ag-header-background-color: theme(colors.black);
|
||||
--ag-odd-row-background-color: theme(colors.black);
|
||||
--ag-header-column-separator-color: theme(colors.neutral[600]);
|
||||
--ag-row-border-color: theme(colors.black);
|
||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
.validators-table .ag-theme-balham-dark .ag-body-horizontal-scroll {
|
||||
opacity: 0.75;
|
||||
}
|
||||
.validators-table .ag-theme-balham-dark *:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
"tranche_start": "2023-04-20T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-20T00:00:00.000Z",
|
||||
"total_added": "19242.125",
|
||||
"total_removed": "494.628795994665",
|
||||
"locked_amount": "14427.42907711226823345",
|
||||
"total_removed": "959.3245960538025",
|
||||
"locked_amount": "10578.4844209587189695125",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "188",
|
||||
@@ -226,6 +226,16 @@
|
||||
"amount": "96.6447222258",
|
||||
"user": "0x1447Efc62d5077d7AbB20492dF831Dd6c9Eb1756",
|
||||
"tx": "0x13b160a21d48eab98cc304bbb46cd675da08afbf6e0ad261c67c10ba34dc4d74"
|
||||
},
|
||||
{
|
||||
"amount": "42.592326391125",
|
||||
"user": "0x1447Efc62d5077d7AbB20492dF831Dd6c9Eb1756",
|
||||
"tx": "0x5f544a659ae728cc7b1a29dcda14740425cadf1f47aa74bae92bf2ade3c8093d"
|
||||
},
|
||||
{
|
||||
"amount": "422.1034736680125",
|
||||
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
|
||||
"tx": "0x5932f574afe019060f5812fc89c7298ab9f768cddebe0a803c27e9914b45dc3d"
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
@@ -281,11 +291,17 @@
|
||||
"user": "0x1447Efc62d5077d7AbB20492dF831Dd6c9Eb1756",
|
||||
"tranche_id": 56,
|
||||
"tx": "0x13b160a21d48eab98cc304bbb46cd675da08afbf6e0ad261c67c10ba34dc4d74"
|
||||
},
|
||||
{
|
||||
"amount": "42.592326391125",
|
||||
"user": "0x1447Efc62d5077d7AbB20492dF831Dd6c9Eb1756",
|
||||
"tranche_id": 56,
|
||||
"tx": "0x5f544a659ae728cc7b1a29dcda14740425cadf1f47aa74bae92bf2ade3c8093d"
|
||||
}
|
||||
],
|
||||
"total_tokens": "1207.5",
|
||||
"withdrawn_tokens": "298.738388303775",
|
||||
"remaining_tokens": "908.761611696225"
|
||||
"withdrawn_tokens": "341.3307146949",
|
||||
"remaining_tokens": "866.1692853051"
|
||||
},
|
||||
{
|
||||
"address": "0x33Ce1D9E53AFb7367E34749517C086405a651a95",
|
||||
@@ -655,10 +671,17 @@
|
||||
"tx": "0x6840e2b579765303fc31a04f43fcea1d5ccf157b46bbd1e3f3e7f6a2e6cea039"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "422.1034736680125",
|
||||
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
|
||||
"tranche_id": 56,
|
||||
"tx": "0x5932f574afe019060f5812fc89c7298ab9f768cddebe0a803c27e9914b45dc3d"
|
||||
}
|
||||
],
|
||||
"total_tokens": "1121.25",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "1121.25"
|
||||
"withdrawn_tokens": "422.1034736680125",
|
||||
"remaining_tokens": "699.1465263319875"
|
||||
},
|
||||
{
|
||||
"address": "0x237D23FcA6d7B2530C7614a9cB921CF27924911E",
|
||||
@@ -820,15 +843,25 @@
|
||||
"tranche_id": 54,
|
||||
"tranche_start": "2023-04-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-06T00:00:00.000Z",
|
||||
"total_added": "8976",
|
||||
"total_removed": "596.64743517951",
|
||||
"locked_amount": "2541.2572777777773456",
|
||||
"total_added": "14520",
|
||||
"total_removed": "5079.76864115505",
|
||||
"locked_amount": "1206.465231481482324",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0x90Cf7B9958C3BbBCddAb52Ecc408535D4f3C4241",
|
||||
"tx": "0x1229e077f48b796678436bfa8143cea3ae71df58b589ac922be205c4482be235"
|
||||
},
|
||||
{
|
||||
"amount": "5010",
|
||||
"user": "0x83D7eD53E7CB97b542F1F71f40561d51F8019C8B",
|
||||
"tx": "0xf0d2927bb8d9363ddefaad3783f9a0abf8e468316c4baea24766991f6cb8cdac"
|
||||
},
|
||||
{
|
||||
"amount": "534",
|
||||
"user": "0x2586bA83696a92b5467Aaa0CF9EEC052F28F2c02",
|
||||
"tx": "0xf970ea0ce3e36fa0014d24bb830dd2ea0dbea06f6e53af486de5ee7e1c63e540"
|
||||
},
|
||||
{
|
||||
"amount": "33",
|
||||
"user": "0xBf1AaB792D729fA125e6D7122D4b916a1E1C44B1",
|
||||
@@ -971,6 +1004,16 @@
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
"tx": "0x4027743264e01043ddb16676c6f7914c110df9e7212bd3f21cb6842614ce238f"
|
||||
},
|
||||
{
|
||||
"amount": "135.13897222254",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
"tx": "0x29905e6ae54245e824fe9ea488f491b1a3c33f6d8b9345cf2b6b00aec305c5bf"
|
||||
},
|
||||
{
|
||||
"amount": "4347.982233753",
|
||||
"user": "0x83D7eD53E7CB97b542F1F71f40561d51F8019C8B",
|
||||
"tx": "0x23b713ac5ee16f744549b0c4d8b49943117d0964ca2b635d9629931ae2f4a961"
|
||||
},
|
||||
{
|
||||
"amount": "5.46866782407",
|
||||
"user": "0x92A1e98C6f09970e45645Cbe57c6DcaF4547c8FE",
|
||||
@@ -1018,6 +1061,43 @@
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "111"
|
||||
},
|
||||
{
|
||||
"address": "0x83D7eD53E7CB97b542F1F71f40561d51F8019C8B",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5010",
|
||||
"user": "0x83D7eD53E7CB97b542F1F71f40561d51F8019C8B",
|
||||
"tranche_id": 54,
|
||||
"tx": "0xf0d2927bb8d9363ddefaad3783f9a0abf8e468316c4baea24766991f6cb8cdac"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "4347.982233753",
|
||||
"user": "0x83D7eD53E7CB97b542F1F71f40561d51F8019C8B",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x23b713ac5ee16f744549b0c4d8b49943117d0964ca2b635d9629931ae2f4a961"
|
||||
}
|
||||
],
|
||||
"total_tokens": "5010",
|
||||
"withdrawn_tokens": "4347.982233753",
|
||||
"remaining_tokens": "662.017766247"
|
||||
},
|
||||
{
|
||||
"address": "0x2586bA83696a92b5467Aaa0CF9EEC052F28F2c02",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "534",
|
||||
"user": "0x2586bA83696a92b5467Aaa0CF9EEC052F28F2c02",
|
||||
"tranche_id": 54,
|
||||
"tx": "0xf970ea0ce3e36fa0014d24bb830dd2ea0dbea06f6e53af486de5ee7e1c63e540"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "534",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "534"
|
||||
},
|
||||
{
|
||||
"address": "0xBf1AaB792D729fA125e6D7122D4b916a1E1C44B1",
|
||||
"deposits": [
|
||||
@@ -1230,6 +1310,12 @@
|
||||
"tranche_id": 54,
|
||||
"tx": "0x4027743264e01043ddb16676c6f7914c110df9e7212bd3f21cb6842614ce238f"
|
||||
},
|
||||
{
|
||||
"amount": "135.13897222254",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x29905e6ae54245e824fe9ea488f491b1a3c33f6d8b9345cf2b6b00aec305c5bf"
|
||||
},
|
||||
{
|
||||
"amount": "155.03186111556",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
@@ -1256,8 +1342,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "858",
|
||||
"withdrawn_tokens": "531.01030786518",
|
||||
"remaining_tokens": "326.98969213482"
|
||||
"withdrawn_tokens": "666.14928008772",
|
||||
"remaining_tokens": "191.85071991228"
|
||||
},
|
||||
{
|
||||
"address": "0x0bBf7580e036eA5D69ABe679CC90117EeC2e3dc1",
|
||||
@@ -1594,7 +1680,7 @@
|
||||
"tranche_start": "2023-03-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-06T00:00:00.000Z",
|
||||
"total_added": "14099",
|
||||
"total_removed": "3112.49002352036",
|
||||
"total_removed": "3722.49002352036",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -2359,6 +2445,16 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "30",
|
||||
"user": "0xBe9F912Ad481C61B653463E8F1D2b2b310D49861",
|
||||
"tx": "0x191a1901917ada560c9c30efd3d24c0ec116b9dc131a6966cc67ad7e3701df3b"
|
||||
},
|
||||
{
|
||||
"amount": "580",
|
||||
"user": "0xAA4110D07E062bAA025BECb15B795447F58B8d2E",
|
||||
"tx": "0x795e8308c0001ad97f322ea94c382cc1799cfd8231a0c492fa8d54f16d2eef56"
|
||||
},
|
||||
{
|
||||
"amount": "30",
|
||||
"user": "0x8499411Bff99ddE9A02903008F917EEa0E27B42A",
|
||||
@@ -2593,10 +2689,17 @@
|
||||
"tx": "0x20d505a766d3d8e376509ed8003a5b9f35fd0524f0979a5a57c7d3714d01d3e5"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "30",
|
||||
"user": "0xBe9F912Ad481C61B653463E8F1D2b2b310D49861",
|
||||
"tranche_id": 53,
|
||||
"tx": "0x191a1901917ada560c9c30efd3d24c0ec116b9dc131a6966cc67ad7e3701df3b"
|
||||
}
|
||||
],
|
||||
"total_tokens": "30",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "30"
|
||||
"withdrawn_tokens": "30",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0xA9821681fEF27Ed817DF77E476DDDAf0aDac4443",
|
||||
@@ -3501,10 +3604,17 @@
|
||||
"tx": "0x46d80a145d2f49ef3152cbfaa6d2bb054deabe21396ab43553a3f076e2db62e1"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "580",
|
||||
"user": "0xAA4110D07E062bAA025BECb15B795447F58B8d2E",
|
||||
"tranche_id": 53,
|
||||
"tx": "0x795e8308c0001ad97f322ea94c382cc1799cfd8231a0c492fa8d54f16d2eef56"
|
||||
}
|
||||
],
|
||||
"total_tokens": "580",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "580"
|
||||
"withdrawn_tokens": "580",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x674038f225586317f3959C00D696Cb53cd52c16C",
|
||||
@@ -4605,7 +4715,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "52591.8412504624188046894",
|
||||
"locked_amount": "51166.9974210106919556663",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "86666.297",
|
||||
@@ -4671,7 +4781,7 @@
|
||||
"tranche_end": "2023-06-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "473.811908577533575",
|
||||
"locked_amount": "391.38319724257215",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
@@ -4704,7 +4814,7 @@
|
||||
"tranche_end": "2023-11-01T00:00:00.000Z",
|
||||
"total_added": "15000.000000000000015",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "15000.000000000000015",
|
||||
"locked_amount": "14795.60027928744001479560027928744",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1.5e-14",
|
||||
@@ -4792,7 +4902,7 @@
|
||||
"tranche_end": "2023-09-01T00:00:00.000Z",
|
||||
"total_added": "17500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "12030.63245395531275",
|
||||
"locked_amount": "11459.903224386071",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
@@ -5058,8 +5168,8 @@
|
||||
"tranche_start": "2023-02-01T00:00:00.000Z",
|
||||
"tranche_end": "2023-08-01T00:00:00.000Z",
|
||||
"total_added": "37500",
|
||||
"total_removed": "12704.026146825",
|
||||
"locked_amount": "19784.56635589318875",
|
||||
"total_removed": "17836.1796921",
|
||||
"locked_amount": "18541.30457719459875",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -5078,6 +5188,16 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x4dc6eefb43f5b1704894582aa926f0c6ecd94550ac354b7d5b5f49b0f17261fd"
|
||||
},
|
||||
{
|
||||
"amount": "4974.3093924",
|
||||
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
|
||||
"tx": "0xfce75d8b6bf0d20cbdb1ef29cfabcae4f4504c30b30ca166b4e66d37f7f04339"
|
||||
},
|
||||
{
|
||||
"amount": "157.844152875",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x6bb6303a6a322698e6e295ad38b2a22bd20d8b44306d423a3e513bb0b858ad56"
|
||||
},
|
||||
{
|
||||
"amount": "126.30064455",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -5242,6 +5362,12 @@
|
||||
"tranche_id": 34,
|
||||
"tx": "0x4dc6eefb43f5b1704894582aa926f0c6ecd94550ac354b7d5b5f49b0f17261fd"
|
||||
},
|
||||
{
|
||||
"amount": "157.844152875",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 34,
|
||||
"tx": "0x6bb6303a6a322698e6e295ad38b2a22bd20d8b44306d423a3e513bb0b858ad56"
|
||||
},
|
||||
{
|
||||
"amount": "126.30064455",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -5406,8 +5532,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "7500",
|
||||
"withdrawn_tokens": "3468.576868425",
|
||||
"remaining_tokens": "4031.423131575"
|
||||
"withdrawn_tokens": "3626.4210213",
|
||||
"remaining_tokens": "3873.5789787"
|
||||
},
|
||||
{
|
||||
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
|
||||
@@ -5420,6 +5546,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "4974.3093924",
|
||||
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
|
||||
"tranche_id": 34,
|
||||
"tx": "0xfce75d8b6bf0d20cbdb1ef29cfabcae4f4504c30b30ca166b4e66d37f7f04339"
|
||||
},
|
||||
{
|
||||
"amount": "6960.3360957",
|
||||
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
|
||||
@@ -5434,8 +5566,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "30000",
|
||||
"withdrawn_tokens": "9235.4492784",
|
||||
"remaining_tokens": "20764.5507216"
|
||||
"withdrawn_tokens": "14209.7586708",
|
||||
"remaining_tokens": "15790.2413292"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -5445,7 +5577,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "52543.857812680580054667",
|
||||
"locked_amount": "51120.313973942497539045",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -5478,7 +5610,7 @@
|
||||
"tranche_end": "2024-04-01T00:00:00.000Z",
|
||||
"total_added": "54144.7663",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "50223.4879614053898767713",
|
||||
"locked_amount": "49335.74898575644407623565",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "54144.7663",
|
||||
@@ -5511,7 +5643,7 @@
|
||||
"tranche_end": "2023-09-03T00:00:00.000Z",
|
||||
"total_added": "62600",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "22037.516533485537",
|
||||
"locked_amount": "21008.336485286656638",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10000",
|
||||
@@ -5704,7 +5836,7 @@
|
||||
"tranche_end": "2023-09-17T00:00:00.000Z",
|
||||
"total_added": "5000",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1951.96584855403365",
|
||||
"locked_amount": "1869.76296930492115",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5000",
|
||||
@@ -6244,8 +6376,8 @@
|
||||
"tranche_start": "2022-11-01T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-01T00:00:00.000Z",
|
||||
"total_added": "22500",
|
||||
"total_removed": "7280.725809525",
|
||||
"locked_amount": "434.275725138121845",
|
||||
"total_removed": "7438.558452225",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -6264,6 +6396,11 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xa9dd827abcfaa4b289c6bcf5923d455380c1d4f9ce4ce615415a513df60b1ea8"
|
||||
},
|
||||
{
|
||||
"amount": "157.8326427",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xb76564ca91603758216069d7b139c683037029425e10b1694af25b478c19e26f"
|
||||
},
|
||||
{
|
||||
"amount": "126.2948895",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -6483,6 +6620,12 @@
|
||||
"tranche_id": 33,
|
||||
"tx": "0xa9dd827abcfaa4b289c6bcf5923d455380c1d4f9ce4ce615415a513df60b1ea8"
|
||||
},
|
||||
{
|
||||
"amount": "157.8326427",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 33,
|
||||
"tx": "0xb76564ca91603758216069d7b139c683037029425e10b1694af25b478c19e26f"
|
||||
},
|
||||
{
|
||||
"amount": "126.2948895",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -6725,8 +6868,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "7500",
|
||||
"withdrawn_tokens": "7280.725809525",
|
||||
"remaining_tokens": "219.274190475"
|
||||
"withdrawn_tokens": "7438.558452225",
|
||||
"remaining_tokens": "61.441547775"
|
||||
},
|
||||
{
|
||||
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
|
||||
@@ -6751,7 +6894,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "1709370.7872515768348",
|
||||
"locked_amount": "188643.4559650818349440268",
|
||||
"locked_amount": "156749.9162904687626235986",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -9771,10 +9914,15 @@
|
||||
"tranche_id": 10,
|
||||
"tranche_start": "2021-07-15T23:37:11.000Z",
|
||||
"tranche_end": "2021-07-15T23:37:11.000Z",
|
||||
"total_added": "6459302.299000000000000001",
|
||||
"total_removed": "6413483.280000000000000001",
|
||||
"total_added": "6559302.299000000000000001",
|
||||
"total_removed": "6513483.280000000000000001",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "100000",
|
||||
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
|
||||
"tx": "0xfe7ab31de818986a1984617dea435e8bb02fba8fca50c1e173b5808fc8c2a81e"
|
||||
},
|
||||
{
|
||||
"amount": "100000",
|
||||
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
|
||||
@@ -10312,6 +10460,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "100000",
|
||||
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
|
||||
"tx": "0xfe7ab31de818986a1984617dea435e8bb02fba8fca50c1e173b5808fc8c2a81e"
|
||||
},
|
||||
{
|
||||
"amount": "100000",
|
||||
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
|
||||
@@ -10792,6 +10945,12 @@
|
||||
{
|
||||
"address": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "100000",
|
||||
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
|
||||
"tranche_id": 10,
|
||||
"tx": "0xfe7ab31de818986a1984617dea435e8bb02fba8fca50c1e173b5808fc8c2a81e"
|
||||
},
|
||||
{
|
||||
"amount": "100000",
|
||||
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
|
||||
@@ -10992,6 +11151,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "100000",
|
||||
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
|
||||
"tranche_id": 10,
|
||||
"tx": "0xfe7ab31de818986a1984617dea435e8bb02fba8fca50c1e173b5808fc8c2a81e"
|
||||
},
|
||||
{
|
||||
"amount": "100000",
|
||||
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
|
||||
@@ -11185,8 +11350,8 @@
|
||||
"tx": "0xac16a4ce688d40a482a59914d68c3a676592f8804ee8f0781b66a4ba5ccfbdfc"
|
||||
}
|
||||
],
|
||||
"total_tokens": "3256651",
|
||||
"withdrawn_tokens": "3256651",
|
||||
"total_tokens": "3356651",
|
||||
"withdrawn_tokens": "3356651",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
@@ -12251,7 +12416,7 @@
|
||||
"tranche_start": "2021-09-03T00:00:00.000Z",
|
||||
"tranche_end": "2022-09-03T00:00:00.000Z",
|
||||
"total_added": "57278.000000000000000003",
|
||||
"total_removed": "48182.21518131551",
|
||||
"total_removed": "50196.35141572991",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -22836,6 +23001,16 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "725",
|
||||
"user": "0x52Dd36AB8e8eec8f6bE968E93679838dc73b19D3",
|
||||
"tx": "0xbc67aa993d42656a84c7da90e6abcc0a3c00e7e9e21da4013251b0f8582567ca"
|
||||
},
|
||||
{
|
||||
"amount": "1289.1362344144",
|
||||
"user": "0xab6dE081Af6C78433AFDaC5B756804bcE17e9eC1",
|
||||
"tx": "0x9e6077e2dc83701a70b2a04028ab533e89cd7f1bb757ea4fe45e1388cfee600d"
|
||||
},
|
||||
{
|
||||
"amount": "25",
|
||||
"user": "0x363D3d06d70761c34D6EfEB25E409A9549E6970D",
|
||||
@@ -32345,10 +32520,17 @@
|
||||
"tx": "0x1a0e42b837a8f3908bc4e36e9b024f48c371b98f066a0db36fd98793cd178f57"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "725",
|
||||
"user": "0x52Dd36AB8e8eec8f6bE968E93679838dc73b19D3",
|
||||
"tranche_id": 11,
|
||||
"tx": "0xbc67aa993d42656a84c7da90e6abcc0a3c00e7e9e21da4013251b0f8582567ca"
|
||||
}
|
||||
],
|
||||
"total_tokens": "725",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "725"
|
||||
"withdrawn_tokens": "725",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0xab6dE081Af6C78433AFDaC5B756804bcE17e9eC1",
|
||||
@@ -33051,6 +33233,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "1289.1362344144",
|
||||
"user": "0xab6dE081Af6C78433AFDaC5B756804bcE17e9eC1",
|
||||
"tranche_id": 11,
|
||||
"tx": "0x9e6077e2dc83701a70b2a04028ab533e89cd7f1bb757ea4fe45e1388cfee600d"
|
||||
},
|
||||
{
|
||||
"amount": "842.29689345648",
|
||||
"user": "0xab6dE081Af6C78433AFDaC5B756804bcE17e9eC1",
|
||||
@@ -33095,8 +33283,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "3439",
|
||||
"withdrawn_tokens": "2149.8637655856",
|
||||
"remaining_tokens": "1289.1362344144"
|
||||
"withdrawn_tokens": "3439",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x23E18CBa049393DFebC5a17e3b5ec9aF584CAE04",
|
||||
@@ -40578,7 +40766,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "715655.108029600523393",
|
||||
"locked_amount": "314380.63787207732811164918",
|
||||
"locked_amount": "265371.371543329369775774863",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -41970,8 +42158,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15870102.715470999700000001",
|
||||
"total_removed": "864782.86597041115934852",
|
||||
"locked_amount": "6414461.14236901907061884390331141365044606",
|
||||
"total_removed": "867610.78878511272497352",
|
||||
"locked_amount": "6240677.4306629368200704822080834181008381",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -42510,6 +42698,21 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x6d5ce55a076149d5f66e5e84720d3bd46f31e5c79c54632927c1a00d1fdbce37"
|
||||
},
|
||||
{
|
||||
"amount": "980.13570973214325",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x45de80ad7201a69e9ac4f1f964218840867adaf64a7fc6020b51baa5de37816e"
|
||||
},
|
||||
{
|
||||
"amount": "924.25597923393625",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x2e3b57da0245aea9164e96cd7421d82a3f34df2ab34bc9a64b91e629fb5cb31c"
|
||||
},
|
||||
{
|
||||
"amount": "923.531125735486125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xc5cea08b124cdf6264d5b0c7494d63d9de264b429822d9a4e4cfadc43e611a1b"
|
||||
},
|
||||
{
|
||||
"amount": "981.387731774910625",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -44642,6 +44845,24 @@
|
||||
"tranche_id": 2,
|
||||
"tx": "0x6d5ce55a076149d5f66e5e84720d3bd46f31e5c79c54632927c1a00d1fdbce37"
|
||||
},
|
||||
{
|
||||
"amount": "980.13570973214325",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x45de80ad7201a69e9ac4f1f964218840867adaf64a7fc6020b51baa5de37816e"
|
||||
},
|
||||
{
|
||||
"amount": "924.25597923393625",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x2e3b57da0245aea9164e96cd7421d82a3f34df2ab34bc9a64b91e629fb5cb31c"
|
||||
},
|
||||
{
|
||||
"amount": "923.531125735486125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0xc5cea08b124cdf6264d5b0c7494d63d9de264b429822d9a4e4cfadc43e611a1b"
|
||||
},
|
||||
{
|
||||
"amount": "981.387731774910625",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -46150,8 +46371,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "259998.8875",
|
||||
"withdrawn_tokens": "154884.69775377100425",
|
||||
"remaining_tokens": "105114.18974622899575"
|
||||
"withdrawn_tokens": "157712.620568472569875",
|
||||
"remaining_tokens": "102286.266931527430125"
|
||||
},
|
||||
{
|
||||
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
|
||||
@@ -48526,8 +48747,8 @@
|
||||
"tranche_start": "2021-11-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-05T00:00:00.000Z",
|
||||
"total_added": "14597706.0446472999",
|
||||
"total_removed": "6130217.967403708954663132",
|
||||
"locked_amount": "200344.3436236687041787785927410316",
|
||||
"total_removed": "6168897.174282186485373182",
|
||||
"locked_amount": "39908.33059608817917720398993360406",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129284.449",
|
||||
@@ -48761,6 +48982,31 @@
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x0c555d0f3b2b4b3fb50f208d9155b5886c83511a552518af11cadd5d5a06acec"
|
||||
},
|
||||
{
|
||||
"amount": "1358.95419763598962675",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x59bc5ab04aa250d84bd2ffe62dba38ed926c26f8ac2497e9414f3a7df2b3a661"
|
||||
},
|
||||
{
|
||||
"amount": "35040.39233886007943262",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
"tx": "0x956f4f6e867e23aa7a3f0fb29612074de4edaf259eb1597f61b5289add0bedb2"
|
||||
},
|
||||
{
|
||||
"amount": "4.18758350477578368",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
"tx": "0xa83729135615d88392e9ec5164e60ae9fe049b0127c7829c44dc94ee1b28d73e"
|
||||
},
|
||||
{
|
||||
"amount": "1289.709285120855118",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x2c531bd9c61e0a3705f161bba2c03703722f1e41533725684fcccb9176117e0e"
|
||||
},
|
||||
{
|
||||
"amount": "985.963473355830749",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x53d67a8df4d069f956ae1eb76d5e099642dcc8d7e40fa9357be0b8ae879b90c4"
|
||||
},
|
||||
{
|
||||
"amount": "1360.32447632896938825",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -52085,6 +52331,24 @@
|
||||
"tranche_id": 3,
|
||||
"tx": "0x0c555d0f3b2b4b3fb50f208d9155b5886c83511a552518af11cadd5d5a06acec"
|
||||
},
|
||||
{
|
||||
"amount": "1358.95419763598962675",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x59bc5ab04aa250d84bd2ffe62dba38ed926c26f8ac2497e9414f3a7df2b3a661"
|
||||
},
|
||||
{
|
||||
"amount": "1289.709285120855118",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x2c531bd9c61e0a3705f161bba2c03703722f1e41533725684fcccb9176117e0e"
|
||||
},
|
||||
{
|
||||
"amount": "985.963473355830749",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x53d67a8df4d069f956ae1eb76d5e099642dcc8d7e40fa9357be0b8ae879b90c4"
|
||||
},
|
||||
{
|
||||
"amount": "1360.32447632896938825",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -54907,8 +55171,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "359123.469575",
|
||||
"withdrawn_tokens": "354157.83455853103848675",
|
||||
"remaining_tokens": "4965.63501646896151325"
|
||||
"withdrawn_tokens": "357792.4615146437139805",
|
||||
"remaining_tokens": "1331.0080603562860195"
|
||||
},
|
||||
{
|
||||
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
|
||||
@@ -55138,6 +55402,18 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "35040.39233886007943262",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x956f4f6e867e23aa7a3f0fb29612074de4edaf259eb1597f61b5289add0bedb2"
|
||||
},
|
||||
{
|
||||
"amount": "4.18758350477578368",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
"tranche_id": 3,
|
||||
"tx": "0xa83729135615d88392e9ec5164e60ae9fe049b0127c7829c44dc94ee1b28d73e"
|
||||
},
|
||||
{
|
||||
"amount": "5074.06012653876566886",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
@@ -55530,8 +55806,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "1266324.603486",
|
||||
"withdrawn_tokens": "1220819.41847917671599364",
|
||||
"remaining_tokens": "45505.18500682328400636"
|
||||
"withdrawn_tokens": "1255863.99840154157120994",
|
||||
"remaining_tokens": "10460.60508445842879006"
|
||||
},
|
||||
{
|
||||
"address": "0xC5d9221EB9c28A69859264c0A2Fe0d3272228296",
|
||||
@@ -56365,7 +56641,7 @@
|
||||
"tranche_start": "2021-10-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "5778205.3912159303",
|
||||
"total_removed": "3480709.486904665102263769",
|
||||
"total_removed": "3480901.398845846498418769",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -56515,6 +56791,11 @@
|
||||
"user": "0x83b1a48376E045D26420200345414e6b93066396",
|
||||
"tx": "0xeb193da202cd833a574a524eff7f1dd13a0ee8669e51a7db36984ef4d6735fd6"
|
||||
},
|
||||
{
|
||||
"amount": "191.911941181396155",
|
||||
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
|
||||
"tx": "0xb741f4e653131d11eed34bc9c5f3f8425d23cf054b336f3dc61fbd936b39744a"
|
||||
},
|
||||
{
|
||||
"amount": "219.945520329711288",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
@@ -57537,6 +57818,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "191.911941181396155",
|
||||
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
|
||||
"tranche_id": 4,
|
||||
"tx": "0xb741f4e653131d11eed34bc9c5f3f8425d23cf054b336f3dc61fbd936b39744a"
|
||||
},
|
||||
{
|
||||
"amount": "10509.407528939748325",
|
||||
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
|
||||
@@ -57647,8 +57934,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "110499.5291",
|
||||
"withdrawn_tokens": "110307.617158818603845",
|
||||
"remaining_tokens": "191.911941181396155"
|
||||
"withdrawn_tokens": "110499.5291",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0xdbC5d439F373EB646345e1c67D1d46231ACE7dD3",
|
||||
@@ -58559,8 +58846,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "43935.8732690563416",
|
||||
"locked_amount": "49815.409147170801381683055403356",
|
||||
"total_removed": "44078.8527972103416",
|
||||
"locked_amount": "42049.610748470926039581595332312",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
@@ -65204,6 +65491,16 @@
|
||||
"user": "0x0a6A5e1da768709A2bF17b2E58B2B73120051D03",
|
||||
"tx": "0x097e43cb4f64f1cb502f321eceb8b91e6378cac0b3b6fe558fc1d543aec597e2"
|
||||
},
|
||||
{
|
||||
"amount": "61.181050224",
|
||||
"user": "0xe7770271d882C54C46B9f00137e96a3BbfFD4d94",
|
||||
"tx": "0x3c7d0394e431000fa5c733213116dc39b0f87a9a32a40e75cec3c2fce7795b0b"
|
||||
},
|
||||
{
|
||||
"amount": "81.79847793",
|
||||
"user": "0x4d4AA3f302cbA998AAFC03F9d73484434ed9Fd53",
|
||||
"tx": "0x1e4902d0a3cb7b4f2ba7119dfcaa681a784e1d724c984961767e9feaa078fe54"
|
||||
},
|
||||
{
|
||||
"amount": "168.502276762",
|
||||
"user": "0x38A292CB98Dc602ECAFF94E8E5fADD9800e4bA13",
|
||||
@@ -78209,6 +78506,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "61.181050224",
|
||||
"user": "0xe7770271d882C54C46B9f00137e96a3BbfFD4d94",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x3c7d0394e431000fa5c733213116dc39b0f87a9a32a40e75cec3c2fce7795b0b"
|
||||
},
|
||||
{
|
||||
"amount": "101.521461192",
|
||||
"user": "0xe7770271d882C54C46B9f00137e96a3BbfFD4d94",
|
||||
@@ -78247,8 +78550,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "600",
|
||||
"withdrawn_tokens": "476.48491248",
|
||||
"remaining_tokens": "123.51508752"
|
||||
"withdrawn_tokens": "537.665962704",
|
||||
"remaining_tokens": "62.334037296"
|
||||
},
|
||||
{
|
||||
"address": "0x79D8ff1699911f065fD97a95a6B47216925C4B0e",
|
||||
@@ -87342,6 +87645,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "81.79847793",
|
||||
"user": "0x4d4AA3f302cbA998AAFC03F9d73484434ed9Fd53",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x1e4902d0a3cb7b4f2ba7119dfcaa681a784e1d724c984961767e9feaa078fe54"
|
||||
},
|
||||
{
|
||||
"amount": "32.02214612",
|
||||
"user": "0x4d4AA3f302cbA998AAFC03F9d73484434ed9Fd53",
|
||||
@@ -87368,8 +87677,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "200",
|
||||
"withdrawn_tokens": "99.05242897",
|
||||
"remaining_tokens": "100.94757103"
|
||||
"withdrawn_tokens": "180.8509069",
|
||||
"remaining_tokens": "19.1490931"
|
||||
},
|
||||
{
|
||||
"address": "0xd9390b46a1749DEe5325A490490491db9a826D1F",
|
||||
@@ -88304,7 +88613,7 @@
|
||||
"tranche_start": "2021-12-05T00:00:00.000Z",
|
||||
"tranche_end": "2022-06-05T00:00:00.000Z",
|
||||
"total_added": "171288.42",
|
||||
"total_removed": "69890.5995794947989",
|
||||
"total_removed": "70140.5995794947989",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -92529,6 +92838,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x0E3407C9A94effa675471afe7A9D37e687C32141",
|
||||
"tx": "0x6086c36011fd863c6a34f1a7cd45a798bb1bf6bde6f0e4106c51071c46dbef79"
|
||||
},
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x99492aF57325BA3971542B9ea4036186BE50fa07",
|
||||
@@ -95803,10 +96117,17 @@
|
||||
"tx": "0xee02e72ef8e0755982704385da512cc12c8ff0ea7b58056fa3d5a5932394e9ca"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x0E3407C9A94effa675471afe7A9D37e687C32141",
|
||||
"tranche_id": 6,
|
||||
"tx": "0x6086c36011fd863c6a34f1a7cd45a798bb1bf6bde6f0e4106c51071c46dbef79"
|
||||
}
|
||||
],
|
||||
"total_tokens": "250",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "250"
|
||||
"withdrawn_tokens": "250",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x953680303b33898962AB7FadCD485374CeDFf87e",
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
|
||||
import { addDays, subDays } from 'date-fns';
|
||||
import {
|
||||
chainIdQuery,
|
||||
statisticsQuery,
|
||||
createDataConnection,
|
||||
oracleSpecDataConnectionQuery,
|
||||
createMarketFragment,
|
||||
marketsQuery,
|
||||
marketsDataQuery,
|
||||
createMarketsDataFragment,
|
||||
assetQuery,
|
||||
networkParamsQuery,
|
||||
} from '@vegaprotocol/mock';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
getDateTimeFormat,
|
||||
} from '@vegaprotocol/utils';
|
||||
|
||||
describe('Closed markets', { tags: '@smoke' }, () => {
|
||||
const rowSelector =
|
||||
'[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row';
|
||||
|
||||
const assetsResult = assetQuery();
|
||||
// @ts-ignore asset definitely exists
|
||||
const settlementAsset = assetsResult.assetsConnection.edges[0].node;
|
||||
|
||||
const settledMarket = createMarketFragment({
|
||||
id: '0',
|
||||
state: MarketState.STATE_SETTLED,
|
||||
marketTimestamps: {
|
||||
open: subDays(new Date(), 10).toISOString(),
|
||||
close: subDays(new Date(), 4).toISOString(),
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: 'market-1-trading-termination-oracle-id',
|
||||
},
|
||||
dataSourceSpecForSettlementData: {
|
||||
id: 'market-1-settlement-data-oracle-id',
|
||||
},
|
||||
settlementAsset,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const terminatedMarket = createMarketFragment({
|
||||
id: '1',
|
||||
state: MarketState.STATE_TRADING_TERMINATED,
|
||||
marketTimestamps: {
|
||||
open: subDays(new Date(), 10).toISOString(),
|
||||
close: null, // market
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
metadata: {
|
||||
tags: [
|
||||
`settlement-expiry-date:${addDays(new Date(), 4).toISOString()}`,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const delayedSettledMarket = createMarketFragment({
|
||||
id: '2',
|
||||
state: MarketState.STATE_TRADING_TERMINATED,
|
||||
marketTimestamps: {
|
||||
open: subDays(new Date(), 10).toISOString(),
|
||||
close: null, // market
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
metadata: {
|
||||
tags: [
|
||||
`settlement-expiry-date:${subDays(new Date(), 2).toISOString()}`,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const unknownMarket = createMarketFragment({
|
||||
id: '3',
|
||||
state: MarketState.STATE_SETTLED,
|
||||
});
|
||||
|
||||
const closedMarketsResult = [
|
||||
{
|
||||
node: settledMarket,
|
||||
},
|
||||
{
|
||||
node: terminatedMarket,
|
||||
},
|
||||
{
|
||||
node: delayedSettledMarket,
|
||||
},
|
||||
{ node: unknownMarket },
|
||||
{
|
||||
node: createMarketFragment({ id: '4', state: MarketState.STATE_PENDING }),
|
||||
},
|
||||
{
|
||||
node: createMarketFragment({ id: '5', state: MarketState.STATE_ACTIVE }),
|
||||
},
|
||||
];
|
||||
|
||||
const settledMarketData = createMarketsDataFragment({
|
||||
market: {
|
||||
id: settledMarket.id,
|
||||
},
|
||||
bestBidPrice: '1000',
|
||||
bestOfferPrice: '2000',
|
||||
markPrice: '1500',
|
||||
});
|
||||
|
||||
const closedMarketsDataResult = [
|
||||
{
|
||||
node: {
|
||||
data: settledMarketData,
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
data: createMarketsDataFragment({
|
||||
market: {
|
||||
id: terminatedMarket.id,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
data: createMarketsDataFragment({
|
||||
market: {
|
||||
id: delayedSettledMarket.id,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
data: createMarketsDataFragment({
|
||||
market: {
|
||||
id: unknownMarket.id,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const specDataConnection = createDataConnection();
|
||||
|
||||
before(() => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'Markets',
|
||||
marketsQuery({
|
||||
marketsConnection: {
|
||||
edges: closedMarketsResult,
|
||||
},
|
||||
})
|
||||
);
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'MarketsData',
|
||||
marketsDataQuery({
|
||||
marketsConnection: {
|
||||
edges: closedMarketsDataResult,
|
||||
},
|
||||
})
|
||||
);
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'OracleSpecDataConnection',
|
||||
oracleSpecDataConnectionQuery()
|
||||
);
|
||||
});
|
||||
|
||||
cy.mockSubscription();
|
||||
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Closed markets"]').click();
|
||||
});
|
||||
|
||||
it('renders a settled market', () => {
|
||||
const expectedMarkets = closedMarketsResult.filter((edge) => {
|
||||
return [
|
||||
MarketState.STATE_SETTLED,
|
||||
MarketState.STATE_TRADING_TERMINATED,
|
||||
].includes(edge.node.state);
|
||||
});
|
||||
const product = settledMarket.tradableInstrument.instrument.product;
|
||||
|
||||
// rows should be filtered to only include settled/terminated markets
|
||||
cy.get(rowSelector).should('have.length', expectedMarkets.length);
|
||||
|
||||
// check each column in the first row renders correctly
|
||||
// 6001-MARK-001
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="code"]')
|
||||
.should('have.text', settledMarket.tradableInstrument.instrument.code);
|
||||
|
||||
// 6001-MARK-002
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="name"]')
|
||||
.should('have.text', settledMarket.tradableInstrument.instrument.name);
|
||||
|
||||
// 6001-MARK-003
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="state"]')
|
||||
.should('have.text', MarketStateMapping[settledMarket.state]);
|
||||
|
||||
// 6001-MARK-004
|
||||
// 6001-MARK-005
|
||||
// 6001-MARK-009
|
||||
// 6001-MARK-008
|
||||
// 6001-MARK-010
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="settlementDate"]')
|
||||
.find('[data-testid="link"]')
|
||||
.should(($el) => {
|
||||
const href = $el.attr('href');
|
||||
expect(href).to.match(
|
||||
new RegExp(
|
||||
`/oracles/${product.dataSourceSpecForTradingTermination.id}`
|
||||
)
|
||||
);
|
||||
})
|
||||
.should('have.text', '4 days ago')
|
||||
.should(
|
||||
'have.attr',
|
||||
'title',
|
||||
getDateTimeFormat().format(
|
||||
new Date(settledMarket.marketTimestamps.close)
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-011
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="bestBidPrice"]')
|
||||
.should(
|
||||
'have.text',
|
||||
addDecimalsFormatNumber(
|
||||
settledMarketData.bestBidPrice,
|
||||
settledMarket.decimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-012
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="bestOfferPrice"]')
|
||||
.should(
|
||||
'have.text',
|
||||
addDecimalsFormatNumber(
|
||||
settledMarketData.bestOfferPrice,
|
||||
settledMarket.decimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-013
|
||||
cy.get(rowSelector).first().find('[col-id="markPrice"]').should(
|
||||
'have.text',
|
||||
|
||||
addDecimalsFormatNumber(
|
||||
settledMarketData.markPrice,
|
||||
settledMarket.decimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-014
|
||||
// 6001-MARK-015
|
||||
// 6001-MARK-016
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="settlementDataOracleId"]')
|
||||
.find('[data-testid="link"]')
|
||||
.should(($el) => {
|
||||
const href = $el.attr('href');
|
||||
expect(href).to.match(
|
||||
new RegExp(`/oracles/${product.dataSourceSpecForSettlementData.id}`)
|
||||
);
|
||||
})
|
||||
.should(
|
||||
'have.text',
|
||||
addDecimalsFormatNumber(
|
||||
// @ts-ignore cannot deep un-partial
|
||||
specDataConnection.externalData.data.data[0].value,
|
||||
settledMarket.decimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-017
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="realisedPNL"]')
|
||||
.should('have.text', '-');
|
||||
|
||||
// 6001-MARK-018
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="settlementAsset"]')
|
||||
.should('have.text', product.settlementAsset.symbol);
|
||||
|
||||
// 6001-MARK-020
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="id"]')
|
||||
.should('have.text', settledMarket.id);
|
||||
});
|
||||
|
||||
// test market list for market in terminated state
|
||||
it('renders a terminated market', () => {
|
||||
cy.get(rowSelector)
|
||||
.eq(1)
|
||||
.find('[col-id="state"]')
|
||||
.should('have.text', MarketStateMapping[terminatedMarket.state]);
|
||||
|
||||
// 6001-MARK-006
|
||||
// 6001-MARK-007
|
||||
cy.get(rowSelector)
|
||||
.eq(1)
|
||||
.find('[col-id="settlementDate"]')
|
||||
.find('[data-testid="link"]')
|
||||
.should('have.text', 'Expected in 4 days');
|
||||
});
|
||||
|
||||
it('renders a terminated market which was expected to have settled', () => {
|
||||
cy.get(rowSelector)
|
||||
.eq(2)
|
||||
.find('[col-id="settlementDate"]')
|
||||
.should('have.class', 'text-danger')
|
||||
.find('[data-testid="link"]')
|
||||
.should('have.text', 'Expected 2 days ago');
|
||||
});
|
||||
|
||||
it('renders terminated market which doesnt have settlement date metadata', () => {
|
||||
cy.get(rowSelector)
|
||||
.eq(3)
|
||||
.find('[col-id="settlementDate"]')
|
||||
.find('[data-testid="link"]')
|
||||
.should('have.text', 'Unknown');
|
||||
});
|
||||
|
||||
it('can open asset detail dialog', () => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Asset', assetsResult);
|
||||
});
|
||||
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="settlementAsset"]')
|
||||
.find('button')
|
||||
.click();
|
||||
|
||||
// 6001-MARK-019
|
||||
cy.get('[data-testid="dialog-title"]').should(
|
||||
'have.text',
|
||||
`Asset details - ${settlementAsset.symbol}`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -101,7 +101,7 @@ describe('deposit actions', { tags: '@smoke' }, () => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
cy.visit('/');
|
||||
cy.visit('/#/markets/market-1');
|
||||
cy.wait('@MarketsCandles');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
});
|
||||
@@ -112,7 +112,6 @@ describe('deposit actions', { tags: '@smoke' }, () => {
|
||||
'be.visible'
|
||||
);
|
||||
cy.contains('[data-testid="deposit"]', 'Deposit to trade').click();
|
||||
connectEthereumWallet('MetaMask');
|
||||
cy.getByTestId('deposit-submit').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -153,7 +153,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
// the choose market overlay is no longer showing
|
||||
cy.contains('Select a market to get started').should('not.exist');
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-1');
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-0');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -172,6 +172,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
],
|
||||
},
|
||||
};
|
||||
// @ts-ignore partial deep check failing
|
||||
const data = marketsDataQuery(override);
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'MarketsData', data);
|
||||
|
||||
@@ -180,25 +180,21 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
it('oracle displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Oracle').click();
|
||||
|
||||
validateMarketDataRow(0, 'Settlement Data Property', 'prices.BTC.value');
|
||||
validateMarketDataRow(
|
||||
1,
|
||||
'Trading Termination Property',
|
||||
'termination.BTC.value'
|
||||
);
|
||||
|
||||
// check that links to github for oracle proofs are shown
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('oracle-proof-links')
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://github.com/vegaprotocol/well-known');
|
||||
.getByTestId('provider-name')
|
||||
.and('contain', 'Another oracle');
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('oracle-spec-links')
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/oracles');
|
||||
.getByTestId('signed-proofs')
|
||||
.and('contain', '1');
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('verified-proofs')
|
||||
.and('contain', '1');
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('signed-proofs')
|
||||
.and('contain', '1');
|
||||
});
|
||||
|
||||
it('proposal displayed', () => {
|
||||
|
||||
@@ -222,6 +222,7 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
],
|
||||
},
|
||||
};
|
||||
// @ts-ignore partial deep check failing
|
||||
const market = marketsQuery(override);
|
||||
aliasGQLQuery(req, 'Market', market);
|
||||
aliasGQLQuery(req, 'ProposalOfMarket', {
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
import { checkSorting } from '@vegaprotocol/cypress';
|
||||
|
||||
const dialogClose = 'dialog-close';
|
||||
|
||||
describe('accounts', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.clearAllLocalStorage();
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Assets');
|
||||
});
|
||||
|
||||
it('renders accounts', () => {
|
||||
// 7001-COLL-001
|
||||
// 7001-COLL-002
|
||||
// 7001-COLL-003
|
||||
// 7001-COLL-004
|
||||
// 7001-COLL-005
|
||||
// 7001-COLL-006
|
||||
// 7001-COLL-007
|
||||
|
||||
const tradingAccountRowId = '[row-id="t-0"]';
|
||||
cy.getByTestId('Collateral').click();
|
||||
|
||||
@@ -22,6 +30,21 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
.find('[col-id="asset.symbol"]')
|
||||
.should('have.text', 'AST0');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="used"]')
|
||||
.should('have.text', '1.010.00%');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="available"]')
|
||||
.should('have.text', '100,000.00');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="total"]')
|
||||
.should('have.text', '100,001.01');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="accounts-actions"]')
|
||||
@@ -31,12 +54,91 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="total"]')
|
||||
.should('have.text', '100,001.01');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get('[col-id="accounts-actions"]')
|
||||
.find('[data-testid="dropdown-menu"]')
|
||||
.eq(1)
|
||||
.click();
|
||||
cy.getByTestId('deposit').should('be.visible');
|
||||
cy.getByTestId('withdraw').should('be.visible');
|
||||
cy.getByTestId('breakdown').should('be.visible');
|
||||
cy.getByTestId('Collateral').click({ force: true });
|
||||
});
|
||||
|
||||
it('should open asset details dialog when clicked on symbol', () => {
|
||||
// 7001-COLL-008
|
||||
// 6501-ASSE-001
|
||||
// 6501-ASSE-002
|
||||
// 6501-ASSE-003
|
||||
// 6501-ASSE-004
|
||||
// 6501-ASSE-005
|
||||
// 6501-ASSE-006
|
||||
// 6501-ASSE-007
|
||||
// 6501-ASSE-008
|
||||
// 6501-ASSE-009
|
||||
// 6501-ASSE-010
|
||||
// 6501-ASSE-011
|
||||
// 6501-ASSE-012
|
||||
// 6501-ASSE-013
|
||||
const titles = [
|
||||
'ID',
|
||||
'Type',
|
||||
'Name',
|
||||
'Symbol',
|
||||
'Decimals',
|
||||
'Quantum',
|
||||
'Status',
|
||||
'Contract address',
|
||||
'Withdrawal threshold',
|
||||
'Lifetime limit',
|
||||
'Infrastructure fee account balance',
|
||||
'Global reward pool account balance',
|
||||
'Maker paid fees account balance',
|
||||
'Maker received fees account balance',
|
||||
'Liquidity provision fee reward account balance',
|
||||
'Market proposer reward account balance',
|
||||
];
|
||||
cy.getByTestId('asset').contains('tEURO').click();
|
||||
cy.get('[data-testid="dialog-content"]:visible').should('exist');
|
||||
cy.get('[data-testid="dialog-close"]:visible').click();
|
||||
cy.get('[data-testid$="_label"]').should('have.length', 16);
|
||||
cy.get('[data-testid$="_label"]').each((element, index) => {
|
||||
cy.wrap(element).should('have.text', titles[index]);
|
||||
});
|
||||
cy.getByTestId(dialogClose).click();
|
||||
cy.getByTestId(dialogClose).should('not.exist');
|
||||
});
|
||||
|
||||
it('should open usage breakdown dialog when clicked on used', () => {
|
||||
// 7001-COLL-009
|
||||
cy.getByTestId('breakdown').contains('1.01').click();
|
||||
const headers = ['Market', 'Account type', 'Balance'];
|
||||
cy.getByTestId('usage-breakdown').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
});
|
||||
cy.getByTestId(dialogClose).click();
|
||||
});
|
||||
|
||||
it('sorting usage breakdown columns should work well', () => {
|
||||
// 7001-COLL-010
|
||||
cy.getByTestId('breakdown').contains('1.01').click();
|
||||
cy.getByTestId('usage-breakdown')
|
||||
.find('[col-id="type"]')
|
||||
.eq(1)
|
||||
.should('have.text', 'Margin');
|
||||
cy.getByTestId('usage-breakdown')
|
||||
.find('[col-id="type"]')
|
||||
.eq(2)
|
||||
.should('have.text', 'Margin');
|
||||
cy.getByTestId('usage-breakdown')
|
||||
.find('[col-id="type"]')
|
||||
.eq(3)
|
||||
.should('have.text', 'General');
|
||||
|
||||
cy.getByTestId(dialogClose).click();
|
||||
});
|
||||
|
||||
describe('sorting by ag-grid columns should work well', () => {
|
||||
|
||||
@@ -88,5 +88,18 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
'Size cannot be lower than 1'
|
||||
);
|
||||
});
|
||||
it('must have total margin available', () => {
|
||||
// 7001-COLL-011
|
||||
cy.getByTestId('tab-ticket')
|
||||
.find('.text-xs')
|
||||
.eq(5)
|
||||
.within(() => {
|
||||
cy.get('[data-state="closed"]').should(
|
||||
'have.text',
|
||||
'Total margin available'
|
||||
);
|
||||
cy.get('.text-neutral-500').should('have.text', '~100,000.01 tDAI');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('fills', { tags: '@regression' }, () => {
|
||||
cy.wrap($role)
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
const roles = ['Maker', 'Taker'];
|
||||
const roles = ['Maker', 'Taker', '-'];
|
||||
expect(roles.indexOf(text.trim())).to.be.greaterThan(-1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -455,7 +455,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
|
||||
});
|
||||
});
|
||||
it('must be warned (pre-submit) if the input price has too many digits after the decimal place for the market', () => {
|
||||
// 7003-MORD-016
|
||||
// 7003-MORD-013
|
||||
updateOrder({
|
||||
id: orderId,
|
||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
||||
|
||||
@@ -26,6 +26,7 @@ export function updateOrder(
|
||||
override?: PartialDeep<OrderUpdateFieldsFragment>
|
||||
): void {
|
||||
const update: OrdersUpdateSubscription = orderUpdateSubscription({
|
||||
// @ts-ignore partial deep check failing
|
||||
orders: [override],
|
||||
});
|
||||
if (!sendOrderUpdate) {
|
||||
|
||||
@@ -46,7 +46,9 @@ const marketDataOverride = (
|
||||
{
|
||||
node: {
|
||||
data: {
|
||||
// @ts-ignore conflict between incoming and outgoing types
|
||||
trigger: data.trigger,
|
||||
// @ts-ignore same as above
|
||||
marketTradingMode: data.tradingMode,
|
||||
marketState: data.state,
|
||||
},
|
||||
@@ -63,6 +65,7 @@ const marketsDataOverride = (
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
// @ts-ignore conflict between incoming and outgoing types
|
||||
tradingMode: data.tradingMode,
|
||||
state: data.state,
|
||||
},
|
||||
|
||||
@@ -27,7 +27,10 @@ import { NO_MARKET } from './constants';
|
||||
import { LiquidityContainer } from '../liquidity/liquidity';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
usePaneLayout,
|
||||
useScreenDimensions,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
useMarketClickHandler,
|
||||
useMarketLiquidityClickHandler,
|
||||
@@ -83,15 +86,20 @@ interface BottomPanelProps {
|
||||
|
||||
const MarketBottomPanel = memo(
|
||||
({ marketId, pinnedAsset }: BottomPanelProps) => {
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' });
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
return 'xxxl' === screenSize ? (
|
||||
<ResizableGrid proportionalLayout minSize={200}>
|
||||
<ResizableGrid
|
||||
proportionalLayout
|
||||
minSize={200}
|
||||
onChange={handleOnLayoutChange}
|
||||
>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize="50%"
|
||||
preferredSize={sizes[0] || '50%'}
|
||||
minSize={50}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@@ -119,7 +127,7 @@ const MarketBottomPanel = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize="50%"
|
||||
preferredSize={sizes[1] || '50%'}
|
||||
minSize={50}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@@ -186,22 +194,29 @@ MarketBottomPanel.displayName = 'MarketBottomPanel';
|
||||
const MainGrid = memo(
|
||||
({
|
||||
marketId,
|
||||
onSelect,
|
||||
pinnedAsset,
|
||||
}: {
|
||||
marketId: string;
|
||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' });
|
||||
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
|
||||
id: 'middle',
|
||||
});
|
||||
|
||||
return (
|
||||
<ResizableGrid vertical>
|
||||
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
||||
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
|
||||
<ResizableGrid proportionalLayout={false} minSize={200}>
|
||||
<ResizableGrid
|
||||
proportionalLayout={false}
|
||||
minSize={200}
|
||||
onChange={handleOnMiddleLayoutChange}
|
||||
>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.High}
|
||||
minSize={200}
|
||||
preferredSize="50%"
|
||||
preferredSize={sizesMiddle[0] || '50%'}
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-main-left">
|
||||
@@ -219,7 +234,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize={330}
|
||||
preferredSize={sizesMiddle[1] || 330}
|
||||
minSize={300}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@@ -238,7 +253,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize={430}
|
||||
preferredSize={sizesMiddle[2] || 430}
|
||||
minSize={200}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@@ -256,7 +271,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize="25%"
|
||||
preferredSize={sizes[1] || '25%'}
|
||||
minSize={50}
|
||||
>
|
||||
<MarketBottomPanel marketId={marketId} pinnedAsset={pinnedAsset} />
|
||||
@@ -278,11 +293,7 @@ export const TradeGrid = ({
|
||||
<TradeMarketHeader market={market} onSelect={onSelect} />
|
||||
<OracleBanner marketId={market?.id || ''} />
|
||||
</div>
|
||||
<MainGrid
|
||||
marketId={market?.id || ''}
|
||||
onSelect={onSelect}
|
||||
pinnedAsset={pinnedAsset}
|
||||
/>
|
||||
<MainGrid marketId={market?.id || ''} pinnedAsset={pinnedAsset} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
import { act, render, screen, within } from '@testing-library/react';
|
||||
import { Closed } from './closed';
|
||||
import { MarketStateMapping } from '@vegaprotocol/types';
|
||||
import { PositionStatus } from '@vegaprotocol/types';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { subDays } from 'date-fns';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { OracleSpecDataConnectionQuery } from '@vegaprotocol/oracles';
|
||||
import { OracleSpecDataConnectionDocument } from '@vegaprotocol/oracles';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import type {
|
||||
PositionsQuery,
|
||||
PositionFieldsFragment,
|
||||
} from '@vegaprotocol/positions';
|
||||
import { PositionsDocument } from '@vegaprotocol/positions';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import type { MarketsDataQuery, MarketsQuery } from '@vegaprotocol/market-list';
|
||||
import { MarketsDataDocument } from '@vegaprotocol/market-list';
|
||||
import { MarketsDocument } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
createMarketFragment,
|
||||
marketsQuery,
|
||||
marketsDataQuery,
|
||||
createMarketsDataFragment,
|
||||
} from '@vegaprotocol/mock';
|
||||
|
||||
describe('Closed', () => {
|
||||
let originalNow: typeof Date.now;
|
||||
const mockNowTimestamp = 1672531200000;
|
||||
const settlementDateMetaDate = subDays(
|
||||
new Date(mockNowTimestamp),
|
||||
3
|
||||
).toISOString();
|
||||
const settlementDateTag = `settlement-expiry-date:${settlementDateMetaDate}`;
|
||||
const pubKey = 'pubKey';
|
||||
const marketId = 'market-0';
|
||||
const settlementDataProperty = 'spec-binding';
|
||||
const settlementDataId = 'settlement-data-oracle-id';
|
||||
|
||||
const market = createMarketFragment({
|
||||
id: marketId,
|
||||
state: MarketState.STATE_SETTLED,
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
metadata: {
|
||||
tags: [settlementDateTag],
|
||||
},
|
||||
product: {
|
||||
dataSourceSpecForSettlementData: {
|
||||
id: settlementDataId,
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
settlementDataProperty,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const marketsMock: MockedResponse<MarketsQuery> = {
|
||||
request: {
|
||||
query: MarketsDocument,
|
||||
},
|
||||
result: {
|
||||
data: marketsQuery({
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: market,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const marketsData = createMarketsDataFragment({
|
||||
__typename: 'MarketData',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: marketId,
|
||||
},
|
||||
bestBidPrice: '1000',
|
||||
bestOfferPrice: '2000',
|
||||
markPrice: '1500',
|
||||
});
|
||||
const marketsDataMock: MockedResponse<MarketsDataQuery> = {
|
||||
request: {
|
||||
query: MarketsDataDocument,
|
||||
},
|
||||
result: {
|
||||
data: marketsDataQuery({
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
data: marketsData,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
// Create mock oracle data
|
||||
const property = {
|
||||
__typename: 'Property' as const,
|
||||
name: settlementDataProperty,
|
||||
value: '12345',
|
||||
};
|
||||
const oracleDataMock: MockedResponse<OracleSpecDataConnectionQuery> = {
|
||||
request: {
|
||||
query: OracleSpecDataConnectionDocument,
|
||||
variables: {
|
||||
oracleSpecId: settlementDataId,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
oracleSpec: {
|
||||
dataConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
externalData: {
|
||||
data: {
|
||||
data: [property],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Create mock position
|
||||
const createPosition = (): PositionFieldsFragment => {
|
||||
return {
|
||||
__typename: 'Position' as const,
|
||||
realisedPNL: '1000',
|
||||
unrealisedPNL: '2000',
|
||||
openVolume: '3000',
|
||||
averageEntryPrice: '100',
|
||||
updatedAt: new Date().toISOString(),
|
||||
positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED,
|
||||
lossSocializationAmount: '1000',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: marketId,
|
||||
},
|
||||
};
|
||||
};
|
||||
const position = createPosition();
|
||||
const positionsMock: MockedResponse<PositionsQuery> = {
|
||||
request: {
|
||||
query: PositionsDocument,
|
||||
variables: {
|
||||
partyId: pubKey,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
party: {
|
||||
__typename: 'Party',
|
||||
id: pubKey,
|
||||
positionsConnection: {
|
||||
__typename: 'PositionConnection',
|
||||
edges: [{ __typename: 'PositionEdge', node: position }],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
originalNow = Date.now;
|
||||
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Date.now = originalNow;
|
||||
});
|
||||
|
||||
it('renders correctly formatted and filtered rows', async () => {
|
||||
await act(async () => {
|
||||
render(
|
||||
<MockedProvider
|
||||
mocks={[marketsMock, marketsDataMock, positionsMock, oracleDataMock]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
>
|
||||
<Closed />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
);
|
||||
});
|
||||
// screen.debug(document, Infinity);
|
||||
|
||||
const headers = screen.getAllByRole('columnheader');
|
||||
const expectedHeaders = [
|
||||
'Market',
|
||||
'Description',
|
||||
'Status',
|
||||
'Settlement date',
|
||||
'Best bid',
|
||||
'Best offer',
|
||||
'Mark price',
|
||||
'Settlement price',
|
||||
'Realised PNL',
|
||||
'Settlement asset',
|
||||
'Market ID',
|
||||
];
|
||||
expect(headers).toHaveLength(expectedHeaders.length);
|
||||
expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders);
|
||||
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
market.tradableInstrument.instrument.code,
|
||||
market.tradableInstrument.instrument.name,
|
||||
MarketStateMapping[market.state],
|
||||
'3 days ago',
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
addDecimalsFormatNumber(marketsData.bestBidPrice, market.decimalPlaces),
|
||||
addDecimalsFormatNumber(
|
||||
marketsData!.bestOfferPrice,
|
||||
market.decimalPlaces
|
||||
),
|
||||
addDecimalsFormatNumber(marketsData!.markPrice, market.decimalPlaces),
|
||||
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
||||
addDecimalsFormatNumber(property.value, market.decimalPlaces),
|
||||
addDecimalsFormatNumber(position.realisedPNL, market.decimalPlaces),
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol,
|
||||
market.id,
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
});
|
||||
|
||||
it('only renders settled and terminated markets', async () => {
|
||||
const mixedMarkets = [
|
||||
{
|
||||
// inlclude as settled
|
||||
__typename: 'MarketEdge' as const,
|
||||
node: createMarketFragment({
|
||||
id: 'include-0',
|
||||
state: MarketState.STATE_SETTLED,
|
||||
}),
|
||||
},
|
||||
{
|
||||
// omit this market
|
||||
__typename: 'MarketEdge' as const,
|
||||
node: createMarketFragment({
|
||||
id: 'discard-0',
|
||||
state: MarketState.STATE_SUSPENDED,
|
||||
}),
|
||||
},
|
||||
{
|
||||
// include as terminated
|
||||
__typename: 'MarketEdge' as const,
|
||||
node: createMarketFragment({
|
||||
id: 'include-1',
|
||||
state: MarketState.STATE_TRADING_TERMINATED,
|
||||
}),
|
||||
},
|
||||
{
|
||||
// omit this market
|
||||
__typename: 'MarketEdge' as const,
|
||||
node: createMarketFragment({
|
||||
id: 'discard-1',
|
||||
state: MarketState.STATE_ACTIVE,
|
||||
}),
|
||||
},
|
||||
];
|
||||
const mixedMarketsMock: MockedResponse<MarketsQuery> = {
|
||||
request: {
|
||||
query: MarketsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
marketsConnection: {
|
||||
__typename: 'MarketConnection',
|
||||
edges: mixedMarkets,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
await act(async () => {
|
||||
render(
|
||||
<MockedProvider
|
||||
mocks={[
|
||||
mixedMarketsMock,
|
||||
marketsDataMock,
|
||||
positionsMock,
|
||||
oracleDataMock,
|
||||
]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
>
|
||||
<Closed />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
);
|
||||
});
|
||||
|
||||
// check that the number of rows in datagrid is 2
|
||||
const container = within(
|
||||
document.querySelector('.ag-center-cols-container') as HTMLElement
|
||||
);
|
||||
const expectedRows = mixedMarkets.filter((m) => {
|
||||
return [
|
||||
MarketState.STATE_SETTLED,
|
||||
MarketState.STATE_TRADING_TERMINATED,
|
||||
].includes(m.node.state);
|
||||
});
|
||||
|
||||
// check rows length is correct
|
||||
const rows = container.getAllByRole('row');
|
||||
expect(rows).toHaveLength(expectedRows.length);
|
||||
|
||||
// check that only included ids are shown
|
||||
const cells = screen
|
||||
.getAllByRole('gridcell')
|
||||
.filter((cell) => cell.getAttribute('col-id') === 'id')
|
||||
.map((cell) => cell.textContent?.trim());
|
||||
expect(cells).toEqual(expectedRows.map((m) => m.node.id));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,285 @@
|
||||
import compact from 'lodash/compact';
|
||||
import { isAfter } from 'date-fns';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { useMemo } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
getMarketExpiryDate,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { usePositionsQuery } from '@vegaprotocol/positions';
|
||||
import type { MarketMaybeWithData } from '@vegaprotocol/market-list';
|
||||
import { closedMarketsWithDataProvider } from '@vegaprotocol/market-list';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import { SettlementDateCell } from './settlement-date-cell';
|
||||
import { SettlementPriceCell } from './settlement-price-cell';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
type SettlementAsset =
|
||||
MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset'];
|
||||
|
||||
interface Row {
|
||||
id: string;
|
||||
code: string;
|
||||
name: string;
|
||||
decimalPlaces: number;
|
||||
state: MarketState;
|
||||
metadata: string[];
|
||||
closeTimestamp: string | null;
|
||||
bestBidPrice: string | undefined;
|
||||
bestOfferPrice: string | undefined;
|
||||
markPrice: string | undefined;
|
||||
settlementDataOracleId: string;
|
||||
settlementDataSpecBinding: string;
|
||||
tradingTerminationOracleId: string;
|
||||
settlementAsset: SettlementAsset;
|
||||
realisedPNL: string | undefined;
|
||||
}
|
||||
|
||||
export const Closed = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const {
|
||||
data: marketData,
|
||||
loading,
|
||||
error,
|
||||
reload,
|
||||
} = useDataProvider({
|
||||
dataProvider: closedMarketsWithDataProvider,
|
||||
variables: undefined,
|
||||
});
|
||||
const { data: positionData } = usePositionsQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
},
|
||||
skip: !pubKey,
|
||||
});
|
||||
|
||||
// find a position for each market and add the realised pnl to
|
||||
// a normalized object
|
||||
const rowData = compact(marketData).map((market) => {
|
||||
const position = positionData?.party?.positionsConnection?.edges?.find(
|
||||
(edge) => {
|
||||
return edge.node.market.id === market.id;
|
||||
}
|
||||
);
|
||||
|
||||
const row: Row = {
|
||||
id: market.id,
|
||||
code: market.tradableInstrument.instrument.code,
|
||||
name: market.tradableInstrument.instrument.name,
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
state: market.state,
|
||||
metadata: market.tradableInstrument.instrument.metadata.tags ?? [],
|
||||
closeTimestamp: market.marketTimestamps.close,
|
||||
bestBidPrice: market.data?.bestBidPrice,
|
||||
bestOfferPrice: market.data?.bestOfferPrice,
|
||||
markPrice: market.data?.markPrice,
|
||||
settlementDataOracleId:
|
||||
market.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementData.id,
|
||||
settlementDataSpecBinding:
|
||||
market.tradableInstrument.instrument.product.dataSourceSpecBinding
|
||||
.settlementDataProperty,
|
||||
tradingTerminationOracleId:
|
||||
market.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForTradingTermination.id,
|
||||
settlementAsset:
|
||||
market.tradableInstrument.instrument.product.settlementAsset,
|
||||
realisedPNL: position?.node.realisedPNL,
|
||||
};
|
||||
|
||||
return row;
|
||||
});
|
||||
return (
|
||||
<div className="h-full relative">
|
||||
<ClosedMarketsDataGrid rowData={rowData} />
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={marketData}
|
||||
noDataMessage={t('No markets')}
|
||||
reload={reload}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
|
||||
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
|
||||
const colDefs = useMemo(() => {
|
||||
const cols: ColDef[] = [
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'code',
|
||||
},
|
||||
{
|
||||
headerName: t('Description'),
|
||||
field: 'name',
|
||||
},
|
||||
{
|
||||
headerName: t('Status'),
|
||||
field: 'state',
|
||||
valueFormatter: ({ value }: VegaValueFormatterParams<Row, 'state'>) => {
|
||||
if (!value) return '-';
|
||||
return MarketStateMapping[value];
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Settlement date'),
|
||||
colId: 'settlementDate', // colId needed if no field property provided otherwise column order is ruined in tests
|
||||
valueGetter: ({ data }: { data: Row }) => {
|
||||
return getMarketExpiryDate(data.metadata);
|
||||
},
|
||||
cellRenderer: ({ value, data }: { value: Date | null; data: Row }) => {
|
||||
return (
|
||||
<SettlementDateCell
|
||||
oracleSpecId={data.tradingTerminationOracleId}
|
||||
metaDate={value}
|
||||
marketState={data.state}
|
||||
closeTimestamp={data.closeTimestamp}
|
||||
/>
|
||||
);
|
||||
},
|
||||
cellClassRules: {
|
||||
'text-danger': ({
|
||||
value,
|
||||
data,
|
||||
}: {
|
||||
value: Date | null;
|
||||
data: Row;
|
||||
}) => {
|
||||
const date = data.closeTimestamp
|
||||
? new Date(data.closeTimestamp)
|
||||
: value;
|
||||
|
||||
if (!date) return false;
|
||||
|
||||
if (
|
||||
// expiry has passed and market is not yet settled
|
||||
isAfter(new Date(), date) &&
|
||||
data.state !== MarketState.STATE_SETTLED
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Best bid'),
|
||||
field: 'bestBidPrice',
|
||||
type: 'numericColumn',
|
||||
cellClass: 'font-mono ag-right-aligned-cell',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'bestBidPrice'>) => {
|
||||
if (!value || !data) return '-';
|
||||
return addDecimalsFormatNumber(value, data.decimalPlaces);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Best offer'),
|
||||
field: 'bestOfferPrice',
|
||||
cellClass: 'font-mono ag-right-aligned-cell',
|
||||
type: 'numericColumn',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'bestOfferPrice'>) => {
|
||||
if (!value || !data) return '-';
|
||||
return addDecimalsFormatNumber(value, data.decimalPlaces);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Mark price'),
|
||||
field: 'markPrice',
|
||||
cellClass: 'font-mono ag-right-aligned-cell',
|
||||
type: 'numericColumn',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'markPrice'>) => {
|
||||
if (!value || !data) return '-';
|
||||
return addDecimalsFormatNumber(value, data.decimalPlaces);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Settlement price'),
|
||||
type: 'numericColumn',
|
||||
field: 'settlementDataOracleId',
|
||||
// 'tradableInstrument.instrument.product.dataSourceSpecForSettlementData.id',
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<Row, 'settlementDataOracleId'>) => (
|
||||
<SettlementPriceCell
|
||||
oracleSpecId={value}
|
||||
decimalPlaces={data?.decimalPlaces ?? 0}
|
||||
settlementDataSpecBinding={data?.settlementDataSpecBinding}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('Realised PNL'),
|
||||
field: 'realisedPNL',
|
||||
cellClass: 'font-mono ag-right-aligned-cell',
|
||||
type: 'numericColumn',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'realisedPNL'>) => {
|
||||
if (!value || !data) return '-';
|
||||
return addDecimalsFormatNumber(value, data.decimalPlaces);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Settlement asset'),
|
||||
field: 'settlementAsset',
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'settlementAsset'>) => (
|
||||
<button
|
||||
className="underline"
|
||||
onClick={() => {
|
||||
if (!value) return;
|
||||
openAssetDialog(value.id);
|
||||
}}
|
||||
>
|
||||
{value ? value.symbol : '-'}
|
||||
</button>
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('Market ID'),
|
||||
field: 'id',
|
||||
},
|
||||
];
|
||||
return cols;
|
||||
}, [openAssetDialog]);
|
||||
|
||||
return (
|
||||
<AgGrid
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
rowData={rowData}
|
||||
columnDefs={colDefs}
|
||||
getRowId={({ data }) => data.id}
|
||||
defaultColDef={{
|
||||
flex: 1,
|
||||
resizable: true,
|
||||
}}
|
||||
overlayNoRowsTemplate="No data"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -5,6 +5,7 @@ import { LocalStoragePersistTabs as Tabs, Tab } from '@vegaprotocol/ui-toolkit';
|
||||
import { Markets } from './markets';
|
||||
import { Proposed } from './proposed';
|
||||
import { usePageTitleStore } from '../../stores';
|
||||
import { Closed } from './closed';
|
||||
|
||||
export const MarketsPage = () => {
|
||||
const { updateTitle } = usePageTitleStore((store) => ({
|
||||
@@ -21,6 +22,9 @@ export const MarketsPage = () => {
|
||||
<Tab id="proposed-markets" name={t('Proposed markets')}>
|
||||
<Proposed />
|
||||
</Tab>
|
||||
<Tab id="closed-markets" name={t('Closed markets')}>
|
||||
<Closed />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,7 +12,9 @@ export const Proposed = () => {
|
||||
const externalLink = tokenLink(TOKEN_NEW_MARKET_PROPOSAL);
|
||||
return (
|
||||
<>
|
||||
<ProposalsList />
|
||||
<div className="min-h-[200px]">
|
||||
<ProposalsList />
|
||||
</div>
|
||||
<ExternalLink className="py-4 px-[11px] text-sm" href={externalLink}>
|
||||
{t('Propose a new market')}
|
||||
</ExternalLink>
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { addDays, subDays } from 'date-fns';
|
||||
import type { SettlementDataCellProps } from './settlement-date-cell';
|
||||
import { SettlementDateCell } from './settlement-date-cell';
|
||||
|
||||
describe('SettlementDateCell', () => {
|
||||
let originalNow: typeof Date.now;
|
||||
const mockNowTimestamp = 1672531200000;
|
||||
|
||||
const createProps = (): SettlementDataCellProps => {
|
||||
return {
|
||||
oracleSpecId: 'oracle-spec-id',
|
||||
metaDate: null,
|
||||
closeTimestamp: null,
|
||||
marketState: MarketState.STATE_SETTLED,
|
||||
};
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
originalNow = Date.now;
|
||||
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Date.now = originalNow;
|
||||
});
|
||||
|
||||
it('renders unknown if date cannot be determined', () => {
|
||||
const props = createProps();
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent('Unknown');
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders using close timestamp if provided', () => {
|
||||
const daysAgo = 3;
|
||||
const props = createProps();
|
||||
props.closeTimestamp = subDays(
|
||||
new Date(mockNowTimestamp),
|
||||
daysAgo
|
||||
).toISOString();
|
||||
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent(`${daysAgo} days ago`);
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders using meta tag date if no close timestamp provided', () => {
|
||||
const daysAgo = 4;
|
||||
const props = createProps();
|
||||
props.metaDate = subDays(new Date(mockNowTimestamp), daysAgo);
|
||||
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent(`${daysAgo} days ago`);
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders past expected settlement date', () => {
|
||||
const daysAgo = 3;
|
||||
const props = createProps();
|
||||
props.metaDate = subDays(new Date(mockNowTimestamp), daysAgo);
|
||||
props.marketState = MarketState.STATE_TRADING_TERMINATED;
|
||||
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent(`Expected ${daysAgo} days ago`);
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders future expected settlement date', () => {
|
||||
const daysAgo = 3;
|
||||
const props = createProps();
|
||||
props.metaDate = addDays(new Date(mockNowTimestamp), daysAgo);
|
||||
props.marketState = MarketState.STATE_TRADING_TERMINATED;
|
||||
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent(`Expected in ${daysAgo} days`);
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import { DApp, EXPLORER_ORACLE, useLinks } from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import { formatDistanceToNowStrict, isAfter } from 'date-fns';
|
||||
|
||||
export interface SettlementDataCellProps {
|
||||
oracleSpecId: string;
|
||||
metaDate: Date | null;
|
||||
closeTimestamp: string | null;
|
||||
marketState: MarketState;
|
||||
}
|
||||
|
||||
export const SettlementDateCell = ({
|
||||
oracleSpecId,
|
||||
metaDate,
|
||||
closeTimestamp,
|
||||
marketState,
|
||||
}: SettlementDataCellProps) => {
|
||||
const linkCreator = useLinks(DApp.Explorer);
|
||||
const date = closeTimestamp ? new Date(closeTimestamp) : metaDate;
|
||||
|
||||
let text = '';
|
||||
if (!date) {
|
||||
text = t('Unknown');
|
||||
} else {
|
||||
// pass Date.now() to date constructor for easier mocking
|
||||
const expiryHasPassed = isAfter(new Date(Date.now()), date);
|
||||
const distance = formatDistanceToNowStrict(date); // X days/mins ago
|
||||
|
||||
if (expiryHasPassed) {
|
||||
if (marketState !== MarketState.STATE_SETTLED) {
|
||||
text = t('Expected %s ago', distance);
|
||||
} else {
|
||||
text = t('%s ago', distance);
|
||||
}
|
||||
} else {
|
||||
text = t('Expected in %s', distance);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={linkCreator(EXPLORER_ORACLE.replace(':id', oracleSpecId))}
|
||||
className="underline"
|
||||
target="_blank"
|
||||
title={
|
||||
date ? getDateTimeFormat().format(date) : t('Unknown settlement date')
|
||||
}
|
||||
>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import type { Property } from '@vegaprotocol/types';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { OracleSpecDataConnectionQuery } from '@vegaprotocol/oracles';
|
||||
import { OracleSpecDataConnectionDocument } from '@vegaprotocol/oracles';
|
||||
import type { SettlementPriceCellProps } from './settlement-price-cell';
|
||||
import { SettlementPriceCell } from './settlement-price-cell';
|
||||
|
||||
describe('SettlementPriceCell', () => {
|
||||
const createMock = (
|
||||
id: string,
|
||||
property: Property
|
||||
): MockedResponse<OracleSpecDataConnectionQuery> => {
|
||||
return {
|
||||
request: {
|
||||
query: OracleSpecDataConnectionDocument,
|
||||
variables: {
|
||||
oracleSpecId: id,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
oracleSpec: {
|
||||
dataConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
externalData: {
|
||||
data: {
|
||||
data: [property],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
const createProps = (): SettlementPriceCellProps => {
|
||||
return {
|
||||
oracleSpecId: 'oracle-spec-id',
|
||||
decimalPlaces: 2,
|
||||
settlementDataSpecBinding: 'settlement-data-spec-binding',
|
||||
};
|
||||
};
|
||||
it('renders fetches and renders the settlment data value', async () => {
|
||||
const props = createProps();
|
||||
const property = {
|
||||
__typename: 'Property' as const,
|
||||
name: props.settlementDataSpecBinding as string,
|
||||
value: '1234',
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const mock = createMock(props.oracleSpecId!, property);
|
||||
|
||||
render(
|
||||
<MockedProvider mocks={[mock]}>
|
||||
<SettlementPriceCell {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
const link = await screen.findByRole('link');
|
||||
expect(link).toHaveTextContent('12.34');
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders "-" if no spec value is found', async () => {
|
||||
const props = createProps();
|
||||
const property = {
|
||||
__typename: 'Property' as const,
|
||||
name: 'no matching spec binding',
|
||||
value: '1234',
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const mock = createMock(props.oracleSpecId!, property);
|
||||
|
||||
render(
|
||||
<MockedProvider mocks={[mock]}>
|
||||
<SettlementPriceCell {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
const link = await screen.findByRole('link');
|
||||
expect(link).toHaveTextContent('-');
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders "-" with no link if oracle spec id is not provided', () => {
|
||||
const props = createProps();
|
||||
props.oracleSpecId = undefined;
|
||||
|
||||
render(
|
||||
<MockedProvider mocks={[]}>
|
||||
<SettlementPriceCell {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
expect(screen.queryByRole('link')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { DApp, EXPLORER_ORACLE, useLinks } from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useOracleSpecBindingData } from '@vegaprotocol/oracles';
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
|
||||
export interface SettlementPriceCellProps {
|
||||
oracleSpecId: string | undefined;
|
||||
decimalPlaces: number;
|
||||
settlementDataSpecBinding: string | undefined;
|
||||
}
|
||||
|
||||
export const SettlementPriceCell = ({
|
||||
oracleSpecId,
|
||||
decimalPlaces,
|
||||
settlementDataSpecBinding,
|
||||
}: SettlementPriceCellProps) => {
|
||||
const linkCreator = useLinks(DApp.Explorer);
|
||||
const { property, loading } = useOracleSpecBindingData(
|
||||
oracleSpecId,
|
||||
settlementDataSpecBinding
|
||||
);
|
||||
|
||||
if (!oracleSpecId || loading) {
|
||||
return <span>-</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={linkCreator(EXPLORER_ORACLE.replace(':id', oracleSpecId))}
|
||||
className="underline font-mono"
|
||||
target="_blank"
|
||||
>
|
||||
{property
|
||||
? addDecimalsFormatNumber(property.value, decimalPlaces)
|
||||
: t('-')}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import { WithdrawalsContainer } from './withdrawals-container';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { usePaneLayout } from '@vegaprotocol/react-helpers';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import { DepositsContainer } from './deposits-container';
|
||||
import { LayoutPriority } from 'allotment';
|
||||
@@ -34,11 +35,11 @@ export const Portfolio = () => {
|
||||
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
||||
const wrapperClasses = 'h-full max-h-full flex flex-col';
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<ResizableGrid vertical>
|
||||
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
||||
<ResizableGridPanel minSize={75}>
|
||||
<PortfolioGridChild>
|
||||
<Tabs storageKey="console-portfolio-top">
|
||||
@@ -78,7 +79,7 @@ export const Portfolio = () => {
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize={300}
|
||||
preferredSize={sizes[1] || 300}
|
||||
minSize={50}
|
||||
>
|
||||
<PortfolioGridChild>
|
||||
|
||||
@@ -36,7 +36,11 @@ export const Settings = () => {
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
<TelemetryApproval isSettingsPage />
|
||||
<TelemetryApproval
|
||||
helpText={t(
|
||||
'Help identify bugs and improve the service by sharing anonymous usage data.'
|
||||
)}
|
||||
/>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
export const THROTTLE_UPDATE_TIME = 500;
|
||||
export const RISK_ACCEPTED_KEY = 'vega-risk-accepted';
|
||||
export const RISK_ACCEPTED_KEY = 'vega_risk_accepted';
|
||||
export const MAINNET_WELCOME_HEADER = t(
|
||||
'Trade cash settled futures on the fully decentralised Vega network.'
|
||||
);
|
||||
|
||||
@@ -39,7 +39,17 @@ const MARKET_A: PartialMarket = {
|
||||
symbol: 'ABC',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '',
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecForSettlementData: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
__typename: 'DataSourceSpecToFutureBinding',
|
||||
tradingTerminationProperty: 'trading-termination-property',
|
||||
settlementDataProperty: 'settlement-data-property',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
@@ -115,7 +125,17 @@ const MARKET_B: PartialMarket = {
|
||||
symbol: 'XYZ',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '',
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecForSettlementData: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
__typename: 'DataSourceSpecToFutureBinding',
|
||||
tradingTerminationProperty: 'trading-termination-property',
|
||||
settlementDataProperty: 'settlement-data-property',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { RiskNoticeDialog } from './risk-notice-dialog';
|
||||
import { WelcomeDialog } from './welcome-dialog';
|
||||
|
||||
jest.mock('@vegaprotocol/environment');
|
||||
|
||||
@@ -14,7 +11,6 @@ const mockEnvDefinitions = {
|
||||
};
|
||||
|
||||
describe('Risk notice dialog', () => {
|
||||
const introText = 'Regulation may apply to use of this app';
|
||||
const mockOnClose = jest.fn();
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -22,13 +18,12 @@ describe('Risk notice dialog', () => {
|
||||
|
||||
it.each`
|
||||
assertion | network
|
||||
${'displays'} | ${Networks.MAINNET}
|
||||
${'displays'} | ${Networks.CUSTOM}
|
||||
${'displays'} | ${Networks.DEVNET}
|
||||
${'displays'} | ${Networks.STAGNET3}
|
||||
${'displays'} | ${Networks.TESTNET}
|
||||
`(
|
||||
'$assertion the risk notice on $network',
|
||||
'$assertion a generic message on $network',
|
||||
async ({ assertion, network }) => {
|
||||
// @ts-ignore ignore mock implementation
|
||||
useEnvironment.mockImplementation(() => ({
|
||||
@@ -36,27 +31,37 @@ describe('Risk notice dialog', () => {
|
||||
VEGA_ENV: network,
|
||||
}));
|
||||
|
||||
render(
|
||||
<MockedProvider>
|
||||
<WelcomeDialog />
|
||||
</MockedProvider>,
|
||||
{ wrapper: BrowserRouter }
|
||||
);
|
||||
render(<RiskNoticeDialog onClose={mockOnClose} network={network} />);
|
||||
|
||||
expect(screen.queryByText(introText)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
new RegExp(
|
||||
`This application for trading on Vega is connected to ${network}`
|
||||
)
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
const button = screen.getByRole('button', {
|
||||
name: 'Continue',
|
||||
});
|
||||
fireEvent.click(button);
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
}
|
||||
);
|
||||
|
||||
it("doesn't display the risk notice when previously acknowledged", () => {
|
||||
it('displays a risk message for mainnet', () => {
|
||||
const introText = 'Regulation may apply to use of this app';
|
||||
const network = Networks.MAINNET;
|
||||
|
||||
// @ts-ignore ignore mock implementation
|
||||
useEnvironment.mockImplementation(() => ({
|
||||
...mockEnvDefinitions,
|
||||
VEGA_ENV: Networks.MAINNET,
|
||||
VEGA_ENV: network,
|
||||
}));
|
||||
|
||||
render(<RiskNoticeDialog onClose={mockOnClose} />);
|
||||
render(<RiskNoticeDialog onClose={mockOnClose} network={network} />);
|
||||
|
||||
expect(screen.queryByText(introText)).toBeInTheDocument();
|
||||
expect(screen.getByText(introText)).toBeInTheDocument();
|
||||
|
||||
const button = screen.getByRole('button', {
|
||||
name: 'I understand, Continue',
|
||||
|
||||
@@ -1,25 +1,100 @@
|
||||
import { useCallback } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { LocalStorage } from '@vegaprotocol/utils';
|
||||
import {
|
||||
Button,
|
||||
Link,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { RISK_ACCEPTED_KEY } from '../constants';
|
||||
import { TelemetryApproval } from './telemetry-approval';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
network: Networks;
|
||||
}
|
||||
export const RiskNoticeDialog = ({ onClose }: Props) => {
|
||||
const handleAcceptRisk = useCallback(() => {
|
||||
onClose();
|
||||
LocalStorage.setItem(RISK_ACCEPTED_KEY, 'true');
|
||||
}, [onClose]);
|
||||
export const RiskNoticeDialog = ({ onClose, network }: Props) => {
|
||||
const [, setValue] = useLocalStorage(RISK_ACCEPTED_KEY);
|
||||
|
||||
const handleAcceptRisk = () => {
|
||||
onClose();
|
||||
setValue('true');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{network === Networks.MAINNET ? (
|
||||
<MainnetContent />
|
||||
) : (
|
||||
<TestnetContent network={network} />
|
||||
)}
|
||||
<div className="my-4">
|
||||
<TelemetryApproval
|
||||
helpText={t(
|
||||
'Help identify bugs and improve the service by sharing anonymous usage data. You can change this in your settings at any time.'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Button onClick={handleAcceptRisk}>
|
||||
{network === Networks.MAINNET
|
||||
? t('I understand, Continue')
|
||||
: t('Continue')}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const TestnetContent = ({ network }: { network: Networks }) => {
|
||||
const { VEGA_DOCS_URL, GITHUB_FEEDBACK_URL, VEGA_WALLET_URL } =
|
||||
useEnvironment();
|
||||
return (
|
||||
<>
|
||||
<p className="mb-4">
|
||||
{t(
|
||||
'This application for trading on Vega is connected to %s, meaning you are free to try out trading with virtual assets and no risk.',
|
||||
[network]
|
||||
)}
|
||||
</p>
|
||||
<p className="mb-4">
|
||||
{t(
|
||||
'Your Vega wallet must also be connected to %s, and your Ethereum wallet must be connected to Sepolia.',
|
||||
[network]
|
||||
)}
|
||||
</p>
|
||||
{VEGA_DOCS_URL && GITHUB_FEEDBACK_URL && VEGA_WALLET_URL && (
|
||||
<ul className="list-disc pl-4">
|
||||
<li className="mb-1">
|
||||
<Link href={VEGA_WALLET_URL} target="_blank">
|
||||
<span className="underline">{t('Learn about Vega wallet')}</span>{' '}
|
||||
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
|
||||
</Link>
|
||||
</li>
|
||||
<li className="mb-1">
|
||||
<Link href={VEGA_DOCS_URL} target="_blank">
|
||||
<span className="underline">{t('View documentation')}</span>{' '}
|
||||
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
|
||||
</Link>
|
||||
</li>
|
||||
<li className="mb-1">
|
||||
<Link href={GITHUB_FEEDBACK_URL} target="_blank">
|
||||
<span className="underline">{t('Provide feedback')}</span>{' '}
|
||||
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const MainnetContent = () => {
|
||||
return (
|
||||
<>
|
||||
<h4 className="text-xl mb-2 mt-4">
|
||||
{t('Regulation may apply to use of this app')}
|
||||
</h4>
|
||||
<p className="text-base mb-6">
|
||||
<p className="mb-6">
|
||||
{t(
|
||||
'This decentralised application allows you to connect to and use publicly available blockchain services operated by third parties that may include trading, financial products, or other services that may be subject to legal and regulatory restrictions in your jurisdiction. This application is a front end only and does not hold any funds or provide any products or services. It is available to anyone with an internet connection via IPFS and other methods, and the ability to access it does not imply any right to use any services or that it is legal for you to do so. By using this application you accept that it is your responsibility to ensure that your use of the application and any blockchain services accessed through it is compliant with applicable laws and regulations in your jusrisdiction.'
|
||||
)}
|
||||
@@ -27,15 +102,11 @@ export const RiskNoticeDialog = ({ onClose }: Props) => {
|
||||
<h4 className="text-xl mb-2">
|
||||
{t('Technical and financial risk of loss')}
|
||||
</h4>
|
||||
<p className="text-base mb-8">
|
||||
<p className="mb-8">
|
||||
{t(
|
||||
'The public blockchain services accessible via this decentralised application are operated by third parties and may carry significant risks including the potential loss of all funds that you deposit or hold with these services. Technical risks include the risk of loss in the event of the failure or compromise of the public blockchain infrastructure or smart contracts that provide any services you use. Financial risks include but are not limited to losses due to volatility, excessive leverage, low liquidity, and your own lack of understanding of the services you use. By using this decentralised application you accept that it is your responsibility to ensure that you understand any services you use and the technical and financial risks inherent in your use. Do not risk what you cannot afford to lose.'
|
||||
)}
|
||||
</p>
|
||||
<Button onClick={handleAcceptRisk}>{t('I understand, Continue')}</Button>
|
||||
<div className="text-base mt-8">
|
||||
<TelemetryApproval />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,8 @@ import { TelemetryApproval } from './telemetry-approval';
|
||||
|
||||
describe('TelemetryApproval', () => {
|
||||
it('click on checkbox should be properly handled', () => {
|
||||
render(<TelemetryApproval />);
|
||||
const helpText = 'My help text';
|
||||
render(<TelemetryApproval helpText={helpText} />);
|
||||
expect(screen.getByRole('checkbox')).toHaveAttribute(
|
||||
'data-state',
|
||||
'unchecked'
|
||||
@@ -15,5 +16,7 @@ describe('TelemetryApproval', () => {
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
expect(screen.getByText('Share usage data')).toBeInTheDocument();
|
||||
expect(screen.getByText(helpText)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,11 +2,7 @@ import { Checkbox } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
|
||||
|
||||
export const TelemetryApproval = ({
|
||||
isSettingsPage,
|
||||
}: {
|
||||
isSettingsPage?: boolean;
|
||||
}) => {
|
||||
export const TelemetryApproval = ({ helpText }: { helpText: string }) => {
|
||||
const [isApproved, setIsApproved] = useTelemetryApproval();
|
||||
return (
|
||||
<div className="flex flex-col px-2 py-3">
|
||||
@@ -19,13 +15,7 @@ export const TelemetryApproval = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm text-vega-light-300 dark:text-vega-dark-300 ml-6">
|
||||
<span>
|
||||
{t(
|
||||
'Help identify bugs and improve the service by sharing anonymous usage data.'
|
||||
)}
|
||||
{!isSettingsPage &&
|
||||
' ' + t('You can change this in your settings at any time.')}
|
||||
</span>
|
||||
<span>{helpText}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -9,8 +9,11 @@ import { RiskNoticeDialog } from './risk-notice-dialog';
|
||||
import { WelcomeNoticeDialog } from './welcome-notice-dialog';
|
||||
import { WelcomeLandingDialog } from './welcome-landing-dialog';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { Networks } from '@vegaprotocol/environment';
|
||||
|
||||
export const WelcomeDialog = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const { pathname } = useLocation();
|
||||
let dialogContent: React.ReactNode;
|
||||
let title = '';
|
||||
@@ -33,8 +36,10 @@ export const WelcomeDialog = () => {
|
||||
}, [update, isRiskDialogNeeded]);
|
||||
|
||||
if (isRiskDialogNeeded) {
|
||||
dialogContent = <RiskNoticeDialog onClose={onCloseDialog} />;
|
||||
title = t('WARNING');
|
||||
dialogContent = (
|
||||
<RiskNoticeDialog onClose={onCloseDialog} network={VEGA_ENV} />
|
||||
);
|
||||
title = VEGA_ENV === Networks.MAINNET ? t('WARNING') : t('Vega Console');
|
||||
size = 'medium';
|
||||
} else if (isWelcomeDialogNeeded && data?.length === 0) {
|
||||
dialogContent = <WelcomeNoticeDialog />;
|
||||
|
||||
@@ -41,7 +41,17 @@ const MARKET_A: PartialMarket = {
|
||||
symbol: 'ABC',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '',
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecForSettlementData: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
__typename: 'DataSourceSpecToFutureBinding',
|
||||
tradingTerminationProperty: 'trading-termination-property',
|
||||
settlementDataProperty: 'settlement-data-property',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
@@ -117,7 +127,17 @@ const MARKET_B: PartialMarket = {
|
||||
symbol: 'XYZ',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '',
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecForSettlementData: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
__typename: 'DataSourceSpecToFutureBinding',
|
||||
tradingTerminationProperty: 'trading-termination-property',
|
||||
settlementDataProperty: 'settlement-data-property',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -24,7 +28,8 @@ html.dark {
|
||||
@apply border-vega-light-200 dark:border-vega-dark-200;
|
||||
}
|
||||
|
||||
/* Styles for charts */
|
||||
/* PENNANT */
|
||||
|
||||
html [data-theme='dark'] {
|
||||
--pennant-color-danger: theme('colors.vega.pink.DEFAULT');
|
||||
|
||||
@@ -71,3 +76,43 @@ html [data-theme='light'] {
|
||||
--pennant-color-depth-sell-fill: theme('colors.vega.pink.400');
|
||||
--pennant-color-depth-sell-stroke: theme('colors.vega.pink.550');
|
||||
}
|
||||
|
||||
/* AG GRID - Do not edit without updating other global stylesheets for each app */
|
||||
|
||||
.vega-ag-grid .ag-root-wrapper {
|
||||
border: solid 0px;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-react-container {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-cell,
|
||||
.vega-ag-grid .ag-full-width-row .ag-cell-wrapper.ag-row-group {
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
.ag-theme-balham {
|
||||
--ag-background-color: theme(colors.white);
|
||||
--ag-border-color: theme(colors.neutral[300]);
|
||||
--ag-header-background-color: theme(colors.white);
|
||||
--ag-odd-row-background-color: theme(colors.white);
|
||||
--ag-header-column-separator-color: theme(colors.neutral[300]);
|
||||
--ag-row-border-color: theme(colors.white);
|
||||
--ag-row-hover-color: theme(colors.neutral[100]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
/* Dark variables */
|
||||
.ag-theme-balham-dark {
|
||||
--ag-background-color: theme(colors.black);
|
||||
--ag-border-color: theme(colors.neutral[700]);
|
||||
--ag-header-background-color: theme(colors.black);
|
||||
--ag-odd-row-background-color: theme(colors.black);
|
||||
--ag-header-column-separator-color: theme(colors.neutral[600]);
|
||||
--ag-row-border-color: theme(colors.black);
|
||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('AccountManager', () => {
|
||||
data: [],
|
||||
};
|
||||
})
|
||||
.mockImplementationOnce((args) => {
|
||||
.mockImplementation((args) => {
|
||||
return {
|
||||
data: [
|
||||
{ asset: { id: 'a1' }, party: { id: 't1' } },
|
||||
@@ -49,7 +49,7 @@ describe('AccountManager', () => {
|
||||
(helpers.useDataProvider as jest.Mock).mock.calls[0][0].variables
|
||||
.partyId
|
||||
).toEqual('partyOne');
|
||||
await act(() => {
|
||||
await act(async () => {
|
||||
rerender(
|
||||
<AccountManager
|
||||
partyId="partyTwo"
|
||||
@@ -66,7 +66,7 @@ describe('AccountManager', () => {
|
||||
|
||||
it('update method should return proper result', async () => {
|
||||
let rerenderer: (ui: React.ReactElement) => void;
|
||||
await act(() => {
|
||||
await act(async () => {
|
||||
const { rerender } = render(
|
||||
<AccountManager
|
||||
partyId="partyOne"
|
||||
@@ -79,7 +79,7 @@ describe('AccountManager', () => {
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('No accounts')).toBeInTheDocument();
|
||||
});
|
||||
await act(() => {
|
||||
await act(async () => {
|
||||
rerenderer(
|
||||
<AccountManager
|
||||
partyId="partyOne"
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
AgGridDynamic as AgGrid,
|
||||
AgGridLazy as AgGrid,
|
||||
CenteredGridCellWrapper,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
@@ -299,6 +299,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
||||
<DropdownMenuTrigger
|
||||
iconName="more"
|
||||
className="hover:bg-vega-light-200 dark:hover:bg-vega-dark-200 p-0.5 focus:rounded-full hover:rounded-full"
|
||||
data-testid="dropdown-menu"
|
||||
></DropdownMenuTrigger>
|
||||
}
|
||||
>
|
||||
@@ -359,7 +360,10 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
||||
}
|
||||
</AgGrid>
|
||||
<Dialog size="medium" open={openBreakdown} onChange={setOpenBreakdown}>
|
||||
<div className="h-[35vh] w-full m-auto flex flex-col">
|
||||
<div
|
||||
className="h-[35vh] w-full m-auto flex flex-col"
|
||||
data-testid="usage-breakdown"
|
||||
>
|
||||
<h1 className="text-xl mb-4">
|
||||
{row?.asset?.symbol} {t('usage breakdown')}
|
||||
</h1>
|
||||
|
||||
@@ -11,7 +11,7 @@ import type {
|
||||
VegaValueFormatterParams,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { progressBarCellRendererSelector } from '@vegaprotocol/datagrid';
|
||||
import { AgGridDynamic as AgGrid, PriceCell } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid, PriceCell } from '@vegaprotocol/datagrid';
|
||||
import type { ValueFormatterParams } from 'ag-grid-community';
|
||||
import { accountValuesComparator } from './accounts-table';
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export * from '../market-list/src/lib/market-data.mock';
|
||||
export * from '../market-list/src/lib/markets-candles.mock';
|
||||
export * from '../market-list/src/lib/markets-data.mock';
|
||||
export * from '../market-list/src/lib/markets.mock';
|
||||
export * from '../oracles/src/lib/oracle-spec-data-connection.mock';
|
||||
export * from '../orders/src/lib/components/order-data-provider/orders.mock';
|
||||
export * from '../positions/src/lib/positions.mock';
|
||||
export * from '../react-helpers/src/hooks/network-params.mock';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './lib/ag-grid/ag-grid-dynamic';
|
||||
export * from './lib/ag-grid/ag-grid-lazy';
|
||||
|
||||
export * from './lib/cells/cumulative-vol-cell';
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import colors from 'tailwindcss/colors';
|
||||
import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
|
||||
|
||||
const agGridDarkVariables = `
|
||||
.ag-theme-balham-dark {
|
||||
--ag-background-color: ${colors.black};
|
||||
--ag-border-color: ${colors.neutral[700]};
|
||||
--ag-header-background-color: ${colors.black};
|
||||
--ag-odd-row-background-color: ${colors.black};
|
||||
--ag-header-column-separator-color: ${colors.neutral[600]};
|
||||
--ag-row-border-color:${colors.black};
|
||||
--ag-row-hover-color: ${colors.neutral[800]};
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
.ag-theme-balham-dark .ag-root-wrapper {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.ag-theme-balham-dark .ag-row {
|
||||
border-width: 1px 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-row.no-hover, .ag-theme-balham-dark .ag-row.no-hover:hover {
|
||||
background: black;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-react-container {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ag-theme-balham-dark .ag-cell, .ag-theme-balham-dark .ag-full-width-row .ag-cell-wrapper.ag-row-group {
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
`;
|
||||
|
||||
export const AgGrid = ({
|
||||
children,
|
||||
customThemeParams,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
customThemeParams?: string;
|
||||
}) => (
|
||||
<>
|
||||
<style>{agGridDarkVariables}</style>
|
||||
{customThemeParams && <style>{customThemeParams}</style>}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
@@ -1,59 +0,0 @@
|
||||
import type { ReactNode, FunctionComponent } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
|
||||
interface GridProps {
|
||||
children: ReactNode;
|
||||
customThemeParams: string;
|
||||
}
|
||||
|
||||
const AgGridLightTheme = dynamic<GridProps>(
|
||||
() => import('./ag-grid-light').then((mod) => mod.AgGrid),
|
||||
{ ssr: false }
|
||||
) as FunctionComponent<GridProps>;
|
||||
|
||||
const AgGridDarkTheme = dynamic(
|
||||
() => import('./ag-grid-dark').then((mod) => mod.AgGrid),
|
||||
{ ssr: false }
|
||||
) as FunctionComponent<GridProps>;
|
||||
|
||||
export const AgGridThemed = ({
|
||||
style,
|
||||
className,
|
||||
gridRef,
|
||||
customThemeParams = '',
|
||||
...props
|
||||
}: (AgGridReactProps | AgReactUiProps) & {
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
gridRef?: React.ForwardedRef<AgGridReact>;
|
||||
customThemeParams?: string;
|
||||
}) => {
|
||||
const { theme } = useThemeSwitcher();
|
||||
const defaultProps = {
|
||||
rowHeight: 22,
|
||||
headerHeight: 22,
|
||||
enableCellTextSelection: true,
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={`${className ?? ''} ${
|
||||
theme === 'dark' ? 'ag-theme-balham-dark' : 'ag-theme-balham'
|
||||
}`}
|
||||
style={style}
|
||||
>
|
||||
{theme === 'dark' ? (
|
||||
<AgGridDarkTheme customThemeParams={customThemeParams}>
|
||||
<AgGridReact {...defaultProps} {...props} ref={gridRef} />
|
||||
</AgGridDarkTheme>
|
||||
) : (
|
||||
<AgGridLightTheme customThemeParams={customThemeParams}>
|
||||
<AgGridReact {...defaultProps} {...props} ref={gridRef} />
|
||||
</AgGridLightTheme>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import type {
|
||||
AgGridReactProps,
|
||||
AgReactUiProps,
|
||||
AgGridReact,
|
||||
} from 'ag-grid-react';
|
||||
|
||||
type Props = (AgGridReactProps | AgReactUiProps) & {
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
gridRef?: React.Ref<AgGridReact>;
|
||||
customThemeParams?: string;
|
||||
};
|
||||
|
||||
// https://stackoverflow.com/questions/69433673/nextjs-reactdomserver-does-not-yet-support-suspense
|
||||
const AgGridDynamicInternal = dynamic<Props>(
|
||||
() => import('./ag-grid-dynamic-themed').then((mod) => mod.AgGridThemed),
|
||||
{
|
||||
ssr: false,
|
||||
// https://nextjs.org/docs/messages/invalid-dynamic-suspense
|
||||
// suspense: true
|
||||
}
|
||||
) as React.FunctionComponent<Props>;
|
||||
|
||||
export const AgGridDynamic = React.forwardRef<AgGridReact, Props>(
|
||||
(props, ref) => <AgGridDynamicInternal {...props} gridRef={ref} />
|
||||
);
|
||||
@@ -1,45 +1,30 @@
|
||||
import * as React from 'react';
|
||||
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const AgGridLightTheme = React.lazy(() =>
|
||||
import('./ag-grid-light').then((module) => ({
|
||||
default: module.AgGrid,
|
||||
}))
|
||||
);
|
||||
|
||||
const AgGridDarkTheme = React.lazy(() =>
|
||||
import('./ag-grid-dark').then((module) => ({
|
||||
default: module.AgGrid,
|
||||
}))
|
||||
);
|
||||
|
||||
export const AgGridThemed = React.forwardRef<
|
||||
AgGridReact,
|
||||
(AgGridReactProps | AgReactUiProps) & {
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
}
|
||||
>(({ style, className, ...props }, ref) => {
|
||||
export const AgGridThemed = ({
|
||||
style,
|
||||
gridRef,
|
||||
...props
|
||||
}: (AgGridReactProps | AgReactUiProps) & {
|
||||
style?: React.CSSProperties;
|
||||
gridRef?: React.ForwardedRef<AgGridReact>;
|
||||
}) => {
|
||||
const { theme } = useThemeSwitcher();
|
||||
const defaultProps = {
|
||||
rowHeight: 22,
|
||||
headerHeight: 22,
|
||||
enableCellTextSelection: true,
|
||||
};
|
||||
const wrapperClasses = classNames('vega-ag-grid', {
|
||||
'ag-theme-balham': theme === 'light',
|
||||
'ag-theme-balham-dark': theme === 'dark',
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${className ?? ''} ${
|
||||
theme === 'dark' ? 'ag-theme-balham-dark' : 'ag-theme-balham'
|
||||
}`}
|
||||
style={style}
|
||||
>
|
||||
{theme === 'dark' ? (
|
||||
<AgGridDarkTheme>
|
||||
<AgGridReact {...props} ref={ref} enableCellTextSelection={true} />
|
||||
</AgGridDarkTheme>
|
||||
) : (
|
||||
<AgGridLightTheme>
|
||||
<AgGridReact {...props} ref={ref} enableCellTextSelection={true} />
|
||||
</AgGridLightTheme>
|
||||
)}
|
||||
<div className={wrapperClasses} style={style}>
|
||||
<AgGridReact {...defaultProps} {...props} ref={gridRef} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { forwardRef, lazy } from 'react';
|
||||
import type { AgGridReactProps, AgGridReact } from 'ag-grid-react';
|
||||
|
||||
export const AgGridLazyInternal = React.lazy(() =>
|
||||
type Props = AgGridReactProps & {
|
||||
style?: React.CSSProperties;
|
||||
gridRef?: React.Ref<AgGridReact>;
|
||||
};
|
||||
|
||||
export const AgGridLazyInternal = lazy(() =>
|
||||
import('./ag-grid-lazy-themed').then((module) => ({
|
||||
default: module.AgGridThemed,
|
||||
}))
|
||||
);
|
||||
|
||||
export const AgGridLazy = React.forwardRef<AgGridReact>((props, ref) => (
|
||||
<AgGridLazyInternal {...props} ref={ref} />
|
||||
export const AgGridLazy = forwardRef<AgGridReact, Props>((props, ref) => (
|
||||
<AgGridLazyInternal {...props} gridRef={ref} />
|
||||
));
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import colors from 'tailwindcss/colors';
|
||||
import 'ag-grid-community/dist/styles/ag-theme-balham.css';
|
||||
|
||||
const agGridLightVariables = `
|
||||
.ag-theme-balham {
|
||||
--ag-background-color: ${colors.white};
|
||||
--ag-border-color: ${colors.neutral[300]};
|
||||
--ag-header-background-color: ${colors.white};
|
||||
--ag-odd-row-background-color: ${colors.white};
|
||||
--ag-header-column-separator-color: ${colors.neutral[300]};
|
||||
--ag-row-border-color: ${colors.white};
|
||||
--ag-row-hover-color: ${colors.neutral[100]};
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
.ag-theme-balham .ag-root-wrapper {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.ag-theme-balham .ag-row {
|
||||
border-width: 1px 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
.ag-theme-balham .ag-row.no-hover, .ag-theme-balham .ag-row.no-hover:hover {
|
||||
background: white;
|
||||
}
|
||||
.ag-theme-balham .ag-react-container {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ag-theme-balham .ag-cell, .ag-theme-balham .ag-full-width-row .ag-cell-wrapper.ag-row-group {
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
`;
|
||||
|
||||
export const AgGrid = ({
|
||||
children,
|
||||
customThemeParams,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
customThemeParams?: string;
|
||||
}) => (
|
||||
<>
|
||||
<style>{agGridLightVariables}</style>
|
||||
{customThemeParams && <style>{customThemeParams}</style>}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
@@ -35,7 +35,17 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
|
||||
__typename: 'Asset',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '',
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecForSettlementData: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
__typename: 'DataSourceSpecToFutureBinding',
|
||||
tradingTerminationProperty: 'trading-termination-property',
|
||||
settlementDataProperty: 'settlement-data-property',
|
||||
},
|
||||
quoteName: 'BTC',
|
||||
__typename: 'Future',
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
|
||||
@@ -100,6 +100,7 @@ export const TOKEN_VALIDATOR = '/validators/:id';
|
||||
|
||||
// Explorer pages
|
||||
export const EXPLORER_TX = '/txs/:hash';
|
||||
export const EXPLORER_ORACLE = '/oracles/:id';
|
||||
|
||||
// Etherscan pages
|
||||
export const ETHERSCAN_ADDRESS = '/address/:hash';
|
||||
|
||||
@@ -15,7 +15,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import {
|
||||
AgGridDynamic as AgGrid,
|
||||
AgGridLazy as AgGrid,
|
||||
positiveClassNames,
|
||||
negativeClassNames,
|
||||
MarketNameCell,
|
||||
|
||||
@@ -152,6 +152,16 @@ const fills = (id?: string): FillFieldsFragment[] => [
|
||||
id: id || 'buyer-id',
|
||||
},
|
||||
}),
|
||||
fillFieldsFragment({
|
||||
id: '4',
|
||||
aggressor: Schema.Side.SIDE_UNSPECIFIED,
|
||||
market: {
|
||||
id: 'market-2',
|
||||
},
|
||||
buyer: {
|
||||
id: id || 'buyer-id',
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
export const fillsEventSubscription = (
|
||||
|
||||
@@ -78,7 +78,17 @@ export const generateFill = (override?: PartialDeep<Trade>) => {
|
||||
},
|
||||
quoteName: '',
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '',
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecForSettlementData: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'oracleId',
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
__typename: 'DataSourceSpecToFutureBinding',
|
||||
tradingTerminationProperty: 'trading-termination-property',
|
||||
settlementDataProperty: 'settlement-data-property',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
TypedDataAgGrid,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import {
|
||||
AgGridDynamic as AgGrid,
|
||||
AgGridLazy as AgGrid,
|
||||
DateRangeFilter,
|
||||
SetFilter,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
VegaValueFormatterParams,
|
||||
TypedDataAgGrid,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
|
||||
@@ -198,4 +198,4 @@ export function useMarketInfoLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions
|
||||
}
|
||||
export type MarketInfoQueryHookResult = ReturnType<typeof useMarketInfoQuery>;
|
||||
export type MarketInfoLazyQueryHookResult = ReturnType<typeof useMarketInfoLazyQuery>;
|
||||
export type MarketInfoQueryResult = Apollo.QueryResult<MarketInfoQuery, MarketInfoQueryVariables>;
|
||||
export type MarketInfoQueryResult = Apollo.QueryResult<MarketInfoQuery, MarketInfoQueryVariables>;
|
||||
|
||||
@@ -103,6 +103,7 @@ const MarketInfoAccordion = ({
|
||||
content: <InsurancePoolInfoPanel market={market} account={a} />,
|
||||
})),
|
||||
];
|
||||
const product = market.tradableInstrument.instrument.product;
|
||||
|
||||
const marketSpecPanels = [
|
||||
{
|
||||
@@ -113,9 +114,13 @@ const MarketInfoAccordion = ({
|
||||
title: t('Instrument'),
|
||||
content: <InstrumentInfoPanel market={market} />,
|
||||
},
|
||||
{
|
||||
title: t('Oracle'),
|
||||
content: <OracleInfoPanel market={market} />,
|
||||
product.dataSourceSpecForSettlementData && {
|
||||
title: t('Settlement Oracle'),
|
||||
content: <OracleInfoPanel market={market} type="settlementData" />,
|
||||
},
|
||||
product.dataSourceSpecForTradingTermination && {
|
||||
title: t('Termination Oracle'),
|
||||
content: <OracleInfoPanel market={market} type="termination" />,
|
||||
},
|
||||
{
|
||||
title: t('Settlement asset'),
|
||||
|
||||
@@ -26,7 +26,7 @@ import { ConditionOperatorMapping } from '@vegaprotocol/types';
|
||||
import { MarketTradingModeMapping } from '@vegaprotocol/types';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { Provider } from '@vegaprotocol/oracles';
|
||||
import { useOracleProofs } from '@vegaprotocol/oracles';
|
||||
import { OracleBasicProfile, useOracleProofs } from '@vegaprotocol/oracles';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
|
||||
type PanelProps = Pick<
|
||||
@@ -449,49 +449,38 @@ export const LiquidityPriceRangeInfoPanel = ({
|
||||
|
||||
export const OracleInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
}: MarketInfoProps & PanelProps) => {
|
||||
type,
|
||||
}: MarketInfoProps &
|
||||
PanelProps & { type: 'settlementData' | 'termination' }) => {
|
||||
const product = market.tradableInstrument.instrument.product;
|
||||
const { VEGA_EXPLORER_URL, ORACLE_PROOFS_URL } = useEnvironment();
|
||||
const { data } = useOracleProofs(ORACLE_PROOFS_URL);
|
||||
|
||||
return (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
settlementDataProperty:
|
||||
product.dataSourceSpecBinding.settlementDataProperty,
|
||||
tradingTerminationProperty:
|
||||
product.dataSourceSpecBinding.tradingTerminationProperty,
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className="flex flex-col gap-2 mt-4"
|
||||
<div className="flex flex-col gap-4">
|
||||
<DataSourceProof
|
||||
data-testid="oracle-proof-links"
|
||||
data={
|
||||
type === 'settlementData'
|
||||
? product.dataSourceSpecForSettlementData.data
|
||||
: product.dataSourceSpecForTradingTermination.data
|
||||
}
|
||||
providers={data}
|
||||
type={type}
|
||||
/>
|
||||
<ExternalLink
|
||||
data-testid="oracle-spec-links"
|
||||
href={`${VEGA_EXPLORER_URL}/oracles/${
|
||||
type === 'settlementData'
|
||||
? product.dataSourceSpecForSettlementData.id
|
||||
: product.dataSourceSpecForTradingTermination.id
|
||||
}`}
|
||||
>
|
||||
<DataSourceProof
|
||||
data={product.dataSourceSpecForSettlementData.data}
|
||||
providers={data}
|
||||
type="settlementData"
|
||||
/>
|
||||
<DataSourceProof
|
||||
data={product.dataSourceSpecForTradingTermination.data}
|
||||
providers={data}
|
||||
type="termination"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2" data-testid="oracle-spec-links">
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/oracles#${product.dataSourceSpecForSettlementData.id}`}
|
||||
>
|
||||
{t('View settlement data specification')}
|
||||
</ExternalLink>
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/oracles#${product.dataSourceSpecForTradingTermination.id}`}
|
||||
>
|
||||
{t('View termination specification')}
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</MarketInfoTable>
|
||||
{type === 'settlementData'
|
||||
? t('View settlement data specification')
|
||||
: t('View termination specification')}
|
||||
</ExternalLink>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -551,20 +540,13 @@ const OracleLink = ({
|
||||
providers,
|
||||
signer,
|
||||
type,
|
||||
index,
|
||||
}: {
|
||||
providers: Provider[];
|
||||
signer: SignerKind;
|
||||
type: 'settlementData' | 'termination';
|
||||
index: number;
|
||||
}) => {
|
||||
const text =
|
||||
type === 'settlementData'
|
||||
? t('View settlement oracle details')
|
||||
: t('View termination oracle details');
|
||||
const textWithCount = index > 0 ? `${text} (${index + 1})` : text;
|
||||
|
||||
const provider = providers.find((p) => {
|
||||
const signerProviders = providers.filter((p) => {
|
||||
if (signer.__typename === 'PubKey') {
|
||||
if (
|
||||
p.oracle.type === 'public_key' &&
|
||||
@@ -586,14 +568,19 @@ const OracleLink = ({
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!provider) {
|
||||
if (!signerProviders.length) {
|
||||
return <NoOracleProof type={type} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<p>
|
||||
<ExternalLink href={provider.github_link}>{textWithCount}</ExternalLink>
|
||||
</p>
|
||||
<div>
|
||||
{signerProviders.map((provider) => (
|
||||
<OracleBasicProfile
|
||||
key={provider.name}
|
||||
provider={provider}
|
||||
></OracleBasicProfile>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+9
-2
@@ -3,12 +3,12 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } };
|
||||
export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } };
|
||||
|
||||
export type MarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null };
|
||||
export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null };
|
||||
|
||||
export const MarketFieldsFragmentDoc = gql`
|
||||
fragment MarketFields on Market {
|
||||
@@ -44,6 +44,13 @@ export const MarketFieldsFragmentDoc = gql`
|
||||
dataSourceSpecForTradingTermination {
|
||||
id
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
id
|
||||
}
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user