2022-08-23 15:33:09 -07:00
import { Transition } from '@headlessui/react'
2022-09-06 21:36:35 -07:00
import {
ChevronDownIcon ,
2022-10-11 04:59:01 -07:00
ChevronRightIcon ,
2022-09-06 21:36:35 -07:00
QuestionMarkCircleIcon ,
} from '@heroicons/react/20/solid'
2022-08-23 15:33:09 -07:00
import { useTranslation } from 'next-i18next'
2022-10-28 14:46:38 -07:00
import Image from 'next/legacy/image'
2022-12-05 19:23:22 -08:00
import { Fragment , useEffect , useMemo , useState } from 'react'
2022-08-23 15:33:09 -07:00
import { useViewport } from '../../hooks/useViewport'
import { formatDecimal , formatFixedDecimals } from '../../utils/numbers'
import { breakpoints } from '../../utils/theme'
2022-10-11 04:59:01 -07:00
import { IconButton , LinkButton } from '../shared/Button'
2022-08-23 15:33:09 -07:00
import ContentBox from '../shared/ContentBox'
2022-09-24 05:28:11 -07:00
import Tooltip from '@components/shared/Tooltip'
2022-10-11 04:59:01 -07:00
import { Bank } from '@blockworks-foundation/mango-v4'
import { useRouter } from 'next/router'
2022-11-18 11:11:06 -08:00
import useJupiterMints from 'hooks/useJupiterMints'
2022-11-20 02:44:14 -08:00
import { Table , Td , Th , TrBody , TrHead } from '@components/shared/TableElements'
2022-11-20 12:32:38 -08:00
import useMangoGroup from 'hooks/useMangoGroup'
2022-12-05 19:23:22 -08:00
import dayjs from 'dayjs'
import mangoStore , { TokenStatsItem } from '@store/mangoStore'
import SheenLoader from '@components/shared/SheenLoader'
import dynamic from 'next/dynamic'
const DetailedAreaChart = dynamic (
( ) = > import ( '@components/shared/DetailedAreaChart' ) ,
{ ssr : false }
)
interface TotalValueItem {
date : string
borrowValue : number
depositValue : number
}
2022-08-23 15:33:09 -07:00
2022-10-11 04:59:01 -07:00
const TokenStats = ( ) = > {
const { t } = useTranslation ( [ 'common' , 'token' ] )
2022-12-05 19:23:22 -08:00
const actions = mangoStore ( ( s ) = > s . actions )
const tokenStats = mangoStore ( ( s ) = > s . tokenStats . data )
2022-12-11 02:08:50 -08:00
const initialStatsLoad = mangoStore ( ( s ) = > s . tokenStats . initialLoad )
2022-12-05 19:23:22 -08:00
const loadingStats = mangoStore ( ( s ) = > s . tokenStats . loading )
2022-08-23 15:33:09 -07:00
const [ showTokenDetails , setShowTokenDetails ] = useState ( '' )
2022-11-20 12:32:38 -08:00
const { group } = useMangoGroup ( )
2022-11-18 11:11:06 -08:00
const { mangoTokens } = useJupiterMints ( )
2022-08-23 15:33:09 -07:00
const { width } = useViewport ( )
const showTableView = width ? width > breakpoints.md : false
2022-10-11 04:59:01 -07:00
const router = useRouter ( )
2022-12-05 19:23:22 -08:00
useEffect ( ( ) = > {
2022-12-11 02:08:50 -08:00
if ( group && ! initialStatsLoad ) {
2022-12-06 03:58:22 -08:00
actions . fetchTokenStats ( )
}
2022-12-05 19:23:22 -08:00
} , [ group ] )
const totalValues = useMemo ( ( ) = > {
2022-12-24 08:38:25 -08:00
if ( ! tokenStats ? . length ) return [ ]
2022-12-05 19:23:22 -08:00
const values : TotalValueItem [ ] = tokenStats . reduce (
( a : TotalValueItem [ ] , c : TokenStatsItem ) = > {
const hasDate = a . find ( ( d : TotalValueItem ) = > d . date === c . date_hour )
if ( ! hasDate ) {
a . push ( {
date : c.date_hour ,
2022-12-07 15:29:59 -08:00
depositValue : Math.floor ( c . total_deposits * c . price ) ,
borrowValue : Math.floor ( c . total_borrows * c . price ) ,
2022-12-05 19:23:22 -08:00
} )
} else {
hasDate . depositValue =
2022-12-07 15:29:59 -08:00
hasDate . depositValue + Math . floor ( c . total_deposits * c . price )
hasDate . borrowValue =
hasDate . borrowValue + Math . floor ( c . total_borrows * c . price )
2022-12-05 19:23:22 -08:00
}
return a
} ,
[ ]
)
return values . reverse ( )
} , [ tokenStats ] )
2022-08-23 15:33:09 -07:00
const banks = useMemo ( ( ) = > {
if ( group ) {
const rawBanks = Array . from ( group ? . banksMapByName , ( [ key , value ] ) = > ( {
key ,
value ,
} ) )
return rawBanks
}
return [ ]
} , [ group ] )
const handleShowTokenDetails = ( name : string ) = > {
showTokenDetails ? setShowTokenDetails ( '' ) : setShowTokenDetails ( name )
}
2022-08-24 20:55:55 -07:00
const [ totalDepositValue , totalBorrowValue ] = useMemo ( ( ) = > {
if ( banks . length ) {
return [
banks . reduce (
2022-11-20 12:32:38 -08:00
( a , c ) = > a + c . value [ 0 ] . uiPrice * c . value [ 0 ] . uiDeposits ( ) ,
2022-08-24 20:55:55 -07:00
0
) ,
banks . reduce (
2022-11-20 12:32:38 -08:00
( a , c ) = > a + c . value [ 0 ] . uiPrice * c . value [ 0 ] . uiBorrows ( ) ,
2022-08-24 20:55:55 -07:00
0
) ,
]
}
2022-12-07 15:29:59 -08:00
return [ 0 , 0 ]
2022-08-24 20:55:55 -07:00
} , [ banks ] )
2022-10-11 04:59:01 -07:00
const goToTokenPage = ( bank : Bank ) = > {
router . push ( ` /token/ ${ bank . name } ` , undefined , { shallow : true } )
}
2022-08-23 15:33:09 -07:00
return (
2022-09-14 19:41:55 -07:00
< ContentBox hideBorder hidePadding >
2022-12-13 17:25:30 -08:00
< div className = "grid grid-cols-2" >
2022-12-05 19:23:22 -08:00
{ loadingStats ? (
2022-12-13 17:25:30 -08:00
< div className = "col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1" >
2022-12-05 19:23:22 -08:00
< SheenLoader className = "flex flex-1" >
< div className = "h-96 w-full rounded-lg bg-th-bkg-2" / >
< / SheenLoader >
< / div >
) : totalValues . length ? (
2022-12-13 17:25:30 -08:00
< div className = "col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1" >
2022-12-05 19:23:22 -08:00
< DetailedAreaChart
data = { totalValues . concat ( [
{
date : dayjs ( ) . toISOString ( ) ,
2022-12-07 15:29:59 -08:00
depositValue : Math.floor ( totalDepositValue ) ,
borrowValue : Math.floor ( totalBorrowValue ) ,
2022-12-05 19:23:22 -08:00
} ,
] ) }
daysToShow = { '999' }
2022-12-14 09:22:32 -08:00
heightClass = "h-64"
2022-12-06 03:58:22 -08:00
prefix = "$"
2022-12-05 19:23:22 -08:00
tickFormat = { ( x ) = > ` $ ${ x . toFixed ( 2 ) } ` }
title = { t ( 'total-deposit-value' ) }
xKey = "date"
yKey = { 'depositValue' }
/ >
< / div >
) : null }
{ loadingStats ? (
2022-12-13 17:25:30 -08:00
< div className = "col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:pl-6" >
2022-12-05 19:23:22 -08:00
< SheenLoader className = "flex flex-1" >
< div className = "h-96 w-full rounded-lg bg-th-bkg-2" / >
< / SheenLoader >
2022-08-24 20:55:55 -07:00
< / div >
2022-12-05 19:23:22 -08:00
) : totalValues . length ? (
2022-12-13 17:25:30 -08:00
< div className = "col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:pl-6" >
2022-12-05 19:23:22 -08:00
< DetailedAreaChart
data = { totalValues . concat ( [
{
date : dayjs ( ) . toISOString ( ) ,
2022-12-07 15:29:59 -08:00
borrowValue : Math.floor ( totalBorrowValue ) ,
depositValue : Math.floor ( totalDepositValue ) ,
2022-12-05 19:23:22 -08:00
} ,
] ) }
daysToShow = { '999' }
2022-12-14 09:22:32 -08:00
heightClass = "h-64"
2022-12-06 03:58:22 -08:00
prefix = "$"
2022-12-05 19:23:22 -08:00
tickFormat = { ( x ) = > ` $ ${ x . toFixed ( 2 ) } ` }
title = { t ( 'total-borrow-value' ) }
xKey = "date"
yKey = { 'borrowValue' }
/ >
2022-08-24 20:55:55 -07:00
< / div >
2022-12-05 19:23:22 -08:00
) : null }
2022-08-24 20:55:55 -07:00
< / div >
2022-08-23 15:33:09 -07:00
{ showTableView ? (
2022-11-20 02:44:14 -08:00
< Table >
2022-08-23 15:33:09 -07:00
< thead >
2022-11-20 02:44:14 -08:00
< TrHead >
< Th className = "text-left" > { t ( 'token' ) } < / Th >
< Th className = "text-right" > { t ( 'total-deposits' ) } < / Th >
< Th className = "text-right" > { t ( 'total-borrows' ) } < / Th >
< Th >
2022-09-25 17:37:04 -07:00
< div className = "flex justify-end" >
< Tooltip content = "The deposit rate (green) will automatically be paid on positive balances and the borrow rate (red) will automatically be charged on negative balances." >
< span className = "tooltip-underline" > { t ( 'rates' ) } < / span >
< / Tooltip >
< / div >
2022-11-20 02:44:14 -08:00
< / Th >
< Th >
2022-09-25 17:37:04 -07:00
< div className = "flex justify-end" >
< Tooltip content = "The percentage of deposits that have been lent out." >
< span className = "tooltip-underline" >
{ t ( 'utilization' ) }
< / span >
< / Tooltip >
< / div >
2022-11-20 02:44:14 -08:00
< / Th >
< Th >
2022-10-11 04:59:01 -07:00
< div className = "flex justify-end text-right" >
2022-09-25 17:37:04 -07:00
< Tooltip content = { t ( 'asset-weight-desc' ) } >
< span className = "tooltip-underline" >
{ t ( 'asset-weight' ) }
< / span >
< / Tooltip >
< / div >
2022-11-20 02:44:14 -08:00
< / Th >
< Th >
2022-08-24 20:55:55 -07:00
< div className = "flex items-center justify-end" >
2022-09-18 23:26:42 -07:00
< span className = "text-right" > { t ( 'liability-weight' ) } < / span >
2022-08-23 15:33:09 -07:00
< / div >
2022-11-20 02:44:14 -08:00
< / Th >
< / TrHead >
2022-08-23 15:33:09 -07:00
< / thead >
< tbody >
{ banks . map ( ( { key , value } ) = > {
const bank = value [ 0 ]
let logoURI
2022-11-20 12:32:38 -08:00
if ( mangoTokens ? . length ) {
2022-11-18 11:11:06 -08:00
logoURI = mangoTokens . find (
2022-08-23 15:33:09 -07:00
( t ) = > t . address === bank . mint . toString ( )
2022-11-20 12:32:38 -08:00
) ? . logoURI
2022-08-23 15:33:09 -07:00
}
return (
2022-11-20 02:44:14 -08:00
< TrBody key = { key } >
< Td >
2022-08-23 15:33:09 -07:00
< div className = "flex items-center" >
< div className = "mr-2.5 flex flex-shrink-0 items-center" >
{ logoURI ? (
2022-09-18 23:26:42 -07:00
< Image alt = "" width = "24" height = "24" src = { logoURI } / >
2022-08-23 15:33:09 -07:00
) : (
2022-09-18 23:26:42 -07:00
< QuestionMarkCircleIcon className = "h-6 w-6 text-th-fgd-3" / >
2022-08-23 15:33:09 -07:00
) }
< / div >
2022-09-18 23:26:42 -07:00
< p className = "font-body tracking-wide" > { bank . name } < / p >
2022-08-23 15:33:09 -07:00
< / div >
2022-11-20 02:44:14 -08:00
< / Td >
< Td >
2022-08-23 15:33:09 -07:00
< div className = "flex flex-col text-right" >
< p > { formatFixedDecimals ( bank . uiDeposits ( ) ) } < / p >
< / div >
2022-11-20 02:44:14 -08:00
< / Td >
< Td >
2022-08-23 15:33:09 -07:00
< div className = "flex flex-col text-right" >
< p > { formatFixedDecimals ( bank . uiBorrows ( ) ) } < / p >
< / div >
2022-11-20 02:44:14 -08:00
< / Td >
< Td >
2022-09-24 05:28:11 -07:00
< div className = "flex justify-end space-x-2" >
2022-11-30 19:32:32 -08:00
< p className = "text-th-up" >
2022-08-23 15:33:09 -07:00
{ formatDecimal ( bank . getDepositRateUi ( ) , 2 , {
fixed : true ,
} ) }
%
< / p >
< span className = "text-th-fgd-4" > | < / span >
2022-11-30 19:32:32 -08:00
< p className = "text-th-down" >
2022-08-23 15:33:09 -07:00
{ formatDecimal ( bank . getBorrowRateUi ( ) , 2 , {
fixed : true ,
} ) }
%
< / p >
< / div >
2022-11-20 02:44:14 -08:00
< / Td >
< Td >
2022-08-24 20:55:55 -07:00
< div className = "flex flex-col text-right" >
< p >
{ bank . uiDeposits ( ) > 0
? formatDecimal (
( bank . uiBorrows ( ) / bank . uiDeposits ( ) ) * 100 ,
1 ,
{ fixed : true }
)
: '0.0' }
%
< / p >
< / div >
2022-11-20 02:44:14 -08:00
< / Td >
< Td >
2022-08-24 20:55:55 -07:00
< div className = "text-right" >
2022-08-23 16:49:36 -07:00
< p > { bank . initAssetWeight . toFixed ( 2 ) } < / p >
< / div >
2022-11-20 02:44:14 -08:00
< / Td >
< Td >
2022-08-24 20:55:55 -07:00
< div className = "text-right" >
2022-08-23 16:49:36 -07:00
< p > { bank . initLiabWeight . toFixed ( 2 ) } < / p >
2022-08-23 15:33:09 -07:00
< / div >
2022-11-20 02:44:14 -08:00
< / Td >
< Td >
2022-10-11 04:59:01 -07:00
< div className = "flex justify-end" >
2022-10-29 04:38:40 -07:00
< IconButton
onClick = { ( ) = > goToTokenPage ( bank ) }
size = "small"
>
2022-10-11 04:59:01 -07:00
< ChevronRightIcon className = "h-5 w-5" / >
< / IconButton >
< / div >
2022-11-20 02:44:14 -08:00
< / Td >
< / TrBody >
2022-08-23 15:33:09 -07:00
)
} ) }
< / tbody >
2022-11-20 02:44:14 -08:00
< / Table >
2022-08-23 15:33:09 -07:00
) : (
2022-09-18 23:26:42 -07:00
< div >
2022-08-23 15:33:09 -07:00
{ banks . map ( ( { key , value } ) = > {
const bank = value [ 0 ]
let logoURI
2022-11-20 12:32:38 -08:00
if ( mangoTokens ? . length ) {
2022-11-18 11:11:06 -08:00
logoURI = mangoTokens . find (
2022-08-23 15:33:09 -07:00
( t ) = > t . address === bank . mint . toString ( )
2022-11-20 12:32:38 -08:00
) ? . logoURI
2022-08-23 15:33:09 -07:00
}
return (
2022-09-18 23:26:42 -07:00
< div key = { key } className = "border-b border-th-bkg-3 px-6 py-4" >
2022-08-23 15:33:09 -07:00
< div className = "flex items-center justify-between" >
< div className = "flex items-center" >
< div className = "mr-2.5 flex flex-shrink-0 items-center" >
{ logoURI ? (
< Image alt = "" width = "24" height = "24" src = { logoURI } / >
) : (
< QuestionMarkCircleIcon className = "h-7 w-7 text-th-fgd-3" / >
) }
< / div >
2022-09-18 23:26:42 -07:00
< p className = "text-th-fgd-1" > { bank . name } < / p >
< / div >
< div className = "flex items-center space-x-4" >
2022-08-23 15:33:09 -07:00
< div >
2022-09-18 23:26:42 -07:00
< p className = "text-right text-xs" >
{ t ( 'total-deposits' ) }
< / p >
< p className = "text-right font-mono text-th-fgd-1" >
{ formatFixedDecimals ( bank . uiDeposits ( ) ) }
< / p >
< / div >
< div >
< p className = "text-right text-xs" > { t ( 'total-borrows' ) } < / p >
< p className = "text-right font-mono text-th-fgd-1" >
{ formatFixedDecimals ( bank . uiBorrows ( ) ) }
< / p >
2022-08-23 15:33:09 -07:00
< / div >
< IconButton
onClick = { ( ) = > handleShowTokenDetails ( bank . name ) }
>
< ChevronDownIcon
className = { ` ${
showTokenDetails === bank . name
? 'rotate-180'
: 'rotate-360'
} h - 6 w - 6 flex - shrink - 0 text - th - fgd - 1 ` }
/ >
< / IconButton >
< / div >
< / div >
< Transition
appear = { true }
show = { showTokenDetails === bank . name }
as = { Fragment }
enter = "transition ease-in duration-200"
enterFrom = "opacity-0"
enterTo = "opacity-100"
leave = "transition ease-out"
leaveFrom = "opacity-100"
leaveTo = "opacity-0"
>
< div className = "mt-4 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4" >
< div className = "col-span-1" >
< p className = "text-xs text-th-fgd-3" > { t ( 'rates' ) } < / p >
2022-09-18 23:26:42 -07:00
< p className = "space-x-2" >
2022-11-30 19:32:32 -08:00
< span className = "font-mono text-th-up" >
2022-08-23 15:33:09 -07:00
{ formatDecimal ( bank . getDepositRate ( ) . toNumber ( ) , 2 ) } %
< / span >
< span className = "font-normal text-th-fgd-4" > | < / span >
2022-11-30 19:32:32 -08:00
< span className = "font-mono text-th-down" >
2022-08-23 15:33:09 -07:00
{ formatDecimal ( bank . getBorrowRate ( ) . toNumber ( ) , 2 ) } %
< / span >
< / p >
< / div >
< div className = "col-span-1" >
2022-09-18 23:26:42 -07:00
< p className = "text-xs text-th-fgd-3" >
{ t ( 'utilization' ) }
< / p >
< p className = "font-mono text-th-fgd-1" >
{ bank . uiDeposits ( ) > 0
? formatDecimal (
( bank . uiBorrows ( ) / bank . uiDeposits ( ) ) * 100 ,
1 ,
{ fixed : true }
)
: '0.0' }
%
< / p >
< / div >
< div className = "col-span-1" >
< p className = "text-xs text-th-fgd-3" >
{ t ( 'asset-weight' ) }
< / p >
< p className = "font-mono text-th-fgd-1" >
{ bank . initAssetWeight . toFixed ( 2 ) }
< / p >
< / div >
< div className = "col-span-1" >
< p className = "text-xs text-th-fgd-3" >
{ t ( 'liability-weight' ) }
< / p >
< p className = "font-mono text-th-fgd-1" >
{ bank . initLiabWeight . toFixed ( 2 ) }
2022-08-23 15:33:09 -07:00
< / p >
< / div >
2022-10-11 04:59:01 -07:00
< div className = "col-span-1" >
< LinkButton
className = "flex items-center"
onClick = { ( ) = > goToTokenPage ( bank ) }
>
{ t ( 'token:token-details' ) }
< ChevronRightIcon className = "ml-2 h-5 w-5" / >
< / LinkButton >
< / div >
2022-08-23 15:33:09 -07:00
< / div >
< / Transition >
< / div >
)
} ) }
< / div >
) }
< / ContentBox >
)
}
2022-10-11 04:59:01 -07:00
export default TokenStats