mango-ui-v3/components/account_page/AccountFunding.tsx

443 lines
15 KiB
TypeScript
Raw Normal View History

2021-10-17 19:34:24 -07:00
import { useEffect, useMemo, useState } from 'react'
2021-11-25 20:17:48 -08:00
import dayjs from 'dayjs'
2021-10-17 19:34:24 -07:00
import useMangoStore from '../../stores/useMangoStore'
2022-03-20 17:06:13 -07:00
import {
Table,
TableDateDisplay,
Td,
Th,
TrBody,
TrHead,
} from '../TableElements'
2022-03-29 19:46:21 -07:00
import isEmpty from 'lodash/isEmpty'
import { useTranslation } from 'next-i18next'
2021-11-06 10:00:10 -07:00
import Select from '../Select'
import Pagination from '../Pagination'
import usePagination from '../../hooks/usePagination'
import { roundToDecimal } from '../../utils'
2021-11-25 20:17:48 -08:00
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)
2021-12-02 16:37:20 -08:00
import { exportDataToCSV } from '../../utils/export'
import Button from '../Button'
import { SaveIcon } from '@heroicons/react/outline'
2021-11-06 10:00:10 -07:00
const QUOTE_DECIMALS = 6
2021-10-17 19:34:24 -07:00
const AccountFunding = () => {
const { t } = useTranslation('common')
2021-10-17 19:34:24 -07:00
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const [fundingStats, setFundingStats] = useState<any>([])
2021-11-06 10:00:10 -07:00
const [hourlyFunding, setHourlyFunding] = useState<any>([])
2022-01-19 18:45:47 -08:00
const [selectedAsset, setSelectedAsset] = useState<string>('')
const [loadHourlyStats, setLoadHourlyStats] = useState(false)
const [loadTotalStats, setLoadTotalStats] = useState(false)
2021-11-06 10:00:10 -07:00
const {
paginatedData,
2021-11-06 10:00:10 -07:00
setData,
totalPages,
nextPage,
previousPage,
page,
firstPage,
lastPage,
2022-01-20 10:09:19 -08:00
} = usePagination(hourlyFunding[selectedAsset] || [])
2021-11-25 20:17:48 -08:00
const [hideFundingDust, setHideFundingDust] = useLocalStorageState(
'hideFundingDust',
false
)
2022-03-29 06:02:29 -07:00
const [chartData, setChartData] = useState<any[]>([])
2021-10-17 22:17:07 -07:00
2021-10-17 19:34:24 -07:00
const mangoAccountPk = useMemo(() => {
2022-03-29 06:02:29 -07:00
if (mangoAccount) {
return mangoAccount.publicKey.toString()
}
2021-10-17 19:34:24 -07:00
}, [mangoAccount])
2021-12-02 16:37:20 -08:00
const exportFundingDataToCSV = () => {
const assets = Object.keys(hourlyFunding)
2022-03-29 06:02:29 -07:00
let dataToExport: any[] = []
2021-12-02 16:37:20 -08:00
for (const asset of assets) {
dataToExport = [
...dataToExport,
...hourlyFunding[asset].map((funding) => {
2021-12-06 11:26:22 -08:00
const timestamp = new Date(funding.time)
2021-12-02 16:37:20 -08:00
return {
2021-12-06 11:26:22 -08:00
timestamp: `${timestamp.toLocaleDateString()} ${timestamp.toLocaleTimeString()}`,
2021-12-02 16:37:20 -08:00
asset: asset,
amount: funding.total_funding,
}
}),
]
}
2021-12-06 11:26:22 -08:00
const title = `${
2022-03-29 06:02:29 -07:00
mangoAccount?.name || mangoAccount?.publicKey
2021-12-06 11:26:22 -08:00
}-Funding-${new Date().toLocaleDateString()}`
2021-12-02 16:37:20 -08:00
const columns = ['Timestamp', 'Asset', 'Amount']
exportDataToCSV(dataToExport, title, columns, t)
}
2021-11-06 10:00:10 -07:00
useEffect(() => {
if (!isEmpty(hourlyFunding)) {
2022-01-20 10:09:19 -08:00
setData(hourlyFunding[selectedAsset] || [])
2021-11-06 10:00:10 -07:00
}
}, [selectedAsset, hourlyFunding])
2021-10-17 19:34:24 -07:00
useEffect(() => {
2022-03-29 06:02:29 -07:00
const hideDust: any[] = []
2021-10-17 19:34:24 -07:00
const fetchFundingStats = async () => {
setLoadTotalStats(true)
2021-10-17 19:34:24 -07:00
const response = await fetch(
`https://mango-transaction-log.herokuapp.com/v3/stats/total-funding?mango-account=${mangoAccountPk}`
)
2021-10-17 22:17:07 -07:00
const parsedResponse = await response.json()
2021-10-17 19:34:24 -07:00
2021-11-25 20:17:48 -08:00
if (hideFundingDust) {
Object.entries(parsedResponse).forEach((r: any) => {
const funding = r[1].total_funding
if (Math.abs(funding) > 1) {
hideDust.push(r)
}
})
setLoadTotalStats(false)
2021-11-25 20:17:48 -08:00
setFundingStats(hideDust)
} else {
setLoadTotalStats(false)
2021-11-25 20:17:48 -08:00
setFundingStats(Object.entries(parsedResponse))
}
2021-10-17 19:34:24 -07:00
}
2021-11-06 10:00:10 -07:00
const fetchHourlyFundingStats = async () => {
setLoadHourlyStats(true)
2021-11-06 10:00:10 -07:00
const response = await fetch(
`https://mango-transaction-log.herokuapp.com/v3/stats/hourly-funding?mango-account=${mangoAccountPk}`
)
const parsedResponse = await response.json()
2021-11-25 20:17:48 -08:00
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)
}
2021-11-06 10:00:10 -07:00
const stats = {}
for (const asset of assets) {
const x: any = Object.entries(parsedResponse[asset])
2021-10-17 22:17:07 -07:00
2021-11-06 10:00:10 -07:00
stats[asset] = x
.map(([key, value]) => {
const funding = roundToDecimal(
value.total_funding,
QUOTE_DECIMALS + 1
)
if (funding !== 0) {
return { ...value, time: key }
} else {
return null
}
})
.filter((x) => x)
.reverse()
}
2021-10-17 22:17:07 -07:00
setLoadHourlyStats(false)
2021-11-06 10:00:10 -07:00
setHourlyFunding(stats)
}
2021-10-17 22:17:07 -07:00
2021-11-25 20:17:48 -08:00
const getStats = async () => {
fetchFundingStats()
2021-11-25 20:17:48 -08:00
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
)
2022-03-29 06:02:29 -07:00
const dailyFunding: any[] = []
2021-11-25 20:17:48 -08:00
2022-01-19 18:45:47 -08:00
for (let i = 0; i < 30; i++) {
dailyFunding.push({
funding: 0,
time: new Date(
// @ts-ignore
dayjs().utc().hour(0).minute(0).subtract(i, 'day')
).getTime(),
})
}
2021-11-25 20:17:48 -08:00
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
}
})
2022-01-19 18:45:47 -08:00
setChartData(dailyFunding.reverse())
2021-11-25 20:17:48 -08:00
}
}, [hourlyFunding, selectedAsset])
useEffect(() => {
if (!selectedAsset && Object.keys(hourlyFunding).length > 0) {
setSelectedAsset(Object.keys(hourlyFunding)[0])
}
}, [hourlyFunding])
2021-10-17 19:34:24 -07:00
return (
<>
2022-03-21 17:20:41 -07:00
<div className="flex flex-col pb-4 sm:flex-row sm:items-center sm:space-x-3">
<div className="flex w-full items-center justify-between pb-4 sm:pb-0">
<h2>{t('total-funding')}</h2>
2021-12-14 03:50:22 -08:00
<Switch
checked={hideFundingDust}
className="ml-2 text-xs"
onChange={() => setHideFundingDust(!hideFundingDust)}
>
{t('hide-dust')}
</Switch>
</div>
2022-03-21 17:20:41 -07:00
<Button
className={`h-8 pt-0 pb-0 pl-3 pr-3 text-xs`}
onClick={exportFundingDataToCSV}
>
<div className={`flex items-center justify-center whitespace-nowrap`}>
<SaveIcon className={`mr-1.5 h-4 w-4`} />
{t('export-data')}
</div>
</Button>
2021-10-17 19:34:24 -07:00
</div>
{mangoAccount ? (
2021-10-17 22:17:07 -07:00
<div>
{loadTotalStats ? (
<div className="space-y-2">
<div className="h-12 w-full animate-pulse rounded-md bg-th-bkg-3" />
<div className="h-12 w-full animate-pulse rounded-md bg-th-bkg-3" />
<div className="h-12 w-full animate-pulse rounded-md bg-th-bkg-3" />
</div>
) : (
<Table>
<thead>
<TrHead>
<Th>{t('token')}</Th>
<Th>{t('total-funding')} (USDC)</Th>
</TrHead>
</thead>
<tbody>
{fundingStats.length === 0 ? (
2022-02-10 15:48:14 -08:00
<TrBody>
<td colSpan={4}>
<div className="flex">
<div className="mx-auto py-4 text-th-fgd-3">
{t('no-funding')}
2021-10-17 22:17:07 -07:00
</div>
</div>
</td>
</TrBody>
) : (
2022-02-10 15:48:14 -08:00
fundingStats.map(([symbol, stats]) => {
return (
2022-02-10 15:48:14 -08:00
<TrBody key={symbol}>
<Td className="w-1/2">
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${symbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
{symbol}-PERP
</div>
</Td>
<Td className="w-1/2">
<div
className={`${
stats.total_funding > 0
? 'text-th-green'
: stats.total_funding < 0
? 'text-th-red'
: 'text-th-fgd-3'
}`}
>
{stats.total_funding
2022-03-17 14:01:16 -07:00
? `${stats.total_funding?.toLocaleString(
undefined,
{
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
)}`
: '-'}
</div>
</Td>
</TrBody>
)
})
)}
</tbody>
</Table>
)}
2021-10-17 22:17:07 -07:00
2021-11-06 10:00:10 -07:00
<>
{!isEmpty(hourlyFunding) && !loadHourlyStats ? (
2021-10-17 22:17:07 -07:00
<>
<div className="flex w-full items-center justify-between pb-4 pt-6">
2022-02-22 15:30:54 -08:00
<h2>{t('history')}</h2>
2021-10-17 22:17:07 -07:00
<Select
value={selectedAsset}
onChange={(a) => setSelectedAsset(a)}
className="w-24 sm:hidden"
>
<div className="space-y-2">
{Object.keys(hourlyFunding).map((token: string) => (
<Select.Option
key={token}
value={token}
className={`default-transition relative flex w-full cursor-pointer rounded-md bg-th-bkg-1 px-3 py-3 hover:bg-th-bkg-3 focus:outline-none`}
2021-10-17 22:17:07 -07:00
>
<div className="flex w-full items-center justify-between">
2021-10-17 22:17:07 -07:00
{token}
</div>
</Select.Option>
))}
</div>
</Select>
<div className="hidden pb-4 sm:flex sm:pb-0">
2021-10-17 22:17:07 -07:00
{Object.keys(hourlyFunding).map((token: string) => (
2021-10-17 19:34:24 -07:00
<div
className={`default-transition ml-2 cursor-pointer rounded-md bg-th-bkg-3 px-2 py-1
2021-10-17 22:17:07 -07:00
${
selectedAsset === token
? `text-th-primary ring-1 ring-inset ring-th-primary`
2021-10-17 22:17:07 -07:00
: `text-th-fgd-1 opacity-50 hover:opacity-100`
}
`}
onClick={() => setSelectedAsset(token)}
key={token}
2021-10-17 19:34:24 -07:00
>
{token}-PERP
2021-10-17 19:34:24 -07:00
</div>
2021-10-17 22:17:07 -07:00
))}
</div>
</div>
2021-11-25 20:17:48 -08:00
{selectedAsset && chartData.length > 0 ? (
<div className="flex w-full flex-col space-x-0 sm:flex-row sm:space-x-4">
2022-01-19 18:45:47 -08:00
{chartData.find((d) => d.funding !== 0) ? (
<div
className="relative mb-6 w-full rounded-md border border-th-bkg-4 p-4"
2022-01-19 18:45:47 -08:00
style={{ height: '330px' }}
>
<Chart
hideRangeFilters
title={t('funding-chart-title')}
xAxis="time"
yAxis="funding"
data={chartData}
labelFormat={(x) =>
x &&
`${x?.toLocaleString(undefined, {
maximumFractionDigits: 6,
})} USDC`
}
tickFormat={handleDustTicks}
titleValue={chartData.reduce(
(a, c) => a + c.funding,
0
)}
type="bar"
useMulticoloredBars
yAxisWidth={70}
zeroLine
/>
</div>
) : null}
2021-11-25 20:17:48 -08:00
</div>
) : null}
<div>
<div>
{paginatedData.length ? (
<Table>
<thead>
<TrHead>
<Th>{t('time')}</Th>
2021-12-07 02:31:48 -08:00
<Th>{t('funding')} (USDC)</Th>
</TrHead>
</thead>
<tbody>
2022-02-10 15:48:14 -08:00
{paginatedData.map((stat) => {
2021-11-25 20:17:48 -08:00
// @ts-ignore
const utc = dayjs.utc(stat.time).format()
return (
2022-02-10 15:48:14 -08:00
<TrBody key={stat.time}>
<Td className="w-1/2">
2022-03-20 17:06:13 -07:00
<TableDateDisplay date={utc} />
</Td>
<Td className="w-1/2">
{stat.total_funding.toFixed(
QUOTE_DECIMALS + 1
2021-12-07 02:31:48 -08:00
)}
</Td>
</TrBody>
)
})}
</tbody>
</Table>
) : (
<div className="flex w-full justify-center bg-th-bkg-3 py-4 text-th-fgd-3">
2021-11-25 20:17:48 -08:00
{t('no-funding')}
</div>
)}
</div>
<Pagination
page={page}
totalPages={totalPages}
nextPage={nextPage}
lastPage={lastPage}
firstPage={firstPage}
previousPage={previousPage}
/>
</div>
2021-10-17 22:17:07 -07:00
</>
) : loadHourlyStats ? (
<div className="space-y-2 pt-8">
<div className="h-12 w-full animate-pulse rounded-md bg-th-bkg-3" />
<div className="h-12 w-full animate-pulse rounded-md bg-th-bkg-3" />
<div className="h-12 w-full animate-pulse rounded-md bg-th-bkg-3" />
2021-10-17 22:17:07 -07:00
</div>
) : null}
2021-11-06 10:00:10 -07:00
</>
2021-10-17 22:17:07 -07:00
</div>
2021-10-17 19:34:24 -07:00
) : (
<div>{t('connect-wallet')}</div>
2021-10-17 19:34:24 -07:00
)}
</>
)
}
export default AccountFunding