Extend perps (#721)

This commit is contained in:
Bob van der Helm 2024-01-10 09:30:32 +01:00 committed by GitHub
parent 14d09409f9
commit bfd03d66a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 279 additions and 164 deletions

View File

@ -1,5 +1,6 @@
import { cacheFn, oraclePriceCache } from 'api/cache'
import { getOracleQueryClient } from 'api/cosmwasm-client'
import { BN_ZERO } from 'constants/math'
import { PRICE_ORACLE_DECIMALS } from 'constants/query'
import { BNCoin } from 'types/classes/BNCoin'
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
return BNCoin.fromDenomAndBigNumber(
asset.denom,
BN(priceResponse.price).shiftedBy(decimalDiff),
BN(priceResponse?.price ?? BN_ZERO).shiftedBy(decimalDiff),
)
})
} catch (ex) {

View File

@ -18,6 +18,7 @@ interface Props {
parentheses?: boolean
showZero?: boolean
options?: FormatOptions
isProfitOrLoss?: boolean
}
export default function DisplayCurrency(props: Props) {
@ -34,10 +35,10 @@ export default function DisplayCurrency(props: Props) {
const isUSD = displayCurrencyAsset.id === 'USD'
const amount = useMemo(() => {
const [amount, absoluteAmount] = useMemo(() => {
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 displayPrice = getCoinValue(
@ -46,10 +47,14 @@ export default function DisplayCurrency(props: Props) {
assets,
)
return coinValue.div(displayPrice).toNumber()
const amount = coinValue.div(displayPrice).toNumber()
return [amount, Math.abs(amount)]
}, [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 prefix = isUSD
@ -64,8 +69,10 @@ export default function DisplayCurrency(props: Props) {
className={classNames(
props.className,
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={{
minDecimals: isUSD ? 2 : 0,
maxDecimals: 2,

View File

@ -35,7 +35,6 @@ export default function ChainSelect() {
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
balances: [],
})
navigate(getRoute(getPage(pathname), searchParams))

View 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' }} />}
/>
)
}

View File

@ -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>
)
}

View File

@ -1,6 +1,7 @@
import classNames from 'classnames'
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'
export const PNL_META = { accessorKey: 'pnl', header: 'Total PnL', id: 'pnl' }
@ -10,19 +11,38 @@ type Props = {
}
export default function PnL(props: Props) {
const isNegative = props.pnl.amount.isNegative()
return (
<span
className={classNames(
'text-xs',
isNegative ? 'text-error' : props.pnl.amount.isZero() ? '' : 'text-success',
)}
<Tooltip
content={
<PnLTooltip
realized={BNCoin.fromDenomAndBigNumber('uusd', BN_ZERO)}
unrealized={props.pnl}
/>
}
type='info'
underline
>
{isNegative ? '-' : props.pnl.amount.isZero() ? '' : '+'}
<DisplayCurrency
className='inline'
coin={BNCoin.fromDenomAndBigNumber(props.pnl.denom, props.pnl.amount.abs())}
/>
</span>
<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>
)
}

View File

@ -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>
)
}

View File

@ -2,11 +2,14 @@ import { ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react'
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 { 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 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'
export default function usePerpsBalancesTable() {
@ -18,12 +21,21 @@ export default function usePerpsBalancesTable() {
},
{
...PERP_TYPE_META,
cell: ({ row }) => <PerpType type={row.original.type} />,
cell: ({ row }) => <TradeDirection tradeDirection={row.original.tradeDirection} />,
},
{
...SIZE_META,
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,
cell: ({ row }) => (

View File

@ -1,34 +1,48 @@
import { useMemo } from 'react'
import { BN_ZERO } from 'constants/math'
import useAllAssets from 'hooks/assets/useAllAssets'
import usePerpsEnabledAssets from 'hooks/assets/usePerpsEnabledAssets'
import useCurrentAccount from 'hooks/useCurrentAccount'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { getAccountNetValue } from 'utils/accounts'
import { byDenom } from 'utils/array'
import { demagnify } from 'utils/formatters'
export default function usePerpsBalancesTable() {
const currentAccount = useCurrentAccount()
const perpAssets = usePerpsEnabledAssets()
const allAssets = useAllAssets()
const { data: prices } = usePrices()
return useMemo<PerpPositionRow[]>(() => {
if (!currentAccount) return []
const netValue = getAccountNetValue(currentAccount, prices, allAssets)
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 {
asset,
type: position.type,
tradeDirection: position.tradeDirection,
size: position.size,
pnl: position.pnl,
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
})
}, [currentAccount, perpAssets])
}, [allAssets, currentAccount, perpAssets, prices])
}
export type PerpPositionRow = {
asset: Asset
type: 'long' | 'short'
tradeDirection: TradeDirection
size: BigNumber
pnl: BNCoin
entryPrice: BigNumber
liquidationPrice: BigNumber
leverage: number
}

View File

@ -1,14 +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'>
<button
key={leverage}
className='w-12 !border:none bg-white/10 rounded-sm py-1 text-xs hover:bg-white/20'
>
{leverage}x
</Button>
</button>
))}
</div>
)

View File

@ -1,10 +1,9 @@
import { useCallback, useState } from 'react'
import { useState } from 'react'
import Button from 'components/Button'
import Card from 'components/Card'
import { DirectionSelect } from 'components/DirectionSelect'
import { LeverageButtons } from 'components/Perps/Module/LeverageButtons'
import { Or } from 'components/Perps/Module/Or'
import PerpsSummary from 'components/Perps/Module/Summary'
import RangeInput from 'components/RangeInput'
import { Spacer } from 'components/Spacer'
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 OrderTypeSelector from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector'
import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types'
import { TradeDirectionSelector } from 'components/TradeDirectionSelector'
import { BN_ZERO } from 'constants/math'
import useBaseAsset from 'hooks/assets/useBasetAsset'
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'
export function PerpsModule() {
const [selectedOrderType, setSelectedOrderType] = useState<AvailableOrderType>('Market')
const [selectedOrderDirection, setSelectedOrderDirection] = useState<OrderDirection>('long')
const baseAsset = useBaseAsset()
const [tradeDirection, setTradeDirection] = useState<TradeDirection>('long')
const { perpsAsset } = usePerpsAsset()
const openPerpPosition = useStore((s) => s.openPerpPosition)
const currentAccount = useCurrentAccount()
const onConfirm = useCallback(async () => {
if (!currentAccount) return
await openPerpPosition({
accountId: currentAccount.id,
coin: BNCoin.fromDenomAndBigNumber(perpsAsset.denom, BN(1000)),
})
}, [currentAccount, openPerpPosition, perpsAsset.denom])
const [amount, setAmount] = useState<BigNumber>(BN_ZERO)
if (!perpsAsset) return null
return (
<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} />}
className='mb-4'
className='mb-4 h-full'
>
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
<DirectionSelect
direction={selectedOrderDirection}
onChangeDirection={setSelectedOrderDirection}
/>
<TradeDirectionSelector direction={tradeDirection} onChangeDirection={setTradeDirection} />
<AssetAmountInput
label='Amount'
max={BN_ZERO}
amount={BN_ZERO}
setAmount={() => {}}
asset={baseAsset}
max={BN(1000)} // TODO: Implement max calculation
amount={amount}
setAmount={setAmount}
asset={perpsAsset}
maxButtonLabel='Max:'
disabled={false}
/>
@ -64,7 +48,7 @@ export function PerpsModule() {
<RangeInput max={0} value={0} onChange={() => {}} />
<LeverageButtons />
<Spacer />
<Button onClick={onConfirm}>{selectedOrderDirection} ETH</Button>
<PerpsSummary amount={amount} tradeDirection={tradeDirection} asset={perpsAsset} />
</Card>
)
}

View 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>
)
}

View 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>
)
}

View File

@ -16,7 +16,7 @@ export default function TooltipContent(props: Props) {
<div>
<div
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 === 'warning' && 'bg-warning',
props.type === 'error' && 'bg-error',

View File

@ -51,7 +51,7 @@ export const Tooltip = (props: Props) => {
<span
className={classNames(
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',
props.className,
)}

View File

@ -7,6 +7,7 @@ import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider'
import { FormattedNumber } from 'components/FormattedNumber'
import { ChevronDown } from 'components/Icons'
import SummaryLine from 'components/SummaryLine'
import Text from 'components/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
@ -36,11 +37,9 @@ interface Props {
sellAsset: Asset
showProgressIndicator: 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) {
const {
buyAsset,
@ -87,7 +86,7 @@ export default function TradeSummary(props: Props) {
}, [assets, route, sellAsset.symbol])
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'
}, [buyAsset.symbol, route, sellAsset.symbol, isAdvanced, direction])
@ -189,17 +188,3 @@ export default function TradeSummary(props: Props) {
</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>
)
}

View File

@ -4,7 +4,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
import estimateExactIn from 'api/swap/estimateExactIn'
import AvailableLiquidityMessage from 'components/AvailableLiquidityMessage'
import DepositCapMessage from 'components/DepositCapMessage'
import { DirectionSelect } from 'components/DirectionSelect'
import Divider from 'components/Divider'
import RangeInput from 'components/RangeInput'
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 { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types'
import TradeSummary from 'components/Trade/TradeModule/SwapForm/TradeSummary'
import { TradeDirectionSelector } from 'components/TradeDirectionSelector'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import { BN_ZERO } from 'constants/math'
@ -52,14 +52,15 @@ export default function SwapForm(props: Props) {
const swap = useStore((s) => s.swap)
const [slippage] = useLocalStorage(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
const { computeMaxSwapAmount } = useHealthComputer(account)
const [orderDirection, setOrderDirection] = useState<OrderDirection>('buy')
const [tradeDirection, setTradeDirection] = useState<TradeDirection>('long')
const { data: borrowAssets } = useMarketBorrowings()
const { data: marketAssets } = useMarketAssets()
const [inputAsset, outputAsset] = useMemo(() => {
if (isAdvanced) return [sellAsset, buyAsset]
if (orderDirection === 'buy') return [sellAsset, buyAsset]
if (tradeDirection === 'long') return [sellAsset, buyAsset]
return [buyAsset, sellAsset]
}, [buyAsset, sellAsset, orderDirection, isAdvanced])
}, [buyAsset, sellAsset, tradeDirection, isAdvanced])
const { data: route, isLoading: isRouteLoading } = useSwapRoute(
inputAsset.denom,
outputAsset.denom,
@ -246,7 +247,7 @@ export default function SwapForm(props: Props) {
useEffect(() => {
onChangeOutputAmount(BN_ZERO)
onChangeInputAmount(BN_ZERO)
}, [orderDirection, onChangeOutputAmount, onChangeInputAmount])
}, [tradeDirection, onChangeOutputAmount, onChangeInputAmount])
useEffect(() => {
setOutputAssetAmount(BN_ZERO)
@ -375,9 +376,9 @@ export default function SwapForm(props: Props) {
/>
) : (
<>
<DirectionSelect
direction={orderDirection}
onChangeDirection={setOrderDirection}
<TradeDirectionSelector
direction={tradeDirection}
onChangeDirection={setTradeDirection}
asset={buyAsset}
/>
<AssetAmountInput
@ -460,7 +461,7 @@ export default function SwapForm(props: Props) {
sellAmount={inputAssetAmount}
buyAmount={outputAssetAmount}
isAdvanced={isAdvanced}
direction={orderDirection}
direction={tradeDirection}
/>
</div>
</>

View File

@ -1,28 +1,27 @@
import classNames from 'classnames'
import { useMemo } from 'react'
import Text from 'components/Text'
interface Props {
direction: OrderDirection
onChangeDirection: (direction: OrderDirection) => void
direction: TradeDirection
onChangeDirection: (direction: TradeDirection) => void
asset?: Asset
}
export function DirectionSelect(props: Props) {
const hasAsset = props.asset
const directions: OrderDirection[] = hasAsset ? ['buy', 'sell'] : ['long', 'short']
export function TradeDirectionSelector(props: Props) {
return (
<div className='flex rounded-sm bg-black/20'>
<Direction
onClick={() => props.onChangeDirection(directions[0])}
direction={directions[0]}
isActive={props.direction === directions[0]}
onClick={() => props.onChangeDirection('long')}
direction={'long'}
isActive={props.direction === 'long'}
asset={props.asset}
/>
<Direction
onClick={() => props.onChangeDirection(directions[1])}
direction={directions[1]}
isActive={props.direction === directions[1]}
onClick={() => props.onChangeDirection('short')}
direction={'short'}
isActive={props.direction === 'short'}
asset={props.asset}
/>
</div>
@ -30,13 +29,22 @@ export function DirectionSelect(props: Props) {
}
interface DirectionProps {
direction: 'long' | 'short' | 'buy' | 'sell'
direction: TradeDirection
isActive: boolean
onClick: () => void
asset?: Asset
}
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 (
<button
className={classNames(
@ -52,7 +60,7 @@ function Direction(props: DirectionProps) {
props.isActive ? `text-${classString}` : 'text-white/20',
)}
>
{props.asset ? `${props.direction} ${props.asset.symbol}` : props.direction}
{label}
</Text>
</button>
)

View File

@ -74,7 +74,6 @@ export default function WalletConnectedButton() {
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
balances: [],
focusComponent: null,
})

View File

@ -72,7 +72,6 @@ export default function WalletConnecting(props: Props) {
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
focusComponent: {
component: (
<WalletSelect
@ -104,7 +103,6 @@ export default function WalletConnecting(props: Props) {
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
focusComponent: {
component: <WalletSelect />,
},
@ -137,7 +135,6 @@ export default function WalletConnecting(props: Props) {
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
focusComponent: {
component: (
<WalletSelect
@ -173,7 +170,6 @@ export default function WalletConnecting(props: Props) {
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
focusComponent: {
component: <WalletSelect />,
},

View File

@ -27,7 +27,6 @@ export default function Wallet() {
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
balances: [],
focusComponent: null,
})

View File

@ -7,7 +7,7 @@ export default function useAccount(accountId?: string, suspense?: boolean) {
const chainConfig = useChainConfig()
return useSWR(
`chains/${chainConfig.id}/accounts/${accountId}`,
accountId && `chains/${chainConfig.id}/accounts/${accountId}`,
() => getAccount(chainConfig, accountId),
{
suspense: suspense,

View File

@ -2,7 +2,6 @@ import useSWR from 'swr'
import getAccounts from 'api/wallets/getAccounts'
import useChainConfig from 'hooks/useChainConfig'
import useStore from 'store'
import { AccountKind } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
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,
fallbackData: [],
revalidateOnFocus: false,
onSuccess: (accounts) => {
if (kind === 'high_levered_strategy') {
useStore.setState({ hlsAccounts: accounts })
return
}
useStore.setState({ accounts: accounts })
},
},
)
}

View File

@ -1,6 +1,6 @@
import useAccounts from 'hooks/accounts/useAccounts'
import useAutoLendEnabledAccountIds from 'hooks/localStorage/useAutoLendEnabledAccountIds'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useStore from 'store'
export default function useAutoLend(): {
autoLendEnabledAccountIds: string[]
@ -10,7 +10,7 @@ export default function useAutoLend(): {
setAutoLendOnAllAccounts: (lendAssets: boolean) => void
enableAutoLendAccountId: (accountId: string) => void
} {
const accounts = useStore((s) => s.accounts)
const { data: accounts } = useAccounts('default', undefined, false)
const currentAccount = useCurrentAccount()
const [autoLendEnabledAccountIds, setAutoLendEnabledAccountIds] = useAutoLendEnabledAccountIds()

View File

@ -1,9 +1,9 @@
import useAccounts from 'hooks/accounts/useAccounts'
import useAccountId from 'hooks/useAccountId'
import useStore from 'store'
export default function useCurrentAccount(): Account | undefined {
const accountId = useAccountId()
const { data: accounts } = useAccounts('default', undefined, false)
const accounts = useStore((s) => s.accounts)
return accounts?.find((account) => account.id === accountId)
}

View File

@ -5,15 +5,13 @@ 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'>
<div className='flex flex-col gap-4'>
<PerpsInfo />
<PerpsChart />
</div>
<div className='grid grid-cols-[auto_376px] grid-rows-[min-content_auto_auto] w-full gap-4'>
<PerpsInfo />
<div className='h-full w-[376px] row-span-3'>
<PerpsModule />
<PerpsPositions />
</div>
<PerpsChart />
<PerpsPositions />
</div>
)
}

View File

@ -120,7 +120,7 @@ export default function createBroadcastSlice(
case 'open-perp':
toast.content.push({
coins: changes.deposits?.map((deposit) => deposit.toCoin()) ?? [],
text: 'Opened perp position',
text: 'Market order executed',
})
break
case 'close-perp':

View File

@ -1,8 +1,6 @@
const BNCoin = import('types/classes/BNCoin').BNCoin
type OrderDirection = PerpsType | ('buy' | 'sell')
type PerpsType = 'long' | 'short'
type TradeDirection = 'long' | 'short'
// TODO: 📈Remove this type when healthcomputer is implemented
type PositionsWithoutPerps = Omit<
@ -13,7 +11,6 @@ type PositionsWithoutPerps = Omit<
type PerpsPosition = {
denom: string
baseDenom: string
type: PerpsType
tradeDirection: TradeDirection
size: BigNumber
// closingFee: BNCoin
}

View File

@ -1,5 +1,4 @@
interface CommonSlice {
accounts: Account[] | null
address?: string
chainConfig: ChainConfig
userDomain?: {
@ -7,7 +6,6 @@ interface CommonSlice {
domain_full: string
}
balances: Coin[]
hlsAccounts: Account[] | null
client?: WalletClient
isOpen: boolean
selectedAccount: string | null

View File

@ -331,3 +331,8 @@ export function isAccountEmpty(account: Account) {
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)
}

View File

@ -82,8 +82,8 @@ export function resolvePerpsPositions(perpPositions: Positions['perps']): PerpsP
return {
denom: position.denom,
baseDenom: position.base_denom,
size: BN(position.size as any),
type: BN(position.size as any).isNegative() ? 'short' : 'long',
size: BN(position.size as any).abs(),
tradeDirection: BN(position.size as any).isNegative() ? 'short' : 'long',
closingFee: BNCoin.fromCoin(position.pnl.coins.closing_fee),
pnl: getPnlCoin(position.pnl.coins.pnl, position.base_denom),
entryPrice: BN(position.entry_price),
@ -94,7 +94,9 @@ export function resolvePerpsPositions(perpPositions: Positions['perps']): PerpsP
function getPnlCoin(pnl: PnL, denom: string): BNCoin {
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)
} else if ('profit' in (pnl as { profit: Coin })) {
amount = BN((pnl as any).profit.amount)