From eb2f4fd27c5594610b6717bb937b51bf425f4273 Mon Sep 17 00:00:00 2001 From: botond <105208209+notbot00@users.noreply.github.com> Date: Thu, 25 Aug 2022 15:21:28 +0100 Subject: [PATCH] Feat/637 Market settlement expiry (#1094) * feat: add market expiry field to the trade grid header * feat: add oracle spec linking to explorer page * fix: oracle id to tooltip link * fix: add dashed underlines to tooltip triggers * fix: lint * fix: format * chore: merge master * fix: readd type generation WIP * fix: wording Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * fix: refactor expiry to components Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> --- .../src/app/components/sub-heading/index.tsx | 1 + .../explorer/src/app/routes/oracles/index.tsx | 20 +++- .../trading/pages/markets/[marketId].page.tsx | 3 + .../pages/markets/__generated__/Market.ts | 12 +++ apps/trading/pages/markets/trade-grid.tsx | 92 ++++++++++++++++++- 5 files changed, 121 insertions(+), 7 deletions(-) diff --git a/apps/explorer/src/app/components/sub-heading/index.tsx b/apps/explorer/src/app/components/sub-heading/index.tsx index 839176ca7..bf6a9ac37 100644 --- a/apps/explorer/src/app/components/sub-heading/index.tsx +++ b/apps/explorer/src/app/components/sub-heading/index.tsx @@ -17,6 +17,7 @@ export const SubHeading = ({ 'uppercase', 'mt-12', 'mb-12', + 'truncate', className ); return ( diff --git a/apps/explorer/src/app/routes/oracles/index.tsx b/apps/explorer/src/app/routes/oracles/index.tsx index a9a78ee0f..d8e165691 100644 --- a/apps/explorer/src/app/routes/oracles/index.tsx +++ b/apps/explorer/src/app/routes/oracles/index.tsx @@ -1,7 +1,8 @@ import { gql, useQuery } from '@apollo/client'; +import { useLocation } from 'react-router-dom'; import type { OracleSpecs as OracleSpecsQuery } from './__generated__/OracleSpecs'; -import React from 'react'; +import React, { useEffect } from 'react'; import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit'; import { RouteTitle } from '../../components/route-title'; import { t } from '@vegaprotocol/react-helpers'; @@ -33,7 +34,20 @@ const ORACLE_SPECS_QUERY = gql` `; const Oracles = () => { - const { data } = useQuery(ORACLE_SPECS_QUERY); + const { hash } = useLocation(); + const { data, loading } = useQuery(ORACLE_SPECS_QUERY); + + useEffect(() => { + if (data && !loading && hash) { + const element = document.getElementById(hash.substring(1)); + if (element) { + window.scrollTo({ + top: element.offsetTop, + }); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hash, loading, !!data]); return (
@@ -41,7 +55,7 @@ const Oracles = () => { {data?.oracleSpecs ? data.oracleSpecs.map((o) => ( - {o.id} + {o.id} )) diff --git a/apps/trading/pages/markets/[marketId].page.tsx b/apps/trading/pages/markets/[marketId].page.tsx index 3f4ef9ce8..f1ebe9194 100644 --- a/apps/trading/pages/markets/[marketId].page.tsx +++ b/apps/trading/pages/markets/[marketId].page.tsx @@ -48,6 +48,9 @@ const MARKET_QUERY = gql` } product { ... on Future { + oracleSpecForTradingTermination { + id + } quoteName settlementAsset { id diff --git a/apps/trading/pages/markets/__generated__/Market.ts b/apps/trading/pages/markets/__generated__/Market.ts index a5ed203da..eb66c911f 100644 --- a/apps/trading/pages/markets/__generated__/Market.ts +++ b/apps/trading/pages/markets/__generated__/Market.ts @@ -81,6 +81,14 @@ export interface Market_market_tradableInstrument_instrument_metadata { tags: string[] | null; } +export interface Market_market_tradableInstrument_instrument_product_oracleSpecForTradingTermination { + __typename: "OracleSpec"; + /** + * ID is a hash generated from the OracleSpec data. + */ + id: string; +} + export interface Market_market_tradableInstrument_instrument_product_settlementAsset { __typename: "Asset"; /** @@ -99,6 +107,10 @@ export interface Market_market_tradableInstrument_instrument_product_settlementA export interface Market_market_tradableInstrument_instrument_product { __typename: "Future"; + /** + * The oracle spec describing the oracle data of interest for trading termination. + */ + oracleSpecForTradingTermination: Market_market_tradableInstrument_instrument_product_oracleSpecForTradingTermination; /** * String representing the quote (e.g. BTCUSD -> USD is quote) */ diff --git a/apps/trading/pages/markets/trade-grid.tsx b/apps/trading/pages/markets/trade-grid.tsx index 891ed1893..726cb6f2c 100644 --- a/apps/trading/pages/markets/trade-grid.tsx +++ b/apps/trading/pages/markets/trade-grid.tsx @@ -8,7 +8,11 @@ import { SelectMarketPopover } from '@vegaprotocol/market-list'; import { OrderListContainer } from '@vegaprotocol/orders'; import { FillsContainer } from '@vegaprotocol/fills'; import { PositionsContainer } from '@vegaprotocol/positions'; -import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers'; +import { + addDecimalsFormatNumber, + getDateFormat, + t, +} from '@vegaprotocol/react-helpers'; import { TradesContainer } from '@vegaprotocol/trades'; import { AuctionTrigger, @@ -18,9 +22,8 @@ import { } from '@vegaprotocol/types'; import { Allotment, LayoutPriority } from 'allotment'; import classNames from 'classnames'; -import { useState } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; - +import { useState } from 'react'; import type { ReactNode } from 'react'; import type { Market_market } from './__generated__/Market'; import type { CandleClose } from '@vegaprotocol/types'; @@ -29,11 +32,13 @@ import { AccountsContainer } from '@vegaprotocol/accounts'; import { DepthChartContainer } from '@vegaprotocol/market-depth'; import { CandlesChartContainer } from '@vegaprotocol/candles-chart'; import { useAssetDetailsDialogStore } from '@vegaprotocol/market-list'; +import { useEnvironment } from '@vegaprotocol/environment'; import { Tab, Tabs, PriceCellChange, Button, + Link, Tooltip, ResizablePanel, } from '@vegaprotocol/ui-toolkit'; @@ -54,6 +59,55 @@ const TradingViews = { type TradingView = keyof typeof TradingViews; +type ExpiryLabelProps = { + market: Market_market; +}; + +const ExpiryLabel = ({ market }: ExpiryLabelProps) => { + if (market.marketTimestamps.close === null) { + return <>{t('Not time-based')}; + } + + const closeDate = new Date(market.marketTimestamps.close); + const isExpired = Date.now() - closeDate.valueOf() > 0; + const expiryDate = getDateFormat().format(closeDate); + + return <>{`${isExpired ? `${t('Expired')} ` : ''} ${expiryDate}`}; +}; + +type ExpiryTooltipContentProps = { + market: Market_market; + explorerUrl?: string; +}; + +const ExpiryTooltipContent = ({ + market, + explorerUrl, +}: ExpiryTooltipContentProps) => { + if (market.marketTimestamps.close === null) { + const oracleId = + market.tradableInstrument.instrument.product + .oracleSpecForTradingTermination?.id; + + return ( + <> +

+ {t( + 'This market expires when triggered by its oracle, not on a set date.' + )} +

+ {explorerUrl && oracleId && ( + + {t('View oracle specification')} + + )} + + ); + } + + return null; +}; + interface TradeMarketHeaderProps { market: Market_market; className?: string; @@ -63,6 +117,7 @@ export const TradeMarketHeader = ({ market, className, }: TradeMarketHeaderProps) => { + const { VEGA_EXPLORER_URL } = useEnvironment(); const { setAssetDetailsDialogOpen, setAssetDetailsDialogSymbol } = useAssetDetailsDialogStore(); const candlesClose: string[] = (market?.candles || []) @@ -87,6 +142,8 @@ export const TradeMarketHeader = ({ } }; + const hasExpiry = market.marketTimestamps.close !== null; + return (
@@ -95,6 +152,27 @@ export const TradeMarketHeader = ({ data-testid="market-summary" className="flex flex-auto items-start gap-64 overflow-x-auto whitespace-nowrap py-8 pr-8" > + + } + > +
+ {t('Expiry')} + + + +
+
{t('Change (24h)')}
{t('Trading mode')} - + {market.tradingMode === MarketTradingMode.TRADING_MODE_MONITORING_AUCTION && market.data?.trigger &&