add total deposit/borrow value charts

This commit is contained in:
saml33 2022-12-06 14:23:22 +11:00
parent 7205f0379b
commit 5102129a49
4 changed files with 165 additions and 60 deletions

View File

@ -30,11 +30,11 @@ dayjs.extend(relativeTime)
interface DetailedAreaChartProps {
data: any[]
daysToShow: string
daysToShow?: string
hideChange?: boolean
hideChart?: () => void
loading?: boolean
setDaysToShow: (x: string) => void
setDaysToShow?: (x: string) => void
tickFormat?: (x: any) => string
title?: string
xKey: string
@ -81,8 +81,9 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
const calculateChartChange = () => {
if (data.length) {
if (mouseData) {
console.log(mouseData)
const index = data.findIndex((d: any) => d[xKey] === mouseData[xKey])
const change = data[index][yKey] - data[0][yKey]
const change = index >= 0 ? data[index][yKey] - data[0][yKey] : 0
return isNaN(change) ? 0 : change
} else return data[data.length - 1][yKey] - data[0][yKey]
}
@ -105,9 +106,11 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
<div className="relative">
<div className="flex items-start justify-between">
<div className="flex flex-col md:flex-row md:items-start md:space-x-6">
<IconButton className="mb-6" onClick={hideChart}>
<ArrowLeftIcon className="h-5 w-5" />
</IconButton>
{hideChart ? (
<IconButton className="mb-6" onClick={hideChart}>
<ArrowLeftIcon className="h-5 w-5" />
</IconButton>
) : null}
<div>
<p className="mb-0.5 text-base text-th-fgd-3">{title}</p>
{mouseData ? (
@ -179,14 +182,16 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
</div>
</div>
<div className="-mt-1 h-96 w-auto">
<div className="absolute -top-1 right-0 -mb-2 flex justify-end">
<ChartRangeButtons
activeValue={daysToShow}
names={['24H', '7D', '30D']}
values={['1', '7', '30']}
onChange={(v) => setDaysToShow(v)}
/>
</div>
{setDaysToShow ? (
<div className="absolute -top-1 right-0 -mb-2 flex justify-end">
<ChartRangeButtons
activeValue={daysToShow}
names={['24H', '7D', '30D']}
values={['1', '7', '30']}
onChange={(v) => setDaysToShow(v)}
/>
</div>
) : null}
<div className="-mx-6 mt-6 h-full">
<ResponsiveContainer width="100%" height="100%">
<AreaChart

View File

@ -12,7 +12,7 @@ const SheenLoader = ({
return (
<div className="flex items-center">
<div
className={`relative ${className} overflow-hidden before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_4s_infinite] before:bg-gradient-to-r before:from-transparent before:via-th-bkg-4 before:to-transparent before:opacity-50`}
className={`relative rounded-md ${className} overflow-hidden before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_4s_infinite] before:bg-gradient-to-r before:from-transparent before:via-th-bkg-4 before:to-transparent before:opacity-50`}
>
{children}
</div>

View File

@ -6,36 +6,72 @@ import {
} from '@heroicons/react/20/solid'
import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import { Fragment, useMemo, useState } from 'react'
import { Fragment, useEffect, useMemo, useState } from 'react'
import { useViewport } from '../../hooks/useViewport'
import { formatDecimal, formatFixedDecimals } from '../../utils/numbers'
import { breakpoints } from '../../utils/theme'
import { IconButton, LinkButton } from '../shared/Button'
import ContentBox from '../shared/ContentBox'
import FlipNumbers from 'react-flip-numbers'
import Tooltip from '@components/shared/Tooltip'
import { Bank } from '@blockworks-foundation/mango-v4'
import { useRouter } from 'next/router'
import useJupiterMints from 'hooks/useJupiterMints'
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
import useMangoGroup from 'hooks/useMangoGroup'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
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
}
const TokenStats = () => {
const { t } = useTranslation(['common', 'token'])
const actions = mangoStore((s) => s.actions)
const tokenStats = mangoStore((s) => s.tokenStats.data)
const loadingStats = mangoStore((s) => s.tokenStats.loading)
const [showTokenDetails, setShowTokenDetails] = useState('')
const { group } = useMangoGroup()
const { mangoTokens } = useJupiterMints()
const { width } = useViewport()
const showTableView = width ? width > breakpoints.md : false
const router = useRouter()
const [animationSettings] = useLocalStorageState(
ANIMATION_SETTINGS_KEY,
INITIAL_ANIMATION_SETTINGS
)
useEffect(() => {
actions.fetchTokenStats()
}, [group])
const totalValues = useMemo(() => {
if (!tokenStats.length) return []
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,
depositValue: c.total_deposits * c.price,
borrowValue: c.total_borrows * c.price,
})
} else {
hasDate.depositValue =
hasDate.depositValue + c.total_deposits * c.price
hasDate.borrowValue = hasDate.borrowValue + c.total_borrows * c.price
}
return a
},
[]
)
return values.reverse()
}, [tokenStats])
const banks = useMemo(() => {
if (group) {
@ -75,44 +111,55 @@ const TokenStats = () => {
return (
<ContentBox hideBorder hidePadding>
<div className="grid grid-cols-2 gap-x-6 border-b border-th-bkg-3 text-5xl">
<div className="col-span-2 border-t border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-t-0 ">
<p className="mb-2 text-base leading-none">
{t('total-deposit-value')}
</p>
<div className="flex items-center font-bold">
{animationSettings['number-scroll'] ? (
<FlipNumbers
height={48}
width={32}
play
delay={0.05}
duration={1}
numbers={formatFixedDecimals(totalDepositValue || 0.0, true)}
/>
) : (
<p>{formatFixedDecimals(totalDepositValue || 0.0, true)}</p>
)}
{loadingStats ? (
<div className="col-span-2 py-4 px-6 md:col-span-1">
<SheenLoader className="flex flex-1">
<div className="h-96 w-full rounded-lg bg-th-bkg-2" />
</SheenLoader>
</div>
</div>
<div className="col-span-2 border-t border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:border-t-0 md:pl-6">
<p className="mb-2 text-base leading-none">
{t('total-borrow-value')}
</p>
<div className="flex items-center font-bold">
{animationSettings['number-scroll'] ? (
<FlipNumbers
height={48}
width={32}
play
delay={0.05}
duration={1}
numbers={formatFixedDecimals(totalBorrowValue || 0.0, true)}
/>
) : (
<span>{formatFixedDecimals(totalBorrowValue || 0.0, true)}</span>
)}
) : totalValues.length ? (
<div className="col-span-2 py-4 px-6 md:col-span-1">
<DetailedAreaChart
data={totalValues.concat([
{
date: dayjs().toISOString(),
depositValue: totalDepositValue!,
borrowValue: totalBorrowValue!,
},
])}
daysToShow={'999'}
tickFormat={(x) => `$${x.toFixed(2)}`}
title={t('total-deposit-value')}
xKey="date"
yKey={'depositValue'}
/>
</div>
</div>
) : null}
{loadingStats ? (
<div className="col-span-2 border-t border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:border-t-0 md:pl-6">
<SheenLoader className="flex flex-1">
<div className="h-96 w-full rounded-lg bg-th-bkg-2" />
</SheenLoader>
</div>
) : totalValues.length ? (
<div className="col-span-2 border-t border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:border-t-0 md:pl-6">
<DetailedAreaChart
data={totalValues.concat([
{
date: dayjs().toISOString(),
borrowValue: totalBorrowValue!,
depositValue: totalDepositValue!,
},
])}
daysToShow={'999'}
// setDaysToShow={() => console.log('fuck')}
tickFormat={(x) => `$${x.toFixed(2)}`}
title={t('total-borrow-value')}
xKey="date"
yKey={'borrowValue'}
/>
</div>
) : null}
</div>
{showTableView ? (
<Table>

View File

@ -37,6 +37,7 @@ import { Orderbook, SpotBalances } from 'types'
import spotBalancesUpdater from './spotBalancesUpdater'
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
import perpPositionsUpdater from './perpPositionsUpdater'
import { token } from '@project-serum/anchor/dist/cjs/utils'
const GROUP = new PublicKey('DLdcpC6AsAJ9xeKMR3WhHrN5sM5o7GVVXQhQ5vwisTtz')
@ -144,6 +145,21 @@ interface TourSettings {
wallet_pk: string
}
export interface TokenStatsItem {
borrow_apr: number
borrow_rate: number
collected_fees: number
date_hour: string
deposit_apr: number
deposit_rate: number
mango_group: string
price: number
symbol: string
token_index: number
total_borrows: number
total_deposits: number
}
// const defaultUserSettings = {
// account_tour_seen: false,
// default_language: 'English',
@ -218,6 +234,10 @@ export type MangoStore = {
success: boolean
}
set: (x: (x: MangoStore) => void) => void
tokenStats: {
loading: boolean
data: TokenStatsItem[]
}
tradeForm: {
side: 'buy' | 'sell'
price: string
@ -252,6 +272,7 @@ export type MangoStore = {
fetchOpenOrders: (ma?: MangoAccount) => Promise<void>
fetchProfileDetails: (walletPk: string) => void
fetchSwapHistory: (mangoAccountPk: string) => Promise<void>
fetchTokenStats: () => void
fetchTourSettings: (walletPk: string) => void
fetchWalletTokens: (wallet: Wallet) => Promise<void>
connectMangoClientWithWallet: (wallet: WalletAdapter) => Promise<void>
@ -321,6 +342,10 @@ const mangoStore = create<MangoStore>()(
},
uiLocked: true,
},
tokenStats: {
loading: false,
data: [],
},
tradeForm: {
side: 'buy',
price: '',
@ -722,6 +747,34 @@ const mangoStore = create<MangoStore>()(
})
}
},
fetchTokenStats: async () => {
const set = get().set
const group = get().group
const stats = get().tokenStats.data
if (stats.length || !group) return
set((state) => {
state.tokenStats.loading = true
})
try {
const response = await fetch(
`https://mango-transaction-log.herokuapp.com/v4/token-historical-stats?mango-group=${group?.publicKey.toString()}`
)
const data = await response.json()
set((state) => {
state.tokenStats.data = data
state.tokenStats.loading = false
})
} catch {
set((state) => {
state.tokenStats.loading = false
})
notify({
title: 'Failed to token stats data',
type: 'error',
})
}
},
fetchWalletTokens: async (wallet: Wallet) => {
const set = get().set
const connection = get().connection