2022-11-30 07:46:20 -08:00
|
|
|
import FavoriteMarketButton from '@components/shared/FavoriteMarketButton'
|
|
|
|
import { Popover } from '@headlessui/react'
|
2023-07-31 21:49:11 -07:00
|
|
|
import { ChevronDownIcon, MagnifyingGlassIcon } from '@heroicons/react/20/solid'
|
2023-04-03 19:48:31 -07:00
|
|
|
import useMangoGroup from 'hooks/useMangoGroup'
|
2022-11-30 07:46:20 -08:00
|
|
|
import useSelectedMarket from 'hooks/useSelectedMarket'
|
2023-02-22 11:04:18 -08:00
|
|
|
import { useTranslation } from 'next-i18next'
|
2022-11-30 07:46:20 -08:00
|
|
|
import Link from 'next/link'
|
2023-07-31 21:49:11 -07:00
|
|
|
import { ChangeEvent, useEffect, useMemo, useRef, useState } from 'react'
|
2023-06-18 22:39:18 -07:00
|
|
|
import {
|
|
|
|
floorToDecimal,
|
|
|
|
formatCurrencyValue,
|
|
|
|
formatNumericValue,
|
|
|
|
getDecimalCount,
|
2023-07-24 05:14:45 -07:00
|
|
|
numberCompacter,
|
2023-06-18 22:39:18 -07:00
|
|
|
} from 'utils/numbers'
|
2022-11-30 07:46:20 -08:00
|
|
|
import MarketLogos from './MarketLogos'
|
2023-06-14 17:43:33 -07:00
|
|
|
import SoonBadge from '@components/shared/SoonBadge'
|
2023-06-18 22:39:18 -07:00
|
|
|
import TabButtons from '@components/shared/TabButtons'
|
2023-06-20 16:30:31 -07:00
|
|
|
import { PerpMarket } from '@blockworks-foundation/mango-v4'
|
2023-06-29 21:00:28 -07:00
|
|
|
import Loading from '@components/shared/Loading'
|
2023-07-13 22:47:05 -07:00
|
|
|
import MarketChange from '@components/shared/MarketChange'
|
2023-07-24 05:14:45 -07:00
|
|
|
import SheenLoader from '@components/shared/SheenLoader'
|
|
|
|
// import Select from '@components/forms/Select'
|
2023-07-31 21:49:11 -07:00
|
|
|
import useListedMarketsWithMarketData, {
|
|
|
|
SerumMarketWithMarketData,
|
|
|
|
} from 'hooks/useListedMarketsWithMarketData'
|
2023-07-24 05:14:45 -07:00
|
|
|
import { AllowedKeys, sortPerpMarkets, sortSpotMarkets } from 'utils/markets'
|
2023-07-31 21:49:11 -07:00
|
|
|
import Input from '@components/forms/Input'
|
2023-09-08 15:01:08 -07:00
|
|
|
import { useSortableData } from 'hooks/useSortableData'
|
|
|
|
import { SortableColumnHeader } from '@components/shared/TableElements'
|
2023-05-03 18:41:18 -07:00
|
|
|
|
|
|
|
const MARKET_LINK_CLASSES =
|
2023-07-24 05:14:45 -07:00
|
|
|
'grid grid-cols-3 md:grid-cols-4 flex items-center w-full py-2 px-4 rounded-r-md focus:outline-none focus-visible:text-th-active md:hover:cursor-pointer md:hover:bg-th-bkg-3 md:hover:text-th-fgd-1'
|
2023-05-03 18:41:18 -07:00
|
|
|
|
2023-06-14 10:19:39 -07:00
|
|
|
const MARKET_LINK_DISABLED_CLASSES =
|
2023-07-24 05:14:45 -07:00
|
|
|
'flex w-full items-center justify-between py-2 px-4 md:hover:cursor-not-allowed'
|
|
|
|
|
|
|
|
// const SORT_KEYS = [
|
|
|
|
// 'quote_volume_24h',
|
|
|
|
// 'quote_volume_1h',
|
|
|
|
// 'change_24h',
|
|
|
|
// 'change_1h',
|
|
|
|
// ]
|
2023-06-14 10:19:39 -07:00
|
|
|
|
2023-07-31 21:49:11 -07:00
|
|
|
const generateSearchTerm = (
|
|
|
|
item: SerumMarketWithMarketData,
|
|
|
|
searchValue: string,
|
|
|
|
) => {
|
|
|
|
const normalizedSearchValue = searchValue.toLowerCase()
|
|
|
|
const value = item.name.toLowerCase()
|
|
|
|
|
|
|
|
const isMatchingWithName =
|
|
|
|
item.name.toLowerCase().indexOf(normalizedSearchValue) >= 0
|
|
|
|
const matchingSymbolPercent = isMatchingWithName
|
|
|
|
? normalizedSearchValue.length / item.name.length
|
|
|
|
: 0
|
|
|
|
|
|
|
|
return {
|
|
|
|
token: item,
|
|
|
|
matchingIdx: value.indexOf(normalizedSearchValue),
|
|
|
|
matchingSymbolPercent,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const startSearch = (
|
|
|
|
items: SerumMarketWithMarketData[],
|
|
|
|
searchValue: string,
|
|
|
|
) => {
|
|
|
|
return items
|
|
|
|
.map((item) => generateSearchTerm(item, searchValue))
|
|
|
|
.filter((item) => item.matchingIdx >= 0)
|
|
|
|
.sort((i1, i2) => i1.matchingIdx - i2.matchingIdx)
|
|
|
|
.sort((i1, i2) => i2.matchingSymbolPercent - i1.matchingSymbolPercent)
|
|
|
|
.map((item) => item.token)
|
|
|
|
}
|
|
|
|
|
2022-11-30 07:46:20 -08:00
|
|
|
const MarketSelectDropdown = () => {
|
2023-02-22 11:04:18 -08:00
|
|
|
const { t } = useTranslation('common')
|
2022-11-30 07:46:20 -08:00
|
|
|
const { selectedMarket } = useSelectedMarket()
|
2023-06-20 16:30:31 -07:00
|
|
|
const [spotOrPerp, setSpotOrPerp] = useState(
|
2023-07-21 11:47:53 -07:00
|
|
|
selectedMarket instanceof PerpMarket ? 'perp' : 'spot',
|
2023-06-20 16:30:31 -07:00
|
|
|
)
|
2023-09-08 15:01:08 -07:00
|
|
|
const defaultSortByKey: AllowedKeys = 'quote_volume_24h'
|
2023-07-31 21:49:11 -07:00
|
|
|
const [search, setSearch] = useState('')
|
|
|
|
const [isOpen, setIsOpen] = useState(false)
|
2023-04-03 19:48:31 -07:00
|
|
|
const { group } = useMangoGroup()
|
2023-06-18 22:39:18 -07:00
|
|
|
const [spotBaseFilter, setSpotBaseFilter] = useState('All')
|
2023-07-24 05:14:45 -07:00
|
|
|
const { perpMarketsWithData, serumMarketsWithData, isLoading, isFetching } =
|
|
|
|
useListedMarketsWithMarketData()
|
2023-07-31 21:49:11 -07:00
|
|
|
const focusRef = useRef<HTMLInputElement>(null)
|
2022-11-30 07:46:20 -08:00
|
|
|
|
2023-09-08 15:01:08 -07:00
|
|
|
const unsortedPerpMarketsToShow = useMemo(() => {
|
2023-07-24 05:14:45 -07:00
|
|
|
if (!perpMarketsWithData.length) return []
|
2023-09-08 15:01:08 -07:00
|
|
|
return sortPerpMarkets(perpMarketsWithData, defaultSortByKey)
|
|
|
|
}, [perpMarketsWithData])
|
2023-01-19 20:17:43 -08:00
|
|
|
|
2023-06-18 22:39:18 -07:00
|
|
|
const spotBaseTokens: string[] = useMemo(() => {
|
2023-07-24 05:14:45 -07:00
|
|
|
if (serumMarketsWithData.length) {
|
2023-06-18 22:39:18 -07:00
|
|
|
const baseTokens: string[] = ['All']
|
2023-07-24 05:14:45 -07:00
|
|
|
serumMarketsWithData.map((m) => {
|
2023-06-18 22:39:18 -07:00
|
|
|
const base = m.name.split('/')[1]
|
|
|
|
if (!baseTokens.includes(base)) {
|
|
|
|
baseTokens.push(base)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return baseTokens.sort((a, b) => a.localeCompare(b))
|
|
|
|
}
|
|
|
|
return ['All']
|
2023-07-24 05:14:45 -07:00
|
|
|
}, [serumMarketsWithData])
|
2023-06-18 22:39:18 -07:00
|
|
|
|
2023-09-08 15:01:08 -07:00
|
|
|
const unsortedSerumMarketsToShow = useMemo(() => {
|
2023-07-24 05:14:45 -07:00
|
|
|
if (!serumMarketsWithData.length) return []
|
2023-06-18 22:39:18 -07:00
|
|
|
if (spotBaseFilter !== 'All') {
|
2023-07-24 05:14:45 -07:00
|
|
|
const filteredMarkets = serumMarketsWithData.filter((m) => {
|
2023-06-18 22:39:18 -07:00
|
|
|
const base = m.name.split('/')[1]
|
|
|
|
return base === spotBaseFilter
|
|
|
|
})
|
2023-07-31 21:49:11 -07:00
|
|
|
return search
|
|
|
|
? startSearch(filteredMarkets, search)
|
2023-09-08 15:01:08 -07:00
|
|
|
: sortSpotMarkets(filteredMarkets, defaultSortByKey)
|
2023-06-18 22:39:18 -07:00
|
|
|
} else {
|
2023-07-31 21:49:11 -07:00
|
|
|
return search
|
|
|
|
? startSearch(serumMarketsWithData, search)
|
2023-09-08 15:01:08 -07:00
|
|
|
: sortSpotMarkets(serumMarketsWithData, defaultSortByKey)
|
2023-06-18 22:39:18 -07:00
|
|
|
}
|
2023-09-08 15:01:08 -07:00
|
|
|
}, [search, serumMarketsWithData, spotBaseFilter])
|
2023-07-31 21:49:11 -07:00
|
|
|
|
|
|
|
const handleUpdateSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
|
|
|
setSearch(e.target.value)
|
|
|
|
}
|
|
|
|
|
2023-09-08 15:01:08 -07:00
|
|
|
const {
|
|
|
|
items: perpMarketsToShow,
|
|
|
|
requestSort: requestPerpSort,
|
|
|
|
sortConfig: perpSortConfig,
|
|
|
|
} = useSortableData(unsortedPerpMarketsToShow)
|
|
|
|
|
|
|
|
const {
|
|
|
|
items: serumMarketsToShow,
|
|
|
|
requestSort: requestSerumSort,
|
|
|
|
sortConfig: serumSortConfig,
|
|
|
|
} = useSortableData(unsortedSerumMarketsToShow)
|
|
|
|
|
2023-07-31 21:49:11 -07:00
|
|
|
useEffect(() => {
|
|
|
|
if (focusRef?.current && spotOrPerp === 'spot') {
|
|
|
|
focusRef.current.focus()
|
|
|
|
}
|
|
|
|
}, [focusRef, isOpen, spotOrPerp])
|
2023-07-24 05:14:45 -07:00
|
|
|
|
|
|
|
const loadingMarketData = isLoading || isFetching
|
2022-11-30 07:46:20 -08:00
|
|
|
|
|
|
|
return (
|
|
|
|
<Popover>
|
2023-02-22 11:04:18 -08:00
|
|
|
{({ open, close }) => (
|
2022-11-30 07:46:20 -08:00
|
|
|
<div
|
2023-04-24 04:45:30 -07:00
|
|
|
className="relative flex flex-col overflow-visible md:-ml-2"
|
2022-11-30 07:46:20 -08:00
|
|
|
id="trade-step-one"
|
|
|
|
>
|
2023-06-29 21:00:28 -07:00
|
|
|
<Popover.Button
|
|
|
|
className="-ml-4 flex h-12 items-center justify-between px-4 focus-visible:bg-th-bkg-3 disabled:cursor-not-allowed disabled:opacity-60 md:hover:bg-th-bkg-2 disabled:md:hover:bg-th-bkg-1"
|
|
|
|
disabled={!group}
|
2023-07-31 21:49:11 -07:00
|
|
|
onClick={() => setIsOpen(!isOpen)}
|
2023-06-29 21:00:28 -07:00
|
|
|
>
|
2022-12-06 21:25:37 -08:00
|
|
|
<div className="flex items-center">
|
2023-06-29 21:00:28 -07:00
|
|
|
{selectedMarket ? (
|
|
|
|
<MarketLogos market={selectedMarket} />
|
|
|
|
) : (
|
|
|
|
<Loading className="mr-2 h-5 w-5 flex-shrink-0" />
|
|
|
|
)}
|
2022-12-06 21:25:37 -08:00
|
|
|
<div className="whitespace-nowrap text-xl font-bold text-th-fgd-1 md:text-base">
|
2023-06-29 21:00:28 -07:00
|
|
|
{selectedMarket?.name || (
|
|
|
|
<span className="text-th-fgd-3">{t('loading')}</span>
|
|
|
|
)}
|
2022-12-06 21:25:37 -08:00
|
|
|
</div>
|
2022-11-30 07:46:20 -08:00
|
|
|
</div>
|
|
|
|
<ChevronDownIcon
|
|
|
|
className={`${
|
|
|
|
open ? 'rotate-180' : 'rotate-360'
|
2023-08-17 16:47:11 -07:00
|
|
|
} ml-2 mt-0.5 h-6 w-6 flex-shrink-0 text-th-fgd-2`}
|
2022-11-30 07:46:20 -08:00
|
|
|
/>
|
|
|
|
</Popover.Button>
|
2023-08-17 16:47:11 -07:00
|
|
|
<Popover.Panel className="absolute -left-4 top-12 z-40 w-screen border-y border-th-bkg-3 bg-th-bkg-2 md:w-[560px] md:border-r">
|
2023-06-18 22:39:18 -07:00
|
|
|
<div className="border-b border-th-bkg-3">
|
|
|
|
<TabButtons
|
|
|
|
activeValue={spotOrPerp}
|
|
|
|
onChange={(v) => setSpotOrPerp(v)}
|
|
|
|
values={[
|
|
|
|
['perp', 0],
|
|
|
|
['spot', 0],
|
|
|
|
]}
|
|
|
|
fillWidth
|
|
|
|
/>
|
|
|
|
</div>
|
2023-08-17 16:47:11 -07:00
|
|
|
<div className="thin-scroll max-h-[calc(100vh-160px)] overflow-auto py-3">
|
2023-07-24 05:14:45 -07:00
|
|
|
{spotOrPerp === 'perp' && perpMarketsToShow.length ? (
|
|
|
|
<>
|
2023-08-17 16:47:11 -07:00
|
|
|
<div className="mb-2 grid grid-cols-3 border-b border-th-bkg-3 pb-1 pl-4 pr-14 text-xxs md:grid-cols-4">
|
2023-09-10 21:28:35 -07:00
|
|
|
<div className="col-span-1 flex-1">
|
2023-09-08 15:01:08 -07:00
|
|
|
<SortableColumnHeader
|
|
|
|
sortKey="name"
|
|
|
|
sort={() => requestPerpSort('name')}
|
|
|
|
sortConfig={perpSortConfig}
|
|
|
|
title={t('market')}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<p className="col-span-1 flex justify-end">
|
|
|
|
<SortableColumnHeader
|
|
|
|
sortKey="marketData.last_price"
|
|
|
|
sort={() => requestPerpSort('marketData.last_price')}
|
|
|
|
sortConfig={perpSortConfig}
|
|
|
|
title={t('price')}
|
|
|
|
/>
|
|
|
|
</p>
|
|
|
|
<p className="col-span-1 flex justify-end">
|
|
|
|
<SortableColumnHeader
|
|
|
|
sortKey="rollingChange"
|
|
|
|
sort={() => requestPerpSort('rollingChange')}
|
|
|
|
sortConfig={perpSortConfig}
|
|
|
|
title={t('rolling-change')}
|
|
|
|
/>
|
2023-07-24 05:14:45 -07:00
|
|
|
</p>
|
2023-09-08 15:01:08 -07:00
|
|
|
<p className="col-span-1 flex justify-end">
|
|
|
|
<SortableColumnHeader
|
|
|
|
sortKey="marketData.quote_volume_24h"
|
|
|
|
sort={() =>
|
|
|
|
requestPerpSort('marketData.quote_volume_24h')
|
|
|
|
}
|
|
|
|
sortConfig={perpSortConfig}
|
|
|
|
title={t('daily-volume')}
|
|
|
|
/>
|
2023-07-24 05:14:45 -07:00
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
{perpMarketsToShow.map((m) => {
|
2023-06-18 22:39:18 -07:00
|
|
|
const isComingSoon = m.oracleLastUpdatedSlot == 0
|
2023-07-24 05:14:45 -07:00
|
|
|
|
|
|
|
const volumeData = m?.marketData?.quote_volume_24h
|
|
|
|
|
|
|
|
const volume = volumeData ? volumeData : 0
|
|
|
|
|
2023-06-18 22:39:18 -07:00
|
|
|
return (
|
2023-08-17 16:47:11 -07:00
|
|
|
<div className="flex w-full items-center" key={m.name}>
|
2023-06-18 22:39:18 -07:00
|
|
|
{!isComingSoon ? (
|
|
|
|
<>
|
|
|
|
<Link
|
|
|
|
className={MARKET_LINK_CLASSES}
|
|
|
|
href={{
|
|
|
|
pathname: '/trade',
|
|
|
|
query: { name: m.name },
|
|
|
|
}}
|
2023-06-20 16:40:51 -07:00
|
|
|
onClick={() => {
|
|
|
|
close()
|
2023-07-31 21:49:11 -07:00
|
|
|
setSearch('')
|
2023-06-20 16:40:51 -07:00
|
|
|
}}
|
2023-06-18 22:39:18 -07:00
|
|
|
shallow={true}
|
|
|
|
>
|
2023-07-24 05:14:45 -07:00
|
|
|
<div className="col-span-1 flex items-center">
|
|
|
|
<MarketLogos market={m} size="small" />
|
2023-08-17 16:47:11 -07:00
|
|
|
<span className="text-xs text-th-fgd-2">
|
2023-07-24 05:14:45 -07:00
|
|
|
{m.name}
|
|
|
|
</span>
|
2023-06-18 22:39:18 -07:00
|
|
|
</div>
|
2023-07-24 05:14:45 -07:00
|
|
|
<div className="col-span-1 flex justify-end">
|
|
|
|
<span className="font-mono text-xs text-th-fgd-2">
|
2023-06-18 22:39:18 -07:00
|
|
|
{formatCurrencyValue(
|
|
|
|
m.uiPrice,
|
2023-07-21 11:47:53 -07:00
|
|
|
getDecimalCount(m.tickSize),
|
2023-06-18 22:39:18 -07:00
|
|
|
)}
|
|
|
|
</span>
|
2023-07-24 05:14:45 -07:00
|
|
|
</div>
|
|
|
|
<div className="col-span-1 flex justify-end">
|
2023-07-13 22:47:05 -07:00
|
|
|
<MarketChange market={m} size="small" />
|
2023-06-18 22:39:18 -07:00
|
|
|
</div>
|
2023-08-17 16:47:11 -07:00
|
|
|
<div className="col-span-1 hidden justify-end md:flex">
|
2023-07-24 05:14:45 -07:00
|
|
|
{loadingMarketData ? (
|
|
|
|
<SheenLoader className="mt-0.5">
|
|
|
|
<div className="h-3.5 w-12 bg-th-bkg-2" />
|
|
|
|
</SheenLoader>
|
|
|
|
) : (
|
|
|
|
<span>
|
|
|
|
{volume ? (
|
|
|
|
<span className="font-mono text-xs text-th-fgd-2">
|
|
|
|
${numberCompacter.format(volume)}
|
|
|
|
</span>
|
|
|
|
) : (
|
|
|
|
<span className="font-mono text-xs text-th-fgd-2">
|
|
|
|
$0
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
</div>
|
2023-06-18 22:39:18 -07:00
|
|
|
</Link>
|
2023-07-24 05:14:45 -07:00
|
|
|
<div className="px-3">
|
|
|
|
<FavoriteMarketButton market={m} />
|
|
|
|
</div>
|
2023-06-18 22:39:18 -07:00
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
<span className={MARKET_LINK_DISABLED_CLASSES}>
|
|
|
|
<div className="flex items-center">
|
2023-07-24 05:14:45 -07:00
|
|
|
<MarketLogos market={m} size="small" />
|
|
|
|
<span className="mr-2 text-xs">{m.name}</span>
|
2023-06-18 22:39:18 -07:00
|
|
|
<SoonBadge />
|
|
|
|
</div>
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
2023-07-24 05:14:45 -07:00
|
|
|
})}
|
|
|
|
</>
|
|
|
|
) : null}
|
|
|
|
{spotOrPerp === 'spot' && serumMarketsToShow.length ? (
|
2023-06-18 22:39:18 -07:00
|
|
|
<>
|
2023-08-17 16:47:11 -07:00
|
|
|
<div className="mb-3 flex items-center justify-between px-4">
|
2023-07-31 21:49:11 -07:00
|
|
|
<div className="relative w-1/2">
|
|
|
|
<Input
|
2023-08-17 16:47:11 -07:00
|
|
|
className="h-8 pl-8"
|
2023-07-31 21:49:11 -07:00
|
|
|
type="text"
|
|
|
|
value={search}
|
|
|
|
onChange={handleUpdateSearch}
|
|
|
|
ref={focusRef}
|
|
|
|
/>
|
|
|
|
<MagnifyingGlassIcon className="absolute left-2 top-2 h-4 w-4" />
|
|
|
|
</div>
|
2023-07-24 05:14:45 -07:00
|
|
|
<div>
|
|
|
|
{spotBaseTokens.map((tab) => (
|
|
|
|
<button
|
2023-08-17 16:47:11 -07:00
|
|
|
className={`rounded-md px-2.5 py-1.5 text-sm font-medium focus-visible:bg-th-bkg-3 focus-visible:text-th-fgd-1 ${
|
2023-07-24 05:14:45 -07:00
|
|
|
spotBaseFilter === tab
|
|
|
|
? 'bg-th-bkg-3 text-th-active md:hover:text-th-active'
|
|
|
|
: 'text-th-fgd-3 md:hover:text-th-fgd-2'
|
|
|
|
}`}
|
|
|
|
onClick={() => setSpotBaseFilter(tab)}
|
|
|
|
key={tab}
|
|
|
|
>
|
|
|
|
{t(tab)}
|
|
|
|
</button>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
2023-08-17 16:47:11 -07:00
|
|
|
<div className="mb-2 grid grid-cols-3 border-b border-th-bkg-3 pb-1 pl-4 pr-14 text-xxs md:grid-cols-4">
|
2023-09-10 21:28:35 -07:00
|
|
|
<p className="col-span-1 flex">
|
2023-09-08 15:01:08 -07:00
|
|
|
<SortableColumnHeader
|
|
|
|
sortKey="name"
|
|
|
|
sort={() => requestSerumSort('name')}
|
|
|
|
sortConfig={serumSortConfig}
|
|
|
|
title={t('market')}
|
|
|
|
/>
|
|
|
|
</p>
|
|
|
|
<p className="col-span-1 flex justify-end">
|
|
|
|
<SortableColumnHeader
|
|
|
|
sortKey="marketData.last_price"
|
|
|
|
sort={() => requestSerumSort('marketData.last_price')}
|
|
|
|
sortConfig={serumSortConfig}
|
|
|
|
title={t('price')}
|
|
|
|
/>
|
2023-07-24 05:14:45 -07:00
|
|
|
</p>
|
2023-09-08 15:01:08 -07:00
|
|
|
<p className="col-span-1 flex justify-end">
|
|
|
|
<SortableColumnHeader
|
|
|
|
sortKey="rollingChange"
|
|
|
|
sort={() => requestSerumSort('rollingChange')}
|
|
|
|
sortConfig={serumSortConfig}
|
|
|
|
title={t('rolling-change')}
|
|
|
|
/>
|
|
|
|
</p>
|
|
|
|
<p className="col-span-1 flex justify-end">
|
|
|
|
<SortableColumnHeader
|
|
|
|
sortKey="marketData.quote_volume_24h"
|
|
|
|
sort={() =>
|
|
|
|
requestSerumSort('marketData.quote_volume_24h')
|
|
|
|
}
|
|
|
|
sortConfig={serumSortConfig}
|
|
|
|
title={t('daily-volume')}
|
|
|
|
/>
|
2023-07-24 05:14:45 -07:00
|
|
|
</p>
|
2023-06-18 22:39:18 -07:00
|
|
|
</div>
|
2023-07-24 05:14:45 -07:00
|
|
|
{serumMarketsToShow.map((m) => {
|
|
|
|
const baseBank = group?.getFirstBankByTokenIndex(
|
|
|
|
m.baseTokenIndex,
|
|
|
|
)
|
|
|
|
const quoteBank = group?.getFirstBankByTokenIndex(
|
|
|
|
m.quoteTokenIndex,
|
|
|
|
)
|
|
|
|
const market = group?.getSerum3ExternalMarket(
|
|
|
|
m.serumMarketExternal,
|
|
|
|
)
|
|
|
|
let price
|
|
|
|
if (baseBank && market && quoteBank) {
|
|
|
|
price = floorToDecimal(
|
|
|
|
baseBank.uiPrice / quoteBank.uiPrice,
|
|
|
|
getDecimalCount(market.tickSize),
|
|
|
|
).toNumber()
|
|
|
|
}
|
|
|
|
|
|
|
|
const volumeData = m?.marketData?.quote_volume_24h
|
|
|
|
|
|
|
|
const volume = volumeData ? volumeData : 0
|
|
|
|
|
|
|
|
return (
|
2023-08-17 16:47:11 -07:00
|
|
|
<div className="flex w-full items-center" key={m.name}>
|
2023-07-24 05:14:45 -07:00
|
|
|
<Link
|
|
|
|
className={MARKET_LINK_CLASSES}
|
|
|
|
href={{
|
|
|
|
pathname: '/trade',
|
|
|
|
query: { name: m.name },
|
|
|
|
}}
|
|
|
|
onClick={() => {
|
|
|
|
close()
|
2023-07-31 21:49:11 -07:00
|
|
|
setSearch('')
|
2023-07-24 05:14:45 -07:00
|
|
|
}}
|
|
|
|
shallow={true}
|
2023-06-18 22:39:18 -07:00
|
|
|
>
|
2023-07-24 05:14:45 -07:00
|
|
|
<div className="col-span-1 flex items-center">
|
|
|
|
<MarketLogos market={m} size="small" />
|
2023-08-17 16:47:11 -07:00
|
|
|
<span className="text-xs text-th-fgd-2">
|
2023-07-24 05:14:45 -07:00
|
|
|
{m.name}
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<div className="col-span-1 flex justify-end">
|
|
|
|
{price && market?.tickSize ? (
|
|
|
|
<span className="font-mono text-xs text-th-fgd-2">
|
|
|
|
{quoteBank?.name === 'USDC' ? '$' : ''}
|
|
|
|
{getDecimalCount(market.tickSize) <= 6
|
|
|
|
? formatNumericValue(
|
|
|
|
price,
|
|
|
|
getDecimalCount(market.tickSize),
|
|
|
|
)
|
|
|
|
: price.toExponential(3)}
|
|
|
|
{quoteBank?.name !== 'USDC' ? (
|
|
|
|
<span className="font-body text-th-fgd-3">
|
|
|
|
{' '}
|
|
|
|
{quoteBank?.name}
|
|
|
|
</span>
|
|
|
|
) : null}
|
|
|
|
</span>
|
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
<div className="col-span-1 flex justify-end">
|
|
|
|
<MarketChange market={m} size="small" />
|
|
|
|
</div>
|
2023-08-17 16:47:11 -07:00
|
|
|
<div className="col-span-1 hidden justify-end md:flex">
|
2023-07-24 05:14:45 -07:00
|
|
|
{loadingMarketData ? (
|
|
|
|
<SheenLoader className="mt-0.5">
|
|
|
|
<div className="h-3.5 w-12 bg-th-bkg-2" />
|
|
|
|
</SheenLoader>
|
|
|
|
) : (
|
|
|
|
<span className="font-mono text-xs text-th-fgd-2">
|
|
|
|
{quoteBank?.name === 'USDC' ? '$' : ''}
|
|
|
|
{volume ? numberCompacter.format(volume) : 0}
|
|
|
|
{quoteBank?.name !== 'USDC' ? (
|
|
|
|
<span className="font-body text-th-fgd-3">
|
|
|
|
{' '}
|
|
|
|
{quoteBank?.name}
|
|
|
|
</span>
|
|
|
|
) : null}
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</Link>
|
|
|
|
<div className="px-3">
|
2023-06-14 10:19:39 -07:00
|
|
|
<FavoriteMarketButton market={m} />
|
2023-06-18 22:39:18 -07:00
|
|
|
</div>
|
2023-07-24 05:14:45 -07:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
})}
|
2023-06-18 22:39:18 -07:00
|
|
|
</>
|
|
|
|
) : null}
|
|
|
|
</div>
|
2022-11-30 07:46:20 -08:00
|
|
|
</Popover.Panel>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</Popover>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default MarketSelectDropdown
|