import { useMemo } from 'react' import FloatingElement from './FloatingElement' import { ElementTitle } from './styles' import useMangoStore, { mangoClient } from '../stores/useMangoStore' import { ceilToDecimal, floorToDecimal, i80f48ToPercent, formatUsdValue, tokenPrecision, } from '../utils/index' import { LinkButton } from './Button' import Tooltip from './Tooltip' import SideBadge from './SideBadge' import { getMarketIndexBySymbol, nativeI80F48ToUi, PerpAccount, PerpMarket, QUOTE_INDEX, ZERO_BN, ZERO_I80F48, } from '@blockworks-foundation/mango-client' import useTradeHistory from '../hooks/useTradeHistory' import { getAvgEntryPrice, getBreakEvenPrice } from './PerpPositionsTable' import { notify } from '../utils/notifications' const handleSettlePnl = async ( perpMarket: PerpMarket, perpAccount: PerpAccount ) => { const mangoAccount = useMangoStore.getState().selectedMangoAccount.current const mangoGroup = useMangoStore.getState().selectedMangoGroup.current const mangoCache = useMangoStore.getState().selectedMangoGroup.cache const wallet = useMangoStore.getState().wallet.current const actions = useMangoStore.getState().actions const marketIndex = mangoGroup.getPerpMarketIndex(perpMarket.publicKey) try { const txid = await mangoClient.settlePnl( mangoGroup, mangoCache, mangoAccount, perpMarket, mangoGroup.rootBankAccounts[QUOTE_INDEX], mangoCache.priceCache[marketIndex].price, wallet ) actions.fetchMangoAccounts() notify({ title: 'Successfully settled PNL', description: '', txid, }) } catch (e) { console.log('Error settling PNL: ', `${e}`, `${perpAccount}`) notify({ title: 'Error settling PNL', description: e.message, txid: e.txid, type: 'error', }) } } export function SettlePnlTooltip() { return (
Settling will update your USDC balance to reflect the unsettled PnL amount.{' '} Learn more
) } export default function MarketPosition() { const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current) const mangoGroupConfig = useMangoStore((s) => s.selectedMangoGroup.config) const mangoGroupCache = useMangoStore((s) => s.selectedMangoGroup.cache) const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current) const selectedMarket = useMangoStore((s) => s.selectedMarket.current) const marketConfig = useMangoStore((s) => s.selectedMarket.config) const connected = useMangoStore((s) => s.wallet.connected) const isLoading = useMangoStore((s) => s.selectedMangoAccount.initialLoad) const setMangoStore = useMangoStore((s) => s.set) const baseSymbol = marketConfig.baseSymbol const marketName = marketConfig.name const tradeHistory = useTradeHistory() const perpTradeHistory = tradeHistory?.filter( (t) => t.marketName === marketName ) const marketIndex = useMemo(() => { return getMarketIndexBySymbol(mangoGroupConfig, baseSymbol) }, [mangoGroupConfig, baseSymbol]) const perpAccount = useMemo(() => { if (marketName.includes('PERP') && mangoAccount) { return mangoAccount.perpAccounts[marketIndex] } }, [marketName, mangoAccount, marketIndex]) const handleSizeClick = (size) => { setMangoStore((state) => { state.tradeForm.baseSize = size }) } if (!mangoGroup) return null return selectedMarket instanceof PerpMarket ? (
Position
Side
{isLoading ? ( ) : perpAccount && !perpAccount.basePosition.eq(ZERO_BN) ? ( ) : ( '--' )}
Position size
{isLoading ? ( ) : perpAccount && Math.abs( selectedMarket.baseLotsToNumber(perpAccount.basePosition) ) > 0 ? ( handleSizeClick( Math.abs( selectedMarket.baseLotsToNumber(perpAccount.basePosition) ) ) } > {`${Math.abs( selectedMarket.baseLotsToNumber(perpAccount.basePosition) )} ${baseSymbol}`} ) : ( `0 ${baseSymbol}` )}
Notional size
{isLoading ? ( ) : perpAccount ? ( formatUsdValue( selectedMarket.baseLotsToNumber(perpAccount.basePosition) * mangoGroup.getPrice(marketIndex, mangoGroupCache).toNumber() ) ) : ( 0 )}
Avg entry price
{isLoading ? ( ) : perpAccount ? ( getAvgEntryPrice( mangoAccount, perpAccount, selectedMarket, perpTradeHistory ) ) : ( 0 )}
Break-even price
{isLoading ? ( ) : perpAccount ? ( getBreakEvenPrice( mangoAccount, perpAccount, selectedMarket, perpTradeHistory ) ) : ( 0 )}
}> Unsettled PnL
{isLoading ? ( ) : perpAccount ? ( formatUsdValue( +nativeI80F48ToUi( perpAccount.getPnl( mangoGroup.perpMarkets[marketIndex], mangoGroupCache.perpMarketCache[marketIndex], mangoGroupCache.priceCache[marketIndex].price ), marketConfig.quoteDecimals ) ) ) : ( '0' )} handleSettlePnl(selectedMarket, perpAccount)} className="ml-2 text-th-primary text-xs disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:underline" disabled={ perpAccount ? perpAccount .getPnl( mangoGroup.perpMarkets[marketIndex], mangoGroupCache.perpMarketCache[marketIndex], mangoGroupCache.priceCache[marketIndex].price ) .eq(ZERO_I80F48) : true } > Settle
) : ( <>
Balances {mangoGroup ? (
{mangoGroupConfig.tokens .filter((t) => t.symbol === baseSymbol || t.symbol === 'USDC') .reverse() .map(({ symbol, mintKey }) => { const tokenIndex = mangoGroup.getTokenIndex(mintKey) const deposit = mangoAccount ? mangoAccount .getUiDeposit( mangoGroupCache.rootBankCache[tokenIndex], mangoGroup, tokenIndex ) .toNumber() : null const borrow = mangoAccount ? mangoAccount .getUiBorrow( mangoGroupCache.rootBankCache[tokenIndex], mangoGroup, tokenIndex ) .toNumber() : null return (
{symbol}
Balance
{isLoading ? ( ) : mangoAccount ? ( deposit > 0 ? ( floorToDecimal( deposit, mangoGroup.tokens[tokenIndex].decimals ).toFixed(tokenPrecision[symbol]) ) : borrow > 0 ? ( `-${ceilToDecimal( borrow, mangoGroup.tokens[tokenIndex].decimals ).toFixed(tokenPrecision[symbol])}` ) : ( 0 ) ) : ( 0 )}
Available Balance
{isLoading ? ( ) : mangoAccount ? ( nativeI80F48ToUi( mangoAccount.getAvailableBalance( mangoGroup, mangoGroupCache, tokenIndex ), mangoGroup.tokens[tokenIndex].decimals ).toFixed(tokenPrecision[symbol]) ) : ( 0 )}
Interest Rates
{i80f48ToPercent( mangoGroup.getDepositRate(tokenIndex) ).toFixed(2)} % {' / '} {i80f48ToPercent( mangoGroup.getBorrowRate(tokenIndex) ).toFixed(2)} %
) })}
) : null}
) } export const DataLoader = () => (
)