Initialize perps (#648)
* setup routing * add basic perps interface * small fix
This commit is contained in:
parent
6fcadfa33f
commit
a4bbf92ced
@ -7,7 +7,7 @@ interface Props {
|
|||||||
|
|
||||||
export default function Divider(props: Props) {
|
export default function Divider(props: Props) {
|
||||||
if (props.orientation === 'vertical') {
|
if (props.orientation === 'vertical') {
|
||||||
return <div className={classNames('h-full w-[1px] bg-white/10', props.className)}></div>
|
return <div className={classNames(props.className, 'h-full w-[1px] bg-white/10')}></div>
|
||||||
}
|
}
|
||||||
return <div className={classNames('h-[1px] w-full bg-white/10', props.className)}></div>
|
return <div className={classNames('h-[1px] w-full bg-white/10', props.className)}></div>
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,13 @@ import Settings from 'components/Settings'
|
|||||||
import Wallet from 'components/Wallet'
|
import Wallet from 'components/Wallet'
|
||||||
import useAccountId from 'hooks/useAccountId'
|
import useAccountId from 'hooks/useAccountId'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import { ENABLE_HLS, ENABLE_PERPS } from 'utils/constants'
|
||||||
import { WalletID } from 'types/enums/wallet'
|
import { WalletID } from 'types/enums/wallet'
|
||||||
import { ENABLE_HLS } from 'utils/constants'
|
|
||||||
import { getGovernanceUrl } from 'utils/helpers'
|
import { getGovernanceUrl } from 'utils/helpers'
|
||||||
|
|
||||||
export const menuTree = (walletId: WalletID): MenuTreeEntry[] => [
|
export const menuTree = (walletId: WalletID): MenuTreeEntry[] => [
|
||||||
{ pages: ['trade'], label: 'Trade' },
|
{ pages: ['trade'], label: 'Trade' },
|
||||||
|
...(ENABLE_PERPS ? [{ pages: ['perps'] as Page[], label: 'Perps' }] : []),
|
||||||
{ pages: ['lend', 'farm'], label: 'Earn' },
|
{ pages: ['lend', 'farm'], label: 'Earn' },
|
||||||
{ pages: ['borrow'], label: 'Borrow' },
|
{ pages: ['borrow'], label: 'Borrow' },
|
||||||
...(ENABLE_HLS ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []),
|
...(ENABLE_HLS ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []),
|
||||||
|
15
src/components/Perps/Module/LeverageButtons.tsx
Normal file
15
src/components/Perps/Module/LeverageButtons.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Button from 'components/Button'
|
||||||
|
|
||||||
|
const LEVERAGE_PRESETS = [1, 2, 3, 5, 10]
|
||||||
|
|
||||||
|
export function LeverageButtons() {
|
||||||
|
return (
|
||||||
|
<div className='flex justify-between'>
|
||||||
|
{LEVERAGE_PRESETS.map((leverage) => (
|
||||||
|
<Button key={leverage} color='tertiary' className='w-12'>
|
||||||
|
{leverage}x
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
14
src/components/Perps/Module/Or.tsx
Normal file
14
src/components/Perps/Module/Or.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import Divider from 'components/Divider'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
|
||||||
|
export function Or() {
|
||||||
|
return (
|
||||||
|
<div className='flex items-center gap-2'>
|
||||||
|
<Divider />
|
||||||
|
<Text size='xs' className='text-white/40'>
|
||||||
|
OR
|
||||||
|
</Text>
|
||||||
|
<Divider />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
59
src/components/Perps/Module/PerpsModule.tsx
Normal file
59
src/components/Perps/Module/PerpsModule.tsx
Normal file
@ -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<AvailableOrderType>('Market')
|
||||||
|
const [selectedOrderDirection, setSelectedOrderDirection] = useState<OrderDirection>('long')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
contentClassName='px-4 gap-5 flex flex-col'
|
||||||
|
title={
|
||||||
|
<div className='flex justify-between bg-white/10 py-4 pl-4 pr-2 items-center'>
|
||||||
|
<Text>
|
||||||
|
ETH<span className='text-white/60'>/USD</span>
|
||||||
|
</Text>
|
||||||
|
<Button color='quaternary' variant='transparent' rightIcon={<ChevronDown />}>
|
||||||
|
All Markets
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
className='mb-4'
|
||||||
|
>
|
||||||
|
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
|
||||||
|
<SelectLongShort
|
||||||
|
direction={selectedOrderDirection}
|
||||||
|
onChangeDirection={setSelectedOrderDirection}
|
||||||
|
/>
|
||||||
|
<AssetAmountInput
|
||||||
|
label='Amount'
|
||||||
|
max={BN_ZERO}
|
||||||
|
amount={BN_ZERO}
|
||||||
|
setAmount={() => {}}
|
||||||
|
asset={ASSETS[0]}
|
||||||
|
maxButtonLabel='Max:'
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
<Or />
|
||||||
|
<Text size='sm'>Position Leverage</Text>
|
||||||
|
<RangeInput max={0} value={0} onChange={() => {}} />
|
||||||
|
<LeverageButtons />
|
||||||
|
<Spacer />
|
||||||
|
<Button>{selectedOrderDirection} ETH</Button>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
62
src/components/Perps/Module/SelectLongShort.tsx
Normal file
62
src/components/Perps/Module/SelectLongShort.tsx
Normal file
@ -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 (
|
||||||
|
<div className='flex bg-black/20 rounded-sm'>
|
||||||
|
<Direction
|
||||||
|
onClick={() => props.onChangeDirection('long')}
|
||||||
|
direction='long'
|
||||||
|
isActive={props.direction === 'long'}
|
||||||
|
/>
|
||||||
|
<Direction
|
||||||
|
onClick={() => props.onChangeDirection('short')}
|
||||||
|
direction='short'
|
||||||
|
isActive={props.direction === 'short'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DirectionProps {
|
||||||
|
direction: 'long' | 'short'
|
||||||
|
isActive: boolean
|
||||||
|
onClick: () => void
|
||||||
|
}
|
||||||
|
function Direction(props: DirectionProps) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classNames(
|
||||||
|
'px-4 py-3 rounded-sm flex-1',
|
||||||
|
borderColors[props.direction],
|
||||||
|
props.isActive && 'border bg-white/10',
|
||||||
|
)}
|
||||||
|
onClick={props.onClick}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className={classNames(
|
||||||
|
'text-center capitalize',
|
||||||
|
props.isActive ? directionColors[props.direction] : 'text-white/20',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{props.direction}
|
||||||
|
</Text>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const directionColors = {
|
||||||
|
long: 'text-success',
|
||||||
|
short: 'text-error',
|
||||||
|
}
|
||||||
|
|
||||||
|
const borderColors = {
|
||||||
|
long: 'border-success',
|
||||||
|
short: 'border-error',
|
||||||
|
}
|
12
src/components/Perps/PerpsChart.tsx
Normal file
12
src/components/Perps/PerpsChart.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import TradeChart from 'components/Trade/TradeChart'
|
||||||
|
import { ASSETS } from 'constants/assets'
|
||||||
|
|
||||||
|
export function PerpsChart() {
|
||||||
|
return (
|
||||||
|
<div className='h-full'>
|
||||||
|
<TradeChart buyAsset={ASSETS[0]} sellAsset={ASSETS[1]} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
47
src/components/Perps/PerpsInfo.tsx
Normal file
47
src/components/Perps/PerpsInfo.tsx
Normal file
@ -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(
|
||||||
|
() => [
|
||||||
|
<Text key='item1'>$6,735</Text>,
|
||||||
|
<InfoItem key='item2' label='Label' item={<Text size='sm'>Value</Text>} />,
|
||||||
|
<InfoItem key='item3' label='Label' item={<Text size='sm'>Value</Text>} />,
|
||||||
|
<InfoItem key='item4' label='Label' item={<Text size='sm'>Value</Text>} />,
|
||||||
|
<InfoItem key='item5' label='Label' item={<Text size='sm'>Value</Text>} />,
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card contentClassName='bg-white/10 py-3.5 px-4'>
|
||||||
|
<div className='flex gap-4 items-center'>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
{item}
|
||||||
|
{index !== items.length - 1 && <Divider orientation='vertical' className='h-9' />}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InfoItemProps {
|
||||||
|
item: React.ReactNode
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function InfoItem(props: InfoItemProps) {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-1'>
|
||||||
|
<Text size='xs' className='text-white/40'>
|
||||||
|
{props.label}
|
||||||
|
</Text>
|
||||||
|
{props.item}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
5
src/components/Perps/PerpsPositions.tsx
Normal file
5
src/components/Perps/PerpsPositions.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import AccountDetailsCard from 'components/Trade/AccountDetailsCard'
|
||||||
|
|
||||||
|
export function PerpsPositions() {
|
||||||
|
return <AccountDetailsCard />
|
||||||
|
}
|
@ -7,9 +7,11 @@ import HLSFarmPage from 'pages/HLSFarmPage'
|
|||||||
import HLSStakingPage from 'pages/HLSStakingPage'
|
import HLSStakingPage from 'pages/HLSStakingPage'
|
||||||
import LendPage from 'pages/LendPage'
|
import LendPage from 'pages/LendPage'
|
||||||
import MobilePage from 'pages/MobilePage'
|
import MobilePage from 'pages/MobilePage'
|
||||||
|
import PerpsPage from 'pages/PerpsPage'
|
||||||
import PortfolioAccountPage from 'pages/PortfolioAccountPage'
|
import PortfolioAccountPage from 'pages/PortfolioAccountPage'
|
||||||
import PortfolioPage from 'pages/PortfolioPage'
|
import PortfolioPage from 'pages/PortfolioPage'
|
||||||
import TradePage from 'pages/TradePage'
|
import TradePage from 'pages/TradePage'
|
||||||
|
import { ENABLE_PERPS } from 'utils/constants'
|
||||||
import { ENABLE_HLS } from 'utils/constants'
|
import { ENABLE_HLS } from 'utils/constants'
|
||||||
|
|
||||||
export default function Routes() {
|
export default function Routes() {
|
||||||
@ -23,6 +25,7 @@ export default function Routes() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Route path='/trade' element={<TradePage />} />
|
<Route path='/trade' element={<TradePage />} />
|
||||||
|
{ENABLE_PERPS && <Route path='/perps' element={<PerpsPage />} />}
|
||||||
<Route path='/farm' element={<FarmPage />} />
|
<Route path='/farm' element={<FarmPage />} />
|
||||||
<Route path='/lend' element={<LendPage />} />
|
<Route path='/lend' element={<LendPage />} />
|
||||||
<Route path='/borrow' element={<BorrowPage />} />
|
<Route path='/borrow' element={<BorrowPage />} />
|
||||||
@ -33,6 +36,7 @@ export default function Routes() {
|
|||||||
<Route path='/' element={<TradePage />} />
|
<Route path='/' element={<TradePage />} />
|
||||||
<Route path='/wallets/:address'>
|
<Route path='/wallets/:address'>
|
||||||
<Route path='trade' element={<TradePage />} />
|
<Route path='trade' element={<TradePage />} />
|
||||||
|
{ENABLE_PERPS && <Route path='perps' element={<PerpsPage />} />}
|
||||||
<Route path='farm' element={<FarmPage />} />
|
<Route path='farm' element={<FarmPage />} />
|
||||||
<Route path='lend' element={<LendPage />} />
|
<Route path='lend' element={<LendPage />} />
|
||||||
<Route path='borrow' element={<BorrowPage />} />
|
<Route path='borrow' element={<BorrowPage />} />
|
||||||
|
3
src/components/Spacer.tsx
Normal file
3
src/components/Spacer.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function Spacer() {
|
||||||
|
return <div className='h-full w-full flex-1' />
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import { useEffect, useMemo, useRef } from 'react'
|
import { useEffect, useMemo, useRef } from 'react'
|
||||||
|
|
||||||
import Card from 'components/Card'
|
import Card from 'components/Card'
|
||||||
import { DataFeed, PAIR_SEPARATOR } from 'components/Trade/TradeChart/DataFeed'
|
|
||||||
import { disabledFeatures, enabledFeatures, overrides } from 'components/Trade/TradeChart/constants'
|
import { disabledFeatures, enabledFeatures, overrides } from 'components/Trade/TradeChart/constants'
|
||||||
|
import { DataFeed, PAIR_SEPARATOR } from 'components/Trade/TradeChart/DataFeed'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import {
|
import {
|
||||||
ChartingLibraryWidgetOptions,
|
ChartingLibraryWidgetOptions,
|
||||||
@ -102,7 +102,7 @@ export const TVChartContainer = (props: Props) => {
|
|||||||
}, [props.buyAsset.denom, props.sellAsset.denom])
|
}, [props.buyAsset.denom, props.sellAsset.denom])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title='Trading Chart' contentClassName='px-0.5 pb-0.5 h-full'>
|
<Card title='Trading Chart' contentClassName='px-0.5 pb-0.5 h-full' className='h-full'>
|
||||||
<div ref={chartContainerRef} className='h-full overflow-hidden rounded-b-base' />
|
<div ref={chartContainerRef} className='h-full overflow-hidden rounded-b-base' />
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
|
@ -16,7 +16,7 @@ export default function OrderTypeSelector(props: Props) {
|
|||||||
const { selected, onChange } = props
|
const { selected, onChange } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-row flex-1 px-3 pt-4'>
|
<div className='flex flex-row pt-4'>
|
||||||
{ORDER_TYPE_TABS.map((tab) => {
|
{ORDER_TYPE_TABS.map((tab) => {
|
||||||
const isSelected = tab.type === selected
|
const isSelected = tab.type === selected
|
||||||
const classes = classNames(
|
const classes = classNames(
|
||||||
|
@ -324,7 +324,9 @@ export default function SwapForm(props: Props) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Divider />
|
<Divider />
|
||||||
|
<div className='px-3'>
|
||||||
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
|
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
|
||||||
|
</div>
|
||||||
<div className='flex flex-col gap-6 px-3 mt-6'>
|
<div className='flex flex-col gap-6 px-3 mt-6'>
|
||||||
<AssetAmountInput
|
<AssetAmountInput
|
||||||
label='Buy'
|
label='Buy'
|
||||||
|
@ -201,7 +201,7 @@ export function useUpdatedAccount(account?: Account) {
|
|||||||
removeDeposits([BNCoin.fromDenomAndBigNumber(collateralDenom, removeDepositAmount)])
|
removeDeposits([BNCoin.fromDenomAndBigNumber(collateralDenom, removeDepositAmount)])
|
||||||
removeDebts([BNCoin.fromDenomAndBigNumber(debtDenom, repayAmount)])
|
removeDebts([BNCoin.fromDenomAndBigNumber(debtDenom, repayAmount)])
|
||||||
},
|
},
|
||||||
[prices],
|
[prices, slippage],
|
||||||
)
|
)
|
||||||
|
|
||||||
const simulateVaultDeposit = useCallback(
|
const simulateVaultDeposit = useCallback(
|
||||||
|
19
src/pages/PerpsPage.tsx
Normal file
19
src/pages/PerpsPage.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { PerpsModule } from 'components/Perps/Module/PerpsModule'
|
||||||
|
import { PerpsChart } from 'components/Perps/PerpsChart'
|
||||||
|
import { PerpsInfo } from 'components/Perps/PerpsInfo'
|
||||||
|
import { PerpsPositions } from 'components/Perps/PerpsPositions'
|
||||||
|
|
||||||
|
export default function PerpsPage() {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col w-full h-full gap-4'>
|
||||||
|
<div className='grid w-full grid-cols-[auto_346px] gap-4 pb-4'>
|
||||||
|
<div className='grid grid-cols-1 grid-rows-[min-content_auto_min-content] gap-4 h-[calc(100vh-93px)] pb-4'>
|
||||||
|
<PerpsInfo />
|
||||||
|
<PerpsChart />
|
||||||
|
<PerpsPositions />
|
||||||
|
</div>
|
||||||
|
<PerpsModule />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -47,7 +47,10 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
|||||||
DEFAULT_SETTINGS.reduceMotion,
|
DEFAULT_SETTINGS.reduceMotion,
|
||||||
)
|
)
|
||||||
const accountDetailsExpanded = useStore((s) => s.accountDetailsExpanded)
|
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()
|
const accountId = useAccountId()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
1
src/types/interfaces/perps.d.ts
vendored
Normal file
1
src/types/interfaces/perps.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
type OrderDirection = 'long' | 'short'
|
1
src/types/interfaces/route.d.ts
vendored
1
src/types/interfaces/route.d.ts
vendored
@ -1,5 +1,6 @@
|
|||||||
type Page =
|
type Page =
|
||||||
| 'trade'
|
| 'trade'
|
||||||
|
| 'perps'
|
||||||
| 'borrow'
|
| 'borrow'
|
||||||
| 'farm'
|
| 'farm'
|
||||||
| 'lend'
|
| 'lend'
|
||||||
|
@ -19,7 +19,16 @@ export function getRoute(page: Page, address?: string, accountId?: string | null
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getPage(pathname: string): Page {
|
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 segments = pathname.split('/')
|
||||||
|
|
||||||
const page = segments.find((segment) => pages.includes(segment as Page))
|
const page = segments.find((segment) => pages.includes(segment as Page))
|
||||||
|
@ -166,6 +166,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
height: {
|
height: {
|
||||||
4.5: '18px',
|
4.5: '18px',
|
||||||
|
9: '36px',
|
||||||
15: '60px',
|
15: '60px',
|
||||||
45: '180px',
|
45: '180px',
|
||||||
50: '200px',
|
50: '200px',
|
||||||
|
Loading…
Reference in New Issue
Block a user