chore: LP tooltips & move market-info lib (#1267)
* feat(#473): add positions metrics data provider * feat(#473) add positions stats * feat(#473) add positions stats * feat(#473): add positions stats * feat(#473): add positions stats * feat(#473): position metrics, test and refactoring * feat(#473): add unit tests to positions table * feat(#473): fix spelling, order positions by updated at desc * feat(#473): protect from division by 0 * feat(#473): fix trading positions e2e tests * feat(#473): fix e2e data mocks * feat(#473): post code review clean up * feat(#993): dependencies handling in data provider * feat(#993): fix e2e tests data mocks * feat(#993): remove position metrics mocks, add market data market id * feat: #994 add price monitoring bounds and candles update interface * fix: move best bid price to diff section * feat(#993): add missing mocks, fix combine function * fix: add insurance pool and calc volume 24h * feat: display some oracle min info, asset id, insurance pool, move open interest and 24hVol * fix: add market-info.cy.ts case * fix: remove # from numbered price monitoring settings * fix: add insurance pool test * fix: format 24hvol * feat(#993): set loading initially to true, add unit tests * feat(#993): cleanup, add comments * feat(#993): remove undefined from client type * fix: remove indicativeVolume and oracleSpecBinding from market info * feat(#993): cosmetic changes * fix: add oracleSpecBinding back * Update libs/deal-ticket/src/components/info-market.tsx Co-authored-by: botond <105208209+notbot00@users.noreply.github.com> * feat: add initial queries * fix: memo yesterday's timestamp * fix: add back info * fix: update query * fix: add view full oracle details link and update mappings * fix: regen code, make link reactnode, fix index.ts * feat: add liquidity lib, refactor info market * fix: remove liquidity query from deal-ticket * feat:(#993): pass informaton about update callback cause * fix: small ui tweaks * fix: display in grid * feat: navigate to oracle by termination id * feat: #491 add use liquidity provision merging * fix: remove logs, add extra check on my liquidity provision * fix: type number trivially inferred from a number literal, remove type annotation * fix: cypress tests and formatting for market info * Update libs/deal-ticket/src/components/market-info/info-market.tsx * fix: use position decimal places for stake and market value proxy * fix: #491 use size/position decimal places for obligation, supplied and commitment amount * fix: add size component and use decimal places * fix: update readme liquidity * fix: #491 add correct asset decimal formatters * Update libs/deal-ticket/src/components/market-info/tooltip-mapping.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * fix: make link instead of button to open liquidity * fix: #491 add liquidity page, link to trading mode tooltip, tabs hidden or choose active * fix: remove LP dialog, use only link to page * fix: add market id in LP view * fix: follow trade grid design * fix: add one line tabs , remove link styling, remove any, add value formatters * fix: remove falsy check LP undefined * fix: keep date formatter in LP table * fix: add generic type market info, hooks in body function * fix: revert number formatters * fix: use one param only in network params query * fix: use network param in web3 lib * fix: move lp container to trading app * fix: remove resizable panel * feat: add header component, remove isEstimate * chore: remove unnecessary type cast * fix: fix build with children map clone element * chore: lint * fix: move use network params to react helpers * fix: add const for LP tabs * fix: fix formatting on LP page * fix: only show tilde for liquidity monitoring auction end date * fix: market id being rendered twice in market info * chore: fix lint * fix: types for generate withdraw form query * chore: fix intermittent failing withdrawal test * Update libs/deal-ticket/src/components/market-info/info-market.tsx * fix: route to LP and update selected market * fix: add market-info lib * fix: add tooltips to header stat * fix: add overflow-y on div within auto sizer * fix: fix merge conflicts * fix: size and dialog * fix: format target stake and supplied stake * fix: add tooltips ag-grid and fix decimals * fix: overflow on both axes auto-sizer * fix: remove symbol, add to commitment * fix: update tooltips and headers after demo * fix: network switcher background * fix: remove any from market info * fix: integration test * fix: remove e2e test for risk factors for now until grpc error sorted * fix: get liquidity panel by test-id * fix: fix market-info.cy.ts test * fix: liquidity test * f ix: fix link data test id Co-authored-by: Bartłomiej Głownia <bglownia@gmail.com> Co-authored-by: botond <105208209+notbot00@users.noreply.github.com> Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: Matthew Russell <mattrussell36@gmail.com> Co-authored-by: Joe <joe@vega.xyz>
This commit is contained in:
parent
7675fe0ab0
commit
d85c5d7c1f
@ -4,6 +4,7 @@ import { mockTradingPage } from '../support/trading';
|
||||
const marketInfoBtn = 'Info';
|
||||
const row = 'key-value-table-row';
|
||||
const marketTitle = 'accordion-title';
|
||||
const link = 'link';
|
||||
const externalLink = 'external-link';
|
||||
|
||||
describe('market info is displayed', () => {
|
||||
@ -85,13 +86,6 @@ describe('market info is displayed', () => {
|
||||
validateMarketDataRow(4, 'Sector', 'crypto');
|
||||
});
|
||||
|
||||
it('risk factors displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Risk factors').click();
|
||||
|
||||
validateMarketDataRow(0, 'Short', '0.008571790367285281');
|
||||
validateMarketDataRow(1, 'Long', '0.008508132993273576');
|
||||
});
|
||||
|
||||
it('risk model displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Risk model').click();
|
||||
|
||||
@ -127,17 +121,13 @@ describe('market info is displayed', () => {
|
||||
});
|
||||
|
||||
it('liquidity displayed', () => {
|
||||
cy.getByTestId('accordion-toggle').eq(14).click();
|
||||
cy.getByTestId('accordion-toggle').eq(13).click();
|
||||
|
||||
validateMarketDataRow(0, 'Target Stake', '0.56789 tBTC');
|
||||
validateMarketDataRow(1, 'Supplied Stake', '0.56767 tBTC');
|
||||
validateMarketDataRow(2, 'Market Value Proxy', '6.77678 tBTC');
|
||||
|
||||
cy.getByTestId(externalLink).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/liquidity/market-0'
|
||||
);
|
||||
cy.getByTestId(link).should('have.text', 'View liquidity provision table');
|
||||
});
|
||||
|
||||
it('oracle displayed', () => {
|
||||
@ -149,16 +139,6 @@ describe('market info is displayed', () => {
|
||||
'Trading Termination Property',
|
||||
'termination.BTC.value'
|
||||
);
|
||||
validateMarketDataRow(
|
||||
2,
|
||||
'Price Oracle',
|
||||
'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f'
|
||||
);
|
||||
validateMarketDataRow(
|
||||
3,
|
||||
'Termination Oracle',
|
||||
'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f'
|
||||
);
|
||||
|
||||
cy.getByTestId(externalLink)
|
||||
.should('have.attr', 'href')
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { MarketInfoQuery } from '@vegaprotocol/deal-ticket';
|
||||
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
|
||||
import {
|
||||
AccountType,
|
||||
AuctionTrigger,
|
||||
|
@ -58,6 +58,7 @@ export const generateMarket = (override?: PartialDeep<Market>): Market => {
|
||||
},
|
||||
quoteName: 'BTCUSD Monthly',
|
||||
settlementAsset: {
|
||||
decimals: 0,
|
||||
id: '000',
|
||||
symbol: 'USD',
|
||||
name: 'United States Dollar',
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { Children } from 'react';
|
||||
import { cloneElement } from 'react';
|
||||
@ -11,9 +12,7 @@ export const Header = ({ title, children }: TradeMarketHeaderProps) => {
|
||||
return (
|
||||
<header className="w-screen xl:px-4 pt-4 border-b border-neutral-300 dark:border-neutral-700">
|
||||
<div className="xl:flex xl:gap-4 items-start">
|
||||
<div className="px-4 mb-2 xl:mb-0 sm:text-lg md:text-xl lg:text-2xl">
|
||||
{title}
|
||||
</div>
|
||||
<div className="px-4 mb-2">{title}</div>
|
||||
<div
|
||||
data-testid="market-summary"
|
||||
className="flex flex-nowrap items-start xl:flex-1 w-full overflow-x-auto text-xs "
|
||||
@ -33,10 +32,12 @@ export const HeaderStat = ({
|
||||
children,
|
||||
heading,
|
||||
id,
|
||||
description,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
heading: string;
|
||||
id?: string;
|
||||
description?: string | ReactNode;
|
||||
}) => {
|
||||
const itemClass =
|
||||
'min-w-min w-[120px] whitespace-nowrap pb-3 px-4 border-l border-neutral-300 dark:border-neutral-700';
|
||||
@ -44,9 +45,11 @@ export const HeaderStat = ({
|
||||
|
||||
return (
|
||||
<div className={itemClass}>
|
||||
<div id={id} className={itemHeading}>
|
||||
{heading}
|
||||
</div>
|
||||
<Tooltip description={description}>
|
||||
<div id={id} className={itemHeading}>
|
||||
{heading}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div aria-labelledby={id}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -27,7 +27,7 @@ export const Navbar = ({ theme, toggleTheme }: NavbarProps) => {
|
||||
<VLogo className="w-6 h-6 fill-white" />
|
||||
</a>
|
||||
</Link>
|
||||
<NetworkSwitcher />
|
||||
<NetworkSwitcher fixedBg="dark" />
|
||||
</div>
|
||||
<nav className="flex items-center">
|
||||
{[
|
||||
|
@ -36,14 +36,17 @@ const MarketDataGrid = ({ grid }: MarketDataGridProps) => {
|
||||
const formatStake = (value: string, market: Market_market) => {
|
||||
const formattedValue = addDecimalsFormatNumber(
|
||||
value,
|
||||
market.positionDecimalPlaces
|
||||
market.tradableInstrument.instrument.product.settlementAsset.decimals
|
||||
);
|
||||
const asset =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
return `${formattedValue} ${asset}`;
|
||||
};
|
||||
|
||||
const compileGridData = (market: Market_market) => {
|
||||
const compileGridData = (
|
||||
market: Market_market,
|
||||
onSelect?: (id: string) => void
|
||||
) => {
|
||||
const grid: MarketDataGridProps['grid'] = [];
|
||||
const isLiquidityMonitoringAuction =
|
||||
market.tradingMode === MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||
@ -80,7 +83,7 @@ const compileGridData = (market: Market_market) => {
|
||||
if (isLiquidityMonitoringAuction && market.data?.suppliedStake) {
|
||||
grid.push({
|
||||
label: (
|
||||
<Link href={`/liquidity/${market.id}`} target="_blank">
|
||||
<Link onClick={() => onSelect && onSelect(market.id)}>
|
||||
{t('Current liquidity')}
|
||||
</Link>
|
||||
),
|
||||
@ -117,9 +120,13 @@ const compileGridData = (market: Market_market) => {
|
||||
|
||||
type TradingModeTooltipProps = {
|
||||
market: Market_market;
|
||||
onSelect?: (marketId: string) => void;
|
||||
};
|
||||
|
||||
export const TradingModeTooltip = ({ market }: TradingModeTooltipProps) => {
|
||||
export const TradingModeTooltip = ({
|
||||
market,
|
||||
onSelect,
|
||||
}: TradingModeTooltipProps) => {
|
||||
switch (market.tradingMode) {
|
||||
case MarketTradingMode.TRADING_MODE_CONTINUOUS: {
|
||||
return (
|
||||
@ -168,7 +175,7 @@ export const TradingModeTooltip = ({ market }: TradingModeTooltipProps) => {
|
||||
{t('Find out more')}
|
||||
</Link>
|
||||
</p>
|
||||
<MarketDataGrid grid={compileGridData(market)} />
|
||||
<MarketDataGrid grid={compileGridData(market, onSelect)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ export function Index() {
|
||||
|
||||
if (marketId) {
|
||||
replace(`/markets/${marketId}`);
|
||||
update({ marketId });
|
||||
}
|
||||
// Fallback to the markets list page
|
||||
else {
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { LiquidityTable, useLiquidityProvision } from '@vegaprotocol/liquidity';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
|
||||
import { LiquidityProvisionStatus } from '@vegaprotocol/types';
|
||||
import { AsyncRenderer, Tab, Tabs } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { Header, HeaderStat } from '../../components/header';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useRef, useMemo } from 'react';
|
||||
import { tooltipMapping } from '@vegaprotocol/market-info';
|
||||
|
||||
const LiquidityPage = ({ id }: { id?: string }) => {
|
||||
const { query } = useRouter();
|
||||
const { query, push } = useRouter();
|
||||
const { keypair } = useVegaWallet();
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
|
||||
@ -20,7 +20,14 @@ const LiquidityPage = ({ id }: { id?: string }) => {
|
||||
id || (Array.isArray(query.marketId) ? query.marketId[0] : query.marketId);
|
||||
|
||||
const {
|
||||
data: { liquidityProviders, suppliedStake, targetStake, code, symbol },
|
||||
data: {
|
||||
liquidityProviders,
|
||||
suppliedStake,
|
||||
targetStake,
|
||||
name,
|
||||
symbol,
|
||||
assetDecimalPlaces,
|
||||
},
|
||||
loading,
|
||||
error,
|
||||
} = useLiquidityProvision({ marketId });
|
||||
@ -62,16 +69,36 @@ const LiquidityPage = ({ id }: { id?: string }) => {
|
||||
<div className="h-full grid grid-rows-[min-content_1fr]">
|
||||
<Header
|
||||
title={
|
||||
<Link href={`/markets/${marketId}`}>
|
||||
{`${code} ${t('liquidity provision')}`}
|
||||
</Link>
|
||||
<button onClick={() => push(`/markets/${marketId}`)}>{`${name} ${t(
|
||||
'liquidity provision'
|
||||
)}`}</button>
|
||||
}
|
||||
>
|
||||
<HeaderStat heading={t('Target stake')}>
|
||||
<div>{`${targetStake} ${symbol}`}</div>
|
||||
<HeaderStat
|
||||
heading={t('Target stake')}
|
||||
description={tooltipMapping['targetStake']}
|
||||
>
|
||||
<div>
|
||||
{targetStake
|
||||
? `${addDecimalsFormatNumber(
|
||||
targetStake,
|
||||
assetDecimalPlaces ?? 0
|
||||
)} ${symbol}`
|
||||
: '-'}
|
||||
</div>
|
||||
</HeaderStat>
|
||||
<HeaderStat heading={t('Supplied stake')}>
|
||||
<div>{`${suppliedStake} ${symbol}`}</div>
|
||||
<HeaderStat
|
||||
heading={t('Supplied stake')}
|
||||
description={tooltipMapping['suppliedStake']}
|
||||
>
|
||||
<div>
|
||||
{suppliedStake
|
||||
? `${addDecimalsFormatNumber(
|
||||
suppliedStake,
|
||||
assetDecimalPlaces ?? 0
|
||||
)} ${symbol}`
|
||||
: '-'}
|
||||
</div>
|
||||
</HeaderStat>
|
||||
<HeaderStat heading={t('Market ID')}>
|
||||
<div className="break-word">{marketId}</div>
|
||||
@ -83,13 +110,28 @@ const LiquidityPage = ({ id }: { id?: string }) => {
|
||||
name={t('My liquidity provision')}
|
||||
hidden={!partyId}
|
||||
>
|
||||
<LiquidityTable ref={gridRef} data={myLpEdges} />
|
||||
<LiquidityTable
|
||||
ref={gridRef}
|
||||
data={myLpEdges}
|
||||
symbol={symbol}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab id={LiquidityTabs.Active} name={t('Active')}>
|
||||
<LiquidityTable ref={gridRef} data={activeEdges} />
|
||||
<LiquidityTable
|
||||
ref={gridRef}
|
||||
data={activeEdges}
|
||||
symbol={symbol}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
|
||||
<LiquidityTable ref={gridRef} data={inactiveEdges} />
|
||||
<LiquidityTable
|
||||
ref={gridRef}
|
||||
data={inactiveEdges}
|
||||
symbol={symbol}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
/>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
@ -41,6 +41,7 @@ query Market($marketId: ID!, $interval: Interval!, $since: String!) {
|
||||
id
|
||||
symbol
|
||||
name
|
||||
decimals
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ const MARKET_QUERY = gql`
|
||||
id
|
||||
symbol
|
||||
name
|
||||
decimals
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,11 +84,20 @@ const MarketPage = ({ id }: { id?: string }) => {
|
||||
update: store.update,
|
||||
})
|
||||
);
|
||||
const { update: updateStore } = useGlobalStore((store) => ({
|
||||
update: store.update,
|
||||
}));
|
||||
|
||||
// Default to first marketId query item if found
|
||||
const marketId =
|
||||
id || (Array.isArray(query.marketId) ? query.marketId[0] : query.marketId);
|
||||
|
||||
const onSelect = (id: string) => {
|
||||
if (id && id !== marketId) {
|
||||
updateStore({ marketId: id });
|
||||
}
|
||||
};
|
||||
|
||||
// Cache timestamp for yesterday to prevent full unmount of market page when
|
||||
// a rerender occurs
|
||||
const yTimestamp = useMemo(() => {
|
||||
@ -128,20 +138,16 @@ const MarketPage = ({ id }: { id?: string }) => {
|
||||
return (
|
||||
<>
|
||||
{w > 960 ? (
|
||||
<TradeGrid market={market} />
|
||||
<TradeGrid market={market} onSelect={onSelect} />
|
||||
) : (
|
||||
<TradePanels market={market} />
|
||||
<TradePanels market={market} onSelect={onSelect} />
|
||||
)}
|
||||
<SelectMarketDialog
|
||||
dialogOpen={landingDialog && !riskNoticeDialog}
|
||||
setDialogOpen={(isOpen: boolean) =>
|
||||
update({ landingDialog: isOpen })
|
||||
}
|
||||
onSelect={(marketId: string) => {
|
||||
if (marketId && marketId !== marketId) {
|
||||
update({ marketId });
|
||||
}
|
||||
}}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -103,6 +103,10 @@ export interface Market_market_tradableInstrument_instrument_product_settlementA
|
||||
* The full name of the asset (e.g: Great British Pound)
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
|
||||
*/
|
||||
decimals: number;
|
||||
}
|
||||
|
||||
export interface Market_market_tradableInstrument_instrument_product {
|
||||
@ -198,14 +202,14 @@ export interface Market_market {
|
||||
/**
|
||||
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
|
||||
* number denominated in the currency of the market. (uint64)
|
||||
*
|
||||
*
|
||||
* Examples:
|
||||
* Currency Balance decimalPlaces Real Balance
|
||||
* GBP 100 0 GBP 100
|
||||
* GBP 100 2 GBP 1.00
|
||||
* GBP 100 4 GBP 0.01
|
||||
* GBP 1 4 GBP 0.0001 ( 0.01p )
|
||||
*
|
||||
*
|
||||
* GBX (pence) 100 0 GBP 1.00 (100p )
|
||||
* GBX (pence) 100 2 GBP 0.01 ( 1p )
|
||||
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
|
||||
|
@ -1,7 +1,5 @@
|
||||
import {
|
||||
DealTicketContainer,
|
||||
MarketInfoContainer,
|
||||
} from '@vegaprotocol/deal-ticket';
|
||||
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
||||
import { MarketInfoContainer } from '@vegaprotocol/market-info';
|
||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
@ -32,7 +30,6 @@ import {
|
||||
t,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { SelectMarketPopover } from '@vegaprotocol/market-list';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { CandleClose } from '@vegaprotocol/types';
|
||||
@ -43,6 +40,7 @@ import {
|
||||
MarketTradingModeMapping,
|
||||
} from '@vegaprotocol/types';
|
||||
import { TradingModeTooltip } from '../../components/trading-mode-tooltip';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
const TradingViews = {
|
||||
Candles: CandlesChartContainer,
|
||||
@ -110,21 +108,16 @@ const ExpiryTooltipContent = ({
|
||||
|
||||
interface TradeMarketHeaderProps {
|
||||
market: Market_market;
|
||||
onSelect: (marketId: string) => void;
|
||||
}
|
||||
|
||||
export const TradeMarketHeader = ({ market }: TradeMarketHeaderProps) => {
|
||||
export const TradeMarketHeader = ({
|
||||
market,
|
||||
onSelect,
|
||||
}: TradeMarketHeaderProps) => {
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { setAssetDetailsDialogOpen, setAssetDetailsDialogSymbol } =
|
||||
useAssetDetailsDialogStore();
|
||||
const { update } = useGlobalStore((store) => ({
|
||||
update: store.update,
|
||||
}));
|
||||
|
||||
const onSelect = (marketId: string) => {
|
||||
if (marketId && marketId !== marketId) {
|
||||
update({ marketId });
|
||||
}
|
||||
};
|
||||
|
||||
const candlesClose: string[] = (market?.candles || [])
|
||||
.map((candle) => candle?.close)
|
||||
@ -136,6 +129,7 @@ export const TradeMarketHeader = ({ market }: TradeMarketHeaderProps) => {
|
||||
const itemClass =
|
||||
'min-w-min w-[120px] whitespace-nowrap pb-3 px-4 border-l border-neutral-300 dark:border-neutral-700';
|
||||
const itemHeading = 'text-neutral-400';
|
||||
const { push } = useRouter();
|
||||
|
||||
return (
|
||||
<header className="w-screen xl:px-4 pt-4 border-b border-neutral-300 dark:border-neutral-700">
|
||||
@ -194,7 +188,15 @@ export const TradeMarketHeader = ({ market }: TradeMarketHeaderProps) => {
|
||||
<div className={itemHeading}>{t('Trading mode')}</div>
|
||||
<Tooltip
|
||||
align="start"
|
||||
description={<TradingModeTooltip market={market} />}
|
||||
description={
|
||||
<TradingModeTooltip
|
||||
market={market}
|
||||
onSelect={(marketId: string) => {
|
||||
onSelect(marketId);
|
||||
push(`/liquidity/${marketId}`);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div data-testid="trading-mode">
|
||||
{market.tradingMode ===
|
||||
@ -242,12 +244,14 @@ export const TradeMarketHeader = ({ market }: TradeMarketHeaderProps) => {
|
||||
|
||||
interface TradeGridProps {
|
||||
market: Market_market;
|
||||
onSelect: (marketId: string) => void;
|
||||
}
|
||||
|
||||
export const TradeGrid = ({ market }: TradeGridProps) => {
|
||||
export const TradeGrid = ({ market, onSelect }: TradeGridProps) => {
|
||||
const { push } = useRouter();
|
||||
return (
|
||||
<div className="h-full grid grid-rows-[min-content_1fr]">
|
||||
<TradeMarketHeader market={market} />
|
||||
<TradeMarketHeader market={market} onSelect={onSelect} />
|
||||
<ResizableGrid vertical={true}>
|
||||
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
|
||||
<ResizableGrid proportionalLayout={false} minSize={200}>
|
||||
@ -278,7 +282,13 @@ export const TradeGrid = ({ market }: TradeGridProps) => {
|
||||
<TradingViews.Ticket marketId={market.id} />
|
||||
</Tab>
|
||||
<Tab id="info" name={t('Info')}>
|
||||
<TradingViews.Info marketId={market.id} />
|
||||
<TradingViews.Info
|
||||
marketId={market.id}
|
||||
onSelect={(id: string) => {
|
||||
onSelect(id);
|
||||
push(`/liquidity/${id}`);
|
||||
}}
|
||||
/>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</TradeGridChild>
|
||||
@ -348,9 +358,11 @@ const TradeGridChild = ({ children }: TradeGridChildProps) => {
|
||||
|
||||
interface TradePanelsProps {
|
||||
market: Market_market;
|
||||
onSelect: (marketId: string) => void;
|
||||
}
|
||||
|
||||
export const TradePanels = ({ market }: TradePanelsProps) => {
|
||||
export const TradePanels = ({ market, onSelect }: TradePanelsProps) => {
|
||||
const { push } = useRouter();
|
||||
const [view, setView] = useState<TradingView>('Candles');
|
||||
|
||||
const renderView = () => {
|
||||
@ -360,16 +372,26 @@ export const TradePanels = ({ market }: TradePanelsProps) => {
|
||||
throw new Error(`No component for view: ${view}`);
|
||||
}
|
||||
|
||||
return <Component marketId={market.id} />;
|
||||
return (
|
||||
<Component
|
||||
marketId={market.id}
|
||||
onSelect={(id: string) => {
|
||||
onSelect(id);
|
||||
push(`/liquidity/${id}`);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
|
||||
<TradeMarketHeader market={market} />
|
||||
<TradeMarketHeader market={market} onSelect={onSelect} />
|
||||
<div className="h-full">
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<div style={{ width, height }}>{renderView()}</div>
|
||||
<div style={{ width, height }} className="overflow-auto">
|
||||
{renderView()}
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
|
@ -1,2 +1 @@
|
||||
export * from './deal-ticket';
|
||||
export * from './market-info';
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useEnvironment } from '../../hooks/use-environment';
|
||||
import { Networks } from '../../types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export const envNameMapping: Record<Networks, string> = {
|
||||
[Networks.CUSTOM]: t('Custom'),
|
||||
@ -68,7 +69,11 @@ const NetworkLabel = ({
|
||||
</span>
|
||||
);
|
||||
|
||||
export const NetworkSwitcher = () => {
|
||||
export const NetworkSwitcher = ({
|
||||
fixedBg,
|
||||
}: {
|
||||
fixedBg?: 'dark' | 'light';
|
||||
}) => {
|
||||
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [isAdvancedView, setAdvancedView] = useState(false);
|
||||
@ -83,9 +88,15 @@ export const NetworkSwitcher = () => {
|
||||
[setOpen, setAdvancedView]
|
||||
);
|
||||
|
||||
const dropdownTriggerClasses = classNames({
|
||||
'dark:text-white dark:bg-black text-black bg-white': !fixedBg,
|
||||
'text-black bg-white': fixedBg === 'light',
|
||||
'text-white bg-black': fixedBg === 'dark',
|
||||
});
|
||||
|
||||
return (
|
||||
<DropdownMenu open={isOpen} onOpenChange={handleOpen}>
|
||||
<DropdownMenuTrigger className="text-white dark:text-white">
|
||||
<DropdownMenuTrigger className={dropdownTriggerClasses}>
|
||||
{envTriggerMapping[VEGA_ENV]}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
|
61
libs/liquidity/src/lib/MarketLiquidity.graphql
Normal file
61
libs/liquidity/src/lib/MarketLiquidity.graphql
Normal file
@ -0,0 +1,61 @@
|
||||
query MarketLiquidity($marketId: ID!, $partyId: String) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
liquidityProvisionsConnection(party: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
party {
|
||||
id
|
||||
accountsConnection(marketId: $marketId, type: ACCOUNT_TYPE_BOND) {
|
||||
edges {
|
||||
node {
|
||||
type
|
||||
balance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
commitmentAmount
|
||||
fee
|
||||
status
|
||||
}
|
||||
}
|
||||
}
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
code
|
||||
name
|
||||
product {
|
||||
... on Future {
|
||||
settlementAsset {
|
||||
id
|
||||
symbol
|
||||
decimals
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
data {
|
||||
market {
|
||||
id
|
||||
}
|
||||
suppliedStake
|
||||
openInterest
|
||||
targetStake
|
||||
marketValueProxy
|
||||
liquidityProviderFeeShare {
|
||||
party {
|
||||
id
|
||||
}
|
||||
equityLikeShare
|
||||
averageEntryValuation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -121,6 +121,10 @@ export interface MarketLiquidity_market_tradableInstrument_instrument {
|
||||
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
|
||||
*/
|
||||
code: string;
|
||||
/**
|
||||
* Full and fairly descriptive name for the instrument
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
|
||||
*/
|
||||
@ -204,14 +208,14 @@ export interface MarketLiquidity_market {
|
||||
/**
|
||||
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
|
||||
* number denominated in the currency of the market. (uint64)
|
||||
*
|
||||
*
|
||||
* Examples:
|
||||
* Currency Balance decimalPlaces Real Balance
|
||||
* GBP 100 0 GBP 100
|
||||
* GBP 100 2 GBP 1.00
|
||||
* GBP 100 4 GBP 0.01
|
||||
* GBP 1 4 GBP 0.0001 ( 0.01p )
|
||||
*
|
||||
*
|
||||
* GBX (pence) 100 0 GBP 1.00 (100p )
|
||||
* GBX (pence) 100 2 GBP 0.01 ( 1p )
|
||||
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
|
||||
|
@ -42,6 +42,7 @@ const MARKET_LIQUIDITY_QUERY = gql`
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
code
|
||||
name
|
||||
product {
|
||||
... on Future {
|
||||
settlementAsset {
|
||||
@ -95,6 +96,7 @@ export interface LiquidityData {
|
||||
decimalPlaces?: number;
|
||||
positionDecimalPlaces?: number;
|
||||
assetDecimalPlaces?: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export const useLiquidityProvision = ({
|
||||
@ -161,6 +163,7 @@ export const useLiquidityProvision = ({
|
||||
decimalPlaces: data?.market?.decimalPlaces,
|
||||
positionDecimalPlaces: data?.market?.positionDecimalPlaces,
|
||||
code: data?.market?.tradableInstrument.instrument.code,
|
||||
name: data?.market?.tradableInstrument.instrument.name,
|
||||
assetDecimalPlaces:
|
||||
data?.market?.tradableInstrument.instrument.product.settlementAsset
|
||||
.decimals,
|
||||
|
@ -5,7 +5,10 @@ import {
|
||||
getDateTimeFormat,
|
||||
t,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
AgGridDynamic as AgGrid,
|
||||
TooltipCellComponent,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import type { LiquidityProvision } from './liquidity-data-provider';
|
||||
@ -14,11 +17,6 @@ import BigNumber from 'bignumber.js';
|
||||
import type { LiquidityProvisionStatus } from '@vegaprotocol/types';
|
||||
import { LiquidityProvisionStatusMapping } from '@vegaprotocol/types';
|
||||
|
||||
const assetDecimalsFormatter = ({ value, data }: ValueFormatterParams) => {
|
||||
if (!value) return '-';
|
||||
return addDecimalsFormatNumber(value, data.assetDecimalPlaces);
|
||||
};
|
||||
|
||||
const percentageFormatter = ({ value }: ValueFormatterParams) => {
|
||||
if (!value) return '-';
|
||||
return formatNumberPercentage(new BigNumber(value).times(100), 4) || '-';
|
||||
@ -33,10 +31,16 @@ const dateValueFormatter = ({ value }: { value?: string | null }) => {
|
||||
|
||||
export interface LiquidityTableProps {
|
||||
data: LiquidityProvision[];
|
||||
symbol?: string;
|
||||
assetDecimalPlaces?: number;
|
||||
}
|
||||
|
||||
export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
|
||||
(props, ref) => {
|
||||
({ data, symbol = '', assetDecimalPlaces }, ref) => {
|
||||
const assetDecimalsFormatter = ({ value }: ValueFormatterParams) => {
|
||||
if (!value) return '-';
|
||||
return `${addDecimalsFormatNumber(value, assetDecimalPlaces ?? 0)}`;
|
||||
};
|
||||
return (
|
||||
<AgGrid
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
@ -44,29 +48,45 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
|
||||
getRowId={({ data }) => data.party}
|
||||
rowHeight={34}
|
||||
ref={ref}
|
||||
tooltipShowDelay={500}
|
||||
defaultColDef={{
|
||||
flex: 1,
|
||||
resizable: true,
|
||||
minWidth: 100,
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
}}
|
||||
rowData={props.data}
|
||||
{...props}
|
||||
rowData={data}
|
||||
>
|
||||
<AgGridColumn headerName={t('Party')} field="party" />
|
||||
<AgGridColumn
|
||||
headerName={t('Commitment')}
|
||||
headerName={t('Party')}
|
||||
field="party"
|
||||
headerTooltip={t(
|
||||
'The public key of the party making this commitment.'
|
||||
)}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t(`Commitment (${symbol})`)}
|
||||
field="commitmentAmount"
|
||||
type="rightAligned"
|
||||
headerTooltip={t(
|
||||
'The amount committed to the market by this liquidity provider.'
|
||||
)}
|
||||
valueFormatter={assetDecimalsFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Share')}
|
||||
field="equityLikeShare"
|
||||
type="rightAligned"
|
||||
headerTooltip={t(
|
||||
'The equity-like share of liquidity of the market, specific to each liquidity provider.'
|
||||
)}
|
||||
valueFormatter={percentageFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Fee')}
|
||||
headerName={t('Proposed fee')}
|
||||
headerTooltip={t(
|
||||
'The fee percentage (per trade) proposed by each liquidity provider.'
|
||||
)}
|
||||
field="fee"
|
||||
type="rightAligned"
|
||||
valueFormatter={percentageFormatter}
|
||||
@ -75,22 +95,33 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
|
||||
headerName={t('Average entry valuation')}
|
||||
field="averageEntryValuation"
|
||||
type="rightAligned"
|
||||
headerTooltip={t(
|
||||
'The average entry valuation of this liquidity provision for the market.'
|
||||
)}
|
||||
minWidth={160}
|
||||
valueFormatter={assetDecimalsFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Obligation (siskas)')}
|
||||
headerName={t('Obligation')}
|
||||
field="obligation"
|
||||
type="rightAligned"
|
||||
headerTooltip={t(
|
||||
'The liquidity provider’s obligation to the market, calculated as the liquidity commitment amount multiplied by the value of the stake_to_ccy_siskas network parameter.'
|
||||
)}
|
||||
valueFormatter={assetDecimalsFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Supplied (siskas)')}
|
||||
headerName={t('Supplied')}
|
||||
headerTooltip={t(
|
||||
'The amount of the settlement asset supplied for liquidity by this provider, calculated as the bond account balance multiplied by the value of the stake_to_ccy_siskas network parameter.'
|
||||
)}
|
||||
field="supplied"
|
||||
type="rightAligned"
|
||||
valueFormatter={assetDecimalsFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Status')}
|
||||
headerTooltip={t('The current status of this liquidity provision.')}
|
||||
field="status"
|
||||
valueFormatter={({ value }: { value: LiquidityProvisionStatus }) => {
|
||||
if (!value) return value;
|
||||
@ -99,12 +130,18 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Created')}
|
||||
headerTooltip={t(
|
||||
'The date and time this liquidity provision was created.'
|
||||
)}
|
||||
field="createdAt"
|
||||
type="rightAligned"
|
||||
valueFormatter={dateValueFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Updated')}
|
||||
headerTooltip={t(
|
||||
'The last time this liquidity provision was updated.'
|
||||
)}
|
||||
field="updatedAt"
|
||||
type="rightAligned"
|
||||
valueFormatter={dateValueFormatter}
|
||||
|
12
libs/market-info/.babelrc
Normal file
12
libs/market-info/.babelrc
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nrwl/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
18
libs/market-info/.eslintrc.json
Normal file
18
libs/market-info/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
7
libs/market-info/README.md
Normal file
7
libs/market-info/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# market-info
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test market-info` to execute the unit tests via [Jest](https://jestjs.io).
|
11
libs/market-info/jest.config.ts
Normal file
11
libs/market-info/jest.config.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'market-info',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/market-info',
|
||||
setupFilesAfterEnv: ['./src/setup-tests.ts'],
|
||||
};
|
4
libs/market-info/package.json
Normal file
4
libs/market-info/package.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "@vegaprotocol/market-info",
|
||||
"version": "0.0.1"
|
||||
}
|
43
libs/market-info/project.json
Normal file
43
libs/market-info/project.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/market-info/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/web:rollup",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/market-info",
|
||||
"tsConfig": "libs/market-info/tsconfig.lib.json",
|
||||
"project": "libs/market-info/package.json",
|
||||
"entryFile": "libs/market-info/src/index.ts",
|
||||
"external": ["react/jsx-runtime"],
|
||||
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
|
||||
"compiler": "babel",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "libs/market-info/README.md",
|
||||
"input": ".",
|
||||
"output": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["libs/market-info/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/libs/market-info"],
|
||||
"options": {
|
||||
"jestConfig": "libs/market-info/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
libs/market-info/src/components/MarketNames.graphql
Normal file
20
libs/market-info/src/components/MarketNames.graphql
Normal file
@ -0,0 +1,20 @@
|
||||
query MarketNames {
|
||||
markets {
|
||||
id
|
||||
name
|
||||
state
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
code
|
||||
metadata {
|
||||
tags
|
||||
}
|
||||
product {
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
libs/market-info/src/components/index.ts
Normal file
1
libs/market-info/src/components/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './market-info';
|
@ -1,3 +1,5 @@
|
||||
export * from './__generated__';
|
||||
export * from './info-key-value-table';
|
||||
export * from './info-market-query';
|
||||
export * from './info-market';
|
||||
export * from './tooltip-mapping';
|
@ -16,8 +16,7 @@ import { tooltipMapping } from './tooltip-mapping';
|
||||
|
||||
interface RowProps {
|
||||
field: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any;
|
||||
value: unknown;
|
||||
decimalPlaces?: number;
|
||||
asPercentage?: boolean;
|
||||
unformatted?: boolean;
|
||||
@ -32,49 +31,48 @@ const Row = ({
|
||||
unformatted,
|
||||
assetSymbol = '',
|
||||
}: RowProps) => {
|
||||
const isNumber = typeof value === 'number' || !isNaN(Number(value));
|
||||
const isPrimitive = typeof value === 'string' || isNumber;
|
||||
const className = 'text-black dark:text-white text-ui !px-0 !font-normal';
|
||||
let formattedValue = value;
|
||||
if (isNumber && !unformatted) {
|
||||
if (decimalPlaces) {
|
||||
formattedValue = `${addDecimalsFormatNumber(
|
||||
value,
|
||||
decimalPlaces
|
||||
)} ${assetSymbol}`;
|
||||
} else if (asPercentage && value) {
|
||||
formattedValue = formatNumberPercentage(new BigNumber(value).times(100));
|
||||
} else {
|
||||
formattedValue = `${formatNumber(Number(value))} ${assetSymbol}`;
|
||||
|
||||
const getFormattedValue = (value: unknown) => {
|
||||
if (typeof value !== 'string' && typeof value !== 'number') return null;
|
||||
if (unformatted || isNaN(Number(value))) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
if (isPrimitive) {
|
||||
return (
|
||||
<KeyValueTableRow
|
||||
key={field}
|
||||
inline={isPrimitive}
|
||||
noBorder={true}
|
||||
dtClassName={className}
|
||||
ddClassName={className}
|
||||
>
|
||||
<Tooltip description={tooltipMapping[field]} align="start">
|
||||
<div tabIndex={-1}>{startCase(t(field))}</div>
|
||||
</Tooltip>
|
||||
<span style={{ wordBreak: 'break-word' }}>{formattedValue}</span>
|
||||
</KeyValueTableRow>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
if (decimalPlaces) {
|
||||
return `${addDecimalsFormatNumber(value, decimalPlaces)} ${assetSymbol}`;
|
||||
}
|
||||
if (asPercentage) {
|
||||
return formatNumberPercentage(new BigNumber(value).times(100));
|
||||
}
|
||||
return `${formatNumber(Number(value))} ${assetSymbol}`;
|
||||
};
|
||||
|
||||
const formattedValue: string | number | null = getFormattedValue(value);
|
||||
|
||||
if (!formattedValue) return null;
|
||||
return (
|
||||
<KeyValueTableRow
|
||||
key={field}
|
||||
inline={true}
|
||||
noBorder={true}
|
||||
dtClassName={className}
|
||||
ddClassName={className}
|
||||
>
|
||||
<Tooltip description={tooltipMapping[field]} align="start">
|
||||
<div tabIndex={-1}>{startCase(t(field))}</div>
|
||||
</Tooltip>
|
||||
<span style={{ wordBreak: 'break-word' }}>{formattedValue}</span>
|
||||
</KeyValueTableRow>
|
||||
);
|
||||
};
|
||||
|
||||
export interface MarketInfoTableProps {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: any;
|
||||
data: unknown;
|
||||
decimalPlaces?: number;
|
||||
asPercentage?: boolean;
|
||||
unformatted?: boolean;
|
||||
omits?: string[];
|
||||
link?: ReactNode;
|
||||
children?: ReactNode;
|
||||
assetSymbol?: string;
|
||||
}
|
||||
|
||||
@ -84,9 +82,12 @@ export const MarketInfoTable = ({
|
||||
asPercentage,
|
||||
unformatted,
|
||||
omits = ['__typename'],
|
||||
link,
|
||||
children,
|
||||
assetSymbol,
|
||||
}: MarketInfoTableProps) => {
|
||||
if (!data || typeof data !== 'object') {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<KeyValueTable>
|
||||
@ -104,7 +105,7 @@ export const MarketInfoTable = ({
|
||||
/>
|
||||
))}
|
||||
</KeyValueTable>
|
||||
{link}
|
||||
<div className="flex flex-col gap-2">{children}</div>
|
||||
</>
|
||||
);
|
||||
};
|
@ -39,11 +39,6 @@ export const MARKET_INFO_QUERY = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
riskFactors {
|
||||
market
|
||||
short
|
||||
long
|
||||
}
|
||||
data {
|
||||
market {
|
||||
id
|
@ -1,6 +1,11 @@
|
||||
import { useMemo } from 'react';
|
||||
import { formatNumber, t } from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer, Splash, Accordion } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
Splash,
|
||||
Accordion,
|
||||
Link,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import pick from 'lodash/pick';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useQuery } from '@apollo/client';
|
||||
@ -19,7 +24,6 @@ import type {
|
||||
} from './__generated__/MarketInfoQuery';
|
||||
import { MarketInfoTable } from './info-key-value-table';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { generatePath } from 'react-router-dom';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
@ -29,6 +33,7 @@ const Links = {
|
||||
|
||||
export interface InfoProps {
|
||||
market: MarketInfoQuery_market;
|
||||
onSelect: (id: string) => void;
|
||||
}
|
||||
|
||||
export const calcCandleVolume = (
|
||||
@ -43,8 +48,12 @@ export const calcCandleVolume = (
|
||||
|
||||
export interface MarketInfoContainerProps {
|
||||
marketId: string;
|
||||
onSelect: (id: string) => void;
|
||||
}
|
||||
export const MarketInfoContainer = ({ marketId }: MarketInfoContainerProps) => {
|
||||
export const MarketInfoContainer = ({
|
||||
marketId,
|
||||
onSelect,
|
||||
}: MarketInfoContainerProps) => {
|
||||
const yTimestamp = useMemo(() => {
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
return new Date(yesterday * 1000).toISOString();
|
||||
@ -63,7 +72,7 @@ export const MarketInfoContainer = ({ marketId }: MarketInfoContainerProps) => {
|
||||
return (
|
||||
<AsyncRenderer<MarketInfoQuery> data={data} loading={loading} error={error}>
|
||||
{data && data.market ? (
|
||||
<Info market={data.market} />
|
||||
<Info market={data.market} onSelect={onSelect} />
|
||||
) : (
|
||||
<Splash>
|
||||
<p>{t('Could not load market')}</p>
|
||||
@ -73,8 +82,8 @@ export const MarketInfoContainer = ({ marketId }: MarketInfoContainerProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Info = ({ market }: InfoProps) => {
|
||||
const { VEGA_TOKEN_URL } = useEnvironment();
|
||||
export const Info = ({ market, onSelect }: InfoProps) => {
|
||||
const { VEGA_TOKEN_URL, VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const headerClassName = 'uppercase text-lg';
|
||||
const dayVolume = calcCandleVolume(market);
|
||||
const assetSymbol =
|
||||
@ -153,7 +162,6 @@ export const Info = ({ market }: InfoProps) => {
|
||||
),
|
||||
})),
|
||||
];
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const keyDetails = {
|
||||
...pick(market, 'decimalPlaces', 'positionDecimalPlaces', 'tradingMode'),
|
||||
state: MarketStateMapping[market.state],
|
||||
@ -218,16 +226,6 @@ export const Info = ({ market }: InfoProps) => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Risk factors'),
|
||||
content: (
|
||||
<MarketInfoTable
|
||||
data={market.riskFactors}
|
||||
unformatted={true}
|
||||
omits={['market', '__typename']}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Risk model'),
|
||||
content: (
|
||||
@ -282,35 +280,30 @@ export const Info = ({ market }: InfoProps) => {
|
||||
.decimals
|
||||
}
|
||||
assetSymbol={assetSymbol}
|
||||
link={
|
||||
<ExternalLink href={`/liquidity/${market.id}`}>
|
||||
{t('View liquidity provision table')}
|
||||
</ExternalLink>
|
||||
}
|
||||
/>
|
||||
>
|
||||
<Link onClick={() => onSelect(market.id)}>
|
||||
{t('View liquidity provision table')}
|
||||
</Link>
|
||||
</MarketInfoTable>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Oracle'),
|
||||
content: (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
...market.tradableInstrument.instrument.product.oracleSpecBinding,
|
||||
priceOracle:
|
||||
market.tradableInstrument.instrument.product
|
||||
.oracleSpecForSettlementPrice.id,
|
||||
terminationOracle:
|
||||
market.tradableInstrument.instrument.product
|
||||
.oracleSpecForTradingTermination.id,
|
||||
}}
|
||||
link={
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/oracles#${market.tradableInstrument.instrument.product.oracleSpecForTradingTermination.id}`}
|
||||
>
|
||||
{t('View full oracle details')}
|
||||
</ExternalLink>
|
||||
}
|
||||
/>
|
||||
data={market.tradableInstrument.instrument.product.oracleSpecBinding}
|
||||
>
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/oracles#${market.tradableInstrument.instrument.product.oracleSpecForSettlementPrice.id}`}
|
||||
>
|
||||
{t('View price oracle specification')}
|
||||
</ExternalLink>
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/oracles#${market.tradableInstrument.instrument.product.oracleSpecForTradingTermination.id}`}
|
||||
>
|
||||
{t('View termination oracle specification')}
|
||||
</ExternalLink>
|
||||
</MarketInfoTable>
|
||||
),
|
||||
},
|
||||
];
|
||||
@ -319,21 +312,19 @@ export const Info = ({ market }: InfoProps) => {
|
||||
{
|
||||
title: t('Proposal'),
|
||||
content: (
|
||||
<p>
|
||||
<ExternalLink
|
||||
href={generatePath(Links.PROPOSAL_PAGE, {
|
||||
tokenUrl: VEGA_TOKEN_URL,
|
||||
proposalId: market.proposal?.id || '',
|
||||
})}
|
||||
title={
|
||||
market.proposal?.rationale.title ||
|
||||
market.proposal?.rationale.description ||
|
||||
''
|
||||
}
|
||||
>
|
||||
{t('View governance proposal')}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
<ExternalLink
|
||||
href={generatePath(Links.PROPOSAL_PAGE, {
|
||||
tokenUrl: VEGA_TOKEN_URL,
|
||||
proposalId: market.proposal?.id || '',
|
||||
})}
|
||||
title={
|
||||
market.proposal?.rationale.title ||
|
||||
market.proposal?.rationale.description ||
|
||||
''
|
||||
}
|
||||
>
|
||||
{t('View governance proposal')}
|
||||
</ExternalLink>
|
||||
),
|
||||
},
|
||||
];
|
@ -7,7 +7,7 @@ export const tooltipMapping: Record<string, ReactNode> = {
|
||||
'Maker portion of the fee is transferred to the non-aggressive, or passive party in the trade (the maker, as opposed to the taker).'
|
||||
),
|
||||
liquidityFee: t(
|
||||
'Liquidity portion of the fee is paid to market makers for providing liquidity, and is transferred to the liquidity fee pool for the market.'
|
||||
'Liquidity portion of the fee is paid to liquidity providers, and is transferred to the liquidity fee pool for the market.'
|
||||
),
|
||||
infrastructureFee: t(
|
||||
'Fees paid to validators as a reward for running the infrastructure of the network.'
|
||||
@ -100,4 +100,8 @@ export const tooltipMapping: Record<string, ReactNode> = {
|
||||
scalingFactor: t(
|
||||
'The scaling between the liquidity demand estimate, based on open interest and target stake.'
|
||||
),
|
||||
targetStake: t(
|
||||
`The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.`
|
||||
),
|
||||
suppliedStake: t('The current amount of liquidity supplied for this market.'),
|
||||
};
|
1
libs/market-info/src/index.ts
Normal file
1
libs/market-info/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './components';
|
1
libs/market-info/src/setup-tests.ts
Normal file
1
libs/market-info/src/setup-tests.ts
Normal file
@ -0,0 +1 @@
|
||||
import '@testing-library/jest-dom';
|
25
libs/market-info/tsconfig.json
Normal file
25
libs/market-info/tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
23
libs/market-info/tsconfig.lib.json
Normal file
23
libs/market-info/tsconfig.lib.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"files": [
|
||||
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||
"../../node_modules/@nrwl/react/typings/image.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"jest.config.ts"
|
||||
],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
20
libs/market-info/tsconfig.spec.json
Normal file
20
libs/market-info/tsconfig.spec.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node", "@testing-library/jest-dom"]
|
||||
},
|
||||
"include": [
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.jsx",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.d.ts",
|
||||
"jest.config.ts"
|
||||
]
|
||||
}
|
@ -23,7 +23,7 @@ import type { AgGridReact, AgGridReactProps } from 'ag-grid-react';
|
||||
import type { IDatasource, IGetRowsParams } from 'ag-grid-community';
|
||||
import type { Position } from './positions-data-providers';
|
||||
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||
import { Intent, Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Intent, Button, TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const getRowId = ({ data }: { data: Position }) => data.marketId;
|
||||
|
||||
@ -151,9 +151,11 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
||||
getRowId={getRowId}
|
||||
rowHeight={34}
|
||||
ref={ref}
|
||||
tooltipShowDelay={500}
|
||||
defaultColDef={{
|
||||
flex: 1,
|
||||
resizable: true,
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
}}
|
||||
components={{ PriceFlashCell, ProgressBarCell }}
|
||||
{...props}
|
||||
|
@ -2,7 +2,6 @@ import { Side } from '@vegaprotocol/types';
|
||||
import type { ICellRendererParams } from 'ag-grid-community';
|
||||
import classNames from 'classnames';
|
||||
import { addDecimalsFormatNumber } from '../format';
|
||||
import { negativeClassNames, positiveClassNames } from './cell-class-rules';
|
||||
|
||||
export const Size = ({
|
||||
value,
|
||||
@ -17,8 +16,8 @@ export const Size = ({
|
||||
<span
|
||||
data-testid="size"
|
||||
className={classNames('text-right', {
|
||||
[positiveClassNames]: side === Side.SIDE_BUY,
|
||||
[negativeClassNames]: side === Side.SIDE_SELL,
|
||||
'text-vega-green-dark dark:text-vega-green': side === Side.SIDE_BUY,
|
||||
'text-vega-red-dark dark:text-vega-red': side === Side.SIDE_SELL,
|
||||
})}
|
||||
>
|
||||
{side === Side.SIDE_BUY ? '+' : side === Side.SIDE_SELL ? '-' : ''}
|
||||
|
@ -1 +1 @@
|
||||
export { Tooltip } from './tooltip';
|
||||
export * from './tooltip';
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
Arrow,
|
||||
Portal,
|
||||
} from '@radix-ui/react-tooltip';
|
||||
import type { ITooltipParams } from 'ag-grid-community';
|
||||
|
||||
export interface TooltipProps {
|
||||
children: React.ReactElement;
|
||||
@ -56,3 +57,11 @@ export const Tooltip = ({
|
||||
) : (
|
||||
children
|
||||
);
|
||||
|
||||
export const TooltipCellComponent = (props: ITooltipParams) => {
|
||||
return (
|
||||
<p className="max-w-sm bg-neutral-200 px-4 py-2 z-20 rounded text-sm break-word text-black">
|
||||
{props.value}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
@ -27,6 +27,7 @@
|
||||
"@vegaprotocol/governance": ["libs/governance/src/index.ts"],
|
||||
"@vegaprotocol/liquidity": ["libs/liquidity/src/index.ts"],
|
||||
"@vegaprotocol/market-depth": ["libs/market-depth/src/index.ts"],
|
||||
"@vegaprotocol/market-info": ["libs/market-info/src/index.ts"],
|
||||
"@vegaprotocol/market-list": ["libs/market-list/src/index.ts"],
|
||||
"@vegaprotocol/network-info": ["libs/network-info/src/index.ts"],
|
||||
"@vegaprotocol/network-stats": ["libs/network-stats/src/index.ts"],
|
||||
|
@ -16,6 +16,7 @@
|
||||
"governance": "libs/governance",
|
||||
"liquidity": "libs/liquidity",
|
||||
"market-depth": "libs/market-depth",
|
||||
"market-info": "libs/market-info",
|
||||
"market-list": "libs/market-list",
|
||||
"network-info": "libs/network-info",
|
||||
"network-stats": "libs/network-stats",
|
||||
|
Loading…
Reference in New Issue
Block a user