diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx index 739d5ce14..bcefdb6e5 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx @@ -1,47 +1,14 @@ -import { useCallback, useState } from 'react'; -import { getAsset, getQuoteName } from '@vegaprotocol/markets'; +import { getAsset } from '@vegaprotocol/markets'; import type { OrderSubmissionBody } from '@vegaprotocol/wallet'; -import { useVegaWallet } from '@vegaprotocol/wallet'; - import type { Market } from '@vegaprotocol/markets'; -import type { EstimatePositionQuery } from '@vegaprotocol/positions'; -import { AccountBreakdownDialog } from '@vegaprotocol/accounts'; - -import { - formatNumberPercentage, - formatRange, - formatValue, -} from '@vegaprotocol/utils'; -import { marketMarginDataProvider } from '@vegaprotocol/accounts'; -import { useDataProvider } from '@vegaprotocol/data-provider'; -import * as AccordionPrimitive from '@radix-ui/react-accordion'; -import * as Schema from '@vegaprotocol/types'; - -import { - MARGIN_DIFF_TOOLTIP_TEXT, - DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT, - TOTAL_MARGIN_AVAILABLE, - LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT, - EST_TOTAL_MARGIN_TOOLTIP_TEXT, - MARGIN_ACCOUNT_TOOLTIP_TEXT, -} from '../../constants'; +import { formatNumberPercentage, formatValue } from '@vegaprotocol/utils'; import { useEstimateFees } from '../../hooks/use-estimate-fees'; import { KeyValue } from './key-value'; -import { - Accordion, - AccordionChevron, - AccordionPanel, - Intent, - ExternalLink, - Pill, - Tooltip, -} from '@vegaprotocol/ui-toolkit'; -import classNames from 'classnames'; +import { Intent, Pill } from '@vegaprotocol/ui-toolkit'; import BigNumber from 'bignumber.js'; import { FeesBreakdown } from '../fees-breakdown'; import { getTotalDiscountFactor, getDiscountedFee } from '../discounts'; -import { useT, ns } from '../../use-t'; -import { Trans } from 'react-i18next'; +import { useT } from '../../use-t'; export const emptyValue = '-'; @@ -119,337 +86,3 @@ export const DealTicketFeeDetails = ({ /> ); }; - -export interface DealTicketMarginDetailsProps { - generalAccountBalance?: string; - marginAccountBalance?: string; - market: Market; - onMarketClick?: (marketId: string, metaKey?: boolean) => void; - assetSymbol: string; - positionEstimate: EstimatePositionQuery['estimatePosition']; - side: Schema.Side; -} - -export const DealTicketMarginDetails = ({ - marginAccountBalance, - generalAccountBalance, - assetSymbol, - market, - onMarketClick, - positionEstimate, - side, -}: DealTicketMarginDetailsProps) => { - const t = useT(); - const [breakdownDialog, setBreakdownDialog] = useState(false); - const { pubKey: partyId } = useVegaWallet(); - const { data: currentMargins } = useDataProvider({ - dataProvider: marketMarginDataProvider, - variables: { marketId: market.id, partyId: partyId || '' }, - skip: !partyId, - }); - const liquidationEstimate = positionEstimate?.liquidation; - const marginEstimate = positionEstimate?.margin; - const totalBalance = - BigInt(generalAccountBalance || '0') + BigInt(marginAccountBalance || '0'); - const asset = getAsset(market); - const { decimals: assetDecimals, quantum } = asset; - let marginRequiredBestCase: string | undefined = undefined; - let marginRequiredWorstCase: string | undefined = undefined; - if (marginEstimate) { - if (currentMargins) { - marginRequiredBestCase = ( - BigInt(marginEstimate.bestCase.initialLevel) - - BigInt(currentMargins.initialLevel) - ).toString(); - if (marginRequiredBestCase.startsWith('-')) { - marginRequiredBestCase = '0'; - } - marginRequiredWorstCase = ( - BigInt(marginEstimate.worstCase.initialLevel) - - BigInt(currentMargins.initialLevel) - ).toString(); - if (marginRequiredWorstCase.startsWith('-')) { - marginRequiredWorstCase = '0'; - } - } else { - marginRequiredBestCase = marginEstimate.bestCase.initialLevel; - marginRequiredWorstCase = marginEstimate.worstCase.initialLevel; - } - } - - const totalMarginAvailable = ( - currentMargins - ? totalBalance - BigInt(currentMargins.maintenanceLevel) - : totalBalance - ).toString(); - - let deductionFromCollateral = null; - let projectedMargin = null; - if (marginAccountBalance) { - const deductionFromCollateralBestCase = - BigInt(marginEstimate?.bestCase.initialLevel ?? 0) - - BigInt(marginAccountBalance); - - const deductionFromCollateralWorstCase = - BigInt(marginEstimate?.worstCase.initialLevel ?? 0) - - BigInt(marginAccountBalance); - - deductionFromCollateral = ( - 0 - ? deductionFromCollateralBestCase.toString() - : '0', - deductionFromCollateralWorstCase > 0 - ? deductionFromCollateralWorstCase.toString() - : '0', - assetDecimals - )} - formattedValue={formatValue( - deductionFromCollateralWorstCase > 0 - ? deductionFromCollateralWorstCase.toString() - : '0', - assetDecimals, - quantum - )} - symbol={assetSymbol} - labelDescription={t( - 'DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT', - DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT, - { assetSymbol } - )} - /> - ); - projectedMargin = ( - - ); - } - - let liquidationPriceEstimate = emptyValue; - let liquidationPriceEstimateRange = emptyValue; - - if (liquidationEstimate) { - const liquidationEstimateBestCaseIncludingBuyOrders = BigInt( - liquidationEstimate.bestCase.including_buy_orders.replace(/\..*/, '') - ); - const liquidationEstimateBestCaseIncludingSellOrders = BigInt( - liquidationEstimate.bestCase.including_sell_orders.replace(/\..*/, '') - ); - const liquidationEstimateBestCase = - side === Schema.Side.SIDE_BUY - ? liquidationEstimateBestCaseIncludingBuyOrders - : liquidationEstimateBestCaseIncludingSellOrders; - - const liquidationEstimateWorstCaseIncludingBuyOrders = BigInt( - liquidationEstimate.worstCase.including_buy_orders.replace(/\..*/, '') - ); - const liquidationEstimateWorstCaseIncludingSellOrders = BigInt( - liquidationEstimate.worstCase.including_sell_orders.replace(/\..*/, '') - ); - const liquidationEstimateWorstCase = - side === Schema.Side.SIDE_BUY - ? liquidationEstimateWorstCaseIncludingBuyOrders - : liquidationEstimateWorstCaseIncludingSellOrders; - - liquidationPriceEstimate = formatValue( - liquidationEstimateWorstCase.toString(), - market.decimalPlaces, - undefined, - market.decimalPlaces - ); - liquidationPriceEstimateRange = formatRange( - (liquidationEstimateBestCase < liquidationEstimateWorstCase - ? liquidationEstimateBestCase - : liquidationEstimateWorstCase - ).toString(), - (liquidationEstimateBestCase > liquidationEstimateWorstCase - ? liquidationEstimateBestCase - : liquidationEstimateWorstCase - ).toString(), - market.decimalPlaces, - undefined, - market.decimalPlaces - ); - } - - const onAccountBreakdownDialogClose = useCallback( - () => setBreakdownDialog(false), - [] - ); - - const quoteName = getQuoteName(market); - - return ( -
- - -
-
- - {t('Margin required')} - - - -
- -
- {formatValue( - marginRequiredWorstCase, - assetDecimals, - quantum - )}{' '} - {assetSymbol || ''} -
-
-
- - } - > -
- - {deductionFromCollateral} - setBreakdownDialog(true) - : undefined - } - value={formatValue(marginAccountBalance, assetDecimals)} - symbol={assetSymbol} - labelDescription={t( - 'MARGIN_ACCOUNT_TOOLTIP_TEXT', - MARGIN_ACCOUNT_TOOLTIP_TEXT - )} - formattedValue={formatValue( - marginAccountBalance, - assetDecimals, - quantum - )} - /> -
-
-
- {projectedMargin} - - - {t( - 'LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT', - LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT - )} - {' '} - - - liquidation price estimate documentation - , - ]} - ns={ns} - /> - - - } - /> - {partyId && ( - - )} -
- ); -}; diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-margin-details.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-margin-details.tsx index ac8c0f4a7..d0094c3ff 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-margin-details.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-margin-details.tsx @@ -26,12 +26,25 @@ import { import classNames from 'classnames'; import { useT, ns } from '../../use-t'; import { Trans } from 'react-i18next'; -import type { DealTicketMarginDetailsProps } from './deal-ticket-fee-details'; +import type { Market } from '@vegaprotocol/markets'; import { emptyValue } from './deal-ticket-fee-details'; +import type { EstimatePositionQuery } from '@vegaprotocol/positions'; + +export interface DealTicketMarginDetailsProps { + generalAccountBalance?: string; + marginAccountBalance?: string; + orderMarginAccountBalance?: string; + market: Market; + onMarketClick?: (marketId: string, metaKey?: boolean) => void; + assetSymbol: string; + positionEstimate: EstimatePositionQuery['estimatePosition']; + side: Schema.Side; +} export const DealTicketMarginDetails = ({ marginAccountBalance, generalAccountBalance, + orderMarginAccountBalance, assetSymbol, market, onMarketClick, @@ -48,31 +61,44 @@ export const DealTicketMarginDetails = ({ }); const liquidationEstimate = positionEstimate?.liquidation; const marginEstimate = positionEstimate?.margin; + const totalMarginAccountBalance = + BigInt(marginAccountBalance || '0') + + BigInt(orderMarginAccountBalance || '0'); const totalBalance = - BigInt(generalAccountBalance || '0') + BigInt(marginAccountBalance || '0'); + BigInt(generalAccountBalance || '0') + totalMarginAccountBalance; const asset = getAsset(market); const { decimals: assetDecimals, quantum } = asset; let marginRequiredBestCase: string | undefined = undefined; let marginRequiredWorstCase: string | undefined = undefined; + const marginEstimateBestCase = + BigInt(marginEstimate?.bestCase.initialLevel ?? 0) + + BigInt(marginEstimate?.bestCase.orderMarginLevel ?? 0); + const marginEstimateWorstCase = + BigInt(marginEstimate?.worstCase.initialLevel ?? 0) + + BigInt(marginEstimate?.worstCase.orderMarginLevel ?? 0); if (marginEstimate) { if (currentMargins) { + const currentMargin = + BigInt(currentMargins.initialLevel) + + BigInt(currentMargins.orderMarginLevel); + marginRequiredBestCase = ( - BigInt(marginEstimate.bestCase.initialLevel) - - BigInt(currentMargins.initialLevel) + marginEstimateBestCase - currentMargin ).toString(); if (marginRequiredBestCase.startsWith('-')) { marginRequiredBestCase = '0'; } + marginRequiredWorstCase = ( - BigInt(marginEstimate.worstCase.initialLevel) - - BigInt(currentMargins.initialLevel) + marginEstimateWorstCase - currentMargin ).toString(); + if (marginRequiredWorstCase.startsWith('-')) { marginRequiredWorstCase = '0'; } } else { - marginRequiredBestCase = marginEstimate.bestCase.initialLevel; - marginRequiredWorstCase = marginEstimate.worstCase.initialLevel; + marginRequiredBestCase = marginEstimateBestCase.toString(); + marginRequiredWorstCase = marginEstimateWorstCase.toString(); } } @@ -84,14 +110,12 @@ export const DealTicketMarginDetails = ({ let deductionFromCollateral = null; let projectedMargin = null; - if (marginAccountBalance) { + if (totalMarginAccountBalance) { const deductionFromCollateralBestCase = - BigInt(marginEstimate?.bestCase.initialLevel ?? 0) - - BigInt(marginAccountBalance); + marginEstimateBestCase - totalMarginAccountBalance; const deductionFromCollateralWorstCase = - BigInt(marginEstimate?.worstCase.initialLevel ?? 0) - - BigInt(marginAccountBalance); + marginEstimateWorstCase - totalMarginAccountBalance; deductionFromCollateral = ( setBreakdownDialog(true) : undefined } - value={formatValue(marginAccountBalance, assetDecimals)} + value={formatValue( + totalMarginAccountBalance.toString(), + assetDecimals + )} symbol={assetSymbol} labelDescription={t( 'MARGIN_ACCOUNT_TOOLTIP_TEXT', MARGIN_ACCOUNT_TOOLTIP_TEXT )} formattedValue={formatValue( - marginAccountBalance, + totalMarginAccountBalance.toString(), assetDecimals, quantum )} diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx index c5c4f5d6c..ddce51b07 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx @@ -178,7 +178,9 @@ export const DealTicket = ({ } = useAccountBalance(asset.id); const balance = ( - BigInt(marginAccountBalance) + BigInt(generalAccountBalance) + BigInt(marginAccountBalance) + + BigInt(generalAccountBalance) + + BigInt(orderMarginAccountBalance) ).toString(); const { marketState, marketTradingMode } = marketData; @@ -279,9 +281,9 @@ export const DealTicket = ({ openVolume, averageEntryPrice, orders, - marginAccountBalance, - generalAccountBalance, - orderMarginAccountBalance, + marginAccountBalance: marginAccountBalance || '0', + generalAccountBalance: generalAccountBalance || '0', + orderMarginAccountBalance: orderMarginAccountBalance || '0', marginFactor: margin?.marginFactor || '1', marginMode: margin?.marginMode || Schema.MarginMode.MARGIN_MODE_CROSS_MARGIN, @@ -337,7 +339,9 @@ export const DealTicket = ({ } const hasNoBalance = - !BigInt(generalAccountBalance) && !BigInt(marginAccountBalance); + !BigInt(generalAccountBalance) && + !BigInt(marginAccountBalance) && + !BigInt(orderMarginAccountBalance); if ( hasNoBalance && !(loadingMarginAccountBalance || loadingGeneralAccountBalance) @@ -367,6 +371,7 @@ export const DealTicket = ({ marketTradingMode, generalAccountBalance, marginAccountBalance, + orderMarginAccountBalance, loadingMarginAccountBalance, loadingGeneralAccountBalance, pubKey, @@ -725,10 +730,16 @@ export const DealTicket = ({ asset={asset} marketTradingMode={marketData.marketTradingMode} balance={balance} - margin={ - positionEstimate?.estimatePosition?.margin.bestCase.initialLevel || - '0' - } + margin={( + BigInt( + positionEstimate?.estimatePosition?.margin.bestCase.initialLevel || + '0' + ) + + BigInt( + positionEstimate?.estimatePosition?.margin.bestCase + .orderMarginLevel || '0' + ) + ).toString()} isReadOnly={isReadOnly} pubKey={pubKey} onDeposit={onDeposit} @@ -761,6 +772,7 @@ export const DealTicket = ({ onMarketClick={onMarketClick} assetSymbol={asset.symbol} marginAccountBalance={marginAccountBalance} + orderMarginAccountBalance={orderMarginAccountBalance} generalAccountBalance={generalAccountBalance} positionEstimate={positionEstimate?.estimatePosition} market={market} diff --git a/libs/deal-ticket/src/components/deal-ticket/margin-mode-selector.tsx b/libs/deal-ticket/src/components/deal-ticket/margin-mode-selector.tsx index 78d5070e0..e012c71f7 100644 --- a/libs/deal-ticket/src/components/deal-ticket/margin-mode-selector.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/margin-mode-selector.tsx @@ -5,6 +5,8 @@ import { TradingInput as Input, FormGroup, LeverageSlider, + Notification, + Intent, } from '@vegaprotocol/ui-toolkit'; import { MarginMode, useVegaWallet } from '@vegaprotocol/wallet'; import * as Types from '@vegaprotocol/types'; @@ -43,9 +45,15 @@ export const MarginChange = ({ const t = useT(); const { data: market } = useMarket(marketId); const asset = market && getAsset(market); - const { marginAccountBalance, orderMarginAccountBalance } = - useMarginAccountBalance(marketId); - const { accountBalance: generalAccountBalance } = useAccountBalance(marketId); + const { + marginAccountBalance, + orderMarginAccountBalance, + loading: marginAccountBalanceLoading, + } = useMarginAccountBalance(marketId); + const { + accountBalance: generalAccountBalance, + loading: generalAccountBalanceLoading, + } = useAccountBalance(asset?.id); const { openVolume, averageEntryPrice } = useOpenVolume( partyId, marketId @@ -65,35 +73,35 @@ export const MarginChange = ({ side: order.side, })) : []; - const skip = !orders?.length || openVolume === '0'; + const skip = + (!orders?.length && openVolume === '0') || + marginAccountBalanceLoading || + generalAccountBalanceLoading; const estimateMargin = usePositionEstimate( { - generalAccountBalance, - marginAccountBalance, + generalAccountBalance: generalAccountBalance || '0', + marginAccountBalance: marginAccountBalance || '0', marginFactor, marginMode, averageEntryPrice, openVolume, marketId, - orderMarginAccountBalance, + orderMarginAccountBalance: orderMarginAccountBalance || '0', includeCollateralIncreaseInAvailableCollateral: true, orders, }, skip ); - const currentMargin = - BigInt(marginAccountBalance) + BigInt(orderMarginAccountBalance); if ( !asset || - !estimateMargin?.estimatePosition?.margin.worstCase.initialLevel || - currentMargin.toString() === '0' + !estimateMargin?.estimatePosition?.collateralIncreaseEstimate.worstCase || + estimateMargin.estimatePosition.collateralIncreaseEstimate.worstCase === '0' ) { return null; } - const initialLevel = BigInt( - estimateMargin.estimatePosition?.margin.worstCase.initialLevel + const diff = BigInt( + estimateMargin.estimatePosition.collateralIncreaseEstimate.worstCase ); - const diff = initialLevel - currentMargin; if (!diff) { return null; } @@ -145,15 +153,22 @@ export const MarginChange = ({ ); } else { marginChangeWarning = t( - 'Changing to this margin mode and leverage will result in {{amount}} {{symbol}} will be moved from yur general account to fund the position.', + 'Changing to this margin mode and leverage will result in {{amount}} {{symbol}} will be moved from your general account to fund the position.', interpolation ); } } return ( -
-

{positionWarning}

-

{marginChangeWarning}

+
+ +

{positionWarning}

+

{marginChangeWarning}

+ + } + />
); }; @@ -203,7 +218,7 @@ const CrossMarginModeDialog = ({