Compare commits

..
109 changed files with 564 additions and 2746 deletions
+1 -1
View File
@@ -50,7 +50,7 @@ jobs:
secrets: inherit
lint-test-build:
timeout-minutes: 60
timeout-minutes: 35
needs: node-modules
runs-on: ubuntu-22.04
name: '(CI) lint + unit test + build'
@@ -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 { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints';
@@ -14,6 +14,7 @@ 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;
@@ -96,19 +97,22 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
),
},
{
title: t('Settlement Oracle'),
title: t('Oracle'),
content: (
<OracleInfoPanel
noBorder={false}
market={market}
type="settlementData"
/>
),
},
{
title: t('Termination Oracle'),
content: (
<OracleInfoPanel noBorder={false} market={market} type="termination" />
<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>
),
},
];
@@ -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 { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
import type {
VegaICellRendererParams,
VegaValueGetterParams,
@@ -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 { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
-44
View File
@@ -1,7 +1,3 @@
@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;
@@ -15,43 +11,3 @@
.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;
}
@@ -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 { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber';
import {
@@ -17,6 +17,7 @@ import {
} from '../../shared';
import {
defaultColDef,
NODE_LIST_GRID_STYLES,
PendingStakeRenderer,
stakedTotalPercentage,
StakeShareRenderer,
@@ -381,14 +382,12 @@ export const ConsensusValidatorsTable = ({
);
return (
<div
data-testid="consensus-validators-table"
className="validators-table"
>
<div data-testid="consensus-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,6 +79,17 @@ 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);
@@ -1,7 +1,7 @@
import { forwardRef, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber';
import {
@@ -13,6 +13,7 @@ import {
} from '../../shared';
import {
defaultColDef,
NODE_LIST_GRID_STYLES,
StakeNeededForPromotionRenderer,
stakedTotalPercentage,
ValidatorFields,
@@ -247,13 +248,11 @@ export const StandbyPendingValidatorsTable = ({
);
return (
<div
data-testid="standby-pending-validators-table"
className="validators-table"
>
<div data-testid="standby-pending-validators-table">
<AgGrid
domLayout="autoHeight"
style={{ width: '100%' }}
customThemeParams={NODE_LIST_GRID_STYLES}
rowHeight={68}
defaultColDef={defaultColDef}
tooltipShowDelay={0}
-44
View File
@@ -1,7 +1,3 @@
@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;
@@ -23,43 +19,3 @@
@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);
}
/* 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;
}
+22 -44
View File
@@ -38,7 +38,7 @@
"tranche_end": "2023-05-20T00:00:00.000Z",
"total_added": "19242.125",
"total_removed": "959.3245960538025",
"locked_amount": "10740.2904998553229338",
"locked_amount": "11220.861086998456172475",
"deposits": [
{
"amount": "188",
@@ -845,7 +845,7 @@
"tranche_end": "2023-05-06T00:00:00.000Z",
"total_added": "14520",
"total_removed": "5079.76864115505",
"locked_amount": "1328.5631944444439712",
"locked_amount": "1691.19907407407385",
"deposits": [
{
"amount": "111",
@@ -4703,7 +4703,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "51226.896539776924515462",
"locked_amount": "51404.799340902463537955",
"deposits": [
{
"amount": "86666.297",
@@ -4769,7 +4769,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "394.848424145299",
"locked_amount": "405.14028795278795",
"deposits": [
{
"amount": "2500",
@@ -4802,7 +4802,7 @@
"tranche_end": "2023-11-01T00:00:00.000Z",
"total_added": "15000.000000000000015",
"total_removed": "0",
"locked_amount": "14816.165647644927014816165647644927",
"locked_amount": "14877.245621980677014877245621980677",
"deposits": [
{
"amount": "1.5e-14",
@@ -4890,7 +4890,7 @@
"tranche_end": "2023-09-01T00:00:00.000Z",
"total_added": "17500",
"total_removed": "0",
"locked_amount": "11483.8961541364725",
"locked_amount": "11555.1561241948475",
"deposits": [
{
"amount": "12500",
@@ -5157,7 +5157,7 @@
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "17836.1796921",
"locked_amount": "18593.57015423572875",
"locked_amount": "18748.80102823818",
"deposits": [
{
"amount": "7500",
@@ -5565,7 +5565,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "51180.15844229965399674",
"locked_amount": "51357.898929503744408733",
"deposits": [
{
"amount": "129999.45",
@@ -5598,7 +5598,7 @@
"tranche_end": "2024-04-01T00:00:00.000Z",
"total_added": "54144.7663",
"total_removed": "0",
"locked_amount": "49373.06871247784644592557",
"locked_amount": "49483.90980760125352528846",
"deposits": [
{
"amount": "54144.7663",
@@ -5631,7 +5631,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "21051.60226407915008",
"locked_amount": "21180.103373921867602",
"deposits": [
{
"amount": "10000",
@@ -5824,7 +5824,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "1873.218702435312",
"locked_amount": "1883.48236935565685",
"deposits": [
{
"amount": "5000",
@@ -6882,7 +6882,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "1709370.7872515768348",
"locked_amount": "158090.6912451388393129414",
"locked_amount": "162072.8469934678874854318",
"deposits": [
{
"amount": "1852091.69",
@@ -40754,7 +40754,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "715655.108029600523393",
"locked_amount": "267431.675840398797767447499",
"locked_amount": "273550.862786203377517877189",
"deposits": [
{
"amount": "1998.95815",
@@ -42147,7 +42147,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "866687.25765937723884852",
"locked_amount": "6247983.1374161640607671780970752321127332",
"locked_amount": "6269681.38143663317600286992855265041416594",
"deposits": [
{
"amount": "16249.93",
@@ -48724,8 +48724,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "6168897.174282186485373182",
"locked_amount": "46652.9124232901388230326056764886",
"total_removed": "6167911.210808830654624182",
"locked_amount": "66684.5927583919648233980957995734",
"deposits": [
{
"amount": "129284.449",
@@ -48979,11 +48979,6 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x2c531bd9c61e0a3705f161bba2c03703722f1e41533725684fcccb9176117e0e"
},
{
"amount": "985.963473355830749",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x53d67a8df4d069f956ae1eb76d5e099642dcc8d7e40fa9357be0b8ae879b90c4"
},
{
"amount": "1360.32447632896938825",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -52320,12 +52315,6 @@
"tranche_id": 3,
"tx": "0x2c531bd9c61e0a3705f161bba2c03703722f1e41533725684fcccb9176117e0e"
},
{
"amount": "985.963473355830749",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x53d67a8df4d069f956ae1eb76d5e099642dcc8d7e40fa9357be0b8ae879b90c4"
},
{
"amount": "1360.32447632896938825",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -55148,8 +55137,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "357792.4615146437139805",
"remaining_tokens": "1331.0080603562860195"
"withdrawn_tokens": "356806.4980412878832315",
"remaining_tokens": "2316.9715337121167685"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@@ -56618,7 +56607,7 @@
"tranche_start": "2021-10-05T00:00:00.000Z",
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "3480901.398845846498418769",
"total_removed": "3480709.486904665102263769",
"locked_amount": "0",
"deposits": [
{
@@ -56768,11 +56757,6 @@
"user": "0x83b1a48376E045D26420200345414e6b93066396",
"tx": "0xeb193da202cd833a574a524eff7f1dd13a0ee8669e51a7db36984ef4d6735fd6"
},
{
"amount": "191.911941181396155",
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
"tx": "0xb741f4e653131d11eed34bc9c5f3f8425d23cf054b336f3dc61fbd936b39744a"
},
{
"amount": "219.945520329711288",
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
@@ -57795,12 +57779,6 @@
}
],
"withdrawals": [
{
"amount": "191.911941181396155",
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
"tranche_id": 4,
"tx": "0xb741f4e653131d11eed34bc9c5f3f8425d23cf054b336f3dc61fbd936b39744a"
},
{
"amount": "10509.407528939748325",
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
@@ -57911,8 +57889,8 @@
}
],
"total_tokens": "110499.5291",
"withdrawn_tokens": "110499.5291",
"remaining_tokens": "0"
"withdrawn_tokens": "110307.617158818603845",
"remaining_tokens": "191.911941181396155"
},
{
"address": "0xdbC5d439F373EB646345e1c67D1d46231ACE7dD3",
@@ -58824,7 +58802,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "44078.8527972103416",
"locked_amount": "42376.077741542987921938344901044",
"locked_amount": "43345.697891869583547956391273456",
"deposits": [
{
"amount": "3000",
@@ -1,375 +0,0 @@
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('/#/markets/market-1');
cy.visit('/');
cy.wait('@MarketsCandles');
cy.getByTestId('dialog-close').click();
});
@@ -112,6 +112,7 @@ 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');
});
});
+1 -2
View File
@@ -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-0');
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-1');
});
});
@@ -172,7 +172,6 @@ describe('home', { tags: '@regression' }, () => {
],
},
};
// @ts-ignore partial deep check failing
const data = marketsDataQuery(override);
cy.mockGQL((req) => {
aliasGQLQuery(req, 'MarketsData', data);
@@ -180,21 +180,25 @@ 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('provider-name')
.and('contain', 'Another oracle');
.getByTestId('oracle-proof-links')
.find(`[data-testid="${externalLink}"]`)
.should('have.attr', 'href')
.and('contain', 'https://github.com/vegaprotocol/well-known');
cy.getByTestId(accordionContent)
.getByTestId('signed-proofs')
.and('contain', '1');
cy.getByTestId(accordionContent)
.getByTestId('verified-proofs')
.and('contain', '1');
cy.getByTestId(accordionContent)
.getByTestId('signed-proofs')
.and('contain', '1');
.getByTestId('oracle-spec-links')
.find(`[data-testid="${externalLink}"]`)
.should('have.attr', 'href')
.and('contain', '/oracles');
});
it('proposal displayed', () => {
@@ -222,7 +222,6 @@ describe('markets table', { tags: '@smoke' }, () => {
],
},
};
// @ts-ignore partial deep check failing
const market = marketsQuery(override);
aliasGQLQuery(req, 'Market', market);
aliasGQLQuery(req, 'ProposalOfMarket', {
@@ -26,7 +26,6 @@ export function updateOrder(
override?: PartialDeep<OrderUpdateFieldsFragment>
): void {
const update: OrdersUpdateSubscription = orderUpdateSubscription({
// @ts-ignore partial deep check failing
orders: [override],
});
if (!sendOrderUpdate) {
-3
View File
@@ -46,9 +46,7 @@ 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,
},
@@ -65,7 +63,6 @@ const marketsDataOverride = (
edges: [
{
node: {
// @ts-ignore conflict between incoming and outgoing types
tradingMode: data.tradingMode,
state: data.state,
},
+17 -28
View File
@@ -27,10 +27,7 @@ import { NO_MARKET } from './constants';
import { LiquidityContainer } from '../liquidity/liquidity';
import { useNavigate } from 'react-router-dom';
import type { PinnedAsset } from '@vegaprotocol/accounts';
import {
usePaneLayout,
useScreenDimensions,
} from '@vegaprotocol/react-helpers';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
@@ -86,20 +83,15 @@ 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}
onChange={handleOnLayoutChange}
>
<ResizableGrid proportionalLayout minSize={200}>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizes[0] || '50%'}
preferredSize="50%"
minSize={50}
>
<TradeGridChild>
@@ -127,7 +119,7 @@ const MarketBottomPanel = memo(
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizes[1] || '50%'}
preferredSize="50%"
minSize={50}
>
<TradeGridChild>
@@ -194,29 +186,22 @@ 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 onChange={handleOnLayoutChange}>
<ResizableGrid vertical>
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
<ResizableGrid
proportionalLayout={false}
minSize={200}
onChange={handleOnMiddleLayoutChange}
>
<ResizableGrid proportionalLayout={false} minSize={200}>
<ResizableGridPanel
priority={LayoutPriority.High}
minSize={200}
preferredSize={sizesMiddle[0] || '50%'}
preferredSize="50%"
>
<TradeGridChild>
<Tabs storageKey="console-trade-grid-main-left">
@@ -234,7 +219,7 @@ const MainGrid = memo(
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizesMiddle[1] || 330}
preferredSize={330}
minSize={300}
>
<TradeGridChild>
@@ -253,7 +238,7 @@ const MainGrid = memo(
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizesMiddle[2] || 430}
preferredSize={430}
minSize={200}
>
<TradeGridChild>
@@ -271,7 +256,7 @@ const MainGrid = memo(
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizes[1] || '25%'}
preferredSize="25%"
minSize={50}
>
<MarketBottomPanel marketId={marketId} pinnedAsset={pinnedAsset} />
@@ -293,7 +278,11 @@ export const TradeGrid = ({
<TradeMarketHeader market={market} onSelect={onSelect} />
<OracleBanner marketId={market?.id || ''} />
</div>
<MainGrid marketId={market?.id || ''} pinnedAsset={pinnedAsset} />
<MainGrid
marketId={market?.id || ''}
onSelect={onSelect}
pinnedAsset={pinnedAsset}
/>
</div>
);
};
@@ -1,335 +0,0 @@
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));
});
});
@@ -1,285 +0,0 @@
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,7 +5,6 @@ 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) => ({
@@ -22,9 +21,6 @@ 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,9 +12,7 @@ export const Proposed = () => {
const externalLink = tokenLink(TOKEN_NEW_MARKET_PROPOSAL);
return (
<>
<div className="min-h-[200px]">
<ProposalsList />
</div>
<ProposalsList />
<ExternalLink className="py-4 px-[11px] text-sm" href={externalLink}>
{t('Propose a new market')}
</ExternalLink>
@@ -1,105 +0,0 @@
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}`)
);
});
});
@@ -1,55 +0,0 @@
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>
);
};
@@ -1,113 +0,0 @@
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();
});
});
@@ -1,39 +0,0 @@
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,7 +7,6 @@ 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';
@@ -35,11 +34,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 onChange={handleOnLayoutChange}>
<ResizableGrid vertical>
<ResizableGridPanel minSize={75}>
<PortfolioGridChild>
<Tabs storageKey="console-portfolio-top">
@@ -79,7 +78,7 @@ export const Portfolio = () => {
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizes[1] || 300}
preferredSize={300}
minSize={50}
>
<PortfolioGridChild>
@@ -39,17 +39,7 @@ const MARKET_A: PartialMarket = {
symbol: 'ABC',
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
id: '',
},
},
metadata: {
@@ -125,17 +115,7 @@ const MARKET_B: PartialMarket = {
symbol: 'XYZ',
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
id: '',
},
},
metadata: {
@@ -41,17 +41,7 @@ const MARKET_A: PartialMarket = {
symbol: 'ABC',
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
id: '',
},
},
metadata: {
@@ -127,17 +117,7 @@ const MARKET_B: PartialMarket = {
symbol: 'XYZ',
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
id: '',
},
},
metadata: {
+1 -46
View File
@@ -1,7 +1,3 @@
@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;
@@ -28,8 +24,7 @@ html.dark {
@apply border-vega-light-200 dark:border-vega-dark-200;
}
/* PENNANT */
/* Styles for charts */
html [data-theme='dark'] {
--pennant-color-danger: theme('colors.vega.pink.DEFAULT');
@@ -76,43 +71,3 @@ 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: [],
};
})
.mockImplementation((args) => {
.mockImplementationOnce((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(async () => {
await act(() => {
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(async () => {
await act(() => {
const { rerender } = render(
<AccountManager
partyId="partyOne"
@@ -79,7 +79,7 @@ describe('AccountManager', () => {
await waitFor(() => {
expect(screen.getByText('No accounts')).toBeInTheDocument();
});
await act(async () => {
await act(() => {
rerenderer(
<AccountManager
partyId="partyOne"
+1 -1
View File
@@ -22,7 +22,7 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
import {
AgGridLazy as AgGrid,
AgGridDynamic as AgGrid,
CenteredGridCellWrapper,
} from '@vegaprotocol/datagrid';
import { AgGridColumn } from 'ag-grid-react';
+1 -1
View File
@@ -11,7 +11,7 @@ import type {
VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import { progressBarCellRendererSelector } from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid, PriceCell } from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid, PriceCell } from '@vegaprotocol/datagrid';
import type { ValueFormatterParams } from 'ag-grid-community';
import { accountValuesComparator } from './accounts-table';
-1
View File
@@ -17,7 +17,6 @@ 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
View File
@@ -1,3 +1,4 @@
export * from './lib/ag-grid/ag-grid-dynamic';
export * from './lib/ag-grid/ag-grid-lazy';
export * from './lib/cells/cumulative-vol-cell';
@@ -0,0 +1,50 @@
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}
</>
);
@@ -0,0 +1,59 @@
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>
);
};
@@ -0,0 +1,29 @@
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,30 +1,45 @@
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 classNames from 'classnames';
import 'ag-grid-community/dist/styles/ag-grid.css';
export const AgGridThemed = ({
style,
gridRef,
...props
}: (AgGridReactProps | AgReactUiProps) & {
style?: React.CSSProperties;
gridRef?: React.ForwardedRef<AgGridReact>;
}) => {
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) => {
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={wrapperClasses} style={style}>
<AgGridReact {...defaultProps} {...props} ref={gridRef} />
<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>
);
};
});
+5 -10
View File
@@ -1,17 +1,12 @@
import { forwardRef, lazy } from 'react';
import type { AgGridReactProps, AgGridReact } from 'ag-grid-react';
import * as React from 'react';
import type { AgGridReact } from 'ag-grid-react';
type Props = AgGridReactProps & {
style?: React.CSSProperties;
gridRef?: React.Ref<AgGridReact>;
};
export const AgGridLazyInternal = lazy(() =>
export const AgGridLazyInternal = React.lazy(() =>
import('./ag-grid-lazy-themed').then((module) => ({
default: module.AgGridThemed,
}))
);
export const AgGridLazy = forwardRef<AgGridReact, Props>((props, ref) => (
<AgGridLazyInternal {...props} gridRef={ref} />
export const AgGridLazy = React.forwardRef<AgGridReact>((props, ref) => (
<AgGridLazyInternal {...props} ref={ref} />
));
@@ -0,0 +1,50 @@
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}
</>
);
+1 -11
View File
@@ -35,17 +35,7 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
__typename: 'Asset',
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
id: '',
},
quoteName: 'BTC',
__typename: 'Future',
+1 -1
View File
@@ -8,7 +8,7 @@ import {
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type { AgGridReact } from 'ag-grid-react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
-1
View File
@@ -100,7 +100,6 @@ 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';
+1 -1
View File
@@ -15,7 +15,7 @@ import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import { AgGridColumn } from 'ag-grid-react';
import {
AgGridLazy as AgGrid,
AgGridDynamic as AgGrid,
positiveClassNames,
negativeClassNames,
MarketNameCell,
+1 -11
View File
@@ -78,17 +78,7 @@ export const generateFill = (override?: PartialDeep<Trade>) => {
},
quoteName: '',
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
id: '',
},
},
},
+1 -1
View File
@@ -10,7 +10,7 @@ import type {
TypedDataAgGrid,
} from '@vegaprotocol/datagrid';
import {
AgGridLazy as AgGrid,
AgGridDynamic as AgGrid,
DateRangeFilter,
SetFilter,
} from '@vegaprotocol/datagrid';
+1 -1
View File
@@ -9,7 +9,7 @@ import type {
VegaValueFormatterParams,
TypedDataAgGrid,
} from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { AgGridDynamic 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,7 +103,6 @@ const MarketInfoAccordion = ({
content: <InsurancePoolInfoPanel market={market} account={a} />,
})),
];
const product = market.tradableInstrument.instrument.product;
const marketSpecPanels = [
{
@@ -114,13 +113,9 @@ const MarketInfoAccordion = ({
title: t('Instrument'),
content: <InstrumentInfoPanel 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('Oracle'),
content: <OracleInfoPanel market={market} />,
},
{
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 { OracleBasicProfile, useOracleProofs } from '@vegaprotocol/oracles';
import { useOracleProofs } from '@vegaprotocol/oracles';
import { useDataProvider } from '@vegaprotocol/react-helpers';
type PanelProps = Pick<
@@ -449,38 +449,49 @@ export const LiquidityPriceRangeInfoPanel = ({
export const OracleInfoPanel = ({
market,
type,
}: MarketInfoProps &
PanelProps & { type: 'settlementData' | 'termination' }) => {
...props
}: MarketInfoProps & PanelProps) => {
const product = market.tradableInstrument.instrument.product;
const { VEGA_EXPLORER_URL, ORACLE_PROOFS_URL } = useEnvironment();
const { data } = useOracleProofs(ORACLE_PROOFS_URL);
return (
<div className="flex flex-col gap-4">
<DataSourceProof
<MarketInfoTable
data={{
settlementDataProperty:
product.dataSourceSpecBinding.settlementDataProperty,
tradingTerminationProperty:
product.dataSourceSpecBinding.tradingTerminationProperty,
}}
{...props}
>
<div
className="flex flex-col gap-2 mt-4"
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
}`}
>
{type === 'settlementData'
? t('View settlement data specification')
: t('View termination specification')}
</ExternalLink>
</div>
<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>
);
};
@@ -540,13 +551,20 @@ const OracleLink = ({
providers,
signer,
type,
index,
}: {
providers: Provider[];
signer: SignerKind;
type: 'settlementData' | 'termination';
index: number;
}) => {
const signerProviders = providers.filter((p) => {
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) => {
if (signer.__typename === 'PubKey') {
if (
p.oracle.type === 'public_key' &&
@@ -568,19 +586,14 @@ const OracleLink = ({
return false;
});
if (!signerProviders.length) {
if (!provider) {
return <NoOracleProof type={type} />;
}
return (
<div>
{signerProviders.map((provider) => (
<OracleBasicProfile
key={provider.name}
provider={provider}
></OracleBasicProfile>
))}
</div>
<p>
<ExternalLink href={provider.github_link}>{textWithCount}</ExternalLink>
</p>
);
};
+2 -9
View File
@@ -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 }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: 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 } } } }, 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 }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: 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 } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null };
export const MarketFieldsFragmentDoc = gql`
fragment MarketFields on Market {
@@ -44,13 +44,6 @@ export const MarketFieldsFragmentDoc = gql`
dataSourceSpecForTradingTermination {
id
}
dataSourceSpecForSettlementData {
id
}
dataSourceSpecBinding {
settlementDataProperty
tradingTerminationProperty
}
}
}
}
@@ -8,7 +8,7 @@ import type {
TypedDataAgGrid,
} from '@vegaprotocol/datagrid';
import {
AgGridLazy as AgGrid,
AgGridDynamic as AgGrid,
PriceFlashCell,
MarketNameCell,
SetFilter,
+42 -16
View File
@@ -25,10 +25,8 @@ export const marketsDataQuery = (
return merge(defaultResult, override);
};
export const createMarketsDataFragment = (
override?: PartialDeep<MarketsDataFieldsFragment>
): MarketsDataFieldsFragment => {
const defaultResult = {
const marketsDataFieldsFragments: MarketsDataFieldsFragment[] = [
{
market: {
id: 'market-0',
__typename: 'Market',
@@ -44,28 +42,56 @@ export const createMarketsDataFragment = (
markPrice: '4612690058',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED,
__typename: 'MarketData',
};
return merge(defaultResult, override);
};
const marketsDataFieldsFragments: MarketsDataFieldsFragment[] = [
createMarketsDataFragment(),
createMarketsDataFragment({
},
{
market: {
id: 'market-1',
__typename: 'Market',
},
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
staticMidPrice: '0',
indicativePrice: '0',
bestStaticBidPrice: '0',
bestStaticOfferPrice: '0',
indicativeVolume: '0',
bestBidPrice: '0',
bestOfferPrice: '0',
markPrice: '8441',
}),
createMarketsDataFragment({
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED,
__typename: 'MarketData',
},
{
market: {
id: 'market-2',
__typename: 'Market',
},
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
staticMidPrice: '0',
indicativePrice: '0',
bestStaticBidPrice: '0',
bestStaticOfferPrice: '0',
indicativeVolume: '0',
bestBidPrice: '0',
bestOfferPrice: '0',
markPrice: '4612690058',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET,
}),
createMarketsDataFragment({
__typename: 'MarketData',
},
{
market: {
id: 'market-3',
__typename: 'Market',
},
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
staticMidPrice: '0',
indicativePrice: '0',
bestStaticBidPrice: '0',
bestStaticOfferPrice: '0',
indicativeVolume: '0',
bestBidPrice: '0',
bestOfferPrice: '0',
markPrice: '4612690058',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET,
}),
__typename: 'MarketData',
},
];
+1 -13
View File
@@ -13,7 +13,7 @@ import type { MarketData } from './market-data-provider';
import type { MarketCandles } from './markets-candles-provider';
import { useMemo } from 'react';
import * as Schema from '@vegaprotocol/types';
import { filterAndSortClosedMarkets, filterAndSortMarkets } from './utils';
import { filterAndSortMarkets } from './utils';
import { MarketsDocument } from './__generated__/markets';
import type { Candle } from './market-candles-provider';
@@ -72,11 +72,6 @@ export const activeMarketsProvider = makeDerivedDataProvider<Market[], never>(
([markets]) => filterAndSortMarkets(markets)
);
export const closedMarketsProvider = makeDerivedDataProvider<Market[], never>(
[marketsProvider],
([markets]) => filterAndSortClosedMarkets(markets)
);
export type MarketMaybeWithCandles = Market & { candles?: Candle[] };
const addCandles = <T extends Market>(
@@ -116,13 +111,6 @@ export const marketsWithDataProvider = makeDerivedDataProvider<
addData(parts[0] as Market[], parts[1] as MarketData[])
);
export const closedMarketsWithDataProvider = makeDerivedDataProvider<
MarketMaybeWithData[],
never
>([closedMarketsProvider, marketsDataProvider], (parts) =>
addData(parts[0] as Market[], parts[1] as MarketData[])
);
export type MarketMaybeWithDataAndCandles = MarketMaybeWithData &
MarketMaybeWithCandles;
-7
View File
@@ -31,13 +31,6 @@ fragment MarketFields on Market {
dataSourceSpecForTradingTermination {
id
}
dataSourceSpecForSettlementData {
id
}
dataSourceSpecBinding {
settlementDataProperty
tradingTerminationProperty
}
}
}
}
+77 -27
View File
@@ -22,10 +22,8 @@ export const marketsQuery = (
return merge(defaultResult, override);
};
export const createMarketFragment = (
override?: PartialDeep<MarketFieldsFragment>
): MarketFieldsFragment => {
const defaultFragment = {
const marketFieldsFragments: MarketFieldsFragment[] = [
{
id: 'market-0',
decimalPlaces: 5,
positionDecimalPlaces: 0,
@@ -63,18 +61,8 @@ export const createMarketFragment = (
__typename: 'Asset',
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
},
quoteName: 'DAI',
__typename: 'Future',
},
@@ -83,20 +71,36 @@ export const createMarketFragment = (
__typename: 'TradableInstrument',
},
__typename: 'Market',
};
return merge(defaultFragment, override);
};
const marketFieldsFragments: MarketFieldsFragment[] = [
createMarketFragment({ id: 'market-0' }),
createMarketFragment({
},
{
id: 'market-1',
decimalPlaces: 2,
positionDecimalPlaces: 0,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
state: Schema.MarketState.STATE_ACTIVE,
marketTimestamps: {
__typename: 'MarketTimestamps',
close: '',
open: '',
},
fees: {
__typename: 'Fees',
factors: {
__typename: 'FeeFactors',
makerFee: '',
infrastructureFee: '',
liquidityFee: '',
},
},
tradableInstrument: {
instrument: {
id: 'SOLUSD',
name: 'SUSPENDED MARKET',
code: 'SOLUSD',
metadata: {
__typename: 'InstrumentMetadata',
tags: [],
},
product: {
settlementAsset: {
id: 'asset-1',
@@ -105,19 +109,33 @@ const marketFieldsFragments: MarketFieldsFragment[] = [
decimals: 5,
__typename: 'Asset',
},
dataSourceSpecForTradingTermination: {
id: 'oracleId',
},
quoteName: 'USD',
__typename: 'Future',
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
}),
createMarketFragment({
__typename: 'Market',
},
{
id: 'market-2',
decimalPlaces: 5,
positionDecimalPlaces: 0,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
state: Schema.MarketState.STATE_SUSPENDED,
marketTimestamps: {
__typename: 'MarketTimestamps',
close: '2022-08-26T11:36:32.252490405Z',
open: null,
},
fees: {
__typename: 'Fees',
factors: {
__typename: 'FeeFactors',
makerFee: '0.0002',
infrastructureFee: '0.0005',
liquidityFee: '0.001',
@@ -125,8 +143,13 @@ const marketFieldsFragments: MarketFieldsFragment[] = [
},
tradableInstrument: {
instrument: {
id: '',
code: 'AAPL.MF21',
name: 'Apple Monthly (30 Jun 2022)',
metadata: {
__typename: 'InstrumentMetadata',
tags: [],
},
product: {
settlementAsset: {
id: 'asset-2',
@@ -135,18 +158,33 @@ const marketFieldsFragments: MarketFieldsFragment[] = [
decimals: 5,
__typename: 'Asset',
},
dataSourceSpecForTradingTermination: {
id: 'oracleId',
},
quoteName: 'USDC',
__typename: 'Future',
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
}),
createMarketFragment({
__typename: 'Market',
},
{
id: 'market-3',
decimalPlaces: 5,
positionDecimalPlaces: 0,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
state: Schema.MarketState.STATE_ACTIVE,
marketTimestamps: {
__typename: 'MarketTimestamps',
close: '2022-08-26T11:36:32.252490405Z',
open: null,
},
fees: {
__typename: 'Fees',
factors: {
__typename: 'FeeFactors',
makerFee: '0.0002',
infrastructureFee: '0.0005',
liquidityFee: '0.001',
@@ -154,8 +192,13 @@ const marketFieldsFragments: MarketFieldsFragment[] = [
},
tradableInstrument: {
instrument: {
id: '',
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (30 Jun 2022)',
metadata: {
__typename: 'InstrumentMetadata',
tags: [],
},
product: {
settlementAsset: {
id: 'asset-3',
@@ -164,9 +207,16 @@ const marketFieldsFragments: MarketFieldsFragment[] = [
decimals: 5,
__typename: 'Asset',
},
dataSourceSpecForTradingTermination: {
id: 'oracleId',
},
quoteName: 'BTC',
__typename: 'Future',
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
}),
__typename: 'Market',
},
];
@@ -43,15 +43,6 @@ export const filterAndSortMarkets = (markets: Market[]) => {
);
};
export const filterAndSortClosedMarkets = (markets: Market[]) => {
return markets.filter((m) => {
return [
MarketState.STATE_SETTLED,
MarketState.STATE_TRADING_TERMINATED,
].includes(m.state);
});
};
export const calcCandleLow = (candles: Candle[]): string | undefined => {
return candles
?.reduce((acc: BigNumber, c) => {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
-28
View File
@@ -1,28 +0,0 @@
const rootMain = require('../../../.storybook/main');
module.exports = {
...rootMain,
core: { ...rootMain.core, builder: 'webpack5' },
stories: [
...rootMain.stories,
'../src/**/*.stories.mdx',
'../src/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
...rootMain.addons,
'@nrwl/react/plugins/storybook',
'storybook-addon-themes',
],
webpackFinal: async (config, { configType }) => {
// apply any global webpack configs that might have been specified in .storybook/main.js
if (rootMain.webpackFinal) {
config = await rootMain.webpackFinal(config, { configType });
}
// add your own webpack tweaks if needed
return config;
},
};
@@ -1 +0,0 @@
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
-90
View File
@@ -1,90 +0,0 @@
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
import classNames from 'classnames';
import { useEffect } from 'react';
import '../src/styles.css';
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
backgrounds: { disable: true },
layout: 'fullscreen',
a11y: {
config: {
rules: [
{
// Disabled only for storybook because we display both the dark and light variants of the components on the same page without differentiating the ids, so it will always error.
id: 'duplicate-id-aria',
selector: '[data-testid="form-group"] > label',
},
{
// Disabled because we can't control the radix radio group component and it claims to be accessible to begin with, so hopefully no issues.
id: 'button-name',
selector: '[role=radiogroup] > button',
},
],
},
},
};
export const globalTypes = {
theme: {
name: 'Theme',
description: 'Global theme for components',
defaultValue: 'dark',
toolbar: {
icon: 'circlehollow',
items: [
{ value: 'light', title: 'Light' },
{ value: 'dark', title: 'Dark' },
{ value: 'sideBySide', title: 'Side by side' },
],
showName: true,
},
},
};
const StoryWrapper = ({ children, fill }) => {
const classes = classNames(
'p-4',
'bg-white dark:bg-black',
'text-neutral-800 dark:text-neutral-200',
{
'w-screen h-screen': fill,
}
);
return <div className={classes}>{children}</div>;
};
const ThemeWrapper = (Story, context) => {
const theme = context.parameters.theme || context.globals.theme;
const { setTheme } = useThemeSwitcher();
useEffect(() => {
// in side by side mode a 'dark' class on the html tag will interfere
// making the light 'side' dark, so remove it in that case
if (theme === 'sideBySide') {
document.documentElement.classList.remove('dark');
} else {
setTheme(theme);
}
}, [setTheme, theme]);
return theme === 'sideBySide' ? (
<>
<div className="bg-white text-black">
<StoryWrapper>
<Story />
</StoryWrapper>
</div>
<div className="dark bg-black text-white">
<StoryWrapper>
<Story />
</StoryWrapper>
</div>
</>
) : (
<StoryWrapper fill={true}>
<Story />
</StoryWrapper>
);
};
export const decorators = [ThemeWrapper];
-20
View File
@@ -1,20 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"emitDecoratorMetadata": true,
"outDir": ""
},
"files": [
"../../../node_modules/@nrwl/react/typings/styled-jsx.d.ts",
"../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"../**/*.spec.ts",
"../**/*.spec.js",
"../**/*.spec.tsx",
"../**/*.spec.jsx",
"jest.config.ts"
],
"include": ["../src/**/*", "*.js"]
}
-1
View File
@@ -7,5 +7,4 @@ export default {
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/oracles',
setupFilesAfterEnv: ['./src/setup-tests.ts'],
};
-10
View File
@@ -1,10 +0,0 @@
const { join } = require('path');
module.exports = {
plugins: {
tailwindcss: {
config: join(__dirname, 'tailwind.config.js'),
},
autoprefixer: {},
},
};
-38
View File
@@ -38,44 +38,6 @@
"jestConfig": "libs/oracles/jest.config.ts",
"passWithNoTests": true
}
},
"storybook": {
"executor": "@nrwl/storybook:storybook",
"options": {
"uiFramework": "@storybook/react",
"port": 4400,
"config": {
"configFolder": "libs/oracles/.storybook"
}
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"build-storybook": {
"executor": "@nrwl/storybook:build",
"outputs": ["{options.outputPath}"],
"options": {
"uiFramework": "@storybook/react",
"outputPath": "dist/storybook/oracles",
"config": {
"configFolder": "libs/oracles/.storybook"
}
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"build-spec": {
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./libs/oracles/tsconfig.spec.json"
}
}
}
}
+2 -1
View File
@@ -1 +1,2 @@
export * from './lib';
export * from './lib/oracle-schema';
export * from './lib/use-oracle-proofs';
@@ -1,18 +0,0 @@
query OracleSpecDataConnection($oracleSpecId: ID!) {
oracleSpec(oracleSpecId: $oracleSpecId) {
dataConnection {
edges {
node {
externalData {
data {
data {
name
value
}
}
}
}
}
}
}
}
@@ -1,61 +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 OracleSpecDataConnectionQueryVariables = Types.Exact<{
oracleSpecId: Types.Scalars['ID'];
}>;
export type OracleSpecDataConnectionQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
export const OracleSpecDataConnectionDocument = gql`
query OracleSpecDataConnection($oracleSpecId: ID!) {
oracleSpec(oracleSpecId: $oracleSpecId) {
dataConnection {
edges {
node {
externalData {
data {
data {
name
value
}
}
}
}
}
}
}
}
`;
/**
* __useOracleSpecDataConnectionQuery__
*
* To run a query within a React component, call `useOracleSpecDataConnectionQuery` and pass it any options that fit your needs.
* When your component renders, `useOracleSpecDataConnectionQuery` 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 } = useOracleSpecDataConnectionQuery({
* variables: {
* oracleSpecId: // value for 'oracleSpecId'
* },
* });
*/
export function useOracleSpecDataConnectionQuery(baseOptions: Apollo.QueryHookOptions<OracleSpecDataConnectionQuery, OracleSpecDataConnectionQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<OracleSpecDataConnectionQuery, OracleSpecDataConnectionQueryVariables>(OracleSpecDataConnectionDocument, options);
}
export function useOracleSpecDataConnectionLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<OracleSpecDataConnectionQuery, OracleSpecDataConnectionQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<OracleSpecDataConnectionQuery, OracleSpecDataConnectionQueryVariables>(OracleSpecDataConnectionDocument, options);
}
export type OracleSpecDataConnectionQueryHookResult = ReturnType<typeof useOracleSpecDataConnectionQuery>;
export type OracleSpecDataConnectionLazyQueryHookResult = ReturnType<typeof useOracleSpecDataConnectionLazyQuery>;
export type OracleSpecDataConnectionQueryResult = Apollo.QueryResult<OracleSpecDataConnectionQuery, OracleSpecDataConnectionQueryVariables>;
-1
View File
@@ -1 +0,0 @@
export * from './OracleSpecDataConnection';
-1
View File
@@ -1 +0,0 @@
export * from './oracle-basic-profile';
@@ -1,2 +0,0 @@
export * from './oracle-basic-profile.stories';
export * from './oracle-basic-profile';
@@ -1,45 +0,0 @@
import { OracleBasicProfile } from './oracle-basic-profile';
import type { Provider } from '../../oracle-schema';
import { render, screen } from '@testing-library/react';
describe('OracleBasicProfile', () => {
const testProvider = {
name: 'Test oracle',
url: 'https://zombo.com',
description_markdown:
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
oracle: {
status: 'GOOD',
status_reason: '',
first_verified: '2023-02-28T00:00:00.000Z',
last_verified: '2023-02-28T00:00:00.000Z',
type: 'eth_address',
eth_address: '0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC',
},
proofs: [
{
format: 'signed_message',
available: true,
type: 'eth_address',
eth_address: '0x949AF81E51D57831AE52591d17fBcdd1014a5f52',
message: 'SOMEHEX',
},
],
github_link:
'https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/eth_address-0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC.toml',
} as Provider;
it('should render successfully', () => {
const component = <OracleBasicProfile provider={testProvider} />;
expect(component).toBeTruthy();
});
it('should render the name', () => {
render(<OracleBasicProfile provider={testProvider} />);
expect(screen.getByTestId('provider-name')).toHaveTextContent(
'Test oracle'
);
expect(screen.getByTestId('verified-proofs')).toHaveTextContent('1');
expect(screen.getByTestId('signed-proofs')).toHaveTextContent('1');
});
});
@@ -1,41 +0,0 @@
import type { Story, Meta } from '@storybook/react';
import { OracleBasicProfile } from './oracle-basic-profile';
export default {
component: OracleBasicProfile,
title: 'OracleBasicProfile',
} as Meta;
const Template: Story = (args) => (
<OracleBasicProfile provider={args['provider']} />
);
export const Default = Template.bind({});
Default.args = {
provider: {
name: 'Test oracle',
url: 'https://zombo.com',
description_markdown:
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
oracle: {
status: 'GOOD',
status_reason: '',
first_verified: '2023-02-28T00:00:00.000Z',
last_verified: '2023-02-28T00:00:00.000Z',
type: 'eth_address',
eth_address: '0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC',
},
proofs: [
{
format: 'signed_message',
available: true,
type: 'eth_address',
eth_address: '0x949AF81E51D57831AE52591d17fBcdd1014a5f52',
message: 'SOMEHEX',
},
],
github_link:
'https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/eth_address-0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC.toml',
},
};
@@ -1,158 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { Provider } from '../../oracle-schema';
import {
ExternalLink,
Icon,
Intent,
Link,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import type { IconName } from '@blueprintjs/icons';
import { IconNames } from '@blueprintjs/icons';
import classNames from 'classnames';
const getVerifiedStatusIcon = (provider: Provider) => {
const getIconIntent = () => {
switch (provider.oracle.status) {
case 'GOOD':
return { icon: IconNames.TICK_CIRCLE, intent: Intent.Success };
case 'RETIRED':
return { icon: IconNames.MOON, intent: Intent.None };
case 'UNKNOWN':
return { icon: IconNames.HELP, intent: Intent.Primary };
case 'MALICIOUS':
return { icon: IconNames.ERROR, intent: Intent.Danger };
case 'SUSPICIOUS':
return { icon: IconNames.ERROR, intent: Intent.Danger };
case 'COMPROMISED':
return { icon: IconNames.ERROR, intent: Intent.Danger };
default:
return { icon: IconNames.HELP, intent: Intent.Primary };
}
};
if (!provider.oracle.first_verified || !provider.oracle.last_verified) {
return {
message: t('Not verified'),
...getIconIntent(),
};
}
const lastVerified = provider.oracle.last_verified
? new Date(provider.oracle.last_verified)
: new Date(provider.oracle.first_verified);
return {
...getIconIntent(),
message: t(
'Verified since %s',
lastVerified.toLocaleDateString(undefined, {
year: 'numeric',
month: 'long',
})
),
};
};
export const OracleBasicProfile = ({ provider }: { provider: Provider }) => {
const { icon, message, intent } = getVerifiedStatusIcon(provider);
const verifiedProofs = provider.proofs.filter(
(proof) => proof.available === true
);
const signedProofs = provider.proofs.filter(
(proof) => proof.format === 'signed_message' && proof.available === true
);
const links = provider.proofs
.filter((proof) => proof.format === 'url' && proof.available === true)
.map((proof) => ({
...proof,
url: 'url' in proof ? proof.url : '',
icon: getLinkIcon(proof.type),
}));
return (
<>
<span className="flex gap-1">
{provider.url && (
<Link
href={provider.github_link}
className="flex align-items-bottom text-md"
target="_blank"
>
<span>
<span data-testid="provider-name" className="underline pr-1">
{provider.name}
</span>
<span
data-testid="verified-proofs"
className="dark:text-vega-light-300 text-vega-dark-300"
>
({verifiedProofs.length})
</span>
</span>
</Link>
)}
<span
className={classNames(
{
'text-gray-700 dark:text-gray-300': intent === Intent.None,
'text-vega-blue': intent === Intent.Primary,
'text-vega-green dark:text-vega-green': intent === Intent.Success,
'text-yellow-600 dark:text-yellow': intent === Intent.Warning,
'text-vega-pink': intent === Intent.Danger,
},
'flex items-start align-text-bottom p-1'
)}
>
<Icon size={3} name={icon as IconName} />
</span>
</span>
<p className="dark:text-vega-light-300 text-vega-dark-300">{message}</p>
<p
data-testid="signed-proofs"
className="dark:text-vega-light-300 text-vega-dark-300"
>
{t('Involved in %s %s', [
signedProofs.length.toString(),
signedProofs.length !== 1 ? t('markets') : t('market'),
])}
</p>
{links.length > 0 && (
<div className="flex flex-row gap-1">
{links.map((link) => (
<ExternalLink
key={link.url}
href={link.url}
data-testid={link.url}
className="flex align-items-bottom underline text-sm"
>
<span className="pt-1">
<VegaIcon name={getLinkIcon(link.type)} />
</span>
<span className="underline capitalize">
{link.type}{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
</span>
</ExternalLink>
))}
</div>
)}
</>
);
};
export const getLinkIcon = (type: string) => {
switch (type) {
case 'twitter':
return VegaIconNames.TWITTER;
case 'github':
return VegaIconNames.GLOBE;
case 'linkedin':
return VegaIconNames.LINKEDIN;
default:
return VegaIconNames.GLOBE;
}
};
-6
View File
@@ -1,6 +0,0 @@
export * from './__generated__';
export * from './components';
export * from './oracle-schema';
export * from './oracle-spec-data-connection.mock';
export * from './use-oracle-proofs';
export * from './use-oracle-spec-binding-data';
-73
View File
@@ -1,73 +0,0 @@
[
{
"name": "Test oracle",
"url": "https://zombo.com",
"description_markdown": "Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n",
"oracle": {
"status": "GOOD",
"status_reason": "",
"first_verified": "2023-02-28T00:00:00.000Z",
"last_verified": "2023-02-28T00:00:00.000Z",
"type": "eth_address",
"eth_address": "0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC"
},
"proofs": [
{
"format": "signed_message",
"available": true,
"type": "eth_address",
"eth_address": "0x949AF81E51D57831AE52591d17fBcdd1014a5f52",
"message": "SOMEHEX"
}
],
"github_link": "https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/eth_address-0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC.toml"
},
{
"name": "Vega Admin Oracle for Testnet",
"url": "https://fairground.wtf",
"description_markdown": "The Vega Fairground admin oracle, uses a json file to settle based on Binance Spot price.\nE.g. in case the market is BTC/USDC the spot price for BTC/USDC on Binance will be used.\n\nTwitter: @VegaFairground\n",
"oracle": {
"status": "GOOD",
"status_reason": "",
"first_verified": "2022-01-01T00:00:00.000Z",
"last_verified": "2022-12-31T00:00:00.000Z",
"type": "public_key",
"public_key": "69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f"
},
"proofs": [
{
"format": "url",
"available": true,
"type": "twitter",
"url": "https://twitter.com/vegaprotocol/status/956833487230730241"
},
{
"format": "url",
"available": true,
"type": "web",
"url": "https://fairground.wtf/"
},
{
"format": "url",
"available": true,
"type": "github",
"url": "https://github.com"
},
{
"format": "signed_message",
"available": true,
"type": "public_key",
"public_key": "69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f",
"message": "SOMEHEX"
},
{
"format": "signed_message",
"available": true,
"type": "eth_address",
"eth_address": "0x0b38D0e7391CF427d309991A110a84daF37114eF",
"message": "SOMEHEX"
}
],
"github_link": "https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/public_key-69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f.toml"
}
]
@@ -1,38 +0,0 @@
import merge from 'lodash/merge';
import type { PartialDeep } from 'type-fest';
import type { OracleSpecDataConnectionQuery } from './__generated__/OracleSpecDataConnection';
import type { OracleData } from '@vegaprotocol/types';
export function createDataConnection(
override?: PartialDeep<OracleData>
): OracleData {
const defaultDataConnection = {
externalData: {
data: {
broadcastAt: new Date().toISOString(),
data: [
{
name: 'settlement-data-property',
value: '100',
},
],
},
},
};
return merge(defaultDataConnection, override);
}
export function oracleSpecDataConnectionQuery(
override?: PartialDeep<OracleSpecDataConnectionQuery>
): OracleSpecDataConnectionQuery {
const defaultResult = {
oracleSpec: {
dataConnection: {
edges: [{ node: createDataConnection() }],
},
},
};
return merge(defaultResult, override);
}
@@ -1,105 +0,0 @@
import { renderHook, waitFor } from '@testing-library/react';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type { ReactNode } from 'react';
import { useOracleSpecBindingData } from './use-oracle-spec-binding-data';
import type { Property } from '@vegaprotocol/types';
import type { OracleSpecDataConnectionQuery } from './__generated__/OracleSpecDataConnection';
import { OracleSpecDataConnectionDocument } from './__generated__/OracleSpecDataConnection';
describe('useSettlementPrice', () => {
const setup = (
oracleSpecId: string,
specBinding: string,
mocks: MockedResponse<OracleSpecDataConnectionQuery>[]
) => {
const wrapper = ({ children }: { children: ReactNode }) => (
<MockedProvider mocks={mocks}>{children}</MockedProvider>
);
return renderHook(
() => useOracleSpecBindingData(oracleSpecId, specBinding),
{
wrapper,
}
);
};
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],
},
},
},
},
],
},
},
},
},
};
};
it('should returns the matching value for the spec binding', async () => {
const oracleSpecId = 'oracle-spec-id';
const specBinding = 'spec-binding';
const value = '123456';
const property = {
__typename: 'Property' as const,
name: specBinding,
value,
};
const mock = createMock(oracleSpecId, property);
const { result } = setup(oracleSpecId, specBinding, [mock]);
expect(result.current.property).toEqual(undefined);
expect(result.current.data).toBe(undefined);
expect(result.current.loading).toBe(true);
expect(result.current.error).toBe(undefined);
await waitFor(() => {
expect(result.current.property).toEqual(property);
expect(result.current.loading).toBe(false);
});
});
it('returns nothing if matching poperty not found', async () => {
const oracleSpecId = 'oracle-spec-id';
const specBinding = 'spec-binding';
const value = '123456';
const property = {
__typename: 'Property' as const,
name: 'does not match',
value,
};
const mock = createMock(oracleSpecId, property);
const { result } = setup(oracleSpecId, specBinding, [mock]);
expect(result.current.property).toEqual(undefined);
expect(result.current.data).toBe(undefined);
expect(result.current.loading).toBe(true);
expect(result.current.error).toBe(undefined);
await waitFor(() => {
expect(result.current.property).toEqual(undefined);
expect(result.current.loading).toBe(false);
});
});
});
@@ -1,26 +0,0 @@
import { useOracleSpecDataConnectionQuery } from './__generated__/OracleSpecDataConnection';
export const useOracleSpecBindingData = (
oracleSpecId: string | undefined,
specBinding: string | undefined
) => {
const { data, loading, error } = useOracleSpecDataConnectionQuery({
variables: {
oracleSpecId: oracleSpecId || '',
},
skip: !oracleSpecId,
});
const dataConnectionEdges = data?.oracleSpec?.dataConnection.edges;
const firstDataConnection = dataConnectionEdges?.[0]?.node;
const property = firstDataConnection?.externalData.data.data?.find(
(d) => d.name === specBinding
);
return {
property,
data,
loading,
error,
};
};
-4
View File
@@ -1,4 +0,0 @@
import '@testing-library/jest-dom';
import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;
-18
View File
@@ -1,18 +0,0 @@
const { join } = require('path');
const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
const theme = require('../tailwindcss-config/src/theme');
const vegaCustomClasses = require('../tailwindcss-config/src/vega-custom-classes');
module.exports = {
content: [
join(__dirname, 'src/**/*.{ts,tsx,html,mdx}'),
join(__dirname, 'src/utils/shared.ts'),
join(__dirname, '.storybook/preview.js'),
...createGlobPatternsForDependencies(__dirname),
],
darkMode: 'class',
theme: {
extend: theme,
},
plugins: [vegaCustomClasses],
};
+2 -4
View File
@@ -8,10 +8,11 @@
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": false,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
@@ -19,9 +20,6 @@
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./.storybook/tsconfig.json"
}
]
}
+2 -6
View File
@@ -9,6 +9,7 @@
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"jest.config.ts",
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
@@ -16,12 +17,7 @@
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.stories.ts",
"**/*.stories.js",
"**/*.stories.jsx",
"**/*.stories.tsx",
"jest.config.ts"
"**/*.test.jsx"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}
+3 -5
View File
@@ -3,9 +3,10 @@
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node", "@testing-library/jest-dom"]
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"**/*.test.ts",
"**/*.spec.ts",
"**/*.test.tsx",
@@ -14,9 +15,6 @@
"**/*.spec.js",
"**/*.test.jsx",
"**/*.spec.jsx",
"**/*.d.ts",
"jest.config.ts",
"../../index.d.ts",
"src/lib/use-oracle-spec-binding-data.spec.tsx"
"**/*.d.ts"
]
}
@@ -49,17 +49,7 @@ export const generateOrder = (partialOrder?: PartialDeep<Order>) => {
name: 'XYZ',
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
id: '',
},
},
},
@@ -10,7 +10,7 @@ import { AgGridColumn } from 'ag-grid-react';
import BigNumber from 'bignumber.js';
import { memo, forwardRef } from 'react';
import {
AgGridLazy as AgGrid,
AgGridDynamic as AgGrid,
SetFilter,
DateRangeFilter,
negativeClassNames,
+1 -1
View File
@@ -10,7 +10,7 @@ import type {
} from '@vegaprotocol/datagrid';
import { ProgressBarCell } from '@vegaprotocol/datagrid';
import {
AgGridLazy as AgGrid,
AgGridDynamic as AgGrid,
DateRangeFilter,
PriceFlashCell,
signedNumberCssClass,
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { t } from '@vegaprotocol/i18n';
import * as Types from '@vegaprotocol/types';
@@ -9,6 +9,13 @@ import type { AgGridReact } from 'ag-grid-react';
import { useColumnDefs } from './use-column-defs';
import type { ProposalListFieldsFragment } from '../proposals-data-provider/__generated__/Proposals';
// prevent cutting filter windows by auto-height layout
const CUSTOM_GRID_STYLES = `
.ag-layout-auto-height {
min-height: 200px !important;
}
`;
export const getNewMarketProposals = (data: ProposalListFieldsFragment[]) =>
data.filter((proposal) =>
[
@@ -51,6 +58,7 @@ export const ProposalsList = () => {
suppressLoadingOverlay
suppressNoRowsOverlay
onFilterChanged={onFilterChanged}
customThemeParams={CUSTOM_GRID_STYLES}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
-1
View File
@@ -17,4 +17,3 @@ export * from './use-yesterday';
export * from './use-previous';
export * from './use-logger';
export * from './use-bottom-placeholder';
export * from './use-pane-layout';
@@ -1,19 +0,0 @@
import { renderHook, act, waitFor } from '@testing-library/react';
import { usePaneLayout } from './use-pane-layout';
describe('usePaneLayout', () => {
it('should return proper values', () => {
const ret = renderHook(() => usePaneLayout({ id: 'testid' }));
expect(ret.result.current[0]).toStrictEqual([]);
expect(ret.result.current[1]).toStrictEqual(expect.any(Function));
});
it('setter should change value', async () => {
const ret = renderHook(() => usePaneLayout({ id: 'testid' }));
await act(() => {
ret.result.current[1]([100, 50, 50]);
});
await waitFor(() => {
expect(ret.result.current[0]).toStrictEqual(['50%', '25%', '25%']);
});
});
});
@@ -1,47 +0,0 @@
import { useCallback } from 'react';
import debounce from 'lodash/debounce';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
const STORAGE_KEY = 'vega_pane_store';
const PANELS_SET_DEBOUNCE_TIME = 300;
export const usePaneLayoutStore = create<{
sizes: Record<string, string[]>;
valueSetter: (id: string, value: string[]) => void;
}>()(
persist(
immer((set) => ({
sizes: {},
valueSetter: (id, value) =>
set((state) => {
state.sizes[id] = value;
return state;
}),
})),
{ name: STORAGE_KEY }
)
);
interface UsePaneLayoutProps {
id: string;
}
export const usePaneLayout = ({
id,
}: UsePaneLayoutProps): [string[], (sizes: number[]) => void] => {
const sizes = usePaneLayoutStore((store) => store.sizes[id]) || [];
const valueSetter = usePaneLayoutStore((store) => store.valueSetter);
// eslint-disable-next-line react-hooks/exhaustive-deps
const handleOnChange = useCallback(
debounce((args) => {
if (args.length) {
const all = args.reduce((agg: number, item: number) => agg + item, 0);
const sizesArr = args.map((arg: number) => `${(arg / all) * 100}%`);
valueSetter(id, sizesArr);
}
}, PANELS_SET_DEBOUNCE_TIME),
[valueSetter, id]
);
return [sizes, handleOnChange];
};
+1 -1
View File
@@ -5,7 +5,7 @@ import type {
VegaICellRendererParams,
VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid, NumericCell } from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid, NumericCell } from '@vegaprotocol/datagrid';
import {
addDecimal,
addDecimalsFormatNumber,
@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.20755 15.7736H6V4H8.20755V15.7736ZM10.4151 17.8585V15.7736H8.20755V17.9811H10.4151V20.066H12.6226V17.9811H14.8302V15.7736H12.6226V17.8585H10.4151ZM16.7925 13.566H14.5849V4H16.7925V13.566ZM16.7925 13.566H19V15.7736H16.7925V13.566Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 401 B

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9946 3C10.2165 3.00106 8.47842 3.52883 6.99987 4.51677C5.51983 5.5057 4.36628 6.91131 3.68509 8.55585C3.0039 10.2004 2.82567 12.01 3.17294 13.7558C3.5202 15.5016 4.37737 17.1053 5.63604 18.364C6.89471 19.6226 8.49836 20.4798 10.2442 20.8271C11.99 21.1743 13.7996 20.9961 15.4442 20.3149C17.0887 19.6337 18.4943 18.4802 19.4832 17.0001C20.4722 15.5201 21 13.78 21 12C21 9.61305 20.0518 7.32386 18.364 5.63604C16.6761 3.94821 14.3869 3 12 3C12.0001 3 11.9999 3 12 3M11.9959 3.936C11.9972 3.936 11.9985 3.936 11.9999 3.936C13.2976 3.93617 14.3772 5.83592 14.9515 8.22998H9.04712C9.62068 5.83664 10.6991 3.93971 11.9959 3.936ZM9.8073 4.24157C9.05208 5.16925 8.44481 6.56185 8.07534 8.22998H4.87438C5.24741 7.52551 5.72602 6.87397 6.3 6.3C7.28288 5.31711 8.49319 4.61388 9.8073 4.24157ZM4.42885 9.22998C4.10667 10.1091 3.93685 11.0458 3.936 12C3.936 12.9499 4.10378 13.8872 4.42669 14.77H7.88922C7.75324 13.8973 7.67969 12.9663 7.67969 12C7.67969 11.0336 7.7527 10.1027 7.8879 9.22998H4.42885ZM4.87153 15.77C5.00006 16.013 5.14133 16.2501 5.29503 16.4801C6.18112 17.8062 7.44054 18.8398 8.91404 19.4502C9.20977 19.5727 9.51146 19.677 9.81744 19.763C9.06048 18.8354 8.44956 17.4409 8.07765 15.77H4.87153ZM14.1834 19.7628C15.5101 19.3896 16.7227 18.6815 17.7021 17.7021C18.2744 17.1298 18.7541 16.4778 19.1285 15.77H15.9224C15.5508 17.4416 14.9402 18.8355 14.1834 19.7628ZM19.5733 14.77C19.7153 14.3819 19.8278 13.9819 19.9091 13.5732C20.1981 12.12 20.0808 10.6174 19.5733 9.22998H16.1106C16.2463 10.1024 16.3197 11.0333 16.3197 12C16.3197 12.9667 16.2463 13.8976 16.1106 14.77H19.5733ZM19.1285 8.22998C18.5047 7.05058 17.596 6.04063 16.4801 5.29503C15.7711 4.82129 14.9955 4.46564 14.1834 4.23723C14.9402 5.16453 15.5508 6.55844 15.9224 8.22998H19.1285ZM8.60129 12C8.60129 11.0806 8.68603 10.1352 8.84194 9.22998H15.1569C15.3132 10.1358 15.3981 11.0814 15.3981 12C15.3981 12.9186 15.314 13.8642 15.1588 14.77H8.84003C8.68519 13.8648 8.60129 12.9194 8.60129 12ZM11.9997 20.064C10.6916 20.064 9.61486 18.1657 9.04394 15.77H14.9547C14.3836 18.1642 13.3072 20.064 11.9997 20.064Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 4.32353V19.6765C21 20.0275 20.8606 20.3641 20.6123 20.6123C20.3641 20.8606 20.0275 21 19.6765 21H4.32353C3.97251 21 3.63586 20.8606 3.38765 20.6123C3.13944 20.3641 3 20.0275 3 19.6765V4.32353C3 3.97251 3.13944 3.63586 3.38765 3.38765C3.63586 3.13944 3.97251 3 4.32353 3H19.6765C20.0275 3 20.3641 3.13944 20.6123 3.38765C20.8606 3.63586 21 3.97251 21 4.32353V4.32353ZM8.29412 9.88235H5.64706V18.3529H8.29412V9.88235ZM8.53235 6.97059C8.53375 6.77036 8.49569 6.57182 8.42035 6.3863C8.34502 6.20078 8.23387 6.03191 8.09328 5.88935C7.95268 5.74678 7.78537 5.6333 7.60091 5.5554C7.41646 5.47749 7.21846 5.43668 7.01824 5.43529H6.97059C6.5634 5.43529 6.1729 5.59705 5.88497 5.88497C5.59705 6.1729 5.43529 6.5634 5.43529 6.97059C5.43529 7.37777 5.59705 7.76828 5.88497 8.05621C6.1729 8.34413 6.5634 8.50588 6.97059 8.50588V8.50588C7.17083 8.51081 7.37008 8.47623 7.55696 8.40413C7.74383 8.33202 7.91467 8.2238 8.0597 8.08565C8.20474 7.94749 8.32113 7.78212 8.40223 7.59897C8.48333 7.41582 8.52755 7.21848 8.53235 7.01824V6.97059ZM18.3529 13.2071C18.3529 10.6606 16.7329 9.67059 15.1235 9.67059C14.5966 9.6442 14.0719 9.75644 13.6019 9.9961C13.1318 10.2358 12.7328 10.5945 12.4447 11.0365H12.3706V9.88235H9.88235V18.3529H12.5294V13.8476C12.4911 13.3862 12.6365 12.9283 12.9339 12.5735C13.2313 12.2186 13.6567 11.9954 14.1176 11.9524H14.2182C15.06 11.9524 15.6847 12.4818 15.6847 13.8159V18.3529H18.3318L18.3529 13.2071Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

@@ -1,5 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 1C4.13 1 1 4.13 1 8C1 11.87 4.13 15 8 15C11.87 15 15 11.87 15 8C15 4.13 11.87 1 8 1ZM8.66 12.44H7.32V11.1H8.66V12.44ZM10.38 6.78C10.29 7.01 10.18 7.2 10.05 7.36C9.92 7.52 9.75 7.7 9.53 7.91C9.3 8.14 9.11 8.34 8.98 8.51C8.85 8.68 8.73 8.89 8.64 9.14C8.55 9.37 8.51 9.65 8.51 9.96V10.04V10.13H7.3V10.04C7.3 9.61 7.36 9.24 7.47 8.92C7.58 8.61 7.71 8.34 7.87 8.13C8.03 7.92 8.22 7.69 8.47 7.43C8.75 7.13 8.96 6.87 9.09 6.66C9.22 6.46 9.28 6.2 9.28 5.88C9.28 5.47 9.16 5.16 8.93 4.93C8.7 4.7 8.38 4.58 7.96 4.58C7.6 4.58 7.28 4.68 7.01 4.89C6.75 5.09 6.56 5.44 6.45 5.96C6.34 6.48 6.43 6.06 6.43 6.06L5.26 5.62L5.28 5.54C5.47 4.87 5.81 4.35 6.29 4.02C6.77 3.69 7.34 3.53 8 3.53C8.75 3.53 9.37 3.75 9.82 4.18C10.28 4.62 10.5 5.22 10.5 5.97C10.5 6.27 10.46 6.53 10.37 6.76L10.38 6.78Z" fill="white"/>
<circle cx="8" cy="8" r="7" fill="white"/>
<path d="M6.15393 5.69232C6.15393 5.10304 6.24054 4.5075 6.46161 4C4.99179 4.63982 4 6.14089 4 7.84607C4 10.1402 5.85982 12 8.15393 12C9.85911 12 11.3602 11.0082 12 9.53839C11.4925 9.75946 10.8964 9.84607 10.3077 9.84607C8.01357 9.84607 6.15393 7.98643 6.15393 5.69232Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 1C4.13 1 1 4.13 1 8C1 11.87 4.13 15 8 15C11.87 15 15 11.87 15 8C15 4.13 11.87 1 8 1ZM8.66 12.44H7.32V11.1H8.66V12.44ZM10.38 6.78C10.29 7.01 10.18 7.2 10.05 7.36C9.92 7.52 9.75 7.7 9.53 7.91C9.3 8.14 9.11 8.34 8.98 8.51C8.85 8.68 8.73 8.89 8.64 9.14C8.55 9.37 8.51 9.65 8.51 9.96V10.04V10.13H7.3V10.04C7.3 9.61 7.36 9.24 7.47 8.92C7.58 8.61 7.71 8.34 7.87 8.13C8.03 7.92 8.22 7.69 8.47 7.43C8.75 7.13 8.96 6.87 9.09 6.66C9.22 6.46 9.28 6.2 9.28 5.88C9.28 5.47 9.16 5.16 8.93 4.93C8.7 4.7 8.38 4.58 7.96 4.58C7.6 4.58 7.28 4.68 7.01 4.89C6.75 5.09 6.56 5.44 6.45 5.96L6.43 6.06L5.26 5.62L5.28 5.54C5.47 4.87 5.81 4.35 6.29 4.02C6.77 3.69 7.34 3.53 8 3.53C8.75 3.53 9.37 3.75 9.82 4.18C10.28 4.62 10.5 5.22 10.5 5.97C10.5 6.27 10.46 6.53 10.37 6.76L10.38 6.78Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 929 B

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.28743 20.2575C15.8423 20.2575 19.9641 14 19.9641 8.58084C19.9641 8.4012 19.9641 8.22156 19.9541 8.0519C20.7525 7.47305 21.4511 6.74451 22 5.92615C21.2615 6.25549 20.4731 6.47505 19.6447 6.57485C20.493 6.06587 21.1417 5.26747 21.4511 4.2994C20.6627 4.76846 19.7844 5.10778 18.8463 5.29741C18.0978 4.499 17.0299 4 15.8523 4C13.5868 4 11.7505 5.83633 11.7505 8.1018C11.7505 8.42116 11.7904 8.74052 11.8603 9.03992C8.43713 8.86028 5.40319 7.22355 3.38723 4.7485C3.02794 5.34731 2.82834 6.05589 2.82834 6.80439C2.82834 8.23154 3.55689 9.48902 4.65469 10.2176C3.97605 10.2076 3.34731 10.018 2.7984 9.70858C2.7984 9.72854 2.7984 9.73852 2.7984 9.75848C2.7984 11.7445 4.21557 13.4112 6.09182 13.7804C5.74251 13.8703 5.38323 13.9202 5.01397 13.9202C4.75449 13.9202 4.49501 13.8902 4.24551 13.8503C4.76447 15.477 6.28144 16.6647 8.07784 16.7046C6.67066 17.8024 4.90419 18.4611 2.97804 18.4611C2.6487 18.4611 2.31936 18.4411 2 18.4012C3.80639 19.5788 5.96208 20.2575 8.28743 20.2575Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

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