import { useEffect, useCallback, useMemo, useState } from 'react' import { Table, Thead, Tbody, Tr, Th, Td } from 'react-super-responsive-table' import styled from '@emotion/styled' import { Menu } from '@headlessui/react' import Link from 'next/link' import { ChartBarIcon, CurrencyDollarIcon, ExclamationIcon, DotsHorizontalIcon, GiftIcon, HeartIcon, XIcon, } from '@heroicons/react/outline' import { ArrowSmDownIcon } from '@heroicons/react/solid' import { getTokenBySymbol, nativeToUi, ZERO_BN, I80F48, } from '@blockworks-foundation/mango-client' import useMangoStore, { mangoClient, MNGO_INDEX, } from '../../stores/useMangoStore' import { useBalances } from '../../hooks/useBalances' import { useSortableData } from '../../hooks/useSortableData' import useLocalStorageState from '../../hooks/useLocalStorageState' import { sleep, tokenPrecision, formatUsdValue } from '../../utils' import { notify } from '../../utils/notifications' import { Market } from '@project-serum/serum' import SideBadge from '../SideBadge' import Button, { LinkButton } from '../Button' import Switch from '../Switch' import PositionsTable from '../PerpPositionsTable' import DepositModal from '../DepositModal' import WithdrawModal from '../WithdrawModal' const StyledAccountValue = styled.div` font-size: 1.8rem; line-height: 1.2; ` export default function AccountOverview() { const [spotPortfolio, setSpotPortfolio] = useState([]) const [unsettled, setUnsettled] = useState([]) const [filteredSpotPortfolio, setFilteredSpotPortfolio] = useState([]) const [showZeroBalances, setShowZeroBalances] = useLocalStorageState( 'showZeroAccountBalances', false ) const [showDepositModal, setShowDepositModal] = useState(false) const [showWithdrawModal, setShowWithdrawModal] = useState(false) const [actionSymbol, setActionSymbol] = useState('') const balances = useBalances() const actions = useMangoStore((s) => s.actions) const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config) const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current) const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current) const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache) const { items, requestSort, sortConfig } = useSortableData( filteredSpotPortfolio ) useEffect(() => { const spotPortfolio = [] const unsettled = [] balances.forEach((b) => { const token = getTokenBySymbol(groupConfig, b.symbol) const tokenIndex = mangoGroup.getTokenIndex(token.mintKey) if (+b.marginDeposits > 0 || b.orders > 0) { spotPortfolio.push({ market: b.symbol, balance: +b.marginDeposits + b.orders + b.unsettled, borrowRate: mangoGroup .getBorrowRate(tokenIndex) .mul(I80F48.fromNumber(100)), depositRate: mangoGroup .getDepositRate(tokenIndex) .mul(I80F48.fromNumber(100)), price: mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(), symbol: b.symbol, value: (+b.marginDeposits + b.orders + b.unsettled) * mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(), type: 'Deposit', }) } else if (+b.borrows > 0) { spotPortfolio.push({ market: b.symbol, balance: +b.borrows, borrowRate: mangoGroup .getBorrowRate(tokenIndex) .mul(I80F48.fromNumber(100)), depositRate: mangoGroup .getDepositRate(tokenIndex) .mul(I80F48.fromNumber(100)), price: mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(), symbol: b.symbol, value: b.borrows.mul(mangoGroup.getPrice(tokenIndex, mangoCache)), type: 'Borrow', }) } else { spotPortfolio.push({ market: b.symbol, balance: 0, borrowRate: mangoGroup .getBorrowRate(tokenIndex) .mul(I80F48.fromNumber(100)), depositRate: mangoGroup .getDepositRate(tokenIndex) .mul(I80F48.fromNumber(100)), price: mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(), symbol: b.symbol, value: 0, type: '–', }) } if (b.unsettled > 0) { unsettled.push({ market: b.symbol, balance: b.unsettled, symbol: b.symbol, value: b.unsettled * mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(), }) } }) setSpotPortfolio(spotPortfolio.sort((a, b) => b.value - a.value)) setFilteredSpotPortfolio( !showZeroBalances ? spotPortfolio .filter((pos) => pos.balance > 0) .sort((a, b) => b.value - a.value) : spotPortfolio.sort((a, b) => b.value - a.value) ) setUnsettled(unsettled) }, [mangoAccount]) const maintHealthRatio = useMemo(() => { return mangoAccount ? mangoAccount.getHealthRatio(mangoGroup, mangoCache, 'Maint') : 100 }, [mangoAccount, mangoGroup, mangoCache]) const initHealthRatio = useMemo(() => { return mangoAccount ? mangoAccount.getHealthRatio(mangoGroup, mangoCache, 'Init') : 100 }, [mangoAccount, mangoGroup, mangoCache]) const mngoAccrued = useMemo(() => { return mangoAccount ? mangoAccount.perpAccounts.reduce((acc, perpAcct) => { return perpAcct.mngoAccrued.add(acc) }, ZERO_BN) : ZERO_BN }, [mangoAccount]) const handleShowZeroBalances = (checked) => { if (checked) { setFilteredSpotPortfolio(spotPortfolio) } else { setFilteredSpotPortfolio(spotPortfolio.filter((pos) => pos.balance > 0)) } setShowZeroBalances(checked) } async function handleSettleAll() { const mangoAccount = useMangoStore.getState().selectedMangoAccount.current const mangoGroup = useMangoStore.getState().selectedMangoGroup.current const markets = useMangoStore.getState().selectedMangoGroup.markets const wallet = useMangoStore.getState().wallet.current try { const spotMarkets = Object.values(markets).filter( (mkt) => mkt instanceof Market ) as Market[] // @ts-ignore await mangoClient.settleAll(mangoGroup, mangoAccount, spotMarkets, wallet) notify({ title: 'Successfully settled funds' }) await sleep(250) actions.fetchMangoAccounts() } 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', }) } } } const handleRedeemMngo = async () => { const wallet = useMangoStore.getState().wallet.current const mngoNodeBank = mangoGroup.rootBankAccounts[MNGO_INDEX].nodeBankAccounts[0] try { const txid = await mangoClient.redeemAllMngo( mangoGroup, mangoAccount, wallet, mangoGroup.tokens[MNGO_INDEX].rootBank, mngoNodeBank.publicKey, mngoNodeBank.vault ) actions.fetchMangoAccounts() notify({ title: 'Successfully redeemed MNGO', description: '', txid, }) } catch (e) { notify({ title: 'Error redeeming MNGO', description: e.message, txid: e.txid, type: 'error', }) } } const handleOpenDepositModal = useCallback((symbol) => { setActionSymbol(symbol) setShowDepositModal(true) }, []) const handleOpenWithdrawModal = useCallback((symbol) => { setActionSymbol(symbol) setShowWithdrawModal(true) }, []) return mangoAccount ? ( <>
|
|
|
|
|
|
|
|
---|---|---|---|---|---|---|---|
{pos.market}
|
{pos.type === 'Long' || pos.type === 'Short' ? (
|
{pos.balance > 0 ? pos.balance.toFixed(tokenPrecision[pos.symbol]) : 0} | {formatUsdValue(pos.price)} | {formatUsdValue(pos.value)} | {pos.depositRate.toFixed(2)}% | {pos.borrowRate.toFixed(2)}% |