import React, { Fragment, useCallback, useEffect, useMemo, useState, } from 'react' import { BellIcon, CurrencyDollarIcon, DuplicateIcon, ExclamationCircleIcon, GiftIcon, LinkIcon, PencilIcon, SwitchHorizontalIcon, TrashIcon, UsersIcon, } from '@heroicons/react/outline' import { ChevronDownIcon } from '@heroicons/react/solid' import { nativeToUi, ZERO_BN } from '@blockworks-foundation/mango-client' import useMangoStore, { serumProgramId, MNGO_INDEX } from 'stores/useMangoStore' import PageBodyContainer from 'components/PageBodyContainer' import TopBar from 'components/TopBar' import AccountOrders from 'components/account_page/AccountOrders' import AccountHistory from 'components/account_page/AccountHistory' import AccountsModal from 'components/AccountsModal' import AccountOverview from 'components/account_page/AccountOverview' import AccountInterest from 'components/account_page/AccountInterest' import AccountFunding from 'components/account_page/AccountFunding' import AccountPerformancePerToken from 'components/account_page/AccountPerformancePerToken' import AccountNameModal from 'components/AccountNameModal' import { IconButton, LinkButton } from 'components/Button' import EmptyState from 'components/EmptyState' import Loading from 'components/Loading' import Swipeable from 'components/mobile/Swipeable' import Tabs from 'components/Tabs' import { useViewport } from 'hooks/useViewport' import { breakpoints } from 'components/TradePageGrid' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { useTranslation } from 'next-i18next' import { useRouter } from 'next/router' import { PublicKey } from '@solana/web3.js' import CloseAccountModal from 'components/CloseAccountModal' import { notify } from 'utils/notifications' import { actionsSelector, mangoAccountSelector, mangoGroupSelector, } from 'stores/selectors' import CreateAlertModal from 'components/CreateAlertModal' import { copyToClipboard } from 'utils' import DelegateModal from 'components/DelegateModal' import { Menu, Transition } from '@headlessui/react' import { useWallet } from '@solana/wallet-adapter-react' import { handleWalletConnect } from 'components/ConnectWalletButton' import { MangoAccountLookup } from 'components/account_page/MangoAccountLookup' import NftProfilePicModal from 'components/NftProfilePicModal' import ProfileImage from 'components/ProfileImage' export async function getStaticProps({ locale }) { return { props: { ...(await serverSideTranslations(locale, [ 'common', 'close-account', 'delegate', 'alerts', 'account-performance', 'share-modal', 'profile', ])), // Will be passed to the page component as props }, } } const TABS = [ 'Portfolio', 'Orders', 'History', 'Interest', 'Funding', 'Performance', ] export default function Account() { const { t } = useTranslation(['common', 'close-account', 'delegate']) const { width } = useViewport() const router = useRouter() const { connected, wallet, publicKey } = useWallet() const isLoading = useMangoStore((s) => s.selectedMangoAccount.initialLoad) const mangoAccount = useMangoStore(mangoAccountSelector) const mangoGroup = useMangoStore(mangoGroupSelector) const actions = useMangoStore(actionsSelector) const setMangoStore = useMangoStore((s) => s.set) const [showAccountsModal, setShowAccountsModal] = useState(false) const [showNameModal, setShowNameModal] = useState(false) const [showCloseAccountModal, setShowCloseAccountModal] = useState(false) const [showAlertsModal, setShowAlertsModal] = useState(false) const [showDelegateModal, setShowDelegateModal] = useState(false) const [isCopied, setIsCopied] = useState(false) const [resetOnLeave, setResetOnLeave] = useState(false) const [mngoAccrued, setMngoAccrued] = useState(ZERO_BN) const [viewIndex, setViewIndex] = useState(0) const [activeTab, setActiveTab] = useState(TABS[0]) const [showProfilePicModal, setShowProfilePicModal] = useState(false) const loadingTransaction = useMangoStore( (s) => s.wallet.nfts.loadingTransaction ) const connecting = wallet?.adapter?.connecting const isMobile = width ? width < breakpoints.sm : false const { pubkey } = router.query const isDelegatedAccount = publicKey ? !mangoAccount?.owner?.equals(publicKey) : false const handleCloseAlertModal = useCallback(() => { setShowAlertsModal(false) }, []) const handleCloseAccounts = useCallback(() => { setShowAccountsModal(false) }, []) const handleCloseNameModal = useCallback(() => { setShowNameModal(false) }, []) const handleCloseCloseAccountModal = useCallback(() => { setShowCloseAccountModal(false) }, []) const handleCloseDelegateModal = useCallback(() => { setShowDelegateModal(false) }, []) const handleCloseProfilePicModal = useCallback(() => { setShowProfilePicModal(false) }, []) const handleConnect = useCallback(() => { if (wallet) { handleWalletConnect(wallet) } }, [wallet]) useEffect(() => { async function loadUnownedMangoAccount() { try { if (!pubkey) { return } const unownedMangoAccountPubkey = new PublicKey(pubkey) const mangoClient = useMangoStore.getState().connection.client if (mangoGroup) { const unOwnedMangoAccount = await mangoClient.getMangoAccount( unownedMangoAccountPubkey, serumProgramId ) setMangoStore((state) => { state.selectedMangoAccount.current = unOwnedMangoAccount state.selectedMangoAccount.initialLoad = false }) actions.fetchTradeHistory() setResetOnLeave(true) } } catch (error) { console.log('error', error) router.push('/account') } } if (pubkey) { setMangoStore((state) => { state.selectedMangoAccount.initialLoad = true }) loadUnownedMangoAccount() } }, [pubkey, mangoGroup]) useEffect(() => { const handleRouteChange = () => { if (resetOnLeave) { setMangoStore((state) => { state.selectedMangoAccount.current = null }) } } router.events.on('routeChangeStart', handleRouteChange) return () => { router.events.off('routeChangeStart', handleRouteChange) } }, [resetOnLeave]) useEffect(() => { if (isCopied) { const timer = setTimeout(() => { setIsCopied(false) }, 1500) return () => clearTimeout(timer) } }, [isCopied]) const handleCopyAddress = (address) => { setIsCopied(true) copyToClipboard(address) } const handleChangeViewIndex = (index) => { setViewIndex(index) } const handleTabChange = (tabName) => { setActiveTab(tabName) setViewIndex(TABS.findIndex((t) => t === tabName)) } useMemo(() => { setMngoAccrued( mangoAccount ? mangoAccount.perpAccounts.reduce((acc, perpAcct) => { return perpAcct.mngoAccrued.add(acc) }, ZERO_BN) : ZERO_BN ) }, [mangoAccount]) useEffect(() => { if (connecting) { router.push('/account') } }, [connecting, router]) const handleRedeemMngo = async () => { const mangoClient = useMangoStore.getState().connection.client const mngoNodeBank = mangoGroup?.rootBankAccounts?.[MNGO_INDEX]?.nodeBankAccounts?.[0] if (!mangoAccount || !mngoNodeBank || !mangoGroup || !wallet) { return } try { const txids = await mangoClient.redeemAllMngo( mangoGroup, mangoAccount, wallet.adapter, mangoGroup.tokens[MNGO_INDEX].rootBank, mngoNodeBank.publicKey, mngoNodeBank.vault ) actions.reloadMangoAccount() setMngoAccrued(ZERO_BN) if (txids) { for (const txid of txids) { notify({ title: t('redeem-success'), description: '', txid, }) } } else { notify({ title: t('redeem-failure'), description: t('transaction-failed'), type: 'error', }) } } catch (e) { notify({ title: t('redeem-failure'), description: e.message, txid: e.txid, type: 'error', }) } } return (