perps: remove modify module and adjust main module (#761)

* perps: remove modify module and adjust main module

* fix build
This commit is contained in:
Bob van der Helm 2024-02-02 14:05:40 +01:00 committed by GitHub
parent 98ab2c635a
commit 19d4d1691d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 125 additions and 177 deletions

View File

@ -29,7 +29,6 @@ export default function Manage(props: Props) {
setSearchParams({ setSearchParams({
...params, ...params,
[SearchParams.PERPS_MARKET]: props.perpPosition.asset.denom, [SearchParams.PERPS_MARKET]: props.perpPosition.asset.denom,
[SearchParams.PERPS_MANAGE]: 'true',
}) })
}, },
}, },

View File

@ -1,109 +0,0 @@
import classNames from 'classnames'
import debounce from 'lodash.debounce'
import { useEffect, useMemo, useState } from 'react'
import { Cross } from 'components/common/Icons'
import LeverageSlider from 'components/common/LeverageSlider'
import { Spacer } from 'components/common/Spacer'
import Text from 'components/common/Text'
import { TradeDirectionSelector } from 'components/common/TradeDirectionSelector'
import { LeverageButtons } from 'components/perps/Module/LeverageButtons'
import { Or } from 'components/perps/Module/Or'
import usePerpsManageModule from 'components/perps/Module/PerpsManageModule/usePerpsManageModule'
import PerpsSummary from 'components/perps/Module/Summary'
import AssetAmountInput from 'components/trade/TradeModule/SwapForm/AssetAmountInput'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import getPerpsPosition from 'utils/getPerpsPosition'
import { BN } from 'utils/helpers'
export function PerpsManageModule() {
const [tradeDirection, setTradeDirection] = useState<TradeDirection | null>(null)
const [amount, setAmount] = useState<BigNumber | null>(null)
const account = useCurrentAccount()
const { simulatePerps, addedPerps } = useUpdatedAccount(account)
const {
closeManagePerpModule,
previousAmount,
previousTradeDirection,
previousLeverage,
leverage,
asset,
} = usePerpsManageModule(amount)
const debouncedUpdateAccount = useMemo(
() =>
debounce((perpsPosition: PerpsPosition) => {
if (
addedPerps &&
perpsPosition.amount === addedPerps.amount &&
perpsPosition.tradeDirection === addedPerps.tradeDirection
)
return
simulatePerps(perpsPosition)
}, 100),
[simulatePerps, addedPerps],
)
useEffect(() => {
const perpsPosition = getPerpsPosition(
asset,
amount ?? previousAmount,
tradeDirection ?? previousTradeDirection,
)
debouncedUpdateAccount(perpsPosition)
}, [
debouncedUpdateAccount,
asset,
amount,
previousAmount,
tradeDirection,
previousTradeDirection,
])
if (!asset) return null
return (
<div
className={classNames(
'px-4 gap-5 flex flex-col h-full pt-6 pb-4 w-full bg-white/5 absolute rounded-base isolate',
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas',
)}
>
<div className='flex justify-between mb-3'>
<Text>Manage Position</Text>
<button onClick={closeManagePerpModule} className='mr-1.5'>
<Cross width={16} />
</button>
</div>
<TradeDirectionSelector
direction={tradeDirection ?? previousTradeDirection}
onChangeDirection={setTradeDirection}
/>
<AssetAmountInput
label='Amount'
max={BN(1000000)} // TODO: Implement max calculation
amount={amount ?? previousAmount}
setAmount={setAmount}
asset={asset}
maxButtonLabel='Max:'
disabled={false}
/>
<Or />
<Text size='sm'>Position Leverage</Text>
<LeverageSlider max={0} value={0} onChange={() => {}} type={tradeDirection || 'long'} />
<LeverageButtons />
<Spacer />
<PerpsSummary
changeTradeDirection
amount={amount ?? previousAmount}
tradeDirection={tradeDirection ?? previousTradeDirection}
asset={asset}
leverage={leverage}
previousAmount={previousAmount}
previousTradeDirection={previousTradeDirection}
previousLeverage={previousLeverage}
/>
</div>
)
}

View File

@ -1,5 +1,5 @@
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import { useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import Card from 'components/common/Card' import Card from 'components/common/Card'
import LeverageSlider from 'components/common/LeverageSlider' import LeverageSlider from 'components/common/LeverageSlider'
@ -9,6 +9,7 @@ import { TradeDirectionSelector } from 'components/common/TradeDirectionSelector
import { LeverageButtons } from 'components/perps/Module/LeverageButtons' import { LeverageButtons } from 'components/perps/Module/LeverageButtons'
import { Or } from 'components/perps/Module/Or' import { Or } from 'components/perps/Module/Or'
import PerpsSummary from 'components/perps/Module/Summary' import PerpsSummary from 'components/perps/Module/Summary'
import usePerpsModule from 'components/perps/Module/usePerpsModule'
import AssetSelectorPerps from 'components/trade/TradeModule/AssetSelector/AssetSelectorPerps' import AssetSelectorPerps from 'components/trade/TradeModule/AssetSelector/AssetSelectorPerps'
import AssetAmountInput from 'components/trade/TradeModule/SwapForm/AssetAmountInput' import AssetAmountInput from 'components/trade/TradeModule/SwapForm/AssetAmountInput'
import OrderTypeSelector from 'components/trade/TradeModule/SwapForm/OrderTypeSelector' import OrderTypeSelector from 'components/trade/TradeModule/SwapForm/OrderTypeSelector'
@ -24,10 +25,11 @@ export function PerpsModule() {
const [selectedOrderType, setSelectedOrderType] = useState<AvailableOrderType>('Market') const [selectedOrderType, setSelectedOrderType] = useState<AvailableOrderType>('Market')
const [tradeDirection, setTradeDirection] = useState<TradeDirection>('long') const [tradeDirection, setTradeDirection] = useState<TradeDirection>('long')
const { perpsAsset } = usePerpsAsset() const { perpsAsset } = usePerpsAsset()
const [leverage, setLeverage] = useState<number>(1)
const account = useCurrentAccount() const account = useCurrentAccount()
const { simulatePerps, addedPerps } = useUpdatedAccount(account) const { simulatePerps, addedPerps } = useUpdatedAccount(account)
const [amount, setAmount] = useState<BigNumber>(BN_ZERO) const [amount, setAmount] = useState<BigNumber>(BN_ZERO)
const { previousAmount, previousTradeDirection, previousLeverage, leverage, hasActivePosition } =
usePerpsModule(amount)
const debouncedUpdateAccount = useMemo( const debouncedUpdateAccount = useMemo(
() => () =>
@ -44,46 +46,92 @@ export function PerpsModule() {
) )
useEffect(() => { useEffect(() => {
const perpsPosition = getPerpsPosition(perpsAsset, amount, tradeDirection) const perpsPosition = getPerpsPosition(
perpsAsset,
amount ?? previousAmount,
tradeDirection ?? previousTradeDirection,
)
debouncedUpdateAccount(perpsPosition) debouncedUpdateAccount(perpsPosition)
}, [debouncedUpdateAccount, amount, perpsAsset, tradeDirection]) }, [
debouncedUpdateAccount,
amount,
perpsAsset,
tradeDirection,
previousAmount,
previousTradeDirection,
])
const setLeverage = useCallback((leverage: number) => {
// TODO: Implement leverage setting
}, [])
const onChangeTradeDirection = useCallback(
(tradeDirection: TradeDirection) => {
setAmount(amount.times(-1))
setTradeDirection(tradeDirection)
},
[amount],
)
const onChangeAmount = useCallback(
(amount: BigNumber) => {
if (tradeDirection === 'short') {
setAmount(amount.times(-1))
return
}
setAmount(amount)
},
[tradeDirection],
)
if (!perpsAsset) return null if (!perpsAsset) return null
return ( return (
<Card <Card
contentClassName='px-4 gap-5 flex flex-col h-full pb-4' contentClassName='px-4 gap-5 flex flex-col h-full pb-4'
title={<AssetSelectorPerps asset={perpsAsset} />} title={<AssetSelectorPerps asset={perpsAsset} hasActivePosition={hasActivePosition} />}
className='h-full mb-4' className='h-full mb-4'
> >
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} /> <OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
<TradeDirectionSelector
<TradeDirectionSelector direction={tradeDirection} onChangeDirection={setTradeDirection} /> direction={tradeDirection}
onChangeDirection={onChangeTradeDirection}
/>
<AssetAmountInput <AssetAmountInput
label='Amount' label='Amount'
max={BN(1000000)} // TODO: Implement max calculation max={BN(1000000)} // TODO: Implement max calculation
amount={amount} amount={amount.abs()}
setAmount={setAmount} setAmount={onChangeAmount}
asset={perpsAsset} asset={perpsAsset}
maxButtonLabel='Max:' maxButtonLabel='Max:'
disabled={false} disabled={false}
/> />
<Or /> {!hasActivePosition && (
<Text size='sm'>Position Leverage</Text> <>
<LeverageSlider <Or />
min={1} <Text size='sm'>Position Leverage</Text>
max={10} <LeverageSlider
value={leverage} min={1}
onChange={setLeverage} max={10}
type={tradeDirection} value={leverage}
/> onChange={setLeverage}
<LeverageButtons /> type={tradeDirection}
/>
<LeverageButtons />
</>
)}
<Spacer /> <Spacer />
<PerpsSummary <PerpsSummary
amount={amount} amount={amount ?? previousAmount}
tradeDirection={tradeDirection} tradeDirection={tradeDirection ?? previousTradeDirection}
asset={perpsAsset} asset={perpsAsset}
leverage={leverage} leverage={leverage}
previousAmount={previousAmount}
previousTradeDirection={previousTradeDirection}
previousLeverage={previousLeverage}
hasActivePosition={hasActivePosition}
/> />
</Card> </Card>
) )

View File

@ -18,10 +18,10 @@ type Props = {
amount: BigNumber amount: BigNumber
tradeDirection: TradeDirection tradeDirection: TradeDirection
asset: Asset asset: Asset
changeTradeDirection?: boolean
previousAmount?: BigNumber previousAmount?: BigNumber
previousTradeDirection?: 'long' | 'short' previousTradeDirection?: 'long' | 'short'
previousLeverage?: number previousLeverage?: number
hasActivePosition: boolean
} }
export default function PerpsSummary(props: Props) { export default function PerpsSummary(props: Props) {
@ -70,14 +70,13 @@ export default function PerpsSummary(props: Props) {
function ManageSummary(props: Props) { function ManageSummary(props: Props) {
const showTradeDirection = const showTradeDirection =
props.previousTradeDirection && props.previousTradeDirection !== props.tradeDirection props.previousTradeDirection && props.previousTradeDirection !== props.tradeDirection
const showAmount = const showAmount = !props.amount.isZero() && props.previousAmount
props.previousAmount && props.amount && !props.previousAmount.isEqualTo(props.amount)
const showLeverage = const showLeverage =
props.previousLeverage && props.previousLeverage &&
props.leverage && props.leverage &&
props.previousLeverage.toFixed(2) !== props.leverage.toFixed(2) props.previousLeverage.toFixed(2) !== props.leverage.toFixed(2)
if (!showTradeDirection && !showLeverage && !showAmount) return null if ((!showTradeDirection && !showLeverage && !showAmount) || !props.hasActivePosition) return null
return ( return (
<div className='pt-4 px-3 flex flex-col gap-1'> <div className='pt-4 px-3 flex flex-col gap-1'>
@ -95,14 +94,17 @@ function ManageSummary(props: Props) {
{showAmount && props.previousAmount && ( {showAmount && props.previousAmount && (
<SummaryLine label='Size' contentClassName='flex gap-1'> <SummaryLine label='Size' contentClassName='flex gap-1'>
<AssetAmount asset={props.asset} amount={props.previousAmount.toNumber()} /> <AssetAmount asset={props.asset} amount={props.previousAmount.abs().toNumber()} />
<ArrowRight <ArrowRight
width={16} width={16}
className={classNames( className={classNames(
props.previousAmount.isGreaterThan(props.amount) ? 'text-error' : 'text-success', props.previousAmount.isGreaterThan(props.amount) ? 'text-error' : 'text-success',
)} )}
/> />
<AssetAmount asset={props.asset} amount={props.amount.toNumber()} /> <AssetAmount
asset={props.asset}
amount={props.previousAmount.plus(props.amount).abs().toNumber()}
/>
</SummaryLine> </SummaryLine>
)} )}

View File

@ -1,5 +1,4 @@
import { useCallback, useMemo } from 'react' import { useMemo } from 'react'
import { useSearchParams } from 'react-router-dom'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount' import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
@ -8,34 +7,33 @@ import usePerpPosition from 'hooks/perps/usePerpPosition'
import usePerpsAsset from 'hooks/perps/usePerpsAsset' import usePerpsAsset from 'hooks/perps/usePerpsAsset'
import usePrice from 'hooks/usePrice' import usePrice from 'hooks/usePrice'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import { SearchParams } from 'types/enums/searchParams'
import { getAccountNetValue } from 'utils/accounts' import { getAccountNetValue } from 'utils/accounts'
import { byDenom } from 'utils/array'
import { demagnify } from 'utils/formatters' import { demagnify } from 'utils/formatters'
import { getSearchParamsObject } from 'utils/route'
export default function usePerpsManageModule(amount: BigNumber | null) { export default function usePerpsModule(amount: BigNumber | null) {
const { perpsAsset } = usePerpsAsset() const { perpsAsset } = usePerpsAsset()
const [searchParams, setSearchParams] = useSearchParams()
const perpPosition = usePerpPosition(perpsAsset.denom) const perpPosition = usePerpPosition(perpsAsset.denom)
const { data: prices } = usePrices() const { data: prices } = usePrices()
const assets = useAllAssets() const assets = useAllAssets()
const account = useCurrentAccount() const account = useCurrentAccount()
const price = usePrice(perpsAsset.denom) const price = usePrice(perpsAsset.denom)
const hasActivePosition = useMemo(
() => !!account?.perps.find(byDenom(perpsAsset.denom)),
[account?.perps, perpsAsset.denom],
)
const accountNetValue = useMemo(() => { const accountNetValue = useMemo(() => {
if (!account || !prices || !assets) return BN_ZERO if (!account || !prices || !assets) return BN_ZERO
return getAccountNetValue(account, prices, assets) return getAccountNetValue(account, prices, assets)
}, [account, assets, prices]) }, [account, assets, prices])
const closeManagePerpModule = useCallback(() => { const previousAmount = useMemo(
const params = getSearchParamsObject(searchParams) () =>
delete params[SearchParams.PERPS_MANAGE] (perpPosition?.amount ?? BN_ZERO).times(perpPosition?.tradeDirection === 'short' ? -1 : 1),
setSearchParams({ [perpPosition?.amount, perpPosition?.tradeDirection],
...params, )
})
}, [searchParams, setSearchParams])
const previousAmount = useMemo(() => perpPosition?.amount ?? BN_ZERO, [perpPosition?.amount])
const previousTradeDirection = useMemo( const previousTradeDirection = useMemo(
() => perpPosition?.tradeDirection || 'long', () => perpPosition?.tradeDirection || 'long',
[perpPosition?.tradeDirection], [perpPosition?.tradeDirection],
@ -43,26 +41,29 @@ export default function usePerpsManageModule(amount: BigNumber | null) {
const previousLeverage = useMemo( const previousLeverage = useMemo(
() => () =>
price.times(demagnify(previousAmount, perpsAsset)).div(accountNetValue).plus(1).toNumber(), price
.times(demagnify(previousAmount.abs(), perpsAsset))
.div(accountNetValue)
.plus(1)
.toNumber(),
[accountNetValue, perpsAsset, previousAmount, price], [accountNetValue, perpsAsset, previousAmount, price],
) )
const leverage = useMemo( const leverage = useMemo(
() => () =>
price price
.times(demagnify(amount ?? BN_ZERO, perpsAsset)) .times(demagnify(previousAmount.plus(amount ?? BN_ZERO).abs(), perpsAsset))
.div(accountNetValue) .div(accountNetValue)
.plus(1) .plus(1)
.toNumber(), .toNumber(),
[accountNetValue, amount, perpsAsset, price], [accountNetValue, amount, perpsAsset, previousAmount, price],
) )
return { return {
closeManagePerpModule,
previousAmount, previousAmount,
previousTradeDirection, previousTradeDirection,
previousLeverage, previousLeverage,
leverage, leverage,
asset: perpsAsset, hasActivePosition,
} }
} }

View File

@ -4,25 +4,32 @@ import Button from 'components/common/Button'
import { ChevronDown } from 'components/common/Icons' import { ChevronDown } from 'components/common/Icons'
import Text from 'components/common/Text' import Text from 'components/common/Text'
import AssetOverlay from 'components/trade/TradeModule/AssetSelector/AssetOverlay' import AssetOverlay from 'components/trade/TradeModule/AssetSelector/AssetOverlay'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import usePerpsEnabledAssets from 'hooks/assets/usePerpsEnabledAssets' import usePerpsEnabledAssets from 'hooks/assets/usePerpsEnabledAssets'
import usePerpsAsset from 'hooks/perps/usePerpsAsset' import usePerpsAsset from 'hooks/perps/usePerpsAsset'
import useStore from 'store' import useStore from 'store'
interface Props { interface Props {
asset: Asset asset: Asset
hasActivePosition: boolean
} }
export default function AssetSelectorPerps(props: Props) { export default function AssetSelectorPerps(props: Props) {
const assetOverlayState = useStore((s) => s.assetOverlayState) const assetOverlayState = useStore((s) => s.assetOverlayState)
const { perpsAsset, updatePerpsAsset } = usePerpsAsset() const { perpsAsset, updatePerpsAsset } = usePerpsAsset()
const currentAccount = useCurrentAccount()
const perpAssets = usePerpsEnabledAssets() const perpAssets = usePerpsEnabledAssets()
const onChangePerpsAsset = useCallback( const onChangePerpsAsset = useCallback(
(asset: Asset) => { (asset: Asset) => {
updatePerpsAsset(asset.denom) let hasPosition = false
if (currentAccount && currentAccount.perps.find((perp) => perp.denom === asset.denom)) {
hasPosition = true
}
updatePerpsAsset(asset.denom, hasPosition)
}, },
[updatePerpsAsset], [currentAccount, updatePerpsAsset],
) )
const handleChangeState = useCallback(() => { const handleChangeState = useCallback(() => {
@ -37,9 +44,15 @@ export default function AssetSelectorPerps(props: Props) {
onClick={() => useStore.setState({ assetOverlayState: 'pair' })} onClick={() => useStore.setState({ assetOverlayState: 'pair' })}
className='flex items-center justify-between w-full py-5 bg-white/5' className='flex items-center justify-between w-full py-5 bg-white/5'
> >
<Text size='sm' className='text-white/60'> <div className='flex gap-2 items-center'>
<span className='text-white'>{perpsAsset.symbol}</span>/USD <Text size='sm' className='text-white/60'>
</Text> <span className='text-white'>{perpsAsset.symbol}</span>/USD
</Text>
{props.hasActivePosition && (
<div className='px-1.5 py-0.5 bg-white/20 rounded-sm text-white text-xs'>Active</div>
)}
</div>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<Text>All markets</Text> <Text>All markets</Text>
<ChevronDown className='w-3 h-3' /> <ChevronDown className='w-3 h-3' />

View File

@ -6,6 +6,7 @@ import { LocalStorageKeys } from 'constants/localStorageKeys'
import usePerpsEnabledAssets from 'hooks/assets/usePerpsEnabledAssets' import usePerpsEnabledAssets from 'hooks/assets/usePerpsEnabledAssets'
import useLocalStorage from 'hooks/localStorage/useLocalStorage' import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useChainConfig from 'hooks/useChainConfig' import useChainConfig from 'hooks/useChainConfig'
import { SearchParams } from 'types/enums/searchParams'
import { getSearchParamsObject } from 'utils/route' import { getSearchParamsObject } from 'utils/route'
export default function usePerpsAsset() { export default function usePerpsAsset() {
@ -18,10 +19,12 @@ export default function usePerpsAsset() {
>(chainConfig.id + '/' + LocalStorageKeys.PERPS_ASSET, DEFAULT_SETTINGS.perpsAsset) >(chainConfig.id + '/' + LocalStorageKeys.PERPS_ASSET, DEFAULT_SETTINGS.perpsAsset)
const updatePerpsAsset = useCallback( const updatePerpsAsset = useCallback(
(denom: string) => { (denom: string, manage?: boolean) => {
const params = getSearchParamsObject(searchParams) const params = getSearchParamsObject(searchParams)
params.perpsMarket = denom setSearchParams({
setSearchParams(params) ...params,
[SearchParams.PERPS_MARKET]: denom,
})
setPerpsAssetInLocalStorage(denom) setPerpsAssetInLocalStorage(denom)
}, },
[searchParams, setPerpsAssetInLocalStorage, setSearchParams], [searchParams, setPerpsAssetInLocalStorage, setSearchParams],

View File

@ -15,8 +15,8 @@ export default function usePerpsMarket() {
clients && perpsAsset && `chains/${chainConfig.id}/perps/${perpsAsset.denom}`, clients && perpsAsset && `chains/${chainConfig.id}/perps/${perpsAsset.denom}`,
() => getPerpsMarket(clients!, perpsAsset!), () => getPerpsMarket(clients!, perpsAsset!),
{ {
refreshInterval: 1000, refreshInterval: 5000,
dedupingInterval: 1000, dedupingInterval: 5000,
}, },
) )
} }

View File

@ -1,22 +1,14 @@
import { useSearchParams } from 'react-router-dom'
import { PerpsManageModule } from 'components/perps/Module/PerpsManageModule'
import { PerpsModule } from 'components/perps/Module/PerpsModule' import { PerpsModule } from 'components/perps/Module/PerpsModule'
import { PerpsChart } from 'components/perps/PerpsChart' import { PerpsChart } from 'components/perps/PerpsChart'
import { PerpsInfo } from 'components/perps/PerpsInfo' import { PerpsInfo } from 'components/perps/PerpsInfo'
import { PerpsPositions } from 'components/perps/PerpsPositions' import { PerpsPositions } from 'components/perps/PerpsPositions'
import { SearchParams } from 'types/enums/searchParams'
export default function PerpsPage() { export default function PerpsPage() {
const [searchParams] = useSearchParams()
const isManagingPosition = searchParams.get(SearchParams.PERPS_MANAGE) === 'true'
return ( return (
<div className='grid grid-cols-[auto_376px] grid-rows-[min-content_auto_auto] w-full gap-4'> <div className='grid grid-cols-[auto_376px] grid-rows-[min-content_auto_auto] w-full gap-4'>
<PerpsInfo /> <PerpsInfo />
<div className='h-full w-[376px] row-span-3 relative'> <div className='h-full w-[376px] row-span-3 relative'>
{isManagingPosition ? <PerpsManageModule /> : <PerpsModule />} <PerpsModule />
</div> </div>
<PerpsChart /> <PerpsChart />
<PerpsPositions /> <PerpsPositions />

View File

@ -1,5 +1,4 @@
export enum SearchParams { export enum SearchParams {
ACCOUNT_ID = 'accountId', ACCOUNT_ID = 'accountId',
PERPS_MARKET = 'perpsMarket', PERPS_MARKET = 'perpsMarket',
PERPS_MANAGE = 'perpsManage',
} }