import { useTranslation } from 'next-i18next' import { useCallback } from 'react' import useMangoGroup from 'hooks/useMangoGroup' import { SortableColumnHeader, Table, Td, Th, TrBody, TrHead, } from '@components/shared/TableElements' import FormatNumericValue from '@components/shared/FormatNumericValue' import { numberCompacter } from 'utils/numbers' import SimpleAreaChart from '@components/shared/SimpleAreaChart' import { Disclosure, Transition } from '@headlessui/react' import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/20/solid' import useThemeWrapper from 'hooks/useThemeWrapper' import { useSortableData } from 'hooks/useSortableData' import Change from '@components/shared/Change' import { Bank } from '@blockworks-foundation/mango-v4' 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' import { BankWithMarketData } from './Spot' import { SerumMarketWithMarketData } from 'hooks/useListedMarketsWithMarketData' import Tooltip from '@components/shared/Tooltip' import dayjs from 'dayjs' import TableTokenName from '@components/shared/TableTokenName' import { LinkButton } from '@components/shared/Button' import { formatTokenSymbol } from 'utils/tokens' type TableData = { assetWeight: string available: Decimal baseBank: Bank borrowRate: number change: number depositRate: number tokenName: string market: SerumMarketWithMarketData | undefined price: number priceHistory: { price: number time: string }[] volume: number isUp: boolean } const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => { const { t } = useTranslation('common') const { group } = useMangoGroup() const { theme } = useThemeWrapper() const { width } = useViewport() const router = useRouter() const showTableView = width ? width > breakpoints.md : false const formattedTableData = useCallback( (tokens: BankWithMarketData[]) => { const formatted = [] for (const token of tokens) { const baseBank = token.bank const price = baseBank.uiPrice const pastPrice = token.market?.marketData?.price_24h const priceHistory = token.market?.marketData?.price_history .sort((a, b) => a.time.localeCompare(b.time)) .concat([{ price: price, time: dayjs().toISOString() }]) || [] const volume = token.market?.marketData?.quote_volume_24h || 0 const change = volume > 0 && pastPrice ? ((price - pastPrice) / pastPrice) * 100 : 0 const tokenName = baseBank.name let availableVaultBalance = 0 let available = new Decimal(0) let availableValue = 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), ) availableValue = available.mul(price).toNumber() depositRate = baseBank.getDepositRateUi() borrowRate = baseBank.getBorrowRateUi() assetWeight = baseBank .scaledInitAssetWeight(baseBank.price) .toFixed(2) } const isUp = price && priceHistory.length ? price >= priceHistory[0].price : false const data = { available, availableValue, assetWeight, borrowRate, baseBank, change, depositRate, market: token.market, price, priceHistory, tokenName, volume, isUp, } formatted.push(data) } return formatted }, [group], ) const { items: tableData, requestSort, sortConfig, } = useSortableData(formattedTableData(tokens)) return ( {showTableView ? (
{tableData.map((data) => { const { available, assetWeight, baseBank, borrowRate, change, depositRate, market, price, priceHistory, tokenName, volume, isUp, } = data if (!baseBank) return null return ( goToTokenPage(tokenName.split(' ')[0], router) } > ) })}
requestSort('tokenName')} sortConfig={sortConfig} title={t('token')} />
requestSort('price')} sortConfig={sortConfig} title={t('price')} />
requestSort('change')} sortConfig={sortConfig} title={t('rolling-change')} />
requestSort('volume')} sortConfig={sortConfig} title={t('trade:24h-volume')} />
requestSort('availableValue')} sortConfig={sortConfig} title={t('available')} titleClass="tooltip-underline" />
requestSort('assetWeight')} sortConfig={sortConfig} title={t('explore:collateral-weight')} titleClass="tooltip-underline" />
requestSort('depositRate')} sortConfig={sortConfig} title={t('rates')} titleClass="tooltip-underline" />

{price ? ( <> ) : ( '–' )}

{market ? ( ) : ( )}
{priceHistory.length ? (
) : !market ? null : (

{t('unavailable')}

)}

{!market ? ( '–' ) : volume ? ( {numberCompacter.format(volume)}{' '} USDC ) : ( 0{' '} USDC )}

{assetWeight}x

%

|

%

) : (
{tableData.map((data) => { return })}
)}
) } export default SpotTable const MobileSpotItem = ({ data }: { data: TableData }) => { const { t } = useTranslation('common') const { theme } = useThemeWrapper() const router = useRouter() const { available, assetWeight, baseBank, borrowRate, change, depositRate, market, price, priceHistory, tokenName, volume, isUp, } = data return ( {({ open }) => ( <>

{tokenName}

{priceHistory.length ? (
) : !market ? null : (

{t('unavailable')}

)}

{price ? : '-'}

{market ? : null}

{t('trade:24h-volume')}

{!market ? ( '–' ) : volume ? ( {numberCompacter.format(volume)}{' '} USDC ) : ( 0 USDC )}

{t('available')}

{t('explore:collateral-weight')}

{assetWeight}x

{t('rates')}

%

|

%

goToTokenPage(baseBank.name.split(' ')[0], router) } > {t('token:token-stats', { token: formatTokenSymbol(baseBank.name), })}
)}
) }