add market close to positions table (#365)
* add market close to positions table * fix variable reference * use secondary buttons
This commit is contained in:
parent
dd1c0fab1d
commit
f226e210ce
|
@ -1,6 +1,10 @@
|
||||||
import { FunctionComponent, useState } from 'react'
|
import { FunctionComponent, useState } from 'react'
|
||||||
import useMangoStore from '../stores/useMangoStore'
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
import { PerpMarket, ZERO_BN } from '@blockworks-foundation/mango-client'
|
import {
|
||||||
|
MarketConfig,
|
||||||
|
PerpMarket,
|
||||||
|
ZERO_BN,
|
||||||
|
} from '@blockworks-foundation/mango-client'
|
||||||
import Button, { LinkButton } from './Button'
|
import Button, { LinkButton } from './Button'
|
||||||
import { notify } from '../utils/notifications'
|
import { notify } from '../utils/notifications'
|
||||||
import Loading from './Loading'
|
import Loading from './Loading'
|
||||||
|
@ -12,26 +16,26 @@ import { useWallet } from '@solana/wallet-adapter-react'
|
||||||
interface MarketCloseModalProps {
|
interface MarketCloseModalProps {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
market: PerpMarket
|
position: {
|
||||||
marketIndex: number
|
marketConfig: MarketConfig
|
||||||
|
perpMarket: PerpMarket
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
||||||
onClose,
|
onClose,
|
||||||
isOpen,
|
isOpen,
|
||||||
market,
|
position,
|
||||||
marketIndex,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const [submitting, setSubmitting] = useState(false)
|
const [submitting, setSubmitting] = useState(false)
|
||||||
const { wallet } = useWallet()
|
const { wallet } = useWallet()
|
||||||
const actions = useMangoStore((s) => s.actions)
|
const actions = useMangoStore((s) => s.actions)
|
||||||
const config = useMangoStore.getState().selectedMarket.config
|
const { marketConfig, perpMarket } = position
|
||||||
|
|
||||||
async function handleMarketClose() {
|
async function handleMarketClose() {
|
||||||
const mangoAccount = useMangoStore.getState().selectedMangoAccount.current
|
const mangoAccount = useMangoStore.getState().selectedMangoAccount.current
|
||||||
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
||||||
const marketConfig = useMangoStore.getState().selectedMarket.config
|
|
||||||
const mangoClient = useMangoStore.getState().connection.client
|
const mangoClient = useMangoStore.getState().connection.client
|
||||||
const askInfo =
|
const askInfo =
|
||||||
useMangoStore.getState().accountInfos[marketConfig.asksKey.toString()]
|
useMangoStore.getState().accountInfos[marketConfig.asksKey.toString()]
|
||||||
|
@ -51,11 +55,11 @@ const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const perpAccount = mangoAccount.perpAccounts[marketIndex]
|
const perpAccount = mangoAccount.perpAccounts[marketConfig.marketIndex]
|
||||||
const side = perpAccount.basePosition.gt(ZERO_BN) ? 'sell' : 'buy'
|
const side = perpAccount.basePosition.gt(ZERO_BN) ? 'sell' : 'buy'
|
||||||
// send a large size to ensure we are reducing the entire position
|
// send a large size to ensure we are reducing the entire position
|
||||||
const size =
|
const size =
|
||||||
Math.abs(market.baseLotsToNumber(perpAccount.basePosition)) * 2
|
Math.abs(perpMarket.baseLotsToNumber(perpAccount.basePosition)) * 2
|
||||||
|
|
||||||
// hard coded for now; market orders are very dangerous and fault prone
|
// hard coded for now; market orders are very dangerous and fault prone
|
||||||
const maxSlippage: number | undefined = 0.025
|
const maxSlippage: number | undefined = 0.025
|
||||||
|
@ -63,7 +67,7 @@ const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
||||||
const txid = await mangoClient.placePerpOrder2(
|
const txid = await mangoClient.placePerpOrder2(
|
||||||
mangoGroup,
|
mangoGroup,
|
||||||
mangoAccount,
|
mangoAccount,
|
||||||
market,
|
perpMarket,
|
||||||
wallet?.adapter,
|
wallet?.adapter,
|
||||||
side,
|
side,
|
||||||
referencePrice * (1 + (side === 'buy' ? 1 : -1) * maxSlippage),
|
referencePrice * (1 + (side === 'buy' ? 1 : -1) * maxSlippage),
|
||||||
|
@ -93,9 +97,9 @@ const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal onClose={onClose} isOpen={isOpen}>
|
<Modal onClose={onClose} isOpen={isOpen}>
|
||||||
<div className="pb-2 text-lg text-th-fgd-1">
|
<h2 className="mb-2">
|
||||||
{t('close-confirm', { config_name: config.name })}
|
{t('close-confirm', { config_name: marketConfig.name })}
|
||||||
</div>
|
</h2>
|
||||||
<div className="pb-6 text-th-fgd-3">{t('price-expect')}</div>
|
<div className="pb-6 text-th-fgd-3">{t('price-expect')}</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Button onClick={handleMarketClose}>
|
<Button onClick={handleMarketClose}>
|
||||||
|
|
|
@ -390,6 +390,7 @@ export default function MarketPosition() {
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setShowMarketCloseModal(true)}
|
onClick={() => setShowMarketCloseModal(true)}
|
||||||
className="mt-2.5 w-full"
|
className="mt-2.5 w-full"
|
||||||
|
primary={false}
|
||||||
>
|
>
|
||||||
<span>{t('market-close')}</span>
|
<span>{t('market-close')}</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -399,8 +400,7 @@ export default function MarketPosition() {
|
||||||
<MarketCloseModal
|
<MarketCloseModal
|
||||||
isOpen={showMarketCloseModal}
|
isOpen={showMarketCloseModal}
|
||||||
onClose={handleCloseWarning}
|
onClose={handleCloseWarning}
|
||||||
market={selectedMarket}
|
position={{ marketConfig: marketConfig, perpMarket: selectedMarket }}
|
||||||
marketIndex={marketIndex}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -143,7 +143,7 @@ const DesktopTable = ({
|
||||||
)} $${order.triggerPrice.toFixed(2)}`}
|
)} $${order.triggerPrice.toFixed(2)}`}
|
||||||
</Td>
|
</Td>
|
||||||
<Td className="w-[14.286%]">
|
<Td className="w-[14.286%]">
|
||||||
<div className={`flex justify-end space-x-3`}>
|
<div className={`flex justify-end space-x-2`}>
|
||||||
{editOrderIndex !== index ? (
|
{editOrderIndex !== index ? (
|
||||||
<>
|
<>
|
||||||
{!order.perpTrigger ? (
|
{!order.perpTrigger ? (
|
||||||
|
@ -157,6 +157,7 @@ const DesktopTable = ({
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleCancelOrder(order, market.account)}
|
onClick={() => handleCancelOrder(order, market.account)}
|
||||||
className="-my-1 h-7 pt-0 pb-0 pl-3 pr-3 text-xs"
|
className="-my-1 h-7 pt-0 pb-0 pl-3 pr-3 text-xs"
|
||||||
|
primary={false}
|
||||||
>
|
>
|
||||||
{cancelledOrderId + '' === order.orderId + '' ? (
|
{cancelledOrderId + '' === order.orderId + '' ? (
|
||||||
<Loading />
|
<Loading />
|
||||||
|
@ -170,6 +171,7 @@ const DesktopTable = ({
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleCancelAllOrders(market.account)}
|
onClick={() => handleCancelAllOrders(market.account)}
|
||||||
className="-my-1 h-7 pt-0 pb-0 pl-3 pr-3 text-xs text-th-red"
|
className="-my-1 h-7 pt-0 pb-0 pl-3 pr-3 text-xs text-th-red"
|
||||||
|
primary={false}
|
||||||
>
|
>
|
||||||
{t('cancel-all') + ' ' + market.config.name}
|
{t('cancel-all') + ' ' + market.config.name}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -335,9 +337,7 @@ const MobileTable = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
className={`${
|
className={`mt-4 h-8 pt-0 pb-0 pl-3 pr-3 text-xs`}
|
||||||
index % 2 === 0 ? 'bg-th-bkg-4' : 'bg-th-bkg-3'
|
|
||||||
} mt-4 h-8 pt-0 pb-0 pl-3 pr-3 text-xs`}
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleModifyOrder(
|
handleModifyOrder(
|
||||||
order,
|
order,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useTranslation } from 'next-i18next'
|
||||||
import { ExclamationIcon } from '@heroicons/react/solid'
|
import { ExclamationIcon } from '@heroicons/react/solid'
|
||||||
import { ZERO_I80F48 } from '@blockworks-foundation/mango-client'
|
import { ZERO_I80F48 } from '@blockworks-foundation/mango-client'
|
||||||
import useMangoStore from '../stores/useMangoStore'
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
import { LinkButton } from '../components/Button'
|
import Button, { LinkButton } from '../components/Button'
|
||||||
import { useViewport } from '../hooks/useViewport'
|
import { useViewport } from '../hooks/useViewport'
|
||||||
import { breakpoints } from './TradePageGrid'
|
import { breakpoints } from './TradePageGrid'
|
||||||
import { ExpandableRow, Table, Td, Th, TrBody, TrHead } from './TableElements'
|
import { ExpandableRow, Table, Td, Th, TrBody, TrHead } from './TableElements'
|
||||||
|
@ -33,6 +33,7 @@ const PositionsTable: React.FC = () => {
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const [showShareModal, setShowShareModal] = useState(false)
|
const [showShareModal, setShowShareModal] = useState(false)
|
||||||
const [showMarketCloseModal, setShowMarketCloseModal] = useState(false)
|
const [showMarketCloseModal, setShowMarketCloseModal] = useState(false)
|
||||||
|
const [positionToClose, setPositionToClose] = useState<any>(null)
|
||||||
const [positionToShare, setPositionToShare] = useState<any>(null)
|
const [positionToShare, setPositionToShare] = useState<any>(null)
|
||||||
const [settleSinglePos, setSettleSinglePos] = useState(null)
|
const [settleSinglePos, setSettleSinglePos] = useState(null)
|
||||||
|
|
||||||
|
@ -61,8 +62,20 @@ const PositionsTable: React.FC = () => {
|
||||||
}
|
}
|
||||||
}, [openPositions])
|
}, [openPositions])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (positionToClose) {
|
||||||
|
const updatedPosition = openPositions.find(
|
||||||
|
(p) => p.marketConfig === positionToClose.marketConfig
|
||||||
|
)
|
||||||
|
if (updatedPosition) {
|
||||||
|
setPositionToClose(updatedPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [openPositions])
|
||||||
|
|
||||||
const handleCloseWarning = useCallback(() => {
|
const handleCloseWarning = useCallback(() => {
|
||||||
setShowMarketCloseModal(false)
|
setShowMarketCloseModal(false)
|
||||||
|
setPositionToClose(null)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleSizeClick = (size, indexPrice) => {
|
const handleSizeClick = (size, indexPrice) => {
|
||||||
|
@ -87,6 +100,11 @@ const PositionsTable: React.FC = () => {
|
||||||
setShowShareModal(true)
|
setShowShareModal(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleShowMarketCloseModal = (position) => {
|
||||||
|
setPositionToClose(position)
|
||||||
|
setShowMarketCloseModal(true)
|
||||||
|
}
|
||||||
|
|
||||||
const handleSettlePnl = async (perpMarket, perpAccount, index) => {
|
const handleSettlePnl = async (perpMarket, perpAccount, index) => {
|
||||||
if (wallet) {
|
if (wallet) {
|
||||||
setSettleSinglePos(index)
|
setSettleSinglePos(index)
|
||||||
|
@ -177,7 +195,6 @@ const PositionsTable: React.FC = () => {
|
||||||
<Th>{t('market')}</Th>
|
<Th>{t('market')}</Th>
|
||||||
<Th>{t('side')}</Th>
|
<Th>{t('side')}</Th>
|
||||||
<Th>{t('position-size')}</Th>
|
<Th>{t('position-size')}</Th>
|
||||||
<Th>{t('notional-size')}</Th>
|
|
||||||
<Th>{t('average-entry')}</Th>
|
<Th>{t('average-entry')}</Th>
|
||||||
<Th>{t('break-even')}</Th>
|
<Th>{t('break-even')}</Th>
|
||||||
<Th>{t('estimated-liq-price')}</Th>
|
<Th>{t('estimated-liq-price')}</Th>
|
||||||
|
@ -260,15 +277,18 @@ const PositionsTable: React.FC = () => {
|
||||||
handleSizeClick(basePosition, indexPrice)
|
handleSizeClick(basePosition, indexPrice)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{`${basePositionUi} ${marketConfig.baseSymbol}`}
|
{`${basePositionUi} (${formatUsdValue(
|
||||||
|
Math.abs(notionalSize)
|
||||||
|
)})`}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span>
|
<span>
|
||||||
{`${basePositionUi} ${marketConfig.baseSymbol}`}
|
{`${basePositionUi} (${formatUsdValue(
|
||||||
|
Math.abs(notionalSize)
|
||||||
|
)})`}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
<Td>{formatUsdValue(Math.abs(notionalSize))}</Td>
|
|
||||||
<Td>
|
<Td>
|
||||||
{avgEntryPrice
|
{avgEntryPrice
|
||||||
? formatUsdValue(avgEntryPrice)
|
? formatUsdValue(avgEntryPrice)
|
||||||
|
@ -322,23 +342,28 @@ const PositionsTable: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<LinkButton
|
<div className="flex items-center space-x-3">
|
||||||
onClick={() =>
|
<Button
|
||||||
handleShowShare(openPositions[index])
|
className="h-8 pt-0 pb-0 pl-3 pr-3 text-xs"
|
||||||
}
|
primary={false}
|
||||||
disabled={!avgEntryPrice ? true : false}
|
onClick={() =>
|
||||||
>
|
handleShowMarketCloseModal(
|
||||||
<TwitterIcon className="h-4 w-4" />
|
openPositions[index]
|
||||||
</LinkButton>
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('market-close')}
|
||||||
|
</Button>
|
||||||
|
<LinkButton
|
||||||
|
onClick={() =>
|
||||||
|
handleShowShare(openPositions[index])
|
||||||
|
}
|
||||||
|
disabled={!avgEntryPrice ? true : false}
|
||||||
|
>
|
||||||
|
<TwitterIcon className="h-4 w-4" />
|
||||||
|
</LinkButton>
|
||||||
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
{showMarketCloseModal ? (
|
|
||||||
<MarketCloseModal
|
|
||||||
isOpen={showMarketCloseModal}
|
|
||||||
onClose={handleCloseWarning}
|
|
||||||
market={perpMarket}
|
|
||||||
marketIndex={marketConfig.marketIndex}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</TrBody>
|
</TrBody>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -489,6 +514,19 @@ const PositionsTable: React.FC = () => {
|
||||||
? usdFormatter(liquidationPrice)
|
? usdFormatter(liquidationPrice)
|
||||||
: 'N/A'}
|
: 'N/A'}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-span-2">
|
||||||
|
<Button
|
||||||
|
className="w-full"
|
||||||
|
primary={false}
|
||||||
|
onClick={() =>
|
||||||
|
handleShowMarketCloseModal(
|
||||||
|
openPositions[index]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('market-close')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -513,6 +551,13 @@ const PositionsTable: React.FC = () => {
|
||||||
position={positionToShare!}
|
position={positionToShare!}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
{showMarketCloseModal ? (
|
||||||
|
<MarketCloseModal
|
||||||
|
isOpen={showMarketCloseModal}
|
||||||
|
onClose={handleCloseWarning}
|
||||||
|
position={positionToClose!}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ const RedeemButtons: React.FC = () => {
|
||||||
<Button
|
<Button
|
||||||
className="flex h-7 items-center justify-center pt-0 pb-0 pl-3 pr-3 text-xs"
|
className="flex h-7 items-center justify-center pt-0 pb-0 pl-3 pr-3 text-xs"
|
||||||
onClick={handleSettleAll}
|
onClick={handleSettleAll}
|
||||||
|
primary={false}
|
||||||
>
|
>
|
||||||
{settling ? <Loading /> : t('redeem-all')}
|
{settling ? <Loading /> : t('redeem-all')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
createRef,
|
createRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
// import { getWeights, MarketConfig } from '@blockworks-foundation/mango-client'
|
|
||||||
import { MarketConfig } from '@blockworks-foundation/mango-client'
|
import { MarketConfig } from '@blockworks-foundation/mango-client'
|
||||||
|
|
||||||
import useMangoStore from '../stores/useMangoStore'
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
|
|
Loading…
Reference in New Issue