feat: liquidity provisions view (#1133)
* 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 * chore: add another wait for market 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
fa6ddcfdf1
commit
cae6162a7f
@ -1,27 +0,0 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import type { NetworkParams } from './__generated__/NetworkParams';
|
||||
|
||||
export const NETWORK_PARAMS_QUERY = gql`
|
||||
query NetworkParams {
|
||||
networkParameters {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function useNetworkParam(params: string[]) {
|
||||
const { data, loading, error } = useQuery<NetworkParams, never>(
|
||||
NETWORK_PARAMS_QUERY
|
||||
);
|
||||
const foundParams = data?.networkParameters
|
||||
?.filter((p) => params.includes(p.key))
|
||||
.sort((a, b) => params.indexOf(a.key) - params.indexOf(b.key));
|
||||
return {
|
||||
data: foundParams ? foundParams.map((f) => f.value) : null,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import { useNetworkParams } from '@vegaprotocol/react-helpers';
|
||||
import React from 'react';
|
||||
|
||||
import { NetworkParams } from '../../../config';
|
||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
import { useNetworkParam } from '../../../hooks/use-network-param';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import { addDecimal } from '../../../lib/decimals';
|
||||
import type {
|
||||
@ -16,7 +16,7 @@ const useProposalNetworkParams = ({
|
||||
}: {
|
||||
proposal: Proposals_proposals;
|
||||
}) => {
|
||||
const { data, loading } = useNetworkParam([
|
||||
const { data, loading } = useNetworkParams([
|
||||
NetworkParams.GOV_UPDATE_MARKET_REQUIRED_MAJORITY,
|
||||
NetworkParams.GOV_UPDATE_MARKET_REQUIRED_PARTICIPATION,
|
||||
NetworkParams.GOV_NEW_MARKET_REQUIRED_MAJORITY,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { Button, Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { formatDistance } from 'date-fns';
|
||||
// @ts-ignore No types availabel for duration-js
|
||||
// @ts-ignore No types available for duration-js
|
||||
import Duration from 'duration-js';
|
||||
import gql from 'graphql-tag';
|
||||
import React from 'react';
|
||||
@ -15,10 +15,10 @@ import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../../contexts/app-state/app-state-context';
|
||||
import { useNetworkParam } from '../../../hooks/use-network-param';
|
||||
import type { Rewards } from './__generated__/Rewards';
|
||||
import { RewardInfo } from './reward-info';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useNetworkParams } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const REWARDS_QUERY = gql`
|
||||
query Rewards($partyId: ID!) {
|
||||
@ -77,7 +77,7 @@ export const RewardsIndex = () => {
|
||||
data: rewardAssetData,
|
||||
loading: rewardAssetLoading,
|
||||
error: rewardAssetError,
|
||||
} = useNetworkParam([
|
||||
} = useNetworkParams([
|
||||
NetworkParams.REWARD_ASSET,
|
||||
NetworkParams.REWARD_PAYOUT_DURATION,
|
||||
]);
|
||||
|
@ -7,7 +7,6 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { TokenInput } from '../../components/token-input';
|
||||
import { NetworkParams } from '../../config';
|
||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||
import { useNetworkParam } from '../../hooks/use-network-param';
|
||||
import { useSearchParams } from '../../hooks/use-search-params';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { addDecimal, removeDecimal } from '../../lib/decimals';
|
||||
@ -31,6 +30,7 @@ import type {
|
||||
UndelegateSubmissionBody,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useNetworkParam } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const PARTY_DELEGATIONS_QUERY = gql`
|
||||
query PartyDelegations($partyId: ID!) {
|
||||
@ -103,9 +103,9 @@ export const StakingForm = ({
|
||||
setAmount('');
|
||||
}, [action, setAmount]);
|
||||
|
||||
const { data } = useNetworkParam([
|
||||
NetworkParams.VALIDATOR_DELEGATION_MIN_AMOUNT,
|
||||
]);
|
||||
const { data } = useNetworkParam(
|
||||
NetworkParams.VALIDATOR_DELEGATION_MIN_AMOUNT
|
||||
);
|
||||
|
||||
const minTokensWithDecimals = React.useMemo(() => {
|
||||
const minTokens = new BigNumber(data && data.length === 1 ? data[0] : '');
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import {
|
||||
MarketState,
|
||||
MarketStateMapping,
|
||||
MarketTradingModeMapping,
|
||||
} from '@vegaprotocol/types';
|
||||
import { mockTradingPage } from '../support/trading';
|
||||
|
||||
const marketInfoBtn = 'Info';
|
||||
@ -51,8 +55,12 @@ describe('market info is displayed', () => {
|
||||
validateMarketDataRow(0, 'Name', 'ETHBTC Quarterly (30 Jun 2022)');
|
||||
validateMarketDataRow(1, 'Decimal Places', '2');
|
||||
validateMarketDataRow(2, 'Position Decimal Places', '0');
|
||||
validateMarketDataRow(3, 'Trading Mode', 'Trading mode continuous');
|
||||
validateMarketDataRow(4, 'State', 'STATE_ACTIVE');
|
||||
validateMarketDataRow(
|
||||
3,
|
||||
'Trading Mode',
|
||||
MarketTradingModeMapping.TRADING_MODE_CONTINUOUS
|
||||
);
|
||||
validateMarketDataRow(4, 'State', MarketStateMapping.STATE_ACTIVE);
|
||||
validateMarketDataRow(5, 'Market ID', 'market-0');
|
||||
});
|
||||
|
||||
|
@ -10,6 +10,7 @@ describe('markets table', () => {
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
cy.visit('/');
|
||||
cy.wait('@Market');
|
||||
cy.wait('@MarketList');
|
||||
cy.get('[data-testid^="market-link-"]')
|
||||
.should('not.be.empty')
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
||||
import { generateAccounts } from '../support/mocks/generate-accounts';
|
||||
import { generateNetworkParameters } from '../support/mocks/generate-network-parameters';
|
||||
import { generateWithdrawFormQuery } from '../support/mocks/generate-withdraw-page-query';
|
||||
import { generateWithdrawals } from '../support/mocks/generate-withdrawals';
|
||||
@ -20,6 +21,7 @@ describe('withdraw', () => {
|
||||
aliasQuery(req, 'Withdrawals', generateWithdrawals());
|
||||
aliasQuery(req, 'NetworkParamsQuery', generateNetworkParameters());
|
||||
aliasQuery(req, 'WithdrawFormQuery', generateWithdrawFormQuery());
|
||||
aliasQuery(req, 'Accounts', generateAccounts());
|
||||
});
|
||||
|
||||
cy.visit('/portfolio');
|
||||
|
@ -71,6 +71,18 @@ export const generateAccounts = (
|
||||
decimals: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'Account',
|
||||
type: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
balance: '100000000',
|
||||
market: null,
|
||||
asset: {
|
||||
__typename: 'Asset',
|
||||
id: 'asset-0',
|
||||
symbol: 'AST0',
|
||||
decimals: 5,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
@ -86,7 +86,9 @@ export const generateMarketInfoQuery = (
|
||||
id: '54b78c1b877e106842ae156332ccec740ad98d6bad43143ac6a029501dd7c6e0',
|
||||
},
|
||||
markPrice: '5749',
|
||||
indicativeVolume: '0',
|
||||
suppliedStake: '56767',
|
||||
marketValueProxy: '677678',
|
||||
targetStake: '56789',
|
||||
bestBidVolume: '5',
|
||||
bestOfferVolume: '1',
|
||||
bestStaticBidVolume: '5',
|
||||
@ -146,6 +148,7 @@ export const generateMarketInfoQuery = (
|
||||
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
|
||||
symbol: 'tBTC',
|
||||
name: 'tBTC TEST',
|
||||
decimals: 1,
|
||||
},
|
||||
oracleSpecForSettlementPrice: {
|
||||
__typename: 'OracleSpec',
|
||||
|
@ -1,66 +1,69 @@
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
import type {
|
||||
WithdrawFormQuery,
|
||||
WithdrawFormQuery_assetsConnection_edges,
|
||||
WithdrawFormQuery_party_accounts,
|
||||
WithdrawFormQuery_party_withdrawals,
|
||||
} from '@vegaprotocol/withdraws';
|
||||
import merge from 'lodash/merge';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
|
||||
export const generateWithdrawFormQuery = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
override?: PartialDeep<any>
|
||||
) => {
|
||||
override?: PartialDeep<WithdrawFormQuery>
|
||||
): WithdrawFormQuery => {
|
||||
const withdrawal: WithdrawFormQuery_party_withdrawals = {
|
||||
id: 'withdrawal-0',
|
||||
txHash: null,
|
||||
__typename: 'Withdrawal',
|
||||
};
|
||||
const account: WithdrawFormQuery_party_accounts = {
|
||||
type: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
balance: '100000000',
|
||||
asset: {
|
||||
__typename: 'Asset',
|
||||
id: 'asset-0',
|
||||
symbol: 'AST0',
|
||||
},
|
||||
__typename: 'Account',
|
||||
};
|
||||
const assetEdge1: WithdrawFormQuery_assetsConnection_edges = {
|
||||
node: {
|
||||
id: 'asset-0',
|
||||
symbol: 'AST0',
|
||||
name: 'Asset 0',
|
||||
decimals: 5,
|
||||
source: {
|
||||
__typename: 'ERC20',
|
||||
contractAddress: '0x5E4b9aDA947130Fc320a144cd22bC1641e5c9d81',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
};
|
||||
const assetEdge2: WithdrawFormQuery_assetsConnection_edges = {
|
||||
node: {
|
||||
id: 'asset-1',
|
||||
symbol: 'AST1',
|
||||
name: 'Asset 1',
|
||||
decimals: 5,
|
||||
source: {
|
||||
__typename: 'ERC20',
|
||||
contractAddress: '0x444b9aDA947130Fc320a144cd22bC1641e5c9d81',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
};
|
||||
const defaultResult = {
|
||||
party: {
|
||||
id: 'party-0',
|
||||
withdrawals: [
|
||||
{
|
||||
id: 'withdrawal-0',
|
||||
txHash: null,
|
||||
__typename: 'Withdrawal',
|
||||
},
|
||||
],
|
||||
accounts: [
|
||||
{
|
||||
type: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
balance: '100000000',
|
||||
asset: {
|
||||
__typename: 'Asset',
|
||||
id: 'asset-0',
|
||||
symbol: 'AST0',
|
||||
},
|
||||
__typename: 'Account',
|
||||
},
|
||||
],
|
||||
withdrawals: [withdrawal],
|
||||
accounts: [account],
|
||||
__typename: 'Party',
|
||||
},
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: 'asset-0',
|
||||
symbol: 'AST0',
|
||||
name: 'Asset 0',
|
||||
decimals: 5,
|
||||
source: {
|
||||
__typename: 'ERC20',
|
||||
contractAddress: '0x5E4b9aDA947130Fc320a144cd22bC1641e5c9d81',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'asset-1',
|
||||
symbol: 'AST1',
|
||||
name: 'Asset 1',
|
||||
decimals: 5,
|
||||
source: {
|
||||
__typename: 'ERC20',
|
||||
contractAddress: '0x444b9aDA947130Fc320a144cd22bC1641e5c9d81',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
],
|
||||
__typename: 'AssetsConnection',
|
||||
edges: [assetEdge1, assetEdge2],
|
||||
},
|
||||
};
|
||||
|
||||
|
53
apps/trading/components/header/header.tsx
Normal file
53
apps/trading/components/header/header.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { Children } from 'react';
|
||||
import { cloneElement } from 'react';
|
||||
|
||||
interface TradeMarketHeaderProps {
|
||||
title: ReactNode;
|
||||
children: ReactElement[];
|
||||
}
|
||||
|
||||
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
|
||||
data-testid="market-summary"
|
||||
className="flex flex-nowrap items-start xl:flex-1 w-full overflow-x-auto text-xs "
|
||||
>
|
||||
{Children.map(children, (child, index) => {
|
||||
return cloneElement(child, {
|
||||
id: `header-stat-${index}`,
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export const HeaderStat = ({
|
||||
children,
|
||||
heading,
|
||||
id,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
heading: string;
|
||||
id?: string;
|
||||
}) => {
|
||||
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';
|
||||
|
||||
return (
|
||||
<div className={itemClass}>
|
||||
<div id={id} className={itemHeading}>
|
||||
{heading}
|
||||
</div>
|
||||
<div aria-labelledby={id}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
1
apps/trading/components/header/index.ts
Normal file
1
apps/trading/components/header/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './header';
|
@ -10,9 +10,8 @@ import type { Market_market } from '../../pages/markets/__generated__/Market';
|
||||
|
||||
type MarketDataGridProps = {
|
||||
grid: {
|
||||
label: string;
|
||||
label: string | ReactNode;
|
||||
value?: ReactNode;
|
||||
isEstimate?: boolean;
|
||||
}[];
|
||||
};
|
||||
|
||||
@ -20,12 +19,11 @@ const MarketDataGrid = ({ grid }: MarketDataGridProps) => {
|
||||
return (
|
||||
<>
|
||||
{grid.map(
|
||||
({ label, value, isEstimate }, index) =>
|
||||
({ label, value }, index) =>
|
||||
value && (
|
||||
<div key={index} className="grid grid-cols-2">
|
||||
<span data-testid="tooltip-label">{label}</span>
|
||||
<span data-testid="tooltip-value">
|
||||
{isEstimate && <span className="ml-[-0.625em]">{'~'}</span>}
|
||||
<span data-testid="tooltip-value" className="text-right">
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
@ -61,12 +59,14 @@ const compileGridData = (market: Market_market) => {
|
||||
}
|
||||
|
||||
if (market.data?.auctionEnd) {
|
||||
const endDate = getDateTimeFormat().format(
|
||||
new Date(market.data.auctionEnd)
|
||||
);
|
||||
grid.push({
|
||||
label: isLiquidityMonitoringAuction
|
||||
? t('Est auction end')
|
||||
: t('Auction end'),
|
||||
value: getDateTimeFormat().format(new Date(market.data.auctionEnd)),
|
||||
isEstimate: isLiquidityMonitoringAuction ? true : false,
|
||||
value: isLiquidityMonitoringAuction ? `~${endDate}` : endDate,
|
||||
});
|
||||
}
|
||||
|
||||
@ -79,8 +79,11 @@ const compileGridData = (market: Market_market) => {
|
||||
|
||||
if (isLiquidityMonitoringAuction && market.data?.suppliedStake) {
|
||||
grid.push({
|
||||
label: t('Current liquidity'),
|
||||
// @TODO: link this to liquidity view when https://github.com/vegaprotocol/frontend-monorepo/issues/491 is done
|
||||
label: (
|
||||
<Link href={`/liquidity/${market.id}`} target="_blank">
|
||||
{t('Current liquidity')}
|
||||
</Link>
|
||||
),
|
||||
value: formatStake(market.data.suppliedStake, market),
|
||||
});
|
||||
}
|
||||
@ -88,22 +91,24 @@ const compileGridData = (market: Market_market) => {
|
||||
if (market.data?.indicativePrice) {
|
||||
grid.push({
|
||||
label: t('Est uncrossing price'),
|
||||
value: addDecimalsFormatNumber(
|
||||
market.data.indicativePrice,
|
||||
market.positionDecimalPlaces
|
||||
),
|
||||
isEstimate: true,
|
||||
value:
|
||||
'~' +
|
||||
addDecimalsFormatNumber(
|
||||
market.data.indicativePrice,
|
||||
market.positionDecimalPlaces
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (market.data?.indicativeVolume) {
|
||||
grid.push({
|
||||
label: t('Est uncrossing vol'),
|
||||
value: addDecimalsFormatNumber(
|
||||
market.data.indicativeVolume,
|
||||
market.positionDecimalPlaces
|
||||
),
|
||||
isEstimate: true,
|
||||
value:
|
||||
'~' +
|
||||
addDecimalsFormatNumber(
|
||||
market.data.indicativeVolume,
|
||||
market.positionDecimalPlaces
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@ -128,7 +133,7 @@ export const TradingModeTooltip = ({ market }: TradingModeTooltipProps) => {
|
||||
case MarketTradingMode.TRADING_MODE_OPENING_AUCTION: {
|
||||
return (
|
||||
<>
|
||||
<p className="mb-16">
|
||||
<p className="mb-4">
|
||||
<span>
|
||||
{t(
|
||||
'This new market is in an opening auction to determine a fair mid-price before starting continuous trading.'
|
||||
@ -150,7 +155,7 @@ export const TradingModeTooltip = ({ market }: TradingModeTooltipProps) => {
|
||||
case AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY: {
|
||||
return (
|
||||
<>
|
||||
<p data-testid="tooltip-market-info" className="mb-16">
|
||||
<p data-testid="tooltip-market-info" className="mb-4">
|
||||
<span>
|
||||
{t(
|
||||
'This market is in auction until it reaches sufficient liquidity.'
|
||||
@ -170,7 +175,7 @@ export const TradingModeTooltip = ({ market }: TradingModeTooltipProps) => {
|
||||
case AuctionTrigger.AUCTION_TRIGGER_PRICE: {
|
||||
return (
|
||||
<>
|
||||
<p className="mb-16">
|
||||
<p className="mb-4">
|
||||
<span>
|
||||
{t('This market is in auction due to high price volatility.')}
|
||||
</span>{' '}
|
||||
|
103
apps/trading/pages/liquidity/[marketId].page.tsx
Normal file
103
apps/trading/pages/liquidity/[marketId].page.tsx
Normal file
@ -0,0 +1,103 @@
|
||||
import { LiquidityTable, useLiquidityProvision } from '@vegaprotocol/liquidity';
|
||||
import { 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';
|
||||
|
||||
const LiquidityPage = ({ id }: { id?: string }) => {
|
||||
const { query } = useRouter();
|
||||
const { keypair } = useVegaWallet();
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
|
||||
const partyId = keypair?.pub;
|
||||
// Default to first marketId query item if found
|
||||
const marketId =
|
||||
id || (Array.isArray(query.marketId) ? query.marketId[0] : query.marketId);
|
||||
|
||||
const {
|
||||
data: { liquidityProviders, suppliedStake, targetStake, code, symbol },
|
||||
loading,
|
||||
error,
|
||||
} = useLiquidityProvision({ marketId });
|
||||
|
||||
const myLpEdges = useMemo(
|
||||
() => liquidityProviders.filter((e) => e.party === partyId),
|
||||
[liquidityProviders, partyId]
|
||||
);
|
||||
const activeEdges = useMemo(
|
||||
() =>
|
||||
liquidityProviders.filter(
|
||||
(e) => e.status === LiquidityProvisionStatus.STATUS_ACTIVE
|
||||
),
|
||||
[liquidityProviders]
|
||||
);
|
||||
const inactiveEdges = useMemo(
|
||||
() =>
|
||||
liquidityProviders.filter(
|
||||
(e) => e.status !== LiquidityProvisionStatus.STATUS_ACTIVE
|
||||
),
|
||||
[liquidityProviders]
|
||||
);
|
||||
|
||||
const enum LiquidityTabs {
|
||||
Active = 'active',
|
||||
Inactive = 'inactive',
|
||||
MyLiquidityProvision = 'myLP',
|
||||
}
|
||||
|
||||
const getActiveDefaultId = () => {
|
||||
if (myLpEdges?.length > 0) return LiquidityTabs.MyLiquidityProvision;
|
||||
if (activeEdges?.length) return LiquidityTabs.Active;
|
||||
else if (inactiveEdges?.length > 0) return LiquidityTabs.Inactive;
|
||||
return LiquidityTabs.Active;
|
||||
};
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={liquidityProviders}>
|
||||
<div className="h-full grid grid-rows-[min-content_1fr]">
|
||||
<Header
|
||||
title={
|
||||
<Link href={`/markets/${marketId}`}>
|
||||
{`${code} ${t('liquidity provision')}`}
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
<HeaderStat heading={t('Target stake')}>
|
||||
<div>{`${targetStake} ${symbol}`}</div>
|
||||
</HeaderStat>
|
||||
<HeaderStat heading={t('Supplied stake')}>
|
||||
<div>{`${suppliedStake} ${symbol}`}</div>
|
||||
</HeaderStat>
|
||||
<HeaderStat heading={t('Market ID')}>
|
||||
<div className="break-word">{marketId}</div>
|
||||
</HeaderStat>
|
||||
</Header>
|
||||
<Tabs active={getActiveDefaultId()}>
|
||||
<Tab
|
||||
id={LiquidityTabs.MyLiquidityProvision}
|
||||
name={t('My liquidity provision')}
|
||||
hidden={!partyId}
|
||||
>
|
||||
<LiquidityTable ref={gridRef} data={myLpEdges} />
|
||||
</Tab>
|
||||
<Tab id={LiquidityTabs.Active} name={t('Active')}>
|
||||
<LiquidityTable ref={gridRef} data={activeEdges} />
|
||||
</Tab>
|
||||
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
|
||||
<LiquidityTable ref={gridRef} data={inactiveEdges} />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
||||
LiquidityPage.getInitialProps = () => ({
|
||||
page: 'liquidity',
|
||||
});
|
||||
|
||||
export default LiquidityPage;
|
1
apps/trading/pages/liquidity/index.ts
Normal file
1
apps/trading/pages/liquidity/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './[marketId].page';
|
@ -5,7 +5,7 @@ import { Interval } from '@vegaprotocol/types';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { PageQueryContainer } from '../../components/page-query-container';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { TradeGrid, TradePanels } from './trade-grid';
|
||||
@ -91,10 +91,19 @@ const MarketPage = ({ id }: { id?: string }) => {
|
||||
|
||||
// Cache timestamp for yesterday to prevent full unmount of market page when
|
||||
// a rerender occurs
|
||||
const [yTimestamp] = useState(() => {
|
||||
const yTimestamp = useMemo(() => {
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
return new Date(yesterday * 1000).toISOString();
|
||||
});
|
||||
}, []);
|
||||
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId || '',
|
||||
interval: Interval.INTERVAL_I1H,
|
||||
since: yTimestamp,
|
||||
}),
|
||||
[marketId, yTimestamp]
|
||||
);
|
||||
|
||||
if (!marketId) {
|
||||
return (
|
||||
@ -109,11 +118,7 @@ const MarketPage = ({ id }: { id?: string }) => {
|
||||
query={MARKET_QUERY}
|
||||
data-testid="market"
|
||||
options={{
|
||||
variables: {
|
||||
marketId,
|
||||
interval: Interval.INTERVAL_I1H,
|
||||
since: yTimestamp,
|
||||
},
|
||||
variables,
|
||||
fetchPolicy: 'network-only',
|
||||
}}
|
||||
render={({ market }) => {
|
||||
|
@ -1,48 +1,47 @@
|
||||
import 'allotment/dist/style.css';
|
||||
import {
|
||||
DealTicketContainer,
|
||||
MarketInfoContainer,
|
||||
} from '@vegaprotocol/deal-ticket';
|
||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||
import { SelectMarketPopover } from '@vegaprotocol/market-list';
|
||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
getDateFormat,
|
||||
t,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { TradesContainer } from '@vegaprotocol/trades';
|
||||
import {
|
||||
AuctionTrigger,
|
||||
AuctionTriggerMapping,
|
||||
MarketTradingMode,
|
||||
MarketTradingModeMapping,
|
||||
} from '@vegaprotocol/types';
|
||||
import { LayoutPriority } from 'allotment';
|
||||
import classNames from 'classnames';
|
||||
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';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { AccountsContainer } from '@vegaprotocol/accounts';
|
||||
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
||||
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
Tab,
|
||||
Tabs,
|
||||
ResizableGrid,
|
||||
ResizableGridPanel,
|
||||
ButtonLink,
|
||||
Tooltip,
|
||||
PriceCellChange,
|
||||
Link,
|
||||
Tooltip,
|
||||
ResizableGrid,
|
||||
ButtonLink,
|
||||
ResizableGridPanel,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
getDateFormat,
|
||||
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';
|
||||
import {
|
||||
AuctionTrigger,
|
||||
AuctionTriggerMapping,
|
||||
MarketTradingMode,
|
||||
MarketTradingModeMapping,
|
||||
} from '@vegaprotocol/types';
|
||||
import { TradingModeTooltip } from '../../components/trading-mode-tooltip';
|
||||
|
||||
const TradingViews = {
|
||||
|
2
libs/deal-ticket/src/components/deal-ticket/__generated__/index.ts
generated
Normal file
2
libs/deal-ticket/src/components/deal-ticket/__generated__/index.ts
generated
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './DealTicketQuery';
|
||||
export * from './MarketNames';
|
@ -48,7 +48,10 @@ export const DealTicketLimitAmount = ({
|
||||
type="number"
|
||||
step={priceStep}
|
||||
data-testid="order-price"
|
||||
{...register('price', { required: true, min: 0 })}
|
||||
{...register('price', {
|
||||
required: true,
|
||||
min: 0,
|
||||
})}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
12
libs/deal-ticket/src/components/deal-ticket/index.ts
Normal file
12
libs/deal-ticket/src/components/deal-ticket/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export * from './__generated__';
|
||||
export * from './deal-ticket-amount';
|
||||
export * from './deal-ticket-container';
|
||||
export * from './deal-ticket-limit-amount';
|
||||
export * from './deal-ticket-manager';
|
||||
export * from './deal-ticket-market-amount';
|
||||
export * from './deal-ticket';
|
||||
export * from './expiry-selector';
|
||||
export * from './market-selector';
|
||||
export * from './side-selector';
|
||||
export * from './time-in-force-selector';
|
||||
export * from './type-selector';
|
@ -1,15 +1,2 @@
|
||||
export * from './__generated__/DealTicketQuery';
|
||||
export * from './__generated__/MarketInfoQuery';
|
||||
export * from './__generated__/MarketNames';
|
||||
export * from './deal-ticket-amount';
|
||||
export * from './deal-ticket-container';
|
||||
export * from './deal-ticket-limit-amount';
|
||||
export * from './deal-ticket-manager';
|
||||
export * from './deal-ticket-market-amount';
|
||||
export * from './deal-ticket';
|
||||
export * from './expiry-selector';
|
||||
export * from './info-market';
|
||||
export * from './side-selector';
|
||||
export * from './time-in-force-selector';
|
||||
export * from './type-selector';
|
||||
export * from './market-selector';
|
||||
export * from './market-info';
|
||||
|
@ -3,7 +3,7 @@
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { Interval, MarketState, AccountType, MarketTradingMode, AuctionTrigger } from "@vegaprotocol/types";
|
||||
import { Interval, MarketState, MarketTradingMode, AccountType, AuctionTrigger } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: MarketInfoQuery
|
||||
@ -191,10 +191,6 @@ export interface MarketInfoQuery_market_data {
|
||||
* the mark price (an unsigned integer)
|
||||
*/
|
||||
markPrice: string;
|
||||
/**
|
||||
* indicative volume if the auction ended now, 0 if not in auction mode
|
||||
*/
|
||||
indicativeVolume: string;
|
||||
/**
|
||||
* the aggregated volume being bid at the best bid price.
|
||||
*/
|
||||
@ -211,10 +207,6 @@ export interface MarketInfoQuery_market_data {
|
||||
* the aggregated volume being offered at the best static offer price, excluding pegged orders.
|
||||
*/
|
||||
bestStaticOfferVolume: string;
|
||||
/**
|
||||
* the sum of the size of all positions greater than 0.
|
||||
*/
|
||||
openInterest: string;
|
||||
/**
|
||||
* the highest price level on an order book for buy orders.
|
||||
*/
|
||||
@ -227,6 +219,22 @@ export interface MarketInfoQuery_market_data {
|
||||
* what triggered an auction (if an auction was started)
|
||||
*/
|
||||
trigger: AuctionTrigger;
|
||||
/**
|
||||
* the sum of the size of all positions greater than 0.
|
||||
*/
|
||||
openInterest: string;
|
||||
/**
|
||||
* the supplied stake for the market
|
||||
*/
|
||||
suppliedStake: string | null;
|
||||
/**
|
||||
* the amount of stake targeted for this market
|
||||
*/
|
||||
targetStake: string | null;
|
||||
/**
|
||||
* the market value proxy
|
||||
*/
|
||||
marketValueProxy: string;
|
||||
/**
|
||||
* a list of valid price ranges per associated trigger
|
||||
*/
|
||||
@ -287,6 +295,10 @@ export interface MarketInfoQuery_market_tradableInstrument_instrument_product_se
|
||||
* 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 MarketInfoQuery_market_tradableInstrument_instrument_product_oracleSpecForSettlementPrice {
|
||||
@ -454,14 +466,14 @@ export interface MarketInfoQuery_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 )
|
||||
@ -479,6 +491,10 @@ export interface MarketInfoQuery_market {
|
||||
* Current state of the market
|
||||
*/
|
||||
state: MarketState;
|
||||
/**
|
||||
* Current mode of execution of the market
|
||||
*/
|
||||
tradingMode: MarketTradingMode;
|
||||
/**
|
||||
* The proposal that initiated this market
|
||||
*/
|
||||
@ -487,10 +503,6 @@ export interface MarketInfoQuery_market {
|
||||
* Get account for a party or market
|
||||
*/
|
||||
accounts: MarketInfoQuery_market_accounts[] | null;
|
||||
/**
|
||||
* Current mode of execution of the market
|
||||
*/
|
||||
tradingMode: MarketTradingMode;
|
||||
/**
|
||||
* Fees related data
|
||||
*/
|
1
libs/deal-ticket/src/components/market-info/__generated__/index.ts
generated
Normal file
1
libs/deal-ticket/src/components/market-info/__generated__/index.ts
generated
Normal file
@ -0,0 +1 @@
|
||||
export * from './MarketInfoQuery';
|
3
libs/deal-ticket/src/components/market-info/index.ts
Normal file
3
libs/deal-ticket/src/components/market-info/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './__generated__';
|
||||
export * from './info-market-query';
|
||||
export * from './info-market';
|
@ -0,0 +1,110 @@
|
||||
import {
|
||||
t,
|
||||
addDecimalsFormatNumber,
|
||||
formatNumberPercentage,
|
||||
formatNumber,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
KeyValueTableRow,
|
||||
KeyValueTable,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import startCase from 'lodash/startCase';
|
||||
import type { ReactNode } from 'react';
|
||||
import { tooltipMapping } from './tooltip-mapping';
|
||||
|
||||
interface RowProps {
|
||||
field: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any;
|
||||
decimalPlaces?: number;
|
||||
asPercentage?: boolean;
|
||||
unformatted?: boolean;
|
||||
assetSymbol?: string;
|
||||
}
|
||||
|
||||
const Row = ({
|
||||
field,
|
||||
value,
|
||||
decimalPlaces,
|
||||
asPercentage,
|
||||
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}`;
|
||||
}
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
export interface MarketInfoTableProps {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: any;
|
||||
decimalPlaces?: number;
|
||||
asPercentage?: boolean;
|
||||
unformatted?: boolean;
|
||||
omits?: string[];
|
||||
link?: ReactNode;
|
||||
assetSymbol?: string;
|
||||
}
|
||||
|
||||
export const MarketInfoTable = ({
|
||||
data,
|
||||
decimalPlaces,
|
||||
asPercentage,
|
||||
unformatted,
|
||||
omits = ['__typename'],
|
||||
link,
|
||||
assetSymbol,
|
||||
}: MarketInfoTableProps) => {
|
||||
return (
|
||||
<>
|
||||
<KeyValueTable>
|
||||
{Object.entries(data)
|
||||
.filter(([key]) => !omits.includes(key))
|
||||
.map(([key, value]) => (
|
||||
<Row
|
||||
key={key}
|
||||
field={key}
|
||||
value={value}
|
||||
decimalPlaces={decimalPlaces}
|
||||
assetSymbol={assetSymbol}
|
||||
asPercentage={asPercentage}
|
||||
unformatted={unformatted || key.toLowerCase().includes('volume')}
|
||||
/>
|
||||
))}
|
||||
</KeyValueTable>
|
||||
{link}
|
||||
</>
|
||||
);
|
||||
};
|
@ -8,6 +8,7 @@ export const MARKET_INFO_QUERY = gql`
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
state
|
||||
tradingMode
|
||||
proposal {
|
||||
id
|
||||
rationale {
|
||||
@ -49,15 +50,18 @@ export const MARKET_INFO_QUERY = gql`
|
||||
id
|
||||
}
|
||||
markPrice
|
||||
indicativeVolume
|
||||
bestBidVolume
|
||||
bestOfferVolume
|
||||
bestStaticBidVolume
|
||||
bestStaticOfferVolume
|
||||
openInterest
|
||||
bestBidPrice
|
||||
bestOfferPrice
|
||||
trigger
|
||||
openInterest
|
||||
suppliedStake
|
||||
openInterest
|
||||
targetStake
|
||||
marketValueProxy
|
||||
priceMonitoringBounds {
|
||||
minValidPrice
|
||||
maxValidPrice
|
||||
@ -94,6 +98,7 @@ export const MARKET_INFO_QUERY = gql`
|
||||
id
|
||||
symbol
|
||||
name
|
||||
decimals
|
||||
}
|
||||
oracleSpecForSettlementPrice {
|
||||
id
|
@ -1,31 +1,23 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { ReactNode } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
formatLabel,
|
||||
formatNumber,
|
||||
formatNumberPercentage,
|
||||
t,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
AsyncRenderer,
|
||||
Splash,
|
||||
Accordion,
|
||||
Tooltip,
|
||||
Link,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import startCase from 'lodash/startCase';
|
||||
import { formatNumber, t } from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer, Splash, Accordion } from '@vegaprotocol/ui-toolkit';
|
||||
import pick from 'lodash/pick';
|
||||
import omit from 'lodash/omit';
|
||||
import type { MarketInfoQuery, MarketInfoQuery_market } from './';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { totalFees } from '@vegaprotocol/market-list';
|
||||
import { AccountType, Interval } from '@vegaprotocol/types';
|
||||
import {
|
||||
AccountType,
|
||||
Interval,
|
||||
MarketStateMapping,
|
||||
MarketTradingModeMapping,
|
||||
} from '@vegaprotocol/types';
|
||||
import { MARKET_INFO_QUERY } from './info-market-query';
|
||||
import type {
|
||||
MarketInfoQuery,
|
||||
MarketInfoQuery_market,
|
||||
MarketInfoQuery_market_candles,
|
||||
} from './__generated__/MarketInfoQuery';
|
||||
import { MarketInfoTable } from './info-key-value-table';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { generatePath } from 'react-router-dom';
|
||||
@ -42,11 +34,11 @@ export interface InfoProps {
|
||||
export const calcCandleVolume = (
|
||||
m: MarketInfoQuery_market
|
||||
): string | undefined => {
|
||||
return m?.candles
|
||||
?.reduce((acc, c) => {
|
||||
return m.candles
|
||||
?.reduce((acc: BigNumber, c: MarketInfoQuery_market_candles | null) => {
|
||||
return acc.plus(new BigNumber(c?.volume ?? 0));
|
||||
}, new BigNumber(0))
|
||||
.toString();
|
||||
}, new BigNumber(m.candles?.[0]?.volume ?? 0))
|
||||
?.toString();
|
||||
};
|
||||
|
||||
export interface MarketInfoContainerProps {
|
||||
@ -57,10 +49,16 @@ export const MarketInfoContainer = ({ marketId }: MarketInfoContainerProps) => {
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
return new Date(yesterday * 1000).toISOString();
|
||||
}, []);
|
||||
|
||||
const { data, loading, error } = useQuery(MARKET_INFO_QUERY, {
|
||||
variables: { marketId, interval: Interval.INTERVAL_I1H, since: yTimestamp },
|
||||
});
|
||||
const variables = useMemo(
|
||||
() => ({ marketId, since: yTimestamp, interval: Interval.INTERVAL_I1H }),
|
||||
[marketId, yTimestamp]
|
||||
);
|
||||
const { data, loading, error } = useQuery<MarketInfoQuery>(
|
||||
MARKET_INFO_QUERY,
|
||||
{
|
||||
variables,
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<AsyncRenderer<MarketInfoQuery> data={data} loading={loading} error={error}>
|
||||
@ -79,6 +77,8 @@ export const Info = ({ market }: InfoProps) => {
|
||||
const { VEGA_TOKEN_URL } = useEnvironment();
|
||||
const headerClassName = 'uppercase text-lg';
|
||||
const dayVolume = calcCandleVolume(market);
|
||||
const assetSymbol =
|
||||
market.tradableInstrument.instrument.product?.settlementAsset.symbol;
|
||||
const marketDataPanels = [
|
||||
{
|
||||
title: t('Current fees'),
|
||||
@ -137,28 +137,33 @@ export const Info = ({ market }: InfoProps) => {
|
||||
},
|
||||
...(market.accounts || [])
|
||||
.filter((a) => a.type === AccountType.ACCOUNT_TYPE_INSURANCE)
|
||||
.map((a, i) => ({
|
||||
.map((a) => ({
|
||||
title: t(`Insurance pool`),
|
||||
content: (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
balance: `${a.balance}
|
||||
${market.tradableInstrument.instrument.product?.settlementAsset.symbol}`,
|
||||
balance: a.balance,
|
||||
}}
|
||||
assetSymbol={assetSymbol}
|
||||
decimalPlaces={
|
||||
market.tradableInstrument.instrument.product.settlementAsset
|
||||
.decimals
|
||||
}
|
||||
/>
|
||||
),
|
||||
})),
|
||||
];
|
||||
|
||||
const keyDetails = pick(
|
||||
market,
|
||||
'name',
|
||||
'decimalPlaces',
|
||||
'positionDecimalPlaces',
|
||||
'tradingMode',
|
||||
'state',
|
||||
'id' as 'marketId'
|
||||
);
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const keyDetails = {
|
||||
...pick(
|
||||
market,
|
||||
'name',
|
||||
'decimalPlaces',
|
||||
'positionDecimalPlaces',
|
||||
'tradingMode'
|
||||
),
|
||||
state: MarketStateMapping[market.state],
|
||||
};
|
||||
const marketSpecPanels = [
|
||||
{
|
||||
title: t('Key details'),
|
||||
@ -166,10 +171,10 @@ export const Info = ({ market }: InfoProps) => {
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
...keyDetails,
|
||||
marketID: keyDetails.id,
|
||||
id: undefined,
|
||||
marketID: market.id,
|
||||
tradingMode:
|
||||
keyDetails.tradingMode && formatLabel(keyDetails.tradingMode),
|
||||
keyDetails.tradingMode &&
|
||||
MarketTradingModeMapping[keyDetails.tradingMode],
|
||||
}}
|
||||
/>
|
||||
),
|
||||
@ -251,6 +256,12 @@ export const Info = ({ market }: InfoProps) => {
|
||||
<MarketInfoTable data={trigger} decimalPlaces={market.decimalPlaces} />
|
||||
),
|
||||
})),
|
||||
...(market.data?.priceMonitoringBounds || []).map((trigger, i) => ({
|
||||
title: t(`Price monitoring bound ${i + 1}`),
|
||||
content: (
|
||||
<MarketInfoTable data={trigger} decimalPlaces={market.decimalPlaces} />
|
||||
),
|
||||
})),
|
||||
{
|
||||
title: t('Liquidity monitoring parameters'),
|
||||
content: (
|
||||
@ -263,6 +274,28 @@ export const Info = ({ market }: InfoProps) => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Liquidity'),
|
||||
content: (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
targetStake: market.data && market.data.targetStake,
|
||||
suppliedStake: market.data && market.data?.suppliedStake,
|
||||
marketValueProxy: market.data && market.data.marketValueProxy,
|
||||
}}
|
||||
decimalPlaces={
|
||||
market.tradableInstrument.instrument.product.settlementAsset
|
||||
.decimals
|
||||
}
|
||||
assetSymbol={assetSymbol}
|
||||
link={
|
||||
<ExternalLink href={`/liquidity/${market.id}`}>
|
||||
{t('View liquidity provision table')}
|
||||
</ExternalLink>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Oracle'),
|
||||
content: (
|
||||
@ -276,6 +309,13 @@ export const Info = ({ market }: InfoProps) => {
|
||||
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>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@ -323,172 +363,3 @@ export const Info = ({ market }: InfoProps) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const tooltipMapping: Record<string, ReactNode> = {
|
||||
makerFee: t(
|
||||
'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 market-maker fee pool for the market.'
|
||||
),
|
||||
infrastructureFee: t(
|
||||
'Fees paid to validators as a reward for running the infrastructure of the network.'
|
||||
),
|
||||
|
||||
markPrice: t(
|
||||
'A concept derived from traditional markets. It is a calculated value for the ‘current market price’ on a market.'
|
||||
),
|
||||
openInterest: t(
|
||||
'The volume of all open positions in a given market (the sum of the size of all positions greater than 0).'
|
||||
),
|
||||
indicativeVolume: t(
|
||||
'The volume at which all trades would occur if the auction was uncrossed now (when in auction mode).'
|
||||
),
|
||||
bestBidVolume: t(
|
||||
'The aggregated volume being bid at the best bid price on the market.'
|
||||
),
|
||||
bestOfferVolume: t(
|
||||
'The aggregated volume being offered at the best offer price on the market.'
|
||||
),
|
||||
bestStaticBidVolume: t(
|
||||
'The aggregated volume being bid at the best static bid price on the market.'
|
||||
),
|
||||
bestStaticOfferVolume: t(
|
||||
'The aggregated volume being offered at the best static offer price on the market.'
|
||||
),
|
||||
|
||||
decimalPlaces: t('The smallest price increment on the book.'),
|
||||
positionDecimalPlaces: t(
|
||||
'How big the smallest order / position on the market can be.'
|
||||
),
|
||||
tradingMode: t('The trading mode the market is currently running.'),
|
||||
state: t('The current state of the market'),
|
||||
|
||||
base: t(
|
||||
'The first currency in a pair for a currency-based derivatives market.'
|
||||
),
|
||||
quote: t(
|
||||
'The second currency in a pair for a currency-based derivatives market.'
|
||||
),
|
||||
class: t(
|
||||
'The classification of the product. Examples: shares, commodities, crypto, FX.'
|
||||
),
|
||||
sector: t(
|
||||
'Data about the sector. Example: "automotive" for a market based on value of Tesla shares.'
|
||||
),
|
||||
|
||||
short: t(
|
||||
'A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.'
|
||||
),
|
||||
long: t(
|
||||
'A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.'
|
||||
),
|
||||
|
||||
tau: (
|
||||
<span>
|
||||
{t('Projection horizon measured as a year fraction used in ')}
|
||||
<Link
|
||||
href="https://vega.xyz/papers/margins-and-credit-risk.pdf#page=7"
|
||||
target="__blank"
|
||||
>
|
||||
{t('Expected Shortfall')}
|
||||
</Link>
|
||||
{t(' calculation when obtaining Risk Factor Long and Risk Factor Short')}
|
||||
</span>
|
||||
),
|
||||
riskAversionParameter: (
|
||||
<span>
|
||||
{t('Probability level used in ')}
|
||||
<Link
|
||||
href="https://vega.xyz/papers/margins-and-credit-risk.pdf#page=7"
|
||||
target="__blank"
|
||||
>
|
||||
{t('Expected Shortfall')}
|
||||
</Link>
|
||||
{t(' calculation when obtaining Risk Factor Long and Risk Factor Short')}
|
||||
</span>
|
||||
),
|
||||
|
||||
horizonSecs: t('Time horizon of the price projection in seconds.'),
|
||||
probability: t(
|
||||
'Probability level for price projection, e.g. value of 0.95 will result in a price range such that over the specified projection horizon, the prices observed in the market should be in that range 95% of the time.'
|
||||
),
|
||||
auctionExtensionSecs: t(
|
||||
'Auction extension duration in seconds, should the price breach its theoretical level over the specified horizon at the specified probability level.'
|
||||
),
|
||||
|
||||
triggeringRatio: t('The triggering ratio for entering liquidity auction.'),
|
||||
timeWindow: t('The length of time over which open interest is measured.'),
|
||||
scalingFactor: t(
|
||||
'The scaling between the liquidity demand estimate, based on open interest and target stake.'
|
||||
),
|
||||
};
|
||||
|
||||
interface RowProps {
|
||||
field: string;
|
||||
value: any;
|
||||
decimalPlaces?: number;
|
||||
asPercentage?: boolean;
|
||||
unformatted?: boolean;
|
||||
}
|
||||
|
||||
const Row = ({
|
||||
field,
|
||||
value,
|
||||
decimalPlaces,
|
||||
asPercentage,
|
||||
unformatted,
|
||||
}: RowProps) => {
|
||||
const isNumber = typeof value === 'number' || !isNaN(Number(value));
|
||||
const isPrimitive = typeof value === 'string' || isNumber;
|
||||
if (isPrimitive) {
|
||||
return (
|
||||
<KeyValueTableRow key={field} inline={isPrimitive} noBorder={true}>
|
||||
<Tooltip description={tooltipMapping[field]} align="start">
|
||||
<div tabIndex={-1}>{startCase(t(field))}</div>
|
||||
</Tooltip>
|
||||
<span style={{ wordBreak: 'break-word' }}>
|
||||
{isNumber && !unformatted
|
||||
? decimalPlaces
|
||||
? addDecimalsFormatNumber(value, decimalPlaces)
|
||||
: asPercentage
|
||||
? formatNumberPercentage(new BigNumber(value * 100))
|
||||
: formatNumber(Number(value))
|
||||
: value}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export interface MarketInfoTableProps {
|
||||
data: any;
|
||||
decimalPlaces?: number;
|
||||
asPercentage?: boolean;
|
||||
unformatted?: boolean;
|
||||
omits?: string[];
|
||||
}
|
||||
|
||||
export const MarketInfoTable = ({
|
||||
data,
|
||||
decimalPlaces,
|
||||
asPercentage,
|
||||
unformatted,
|
||||
omits = ['__typename'],
|
||||
}: MarketInfoTableProps) => {
|
||||
return (
|
||||
<KeyValueTable>
|
||||
{Object.entries(omit(data, ...omits) || []).map(([key, value]) => (
|
||||
<Row
|
||||
key={key}
|
||||
field={key}
|
||||
value={value}
|
||||
decimalPlaces={decimalPlaces}
|
||||
asPercentage={asPercentage}
|
||||
unformatted={unformatted || key.toLowerCase().includes('volume')}
|
||||
/>
|
||||
))}
|
||||
</KeyValueTable>
|
||||
);
|
||||
};
|
103
libs/deal-ticket/src/components/market-info/tooltip-mapping.tsx
Normal file
103
libs/deal-ticket/src/components/market-info/tooltip-mapping.tsx
Normal file
@ -0,0 +1,103 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export const tooltipMapping: Record<string, ReactNode> = {
|
||||
makerFee: t(
|
||||
'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.'
|
||||
),
|
||||
infrastructureFee: t(
|
||||
'Fees paid to validators as a reward for running the infrastructure of the network.'
|
||||
),
|
||||
|
||||
markPrice: t(
|
||||
'A concept derived from traditional markets. It is a calculated value for the ‘current market price’ on a market.'
|
||||
),
|
||||
openInterest: t(
|
||||
'The volume of all open positions in a given market (the sum of the size of all positions greater than 0).'
|
||||
),
|
||||
indicativeVolume: t(
|
||||
'The volume at which all trades would occur if the auction was uncrossed now (when in auction mode).'
|
||||
),
|
||||
bestBidVolume: t(
|
||||
'The aggregated volume being bid at the best bid price on the market.'
|
||||
),
|
||||
bestOfferVolume: t(
|
||||
'The aggregated volume being offered at the best offer price on the market.'
|
||||
),
|
||||
bestStaticBidVolume: t(
|
||||
'The aggregated volume being bid at the best static bid price on the market.'
|
||||
),
|
||||
bestStaticOfferVolume: t(
|
||||
'The aggregated volume being offered at the best static offer price on the market.'
|
||||
),
|
||||
|
||||
decimalPlaces: t('The smallest price increment on the book.'),
|
||||
positionDecimalPlaces: t(
|
||||
'How big the smallest order / position on the market can be.'
|
||||
),
|
||||
tradingMode: t('The trading mode the market is currently running.'),
|
||||
state: t('The current state of the market'),
|
||||
|
||||
base: t(
|
||||
'The first currency in a pair for a currency-based derivatives market.'
|
||||
),
|
||||
quote: t(
|
||||
'The second currency in a pair for a currency-based derivatives market.'
|
||||
),
|
||||
class: t(
|
||||
'The classification of the product. Examples: shares, commodities, crypto, FX.'
|
||||
),
|
||||
sector: t(
|
||||
'Data about the sector. Example: "automotive" for a market based on value of Tesla shares.'
|
||||
),
|
||||
|
||||
short: t(
|
||||
'A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.'
|
||||
),
|
||||
long: t(
|
||||
'A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.'
|
||||
),
|
||||
|
||||
tau: (
|
||||
<span>
|
||||
{t('Projection horizon measured as a year fraction used in ')}
|
||||
<Link
|
||||
href="https://vega.xyz/papers/margins-and-credit-risk.pdf#page=7"
|
||||
target="__blank"
|
||||
>
|
||||
{t('Expected Shortfall')}
|
||||
</Link>
|
||||
{t(' calculation when obtaining Risk Factor Long and Risk Factor Short')}
|
||||
</span>
|
||||
),
|
||||
riskAversionParameter: (
|
||||
<span>
|
||||
{t('Probability level used in ')}
|
||||
<Link
|
||||
href="https://vega.xyz/papers/margins-and-credit-risk.pdf#page=7"
|
||||
target="__blank"
|
||||
>
|
||||
{t('Expected Shortfall')}
|
||||
</Link>
|
||||
{t(' calculation when obtaining Risk Factor Long and Risk Factor Short')}
|
||||
</span>
|
||||
),
|
||||
|
||||
horizonSecs: t('Time horizon of the price projection in seconds.'),
|
||||
probability: t(
|
||||
'Probability level for price projection, e.g. value of 0.95 will result in a price range such that over the specified projection horizon, the prices observed in the market should be in that range 95% of the time.'
|
||||
),
|
||||
auctionExtensionSecs: t(
|
||||
'Auction extension duration in seconds, should the price breach its theoretical level over the specified horizon at the specified probability level.'
|
||||
),
|
||||
|
||||
triggeringRatio: t('The triggering ratio for entering liquidity auction.'),
|
||||
timeWindow: t('The length of time over which open interest is measured.'),
|
||||
scalingFactor: t(
|
||||
'The scaling between the liquidity demand estimate, based on open interest and target stake.'
|
||||
),
|
||||
};
|
4
libs/liquidity/.babelrc
Normal file
4
libs/liquidity/.babelrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"presets": ["@nrwl/next/babel"],
|
||||
"plugins": []
|
||||
}
|
18
libs/liquidity/.eslintrc.json
Normal file
18
libs/liquidity/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*", "__generated__"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
7
libs/liquidity/README.md
Normal file
7
libs/liquidity/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Liquidity
|
||||
|
||||
This library contains liquidity provision data providers and view containers.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test liquidity` to execute the unit tests via [Jest](https://jestjs.io).
|
15
libs/liquidity/jest.config.js
Normal file
15
libs/liquidity/jest.config.js
Normal file
@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
displayName: 'positions',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/positions',
|
||||
setupFilesAfterEnv: ['./src/setup-tests.ts'],
|
||||
};
|
4
libs/liquidity/package.json
Normal file
4
libs/liquidity/package.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "@vegaprotocol/liquidity",
|
||||
"version": "0.0.1"
|
||||
}
|
43
libs/liquidity/project.json
Normal file
43
libs/liquidity/project.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"root": "libs/liquidity",
|
||||
"sourceRoot": "libs/liquidity/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/web:rollup",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/liquidity",
|
||||
"tsConfig": "libs/liquidity/tsconfig.lib.json",
|
||||
"project": "libs/liquidity/package.json",
|
||||
"entryFile": "libs/liquidity/src/index.ts",
|
||||
"external": ["react/jsx-runtime"],
|
||||
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
|
||||
"compiler": "babel",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "libs/liquidity/README.md",
|
||||
"input": ".",
|
||||
"output": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["libs/liquidity/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/libs/liquidity"],
|
||||
"options": {
|
||||
"jestConfig": "libs/liquidity/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
libs/liquidity/src/index.ts
Normal file
1
libs/liquidity/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './lib';
|
252
libs/liquidity/src/lib/__generated__/MarketLiquidity.ts
generated
Normal file
252
libs/liquidity/src/lib/__generated__/MarketLiquidity.ts
generated
Normal file
@ -0,0 +1,252 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AccountType, LiquidityProvisionStatus } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: MarketLiquidity
|
||||
// ====================================================
|
||||
|
||||
export interface MarketLiquidity_market_liquidityProvisionsConnection_edges_node_party_accountsConnection_edges_node {
|
||||
__typename: "Account";
|
||||
/**
|
||||
* Account type (General, Margin, etc)
|
||||
*/
|
||||
type: AccountType;
|
||||
/**
|
||||
* Balance as string - current account balance (approx. as balances can be updated several times per second)
|
||||
*/
|
||||
balance: string;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_liquidityProvisionsConnection_edges_node_party_accountsConnection_edges {
|
||||
__typename: "AccountEdge";
|
||||
/**
|
||||
* The account
|
||||
*/
|
||||
node: MarketLiquidity_market_liquidityProvisionsConnection_edges_node_party_accountsConnection_edges_node;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_liquidityProvisionsConnection_edges_node_party_accountsConnection {
|
||||
__typename: "AccountsConnection";
|
||||
/**
|
||||
* List of accounts available for the connection
|
||||
*/
|
||||
edges: (MarketLiquidity_market_liquidityProvisionsConnection_edges_node_party_accountsConnection_edges | null)[] | null;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_liquidityProvisionsConnection_edges_node_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Collateral accounts relating to a party
|
||||
*/
|
||||
accountsConnection: MarketLiquidity_market_liquidityProvisionsConnection_edges_node_party_accountsConnection;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_liquidityProvisionsConnection_edges_node {
|
||||
__typename: "LiquidityProvision";
|
||||
/**
|
||||
* Unique identifier for the order (set by the system after consensus)
|
||||
*/
|
||||
id: string | null;
|
||||
/**
|
||||
* The Id of the party making this commitment
|
||||
*/
|
||||
party: MarketLiquidity_market_liquidityProvisionsConnection_edges_node_party;
|
||||
/**
|
||||
* When the liquidity provision was initially created (formatted RFC3339)
|
||||
*/
|
||||
createdAt: string;
|
||||
/**
|
||||
* RFC3339Nano time of when the liquidity provision was updated
|
||||
*/
|
||||
updatedAt: string | null;
|
||||
/**
|
||||
* Specified as a unit-less number that represents the amount of settlement asset of the market.
|
||||
*/
|
||||
commitmentAmount: string;
|
||||
/**
|
||||
* Nominated liquidity fee factor, which is an input to the calculation of maker fees on the market, as per setting fees and rewarding liquidity providers.
|
||||
*/
|
||||
fee: string;
|
||||
/**
|
||||
* The current status of this liquidity provision
|
||||
*/
|
||||
status: LiquidityProvisionStatus;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_liquidityProvisionsConnection_edges {
|
||||
__typename: "LiquidityProvisionsEdge";
|
||||
node: MarketLiquidity_market_liquidityProvisionsConnection_edges_node;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_liquidityProvisionsConnection {
|
||||
__typename: "LiquidityProvisionsConnection";
|
||||
edges: (MarketLiquidity_market_liquidityProvisionsConnection_edges | null)[] | null;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_tradableInstrument_instrument_product_settlementAsset {
|
||||
__typename: "Asset";
|
||||
/**
|
||||
* The ID of the asset
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The symbol of the asset (e.g: GBP)
|
||||
*/
|
||||
symbol: 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 MarketLiquidity_market_tradableInstrument_instrument_product {
|
||||
__typename: "Future";
|
||||
/**
|
||||
* The name of the asset (string)
|
||||
*/
|
||||
settlementAsset: MarketLiquidity_market_tradableInstrument_instrument_product_settlementAsset;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_tradableInstrument_instrument {
|
||||
__typename: "Instrument";
|
||||
/**
|
||||
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
|
||||
*/
|
||||
code: string;
|
||||
/**
|
||||
* A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
|
||||
*/
|
||||
product: MarketLiquidity_market_tradableInstrument_instrument_product;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_tradableInstrument {
|
||||
__typename: "TradableInstrument";
|
||||
/**
|
||||
* An instance of, or reference to, a fully specified instrument.
|
||||
*/
|
||||
instrument: MarketLiquidity_market_tradableInstrument_instrument;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_data_market {
|
||||
__typename: "Market";
|
||||
/**
|
||||
* Market ID
|
||||
*/
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_data_liquidityProviderFeeShare_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
*/
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_data_liquidityProviderFeeShare {
|
||||
__typename: "LiquidityProviderFeeShare";
|
||||
/**
|
||||
* The liquidity provider party ID
|
||||
*/
|
||||
party: MarketLiquidity_market_data_liquidityProviderFeeShare_party;
|
||||
/**
|
||||
* The share owned by this liquidity provider (float)
|
||||
*/
|
||||
equityLikeShare: string;
|
||||
/**
|
||||
* The average entry valuation of the liquidity provider for the market
|
||||
*/
|
||||
averageEntryValuation: string;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market_data {
|
||||
__typename: "MarketData";
|
||||
/**
|
||||
* market ID of the associated mark price
|
||||
*/
|
||||
market: MarketLiquidity_market_data_market;
|
||||
/**
|
||||
* the supplied stake for the market
|
||||
*/
|
||||
suppliedStake: string | null;
|
||||
/**
|
||||
* the sum of the size of all positions greater than 0.
|
||||
*/
|
||||
openInterest: string;
|
||||
/**
|
||||
* the amount of stake targeted for this market
|
||||
*/
|
||||
targetStake: string | null;
|
||||
/**
|
||||
* the market value proxy
|
||||
*/
|
||||
marketValueProxy: string;
|
||||
/**
|
||||
* the equity like share of liquidity fee for each liquidity provider
|
||||
*/
|
||||
liquidityProviderFeeShare: MarketLiquidity_market_data_liquidityProviderFeeShare[] | null;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity_market {
|
||||
__typename: "Market";
|
||||
/**
|
||||
* Market ID
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* 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 )
|
||||
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
||||
*/
|
||||
decimalPlaces: number;
|
||||
/**
|
||||
* positionDecimalPlaces indicates the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
|
||||
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
|
||||
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
|
||||
* This sets how big the smallest order / position on the market can be.
|
||||
*/
|
||||
positionDecimalPlaces: number;
|
||||
/**
|
||||
* The list of the liquidity provision commitments for this market
|
||||
*/
|
||||
liquidityProvisionsConnection: MarketLiquidity_market_liquidityProvisionsConnection;
|
||||
/**
|
||||
* An instance of, or reference to, a tradable instrument.
|
||||
*/
|
||||
tradableInstrument: MarketLiquidity_market_tradableInstrument;
|
||||
/**
|
||||
* marketData for the given market
|
||||
*/
|
||||
data: MarketLiquidity_market_data | null;
|
||||
}
|
||||
|
||||
export interface MarketLiquidity {
|
||||
/**
|
||||
* An instrument that is trading on the Vega network
|
||||
*/
|
||||
market: MarketLiquidity_market | null;
|
||||
}
|
||||
|
||||
export interface MarketLiquidityVariables {
|
||||
marketId: string;
|
||||
partyId?: string | null;
|
||||
}
|
1
libs/liquidity/src/lib/__generated__/index.ts
generated
Normal file
1
libs/liquidity/src/lib/__generated__/index.ts
generated
Normal file
@ -0,0 +1 @@
|
||||
export * from './MarketLiquidity';
|
3
libs/liquidity/src/lib/index.ts
Normal file
3
libs/liquidity/src/lib/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './__generated__';
|
||||
export * from './liquidity-data-provider';
|
||||
export * from './liquidity-table';
|
172
libs/liquidity/src/lib/liquidity-data-provider.ts
Normal file
172
libs/liquidity/src/lib/liquidity-data-provider.ts
Normal file
@ -0,0 +1,172 @@
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import type { LiquidityProvisionStatus } from '@vegaprotocol/types';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
import { useNetworkParam } from '@vegaprotocol/react-helpers';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type {
|
||||
MarketLiquidity,
|
||||
MarketLiquidity_market_data_liquidityProviderFeeShare,
|
||||
} from './__generated__';
|
||||
|
||||
const SISKA_NETWORK_PARAMETER = 'market.liquidity.stakeToCcySiskas';
|
||||
|
||||
const MARKET_LIQUIDITY_QUERY = gql`
|
||||
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
|
||||
product {
|
||||
... on Future {
|
||||
settlementAsset {
|
||||
id
|
||||
symbol
|
||||
decimals
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
data {
|
||||
market {
|
||||
id
|
||||
}
|
||||
suppliedStake
|
||||
openInterest
|
||||
targetStake
|
||||
marketValueProxy
|
||||
liquidityProviderFeeShare {
|
||||
party {
|
||||
id
|
||||
}
|
||||
equityLikeShare
|
||||
averageEntryValuation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export interface LiquidityProvision {
|
||||
party: string;
|
||||
commitmentAmount: string | undefined;
|
||||
fee: string | undefined;
|
||||
equityLikeShare: string;
|
||||
averageEntryValuation: string;
|
||||
obligation: string | null;
|
||||
supplied: string | null;
|
||||
status?: LiquidityProvisionStatus;
|
||||
createdAt: string | undefined;
|
||||
updatedAt: string | null | undefined;
|
||||
}
|
||||
|
||||
export interface LiquidityData {
|
||||
liquidityProviders: LiquidityProvision[];
|
||||
suppliedStake?: string | null;
|
||||
targetStake?: string | null;
|
||||
code?: string;
|
||||
symbol?: string;
|
||||
decimalPlaces?: number;
|
||||
positionDecimalPlaces?: number;
|
||||
assetDecimalPlaces?: number;
|
||||
}
|
||||
|
||||
export const useLiquidityProvision = ({
|
||||
marketId,
|
||||
partyId,
|
||||
}: {
|
||||
partyId?: string;
|
||||
marketId?: string;
|
||||
}) => {
|
||||
const { data: stakeToCcySiskas } = useNetworkParam(SISKA_NETWORK_PARAMETER);
|
||||
const stakeToCcySiska = stakeToCcySiskas && stakeToCcySiskas[0];
|
||||
const { data, loading, error } = useQuery<MarketLiquidity>(
|
||||
MARKET_LIQUIDITY_QUERY,
|
||||
{
|
||||
variables: { marketId },
|
||||
}
|
||||
);
|
||||
const liquidityProviders = (
|
||||
data?.market?.data?.liquidityProviderFeeShare || []
|
||||
)
|
||||
?.filter(
|
||||
(p: MarketLiquidity_market_data_liquidityProviderFeeShare) =>
|
||||
!partyId || p.party.id === partyId
|
||||
) // if partyId is provided, filter out other parties
|
||||
.map((provider: MarketLiquidity_market_data_liquidityProviderFeeShare) => {
|
||||
const liquidityProvisionConnection =
|
||||
data?.market?.liquidityProvisionsConnection.edges?.find(
|
||||
(e) => e?.node.party.id === provider.party.id
|
||||
);
|
||||
const balance =
|
||||
liquidityProvisionConnection?.node?.party.accountsConnection.edges?.reduce(
|
||||
(acc, e) => {
|
||||
return e?.node.type === AccountType.ACCOUNT_TYPE_BOND // just an extra check to make sure we only use bond accounts
|
||||
? acc.plus(new BigNumber(e?.node.balance ?? 0))
|
||||
: acc;
|
||||
},
|
||||
new BigNumber(0)
|
||||
);
|
||||
const obligation =
|
||||
stakeToCcySiska &&
|
||||
new BigNumber(stakeToCcySiska)
|
||||
.times(liquidityProvisionConnection?.node?.commitmentAmount ?? 1)
|
||||
.toString();
|
||||
const supplied =
|
||||
stakeToCcySiska &&
|
||||
new BigNumber(stakeToCcySiska).times(balance ?? 1).toString();
|
||||
return {
|
||||
party: provider.party.id,
|
||||
createdAt: liquidityProvisionConnection?.node?.createdAt,
|
||||
updatedAt: liquidityProvisionConnection?.node?.updatedAt,
|
||||
commitmentAmount: liquidityProvisionConnection?.node?.commitmentAmount,
|
||||
fee: liquidityProvisionConnection?.node?.fee,
|
||||
status: liquidityProvisionConnection?.node?.status,
|
||||
equityLikeShare: provider.equityLikeShare,
|
||||
averageEntryValuation: provider.averageEntryValuation,
|
||||
obligation,
|
||||
supplied,
|
||||
};
|
||||
});
|
||||
const liquidityData: LiquidityData = {
|
||||
liquidityProviders,
|
||||
suppliedStake: data?.market?.data?.suppliedStake,
|
||||
targetStake: data?.market?.data?.targetStake,
|
||||
decimalPlaces: data?.market?.decimalPlaces,
|
||||
positionDecimalPlaces: data?.market?.positionDecimalPlaces,
|
||||
code: data?.market?.tradableInstrument.instrument.code,
|
||||
assetDecimalPlaces:
|
||||
data?.market?.tradableInstrument.instrument.product.settlementAsset
|
||||
.decimals,
|
||||
symbol:
|
||||
data?.market?.tradableInstrument.instrument.product.settlementAsset
|
||||
.symbol,
|
||||
};
|
||||
return { data: liquidityData, loading, error };
|
||||
};
|
53
libs/liquidity/src/lib/liquidity-table.spec.tsx
Normal file
53
libs/liquidity/src/lib/liquidity-table.spec.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import LiquidityTable from './liquidity-table';
|
||||
import { act, render, screen, waitFor } from '@testing-library/react';
|
||||
import { LiquidityProvisionStatus } from '@vegaprotocol/types';
|
||||
import type { LiquidityProvision } from './liquidity-data-provider';
|
||||
|
||||
const singleRow: LiquidityProvision = {
|
||||
party: 'a3f762f0a6e998e1d0c6e73017a13ec8a22386c30f7f64a1bdca47330bc592dd',
|
||||
createdAt: '2022-08-19T17:18:36.257028Z',
|
||||
updatedAt: '2022-08-19T17:18:36.257028Z',
|
||||
commitmentAmount: '56298653179',
|
||||
fee: '0.001',
|
||||
status: LiquidityProvisionStatus.STATUS_ACTIVE,
|
||||
equityLikeShare: '0.5',
|
||||
averageEntryValuation: '0.5',
|
||||
supplied: '67895',
|
||||
obligation: '56785',
|
||||
};
|
||||
|
||||
const singleRowData = [singleRow];
|
||||
|
||||
describe('LiquidityTable', () => {
|
||||
it('should render successfully', async () => {
|
||||
await act(async () => {
|
||||
const { baseElement } = render(<LiquidityTable data={[]} />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render correct columns', async () => {
|
||||
act(async () => {
|
||||
render(<LiquidityTable data={singleRowData} />);
|
||||
await waitFor(async () => {
|
||||
const headers = await screen.getAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(9);
|
||||
expect(
|
||||
headers.map((h) =>
|
||||
h.querySelector('[ref="eText"]')?.textContent?.trim()
|
||||
)
|
||||
).toEqual([
|
||||
'Party',
|
||||
'Average entry valuation',
|
||||
'Updated',
|
||||
'Created',
|
||||
'Supplied (siskas)',
|
||||
'Obligation (siskas)',
|
||||
'Share',
|
||||
'Fee',
|
||||
'Status',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
117
libs/liquidity/src/lib/liquidity-table.tsx
Normal file
117
libs/liquidity/src/lib/liquidity-table.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import { forwardRef } from 'react';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
formatNumberPercentage,
|
||||
getDateTimeFormat,
|
||||
t,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import type { LiquidityProvision } from './liquidity-data-provider';
|
||||
import type { ValueFormatterParams } from 'ag-grid-community';
|
||||
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) || '-';
|
||||
};
|
||||
|
||||
const dateValueFormatter = ({ value }: { value?: string | null }) => {
|
||||
if (!value) {
|
||||
return '-';
|
||||
}
|
||||
return getDateTimeFormat().format(new Date(value));
|
||||
};
|
||||
|
||||
export interface LiquidityTableProps {
|
||||
data: LiquidityProvision[];
|
||||
}
|
||||
|
||||
export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
|
||||
(props, ref) => {
|
||||
return (
|
||||
<AgGrid
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
overlayNoRowsTemplate="No liquidity provisions"
|
||||
getRowId={({ data }) => data.party}
|
||||
rowHeight={34}
|
||||
ref={ref}
|
||||
defaultColDef={{
|
||||
flex: 1,
|
||||
resizable: true,
|
||||
minWidth: 100,
|
||||
}}
|
||||
rowData={props.data}
|
||||
{...props}
|
||||
>
|
||||
<AgGridColumn headerName={t('Party')} field="party" />
|
||||
<AgGridColumn
|
||||
headerName={t('Commitment')}
|
||||
field="commitmentAmount"
|
||||
type="rightAligned"
|
||||
valueFormatter={assetDecimalsFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Share')}
|
||||
field="equityLikeShare"
|
||||
type="rightAligned"
|
||||
valueFormatter={percentageFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Fee')}
|
||||
field="fee"
|
||||
type="rightAligned"
|
||||
valueFormatter={percentageFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Average entry valuation')}
|
||||
field="averageEntryValuation"
|
||||
type="rightAligned"
|
||||
valueFormatter={assetDecimalsFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Obligation (siskas)')}
|
||||
field="obligation"
|
||||
type="rightAligned"
|
||||
valueFormatter={assetDecimalsFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Supplied (siskas)')}
|
||||
field="supplied"
|
||||
type="rightAligned"
|
||||
valueFormatter={assetDecimalsFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Status')}
|
||||
field="status"
|
||||
valueFormatter={({ value }: { value: LiquidityProvisionStatus }) => {
|
||||
if (!value) return value;
|
||||
return LiquidityProvisionStatusMapping[value];
|
||||
}}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Created')}
|
||||
field="createdAt"
|
||||
type="rightAligned"
|
||||
valueFormatter={dateValueFormatter}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Updated')}
|
||||
field="updatedAt"
|
||||
type="rightAligned"
|
||||
valueFormatter={dateValueFormatter}
|
||||
/>
|
||||
</AgGrid>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default LiquidityTable;
|
1
libs/liquidity/src/setup-tests.ts
Normal file
1
libs/liquidity/src/setup-tests.ts
Normal file
@ -0,0 +1 @@
|
||||
import '@testing-library/jest-dom';
|
25
libs/liquidity/tsconfig.json
Normal file
25
libs/liquidity/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"
|
||||
}
|
||||
]
|
||||
}
|
22
libs/liquidity/tsconfig.lib.json
Normal file
22
libs/liquidity/tsconfig.lib.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"files": [
|
||||
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||
"../../node_modules/@nrwl/next/typings/image.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx"
|
||||
],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
20
libs/liquidity/tsconfig.spec.json
Normal file
20
libs/liquidity/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",
|
||||
"../react-helpers/src/lib/grid-cells/summary-row.spec.ts"
|
||||
]
|
||||
}
|
@ -100,8 +100,7 @@ export const SelectMarketPopover = ({
|
||||
marketName: string;
|
||||
onSelect: (id: string) => void;
|
||||
}) => {
|
||||
const triggerClasses =
|
||||
'sm:text-lg md:text-xl lg:text-2xl flex items-center gap-4 whitespace-nowrap';
|
||||
const triggerClasses = 'flex items-center gap-4 whitespace-nowrap';
|
||||
const { keypair } = useVegaWallet();
|
||||
const [open, setOpen] = useState(false);
|
||||
const { data, loading: marketsLoading } = useMarketList();
|
||||
|
@ -83,7 +83,7 @@ describe('OrderFeedback', () => {
|
||||
'1.00'
|
||||
);
|
||||
expect(screen.getByText('Size').nextElementSibling).toHaveTextContent(
|
||||
`+ 200`
|
||||
`+200`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,20 +1,15 @@
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { OrderEvent_busEvents_event_Order } from '../../order-hooks/__generated__/OrderEvent';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
t,
|
||||
positiveClassNames,
|
||||
negativeClassNames,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { addDecimalsFormatNumber, Size, t } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
OrderRejectionReasonMapping,
|
||||
OrderStatus,
|
||||
OrderStatusMapping,
|
||||
OrderTimeInForceMapping,
|
||||
OrderType,
|
||||
Side,
|
||||
} from '@vegaprotocol/types';
|
||||
import type { VegaTxState } from '@vegaprotocol/wallet';
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export interface OrderFeedbackProps {
|
||||
transaction: VegaTxState;
|
||||
@ -51,20 +46,12 @@ export const OrderFeedback = ({ transaction, order }: OrderFeedbackProps) => {
|
||||
)}
|
||||
<div>
|
||||
<p className={labelClass}>{t(`Size`)}</p>
|
||||
<p
|
||||
className={
|
||||
order.side === Side.SIDE_BUY
|
||||
? positiveClassNames
|
||||
: negativeClassNames
|
||||
}
|
||||
>
|
||||
{`${
|
||||
order.side === Side.SIDE_BUY ? '+' : '-'
|
||||
} ${addDecimalsFormatNumber(
|
||||
order.size,
|
||||
order.market?.positionDecimalPlaces ?? 0
|
||||
)}
|
||||
`}
|
||||
<p>
|
||||
<Size
|
||||
value={order.size}
|
||||
side={order.side}
|
||||
positionDecimalPlaces={order.market.positionDecimalPlaces}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,8 +59,7 @@ export const OrderFeedback = ({ transaction, order }: OrderFeedbackProps) => {
|
||||
{transaction.txHash && (
|
||||
<div>
|
||||
<p className={labelClass}>{t('Transaction')}</p>
|
||||
<a
|
||||
className="underline"
|
||||
<Link
|
||||
style={{ wordBreak: 'break-word' }}
|
||||
data-testid="tx-block-explorer"
|
||||
href={`${VEGA_EXPLORER_URL}/txs/0x${transaction.txHash}`}
|
||||
@ -81,7 +67,7 @@ export const OrderFeedback = ({ transaction, order }: OrderFeedbackProps) => {
|
||||
rel="noreferrer"
|
||||
>
|
||||
{transaction.txHash}
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -2,8 +2,9 @@ import {
|
||||
t,
|
||||
addDecimalsFormatNumber,
|
||||
toDecimal,
|
||||
Size,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { OrderType, Side } from '@vegaprotocol/types';
|
||||
import { OrderType } from '@vegaprotocol/types';
|
||||
import {
|
||||
FormGroup,
|
||||
Input,
|
||||
@ -64,16 +65,15 @@ export const OrderEditDialog = ({
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<p className={headerClassName}>{t(`Remaining size`)}</p>
|
||||
<p
|
||||
className={
|
||||
order.side === Side.SIDE_BUY
|
||||
? 'text-dark-green dark:text-vega-green'
|
||||
: 'text-red dark:text-vega-red'
|
||||
<p className={headerClassName}>{t(`Size`)}</p>
|
||||
<p>
|
||||
{
|
||||
<Size
|
||||
value={order.size}
|
||||
side={order.side}
|
||||
positionDecimalPlaces={order.market.positionDecimalPlaces}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{order.side === Side.SIDE_BUY ? '+' : '-'}
|
||||
{order.size}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"ignorePatterns": ["!**/*", "__generated__"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
|
27
libs/react-helpers/src/hooks/__generated__/NetworkParams.ts
generated
Normal file
27
libs/react-helpers/src/hooks/__generated__/NetworkParams.ts
generated
Normal file
@ -0,0 +1,27 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: NetworkParams
|
||||
// ====================================================
|
||||
|
||||
export interface NetworkParams_networkParameters {
|
||||
__typename: "NetworkParameter";
|
||||
/**
|
||||
* The name of the network parameter
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* The value of the network parameter
|
||||
*/
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface NetworkParams {
|
||||
/**
|
||||
* return the full list of network parameters
|
||||
*/
|
||||
networkParameters: NetworkParams_networkParameters[] | null;
|
||||
}
|
1
libs/react-helpers/src/hooks/__generated__/index.ts
generated
Normal file
1
libs/react-helpers/src/hooks/__generated__/index.ts
generated
Normal file
@ -0,0 +1 @@
|
||||
export * from './NetworkParams';
|
@ -1,7 +1,8 @@
|
||||
export * from './use-apply-grid-transaction';
|
||||
export * from './use-data-provider';
|
||||
export * from './use-theme-switcher';
|
||||
export * from './use-fetch';
|
||||
export * from './use-resize';
|
||||
export * from './use-network-params';
|
||||
export * from './use-outside-click';
|
||||
export * from './use-resize';
|
||||
export * from './use-screen-dimensions';
|
||||
export * from './use-theme-switcher';
|
||||
|
37
libs/react-helpers/src/hooks/use-network-params.ts
Normal file
37
libs/react-helpers/src/hooks/use-network-params.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import type { NetworkParams } from './__generated__/NetworkParams';
|
||||
|
||||
export const NETWORK_PARAMETERS_QUERY = gql`
|
||||
query NetworkParams {
|
||||
networkParameters {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const useNetworkParam = (param: string) => {
|
||||
const { data, loading, error } = useQuery<NetworkParams, never>(
|
||||
NETWORK_PARAMETERS_QUERY
|
||||
);
|
||||
const foundParams = data?.networkParameters?.filter((p) => param === p.key);
|
||||
return {
|
||||
data: foundParams ? foundParams.map((f) => f.value) : null,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export const useNetworkParams = (params: string[]) => {
|
||||
const { data, loading, error } = useQuery<NetworkParams, never>(
|
||||
NETWORK_PARAMETERS_QUERY
|
||||
);
|
||||
const foundParams = data?.networkParameters
|
||||
?.filter((p) => params.includes(p.key))
|
||||
.sort((a, b) => params.indexOf(a.key) - params.indexOf(b.key));
|
||||
return {
|
||||
data: foundParams ? foundParams.map((f) => f.value) : null,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
export * from './date';
|
||||
export * from './number';
|
||||
export * from './truncate';
|
||||
export * from './size';
|
||||
export * from './truncate';
|
||||
export * from './utils';
|
||||
|
@ -3,5 +3,6 @@ export * from './cumulative-vol-cell';
|
||||
export * from './flash-cell';
|
||||
export * from './price-cell';
|
||||
export * from './price-flash-cell';
|
||||
export * from './size';
|
||||
export * from './summary-rows';
|
||||
export * from './vol-cell';
|
47
libs/react-helpers/src/lib/grid/size.tsx
Normal file
47
libs/react-helpers/src/lib/grid/size.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
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,
|
||||
side,
|
||||
positionDecimalPlaces = 0,
|
||||
}: {
|
||||
value: string;
|
||||
side: Side;
|
||||
positionDecimalPlaces?: number;
|
||||
}) => {
|
||||
return (
|
||||
<span
|
||||
data-testid="size"
|
||||
className={classNames('text-right', {
|
||||
[positiveClassNames]: side === Side.SIDE_BUY,
|
||||
[negativeClassNames]: side === Side.SIDE_SELL,
|
||||
})}
|
||||
>
|
||||
{side === Side.SIDE_BUY ? '+' : side === Side.SIDE_SELL ? '-' : ''}
|
||||
{addDecimalsFormatNumber(value, positionDecimalPlaces)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ISizeCellProps extends ICellRendererParams {
|
||||
value: number | string;
|
||||
}
|
||||
|
||||
export const SizeCell = ({ value, data }: ISizeCellProps) => {
|
||||
if ((!value && value !== 0) || isNaN(Number(value))) {
|
||||
return <span data-testid="size">-</span>;
|
||||
}
|
||||
return (
|
||||
<Size
|
||||
value={value.toString()}
|
||||
side={data.side}
|
||||
positionDecimalPlaces={data.positionDecimalPlaces}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
SizeCell.displayName = 'SizeCell';
|
12
libs/react-helpers/src/lib/index.ts
Normal file
12
libs/react-helpers/src/lib/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export * from './context';
|
||||
export * from './format';
|
||||
export * from './grid';
|
||||
export * from './storage';
|
||||
export * from './validate';
|
||||
export * from './assets';
|
||||
export * from './determine-id';
|
||||
export * from './generic-data-provider';
|
||||
export * from './i18n';
|
||||
export * from './pagination';
|
||||
export * from './remove-0x';
|
||||
export * from './time';
|
12
libs/types/src/__generated__/globalTypes.ts
generated
12
libs/types/src/__generated__/globalTypes.ts
generated
@ -98,6 +98,18 @@ export enum Interval {
|
||||
INTERVAL_I6H = "INTERVAL_I6H",
|
||||
}
|
||||
|
||||
/**
|
||||
* Status of a liquidity provision order
|
||||
*/
|
||||
export enum LiquidityProvisionStatus {
|
||||
STATUS_ACTIVE = "STATUS_ACTIVE",
|
||||
STATUS_CANCELLED = "STATUS_CANCELLED",
|
||||
STATUS_PENDING = "STATUS_PENDING",
|
||||
STATUS_REJECTED = "STATUS_REJECTED",
|
||||
STATUS_STOPPED = "STATUS_STOPPED",
|
||||
STATUS_UNDEPLOYED = "STATUS_UNDEPLOYED",
|
||||
}
|
||||
|
||||
/**
|
||||
* The current state of a market
|
||||
*/
|
||||
|
@ -17,6 +17,18 @@ export enum AccountTypeMapping {
|
||||
ACCOUNT_TYPE_SETTLEMENT = 'Settlement',
|
||||
}
|
||||
|
||||
/**
|
||||
* Status of a liquidity provision order
|
||||
*/
|
||||
export enum LiquidityProvisionStatusMapping {
|
||||
STATUS_ACTIVE = 'Active',
|
||||
STATUS_CANCELLED = 'Cancelled',
|
||||
STATUS_PENDING = 'Pending',
|
||||
STATUS_REJECTED = 'Rejected',
|
||||
STATUS_STOPPED = 'Stopped',
|
||||
STATUS_UNDEPLOYED = 'Undeployed',
|
||||
}
|
||||
|
||||
export enum AuctionTriggerMapping {
|
||||
AUCTION_TRIGGER_BATCH = 'batch',
|
||||
AUCTION_TRIGGER_LIQUIDITY = 'liquidity',
|
||||
@ -25,17 +37,6 @@ export enum AuctionTriggerMapping {
|
||||
AUCTION_TRIGGER_UNSPECIFIED = 'unspecified',
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator describes the type of comparison.
|
||||
*/
|
||||
export enum ConditionOperatorMapping {
|
||||
OPERATOR_EQUALS = 'EQUALS',
|
||||
OPERATOR_GREATER_THAN = 'GREATER_THAN',
|
||||
OPERATOR_GREATER_THAN_OR_EQUAL = 'GREATER_THAN_OR_EQUAL',
|
||||
OPERATOR_LESS_THAN = 'LESS_THAN',
|
||||
OPERATOR_LESS_THAN_OR_EQUAL = 'LESS_THAN_OR_EQUAL',
|
||||
}
|
||||
|
||||
/**
|
||||
* The status of a deposit
|
||||
*/
|
||||
@ -201,7 +202,7 @@ export enum ProposalRejectionReasonMapping {
|
||||
PROPOSAL_ERROR_INVALID_SHAPE = 'Invalid shape',
|
||||
PROPOSAL_ERROR_MAJORITY_THRESHOLD_NOT_REACHED = 'Majority threshold not reached',
|
||||
PROPOSAL_ERROR_MARKET_MISSING_LIQUIDITY_COMMITMENT = 'Market missing liquidity commitment',
|
||||
PROPOSAL_ERROR_MISSING_BUILTIN_ASSET_FIELD = 'Missing builtin asset field',
|
||||
PROPOSAL_ERROR_MISSING_BUILTIN_ASSET_FIELD = 'Missing built-in asset field',
|
||||
PROPOSAL_ERROR_MISSING_COMMITMENT_AMOUNT = 'Missing commitment amount',
|
||||
PROPOSAL_ERROR_MISSING_ERC20_CONTRACT_ADDRESS = 'Missing ERC20 contract address',
|
||||
PROPOSAL_ERROR_NETWORK_PARAMETER_INVALID_KEY = 'Network parameter invalid key',
|
||||
|
@ -5,11 +5,12 @@ import { Children, isValidElement, useState } from 'react';
|
||||
|
||||
interface TabsProps {
|
||||
children: ReactElement<TabProps>[];
|
||||
active?: string;
|
||||
}
|
||||
|
||||
export const Tabs = ({ children }: TabsProps) => {
|
||||
export const Tabs = ({ children, active: activeDefaultId }: TabsProps) => {
|
||||
const [activeTab, setActiveTab] = useState<string>(() => {
|
||||
return children[0].props.id;
|
||||
return activeDefaultId ?? children[0].props.id;
|
||||
});
|
||||
|
||||
return (
|
||||
@ -24,7 +25,7 @@ export const Tabs = ({ children }: TabsProps) => {
|
||||
role="tablist"
|
||||
>
|
||||
{Children.map(children, (child) => {
|
||||
if (!isValidElement(child)) return null;
|
||||
if (!isValidElement(child) || child.props.hidden) return null;
|
||||
const isActive = child.props.id === activeTab;
|
||||
const triggerClass = classNames(
|
||||
'relative px-4 py-2 border-r border-neutral-300 dark:border-neutral-700',
|
||||
@ -51,7 +52,7 @@ export const Tabs = ({ children }: TabsProps) => {
|
||||
</div>
|
||||
<div className="h-full overflow-auto">
|
||||
{Children.map(children, (child) => {
|
||||
if (!isValidElement(child)) return null;
|
||||
if (!isValidElement(child) || child.props.hidden) return null;
|
||||
return (
|
||||
<TabsPrimitive.Content
|
||||
value={child.props.id}
|
||||
@ -71,6 +72,7 @@ interface TabProps {
|
||||
children: ReactNode;
|
||||
id: string;
|
||||
name: string;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export const Tab = ({ children, ...props }: TabProps) => {
|
||||
|
@ -10,3 +10,4 @@ export * from './lib/use-verify-withdrawal';
|
||||
export * from './lib/use-withdrawals';
|
||||
export * from './lib/__generated__/Withdrawals';
|
||||
export * from './lib/__generated__/WithdrawalFields';
|
||||
export * from './lib/__generated__/WithdrawFormQuery';
|
||||
|
@ -25,6 +25,7 @@
|
||||
"@vegaprotocol/environment": ["libs/environment/src/index.ts"],
|
||||
"@vegaprotocol/fills": ["libs/fills/src/index.ts"],
|
||||
"@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-list": ["libs/market-list/src/index.ts"],
|
||||
"@vegaprotocol/network-info": ["libs/network-info/src/index.ts"],
|
||||
|
@ -14,6 +14,7 @@
|
||||
"explorer-e2e": "apps/explorer-e2e",
|
||||
"fills": "libs/fills",
|
||||
"governance": "libs/governance",
|
||||
"liquidity": "libs/liquidity",
|
||||
"market-depth": "libs/market-depth",
|
||||
"market-list": "libs/market-list",
|
||||
"network-info": "libs/network-info",
|
||||
|
Loading…
Reference in New Issue
Block a user