mango-v4-ui/components/explore/SpotTable.tsx

486 lines
16 KiB
TypeScript
Raw Normal View History

2022-10-08 04:37:08 -07:00
import { useTranslation } from 'next-i18next'
2023-07-27 19:44:43 -07:00
import { useCallback } from 'react'
2022-11-18 11:11:06 -08:00
import useMangoGroup from 'hooks/useMangoGroup'
2023-07-27 19:44:43 -07:00
import {
SortableColumnHeader,
Table,
Td,
Th,
TrBody,
TrHead,
} from '@components/shared/TableElements'
2023-01-24 16:54:24 -08:00
import FormatNumericValue from '@components/shared/FormatNumericValue'
2023-10-04 04:52:39 -07:00
import { numberCompacter } from 'utils/numbers'
2023-05-01 19:01:14 -07:00
import SimpleAreaChart from '@components/shared/SimpleAreaChart'
2023-05-31 05:15:50 -07:00
import { Disclosure, Transition } from '@headlessui/react'
2023-10-03 22:05:16 -07:00
import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/20/solid'
2023-07-21 11:50:06 -07:00
import useThemeWrapper from 'hooks/useThemeWrapper'
2023-07-27 19:44:43 -07:00
import { useSortableData } from 'hooks/useSortableData'
import Change from '@components/shared/Change'
import { Bank } from '@blockworks-foundation/mango-v4'
2023-10-03 22:05:16 -07:00
import { useViewport } from 'hooks/useViewport'
import { breakpoints } from 'utils/theme'
import ContentBox from '@components/shared/ContentBox'
import { COLORS } from 'styles/colors'
import TokenLogo from '@components/shared/TokenLogo'
import { goToTokenPage } from '@components/stats/tokens/TokenOverviewTable'
import { useRouter } from 'next/router'
import Decimal from 'decimal.js'
import BankAmountWithValue from '@components/shared/BankAmountWithValue'
2023-10-04 04:52:39 -07:00
import { BankWithMarketData } from './Spot'
import { SerumMarketWithMarketData } from 'hooks/useListedMarketsWithMarketData'
2023-10-04 16:04:51 -07:00
import Tooltip from '@components/shared/Tooltip'
2023-07-27 19:44:43 -07:00
type TableData = {
2023-10-04 04:52:39 -07:00
assetWeight: string
available: Decimal
baseBank: Bank
borrowRate: number
2023-07-27 19:44:43 -07:00
change: number
2023-10-04 04:52:39 -07:00
depositRate: number
tokenName: string
market: SerumMarketWithMarketData | undefined
2023-07-27 19:44:43 -07:00
price: number
priceHistory:
| {
price: number
time: string
}[]
| undefined
volume: number
isUp: boolean
}
2022-10-08 04:37:08 -07:00
2023-10-04 04:52:39 -07:00
const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
2022-10-08 04:37:08 -07:00
const { t } = useTranslation('common')
2022-11-20 12:32:38 -08:00
const { group } = useMangoGroup()
2023-07-21 11:50:06 -07:00
const { theme } = useThemeWrapper()
2022-10-08 04:37:08 -07:00
const { width } = useViewport()
2023-10-03 22:05:16 -07:00
const router = useRouter()
2022-10-08 04:37:08 -07:00
const showTableView = width ? width > breakpoints.md : false
2023-07-21 05:03:44 -07:00
2023-07-27 22:04:38 -07:00
const formattedTableData = useCallback(
2023-10-04 04:52:39 -07:00
(tokens: BankWithMarketData[]) => {
2023-07-27 19:44:43 -07:00
const formatted = []
2023-10-04 04:52:39 -07:00
for (const token of tokens) {
const baseBank = token.bank
const price = baseBank.uiPrice
2023-07-27 19:44:43 -07:00
2023-10-04 04:52:39 -07:00
const pastPrice = token.market?.marketData?.price_24h || 0
2023-07-27 19:44:43 -07:00
2023-10-05 19:35:55 -07:00
let priceHistory
if (
token.market?.marketData?.price_history &&
token.market?.marketData?.price_history.length
) {
priceHistory = token.market.marketData.price_history.concat([
{ price: price, time: new Date().toDateString() },
])
}
2023-07-27 19:44:43 -07:00
2023-10-04 04:52:39 -07:00
const volume = token.market?.marketData?.quote_volume_24h || 0
2023-07-27 19:44:43 -07:00
const change = volume > 0 ? ((price - pastPrice) / pastPrice) * 100 : 0
2023-10-04 04:52:39 -07:00
const tokenName = baseBank.name
2023-10-03 22:05:16 -07:00
let availableVaultBalance = 0
let available = new Decimal(0)
let depositRate = 0
let borrowRate = 0
let assetWeight = '0'
if (baseBank) {
availableVaultBalance = group
? group.getTokenVaultBalanceByMintUi(baseBank.mint) -
baseBank.uiDeposits() * baseBank.minVaultToDepositsRatio
: 0
available = Decimal.max(
0,
availableVaultBalance.toFixed(baseBank.mintDecimals),
)
depositRate = baseBank.getDepositRateUi()
borrowRate = baseBank.getBorrowRateUi()
assetWeight = baseBank
.scaledInitAssetWeight(baseBank.price)
.toFixed(2)
}
2023-07-27 19:44:43 -07:00
const isUp =
price && priceHistory && priceHistory.length
? price >= priceHistory[0].price
: false
const data = {
2023-10-03 22:05:16 -07:00
available,
assetWeight,
borrowRate,
2023-07-27 19:44:43 -07:00
baseBank,
change,
2023-10-03 22:05:16 -07:00
depositRate,
2023-10-04 04:52:39 -07:00
market: token.market,
2023-07-27 19:44:43 -07:00
price,
priceHistory,
2023-10-04 04:52:39 -07:00
tokenName,
2023-07-27 19:44:43 -07:00
volume,
isUp,
}
formatted.push(data)
}
return formatted
},
[group],
)
const {
items: tableData,
requestSort,
sortConfig,
2023-10-04 04:52:39 -07:00
} = useSortableData(formattedTableData(tokens))
2023-05-31 05:15:50 -07:00
2022-10-08 04:37:08 -07:00
return (
<ContentBox hideBorder hidePadding>
{showTableView ? (
2022-11-20 02:44:14 -08:00
<Table>
2022-10-08 04:37:08 -07:00
<thead>
2022-11-20 02:44:14 -08:00
<TrHead>
2023-07-27 19:44:43 -07:00
<Th className="text-left">
<SortableColumnHeader
2023-10-03 22:05:16 -07:00
sortKey="tokenName"
sort={() => requestSort('tokenName')}
2023-07-27 19:44:43 -07:00
sortConfig={sortConfig}
2023-10-03 22:05:16 -07:00
title={t('token')}
2023-07-27 19:44:43 -07:00
/>
</Th>
<Th>
<div className="flex justify-end">
<SortableColumnHeader
sortKey="price"
sort={() => requestSort('price')}
sortConfig={sortConfig}
title={t('price')}
/>
</div>
</Th>
<Th>
<div className="flex justify-end">
<SortableColumnHeader
sortKey="change"
sort={() => requestSort('change')}
sortConfig={sortConfig}
title={t('rolling-change')}
/>
</div>
</Th>
2023-07-21 05:03:44 -07:00
<Th className="hidden text-right md:block"></Th>
2023-07-27 19:44:43 -07:00
<Th>
<div className="flex justify-end">
<SortableColumnHeader
sortKey="volume"
sort={() => requestSort('volume')}
sortConfig={sortConfig}
title={t('trade:24h-volume')}
/>
</div>
</Th>
2023-10-03 22:05:16 -07:00
<Th>
<div className="flex justify-end">
2023-10-04 16:04:51 -07:00
<Tooltip content={t('tooltip-available', { token: '' })}>
<SortableColumnHeader
sortKey="available"
sort={() => requestSort('available')}
sortConfig={sortConfig}
title={t('available')}
titleClass="tooltip-underline"
/>
</Tooltip>
2023-10-03 22:05:16 -07:00
</div>
</Th>
<Th>
<div className="flex justify-end">
2023-10-04 16:04:51 -07:00
<Tooltip content={t('tooltip-collateral-weight')}>
<SortableColumnHeader
sortKey="assetWeight"
sort={() => requestSort('assetWeight')}
sortConfig={sortConfig}
title={t('explore:collateral-weight')}
titleClass="tooltip-underline"
/>
</Tooltip>
2023-10-03 22:05:16 -07:00
</div>
</Th>
<Th>
<div className="flex justify-end">
2023-10-04 16:04:51 -07:00
<Tooltip content={t('tooltip-interest-rates')}>
<SortableColumnHeader
sortKey="depositRate"
sort={() => requestSort('depositRate')}
sortConfig={sortConfig}
title={t('rates')}
titleClass="tooltip-underline"
/>
</Tooltip>
2023-10-03 22:05:16 -07:00
</div>
</Th>
<Th />
2022-11-20 02:44:14 -08:00
</TrHead>
2022-10-08 04:37:08 -07:00
</thead>
<tbody>
2023-07-27 19:44:43 -07:00
{tableData.map((data) => {
const {
2023-10-03 22:05:16 -07:00
available,
assetWeight,
2023-07-27 19:44:43 -07:00
baseBank,
2023-10-03 22:05:16 -07:00
borrowRate,
2023-07-27 19:44:43 -07:00
change,
2023-10-03 22:05:16 -07:00
depositRate,
2023-07-27 19:44:43 -07:00
market,
price,
priceHistory,
2023-10-04 04:52:39 -07:00
tokenName,
2023-07-27 19:44:43 -07:00
volume,
isUp,
} = data
2022-10-08 04:37:08 -07:00
2023-10-03 22:05:16 -07:00
if (!baseBank) return null
2023-07-27 19:44:43 -07:00
return (
2023-10-03 22:05:16 -07:00
<TrBody
className="default-transition md:hover:cursor-pointer md:hover:bg-th-bkg-2"
2023-10-04 04:52:39 -07:00
key={tokenName}
onClick={() => goToTokenPage(tokenName.split(' ')[0], router)}
2023-10-03 22:05:16 -07:00
>
2023-07-27 19:44:43 -07:00
<Td>
<div className="flex items-center">
2023-10-03 22:05:16 -07:00
<TokenLogo bank={baseBank} />
<p className="ml-3 font-body">{tokenName}</p>
2023-07-27 19:44:43 -07:00
</div>
</Td>
<Td>
<div className="flex flex-col text-right">
<p>
{price ? (
<>
2023-10-03 22:05:16 -07:00
<FormatNumericValue value={price} isUsd />
2023-07-27 19:44:43 -07:00
</>
) : (
''
)}
</p>
</div>
</Td>
<Td>
<div className="flex flex-col items-end">
2023-10-04 04:52:39 -07:00
{market ? (
<Change change={change} suffix="%" />
) : (
<span></span>
)}
2023-07-27 19:44:43 -07:00
</div>
</Td>
<Td>
2023-10-03 22:05:16 -07:00
{priceHistory && priceHistory.length ? (
<div className="h-10 w-24">
<SimpleAreaChart
color={isUp ? COLORS.UP[theme] : COLORS.DOWN[theme]}
data={priceHistory}
2023-10-04 04:52:39 -07:00
name={tokenName}
2023-10-03 22:05:16 -07:00
xKey="time"
yKey="price"
/>
</div>
2023-10-04 04:52:39 -07:00
) : !market ? null : (
2023-10-03 22:05:16 -07:00
<p className="mb-0 text-th-fgd-4">{t('unavailable')}</p>
2023-07-27 19:44:43 -07:00
)}
</Td>
<Td>
<div className="flex flex-col text-right">
<p>
2023-10-04 04:52:39 -07:00
{!market ? (
''
) : volume ? (
2023-07-27 19:44:43 -07:00
<span>
{numberCompacter.format(volume)}{' '}
<span className="font-body text-th-fgd-4">
2023-10-04 04:52:39 -07:00
USDC
2023-05-31 05:15:50 -07:00
</span>
2023-07-27 19:44:43 -07:00
</span>
) : (
<span>
0{' '}
<span className="font-body text-th-fgd-4">
2023-10-04 04:52:39 -07:00
USDC
2023-07-21 05:20:12 -07:00
</span>
2023-07-27 19:44:43 -07:00
</span>
)}
</p>
</div>
</Td>
2023-10-03 22:05:16 -07:00
<Td>
<div className="flex flex-col text-right">
<BankAmountWithValue
amount={available}
bank={baseBank}
fixDecimals={false}
stacked
/>
</div>
</Td>
<Td className="text-right font-mono">{assetWeight}x</Td>
<Td>
<div className="flex justify-end space-x-1.5">
<p className="text-th-up">
<FormatNumericValue value={depositRate} decimals={2} />%
</p>
<span className="text-th-fgd-4">|</span>
<p className="text-th-down">
<FormatNumericValue value={borrowRate} decimals={2} />%
</p>
</div>
</Td>
<Td>
<div className="flex justify-end">
<ChevronRightIcon className="h-5 w-5 text-th-fgd-3" />
</div>
</Td>
2023-07-27 19:44:43 -07:00
</TrBody>
)
})}
2022-10-08 04:37:08 -07:00
</tbody>
2022-11-20 02:44:14 -08:00
</Table>
2022-10-08 04:37:08 -07:00
) : (
2023-05-31 05:15:50 -07:00
<div className="border-b border-th-bkg-3">
2023-07-27 19:44:43 -07:00
{tableData.map((data) => {
2023-10-04 04:52:39 -07:00
return <MobileSpotItem key={data.tokenName} data={data} />
2023-07-27 19:44:43 -07:00
})}
2022-10-08 04:37:08 -07:00
</div>
)}
</ContentBox>
)
}
2023-10-04 04:52:39 -07:00
export default SpotTable
2022-10-08 04:37:08 -07:00
2023-10-04 04:52:39 -07:00
const MobileSpotItem = ({ data }: { data: TableData }) => {
2022-10-08 04:37:08 -07:00
const { t } = useTranslation('common')
2023-07-21 11:50:06 -07:00
const { theme } = useThemeWrapper()
2022-10-08 04:37:08 -07:00
2023-07-27 19:44:43 -07:00
const {
2023-10-04 04:52:39 -07:00
available,
assetWeight,
2023-07-27 19:44:43 -07:00
baseBank,
2023-10-04 04:52:39 -07:00
borrowRate,
2023-07-27 19:44:43 -07:00
change,
2023-10-04 04:52:39 -07:00
depositRate,
2023-07-27 19:44:43 -07:00
market,
price,
priceHistory,
2023-10-04 04:52:39 -07:00
tokenName,
2023-07-27 19:44:43 -07:00
volume,
isUp,
} = data
2023-05-31 05:15:50 -07:00
2022-10-08 04:37:08 -07:00
return (
2023-05-31 05:15:50 -07:00
<Disclosure>
{({ open }) => (
<>
<Disclosure.Button
className={`w-full border-t border-th-bkg-3 p-4 text-left first:border-t-0 focus:outline-none`}
>
<div className="flex items-center justify-between">
<div className="flex items-center">
<div className="flex flex-shrink-0 items-center">
2023-10-04 04:52:39 -07:00
<TokenLogo bank={baseBank} />
2023-05-31 05:15:50 -07:00
</div>
2023-10-04 04:52:39 -07:00
<p className="ml-3 leading-none text-th-fgd-1">{tokenName}</p>
2023-05-31 05:15:50 -07:00
</div>
<div className="flex items-center space-x-3">
2023-10-03 22:05:16 -07:00
{priceHistory && priceHistory.length ? (
<div className="h-10 w-20">
<SimpleAreaChart
color={isUp ? COLORS.UP[theme] : COLORS.DOWN[theme]}
data={priceHistory}
2023-10-04 04:52:39 -07:00
name={tokenName}
2023-10-03 22:05:16 -07:00
xKey="time"
yKey="price"
/>
</div>
2023-10-04 04:52:39 -07:00
) : !market ? null : (
2023-10-03 22:05:16 -07:00
<p className="mb-0 text-th-fgd-4">{t('unavailable')}</p>
2023-05-31 05:15:50 -07:00
)}
2023-10-04 04:52:39 -07:00
{market ? <Change change={change} suffix="%" /> : null}
2023-05-31 05:15:50 -07:00
<ChevronDownIcon
className={`${
open ? 'rotate-180' : 'rotate-360'
} h-6 w-6 flex-shrink-0 text-th-fgd-3`}
/>
</div>
2022-12-21 03:07:17 -08:00
</div>
2023-05-31 05:15:50 -07:00
</Disclosure.Button>
<Transition
enter="transition ease-in duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
>
<Disclosure.Panel>
<div className="mx-4 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pb-4 pt-4">
2023-05-31 05:15:50 -07:00
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('price')}</p>
<p className="font-mono text-th-fgd-2">
2023-10-04 04:52:39 -07:00
{price ? <FormatNumericValue value={price} isUsd /> : '-'}
2023-05-31 05:15:50 -07:00
</p>
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('trade:24h-volume')}
</p>
<p className="font-mono text-th-fgd-2">
2023-10-04 04:52:39 -07:00
{!market ? (
''
) : volume ? (
2023-05-31 05:15:50 -07:00
<span>
2023-07-21 05:03:44 -07:00
{numberCompacter.format(volume)}{' '}
2023-10-04 04:52:39 -07:00
<span className="font-body text-th-fgd-4">USDC</span>
2023-05-31 05:15:50 -07:00
</span>
) : (
2023-07-21 05:20:12 -07:00
<span>
2023-10-04 04:52:39 -07:00
0 <span className="font-body text-th-fgd-4">USDC</span>
2023-07-21 05:20:12 -07:00
</span>
2023-05-31 05:15:50 -07:00
)}
</p>
</div>
2023-10-04 04:52:39 -07:00
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('available')}</p>
<BankAmountWithValue
amount={available}
bank={baseBank}
fixDecimals={false}
/>
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('explore:collateral-weight')}
</p>
<p className="font-mono text-th-fgd-2">{assetWeight}x</p>
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('rates')}</p>
<div className="flex space-x-1.5">
<p className="text-th-up">
<FormatNumericValue value={depositRate} decimals={2} />%
</p>
<span className="text-th-fgd-4">|</span>
<p className="text-th-down">
<FormatNumericValue value={borrowRate} decimals={2} />%
</p>
</div>
</div>
2023-05-31 05:15:50 -07:00
</div>
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>
2022-10-08 04:37:08 -07:00
)
}