diff --git a/apps/trading/client-pages/markets/closed.spec.tsx b/apps/trading/client-pages/markets/closed.spec.tsx index 85f302be9..d5482ba26 100644 --- a/apps/trading/client-pages/markets/closed.spec.tsx +++ b/apps/trading/client-pages/markets/closed.spec.tsx @@ -1,4 +1,4 @@ -import { act, render, screen, within, waitFor } from '@testing-library/react'; +import { act, render, screen, within } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { Closed } from './closed'; import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types'; @@ -10,55 +10,21 @@ import type { OracleSpecDataConnectionQuery, MarketsDataQuery, MarketsQuery, - SuccessorMarketIdsQuery, } from '@vegaprotocol/markets'; import { OracleSpecDataConnectionDocument, MarketsDataDocument, MarketsDocument, - SuccessorMarketIdsDocument, } from '@vegaprotocol/markets'; import type { VegaWalletContextShape } from '@vegaprotocol/wallet'; import { VegaWalletContext } from '@vegaprotocol/wallet'; import { addDecimalsFormatNumber } from '@vegaprotocol/utils'; -import { FLAGS } from '@vegaprotocol/environment'; import { createMarketFragment, marketsQuery, marketsDataQuery, createMarketsDataFragment, } from '@vegaprotocol/mock'; -import type { FeatureFlags } from '@vegaprotocol/environment'; - -jest.mock('@vegaprotocol/markets', () => ({ - ...jest.requireActual('@vegaprotocol/markets'), - useSuccessorMarket: (marketId: string) => - marketId === 'include-0' - ? { - data: { - id: 'successorMarketID', - state: 'STATE_ACTIVE', - tradableInstrument: { - instrument: { - name: 'Successor Market Name', - code: 'SuccessorCode', - }, - }, - }, - } - : { data: undefined }, -})); - -jest.mock('@vegaprotocol/environment', () => { - const actual = jest.requireActual('@vegaprotocol/environment'); - return { - ...actual, - FLAGS: { - ...actual.FLAGS, - SUCCESSOR_MARKETS: true, - } as FeatureFlags, - }; -}); describe('Closed', () => { let originalNow: typeof Date.now; @@ -218,10 +184,8 @@ describe('Closed', () => { const headers = screen.getAllByRole('columnheader'); const expectedHeaders = [ 'Market', - 'Description', 'Status', 'Settlement date', - 'Successor market', 'Best bid', 'Best offer', 'Mark price', @@ -235,10 +199,8 @@ describe('Closed', () => { 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( @@ -340,43 +302,26 @@ describe('Closed', () => { .getAllByRole('gridcell') .filter((cell) => cell.getAttribute('col-id') === 'code') .map((cell) => { - const marketId = within(cell) - .getByTestId('market-code') - .getAttribute('data-market-id'); - return marketId; + const marketCode = within(cell).getByTestId('stack-cell-primary'); + return marketCode.textContent; }); - expect(cells).toEqual(expectedRows.map((m) => m.node.id)); + expect(cells).toEqual( + expectedRows.map((m) => m.node.tradableInstrument.instrument.code) + ); }); it('successor marked should be visible', async () => { - const mixedMarkets = [ + const marketsWithSuccessorID = [ { __typename: 'MarketEdge' as const, node: createMarketFragment({ id: 'include-0', state: MarketState.STATE_SETTLED, + successorMarketID: 'successor', }), }, - { - __typename: 'MarketEdge' as const, - node: { - ...createMarketFragment({ - id: 'successorMarketID', - state: MarketState.STATE_ACTIVE, - }), - tradableInstrument: { - ...createMarketFragment().tradableInstrument, - instrument: { - ...createMarketFragment().tradableInstrument.instrument, - id: 'successorAssset', - name: 'Successor Market Name', - code: 'SuccessorCode', - }, - }, - }, - }, ]; - const mixedMarketsMock: MockedResponse = { + const mockWithSuccessors: MockedResponse = { request: { query: MarketsDocument, }, @@ -384,42 +329,17 @@ describe('Closed', () => { data: { marketsConnection: { __typename: 'MarketConnection', - edges: mixedMarkets, + edges: marketsWithSuccessorID, }, }, }, }; - const successorMarketsMock: MockedResponse = { - request: { - query: SuccessorMarketIdsDocument, - }, - result: { - data: { - marketsConnection: { - __typename: 'MarketConnection', - edges: [ - { - node: { - id: 'include-0', - successorMarketID: 'successorMarketID', - parentMarketID: '', - }, - }, - ], - }, - }, - }, - }; - await act(() => { + + await act(async () => { render( { ); }); - await waitFor(() => { - expect( - screen.getByRole('button', { name: /^SuccessorCode/ }) - ).toBeInTheDocument(); - }); - expect( - screen.getByRole('columnheader', { - name: (_name, element) => - element.getAttribute('col-id') === 'successorMarket', - }) - ).toBeInTheDocument(); - screen - .getAllByRole('gridcell', { - name: (_name, element) => - element.getAttribute('col-id') === 'successorMarket', - }) - .forEach((element) => { - expect(element.querySelector('[title="Future"]')?.textContent).toEqual( - 'Futr' - ); - }); - }); - - it('feature flag should hide successors', async () => { - const mockedFlags = jest.mocked(FLAGS); - mockedFlags.SUCCESSOR_MARKETS = false; - - const mixedMarkets = [ - { - __typename: 'MarketEdge' as const, - node: createMarketFragment({ - id: 'include-0', - state: MarketState.STATE_SETTLED, - }), - }, - { - __typename: 'MarketEdge' as const, - node: { - ...createMarketFragment({ - id: 'successorMarketID', - state: MarketState.STATE_ACTIVE, - }), - tradableInstrument: { - ...createMarketFragment().tradableInstrument, - instrument: { - ...createMarketFragment().tradableInstrument.instrument, - id: 'successorAssset', - name: 'Successor Market Name', - code: 'SuccessorCode', - }, - }, - }, - }, - ]; - const mixedMarketsMock: MockedResponse = { - request: { - query: MarketsDocument, - }, - result: { - data: { - marketsConnection: { - __typename: 'MarketConnection', - edges: mixedMarkets, - }, - }, - }, - }; - const successorMarketsMock: MockedResponse = { - request: { - query: SuccessorMarketIdsDocument, - }, - result: { - data: { - marketsConnection: { - __typename: 'MarketConnection', - edges: [ - { - node: { - id: 'include-0', - successorMarketID: 'successorMarketID', - parentMarketID: '', - }, - }, - ], - }, - }, - }, - }; - render( - - - - - - - + const container = within( + document.querySelector('.ag-center-cols-container') as HTMLElement + ); + const cell = container.getAllByRole('gridcell', { + name: (_name, element) => element.getAttribute('col-id') === 'code', + })[0]; + + expect(within(cell).getByTestId('stack-cell-secondary')).toHaveTextContent( + 'PRNT' ); - await waitFor(() => { - expect( - screen.getByRole('columnheader', { - name: (_name, element) => - element.getAttribute('col-id') === 'settlementDate', - }) - ).toBeInTheDocument(); - }); - screen.getAllByRole('columnheader').forEach((element) => { - expect(element.getAttribute('col-id')).not.toEqual('successorMarket'); - }); }); }); diff --git a/apps/trading/client-pages/markets/closed.tsx b/apps/trading/client-pages/markets/closed.tsx index cc5b2267c..152585663 100644 --- a/apps/trading/client-pages/markets/closed.tsx +++ b/apps/trading/client-pages/markets/closed.tsx @@ -4,13 +4,10 @@ import type { VegaICellRendererParams, VegaValueFormatterParams, } from '@vegaprotocol/datagrid'; -import { - AgGridLazy as AgGrid, - COL_DEFS, - MarketNameCell, -} from '@vegaprotocol/datagrid'; +import { AgGridLazy as AgGrid, COL_DEFS } from '@vegaprotocol/datagrid'; import { useMemo } from 'react'; import { t } from '@vegaprotocol/i18n'; +import type { ProductType } from '@vegaprotocol/types'; import { MarketState, MarketStateMapping } from '@vegaprotocol/types'; import { addDecimalsFormatNumber, @@ -20,17 +17,13 @@ import type { DataSourceFilterFragment, MarketMaybeWithData, } from '@vegaprotocol/markets'; -import { - MarketActionsDropdown, - closedMarketsWithDataProvider, -} from '@vegaprotocol/markets'; +import { closedMarketsWithDataProvider } from '@vegaprotocol/markets'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; -import type { ColDef } from 'ag-grid-community'; -import { FLAGS } from '@vegaprotocol/environment'; import { SettlementDateCell } from './settlement-date-cell'; import { SettlementPriceCell } from './settlement-price-cell'; import { useDataProvider } from '@vegaprotocol/data-provider'; -import { SuccessorMarketRenderer } from './successor-market-cell'; +import { MarketActionsDropdown } from './market-table-actions'; +import { MarketCodeCell } from './market-code-cell'; type SettlementAsset = MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset']; @@ -51,7 +44,9 @@ interface Row { setlementDataSourceFilter: DataSourceFilterFragment | undefined; tradingTerminationOracleId: string; settlementAsset: SettlementAsset; - productType: string; + productType: ProductType | undefined; + successorMarketID: string | null | undefined; + parentMarketID: string | null | undefined; } export const Closed = () => { @@ -95,16 +90,19 @@ export const Closed = () => { tradingTerminationOracleId: instrument.product.dataSourceSpecForTradingTermination.id, settlementAsset: instrument.product.settlementAsset, - productType: instrument.product.__typename || '', + productType: instrument.product.__typename, + successorMarketID: market.successorMarketID, + parentMarketID: market.parentMarketID, }; return row; }); - return ( -
- -
- ); + + return ; +}; + +const components = { + MarketCodeCell, }; const ClosedMarketsDataGrid = ({ @@ -117,15 +115,11 @@ const ClosedMarketsDataGrid = ({ const openAssetDialog = useAssetDetailsDialogStore((store) => store.open); const colDefs = useMemo(() => { - const cols: ColDef[] = compact([ + return [ { headerName: t('Market'), field: 'code', - cellRenderer: 'MarketNameCell', - }, - { - headerName: t('Description'), - field: 'name', + cellRenderer: 'MarketCodeCell', }, { headerName: t('Status'), @@ -176,12 +170,6 @@ const ClosedMarketsDataGrid = ({ }, }, }, - FLAGS.SUCCESSOR_MARKETS && { - headerName: t('Successor market'), - field: 'id', - colId: 'successorMarket', - cellRenderer: 'SuccessorMarketRenderer', - }, { headerName: t('Best bid'), field: 'bestBidPrice', @@ -263,12 +251,13 @@ const ClosedMarketsDataGrid = ({ ); }, }, - ]); - return cols; + ]; }, [openAssetDialog]); return ( @@ -276,8 +265,8 @@ const ClosedMarketsDataGrid = ({ rowData={rowData} columnDefs={colDefs} getRowId={({ data }) => data.id} - components={{ SuccessorMarketRenderer, MarketNameCell }} overlayNoRowsTemplate={error ? error.message : t('No markets')} + components={components} /> ); }; diff --git a/apps/trading/client-pages/markets/market-code-cell.spec.tsx b/apps/trading/client-pages/markets/market-code-cell.spec.tsx new file mode 100644 index 000000000..5570aae86 --- /dev/null +++ b/apps/trading/client-pages/markets/market-code-cell.spec.tsx @@ -0,0 +1,82 @@ +import { render, screen } from '@testing-library/react'; +import { ProductTypeShortName } from '@vegaprotocol/types'; +import type { MarketCodeCellProps } from './market-code-cell'; +import { MarketCodeCell } from './market-code-cell'; + +describe('MarketCodeCell', () => { + const renderComponent = (props: MarketCodeCellProps) => { + return render(); + }; + + it('renders SCCR if the market is a successor', () => { + const productType = 'Future'; + const code = 'code'; + const props = { + value: 'code', + data: { + productType, + parentMarketID: 'foo', + successorMarketID: undefined, + }, + } as const; + renderComponent(props); + expect(screen.getByTestId('stack-cell-primary')).toHaveTextContent(code); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + ProductTypeShortName[productType] + ); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + 'SCCR' + ); + expect(screen.getByTestId('stack-cell-secondary')).not.toHaveTextContent( + 'PRNT' + ); + }); + + it('renders PRNT if the market is a parent', () => { + const productType = 'Future'; + const code = 'code'; + const props = { + value: 'code', + data: { + productType, + parentMarketID: undefined, + successorMarketID: 'foo', + }, + } as const; + renderComponent(props); + expect(screen.getByTestId('stack-cell-primary')).toHaveTextContent(code); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + ProductTypeShortName[productType] + ); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + 'PRNT' + ); + expect(screen.getByTestId('stack-cell-secondary')).not.toHaveTextContent( + 'SCCR' + ); + }); + + it('renders both SCCR and PRNT if the market is both a parent and a successor', () => { + const productType = 'Future'; + const code = 'code'; + const props = { + value: 'code', + data: { + productType, + parentMarketID: 'foo', + successorMarketID: 'bar', + }, + } as const; + renderComponent(props); + expect(screen.getByTestId('stack-cell-primary')).toHaveTextContent(code); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + ProductTypeShortName[productType] + ); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + 'PRNT' + ); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + 'SCCR' + ); + }); +}); diff --git a/apps/trading/client-pages/markets/market-code-cell.tsx b/apps/trading/client-pages/markets/market-code-cell.tsx new file mode 100644 index 000000000..68c68e988 --- /dev/null +++ b/apps/trading/client-pages/markets/market-code-cell.tsx @@ -0,0 +1,51 @@ +import compact from 'lodash/compact'; +import type { ProductType } from '@vegaprotocol/types'; +import { ProductTypeMapping, ProductTypeShortName } from '@vegaprotocol/types'; +import { StackedCell } from '@vegaprotocol/datagrid'; +import { t } from '@vegaprotocol/i18n'; + +export interface MarketCodeCellProps { + value: string | undefined; // market code + data: { + productType: ProductType | undefined; + parentMarketID: string | null | undefined; + successorMarketID: string | null | undefined; + }; +} + +export const MarketCodeCell = ({ value, data }: MarketCodeCellProps) => { + if (!value || !data || !data.productType) return null; + + const infoSpanClasses = + 'mr-1 pr-1 uppercase border-r last:pr-0 last:mr-0 last:border-r-0 border-vega-clight-200 dark:border-vega-cdark-200'; + + const info = compact([ + + {ProductTypeShortName[data.productType]} + , + data.parentMarketID && ( + + {t('SCCR')} + + ), + data.successorMarketID && ( + + {t('PRNT')} + + ), + ]); + + return ; +}; diff --git a/apps/trading/client-pages/markets/market-list-table.tsx b/apps/trading/client-pages/markets/market-list-table.tsx new file mode 100644 index 000000000..8ead66f63 --- /dev/null +++ b/apps/trading/client-pages/markets/market-list-table.tsx @@ -0,0 +1,35 @@ +import type { TypedDataAgGrid } from '@vegaprotocol/datagrid'; +import { AgGridLazy as AgGrid, PriceFlashCell } from '@vegaprotocol/datagrid'; +import type { MarketMaybeWithData } from '@vegaprotocol/markets'; +import { useColumnDefs } from './use-column-defs'; + +export const getRowId = ({ data }: { data: { id: string } }) => data.id; + +const defaultColDef = { + sortable: true, + filter: true, + filterParams: { buttons: ['reset'] }, +}; + +const components = { + PriceFlashCell, +}; + +type Props = TypedDataAgGrid; + +export const MarketListTable = (props: Props) => { + const columnDefs = useColumnDefs(); + + return ( + + ); +}; + +export default MarketListTable; diff --git a/libs/markets/src/lib/components/markets-container/market-table-actions.tsx b/apps/trading/client-pages/markets/market-table-actions.tsx similarity index 60% rename from libs/markets/src/lib/components/markets-container/market-table-actions.tsx rename to apps/trading/client-pages/markets/market-table-actions.tsx index 21c8df2da..01db1d70f 100644 --- a/libs/markets/src/lib/components/markets-container/market-table-actions.tsx +++ b/apps/trading/client-pages/markets/market-table-actions.tsx @@ -9,14 +9,21 @@ import { } from '@vegaprotocol/ui-toolkit'; import { DApp, EXPLORER_MARKET, useLinks } from '@vegaprotocol/environment'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; +import { useNavigate } from 'react-router-dom'; +import { Links, Routes } from '../../pages/client-router'; export const MarketActionsDropdown = ({ marketId, assetId, + successorMarketID, + parentMarketID, }: { marketId: string; assetId: string; + successorMarketID: string | null | undefined; + parentMarketID: string | null | undefined; }) => { + const navigate = useNavigate(); const open = useAssetDetailsDialogStore((store) => store.open); const linkCreator = useLinks(DApp.Explorer); @@ -42,6 +49,26 @@ export const MarketActionsDropdown = ({ {t('View settlement asset details')} + {parentMarketID && ( + { + navigate(Links[Routes.MARKET](parentMarketID)); + }} + > + + {t('View parent market')} + + )} + {successorMarketID && ( + { + navigate(Links[Routes.MARKET](successorMarketID)); + }} + > + + {t('View successor market')} + + )} ); }; diff --git a/apps/trading/client-pages/markets/markets-page.tsx b/apps/trading/client-pages/markets/markets-page.tsx index e318222c5..b32bd0a0f 100644 --- a/apps/trading/client-pages/markets/markets-page.tsx +++ b/apps/trading/client-pages/markets/markets-page.tsx @@ -6,7 +6,7 @@ import { Tab, TradingAnchorButton, } from '@vegaprotocol/ui-toolkit'; -import { Markets } from './markets'; +import { OpenMarkets } from './open-markets'; import { Proposed } from './proposed'; import { usePageTitleStore } from '../../stores'; import { Closed } from './closed'; @@ -33,7 +33,7 @@ export const MarketsPage = () => {
- + { - const handleOnSelect = useMarketClickHandler(); - return ( - - ); -}; diff --git a/apps/trading/client-pages/markets/open-markets.tsx b/apps/trading/client-pages/markets/open-markets.tsx new file mode 100644 index 000000000..ee8a20473 --- /dev/null +++ b/apps/trading/client-pages/markets/open-markets.tsx @@ -0,0 +1,63 @@ +import { useDataProvider } from '@vegaprotocol/data-provider'; +import type { MarketMaybeWithData } from '@vegaprotocol/markets'; +import { marketListProvider } from '@vegaprotocol/markets'; +import { useEffect } from 'react'; +import { t } from '@vegaprotocol/i18n'; +import type { CellClickedEvent } from 'ag-grid-community'; +import MarketListTable from './market-list-table'; +import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; +import { Interval } from '@vegaprotocol/types'; +import { useYesterday } from '@vegaprotocol/react-helpers'; + +const POLLING_TIME = 2000; + +export const OpenMarkets = () => { + const handleOnSelect = useMarketClickHandler(); + const yesterday = useYesterday(); + const { data, error, reload } = useDataProvider({ + dataProvider: marketListProvider, + variables: { + since: new Date(yesterday).toISOString(), + interval: Interval.INTERVAL_I1H, + }, + }); + + useEffect(() => { + const interval = setInterval(() => { + reload(); + }, POLLING_TIME); + return () => { + clearInterval(interval); + }; + }, [reload]); + + return ( + ) => { + if (!data) return; + + // prevent navigating to the market page if any of the below cells are clicked + // event.preventDefault or event.stopPropagation dont seem to apply for aggird + const colId = column.getColId(); + + if ( + [ + 'tradableInstrument.instrument.product.settlementAsset.symbol', + 'market-actions', + ].includes(colId) + ) { + return; + } + + // @ts-ignore metaKey exists + handleOnSelect(data.id, event ? event.metaKey : false); + }} + overlayNoRowsTemplate={error ? error.message : t('No markets')} + /> + ); +}; diff --git a/apps/trading/client-pages/markets/oracle-status.tsx b/apps/trading/client-pages/markets/oracle-status.tsx new file mode 100644 index 000000000..4e4f2f984 --- /dev/null +++ b/apps/trading/client-pages/markets/oracle-status.tsx @@ -0,0 +1,46 @@ +import { useEnvironment } from '@vegaprotocol/environment'; +import { Icon } from '@vegaprotocol/ui-toolkit'; +import type { IconName } from '@blueprintjs/icons'; +import type { Market } from '@vegaprotocol/markets'; +import { + getMatchingOracleProvider, + getVerifiedStatusIcon, + useOracleProofs, +} from '@vegaprotocol/markets'; + +export const OracleStatus = ({ + dataSourceSpecForSettlementData, + dataSourceSpecForTradingTermination, +}: Pick< + Market['tradableInstrument']['instrument']['product'], + 'dataSourceSpecForSettlementData' | 'dataSourceSpecForTradingTermination' +>) => { + const { ORACLE_PROOFS_URL } = useEnvironment(); + const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL); + + if (providers) { + const settlementDataProvider = getMatchingOracleProvider( + dataSourceSpecForSettlementData.data, + providers + ); + const tradingTerminationDataProvider = getMatchingOracleProvider( + dataSourceSpecForTradingTermination.data, + providers + ); + let maliciousOracleProvider = null; + + if (settlementDataProvider?.oracle.status !== 'GOOD') { + maliciousOracleProvider = settlementDataProvider; + } else if (tradingTerminationDataProvider?.oracle.status !== 'GOOD') { + maliciousOracleProvider = tradingTerminationDataProvider; + } + + if (!maliciousOracleProvider) return null; + + const { icon } = getVerifiedStatusIcon(maliciousOracleProvider); + + return ; + } + + return null; +}; diff --git a/apps/trading/client-pages/markets/parent-market-cell.tsx b/apps/trading/client-pages/markets/parent-market-cell.tsx new file mode 100644 index 000000000..b5a4bb2fe --- /dev/null +++ b/apps/trading/client-pages/markets/parent-market-cell.tsx @@ -0,0 +1,15 @@ +import { useMarketsMapProvider } from '@vegaprotocol/markets'; + +export const ParentMarketCell = ({ + value, +}: { + value: string; // parentMarketId +}) => { + const { data, loading } = useMarketsMapProvider(); + + if (loading) return null; + + if (!data || !data[value]) return -; + + return
{data[value].tradableInstrument.instrument.code}
; +}; diff --git a/apps/trading/client-pages/markets/proposed.tsx b/apps/trading/client-pages/markets/proposed.tsx index 67a20cbc9..0cf9175c1 100644 --- a/apps/trading/client-pages/markets/proposed.tsx +++ b/apps/trading/client-pages/markets/proposed.tsx @@ -1,6 +1,10 @@ import { ProposalsList } from '@vegaprotocol/proposals'; -import { SuccessorMarketRenderer } from './successor-market-cell'; +import { ParentMarketCell } from './parent-market-cell'; + +const cellRenderers = { + ParentMarketCell, +}; export const Proposed = () => { - return ; + return ; }; diff --git a/apps/trading/client-pages/markets/successor-market-cell.spec.tsx b/apps/trading/client-pages/markets/successor-market-cell.spec.tsx deleted file mode 100644 index f91997194..000000000 --- a/apps/trading/client-pages/markets/successor-market-cell.spec.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { render, screen, act, waitFor } from '@testing-library/react'; -import { MockedProvider } from '@apollo/client/testing'; -import userEvent from '@testing-library/user-event'; -import type { Market } from '@vegaprotocol/markets'; -import { SuccessorMarketRenderer } from './successor-market-cell'; -import { - MarketsDocument, - SuccessorMarketIdsDocument, -} from '@vegaprotocol/markets'; -import { createMarketFragment } from '@vegaprotocol/mock'; - -const mockSuccessorsQuery = [ - { - id: 'market1', - parentMarketID: 'parentMarket1', - successorMarketID: 'successorMarket1', - }, - { id: 'market2', parentMarketID: 'parentMarket2' }, - { id: 'market3', successorMarketID: 'successorMarket3' }, -]; -const parentMarket1 = { - id: 'parentMarket1', - tradableInstrument: { - instrument: { code: 'code parent 1', id: '1' }, - }, -} as unknown as Market; -const successorMarket1 = { - id: 'successorMarket1', - tradableInstrument: { - instrument: { code: 'code successor 1', id: '2' }, - }, -} as unknown as Market; -const parentMarket2 = { - id: 'parentMarket2', - tradableInstrument: { - instrument: { code: 'code parent 2', id: '3' }, - }, -} as unknown as Market; -const successorMarket3 = { - id: 'successorMarket3', - tradableInstrument: { - instrument: { code: 'code successor 3', id: '4' }, - }, -} as unknown as Market; - -const mockMarkets = [ - parentMarket1, - successorMarket1, - parentMarket2, - successorMarket3, -]; - -const mockClickHandler = jest.fn(); -jest.mock('../../lib/hooks/use-market-click-handler', () => ({ - useMarketClickHandler: jest.fn().mockImplementation(() => mockClickHandler), -})); - -const marketMock = { - request: { - query: MarketsDocument, - variables: undefined, - }, - result: { - data: { - marketsConnection: { - edges: mockMarkets.map((item) => ({ - node: { - ...createMarketFragment(item), - }, - })), - }, - }, - }, -}; - -const successorMock = { - request: { - query: SuccessorMarketIdsDocument, - }, - result: { - data: { - marketsConnection: { - edges: mockSuccessorsQuery.map((item) => ({ - node: { - ...item, - }, - })), - }, - }, - }, -}; - -const mocks = [marketMock, successorMock]; - -describe('SuccessorMarketRenderer', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should properly rendered successor market', async () => { - const successorValue = 'market1'; - render( - - - - ); - await waitFor(() => { - expect(screen.getByTestId('market-code')).toBeInTheDocument(); - }); - expect(screen.getByText('code successor 1')).toBeInTheDocument(); - expect(screen.getByText('Futr')).toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button')); - await waitFor(() => { - expect(mockClickHandler).toHaveBeenCalledWith('successorMarket1', false); - }); - }); - it('should properly rendered parent market', async () => { - const successorValue = 'market1'; - render( - - - - ); - await waitFor(() => { - expect(screen.getByTestId('market-code')).toBeInTheDocument(); - }); - expect(screen.getByText('code parent 1')).toBeInTheDocument(); - expect(screen.getByText('Futr')).toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button')); - - await waitFor(() => { - expect(mockClickHandler).toHaveBeenCalledWith('parentMarket1', false); - }); - }); - it('should properly rendered only parent market', async () => { - const successorValue = 'market2'; - const { rerender } = render( - - - - ); - await waitFor(() => { - expect(screen.getByTestId('market-code')).toBeInTheDocument(); - }); - expect(screen.getByText('code parent 2')).toBeInTheDocument(); - expect(screen.getByText('Futr')).toBeInTheDocument(); - await userEvent.click(screen.getByRole('button')); - - await waitFor(() => { - expect(mockClickHandler).toHaveBeenCalledWith('parentMarket2', false); - }); - - rerender( - - - - ); - - expect(screen.getByText('-')).toBeInTheDocument(); - }); - it('should properly rendered only successor market', async () => { - const successorValue = 'market3'; - const { rerender } = render( - - - - ); - await waitFor(() => { - expect(screen.getByTestId('market-code')).toBeInTheDocument(); - }); - expect(screen.getByText('code successor 3')).toBeInTheDocument(); - expect(screen.getByText('Futr')).toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button')); - - await waitFor(() => { - expect(mockClickHandler).toHaveBeenCalledWith('successorMarket3', false); - }); - - await act(() => { - rerender( - - - - ); - }); - expect(screen.getByText('-')).toBeInTheDocument(); - }); -}); diff --git a/apps/trading/client-pages/markets/successor-market-cell.tsx b/apps/trading/client-pages/markets/successor-market-cell.tsx deleted file mode 100644 index da8bb4677..000000000 --- a/apps/trading/client-pages/markets/successor-market-cell.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { MarketNameCell } from '@vegaprotocol/datagrid'; -import { useDataProvider } from '@vegaprotocol/data-provider'; -import { marketProvider, useSuccessorMarketIds } from '@vegaprotocol/markets'; -import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; - -export const SuccessorMarketRenderer = ({ - value, - parent, -}: { - value: string; - parent?: boolean; -}) => { - const successors = useSuccessorMarketIds(value); - const onMarketClick = useMarketClickHandler(); - - const lookupValue = successors - ? parent - ? successors.parentMarketID - : successors.successorMarketID - : ''; - - const { data } = useDataProvider({ - dataProvider: marketProvider, - variables: { - marketId: lookupValue || '', - }, - skip: !lookupValue, - }); - - return data ? ( - - ) : ( - '-' - ); -}; diff --git a/apps/trading/client-pages/markets/use-column-defs.tsx b/apps/trading/client-pages/markets/use-column-defs.tsx new file mode 100644 index 000000000..6f9156c9f --- /dev/null +++ b/apps/trading/client-pages/markets/use-column-defs.tsx @@ -0,0 +1,226 @@ +import { useMemo } from 'react'; +import type { ColDef, ValueFormatterParams } from 'ag-grid-community'; +import { t } from '@vegaprotocol/i18n'; +import type { + VegaICellRendererParams, + VegaValueFormatterParams, + VegaValueGetterParams, +} from '@vegaprotocol/datagrid'; +import { COL_DEFS, SetFilter } from '@vegaprotocol/datagrid'; +import * as Schema from '@vegaprotocol/types'; +import { addDecimalsFormatNumber, toBigNum } from '@vegaprotocol/utils'; +import { ButtonLink, Tooltip } from '@vegaprotocol/ui-toolkit'; +import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; +import type { + MarketMaybeWithData, + MarketMaybeWithDataAndCandles, +} from '@vegaprotocol/markets'; +import { MarketActionsDropdown } from './market-table-actions'; +import { calcCandleVolume } from '@vegaprotocol/markets'; +import { MarketCodeCell } from './market-code-cell'; + +const { MarketTradingMode, AuctionTrigger } = Schema; + +export const useColumnDefs = () => { + const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); + return useMemo( + () => [ + { + headerName: t('Market'), + field: 'tradableInstrument.instrument.code', + flex: 2, + cellRenderer: ({ + value, + data, + }: VegaICellRendererParams< + MarketMaybeWithData, + 'tradableInstrument.instrument.code' + >) => ( + + ), + }, + { + headerName: t('Description'), + field: 'tradableInstrument.instrument.name', + flex: 2, + }, + { + headerName: t('Trading mode'), + field: 'tradingMode', + cellRenderer: ({ + data, + }: VegaICellRendererParams) => { + if (!data?.data) return '-'; + const { trigger, marketTradingMode } = data.data; + + const withTriggerInfo = + marketTradingMode === + MarketTradingMode.TRADING_MODE_MONITORING_AUCTION && + trigger && + trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED; + + if (withTriggerInfo) { + return ( + + + {Schema.MarketTradingModeMapping[marketTradingMode]} + + + ); + } + + return Schema.MarketTradingModeMapping[marketTradingMode]; + }, + filter: SetFilter, + filterParams: { + set: Schema.MarketTradingModeMapping, + }, + }, + { + headerName: t('Status'), + field: 'state', + valueFormatter: ({ + data, + }: VegaValueFormatterParams) => { + return data?.state ? Schema.MarketStateMapping[data.state] : '-'; + }, + filter: SetFilter, + filterParams: { + set: Schema.MarketStateMapping, + }, + }, + { + headerName: t('Mark price'), + field: 'data.markPrice', + type: 'rightAligned', + cellRenderer: 'PriceFlashCell', + filter: 'agNumberColumnFilter', + valueGetter: ({ data }: VegaValueGetterParams) => { + return data?.data?.markPrice === undefined + ? undefined + : toBigNum(data?.data?.markPrice, data.decimalPlaces).toNumber(); + }, + valueFormatter: ({ + data, + }: VegaValueFormatterParams) => + data?.data?.bestOfferPrice === undefined + ? '-' + : addDecimalsFormatNumber(data.data.markPrice, data.decimalPlaces), + }, + { + headerName: t('24h volume'), + type: 'rightAligned', + field: 'data.candles', + valueGetter: ({ + data, + }: VegaValueGetterParams) => { + if (!data) return 0; + const candles = data?.candles; + const vol = candles ? calcCandleVolume(candles) : '0'; + return Number(vol); + }, + valueFormatter: ({ + data, + }: ValueFormatterParams) => { + const candles = data?.candles; + const vol = candles ? calcCandleVolume(candles) : '0'; + const volume = + data && vol && vol !== '0' + ? addDecimalsFormatNumber(vol, data.positionDecimalPlaces) + : '0.00'; + return volume; + }, + }, + { + headerName: t('Settlement asset'), + field: 'tradableInstrument.instrument.product.settlementAsset.symbol', + cellRenderer: ({ + data, + }: VegaICellRendererParams< + MarketMaybeWithData, + 'tradableInstrument.instrument.product.settlementAsset.symbol' + >) => { + const value = + data?.tradableInstrument.instrument.product.settlementAsset; + return value ? ( + { + openAssetDetailsDialog(value.id, e.target as HTMLElement); + }} + > + {value.symbol} + + ) : ( + '' + ); + }, + }, + { + headerName: t('Spread'), + field: 'data.bestBidPrice', + type: 'rightAligned', + filter: 'agNumberColumnFilter', + cellRenderer: 'PriceFlashCell', + valueGetter: ({ data }: VegaValueGetterParams) => { + if (!data || !data.data?.bestOfferPrice || !data.data?.bestBidPrice) { + return undefined; + } + + const offer = toBigNum(data.data.bestOfferPrice, data.decimalPlaces); + const bid = toBigNum(data.data.bestBidPrice, data.decimalPlaces); + + const spread = offer.minus(bid).toNumber(); + + // The calculation above can result in '-0' being rendered after formatting + // so return Math.abs to remove it and just render '0' + if (spread === 0) { + return Math.abs(spread); + } + + return spread; + }, + valueFormatter: ({ + value, + }: VegaValueFormatterParams< + MarketMaybeWithData, + 'data.bestBidPrice' + >) => { + if (!value) return '-'; + return value.toString(); + }, + }, + { + colId: 'market-actions', + field: 'id', + ...COL_DEFS.actions, + cellRenderer: ({ + data, + }: VegaICellRendererParams) => { + if (!data) return null; + return ( + + ); + }, + }, + ], + [openAssetDetailsDialog] + ); +}; diff --git a/apps/trading/lib/hooks/use-market-click-handler.ts b/apps/trading/lib/hooks/use-market-click-handler.ts index 15db3617e..2fef70d08 100644 --- a/apps/trading/lib/hooks/use-market-click-handler.ts +++ b/apps/trading/lib/hooks/use-market-click-handler.ts @@ -1,24 +1,18 @@ -import { useNavigate, useParams, useLocation } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import { useCallback } from 'react'; import { Links, Routes } from '../../pages/client-router'; export const useMarketClickHandler = (replace = false) => { const navigate = useNavigate(); - const { marketId } = useParams(); - const { pathname } = useLocation(); - const isMarketPage = pathname.match(/^\/markets\/(.+)/); - return useCallback( - (selectedId: string, metaKey?: boolean) => { - const link = Links[Routes.MARKET](selectedId); - if (metaKey) { - window.open(`/#${link}`, '_blank'); - } else if (selectedId !== marketId || !isMarketPage) { - navigate(link, { replace }); - } - }, - [navigate, marketId, replace, isMarketPage] - ); + return (selectedId: string, metaKey?: boolean) => { + const link = Links[Routes.MARKET](selectedId); + if (metaKey) { + window.open(`/#${link}`, '_blank'); + } else { + navigate(link, { replace }); + } + }; }; export const useMarketLiquidityClickHandler = () => { diff --git a/libs/datagrid/src/index.ts b/libs/datagrid/src/index.ts index 76938986b..b166a9b7c 100644 --- a/libs/datagrid/src/index.ts +++ b/libs/datagrid/src/index.ts @@ -12,6 +12,7 @@ export * from './lib/cells/centered-grid-cell'; export * from './lib/cells/market-name-cell'; export * from './lib/cells/order-type-cell'; export * from './lib/cells/size'; +export * from './lib/cells/stacked-cell'; export * from './lib/filters/date-range-filter'; export * from './lib/filters/set-filter'; diff --git a/libs/positions/src/lib/stacked-cell.tsx b/libs/datagrid/src/lib/cells/stacked-cell.tsx similarity index 100% rename from libs/positions/src/lib/stacked-cell.tsx rename to libs/datagrid/src/lib/cells/stacked-cell.tsx diff --git a/libs/markets/src/lib/__generated__/markets.ts b/libs/markets/src/lib/__generated__/markets.ts index addf669a7..9a3feea33 100644 --- a/libs/markets/src/lib/__generated__/markets.ts +++ b/libs/markets/src/lib/__generated__/markets.ts @@ -7,12 +7,12 @@ export type DataSourceFilterFragment = { __typename?: 'Filter', key: { __typenam export type DataSourceSpecFragment = { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }; -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 | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, 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, parentMarketID?: string | null, successorMarketID?: string | null, 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 | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } }; export type MarketsQueryVariables = Types.Exact<{ [key: string]: never; }>; -export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, 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, parentMarketID?: string | null, successorMarketID?: string | null, 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 | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null }; export const DataSourceFilterFragmentDoc = gql` fragment DataSourceFilter on Filter { @@ -55,6 +55,8 @@ export const MarketFieldsFragmentDoc = gql` positionDecimalPlaces state tradingMode + parentMarketID + successorMarketID fees { factors { makerFee diff --git a/libs/markets/src/lib/components/index.ts b/libs/markets/src/lib/components/index.ts index 64fd41a55..b8bcfcaea 100644 --- a/libs/markets/src/lib/components/index.ts +++ b/libs/markets/src/lib/components/index.ts @@ -2,7 +2,6 @@ export * from './fees-breakdown'; export * from './last-24h-price-change'; export * from './last-24h-volume'; export * from './market-info'; -export * from './markets-container'; export * from './oracle-banner'; export * from './oracle-basic-profile'; export * from './oracle-full-profile'; diff --git a/libs/markets/src/lib/components/markets-container/index.ts b/libs/markets/src/lib/components/markets-container/index.ts deleted file mode 100644 index 1c9c1e696..000000000 --- a/libs/markets/src/lib/components/markets-container/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './markets-container'; -export * from './market-table-actions'; diff --git a/libs/markets/src/lib/components/markets-container/market-list-table.tsx b/libs/markets/src/lib/components/markets-container/market-list-table.tsx deleted file mode 100644 index a16613077..000000000 --- a/libs/markets/src/lib/components/markets-container/market-list-table.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import type { TypedDataAgGrid } from '@vegaprotocol/datagrid'; -import { - AgGridLazy as AgGrid, - PriceFlashCell, - MarketNameCell, -} from '@vegaprotocol/datagrid'; -import type { MarketMaybeWithData } from '../../markets-provider'; -import { OracleStatus } from './oracle-status'; -import { useColumnDefs } from './use-column-defs'; - -export const getRowId = ({ data }: { data: { id: string } }) => data.id; - -interface MarketNameCellProps { - value?: string; - data?: MarketMaybeWithData; - onMarketClick?: (marketId: string, metaKey?: boolean) => void; -} - -const MarketName = (props: MarketNameCellProps) => ( - <> - - {props.data ? ( - - ) : null} - -); - -const defaultColDef = { - sortable: true, - filter: true, - filterParams: { buttons: ['reset'] }, -}; -type Props = TypedDataAgGrid & { - onMarketClick: (marketId: string, metaKey?: boolean) => void; - SuccessorMarketRenderer?: React.FC<{ value: string }>; -}; -export const MarketListTable = ({ - onMarketClick, - SuccessorMarketRenderer, - ...props -}: Props) => { - const columnDefs = useColumnDefs({ onMarketClick }); - const components = { - PriceFlashCell, - MarketName, - ...(SuccessorMarketRenderer ? { SuccessorMarketRenderer } : null), - }; - return ( - - ); -}; - -export default MarketListTable; diff --git a/libs/markets/src/lib/components/markets-container/markets-container.spec.tsx b/libs/markets/src/lib/components/markets-container/markets-container.spec.tsx deleted file mode 100644 index fe88d1dbc..000000000 --- a/libs/markets/src/lib/components/markets-container/markets-container.spec.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { render, screen, act, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import * as DataProviders from '@vegaprotocol/data-provider'; -import { MockedProvider } from '@apollo/react-testing'; -import type { MarketMaybeWithData } from '../../markets-provider'; -import { MarketsContainer } from './markets-container'; -import { FLAGS } from '@vegaprotocol/environment'; - -jest.mock('@vegaprotocol/environment', () => { - const actual = jest.requireActual('@vegaprotocol/environment'); - return { - ...actual, - FLAGS: { - ...actual.FLAGS, - SUCCESSOR_MARKETS: true, - }, - }; -}); -const SuccessorMarketRenderer = ({ value }: { value: string }) => { - return '-'; -}; - -const market = { - id: 'id-1', - tradableInstrument: { - instrument: { - product: { settlementAsset: { id: 'assetId-1' } }, - }, - }, - decimalPlaces: 1, - positionDecimalPlaces: 1, - state: 'STATE_ACTIVE', - tradingMode: 'TRADING_MODE_OPENING_AUCTION', - data: { - bestBidPrice: 100, - }, -} as unknown as MarketMaybeWithData; - -describe('MarketsContainer', () => { - const spyOnSelect = jest.fn(); - beforeEach(() => { - jest.clearAllMocks(); - - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(DataProviders, 'useDataProvider') - .mockImplementation(() => { - return { - error: null, - reload: jest.fn(), - data: [market], - }; - }); - }); - it('context menu should stay open', async () => { - let rerenderRef: (ui: React.ReactElement) => void; - await act(async () => { - const { rerender } = render( - - - - ); - rerenderRef = rerender; - }); - - // make sure ag grid is finished initializaing - const rowContainer = await screen.findByRole('rowgroup', { - name: (_name, element) => - element.classList.contains('ag-center-cols-container'), - }); - expect(within(rowContainer).getAllByRole('row')).toHaveLength(1); - expect( - screen.getByRole('rowgroup', { - name: (_name, element) => - element.classList.contains('ag-pinned-right-cols-container'), - }) - ).toBeInTheDocument(); - - // open the dropdown - await userEvent.click( - screen.getByRole('button', { - name: (_name, element) => - (element.parentNode as Element) - ?.getAttribute('id') - ?.startsWith('cell-market-actions-') || false, - }) - ); - - await checkDropdown(); - - // reset the mock and rerender so the component - // updates with new data - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(DataProviders, 'useDataProvider') - .mockImplementation(() => { - return { - error: null, - reload: jest.fn(), - data: [{ ...market, state: 'STATE_PENDING' }], - }; - }); - - // @ts-ignore we await the act above so rerenderRef is definitely defined - rerenderRef( - - - - ); - - // make sure dropdown is still open - await checkDropdown(); - - async function checkDropdown() { - const dropdownContent = await screen.findByTestId( - 'market-actions-content' - ); - expect(dropdownContent).toBeInTheDocument(); - expect( - within(dropdownContent).getByRole('menuitem', { - name: 'Copy Market ID', - }) - ).toBeInTheDocument(); - } - }); - - it('feature flag should hide successorMarketID column', async () => { - const mockedFlags = jest.mocked(FLAGS); - mockedFlags.SUCCESSOR_MARKETS = false; - - const spySuccessorMarketRenderer = jest.fn(); - - render( - - - - ); - - expect(spySuccessorMarketRenderer).not.toHaveBeenCalled(); - screen.getAllByRole('columnheader').forEach((element) => { - expect(element.getAttribute('col-id')).not.toEqual('successorMarketID'); - }); - }); -}); diff --git a/libs/markets/src/lib/components/markets-container/markets-container.tsx b/libs/markets/src/lib/components/markets-container/markets-container.tsx deleted file mode 100644 index 826bd0aac..000000000 --- a/libs/markets/src/lib/components/markets-container/markets-container.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import type { MouseEvent } from 'react'; -import React, { useEffect } from 'react'; -import type { CellClickedEvent } from 'ag-grid-community'; -import { t } from '@vegaprotocol/i18n'; -import { MarketListTable } from './market-list-table'; -import { useDataProvider } from '@vegaprotocol/data-provider'; -import { marketListProvider as dataProvider } from '../../markets-provider'; -import type { MarketMaybeWithData } from '../../markets-provider'; -import { useYesterday } from '@vegaprotocol/react-helpers'; -import { Interval } from '@vegaprotocol/types'; - -const POLLING_TIME = 2000; -interface MarketsContainerProps { - onSelect: (marketId: string, metaKey?: boolean) => void; - SuccessorMarketRenderer?: React.FC<{ value: string }>; -} - -export const MarketsContainer = ({ - onSelect, - SuccessorMarketRenderer, -}: MarketsContainerProps) => { - const yesterday = useYesterday(); - const { data, error, reload } = useDataProvider({ - dataProvider, - variables: { - since: new Date(yesterday).toISOString(), - interval: Interval.INTERVAL_I1H, - }, - }); - - useEffect(() => { - const interval = setInterval(() => { - reload(); - }, POLLING_TIME); - return () => { - clearInterval(interval); - }; - }, [reload]); - - return ( -
- { - const { data, column, event } = cellEvent; - // prevent navigating to the market page if any of the below cells are clicked - // event.preventDefault or event.stopPropagation dont seem to apply for aggird - const colId = column.getColId(); - if ( - [ - 'id', - 'tradableInstrument.instrument.code', - 'tradableInstrument.instrument.product.settlementAsset', - 'tradableInstrument.instrument.product.settlementAsset.symbol', - 'market-actions', - ].includes(colId) - ) { - return; - } - onSelect( - (data as MarketMaybeWithData).id, - (event as unknown as MouseEvent)?.metaKey || - (event as unknown as MouseEvent)?.ctrlKey - ); - }} - onMarketClick={onSelect} - overlayNoRowsTemplate={error ? error.message : t('No markets')} - SuccessorMarketRenderer={SuccessorMarketRenderer} - /> -
- ); -}; diff --git a/libs/markets/src/lib/components/markets-container/oracle-status.tsx b/libs/markets/src/lib/components/markets-container/oracle-status.tsx deleted file mode 100644 index 3165c6ce7..000000000 --- a/libs/markets/src/lib/components/markets-container/oracle-status.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useMemo } from 'react'; -import { useEnvironment } from '@vegaprotocol/environment'; -import { Icon } from '@vegaprotocol/ui-toolkit'; -import type { IconName } from '@blueprintjs/icons'; -import { getMatchingOracleProvider, useOracleProofs } from '../../hooks'; -import type { Market } from '../../markets-provider'; -import { getVerifiedStatusIcon } from '../oracle-basic-profile'; - -export const OracleStatus = ({ - dataSourceSpecForSettlementData, - dataSourceSpecForTradingTermination, -}: Pick< - Market['tradableInstrument']['instrument']['product'], - 'dataSourceSpecForSettlementData' | 'dataSourceSpecForTradingTermination' ->) => { - const { ORACLE_PROOFS_URL } = useEnvironment(); - const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL); - return useMemo(() => { - if (providers) { - const settlementDataProvider = getMatchingOracleProvider( - dataSourceSpecForSettlementData.data, - providers - ); - const tradingTerminationDataProvider = getMatchingOracleProvider( - dataSourceSpecForTradingTermination.data, - providers - ); - let maliciousOracleProvider = null; - if (settlementDataProvider?.oracle.status !== 'GOOD') { - maliciousOracleProvider = settlementDataProvider; - } else if (tradingTerminationDataProvider?.oracle.status !== 'GOOD') { - maliciousOracleProvider = tradingTerminationDataProvider; - } - if (!maliciousOracleProvider) return null; - const { icon } = getVerifiedStatusIcon(maliciousOracleProvider); - return ; - } - return null; - }, [ - providers, - dataSourceSpecForSettlementData, - dataSourceSpecForTradingTermination, - ]); -}; diff --git a/libs/markets/src/lib/components/markets-container/summary-cell.tsx b/libs/markets/src/lib/components/markets-container/summary-cell.tsx deleted file mode 100644 index 1d558ee9d..000000000 --- a/libs/markets/src/lib/components/markets-container/summary-cell.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import * as React from 'react'; - -/* -import { useMarketOverview } from '../../../../hooks/use-market-overview' -import { colorByMarketMovement } from '../../../../lib/vega-colours' -import { Sparkline } from '../../components/sparkline' -import { VEGA_TABLE_CLASSES } from '../../components/vega-table' -*/ - -export interface SummaryCellProps { - value: string; // marketId -} - -export const SummaryCellView = ({ value }: SummaryCellProps) => { - // const { sparkline, change, bullish } = useMarketOverview(value) - // const color = colorByMarketMovement(bullish) - - return ( - <> - {/* */} - {'change'} - - ); -}; - -SummaryCellView.displayName = 'SummaryCellView'; - -export const SummaryCell = React.memo(SummaryCellView); -SummaryCell.displayName = 'SummaryCell'; diff --git a/libs/markets/src/lib/components/markets-container/use-column-defs.tsx b/libs/markets/src/lib/components/markets-container/use-column-defs.tsx deleted file mode 100644 index a0511303c..000000000 --- a/libs/markets/src/lib/components/markets-container/use-column-defs.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import { useMemo } from 'react'; -import type { ColDef, ValueFormatterParams } from 'ag-grid-community'; -import compact from 'lodash/compact'; -import { t } from '@vegaprotocol/i18n'; -import type { - VegaICellRendererParams, - VegaValueFormatterParams, - VegaValueGetterParams, -} from '@vegaprotocol/datagrid'; -import { COL_DEFS, SetFilter } from '@vegaprotocol/datagrid'; -import * as Schema from '@vegaprotocol/types'; -import { addDecimalsFormatNumber, toBigNum } from '@vegaprotocol/utils'; -import { ButtonLink, Tooltip } from '@vegaprotocol/ui-toolkit'; -import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; -import type { - MarketMaybeWithData, - MarketMaybeWithDataAndCandles, -} from '../../markets-provider'; -import { MarketActionsDropdown } from './market-table-actions'; -import { calcCandleVolume } from '../../market-utils'; - -interface Props { - onMarketClick: (marketId: string, metaKey?: boolean) => void; -} - -const { MarketTradingMode, AuctionTrigger } = Schema; - -export const useColumnDefs = ({ onMarketClick }: Props) => { - const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); - return useMemo( - () => - compact([ - { - headerName: t('Market'), - field: 'tradableInstrument.instrument.code', - cellRenderer: 'MarketName', - cellRendererParams: { onMarketClick }, - flex: 2, - }, - { - headerName: t('Description'), - field: 'tradableInstrument.instrument.name', - flex: 2, - }, - { - headerName: t('Trading mode'), - field: 'tradingMode', - cellRenderer: ({ - data, - }: VegaICellRendererParams) => { - if (!data?.data) return '-'; - const { trigger, marketTradingMode } = data.data; - - const withTriggerInfo = - marketTradingMode === - MarketTradingMode.TRADING_MODE_MONITORING_AUCTION && - trigger && - trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED; - - if (withTriggerInfo) { - return ( - - - {Schema.MarketTradingModeMapping[marketTradingMode]} - - - ); - } - - return Schema.MarketTradingModeMapping[marketTradingMode]; - }, - filter: SetFilter, - filterParams: { - set: Schema.MarketTradingModeMapping, - }, - }, - { - headerName: t('Status'), - field: 'state', - valueFormatter: ({ - data, - }: VegaValueFormatterParams) => { - return data?.state ? Schema.MarketStateMapping[data.state] : '-'; - }, - filter: SetFilter, - filterParams: { - set: Schema.MarketStateMapping, - }, - }, - { - headerName: t('Mark price'), - field: 'data.markPrice', - type: 'rightAligned', - cellRenderer: 'PriceFlashCell', - filter: 'agNumberColumnFilter', - valueGetter: ({ - data, - }: VegaValueGetterParams) => { - return data?.data?.markPrice === undefined - ? undefined - : toBigNum(data?.data?.markPrice, data.decimalPlaces).toNumber(); - }, - valueFormatter: ({ - data, - }: VegaValueFormatterParams) => - data?.data?.bestOfferPrice === undefined - ? '-' - : addDecimalsFormatNumber( - data.data.markPrice, - data.decimalPlaces - ), - }, - { - headerName: t('24h volume'), - type: 'rightAligned', - field: 'data.candles', - valueGetter: ({ - data, - }: VegaValueGetterParams) => { - if (!data) return 0; - const candles = data?.candles; - const vol = candles ? calcCandleVolume(candles) : '0'; - return Number(vol); - }, - valueFormatter: ({ - data, - }: ValueFormatterParams< - MarketMaybeWithDataAndCandles, - 'candles' - >) => { - const candles = data?.candles; - const vol = candles ? calcCandleVolume(candles) : '0'; - const volume = - data && vol && vol !== '0' - ? addDecimalsFormatNumber(vol, data.positionDecimalPlaces) - : '0.00'; - return volume; - }, - }, - { - headerName: t('Settlement asset'), - field: 'tradableInstrument.instrument.product.settlementAsset.symbol', - cellRenderer: ({ - data, - }: VegaICellRendererParams< - MarketMaybeWithData, - 'tradableInstrument.instrument.product.settlementAsset.symbol' - >) => { - const value = - data?.tradableInstrument.instrument.product.settlementAsset; - return value ? ( - { - openAssetDetailsDialog(value.id, e.target as HTMLElement); - }} - > - {value.symbol} - - ) : ( - '' - ); - }, - }, - { - headerName: t('Spread'), - field: 'data.bestBidPrice', - type: 'rightAligned', - filter: 'agNumberColumnFilter', - cellRenderer: 'PriceFlashCell', - valueGetter: ({ - data, - }: VegaValueGetterParams) => { - if ( - !data || - !data.data?.bestOfferPrice || - !data.data?.bestBidPrice - ) { - return undefined; - } - - const offer = toBigNum( - data.data.bestOfferPrice, - data.decimalPlaces - ); - const bid = toBigNum(data.data.bestBidPrice, data.decimalPlaces); - - const spread = offer.minus(bid).toNumber(); - - // The calculation above can result in '-0' being rendered after formatting - // so return Math.abs to remove it and just render '0' - if (spread === 0) { - return Math.abs(spread); - } - - return spread; - }, - valueFormatter: ({ - value, - }: VegaValueFormatterParams< - MarketMaybeWithData, - 'data.bestBidPrice' - >) => { - if (!value) return '-'; - return value.toString(); - }, - }, - { - colId: 'market-actions', - field: 'id', - ...COL_DEFS.actions, - cellRenderer: ({ - data, - }: VegaICellRendererParams) => { - if (!data) return null; - return ( - - ); - }, - }, - ]), - [onMarketClick, openAssetDetailsDialog] - ); -}; diff --git a/libs/markets/src/lib/components/oracle-basic-profile/oracle-basic-profile.tsx b/libs/markets/src/lib/components/oracle-basic-profile/oracle-basic-profile.tsx index a8afd2966..53531bca1 100644 --- a/libs/markets/src/lib/components/oracle-basic-profile/oracle-basic-profile.tsx +++ b/libs/markets/src/lib/components/oracle-basic-profile/oracle-basic-profile.tsx @@ -43,6 +43,7 @@ export const getVerifiedStatusIcon = (provider: Provider) => { const lastVerified = provider.oracle.last_verified ? new Date(provider.oracle.last_verified) : new Date(provider.oracle.first_verified); + return { ...getIconIntent(), message: t( @@ -112,13 +113,13 @@ export const OracleBasicProfile = ({ -

+

{message}

{oracleMarkets && (

{t('Involved in %s %s', [ oracleMarkets.length.toString(), @@ -130,9 +131,9 @@ export const OracleBasicProfile = ({

{links.map((link) => ( - + - {link.type} + {link.type} diff --git a/libs/markets/src/lib/markets-provider.ts b/libs/markets/src/lib/markets-provider.ts index 021b78fca..c9ded0346 100644 --- a/libs/markets/src/lib/markets-provider.ts +++ b/libs/markets/src/lib/markets-provider.ts @@ -28,8 +28,6 @@ import { } from './market-utils'; import { MarketsDocument } from './__generated__/markets'; import type { Candle } from './market-candles-provider'; -import type { SuccessorMarketIdsQuery } from './__generated__/SuccessorMarket'; -import { SuccessorMarketIdsDocument } from './__generated__'; export type Market = MarketFieldsFragment; @@ -240,34 +238,3 @@ export const useMarketList = () => { reload, }; }; - -export type MarketSuccessors = { - __typename?: 'Market'; - id: string; - successorMarketID?: string | null; - parentMarketID?: string | null; -}; -const getMarketSuccessorData = ( - responseData: SuccessorMarketIdsQuery | null -): MarketSuccessors[] | null => - responseData?.marketsConnection?.edges.map((edge) => edge.node) || null; - -export const marketSuccessorProvider = makeDataProvider< - SuccessorMarketIdsQuery, - MarketSuccessors[], - never, - never ->({ - query: SuccessorMarketIdsDocument, - getData: getMarketSuccessorData, - fetchPolicy: 'no-cache', -}); - -export const useSuccessorMarketIds = (marketId: string) => { - const { data } = useDataProvider({ - dataProvider: marketSuccessorProvider, - variables: undefined, - skip: !marketId, - }); - return data?.find((item) => item.id === marketId) ?? null; -}; diff --git a/libs/markets/src/lib/markets.graphql b/libs/markets/src/lib/markets.graphql index 84e25a6e2..ca424da28 100644 --- a/libs/markets/src/lib/markets.graphql +++ b/libs/markets/src/lib/markets.graphql @@ -36,6 +36,8 @@ fragment MarketFields on Market { positionDecimalPlaces state tradingMode + parentMarketID + successorMarketID fees { factors { makerFee diff --git a/libs/markets/src/lib/markets.mock.ts b/libs/markets/src/lib/markets.mock.ts index 01af90c17..1a9d1ae48 100644 --- a/libs/markets/src/lib/markets.mock.ts +++ b/libs/markets/src/lib/markets.mock.ts @@ -42,6 +42,8 @@ export const createMarketFragment = ( close: null, open: null, }, + successorMarketID: null, + parentMarketID: null, fees: { __typename: 'Fees', factors: { diff --git a/libs/positions/src/lib/positions-table.tsx b/libs/positions/src/lib/positions-table.tsx index a7ef20e93..8d7d5a652 100644 --- a/libs/positions/src/lib/positions-table.tsx +++ b/libs/positions/src/lib/positions-table.tsx @@ -15,6 +15,7 @@ import { MarketNameCell, ProgressBarCell, MarketProductPill, + StackedCell, } from '@vegaprotocol/datagrid'; import { ButtonLink, @@ -40,7 +41,6 @@ import { import { DocsLinks } from '@vegaprotocol/environment'; import { PositionActionsDropdown } from './position-actions-dropdown'; import { LiquidationPrice } from './liquidation-price'; -import { StackedCell } from './stacked-cell'; interface Props extends TypedDataAgGrid { onClose?: (data: Position) => void; diff --git a/libs/proposals/src/components/proposals-list/proposal-list.spec.tsx b/libs/proposals/src/components/proposals-list/proposal-list.spec.tsx index dc003b0e9..80354dbc1 100644 --- a/libs/proposals/src/components/proposals-list/proposal-list.spec.tsx +++ b/libs/proposals/src/components/proposals-list/proposal-list.spec.tsx @@ -1,10 +1,4 @@ -import { - render, - screen, - act, - waitFor, - getAllByRole, -} from '@testing-library/react'; +import { render, screen, waitFor, within } from '@testing-library/react'; import merge from 'lodash/merge'; import type { MockedResponse } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing'; @@ -14,25 +8,13 @@ import { createProposalListFieldsFragment } from '../../lib/proposals-data-provi import type { ProposalsListQuery } from '../../lib'; import { ProposalsListDocument } from '../../lib'; import type { PartialDeep } from 'type-fest'; -import { FLAGS } from '@vegaprotocol/environment'; -jest.mock('@vegaprotocol/environment', () => { - const actual = jest.requireActual('@vegaprotocol/environment'); - return { - ...actual, - FLAGS: { - ...actual.FLAGS, - SUCCESSOR_MARKETS: true, - }, - }; -}); - -const successorMarketName = 'Successor Market Name'; -const spySuccessorMarketRenderer = jest - .fn() - .mockReturnValue(successorMarketName); +const parentMarketName = 'Parent Market Name'; +const ParentMarketCell = () => {parentMarketName}; describe('ProposalsList', () => { + const rowContainerSelector = '.ag-center-cols-container'; + const createProposalsMock = (override?: PartialDeep) => { const defaultProposalEdges = [ { @@ -81,75 +63,55 @@ describe('ProposalsList', () => { return mock; }; + beforeEach(() => { jest.clearAllMocks(); }); + it('should be properly rendered', async () => { const mock = createProposalsMock(); - await act(() => { - render( - - - - ); - }); - const container = document.querySelector('.ag-center-cols-container'); + render( + + + + ); + await waitFor(() => { - expect(container).toBeInTheDocument(); - }); - expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(3); - }); - - it('some of states should be filtered out', async () => { - const proposalNode = createProposalListFieldsFragment({ - id: 'id-1', - state: Types.ProposalState.STATE_ENACTED, + expect(document.querySelector(rowContainerSelector)).toBeInTheDocument(); }); - const mock = createProposalsMock({ - proposalsConnection: { - edges: [ - { - __typename: 'ProposalEdge', - node: { - ...proposalNode, - terms: { - ...proposalNode.terms, - change: { - ...proposalNode.terms.change, - }, - }, - }, - }, - ], - }, - } as PartialDeep); - await act(() => { - render( - - - - ); - }); - const container = document.querySelector('.ag-center-cols-container'); - await waitFor(() => { - expect(container).toBeInTheDocument(); - expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2); - }); + const expectedHeaders = [ + 'Market', + 'Settlement asset', + 'State', + 'Parent market', + 'Voting', + 'Closing date', + 'Enactment date', + '', // actions col + ]; - expect(spySuccessorMarketRenderer).toHaveBeenCalled(); + const headers = screen.getAllByRole('columnheader'); + expect(headers).toHaveLength(expectedHeaders.length); expect( - screen.getByRole('columnheader', { - name: (_name, element) => - element.getAttribute('col-id') === 'parentMarket', - }) - ).toBeInTheDocument(); + headers.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim()) + ).toEqual(expectedHeaders); + + const container = within( + document.querySelector(rowContainerSelector) as HTMLElement + ); + expect(container.getAllByRole('row')).toHaveLength( + // @ts-ignore data is mocked + mock?.result?.data.proposalsConnection.edges.length + ); + expect( - screen.getAllByRole('gridcell', { - name: (name, element) => - element.getAttribute('col-id') === 'parentMarket', + container.getAllByRole('gridcell', { + name: (_, element) => + element.getAttribute('col-id') === + 'terms.change.successorConfiguration.parentMarketId', })[0] - ).toHaveTextContent(successorMarketName); + ).toHaveTextContent(parentMarketName); }); it('empty response should causes no data message display', async () => { @@ -169,58 +131,11 @@ describe('ProposalsList', () => { }, }, }; - await act(() => { - render( - - - - ); - }); - expect(await screen.findByText('No markets')).toBeInTheDocument(); - - expect( - screen.getByRole('columnheader', { - name: (_name, element) => - element.getAttribute('col-id') === 'parentMarket', - }) - ).toBeInTheDocument(); - }); - - it('feature flag should hide parent marketcolumn', async () => { - const mockedFlags = jest.mocked(FLAGS); - mockedFlags.SUCCESSOR_MARKETS = false; - const mock: MockedResponse = { - request: { - query: ProposalsListDocument, - variables: { - proposalType: Types.ProposalType.TYPE_NEW_MARKET, - }, - }, - result: { - data: { - proposalsConnection: { - __typename: 'ProposalsConnection', - edges: [], - }, - }, - }, - }; - await act(() => { - render( - - - - ); - }); - await waitFor(() => { - expect( - screen.getByRole('columnheader', { - name: (_name, element) => element.getAttribute('col-id') === 'market', - }) - ).toBeInTheDocument(); - }); - screen.getAllByRole('columnheader').forEach((element) => { - expect(element.getAttribute('col-id')).not.toEqual('parentMarket'); - }); + render( + + + + ); + expect(await screen.findByText('No proposed markets')).toBeInTheDocument(); }); }); diff --git a/libs/proposals/src/components/proposals-list/proposals-list.tsx b/libs/proposals/src/components/proposals-list/proposals-list.tsx index 9cdb01aef..d4cd1a8c2 100644 --- a/libs/proposals/src/components/proposals-list/proposals-list.tsx +++ b/libs/proposals/src/components/proposals-list/proposals-list.tsx @@ -1,11 +1,11 @@ -import React from 'react'; +import type { FC } from 'react'; import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { t } from '@vegaprotocol/i18n'; import * as Types from '@vegaprotocol/types'; -import { MarketNameProposalCell, useColumnDefs } from './use-column-defs'; +import { removePaginationWrapper } from '@vegaprotocol/utils'; import type { ProposalListFieldsFragment } from '../../lib/proposals-data-provider/__generated__/Proposals'; import { useProposalsListQuery } from '../../lib/proposals-data-provider/__generated__/Proposals'; -import { removePaginationWrapper } from '@vegaprotocol/utils'; +import { useColumnDefs } from './use-column-defs'; export const getNewMarketProposals = (data: ProposalListFieldsFragment[]) => data.filter((proposal) => @@ -16,13 +16,19 @@ export const getNewMarketProposals = (data: ProposalListFieldsFragment[]) => ].includes(proposal.state) ); +const defaultColDef = { + sortable: true, + filter: true, + filterParams: { buttons: ['reset'] }, +}; + interface ProposalListProps { - SuccessorMarketRenderer: React.FC<{ value: string }>; + cellRenderers: { + [name: string]: FC<{ value: string; data: ProposalListFieldsFragment }>; + }; } -export const ProposalsList = ({ - SuccessorMarketRenderer, -}: ProposalListProps) => { +export const ProposalsList = ({ cellRenderers }: ProposalListProps) => { const { data } = useProposalsListQuery({ variables: { proposalType: Types.ProposalType.TYPE_NEW_MARKET, @@ -32,7 +38,7 @@ export const ProposalsList = ({ const filteredData = getNewMarketProposals( removePaginationWrapper(data?.proposalsConnection?.edges) ); - const { columnDefs, defaultColDef } = useColumnDefs(); + const columnDefs = useColumnDefs(); return ( data.id} - overlayNoRowsTemplate={t('No markets')} - components={{ SuccessorMarketRenderer, MarketNameProposalCell }} + overlayNoRowsTemplate={t('No proposed markets')} + components={cellRenderers} + rowHeight={45} /> ); }; diff --git a/libs/proposals/src/components/proposals-list/use-column-defs.tsx b/libs/proposals/src/components/proposals-list/use-column-defs.tsx index 795a0fa8c..c0ce65d31 100644 --- a/libs/proposals/src/components/proposals-list/use-column-defs.tsx +++ b/libs/proposals/src/components/proposals-list/use-column-defs.tsx @@ -6,9 +6,9 @@ import { COL_DEFS, DateRangeFilter, SetFilter, + StackedCell, } from '@vegaprotocol/datagrid'; import compact from 'lodash/compact'; -import { useEnvironment, FLAGS } from '@vegaprotocol/environment'; import { getDateTimeFormat } from '@vegaprotocol/utils'; import { t } from '@vegaprotocol/i18n'; import { @@ -19,50 +19,15 @@ import type { VegaICellRendererParams, VegaValueFormatterParams, } from '@vegaprotocol/datagrid'; -import { ExternalLink, Pill } from '@vegaprotocol/ui-toolkit'; import { - ProposalProductTypeMapping, - ProposalProductTypeShortName, + ProductTypeMapping, + ProductTypeShortName, ProposalStateMapping, } from '@vegaprotocol/types'; import type { ProposalListFieldsFragment } from '../../lib/proposals-data-provider/__generated__/Proposals'; import { VoteProgress } from '../voting-progress'; import { ProposalActionsDropdown } from '../proposal-actions-dropdown'; -export const MarketNameProposalCell = ({ - value, - data, -}: VegaICellRendererParams< - ProposalListFieldsFragment, - 'terms.change.instrument.code' ->) => { - const { VEGA_TOKEN_URL } = useEnvironment(); - const { change } = data?.terms || {}; - if (change?.__typename === 'NewMarket' && VEGA_TOKEN_URL) { - const type = change.instrument.futureProduct?.__typename; - const content = ( - <> - {value as string} - {type && ( - - {ProposalProductTypeShortName[type]} - - )} - - ); - if (data?.id) { - const link = `${VEGA_TOKEN_URL}/proposals/${data.id}`; - return {content}; - } - return content; - } - return null; -}; - export const useColumnDefs = () => { const { params } = useNetworkParams([ NetworkParams.governance_proposal_market_requiredMajority, @@ -80,17 +45,36 @@ export const useColumnDefs = () => { headerName: t('Market'), field: 'terms.change.instrument.code', cellStyle: { lineHeight: '14px' }, - cellRenderer: 'MarketNameProposalCell', - }, - { - colId: 'description', - headerName: t('Description'), - field: 'terms.change.instrument.name', + cellRenderer: ({ + value, + data, + }: { + value: string; + data: ProposalListFieldsFragment; + }) => { + if (!value || !data) return '-'; + + // TODO: update when we switch to ProductConfiguration + const productType = 'Future'; + return ( + + {ProductTypeShortName[productType]} + + } + /> + ); + }, }, { colId: 'asset', headerName: t('Settlement asset'), - field: 'terms.change.instrument.futureProduct.settlementAsset.name', + field: 'terms.change.instrument.futureProduct.settlementAsset.symbol', }, { colId: 'state', @@ -105,12 +89,10 @@ export const useColumnDefs = () => { set: ProposalStateMapping, }, }, - FLAGS.SUCCESSOR_MARKETS && { + { headerName: t('Parent market'), - field: 'id', - colId: 'parentMarket', - cellRenderer: 'SuccessorMarketRenderer', - cellRendererParams: { parent: true }, + field: 'terms.change.successorConfiguration.parentMarketId', + cellRenderer: 'ParentMarketCell', }, { colId: 'voting', @@ -169,25 +151,12 @@ export const useColumnDefs = () => { data, }: VegaICellRendererParams) => { if (!data?.id) return null; + return ; }, }, ]); }, [requiredMajorityPercentage]); - const defaultColDef: ColDef = useMemo(() => { - return { - sortable: true, - filter: true, - filterParams: { buttons: ['reset'] }, - }; - }, []); - - return useMemo( - () => ({ - columnDefs, - defaultColDef, - }), - [columnDefs, defaultColDef] - ); + return columnDefs; }; diff --git a/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql b/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql index dde320b6c..1429c9107 100644 --- a/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql +++ b/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql @@ -114,6 +114,9 @@ fragment NewMarketFields on NewMarket { lpPriceRange # linearSlippageFactor # quadraticSlippageFactor + successorConfiguration { + parentMarketId + } } fragment UpdateMarketFields on UpdateMarket { diff --git a/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts b/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts index 8f4c71956..90aa2c975 100644 --- a/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts +++ b/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts @@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; const defaultOptions = {} as const; -export type NewMarketFieldsFragment = { __typename?: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } }; +export type NewMarketFieldsFragment = { __typename?: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null }; export type UpdateMarketFieldsFragment = { __typename?: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } }; @@ -13,7 +13,7 @@ export type UpdateAssetFieldsFragment = { __typename?: 'UpdateAsset', assetId: s export type UpdateNetworkParameterFielsFragment = { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } }; -export type ProposalListFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, requiredMajority: string, requiredParticipation: string, requiredLpMajority?: string | null, requiredLpParticipation?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'NewFreeform' } | { __typename: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } } | { __typename: 'NewTransfer' } | { __typename: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } }; +export type ProposalListFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, requiredMajority: string, requiredParticipation: string, requiredLpMajority?: string | null, requiredLpParticipation?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'NewFreeform' } | { __typename: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null } | { __typename: 'NewTransfer' } | { __typename: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } }; export type ProposalsListQueryVariables = Types.Exact<{ proposalType?: Types.InputMaybe; @@ -21,7 +21,7 @@ export type ProposalsListQueryVariables = Types.Exact<{ }>; -export type ProposalsListQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, requiredMajority: string, requiredParticipation: string, requiredLpMajority?: string | null, requiredLpParticipation?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'NewFreeform' } | { __typename: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } } | { __typename: 'NewTransfer' } | { __typename: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } } } | null> | null } | null }; +export type ProposalsListQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, requiredMajority: string, requiredParticipation: string, requiredLpMajority?: string | null, requiredLpParticipation?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'NewFreeform' } | { __typename: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null } | { __typename: 'NewTransfer' } | { __typename: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } } } | null> | null } | null }; export type NewMarketSuccessorFieldsFragment = { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null }; @@ -132,6 +132,9 @@ export const NewMarketFieldsFragmentDoc = gql` } metadata lpPriceRange + successorConfiguration { + parentMarketId + } } `; export const UpdateMarketFieldsFragmentDoc = gql` diff --git a/libs/proposals/src/lib/proposals-data-provider/proposals.mock.ts b/libs/proposals/src/lib/proposals-data-provider/proposals.mock.ts index 3b8add493..2fd351dee 100644 --- a/libs/proposals/src/lib/proposals-data-provider/proposals.mock.ts +++ b/libs/proposals/src/lib/proposals-data-provider/proposals.mock.ts @@ -109,6 +109,58 @@ export const marketUpdateProposal: ProposalListFieldsFragment = { export const createProposalListFieldsFragment = ( override?: PartialDeep ): ProposalListFieldsFragment => { + const newMarket = { + decimalPlaces: 1, + lpPriceRange: '', + riskParameters: { + __typename: 'SimpleRiskModel', + params: { + __typename: 'SimpleRiskModelParams', + factorLong: 0, + factorShort: 1, + }, + }, + metadata: undefined, + successorConfiguration: { + __typename: 'SuccessorConfiguration', + parentMarketId: 'xyz', + }, + instrument: { + code: 'ETHUSD', + name: 'ETHUSD', + futureProduct: { + settlementAsset: { + id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663', + name: 'tDAI TEST', + symbol: 'tDAI', + decimals: 1, + quantum: '1', + __typename: 'Asset', + }, + quoteName: '', + dataSourceSpecBinding: { + __typename: 'DataSourceSpecToFutureBinding', + settlementDataProperty: '', + tradingTerminationProperty: '', + }, + dataSourceSpecForSettlementData: { + __typename: 'DataSourceDefinition', + sourceType: { + __typename: 'DataSourceDefinitionInternal', + }, + }, + dataSourceSpecForTradingTermination: { + __typename: 'DataSourceDefinition', + sourceType: { + __typename: 'DataSourceDefinitionInternal', + }, + }, + __typename: 'FutureProduct', + }, + __typename: 'InstrumentConfiguration', + }, + __typename: 'NewMarket', + } as const; const defaultProposal: ProposalListFieldsFragment = { id: 'e9ec6d5c46a7e7bcabf9ba7a893fa5a5eeeec08b731f06f7a6eb7bf0e605b829', reference: 'injected_at_runtime', @@ -147,54 +199,7 @@ export const createProposalListFieldsFragment = ( terms: { closingDatetime: '2022-11-15T12:44:34Z', enactmentDatetime: '2022-11-15T12:44:54Z', - change: { - decimalPlaces: 1, - lpPriceRange: '', - riskParameters: { - __typename: 'SimpleRiskModel', - params: { - __typename: 'SimpleRiskModelParams', - factorLong: 0, - factorShort: 1, - }, - }, - metadata: [], - instrument: { - code: 'ETHUSD', - name: 'ETHUSD', - futureProduct: { - settlementAsset: { - id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663', - name: 'tDAI TEST', - symbol: 'tDAI', - decimals: 1, - quantum: '1', - __typename: 'Asset', - }, - quoteName: '', - dataSourceSpecBinding: { - __typename: 'DataSourceSpecToFutureBinding', - settlementDataProperty: '', - tradingTerminationProperty: '', - }, - dataSourceSpecForSettlementData: { - __typename: 'DataSourceDefinition', - sourceType: { - __typename: 'DataSourceDefinitionInternal', - }, - }, - dataSourceSpecForTradingTermination: { - __typename: 'DataSourceDefinition', - sourceType: { - __typename: 'DataSourceDefinitionInternal', - }, - }, - __typename: 'FutureProduct', - }, - __typename: 'InstrumentConfiguration', - }, - __typename: 'NewMarket', - }, + change: newMarket, __typename: 'ProposalTerms', }, __typename: 'Proposal',