Merge pull request #305 from blockworks-foundation/rate-curve-chart

add rate curve chart
This commit is contained in:
saml33 2023-10-31 21:35:09 +11:00 committed by GitHub
commit 4494f4b540
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 263 additions and 18 deletions

View File

@ -0,0 +1,255 @@
/* 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: number
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
return [currentRate, currentUtil]
}, [bank])
const rateCurveChartData = useMemo(() => {
if (!bank) return []
const defaults = [
{ util: 0, rate: 0 },
{
util: bank.util0.toNumber() * 100,
rate: bank.rate0.toNumber() * 100,
},
{
util: bank.util1.toNumber() * 100,
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) => a.util - 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="linear"
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()}%`}
type="number"
/>
<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

View File

@ -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 ? (

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -30,6 +30,7 @@
"oracle-confidence": "预言机可信度",
"oracle-staleness": "预言机不新鲜性",
"parameters": "参数",
"rate-curve": "Interest Rate Curve",
"token-stats": "币种细节",
"token-fees-collected": "收取的币种费用",
"token-not-found": "查不到币种",

View File

@ -30,6 +30,7 @@
"oracle-confidence": "預言機可信度",
"oracle-staleness": "預言機不新鮮性",
"parameters": "參數",
"rate-curve": "Interest Rate Curve",
"token-stats": "幣種細節",
"token-fees-collected": "收取的幣種費用",
"token-not-found": "查不到幣種",