import { useCallback, useMemo, useState } from 'react' import { ElementTitle } from './styles' import useMangoStore from '../stores/useMangoStore' import { formatUsdValue, getPrecisionDigits, roundPerpSize } from '../utils' import Button, { LinkButton } from './Button' import Tooltip from './Tooltip' import PerpSideBadge from './PerpSideBadge' import { getMarketIndexBySymbol, MangoAccount, PerpAccount, PerpMarket, QUOTE_INDEX, } from '@blockworks-foundation/mango-client' import { notify } from '../utils/notifications' import MarketCloseModal from './MarketCloseModal' import PnlText from './PnlText' import Loading from './Loading' import { useViewport } from '../hooks/useViewport' import { breakpoints } from './TradePageGrid' import { useTranslation } from 'next-i18next' import useMangoAccount from '../hooks/useMangoAccount' import { useWallet, Wallet } from '@solana/wallet-adapter-react' import { useRouter } from 'next/router' export const settlePosPnl = async ( perpMarkets: PerpMarket[], t, mangoAccounts: MangoAccount[] | undefined, wallet: Wallet ) => { const mangoAccount = useMangoStore.getState().selectedMangoAccount.current const mangoGroup = useMangoStore.getState().selectedMangoGroup.current const mangoCache = useMangoStore.getState().selectedMangoGroup.cache const actions = useMangoStore.getState().actions const mangoClient = useMangoStore.getState().connection.client const rootBankAccount = mangoGroup?.rootBankAccounts[QUOTE_INDEX] if (!mangoGroup || !mangoCache || !mangoAccount || !rootBankAccount) return try { const txids = await mangoClient.settlePosPnl( mangoGroup, mangoCache, mangoAccount, perpMarkets, rootBankAccount, wallet?.adapter, mangoAccounts ) actions.reloadMangoAccount() // FIXME: Remove filter when settlePosPnl return type is undefined or string[] const filteredTxids = txids?.filter( (x) => typeof x === 'string' ) as string[] if (filteredTxids) { for (const txid of filteredTxids) { notify({ title: t('pnl-success'), description: '', txid, }) } } else { notify({ title: t('pnl-error'), description: t('transaction-failed'), type: 'error', }) } } catch (e) { console.log('Error settling PNL: ', `${e}`) notify({ title: t('pnl-error'), description: e.message, txid: e.txid, type: 'error', }) } } export async function settleAllPnl( perpMarkets: PerpMarket[], t, mangoAccounts: MangoAccount[] | undefined, wallet: Wallet ) { const mangoAccount = useMangoStore.getState().selectedMangoAccount.current const mangoGroup = useMangoStore.getState().selectedMangoGroup.current const mangoCache = useMangoStore.getState().selectedMangoGroup.cache const actions = useMangoStore.getState().actions const mangoClient = useMangoStore.getState().connection.client const rootBankAccount = mangoGroup?.rootBankAccounts[QUOTE_INDEX] if (!mangoGroup || !mangoCache || !mangoAccount || !rootBankAccount) return try { const txids = await mangoClient.settleAllPerpPnl( mangoGroup, mangoCache, mangoAccount, perpMarkets, rootBankAccount, wallet?.adapter, mangoAccounts ) actions.reloadMangoAccount() const filteredTxids = txids?.filter((x) => x !== null) as string[] if (filteredTxids) { for (const txid of filteredTxids) { notify({ title: t('pnl-success'), description: '', txid, }) } } else { notify({ title: t('pnl-error'), description: t('transaction-failed'), type: 'error', }) } } catch (e) { console.log('Error settling PNL: ', `${e}`) notify({ title: t('pnl-error'), description: e.message, txid: e.txid, type: 'error', }) } } export const settlePnl = async ( perpMarket: PerpMarket, perpAccount: PerpAccount, t, mangoAccounts: MangoAccount[] | undefined, wallet: Wallet ) => { const mangoAccount = useMangoStore.getState().selectedMangoAccount.current const mangoGroup = useMangoStore.getState().selectedMangoGroup.current const mangoCache = useMangoStore.getState().selectedMangoGroup.cache const actions = useMangoStore.getState().actions const marketIndex = mangoGroup?.getPerpMarketIndex(perpMarket.publicKey) const mangoClient = useMangoStore.getState().connection.client const rootBank = mangoGroup?.rootBankAccounts[QUOTE_INDEX] if ( !rootBank || !mangoGroup || !mangoCache || !mangoAccount || typeof marketIndex !== 'number' ) return try { const txid = await mangoClient.settlePnl( mangoGroup, mangoCache, mangoAccount, perpMarket, rootBank, mangoCache.priceCache[marketIndex].price, wallet?.adapter, mangoAccounts ) actions.reloadMangoAccount() if (txid) { notify({ title: t('pnl-success'), description: '', txid, }) } else { notify({ title: t('pnl-error'), description: t('transaction-failed'), type: 'error', }) } } catch (e) { console.log('Error settling PNL: ', `${e}`, `${perpAccount}`) notify({ title: t('pnl-error'), description: e.message, txid: e.txid, type: 'error', }) } } export default function MarketPosition() { const { t } = useTranslation('common') const { wallet, connected } = useWallet() const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current) const mangoGroupConfig = useMangoStore((s) => s.selectedMangoGroup.config) const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache) const { mangoAccount, initialLoad } = useMangoAccount() const selectedMarket = useMangoStore((s) => s.selectedMarket.current) const marketConfig = useMangoStore((s) => s.selectedMarket.config) const setMangoStore = useMangoStore((s) => s.set) const price = useMangoStore((s) => s.tradeForm.price) const perpAccounts = useMangoStore((s) => s.selectedMangoAccount.perpAccounts) const baseSymbol = marketConfig.baseSymbol const marketName = marketConfig.name const router = useRouter() const { pubkey } = router.query const [showMarketCloseModal, setShowMarketCloseModal] = useState(false) const [settling, setSettling] = useState(false) const { width } = useViewport() const isMobile = width ? width < breakpoints.sm : false const marketIndex = useMemo(() => { return getMarketIndexBySymbol(mangoGroupConfig, baseSymbol) }, [mangoGroupConfig, baseSymbol]) let perpAccount if (marketName.includes('PERP') && mangoAccount) { perpAccount = mangoAccount.perpAccounts[marketIndex] } const handleSizeClick = (size) => { if (!mangoGroup || !mangoCache || !selectedMarket) return const sizePrecisionDigits = getPrecisionDigits(selectedMarket.minOrderSize) const priceOrDefault = price ? price : mangoGroup.getPriceUi(marketIndex, mangoCache) const roundedSize = parseFloat(Math.abs(size).toFixed(sizePrecisionDigits)) const quoteSize = parseFloat((roundedSize * priceOrDefault).toFixed(2)) setMangoStore((state) => { state.tradeForm.baseSize = roundedSize state.tradeForm.quoteSize = quoteSize state.tradeForm.side = size > 0 ? 'sell' : 'buy' }) } const handleCloseWarning = useCallback(() => { setShowMarketCloseModal(false) }, []) const handleSettlePnl = (perpMarket, perpAccount) => { if (wallet) { setSettling(true) settlePnl(perpMarket, perpAccount, t, undefined, wallet).then(() => { setSettling(false) }) } } if (!mangoGroup || !selectedMarket || !(selectedMarket instanceof PerpMarket)) return null const { basePosition = 0, avgEntryPrice = 0, breakEvenPrice = 0, notionalSize = 0, unsettledPnl = 0, } = perpAccounts.length ? perpAccounts.find((pa) => pa.perpMarket.publicKey.equals(selectedMarket.publicKey) ) : {} function SettlePnlTooltip() { return (