Merge pull request #76 from blockworks-foundation/30-day-interest-chart

30 day interest and funding charts
This commit is contained in:
tjshipe 2021-12-14 17:50:46 -05:00 committed by GitHub
commit 485e26c6fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 461 additions and 106 deletions

View File

@ -7,9 +7,11 @@ import { numberCompactFormatter } from '../utils'
interface ChartProps {
data: any
hideRangeFilters?: boolean
title?: string
xAxis: string
yAxis: string
yAxisWidth?: number
type: string
labelFormat: (x) => ReactNode
tickFormat?: (x) => any
@ -23,6 +25,8 @@ const Chart: FunctionComponent<ChartProps> = ({
labelFormat,
tickFormat,
type,
hideRangeFilters,
yAxisWidth,
}) => {
const [mouseData, setMouseData] = useState<string | null>(null)
const [daysToShow, setDaysToShow] = useState(30)
@ -87,32 +91,34 @@ const Chart: FunctionComponent<ChartProps> = ({
</>
)}
</div>
<div className="flex h-5">
<button
className={`default-transition font-bold mx-3 text-th-fgd-1 text-xs hover:text-th-primary focus:outline-none ${
daysToShow === 1 && 'text-th-primary'
}`}
onClick={() => setDaysToShow(1)}
>
24H
</button>
<button
className={`default-transition font-bold mx-3 text-th-fgd-1 text-xs hover:text-th-primary focus:outline-none ${
daysToShow === 7 && 'text-th-primary'
}`}
onClick={() => setDaysToShow(7)}
>
7D
</button>
<button
className={`default-transition font-bold ml-3 text-th-fgd-1 text-xs hover:text-th-primary focus:outline-none ${
daysToShow === 30 && 'text-th-primary'
}`}
onClick={() => setDaysToShow(30)}
>
30D
</button>
</div>
{!hideRangeFilters ? (
<div className="flex h-5">
<button
className={`default-transition font-bold mx-3 text-th-fgd-1 text-xs hover:text-th-primary focus:outline-none ${
daysToShow === 1 && 'text-th-primary'
}`}
onClick={() => setDaysToShow(1)}
>
24H
</button>
<button
className={`default-transition font-bold mx-3 text-th-fgd-1 text-xs hover:text-th-primary focus:outline-none ${
daysToShow === 7 && 'text-th-primary'
}`}
onClick={() => setDaysToShow(7)}
>
7D
</button>
<button
className={`default-transition font-bold ml-3 text-th-fgd-1 text-xs hover:text-th-primary focus:outline-none ${
daysToShow === 30 && 'text-th-primary'
}`}
onClick={() => setDaysToShow(30)}
>
30D
</button>
</div>
) : null}
</div>
{width > 0 && type === 'area' ? (
<AreaChart
@ -172,7 +178,7 @@ const Chart: FunctionComponent<ChartProps> = ({
: (v) => numberCompactFormatter.format(v)
}
type="number"
width={50}
width={yAxisWidth || 50}
/>
</AreaChart>
) : null}
@ -219,6 +225,7 @@ const Chart: FunctionComponent<ChartProps> = ({
/>
<YAxis
dataKey={yAxis}
interval="preserveStartEnd"
axisLine={false}
hide={data.length > 0 ? false : true}
dx={-10}
@ -234,7 +241,7 @@ const Chart: FunctionComponent<ChartProps> = ({
: (v) => numberCompactFormatter.format(v)
}
type="number"
width={50}
width={yAxisWidth || 50}
/>
</BarChart>
) : null}

View File

@ -19,22 +19,22 @@ export default function Pagination({
<button
onClick={firstPage}
disabled={page === 1}
className={`border border-th-bkg-4 px-1 py-1 ${
className={`bg-th-bkg-4 px-1 py-1 ${
page !== 1
? 'hover:text-th-primary hover:cursor-pointer'
: 'hover:cursor-not-allowed'
}`}
} disabled:text-th-fgd-4`}
>
<ChevronDoubleLeftIcon className={`w-5 h-5`} />
</button>
<button
onClick={previousPage}
disabled={page === 1}
className={`border border-th-bkg-4 px-1 py-1 ml-2 ${
className={`bg-th-bkg-4 px-1 py-1 ml-2 ${
page !== 1
? 'hover:text-th-primary hover:cursor-pointer'
: 'hover:cursor-not-allowed'
}`}
} disabled:text-th-fgd-4`}
>
<ChevronLeftIcon className={`w-5 h-5`} />
</button>
@ -46,22 +46,22 @@ export default function Pagination({
<button
onClick={nextPage}
disabled={page === totalPages}
className={`px-1 py-1 border border-th-bkg-4 ml-2 ${
className={`px-1 py-1 bg-th-bkg-4 ml-2 ${
page !== totalPages
? 'hover:text-th-primary hover:cursor-pointer'
: 'hover:cursor-not-allowed'
}`}
} disabled:text-th-fgd-4`}
>
<ChevronRightIcon className={`w-5 h-5`} />
</button>
<button
onClick={lastPage}
disabled={page === totalPages}
className={`px-1 py-1 border border-th-bkg-4 ml-2 ${
className={`px-1 py-1 bg-th-bkg-4 ml-2 ${
page !== totalPages
? 'hover:text-th-primary hover:cursor-pointer'
: 'hover:cursor-not-allowed'
}`}
} disabled:text-th-fgd-4`}
>
<ChevronDoubleRightIcon className={`w-5 h-5`} />
</button>

View File

@ -1,13 +1,20 @@
import { useEffect, useMemo, useState } from 'react'
import dayjs from 'dayjs'
import useMangoStore from '../../stores/useMangoStore'
import { Table, Td, Th, TrBody, TrHead } from '../TableElements'
import { isEmpty } from 'lodash'
import { useTranslation } from 'next-i18next'
import Select from '../Select'
import Loading from '../Loading'
import Pagination from '../Pagination'
import usePagination from '../../hooks/usePagination'
import { roundToDecimal } from '../../utils'
import Chart from '../Chart'
import Switch from '../Switch'
import useLocalStorageState from '../../hooks/useLocalStorageState'
import { handleDustTicks } from './AccountInterest'
const utc = require('dayjs/plugin/utc')
dayjs.extend(utc)
import { exportDataToCSV } from '../../utils/export'
import Button from '../Button'
import { SaveIcon } from '@heroicons/react/outline'
@ -31,6 +38,11 @@ const AccountFunding = () => {
firstPage,
lastPage,
} = usePagination(hourlyFunding[selectedAsset])
const [hideFundingDust, setHideFundingDust] = useLocalStorageState(
'hideFundingDust',
false
)
const [chartData, setChartData] = useState([])
const mangoAccountPk = useMemo(() => {
return mangoAccount.publicKey.toString()
@ -69,13 +81,24 @@ const AccountFunding = () => {
}, [selectedAsset, hourlyFunding])
useEffect(() => {
const hideDust = []
const fetchFundingStats = async () => {
const response = await fetch(
`https://mango-transaction-log.herokuapp.com/v3/stats/total-funding?mango-account=${mangoAccountPk}`
)
const parsedResponse = await response.json()
setFundingStats(Object.entries(parsedResponse))
if (hideFundingDust) {
Object.entries(parsedResponse).forEach((r: any) => {
const funding = r[1].total_funding
if (Math.abs(funding) > 1) {
hideDust.push(r)
}
})
setFundingStats(hideDust)
} else {
setFundingStats(Object.entries(parsedResponse))
}
}
const fetchHourlyFundingStats = async () => {
@ -84,7 +107,17 @@ const AccountFunding = () => {
`https://mango-transaction-log.herokuapp.com/v3/stats/hourly-funding?mango-account=${mangoAccountPk}`
)
const parsedResponse = await response.json()
const assets = Object.keys(parsedResponse)
let assets
if (hideFundingDust) {
const assetsToShow = hideDust.map((a) => a[0])
assets = Object.keys(parsedResponse).filter((a) =>
assetsToShow.includes(a)
)
setSelectedAsset(assetsToShow[0])
} else {
assets = Object.keys(parsedResponse)
}
const stats = {}
for (const asset of assets) {
@ -110,23 +143,75 @@ const AccountFunding = () => {
setHourlyFunding(stats)
}
fetchFundingStats()
fetchHourlyFundingStats()
}, [mangoAccountPk])
const getStats = async () => {
await fetchFundingStats()
fetchHourlyFundingStats()
}
getStats()
}, [mangoAccountPk, hideFundingDust])
useEffect(() => {
if (hourlyFunding[selectedAsset]) {
const start = new Date(
// @ts-ignore
dayjs().utc().hour(0).minute(0).subtract(29, 'day')
).getTime()
const filtered = hourlyFunding[selectedAsset].filter(
(d) => new Date(d.time).getTime() > start
)
const dailyFunding = []
filtered.forEach((d) => {
const found = dailyFunding.find(
(x) =>
dayjs(x.time).format('DD-MMM') === dayjs(d.time).format('DD-MMM')
)
if (found) {
const newFunding = d.total_funding
found.funding = found.funding + newFunding
} else {
dailyFunding.push({
time: new Date(d.time).getTime(),
funding: d.total_funding,
})
}
})
setChartData(dailyFunding.reverse())
}
}, [hourlyFunding, selectedAsset])
useEffect(() => {
if (!selectedAsset && Object.keys(hourlyFunding).length > 0) {
setSelectedAsset(Object.keys(hourlyFunding)[0])
}
}, [hourlyFunding])
const increaseYAxisWidth = !!chartData.find((data) => data.value < 0.001)
return (
<>
<div className="pb-4 text-th-fgd-1 text-lg">
{t('total-funding-stats')}
<Button
className={`float-right text-xs h-8 pt-0 pb-0 pl-3 pr-3`}
onClick={exportFundingDataToCSV}
>
<div className={`flex items-center`}>
<SaveIcon className={`h-4 w-4 mr-1.5`} />
{t('export-data')}
</div>
</Button>
<div className="flex items-center justify-between pb-4">
<div className="text-th-fgd-1 text-lg">{t('total-funding')}</div>
<div className="flex items-center">
<Button
className={`float-right text-xs h-8 pt-0 pb-0 pl-3 pr-3`}
onClick={exportFundingDataToCSV}
>
<div className={`flex items-center whitespace-nowrap`}>
<SaveIcon className={`h-4 w-4 mr-1.5`} />
{t('export-data')}
</div>
</Button>
<Switch
checked={hideFundingDust}
className="ml-2 text-xs"
onChange={() => setHideFundingDust(!hideFundingDust)}
>
{t('hide-dust')}
</Switch>
</div>
</div>
{mangoAccount ? (
<div>
@ -134,7 +219,7 @@ const AccountFunding = () => {
<thead>
<TrHead>
<Th>{t('token')}</Th>
<Th>{t('total-funding')}</Th>
<Th>{t('total-funding')} (USDC)</Th>
</TrHead>
</thead>
<tbody>
@ -142,7 +227,9 @@ const AccountFunding = () => {
<TrBody index={0}>
<td colSpan={4}>
<div className="flex">
<div className="mx-auto py-4">{t('no-funding')}</div>
<div className="mx-auto py-4 text-th-fgd-3">
{t('no-funding')}
</div>
</div>
</td>
</TrBody>
@ -173,7 +260,7 @@ const AccountFunding = () => {
}`}
>
{stats.total_funding
? `$${stats.total_funding?.toFixed(6)}`
? `${stats.total_funding?.toFixed(6)}`
: '-'}
</div>
</Td>
@ -226,6 +313,33 @@ const AccountFunding = () => {
))}
</div>
</div>
{selectedAsset && chartData.length > 0 ? (
<div className="flex flex-col sm:flex-row space-x-0 sm:space-x-4 w-full">
<div
className="border border-th-bkg-4 relative mb-6 p-4 rounded-md w-full"
style={{ height: '330px' }}
>
<Chart
hideRangeFilters
title={t('funding-chart-title', {
symbol: selectedAsset,
})}
xAxis="time"
yAxis="funding"
data={chartData}
labelFormat={(x) =>
x &&
`${x.toLocaleString(undefined, {
maximumFractionDigits: 6,
})} USDC`
}
tickFormat={handleDustTicks}
type="bar"
yAxisWidth={increaseYAxisWidth ? 70 : 50}
/>
</div>
</div>
) : null}
<div>
<div>
{paginatedData.length ? (
@ -233,24 +347,21 @@ const AccountFunding = () => {
<thead>
<TrHead>
<Th>{t('time')}</Th>
<Th>{t('funding')}</Th>
<Th>{t('funding')} (USDC)</Th>
</TrHead>
</thead>
<tbody>
{paginatedData.map((stat, index) => {
const date = new Date(stat.time)
// @ts-ignore
const utc = dayjs.utc(stat.time).format()
return (
<TrBody index={index} key={stat.time}>
<Td>
{date.toLocaleDateString()}{' '}
{date.toLocaleTimeString()}
</Td>
<Td>{dayjs(utc).format('DD/MM/YY, h:mma')}</Td>
<Td>
{stat.total_funding.toFixed(
QUOTE_DECIMALS + 1
)}{' '}
USDC
)}
</Td>
</TrBody>
)
@ -258,7 +369,7 @@ const AccountFunding = () => {
</tbody>
</Table>
) : (
<div className="flex justify-center w-full bg-th-bkg-3 py-4">
<div className="flex justify-center w-full bg-th-bkg-3 py-4 text-th-fgd-3">
{t('no-funding')}
</div>
)}
@ -274,10 +385,10 @@ const AccountFunding = () => {
</div>
</>
) : loading ? (
<div className="flex justify-center my-8">
<div>
<Loading />
</div>
<div className="pt-8 space-y-2">
<div className="animate-pulse bg-th-bkg-3 h-12 rounded-md w-full" />
<div className="animate-pulse bg-th-bkg-3 h-12 rounded-md w-full" />
<div className="animate-pulse bg-th-bkg-3 h-12 rounded-md w-full" />
</div>
) : null}
</>

View File

@ -1,17 +1,29 @@
import { getTokenBySymbol } from '@blockworks-foundation/mango-client'
import { useEffect, useMemo, useState } from 'react'
import dayjs from 'dayjs'
// import { CurrencyDollarIcon } from '@heroicons/react/outline'
import useMangoStore from '../../stores/useMangoStore'
import Select from '../Select'
import { Table, Td, Th, TrBody, TrHead } from '../TableElements'
import { useTranslation } from 'next-i18next'
import { isEmpty } from 'lodash'
import usePagination from '../../hooks/usePagination'
import { roundToDecimal } from '../../utils/'
import {
// formatUsdValue,
numberCompactFormatter,
roundToDecimal,
} from '../../utils/'
import Pagination from '../Pagination'
import { useViewport } from '../../hooks/useViewport'
import { breakpoints } from '../TradePageGrid'
import { ExpandableRow } from '../TableElements'
import MobileTableHeader from '../mobile/MobileTableHeader'
import Chart from '../Chart'
import Switch from '../Switch'
import useLocalStorageState from '../../hooks/useLocalStorageState'
const utc = require('dayjs/plugin/utc')
dayjs.extend(utc)
import { exportDataToCSV } from '../../utils/export'
import { SaveIcon } from '@heroicons/react/outline'
import Button from '../Button'
@ -23,14 +35,32 @@ interface InterestStats {
}
}
export const handleDustTicks = (v) =>
v < 0.000001
? v === 0
? 0
: v.toExponential()
: numberCompactFormatter.format(v)
const handleUsdDustTicks = (v) =>
v < 0.000001
? v === 0
? '$0'
: `$${v.toExponential()}`
: `$${numberCompactFormatter.format(v)}`
const AccountInterest = () => {
const { t } = useTranslation('common')
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
const [interestStats, setInterestStats] = useState<any>([])
const [hourlyInterestStats, setHourlyInterestStats] = useState<any>({})
// const [totalInterestValue, setTotalInterestValue] = useState(null)
const [loading, setLoading] = useState(false)
const [selectedAsset, setSelectedAsset] = useState<string>('')
const [chartData, setChartData] = useState([])
const {
paginatedData,
setData,
@ -43,6 +73,10 @@ const AccountInterest = () => {
} = usePagination(hourlyInterestStats[selectedAsset])
const { width } = useViewport()
const isMobile = width ? width < breakpoints.md : false
const [hideInterestDust, sethideInterestDust] = useLocalStorageState(
'hideInterestDust',
false
)
const mangoAccountPk = useMemo(() => {
return mangoAccount.publicKey.toString()
@ -99,22 +133,58 @@ const AccountInterest = () => {
}, [hourlyInterestStats])
useEffect(() => {
const hideDust = []
const fetchInterestStats = async () => {
const response = await fetch(
`https://mango-transaction-log.herokuapp.com/v3/stats/total-interest-earned?mango-account=${mangoAccountPk}`
)
const parsedResponse: InterestStats = await response.json()
setInterestStats(Object.entries(parsedResponse))
if (hideInterestDust) {
Object.entries(parsedResponse).forEach((r) => {
const tokens = groupConfig.tokens
const token = tokens.find((t) => t.symbol === r[0])
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
const price = mangoGroup.getPrice(tokenIndex, mangoCache).toNumber()
const interest =
r[1].total_deposit_interest > 0
? r[1].total_deposit_interest
: r[1].total_borrow_interest
if (price * interest > 1) {
hideDust.push(r)
}
})
setInterestStats(hideDust)
} else {
const stats = Object.entries(parsedResponse)
const filterMicroBalances = stats.filter(([symbol, stats]) => {
const decimals = getTokenBySymbol(groupConfig, symbol).decimals
const smallestValue = Math.pow(10, (decimals + 1) * -1)
return (
stats.total_borrow_interest > smallestValue ||
stats.total_deposit_interest > smallestValue
)
})
setInterestStats(filterMicroBalances)
}
}
const fetchHourlyInterestStats = async () => {
setLoading(true)
const response = await fetch(
`https://mango-transaction-log.herokuapp.com/v3/stats/hourly-interest?mango-account=${mangoAccountPk}`
`https://mango-transaction-log.herokuapp.com/v3/stats/hourly-interest-prices?mango-account=${mangoAccountPk}`
)
const parsedResponse = await response.json()
const assets = Object.keys(parsedResponse)
let assets
if (hideInterestDust) {
const assetsToShow = hideDust.map((a) => a[0])
assets = Object.keys(parsedResponse).filter((a) =>
assetsToShow.includes(a)
)
setSelectedAsset(assetsToShow[0])
} else {
assets = Object.keys(parsedResponse)
}
const stats = {}
for (const asset of assets) {
@ -122,7 +192,7 @@ const AccountInterest = () => {
const token = getTokenBySymbol(groupConfig, asset)
stats[asset] = x
.map(([key, value]) => {
.map(([key, value, price]) => {
const borrows = roundToDecimal(
value.borrow_interest,
token.decimals + 1
@ -132,36 +202,115 @@ const AccountInterest = () => {
token.decimals + 1
)
if (borrows > 0 || deposits > 0) {
return { ...value, time: key }
return { ...value, time: key, ...price }
} else {
return null
}
})
.filter((x) => x)
.reverse()
if (stats[asset].length === 0) {
delete stats[asset]
}
}
setLoading(false)
setHourlyInterestStats(stats)
}
fetchHourlyInterestStats()
fetchInterestStats()
}, [mangoAccountPk])
const getStats = async () => {
await fetchInterestStats()
fetchHourlyInterestStats()
}
getStats()
}, [mangoAccountPk, hideInterestDust])
// For net interest value to be useful we would need to filter on the dates of the user's financial year and convert the USD value below to the user's home currency.
// useEffect(() => {
// console.log(Object.entries(hourlyInterestStats).flat(Infinity))
// const totalInterestValue = Object.entries(hourlyInterestStats)
// .flat(Infinity)
// .reduce((a: number, c: any) => {
// if (c.time) {
// return (
// a + (c.deposit_interest * c.price - c.borrow_interest * c.price)
// )
// } else return a
// }, 0)
// setTotalInterestValue(totalInterestValue)
// }, [hourlyInterestStats])
useEffect(() => {
if (hourlyInterestStats[selectedAsset]) {
const start = new Date(
// @ts-ignore
dayjs().utc().hour(0).minute(0).subtract(29, 'day')
).getTime()
const filtered = hourlyInterestStats[selectedAsset].filter(
(d) => new Date(d.time).getTime() > start
)
const dailyInterest = []
filtered.forEach((d) => {
const found = dailyInterest.find(
(x) =>
dayjs(x.time).format('DD-MMM') === dayjs(d.time).format('DD-MMM')
)
if (found) {
const newInterest =
d.borrow_interest > 0 ? d.borrow_interest * -1 : d.deposit_interest
const newValue =
d.borrow_interest > 0
? d.borrow_interest * -1 * d.price
: d.deposit_interest * d.price
found.interest = found.interest + newInterest
found.value = found.value + newValue
} else {
dailyInterest.push({
// @ts-ignore
time: new Date(d.time).getTime(),
interest:
d.borrow_interest > 0
? d.borrow_interest * -1
: d.deposit_interest,
value:
d.borrow_interest > 0
? d.borrow_interest * d.price * -1
: d.deposit_interest * d.price,
})
}
})
setChartData(dailyInterest.reverse())
}
}, [hourlyInterestStats, selectedAsset])
const increaseYAxisWidth = !!chartData.find((data) => data.value < 0.001)
return (
<>
<div className="pb-4 text-th-fgd-1 text-lg">
{t('interest-earned')}
<Button
className={`float-right text-xs h-8 pt-0 pb-0 pl-3 pr-3`}
onClick={exportInterestDataToCSV}
>
<div className={`flex items-center`}>
<SaveIcon className={`h-4 w-4 mr-1.5`} />
{t('export-data')}
</div>
</Button>
</div>{' '}
<div className="flex items-center justify-between pb-4">
<div className="text-th-fgd-1 text-lg">{t('interest-earned')}</div>
<div className="flex items-center">
<Button
className={`float-right text-xs h-8 pt-0 pb-0 pl-3 pr-3`}
onClick={exportInterestDataToCSV}
>
<div className={`flex items-center whitespace-nowrap`}>
<SaveIcon className={`h-4 w-4 mr-1.5`} />
{t('export-data')}
</div>
</Button>
<Switch
checked={hideInterestDust}
className="ml-2 text-xs"
onChange={() => sethideInterestDust(!hideInterestDust)}
>
{t('hide-dust')}
</Switch>
</div>
</div>
{mangoAccount ? (
<div>
{!isMobile ? (
@ -288,10 +437,26 @@ const AccountInterest = () => {
})}
</>
)}
{/* {totalInterestValue > 0 ? (
<div className="border border-th-bkg-4 mt-8 p-3 sm:p-4 rounded-md sm:rounded-lg">
<div className="font-bold pb-0.5 text-th-fgd-1 text-xs sm:text-sm">
{t('net-interest-value')}
</div>
<div className="pb-0.5 sm:pb-2 text-th-fgd-3 text-xs">
{t('net-interest-value-desc')}
</div>
<div className="flex items-center">
<CurrencyDollarIcon className="flex-shrink-0 h-5 w-5 sm:h-7 sm:w-7 mr-1.5 text-th-primary" />
<div className="font-bold text-th-fgd-1 text-xl sm:text-2xl">
{formatUsdValue(totalInterestValue)}
</div>
</div>
</div>
) : null} */}
<>
{!isEmpty(hourlyInterestStats) && !loading ? (
<>
<div className="flex items-center justify-between pb-4 pt-6 w-full">
<div className="flex items-center justify-between pb-4 pt-8 w-full">
<div className="text-th-fgd-1 text-lg">{t('history')}</div>
<Select
value={selectedAsset}
@ -330,6 +495,50 @@ const AccountInterest = () => {
))}
</div>
</div>
{selectedAsset && chartData.length > 0 ? (
<div className="flex flex-col sm:flex-row space-x-0 sm:space-x-4 w-full">
<div
className="border border-th-bkg-4 relative mb-6 p-4 rounded-md w-full sm:w-1/2"
style={{ height: '330px' }}
>
<Chart
hideRangeFilters
title={t('interest-chart-title', {
symbol: selectedAsset,
})}
xAxis="time"
yAxis="interest"
data={chartData}
labelFormat={(x) => x && x.toFixed(token.decimals + 1)}
tickFormat={handleDustTicks}
type="bar"
yAxisWidth={increaseYAxisWidth ? 70 : 50}
/>
</div>
<div
className="border border-th-bkg-4 relative mb-6 p-4 rounded-md w-full sm:w-1/2"
style={{ height: '330px' }}
>
<Chart
hideRangeFilters
title={t('interest-chart-value-title', {
symbol: selectedAsset,
})}
xAxis="time"
yAxis="value"
data={chartData}
labelFormat={(x) =>
x && x < 0
? `-$${Math.abs(x).toFixed(token.decimals + 1)}`
: `$${x.toFixed(token.decimals + 1)}`
}
tickFormat={handleUsdDustTicks}
type="bar"
yAxisWidth={increaseYAxisWidth ? 70 : 50}
/>
</div>
</div>
) : null}
<div>
<div>
{paginatedData.length ? (
@ -338,20 +547,16 @@ const AccountInterest = () => {
<TrHead>
<Th>{t('time')}</Th>
<Th>{t('interest')}</Th>
<Th>{t('value')}</Th>
</TrHead>
</thead>
<tbody>
{paginatedData.map((stat, index) => {
const date = new Date(stat.time)
// @ts-ignore
const utc = dayjs.utc(stat.time).format()
return (
<TrBody index={index} key={stat.time}>
<Td>
<div>{date.toLocaleDateString()}</div>
<div className="text-xs text-th-fgd-3">
{date.toLocaleTimeString()}
</div>
</Td>
<Td>{dayjs(utc).format('DD/MM/YY, h:mma')}</Td>
<Td>
{stat.borrow_interest > 0
? `-${stat.borrow_interest.toFixed(
@ -362,13 +567,22 @@ const AccountInterest = () => {
)}{' '}
{selectedAsset}
</Td>
<Td>
{stat.borrow_interest > 0
? `-$${(
stat.borrow_interest * stat.price
).toFixed(token.decimals + 1)}`
: `$${(
stat.deposit_interest * stat.price
).toFixed(token.decimals + 1)}`}
</Td>
</TrBody>
)
})}
</tbody>
</Table>
) : (
<div className="flex justify-center w-full bg-th-bkg-3 py-4">
<div className="flex justify-center w-full bg-th-bkg-3 py-4 text-th-fgd-3">
{t('no-interest')}
</div>
)}

View File

@ -1,6 +1,5 @@
import { useMemo } from 'react'
import {
// ChartBarIcon,
ScaleIcon,
CurrencyDollarIcon,
GiftIcon,

View File

@ -118,11 +118,13 @@
"fee": "Fee",
"fee-discount": "Fee Discount",
"funding": "Funding",
"funding-chart-title": "Funding (Last 30 days)",
"get-started": "Get Started",
"health": "Health",
"health-check": "Account Health Check",
"health-ratio": "Health Ratio",
"hide-all": "Hide all from Nav",
"hide-dust": "Hide dust",
"high": "High",
"history": "History",
"history-empty": "History empty.",
@ -138,7 +140,9 @@
"insufficient-balance-withdraw": "Insufficient balance. Borrow funds to withdraw",
"insufficient-sol": "You need 0.035 SOL to create a Mango Account.",
"interest": "Interest",
"interest-earned": "Total Interest Earned/Paid",
"interest-chart-title": "{{symbol}} Interest (Last 30 days)",
"interest-chart-value-title": "{{symbol}} Interest Value (Last 30 days)",
"interest-earned": "Total Interest",
"interest-info": "Interest is earned continuously on all deposits.",
"intro-feature-1": "Crosscollateralized leverage trading",
"intro-feature-2": "All assets count as collateral to trade or borrow",
@ -198,16 +202,18 @@
"name-your-account": "Name Your Account",
"net": "Net",
"net-balance": "Net Balance",
"net-interest-value": "Net Interest Value",
"net-interest-value-desc": "Calculated at the time it was earned/paid. This might be useful at tax time.",
"new-account": "New",
"next": "Next",
"no-account-found": "No Account Found",
"no-address": "No {{tokenSymbol}} wallet address found",
"no-balances": "No balances",
"no-borrows": "No borrows found.",
"no-funding": "No funding earned/paid",
"no-funding": "No funding earned or paid",
"no-history": "No trade history",
"no-interest": "No interest earned/paid",
"no-margin": "No Margin Accounts Found",
"no-interest": "No interest earned or paid",
"no-margin": "No margin accounts found",
"no-orders": "No open orders",
"no-perp": "No perp positions",
"no-unsettled": "There are no unsettled funds",

View File

@ -116,12 +116,14 @@
"export-data-success": "CSV exported successfully",
"fee": "Tarifa",
"fee-discount": "comisiones",
"funding": "Funding",
"funding": "Fondos",
"funding-chart-title": "Fondos (últimos 30 días)",
"get-started": "Comenzar",
"health": "Salud",
"health-check": "Verificación del estado de la cuenta",
"health-ratio": "Relación de salud",
"hide-all": "Ocultar todo de Nav",
"hide-dust": "Ocultar saldos pequeños",
"high": "Alta",
"history": "La Historia",
"history-empty": "History empty.",
@ -137,6 +139,8 @@
"insufficient-balance-withdraw": "Saldo insuficiente. Pedir prestados fondos para retirar",
"insufficient-sol": "Necesita 0.035 SOL para crear una cuenta de mango.",
"interest": "Interés",
"interest-chart-title": "{{symbol}} Interés (últimos 30 días)",
"interest-chart-value-title": "{{symbol}} Valor de interés (últimos 30 días)",
"interest-earned": "Interés total devengado / pagado",
"interest-info": "El interés se gana continuamente en todos los depósitos.",
"intro-feature-1": "Operaciones de apalancamiento con garantía cruzada",
@ -196,6 +200,8 @@
"name-your-account": "Nombra tu cuenta",
"net": "Neto",
"net-balance": "Balance neto",
"net-interest-value": "Valor de interés neto",
"net-interest-value-desc": "Calculado en el momento en que se ganó / pagó. Esto podría ser útil al momento de impuestos.",
"new-account": "Nuevo",
"next": "Próximo",
"no-account-found": "No Account Found",

View File

@ -117,11 +117,13 @@
"fee": "费率",
"fee-discount": "费率折扣",
"funding": "资金费",
"funding-chart-title": "资金费",
"get-started": "开始",
"health": "健康度",
"health-check": "帐户健康检查",
"health-ratio": "健康比率",
"hide-all": "在导航栏中隐藏全部",
"hide-dust": "Hide dust",
"high": "高",
"history": "历史",
"history-empty": "没有历史",
@ -137,6 +139,8 @@
"insufficient-balance-withdraw": "帐户余额不够。您得以借贷而前往",
"insufficient-sol": "创建Mango帐户最少需要0.035 SOL。",
"interest": "利息",
"interest-chart-title": "{{symbol}} 利息",
"interest-chart-value-title": "{{symbol}} 利息价值",
"interest-earned": "存借利息",
"interest-info": "您的存款会持续赚取利息。",
"intro-feature-1": "交叉质押的杠杆交易",
@ -196,6 +200,8 @@
"name-your-account": "给帐户标签",
"net": "净",
"net-balance": "净余额",
"net-interest-value": "Net Interest Value",
"net-interest-value-desc": "Calculated at the time it was earned/paid. This might be useful at tax time.",
"new-account": "新子帐户",
"next": "前往",
"no-account-found": "您没有帐户",

View File

@ -117,11 +117,13 @@
"fee": "費率",
"fee-discount": "費率折扣",
"funding": "資金費",
"funding-chart-title": "資金費",
"get-started": "開始",
"health": "健康度",
"health-check": "帳戶健康檢查",
"health-ratio": "健康比率",
"hide-all": "在導航欄中隱藏全部",
"hide-dust": "Hide dust",
"high": "高",
"history": "歷史",
"history-empty": "沒有歷史",
@ -137,6 +139,8 @@
"insufficient-balance-withdraw": "帳戶餘額不夠。您得以借貸而前往",
"insufficient-sol": "創建Mango帳戶最少需要0.035 SOL。",
"interest": "利息",
"interest-chart-title": "{{symbol}} 利息",
"interest-chart-value-title": "{{symbol}} 利息價值",
"interest-earned": "存借利息",
"interest-info": "您的存款會持續賺取利息。",
"intro-feature-1": "交叉質押的槓桿交易",
@ -196,6 +200,8 @@
"name-your-account": "給帳戶標籤",
"net": "淨",
"net-balance": "淨餘額",
"net-interest-value": "Net Interest Value",
"net-interest-value-desc": "Calculated at the time it was earned/paid. This might be useful at tax time.",
"new-account": "新子帳戶",
"next": "前往",
"no-account-found": "您沒有帳戶",