import { useCallback } from 'react' import { useBalances } from '../hooks/useBalances' import useMangoStore from '../stores/useMangoStore' import Button, { LinkButton } from '../components/Button' import { notify } from '../utils/notifications' import { ArrowSmDownIcon, ExclamationIcon } from '@heroicons/react/outline' import { Market } from '@project-serum/serum' import { getMarketIndexBySymbol, getTokenBySymbol, } from '@blockworks-foundation/mango-client' import { useState } from 'react' import Loading from './Loading' import { useViewport } from '../hooks/useViewport' import { breakpoints } from './TradePageGrid' import { floorToDecimal, formatUsdValue } from '../utils' import { Table, Td, Th, TrBody, TrHead } from './TableElements' import { useSortableData } from '../hooks/useSortableData' import DepositModal from './DepositModal' import WithdrawModal from './WithdrawModal' import { ExpandableRow } from './TableElements' import MobileTableHeader from './mobile/MobileTableHeader' const BalancesTable = ({ showZeroBalances = false }) => { const [showDepositModal, setShowDepositModal] = useState(false) const [showWithdrawModal, setShowWithdrawModal] = useState(false) const [actionSymbol, setActionSymbol] = useState('') const balances = useBalances() const { items, requestSort, sortConfig } = useSortableData( balances .filter( (bal) => showZeroBalances || +bal.deposits > 0 || +bal.borrows > 0 || bal.orders > 0 || bal.unsettled > 0 ) .sort((a, b) => Math.abs(+b.value) - Math.abs(+a.value)) ) const actions = useMangoStore((s) => s.actions) const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current) const mangoGroupConfig = useMangoStore((s) => s.selectedMangoGroup.config) const mangoClient = useMangoStore((s) => s.connection.client) 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 mangoGroupCache = useMangoStore((s) => s.selectedMangoGroup.cache) const { width } = useViewport() const [submitting, setSubmitting] = useState(false) const isMobile = width ? width < breakpoints.md : false const handleSizeClick = (size, symbol) => { const step = selectedMarket.minOrderSize const marketIndex = getMarketIndexBySymbol( mangoGroupConfig, marketConfig.baseSymbol ) const priceOrDefault = price ? price : mangoGroup.getPrice(marketIndex, mangoGroupCache).toNumber() if (symbol === 'USDC') { const baseSize = Math.floor(size / priceOrDefault / step) * step setMangoStore((state) => { state.tradeForm.baseSize = baseSize state.tradeForm.quoteSize = (baseSize * priceOrDefault).toFixed(2) state.tradeForm.side = 'buy' }) } else { const roundedSize = Math.round(size / step) * step const quoteSize = roundedSize * priceOrDefault setMangoStore((state) => { state.tradeForm.baseSize = roundedSize state.tradeForm.quoteSize = quoteSize.toFixed(2) state.tradeForm.side = 'sell' }) } } const handleOpenDepositModal = useCallback((symbol) => { setActionSymbol(symbol) setShowDepositModal(true) }, []) const handleOpenWithdrawModal = useCallback((symbol) => { setActionSymbol(symbol) setShowWithdrawModal(true) }, []) async function handleSettleAll() { const mangoAccount = useMangoStore.getState().selectedMangoAccount.current const markets = useMangoStore.getState().selectedMangoGroup.markets const wallet = useMangoStore.getState().wallet.current try { setSubmitting(true) const spotMarkets = Object.values(markets).filter( (mkt) => mkt instanceof Market ) as Market[] await mangoClient.settleAll(mangoGroup, mangoAccount, spotMarkets, wallet) notify({ title: 'Successfully settled funds' }) } catch (e) { console.warn('Error settling all:', e) if (e.message === 'No unsettled funds') { notify({ title: 'There are no unsettled funds', type: 'error', }) } else { notify({ title: 'Error settling funds', description: e.message, txid: e.txid, type: 'error', }) } } finally { await actions.reloadMangoAccount() setSubmitting(false) } } const unsettledBalances = balances.filter((bal) => bal.unsettled > 0) return (
{unsettledBalances.length > 0 ? (
Unsettled Balances
{unsettledBalances.map((bal) => { const tokenConfig = getTokenBySymbol(mangoGroupConfig, bal.symbol) return (
{bal.symbol}
{`${floorToDecimal(bal.unsettled, tokenConfig.decimals)} ${ bal.symbol }`}
) })}
) : null}
{items.length > 0 ? ( !isMobile ? ( {items.map((balance, index) => ( ))} {showDepositModal && ( setShowDepositModal(false)} tokenSymbol={actionSymbol} /> )} {showWithdrawModal && ( setShowWithdrawModal(false)} tokenSymbol={actionSymbol} /> )}
requestSort('symbol')} > Asset requestSort('deposits')} > Deposits requestSort('borrows')} > Borrows requestSort('orders')} > In Orders requestSort('unsettled')} > Unsettled requestSort('net')} > Net Balance requestSort('value')} > Value requestSort('depositRate')} > Deposit Rate requestSort('borrowRate')} > Borrow Rate
{balance.symbol}
{balance.deposits.toFixed()} {balance.borrows.toFixed()} {balance.orders} {balance.unsettled} {marketConfig.kind === 'spot' && marketConfig.name.includes(balance.symbol) && selectedMarket ? ( 0 ? 'cursor-pointer underline hover:no-underline' : '' } onClick={() => handleSizeClick(balance.net, balance.symbol) } > {balance.net.toFixed()} ) : ( balance.net.toFixed() )} {formatUsdValue(balance.value)} {balance.depositRate.toFixed(2)}% {balance.borrowRate.toFixed(2)}%
) : ( <>
Asset
Net Balance
} /> {items.map((balance, index) => (
{balance.symbol}
{balance.net.toFixed()}
} key={`${balance.symbol}${index}`} index={index} panelTemplate={ <>
Deposits
{balance.deposits.toFixed()}
Borrows
{balance.borrows.toFixed()}
In Orders
{balance.orders.toFixed()}
Unsettled
{balance.unsettled.toFixed()}
Value
{formatUsdValue(balance.value)}
Deposit/Borrow Rates
{balance.depositRate.toFixed(2)}% / {balance.borrowRate.toFixed(2)}%
} /> ))} ) ) : (
No balances
)}
) } export default BalancesTable