314 lines
11 KiB
TypeScript
314 lines
11 KiB
TypeScript
import { Bank } from '@blockworks-foundation/mango-v4'
|
|
import TabButtons from '@components/shared/TabButtons'
|
|
import { useTranslation } from 'next-i18next'
|
|
import { useMemo, useState } from 'react'
|
|
import { TokenStatsItem } from 'types'
|
|
import { formatYAxis } from 'utils/formatting'
|
|
import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart'
|
|
import TokenRatesChart from './TokenRatesChart'
|
|
import Switch from '@components/forms/Switch'
|
|
import { useTokenStats } from 'hooks/useTokenStats'
|
|
|
|
const SWITCH_WRAPPER_CLASSES =
|
|
'mt-4 flex justify-end space-x-4 border-t border-th-bkg-3 px-4 py-2 md:px-6'
|
|
|
|
interface GroupedTokenDataItem extends TokenStatsItem {
|
|
intervalStartMillis: number
|
|
}
|
|
|
|
const groupTokenByHourlyInterval = (
|
|
data: TokenStatsItem[],
|
|
daysToShow: string,
|
|
dataKey: 'total_deposits' | 'total_borrows',
|
|
) => {
|
|
let intervalDurationHours
|
|
if (daysToShow === '30') {
|
|
intervalDurationHours = 24
|
|
} else if (daysToShow === '7') {
|
|
intervalDurationHours = 4
|
|
} else {
|
|
intervalDurationHours = 1
|
|
}
|
|
const intervalMillis = intervalDurationHours * 60 * 60 * 1000
|
|
const groupedData = []
|
|
let currentGroup: GroupedTokenDataItem | null = null
|
|
for (let i = 0; i < data.length; i++) {
|
|
const obj = data[i]
|
|
const date = new Date(obj.date_hour)
|
|
const intervalStartMillis =
|
|
Math.floor(date.getTime() / intervalMillis) * intervalMillis
|
|
if (
|
|
!currentGroup ||
|
|
currentGroup.intervalStartMillis !== intervalStartMillis
|
|
) {
|
|
currentGroup = {
|
|
...obj,
|
|
intervalStartMillis: intervalStartMillis,
|
|
}
|
|
groupedData.push(currentGroup)
|
|
} else {
|
|
currentGroup[dataKey] += obj[dataKey]
|
|
}
|
|
}
|
|
return groupedData
|
|
}
|
|
|
|
const ChartTabs = ({ bank }: { bank: Bank }) => {
|
|
const { t } = useTranslation('token')
|
|
const [showDepositsRelativeChange, setShowDepositsRelativeChange] =
|
|
useState(true)
|
|
const [showDepositsNotional, setShowDepositsNotional] = useState(false)
|
|
const [showBorrowsRelativeChange, setShowBorrowsRelativeChange] =
|
|
useState(true)
|
|
const [showBorrowsNotional, setShowBorrowsNotional] = useState(false)
|
|
const [activeDepositsTab, setActiveDepositsTab] = useState(
|
|
'token:total-deposits',
|
|
)
|
|
const [activeBorrowsTab, setActiveBorrowsTab] = useState(
|
|
'token:total-borrows',
|
|
)
|
|
const [depositDaysToShow, setDepositDaysToShow] = useState('30')
|
|
const [borrowDaysToShow, setBorrowDaysToShow] = useState('30')
|
|
const [depositRateDaysToShow, setDepositRateDaysToShow] = useState('30')
|
|
const [borrowRateDaysToShow, setBorrowRateDaysToShow] = useState('30')
|
|
const { data: tokenStats, isLoading } = useTokenStats(bank.mint)
|
|
const formattedStats = useMemo(() => {
|
|
if (!tokenStats?.data?.length) return []
|
|
return tokenStats.data
|
|
.filter((c) => c.token_index === bank.tokenIndex)
|
|
.sort(
|
|
(a, b) =>
|
|
new Date(a.date_hour).getTime() - new Date(b.date_hour).getTime(),
|
|
)
|
|
}, [bank, tokenStats])
|
|
|
|
const depositsStats = useMemo(() => {
|
|
if (!formattedStats.length) return []
|
|
|
|
if (showDepositsRelativeChange) {
|
|
if (!showDepositsNotional) return formattedStats
|
|
return formattedStats.map((currentStat) => {
|
|
currentStat = {
|
|
...currentStat,
|
|
total_deposits: currentStat.total_deposits * bank.uiPrice,
|
|
}
|
|
return currentStat
|
|
})
|
|
}
|
|
|
|
return formattedStats
|
|
.map((currentStat, index, array) => {
|
|
const previousStat = array[index - 1]
|
|
|
|
if (previousStat) {
|
|
const isNotionalMultiplier = showDepositsNotional ? bank.uiPrice : 1
|
|
currentStat = {
|
|
...currentStat,
|
|
total_deposits:
|
|
(currentStat.total_deposits - previousStat.total_deposits) *
|
|
isNotionalMultiplier,
|
|
}
|
|
}
|
|
|
|
return currentStat
|
|
})
|
|
.slice(1)
|
|
}, [bank, formattedStats, showDepositsNotional, showDepositsRelativeChange])
|
|
|
|
const borrowsStats = useMemo(() => {
|
|
if (!formattedStats.length) return []
|
|
|
|
if (showBorrowsRelativeChange) {
|
|
if (!showBorrowsNotional) return formattedStats
|
|
return formattedStats.map((currentStat) => {
|
|
currentStat = {
|
|
...currentStat,
|
|
total_borrows: currentStat.total_borrows * bank.uiPrice,
|
|
}
|
|
return currentStat
|
|
})
|
|
}
|
|
|
|
return formattedStats
|
|
.map((currentStat, index, array) => {
|
|
const previousStat = array[index - 1]
|
|
|
|
if (previousStat) {
|
|
const isNotionalMultiplier = showBorrowsNotional ? bank.uiPrice : 1
|
|
currentStat = {
|
|
...currentStat,
|
|
total_borrows:
|
|
(currentStat.total_borrows - previousStat.total_borrows) *
|
|
isNotionalMultiplier,
|
|
}
|
|
}
|
|
|
|
return currentStat
|
|
})
|
|
.slice(1)
|
|
}, [bank, formattedStats, showBorrowsNotional, showBorrowsRelativeChange])
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 overflow-x-hidden md:grid-cols-2">
|
|
<div className="col-span-1 border-b border-th-bkg-3 md:border-b-0 md:border-r">
|
|
<div className="w-full">
|
|
<TabButtons
|
|
activeValue={activeDepositsTab}
|
|
onChange={(v) => setActiveDepositsTab(v)}
|
|
showBorders
|
|
values={[
|
|
['token:total-deposits', 0],
|
|
['token:deposit-rates', 0],
|
|
]}
|
|
/>
|
|
<div className="border-t border-th-bkg-3">
|
|
{activeDepositsTab === 'token:total-deposits' ? (
|
|
<>
|
|
<div className="px-4 pt-4 md:px-6 lg:pt-6">
|
|
<DetailedAreaOrBarChart
|
|
changeAsPercent
|
|
hideChange={!showDepositsRelativeChange}
|
|
data={
|
|
showDepositsRelativeChange
|
|
? depositsStats
|
|
: groupTokenByHourlyInterval(
|
|
depositsStats,
|
|
depositDaysToShow,
|
|
'total_deposits',
|
|
)
|
|
}
|
|
daysToShow={depositDaysToShow}
|
|
setDaysToShow={setDepositDaysToShow}
|
|
heightClass="h-64"
|
|
loaderHeightClass="h-[334px]"
|
|
domain={[0, 'dataMax']}
|
|
loading={isLoading}
|
|
small
|
|
tickFormat={(x) => formatYAxis(x)}
|
|
title={`${t('token:deposits')}`}
|
|
xKey="date_hour"
|
|
yKey={'total_deposits'}
|
|
chartType={showDepositsRelativeChange ? 'area' : 'bar'}
|
|
prefix={showDepositsNotional ? '$' : ''}
|
|
/>
|
|
</div>
|
|
<div className={SWITCH_WRAPPER_CLASSES}>
|
|
<Switch
|
|
checked={showDepositsRelativeChange}
|
|
onChange={() =>
|
|
setShowDepositsRelativeChange(!showDepositsRelativeChange)
|
|
}
|
|
small
|
|
>
|
|
{t('stats:show-relative')}
|
|
</Switch>
|
|
<Switch
|
|
checked={showDepositsNotional}
|
|
onChange={() =>
|
|
setShowDepositsNotional(!showDepositsNotional)
|
|
}
|
|
small
|
|
>
|
|
{t('stats:notional')}
|
|
</Switch>
|
|
</div>
|
|
</>
|
|
) : (
|
|
<div className="px-4 pt-4 md:px-6 lg:pt-6">
|
|
<TokenRatesChart
|
|
data={formattedStats}
|
|
dataKey="deposit_apr"
|
|
daysToShow={depositRateDaysToShow}
|
|
loading={isLoading}
|
|
setDaysToShow={setDepositRateDaysToShow}
|
|
title={`${t('token:average-deposit-rate')} (APR)`}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="col-span-1">
|
|
<div className="w-full">
|
|
<TabButtons
|
|
activeValue={activeBorrowsTab}
|
|
onChange={(v) => setActiveBorrowsTab(v)}
|
|
showBorders
|
|
values={[
|
|
['token:total-borrows', 0],
|
|
['token:borrow-rates', 0],
|
|
]}
|
|
/>
|
|
<div className="border-t border-th-bkg-3">
|
|
{activeBorrowsTab === 'token:total-borrows' ? (
|
|
<>
|
|
<div className="px-4 pt-4 md:px-6 lg:pt-6">
|
|
<DetailedAreaOrBarChart
|
|
changeAsPercent
|
|
hideChange={!showBorrowsRelativeChange}
|
|
data={
|
|
showBorrowsRelativeChange
|
|
? borrowsStats
|
|
: groupTokenByHourlyInterval(
|
|
borrowsStats,
|
|
borrowDaysToShow,
|
|
'total_borrows',
|
|
)
|
|
}
|
|
daysToShow={borrowDaysToShow}
|
|
setDaysToShow={setBorrowDaysToShow}
|
|
heightClass="h-64"
|
|
loaderHeightClass="h-[334px]"
|
|
domain={[0, 'dataMax']}
|
|
loading={isLoading}
|
|
small
|
|
tickFormat={(x) => formatYAxis(x)}
|
|
title={`${t('token:borrows')}`}
|
|
xKey="date_hour"
|
|
yKey={'total_borrows'}
|
|
chartType={showBorrowsRelativeChange ? 'area' : 'bar'}
|
|
prefix={showBorrowsNotional ? '$' : ''}
|
|
/>
|
|
</div>
|
|
<div className={SWITCH_WRAPPER_CLASSES}>
|
|
<Switch
|
|
checked={showBorrowsRelativeChange}
|
|
onChange={() =>
|
|
setShowBorrowsRelativeChange(!showBorrowsRelativeChange)
|
|
}
|
|
small
|
|
>
|
|
{t('stats:show-relative')}
|
|
</Switch>
|
|
<Switch
|
|
checked={showBorrowsNotional}
|
|
onChange={() =>
|
|
setShowBorrowsNotional(!showBorrowsNotional)
|
|
}
|
|
small
|
|
>
|
|
{t('stats:notional')}
|
|
</Switch>
|
|
</div>
|
|
</>
|
|
) : (
|
|
<div className="px-4 pt-4 md:px-6 lg:pt-6">
|
|
<TokenRatesChart
|
|
data={formattedStats}
|
|
dataKey="borrow_apr"
|
|
daysToShow={borrowRateDaysToShow}
|
|
loading={isLoading}
|
|
setDaysToShow={setBorrowRateDaysToShow}
|
|
title={`${t('token:average-borrow-rate')} (APR)`}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default ChartTabs
|