add close all perp positions

This commit is contained in:
saml33 2023-08-28 13:47:50 +10:00
parent 559170bded
commit cfe4b8f0dc
8 changed files with 204 additions and 5 deletions

View File

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

View File

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

View File

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

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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": "连接以查看未结订单",

View File

@ -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": "連接以查看未結訂單",