import { HealthType, toUiDecimalsForQuote, } from '@blockworks-foundation/mango-v4' import { useTranslation } from 'next-i18next' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { useEffect, useMemo, useState } from 'react' import AccountActions from './AccountActions' import DepositModal from '../modals/DepositModal' import WithdrawModal from '../modals/WithdrawModal' import mangoStore, { PerformanceDataItem } from '@store/mangoStore' import { formatFixedDecimals } from '../../utils/numbers' import FlipNumbers from 'react-flip-numbers' import dynamic from 'next/dynamic' const SimpleAreaChart = dynamic( () => import('@components/shared/SimpleAreaChart'), { ssr: false } ) import { COLORS } from '../../styles/colors' import { useTheme } from 'next-themes' import { IconButton } from '../shared/Button' import { ArrowsPointingOutIcon, ChevronRightIcon, } from '@heroicons/react/20/solid' import { Transition } from '@headlessui/react' import AccountTabs from './AccountTabs' import SheenLoader from '../shared/SheenLoader' import AccountChart from './AccountChart' import useMangoAccount from '../../hooks/useMangoAccount' import Change from '../shared/Change' import Tooltip from '@components/shared/Tooltip' import { ANIMATION_SETTINGS_KEY, IS_ONBOARDED_KEY } from 'utils/constants' import { useWallet } from '@solana/wallet-adapter-react' import useLocalStorageState from 'hooks/useLocalStorageState' import AccountOnboardingTour from '@components/tours/AccountOnboardingTour' import dayjs from 'dayjs' import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' export async function getStaticProps({ locale }: { locale: string }) { return { props: { ...(await serverSideTranslations(locale, [ 'common', 'close-account', 'trade', ])), }, } } const AccountPage = () => { const { t } = useTranslation('common') const { connected } = useWallet() const group = mangoStore.getState().group const { mangoAccount } = useMangoAccount() const actions = mangoStore((s) => s.actions) const loadPerformanceData = mangoStore( (s) => s.mangoAccount.stats.performance.loading ) const performanceData = mangoStore( (s) => s.mangoAccount.stats.performance.data ) const totalInterestData = mangoStore( (s) => s.mangoAccount.stats.interestTotals.data ) const [showDepositModal, setShowDepositModal] = useState(false) const [showWithdrawModal, setShowWithdrawModal] = useState(false) const [chartToShow, setChartToShow] = useState< 'account-value' | 'cumulative-interest-value' | 'pnl' | '' >('') const [oneDayPerformanceData, setOneDayPerformanceData] = useState< PerformanceDataItem[] >([]) const [showExpandChart, setShowExpandChart] = useState(false) const { theme } = useTheme() const tourSettings = mangoStore((s) => s.settings.tours) const [isOnBoarded] = useLocalStorageState(IS_ONBOARDED_KEY) const [animationSettings] = useLocalStorageState( ANIMATION_SETTINGS_KEY, INITIAL_ANIMATION_SETTINGS ) const leverage = useMemo(() => { if (!group || !mangoAccount) return 0 const liabsValue = mangoAccount .getLiabsValue(group, HealthType.init)! .toNumber() const totalCollateral = mangoAccount .getAssetsValue(group, HealthType.init)! .toNumber() if (isNaN(liabsValue / totalCollateral)) { return 0 } else return liabsValue / totalCollateral }, [mangoAccount, group]) useEffect(() => { if (mangoAccount) { const pubKey = mangoAccount.publicKey.toString() actions.fetchAccountPerformance(pubKey, 1) actions.fetchAccountInterestTotals(pubKey) } }, [actions, mangoAccount]) useEffect(() => { if (mangoAccount && performanceData.length && !chartToShow) { setOneDayPerformanceData(performanceData) } }, [mangoAccount, performanceData, chartToShow]) const onHoverMenu = (open: boolean, action: string) => { if ( (!open && action === 'onMouseEnter') || (open && action === 'onMouseLeave') ) { setShowExpandChart(!open) } } const handleShowAccountValueChart = () => { setChartToShow('account-value') setShowExpandChart(false) } const handleHideChart = () => { const set = mangoStore.getState().set set((s) => { s.mangoAccount.stats.performance.data = oneDayPerformanceData }) setChartToShow('') } const accountValue = useMemo(() => { if (!group || !mangoAccount) return 0.0 return toUiDecimalsForQuote(mangoAccount.getEquity(group)!.toNumber()) }, [group, mangoAccount]) const { accountPnl, accountValueChange } = useMemo(() => { if (accountValue && performanceData.length) { return { accountPnl: performanceData[performanceData.length - 1].pnl, accountValueChange: accountValue - performanceData[0].account_equity, } } return { accountPnl: 0, accountValueChange: 0 } }, [accountValue, performanceData]) const oneDayPnlChange = useMemo(() => { if (accountPnl && oneDayPerformanceData.length) { const startDayPnl = oneDayPerformanceData[0].pnl return accountPnl - startDayPnl } return 0.0 }, [accountPnl, oneDayPerformanceData]) const interestTotalValue = useMemo(() => { if (totalInterestData.length) { return totalInterestData.reduce( (a, c) => a + c.borrow_interest_usd + c.deposit_interest_usd, 0 ) } return 0.0 }, [totalInterestData]) const oneDayInterestChange = useMemo(() => { if (oneDayPerformanceData.length && mangoAccount) { const startDayInterest = oneDayPerformanceData[0].borrow_interest_cumulative_usd + oneDayPerformanceData[0].deposit_interest_cumulative_usd const latest = oneDayPerformanceData.length - 1 const endDayInterest = oneDayPerformanceData[latest].borrow_interest_cumulative_usd + oneDayPerformanceData[latest].deposit_interest_cumulative_usd return endDayInterest - startDayInterest } return 0.0 }, [oneDayPerformanceData, mangoAccount]) const maintHealth = useMemo(() => { return group && mangoAccount ? mangoAccount.getHealthRatioUi(group, HealthType.maint) : 0 }, [mangoAccount, group]) const handleChartToShow = ( chartName: 'pnl' | 'cumulative-interest-value' ) => { if ( (chartName === 'cumulative-interest-value' && interestTotalValue > 1) || interestTotalValue < -1 ) { setChartToShow(chartName) } if (chartName === 'pnl' && performanceData.length > 4) { setChartToShow(chartName) } } const latestAccountData = useMemo(() => { if (!accountValue || !performanceData.length) return [] const latestIndex = performanceData.length - 1 return [ { account_equity: accountValue, time: dayjs(Date.now()).toISOString(), borrow_interest_cumulative_usd: performanceData[latestIndex].borrow_interest_cumulative_usd, deposit_interest_cumulative_usd: performanceData[latestIndex].deposit_interest_cumulative_usd, pnl: performanceData[latestIndex].pnl, spot_value: performanceData[latestIndex].spot_value, transfer_balance: performanceData[latestIndex].transfer_balance, }, ] }, [accountValue, performanceData]) return !chartToShow ? ( <>

{t('account-value')}

{animationSettings['number-scroll'] ? ( group && mangoAccount ? ( ) : ( ) ) : ( {formatFixedDecimals(accountValue, true)} )}

{t('today')}

{!loadPerformanceData ? ( mangoAccount && performanceData.length ? (
onHoverMenu(showExpandChart, 'onMouseEnter') } onMouseLeave={() => onHoverMenu(showExpandChart, 'onMouseLeave') } > = 0 ? COLORS.GREEN[theme] : COLORS.RED[theme] } data={performanceData.concat(latestAccountData)} height={88} name="accountValue" width={180} xKey="time" yKey="account_equity" /> handleShowAccountValueChart()} >
) : null ) : (
)}

Health describes how close your account is to liquidation. The lower your account health is the more likely you are to get liquidated when prices fluctuate.

Your account health is {maintHealth}%

Scenario:{' '} If the prices of all your liabilities increase by{' '} {maintHealth}%, even for just a moment, some of your liabilities will be liquidated.

Scenario:{' '} If the value of your total collateral decreases by{' '} {((1 - 1 / ((maintHealth || 0) / 100 + 1)) * 100).toFixed( 2 )} % , some of your liabilities will be liquidated.

These are examples. A combination of events can also lead to liquidation.

} >

{t('health')}

{maintHealth}%

{t('free-collateral')}

{group && mangoAccount ? formatFixedDecimals( toUiDecimalsForQuote( mangoAccount.getCollateralValue(group)!.toNumber() ), true ) : (0).toFixed(2)}

Total: {group && mangoAccount ? formatFixedDecimals( toUiDecimalsForQuote( mangoAccount .getAssetsValue(group, HealthType.init)! .toNumber() ), true ) : `$${(0).toFixed(2)}`}

{t('leverage')}

{leverage.toFixed(2)}x

{showDepositModal ? ( setShowDepositModal(false)} /> ) : null} {showWithdrawModal ? ( setShowWithdrawModal(false)} /> ) : null} {!tourSettings?.account_tour_seen && isOnBoarded && connected ? ( ) : null} ) : (
{chartToShow === 'account-value' ? ( ) : chartToShow === 'pnl' ? ( ) : ( ({ interest_value: d.borrow_interest_cumulative_usd + d.deposit_interest_cumulative_usd, time: d.time, }))} hideChart={handleHideChart} mangoAccount={mangoAccount!} yKey="interest_value" /> )}
) } export default AccountPage