diff --git a/packages/app/package.json b/packages/app/package.json
index 2df1a08351..45ec112b8b 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -8,7 +8,7 @@
"export": "next export",
"check-types": "tsc --noEmit",
"jest-preview": "jest-preview",
- "test:jest": "jest --coverage --detectOpenHandles",
+ "test:jest": "NODE_OPTIONS=--max-old-space-size=2048 jest --coverage",
"prepublishOnly": "pinst --disable",
"postpublish": "pinst --enable",
"storybook": "storybook dev -p 6006",
@@ -88,7 +88,7 @@
"@testing-library/user-event": "14.4.3",
"@typechain/ethers-v5": "^10.2.1",
"@types/cors": "^2.8.13",
- "@types/jest": "27.0.2",
+ "@types/jest": "^29.5.3",
"@types/lodash": "4.14.195",
"@types/node": "14.0.13",
"@types/react": "18.2.16",
@@ -102,8 +102,8 @@
"css-loader": "^6.8.1",
"eslint-config-turbo": "1.10.11",
"html-to-image": "1.11.11",
- "jest": "28.1.0",
- "jest-environment-jsdom": "28.1.0",
+ "jest": "^29.6.2",
+ "jest-environment-jsdom": "^29.6.2",
"jest-preview": "^0.3.1",
"jest-transformer-svg": "^2.0.1",
"next-router-mock": "0.9.7",
diff --git a/packages/app/src/__tests__/pages/crossMargin.test.tsx b/packages/app/src/__tests__/pages/crossMargin.test.tsx
new file mode 100644
index 0000000000..337b973f15
--- /dev/null
+++ b/packages/app/src/__tests__/pages/crossMargin.test.tsx
@@ -0,0 +1,82 @@
+import { fireEvent, render } from '@testing-library/react'
+import { ReactNode } from 'react'
+
+import { mockFuturesService } from 'state/__mocks__/sdk'
+
+import { mockResizeObserver } from '../../../testing/unit/mocks/app'
+import { createState } from '../../../testing/unit/mocks/data/app'
+import { mockUseWindowSize } from '../../../testing/unit/mocks/hooks'
+import mockConnector from '../../../testing/unit/mocks/mockConnector'
+import MockProviders from '../../../testing/unit/mocks/MockProviders'
+import { mockReactQuery } from '../../../testing/unit/mocks/queries'
+import Market from '../../pages/market'
+import sdk from '../../state/sdk'
+
+jest.mock('../../state/sdk')
+
+jest.mock('../../queries/futures/useGetFuturesTrades', () => {
+ return jest.fn(() => ({
+ data: [],
+ isLoading: false,
+ fetchNextPage: () => {},
+ }))
+})
+
+jest.mock('../../components/Media', () => ({
+ ...jest.requireActual('../../components/Media'),
+ DesktopOnlyView: ({ children }: { children: ReactNode }) =>
{children}
,
+ MobileOnlyView: ({ children }: { children: ReactNode }) => {children}
,
+}))
+
+describe('Futures market page - cross margin', () => {
+ beforeAll(() => {
+ jest.setTimeout(60000)
+ mockUseWindowSize()
+ mockReactQuery()
+ mockResizeObserver()
+ mockConnector()
+ })
+
+ beforeEach(() => {
+ // Reset the SDK mock
+ // @ts-ignore
+ sdk.futures = mockFuturesService()
+ })
+
+ test('Displays the correct trade panel', async () => {
+ const { findByTestId } = render(
+
+
+
+ )
+
+ await findByTestId('cross-margin-trade-panel')
+ })
+
+ test('Can create a new cross margin account', async () => {
+ sdk.perpsV3.getPerpsV3AccountIds = () => Promise.resolve([])
+
+ const { findByTestId, findByText } = render(
+
+
+
+ )
+
+ const newAccountBtn = await findByTestId('create-cross-margin-account-button')
+ fireEvent.click(newAccountBtn)
+
+ sdk.perpsV3.getPerpsV3AccountIds = () => Promise.resolve([100])
+
+ const submitButton = await findByText('Create Account')
+ fireEvent.click(submitButton)
+
+ // Users account has been created but there is no available margin
+ await findByText('No available margin')
+ })
+})
diff --git a/packages/app/src/__tests__/pages/market.test.tsx b/packages/app/src/__tests__/pages/smartMargin.test.tsx
similarity index 90%
rename from packages/app/src/__tests__/pages/market.test.tsx
rename to packages/app/src/__tests__/pages/smartMargin.test.tsx
index 5306df7cfe..3ea217f358 100644
--- a/packages/app/src/__tests__/pages/market.test.tsx
+++ b/packages/app/src/__tests__/pages/smartMargin.test.tsx
@@ -1,10 +1,11 @@
-import { FuturesMarket, PositionSide } from '@kwenta/sdk/types'
+import { PerpsMarketV2, PositionSide } from '@kwenta/sdk/types'
import { wei } from '@synthetixio/wei'
import { fireEvent, render, waitFor } from '@testing-library/react'
import { ReactNode } from 'react'
import { mockFuturesService } from 'state/__mocks__/sdk'
import { fetchMarkets } from 'state/futures/actions'
+import { selectTradePreview } from 'state/futures/smartMargin/selectors'
import { mockResizeObserver } from '../../../testing/unit/mocks/app'
import { PRELOADED_STATE } from '../../../testing/unit/mocks/data/app'
@@ -19,7 +20,6 @@ import mockConnector from '../../../testing/unit/mocks/mockConnector'
import MockProviders from '../../../testing/unit/mocks/MockProviders'
import { mockReactQuery } from '../../../testing/unit/mocks/queries'
import Market from '../../pages/market'
-import { selectTradePreview } from '../../state/futures/selectors'
import sdk from '../../state/sdk'
import { setupStore } from '../../state/store'
@@ -57,7 +57,7 @@ describe('Futures market page - smart margin', () => {
test('Calculates correct fees from trade preview', async () => {
const { findByTestId, findByText } = render(
@@ -76,8 +76,8 @@ describe('Futures market page - smart margin', () => {
test('Submits LONG order with correct desired fill price', async () => {
const store = setupStore(preloadedStateWithSmartMarginAccount())
- const { findByTestId, findByText } = render(
-
+ const { findByTestId, findByText, findAllByText } = render(
+
)
@@ -97,9 +97,9 @@ describe('Futures market page - smart margin', () => {
const confirmButton = await findByTestId('trade-confirm-order-button')
fireEvent.click(confirmButton)
- // Preview generated fill price displayed in confirmation view
- const fillPrice = await findByText('$1,847.76')
- expect(fillPrice).toBeTruthy()
+ // Preview generated fill price displayed in confirmation view and trade panel
+ const fillPrice = await findAllByText('$1,847.76')
+ expect(fillPrice.length).toEqual(2)
// Desired fill price is higher than fill price by 1%
// (as a long order the price is worse to account for slippage in delayed order)
@@ -110,8 +110,8 @@ describe('Futures market page - smart margin', () => {
test('Submits SHORT order with correct desired fill price', async () => {
const store = setupStore(preloadedStateWithSmartMarginAccount())
- const { findByTestId, findByText } = render(
-
+ const { findByTestId, findByText, findAllByText } = render(
+
)
@@ -134,9 +134,9 @@ describe('Futures market page - smart margin', () => {
const confirmButton = await findByTestId('trade-confirm-order-button')
fireEvent.click(confirmButton)
- // Preview generated fill price displayed in confirmation view
- const fillPrice = await findByText('$1,847.76')
- expect(fillPrice).toBeTruthy()
+ // Preview generated fill price displayed in confirmation view and trade panel
+ const fillPrice = await findAllByText('$1,847.76')
+ expect(fillPrice.length).toEqual(2)
// Desired fill price is lower than fill price by 1%
// (as a short order the price is worse to account for slippage in delayed order)
@@ -148,13 +148,13 @@ describe('Futures market page - smart margin', () => {
test('Displays error when trade exceeds max OI', async () => {
// Update the mock to return some different data
sdk.futures.getMarkets = () =>
- Promise.resolve([{ ...SDK_MARKETS[1], marketLimitUsd: wei(100000) } as FuturesMarket])
+ Promise.resolve([{ ...SDK_MARKETS[1], marketLimitUsd: wei(100000) } as PerpsMarketV2])
const store = setupStore(
preloadedStateWithSmartMarginAccount(mockSmartMarginAccount('1000000'))
)
const { findByTestId, findByText } = render(
-
+
)
@@ -171,10 +171,10 @@ describe('Futures market page - smart margin', () => {
})
test('Trade panel is disabled when market is closed', async () => {
- sdk.futures.getMarkets = () => Promise.resolve([...SDK_MARKETS] as FuturesMarket[])
+ sdk.futures.getMarkets = () => Promise.resolve([...SDK_MARKETS] as PerpsMarketV2[])
const store = setupStore(preloadedStateWithSmartMarginAccount())
const { findByTestId, findByText } = render(
-
+
)
@@ -192,7 +192,7 @@ describe('Futures market page - smart margin', () => {
expect(submitButton).toBeEnabled()
sdk.futures.getMarkets = () =>
- Promise.resolve([{ ...SDK_MARKETS[1], isSuspended: true } as FuturesMarket])
+ Promise.resolve([{ ...SDK_MARKETS[1], isSuspended: true } as PerpsMarketV2])
waitFor(() => store.dispatch(fetchMarkets()))
@@ -221,7 +221,7 @@ describe('Futures market page - stop loss validation', () => {
test('Restricts stop loss for LONG trade at correct price depending on leverage', async () => {
const store = setupStore(preloadedStateWithSmartMarginAccount())
const { findByTestId, findByText } = render(
-
+
)
@@ -248,7 +248,7 @@ describe('Futures market page - stop loss validation', () => {
fireEvent.change(stopLossInput, { target: { value: '1700' } })
// Min / Max SL is shown when invalid
- const slMinMaxLabel = await findByText('Min: 1,701.82')
+ const slMinMaxLabel = await findByText('Min: 1,735.52')
expect(slMinMaxLabel).toBeTruthy()
expect(submitButton).toBeDisabled()
@@ -260,12 +260,12 @@ describe('Futures market page - stop loss validation', () => {
test('Restricts stop loss for SHORT trade at correct price depending on leverage', async () => {
const store = setupStore(preloadedStateWithSmartMarginAccount())
const { findByTestId, findByText } = render(
-
+
)
- sdk.futures.getCrossMarginTradePreview = () =>
+ sdk.futures.getSmartMarginTradePreview = () =>
Promise.resolve({
...MOCK_TRADE_PREVIEW,
liqPrice: wei('2172.467580351348039045'),
@@ -299,7 +299,7 @@ describe('Futures market page - stop loss validation', () => {
// Min / Max SL is shown when invalid
// Liqudation price is 2,172.46 and stop is limited to 2,172.29
- const slMinMaxLabel = await findByText('Max: 2,150.74')
+ const slMinMaxLabel = await findByText('Max: 2,107.29')
expect(slMinMaxLabel).toBeTruthy()
expect(submitButton).toBeDisabled()
@@ -312,12 +312,12 @@ describe('Futures market page - stop loss validation', () => {
test('Stop loss becomes disabled above a certain leverage', async () => {
const store = setupStore(preloadedStateWithSmartMarginAccount())
const { findByTestId, findByText } = render(
-
+
)
- sdk.futures.getCrossMarginTradePreview = () =>
+ sdk.futures.getSmartMarginTradePreview = () =>
Promise.resolve({
...MOCK_TRADE_PREVIEW,
liqPrice: wei('1795'),
@@ -352,12 +352,12 @@ describe('Futures market page - stop loss validation', () => {
test('Displays stop-loss warning in confirmation view when within 5% of liquidation price', async () => {
const store = setupStore(preloadedStateWithSmartMarginAccount())
const { findByTestId, findByText } = render(
-
+
)
- sdk.futures.getCrossMarginTradePreview = () =>
+ sdk.futures.getSmartMarginTradePreview = () =>
Promise.resolve({
...MOCK_TRADE_PREVIEW,
liqPrice: wei('2172.467580351348039045'),
diff --git a/packages/app/src/components/Nav/FuturesIcon.tsx b/packages/app/src/components/Nav/FuturesIcon.tsx
index 28725c691e..7568808f8b 100644
--- a/packages/app/src/components/Nav/FuturesIcon.tsx
+++ b/packages/app/src/components/Nav/FuturesIcon.tsx
@@ -1,4 +1,4 @@
-import { FuturesAccountType } from '@kwenta/sdk/utils'
+import { FuturesMarginType } from '@kwenta/sdk/types'
import CrossMarginIconDark from 'assets/svg/futures/cross-margin-icon-dark.svg'
import CrossMarginIconLight from 'assets/svg/futures/cross-margin-icon-light.svg'
@@ -8,7 +8,7 @@ import { useAppSelector } from 'state/hooks'
import { selectCurrentTheme } from 'state/preferences/selectors'
type IconProps = {
- type: FuturesAccountType
+ type: FuturesMarginType
}
export default function FuturesIcon(props: IconProps) {
@@ -17,7 +17,7 @@ export default function FuturesIcon(props: IconProps) {
const CrossMarginIcon = currentTheme === 'dark' ? CrossMarginIconDark : CrossMarginIconLight
const IsolatedMarginIcon =
currentTheme === 'dark' ? IsolatedMarginIconDark : IsolatedMarginIconLight
- return props.type === 'cross_margin' || props.type === 'smart_margin' ? (
+ return props.type === FuturesMarginType.SMART_MARGIN ? (
) : (
diff --git a/packages/app/src/components/Table/Table.tsx b/packages/app/src/components/Table/Table.tsx
index a4ebdf34f7..af191e46ec 100644
--- a/packages/app/src/components/Table/Table.tsx
+++ b/packages/app/src/components/Table/Table.tsx
@@ -190,9 +190,9 @@ const Table = ({
noResultsMessage
) : (
- {table.getRowModel().rows.map((row, idx) => {
+ {table.getRowModel().rows.map((row, i) => {
const localRef =
- lastRef && idx === table.getState().pagination.pageSize - 1 ? lastRef : defaultRef
+ lastRef && i === table.getState().pagination.pageSize - 1 ? lastRef : defaultRef
return (
= memo(
return (
- {props.children ?? formatNumber(value, options)}
+ {props.children ?? formatNumber(value ?? 0, options)}
{suffix}
)
diff --git a/packages/app/src/constants/defaults.ts b/packages/app/src/constants/defaults.ts
index 62c7e47883..af9ef65c54 100644
--- a/packages/app/src/constants/defaults.ts
+++ b/packages/app/src/constants/defaults.ts
@@ -1,3 +1,5 @@
+import { FuturesMarginType } from '@kwenta/sdk/types'
+
import { Language } from 'translations/constants'
// app defaults
@@ -31,10 +33,10 @@ export const DEFAULT_LEADERBOARD_ROWS = 20
// for perps v2
export const DEFAULT_DELAYED_EXECUTION_BUFFER = 15
-export const DEFAULT_DELAYED_CANCEL_BUFFER = 15
+export const DEFAULT_DELAYED_CANCEL_BUFFER = 10
-export const CROSS_MARGIN_ENABLED = true
+export const CROSS_MARGIN_ENABLED = process.env.NODE_ENV !== 'production'
-export const DEFAULT_FUTURES_MARGIN_TYPE = 'cross_margin'
+export const DEFAULT_FUTURES_MARGIN_TYPE = FuturesMarginType.SMART_MARGIN
export const DEFAULT_LEVERAGE = '1'
diff --git a/packages/app/src/constants/links.ts b/packages/app/src/constants/links.ts
index d5e8c77996..5a254fadd9 100644
--- a/packages/app/src/constants/links.ts
+++ b/packages/app/src/constants/links.ts
@@ -53,7 +53,7 @@ export const EXTERNAL_LINKS = {
Home: 'https://optimism.io/',
},
Trade: {
- PerpsV2: 'https://kwenta.eth.limo/market/?accountType=cross_margin&asset=sETH',
+ PerpsV2: 'https://kwenta.eth.limo/market/?accountType=smart_margin&asset=sETH',
Spot: 'https://kwenta.eth.limo/exchange/',
V1: 'https://v1.kwenta.eth.limo/dashboard',
},
diff --git a/packages/app/src/constants/queryKeys.ts b/packages/app/src/constants/queryKeys.ts
index 7d04daf240..d2fe7ac2e2 100644
--- a/packages/app/src/constants/queryKeys.ts
+++ b/packages/app/src/constants/queryKeys.ts
@@ -1,5 +1,5 @@
import { Period } from '@kwenta/sdk/constants'
-import { NetworkId, FuturesAccountType, FuturesMarketAsset } from '@kwenta/sdk/types'
+import { NetworkId, FuturesMarketAsset } from '@kwenta/sdk/types'
import { CurrencyKey } from './currency'
@@ -239,25 +239,6 @@ export const QUERY_KEYS = {
TotalLiquidations: ['futures', 'totalLiquidations'],
TotalTrades: (networkId: NetworkId) => ['futures', 'totalTrades', networkId],
TotalVolume: ['futures', 'totalVolume'],
- PotentialTrade: (
- networkId: NetworkId,
- market: string | null,
- tradeSize: string,
- walletAddress: string,
- selectedAccountType: FuturesAccountType,
- marginDelta: string,
- leverageSide: string
- ) => [
- 'futures',
- 'potentialTrade',
- tradeSize,
- networkId,
- market,
- walletAddress,
- selectedAccountType,
- marginDelta,
- leverageSide,
- ],
MarketLimit: (networkId: NetworkId, market: string | null) => [
'futures',
'marketLimit',
diff --git a/packages/app/src/constants/routes.ts b/packages/app/src/constants/routes.ts
index 1696439923..5858d3e27b 100644
--- a/packages/app/src/constants/routes.ts
+++ b/packages/app/src/constants/routes.ts
@@ -1,4 +1,6 @@
-import { FuturesAccountType, FuturesMarketAsset } from '@kwenta/sdk/types'
+import { FuturesMarketAsset } from '@kwenta/sdk/types'
+
+import { AppFuturesMarginType } from 'state/futures/common/types'
import { EXTERNAL_LINKS } from './links'
@@ -32,22 +34,23 @@ export const ROUTES = {
Into: (currencyKey: string) => `/exchange/?quote=${currencyKey}`,
},
Markets: {
- Home: (accountType: FuturesAccountType) => formatUrl('/market', { accountType, asset: 'sETH' }),
- MarketPair: (asset: FuturesMarketAsset | string, accountType: FuturesAccountType) =>
+ Home: (accountType: AppFuturesMarginType) =>
+ formatUrl('/market', { accountType, asset: 'sETH' }),
+ MarketPair: (asset: FuturesMarketAsset | string, accountType: AppFuturesMarginType) =>
formatUrl('/market', { asset, accountType }),
- Position: (asset: FuturesMarketAsset, accountType: FuturesAccountType) =>
+ Position: (asset: FuturesMarketAsset, accountType: AppFuturesMarginType) =>
formatUrl('/market', {
asset,
accountType,
tab: 'position',
}),
- Orders: (asset: FuturesMarketAsset, accountType: FuturesAccountType) =>
+ Orders: (asset: FuturesMarketAsset, accountType: AppFuturesMarginType) =>
formatUrl('/market', { asset, accountType, tab: 'orders' }),
- ConditionalOrders: (asset: FuturesMarketAsset, accountType: FuturesAccountType) =>
+ ConditionalOrders: (asset: FuturesMarketAsset, accountType: AppFuturesMarginType) =>
formatUrl('/market', { asset, accountType, tab: 'conditional_orders' }),
- Trades: (asset: FuturesMarketAsset, accountType: FuturesAccountType) =>
+ Trades: (asset: FuturesMarketAsset, accountType: AppFuturesMarginType) =>
formatUrl('/market', { asset, accountType, tab: 'trades' }),
- Transfers: (asset: FuturesMarketAsset, accountType: FuturesAccountType) =>
+ Transfers: (asset: FuturesMarketAsset, accountType: AppFuturesMarginType) =>
formatUrl('/market', { asset, accountType, tab: 'transfers' }),
},
Stats: {
@@ -75,7 +78,10 @@ export const SUB_MENUS = {
],
}
-export const setLastVisited = (baseCurrencyPair: string, accountType: FuturesAccountType): void => {
+export const setLastVisited = (
+ baseCurrencyPair: string,
+ accountType: AppFuturesMarginType
+): void => {
localStorage.setItem('lastVisited', ROUTES.Markets.MarketPair(baseCurrencyPair, accountType))
}
diff --git a/packages/app/src/hooks/useAverageEntryPrice.ts b/packages/app/src/hooks/useAverageEntryPrice.ts
index 7741825418..bd6666a99c 100644
--- a/packages/app/src/hooks/useAverageEntryPrice.ts
+++ b/packages/app/src/hooks/useAverageEntryPrice.ts
@@ -1,7 +1,7 @@
import { FuturesPositionHistory, PositionSide } from '@kwenta/sdk/types'
import { useMemo } from 'react'
-import { selectTradePreview } from 'state/futures/selectors'
+import { selectTradePreview } from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
// Used to calculate the new average entry price of a modified position
diff --git a/packages/app/src/hooks/usePerpsContracts.ts b/packages/app/src/hooks/usePerpsContracts.ts
index 46229ae23d..6ef437ae49 100644
--- a/packages/app/src/hooks/usePerpsContracts.ts
+++ b/packages/app/src/hooks/usePerpsContracts.ts
@@ -2,20 +2,20 @@ import { PerpsV2Market, PerpsV2Market__factory } from '@kwenta/sdk/types'
import { useMemo } from 'react'
import Connector from 'containers/Connector'
-import { selectMarketInfo } from 'state/futures/selectors'
+import { selectV2MarketInfo } from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
export default function usePerpsContracts(): {
perpsMarketContract: PerpsV2Market | null
} {
const { signer } = Connector.useContainer()
- const marketInfo = useAppSelector(selectMarketInfo)
+ const marketInfo = useAppSelector(selectV2MarketInfo)
const perpsMarketContract = useMemo(() => {
- if (!signer || !marketInfo?.market) return null
+ if (!signer || !marketInfo?.marketAddress) return null
- return PerpsV2Market__factory.connect(marketInfo.market, signer)
- }, [signer, marketInfo?.market])
+ return PerpsV2Market__factory.connect(marketInfo.marketAddress, signer)
+ }, [signer, marketInfo?.marketAddress])
return { perpsMarketContract }
}
diff --git a/packages/app/src/hooks/useStatsData.ts b/packages/app/src/hooks/useStatsData.ts
index e5545f9928..d4b2ed7e6f 100644
--- a/packages/app/src/hooks/useStatsData.ts
+++ b/packages/app/src/hooks/useStatsData.ts
@@ -2,7 +2,10 @@ import { useMemo } from 'react'
import { UseQueryResult } from 'react-query'
import useGetFile from 'queries/files/useGetFile'
-import { selectOptimismMarkPrices, selectOptimismMarkets } from 'state/futures/selectors'
+import {
+ selectOptimismMarkPrices,
+ selectOptimismMarkets,
+} from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
import { selectMinTimestamp } from 'state/stats/selectors'
diff --git a/packages/app/src/pages/market.tsx b/packages/app/src/pages/market.tsx
index 899cce6da4..2d85d6b10d 100644
--- a/packages/app/src/pages/market.tsx
+++ b/packages/app/src/pages/market.tsx
@@ -1,44 +1,49 @@
-import { FuturesMarketAsset } from '@kwenta/sdk/types'
+import { FuturesMarginType, FuturesMarketAsset } from '@kwenta/sdk/types'
import { MarketKeyByAsset } from '@kwenta/sdk/utils'
import { useRouter } from 'next/router'
-import { useEffect, FC, useState, ReactNode } from 'react'
+import { useEffect, FC, ReactNode, useMemo } from 'react'
import styled from 'styled-components'
import Loader from 'components/Loader'
import { DesktopOnlyView, MobileOrTabletView } from 'components/Media'
+import { CROSS_MARGIN_ENABLED } from 'constants/defaults'
import Connector from 'containers/Connector'
import useIsL2 from 'hooks/useIsL2'
import useWindowSize from 'hooks/useWindowSize'
+import CloseCrossMarginPositionModal from 'sections/futures/ClosePositionModal/CloseCrossMarginPositionModal'
import ClosePositionModal from 'sections/futures/ClosePositionModal/ClosePositionModal'
-import CrossMarginOnboard from 'sections/futures/CrossMarginOnboard'
+import CreatePerpsV3AccountModal from 'sections/futures/CreatePerpsV3AccountModal'
import EditPositionMarginModal from 'sections/futures/EditPositionModal/EditPositionMarginModal'
import EditPositionSizeModal from 'sections/futures/EditPositionModal/EditPositionSizeModal'
import EditStopLossAndTakeProfitModal from 'sections/futures/EditPositionModal/EditStopLossAndTakeProfitModal'
import MarketInfo from 'sections/futures/MarketInfo'
import MarketHead from 'sections/futures/MarketInfo/MarketHead'
import MobileTrade from 'sections/futures/MobileTrade/MobileTrade'
+import SmartMarginOnboard from 'sections/futures/SmartMarginOnboard'
import { TRADE_PANEL_WIDTH_LG, TRADE_PANEL_WIDTH_MD } from 'sections/futures/styles'
+import DepositWithdrawCrossMarginModal from 'sections/futures/Trade/DepositWithdrawCrossMargin'
import FuturesUnsupportedNetwork from 'sections/futures/Trade/FuturesUnsupported'
-import SwitchToSmartMargin from 'sections/futures/Trade/SwitchToSmartMargin'
-import TradeIsolatedMargin from 'sections/futures/Trade/TradePanel'
-import TransferIsolatedMarginModal from 'sections/futures/Trade/TransferIsolatedMarginModal'
+import TradePanelCrossMargin from 'sections/futures/Trade/TradePanelCrossMargin'
+import TradePanelSmartMargin from 'sections/futures/Trade/TradePanelSmartMargin'
import TransferSmartMarginModal from 'sections/futures/Trade/TransferSmartMarginModal'
-import DelayedOrderConfirmationModal from 'sections/futures/TradeConfirmation/DelayedOrderConfirmationModal'
+import DelayedOrderConfirmationModal from 'sections/futures/TradeConfirmation/CrossMarginOrderConfirmation'
import TradeConfirmationModalCrossMargin from 'sections/futures/TradeConfirmation/TradeConfirmationModalCrossMargin'
import AppLayout from 'sections/shared/Layout/AppLayout'
import { setOpenModal } from 'state/app/reducer'
import { selectShowModal, selectShowPositionModal } from 'state/app/selectors'
import { clearTradeInputs } from 'state/futures/actions'
+import { selectFuturesType, selectMarketAsset } from 'state/futures/common/selectors'
+import { AppFuturesMarginType } from 'state/futures/common/types'
+import { selectCrossMarginSupportedNetwork } from 'state/futures/crossMargin/selectors'
+import { selectShowCrossMarginOnboard } from 'state/futures/crossMargin/selectors'
import { usePollMarketFuturesData } from 'state/futures/hooks'
-import { setFuturesAccountType, setMarketAsset } from 'state/futures/reducer'
+import { setFuturesAccountType } from 'state/futures/reducer'
+import { setMarketAsset } from 'state/futures/smartMargin/reducer'
import {
- selectActiveIsolatedPositionsCount,
- selectCMAccountQueryStatus,
- selectCrossMarginAccount,
- selectFuturesType,
- selectMarketAsset,
- selectShowCrossMarginOnboard,
-} from 'state/futures/selectors'
+ selectShowSmartMarginOnboard,
+ selectSmartMarginAccount,
+ selectSmartMarginAccountQueryStatus,
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import { FetchStatus } from 'state/types'
import { PageContent } from 'styles/common'
@@ -56,14 +61,24 @@ const Market: MarketComponent = () => {
const routerMarketAsset = router.query.asset as FuturesMarketAsset
const setCurrentMarket = useAppSelector(selectMarketAsset)
- const showOnboard = useAppSelector(selectShowCrossMarginOnboard)
+ const showOnboard = useAppSelector(selectShowSmartMarginOnboard)
+ const showCrossMarginOnboard = useAppSelector(selectShowCrossMarginOnboard)
const openModal = useAppSelector(selectShowModal)
const showPositionModal = useAppSelector(selectShowPositionModal)
const accountType = useAppSelector(selectFuturesType)
const selectedMarketAsset = useAppSelector(selectMarketAsset)
+ const crossMarginSupportedNetwork = useAppSelector(selectCrossMarginSupportedNetwork)
- const routerAccountType =
- router.query.accountType === 'cross_margin' ? 'cross_margin' : 'isolated_margin'
+ const routerAccountType = useMemo(() => {
+ if (
+ router.query.accountType === 'cross_margin' &&
+ crossMarginSupportedNetwork &&
+ CROSS_MARGIN_ENABLED
+ ) {
+ return router.query.accountType as AppFuturesMarginType
+ }
+ return FuturesMarginType.SMART_MARGIN
+ }, [router.query.accountType, crossMarginSupportedNetwork])
useEffect(() => {
if (router.isReady && accountType !== routerAccountType) {
@@ -92,7 +107,8 @@ const Market: MarketComponent = () => {
return (
<>
-
+
+
{lessThanWidth('lg') ? (
@@ -115,19 +131,22 @@ const Market: MarketComponent = () => {
- {showPositionModal?.type === 'futures_close_position' && }
+ {showPositionModal?.type === 'smart_margin_close_position' && }
+ {showPositionModal?.type === 'cross_margin_close_position' && (
+
+ )}
{showPositionModal?.type === 'futures_edit_stop_loss_take_profit' && (
)}
{showPositionModal?.type === 'futures_edit_position_size' && }
{showPositionModal?.type === 'futures_edit_position_margin' && }
- {openModal === 'futures_isolated_transfer' && (
- dispatch(setOpenModal(null))}
/>
)}
- {openModal === 'futures_cross_withdraw' && (
+ {openModal === 'futures_deposit_withdraw_smart_margin' && (
dispatch(setOpenModal(null))}
@@ -135,7 +154,7 @@ const Market: MarketComponent = () => {
)}
{openModal === 'futures_confirm_smart_margin_trade' && }
- {openModal === 'futures_confirm_isolated_margin_trade' && }
+ {openModal === 'futures_confirm_cross_margin_trade' && }
>
)
}
@@ -145,31 +164,24 @@ function TradePanelDesktop() {
const isL2 = useIsL2()
const { walletAddress } = Connector.useContainer()
const accountType = useAppSelector(selectFuturesType)
- const queryStatus = useAppSelector(selectCMAccountQueryStatus)
+ const queryStatus = useAppSelector(selectSmartMarginAccountQueryStatus)
+ const smartMarginAccount = useAppSelector(selectSmartMarginAccount)
const openModal = useAppSelector(selectShowModal)
- const crossMarginAccount = useAppSelector(selectCrossMarginAccount)
- const isolatedPositionsCount = useAppSelector(selectActiveIsolatedPositionsCount)
- const [open, setOpen] = useState(false)
-
- useEffect(
- () => setOpen(accountType === 'isolated_margin' && isolatedPositionsCount === 0),
- [accountType, isolatedPositionsCount]
- )
if (
walletAddress &&
!isL2 &&
openModal !== 'futures_smart_margin_socket' &&
- openModal !== 'futures_cross_withdraw'
+ openModal !== 'futures_deposit_withdraw_smart_margin'
) {
return
}
if (
!router.isReady ||
- (accountType === 'cross_margin' &&
+ (accountType === FuturesMarginType.SMART_MARGIN &&
walletAddress &&
- !crossMarginAccount &&
+ !smartMarginAccount &&
queryStatus.status === FetchStatus.Idle)
) {
return (
@@ -179,7 +191,11 @@ function TradePanelDesktop() {
)
}
- return open ? setOpen(false)} /> :
+ return accountType === FuturesMarginType.CROSS_MARGIN ? (
+
+ ) : (
+
+ )
}
Market.getLayout = (page) => {page}
diff --git a/packages/app/src/queries/futures/types.ts b/packages/app/src/queries/futures/types.ts
index 7cfbb4f778..752ae6993e 100644
--- a/packages/app/src/queries/futures/types.ts
+++ b/packages/app/src/queries/futures/types.ts
@@ -1,14 +1,5 @@
import Wei from '@synthetixio/wei'
-export type FuturesStat = {
- account: string
- pnlWithFeesPaid: Wei
- liquidations: Wei
- totalTrades: Wei
- totalVolume: Wei
- pnl?: Wei
-}
-
export type AccountStat = {
rank: number
account: string
@@ -32,8 +23,3 @@ export type FuturesCumulativeStats = {
totalLiquidations: string
averageTradeSize: string
}
-
-export enum FuturesAccountTypes {
- ISOLATED_MARGIN = 'isolated_margin',
- CROSS_MARGIN = 'cross_margin',
-}
diff --git a/packages/app/src/queries/synths/type.ts b/packages/app/src/queries/synths/type.ts
deleted file mode 100644
index ccc53d3625..0000000000
--- a/packages/app/src/queries/synths/type.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import Wei from '@synthetixio/wei'
-
-export type SynthsVolumes = {
- [asset: string]: Wei
-}
diff --git a/packages/app/src/queries/synths/useGetWalletTrades.ts b/packages/app/src/queries/synths/useGetWalletTrades.ts
deleted file mode 100644
index d6b24f2181..0000000000
--- a/packages/app/src/queries/synths/useGetWalletTrades.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { NetworkId } from '@kwenta/sdk/types'
-import { getMainEndpoint } from '@kwenta/sdk/utils'
-import request, { gql } from 'graphql-request'
-import { useQuery, UseQueryOptions } from 'react-query'
-
-import QUERY_KEYS from 'constants/queryKeys'
-import Connector from 'containers/Connector'
-import logError from 'utils/logError'
-
-import { SynthsVolumes } from './type'
-
-const useGetWalletTrades = (
- walletAddress: string,
- options?: UseQueryOptions
-) => {
- const { network } = Connector.useContainer()
- const synthsEndpoint = getMainEndpoint(network?.id as NetworkId)
-
- return useQuery(
- QUERY_KEYS.Trades.WalletTrades(walletAddress, network?.id as NetworkId),
- async () => {
- try {
- const response = await request(
- synthsEndpoint,
- gql`
- query WalletTrades($walletAddress: String!) {
- synthExchanges(
- where: { account: $walletAddress }
- first: 1000
- orderBy: "timestamp"
- orderDirection: "desc"
- ) {
- id
- fromAmount
- fromAmountInUSD
- fromSynth {
- name
- symbol
- id
- }
- toSynth {
- name
- symbol
- id
- }
- toAmount
- toAmountInUSD
- feesInUSD
- toAddress
- timestamp
- gasPrice
- }
- }
- `,
- { walletAddress: walletAddress.toLowerCase() }
- )
-
- return response
- } catch (e) {
- logError(e)
- return null
- }
- },
- { enabled: !!walletAddress, ...options }
- )
-}
-
-export default useGetWalletTrades
diff --git a/packages/app/src/sections/dashboard/FuturesHistoryTable.tsx b/packages/app/src/sections/dashboard/FuturesHistoryTable.tsx
new file mode 100644
index 0000000000..5397678b11
--- /dev/null
+++ b/packages/app/src/sections/dashboard/FuturesHistoryTable.tsx
@@ -0,0 +1,414 @@
+import { FuturesMarketAsset, FuturesTrade } from '@kwenta/sdk/types'
+import {
+ MarketKeyByAsset,
+ getDisplayAsset,
+ formatCryptoCurrency,
+ formatDollars,
+ formatShortDateWithoutYear,
+ getMarketName,
+} from '@kwenta/sdk/utils'
+import { wei } from '@synthetixio/wei'
+import * as _ from 'lodash/fp'
+import Link from 'next/link'
+import { FC, useMemo, ReactElement, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
+
+import Currency from 'components/Currency'
+import { DesktopOnlyView, MobileOrTabletView } from 'components/Media'
+import FuturesIcon from 'components/Nav/FuturesIcon'
+import Table, { TableNoResults } from 'components/Table'
+import { Body } from 'components/Text'
+import { ETH_UNIT } from 'constants/network'
+import { NO_VALUE } from 'constants/placeholder'
+import ROUTES from 'constants/routes'
+import useIsL2 from 'hooks/useIsL2'
+import useNetworkSwitcher from 'hooks/useNetworkSwitcher'
+import useSelectedPriceCurrency from 'hooks/useSelectedPriceCurrency'
+import TradeDrawer from 'sections/futures/MobileTrade/drawers/TradeDrawer'
+import PositionType from 'sections/futures/PositionType'
+import { TradeStatus } from 'sections/futures/types'
+import { selectFuturesType } from 'state/futures/common/selectors'
+import { selectAllUsersTrades, selectQueryStatuses } from 'state/futures/selectors'
+import { useAppSelector } from 'state/hooks'
+import { FetchStatus } from 'state/types'
+
+import TimeDisplay from '../futures/Trades/TimeDisplay'
+
+const conditionalRender = (prop: T, children: ReactElement) =>
+ _.isNil(prop) ? {NO_VALUE} : children
+
+const FuturesHistoryTable: FC = () => {
+ const [selectedTrade, setSelectedTrade] = useState()
+ const { t } = useTranslation()
+ const isL2 = useIsL2()
+ const { selectPriceCurrencyRate, selectedPriceCurrency } = useSelectedPriceCurrency()
+ const { switchToL2 } = useNetworkSwitcher()
+
+ const accountType = useAppSelector(selectFuturesType)
+ const trades = useAppSelector(selectAllUsersTrades)
+ const { trades: tradesQueryStatus } = useAppSelector(selectQueryStatuses)
+
+ const mappedHistoricalTrades = useMemo(
+ () =>
+ isL2
+ ? trades
+ .map((trade) => {
+ const pnl = trade.pnl.div(ETH_UNIT)
+ const feesPaid = trade.feesPaid.div(ETH_UNIT)
+ const netPnl = pnl.sub(feesPaid)
+ return {
+ ...trade,
+ pnl,
+ feesPaid,
+ netPnl,
+ displayAsset: getDisplayAsset(trade.asset),
+ market: getMarketName(trade.asset),
+ price: trade.price.div(ETH_UNIT),
+ size: trade.size.div(ETH_UNIT).abs(),
+ timestamp: trade.timestamp * 1000,
+ date: formatShortDateWithoutYear(new Date(trade.timestamp * 1000)),
+ id: trade.txnHash,
+ status: trade.positionClosed ? TradeStatus.CLOSED : TradeStatus.OPEN,
+ }
+ })
+ .sort((a, b) => b.timestamp - a.timestamp)
+ : [],
+ [isL2, trades]
+ )
+
+ return (
+ <>
+
+
+
+ {t('common.l2-cta')}
+ {t('homepage.l2.cta-buttons.switch-l2')}
+
+ ) : (
+
+ {t('dashboard.history.futures-history-table.no-result')}
+
+ {t('common.perp-cta')}
+
+
+ )
+ }
+ highlightRowsOnHover
+ columns={[
+ {
+ header: () => {t('dashboard.history.futures-history-table.date-time')}
,
+ accessorKey: 'dateTime',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.timestamp,
+
+
+
+ )
+ },
+ size: 100,
+ },
+ {
+ header: () => {t('dashboard.history.futures-history-table.market')}
,
+ accessorKey: 'market',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.asset,
+ <>
+ {cellProps.row.original.asset && (
+
+
+ {cellProps.getValue()}
+
+
+ )}
+ >
+ )
+ },
+ size: 120,
+ },
+ {
+ header: () => {t('dashboard.history.futures-history-table.side')}
,
+ accessorKey: 'side',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.side,
+
+ )
+ },
+ size: 70,
+ },
+ {
+ header: () => {t('dashboard.history.futures-history-table.size')}
,
+ accessorKey: 'size',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.size,
+ <>{formatCryptoCurrency(cellProps.getValue(), { suggestDecimals: true })}>
+ )
+ },
+ size: 100,
+ },
+ {
+ header: () => {t('dashboard.history.futures-history-table.price')}
,
+ accessorKey: 'price',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.price,
+ <>{formatDollars(cellProps.getValue(), { suggestDecimals: true })}>
+ )
+ },
+ size: 120,
+ },
+ {
+ header: () => {t('dashboard.history.futures-history-table.pnl')}
,
+ accessorKey: 'netPnl',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.getValue(),
+ cellProps.getValue().eq(wei(0)) ? (
+ --
+ ) : (
+
+ {formatDollars(cellProps.getValue(), { maxDecimals: 2 })}
+
+ )
+ )
+ },
+ size: 120,
+ },
+ {
+ header: () => {t('dashboard.history.futures-history-table.fees')}
,
+ accessorKey: 'fees',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.feesPaid,
+
+ )
+ },
+ size: 120,
+ },
+ {
+ header: () => {t('dashboard.history.futures-history-table.type')}
,
+ accessorKey: 'orderType',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.orderType,
+ {cellProps.row.original.orderType}
+ )
+ },
+ size: 80,
+ },
+ ]}
+ />
+
+
+
+
+ {
+ setSelectedTrade(row.original)
+ }}
+ isLoading={tradesQueryStatus.status === FetchStatus.Loading}
+ noResultsMessage={
+ !isL2 ? (
+
+ {t('common.l2-cta')}
+ {t('homepage.l2.cta-buttons.switch-l2')}
+
+ ) : (
+
+ {t('dashboard.history.futures-history-table.no-result')}
+
+ {t('common.perp-cta')}
+
+
+ )
+ }
+ columns={[
+ {
+ header: () => {t('dashboard.history.futures-history-table.asset')}
,
+ accessorKey: 'displayAsset',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.asset,
+ <>
+ {cellProps.row.original.asset && (
+
+
+
+ {cellProps.getValue()}
+
+
+ {cellProps.row.original.date}
+
+ )}
+ >
+ )
+ },
+ size: 60,
+ },
+ {
+ header: () => (
+
+
{t('dashboard.history.futures-history-table.side')}
+
{t('dashboard.history.futures-history-table.type')}
+
+ ),
+ accessorKey: 'side',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.side,
+
+
+
{cellProps.row.original.orderType}
+
+ )
+ },
+ size: 60,
+ },
+ {
+ header: () => (
+
+
{t('dashboard.history.futures-history-table.size')}
+
{t('dashboard.history.futures-history-table.price')}
+
+ ),
+ accessorKey: 'size',
+ cell: (cellProps) => {
+ return conditionalRender(
+ cellProps.row.original.price,
+
+
+ {formatCryptoCurrency(cellProps.getValue(), { suggestDecimals: true })}
+
+
{formatDollars(cellProps.row.original.price ?? 0)}
+
+ )
+ },
+ size: 60,
+ },
+ {
+ header: () => {t('dashboard.history.futures-history-table.pnl')}
,
+ accessorKey: 'netPnl',
+ cell: (cellProps) => {
+ const value = cellProps.getValue()
+
+ return conditionalRender(
+ value,
+ value.eq(wei(0)) ? (
+ --
+ ) : (
+
+ {formatDollars(value, { maxDecimals: 2 })}
+
+ )
+ )
+ },
+ size: 60,
+ },
+ ]}
+ />
+
+ setSelectedTrade(undefined)} />
+
+ >
+ )
+}
+
+const StyledTimeDisplay = styled.div`
+ div {
+ margin-left: 2px;
+ }
+`
+
+const StyledCurrencyIcon = styled(Currency.Icon)`
+ width: 30px;
+ height: 30px;
+`
+
+const MobileStyledCurrencyIcon = styled(Currency.Icon)`
+ grid-row: 1 / span 2;
+ width: 20px;
+ height: 20px;
+`
+
+const TableContainer = styled.div`
+ margin-top: 16px;
+ margin-bottom: 40px;
+ .paused {
+ color: ${(props) => props.theme.colors.common.secondaryGray};
+ }
+`
+
+const StyledTable = styled(Table)`
+ margin-bottom: 20px;
+` as typeof Table
+
+const MobileStyledTable = styled(Table)`
+ margin-bottom: 20px;
+ border-radius: initial;
+ border-top: none;
+ border-left: none;
+ border-right: none;
+` as typeof Table
+
+const StyledText = styled.div`
+ color: ${(props) => props.theme.colors.selectedTheme.button.text.primary};
+`
+
+const SynthContainer = styled.div`
+ display: flex;
+ align-items: center;
+ column-gap: 5px;
+ margin-left: -4px;
+`
+
+const MobileSynthContainer = styled.div`
+ display: grid;
+ align-items: center;
+ grid-template-columns: repeat(2, auto);
+ grid-template-rows: repeat(2, auto);
+ column-gap: 5px;
+ margin-left: -4px;
+`
+
+const MobileMarketContainer = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 5px;
+`
+
+const PNL = styled.div<{ negative?: boolean; normal?: boolean }>`
+ color: ${(props) =>
+ props.normal
+ ? props.theme.colors.selectedTheme.button.text
+ : props.negative
+ ? props.theme.colors.selectedTheme.red
+ : props.theme.colors.selectedTheme.green};
+`
+
+export default FuturesHistoryTable
diff --git a/packages/app/src/sections/dashboard/FuturesMarketsTable.tsx b/packages/app/src/sections/dashboard/FuturesMarketsTable.tsx
index 00d13b64ad..e38010e569 100644
--- a/packages/app/src/sections/dashboard/FuturesMarketsTable.tsx
+++ b/packages/app/src/sections/dashboard/FuturesMarketsTable.tsx
@@ -1,4 +1,4 @@
-import { FuturesMarketAsset } from '@kwenta/sdk/types'
+import { FuturesMarket, FuturesMarketAsset } from '@kwenta/sdk/types'
import {
AssetDisplayByAsset,
MarketKeyByAsset,
@@ -20,12 +20,8 @@ import { DesktopOnlyView, MobileOrTabletView } from 'components/Media'
import Spacer from 'components/Spacer'
import Table, { TableHeader } from 'components/Table'
import ROUTES from 'constants/routes'
-import {
- selectFuturesType,
- selectMarkets,
- selectMarketVolumes,
- selectMarkPrices,
-} from 'state/futures/selectors'
+import { selectFuturesType } from 'state/futures/common/selectors'
+import { selectMarkets, selectMarketVolumes, selectMarkPrices } from 'state/futures/selectors'
import { useAppSelector } from 'state/hooks'
import { selectPreviousDayPrices, selectOffchainPricesInfo } from 'state/prices/selectors'
import { getSynthDescription } from 'utils/futures'
@@ -50,8 +46,8 @@ const FuturesMarketsTable: React.FC = ({ search }) =>
let data = useMemo(() => {
const lowerSearch = search?.toLowerCase()
- const markets = lowerSearch
- ? futuresMarkets.filter(
+ const markets: FuturesMarket[] = lowerSearch
+ ? (futuresMarkets as FuturesMarket[]).filter(
(m) =>
m.asset.toLowerCase().includes(lowerSearch) ||
AssetDisplayByAsset[m.asset]?.toLocaleLowerCase().includes(lowerSearch)
diff --git a/packages/app/src/sections/dashboard/FuturesPositionsTable/FuturesPositionsTable.tsx b/packages/app/src/sections/dashboard/FuturesPositionsTable/FuturesPositionsTable.tsx
index 2849d3d2dc..b94ff3971c 100644
--- a/packages/app/src/sections/dashboard/FuturesPositionsTable/FuturesPositionsTable.tsx
+++ b/packages/app/src/sections/dashboard/FuturesPositionsTable/FuturesPositionsTable.tsx
@@ -1,4 +1,4 @@
-import { FuturesAccountType } from '@kwenta/sdk/utils'
+import { FuturesMarginType } from '@kwenta/sdk/types'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { FC, useMemo } from 'react'
@@ -17,19 +17,16 @@ import ROUTES from 'constants/routes'
import useIsL2 from 'hooks/useIsL2'
import useNetworkSwitcher from 'hooks/useNetworkSwitcher'
import PositionType from 'sections/futures/PositionType'
-import {
- selectCrossMarginPositions,
- selectIsolatedMarginPositions,
- selectMarkets,
- selectPositionHistory,
-} from 'state/futures/selectors'
+import { AppFuturesMarginType } from 'state/futures/common/types'
+import { selectCrossMarginPositions } from 'state/futures/crossMargin/selectors'
+import { selectSmartMarginPositions } from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
import { getSynthDescription } from 'utils/futures'
import MobilePositionRow from './MobilePositionRow'
type FuturesPositionTableProps = {
- accountType: FuturesAccountType
+ accountType: AppFuturesMarginType
showCurrentMarket?: boolean
showEmptyTable?: boolean
}
@@ -45,32 +42,27 @@ const FuturesPositionsTable: FC = ({
const isL2 = useIsL2()
- const isolatedPositions = useAppSelector(selectIsolatedMarginPositions)
const crossMarginPositions = useAppSelector(selectCrossMarginPositions)
- const positionHistory = useAppSelector(selectPositionHistory)
- const futuresMarkets = useAppSelector(selectMarkets)
+ const smartMarginPositions = useAppSelector(selectSmartMarginPositions)
let data = useMemo(() => {
- const positions = accountType === 'cross_margin' ? crossMarginPositions : isolatedPositions
+ const positions =
+ accountType === FuturesMarginType.SMART_MARGIN ? smartMarginPositions : crossMarginPositions
return positions
.map((position) => {
- const market = futuresMarkets.find((market) => market.asset === position.asset)
- const description = getSynthDescription(position.asset, t)
- const thisPositionHistory = positionHistory.find((ph) => {
- return ph.isOpen && ph.asset === position.asset
- })
+ const description = getSynthDescription(position.market.asset, t)
return {
- market: market!,
- position: position.position!,
+ market: position.market,
+ position: position,
description,
- avgEntryPrice: thisPositionHistory?.avgEntryPrice,
+ avgEntryPrice: position?.avgEntryPrice,
stopLoss: position.stopLoss?.targetPrice,
takeProfit: position.takeProfit?.targetPrice,
}
})
.filter(({ position, market }) => !!position && !!market)
- }, [accountType, isolatedPositions, crossMarginPositions, futuresMarkets, t, positionHistory])
+ }, [accountType, crossMarginPositions, smartMarginPositions, t])
return (
<>
@@ -78,7 +70,7 @@ const FuturesPositionsTable: FC = ({
router.push(ROUTES.Markets.MarketPair(row.original.market.asset, accountType))
}
@@ -93,7 +85,7 @@ const FuturesPositionsTable: FC = ({
{!showCurrentMarket ? (
t('dashboard.overview.futures-positions-table.no-result')
) : (
-
+
{t('common.perp-cta')}
)}
@@ -278,7 +270,7 @@ const FuturesPositionsTable: FC = ({
{data.length === 0 ? (
-
+
{t('common.perp-cta')}
diff --git a/packages/app/src/sections/dashboard/FuturesPositionsTable/MobilePositionRow.tsx b/packages/app/src/sections/dashboard/FuturesPositionsTable/MobilePositionRow.tsx
index 164a320ca2..ec2a31d151 100644
--- a/packages/app/src/sections/dashboard/FuturesPositionsTable/MobilePositionRow.tsx
+++ b/packages/app/src/sections/dashboard/FuturesPositionsTable/MobilePositionRow.tsx
@@ -1,8 +1,9 @@
-import { FuturesFilledPosition, FuturesMarket, PositionSide } from '@kwenta/sdk/types'
+import { FuturesMarket, PositionSide } from '@kwenta/sdk/types'
import { getMarketName, MarketKeyByAsset, formatNumber } from '@kwenta/sdk/utils'
import Wei, { wei } from '@synthetixio/wei'
import { memo, FC } from 'react'
import styled, { css } from 'styled-components'
+import { FuturesPositionTablePosition } from 'types/futures'
import { border } from 'components/Button'
import ChangePercent from 'components/ChangePercent'
@@ -15,7 +16,7 @@ import { isDecimalFour } from 'utils/futures'
type MobilePositionRowProps = {
row: {
market?: FuturesMarket
- position: FuturesFilledPosition | null
+ position: FuturesPositionTablePosition | null
avgEntryPrice?: Wei
}
onClick(): void
diff --git a/packages/app/src/sections/dashboard/MobileDashboard/OpenPositions.tsx b/packages/app/src/sections/dashboard/MobileDashboard/OpenPositions.tsx
index d17d605125..ea3dc107e4 100644
--- a/packages/app/src/sections/dashboard/MobileDashboard/OpenPositions.tsx
+++ b/packages/app/src/sections/dashboard/MobileDashboard/OpenPositions.tsx
@@ -1,3 +1,4 @@
+import { FuturesMarginType } from '@kwenta/sdk/types'
import { formatDollars } from '@kwenta/sdk/utils'
import Wei from '@synthetixio/wei'
import { useMemo, useState } from 'react'
@@ -7,10 +8,10 @@ import { ExchangeTokens } from 'types/synths'
import TabButton from 'components/Button/TabButton'
import { TabPanel } from 'components/Tab'
-import { FuturesAccountTypes } from 'queries/futures/types'
import { SectionHeader, SectionTitle } from 'sections/futures/mobile'
import { selectBalances } from 'state/balances/selectors'
-import { selectFuturesPortfolio, selectActiveSmartPositionsCount } from 'state/futures/selectors'
+import { selectFuturesPortfolio } from 'state/futures/selectors'
+import { selectActiveSmartPositionsCount } from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
import FuturesPositionsTable from '../FuturesPositionsTable'
@@ -41,7 +42,7 @@ const OpenPositions: React.FC
= ({ exchangeTokens, exchangeT
label: t('dashboard.overview.positions-tabs.smart-margin'),
badge: smartPositionsCount,
active: activePositionsTab === PositionsTab.SMART_MARGIN,
- detail: formatDollars(portfolio.crossMarginFutures),
+ detail: formatDollars(portfolio.smartMargin),
disabled: false,
onClick: () => setActivePositionsTab(PositionsTab.SMART_MARGIN),
},
@@ -58,7 +59,7 @@ const OpenPositions: React.FC = ({ exchangeTokens, exchangeT
t,
activePositionsTab,
smartPositionsCount,
- portfolio.crossMarginFutures,
+ portfolio.smartMargin,
balances.totalUSDBalance,
exchangeTokenBalances,
setActivePositionsTab,
@@ -80,7 +81,7 @@ const OpenPositions: React.FC = ({ exchangeTokens, exchangeT
-
+
diff --git a/packages/app/src/sections/dashboard/Overview.tsx b/packages/app/src/sections/dashboard/Overview.tsx
index 461a3dcf70..30cc72c3b5 100644
--- a/packages/app/src/sections/dashboard/Overview.tsx
+++ b/packages/app/src/sections/dashboard/Overview.tsx
@@ -1,5 +1,6 @@
import { ETH_ADDRESS, ETH_COINGECKO_ADDRESS, ZERO_WEI } from '@kwenta/sdk/constants'
import { SynthSymbol } from '@kwenta/sdk/data'
+import { FuturesMarginType } from '@kwenta/sdk/types'
import { formatDollars, toWei } from '@kwenta/sdk/utils'
import Wei from '@synthetixio/wei'
import { FC, useEffect, useMemo, useState } from 'react'
@@ -14,15 +15,11 @@ import { TabPanel } from 'components/Tab'
import Search from 'components/Table/Search'
import * as Text from 'components/Text'
import Connector from 'containers/Connector'
-import { FuturesAccountTypes } from 'queries/futures/types'
import { selectBalances } from 'state/balances/selectors'
import { fetchTokenList } from 'state/exchange/actions'
import { setFuturesAccountType } from 'state/futures/reducer'
-import {
- selectActiveSmartPositionsCount,
- selectFuturesPortfolio,
- selectFuturesType,
-} from 'state/futures/selectors'
+import { selectFuturesPortfolio } from 'state/futures/selectors'
+import { selectActiveSmartPositionsCount } from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector, useFetchAction } from 'state/hooks'
import sdk from 'state/sdk'
import { selectSynthsMap } from 'state/wallet/selectors'
@@ -36,7 +33,6 @@ import SynthBalancesTable from './SynthBalancesTable'
export enum PositionsTab {
SMART_MARGIN = 'smart margin',
- ISOLATED_MARGIN = 'isolated margin',
SPOT = 'spot',
}
@@ -46,21 +42,14 @@ const Overview: FC = () => {
const { t } = useTranslation()
const dispatch = useAppDispatch()
- const accountType = useAppSelector(selectFuturesType)
const balances = useAppSelector(selectBalances)
const portfolio = useAppSelector(selectFuturesPortfolio)
const smartPositionsCount = useAppSelector(selectActiveSmartPositionsCount)
const [activePositionsTab, setActivePositionsTab] = useState(
- accountType === 'isolated_margin' ? PositionsTab.ISOLATED_MARGIN : PositionsTab.SMART_MARGIN
+ PositionsTab.SMART_MARGIN
)
- useEffect(() => {
- accountType === 'isolated_margin'
- ? setActivePositionsTab(PositionsTab.ISOLATED_MARGIN)
- : setActivePositionsTab(PositionsTab.SMART_MARGIN)
- }, [accountType, setActivePositionsTab])
-
const { network } = Connector.useContainer()
const synthsMap = useAppSelector(selectSynthsMap)
@@ -137,13 +126,13 @@ const Overview: FC = () => {
name: PositionsTab.SMART_MARGIN,
label: t('dashboard.overview.positions-tabs.smart-margin'),
badge: smartPositionsCount,
- titleIcon: ,
+ titleIcon: ,
active: activePositionsTab === PositionsTab.SMART_MARGIN,
- detail: formatDollars(portfolio.crossMarginFutures),
+ detail: formatDollars(portfolio.smartMargin),
disabled: false,
onClick: () => {
setActivePositionsTab(PositionsTab.SMART_MARGIN)
- dispatch(setFuturesAccountType(FuturesAccountTypes.CROSS_MARGIN))
+ dispatch(setFuturesAccountType(FuturesMarginType.SMART_MARGIN))
},
},
{
@@ -162,7 +151,7 @@ const Overview: FC = () => {
exchangeTokens,
balances.totalUSDBalance,
activePositionsTab,
- portfolio.crossMarginFutures,
+ portfolio.smartMargin,
setActivePositionsTab,
])
@@ -177,7 +166,7 @@ const Overview: FC = () => {
))}
-
+
diff --git a/packages/app/src/sections/dashboard/PortfolioChart.tsx b/packages/app/src/sections/dashboard/PortfolioChart.tsx
index 43990b471f..63165fd582 100644
--- a/packages/app/src/sections/dashboard/PortfolioChart.tsx
+++ b/packages/app/src/sections/dashboard/PortfolioChart.tsx
@@ -16,14 +16,14 @@ import { MobileHiddenView, MobileOnlyView } from 'components/Media'
import { Body, NumericValue, Heading } from 'components/Text'
import { DEFAULT_FUTURES_MARGIN_TYPE } from 'constants/defaults'
import ROUTES from 'constants/routes'
+import { selectFuturesType } from 'state/futures/common/selectors'
import {
- selectBuyingPower,
selectFuturesPortfolio,
- selectFuturesType,
selectPortfolioChartData,
selectSelectedPortfolioTimeframe,
selectTotalUnrealizedPnl,
} from 'state/futures/selectors'
+import { selectBuyingPower } from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
import { Timeframe } from './Timeframe'
@@ -53,12 +53,11 @@ const PriceChart: FC = ({ setHoverValue, setHoverTitle }) => {
const theme = useTheme()
const portfolioTimeframe = useAppSelector(selectSelectedPortfolioTimeframe)
const accountType = useAppSelector(selectFuturesType)
- const { isolated_margin: isolatedPortfolioData, cross_margin: smartPortfolioData } =
- useAppSelector(selectPortfolioChartData)
+ const portfolioChartData = useAppSelector(selectPortfolioChartData)
const portfolioData = useMemo(
- () => (accountType === 'isolated_margin' ? isolatedPortfolioData : smartPortfolioData),
- [accountType, isolatedPortfolioData, smartPortfolioData]
+ () => portfolioChartData[accountType],
+ [portfolioChartData, accountType]
)
const lineColor = useMemo(() => {
@@ -66,7 +65,7 @@ const PriceChart: FC = ({ setHoverValue, setHoverTitle }) => {
portfolioData.length > 2
? portfolioData[portfolioData.length - 1].total - portfolioData[0].total < 0
: false
- return isNegative ? theme.colors.selectedTheme.red : theme.colors.selectedTheme.green
+ return theme.colors.selectedTheme[isNegative ? 'red' : 'green']
}, [portfolioData, theme])
return (
@@ -112,8 +111,8 @@ const PriceChart: FC = ({ setHoverValue, setHoverTitle }) => {
align="left"
formatter={(value) =>
value === 'total'
- ? accountType === 'isolated_margin'
- ? 'Isolated Margin'
+ ? accountType === 'cross_margin'
+ ? 'Cross Margin'
: 'Smart Margin'
: value
}
@@ -132,10 +131,10 @@ const PriceChart: FC = ({ setHoverValue, setHoverTitle }) => {
const PortfolioChart: FC = () => {
const { t } = useTranslation()
- const { isolatedMarginFutures: isolatedTotal, crossMarginFutures: smartTotal } =
+ const { crossMargin: crossTotal, smartMargin: smartTotal } =
useAppSelector(selectFuturesPortfolio)
const accountType = useAppSelector(selectFuturesType)
- const { isolated_margin: isolatedPortfolioData, cross_margin: smartPortfolioData } =
+ const { cross_margin: crossPortfolioData, smart_margin: smartPortfolioData } =
useAppSelector(selectPortfolioChartData)
const buyingPower = useAppSelector(selectBuyingPower)
@@ -145,13 +144,13 @@ const PortfolioChart: FC = () => {
const [hoverTitle, setHoverTitle] = useState(null)
const total = useMemo(
- () => (accountType === 'isolated_margin' ? isolatedTotal : smartTotal),
- [accountType, isolatedTotal, smartTotal]
+ () => (accountType === 'cross_margin' ? crossTotal : smartTotal),
+ [accountType, crossTotal, smartTotal]
)
const portfolioData = useMemo(() => {
- return accountType === 'isolated_margin' ? isolatedPortfolioData : smartPortfolioData
- }, [accountType, isolatedPortfolioData, smartPortfolioData])
+ return accountType === 'cross_margin' ? crossPortfolioData : smartPortfolioData
+ }, [accountType, crossPortfolioData, smartPortfolioData])
const changeValue = useMemo(() => {
if (portfolioData.length < 2) {
diff --git a/packages/app/src/sections/dashboard/Stake/RewardsTab.tsx b/packages/app/src/sections/dashboard/Stake/RewardsTab.tsx
index beed1fe709..3bb71237e3 100644
--- a/packages/app/src/sections/dashboard/Stake/RewardsTab.tsx
+++ b/packages/app/src/sections/dashboard/Stake/RewardsTab.tsx
@@ -18,7 +18,7 @@ import { STAKING_DISABLED } from 'constants/ui'
import useIsL2 from 'hooks/useIsL2'
import { TradingRewardProps } from 'queries/staking/utils'
import { StakingCard } from 'sections/dashboard/Stake/card'
-import { selectFuturesFees, selectFuturesFeesForAccount } from 'state/futures/selectors'
+import { selectFuturesFees, selectFuturesFeesForAccount } from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import { claimMultipleAllRewards } from 'state/staking/actions'
import { setSelectedEpoch } from 'state/staking/reducer'
diff --git a/packages/app/src/sections/futures/ClosePositionModal/CloseCrossMarginPositionModal.tsx b/packages/app/src/sections/futures/ClosePositionModal/CloseCrossMarginPositionModal.tsx
new file mode 100644
index 0000000000..d428aa2395
--- /dev/null
+++ b/packages/app/src/sections/futures/ClosePositionModal/CloseCrossMarginPositionModal.tsx
@@ -0,0 +1,221 @@
+import { ZERO_WEI } from '@kwenta/sdk/constants'
+import { PositionSide, PotentialTradeStatus } from '@kwenta/sdk/types'
+import {
+ floorNumber,
+ formatDollars,
+ formatNumber,
+ formatPercent,
+ stripZeros,
+} from '@kwenta/sdk/utils'
+import { wei } from '@synthetixio/wei'
+import React, { useCallback, useMemo } from 'react'
+import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
+
+import BaseModal from 'components/BaseModal'
+import Button from 'components/Button'
+import ErrorView from 'components/ErrorView'
+import { InfoBoxContainer, InfoBoxRow } from 'components/InfoBox'
+import { FlexDivRowCentered } from 'components/layout/flex'
+import PreviewArrow from 'components/PreviewArrow'
+import SelectorButtons from 'components/SelectorButtons'
+import Spacer from 'components/Spacer'
+import { Body } from 'components/Text'
+import { previewErrorI18n } from 'queries/futures/constants'
+import { setShowPositionModal } from 'state/app/reducer'
+import { selectTransaction } from 'state/app/selectors'
+import { submitCrossMarginReducePositionOrder } from 'state/futures/crossMargin/actions'
+import {
+ selectCloseCMPositionOrderInputs,
+ selectCloseCMPositionPreview,
+} from 'state/futures/crossMargin/selectors'
+import { selectSubmittingFuturesTx } from 'state/futures/selectors'
+import {
+ editClosePositionPrice,
+ editClosePositionSizeDelta,
+} from 'state/futures/smartMargin/actions'
+import {
+ selectEditPositionModalInfo,
+ selectIsFetchingTradePreview,
+ selectTradePreviewError,
+} from 'state/futures/smartMargin/selectors'
+import { useAppDispatch, useAppSelector } from 'state/hooks'
+
+import ClosePositionFeeInfo from '../FeeInfoBox/ClosePositionFeeInfo'
+
+import ClosePositionSizeInput from './ClosePositionSizeInput'
+
+const CLOSE_PERCENT_OPTIONS = ['25%', '50%', '75%', '100%']
+
+// TODO: Share some logic between close modals
+
+export default function CloseCrossMarginPositionModal() {
+ const { t } = useTranslation()
+ const dispatch = useAppDispatch()
+
+ const transactionState = useAppSelector(selectTransaction)
+ const isSubmitting = useAppSelector(selectSubmittingFuturesTx)
+ const isFetchingPreview = useAppSelector(selectIsFetchingTradePreview)
+ const previewTrade = useAppSelector(selectCloseCMPositionPreview)
+ const previewError = useAppSelector(selectTradePreviewError)
+ const { nativeSizeDelta } = useAppSelector(selectCloseCMPositionOrderInputs)
+ const { market, position } = useAppSelector(selectEditPositionModalInfo)
+
+ const submitCloseOrder = useCallback(() => {
+ dispatch(submitCrossMarginReducePositionOrder())
+ }, [dispatch])
+
+ const isLoading = useMemo(
+ () => isSubmitting || isFetchingPreview,
+ [isSubmitting, isFetchingPreview]
+ )
+
+ const maxNativeValue = useMemo(() => {
+ return position?.size ?? ZERO_WEI
+ }, [position?.size])
+
+ const sizeWei = useMemo(
+ () => (!nativeSizeDelta || isNaN(Number(nativeSizeDelta)) ? wei(0) : wei(nativeSizeDelta)),
+ [nativeSizeDelta]
+ )
+
+ const invalidSize = useMemo(() => {
+ return sizeWei.abs().gt(maxNativeValue.abs())
+ }, [sizeWei, maxNativeValue])
+
+ const orderError = useMemo(() => {
+ if (previewError) return t(previewErrorI18n(previewError))
+ if (previewTrade?.showStatus) return previewTrade?.statusMessage
+ return null
+ }, [previewTrade?.showStatus, previewTrade?.statusMessage, previewError, t])
+
+ const submitDisabled = useMemo(() => {
+ return false
+ return (
+ sizeWei.eq(0) ||
+ invalidSize ||
+ isLoading ||
+ orderError ||
+ previewTrade?.status !== PotentialTradeStatus.OK
+ )
+ }, [sizeWei, invalidSize, isLoading, orderError, previewTrade?.status])
+
+ const onClose = () => {
+ if (market) {
+ dispatch(editClosePositionSizeDelta(market.marketKey, ''))
+ dispatch(editClosePositionPrice(market.marketKey, ''))
+ }
+ dispatch(setShowPositionModal(null))
+ }
+
+ const onSelectPercent = useCallback(
+ (index: number) => {
+ if (!position?.size || !market?.marketKey) return
+ const option = CLOSE_PERCENT_OPTIONS[index]
+ const percent = Math.abs(Number(option.replace('%', ''))) / 100
+ const size =
+ percent === 1 ? position.size.abs() : floorNumber(position.size.abs().mul(percent))
+
+ const sizeDelta = position?.side === PositionSide.LONG ? wei(size).neg() : wei(size)
+ const decimals = sizeDelta.abs().eq(position.size.abs()) ? undefined : 4
+
+ dispatch(
+ editClosePositionSizeDelta(market.marketKey, stripZeros(sizeDelta.toString(decimals)))
+ )
+ },
+ [dispatch, position?.size, position?.side, market?.marketKey]
+ )
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {previewTrade.leverage.toString(2)}x
+ )
+ }
+ title={t('futures.market.trade.edit-position.leverage-change')}
+ textValue={position?.leverage ? position.leverage?.toString(2) + 'x' : '-'}
+ />
+
+ {previewTrade?.sizeDelta
+ ? formatNumber(previewTrade.sizeDelta.abs(), { suggestDecimals: true })
+ : '-'}
+
+ )
+ }
+ title={t('futures.market.trade.edit-position.position-size')}
+ textValue={formatNumber(position?.size || 0, { suggestDecimals: true })}
+ />
+
+
+
+
+
+
+
+
+ {(orderError || transactionState?.error) && (
+
+ )}
+
+
+
+ )
+}
+
+export const StyledBaseModal = styled(BaseModal)`
+ [data-reach-dialog-content] {
+ width: 400px;
+ }
+`
+
+export const InfoContainer = styled(FlexDivRowCentered)`
+ margin: 16px 0;
+`
+
+export const BalanceText = styled(Body)`
+ color: ${(props) => props.theme.colors.selectedTheme.gray};
+ span {
+ color: ${(props) => props.theme.colors.selectedTheme.button.text.primary};
+ }
+`
diff --git a/packages/app/src/sections/futures/ClosePositionModal/ClosePositionModal.tsx b/packages/app/src/sections/futures/ClosePositionModal/ClosePositionModal.tsx
index 949da35bdf..0157d17740 100644
--- a/packages/app/src/sections/futures/ClosePositionModal/ClosePositionModal.tsx
+++ b/packages/app/src/sections/futures/ClosePositionModal/ClosePositionModal.tsx
@@ -1,5 +1,5 @@
import { ZERO_WEI } from '@kwenta/sdk/constants'
-import { PositionSide, PotentialTradeStatus } from '@kwenta/sdk/types'
+import { FuturesMarginType, PositionSide, PotentialTradeStatus } from '@kwenta/sdk/types'
import {
floorNumber,
formatDollars,
@@ -24,23 +24,23 @@ import { Body } from 'components/Text'
import { previewErrorI18n } from 'queries/futures/constants'
import { setShowPositionModal } from 'state/app/reducer'
import { selectTransaction } from 'state/app/selectors'
+import { selectFuturesType } from 'state/futures/common/selectors'
+import { submitCrossMarginReducePositionOrder } from 'state/futures/crossMargin/actions'
+import { selectSubmittingFuturesTx } from 'state/futures/selectors'
import {
editClosePositionPrice,
editClosePositionSizeDelta,
- submitIsolatedMarginReducePositionOrder,
submitSmartMarginReducePositionOrder,
-} from 'state/futures/actions'
-import { setClosePositionOrderType } from 'state/futures/reducer'
+} from 'state/futures/smartMargin/actions'
+import { setClosePositionOrderType } from 'state/futures/smartMargin/reducer'
import {
- selectClosePositionOrderInputs,
+ selectCloseSMPositionOrderInputs,
selectClosePositionPreview,
selectEditPositionModalInfo,
- selectFuturesType,
selectIsFetchingTradePreview,
selectKeeperDepositExceedsBal,
- selectSubmittingFuturesTx,
selectTradePreviewError,
-} from 'state/futures/selectors'
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import AcceptWarningView from '../../../components/AcceptWarningView'
@@ -63,16 +63,16 @@ export default function ClosePositionModal() {
const previewError = useAppSelector(selectTradePreviewError)
const accountType = useAppSelector(selectFuturesType)
const ethBalanceExceeded = useAppSelector(selectKeeperDepositExceedsBal)
- const { nativeSizeDelta, orderType, price } = useAppSelector(selectClosePositionOrderInputs)
+ const { nativeSizeDelta, orderType, price } = useAppSelector(selectCloseSMPositionOrderInputs)
const { market, position } = useAppSelector(selectEditPositionModalInfo)
const [overridePriceProtection, setOverridePriceProtection] = useState(false)
const submitCloseOrder = useCallback(() => {
- if (accountType === 'cross_margin') {
+ if (accountType === FuturesMarginType.SMART_MARGIN) {
dispatch(submitSmartMarginReducePositionOrder(overridePriceProtection))
} else {
- dispatch(submitIsolatedMarginReducePositionOrder())
+ dispatch(submitCrossMarginReducePositionOrder())
}
}, [dispatch, accountType, overridePriceProtection])
@@ -82,8 +82,8 @@ export default function ClosePositionModal() {
)
const maxNativeValue = useMemo(() => {
- return position?.position?.size ?? ZERO_WEI
- }, [position?.position?.size])
+ return position?.size ?? ZERO_WEI
+ }, [position?.size])
const sizeWei = useMemo(
() => (!nativeSizeDelta || isNaN(Number(nativeSizeDelta)) ? wei(0) : wei(nativeSizeDelta)),
@@ -149,28 +149,26 @@ export default function ClosePositionModal() {
const onSelectPercent = useCallback(
(index: number) => {
- if (!position?.position?.size || !market?.marketKey) return
+ if (!position?.size || !market?.marketKey) return
const option = CLOSE_PERCENT_OPTIONS[index]
const percent = Math.abs(Number(option.replace('%', ''))) / 100
const size =
- percent === 1
- ? position.position.size.abs()
- : floorNumber(position.position.size.abs().mul(percent))
+ percent === 1 ? position.size.abs() : floorNumber(position.size.abs().mul(percent))
- const sizeDelta = position?.position.side === PositionSide.LONG ? wei(size).neg() : wei(size)
- const decimals = sizeDelta.abs().eq(position.position.size.abs()) ? undefined : 4
+ const sizeDelta = position.side === PositionSide.LONG ? wei(size).neg() : wei(size)
+ const decimals = sizeDelta.abs().eq(position.size.abs()) ? undefined : 4
dispatch(
editClosePositionSizeDelta(market.marketKey, stripZeros(sizeDelta.toString(decimals)))
)
},
- [dispatch, position?.position?.size, position?.position?.side, market?.marketKey]
+ [dispatch, position?.size, position?.side, market?.marketKey]
)
return (
- {accountType === 'cross_margin' && (
+ {accountType === FuturesMarginType.SMART_MARGIN && (
<>
@@ -201,7 +199,7 @@ export default function ClosePositionModal() {
)
}
title={t('futures.market.trade.edit-position.leverage-change')}
- textValue={position?.position ? position?.position?.leverage.toString(2) + 'x' : '-'}
+ textValue={position?.leverage ? position.leverage.toString(2) + 'x' : '-'}
/>
, v: string) => {
diff --git a/packages/app/src/sections/futures/ClosePositionModal/ClosePositionSizeInput.tsx b/packages/app/src/sections/futures/ClosePositionModal/ClosePositionSizeInput.tsx
index 07b4039c41..127bfa5289 100644
--- a/packages/app/src/sections/futures/ClosePositionModal/ClosePositionSizeInput.tsx
+++ b/packages/app/src/sections/futures/ClosePositionModal/ClosePositionSizeInput.tsx
@@ -9,11 +9,11 @@ import NumericInput from 'components/Input/NumericInput'
import { FlexDivRow } from 'components/layout/flex'
import Spacer from 'components/Spacer'
import { selectShowPositionModal } from 'state/app/selectors'
-import { editClosePositionSizeDelta } from 'state/futures/actions'
+import { editClosePositionSizeDelta } from 'state/futures/smartMargin/actions'
import {
- selectClosePositionOrderInputs,
+ selectCloseSMPositionOrderInputs,
selectEditPositionModalInfo,
-} from 'state/futures/selectors'
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
type OrderSizingProps = {
@@ -24,7 +24,7 @@ type OrderSizingProps = {
const ClosePositionSizeInput: React.FC = memo(({ isMobile, maxNativeValue }) => {
const dispatch = useAppDispatch()
- const { nativeSizeDelta } = useAppSelector(selectClosePositionOrderInputs)
+ const { nativeSizeDelta } = useAppSelector(selectCloseSMPositionOrderInputs)
const { position } = useAppSelector(selectEditPositionModalInfo)
const modal = useAppSelector(selectShowPositionModal)
@@ -34,12 +34,12 @@ const ClosePositionSizeInput: React.FC = memo(({ isMobile, max
dispatch(
editClosePositionSizeDelta(
modal.marketKey,
- position?.position?.side === PositionSide.LONG ? '-' + value : value
+ position?.side === PositionSide.LONG ? '-' + value : value
)
)
}
},
- [dispatch, modal, position?.position?.side]
+ [dispatch, modal, position?.side]
)
const onChangeValue = useCallback(
diff --git a/packages/app/src/sections/futures/CreatePerpsV3AccountModal.tsx b/packages/app/src/sections/futures/CreatePerpsV3AccountModal.tsx
new file mode 100644
index 0000000000..48ea2edf2a
--- /dev/null
+++ b/packages/app/src/sections/futures/CreatePerpsV3AccountModal.tsx
@@ -0,0 +1,72 @@
+import { useCallback } from 'react'
+import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
+
+import BaseModal from 'components/BaseModal'
+import Button from 'components/Button'
+import ErrorView from 'components/ErrorView'
+import Loader from 'components/Loader'
+import { setOpenModal } from 'state/app/reducer'
+import { createPerpsV3Account } from 'state/futures/crossMargin/actions'
+import { selectCrossMarginSupportedNetwork } from 'state/futures/crossMargin/selectors'
+import { selectSubmittingFuturesTx } from 'state/futures/selectors'
+import { useAppDispatch, useAppSelector } from 'state/hooks'
+
+type Props = {
+ isOpen: boolean
+}
+
+export default function CreatePerpsV3AccountModal({ isOpen }: Props) {
+ const { t } = useTranslation()
+ const dispatch = useAppDispatch()
+ const crossMarginAvailable = useAppSelector(selectCrossMarginSupportedNetwork)
+ const txProcessing = useAppSelector(selectSubmittingFuturesTx)
+
+ const onClose = () => dispatch(setOpenModal(null))
+
+ const createAccount = useCallback(async () => {
+ dispatch(createPerpsV3Account())
+ }, [dispatch])
+
+ const renderContent = () => {
+ if (!crossMarginAvailable) {
+ return
+ }
+
+ return (
+ <>
+ {t('futures.modals.onboard.step1-intro')}
+
+ {txProcessing ? : 'Create Account'}
+
+ >
+ )
+ }
+
+ return (
+
+ {renderContent()}
+
+ )
+}
+
+const StyledBaseModal = styled(BaseModal)`
+ color: ${(props) => props.theme.colors.selectedTheme.gray};
+ [data-reach-dialog-content] {
+ width: 400px;
+ }
+`
+
+const StyledButton = styled(Button)`
+ margin-top: 24px;
+ height: 50px;
+ width: 100%;
+`
+
+const Intro = styled.div`
+ margin-bottom: 30px;
+`
diff --git a/packages/app/src/sections/futures/CrossMarginOnboard/index.tsx b/packages/app/src/sections/futures/CrossMarginOnboard/index.tsx
deleted file mode 100644
index 46a425742e..0000000000
--- a/packages/app/src/sections/futures/CrossMarginOnboard/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './CrossMarginOnboard'
diff --git a/packages/app/src/sections/futures/EditPositionModal/EditPositionMarginInput.tsx b/packages/app/src/sections/futures/EditPositionModal/EditPositionMarginInput.tsx
index 2d9f4f51f1..f58b3fb0a5 100644
--- a/packages/app/src/sections/futures/EditPositionModal/EditPositionMarginInput.tsx
+++ b/packages/app/src/sections/futures/EditPositionModal/EditPositionMarginInput.tsx
@@ -10,8 +10,8 @@ import { getStep } from 'components/Slider/Slider'
import StyledSlider from 'components/Slider/StyledSlider'
import Spacer from 'components/Spacer'
import { selectShowPositionModal } from 'state/app/selectors'
-import { editCrossMarginPositionMargin } from 'state/futures/actions'
-import { selectEditPositionInputs } from 'state/futures/selectors'
+import { editSmartMarginPositionMargin } from 'state/futures/smartMargin/actions'
+import { selectSmartMarginEditPosInputs } from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
type OrderSizingProps = {
@@ -24,14 +24,14 @@ const EditPositionMarginInput: React.FC = memo(
({ isMobile, type, maxUsdInput }) => {
const dispatch = useAppDispatch()
- const { marginDelta } = useAppSelector(selectEditPositionInputs)
+ const { marginDelta } = useAppSelector(selectSmartMarginEditPosInputs)
const positionModal = useAppSelector(selectShowPositionModal)
const onChangeMargin = useCallback(
(value: string) => {
if (positionModal?.marketKey) {
dispatch(
- editCrossMarginPositionMargin(
+ editSmartMarginPositionMargin(
positionModal.marketKey,
type === 'deposit' || !value ? value : '-' + value
)
diff --git a/packages/app/src/sections/futures/EditPositionModal/EditPositionMarginModal.tsx b/packages/app/src/sections/futures/EditPositionModal/EditPositionMarginModal.tsx
index b2eb01f53d..5480d59aeb 100644
--- a/packages/app/src/sections/futures/EditPositionModal/EditPositionMarginModal.tsx
+++ b/packages/app/src/sections/futures/EditPositionModal/EditPositionMarginModal.tsx
@@ -17,21 +17,21 @@ import Spacer from 'components/Spacer'
import { Body } from 'components/Text'
import { setShowPositionModal } from 'state/app/reducer'
import { selectShowPositionModal, selectTransaction } from 'state/app/selectors'
+import { clearTradeInputs } from 'state/futures/actions'
+import { selectSubmittingFuturesTx } from 'state/futures/selectors'
import {
- approveCrossMargin,
- clearTradeInputs,
- editCrossMarginPositionMargin,
- submitCrossMarginAdjustMargin,
-} from 'state/futures/actions'
+ approveSmartMargin,
+ editSmartMarginPositionMargin,
+ submitSmartMarginAdjustMargin,
+} from 'state/futures/smartMargin/actions'
import {
selectEditMarginAllowanceValid,
- selectEditPositionInputs,
selectEditPositionModalInfo,
selectEditPositionPreview,
selectIdleMargin,
selectIsFetchingTradePreview,
- selectSubmittingFuturesTx,
-} from 'state/futures/selectors'
+ selectSmartMarginEditPosInputs,
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import EditPositionMarginInput from './EditPositionMarginInput'
@@ -44,7 +44,7 @@ export default function EditPositionMarginModal() {
const isSubmitting = useAppSelector(selectSubmittingFuturesTx)
const isFetchingPreview = useAppSelector(selectIsFetchingTradePreview)
const preview = useAppSelector(selectEditPositionPreview)
- const { marginDelta } = useAppSelector(selectEditPositionInputs)
+ const { marginDelta } = useAppSelector(selectSmartMarginEditPosInputs)
const idleMargin = useAppSelector(selectIdleMargin)
const modal = useAppSelector(selectShowPositionModal)
const { market, position } = useAppSelector(selectEditPositionModalInfo)
@@ -62,18 +62,18 @@ export default function EditPositionMarginModal() {
)
const maxWithdraw = useMemo(() => {
- const maxSize = position?.remainingMargin.mul(market?.appMaxLeverage ?? 1)
- const currentSize = position?.position?.notionalValue
+ const maxSize = position?.remainingMargin?.mul(market?.appMaxLeverage ?? 1)
+ const currentSize = position?.notionalValue
const max = maxSize?.sub(currentSize).div(market?.appMaxLeverage ?? 1) ?? wei(0)
- const resultingMarginMax = position?.remainingMargin.sub(max) ?? wei(0)
- const remainingMarginMax = position?.remainingMargin.sub(MIN_MARGIN_AMOUNT) ?? wei(0)
+ const resultingMarginMax = position?.remainingMargin?.sub(max) ?? wei(0)
+ const remainingMarginMax = position?.remainingMargin?.sub(MIN_MARGIN_AMOUNT) ?? wei(0)
return max.lt(0) || remainingMarginMax.lt(0)
? ZERO_WEI
: resultingMarginMax.gte(MIN_MARGIN_AMOUNT)
? max
: remainingMarginMax
- }, [position?.remainingMargin, position?.position?.notionalValue, market?.appMaxLeverage])
+ }, [position?.remainingMargin, position?.notionalValue, market?.appMaxLeverage])
const maxUsdInputAmount = useMemo(
() => (transferType === 0 ? idleMargin : maxWithdraw),
@@ -88,8 +88,8 @@ export default function EditPositionMarginModal() {
const invalid = useMemo(() => marginWei.gt(maxUsdInputAmount), [marginWei, maxUsdInputAmount])
const maxLeverageExceeded = useMemo(
- () => transferType === 1 && position?.position?.leverage.gt(market?.appMaxLeverage ?? 1),
- [transferType, position?.position?.leverage, market?.appMaxLeverage]
+ () => transferType === 1 && position?.leverage?.gt(market?.appMaxLeverage ?? 1),
+ [transferType, position?.leverage, market?.appMaxLeverage]
)
const previewError = useMemo(() => {
@@ -112,18 +112,18 @@ export default function EditPositionMarginModal() {
}
const submitMarginChange = useCallback(() => {
- dispatch(submitCrossMarginAdjustMargin())
+ dispatch(submitSmartMarginAdjustMargin())
}, [dispatch])
const onClose = () => {
if (modal?.marketKey) {
- dispatch(editCrossMarginPositionMargin(modal.marketKey, ''))
+ dispatch(editSmartMarginPositionMargin(modal.marketKey, ''))
}
dispatch(setShowPositionModal(null))
}
const handleApproveSmartMargin = useCallback(async () => {
- dispatch(approveCrossMargin())
+ dispatch(approveSmartMargin())
}, [dispatch])
const depositButtonText = allowanceValid
@@ -164,7 +164,7 @@ export default function EditPositionMarginModal() {
)
}
title={t('futures.market.trade.edit-position.leverage-change')}
- textValue={position?.position?.leverage.toString(2) + 'x'}
+ textValue={position?.leverage?.toString(2) + 'x'}
/>
diff --git a/packages/app/src/sections/futures/EditPositionModal/EditPositionSizeInput.tsx b/packages/app/src/sections/futures/EditPositionModal/EditPositionSizeInput.tsx
index d42cb1d446..294c75982c 100644
--- a/packages/app/src/sections/futures/EditPositionModal/EditPositionSizeInput.tsx
+++ b/packages/app/src/sections/futures/EditPositionModal/EditPositionSizeInput.tsx
@@ -13,8 +13,11 @@ import { getStep } from 'components/Slider/Slider'
import StyledSlider from 'components/Slider/StyledSlider'
import Spacer from 'components/Spacer'
import { selectShowPositionModal } from 'state/app/selectors'
-import { editCrossMarginPositionSize } from 'state/futures/actions'
-import { selectEditPositionInputs, selectEditPositionModalInfo } from 'state/futures/selectors'
+import { editCrossMarginPositionSize } from 'state/futures/smartMargin/actions'
+import {
+ selectEditPositionModalInfo,
+ selectSmartMarginEditPosInputs,
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
type OrderSizingProps = {
@@ -28,7 +31,7 @@ const EditPositionSizeInput: React.FC = memo(
({ isMobile, type, maxNativeValue, minNativeValue }) => {
const dispatch = useAppDispatch()
- const { nativeSizeDelta } = useAppSelector(selectEditPositionInputs)
+ const { nativeSizeDelta } = useAppSelector(selectSmartMarginEditPosInputs)
const { position } = useAppSelector(selectEditPositionModalInfo)
const modal = useAppSelector(selectShowPositionModal)
@@ -36,7 +39,7 @@ const EditPositionSizeInput: React.FC = memo(
const onSizeChange = useCallback(
(value: string) => {
if (modal) {
- const side = position?.position?.side
+ const side = position?.side
const sizeDelta =
(side === PositionSide.LONG && type === 'decrease') ||
(side === PositionSide.SHORT && type === 'increase')
@@ -45,7 +48,7 @@ const EditPositionSizeInput: React.FC = memo(
dispatch(editCrossMarginPositionSize(modal.marketKey, sizeDelta))
}
},
- [dispatch, type, modal, position?.position?.side]
+ [dispatch, type, modal, position?.side]
)
const handleSetMax = useCallback(() => {
diff --git a/packages/app/src/sections/futures/EditPositionModal/EditPositionSizeModal.tsx b/packages/app/src/sections/futures/EditPositionModal/EditPositionSizeModal.tsx
index b4d7e204f2..9ac2d37b05 100644
--- a/packages/app/src/sections/futures/EditPositionModal/EditPositionSizeModal.tsx
+++ b/packages/app/src/sections/futures/EditPositionModal/EditPositionSizeModal.tsx
@@ -17,18 +17,18 @@ import Spacer from 'components/Spacer'
import { Body } from 'components/Text'
import { setShowPositionModal } from 'state/app/reducer'
import { selectTransaction } from 'state/app/selectors'
+import { clearTradeInputs } from 'state/futures/actions'
+import { selectSubmittingFuturesTx } from 'state/futures/selectors'
import {
- clearTradeInputs,
editCrossMarginPositionSize,
- submitCrossMarginAdjustPositionSize,
-} from 'state/futures/actions'
+ submitSmartMarginAdjustPositionSize,
+} from 'state/futures/smartMargin/actions'
import {
- selectEditPositionInputs,
selectEditPositionModalInfo,
selectEditPositionPreview,
selectIsFetchingTradePreview,
- selectSubmittingFuturesTx,
-} from 'state/futures/selectors'
+ selectSmartMarginEditPosInputs,
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import EditPositionFeeInfo from '../FeeInfoBox/EditPositionFeeInfo'
@@ -43,7 +43,7 @@ export default function EditPositionSizeModal() {
const isSubmitting = useAppSelector(selectSubmittingFuturesTx)
const isFetchingPreview = useAppSelector(selectIsFetchingTradePreview)
const preview = useAppSelector(selectEditPositionPreview)
- const { nativeSizeDelta } = useAppSelector(selectEditPositionInputs)
+ const { nativeSizeDelta } = useAppSelector(selectSmartMarginEditPosInputs)
const { market, position, marketPrice } = useAppSelector(selectEditPositionModalInfo)
const [overridePriceProtection, setOverridePriceProtection] = useState(false)
@@ -62,7 +62,7 @@ export default function EditPositionSizeModal() {
}
const submitMarginChange = useCallback(() => {
- dispatch(submitCrossMarginAdjustPositionSize(overridePriceProtection))
+ dispatch(submitSmartMarginAdjustPositionSize(overridePriceProtection))
}, [dispatch, overridePriceProtection])
const isLoading = useMemo(
@@ -77,33 +77,33 @@ export default function EditPositionSizeModal() {
const resultingLeverage = useMemo(() => {
if (!preview || !position) return
- return position.remainingMargin.gt(0)
+ return position.remainingMargin?.gt(0)
? preview.size.mul(marketPrice).div(position.remainingMargin).abs()
: wei(0)
}, [preview, position, marketPrice])
const maxNativeIncreaseValue = useMemo(() => {
if (!marketPrice || marketPrice.eq(0)) return ZERO_WEI
- const totalMax = position?.remainingMargin.mul(maxLeverage) ?? ZERO_WEI
- let max = totalMax.sub(position?.position?.notionalValue ?? 0)
+ const totalMax = position?.remainingMargin?.mul(maxLeverage) ?? ZERO_WEI
+ let max = totalMax.sub(position?.notionalValue ?? 0)
max = max.gt(0) ? max : ZERO_WEI
return max.div(marketPrice)
- }, [marketPrice, position?.remainingMargin, position?.position?.notionalValue, maxLeverage])
+ }, [marketPrice, position?.remainingMargin, position?.notionalValue, maxLeverage])
const maxNativeValue = useMemo(() => {
- return editType === 0 ? maxNativeIncreaseValue : position?.position?.size ?? ZERO_WEI
- }, [editType, maxNativeIncreaseValue, position?.position?.size])
+ return editType === 0 ? maxNativeIncreaseValue : position?.size ?? ZERO_WEI
+ }, [editType, maxNativeIncreaseValue, position?.size])
const minNativeValue = useMemo(() => {
if (editType === 0) return ZERO_WEI
// If a user is over max leverage they can only
// decrease to a value below max leverage
- if (position?.position && position?.position?.leverage.gt(maxLeverage)) {
- const safeSize = position.remainingMargin.mul(maxLeverage).div(marketPrice)
- return position.position.size.sub(safeSize)
+ if (position && position?.leverage?.gt(maxLeverage)) {
+ const safeSize = position.remainingMargin?.mul(maxLeverage).div(marketPrice)
+ return position.size.sub(safeSize)
}
return ZERO_WEI
- }, [maxLeverage, position?.position, editType, marketPrice, position?.remainingMargin])
+ }, [maxLeverage, position, editType, marketPrice])
const maxNativeValueWithBuffer = useMemo(() => {
if (editType === 1) return maxNativeValue
@@ -117,10 +117,10 @@ export default function EditPositionSizeModal() {
const maxLeverageExceeded = useMemo(() => {
return (
- (editType === 0 && position?.position?.leverage.gt(maxLeverage)) ||
+ (editType === 0 && position?.leverage?.gt(maxLeverage)) ||
(editType === 1 && resultingLeverage?.gt(maxLeverage))
)
- }, [editType, position?.position?.leverage, maxLeverage, resultingLeverage])
+ }, [editType, position?.leverage, maxLeverage, resultingLeverage])
const invalid = useMemo(
() => sizeWei.abs().gt(maxNativeValueWithBuffer),
@@ -191,7 +191,7 @@ export default function EditPositionSizeModal() {
)
}
title={t('futures.market.trade.edit-position.leverage-change')}
- textValue={position?.position ? position?.position?.leverage.toString(2) + 'x' : '-'}
+ textValue={position?.leverage ? position.leverage.toString(2) + 'x' : '-'}
/>
diff --git a/packages/app/src/sections/futures/EditPositionModal/EditStopLossAndTakeProfitModal.tsx b/packages/app/src/sections/futures/EditPositionModal/EditStopLossAndTakeProfitModal.tsx
index dea6102569..f888f64165 100644
--- a/packages/app/src/sections/futures/EditPositionModal/EditStopLossAndTakeProfitModal.tsx
+++ b/packages/app/src/sections/futures/EditPositionModal/EditStopLossAndTakeProfitModal.tsx
@@ -16,24 +16,23 @@ import Spacer from 'components/Spacer'
import { Body } from 'components/Text'
import { setShowPositionModal } from 'state/app/reducer'
import { selectAckedOrdersWarning, selectTransaction } from 'state/app/selectors'
+import { clearTradeInputs } from 'state/futures/actions'
+import { selectModalSLValidity, selectSubmittingFuturesTx } from 'state/futures/selectors'
import {
calculateKeeperDeposit,
- clearTradeInputs,
updateStopLossAndTakeProfit,
-} from 'state/futures/actions'
-import { setSLTPModalStopLoss, setSLTPModalTakeProfit } from 'state/futures/reducer'
+} from 'state/futures/smartMargin/actions'
+import { setSLTPModalStopLoss, setSLTPModalTakeProfit } from 'state/futures/smartMargin/reducer'
import {
selectAllSLTPOrders,
selectEditPositionModalInfo,
selectKeeperDepositExceedsBal,
- selectModalSLValidity,
selectSlTpModalInputs,
selectSmartMarginKeeperDeposit,
- selectSubmittingFuturesTx,
-} from 'state/futures/selectors'
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
-import { KeeperDepositRow } from '../FeeInfoBox/FeesRow'
+import { KeeperDepositRow } from '../FeeInfoBox/FeeRows'
import PositionType from '../PositionType'
import OrderAcknowledgement from '../Trade/OrderAcknowledgement'
@@ -75,8 +74,8 @@ export default function EditStopLossAndTakeProfitModal() {
const hasOrders = useMemo(() => stopLoss || takeProfit, [stopLoss, takeProfit])
const leverageWei = useMemo(() => {
- return position?.position?.leverage.gt(0) ? wei(position.position.leverage) : wei(1)
- }, [position?.position?.leverage])
+ return position?.leverage?.gt(0) ? wei(position.leverage) : wei(1)
+ }, [position?.leverage])
const hasChangeOrders = useMemo(() => {
const tpOrderPrice = takeProfit?.targetPrice
@@ -87,12 +86,12 @@ export default function EditStopLossAndTakeProfitModal() {
}, [hasOrders, stopLoss?.targetPrice, stopLossPrice, takeProfit?.targetPrice, takeProfitPrice])
const tpInvalid = useMemo(() => {
- if (position?.position?.side === 'long') {
+ if (position?.side === 'long') {
return !!takeProfitPrice && wei(takeProfitPrice || 0).lt(marketPrice)
} else {
return !!takeProfitPrice && wei(takeProfitPrice || 0).gt(marketPrice)
}
- }, [takeProfitPrice, marketPrice, position?.position?.side])
+ }, [takeProfitPrice, marketPrice, position?.side])
const ethBalWarningMessage = ethBalanceExceeded
? t('futures.market.trade.confirmation.modal.eth-bal-warning')
@@ -156,14 +155,14 @@ export default function EditStopLossAndTakeProfitModal() {
const percent = Math.abs(Number(option.replace('%', ''))) / 100
const relativePercent = wei(percent).div(leverageWei)
const stopLoss =
- position?.position?.side === 'short'
+ position?.side === 'short'
? marketPrice.add(marketPrice.mul(relativePercent))
: marketPrice.sub(marketPrice.mul(relativePercent))
const dp = suggestedDecimals(stopLoss)
dispatch(setSLTPModalStopLoss(stopLoss.toString(dp)))
}
},
- [marketPrice, dispatch, position?.position?.side, leverageWei, slValidity.disabled]
+ [marketPrice, dispatch, position?.side, leverageWei, slValidity.disabled]
)
const onSelectTakeProfit = useCallback(
@@ -175,14 +174,14 @@ export default function EditStopLossAndTakeProfitModal() {
const percent = Math.abs(Number(option.replace('%', ''))) / 100
const relativePercent = wei(percent).div(leverageWei)
const takeProfit =
- position?.position?.side === 'short'
+ position?.side === 'short'
? marketPrice.sub(marketPrice.mul(relativePercent))
: marketPrice.add(marketPrice.mul(relativePercent))
const dp = suggestedDecimals(takeProfit)
dispatch(setSLTPModalTakeProfit(takeProfit.toString(dp)))
}
},
- [marketPrice, dispatch, position?.position?.side, leverageWei]
+ [marketPrice, dispatch, position?.side, leverageWei]
)
const onChangeStopLoss = useCallback(
@@ -216,8 +215,7 @@ export default function EditStopLossAndTakeProfitModal() {
nodeValue={
{market?.marketName}
- {' '}
-
+
}
/>
@@ -232,8 +230,8 @@ export default function EditStopLossAndTakeProfitModal() {
invalid={tpInvalid}
currentPrice={marketPrice}
value={takeProfitPrice}
- positionSide={position?.position?.side || PositionSide.LONG}
- leverage={position?.position?.leverage || wei(1)}
+ positionSide={position?.side || PositionSide.LONG}
+ leverage={position?.leverage || wei(1)}
onChange={onChangeTakeProfit}
/>
@@ -249,8 +247,8 @@ export default function EditStopLossAndTakeProfitModal() {
type={'stop-loss'}
disabled={!!slValidity.disabled}
disabledReason={slValidity.disabled ? 'Leverage Too High' : undefined}
- positionSide={position?.position?.side || PositionSide.LONG}
- leverage={position?.position?.leverage || wei(1)}
+ positionSide={position?.side || PositionSide.LONG}
+ leverage={position?.leverage || wei(1)}
invalid={slValidity.invalid}
currentPrice={marketPrice}
minMaxPrice={slValidity.minMaxStopPrice}
diff --git a/packages/app/src/sections/futures/FeeInfoBox/ClosePositionFeeInfo.tsx b/packages/app/src/sections/futures/FeeInfoBox/ClosePositionFeeInfo.tsx
index bc02b42dfa..5303020987 100644
--- a/packages/app/src/sections/futures/FeeInfoBox/ClosePositionFeeInfo.tsx
+++ b/packages/app/src/sections/futures/FeeInfoBox/ClosePositionFeeInfo.tsx
@@ -2,20 +2,20 @@ import { ZERO_WEI } from '@kwenta/sdk/constants'
import { memo } from 'react'
import {
- selectClosePositionOrderInputs,
+ selectCloseSMPositionOrderInputs,
selectClosePositionPreview,
- selectSmartMarginKeeperDeposit,
selectEditPositionModalInfo,
-} from 'state/futures/selectors'
+ selectSmartMarginKeeperDeposit,
+} from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
-import FeesRow from './FeesRow'
+import FeesRow from './FeeRows'
const ClosePositionFeeInfo = memo(() => {
const tradePreview = useAppSelector(selectClosePositionPreview)
const { market } = useAppSelector(selectEditPositionModalInfo)
const keeperEthDeposit = useAppSelector(selectSmartMarginKeeperDeposit)
- const { orderType } = useAppSelector(selectClosePositionOrderInputs)
+ const { orderType } = useAppSelector(selectCloseSMPositionOrderInputs)
return (
{
+ const tradePreview = useAppSelector(selectCrossMarginTradePreview)
+ const marketInfo = useAppSelector(selectV3MarketInfo)
+
+ return (
+
+ )
+})
+
+export default CrossMarginTradeFees
diff --git a/packages/app/src/sections/futures/FeeInfoBox/EditPositionFeeInfo.tsx b/packages/app/src/sections/futures/FeeInfoBox/EditPositionFeeInfo.tsx
index 447a417591..e924dbc08b 100644
--- a/packages/app/src/sections/futures/FeeInfoBox/EditPositionFeeInfo.tsx
+++ b/packages/app/src/sections/futures/FeeInfoBox/EditPositionFeeInfo.tsx
@@ -2,20 +2,20 @@ import { ZERO_WEI } from '@kwenta/sdk/constants'
import { memo } from 'react'
import {
- selectSmartMarginKeeperDeposit,
selectEditPositionModalInfo,
selectEditPositionPreview,
- selectOrderType,
-} from 'state/futures/selectors'
+ selectSmartMarginKeeperDeposit,
+ selectSmartMarginOrderType,
+} from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
-import FeesRow from './FeesRow'
+import FeesRow from './FeeRows'
const EditPositionFeeInfo = memo(() => {
const tradePreview = useAppSelector(selectEditPositionPreview)
const { market } = useAppSelector(selectEditPositionModalInfo)
const keeperEthDeposit = useAppSelector(selectSmartMarginKeeperDeposit)
- const orderType = useAppSelector(selectOrderType)
+ const orderType = useAppSelector(selectSmartMarginOrderType)
return (
{
const [expanded, toggleExpanded] = useReducer((s) => !s, false)
@@ -118,7 +118,7 @@ const FeesRow = memo(
>
- {(orderType === 'limit' || orderType === 'stop_market') && (
+ {(orderType === 'limit' || orderType === 'stop_market') && smartMarginKeeperDeposit && (
)}
@@ -126,7 +126,7 @@ const FeesRow = memo(
}
)
-export default FeesRow
+export default FeeRows
const StyledHelpIcon = styled(HelpIcon)`
margin-left: 4px;
diff --git a/packages/app/src/sections/futures/FeeInfoBox/TradeTotalFeesRow.tsx b/packages/app/src/sections/futures/FeeInfoBox/SmartMarginTradeFees.tsx
similarity index 78%
rename from packages/app/src/sections/futures/FeeInfoBox/TradeTotalFeesRow.tsx
rename to packages/app/src/sections/futures/FeeInfoBox/SmartMarginTradeFees.tsx
index ac06567bdd..57753cdd15 100644
--- a/packages/app/src/sections/futures/FeeInfoBox/TradeTotalFeesRow.tsx
+++ b/packages/app/src/sections/futures/FeeInfoBox/SmartMarginTradeFees.tsx
@@ -1,24 +1,24 @@
import { ZERO_WEI } from '@kwenta/sdk/constants'
import { memo } from 'react'
+import { selectMarketInfo } from 'state/futures/selectors'
import {
- selectSmartMarginKeeperDeposit,
- selectMarketInfo,
selectOrderType,
+ selectSmartMarginKeeperDeposit,
selectTradePreview,
-} from 'state/futures/selectors'
+} from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
-import FeesRow from './FeesRow'
+import FeeRows from './FeeRows'
-const TradeTotalFeesRow = memo(() => {
+const SmartMarginTradeFees = memo(() => {
const tradePreview = useAppSelector(selectTradePreview)
const marketInfo = useAppSelector(selectMarketInfo)
const keeperEthDeposit = useAppSelector(selectSmartMarginKeeperDeposit)
const orderType = useAppSelector(selectOrderType)
return (
- {
)
})
-export default TradeTotalFeesRow
+export default SmartMarginTradeFees
diff --git a/packages/app/src/sections/futures/FundingChart.tsx b/packages/app/src/sections/futures/FundingChart.tsx
index 7450532b98..5a5541ea04 100644
--- a/packages/app/src/sections/futures/FundingChart.tsx
+++ b/packages/app/src/sections/futures/FundingChart.tsx
@@ -13,7 +13,8 @@ import styled, { css } from 'styled-components'
import { useTheme } from 'styled-components'
import { fetchFundingRatesHistory } from 'state/futures/actions'
-import { selectHistoricalFundingRatePeriod, selectMarketAsset } from 'state/futures/selectors'
+import { selectMarketAsset } from 'state/futures/common/selectors'
+import { selectHistoricalFundingRatePeriod } from 'state/futures/selectors'
import { useAppSelector, usePollAction } from 'state/hooks'
import FundingChartTooltip, { formatFundingRate } from './FundingChartTooltip'
@@ -29,7 +30,9 @@ const FundingChart: FC = ({ display }) => {
const theme = useTheme()
const marketAsset = useAppSelector(selectMarketAsset)
const period = useAppSelector(selectHistoricalFundingRatePeriod)
- const historicalFundingRates = useAppSelector(({ futures }) => futures.historicalFundingRates)
+ const historicalFundingRates = useAppSelector(
+ ({ smartMargin }) => smartMargin.historicalFundingRates
+ )
usePollAction(
'fetchFundingRatesHistory',
diff --git a/packages/app/src/sections/futures/LeverageInput.tsx b/packages/app/src/sections/futures/LeverageInput.tsx
index d9d8b675ed..ed989e3207 100644
--- a/packages/app/src/sections/futures/LeverageInput.tsx
+++ b/packages/app/src/sections/futures/LeverageInput.tsx
@@ -1,4 +1,5 @@
import { ZERO_WEI } from '@kwenta/sdk/constants'
+import { FuturesMarginType } from '@kwenta/sdk/types'
import { floorNumber, truncateNumbers } from '@kwenta/sdk/utils'
import { wei } from '@synthetixio/wei'
import { Dispatch, FC, memo, SetStateAction, useCallback, useMemo, useState } from 'react'
@@ -13,16 +14,15 @@ import NumericInput from 'components/Input/NumericInput'
import { FlexDivCol, FlexDivRow } from 'components/layout/flex'
import { DEFAULT_FIAT_DECIMALS } from 'constants/defaults'
import { editTradeSizeInput } from 'state/futures/actions'
-import { setLeverageInput } from 'state/futures/reducer'
+import { selectFuturesType, selectMarketIndexPrice } from 'state/futures/common/selectors'
import {
selectLeverageInput,
- selectMarketIndexPrice,
selectMaxLeverage,
selectPosition,
- selectFuturesType,
- selectCrossMarginMarginDelta,
selectTradeSizeInputsDisabled,
} from 'state/futures/selectors'
+import { setSmartMarginLeverageInput } from 'state/futures/smartMargin/reducer'
+import { selectSmartMarginMarginDelta } from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import LeverageSlider from './LeverageSlider'
@@ -47,12 +47,14 @@ const LeverageInput: FC = memo(() => {
const marketPrice = useAppSelector(selectMarketIndexPrice)
const leverageInput = useAppSelector(selectLeverageInput)
const futuresType = useAppSelector(selectFuturesType)
- const crossMarginMarginDelta = useAppSelector(selectCrossMarginMarginDelta)
+ const smartMarginMarginDelta = useAppSelector(selectSmartMarginMarginDelta)
const isDisabled = useAppSelector(selectTradeSizeInputsDisabled)
const availableMargin = useMemo(() => {
- return futuresType === 'isolated_margin' ? position?.remainingMargin : crossMarginMarginDelta
- }, [position?.remainingMargin, crossMarginMarginDelta, futuresType])
+ return futuresType === FuturesMarginType.CROSS_MARGIN
+ ? position?.remainingMargin
+ : smartMarginMarginDelta
+ }, [position?.remainingMargin, smartMarginMarginDelta, futuresType])
const leverageButtons = useMemo(
() => (maxLeverage.eq(50) ? ['2', '5', '25', '50'] : ['2', '5', '10', '25']),
@@ -68,7 +70,7 @@ const LeverageInput: FC = memo(() => {
: wei(Number(newLeverage)).mul(remainingMargin).div(marketPrice).toString()
const floored = floorNumber(Number(newTradeSize), 4)
dispatch(editTradeSizeInput(String(floored), 'native'))
- dispatch(setLeverageInput(newLeverage))
+ dispatch(setSmartMarginLeverageInput(newLeverage))
},
[marketPrice, dispatch, availableMargin]
)
diff --git a/packages/app/src/sections/futures/MarginInput.tsx b/packages/app/src/sections/futures/MarginInput.tsx
index 56815c1b9e..770465c57c 100644
--- a/packages/app/src/sections/futures/MarginInput.tsx
+++ b/packages/app/src/sections/futures/MarginInput.tsx
@@ -9,13 +9,9 @@ import NumericInput from 'components/Input/NumericInput'
import { FlexDivRow } from 'components/layout/flex'
import SelectorButtons from 'components/SelectorButtons'
import { Body } from 'components/Text'
-import { editCrossMarginTradeMarginDelta } from 'state/futures/actions'
-import {
- selectSelectedInputDenomination,
- selectMarginDeltaInputValue,
- selectIdleMargin,
- selectPosition,
-} from 'state/futures/selectors'
+import { selectSelectedInputDenomination, selectPosition } from 'state/futures/selectors'
+import { editSmartMarginTradeMarginDelta } from 'state/futures/smartMargin/actions'
+import { selectIdleMargin, selectMarginDeltaInputValue } from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
const PERCENT_OPTIONS = ['10%', '25%', '50%', '100%']
@@ -34,14 +30,14 @@ const MarginInput: React.FC = memo(({ isMobile }) => {
const position = useAppSelector(selectPosition)
const onChangeValue = (_: ChangeEvent, v: string) => {
- dispatch(editCrossMarginTradeMarginDelta(v))
+ dispatch(editSmartMarginTradeMarginDelta(v))
}
const onSelectPercent = (index: number) => {
const percent = PERCENT_OPTIONS[index].replace('%', '')
const margin = idleMargin.div(100).mul(percent)
- dispatch(editCrossMarginTradeMarginDelta(floorNumber(margin).toString()))
+ dispatch(editSmartMarginTradeMarginDelta(floorNumber(margin).toString()))
}
const belowMinMargin = useMemo(
diff --git a/packages/app/src/sections/futures/MarketDetails/MarketDetails.tsx b/packages/app/src/sections/futures/MarketDetails/MarketDetails.tsx
index 43c3c39b23..2a9430d379 100644
--- a/packages/app/src/sections/futures/MarketDetails/MarketDetails.tsx
+++ b/packages/app/src/sections/futures/MarketDetails/MarketDetails.tsx
@@ -10,10 +10,9 @@ import { FlexDivCol, FlexDivRow, FlexDivRowCentered } from 'components/layout/fl
import { Body } from 'components/Text'
import { NO_VALUE } from 'constants/placeholder'
import useWindowSize from 'hooks/useWindowSize'
+import { selectMarketAsset, selectMarketPriceInfo } from 'state/futures/common/selectors'
import {
- selectMarketAsset,
selectMarketInfo,
- selectMarketPriceInfo,
selectSelectedInputHours,
selectSkewAdjustedPriceInfo,
} from 'state/futures/selectors'
diff --git a/packages/app/src/sections/futures/MarketInfo/MarketHead.tsx b/packages/app/src/sections/futures/MarketInfo/MarketHead.tsx
index 41389d71eb..701fe91056 100644
--- a/packages/app/src/sections/futures/MarketInfo/MarketHead.tsx
+++ b/packages/app/src/sections/futures/MarketInfo/MarketHead.tsx
@@ -3,7 +3,8 @@ import Head from 'next/head'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
-import { selectMarketAsset, selectSkewAdjustedPrice } from 'state/futures/selectors'
+import { selectMarketAsset } from 'state/futures/common/selectors'
+import { selectSkewAdjustedPrice } from 'state/futures/selectors'
import { useAppSelector } from 'state/hooks'
const MarketHead: FC = () => {
diff --git a/packages/app/src/sections/futures/MarketInfoBox.tsx b/packages/app/src/sections/futures/MarketInfoBox.tsx
index 7219773884..96683cac1a 100644
--- a/packages/app/src/sections/futures/MarketInfoBox.tsx
+++ b/packages/app/src/sections/futures/MarketInfoBox.tsx
@@ -1,21 +1,20 @@
-import { formatDollars, formatPercent } from '@kwenta/sdk/utils'
+import { formatDollars } from '@kwenta/sdk/utils'
import React, { memo } from 'react'
import styled from 'styled-components'
import { InfoBoxContainer, InfoBoxRow } from 'components/InfoBox'
import PreviewArrow from 'components/PreviewArrow'
+import { selectCrossMarginAvailableMargin } from 'state/futures/crossMargin/selectors'
+import { selectMarketSuspended } from 'state/futures/selectors'
import {
- selectAvailableMargin,
selectBuyingPower,
- selectMarginUsage,
- selectMarketSuspended,
selectPreviewMarginChange,
selectTradePreview,
-} from 'state/futures/selectors'
+} from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
const AvailableMarginRow = memo(() => {
- const availableMargin = useAppSelector(selectAvailableMargin)
+ const availableMargin = useAppSelector(selectCrossMarginAvailableMargin)
const potentialTrade = useAppSelector(selectTradePreview)
const previewTradeData = useAppSelector(selectPreviewMarginChange)
const marketSuspended = useAppSelector(selectMarketSuspended)
@@ -56,32 +55,11 @@ const BuyingPowerRow = memo(() => {
)
})
-const MarginUsageRow = memo(() => {
- const previewTradeData = useAppSelector(selectPreviewMarginChange)
- const potentialTrade = useAppSelector(selectTradePreview)
- const marginUsage = useAppSelector(selectMarginUsage)
- const marketSuspended = useAppSelector(selectMarketSuspended)
-
- return (
-
- {formatPercent(previewTradeData?.marginUsage)}
-
- }
- disabled={marketSuspended}
- />
- )
-})
-
const MarketInfoBox: React.FC = memo(() => {
return (
-
)
})
diff --git a/packages/app/src/sections/futures/MobileTrade/MobileTrade.tsx b/packages/app/src/sections/futures/MobileTrade/MobileTrade.tsx
index ac95a82c1b..ed2c9bc8de 100644
--- a/packages/app/src/sections/futures/MobileTrade/MobileTrade.tsx
+++ b/packages/app/src/sections/futures/MobileTrade/MobileTrade.tsx
@@ -12,7 +12,7 @@ import MarketDetails from '../MarketDetails/MarketDetails'
import FuturesUnsupportedNetwork from '../Trade/FuturesUnsupported'
import MarketsDropdown from '../Trade/MarketsDropdown'
import { MARKET_SELECTOR_HEIGHT_MOBILE } from '../Trade/MarketsDropdownSelector'
-import TradeBalance from '../Trade/TradeBalance'
+import TradeBalance from '../Trade/TradeBalanceSmartMargin'
import TradePanelDrawer from './drawers/TradePanelDrawer'
import OverviewTabs from './OverviewTabs'
diff --git a/packages/app/src/sections/futures/MobileTrade/OverviewTabs/AccountTab.tsx b/packages/app/src/sections/futures/MobileTrade/OverviewTabs/AccountTab.tsx
index 284231a195..fcdf8f3bbf 100644
--- a/packages/app/src/sections/futures/MobileTrade/OverviewTabs/AccountTab.tsx
+++ b/packages/app/src/sections/futures/MobileTrade/OverviewTabs/AccountTab.tsx
@@ -1,10 +1,11 @@
+import { FuturesMarginType } from '@kwenta/sdk/types'
import React from 'react'
import MarketInfoBox from 'sections/futures/MarketInfoBox'
import { Pane, SectionHeader, SectionTitle } from 'sections/futures/mobile'
import MarketActions from 'sections/futures/Trade/MarketActions'
-import MarginInfoBox from 'sections/futures/TradeCrossMargin/CrossMarginInfoBox'
-import { selectFuturesType } from 'state/futures/selectors'
+import MarginInfoBox from 'sections/futures/TradeSmartMargin/SmartMarginInfoBox'
+import { selectFuturesType } from 'state/futures/common/selectors'
import { useAppSelector } from 'state/hooks'
const AccountTab: React.FC = () => {
@@ -15,7 +16,7 @@ const AccountTab: React.FC = () => {
Account
- {accountType === 'isolated_margin' ? (
+ {accountType === FuturesMarginType.CROSS_MARGIN ? (
<>
diff --git a/packages/app/src/sections/futures/MobileTrade/PositionDetails.tsx b/packages/app/src/sections/futures/MobileTrade/PositionDetails.tsx
index 56796039f5..08488a2afb 100644
--- a/packages/app/src/sections/futures/MobileTrade/PositionDetails.tsx
+++ b/packages/app/src/sections/futures/MobileTrade/PositionDetails.tsx
@@ -3,14 +3,15 @@ import styled from 'styled-components'
import FuturesPositionsTable from 'sections/dashboard/FuturesPositionsTable'
import { SectionHeader, SectionSeparator, SectionTitle } from 'sections/futures/mobile'
-import { selectFuturesType, selectPosition } from 'state/futures/selectors'
+import { selectFuturesType } from 'state/futures/common/selectors'
+import { selectPosition } from 'state/futures/selectors'
import { useAppSelector } from 'state/hooks'
const PositionDetails = () => {
const position = useAppSelector(selectPosition)
const accountType = useAppSelector(selectFuturesType)
- return position?.position ? (
+ return position ? (
<>
diff --git a/packages/app/src/sections/futures/MobileTrade/UserTabs/ConditionalOrdersTab.tsx b/packages/app/src/sections/futures/MobileTrade/UserTabs/ConditionalOrdersTab.tsx
index b933a92e0a..f12970b861 100644
--- a/packages/app/src/sections/futures/MobileTrade/UserTabs/ConditionalOrdersTab.tsx
+++ b/packages/app/src/sections/futures/MobileTrade/UserTabs/ConditionalOrdersTab.tsx
@@ -13,12 +13,12 @@ import { Body } from 'components/Text'
import { NO_VALUE } from 'constants/placeholder'
import PositionType from 'sections/futures/PositionType'
import ConditionalOrdersWarning from 'sections/futures/UserInfo/ConditionalOrdersWarning'
-import { cancelConditionalOrder } from 'state/futures/actions'
+import { selectMarketAsset } from 'state/futures/common/selectors'
+import { cancelConditionalOrder } from 'state/futures/smartMargin/actions'
import {
selectAllConditionalOrders,
selectCancellingConditionalOrder,
- selectMarketAsset,
-} from 'state/futures/selectors'
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
const ConditionalOrdersTab: React.FC = () => {
diff --git a/packages/app/src/sections/futures/MobileTrade/UserTabs/OrdersTab.tsx b/packages/app/src/sections/futures/MobileTrade/UserTabs/OrdersTab.tsx
index 29276ba79e..1b1b6b26d1 100644
--- a/packages/app/src/sections/futures/MobileTrade/UserTabs/OrdersTab.tsx
+++ b/packages/app/src/sections/futures/MobileTrade/UserTabs/OrdersTab.tsx
@@ -1,6 +1,6 @@
-import { FuturesMarketKey, PositionSide } from '@kwenta/sdk/types'
+import { FuturesMarginType, FuturesMarketKey, PositionSide } from '@kwenta/sdk/types'
import { getDisplayAsset, formatCurrency, suggestedDecimals } from '@kwenta/sdk/utils'
-import { useState, useMemo, useCallback } from 'react'
+import { useState, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@@ -14,13 +14,13 @@ import useInterval from 'hooks/useInterval'
import useIsL2 from 'hooks/useIsL2'
import useNetworkSwitcher from 'hooks/useNetworkSwitcher'
import PositionType from 'sections/futures/PositionType'
-import { cancelDelayedOrder, executeDelayedOrder } from 'state/futures/actions'
-import {
- selectOpenDelayedOrders,
- selectMarketAsset,
- selectMarkets,
- selectIsExecutingOrder,
-} from 'state/futures/selectors'
+import { cancelDelayedOrder } from 'state/futures/actions'
+import { selectFuturesType, selectMarketAsset } from 'state/futures/common/selectors'
+import { cancelAsyncOrder, executeAsyncOrder } from 'state/futures/crossMargin/actions'
+import { selectAsyncCrossMarginOrders } from 'state/futures/crossMargin/selectors'
+import { selectIsExecutingOrder, selectIsCancellingOrder } from 'state/futures/selectors'
+import { executeDelayedOrder } from 'state/futures/smartMargin/actions'
+import { selectSmartMarginDelayedOrders } from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
type CountdownTimers = Record<
@@ -35,35 +35,27 @@ const OrdersTab: React.FC = () => {
const isL2 = useIsL2()
const marketAsset = useAppSelector(selectMarketAsset)
- const openDelayedOrders = useAppSelector(selectOpenDelayedOrders)
- const futuresMarkets = useAppSelector(selectMarkets)
+ const smartMarginOrders = useAppSelector(selectSmartMarginDelayedOrders)
+ const crossMarginOrders = useAppSelector(selectAsyncCrossMarginOrders)
const isExecuting = useAppSelector(selectIsExecutingOrder)
+ const futuresType = useAppSelector(selectFuturesType)
+ const isCancelling = useAppSelector(selectIsCancellingOrder)
- const [countdownTimers, setCountdownTimers] = useState()
-
- const handleCancel = useCallback(
- (marketAddress: string, isOffchain: boolean) => () => {
- dispatch(cancelDelayedOrder({ marketAddress, isOffchain }))
- },
- [dispatch]
+ const orders = useMemo(
+ () => (futuresType === FuturesMarginType.CROSS_MARGIN ? crossMarginOrders : smartMarginOrders),
+ [futuresType, crossMarginOrders, smartMarginOrders]
)
- const handleExecute = useCallback(
- (marketKey: FuturesMarketKey, marketAddress: string, isOffchain: boolean) => () => {
- dispatch(executeDelayedOrder({ marketKey, marketAddress, isOffchain }))
- },
- [dispatch]
- )
+ const [countdownTimers, setCountdownTimers] = useState()
const rowsData = useMemo(() => {
- const ordersWithCancel = openDelayedOrders
+ const ordersWithCancel = orders
.map((o) => {
- const market = futuresMarkets.find((m) => m.market === o.marketAddress)
- const timer = countdownTimers ? countdownTimers[o.marketKey] : null
+ const timer = countdownTimers ? countdownTimers[o.market.marketKey] : null
const order = {
...o,
- sizeTxt: formatCurrency(o.asset, o.size.abs(), {
- currencyKey: getDisplayAsset(o.asset) ?? '',
+ sizeTxt: formatCurrency(o.market.asset, o.size.abs(), {
+ currencyKey: getDisplayAsset(o.market.asset) ?? '',
minDecimals: suggestedDecimals(o.size),
}),
timeToExecution: timer?.timeToExecution,
@@ -71,52 +63,66 @@ const OrdersTab: React.FC = () => {
show: !!timer,
isStale:
timer &&
- market?.settings &&
+ o.market.settings &&
timer.timeToExecution === 0 &&
- timer.timePastExecution >
- DEFAULT_DELAYED_CANCEL_BUFFER +
- (o.isOffchain
- ? market.settings.offchainDelayedOrderMaxAge
- : market.settings.maxDelayTimeDelta),
+ timer.timePastExecution > DEFAULT_DELAYED_CANCEL_BUFFER + o.settlementWindowDuration,
isFailed:
timer &&
- market?.settings &&
+ o.market.settings &&
timer.timeToExecution === 0 &&
- timer.timePastExecution >
- DEFAULT_DELAYED_EXECUTION_BUFFER +
- (o.isOffchain
- ? market.settings.offchainDelayedOrderMinAge
- : market.settings.minDelayTimeDelta),
+ timer.timePastExecution > DEFAULT_DELAYED_EXECUTION_BUFFER,
isExecutable:
timer &&
- market?.settings &&
timer.timeToExecution === 0 &&
- timer.timePastExecution <=
- (o.isOffchain
- ? market.settings.offchainDelayedOrderMaxAge
- : market.settings.maxDelayTimeDelta),
- totalDeposit: o.commitDeposit.add(o.keeperDeposit),
+ timer.timePastExecution <= o.settlementWindowDuration,
+ totalDeposit: o.settlementFee,
+ onCancel: () => {
+ if (o.market.version === 2) {
+ dispatch(cancelDelayedOrder(o.market.marketAddress))
+ } else {
+ dispatch(cancelAsyncOrder(o.market.marketId))
+ }
+ },
+ onExecute: () => {
+ if (o.market.version === 2) {
+ dispatch(
+ executeDelayedOrder({
+ marketKey: o.market.marketKey,
+ marketAddress: o.market.marketAddress,
+ })
+ )
+ } else {
+ dispatch(
+ executeAsyncOrder({
+ marketKey: o.market.marketKey,
+ marketId: o.market.marketId,
+ })
+ )
+ }
+ },
}
return order
})
.sort((a, b) => {
- return b.asset === marketAsset && a.asset !== marketAsset
+ return b.market.asset === marketAsset && a.market.asset !== marketAsset
? 1
- : b.asset === marketAsset && a.asset === marketAsset
+ : b.market.asset === marketAsset && a.market.asset === marketAsset
? 0
: -1
})
return ordersWithCancel
- }, [openDelayedOrders, futuresMarkets, marketAsset, countdownTimers])
+ }, [marketAsset, countdownTimers, orders, dispatch])
+
+ // TODO: Combine this with the one in OpenDelayedOrdersTable
useInterval(
() => {
const newCountdownTimers = rowsData.reduce((acc, order) => {
- const timeToExecution = Math.floor((order.executableAtTimestamp - Date.now()) / 1000)
- const timePastExecution = Math.floor((Date.now() - order.executableAtTimestamp) / 1000)
+ const timeToExecution = Math.floor(order.executableStartTime - Date.now() / 1000)
+ const timePastExecution = Math.floor(Date.now() / 1000 - order.executableStartTime)
// Only updated delayed orders
- acc[order.marketKey] = {
+ acc[order.market.marketKey] = {
timeToExecution: Math.max(timeToExecution, 0),
timePastExecution: Math.max(timePastExecution, 0),
}
@@ -127,7 +133,6 @@ const OrdersTab: React.FC = () => {
1000,
[rowsData]
)
-
return (
{!isL2 ? (
@@ -144,9 +149,9 @@ const OrdersTab: React.FC = () => {
- {order.market}
+ {order.market.marketName}
- {order.orderType}
+ Market
@@ -154,8 +159,14 @@ const OrdersTab: React.FC = () => {
{order.show && order.isStale && (
{
+ if (order.market.version === 2) {
+ dispatch(cancelDelayedOrder(order.market.marketAddress))
+ } else {
+ dispatch(cancelAsyncOrder(order.market.marketId))
+ }
+ }}
+ disabled={isCancelling}
color="red"
>
Cancel
@@ -166,11 +177,23 @@ const OrdersTab: React.FC = () => {
{
+ if (order.market.version === 2) {
+ dispatch(
+ executeDelayedOrder({
+ marketKey: order.market.marketKey,
+ marketAddress: order.market.marketAddress,
+ })
+ )
+ } else {
+ dispatch(
+ executeAsyncOrder({
+ marketKey: order.market.marketKey,
+ marketId: order.market.marketId,
+ })
+ )
+ }
+ }}
disabled={isExecuting}
>
Execute
diff --git a/packages/app/src/sections/futures/MobileTrade/UserTabs/PositionsTab.tsx b/packages/app/src/sections/futures/MobileTrade/UserTabs/PositionsTab.tsx
index f3e610cbea..ee6e556909 100644
--- a/packages/app/src/sections/futures/MobileTrade/UserTabs/PositionsTab.tsx
+++ b/packages/app/src/sections/futures/MobileTrade/UserTabs/PositionsTab.tsx
@@ -1,9 +1,9 @@
-import { ZERO_WEI } from '@kwenta/sdk/constants'
-import { FuturesMarketKey, PositionSide } from '@kwenta/sdk/types'
+import { FuturesMarginType, FuturesMarketKey, PositionSide } from '@kwenta/sdk/types'
import Router from 'next/router'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
+import { FuturesPositionTablePosition } from 'types/futures'
import Currency from 'components/Currency'
import { FlexDiv, FlexDivRow, FlexDivRowCentered } from 'components/layout/flex'
@@ -19,16 +19,9 @@ import PositionType from 'sections/futures/PositionType'
import ShareModal from 'sections/futures/ShareModal'
import EditPositionButton from 'sections/futures/UserInfo/EditPositionButton'
import { setShowPositionModal } from 'state/app/reducer'
-import {
- selectCrossMarginPositions,
- selectFuturesType,
- selectIsolatedMarginPositions,
- selectMarketAsset,
- selectMarkets,
- selectMarkPrices,
- selectPositionHistory,
-} from 'state/futures/selectors'
-import { SharePositionParams } from 'state/futures/types'
+import { selectFuturesType, selectMarketAsset } from 'state/futures/common/selectors'
+import { selectCrossMarginPositions } from 'state/futures/crossMargin/selectors'
+import { selectSmartMarginPositions } from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import media from 'styles/media'
@@ -39,57 +32,36 @@ const PositionsTab = () => {
const isL2 = useIsL2()
- const isolatedPositions = useAppSelector(selectIsolatedMarginPositions)
const crossMarginPositions = useAppSelector(selectCrossMarginPositions)
- const positionHistory = useAppSelector(selectPositionHistory)
+ const smartMarginPositions = useAppSelector(selectSmartMarginPositions)
const currentMarket = useAppSelector(selectMarketAsset)
- const futuresMarkets = useAppSelector(selectMarkets)
- const markPrices = useAppSelector(selectMarkPrices)
const accountType = useAppSelector(selectFuturesType)
const [showShareModal, setShowShareModal] = useState(false)
- const [sharePosition, setSharePosition] = useState(null)
+ const [sharePosition, setSharePosition] = useState(null)
let data = useMemo(() => {
- const positions = accountType === 'cross_margin' ? crossMarginPositions : isolatedPositions
+ const positions =
+ accountType === FuturesMarginType.SMART_MARGIN ? smartMarginPositions : crossMarginPositions
return positions
.map((position) => {
- const market = futuresMarkets.find((market) => market.asset === position.asset)
- const thisPositionHistory = positionHistory.find((ph) => {
- return ph.isOpen && ph.asset === position.asset
- })
- const markPrice = markPrices[market?.marketKey!] ?? ZERO_WEI
return {
- market: market!,
+ market: position.market,
remainingMargin: position.remainingMargin,
- position: position.position!,
- avgEntryPrice: thisPositionHistory?.avgEntryPrice,
+ position: position,
+ avgEntryPrice: position.history?.avgEntryPrice,
stopLoss: position.stopLoss?.targetPrice,
takeProfit: position.takeProfit?.targetPrice,
- share: {
- asset: position.asset,
- position: position.position!,
- positionHistory: thisPositionHistory!,
- marketPrice: markPrice,
- },
}
})
.filter(({ position, market }) => !!position && !!market)
.sort((a) => (a.market.asset === currentMarket ? -1 : 1))
- }, [
- accountType,
- crossMarginPositions,
- isolatedPositions,
- futuresMarkets,
- positionHistory,
- markPrices,
- currentMarket,
- ])
+ }, [accountType, smartMarginPositions, crossMarginPositions, currentMarket])
const handleOpenPositionCloseModal = useCallback(
(marketKey: FuturesMarketKey) => () => {
dispatch(
setShowPositionModal({
- type: 'futures_close_position',
+ type: 'smart_margin_close_position',
marketKey,
})
)
@@ -97,7 +69,7 @@ const PositionsTab = () => {
[dispatch]
)
- const handleOpenShareModal = useCallback((share: SharePositionParams) => {
+ const handleOpenShareModal = useCallback((share: FuturesPositionTablePosition) => {
setSharePosition(share)
setShowShareModal((s) => !s)
}, [])
@@ -127,7 +99,9 @@ const PositionsTab = () => {
{row.market.marketName}
- {accountType === 'isolated_margin' ? 'Isolated Margin' : 'Smart Margin'}
+ {accountType === FuturesMarginType.CROSS_MARGIN
+ ? 'Cross Margin'
+ : 'Smart Margin'}
@@ -135,7 +109,7 @@ const PositionsTab = () => {
Close
- handleOpenShareModal(row.share)}>
+ handleOpenShareModal(row.position)}>
Share
@@ -146,7 +120,7 @@ const PositionsTab = () => {
- {accountType === 'cross_margin' && (
+ {accountType === FuturesMarginType.SMART_MARGIN && (
<>
{
Market Margin
- {accountType === 'cross_margin' && (
+ {accountType === FuturesMarginType.SMART_MARGIN && (
<>
{
) : (
)}
- {accountType === 'cross_margin' && (
+ {accountType === FuturesMarginType.SMART_MARGIN && (
<>
{
const [selectedTrade, setSelectedTrade] = React.useState()
- useFetchAction(fetchAllTradesForAccount, {
+ useFetchAction(fetchAllV2TradesForAccount, {
dependencies: [walletAddress, accountType, marketAsset],
disabled: !walletAddress,
})
diff --git a/packages/app/src/sections/futures/MobileTrade/UserTabs/TransfersTab.tsx b/packages/app/src/sections/futures/MobileTrade/UserTabs/TransfersTab.tsx
index 085771c8b4..37a3032c0d 100644
--- a/packages/app/src/sections/futures/MobileTrade/UserTabs/TransfersTab.tsx
+++ b/packages/app/src/sections/futures/MobileTrade/UserTabs/TransfersTab.tsx
@@ -1,3 +1,4 @@
+import { FuturesMarginType } from '@kwenta/sdk/types'
import { formatDollars } from '@kwenta/sdk/utils'
import React, { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@@ -8,12 +9,9 @@ import Table, { TableHeader, TableNoResults } from 'components/Table'
import { Body } from 'components/Text'
import useIsL2 from 'hooks/useIsL2'
import useNetworkSwitcher from 'hooks/useNetworkSwitcher'
-import {
- selectFuturesType,
- selectIdleMarginTransfers,
- selectMarketMarginTransfers,
- selectQueryStatuses,
-} from 'state/futures/selectors'
+import { selectFuturesType } from 'state/futures/common/selectors'
+import { selectMarketMarginTransfers, selectQueryStatuses } from 'state/futures/selectors'
+import { selectIdleMarginTransfers } from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
import { FetchStatus } from 'state/types'
import { timePresentation } from 'utils/formatters/date'
@@ -36,8 +34,11 @@ const TransfersTab: React.FC = () => {
[marketMarginTransfers, idleMarginTransfers, marginTransfersStatus]
)
+ // TODO: Move to selector
const marginTransfers = useMemo(() => {
- return accountType === 'isolated_margin' ? marketMarginTransfers : idleMarginTransfers
+ return accountType === FuturesMarginType.CROSS_MARGIN
+ ? marketMarginTransfers
+ : idleMarginTransfers
}, [accountType, idleMarginTransfers, marketMarginTransfers])
return (
diff --git a/packages/app/src/sections/futures/MobileTrade/UserTabs/UserTabs.tsx b/packages/app/src/sections/futures/MobileTrade/UserTabs/UserTabs.tsx
index b67a03dc10..3a7650b715 100644
--- a/packages/app/src/sections/futures/MobileTrade/UserTabs/UserTabs.tsx
+++ b/packages/app/src/sections/futures/MobileTrade/UserTabs/UserTabs.tsx
@@ -2,11 +2,11 @@ import React, { useMemo } from 'react'
import styled from 'styled-components'
import TabButton from 'components/Button/TabButton'
+import { selectPendingOrdersCount } from 'state/futures/selectors'
import {
selectActiveSmartPositionsCount,
selectAllConditionalOrders,
- selectOpenDelayedOrders,
-} from 'state/futures/selectors'
+} from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
import ConditionalOrdersTab from './ConditionalOrdersTab'
@@ -16,7 +16,7 @@ import TradesTab from './TradesTab'
const UserTabs: React.FC = () => {
const [activeTab, setActiveTab] = React.useState(0)
- const openOrders = useAppSelector(selectOpenDelayedOrders)
+ const pendingOrdersCount = useAppSelector(selectPendingOrdersCount)
const conditionalOrders = useAppSelector(selectAllConditionalOrders)
const smartPositionsCount = useAppSelector(selectActiveSmartPositionsCount)
@@ -30,7 +30,7 @@ const UserTabs: React.FC = () => {
{
title: 'Pending',
component: ,
- badge: openOrders.length,
+ badge: pendingOrdersCount,
},
{
title: 'Orders',
@@ -42,7 +42,7 @@ const UserTabs: React.FC = () => {
component: ,
},
]
- }, [conditionalOrders.length, openOrders.length, smartPositionsCount])
+ }, [conditionalOrders.length, pendingOrdersCount, smartPositionsCount])
return (
diff --git a/packages/app/src/sections/futures/MobileTrade/drawers/ConditionalOrderDrawer.tsx b/packages/app/src/sections/futures/MobileTrade/drawers/ConditionalOrderDrawer.tsx
deleted file mode 100644
index fb31808bc6..0000000000
--- a/packages/app/src/sections/futures/MobileTrade/drawers/ConditionalOrderDrawer.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import { ConditionalOrder, PositionSide } from '@kwenta/sdk/types'
-import { getDisplayAsset, formatCurrency } from '@kwenta/sdk/utils'
-import React, { useCallback } from 'react'
-import { useTranslation } from 'react-i18next'
-import styled, { css } from 'styled-components'
-
-import Button from 'components/Button'
-import { cancelConditionalOrder } from 'state/futures/actions'
-import { useAppDispatch } from 'state/hooks'
-
-import BaseDrawer from './BaseDrawer'
-
-type OrderDrawerProps = {
- open: boolean
- order: ConditionalOrder
- closeDrawer(): void
-}
-
-export default function ConditionalOrderDrawer({ open, order, closeDrawer }: OrderDrawerProps) {
- const { t } = useTranslation()
- const dispatch = useAppDispatch()
-
- const onCancel = useCallback(
- (order: ConditionalOrder) => {
- dispatch(cancelConditionalOrder(order.id))
- },
- [dispatch]
- )
-
- const items = React.useMemo(() => {
- if (!order || !order.side || !order.asset) return []
-
- return [
- {
- label: t('futures.market.user.open-orders.table.market'),
- value: getDisplayAsset(order.asset),
- },
- {
- label: t('futures.market.user.open-orders.table.side'),
- value: {order.side},
- },
- {
- label: t('futures.market.user.open-orders.table.size'),
- value: formatCurrency(order.asset, order.size.abs(), {
- currencyKey: getDisplayAsset(order.asset) ?? '',
- minDecimals: order.size.abs().lt(0.01) ? 4 : 2,
- }),
- },
- {
- label: t('futures.market.user.open-orders.table.price'),
- value: order.targetPriceTxt,
- },
- {
- label: t('futures.market.user.open-orders.table.type'),
- value: order.orderTypeDisplay,
- },
- ]
- }, [t, order])
-
- return (
- onCancel(order)}>Cancel}
- />
- )
-}
-
-const StyledPositionSide = styled.div<{ side: PositionSide }>`
- text-transform: uppercase;
- font-weight: bold;
- ${(props) =>
- props.side === PositionSide.LONG &&
- css`
- color: ${props.theme.colors.common.primaryGreen};
- `}
-
- ${(props) =>
- props.side === PositionSide.SHORT &&
- css`
- color: ${props.theme.colors.common.primaryRed};
- `}
-`
-
-const CancelOrderButton = styled(Button)`
- font-size: 16px;
- height: 41px;
- text-align: center;
- white-space: normal;
- background: rgba(239, 104, 104, 0.04);
- border: 1px solid #ef6868;
- box-shadow: none;
- transition: all 0s ease-in-out;
- flex: 1;
-
- &:hover {
- background: ${(props) => props.theme.colors.common.primaryRed};
- color: ${(props) => props.theme.colors.white};
- transform: scale(0.98);
- }
-
- &:disabled {
- border: ${(props) => props.theme.colors.selectedTheme.border};
- background: transparent;
- color: ${(props) => props.theme.colors.selectedTheme.button.disabled.text};
- transform: none;
- }
-`
diff --git a/packages/app/src/sections/futures/MobileTrade/drawers/OrderDrawer.tsx b/packages/app/src/sections/futures/MobileTrade/drawers/OrderDrawer.tsx
deleted file mode 100644
index e5335e6d0b..0000000000
--- a/packages/app/src/sections/futures/MobileTrade/drawers/OrderDrawer.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import { PositionSide } from '@kwenta/sdk/types'
-import { getDisplayAsset, formatCurrency } from '@kwenta/sdk/utils'
-import React, { useCallback } from 'react'
-import { useTranslation } from 'react-i18next'
-import styled, { css } from 'styled-components'
-
-import Button from 'components/Button'
-import { cancelDelayedOrder, executeDelayedOrder } from 'state/futures/actions'
-import { DelayedOrderWithDetails } from 'state/futures/types'
-import { useAppDispatch } from 'state/hooks'
-
-import BaseDrawer from './BaseDrawer'
-
-type OrderDrawerProps = {
- open: boolean
- order: DelayedOrderWithDetails
- closeDrawer(): void
-}
-
-const OrderDrawer: React.FC = ({ open, order, closeDrawer }) => {
- const { t } = useTranslation()
- const dispatch = useAppDispatch()
-
- const onCancel = useCallback(
- (order: DelayedOrderWithDetails) => {
- dispatch(
- cancelDelayedOrder({
- marketAddress: order.marketAddress,
- isOffchain: order.isOffchain,
- })
- )
- },
- [dispatch]
- )
-
- const onExecute = useCallback(
- (order: DelayedOrderWithDetails) => {
- dispatch(
- executeDelayedOrder({
- marketKey: order.marketKey,
- marketAddress: order.marketAddress,
- isOffchain: order.isOffchain,
- })
- )
- },
- [dispatch]
- )
-
- const items = React.useMemo(() => {
- if (!order || !order.side || !order.asset) return []
-
- return [
- {
- label: t('futures.market.user.open-orders.table.market'),
- value: getDisplayAsset(order.asset),
- },
- {
- label: t('futures.market.user.open-orders.table.side'),
- value: {order.side},
- },
- {
- label: t('futures.market.user.open-orders.table.size'),
- value: formatCurrency(order.asset, order.size.abs(), {
- currencyKey: getDisplayAsset(order.asset) ?? '',
- minDecimals: order.size.abs().lt(0.01) ? 4 : 2,
- }),
- },
- {
- label: t('futures.market.user.open-orders.table.type'),
- value: order.orderType,
- },
- ]
- }, [t, order])
-
- return (
-
- {order?.isExecutable && (
- onExecute(order)}>Execute
- )}
- onCancel(order)}>Cancel
- >
- }
- />
- )
-}
-
-const StyledPositionSide = styled.div<{ side: PositionSide }>`
- text-transform: uppercase;
- font-weight: bold;
- ${(props) =>
- props.side === PositionSide.LONG &&
- css`
- color: ${props.theme.colors.common.primaryGreen};
- `}
-
- ${(props) =>
- props.side === PositionSide.SHORT &&
- css`
- color: ${props.theme.colors.common.primaryRed};
- `}
-`
-
-const ExecuteButton = styled(Button)`
- margin-right: 10px;
- height: 41px;
- flex: 1;
-`
-
-const CancelOrderButton = styled(Button)`
- font-size: 16px;
- height: 41px;
- text-align: center;
- white-space: normal;
- background: rgba(239, 104, 104, 0.04);
- border: 1px solid #ef6868;
- box-shadow: none;
- transition: all 0s ease-in-out;
- flex: 1;
-
- &:hover {
- background: ${(props) => props.theme.colors.common.primaryRed};
- color: ${(props) => props.theme.colors.white};
- transform: scale(0.98);
- }
-
- &:disabled {
- border: ${(props) => props.theme.colors.selectedTheme.border};
- background: transparent;
- color: ${(props) => props.theme.colors.selectedTheme.button.disabled.text};
- transform: none;
- }
-`
-
-export default OrderDrawer
diff --git a/packages/app/src/sections/futures/MobileTrade/drawers/TradePanelDrawer.tsx b/packages/app/src/sections/futures/MobileTrade/drawers/TradePanelDrawer.tsx
index 52786251c2..e51da291d7 100644
--- a/packages/app/src/sections/futures/MobileTrade/drawers/TradePanelDrawer.tsx
+++ b/packages/app/src/sections/futures/MobileTrade/drawers/TradePanelDrawer.tsx
@@ -1,21 +1,30 @@
+import { FuturesMarginType } from '@kwenta/sdk/types'
import { FC } from 'react'
import styled from 'styled-components'
import FullScreenModal from 'components/FullScreenModal'
import { zIndex } from 'constants/ui'
-import TradeIsolatedMargin from 'sections/futures/Trade/TradePanel'
+import TradePanelCrossMargin from 'sections/futures/Trade/TradePanelCrossMargin'
+import TradePanelSmartMargin from 'sections/futures/Trade/TradePanelSmartMargin'
+import { selectFuturesType } from 'state/futures/common/selectors'
+import { useAppSelector } from 'state/hooks'
type TradePanelDrawerProps = {
open: boolean
closeDrawer(): void
}
const TradePanelDrawer: FC = ({ open, closeDrawer }) => {
+ const type = useAppSelector(selectFuturesType)
return (
-
+ {type === FuturesMarginType.CROSS_MARGIN ? (
+
+ ) : (
+
+ )}
diff --git a/packages/app/src/sections/futures/OrderSizing/DenominationToggle.tsx b/packages/app/src/sections/futures/OrderSizing/DenominationToggle.tsx
index 5791576702..f1f4fe03a9 100644
--- a/packages/app/src/sections/futures/OrderSizing/DenominationToggle.tsx
+++ b/packages/app/src/sections/futures/OrderSizing/DenominationToggle.tsx
@@ -3,8 +3,9 @@ import { memo, useCallback } from 'react'
import SwitchAssetArrows from 'assets/svg/futures/switch-arrows.svg'
import InputButton from 'components/Input/InputButton'
+import { selectMarketAsset } from 'state/futures/common/selectors'
import { setSelectedInputDenomination } from 'state/futures/reducer'
-import { selectMarketAsset, selectSelectedInputDenomination } from 'state/futures/selectors'
+import { selectSelectedInputDenomination } from 'state/futures/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
export const DenominationToggle = memo(() => {
diff --git a/packages/app/src/sections/futures/OrderSizing/OrderSizeSlider.tsx b/packages/app/src/sections/futures/OrderSizing/OrderSizeSlider.tsx
index 443ff73130..0b10fb285f 100644
--- a/packages/app/src/sections/futures/OrderSizing/OrderSizeSlider.tsx
+++ b/packages/app/src/sections/futures/OrderSizing/OrderSizeSlider.tsx
@@ -6,28 +6,30 @@ import styled from 'styled-components'
import ErrorView from 'components/ErrorView'
import { FlexDivRow } from 'components/layout/flex'
import StyledSlider from 'components/Slider/StyledSlider'
-import { editCrossMarginTradeSize } from 'state/futures/actions'
import {
- selectAboveMaxLeverage,
- selectCrossMarginMarginDelta,
selectLeverageSide,
selectMaxLeverage,
selectMaxUsdSizeInput,
selectPosition,
- selectTradeSizeInputs,
} from 'state/futures/selectors'
+import { editSmartMarginTradeSize } from 'state/futures/smartMargin/actions'
+import {
+ selectAboveMaxLeverage,
+ selectSmartMarginMarginDelta,
+ selectSmartMarginTradeInputs,
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
export default function OrderSizeSlider() {
const { t } = useTranslation()
const dispatch = useAppDispatch()
- const { susdSizeString } = useAppSelector(selectTradeSizeInputs)
+ const { susdSizeString } = useAppSelector(selectSmartMarginTradeInputs)
const aboveMaxLeverage = useAppSelector(selectAboveMaxLeverage)
const maxLeverage = useAppSelector(selectMaxLeverage)
const leverageSide = useAppSelector(selectLeverageSide)
const position = useAppSelector(selectPosition)
const maxUsdInputAmount = useAppSelector(selectMaxUsdSizeInput)
- const marginDelta = useAppSelector(selectCrossMarginMarginDelta)
+ const marginDelta = useAppSelector(selectSmartMarginMarginDelta)
const [percent, setPercent] = useState(0)
const [usdValue, setUsdValue] = useState(susdSizeString)
@@ -40,7 +42,7 @@ export default function OrderSizeSlider() {
const usdValue = Number(usdAmount).toFixed(0)
setUsdValue(usdValue)
if (commit) {
- dispatch(editCrossMarginTradeSize(usdValue, 'usd'))
+ dispatch(editSmartMarginTradeSize(usdValue, 'usd'))
}
},
[maxUsdInputAmount, dispatch]
@@ -59,7 +61,7 @@ export default function OrderSizeSlider() {
// eslint-disable-next-line
}, [susdSizeString])
- if (aboveMaxLeverage && position?.position?.side === leverageSide) {
+ if (aboveMaxLeverage && position?.side === leverageSide) {
return (
= memo(({ isMobile }) => {
const position = useAppSelector(selectPosition)
const marketAssetRate = useAppSelector(selectMarketIndexPrice)
- const orderPrice = useAppSelector(selectCrossMarginOrderPrice)
+ const orderPrice = useAppSelector(selectTradePrice)
const assetInputType = useAppSelector(selectSelectedInputDenomination)
const maxUsdInputAmount = useAppSelector(selectMaxUsdSizeInput)
const tradeSide = useAppSelector(selectLeverageSide)
@@ -47,19 +47,19 @@ const OrderSizing: React.FC = memo(({ isMobile }) => {
[orderPrice, marketAssetRate]
)
- const increasingPosition = !position?.position?.side || position?.position?.side === tradeSide
+ const increasingPosition = !position?.side || position?.side === tradeSide
const availableOiUsd = useMemo(() => {
return increasingPosition
? availableOi[tradeSide].usd
- : availableOi[tradeSide].usd.add(position?.position?.notionalValue || 0)
- }, [tradeSide, availableOi, increasingPosition, position?.position?.notionalValue])
+ : availableOi[tradeSide].usd.add(position?.notionalValue || 0)
+ }, [tradeSide, availableOi, increasingPosition, position?.notionalValue])
const availableOiNative = useMemo(() => {
return increasingPosition
? availableOi[tradeSide].native
- : availableOi[tradeSide].native.add(position?.position?.size || 0)
- }, [tradeSide, availableOi, increasingPosition, position?.position?.size])
+ : availableOi[tradeSide].native.add(position?.size || 0)
+ }, [tradeSide, availableOi, increasingPosition, position?.size])
const maxNativeValue = useMemo(() => {
const max = !isZero(tradePrice) ? maxUsdInputAmount.div(tradePrice) : ZERO_WEI
diff --git a/packages/app/src/sections/futures/PositionChart.tsx b/packages/app/src/sections/futures/PositionChart.tsx
index 88142af25a..3e030aa91e 100644
--- a/packages/app/src/sections/futures/PositionChart.tsx
+++ b/packages/app/src/sections/futures/PositionChart.tsx
@@ -4,14 +4,13 @@ import styled, { css } from 'styled-components'
import { FlexDiv } from 'components/layout/flex'
import TVChart from 'components/TVChart'
+import { selectMarketIndexPrice } from 'state/futures/common/selectors'
+import { selectPosition, selectSelectedMarketPositionHistory } from 'state/futures/selectors'
import {
selectConditionalOrdersForMarket,
- selectMarketIndexPrice,
- selectPosition,
selectPositionPreviewData,
- selectSelectedMarketPositionHistory,
selectTradePreview,
-} from 'state/futures/selectors'
+} from 'state/futures/smartMargin/selectors'
import { useAppSelector } from 'state/hooks'
type PositionChartProps = {
@@ -32,16 +31,16 @@ export default function PositionChart({ display = true }: PositionChartProps) {
const modifiedAverage = positionPreview?.avgEntryPrice ?? ZERO_WEI
const activePosition = useMemo(() => {
- if (!position?.position) {
+ if (!position) {
return null
}
return {
// As there's often a delay in subgraph sync we use the contract last
// price until we get average price to keep it snappy on opening a position
- price: subgraphPosition?.avgEntryPrice ?? position.position.lastPrice,
- size: position.position.size,
- liqPrice: position.position?.liquidationPrice,
+ price: subgraphPosition?.avgEntryPrice ?? position.lastPrice,
+ size: position.size,
+ liqPrice: position.liquidationPrice,
}
}, [subgraphPosition, position])
diff --git a/packages/app/src/sections/futures/ProfitCalculator/ProfitCalculator.tsx b/packages/app/src/sections/futures/ProfitCalculator/ProfitCalculator.tsx
index 3cf6cd572a..2cc4ed73e0 100644
--- a/packages/app/src/sections/futures/ProfitCalculator/ProfitCalculator.tsx
+++ b/packages/app/src/sections/futures/ProfitCalculator/ProfitCalculator.tsx
@@ -7,7 +7,7 @@ import styled from 'styled-components'
import BaseModal from 'components/BaseModal'
import PositionButtons from 'sections/futures/PositionButtons'
-import { selectMarketAsset, selectMarketIndexPrice } from 'state/futures/selectors'
+import { selectMarketAsset, selectMarketIndexPrice } from 'state/futures/common/selectors'
import { useAppSelector } from 'state/hooks'
import LabelWithInput from './LabelWithInput'
diff --git a/packages/app/src/sections/futures/ShareModal/AmountContainer.tsx b/packages/app/src/sections/futures/ShareModal/AmountContainer.tsx
index 5754d54f6c..4eb5680e05 100644
--- a/packages/app/src/sections/futures/ShareModal/AmountContainer.tsx
+++ b/packages/app/src/sections/futures/ShareModal/AmountContainer.tsx
@@ -3,16 +3,20 @@ import { PositionSide } from '@kwenta/sdk/types'
import { MarketKeyByAsset, formatNumber, getMarketName } from '@kwenta/sdk/utils'
import { FC, useMemo } from 'react'
import styled from 'styled-components'
+import { FuturesPositionTablePosition } from 'types/futures'
import CurrencyIcon from 'components/Currency/CurrencyIcon'
-import { selectMarketAsset } from 'state/futures/selectors'
-import { SharePositionParams } from 'state/futures/types'
+import { selectMarketAsset } from 'state/futures/common/selectors'
import { useAppSelector } from 'state/hooks'
import media from 'styles/media'
-const AmountContainer: FC = ({ asset, position }) => {
+type Props = {
+ position?: FuturesPositionTablePosition
+}
+
+const AmountContainer: FC = ({ position }) => {
const defaultAsset = useAppSelector(selectMarketAsset)
- const marketAsset = asset ?? defaultAsset
+ const marketAsset = position?.market.asset ?? defaultAsset
const marketName = getMarketName(marketAsset)
const positionDetails = position ?? null
const leverage = formatNumber(positionDetails?.leverage ?? ZERO_WEI) + 'x'
diff --git a/packages/app/src/sections/futures/ShareModal/PositionMetadata.tsx b/packages/app/src/sections/futures/ShareModal/PositionMetadata.tsx
index 04cde8bb46..41af4bc324 100644
--- a/packages/app/src/sections/futures/ShareModal/PositionMetadata.tsx
+++ b/packages/app/src/sections/futures/ShareModal/PositionMetadata.tsx
@@ -4,8 +4,8 @@ import { format } from 'date-fns'
import { useLayoutEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
+import { FuturesPositionTablePosition } from 'types/futures'
-import { SharePositionParams } from 'state/futures/types'
import media from 'styles/media'
import getLocale from 'utils/formatters/getLocale'
@@ -80,12 +80,12 @@ function getFontFamily(props: any) {
}
}
-const PositionMetadata: React.FC = ({ positionHistory, marketPrice }) => {
+const PositionMetadata: React.FC<{ position: FuturesPositionTablePosition }> = ({ position }) => {
const { t } = useTranslation()
const [currentTimestamp, setCurrentTimestamp] = useState(0)
- const avgEntryPrice = positionHistory?.avgEntryPrice.toNumber().toString() ?? ''
- const openTimestamp = positionHistory?.openTimestamp ?? 0
+ const avgEntryPrice = position?.history?.avgEntryPrice.toNumber().toString() ?? ''
+ const openTimestamp = position?.history?.openTimestamp ?? 0
useLayoutEffect(() => {
const now = new Date().getTime()
@@ -128,7 +128,7 @@ const PositionMetadata: React.FC = ({ positionHistory, mark
{t('futures.modals.share.position-metadata.current-price')}
- {formatNumber(marketPrice ?? ZERO_WEI)}
+ {formatNumber(position?.lastPrice ?? ZERO_WEI)}
>
diff --git a/packages/app/src/sections/futures/ShareModal/ShareModal.tsx b/packages/app/src/sections/futures/ShareModal/ShareModal.tsx
index bd46050c70..f9908f1686 100644
--- a/packages/app/src/sections/futures/ShareModal/ShareModal.tsx
+++ b/packages/app/src/sections/futures/ShareModal/ShareModal.tsx
@@ -1,12 +1,12 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
+import { FuturesPositionTablePosition } from 'types/futures'
import MobilePNLGraphicPNG from 'assets/png/mobile-pnl-graphic.png'
import PNLGraphicPNG from 'assets/png/pnl-graphic.png'
import BaseModal from 'components/BaseModal'
import { DesktopOnlyView, MobileOrTabletView } from 'components/Media'
-import { SharePositionParams } from 'state/futures/types'
import media from 'styles/media'
import AmountContainer from './AmountContainer'
@@ -14,7 +14,7 @@ import PositionMetadata from './PositionMetadata'
import ShareModalButton from './ShareModalButton'
type ShareModalProps = {
- sharePosition: SharePositionParams
+ sharePosition: FuturesPositionTablePosition
setShowShareModal: React.Dispatch>
}
@@ -38,11 +38,8 @@ const ShareModal: FC = ({ sharePosition, setShowShareModal }) =
-
-
+
+
diff --git a/packages/app/src/sections/futures/ShareModal/ShareModalButton.tsx b/packages/app/src/sections/futures/ShareModal/ShareModalButton.tsx
index 61d5f203dd..4ca1c10584 100644
--- a/packages/app/src/sections/futures/ShareModal/ShareModalButton.tsx
+++ b/packages/app/src/sections/futures/ShareModal/ShareModalButton.tsx
@@ -5,11 +5,11 @@ import { toPng } from 'html-to-image'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
+import { FuturesPositionTablePosition } from 'types/futures'
import TwitterIcon from 'assets/svg/social/twitter.svg'
import Button from 'components/Button'
import { DesktopOnlyView, MobileOrTabletView } from 'components/Media'
-import { SharePositionParams } from 'state/futures/types'
function getTwitterText(
side: PositionSide,
@@ -37,7 +37,7 @@ function downloadPng(dataUrl: string) {
}
type ShareModalButtonProps = {
- position: SharePositionParams
+ position: FuturesPositionTablePosition
}
const ShareModalButton: FC = ({ position }) => {
@@ -53,17 +53,16 @@ const ShareModalButton: FC = ({ position }) => {
}
const handleTweet = () => {
- const positionDetails = position.position ?? null
- const side = positionDetails?.side === 'long' ? PositionSide.LONG : PositionSide.SHORT
- const marketName = getMarketName(position.asset!)
- const leverage = formatNumber(positionDetails?.leverage ?? ZERO_WEI) + 'x'
- const pnlPct = `+${positionDetails?.pnlPct.mul(100).toNumber().toFixed(2)}%`
-
- const avgEntryPrice = position.positionHistory?.avgEntryPrice
- ? formatNumber(position.positionHistory?.avgEntryPrice)
+ const side = position?.side === 'long' ? PositionSide.LONG : PositionSide.SHORT
+ const marketName = getMarketName(position.market.asset!)
+ const leverage = formatNumber(position?.leverage ?? ZERO_WEI) + 'x'
+ const pnlPct = `+${position?.pnlPct.mul(100).toNumber().toFixed(2)}%`
+
+ const avgEntryPrice = position.history?.avgEntryPrice
+ ? formatNumber(position.history?.avgEntryPrice)
: ''
const dollarEntry = formatDollars(avgEntryPrice ?? ZERO_WEI, { suggestDecimals: true })
- const dollarCurrent = formatNumber(position.marketPrice ?? ZERO_WEI)
+ const dollarCurrent = formatNumber(position.lastPrice ?? ZERO_WEI)
const text = getTwitterText(side, marketName, leverage, pnlPct, dollarEntry, dollarCurrent)
window.open(`https://twitter.com/intent/tweet?text=${text}`, '_blank')
}
diff --git a/packages/app/src/sections/futures/CrossMarginOnboard/CrossMarginFAQ.tsx b/packages/app/src/sections/futures/SmartMarginOnboard/SmartMarginFAQ.tsx
similarity index 100%
rename from packages/app/src/sections/futures/CrossMarginOnboard/CrossMarginFAQ.tsx
rename to packages/app/src/sections/futures/SmartMarginOnboard/SmartMarginFAQ.tsx
diff --git a/packages/app/src/sections/futures/CrossMarginOnboard/CrossMarginOnboard.tsx b/packages/app/src/sections/futures/SmartMarginOnboard/SmartMarginOnboard.tsx
similarity index 79%
rename from packages/app/src/sections/futures/CrossMarginOnboard/CrossMarginOnboard.tsx
rename to packages/app/src/sections/futures/SmartMarginOnboard/SmartMarginOnboard.tsx
index fcb0063244..1c95f3d0dd 100644
--- a/packages/app/src/sections/futures/CrossMarginOnboard/CrossMarginOnboard.tsx
+++ b/packages/app/src/sections/futures/SmartMarginOnboard/SmartMarginOnboard.tsx
@@ -9,30 +9,30 @@ import ErrorView from 'components/ErrorView'
import Loader from 'components/Loader'
import ProgressSteps from 'components/ProgressSteps'
import { setOpenModal } from 'state/app/reducer'
-import { approveCrossMargin, createCrossMarginAccount } from 'state/futures/actions'
+import { selectSubmittingFuturesTx } from 'state/futures/selectors'
+import { approveSmartMargin, createSmartMarginAccount } from 'state/futures/smartMargin/actions'
import {
- selectCMAccountQueryStatus,
+ selectSmartMarginAccount,
+ selectSmartMarginAccountQueryStatus,
selectSmartMarginDepositApproved,
- selectCrossMarginAccount,
- selectFuturesSupportedNetwork,
- selectSubmittingFuturesTx,
+ selectSmartMarginSupportedNetwork,
selectTradePreview,
-} from 'state/futures/selectors'
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import { FetchStatus } from 'state/types'
-import CrossMarginFAQ from './CrossMarginFAQ'
+import SmartMarginFAQ from './SmartMarginFAQ'
type Props = {
isOpen: boolean
}
-export default function CrossMarginOnboard({ isOpen }: Props) {
+export default function SmartMarginOnboard({ isOpen }: Props) {
const { t } = useTranslation()
const dispatch = useAppDispatch()
- const crossMarginAvailable = useAppSelector(selectFuturesSupportedNetwork)
- const crossMarginAccount = useAppSelector(selectCrossMarginAccount)
- const queryStatus = useAppSelector(selectCMAccountQueryStatus)
+ const smartMarginAvailable = useAppSelector(selectSmartMarginSupportedNetwork)
+ const smartMarginAccount = useAppSelector(selectSmartMarginAccount)
+ const queryStatus = useAppSelector(selectSmartMarginAccountQueryStatus)
const depositApproved = useAppSelector(selectSmartMarginDepositApproved)
const txProcessing = useAppSelector(selectSubmittingFuturesTx)
const preview = useAppSelector(selectTradePreview)
@@ -48,11 +48,11 @@ export default function CrossMarginOnboard({ isOpen }: Props) {
}
const createAccount = useCallback(async () => {
- dispatch(createCrossMarginAccount())
+ dispatch(createSmartMarginAccount())
}, [dispatch])
const onClickApprove = useCallback(async () => {
- dispatch(approveCrossMargin())
+ dispatch(approveSmartMargin())
}, [dispatch])
const renderProgress = (step: number, complete?: boolean) => {
@@ -64,10 +64,10 @@ export default function CrossMarginOnboard({ isOpen }: Props) {
}
const renderContent = () => {
- if (!crossMarginAvailable) {
+ if (!smartMarginAvailable) {
return
}
- if (!crossMarginAccount && queryStatus.status === FetchStatus.Loading) {
+ if (!smartMarginAccount && queryStatus.status === FetchStatus.Loading) {
return (
@@ -90,13 +90,13 @@ export default function CrossMarginOnboard({ isOpen }: Props) {
)
}
- if (crossMarginAccount && !depositApproved) {
+ if (smartMarginAccount && !depositApproved) {
return (
<>
{t('futures.modals.onboard.step2-intro')}
FAQ:
-
+
{renderProgress(2)}
@@ -138,10 +138,10 @@ export default function CrossMarginOnboard({ isOpen }: Props) {
return (
<>
- {t('futures.modals.onboard.step1-intro')}
+ {t('futures.modals.onboard.cm-intro')}
FAQ:
-
+
{renderProgress(1)}
@@ -152,7 +152,11 @@ export default function CrossMarginOnboard({ isOpen }: Props) {
}
return (
-
+
{renderContent()}
)
diff --git a/packages/app/src/sections/futures/SmartMarginOnboard/index.tsx b/packages/app/src/sections/futures/SmartMarginOnboard/index.tsx
new file mode 100644
index 0000000000..7d287535ec
--- /dev/null
+++ b/packages/app/src/sections/futures/SmartMarginOnboard/index.tsx
@@ -0,0 +1 @@
+export { default } from './SmartMarginOnboard'
diff --git a/packages/app/src/sections/futures/Trade/CrossMarginTradePanelPreview.tsx b/packages/app/src/sections/futures/Trade/CrossMarginTradePanelPreview.tsx
new file mode 100644
index 0000000000..7b59390074
--- /dev/null
+++ b/packages/app/src/sections/futures/Trade/CrossMarginTradePanelPreview.tsx
@@ -0,0 +1,27 @@
+import React, { memo } from 'react'
+import styled from 'styled-components'
+
+import { selectCrossMarginTradePreview } from 'state/futures/crossMargin/selectors'
+import { useAppSelector } from 'state/hooks'
+
+import CrossMarginTradeFees from '../FeeInfoBox/CrossMarginTradeFees'
+
+import { PriceImpactRow, FillPriceRow } from './PreviewRows'
+
+export const CrossMarginTradePanelPreview = memo(() => {
+ const preview = useAppSelector(selectCrossMarginTradePreview)
+
+ return (
+
+
+
+
+
+ )
+})
+
+const FeeInfoBoxContainer = styled.div`
+ margin-bottom: 16px;
+`
+
+export default CrossMarginTradePanelPreview
diff --git a/packages/app/src/sections/futures/Trade/TransferIsolatedMarginModal.tsx b/packages/app/src/sections/futures/Trade/DepositWithdrawCrossMargin.tsx
similarity index 78%
rename from packages/app/src/sections/futures/Trade/TransferIsolatedMarginModal.tsx
rename to packages/app/src/sections/futures/Trade/DepositWithdrawCrossMargin.tsx
index 960c577dfd..a76e970e55 100644
--- a/packages/app/src/sections/futures/Trade/TransferIsolatedMarginModal.tsx
+++ b/packages/app/src/sections/futures/Trade/DepositWithdrawCrossMargin.tsx
@@ -16,13 +16,20 @@ import NumericInput from 'components/Input/NumericInput'
import { FlexDivRowCentered } from 'components/layout/flex'
import SegmentedControl from 'components/SegmentedControl'
import Spacer from 'components/Spacer'
-import { selectSusdBalance } from 'state/balances/selectors'
-import { depositIsolatedMargin, withdrawIsolatedMargin } from 'state/futures/actions'
+import { selectSNXUSDBalance } from 'state/balances/selectors'
import {
- selectAvailableMargin,
+ approveCrossMarginDeposit,
+ depositCrossMarginMargin,
+ withdrawCrossMargin,
+} from 'state/futures/crossMargin/actions'
+import {
+ selectCrossMarginAvailableMargin,
+ selectDepositAllowances,
+} from 'state/futures/crossMargin/selectors'
+import {
+ selectIsApprovingCrossDeposit,
selectIsolatedTransferError,
selectIsSubmittingIsolatedTransfer,
- selectPosition,
} from 'state/futures/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
@@ -39,38 +46,45 @@ const SocketBridge = dynamic(() => import('../../../components/SocketBridge'), {
const PLACEHOLDER = '$0.00'
-const TransferIsolatedMarginModal: React.FC = ({ onDismiss, defaultTab }) => {
+const DepositWithdrawCrossMarginModal: React.FC = ({ onDismiss, defaultTab }) => {
const { t } = useTranslation()
const dispatch = useAppDispatch()
- const position = useAppSelector(selectPosition)
const submitting = useAppSelector(selectIsSubmittingIsolatedTransfer)
+ const approving = useAppSelector(selectIsApprovingCrossDeposit)
const txError = useAppSelector(selectIsolatedTransferError)
- const susdBalance = useAppSelector(selectSusdBalance)
- const availableMargin = useAppSelector(selectAvailableMargin)
+ const usdBalance = useAppSelector(selectSNXUSDBalance)
+ const availableMargin = useAppSelector(selectCrossMarginAvailableMargin)
+ const allowances = useAppSelector(selectDepositAllowances)
const minDeposit = useMemo(() => {
- const accessibleMargin = position?.accessibleMargin ?? ZERO_WEI
- const min = MIN_MARGIN_AMOUNT.sub(accessibleMargin)
- return min.lt(ZERO_WEI) ? ZERO_WEI : min
- }, [position?.accessibleMargin])
+ // TODO: Calculate min deposit based on accessible margin
+ return MIN_MARGIN_AMOUNT
+ }, [])
const [openSocket, setOpenSocket] = useState(false)
const [amount, setAmount] = useState('')
const [transferType, setTransferType] = useState(defaultTab === 'deposit' ? 0 : 1)
- const susdBal = transferType === 0 ? susdBalance : availableMargin
+ const susdBal = transferType === 0 ? usdBalance : availableMargin
const balanceStatus: BalanceStatus = useMemo(
() =>
- availableMargin.gt(ZERO_WEI) || susdBalance.gt(minDeposit)
+ availableMargin.gt(ZERO_WEI) || usdBalance.gt(minDeposit)
? 'high_balance'
- : susdBalance.eq(ZERO_WEI)
+ : usdBalance.eq(ZERO_WEI)
? 'no_balance'
: 'low_balance',
- [availableMargin, minDeposit, susdBalance]
+ [availableMargin, minDeposit, usdBalance]
)
+ const requiresApproval = useMemo(() => {
+ if (transferType === 0) {
+ return !allowances.SNXUSD?.gt(amount || 0)
+ }
+ return false
+ }, [allowances, amount, transferType])
+
useEffect(() => {
switch (balanceStatus) {
case 'no_balance':
@@ -83,7 +97,7 @@ const TransferIsolatedMarginModal: React.FC = ({ onDismiss, defaultTab })
}, [balanceStatus])
const isDisabled = useMemo(() => {
- if (!amount || submitting) {
+ if (!amount || submitting || approving) {
return true
}
const amtWei = wei(amount)
@@ -91,7 +105,7 @@ const TransferIsolatedMarginModal: React.FC = ({ onDismiss, defaultTab })
return true
}
return false
- }, [amount, susdBal, minDeposit, transferType, submitting])
+ }, [amount, susdBal, minDeposit, transferType, submitting, approving])
const computedWithdrawAmount = useMemo(
() => (availableMargin.eq(wei(amount || 0)) ? availableMargin : wei(amount || 0)),
@@ -113,12 +127,16 @@ const TransferIsolatedMarginModal: React.FC = ({ onDismiss, defaultTab })
setAmount('')
}
- const onDeposit = () => {
- dispatch(depositIsolatedMargin(wei(amount)))
+ const onDepositOrApprove = () => {
+ if (!allowances.SNXUSD?.gt(amount)) {
+ dispatch(approveCrossMarginDeposit())
+ } else {
+ dispatch(depositCrossMarginMargin(wei(amount)))
+ }
}
const onWithdraw = () => {
- dispatch(withdrawIsolatedMargin(computedWithdrawAmount))
+ dispatch(withdrawCrossMargin(computedWithdrawAmount))
}
return (
@@ -177,12 +195,15 @@ const TransferIsolatedMarginModal: React.FC = ({ onDismiss, defaultTab })
{txError && (
@@ -262,4 +283,4 @@ const StyledCardHeader = styled(CardHeader)<{ noBorder: boolean }>`
cursor: pointer;
`
-export default TransferIsolatedMarginModal
+export default DepositWithdrawCrossMarginModal
diff --git a/packages/app/src/sections/futures/Trade/ManagePosition.tsx b/packages/app/src/sections/futures/Trade/ManagePosition.tsx
index 1868301fb3..ddabbb0154 100644
--- a/packages/app/src/sections/futures/Trade/ManagePosition.tsx
+++ b/packages/app/src/sections/futures/Trade/ManagePosition.tsx
@@ -1,4 +1,5 @@
import { ZERO_WEI } from '@kwenta/sdk/constants'
+import { FuturesMarginType } from '@kwenta/sdk/types'
import { isZero } from '@kwenta/sdk/utils'
import { wei } from '@synthetixio/wei'
import React, { useCallback, useMemo } from 'react'
@@ -10,29 +11,33 @@ import { ERROR_MESSAGES } from 'components/ErrorNotifier'
import Error from 'components/ErrorView'
import { previewErrorI18n } from 'queries/futures/constants'
import { setOpenModal } from 'state/app/reducer'
-import { setTradePanelDrawerOpen } from 'state/futures/reducer'
import {
- selectMarketInfo,
- selectIsMarketCapReached,
+ selectFuturesType,
selectMarketIndexPrice,
- selectPlaceOrderTranslationKey,
+ selectMarketPriceInfo,
+} from 'state/futures/common/selectors'
+import { setTradePanelDrawerOpen } from 'state/futures/reducer'
+import {
selectMaxLeverage,
- selectTradePreviewError,
- selectTradePreview,
- selectTradePreviewStatus,
- selectTradeSizeInputs,
- selectIsolatedMarginLeverage,
- selectCrossMarginOrderPrice,
- selectOrderType,
- selectFuturesType,
selectLeverageSide,
selectPendingDelayedOrder,
selectMaxUsdSizeInput,
- selectCrossMarginAccount,
- selectPosition,
- selectMarketPriceInfo,
selectTradePanelSLValidity,
} from 'state/futures/selectors'
+import {
+ selectIsMarketCapReached,
+ selectOrderType,
+ selectPlaceOrderTranslationKey,
+ selectSmartMarginPosition,
+ selectSmartMarginAccount,
+ selectSmartMarginLeverage,
+ selectSmartMarginOrderPrice,
+ selectSmartMarginTradeInputs,
+ selectTradePreview,
+ selectTradePreviewError,
+ selectTradePreviewStatus,
+ selectV2MarketInfo,
+} from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import { FetchStatus } from 'state/types'
import { orderPriceInvalidLabel } from 'utils/futures'
@@ -41,25 +46,25 @@ const ManagePosition: React.FC = () => {
const { t } = useTranslation()
const dispatch = useAppDispatch()
- const { susdSize } = useAppSelector(selectTradeSizeInputs)
+ const { susdSize } = useAppSelector(selectSmartMarginTradeInputs)
const maxLeverageValue = useAppSelector(selectMaxLeverage)
const selectedAccountType = useAppSelector(selectFuturesType)
const previewTrade = useAppSelector(selectTradePreview)
const previewError = useAppSelector(selectTradePreviewError)
- const leverage = useAppSelector(selectIsolatedMarginLeverage)
+ const leverage = useAppSelector(selectSmartMarginLeverage)
const orderType = useAppSelector(selectOrderType)
const openOrder = useAppSelector(selectPendingDelayedOrder)
const leverageSide = useAppSelector(selectLeverageSide)
const maxUsdInputAmount = useAppSelector(selectMaxUsdSizeInput)
const isMarketCapReached = useAppSelector(selectIsMarketCapReached)
const placeOrderTranslationKey = useAppSelector(selectPlaceOrderTranslationKey)
- const orderPrice = useAppSelector(selectCrossMarginOrderPrice)
+ const orderPrice = useAppSelector(selectSmartMarginOrderPrice)
const marketAssetRate = useAppSelector(selectMarketIndexPrice)
- const marketInfo = useAppSelector(selectMarketInfo)
+ const marketInfo = useAppSelector(selectV2MarketInfo)
const indexPrice = useAppSelector(selectMarketPriceInfo)
const previewStatus = useAppSelector(selectTradePreviewStatus)
- const smartMarginAccount = useAppSelector(selectCrossMarginAccount)
- const position = useAppSelector(selectPosition)
+ const smartMarginAccount = useAppSelector(selectSmartMarginAccount)
+ const position = useAppSelector(selectSmartMarginPosition)
const stopLossInvlid = useAppSelector(selectTradePanelSLValidity)
const orderError = useMemo(() => {
@@ -69,19 +74,19 @@ const ManagePosition: React.FC = () => {
return null
}, [previewTrade?.statusMessage, previewError, t])
- const increasingPosition = !position?.position?.side || position?.position?.side === leverageSide
+ const increasingPosition = !position?.side || position?.side === leverageSide
const onSubmit = useCallback(() => {
dispatch(setTradePanelDrawerOpen(false))
- if (selectedAccountType === 'cross_margin' && !smartMarginAccount) {
+ if (selectedAccountType === FuturesMarginType.SMART_MARGIN && !smartMarginAccount) {
dispatch(setOpenModal('futures_smart_margin_onboard'))
return
}
dispatch(
setOpenModal(
- selectedAccountType === 'cross_margin'
+ selectedAccountType === FuturesMarginType.SMART_MARGIN
? 'futures_confirm_smart_margin_trade'
- : 'futures_confirm_isolated_margin_trade'
+ : 'futures_confirm_cross_margin_trade'
)
)
}, [selectedAccountType, smartMarginAccount, dispatch])
@@ -155,7 +160,7 @@ const ManagePosition: React.FC = () => {
message: ERROR_MESSAGES.ORDER_PENDING,
}
}
- if (selectedAccountType === 'cross_margin') {
+ if (selectedAccountType === FuturesMarginType.SMART_MARGIN) {
if (previewTrade?.status !== 0 || previewStatus.status === FetchStatus.Loading)
return { message: 'awaiting_preview' }
if (orderType !== 'market' && isZero(orderPrice)) return { message: 'trade price required' }
diff --git a/packages/app/src/sections/futures/Trade/MarketActions.tsx b/packages/app/src/sections/futures/Trade/MarketActions.tsx
index 7533bd5303..20731a5527 100644
--- a/packages/app/src/sections/futures/Trade/MarketActions.tsx
+++ b/packages/app/src/sections/futures/Trade/MarketActions.tsx
@@ -11,7 +11,7 @@ import { selectShowModal } from 'state/app/selectors'
import { selectMarketInfo, selectPosition } from 'state/futures/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
-import TransferIsolatedMarginModal from './TransferIsolatedMarginModal'
+import DepositWithdrawCrossMarginModal from './DepositWithdrawCrossMargin'
const MarketActions: React.FC = () => {
const { t } = useTranslation()
@@ -29,7 +29,7 @@ const MarketActions: React.FC = () => {
dispatch(setOpenModal('futures_isolated_transfer'))}
+ onClick={() => dispatch(setOpenModal('futures_deposit_withdraw_cross_margin'))}
noOutline
>
{t('futures.market.trade.button.deposit')}
@@ -42,21 +42,21 @@ const MarketActions: React.FC = () => {
!isL2 ||
!walletAddress
}
- onClick={() => dispatch(setOpenModal('futures_isolated_transfer'))}
+ onClick={() => dispatch(setOpenModal('futures_deposit_withdraw_cross_margin'))}
noOutline
>
{t('futures.market.trade.button.withdraw')}
- {openModal === 'futures_isolated_transfer' && (
- dispatch(setOpenModal(null))}
/>
)}
- {openModal === 'futures_isolated_transfer' && (
- dispatch(setOpenModal(null))}
/>
diff --git a/packages/app/src/sections/futures/Trade/MarketsDropdown.tsx b/packages/app/src/sections/futures/Trade/MarketsDropdown.tsx
index 84689432ef..aa39c37c86 100644
--- a/packages/app/src/sections/futures/Trade/MarketsDropdown.tsx
+++ b/packages/app/src/sections/futures/Trade/MarketsDropdown.tsx
@@ -1,5 +1,5 @@
import { ZERO_WEI } from '@kwenta/sdk/constants'
-import { FuturesMarketAsset } from '@kwenta/sdk/types'
+import { FuturesMarket, FuturesMarketAsset } from '@kwenta/sdk/types'
import {
getDisplayAsset,
AssetDisplayByAsset,
@@ -31,11 +31,10 @@ import { zIndex } from 'constants/ui'
import useClickOutside from 'hooks/useClickOutside'
import useLocalStorage from 'hooks/useLocalStorage'
import { selectShowBanner } from 'state/app/selectors'
+import { selectFuturesType, selectMarketAsset } from 'state/futures/common/selectors'
import {
- selectMarketAsset,
selectMarkets,
selectMarketsQueryStatus,
- selectFuturesType,
selectMarketInfo,
selectMarkPriceInfos,
} from 'state/futures/selectors'
@@ -117,7 +116,7 @@ const MarketsDropdown: React.FC = ({ mobile }) => {
const options = useMemo(() => {
const lowerSearch = search?.toLowerCase()
const markets = lowerSearch
- ? futuresMarkets.filter(
+ ? (futuresMarkets as FuturesMarket[]).filter(
(m) =>
m.asset.toLowerCase().includes(lowerSearch) ||
AssetDisplayByAsset[m.asset]?.toLocaleLowerCase().includes(lowerSearch)
diff --git a/packages/app/src/sections/futures/Trade/OrderTypeSelector.tsx b/packages/app/src/sections/futures/Trade/OrderTypeSelector.tsx
index 628ba220e9..9c262cb27b 100644
--- a/packages/app/src/sections/futures/Trade/OrderTypeSelector.tsx
+++ b/packages/app/src/sections/futures/Trade/OrderTypeSelector.tsx
@@ -3,7 +3,7 @@ import { SmartMarginOrderType } from '@kwenta/sdk/types'
import { OrderNameByType } from '@kwenta/sdk/utils'
import SegmentedControl from 'components/SegmentedControl'
-import { editTradeOrderPrice } from 'state/futures/actions'
+import { editTradeOrderPrice } from 'state/futures/smartMargin/actions'
import { useAppDispatch } from 'state/hooks'
type Props = {
diff --git a/packages/app/src/sections/futures/Trade/PreviewRows.tsx b/packages/app/src/sections/futures/Trade/PreviewRows.tsx
new file mode 100644
index 0000000000..33bcb016fd
--- /dev/null
+++ b/packages/app/src/sections/futures/Trade/PreviewRows.tsx
@@ -0,0 +1,34 @@
+import { formatDollars, formatPercent } from '@kwenta/sdk/utils'
+import Wei from '@synthetixio/wei'
+import { memo } from 'react'
+
+import { InfoBoxRow } from 'components/InfoBox'
+import { NO_VALUE } from 'constants/placeholder'
+
+export const LiquidationRow = memo(({ liqPrice }: { liqPrice?: Wei | undefined }) => {
+ return (
+
+ )
+})
+
+export const PriceImpactRow = memo(({ priceImpact }: { priceImpact: Wei | undefined }) => {
+ return (
+
+ )
+})
+
+export const FillPriceRow = memo(({ fillPrice }: { fillPrice: Wei | undefined }) => {
+ return (
+
+ )
+})
diff --git a/packages/app/src/sections/futures/Trade/SLTPInputs.tsx b/packages/app/src/sections/futures/Trade/SLTPInputs.tsx
index c61d63b188..121771e511 100644
--- a/packages/app/src/sections/futures/Trade/SLTPInputs.tsx
+++ b/packages/app/src/sections/futures/Trade/SLTPInputs.tsx
@@ -12,14 +12,17 @@ import { StyledCaretDownIcon } from 'components/Select'
import SelectorButtons from 'components/SelectorButtons'
import Spacer from 'components/Spacer'
import { selectAckedOrdersWarning } from 'state/app/selectors'
-import { setCrossMarginTradeStopLoss, setCrossMarginTradeTakeProfit } from 'state/futures/reducer'
+import { selectMarketIndexPrice } from 'state/futures/common/selectors'
import {
selectLeverageInput,
selectLeverageSide,
- selectMarketIndexPrice,
- selectSlTpTradeInputs,
selectTradePanelSLValidity,
} from 'state/futures/selectors'
+import {
+ setSmartMarginTradeStopLoss,
+ setSmartMarginTradeTakeProfit,
+} from 'state/futures/smartMargin/reducer'
+import { selectSlTpTradeInputs } from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import OrderAcknowledgement from './OrderAcknowledgement'
@@ -64,7 +67,7 @@ export default function SLTPInputs() {
? currentPrice.add(currentPrice.mul(relativePercent))
: currentPrice.sub(currentPrice.mul(relativePercent))
const dp = suggestedDecimals(stopLoss)
- dispatch(setCrossMarginTradeStopLoss(stopLoss.toString(dp)))
+ dispatch(setSmartMarginTradeStopLoss(stopLoss.toString(dp)))
},
[currentPrice, dispatch, leverageSide, leverageWei, slValidity.disabled]
)
@@ -79,21 +82,21 @@ export default function SLTPInputs() {
? currentPrice.sub(currentPrice.mul(relativePercent))
: currentPrice.add(currentPrice.mul(relativePercent))
const dp = suggestedDecimals(takeProfit)
- dispatch(setCrossMarginTradeTakeProfit(takeProfit.toString(dp)))
+ dispatch(setSmartMarginTradeTakeProfit(takeProfit.toString(dp)))
},
[currentPrice, dispatch, leverageSide, leverageWei]
)
const onChangeStopLoss = useCallback(
(_: ChangeEvent, v: string) => {
- dispatch(setCrossMarginTradeStopLoss(v))
+ dispatch(setSmartMarginTradeStopLoss(v))
},
[dispatch]
)
const onChangeTakeProfit = useCallback(
(_: ChangeEvent, v: string) => {
- dispatch(setCrossMarginTradeTakeProfit(v))
+ dispatch(setSmartMarginTradeTakeProfit(v))
},
[dispatch]
)
diff --git a/packages/app/src/sections/futures/Trade/SmartMarginOnboardModal.tsx b/packages/app/src/sections/futures/Trade/SmartMarginOnboardModal.tsx
index ae12dc9580..01b12f3d8f 100644
--- a/packages/app/src/sections/futures/Trade/SmartMarginOnboardModal.tsx
+++ b/packages/app/src/sections/futures/Trade/SmartMarginOnboardModal.tsx
@@ -7,7 +7,7 @@ import styled from 'styled-components'
import BaseModal from 'components/BaseModal'
import { FlexDivRowCentered } from 'components/layout/flex'
import Spacer from 'components/Spacer'
-import { selectSusdBalance } from 'state/balances/selectors'
+import { selectSNXUSDBalance } from 'state/balances/selectors'
import { useAppSelector } from 'state/hooks'
type Props = {
@@ -21,7 +21,7 @@ const SocketBridge = dynamic(() => import('../../../components/SocketBridge'), {
const SmartMarginOnboardModal: React.FC = memo(({ onDismiss }) => {
const { t } = useTranslation()
- const susdBalance = useAppSelector(selectSusdBalance)
+ const susdBalance = useAppSelector(selectSNXUSDBalance)
return (
{
+ const potentialTradeDetails = useAppSelector(selectTradePreview)
-export const TradePanelFeeInfo = memo(() => {
return (
-
-
-
+
+
+
+
)
@@ -82,40 +85,6 @@ const TradingRewardRow = memo(() => {
)
})
-const LiquidationRow = memo(() => {
- const potentialTradeDetails = useAppSelector(selectTradePreview)
-
- return (
-
- )
-})
-
-const PriceImpactRow = memo(() => {
- const potentialTradeDetails = useAppSelector(selectTradePreview)
-
- return (
-
- )
-})
-
const FeeInfoBoxContainer = styled.div`
margin-bottom: 16px;
`
@@ -138,4 +107,4 @@ const CompactBox = styled.div<{ $isEligible: boolean }>`
`}
`
-export default TradePanelFeeInfo
+export default SmartMarginTradePanelPreview
diff --git a/packages/app/src/sections/futures/Trade/SubmitCrossMarginTrade.tsx b/packages/app/src/sections/futures/Trade/SubmitCrossMarginTrade.tsx
new file mode 100644
index 0000000000..98adcdaffb
--- /dev/null
+++ b/packages/app/src/sections/futures/Trade/SubmitCrossMarginTrade.tsx
@@ -0,0 +1,215 @@
+import { ZERO_WEI } from '@kwenta/sdk/constants'
+import { isZero } from '@kwenta/sdk/utils'
+import { wei } from '@synthetixio/wei'
+import React, { useCallback, useMemo } from 'react'
+import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
+
+import Button from 'components/Button'
+import { ERROR_MESSAGES } from 'components/ErrorNotifier'
+import Error from 'components/ErrorView'
+import { previewErrorI18n } from 'queries/futures/constants'
+import { setOpenModal } from 'state/app/reducer'
+import { selectMarketIndexPrice, selectMarketPriceInfo } from 'state/futures/common/selectors'
+import {
+ selectCrossMarginAccount,
+ selectCrossMarginPosition,
+ selectCrossMarginTradeInputs,
+ selectV3MarketInfo,
+} from 'state/futures/crossMargin/selectors'
+import { setTradePanelDrawerOpen } from 'state/futures/reducer'
+import {
+ selectMaxLeverage,
+ selectLeverageSide,
+ selectPendingDelayedOrder,
+ selectMaxUsdSizeInput,
+} from 'state/futures/selectors'
+import {
+ selectIsMarketCapReached,
+ selectOrderType,
+ selectPlaceOrderTranslationKey,
+ selectSmartMarginLeverage,
+ selectSmartMarginOrderPrice,
+ selectTradePreview,
+ selectTradePreviewError,
+ selectTradePreviewStatus,
+} from 'state/futures/smartMargin/selectors'
+import { useAppDispatch, useAppSelector } from 'state/hooks'
+import { FetchStatus } from 'state/types'
+import { orderPriceInvalidLabel } from 'utils/futures'
+
+const SubmitCrossMarginTradeButton: React.FC = () => {
+ const { t } = useTranslation()
+ const dispatch = useAppDispatch()
+
+ const { susdSize } = useAppSelector(selectCrossMarginTradeInputs)
+ const maxLeverageValue = useAppSelector(selectMaxLeverage)
+ const previewTrade = useAppSelector(selectTradePreview)
+ const previewError = useAppSelector(selectTradePreviewError)
+ const leverage = useAppSelector(selectSmartMarginLeverage)
+ const orderType = useAppSelector(selectOrderType)
+ const openOrder = useAppSelector(selectPendingDelayedOrder)
+ const leverageSide = useAppSelector(selectLeverageSide)
+ const maxUsdInputAmount = useAppSelector(selectMaxUsdSizeInput)
+ const isMarketCapReached = useAppSelector(selectIsMarketCapReached)
+ const placeOrderTranslationKey = useAppSelector(selectPlaceOrderTranslationKey)
+ const orderPrice = useAppSelector(selectSmartMarginOrderPrice)
+ const marketAssetRate = useAppSelector(selectMarketIndexPrice)
+ const marketInfo = useAppSelector(selectV3MarketInfo)
+ const indexPrice = useAppSelector(selectMarketPriceInfo)
+ const previewStatus = useAppSelector(selectTradePreviewStatus)
+ const crossMarginAccount = useAppSelector(selectCrossMarginAccount)
+ const position = useAppSelector(selectCrossMarginPosition)
+
+ const orderError = useMemo(() => {
+ if (previewError) return t(previewErrorI18n(previewError))
+ if (previewTrade?.statusMessage && previewTrade.statusMessage !== 'Success')
+ return previewTrade?.statusMessage
+ return null
+ }, [previewTrade?.statusMessage, previewError, t])
+
+ const increasingPosition = !position?.side || position?.side === leverageSide
+
+ const onSubmit = useCallback(() => {
+ dispatch(setTradePanelDrawerOpen(false))
+ if (!crossMarginAccount) {
+ dispatch(setOpenModal('futures_cross_margin_onboard'))
+ return
+ }
+ dispatch(setOpenModal('futures_confirm_cross_margin_trade'))
+ }, [crossMarginAccount, dispatch])
+
+ // TODO: Clean up errors and warnings and share logic with smart margin
+
+ const placeOrderDisabledReason = useMemo<{
+ message: string
+ show?: 'warn' | 'error'
+ } | null>(() => {
+ if (orderError) {
+ return { message: orderError, show: 'error' }
+ }
+ const maxLeverage = marketInfo?.appMaxLeverage ?? wei(1)
+
+ const indexPriceWei = indexPrice?.price ?? ZERO_WEI
+ const canLiquidate =
+ (previewTrade?.size.gt(0) && indexPriceWei.lt(previewTrade?.liqPrice)) ||
+ (previewTrade?.size.lt(0) && indexPriceWei.gt(previewTrade?.liqPrice))
+ if (canLiquidate) {
+ return {
+ show: 'warn',
+ message: `Position can be liquidated`,
+ }
+ }
+
+ if (leverage.gt(maxLeverageValue))
+ return {
+ show: 'warn',
+ message: `Max leverage ${maxLeverage.toString(0)}x exceeded`,
+ }
+ if (marketInfo?.isSuspended)
+ return {
+ show: 'warn',
+ message: `Market suspended`,
+ }
+ if (isMarketCapReached && increasingPosition)
+ return {
+ show: 'warn',
+ message: `Open interest limit exceeded`,
+ }
+
+ const invalidReason = orderPriceInvalidLabel(
+ orderPrice,
+ leverageSide,
+ marketAssetRate,
+ orderType
+ )
+
+ if ((orderType === 'limit' || orderType === 'stop_market') && !!invalidReason)
+ return {
+ show: 'warn',
+ message: invalidReason,
+ }
+ if (susdSize.gt(maxUsdInputAmount))
+ return {
+ show: 'warn',
+ message: 'Max trade size exceeded',
+ }
+ if (placeOrderTranslationKey === 'futures.market.trade.button.deposit-margin-minimum')
+ return {
+ show: 'warn',
+ message: 'Min $50 margin required',
+ }
+
+ if (isZero(susdSize)) {
+ return { message: 'Trade size required' }
+ }
+ if (orderType === 'market' && !!openOrder && !openOrder.isStale) {
+ return {
+ show: 'warn',
+ message: ERROR_MESSAGES.ORDER_PENDING,
+ }
+ }
+
+ return null
+ }, [
+ susdSize,
+ orderType,
+ openOrder,
+ orderError,
+ orderPrice,
+ leverageSide,
+ marketAssetRate,
+ marketInfo?.isSuspended,
+ placeOrderTranslationKey,
+ maxUsdInputAmount,
+ isMarketCapReached,
+ increasingPosition,
+ maxLeverageValue,
+ leverage,
+ indexPrice,
+ previewTrade,
+ marketInfo?.appMaxLeverage,
+ ])
+
+ return (
+ <>
+
+
+
+ {t(placeOrderTranslationKey)}
+
+
+
+
+ {placeOrderDisabledReason?.show ? (
+
+ ) : null}
+ >
+ )
+}
+
+const ManagePositionContainer = styled.div`
+ display: flex;
+ grid-gap: 15px;
+ margin-bottom: 16px;
+`
+
+const PlaceOrderButton = styled(Button)`
+ font-size: 16px;
+ height: 55px;
+ text-align: center;
+ white-space: normal;
+`
+
+export default SubmitCrossMarginTradeButton
diff --git a/packages/app/src/sections/futures/Trade/SwitchToSmartMargin.tsx b/packages/app/src/sections/futures/Trade/SwitchToSmartMargin.tsx
deleted file mode 100644
index bcd7346ae3..0000000000
--- a/packages/app/src/sections/futures/Trade/SwitchToSmartMargin.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import { useRouter } from 'next/router'
-import { useCallback } from 'react'
-import { useTranslation } from 'react-i18next'
-import styled from 'styled-components'
-
-import AlertIcon from 'assets/svg/app/alert.svg'
-import Button from 'components/Button'
-import { FlexDivColCentered } from 'components/layout/flex'
-import { BANNER_LINK_URL } from 'constants/announcement'
-import ROUTES from 'constants/routes'
-import { selectMarketAsset } from 'state/futures/selectors'
-import { useAppSelector } from 'state/hooks'
-
-type Props = {
- onDismiss: () => void
-}
-
-const SwitchToSmartMargin: React.FC = ({ onDismiss }) => {
- const { t } = useTranslation()
- const router = useRouter()
- const currentMarket = useAppSelector(selectMarketAsset)
-
- const switchToSM = useCallback(() => {
- router.push(ROUTES.Markets.MarketPair(currentMarket, 'cross_margin'))
- }, [currentMarket, router])
-
- return (
-
-
-
-
-
- {t('futures.cta-buttons.copy')}
-
- {t('futures.cta-buttons.learn-more')}
-
-
-
-
- {t('futures.cta-buttons.dismiss')}
-
-
- )
-}
-
-const StyledLink = styled.a`
- text-decoration: underline;
- color: ${(props) => props.theme.colors.selectedTheme.newTheme.text.secondary};
- font-size: 15px;
- cursor: pointer;
-`
-
-const UnsupportedMessage = styled.div`
- margin-top: 26.25px;
- font-size: 15px;
- color: ${(props) => props.theme.colors.selectedTheme.newTheme.text.primary};
- margin-bottom: 45px;
- line-height: 18px;
-`
-
-const ButtonContainer = styled(FlexDivColCentered)`
- width: 100%;
- justify-content: center;
- row-gap: 17.5px;
-`
-
-const Title = styled.div`
- font-family: ${(props) => props.theme.fonts.monoBold};
- font-size: 23px;
- color: ${(props) => props.theme.colors.selectedTheme.button.text.primary};
-`
-
-const MessageContainer = styled.div`
- margin-top: 51.25px;
- padding: 0 40px;
- text-align: center;
-`
-
-export default SwitchToSmartMargin
diff --git a/packages/app/src/sections/futures/Trade/TradeBalanceCrossMargin.tsx b/packages/app/src/sections/futures/Trade/TradeBalanceCrossMargin.tsx
new file mode 100644
index 0000000000..c6454fed43
--- /dev/null
+++ b/packages/app/src/sections/futures/Trade/TradeBalanceCrossMargin.tsx
@@ -0,0 +1,175 @@
+import { MIN_MARGIN_AMOUNT } from '@kwenta/sdk/constants'
+import { FuturesMarginType } from '@kwenta/sdk/types'
+import { formatDollars } from '@kwenta/sdk/utils'
+import { memo, useMemo, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
+
+import Button from 'components/Button'
+import { FlexDivCol, FlexDivRow, FlexDivRowCentered } from 'components/layout/flex'
+import { StyledCaretDownIcon } from 'components/Select'
+import { Body, NumericValue } from 'components/Text'
+import useWindowSize from 'hooks/useWindowSize'
+import { setOpenModal } from 'state/app/reducer'
+import { selectSNXUSDBalance } from 'state/balances/selectors'
+import { selectFuturesType } from 'state/futures/common/selectors'
+import {
+ selectCrossMarginAccount,
+ selectCrossMarginAvailableMargin,
+ selectWithdrawableCrossMargin,
+} from 'state/futures/crossMargin/selectors'
+import { useAppDispatch, useAppSelector } from 'state/hooks'
+
+import PencilButton from '../../../components/Button/PencilButton'
+
+type TradeBalanceProps = {
+ isMobile?: boolean
+}
+
+const TradeBalanceCrossMargin: React.FC = memo(({ isMobile = false }) => {
+ const { t } = useTranslation()
+ const dispatch = useAppDispatch()
+ const { deviceType } = useWindowSize()
+
+ const accountType = useAppSelector(selectFuturesType)
+ const availableCrossMargin = useAppSelector(selectCrossMarginAvailableMargin)
+ const withdrawable = useAppSelector(selectWithdrawableCrossMargin)
+ const crossMarginAccount = useAppSelector(selectCrossMarginAccount)
+ const walletBal = useAppSelector(selectSNXUSDBalance)
+
+ const [expanded, setExpanded] = useState(false)
+
+ const isDepositRequired = useMemo(() => {
+ return availableCrossMargin.lt(MIN_MARGIN_AMOUNT)
+ }, [availableCrossMargin])
+
+ const onClickContainer = () => {
+ if (accountType === FuturesMarginType.CROSS_MARGIN) return
+ setExpanded(!expanded)
+ }
+
+ const content = useMemo(() => {
+ if (!crossMarginAccount) {
+ return (
+
+ )
+ }
+ if (isDepositRequired) {
+ return (
+
+
+
+
+ {t('futures.market.trade.trade-balance.no-available-margin')}
+
+
+
+
+ {t('futures.market.trade.trade-balance.min-margin')}
+
+
+
+
+ )
+ }
+
+ if (isMobile) {
+ return (
+
+
+
+ {t('futures.market.trade.trade-balance.available-margin')}:
+
+
+ {formatDollars(availableCrossMargin)}
+
+
+
+ )
+ }
+
+ return (
+
+
+
+ {t('futures.market.trade.trade-balance.available-margin')}
+
+
+ {formatDollars(availableCrossMargin)}
+
+
+
+ )
+ }, [
+ t,
+ crossMarginAccount,
+ isDepositRequired,
+ availableCrossMargin,
+ isMobile,
+ expanded,
+ walletBal,
+ dispatch,
+ ])
+
+ return (
+
+
+ {content}
+
+ {withdrawable.gt(0) && (
+
+ {
+ e.stopPropagation()
+ dispatch(setOpenModal('futures_deposit_withdraw_cross_margin'))
+ }}
+ />
+
+ )}
+
+
+ )
+})
+
+const DepositContainer = styled(FlexDivRowCentered)`
+ width: 100%;
+`
+
+const Container = styled.div<{ mobile?: boolean }>`
+ width: 100%;
+ padding: 13px 15px;
+ border-bottom: ${(props) => (props.mobile ? props.theme.colors.selectedTheme.border : 0)};
+`
+
+const BalanceContainer = styled(FlexDivRowCentered)<{ clickable: boolean }>`
+ cursor: ${(props) => (props.clickable ? 'pointer' : 'default')};
+ width: 100%;
+`
+
+export default TradeBalanceCrossMargin
diff --git a/packages/app/src/sections/futures/Trade/TradeBalance.tsx b/packages/app/src/sections/futures/Trade/TradeBalanceSmartMargin.tsx
similarity index 78%
rename from packages/app/src/sections/futures/Trade/TradeBalance.tsx
rename to packages/app/src/sections/futures/Trade/TradeBalanceSmartMargin.tsx
index f73fab4b6e..29c1ef9b6b 100644
--- a/packages/app/src/sections/futures/Trade/TradeBalance.tsx
+++ b/packages/app/src/sections/futures/Trade/TradeBalanceSmartMargin.tsx
@@ -15,16 +15,11 @@ import useWindowSize from 'hooks/useWindowSize'
import { setOpenModal } from 'state/app/reducer'
import { selectShowModal } from 'state/app/selectors'
import { ModalType } from 'state/app/types'
-import {
- selectAvailableMargin,
- selectFuturesType,
- selectIdleMargin,
- selectLockedMarginInMarkets,
-} from 'state/futures/selectors'
+import { selectIdleMargin, selectLockedMarginInMarkets } from 'state/futures/smartMargin/selectors'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import PencilButton from '../../../components/Button/PencilButton'
-import CrossMarginInfoBox from '../TradeCrossMargin/CrossMarginInfoBox'
+import SmartMarginInfoBox from '../TradeSmartMargin/SmartMarginInfoBox'
import SmartMarginOnboardModal from './SmartMarginOnboardModal'
@@ -63,10 +58,8 @@ const TradeBalance = memo(() => {
const dispatch = useAppDispatch()
const { deviceType } = useWindowSize()
- const accountType = useAppSelector(selectFuturesType)
- const availableCrossMargin = useAppSelector(selectIdleMargin)
+ const accountMargin = useAppSelector(selectIdleMargin)
const lockedMargin = useAppSelector(selectLockedMarginInMarkets)
- const availableIsolatedMargin = useAppSelector(selectAvailableMargin)
const openModal = useAppSelector(selectShowModal)
const [expanded, setExpanded] = useState(false)
@@ -77,31 +70,28 @@ const TradeBalance = memo(() => {
return { isMobile, size }
}, [deviceType])
- const isCrossMarginAccount = useMemo(() => accountType === 'cross_margin', [accountType])
-
const isDepositRequired = useMemo(() => {
- return isCrossMarginAccount && availableCrossMargin.lt(MIN_MARGIN_AMOUNT) && lockedMargin.eq(0)
- }, [availableCrossMargin, isCrossMarginAccount, lockedMargin])
+ return accountMargin.lt(MIN_MARGIN_AMOUNT) && lockedMargin.eq(0)
+ }, [accountMargin, lockedMargin])
const onClickContainer = useCallback(() => {
- if (!isCrossMarginAccount) return
setExpanded(!expanded)
- }, [expanded, isCrossMarginAccount])
+ }, [expanded])
return (
-
+
{isDepositRequired ? (
- {availableCrossMargin.lt(0.01) ? (
+ {accountMargin.lt(0.01) ? (
t('futures.market.trade.trade-balance.no-available-margin')
) : (
)}
@@ -111,7 +101,7 @@ const TradeBalance = memo(() => {
{t('futures.market.trade.trade-balance.min-margin')}