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 { formatCurrencyValue } from 'utils/numbers'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import SheenLoader from '@components/shared/SheenLoader'
|
import SheenLoader from '@components/shared/SheenLoader'
|
||||||
import Tooltip from '@components/shared/Tooltip'
|
|
||||||
import useMangoGroup from 'hooks/useMangoGroup'
|
import useMangoGroup from 'hooks/useMangoGroup'
|
||||||
import useJupiterMints from 'hooks/useJupiterMints'
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
|
@ -23,6 +22,7 @@ import TokenParams from './TokenParams'
|
||||||
import { formatTokenSymbol } from 'utils/tokens'
|
import { formatTokenSymbol } from 'utils/tokens'
|
||||||
import TokenLogo from '@components/shared/TokenLogo'
|
import TokenLogo from '@components/shared/TokenLogo'
|
||||||
import { ArrowLeftIcon } from '@heroicons/react/20/solid'
|
import { ArrowLeftIcon } from '@heroicons/react/20/solid'
|
||||||
|
import RateCurveChart from './RateCurveChart'
|
||||||
|
|
||||||
const DEFAULT_COINGECKO_VALUES = {
|
const DEFAULT_COINGECKO_VALUES = {
|
||||||
ath: 0,
|
ath: 0,
|
||||||
|
@ -183,23 +183,8 @@ const TokenPage = () => {
|
||||||
<ActionPanel bank={bank} />
|
<ActionPanel bank={bank} />
|
||||||
</div>
|
</div>
|
||||||
<ChartTabs bank={bank} />
|
<ChartTabs bank={bank} />
|
||||||
<div className="flex items-center justify-center border-y border-th-bkg-3 px-6 py-4 text-center">
|
<div className="border-y border-th-bkg-3 px-6 pb-2 pt-6">
|
||||||
<Tooltip
|
<RateCurveChart bank={bank} />
|
||||||
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>
|
</div>
|
||||||
<TopTokenAccounts bank={bank} />
|
<TopTokenAccounts bank={bank} />
|
||||||
{coingeckoTokenInfo?.market_data ? (
|
{coingeckoTokenInfo?.market_data ? (
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"oracle-confidence": "Oracle Confidence",
|
"oracle-confidence": "Oracle Confidence",
|
||||||
"oracle-staleness": "Oracle Staleness",
|
"oracle-staleness": "Oracle Staleness",
|
||||||
"parameters": "Parameters",
|
"parameters": "Parameters",
|
||||||
|
"rate-curve": "Interest Rate Curve",
|
||||||
"token-stats": "{{token}} Stats",
|
"token-stats": "{{token}} Stats",
|
||||||
"token-fees-collected": "Token Fees Collected",
|
"token-fees-collected": "Token Fees Collected",
|
||||||
"token-not-found": "Token Not Found",
|
"token-not-found": "Token Not Found",
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"oracle-confidence": "Oracle Confidence",
|
"oracle-confidence": "Oracle Confidence",
|
||||||
"oracle-staleness": "Oracle Staleness",
|
"oracle-staleness": "Oracle Staleness",
|
||||||
"parameters": "Parameters",
|
"parameters": "Parameters",
|
||||||
|
"rate-curve": "Interest Rate Curve",
|
||||||
"token-stats": "{{token}} Stats",
|
"token-stats": "{{token}} Stats",
|
||||||
"token-fees-collected": "Token Fees Collected",
|
"token-fees-collected": "Token Fees Collected",
|
||||||
"token-not-found": "Token Not Found",
|
"token-not-found": "Token Not Found",
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"oracle-confidence": "Oracle Confidence",
|
"oracle-confidence": "Oracle Confidence",
|
||||||
"oracle-staleness": "Oracle Staleness",
|
"oracle-staleness": "Oracle Staleness",
|
||||||
"parameters": "Parameters",
|
"parameters": "Parameters",
|
||||||
|
"rate-curve": "Interest Rate Curve",
|
||||||
"token-stats": "{{token}} Stats",
|
"token-stats": "{{token}} Stats",
|
||||||
"token-fees-collected": "Token Fees Collected",
|
"token-fees-collected": "Token Fees Collected",
|
||||||
"token-not-found": "Token Not Found",
|
"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": "查不到幣種",
|
||||||
|
|
Loading…
Reference in New Issue