From a4bbf92ced7f1c2bafa49a26e6db314f76a5fe82 Mon Sep 17 00:00:00 2001 From: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com> Date: Thu, 23 Nov 2023 15:42:08 +0100 Subject: [PATCH] Initialize perps (#648) * setup routing * add basic perps interface * small fix --- src/components/Divider.tsx | 2 +- src/components/Header/DesktopHeader.tsx | 3 +- .../Perps/Module/LeverageButtons.tsx | 15 +++++ src/components/Perps/Module/Or.tsx | 14 +++++ src/components/Perps/Module/PerpsModule.tsx | 59 ++++++++++++++++++ .../Perps/Module/SelectLongShort.tsx | 62 +++++++++++++++++++ src/components/Perps/PerpsChart.tsx | 12 ++++ src/components/Perps/PerpsInfo.tsx | 47 ++++++++++++++ src/components/Perps/PerpsPositions.tsx | 5 ++ src/components/Routes.tsx | 4 ++ src/components/Spacer.tsx | 3 + .../Trade/TradeChart/TVChartContainer.tsx | 4 +- .../SwapForm/OrderTypeSelector/index.tsx | 2 +- .../Trade/TradeModule/SwapForm/index.tsx | 4 +- src/hooks/useUpdatedAccount/index.ts | 2 +- src/pages/PerpsPage.tsx | 19 ++++++ src/pages/_layout.tsx | 5 +- src/types/interfaces/perps.d.ts | 1 + src/types/interfaces/route.d.ts | 1 + src/utils/route.ts | 11 +++- tailwind.config.js | 1 + 21 files changed, 267 insertions(+), 9 deletions(-) create mode 100644 src/components/Perps/Module/LeverageButtons.tsx create mode 100644 src/components/Perps/Module/Or.tsx create mode 100644 src/components/Perps/Module/PerpsModule.tsx create mode 100644 src/components/Perps/Module/SelectLongShort.tsx create mode 100644 src/components/Perps/PerpsChart.tsx create mode 100644 src/components/Perps/PerpsInfo.tsx create mode 100644 src/components/Perps/PerpsPositions.tsx create mode 100644 src/components/Spacer.tsx create mode 100644 src/pages/PerpsPage.tsx create mode 100644 src/types/interfaces/perps.d.ts diff --git a/src/components/Divider.tsx b/src/components/Divider.tsx index cd57f1df..efd9fcca 100644 --- a/src/components/Divider.tsx +++ b/src/components/Divider.tsx @@ -7,7 +7,7 @@ interface Props { export default function Divider(props: Props) { if (props.orientation === 'vertical') { - return
+ return
} return
} diff --git a/src/components/Header/DesktopHeader.tsx b/src/components/Header/DesktopHeader.tsx index 6ec89232..555d40c0 100644 --- a/src/components/Header/DesktopHeader.tsx +++ b/src/components/Header/DesktopHeader.tsx @@ -10,12 +10,13 @@ import Settings from 'components/Settings' import Wallet from 'components/Wallet' import useAccountId from 'hooks/useAccountId' import useStore from 'store' +import { ENABLE_HLS, ENABLE_PERPS } from 'utils/constants' import { WalletID } from 'types/enums/wallet' -import { ENABLE_HLS } from 'utils/constants' import { getGovernanceUrl } from 'utils/helpers' export const menuTree = (walletId: WalletID): MenuTreeEntry[] => [ { pages: ['trade'], label: 'Trade' }, + ...(ENABLE_PERPS ? [{ pages: ['perps'] as Page[], label: 'Perps' }] : []), { pages: ['lend', 'farm'], label: 'Earn' }, { pages: ['borrow'], label: 'Borrow' }, ...(ENABLE_HLS ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []), diff --git a/src/components/Perps/Module/LeverageButtons.tsx b/src/components/Perps/Module/LeverageButtons.tsx new file mode 100644 index 00000000..66a61bb9 --- /dev/null +++ b/src/components/Perps/Module/LeverageButtons.tsx @@ -0,0 +1,15 @@ +import Button from 'components/Button' + +const LEVERAGE_PRESETS = [1, 2, 3, 5, 10] + +export function LeverageButtons() { + return ( +
+ {LEVERAGE_PRESETS.map((leverage) => ( + + ))} +
+ ) +} diff --git a/src/components/Perps/Module/Or.tsx b/src/components/Perps/Module/Or.tsx new file mode 100644 index 00000000..cb5fb5d6 --- /dev/null +++ b/src/components/Perps/Module/Or.tsx @@ -0,0 +1,14 @@ +import Divider from 'components/Divider' +import Text from 'components/Text' + +export function Or() { + return ( +
+ + + OR + + +
+ ) +} diff --git a/src/components/Perps/Module/PerpsModule.tsx b/src/components/Perps/Module/PerpsModule.tsx new file mode 100644 index 00000000..29636a1c --- /dev/null +++ b/src/components/Perps/Module/PerpsModule.tsx @@ -0,0 +1,59 @@ +import { useState } from 'react' + +import Button from 'components/Button' +import Card from 'components/Card' +import { ChevronDown } from 'components/Icons' +import { LeverageButtons } from 'components/Perps/Module/LeverageButtons' +import { Or } from 'components/Perps/Module/Or' +import { SelectLongShort } from 'components/Perps/Module/SelectLongShort' +import RangeInput from 'components/RangeInput' +import { Spacer } from 'components/Spacer' +import Text from 'components/Text' +import AssetAmountInput from 'components/Trade/TradeModule/SwapForm/AssetAmountInput' +import OrderTypeSelector from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector' +import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types' +import { ASSETS } from 'constants/assets' +import { BN_ZERO } from 'constants/math' + +export function PerpsModule() { + const [selectedOrderType, setSelectedOrderType] = useState('Market') + const [selectedOrderDirection, setSelectedOrderDirection] = useState('long') + + return ( + + + ETH/USD + + + + } + className='mb-4' + > + + + {}} + asset={ASSETS[0]} + maxButtonLabel='Max:' + disabled={false} + /> + + Position Leverage + {}} /> + + + + + ) +} diff --git a/src/components/Perps/Module/SelectLongShort.tsx b/src/components/Perps/Module/SelectLongShort.tsx new file mode 100644 index 00000000..6988ba6f --- /dev/null +++ b/src/components/Perps/Module/SelectLongShort.tsx @@ -0,0 +1,62 @@ +import classNames from 'classnames' + +import Text from 'components/Text' + +interface Props { + direction: OrderDirection + onChangeDirection: (direction: OrderDirection) => void +} + +export function SelectLongShort(props: Props) { + return ( +
+ props.onChangeDirection('long')} + direction='long' + isActive={props.direction === 'long'} + /> + props.onChangeDirection('short')} + direction='short' + isActive={props.direction === 'short'} + /> +
+ ) +} + +interface DirectionProps { + direction: 'long' | 'short' + isActive: boolean + onClick: () => void +} +function Direction(props: DirectionProps) { + return ( + + ) +} + +const directionColors = { + long: 'text-success', + short: 'text-error', +} + +const borderColors = { + long: 'border-success', + short: 'border-error', +} diff --git a/src/components/Perps/PerpsChart.tsx b/src/components/Perps/PerpsChart.tsx new file mode 100644 index 00000000..f6d52fae --- /dev/null +++ b/src/components/Perps/PerpsChart.tsx @@ -0,0 +1,12 @@ +import React from 'react' + +import TradeChart from 'components/Trade/TradeChart' +import { ASSETS } from 'constants/assets' + +export function PerpsChart() { + return ( +
+ +
+ ) +} diff --git a/src/components/Perps/PerpsInfo.tsx b/src/components/Perps/PerpsInfo.tsx new file mode 100644 index 00000000..13e9c4c4 --- /dev/null +++ b/src/components/Perps/PerpsInfo.tsx @@ -0,0 +1,47 @@ +import React, { useMemo } from 'react' + +import Card from 'components/Card' +import Divider from 'components/Divider' +import Text from 'components/Text' + +export function PerpsInfo() { + const items = useMemo( + () => [ + $6,735, + Value} />, + Value} />, + Value} />, + Value} />, + ], + [], + ) + + return ( + +
+ {items.map((item, index) => ( + + {item} + {index !== items.length - 1 && } + + ))} +
+
+ ) +} + +interface InfoItemProps { + item: React.ReactNode + label: string +} + +function InfoItem(props: InfoItemProps) { + return ( +
+ + {props.label} + + {props.item} +
+ ) +} diff --git a/src/components/Perps/PerpsPositions.tsx b/src/components/Perps/PerpsPositions.tsx new file mode 100644 index 00000000..03cb490e --- /dev/null +++ b/src/components/Perps/PerpsPositions.tsx @@ -0,0 +1,5 @@ +import AccountDetailsCard from 'components/Trade/AccountDetailsCard' + +export function PerpsPositions() { + return +} diff --git a/src/components/Routes.tsx b/src/components/Routes.tsx index c93eaa87..5ad3abf9 100644 --- a/src/components/Routes.tsx +++ b/src/components/Routes.tsx @@ -7,9 +7,11 @@ import HLSFarmPage from 'pages/HLSFarmPage' import HLSStakingPage from 'pages/HLSStakingPage' import LendPage from 'pages/LendPage' import MobilePage from 'pages/MobilePage' +import PerpsPage from 'pages/PerpsPage' import PortfolioAccountPage from 'pages/PortfolioAccountPage' import PortfolioPage from 'pages/PortfolioPage' import TradePage from 'pages/TradePage' +import { ENABLE_PERPS } from 'utils/constants' import { ENABLE_HLS } from 'utils/constants' export default function Routes() { @@ -23,6 +25,7 @@ export default function Routes() { } > } /> + {ENABLE_PERPS && } />} } /> } /> } /> @@ -33,6 +36,7 @@ export default function Routes() { } /> } /> + {ENABLE_PERPS && } />} } /> } /> } /> diff --git a/src/components/Spacer.tsx b/src/components/Spacer.tsx new file mode 100644 index 00000000..87ec3d34 --- /dev/null +++ b/src/components/Spacer.tsx @@ -0,0 +1,3 @@ +export function Spacer() { + return
+} diff --git a/src/components/Trade/TradeChart/TVChartContainer.tsx b/src/components/Trade/TradeChart/TVChartContainer.tsx index b8497d68..45abea40 100644 --- a/src/components/Trade/TradeChart/TVChartContainer.tsx +++ b/src/components/Trade/TradeChart/TVChartContainer.tsx @@ -1,8 +1,8 @@ import { useEffect, useMemo, useRef } from 'react' import Card from 'components/Card' -import { DataFeed, PAIR_SEPARATOR } from 'components/Trade/TradeChart/DataFeed' import { disabledFeatures, enabledFeatures, overrides } from 'components/Trade/TradeChart/constants' +import { DataFeed, PAIR_SEPARATOR } from 'components/Trade/TradeChart/DataFeed' import useStore from 'store' import { ChartingLibraryWidgetOptions, @@ -102,7 +102,7 @@ export const TVChartContainer = (props: Props) => { }, [props.buyAsset.denom, props.sellAsset.denom]) return ( - +
) diff --git a/src/components/Trade/TradeModule/SwapForm/OrderTypeSelector/index.tsx b/src/components/Trade/TradeModule/SwapForm/OrderTypeSelector/index.tsx index 956719ba..c5527818 100644 --- a/src/components/Trade/TradeModule/SwapForm/OrderTypeSelector/index.tsx +++ b/src/components/Trade/TradeModule/SwapForm/OrderTypeSelector/index.tsx @@ -16,7 +16,7 @@ export default function OrderTypeSelector(props: Props) { const { selected, onChange } = props return ( -
+
{ORDER_TYPE_TABS.map((tab) => { const isSelected = tab.type === selected const classes = classNames( diff --git a/src/components/Trade/TradeModule/SwapForm/index.tsx b/src/components/Trade/TradeModule/SwapForm/index.tsx index 4c47d6be..3c62d04d 100644 --- a/src/components/Trade/TradeModule/SwapForm/index.tsx +++ b/src/components/Trade/TradeModule/SwapForm/index.tsx @@ -324,7 +324,9 @@ export default function SwapForm(props: Props) { /> )} - +
+ +
+
+
+ + + +
+ +
+
+ ) +} diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx index 63ab7c5e..bb71f862 100644 --- a/src/pages/_layout.tsx +++ b/src/pages/_layout.tsx @@ -47,7 +47,10 @@ export default function Layout({ children }: { children: React.ReactNode }) { DEFAULT_SETTINGS.reduceMotion, ) const accountDetailsExpanded = useStore((s) => s.accountDetailsExpanded) - const isFullWidth = location.pathname.includes('trade') || location.pathname === '/' + const isFullWidth = + location.pathname.includes('trade') || + location.pathname === '/' || + location.pathname.includes('perps') const accountId = useAccountId() return ( diff --git a/src/types/interfaces/perps.d.ts b/src/types/interfaces/perps.d.ts new file mode 100644 index 00000000..16d6cd20 --- /dev/null +++ b/src/types/interfaces/perps.d.ts @@ -0,0 +1 @@ +type OrderDirection = 'long' | 'short' diff --git a/src/types/interfaces/route.d.ts b/src/types/interfaces/route.d.ts index d1563795..7b025aab 100644 --- a/src/types/interfaces/route.d.ts +++ b/src/types/interfaces/route.d.ts @@ -1,5 +1,6 @@ type Page = | 'trade' + | 'perps' | 'borrow' | 'farm' | 'lend' diff --git a/src/utils/route.ts b/src/utils/route.ts index 80207bdc..df38bd2f 100644 --- a/src/utils/route.ts +++ b/src/utils/route.ts @@ -19,7 +19,16 @@ export function getRoute(page: Page, address?: string, accountId?: string | null } export function getPage(pathname: string): Page { - const pages: Page[] = ['trade', 'borrow', 'farm', 'lend', 'portfolio', 'hls-farm', 'hls-staking'] + const pages: Page[] = [ + 'trade', + 'perps', + 'borrow', + 'farm', + 'lend', + 'portfolio', + 'hls-farm', + 'hls-staking', + ] const segments = pathname.split('/') const page = segments.find((segment) => pages.includes(segment as Page)) diff --git a/tailwind.config.js b/tailwind.config.js index 5c1ff0a6..f8e3f952 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -166,6 +166,7 @@ module.exports = { }, height: { 4.5: '18px', + 9: '36px', 15: '60px', 45: '180px', 50: '200px',