diff --git a/src/abacus-ts/logs.ts b/src/abacus-ts/logs.ts index 7fe963e67..2b40edfaa 100644 --- a/src/abacus-ts/logs.ts +++ b/src/abacus-ts/logs.ts @@ -1,5 +1,5 @@ import { log } from '@/lib/telemetry'; export function logAbacusTsError(source: string, message: string, ...args: any[]) { - log(`${source}: ${message}`, undefined, { context: args }); + log(`${source}: ${message}`, undefined, { context: args }, true); } diff --git a/src/constants/dialogs.ts b/src/constants/dialogs.ts index 15b8c5f44..0ac69895b 100644 --- a/src/constants/dialogs.ts +++ b/src/constants/dialogs.ts @@ -1,10 +1,11 @@ import { ReactNode } from 'react'; +import { IndexerPositionSide } from '@/types/indexer/indexerApiGen'; import { TagsOf, UnionOf, ofType, unionize } from 'unionize'; import { BigNumberish } from '@/lib/numbers'; -import { AbacusPositionSides, Nullable, SubaccountOrder, SubaccountPosition } from './abacus'; +import { Nullable, SubaccountOrder, SubaccountPosition } from './abacus'; import { IAffiliateStats } from './affiliates'; import { DydxChainAsset } from './wallets'; @@ -64,7 +65,7 @@ export type SharePNLAnalyticsDialogProps = { oraclePrice: Nullable; entryPrice: Nullable; unrealizedPnl: Nullable; - side: Nullable; + side: Nullable; sideLabel: Nullable; }; export type StakeDialogProps = {}; diff --git a/src/lib/numbers.ts b/src/lib/numbers.ts index 7723ad7d3..91772117a 100644 --- a/src/lib/numbers.ts +++ b/src/lib/numbers.ts @@ -19,6 +19,9 @@ export const MustBigNumber = (amount?: BigNumberish | null): BigNumber => export const MaybeBigNumber = (amount?: BigNumberish | null): BigNumber | undefined => amount ? MustBigNumber(amount) : undefined; +export const MaybeNumber = (amount?: BigNumberish | null): number | undefined => + amount ? MustBigNumber(amount).toNumber() : undefined; + // doesnt allow null, always returns big number // empty string becomes null though export const ToBigNumber = (amount: BigNumberish): BigNumber => { diff --git a/src/lib/telemetry.ts b/src/lib/telemetry.ts index 075c4672c..6705b0e60 100644 --- a/src/lib/telemetry.ts +++ b/src/lib/telemetry.ts @@ -4,8 +4,8 @@ import { isDev } from '@/constants/networks'; import { track } from './analytics/analytics'; import { dd } from './analytics/datadog'; -export const log = (location: string, error?: Error, metadata?: object) => { - if (isDev) { +export const log = (location: string, error?: Error, metadata?: object, forceLog?: boolean) => { + if (isDev || forceLog) { // eslint-disable-next-line no-console console.warn('telemetry/log:', { location, error, metadata }); } diff --git a/src/pages/portfolio/Portfolio.tsx b/src/pages/portfolio/Portfolio.tsx index 8d7c13a32..a081eb091 100644 --- a/src/pages/portfolio/Portfolio.tsx +++ b/src/pages/portfolio/Portfolio.tsx @@ -1,5 +1,6 @@ import { lazy, Suspense } from 'react'; +import { selectParentSubaccountOpenPositions } from '@/abacus-ts/selectors/account'; import { shallowEqual } from 'react-redux'; import { Navigate, Route, Routes } from 'react-router-dom'; import styled from 'styled-components'; @@ -9,6 +10,7 @@ import { ButtonAction } from '@/constants/buttons'; import { ComplianceStates } from '@/constants/compliance'; import { DialogTypes } from '@/constants/dialogs'; import { STRING_KEYS } from '@/constants/localization'; +import { EMPTY_ARR } from '@/constants/objects'; import { HistoryRoute, PortfolioRoute } from '@/constants/routes'; import { useAccountBalance } from '@/hooks/useAccountBalance'; @@ -61,8 +63,10 @@ const PortfolioPage = () => { const { freeCollateral } = useAppSelector(getSubaccount, shallowEqual) ?? {}; const { nativeTokenBalance } = useAccountBalance(); - const { numTotalPositions, numTotalOpenOrders } = - useAppSelector(getTradeInfoNumbers, shallowEqual) ?? {}; + const { numTotalOpenOrders } = useAppSelector(getTradeInfoNumbers, shallowEqual); + const numTotalPositions = (useAppSelector(selectParentSubaccountOpenPositions) ?? EMPTY_ARR) + .length; + const numPositions = shortenNumberForDisplay(numTotalPositions); const numOrders = shortenNumberForDisplay(numTotalOpenOrders); diff --git a/src/pages/trade/HorizontalPanel.tsx b/src/pages/trade/HorizontalPanel.tsx index aaeac826d..72700d579 100644 --- a/src/pages/trade/HorizontalPanel.tsx +++ b/src/pages/trade/HorizontalPanel.tsx @@ -1,11 +1,11 @@ import { useCallback, useMemo, useState } from 'react'; import { BonsaiCore } from '@/abacus-ts/ontology'; -import { shallowEqual } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; import { STRING_KEYS } from '@/constants/localization'; +import { EMPTY_ARR } from '@/constants/objects'; import { AppRoute } from '@/constants/routes'; import { useBreakpoints } from '@/hooks/useBreakpoints'; @@ -31,7 +31,6 @@ import { createGetUnseenFillsCount, createGetUnseenOpenOrdersCount, createGetUnseenOrderHistoryCount, - getTradeInfoNumbers, } from '@/state/accountSelectors'; import { useAppSelector } from '@/state/appTypes'; import { getDefaultToAllMarketsInPositionsOrdersFills } from '@/state/appUiConfigsSelectors'; @@ -72,8 +71,6 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => { const currentMarketId = useAppSelector(getCurrentMarketId); - const { numTotalPositions } = useAppSelector(getTradeInfoNumbers, shallowEqual); - const isAccountViewOnly = useAppSelector(calculateIsAccountViewOnly); const shouldRenderTriggers = useShouldShowTriggers(); const shouldRenderActions = useParameterizedSelector( @@ -87,6 +84,10 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => { createGetOpenOrdersCount, showCurrentMarket ? currentMarketId : undefined ); + const numTotalPositions = ( + useAppSelector(BonsaiCore.account.parentSubaccountPositions.data) ?? EMPTY_ARR + ).length; + const unseenOrders = useParameterizedSelector( createGetUnseenOpenOrdersCount, showCurrentMarket ? currentMarketId : undefined diff --git a/src/pages/trade/types.ts b/src/pages/trade/types.ts index b1718ebb6..6d65d2e2b 100644 --- a/src/pages/trade/types.ts +++ b/src/pages/trade/types.ts @@ -1,3 +1,5 @@ +import { MarginMode } from '@/abacus-ts/summaryTypes'; + export enum MarketTypeFilter { AllMarkets = 'AllMarkets', Cross = 'Cross', @@ -13,6 +15,15 @@ export function marketTypeMatchesFilter(type: 'Isolated' | 'Cross', filter?: Mar ); } +export function marginModeMatchesFilter(type: MarginMode, filter?: MarketTypeFilter) { + return ( + filter == null || + filter === MarketTypeFilter.AllMarkets || + (type === 'ISOLATED' && filter === MarketTypeFilter.Isolated) || + (type === 'CROSS' && filter === MarketTypeFilter.Cross) + ); +} + export enum PanelView { AllMarkets = 'AllMarkets', CurrentMarket = 'CurrentMarket', diff --git a/src/views/dialogs/SharePNLAnalyticsDialog.tsx b/src/views/dialogs/SharePNLAnalyticsDialog.tsx index e9896dbfa..af0042b55 100644 --- a/src/views/dialogs/SharePNLAnalyticsDialog.tsx +++ b/src/views/dialogs/SharePNLAnalyticsDialog.tsx @@ -1,5 +1,6 @@ import { useMemo } from 'react'; +import { IndexerPositionSide } from '@/types/indexer/indexerApiGen'; import { useToBlob } from '@hugocxl/react-to-image'; import styled from 'styled-components'; import tw from 'twin.macro'; @@ -8,7 +9,6 @@ import { AnalyticsEvents } from '@/constants/analytics'; import { ButtonAction } from '@/constants/buttons'; import { DialogProps, SharePNLAnalyticsDialogProps } from '@/constants/dialogs'; import { STRING_KEYS } from '@/constants/localization'; -import { PositionSide } from '@/constants/trade'; import { useStringGetter } from '@/hooks/useStringGetter'; @@ -82,10 +82,10 @@ export const SharePNLAnalyticsDialog = ({ }); const sideSign = useMemo(() => { - switch (side?.name) { - case PositionSide.Long: + switch (side) { + case IndexerPositionSide.LONG: return TagSign.Positive; - case PositionSide.Short: + case IndexerPositionSide.SHORT: return TagSign.Negative; default: return TagSign.Neutral; diff --git a/src/views/tables/PositionsTable.tsx b/src/views/tables/PositionsTable.tsx index 0d4591dc7..bd69d6348 100644 --- a/src/views/tables/PositionsTable.tsx +++ b/src/views/tables/PositionsTable.tsx @@ -1,22 +1,22 @@ -import { forwardRef, Key, useMemo } from 'react'; +import { forwardRef, useMemo } from 'react'; +import { AssetInfo } from '@/abacus-ts/rawTypes'; +import { selectParentSubaccountOpenPositions } from '@/abacus-ts/selectors/account'; +import { selectRawAssetsData, selectRawMarketsData } from '@/abacus-ts/selectors/base'; +import { SubaccountPosition } from '@/abacus-ts/summaryTypes'; +import { IndexerPositionSide } from '@/types/indexer/indexerApiGen'; import { Separator } from '@radix-ui/react-separator'; import type { ColumnSize } from '@react-types/table'; +import BigNumber from 'bignumber.js'; import { shallowEqual } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; -import { - type Asset, - type Nullable, - type SubaccountOrder, - type SubaccountPosition, -} from '@/constants/abacus'; +import { Asset, type Nullable, type SubaccountOrder } from '@/constants/abacus'; import { STRING_KEYS, StringGetterFunction } from '@/constants/localization'; import { NumberSign, TOKEN_DECIMALS, USD_DECIMALS } from '@/constants/numbers'; import { EMPTY_ARR } from '@/constants/objects'; import { AppRoute } from '@/constants/routes'; -import { PositionSide } from '@/constants/trade'; import { MediaQueryKeys, useBreakpoints } from '@/hooks/useBreakpoints'; import { useEnvFeatures } from '@/hooks/useEnvFeatures'; @@ -33,17 +33,15 @@ import { TableCell } from '@/components/Table/TableCell'; import { TableColumnHeader } from '@/components/Table/TableColumnHeader'; import { PageSize } from '@/components/Table/TablePaginationRow'; import { Tag } from '@/components/Tag'; -import { MarketTypeFilter, marketTypeMatchesFilter } from '@/pages/trade/types'; +import { marginModeMatchesFilter, MarketTypeFilter } from '@/pages/trade/types'; import { calculateIsAccountViewOnly } from '@/state/accountCalculators'; -import { getExistingOpenPositions, getSubaccountConditionalOrders } from '@/state/accountSelectors'; +import { getSubaccountConditionalOrders } from '@/state/accountSelectors'; import { useAppSelector } from '@/state/appTypes'; -import { getAssets } from '@/state/assetsSelectors'; -import { getPerpetualMarkets } from '@/state/perpetualsSelectors'; -import { getNumberSign, MustBigNumber } from '@/lib/numbers'; +import { getDisplayableTickerFromMarket } from '@/lib/assetUtils'; +import { getNumberSign, MaybeBigNumber, MustBigNumber } from '@/lib/numbers'; import { safeAssign } from '@/lib/objectHelpers'; -import { getMarginModeFromSubaccountNumber, getPositionMargin } from '@/lib/tradeData'; import { orEmptyRecord } from '@/lib/typeUtils'; import { CloseAllPositionsButton } from './PositionsTable/CloseAllPositionsButton'; @@ -68,19 +66,13 @@ export enum PositionsTableColumnKey { Triggers = 'Triggers', NetFunding = 'NetFunding', Actions = 'Actions', - - // TODO: CT-1292 remove deprecated fields - LiquidationAndOraclePrice = 'LiquidationAndOraclePrice', - RealizedPnl = 'RealizedPnl', - UnrealizedPnl = 'UnrealizedPnl', - AverageOpenAndClose = 'AverageOpenAndClose', } type PositionTableRow = { - asset: Asset | undefined; - oraclePrice: Nullable; + asset: AssetInfo | undefined; + oraclePrice: Nullable; tickSizeDecimals: number; - fundingRate: Nullable; + fundingRate: Nullable; stopLossOrders: SubaccountOrder[]; takeProfitOrders: SubaccountOrder[]; stepSizeDecimals: number; @@ -110,14 +102,14 @@ const getPositionsTableColumnDef = ({ { [PositionsTableColumnKey.Details]: { columnKey: 'details', - getCellValue: (row) => row.id, + getCellValue: (row) => row.uniqueId, label: stringGetter({ key: STRING_KEYS.DETAILS }), - renderCell: ({ asset, leverage, resources, size }) => ( + renderCell: ({ asset, leverage, signedSize, side }) => ( @@ -125,20 +117,19 @@ const getPositionsTableColumnDef = ({ > <$HighlightOutput type={OutputType.Asset} - value={size.current} + value={signedSize} fractionDigits={TOKEN_DECIMALS} showSign={ShowSign.None} tag={asset?.id} />
<$PositionSide> - {resources.sideStringKey.current && - stringGetter({ key: resources.sideStringKey.current })} + {stringGetter({ key: getIndexerPositionSideStringKey(side) })} @ <$HighlightOutput type={OutputType.Multiple} - value={leverage.current} + value={leverage} showSign={ShowSign.None} />
@@ -147,7 +138,7 @@ const getPositionsTableColumnDef = ({ }, [PositionsTableColumnKey.IndexEntry]: { columnKey: 'oracleEntry', - getCellValue: (row) => row.entryPrice.current, + getCellValue: (row) => row.entryPrice.toNumber(), label: ( {stringGetter({ key: STRING_KEYS.ORACLE_PRICE_ABBREVIATED })} @@ -170,7 +161,7 @@ const getPositionsTableColumnDef = ({
@@ -178,21 +169,21 @@ const getPositionsTableColumnDef = ({ }, [PositionsTableColumnKey.PnL]: { columnKey: 'combinedPnl', - getCellValue: (row) => row.unrealizedPnl.current, + getCellValue: (row) => row.updatedUnrealizedPnl.toNumber(), label: stringGetter({ key: STRING_KEYS.PNL }), - renderCell: ({ unrealizedPnl, unrealizedPnlPercent }) => { + renderCell: ({ updatedUnrealizedPnl, updatedUnrealizedPnlPercent }) => { return !isTablet ? ( <$OutputSigned - sign={getNumberSign(unrealizedPnl.current)} + sign={getNumberSign(updatedUnrealizedPnl)} type={OutputType.Fiat} - value={unrealizedPnl.current} + value={updatedUnrealizedPnl} showSign={ShowSign.Negative} /> <$OutputSigned - sign={getNumberSign(unrealizedPnl.current)} + sign={getNumberSign(updatedUnrealizedPnl)} type={OutputType.Percent} - value={unrealizedPnlPercent.current} + value={updatedUnrealizedPnlPercent} showSign={ShowSign.Negative} fractionDigits={0} withParentheses @@ -201,15 +192,15 @@ const getPositionsTableColumnDef = ({ ) : ( <$OutputSigned - sign={getNumberSign(unrealizedPnlPercent.current)} + sign={getNumberSign(updatedUnrealizedPnlPercent)} type={OutputType.Percent} - value={unrealizedPnlPercent.current} + value={updatedUnrealizedPnlPercent} showSign={ShowSign.None} /> <$HighlightOutput - isNegative={MustBigNumber(unrealizedPnl.current).isNegative()} + isNegative={MustBigNumber(updatedUnrealizedPnl).isNegative()} type={OutputType.Fiat} - value={unrealizedPnl.current} + value={updatedUnrealizedPnl} showSign={ShowSign.Both} /> @@ -218,50 +209,68 @@ const getPositionsTableColumnDef = ({ }, [PositionsTableColumnKey.Market]: { columnKey: 'market', - getCellValue: (row) => row.displayId, + getCellValue: (row) => getDisplayableTickerFromMarket(row.market), label: stringGetter({ key: STRING_KEYS.MARKET }), hideOnBreakpoint: MediaQueryKeys.isMobile, renderCell: ({ asset }) => { - return ; + return ( + + ); }, }, [PositionsTableColumnKey.Leverage]: { columnKey: 'leverage', - getCellValue: (row) => row.leverage.current, + getCellValue: (row) => row.leverage?.toNumber(), label: stringGetter({ key: STRING_KEYS.LEVERAGE }), hideOnBreakpoint: MediaQueryKeys.isMobile, renderCell: ({ leverage }) => ( - + ), }, [PositionsTableColumnKey.Type]: { columnKey: 'type', - getCellValue: (row) => getMarginModeFromSubaccountNumber(row.childSubaccountNumber).name, + getCellValue: (row) => row.marginMode, label: stringGetter({ key: STRING_KEYS.TYPE }), hideOnBreakpoint: MediaQueryKeys.isMobile, - renderCell: ({ childSubaccountNumber }) => ( + renderCell: ({ marginMode }) => ( - {getMarginModeFromSubaccountNumber(childSubaccountNumber).name} + + {marginMode === 'CROSS' + ? stringGetter({ key: STRING_KEYS.CROSS }) + : stringGetter({ key: STRING_KEYS.ISOLATED })} + ), }, [PositionsTableColumnKey.Size]: { columnKey: 'size', getCellValue: (row) => { - return row.size.current; + return row.signedSize.toNumber(); }, label: stringGetter({ key: STRING_KEYS.SIZE }), hideOnBreakpoint: MediaQueryKeys.isMobile, - renderCell: ({ size, stepSizeDecimals }) => { + renderCell: ({ signedSize, stepSizeDecimals }) => { return ( <$OutputSigned type={OutputType.Asset} - value={size.current} + value={signedSize} showSign={ShowSign.Negative} - sign={getNumberSign(size.current)} + sign={getNumberSign(signedSize)} fractionDigits={stepSizeDecimals} /> @@ -270,20 +279,20 @@ const getPositionsTableColumnDef = ({ }, [PositionsTableColumnKey.Value]: { columnKey: 'value', - getCellValue: (row) => row.notionalTotal.current, + getCellValue: (row) => row.notional.toNumber(), label: stringGetter({ key: STRING_KEYS.VALUE }), hideOnBreakpoint: MediaQueryKeys.isMobile, - renderCell: ({ notionalTotal }) => { + renderCell: ({ notional }) => { return ( - + ); }, }, [PositionsTableColumnKey.Margin]: { columnKey: 'margin', - getCellValue: (row) => getPositionMargin({ position: row }), + getCellValue: (row) => row.marginValueInitial.toNumber(), label: stringGetter({ key: STRING_KEYS.MARGIN }), hideOnBreakpoint: MediaQueryKeys.isMobile, isActionable: true, @@ -291,7 +300,7 @@ const getPositionsTableColumnDef = ({ }, [PositionsTableColumnKey.AverageOpen]: { columnKey: 'averageOpen', - getCellValue: (row) => row.entryPrice.current, + getCellValue: (row) => row.entryPrice.toNumber(), label: stringGetter({ key: STRING_KEYS.AVG_OPEN }), hideOnBreakpoint: MediaQueryKeys.isMobile, isActionable: true, @@ -300,7 +309,7 @@ const getPositionsTableColumnDef = ({ @@ -308,7 +317,7 @@ const getPositionsTableColumnDef = ({ }, [PositionsTableColumnKey.Oracle]: { columnKey: 'oracle', - getCellValue: (row) => row.oraclePrice, + getCellValue: (row) => row.oraclePrice?.toNumber(), label: stringGetter({ key: STRING_KEYS.ORACLE_PRICE_ABBREVIATED }), renderCell: ({ oraclePrice, tickSizeDecimals }) => ( @@ -323,14 +332,14 @@ const getPositionsTableColumnDef = ({ }, [PositionsTableColumnKey.Liquidation]: { columnKey: 'liquidation', - getCellValue: (row) => row.liquidationPrice.current, + getCellValue: (row) => row.liquidationPrice?.toNumber(), label: stringGetter({ key: STRING_KEYS.LIQUIDATION }), renderCell: ({ liquidationPrice, tickSizeDecimals }) => ( @@ -338,7 +347,7 @@ const getPositionsTableColumnDef = ({ }, [PositionsTableColumnKey.NetFunding]: { columnKey: 'netFunding', - getCellValue: (row) => row.netFunding, + getCellValue: (row) => row.netFunding.toNumber(), label: stringGetter({ key: STRING_KEYS.FUNDING_PAYMENTS_SHORT }), hideOnBreakpoint: MediaQueryKeys.isTablet, renderCell: ({ netFunding }) => { @@ -354,92 +363,6 @@ const getPositionsTableColumnDef = ({ ); }, }, - [PositionsTableColumnKey.LiquidationAndOraclePrice]: { - columnKey: 'price', - getCellValue: (row) => row.liquidationPrice.current, - label: ( - - {stringGetter({ key: STRING_KEYS.LIQUIDATION_PRICE_SHORT })} - {stringGetter({ key: STRING_KEYS.ORACLE_PRICE_ABBREVIATED })} - - ), - renderCell: ({ liquidationPrice, oraclePrice, tickSizeDecimals }) => ( - - - - - ), - }, - [PositionsTableColumnKey.UnrealizedPnl]: { - columnKey: 'unrealizedPnl', - getCellValue: (row) => row.unrealizedPnl.current, - label: stringGetter({ key: STRING_KEYS.UNREALIZED_PNL }), - hideOnBreakpoint: MediaQueryKeys.isTablet, - renderCell: ({ unrealizedPnl, unrealizedPnlPercent }) => ( - - <$OutputSigned - sign={getNumberSign(unrealizedPnl.current)} - type={OutputType.Fiat} - value={unrealizedPnl.current} - /> - - - ), - }, - [PositionsTableColumnKey.RealizedPnl]: { - columnKey: 'realizedPnl', - getCellValue: (row) => row.realizedPnl.current, - label: stringGetter({ key: STRING_KEYS.REALIZED_PNL }), - hideOnBreakpoint: MediaQueryKeys.isTablet, - renderCell: ({ realizedPnl, realizedPnlPercent }) => ( - - <$OutputSigned - sign={getNumberSign(realizedPnl.current)} - type={OutputType.Fiat} - value={realizedPnl.current} - showSign={ShowSign.Negative} - /> - - - ), - }, - [PositionsTableColumnKey.AverageOpenAndClose]: { - columnKey: 'entryExitPrice', - getCellValue: (row) => row.entryPrice.current, - label: ( - - {stringGetter({ key: STRING_KEYS.AVERAGE_OPEN_SHORT })} - {stringGetter({ key: STRING_KEYS.AVERAGE_CLOSE_SHORT })} - - ), - hideOnBreakpoint: MediaQueryKeys.isTablet, - renderCell: ({ entryPrice, exitPrice, tickSizeDecimals }) => ( - - - - - ), - }, [PositionsTableColumnKey.Triggers]: { columnKey: 'triggers', label: ( @@ -453,25 +376,25 @@ const getPositionsTableColumnDef = ({ hideOnBreakpoint: MediaQueryKeys.isTablet, align: 'center', renderCell: ({ - id, + market, assetId, tickSizeDecimals, liquidationPrice, side, - size, + signedSize, stopLossOrders, takeProfitOrders, }) => { return ( @@ -485,27 +408,23 @@ const getPositionsTableColumnDef = ({ allowsSorting: false, hideOnBreakpoint: MediaQueryKeys.isTablet, renderCell: ({ - id, + market, assetId, leverage, side, oraclePrice, entryPrice, unrealizedPnl, - resources, }) => ( @@ -515,6 +434,13 @@ const getPositionsTableColumnDef = ({ )[key], }); +function getIndexerPositionSideStringKey(side: IndexerPositionSide) { + if (side === IndexerPositionSide.LONG) { + return STRING_KEYS.LONG_POSITION_SHORT; + } + return STRING_KEYS.SHORT_POSITION_SHORT; +} + type ElementProps = { columnKeys: PositionsTableColumnKey[]; columnWidths?: Partial>; @@ -554,22 +480,25 @@ export const PositionsTable = forwardRef( const { isSlTpLimitOrdersEnabled } = useEnvFeatures(); const { isTablet } = useBreakpoints(); + // todo this uses the old subaccount id for now const isAccountViewOnly = useAppSelector(calculateIsAccountViewOnly); - const perpetualMarkets = orEmptyRecord(useAppSelector(getPerpetualMarkets, shallowEqual)); - const assets = orEmptyRecord(useAppSelector(getAssets, shallowEqual)); - const openPositions = useAppSelector(getExistingOpenPositions, shallowEqual) ?? EMPTY_ARR; + const perpetualMarkets = orEmptyRecord(useAppSelector(selectRawMarketsData)); + const assets = orEmptyRecord(useAppSelector(selectRawAssetsData)); + + const openPositions = useAppSelector(selectParentSubaccountOpenPositions) ?? EMPTY_ARR; + const positions = useMemo(() => { return openPositions.filter((position) => { - const matchesMarket = currentMarket == null || position.id === currentMarket; - const subaccountNumber = position.childSubaccountNumber; - const marginType = getMarginModeFromSubaccountNumber(subaccountNumber).name; - const matchesType = marketTypeMatchesFilter(marginType, marketTypeFilter); + const matchesMarket = currentMarket == null || position.market === currentMarket; + const marginType = position.marginMode; + const matchesType = marginModeMatchesFilter(marginType, marketTypeFilter); return matchesMarket && matchesType; }); }, [currentMarket, marketTypeFilter, openPositions]); const conditionalOrderSelector = useMemo(getSubaccountConditionalOrders, []); + // todo calculate this too const { stopLossOrders: allStopLossOrders, takeProfitOrders: allTakeProfitOrders } = useAppSelector((s) => conditionalOrderSelector(s, isSlTpLimitOrdersEnabled), { equalityFn: (oldVal, newVal) => { @@ -587,18 +516,24 @@ export const PositionsTable = forwardRef( {}, { tickSizeDecimals: - perpetualMarkets[position.id]?.configs?.tickSizeDecimals ?? USD_DECIMALS, + MaybeBigNumber(perpetualMarkets[position.market]?.tickSize)?.decimalPlaces() ?? + USD_DECIMALS, asset: assets[position.assetId], - oraclePrice: perpetualMarkets[position.id]?.oraclePrice, - fundingRate: perpetualMarkets[position.id]?.perpetual?.nextFundingRate, + oraclePrice: MaybeBigNumber(perpetualMarkets[position.market]?.oraclePrice), + fundingRate: MaybeBigNumber(perpetualMarkets[position.market]?.nextFundingRate), stopLossOrders: allStopLossOrders.filter( - (order: SubaccountOrder) => order.marketId === position.id + (order: SubaccountOrder) => + order.marketId === position.market && + order.subaccountNumber === position.subaccountNumber ), takeProfitOrders: allTakeProfitOrders.filter( - (order: SubaccountOrder) => order.marketId === position.id + (order: SubaccountOrder) => + order.marketId === position.market && + order.subaccountNumber === position.subaccountNumber ), stepSizeDecimals: - perpetualMarkets[position.id]?.configs?.stepSizeDecimals ?? TOKEN_DECIMALS, + MaybeBigNumber(perpetualMarkets[position.market]?.stepSize)?.decimalPlaces() ?? + TOKEN_DECIMALS, }, position ); @@ -623,19 +558,19 @@ export const PositionsTable = forwardRef( isTablet, }) )} - getRowKey={(row: PositionTableRow) => row.id} + getRowKey={(row: PositionTableRow) => row.uniqueId} onRowAction={ currentMarket ? undefined - : (market: Key) => { - navigate(`${AppRoute.Trade}/${market}`, { + : (id, row) => { + navigate(`${AppRoute.Trade}/${row.market}`, { state: { from: currentRoute }, }); onNavigate?.(); } } getRowAttributes={(row: PositionTableRow) => ({ - 'data-side': row.side.current, + 'data-side': row.side, })} slotEmpty={ <> @@ -668,12 +603,12 @@ const $Table = styled(Table)` overflow: hidden; position: relative; - &[data-side='${PositionSide.Long}'] { + &[data-side='${IndexerPositionSide.LONG}'] { --side-color: var(--color-positive); --table-row-gradient-to-color: var(--color-gradient-positive); } - &[data-side='${PositionSide.Short}'] { + &[data-side='${IndexerPositionSide.SHORT}'] { --side-color: var(--color-negative); --table-row-gradient-to-color: var(--color-gradient-negative); } diff --git a/src/views/tables/PositionsTable/PositionsActionsCell.tsx b/src/views/tables/PositionsTable/PositionsActionsCell.tsx index 0354db53b..d2f2458d6 100644 --- a/src/views/tables/PositionsTable/PositionsActionsCell.tsx +++ b/src/views/tables/PositionsTable/PositionsActionsCell.tsx @@ -1,7 +1,9 @@ +import { IndexerPositionSide } from '@/types/indexer/indexerApiGen'; +import BigNumber from 'bignumber.js'; import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; -import { AbacusPositionSides, Nullable } from '@/constants/abacus'; +import { Nullable } from '@/constants/abacus'; import { ButtonShape, ButtonStyle } from '@/constants/buttons'; import { DialogTypes, TradeBoxDialogTypes } from '@/constants/dialogs'; import { STRING_KEYS } from '@/constants/localization'; @@ -24,11 +26,11 @@ import abacusStateManager from '@/lib/abacus'; type ElementProps = { marketId: string; assetId: string; - leverage: Nullable; - oraclePrice: Nullable; - entryPrice: Nullable; - unrealizedPnl: Nullable; - side: Nullable; + leverage: Nullable; + oraclePrice: Nullable; + entryPrice: Nullable; + unrealizedPnl: Nullable; + side: Nullable; sideLabel: Nullable; isDisabled?: boolean; showClosePositionAction: boolean; @@ -72,10 +74,10 @@ export const PositionsActionsCell = ({ DialogTypes.SharePNLAnalytics({ marketId, assetId, - leverage, - oraclePrice, - entryPrice, - unrealizedPnl, + leverage: leverage?.toNumber(), + oraclePrice: oraclePrice?.toNumber(), + entryPrice: entryPrice?.toNumber(), + unrealizedPnl: unrealizedPnl?.toNumber(), side, sideLabel, }) diff --git a/src/views/tables/PositionsTable/PositionsMarginCell.tsx b/src/views/tables/PositionsTable/PositionsMarginCell.tsx index b4ca1e903..1c0daa1b6 100644 --- a/src/views/tables/PositionsTable/PositionsMarginCell.tsx +++ b/src/views/tables/PositionsTable/PositionsMarginCell.tsx @@ -1,8 +1,8 @@ import { useMemo } from 'react'; +import { SubaccountPosition } from '@/abacus-ts/summaryTypes'; import styled from 'styled-components'; -import { AbacusMarginMode, type SubaccountPosition } from '@/constants/abacus'; import { ButtonShape, ButtonSize } from '@/constants/buttons'; import { DialogTypes } from '@/constants/dialogs'; import { STRING_KEYS } from '@/constants/localization'; @@ -18,8 +18,6 @@ import { WithTooltip } from '@/components/WithTooltip'; import { useAppDispatch } from '@/state/appTypes'; import { openDialog } from '@/state/dialogs'; -import { getMarginModeFromSubaccountNumber, getPositionMargin } from '@/lib/tradeData'; - type PositionsMarginCellProps = { position: SubaccountPosition; }; @@ -29,23 +27,22 @@ export const PositionsMarginCell = ({ position }: PositionsMarginCellProps) => { const dispatch = useAppDispatch(); const { marginMode, margin } = useMemo(() => { - const { childSubaccountNumber } = position; - const derivedMarginMode = getMarginModeFromSubaccountNumber(childSubaccountNumber); + const { marginMode: marginModeInner, marginValueInitial } = position; return { - marginMode: derivedMarginMode, + marginMode: marginModeInner, marginModeLabel: - derivedMarginMode === AbacusMarginMode.Cross + marginModeInner === 'CROSS' ? stringGetter({ key: STRING_KEYS.CROSS }) : stringGetter({ key: STRING_KEYS.ISOLATED }), - margin: getPositionMargin({ position }), + margin: marginValueInitial, }; }, [position, stringGetter]); return ( <$EditButton key="edit-margin" @@ -53,7 +50,10 @@ export const PositionsMarginCell = ({ position }: PositionsMarginCellProps) => { shape={ButtonShape.Square} size={ButtonSize.XSmall} onClick={() => - dispatch(openDialog(DialogTypes.AdjustIsolatedMargin({ positionId: position.id }))) + // todo this handoff should be using uniqueid + dispatch( + openDialog(DialogTypes.AdjustIsolatedMargin({ positionId: position.market })) + ) } /> diff --git a/src/views/tables/PositionsTable/PositionsTriggersCell.tsx b/src/views/tables/PositionsTable/PositionsTriggersCell.tsx index c61322263..f709f4d63 100644 --- a/src/views/tables/PositionsTable/PositionsTriggersCell.tsx +++ b/src/views/tables/PositionsTable/PositionsTriggersCell.tsx @@ -1,12 +1,9 @@ +import { IndexerPositionSide } from '@/types/indexer/indexerApiGen'; import { Separator } from '@radix-ui/react-separator'; +import BigNumber from 'bignumber.js'; import styled, { css } from 'styled-components'; -import { - AbacusPositionSide, - Nullable, - type AbacusPositionSides, - type SubaccountOrder, -} from '@/constants/abacus'; +import { Nullable, type SubaccountOrder } from '@/constants/abacus'; import { ButtonAction, ButtonShape, ButtonSize, ButtonStyle } from '@/constants/buttons'; import { ComplianceStates } from '@/constants/compliance'; import { DialogTypes } from '@/constants/dialogs'; @@ -35,12 +32,12 @@ type ElementProps = { marketId: string; assetId: string; tickSizeDecimals: number; - liquidationPrice: Nullable; + liquidationPrice: Nullable; stopLossOrders: SubaccountOrder[]; takeProfitOrders: SubaccountOrder[]; onViewOrdersClick: (marketId: string) => void; - positionSide: Nullable; - positionSize: Nullable; + positionSide: Nullable; + positionSize: Nullable; isDisabled?: boolean; }; @@ -69,10 +66,10 @@ export const PositionsTriggersCell = ({ return false; } return ( - (positionSide === AbacusPositionSide.SHORT && - (order.triggerPrice ?? order.price) > liquidationPrice) || - (positionSide === AbacusPositionSide.LONG && - (order.triggerPrice ?? order.price) < liquidationPrice) + (positionSide === IndexerPositionSide.SHORT && + (order.triggerPrice ?? order.price) > liquidationPrice.toNumber()) || + (positionSide === IndexerPositionSide.LONG && + (order.triggerPrice ?? order.price) < liquidationPrice.toNumber()) ); }; @@ -141,7 +138,7 @@ export const PositionsTriggersCell = ({ const order = orders[0]!; const { size, triggerPrice } = order; - const isPartialPosition = !!(positionSize && Math.abs(size) < Math.abs(positionSize)); + const isPartialPosition = !!(positionSize && Math.abs(size) < positionSize.abs().toNumber()); const liquidationWarningSide = showLiquidationWarning(order) ? positionSide : undefined; const output = ( @@ -161,7 +158,7 @@ export const PositionsTriggersCell = ({ align="start" side="left" hovercard={ - liquidationWarningSide === AbacusPositionSide.LONG + liquidationWarningSide === IndexerPositionSide.LONG ? 'liquidation-warning-long' : 'liquidation-warning-short' }