add close all perp positions
This commit is contained in:
parent
559170bded
commit
cfe4b8f0dc
|
@ -0,0 +1,25 @@
|
|||
const NukeIcon = ({ className }: { className?: string }) => {
|
||||
return (
|
||||
<svg
|
||||
className={`${className}`}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
fill="currentColor"
|
||||
>
|
||||
<g clipPath="url(#a)">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M16 32c8.8366 0 16-7.1634 16-16 0-8.83656-7.1634-16-16-16C7.16344 0 0 7.16344 0 16c0 8.8366 7.16344 16 16 16Zm.026-20.9524c.7047 0 1.3722.1482 1.9656.4077.2967.1482.6675 0 .8159-.2595l3.3748-5.85549c.1854-.33355.0742-.74121-.2596-.88945-3.5973-1.85302-8.0105-2.00126-11.8675-.03706-.33375.1853-.445.59296-.25957.92651l3.44897 5.81849c.1484.2965.5192.4076.8159.2594.5934-.2224 1.2609-.3706 1.9655-.3706Zm-5.5628 4.9661H3.67652c-.37086 0-.66755.2965-.66755.6671.11126 1.9642.66755 3.9654 1.70595 5.7814 1.07549 1.853 2.52184 3.3354 4.22779 4.4472.29668.1853.74171.0741.89006-.2224l3.44893-5.8925c.1484-.2965.1113-.6301-.1483-.8524-1.0755-.7783-1.8172-2.0013-2.0026-3.3725-.0371-.2965-.3338-.5559-.6676-.5559Zm10.4953.5559c-.1855 1.3712-.9272 2.5942-2.0027 3.3725-.2596.1853-.3338.5559-.1483.8524l3.3748 5.8555c.1854.3335.5934.4447.9271.2224 3.5603-2.2978 5.6371-6.152 5.8967-10.1546.0371-.3706-.2967-.667-.6676-.667h-6.7496c-.3338-.0371-.5934.2223-.6304.5188ZM16.026 19.312c1.8434 0 3.3377-1.4933 3.3377-3.3354 0-1.8421-1.4943-3.3354-3.3377-3.3354s-3.3377 1.4933-3.3377 3.3354c0 1.8421 1.4943 3.3354 3.3377 3.3354Z"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path d="M0 0h32v32H0z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default NukeIcon
|
|
@ -0,0 +1,128 @@
|
|||
import { FunctionComponent, useCallback, useState } from 'react'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Modal from '@components/shared/Modal'
|
||||
import Button, { LinkButton } from '@components/shared/Button'
|
||||
import { notify } from 'utils/notifications'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import { isMangoError } from 'types'
|
||||
import { ModalProps } from 'types/modal'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import useOpenPerpPositions from 'hooks/useOpenPerpPositions'
|
||||
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
|
||||
import MarketLogos from './MarketLogos'
|
||||
import PerpSideBadge from './PerpSideBadge'
|
||||
import FormatNumericValue from '@components/shared/FormatNumericValue'
|
||||
|
||||
const CloseAllPositionsModal: FunctionComponent<ModalProps> = ({
|
||||
onClose,
|
||||
isOpen,
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const openPerpPositions = useOpenPerpPositions()
|
||||
const { group } = useMangoGroup()
|
||||
|
||||
const handleCloseAll = useCallback(async () => {
|
||||
const client = mangoStore.getState().client
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const actions = mangoStore.getState().actions
|
||||
|
||||
if (!group || !mangoAccount) {
|
||||
notify({
|
||||
title: 'Something went wrong. Try again later',
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
setSubmitting(true)
|
||||
try {
|
||||
const maxSlippage = 0.025
|
||||
const { signature: tx } = await client.perpCloseAll(
|
||||
group,
|
||||
mangoAccount,
|
||||
maxSlippage,
|
||||
)
|
||||
actions.fetchOpenOrders()
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Transaction successful',
|
||||
txid: tx,
|
||||
})
|
||||
} catch (e) {
|
||||
if (isMangoError(e)) {
|
||||
notify({
|
||||
title: 'There was an issue.',
|
||||
description: e.message,
|
||||
txid: e?.txid,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
console.error('Place trade error:', e)
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
onClose()
|
||||
}
|
||||
}, [group, onClose])
|
||||
|
||||
if (!group) return null
|
||||
|
||||
return (
|
||||
<Modal onClose={onClose} isOpen={isOpen}>
|
||||
<h3 className="mb-2 text-center">{t('trade:close-all-positions')}</h3>
|
||||
<div className="pb-6 text-th-fgd-3">{t('trade:price-expect')}</div>
|
||||
<div className="border-b border-th-bkg-3">
|
||||
{openPerpPositions.map((position, i) => {
|
||||
const market = group.getPerpMarketByMarketIndex(position.marketIndex)
|
||||
const basePosition = position.getBasePositionUi(market)
|
||||
const floorBasePosition = floorToDecimal(
|
||||
basePosition,
|
||||
getDecimalCount(market.minOrderSize),
|
||||
).toNumber()
|
||||
|
||||
if (!basePosition) return null
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between border-t border-th-bkg-3 py-3"
|
||||
key={market.name + i}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
<p className="mr-2">{market.name}</p>
|
||||
<PerpSideBadge basePosition={basePosition} />
|
||||
</div>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue value={Math.abs(floorBasePosition)} />
|
||||
<span className="mx-1 text-th-bkg-4">|</span>
|
||||
<FormatNumericValue
|
||||
value={Math.abs(floorBasePosition * market.uiPrice)}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<Button
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center"
|
||||
onClick={handleCloseAll}
|
||||
size="large"
|
||||
>
|
||||
{submitting ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<span>{t('trade:close-all-positions')}</span>
|
||||
)}
|
||||
</Button>
|
||||
<LinkButton
|
||||
className="inline-flex w-full items-center justify-center"
|
||||
onClick={onClose}
|
||||
>
|
||||
{t('cancel')}
|
||||
</LinkButton>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default CloseAllPositionsModal
|
|
@ -26,11 +26,14 @@ import { Disclosure, Transition } from '@headlessui/react'
|
|||
import useOpenPerpPositions from 'hooks/useOpenPerpPositions'
|
||||
import PnlTooltipContent from '@components/shared/PnlTooltipContent'
|
||||
import PerpSideBadge from './PerpSideBadge'
|
||||
import CloseAllPositionsModal from './CloseAllPositionsModal'
|
||||
import NukeIcon from '@components/icons/NukeIcon'
|
||||
|
||||
const PerpPositions = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const { group } = useMangoGroup()
|
||||
const [showMarketCloseModal, setShowMarketCloseModal] = useState(false)
|
||||
const [showCloseAllModal, setShowCloseAllModal] = useState(false)
|
||||
const [positionToClose, setPositionToClose] = useState<PerpPosition | null>(
|
||||
null,
|
||||
)
|
||||
|
@ -147,7 +150,22 @@ const PerpPositions = () => {
|
|||
</div>
|
||||
</Th>
|
||||
<Th className="text-right">{t('trade:unrealized-pnl')}</Th>
|
||||
{!isUnownedAccount ? <Th /> : null}
|
||||
{!isUnownedAccount ? (
|
||||
<Th>
|
||||
{openPerpPositions?.length > 1 ? (
|
||||
<div className="flex justify-end">
|
||||
<div className="flex items-center">
|
||||
<NukeIcon className="mr-1.5 h-4 w-4 text-th-active" />
|
||||
<LinkButton
|
||||
onClick={() => setShowCloseAllModal(true)}
|
||||
>
|
||||
{t('trade:close-all')}
|
||||
</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</Th>
|
||||
) : null}
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -300,7 +318,7 @@ const PerpPositions = () => {
|
|||
size="small"
|
||||
onClick={() => showClosePositionModal(position)}
|
||||
>
|
||||
Close
|
||||
{t('close')}
|
||||
</Button>
|
||||
<IconButton
|
||||
hideBg
|
||||
|
@ -613,14 +631,26 @@ const PerpPositions = () => {
|
|||
</div>
|
||||
<div className="col-span-2 mt-3 flex space-x-3">
|
||||
<Button
|
||||
className="w-1/2"
|
||||
className="w-full text-xs sm:text-sm"
|
||||
secondary
|
||||
onClick={() => showClosePositionModal(position)}
|
||||
>
|
||||
{t('trade:close-position')}
|
||||
{t('close')}
|
||||
</Button>
|
||||
{openPerpPositions?.length > 1 ? (
|
||||
<Button
|
||||
className="w-full text-xs sm:text-sm"
|
||||
secondary
|
||||
onClick={() => setShowCloseAllModal(true)}
|
||||
>
|
||||
<div className="flex items-center justify-center">
|
||||
<NukeIcon className="mr-2 h-4 w-4 flex-shrink-0 text-th-active" />
|
||||
{t('trade:close-all')}
|
||||
</div>
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
className="w-1/2"
|
||||
className="w-full text-xs sm:text-sm"
|
||||
secondary
|
||||
onClick={() =>
|
||||
handleShowShare(openPerpPositions[i])
|
||||
|
@ -734,6 +764,12 @@ const PerpPositions = () => {
|
|||
position={positionToClose}
|
||||
/>
|
||||
) : null}
|
||||
{showCloseAllModal ? (
|
||||
<CloseAllPositionsModal
|
||||
isOpen={showCloseAllModal}
|
||||
onClose={() => setShowCloseAllModal(false)}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"cancel-all": "Cancel All",
|
||||
"cancel-order": "Cancel Order",
|
||||
"cancel-order-error": "Failed to cancel order",
|
||||
"close-all": "Close All",
|
||||
"close-all-positions": "Close All Positions",
|
||||
"close-confirm": "Market close your {{config_name}} position",
|
||||
"close-position": "Close Position",
|
||||
"connect-orders": "Connect to view your open orders",
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"cancel-all": "Cancel All",
|
||||
"cancel-order": "Cancel Order",
|
||||
"cancel-order-error": "Failed to cancel order",
|
||||
"close-all": "Close All",
|
||||
"close-all-positions": "Close All Positions",
|
||||
"close-confirm": "Market close your {{config_name}} position",
|
||||
"close-position": "Close Position",
|
||||
"connect-orders": "Connect to view your open orders",
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"cancel-all": "Cancel All",
|
||||
"cancel-order": "Cancel Order",
|
||||
"cancel-order-error": "Failed to cancel order",
|
||||
"close-all": "Close All",
|
||||
"close-all-positions": "Close All Positions",
|
||||
"close-confirm": "Market close your {{config_name}} position",
|
||||
"close-position": "Close Position",
|
||||
"connect-orders": "Connect to view your open orders",
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"cancel-all": "Cancel All",
|
||||
"cancel-order": "Cancel Order",
|
||||
"cancel-order-error": "取消订单失败",
|
||||
"close-all": "Close All",
|
||||
"close-all-positions": "Close All Positions",
|
||||
"close-confirm": "市场平仓 {{config_name}}",
|
||||
"close-position": "平仓",
|
||||
"connect-orders": "连接以查看未结订单",
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"cancel-all": "Cancel All",
|
||||
"cancel-order": "Cancel Order",
|
||||
"cancel-order-error": "取消訂單失敗",
|
||||
"close-all": "Close All",
|
||||
"close-all-positions": "Close All Positions",
|
||||
"close-confirm": "市場平倉 {{config_name}}",
|
||||
"close-position": "平倉",
|
||||
"connect-orders": "連接以查看未結訂單",
|
||||
|
|
Loading…
Reference in New Issue