Extend perps (#721)
This commit is contained in:
parent
14d09409f9
commit
bfd03d66a4
@ -1,5 +1,6 @@
|
|||||||
import { cacheFn, oraclePriceCache } from 'api/cache'
|
import { cacheFn, oraclePriceCache } from 'api/cache'
|
||||||
import { getOracleQueryClient } from 'api/cosmwasm-client'
|
import { getOracleQueryClient } from 'api/cosmwasm-client'
|
||||||
|
import { BN_ZERO } from 'constants/math'
|
||||||
import { PRICE_ORACLE_DECIMALS } from 'constants/query'
|
import { PRICE_ORACLE_DECIMALS } from 'constants/query'
|
||||||
import { BNCoin } from 'types/classes/BNCoin'
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
import { PriceResponse } from 'types/generated/mars-oracle-osmosis/MarsOracleOsmosis.types'
|
import { PriceResponse } from 'types/generated/mars-oracle-osmosis/MarsOracleOsmosis.types'
|
||||||
@ -27,7 +28,7 @@ export default async function getOraclePrices(
|
|||||||
const decimalDiff = asset.decimals - PRICE_ORACLE_DECIMALS
|
const decimalDiff = asset.decimals - PRICE_ORACLE_DECIMALS
|
||||||
return BNCoin.fromDenomAndBigNumber(
|
return BNCoin.fromDenomAndBigNumber(
|
||||||
asset.denom,
|
asset.denom,
|
||||||
BN(priceResponse.price).shiftedBy(decimalDiff),
|
BN(priceResponse?.price ?? BN_ZERO).shiftedBy(decimalDiff),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
@ -18,6 +18,7 @@ interface Props {
|
|||||||
parentheses?: boolean
|
parentheses?: boolean
|
||||||
showZero?: boolean
|
showZero?: boolean
|
||||||
options?: FormatOptions
|
options?: FormatOptions
|
||||||
|
isProfitOrLoss?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DisplayCurrency(props: Props) {
|
export default function DisplayCurrency(props: Props) {
|
||||||
@ -34,10 +35,10 @@ export default function DisplayCurrency(props: Props) {
|
|||||||
|
|
||||||
const isUSD = displayCurrencyAsset.id === 'USD'
|
const isUSD = displayCurrencyAsset.id === 'USD'
|
||||||
|
|
||||||
const amount = useMemo(() => {
|
const [amount, absoluteAmount] = useMemo(() => {
|
||||||
const coinValue = getCoinValue(props.coin, prices, assets)
|
const coinValue = getCoinValue(props.coin, prices, assets)
|
||||||
|
|
||||||
if (displayCurrency === ORACLE_DENOM) return coinValue.toNumber()
|
if (displayCurrency === ORACLE_DENOM) return [coinValue.toNumber(), coinValue.abs().toNumber()]
|
||||||
|
|
||||||
const displayDecimals = displayCurrencyAsset.decimals
|
const displayDecimals = displayCurrencyAsset.decimals
|
||||||
const displayPrice = getCoinValue(
|
const displayPrice = getCoinValue(
|
||||||
@ -46,10 +47,14 @@ export default function DisplayCurrency(props: Props) {
|
|||||||
assets,
|
assets,
|
||||||
)
|
)
|
||||||
|
|
||||||
return coinValue.div(displayPrice).toNumber()
|
const amount = coinValue.div(displayPrice).toNumber()
|
||||||
|
|
||||||
|
return [amount, Math.abs(amount)]
|
||||||
}, [assets, displayCurrency, displayCurrencyAsset.decimals, prices, props.coin])
|
}, [assets, displayCurrency, displayCurrencyAsset.decimals, prices, props.coin])
|
||||||
|
|
||||||
const isLessThanACent = (isUSD && amount < 0.01 && amount > 0) || (amount === 0 && props.showZero)
|
const isLessThanACent =
|
||||||
|
(isUSD && absoluteAmount < 0.01 && absoluteAmount > 0) ||
|
||||||
|
(absoluteAmount === 0 && props.showZero)
|
||||||
const smallerThanPrefix = isLessThanACent ? '< ' : ''
|
const smallerThanPrefix = isLessThanACent ? '< ' : ''
|
||||||
|
|
||||||
const prefix = isUSD
|
const prefix = isUSD
|
||||||
@ -64,8 +69,10 @@ export default function DisplayCurrency(props: Props) {
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
props.className,
|
props.className,
|
||||||
props.parentheses && 'before:content-["("] after:content-[")"]',
|
props.parentheses && 'before:content-["("] after:content-[")"]',
|
||||||
|
props.isProfitOrLoss && (amount < 0 ? 'text-error' : amount === 0 ? '' : 'text-success'),
|
||||||
|
props.isProfitOrLoss && amount < 0 && 'before:content-["-"]',
|
||||||
)}
|
)}
|
||||||
amount={isLessThanACent ? 0.01 : amount}
|
amount={isLessThanACent ? 0.01 : absoluteAmount}
|
||||||
options={{
|
options={{
|
||||||
minDecimals: isUSD ? 2 : 0,
|
minDecimals: isUSD ? 2 : 0,
|
||||||
maxDecimals: 2,
|
maxDecimals: 2,
|
||||||
|
@ -35,7 +35,6 @@ export default function ChainSelect() {
|
|||||||
client: undefined,
|
client: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
userDomain: undefined,
|
userDomain: undefined,
|
||||||
accounts: null,
|
|
||||||
balances: [],
|
balances: [],
|
||||||
})
|
})
|
||||||
navigate(getRoute(getPage(pathname), searchParams))
|
navigate(getRoute(getPage(pathname), searchParams))
|
||||||
|
31
src/components/Perps/BalancesTable/Columns/Leverage.tsx
Normal file
31
src/components/Perps/BalancesTable/Columns/Leverage.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||||
|
|
||||||
|
export const LEVERAGE_META = {
|
||||||
|
accessorKey: 'leverage',
|
||||||
|
header: () => (
|
||||||
|
<div className='flex flex-col gap-1'>
|
||||||
|
<Text size='xs'>Liquidation Price</Text>
|
||||||
|
<Text size='xs' className='text-white/40'>
|
||||||
|
Leverage
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
liquidationPrice: BigNumber
|
||||||
|
leverage: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Leverage(props: Props) {
|
||||||
|
return (
|
||||||
|
<TitleAndSubCell
|
||||||
|
title={
|
||||||
|
<FormattedNumber amount={props.liquidationPrice.toNumber()} options={{ prefix: '$' }} />
|
||||||
|
}
|
||||||
|
sub={<FormattedNumber amount={props.leverage} options={{ suffix: 'x' }} />}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
@ -1,24 +0,0 @@
|
|||||||
import classNames from 'classnames'
|
|
||||||
|
|
||||||
import Text from 'components/Text'
|
|
||||||
|
|
||||||
export const PERP_TYPE_META = { accessorKey: 'type', header: 'Side' }
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
type: PerpsType
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PerpType(props: Props) {
|
|
||||||
return (
|
|
||||||
<Text
|
|
||||||
size='xs'
|
|
||||||
className={classNames(
|
|
||||||
'capitalize px-1 py-0.5 rounded-sm inline',
|
|
||||||
props.type === 'short' && 'text-error bg-error/20',
|
|
||||||
props.type === 'long' && 'text-success bg-success/20',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{props.type}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
import classNames from 'classnames'
|
|
||||||
|
|
||||||
import DisplayCurrency from 'components/DisplayCurrency'
|
import DisplayCurrency from 'components/DisplayCurrency'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
import { Tooltip } from 'components/Tooltip'
|
||||||
|
import { BN_ZERO } from 'constants/math'
|
||||||
import { BNCoin } from 'types/classes/BNCoin'
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
export const PNL_META = { accessorKey: 'pnl', header: 'Total PnL', id: 'pnl' }
|
export const PNL_META = { accessorKey: 'pnl', header: 'Total PnL', id: 'pnl' }
|
||||||
@ -10,19 +11,38 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function PnL(props: Props) {
|
export default function PnL(props: Props) {
|
||||||
const isNegative = props.pnl.amount.isNegative()
|
|
||||||
return (
|
return (
|
||||||
<span
|
<Tooltip
|
||||||
className={classNames(
|
content={
|
||||||
'text-xs',
|
<PnLTooltip
|
||||||
isNegative ? 'text-error' : props.pnl.amount.isZero() ? '' : 'text-success',
|
realized={BNCoin.fromDenomAndBigNumber('uusd', BN_ZERO)}
|
||||||
)}
|
unrealized={props.pnl}
|
||||||
>
|
|
||||||
{isNegative ? '-' : props.pnl.amount.isZero() ? '' : '+'}
|
|
||||||
<DisplayCurrency
|
|
||||||
className='inline'
|
|
||||||
coin={BNCoin.fromDenomAndBigNumber(props.pnl.denom, props.pnl.amount.abs())}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
}
|
||||||
|
type='info'
|
||||||
|
underline
|
||||||
|
>
|
||||||
|
<DisplayCurrency className='inline text-xs' coin={props.pnl} isProfitOrLoss />
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PnLTooltipProps = {
|
||||||
|
realized: BNCoin
|
||||||
|
unrealized: BNCoin
|
||||||
|
}
|
||||||
|
|
||||||
|
function PnLTooltip(props: PnLTooltipProps) {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-2 w-full'>
|
||||||
|
{[props.realized, props.unrealized].map((coin, i) => (
|
||||||
|
<div key={i} className='flex w-full text-white/60 space-between items-center gap-8'>
|
||||||
|
<Text className='mr-auto' size='sm'>
|
||||||
|
{i === 0 ? 'Realized' : 'Unrealized'} PnL
|
||||||
|
</Text>
|
||||||
|
<DisplayCurrency coin={coin} className='self-end text-end' isProfitOrLoss />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
import Text from 'components/Text'
|
||||||
|
|
||||||
|
export const PERP_TYPE_META = { accessorKey: 'tradeDirection', header: 'Side' }
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
tradeDirection: TradeDirection
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TradeDirection(props: Props) {
|
||||||
|
const { tradeDirection } = props
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
size='xs'
|
||||||
|
className={classNames(
|
||||||
|
'capitalize px-1 py-0.5 rounded-sm inline',
|
||||||
|
tradeDirection === 'short' && 'text-error bg-error/20',
|
||||||
|
tradeDirection === 'long' && 'text-success bg-success/20',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{tradeDirection}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
@ -2,11 +2,14 @@ import { ColumnDef } from '@tanstack/react-table'
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import EntryPrice, { ENTRY_PRICE_META } from 'components/Perps/BalancesTable/Columns/EntryPrice'
|
import EntryPrice, { ENTRY_PRICE_META } from 'components/Perps/BalancesTable/Columns/EntryPrice'
|
||||||
|
import Leverage, { LEVERAGE_META } from 'components/Perps/BalancesTable/Columns/Leverage'
|
||||||
import Manage, { MANAGE_META } from 'components/Perps/BalancesTable/Columns/Manage'
|
import Manage, { MANAGE_META } from 'components/Perps/BalancesTable/Columns/Manage'
|
||||||
import { PERP_NAME_META, PerpName } from 'components/Perps/BalancesTable/Columns/PerpName'
|
import { PERP_NAME_META, PerpName } from 'components/Perps/BalancesTable/Columns/PerpName'
|
||||||
import PerpType, { PERP_TYPE_META } from 'components/Perps/BalancesTable/Columns/PerpType'
|
|
||||||
import PnL, { PNL_META } from 'components/Perps/BalancesTable/Columns/PnL'
|
import PnL, { PNL_META } from 'components/Perps/BalancesTable/Columns/PnL'
|
||||||
import Size, { SIZE_META } from 'components/Perps/BalancesTable/Columns/Size'
|
import Size, { SIZE_META } from 'components/Perps/BalancesTable/Columns/Size'
|
||||||
|
import TradeDirection, {
|
||||||
|
PERP_TYPE_META,
|
||||||
|
} from 'components/Perps/BalancesTable/Columns/TradeDirection'
|
||||||
import { PerpPositionRow } from 'components/Perps/BalancesTable/usePerpsBalancesData'
|
import { PerpPositionRow } from 'components/Perps/BalancesTable/usePerpsBalancesData'
|
||||||
|
|
||||||
export default function usePerpsBalancesTable() {
|
export default function usePerpsBalancesTable() {
|
||||||
@ -18,12 +21,21 @@ export default function usePerpsBalancesTable() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
...PERP_TYPE_META,
|
...PERP_TYPE_META,
|
||||||
cell: ({ row }) => <PerpType type={row.original.type} />,
|
cell: ({ row }) => <TradeDirection tradeDirection={row.original.tradeDirection} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...SIZE_META,
|
...SIZE_META,
|
||||||
cell: ({ row }) => <Size size={row.original.size} asset={row.original.asset} />,
|
cell: ({ row }) => <Size size={row.original.size} asset={row.original.asset} />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
...LEVERAGE_META,
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Leverage
|
||||||
|
liquidationPrice={row.original.liquidationPrice}
|
||||||
|
leverage={row.original.leverage}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
...ENTRY_PRICE_META,
|
...ENTRY_PRICE_META,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
|
@ -1,34 +1,48 @@
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
|
import { BN_ZERO } from 'constants/math'
|
||||||
|
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||||
import usePerpsEnabledAssets from 'hooks/assets/usePerpsEnabledAssets'
|
import usePerpsEnabledAssets from 'hooks/assets/usePerpsEnabledAssets'
|
||||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||||
|
import usePrices from 'hooks/usePrices'
|
||||||
import { BNCoin } from 'types/classes/BNCoin'
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
import { getAccountNetValue } from 'utils/accounts'
|
||||||
import { byDenom } from 'utils/array'
|
import { byDenom } from 'utils/array'
|
||||||
|
import { demagnify } from 'utils/formatters'
|
||||||
|
|
||||||
export default function usePerpsBalancesTable() {
|
export default function usePerpsBalancesTable() {
|
||||||
const currentAccount = useCurrentAccount()
|
const currentAccount = useCurrentAccount()
|
||||||
const perpAssets = usePerpsEnabledAssets()
|
const perpAssets = usePerpsEnabledAssets()
|
||||||
|
const allAssets = useAllAssets()
|
||||||
|
const { data: prices } = usePrices()
|
||||||
|
|
||||||
return useMemo<PerpPositionRow[]>(() => {
|
return useMemo<PerpPositionRow[]>(() => {
|
||||||
if (!currentAccount) return []
|
if (!currentAccount) return []
|
||||||
|
|
||||||
|
const netValue = getAccountNetValue(currentAccount, prices, allAssets)
|
||||||
|
|
||||||
return currentAccount.perps.map((position) => {
|
return currentAccount.perps.map((position) => {
|
||||||
const asset = perpAssets.find(byDenom(position.denom))
|
const price = prices.find(byDenom(position.denom))?.amount ?? BN_ZERO
|
||||||
|
const asset = perpAssets.find(byDenom(position.denom))!
|
||||||
return {
|
return {
|
||||||
asset,
|
asset,
|
||||||
type: position.type,
|
tradeDirection: position.tradeDirection,
|
||||||
size: position.size,
|
size: position.size,
|
||||||
pnl: position.pnl,
|
pnl: position.pnl,
|
||||||
entryPrice: position.entryPrice,
|
entryPrice: position.entryPrice,
|
||||||
|
liquidationPrice: position.entryPrice, // TODO: 📈 Get actual liquidation price from HC
|
||||||
|
leverage: price.times(demagnify(position.size, asset)).div(netValue).plus(1).toNumber(),
|
||||||
} as PerpPositionRow
|
} as PerpPositionRow
|
||||||
})
|
})
|
||||||
}, [currentAccount, perpAssets])
|
}, [allAssets, currentAccount, perpAssets, prices])
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PerpPositionRow = {
|
export type PerpPositionRow = {
|
||||||
asset: Asset
|
asset: Asset
|
||||||
type: 'long' | 'short'
|
tradeDirection: TradeDirection
|
||||||
size: BigNumber
|
size: BigNumber
|
||||||
pnl: BNCoin
|
pnl: BNCoin
|
||||||
entryPrice: BigNumber
|
entryPrice: BigNumber
|
||||||
|
liquidationPrice: BigNumber
|
||||||
|
leverage: number
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import Button from 'components/Button'
|
|
||||||
|
|
||||||
const LEVERAGE_PRESETS = [1, 2, 3, 5, 10]
|
const LEVERAGE_PRESETS = [1, 2, 3, 5, 10]
|
||||||
|
|
||||||
export function LeverageButtons() {
|
export function LeverageButtons() {
|
||||||
return (
|
return (
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
{LEVERAGE_PRESETS.map((leverage) => (
|
{LEVERAGE_PRESETS.map((leverage) => (
|
||||||
<Button key={leverage} color='tertiary' className='w-12'>
|
<button
|
||||||
|
key={leverage}
|
||||||
|
className='w-12 !border:none bg-white/10 rounded-sm py-1 text-xs hover:bg-white/20'
|
||||||
|
>
|
||||||
{leverage}x
|
{leverage}x
|
||||||
</Button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { useCallback, useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
import Button from 'components/Button'
|
|
||||||
import Card from 'components/Card'
|
import Card from 'components/Card'
|
||||||
import { DirectionSelect } from 'components/DirectionSelect'
|
|
||||||
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 RangeInput from 'components/RangeInput'
|
import RangeInput from 'components/RangeInput'
|
||||||
import { Spacer } from 'components/Spacer'
|
import { Spacer } from 'components/Spacer'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
@ -12,50 +11,35 @@ import AssetSelectorPerps from 'components/Trade/TradeModule/AssetSelector/Asset
|
|||||||
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'
|
||||||
import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types'
|
import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types'
|
||||||
|
import { TradeDirectionSelector } from 'components/TradeDirectionSelector'
|
||||||
import { BN_ZERO } from 'constants/math'
|
import { BN_ZERO } from 'constants/math'
|
||||||
import useBaseAsset from 'hooks/assets/useBasetAsset'
|
|
||||||
import usePerpsAsset from 'hooks/perps/usePerpsAsset'
|
import usePerpsAsset from 'hooks/perps/usePerpsAsset'
|
||||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
|
||||||
import useStore from 'store'
|
|
||||||
import { BNCoin } from 'types/classes/BNCoin'
|
|
||||||
import { BN } from 'utils/helpers'
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
export function PerpsModule() {
|
export function PerpsModule() {
|
||||||
const [selectedOrderType, setSelectedOrderType] = useState<AvailableOrderType>('Market')
|
const [selectedOrderType, setSelectedOrderType] = useState<AvailableOrderType>('Market')
|
||||||
const [selectedOrderDirection, setSelectedOrderDirection] = useState<OrderDirection>('long')
|
const [tradeDirection, setTradeDirection] = useState<TradeDirection>('long')
|
||||||
const baseAsset = useBaseAsset()
|
|
||||||
const { perpsAsset } = usePerpsAsset()
|
const { perpsAsset } = usePerpsAsset()
|
||||||
const openPerpPosition = useStore((s) => s.openPerpPosition)
|
|
||||||
const currentAccount = useCurrentAccount()
|
|
||||||
|
|
||||||
const onConfirm = useCallback(async () => {
|
const [amount, setAmount] = useState<BigNumber>(BN_ZERO)
|
||||||
if (!currentAccount) return
|
|
||||||
await openPerpPosition({
|
|
||||||
accountId: currentAccount.id,
|
|
||||||
coin: BNCoin.fromDenomAndBigNumber(perpsAsset.denom, BN(1000)),
|
|
||||||
})
|
|
||||||
}, [currentAccount, openPerpPosition, perpsAsset.denom])
|
|
||||||
|
|
||||||
if (!perpsAsset) return null
|
if (!perpsAsset) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
contentClassName='px-4 gap-5 flex flex-col'
|
contentClassName='px-4 gap-5 flex flex-col h-full pb-4'
|
||||||
title={<AssetSelectorPerps asset={perpsAsset} />}
|
title={<AssetSelectorPerps asset={perpsAsset} />}
|
||||||
className='mb-4'
|
className='mb-4 h-full'
|
||||||
>
|
>
|
||||||
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
|
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
|
||||||
|
|
||||||
<DirectionSelect
|
<TradeDirectionSelector direction={tradeDirection} onChangeDirection={setTradeDirection} />
|
||||||
direction={selectedOrderDirection}
|
|
||||||
onChangeDirection={setSelectedOrderDirection}
|
|
||||||
/>
|
|
||||||
<AssetAmountInput
|
<AssetAmountInput
|
||||||
label='Amount'
|
label='Amount'
|
||||||
max={BN_ZERO}
|
max={BN(1000)} // TODO: Implement max calculation
|
||||||
amount={BN_ZERO}
|
amount={amount}
|
||||||
setAmount={() => {}}
|
setAmount={setAmount}
|
||||||
asset={baseAsset}
|
asset={perpsAsset}
|
||||||
maxButtonLabel='Max:'
|
maxButtonLabel='Max:'
|
||||||
disabled={false}
|
disabled={false}
|
||||||
/>
|
/>
|
||||||
@ -64,7 +48,7 @@ export function PerpsModule() {
|
|||||||
<RangeInput max={0} value={0} onChange={() => {}} />
|
<RangeInput max={0} value={0} onChange={() => {}} />
|
||||||
<LeverageButtons />
|
<LeverageButtons />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<Button onClick={onConfirm}>{selectedOrderDirection} ETH</Button>
|
<PerpsSummary amount={amount} tradeDirection={tradeDirection} asset={perpsAsset} />
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
47
src/components/Perps/Module/Summary.tsx
Normal file
47
src/components/Perps/Module/Summary.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
|
import ActionButton from 'components/Button/ActionButton'
|
||||||
|
import SummaryLine from 'components/SummaryLine'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||||
|
import useStore from 'store'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
amount: BigNumber
|
||||||
|
tradeDirection: TradeDirection
|
||||||
|
asset: Asset
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PerpsSummary(props: Props) {
|
||||||
|
const openPerpPosition = useStore((s) => s.openPerpPosition)
|
||||||
|
const currentAccount = useCurrentAccount()
|
||||||
|
|
||||||
|
const onConfirm = useCallback(async () => {
|
||||||
|
if (!currentAccount) return
|
||||||
|
await openPerpPosition({
|
||||||
|
accountId: currentAccount.id,
|
||||||
|
coin: BNCoin.fromDenomAndBigNumber(
|
||||||
|
props.asset.denom,
|
||||||
|
props.amount.times(props.tradeDirection === 'short' ? -1 : 1),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}, [currentAccount, openPerpPosition, props.amount, props.asset.denom, props.tradeDirection])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='border border-white/10 rounded-sm bg-white/5'>
|
||||||
|
<div className='py-4 px-3 flex flex-col gap-1'>
|
||||||
|
<Text size='xs' className='font-bold mb-2'>
|
||||||
|
Summary
|
||||||
|
</Text>
|
||||||
|
<SummaryLine label='Expected Price'>Something</SummaryLine>
|
||||||
|
<SummaryLine label='Fees'>Something</SummaryLine>
|
||||||
|
<SummaryLine label='Total'>Something</SummaryLine>
|
||||||
|
</div>
|
||||||
|
<ActionButton onClick={onConfirm} className='w-full py-2.5'>
|
||||||
|
<span className='capitalize mr-1'>{props.tradeDirection}</span>
|
||||||
|
{props.asset.symbol}
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
18
src/components/SummaryLine.tsx
Normal file
18
src/components/SummaryLine.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import classNames from 'classnames'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const infoLineClasses = 'flex flex-row justify-between flex-1 mb-1 text-xs text-white'
|
||||||
|
|
||||||
|
interface SummaryLineProps {
|
||||||
|
children: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
export default function SummaryLine(props: SummaryLineProps) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(infoLineClasses, props.className)}>
|
||||||
|
<span className='opacity-40'>{props.label}</span>
|
||||||
|
<span>{props.children}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -16,7 +16,7 @@ export default function TooltipContent(props: Props) {
|
|||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'flex max-w-[320px] flex-1 gap-2 rounded-sm py-1 px-2 text-sm shadow-tooltip backdrop-blur-lg',
|
'flex max-w-[320px] flex-1 gap-2 rounded-sm p-3 text-sm shadow-tooltip backdrop-blur-lg',
|
||||||
props.type === 'info' && 'bg-white/20',
|
props.type === 'info' && 'bg-white/20',
|
||||||
props.type === 'warning' && 'bg-warning',
|
props.type === 'warning' && 'bg-warning',
|
||||||
props.type === 'error' && 'bg-error',
|
props.type === 'error' && 'bg-error',
|
||||||
|
@ -51,7 +51,7 @@ export const Tooltip = (props: Props) => {
|
|||||||
<span
|
<span
|
||||||
className={classNames(
|
className={classNames(
|
||||||
props.underline &&
|
props.underline &&
|
||||||
'border-b-1 hover:cursor-pointer border border-x-0 border-t-0 border-dashed border-white/50 hover:border-transparent',
|
'border-b-1 hover:cursor-pointer border border-x-0 border-t-0 border-dashed border-white/20 pb-1',
|
||||||
!reduceMotion && 'transition-all',
|
!reduceMotion && 'transition-all',
|
||||||
props.className,
|
props.className,
|
||||||
)}
|
)}
|
||||||
|
@ -7,6 +7,7 @@ import DisplayCurrency from 'components/DisplayCurrency'
|
|||||||
import Divider from 'components/Divider'
|
import Divider from 'components/Divider'
|
||||||
import { FormattedNumber } from 'components/FormattedNumber'
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
import { ChevronDown } from 'components/Icons'
|
import { ChevronDown } from 'components/Icons'
|
||||||
|
import SummaryLine from 'components/SummaryLine'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||||
@ -36,11 +37,9 @@ interface Props {
|
|||||||
sellAsset: Asset
|
sellAsset: Asset
|
||||||
showProgressIndicator: boolean
|
showProgressIndicator: boolean
|
||||||
isAdvanced?: boolean
|
isAdvanced?: boolean
|
||||||
direction?: OrderDirection
|
direction?: TradeDirection
|
||||||
}
|
}
|
||||||
|
|
||||||
const infoLineClasses = 'flex flex-row justify-between flex-1 mb-1 text-xs text-white'
|
|
||||||
|
|
||||||
export default function TradeSummary(props: Props) {
|
export default function TradeSummary(props: Props) {
|
||||||
const {
|
const {
|
||||||
buyAsset,
|
buyAsset,
|
||||||
@ -87,7 +86,7 @@ export default function TradeSummary(props: Props) {
|
|||||||
}, [assets, route, sellAsset.symbol])
|
}, [assets, route, sellAsset.symbol])
|
||||||
|
|
||||||
const buttonText = useMemo(() => {
|
const buttonText = useMemo(() => {
|
||||||
if (!isAdvanced && direction === 'sell') return `Sell ${sellAsset.symbol}`
|
if (!isAdvanced && direction === 'short') return `Sell ${sellAsset.symbol}`
|
||||||
return route.length ? `Buy ${buyAsset.symbol}` : 'No route found'
|
return route.length ? `Buy ${buyAsset.symbol}` : 'No route found'
|
||||||
}, [buyAsset.symbol, route, sellAsset.symbol, isAdvanced, direction])
|
}, [buyAsset.symbol, route, sellAsset.symbol, isAdvanced, direction])
|
||||||
|
|
||||||
@ -189,17 +188,3 @@ export default function TradeSummary(props: Props) {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SummaryLineProps {
|
|
||||||
children: React.ReactNode
|
|
||||||
label: string
|
|
||||||
className?: string
|
|
||||||
}
|
|
||||||
function SummaryLine(props: SummaryLineProps) {
|
|
||||||
return (
|
|
||||||
<div className={classNames(infoLineClasses, props.className)}>
|
|
||||||
<span className='opacity-40'>{props.label}</span>
|
|
||||||
<span>{props.children}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
|||||||
import estimateExactIn from 'api/swap/estimateExactIn'
|
import estimateExactIn from 'api/swap/estimateExactIn'
|
||||||
import AvailableLiquidityMessage from 'components/AvailableLiquidityMessage'
|
import AvailableLiquidityMessage from 'components/AvailableLiquidityMessage'
|
||||||
import DepositCapMessage from 'components/DepositCapMessage'
|
import DepositCapMessage from 'components/DepositCapMessage'
|
||||||
import { DirectionSelect } from 'components/DirectionSelect'
|
|
||||||
import Divider from 'components/Divider'
|
import Divider from 'components/Divider'
|
||||||
import RangeInput from 'components/RangeInput'
|
import RangeInput from 'components/RangeInput'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
@ -16,6 +15,7 @@ import MarginToggle from 'components/Trade/TradeModule/SwapForm/MarginToggle'
|
|||||||
import OrderTypeSelector from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector'
|
import OrderTypeSelector from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector'
|
||||||
import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types'
|
import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types'
|
||||||
import TradeSummary from 'components/Trade/TradeModule/SwapForm/TradeSummary'
|
import TradeSummary from 'components/Trade/TradeModule/SwapForm/TradeSummary'
|
||||||
|
import { TradeDirectionSelector } from 'components/TradeDirectionSelector'
|
||||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||||
import { BN_ZERO } from 'constants/math'
|
import { BN_ZERO } from 'constants/math'
|
||||||
@ -52,14 +52,15 @@ export default function SwapForm(props: Props) {
|
|||||||
const swap = useStore((s) => s.swap)
|
const swap = useStore((s) => s.swap)
|
||||||
const [slippage] = useLocalStorage(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
const [slippage] = useLocalStorage(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||||
const { computeMaxSwapAmount } = useHealthComputer(account)
|
const { computeMaxSwapAmount } = useHealthComputer(account)
|
||||||
const [orderDirection, setOrderDirection] = useState<OrderDirection>('buy')
|
const [tradeDirection, setTradeDirection] = useState<TradeDirection>('long')
|
||||||
const { data: borrowAssets } = useMarketBorrowings()
|
const { data: borrowAssets } = useMarketBorrowings()
|
||||||
const { data: marketAssets } = useMarketAssets()
|
const { data: marketAssets } = useMarketAssets()
|
||||||
|
|
||||||
const [inputAsset, outputAsset] = useMemo(() => {
|
const [inputAsset, outputAsset] = useMemo(() => {
|
||||||
if (isAdvanced) return [sellAsset, buyAsset]
|
if (isAdvanced) return [sellAsset, buyAsset]
|
||||||
if (orderDirection === 'buy') return [sellAsset, buyAsset]
|
if (tradeDirection === 'long') return [sellAsset, buyAsset]
|
||||||
return [buyAsset, sellAsset]
|
return [buyAsset, sellAsset]
|
||||||
}, [buyAsset, sellAsset, orderDirection, isAdvanced])
|
}, [buyAsset, sellAsset, tradeDirection, isAdvanced])
|
||||||
const { data: route, isLoading: isRouteLoading } = useSwapRoute(
|
const { data: route, isLoading: isRouteLoading } = useSwapRoute(
|
||||||
inputAsset.denom,
|
inputAsset.denom,
|
||||||
outputAsset.denom,
|
outputAsset.denom,
|
||||||
@ -246,7 +247,7 @@ export default function SwapForm(props: Props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChangeOutputAmount(BN_ZERO)
|
onChangeOutputAmount(BN_ZERO)
|
||||||
onChangeInputAmount(BN_ZERO)
|
onChangeInputAmount(BN_ZERO)
|
||||||
}, [orderDirection, onChangeOutputAmount, onChangeInputAmount])
|
}, [tradeDirection, onChangeOutputAmount, onChangeInputAmount])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOutputAssetAmount(BN_ZERO)
|
setOutputAssetAmount(BN_ZERO)
|
||||||
@ -375,9 +376,9 @@ export default function SwapForm(props: Props) {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<DirectionSelect
|
<TradeDirectionSelector
|
||||||
direction={orderDirection}
|
direction={tradeDirection}
|
||||||
onChangeDirection={setOrderDirection}
|
onChangeDirection={setTradeDirection}
|
||||||
asset={buyAsset}
|
asset={buyAsset}
|
||||||
/>
|
/>
|
||||||
<AssetAmountInput
|
<AssetAmountInput
|
||||||
@ -460,7 +461,7 @@ export default function SwapForm(props: Props) {
|
|||||||
sellAmount={inputAssetAmount}
|
sellAmount={inputAssetAmount}
|
||||||
buyAmount={outputAssetAmount}
|
buyAmount={outputAssetAmount}
|
||||||
isAdvanced={isAdvanced}
|
isAdvanced={isAdvanced}
|
||||||
direction={orderDirection}
|
direction={tradeDirection}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -1,28 +1,27 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
direction: OrderDirection
|
direction: TradeDirection
|
||||||
onChangeDirection: (direction: OrderDirection) => void
|
onChangeDirection: (direction: TradeDirection) => void
|
||||||
asset?: Asset
|
asset?: Asset
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DirectionSelect(props: Props) {
|
export function TradeDirectionSelector(props: Props) {
|
||||||
const hasAsset = props.asset
|
|
||||||
const directions: OrderDirection[] = hasAsset ? ['buy', 'sell'] : ['long', 'short']
|
|
||||||
return (
|
return (
|
||||||
<div className='flex rounded-sm bg-black/20'>
|
<div className='flex rounded-sm bg-black/20'>
|
||||||
<Direction
|
<Direction
|
||||||
onClick={() => props.onChangeDirection(directions[0])}
|
onClick={() => props.onChangeDirection('long')}
|
||||||
direction={directions[0]}
|
direction={'long'}
|
||||||
isActive={props.direction === directions[0]}
|
isActive={props.direction === 'long'}
|
||||||
asset={props.asset}
|
asset={props.asset}
|
||||||
/>
|
/>
|
||||||
<Direction
|
<Direction
|
||||||
onClick={() => props.onChangeDirection(directions[1])}
|
onClick={() => props.onChangeDirection('short')}
|
||||||
direction={directions[1]}
|
direction={'short'}
|
||||||
isActive={props.direction === directions[1]}
|
isActive={props.direction === 'short'}
|
||||||
asset={props.asset}
|
asset={props.asset}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -30,13 +29,22 @@ export function DirectionSelect(props: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface DirectionProps {
|
interface DirectionProps {
|
||||||
direction: 'long' | 'short' | 'buy' | 'sell'
|
direction: TradeDirection
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
asset?: Asset
|
asset?: Asset
|
||||||
}
|
}
|
||||||
function Direction(props: DirectionProps) {
|
function Direction(props: DirectionProps) {
|
||||||
const classString = props.direction === 'long' || props.direction === 'buy' ? 'success' : 'error'
|
const classString = props.direction === 'long' ? 'success' : 'error'
|
||||||
|
|
||||||
|
const label = useMemo(() => {
|
||||||
|
if (props.asset) {
|
||||||
|
return props.direction === 'long' ? `Buy ${props.asset.symbol}` : `Sell ${props.asset.symbol}`
|
||||||
|
} else {
|
||||||
|
return props.direction === 'long' ? 'Long' : 'Short'
|
||||||
|
}
|
||||||
|
}, [props.asset, props.direction])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={classNames(
|
className={classNames(
|
||||||
@ -52,7 +60,7 @@ function Direction(props: DirectionProps) {
|
|||||||
props.isActive ? `text-${classString}` : 'text-white/20',
|
props.isActive ? `text-${classString}` : 'text-white/20',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{props.asset ? `${props.direction} ${props.asset.symbol}` : props.direction}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
@ -74,7 +74,6 @@ export default function WalletConnectedButton() {
|
|||||||
client: undefined,
|
client: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
userDomain: undefined,
|
userDomain: undefined,
|
||||||
accounts: null,
|
|
||||||
balances: [],
|
balances: [],
|
||||||
focusComponent: null,
|
focusComponent: null,
|
||||||
})
|
})
|
||||||
|
@ -72,7 +72,6 @@ export default function WalletConnecting(props: Props) {
|
|||||||
client: undefined,
|
client: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
userDomain: undefined,
|
userDomain: undefined,
|
||||||
accounts: null,
|
|
||||||
focusComponent: {
|
focusComponent: {
|
||||||
component: (
|
component: (
|
||||||
<WalletSelect
|
<WalletSelect
|
||||||
@ -104,7 +103,6 @@ export default function WalletConnecting(props: Props) {
|
|||||||
client: undefined,
|
client: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
userDomain: undefined,
|
userDomain: undefined,
|
||||||
accounts: null,
|
|
||||||
focusComponent: {
|
focusComponent: {
|
||||||
component: <WalletSelect />,
|
component: <WalletSelect />,
|
||||||
},
|
},
|
||||||
@ -137,7 +135,6 @@ export default function WalletConnecting(props: Props) {
|
|||||||
client: undefined,
|
client: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
userDomain: undefined,
|
userDomain: undefined,
|
||||||
accounts: null,
|
|
||||||
focusComponent: {
|
focusComponent: {
|
||||||
component: (
|
component: (
|
||||||
<WalletSelect
|
<WalletSelect
|
||||||
@ -173,7 +170,6 @@ export default function WalletConnecting(props: Props) {
|
|||||||
client: undefined,
|
client: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
userDomain: undefined,
|
userDomain: undefined,
|
||||||
accounts: null,
|
|
||||||
focusComponent: {
|
focusComponent: {
|
||||||
component: <WalletSelect />,
|
component: <WalletSelect />,
|
||||||
},
|
},
|
||||||
|
@ -27,7 +27,6 @@ export default function Wallet() {
|
|||||||
client: undefined,
|
client: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
userDomain: undefined,
|
userDomain: undefined,
|
||||||
accounts: null,
|
|
||||||
balances: [],
|
balances: [],
|
||||||
focusComponent: null,
|
focusComponent: null,
|
||||||
})
|
})
|
||||||
|
@ -7,7 +7,7 @@ export default function useAccount(accountId?: string, suspense?: boolean) {
|
|||||||
const chainConfig = useChainConfig()
|
const chainConfig = useChainConfig()
|
||||||
|
|
||||||
return useSWR(
|
return useSWR(
|
||||||
`chains/${chainConfig.id}/accounts/${accountId}`,
|
accountId && `chains/${chainConfig.id}/accounts/${accountId}`,
|
||||||
() => getAccount(chainConfig, accountId),
|
() => getAccount(chainConfig, accountId),
|
||||||
{
|
{
|
||||||
suspense: suspense,
|
suspense: suspense,
|
||||||
|
@ -2,7 +2,6 @@ import useSWR from 'swr'
|
|||||||
|
|
||||||
import getAccounts from 'api/wallets/getAccounts'
|
import getAccounts from 'api/wallets/getAccounts'
|
||||||
import useChainConfig from 'hooks/useChainConfig'
|
import useChainConfig from 'hooks/useChainConfig'
|
||||||
import useStore from 'store'
|
|
||||||
import { AccountKind } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
|
import { AccountKind } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
|
||||||
|
|
||||||
export default function useAccounts(kind: AccountKind, address?: string, suspense = true) {
|
export default function useAccounts(kind: AccountKind, address?: string, suspense = true) {
|
||||||
@ -15,13 +14,6 @@ export default function useAccounts(kind: AccountKind, address?: string, suspens
|
|||||||
suspense: suspense,
|
suspense: suspense,
|
||||||
fallbackData: [],
|
fallbackData: [],
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
onSuccess: (accounts) => {
|
|
||||||
if (kind === 'high_levered_strategy') {
|
|
||||||
useStore.setState({ hlsAccounts: accounts })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
useStore.setState({ accounts: accounts })
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
import useAccounts from 'hooks/accounts/useAccounts'
|
||||||
import useAutoLendEnabledAccountIds from 'hooks/localStorage/useAutoLendEnabledAccountIds'
|
import useAutoLendEnabledAccountIds from 'hooks/localStorage/useAutoLendEnabledAccountIds'
|
||||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||||
import useStore from 'store'
|
|
||||||
|
|
||||||
export default function useAutoLend(): {
|
export default function useAutoLend(): {
|
||||||
autoLendEnabledAccountIds: string[]
|
autoLendEnabledAccountIds: string[]
|
||||||
@ -10,7 +10,7 @@ export default function useAutoLend(): {
|
|||||||
setAutoLendOnAllAccounts: (lendAssets: boolean) => void
|
setAutoLendOnAllAccounts: (lendAssets: boolean) => void
|
||||||
enableAutoLendAccountId: (accountId: string) => void
|
enableAutoLendAccountId: (accountId: string) => void
|
||||||
} {
|
} {
|
||||||
const accounts = useStore((s) => s.accounts)
|
const { data: accounts } = useAccounts('default', undefined, false)
|
||||||
const currentAccount = useCurrentAccount()
|
const currentAccount = useCurrentAccount()
|
||||||
const [autoLendEnabledAccountIds, setAutoLendEnabledAccountIds] = useAutoLendEnabledAccountIds()
|
const [autoLendEnabledAccountIds, setAutoLendEnabledAccountIds] = useAutoLendEnabledAccountIds()
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
import useAccounts from 'hooks/accounts/useAccounts'
|
||||||
import useAccountId from 'hooks/useAccountId'
|
import useAccountId from 'hooks/useAccountId'
|
||||||
import useStore from 'store'
|
|
||||||
|
|
||||||
export default function useCurrentAccount(): Account | undefined {
|
export default function useCurrentAccount(): Account | undefined {
|
||||||
const accountId = useAccountId()
|
const accountId = useAccountId()
|
||||||
|
const { data: accounts } = useAccounts('default', undefined, false)
|
||||||
|
|
||||||
const accounts = useStore((s) => s.accounts)
|
|
||||||
return accounts?.find((account) => account.id === accountId)
|
return accounts?.find((account) => account.id === accountId)
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,13 @@ import { PerpsPositions } from 'components/Perps/PerpsPositions'
|
|||||||
|
|
||||||
export default function PerpsPage() {
|
export default function PerpsPage() {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col w-full h-full gap-4'>
|
<div className='grid grid-cols-[auto_376px] grid-rows-[min-content_auto_auto] w-full gap-4'>
|
||||||
<div className='grid w-full grid-cols-[auto_346px] gap-4'>
|
|
||||||
<div className='flex flex-col gap-4'>
|
|
||||||
<PerpsInfo />
|
<PerpsInfo />
|
||||||
<PerpsChart />
|
<div className='h-full w-[376px] row-span-3'>
|
||||||
</div>
|
|
||||||
<PerpsModule />
|
<PerpsModule />
|
||||||
<PerpsPositions />
|
|
||||||
</div>
|
</div>
|
||||||
|
<PerpsChart />
|
||||||
|
<PerpsPositions />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ export default function createBroadcastSlice(
|
|||||||
case 'open-perp':
|
case 'open-perp':
|
||||||
toast.content.push({
|
toast.content.push({
|
||||||
coins: changes.deposits?.map((deposit) => deposit.toCoin()) ?? [],
|
coins: changes.deposits?.map((deposit) => deposit.toCoin()) ?? [],
|
||||||
text: 'Opened perp position',
|
text: 'Market order executed',
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'close-perp':
|
case 'close-perp':
|
||||||
|
7
src/types/interfaces/perps.d.ts
vendored
7
src/types/interfaces/perps.d.ts
vendored
@ -1,8 +1,6 @@
|
|||||||
const BNCoin = import('types/classes/BNCoin').BNCoin
|
const BNCoin = import('types/classes/BNCoin').BNCoin
|
||||||
|
|
||||||
type OrderDirection = PerpsType | ('buy' | 'sell')
|
type TradeDirection = 'long' | 'short'
|
||||||
|
|
||||||
type PerpsType = 'long' | 'short'
|
|
||||||
|
|
||||||
// TODO: 📈Remove this type when healthcomputer is implemented
|
// TODO: 📈Remove this type when healthcomputer is implemented
|
||||||
type PositionsWithoutPerps = Omit<
|
type PositionsWithoutPerps = Omit<
|
||||||
@ -13,7 +11,6 @@ type PositionsWithoutPerps = Omit<
|
|||||||
type PerpsPosition = {
|
type PerpsPosition = {
|
||||||
denom: string
|
denom: string
|
||||||
baseDenom: string
|
baseDenom: string
|
||||||
type: PerpsType
|
tradeDirection: TradeDirection
|
||||||
size: BigNumber
|
size: BigNumber
|
||||||
// closingFee: BNCoin
|
|
||||||
}
|
}
|
||||||
|
2
src/types/interfaces/store/common.d.ts
vendored
2
src/types/interfaces/store/common.d.ts
vendored
@ -1,5 +1,4 @@
|
|||||||
interface CommonSlice {
|
interface CommonSlice {
|
||||||
accounts: Account[] | null
|
|
||||||
address?: string
|
address?: string
|
||||||
chainConfig: ChainConfig
|
chainConfig: ChainConfig
|
||||||
userDomain?: {
|
userDomain?: {
|
||||||
@ -7,7 +6,6 @@ interface CommonSlice {
|
|||||||
domain_full: string
|
domain_full: string
|
||||||
}
|
}
|
||||||
balances: Coin[]
|
balances: Coin[]
|
||||||
hlsAccounts: Account[] | null
|
|
||||||
client?: WalletClient
|
client?: WalletClient
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
selectedAccount: string | null
|
selectedAccount: string | null
|
||||||
|
@ -331,3 +331,8 @@ export function isAccountEmpty(account: Account) {
|
|||||||
account.deposits.length === 0
|
account.deposits.length === 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAccountNetValue(account: Account, prices: BNCoin[], assets: Asset[]) {
|
||||||
|
const [deposits, lends, debts, vaults] = getAccountPositionValues(account, prices, assets)
|
||||||
|
return deposits.plus(lends).plus(vaults).minus(debts)
|
||||||
|
}
|
||||||
|
@ -82,8 +82,8 @@ export function resolvePerpsPositions(perpPositions: Positions['perps']): PerpsP
|
|||||||
return {
|
return {
|
||||||
denom: position.denom,
|
denom: position.denom,
|
||||||
baseDenom: position.base_denom,
|
baseDenom: position.base_denom,
|
||||||
size: BN(position.size as any),
|
size: BN(position.size as any).abs(),
|
||||||
type: BN(position.size as any).isNegative() ? 'short' : 'long',
|
tradeDirection: BN(position.size as any).isNegative() ? 'short' : 'long',
|
||||||
closingFee: BNCoin.fromCoin(position.pnl.coins.closing_fee),
|
closingFee: BNCoin.fromCoin(position.pnl.coins.closing_fee),
|
||||||
pnl: getPnlCoin(position.pnl.coins.pnl, position.base_denom),
|
pnl: getPnlCoin(position.pnl.coins.pnl, position.base_denom),
|
||||||
entryPrice: BN(position.entry_price),
|
entryPrice: BN(position.entry_price),
|
||||||
@ -94,7 +94,9 @@ export function resolvePerpsPositions(perpPositions: Positions['perps']): PerpsP
|
|||||||
function getPnlCoin(pnl: PnL, denom: string): BNCoin {
|
function getPnlCoin(pnl: PnL, denom: string): BNCoin {
|
||||||
let amount = BN_ZERO
|
let amount = BN_ZERO
|
||||||
|
|
||||||
if ('loss' in (pnl as { loss: Coin })) {
|
if (pnl === 'break_even') return BNCoin.fromDenomAndBigNumber(denom, amount)
|
||||||
|
|
||||||
|
if ('loss' in (pnl as any)) {
|
||||||
amount = BN((pnl as any).loss.amount).times(-1)
|
amount = BN((pnl as any).loss.amount).times(-1)
|
||||||
} else if ('profit' in (pnl as { profit: Coin })) {
|
} else if ('profit' in (pnl as { profit: Coin })) {
|
||||||
amount = BN((pnl as any).profit.amount)
|
amount = BN((pnl as any).profit.amount)
|
||||||
|
Loading…
Reference in New Issue
Block a user