mango-v4-ui/components/account/AccountHeroStats.tsx

286 lines
9.9 KiB
TypeScript

import useMangoAccount from 'hooks/useMangoAccount'
import useMangoGroup from 'hooks/useMangoGroup'
import { useTranslation } from 'next-i18next'
import { useMemo, useState } from 'react'
import { handleViewChange } from './AccountPage'
import Tooltip from '@components/shared/Tooltip'
import {
HealthType,
toUiDecimalsForQuote,
} from '@blockworks-foundation/mango-v4'
import HealthBar from './HealthBar'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import { IconButton } from '@components/shared/Button'
import {
CalendarIcon,
ChartBarIcon,
ChevronRightIcon,
} from '@heroicons/react/20/solid'
import { useRouter } from 'next/router'
import useAccountPerformanceData from 'hooks/useAccountPerformanceData'
import Change from '@components/shared/Change'
import PnlHistoryModal from '@components/modals/PnlHistoryModal'
const AccountHeroStats = ({ accountValue }: { accountValue: number }) => {
const { t } = useTranslation(['common', 'account'])
const { group } = useMangoGroup()
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const { rollingDailyData } = useAccountPerformanceData()
const router = useRouter()
const [showPnlHistory, setShowPnlHistory] = useState(false)
const handleGoToStats = () => {
const query = { ...router.query, ['view']: 'account-stats' }
router.push({ pathname: router.pathname, query }, undefined, {
shallow: true,
})
}
const maintHealth = useMemo(() => {
return group && mangoAccount
? mangoAccount.getHealthRatioUi(group, HealthType.maint)
: 0
}, [mangoAccount, group])
const leverage = useMemo(() => {
if (!group || !mangoAccount) return 0
const assetsValue = toUiDecimalsForQuote(
mangoAccount.getAssetsValue(group).toNumber(),
)
if (isNaN(assetsValue / accountValue)) {
return 0
} else {
return Math.abs(1 - assetsValue / accountValue)
}
}, [mangoAccount, group, accountValue])
const accountPnl = useMemo(() => {
if (!group || !mangoAccount) return 0
return toUiDecimalsForQuote(mangoAccount.getPnl(group).toNumber())
}, [group, mangoAccount])
const pnlChangeToday = useMemo(() => {
if (!accountPnl || !rollingDailyData.length) return 0
const startHour = rollingDailyData.find((item) => {
const itemHour = new Date(item.time).getHours()
return itemHour === 0
})
const startDayPnl = startHour?.pnl
const pnlChangeToday = startDayPnl ? accountPnl - startDayPnl : 0
return pnlChangeToday
}, [accountPnl, rollingDailyData])
const rollingDailyPnlChange = useMemo(() => {
if (!accountPnl || !rollingDailyData.length) return 0
return accountPnl - rollingDailyData[0].pnl
}, [accountPnl, rollingDailyData])
return (
<>
<div
className="border-b border-th-bkg-3 px-4 pb-4 pt-3 md:px-6"
id="account-health"
>
<div>
<div className="flex justify-between">
<Tooltip
maxWidth="20rem"
placement="top-start"
delay={100}
content={
<div className="flex-col space-y-2 text-sm">
<p className="text-xs">
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.
</p>
{maintHealth < 100 && mangoAccountAddress ? (
<>
<p className="text-xs font-bold text-th-fgd-1">
Your account health is {maintHealth}%
</p>
<p className="text-xs">
<span className="font-bold text-th-fgd-1">
Scenario:
</span>{' '}
If the prices of all your liabilities increase by{' '}
{maintHealth}%, even for just a moment, some of your
liabilities will be liquidated.
</p>
<p className="text-xs">
<span className="font-bold text-th-fgd-1">
Scenario:
</span>{' '}
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.
</p>
<p className="text-xs">
These are examples. A combination of events can also
lead to liquidation.
</p>
</>
) : null}
</div>
}
>
<p className="tooltip-underline">{t('health')}</p>
</Tooltip>
{mangoAccountAddress ? (
<Tooltip
className="hidden md:block"
content={t('account:health-contributions')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() =>
handleViewChange('health-contributions', router)
}
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
) : null}
</div>
<div className="mb-0.5 mt-2 flex items-center space-x-3">
<p className="text-2xl font-bold text-th-fgd-1 lg:text-3xl">
{maintHealth}%
</p>
<HealthBar health={maintHealth} />
</div>
<span className="flex font-normal text-th-fgd-4">
<Tooltip
content={t('account:tooltip-leverage')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<span className="tooltip-underline">{t('leverage')}</span>:
</Tooltip>
<span className="ml-1 font-mono text-th-fgd-2">
<FormatNumericValue value={leverage} decimals={2} roundUp />x
</span>
</span>
</div>
</div>
<div
className="flex border-b border-th-bkg-3 px-4 pb-4 pt-3 md:px-6"
id="account-free-collateral"
>
<div>
<Tooltip
content={t('account:tooltip-free-collateral')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<p className="tooltip-underline">{t('free-collateral')}</p>
</Tooltip>
<p className="mb-0.5 mt-2 text-2xl font-bold text-th-fgd-1 lg:text-3xl">
<FormatNumericValue
value={
group && mangoAccount
? toUiDecimalsForQuote(mangoAccount.getCollateralValue(group))
: 0
}
decimals={2}
isUsd={true}
isPrivate
/>
</p>
<span className="font-normal text-th-fgd-4">
<Tooltip
content={t('account:tooltip-total-collateral')}
maxWidth="20rem"
placement="top-start"
delay={100}
>
<span className="tooltip-underline">{t('total')}</span>:
<span className="ml-1 font-mono text-th-fgd-2">
<FormatNumericValue
value={
group && mangoAccount
? toUiDecimalsForQuote(
mangoAccount.getAssetsValue(group, HealthType.init),
)
: 0
}
decimals={2}
isUsd={true}
isPrivate
/>
</span>
</Tooltip>
</span>
</div>
</div>
<div
id="account-pnl"
className="border-b border-th-bkg-3 px-4 pb-4 pt-3 md:px-6"
>
<div className="flex items-center justify-between">
<Tooltip
content={t('account:tooltip-pnl')}
placement="top-start"
delay={100}
>
<p className="tooltip-underline">{t('pnl')}</p>
</Tooltip>
{mangoAccountAddress ? (
<Tooltip
className="hidden md:block"
content={t('account:pnl-history')}
delay={100}
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => setShowPnlHistory(true)}
>
<CalendarIcon className="h-5 w-5" />
</IconButton>
</Tooltip>
) : null}
</div>
<p className="mb-0.5 mt-2 text-2xl font-bold text-th-fgd-1 lg:text-3xl">
<FormatNumericValue
value={accountPnl}
decimals={2}
isUsd={true}
isPrivate
/>
</p>
<div className="flex items-center space-x-1.5">
<Change change={rollingDailyPnlChange} prefix="$" isPrivate />
<p className="text-th-fgd-4">{t('rolling-change')}</p>
</div>
</div>
<button
className="default-transition flex h-10 w-full items-center justify-between px-4 focus:outline-none disabled:cursor-not-allowed md:px-6 md:hover:bg-th-bkg-2"
id="account-more-stats"
onClick={() => handleGoToStats()}
disabled={!mangoAccountAddress}
>
<p>{t('account:more-account-stats')}</p>
<ChevronRightIcon className="h-5 w-5 text-th-fgd-3" />
</button>
{showPnlHistory ? (
<PnlHistoryModal
isOpen={showPnlHistory}
onClose={() => setShowPnlHistory(false)}
pnlChangeToday={pnlChangeToday}
/>
) : null}
</>
)
}
export default AccountHeroStats