add rate curve chart
This commit is contained in:
parent
b621c458a3
commit
3d8f7aeb88
|
@ -0,0 +1,257 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Bank } from '@blockworks-foundation/mango-v4'
|
||||
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||
import ContentBox from '@components/shared/ContentBox'
|
||||
import { FadeInFadeOut } from '@components/shared/Transitions'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
import { useMemo, useState } from 'react'
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
Label,
|
||||
ReferenceDot,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts'
|
||||
import { COLORS } from 'styles/colors'
|
||||
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
||||
import FlipNumbers from 'react-flip-numbers'
|
||||
import { floorToDecimal, formatNumericValue } from 'utils/numbers'
|
||||
import FormatNumericValue from '@components/shared/FormatNumericValue'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { NoSymbolIcon } from '@heroicons/react/20/solid'
|
||||
|
||||
type RateCurveData = {
|
||||
util: string
|
||||
rate: number
|
||||
}
|
||||
|
||||
const RateCurveChart = ({ bank }: { bank: Bank | undefined }) => {
|
||||
const { t } = useTranslation(['common', 'token'])
|
||||
const { theme } = useThemeWrapper()
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
INITIAL_ANIMATION_SETTINGS,
|
||||
)
|
||||
const [mouseData, setMouseData] = useState<RateCurveData | null>(null)
|
||||
|
||||
const handleMouseMove = (coords: any) => {
|
||||
if (coords.activePayload) {
|
||||
setMouseData(coords.activePayload[0].payload)
|
||||
}
|
||||
}
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setMouseData(null)
|
||||
}
|
||||
|
||||
const [currentRate, currentUtil] = useMemo(() => {
|
||||
if (!bank) return [0, '0']
|
||||
const currentRate = bank.getDepositRateUi()
|
||||
const currentUtil = (
|
||||
(bank.uiBorrows() / bank.uiDeposits()) *
|
||||
100
|
||||
).toString()
|
||||
return [currentRate, currentUtil]
|
||||
}, [bank])
|
||||
|
||||
const rateCurveChartData = useMemo(() => {
|
||||
if (!bank) return []
|
||||
const defaults = [
|
||||
{ util: '0', rate: 0 },
|
||||
{
|
||||
util: (bank.util0.toNumber() * 100).toString(),
|
||||
rate: bank.rate0.toNumber() * 100,
|
||||
},
|
||||
{
|
||||
util: (bank.util1.toNumber() * 100).toString(),
|
||||
rate: bank.rate1.toNumber() * 100,
|
||||
},
|
||||
{ util: '100', rate: bank.maxRate.toNumber() * 100 },
|
||||
]
|
||||
if (currentRate && currentUtil) {
|
||||
defaults.push({ util: currentUtil, rate: currentRate })
|
||||
}
|
||||
return defaults.sort((a, b) => parseInt(a.util) - parseInt(b.util))
|
||||
}, [bank, currentRate, currentUtil])
|
||||
|
||||
return (
|
||||
<FadeInFadeOut show={true}>
|
||||
<ContentBox hideBorder hidePadding>
|
||||
{rateCurveChartData.length && bank ? (
|
||||
<>
|
||||
<div>
|
||||
<p className="mb-0.5 text-base">{t('token:rate-curve')}</p>
|
||||
{mouseData ? (
|
||||
<div>
|
||||
<div
|
||||
className={`flex h-8 items-end font-display text-2xl text-th-fgd-1`}
|
||||
>
|
||||
{animationSettings['number-scroll'] ? (
|
||||
<FlipNumbers
|
||||
height={24}
|
||||
width={17}
|
||||
play
|
||||
numbers={`${formatNumericValue(
|
||||
Math.abs(mouseData.rate),
|
||||
2,
|
||||
)}%`}
|
||||
/>
|
||||
) : (
|
||||
<span className="tabular-nums">
|
||||
<FormatNumericValue
|
||||
value={Math.abs(mouseData.rate)}
|
||||
decimals={2}
|
||||
/>
|
||||
%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-th-fgd-4">
|
||||
{t('utilization')}:{' '}
|
||||
<span className="font-mono text-th-fgd-2">
|
||||
{floorToDecimal(mouseData.util, 2).toString()}%
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div className="flex h-8 items-end font-display text-2xl text-th-fgd-1">
|
||||
{animationSettings['number-scroll'] ? (
|
||||
<FlipNumbers
|
||||
height={24}
|
||||
width={17}
|
||||
play
|
||||
numbers={`${formatNumericValue(currentRate)}%`}
|
||||
/>
|
||||
) : (
|
||||
<span>
|
||||
<span className="tabular-nums">
|
||||
<FormatNumericValue
|
||||
value={currentRate}
|
||||
decimals={2}
|
||||
/>
|
||||
</span>
|
||||
%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-th-fgd-4">
|
||||
{t('utilization')}:{' '}
|
||||
<span className="font-mono text-th-fgd-2">
|
||||
{floorToDecimal(currentUtil, 2).toString()}%
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="-mx-6 mt-2 h-72">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<AreaChart
|
||||
data={rateCurveChartData}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="gradientArea-rate-curve"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="1"
|
||||
>
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={COLORS.UP[theme]}
|
||||
stopOpacity={0.15}
|
||||
/>
|
||||
<stop
|
||||
offset="99%"
|
||||
stopColor={COLORS.UP[theme]}
|
||||
stopOpacity={0}
|
||||
/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<Area
|
||||
isAnimationActive={false}
|
||||
type="monotone"
|
||||
dataKey="rate"
|
||||
stroke={COLORS.UP[theme]}
|
||||
strokeWidth={1.5}
|
||||
fill="url(#gradientArea-rate-curve)"
|
||||
/>
|
||||
<XAxis
|
||||
axisLine={false}
|
||||
dataKey="util"
|
||||
minTickGap={20}
|
||||
padding={{ left: 20, right: 20 }}
|
||||
tick={{
|
||||
fill: 'var(--fgd-4)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickLine={false}
|
||||
tickFormatter={(d) => `${floorToDecimal(d, 2).toString()}%`}
|
||||
/>
|
||||
<YAxis
|
||||
axisLine={false}
|
||||
dataKey="rate"
|
||||
minTickGap={20}
|
||||
type="number"
|
||||
domain={['dataMin', 'dataMax']}
|
||||
padding={{ top: 20, bottom: 20 }}
|
||||
tick={{
|
||||
fill: 'var(--fgd-4)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickFormatter={(v) => `${floorToDecimal(v, 2).toString()}%`}
|
||||
tickLine={false}
|
||||
/>
|
||||
<ReferenceDot
|
||||
x={(
|
||||
(bank.uiBorrows() / bank.uiDeposits()) *
|
||||
100
|
||||
).toString()}
|
||||
y={bank.getDepositRateUi()}
|
||||
r={4}
|
||||
fill={COLORS.BKG1[theme]}
|
||||
stroke={'var(--active)'}
|
||||
strokeWidth={2}
|
||||
isFront
|
||||
>
|
||||
<Label
|
||||
value="Current"
|
||||
offset={12}
|
||||
position="top"
|
||||
fill="var(--fgd-2)"
|
||||
fontSize={12}
|
||||
/>
|
||||
</ReferenceDot>
|
||||
<Tooltip
|
||||
cursor={{
|
||||
strokeOpacity: 0.09,
|
||||
}}
|
||||
content={<></>}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
className={`flex h-72 items-center justify-center p-4 text-th-fgd-3`}
|
||||
>
|
||||
<div className="">
|
||||
<NoSymbolIcon className="mx-auto mb-1 h-6 w-6 text-th-fgd-4" />
|
||||
<p className="text-th-fgd-4">{t('chart-unavailable')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ContentBox>
|
||||
</FadeInFadeOut>
|
||||
)
|
||||
}
|
||||
|
||||
export default RateCurveChart
|
|
@ -7,7 +7,6 @@ import FlipNumbers from 'react-flip-numbers'
|
|||
import { formatCurrencyValue } from 'utils/numbers'
|
||||
import Link from 'next/link'
|
||||
import SheenLoader from '@components/shared/SheenLoader'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import useJupiterMints from 'hooks/useJupiterMints'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
|
@ -23,6 +22,7 @@ import TokenParams from './TokenParams'
|
|||
import { formatTokenSymbol } from 'utils/tokens'
|
||||
import TokenLogo from '@components/shared/TokenLogo'
|
||||
import { ArrowLeftIcon } from '@heroicons/react/20/solid'
|
||||
import RateCurveChart from './RateCurveChart'
|
||||
|
||||
const DEFAULT_COINGECKO_VALUES = {
|
||||
ath: 0,
|
||||
|
@ -183,23 +183,8 @@ const TokenPage = () => {
|
|||
<ActionPanel bank={bank} />
|
||||
</div>
|
||||
<ChartTabs bank={bank} />
|
||||
<div className="flex items-center justify-center border-y border-th-bkg-3 px-6 py-4 text-center">
|
||||
<Tooltip
|
||||
content={'The percentage of deposits that have been lent out.'}
|
||||
>
|
||||
<p className="tooltip-underline mr-1">{t('utilization')}:</p>
|
||||
</Tooltip>
|
||||
<span className="font-mono text-th-fgd-2 no-underline">
|
||||
{bank.uiDeposits() > 0 ? (
|
||||
<FormatNumericValue
|
||||
value={(bank.uiBorrows() / bank.uiDeposits()) * 100}
|
||||
decimals={1}
|
||||
/>
|
||||
) : (
|
||||
'0.0'
|
||||
)}
|
||||
%
|
||||
</span>
|
||||
<div className="border-y border-th-bkg-3 px-6 pb-2 pt-6">
|
||||
<RateCurveChart bank={bank} />
|
||||
</div>
|
||||
<TopTokenAccounts bank={bank} />
|
||||
{coingeckoTokenInfo?.market_data ? (
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"oracle-confidence": "Oracle Confidence",
|
||||
"oracle-staleness": "Oracle Staleness",
|
||||
"parameters": "Parameters",
|
||||
"rate-curve": "Interest Rate Curve",
|
||||
"token-stats": "{{token}} Stats",
|
||||
"token-fees-collected": "Token Fees Collected",
|
||||
"token-not-found": "Token Not Found",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"oracle-confidence": "Oracle Confidence",
|
||||
"oracle-staleness": "Oracle Staleness",
|
||||
"parameters": "Parameters",
|
||||
"rate-curve": "Interest Rate Curve",
|
||||
"token-stats": "{{token}} Stats",
|
||||
"token-fees-collected": "Token Fees Collected",
|
||||
"token-not-found": "Token Not Found",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"oracle-confidence": "Oracle Confidence",
|
||||
"oracle-staleness": "Oracle Staleness",
|
||||
"parameters": "Parameters",
|
||||
"rate-curve": "Interest Rate Curve",
|
||||
"token-stats": "{{token}} Stats",
|
||||
"token-fees-collected": "Token Fees Collected",
|
||||
"token-not-found": "Token Not Found",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"oracle-confidence": "预言机可信度",
|
||||
"oracle-staleness": "预言机不新鲜性",
|
||||
"parameters": "参数",
|
||||
"rate-curve": "Interest Rate Curve",
|
||||
"token-stats": "币种细节",
|
||||
"token-fees-collected": "收取的币种费用",
|
||||
"token-not-found": "查不到币种",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"oracle-confidence": "預言機可信度",
|
||||
"oracle-staleness": "預言機不新鮮性",
|
||||
"parameters": "參數",
|
||||
"rate-curve": "Interest Rate Curve",
|
||||
"token-stats": "幣種細節",
|
||||
"token-fees-collected": "收取的幣種費用",
|
||||
"token-not-found": "查不到幣種",
|
||||
|
|
Loading…
Reference in New Issue