2023-02-22 11:04:18 -08:00
|
|
|
|
// import ChartRangeButtons from '@components/shared/ChartRangeButtons'
|
2023-04-03 19:48:31 -07:00
|
|
|
|
import Change from '@components/shared/Change'
|
2022-11-30 07:46:20 -08:00
|
|
|
|
import FavoriteMarketButton from '@components/shared/FavoriteMarketButton'
|
2023-04-03 19:48:31 -07:00
|
|
|
|
import SheenLoader from '@components/shared/SheenLoader'
|
2023-05-02 10:47:26 -07:00
|
|
|
|
import { getOneDayPerpStats } from '@components/stats/PerpMarketsOverviewTable'
|
2022-11-30 07:46:20 -08:00
|
|
|
|
import { Popover } from '@headlessui/react'
|
|
|
|
|
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
|
|
|
|
import mangoStore from '@store/mangoStore'
|
2023-04-03 19:48:31 -07:00
|
|
|
|
import { useBirdeyeMarketPrices } from 'hooks/useBirdeyeMarketPrices'
|
|
|
|
|
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-02-22 11:04:18 -08:00
|
|
|
|
import { useMemo } from 'react'
|
2022-11-30 07:46:20 -08:00
|
|
|
|
import { DEFAULT_MARKET_NAME } from 'utils/constants'
|
2023-04-25 05:41:23 -07:00
|
|
|
|
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
|
2022-11-30 07:46:20 -08:00
|
|
|
|
import MarketLogos from './MarketLogos'
|
|
|
|
|
|
2023-05-03 18:41:18 -07:00
|
|
|
|
const MARKET_LINK_WRAPPER_CLASSES =
|
|
|
|
|
'flex items-center justify-between px-4 md:pl-6 md:pr-4'
|
|
|
|
|
|
|
|
|
|
const MARKET_LINK_CLASSES =
|
|
|
|
|
'mr-2 -ml-3 flex w-full items-center justify-between rounded-md py-2 px-3 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-06-14 10:19:39 -07:00
|
|
|
|
const MARKET_LINK_DISABLED_CLASSES =
|
|
|
|
|
'mr-2 -ml-3 flex w-full items-center justify-between rounded-md py-2 px-3 md:hover:cursor-not-allowed'
|
|
|
|
|
|
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()
|
|
|
|
|
const serumMarkets = mangoStore((s) => s.serumMarkets)
|
2023-01-19 20:17:43 -08:00
|
|
|
|
const allPerpMarkets = mangoStore((s) => s.perpMarkets)
|
2023-04-03 19:48:31 -07:00
|
|
|
|
const perpStats = mangoStore((s) => s.perpStats.data)
|
|
|
|
|
const loadingPerpStats = mangoStore((s) => s.perpStats.loading)
|
|
|
|
|
const { group } = useMangoGroup()
|
|
|
|
|
const { data: birdeyePrices, isLoading: loadingPrices } =
|
|
|
|
|
useBirdeyeMarketPrices()
|
2023-02-22 11:04:18 -08:00
|
|
|
|
// const [spotBaseFilter, setSpotBaseFilter] = useState('All')
|
2022-11-30 07:46:20 -08:00
|
|
|
|
|
2023-01-19 20:17:43 -08:00
|
|
|
|
const perpMarkets = useMemo(() => {
|
2023-02-13 16:37:23 -08:00
|
|
|
|
return allPerpMarkets
|
|
|
|
|
.filter(
|
|
|
|
|
(p) =>
|
|
|
|
|
p.publicKey.toString() !==
|
|
|
|
|
'9Y8paZ5wUpzLFfQuHz8j2RtPrKsDtHx9sbgFmWb5abCw'
|
|
|
|
|
)
|
2023-06-14 10:19:39 -07:00
|
|
|
|
.sort((a, b) =>
|
|
|
|
|
a.oracleLastUpdatedSlot != 0 ? -1 : a.name.localeCompare(b.name)
|
|
|
|
|
)
|
2023-01-19 20:17:43 -08:00
|
|
|
|
}, [allPerpMarkets])
|
|
|
|
|
|
2023-02-22 11:04:18 -08:00
|
|
|
|
// const spotBaseTokens: string[] = useMemo(() => {
|
|
|
|
|
// if (serumMarkets.length) {
|
|
|
|
|
// const baseTokens: string[] = []
|
|
|
|
|
// serumMarkets.map((m) => {
|
|
|
|
|
// const base = m.name.split('/')[1]
|
|
|
|
|
// if (!baseTokens.includes(base)) {
|
|
|
|
|
// baseTokens.push(base)
|
|
|
|
|
// }
|
|
|
|
|
// })
|
|
|
|
|
// return baseTokens
|
|
|
|
|
// }
|
|
|
|
|
// return []
|
|
|
|
|
// }, [serumMarkets])
|
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-04-19 18:12:45 -07:00
|
|
|
|
<Popover.Button className="-ml-4 flex h-12 items-center justify-between px-4 focus-visible:bg-th-bkg-3 md:hover:bg-th-bkg-2">
|
2022-12-06 21:25:37 -08:00
|
|
|
|
<div className="flex items-center">
|
2022-11-30 07:46:20 -08:00
|
|
|
|
{selectedMarket ? <MarketLogos market={selectedMarket} /> : null}
|
2022-12-06 21:25:37 -08:00
|
|
|
|
<div className="whitespace-nowrap text-xl font-bold text-th-fgd-1 md:text-base">
|
|
|
|
|
{selectedMarket?.name || DEFAULT_MARKET_NAME}
|
|
|
|
|
</div>
|
2022-11-30 07:46:20 -08:00
|
|
|
|
</div>
|
|
|
|
|
<ChevronDownIcon
|
|
|
|
|
className={`${
|
|
|
|
|
open ? 'rotate-180' : 'rotate-360'
|
2022-12-06 21:25:37 -08:00
|
|
|
|
} mt-0.5 ml-2 h-6 w-6 flex-shrink-0 text-th-fgd-2`}
|
2022-11-30 07:46:20 -08:00
|
|
|
|
/>
|
|
|
|
|
</Popover.Button>
|
2023-04-03 19:48:31 -07:00
|
|
|
|
<Popover.Panel className="absolute -left-4 top-12 z-40 mr-4 w-screen rounded-none bg-th-bkg-2 pb-4 pt-2 md:-left-6 md:w-96 md:rounded-br-md">
|
2023-03-05 19:33:13 -08:00
|
|
|
|
<p className="my-2 ml-4 text-xs md:ml-6">{t('perp')}</p>
|
2023-02-22 11:04:18 -08:00
|
|
|
|
{perpMarkets?.length
|
|
|
|
|
? perpMarkets.map((m) => {
|
2023-04-03 19:48:31 -07:00
|
|
|
|
const changeData = getOneDayPerpStats(perpStats, m.name)
|
2023-06-14 10:19:39 -07:00
|
|
|
|
const isComingSoon = m.oracleLastUpdatedSlot == 0
|
2023-04-03 19:48:31 -07:00
|
|
|
|
|
|
|
|
|
const change = changeData.length
|
|
|
|
|
? ((m.uiPrice - changeData[0].price) /
|
|
|
|
|
changeData[0].price) *
|
|
|
|
|
100
|
|
|
|
|
: 0
|
2023-02-22 11:04:18 -08:00
|
|
|
|
return (
|
|
|
|
|
<div
|
2023-05-03 18:41:18 -07:00
|
|
|
|
className={MARKET_LINK_WRAPPER_CLASSES}
|
2023-02-22 11:04:18 -08:00
|
|
|
|
key={m.publicKey.toString()}
|
|
|
|
|
onClick={() => {
|
2023-06-14 10:19:39 -07:00
|
|
|
|
if (!isComingSoon) close()
|
2023-02-22 11:04:18 -08:00
|
|
|
|
}}
|
|
|
|
|
>
|
2023-06-14 10:19:39 -07:00
|
|
|
|
{!isComingSoon ? (
|
|
|
|
|
<>
|
|
|
|
|
<Link
|
|
|
|
|
className={MARKET_LINK_CLASSES}
|
|
|
|
|
href={{
|
|
|
|
|
pathname: '/trade',
|
|
|
|
|
query: { name: m.name },
|
|
|
|
|
}}
|
|
|
|
|
shallow={true}
|
|
|
|
|
>
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<MarketLogos market={m} />
|
|
|
|
|
<span>{m.name}</span>
|
|
|
|
|
</div>
|
|
|
|
|
{!loadingPerpStats ? (
|
|
|
|
|
<Change change={change} suffix="%" />
|
|
|
|
|
) : (
|
|
|
|
|
<SheenLoader className="mt-0.5">
|
|
|
|
|
<div className="h-3.5 w-12 bg-th-bkg-2" />
|
|
|
|
|
</SheenLoader>
|
|
|
|
|
)}
|
|
|
|
|
</Link>
|
|
|
|
|
<FavoriteMarketButton market={m} />
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<span className={MARKET_LINK_DISABLED_CLASSES}>
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<MarketLogos market={m} />
|
|
|
|
|
<span>{m.name}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mr-4 flex items-center space-x-1.5 text-xs uppercase">
|
|
|
|
|
{t('soon')}™
|
|
|
|
|
</div>
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
2023-02-22 11:04:18 -08:00
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
: null}
|
2023-03-05 19:33:13 -08:00
|
|
|
|
<p className="my-2 ml-4 text-xs md:ml-6">{t('spot')}</p>
|
2023-02-22 11:04:18 -08:00
|
|
|
|
{serumMarkets?.length ? (
|
|
|
|
|
<>
|
|
|
|
|
{serumMarkets
|
|
|
|
|
.map((x) => x)
|
|
|
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
|
|
|
.map((m) => {
|
2023-04-03 19:48:31 -07:00
|
|
|
|
const birdeyeData = birdeyePrices?.length
|
|
|
|
|
? birdeyePrices.find(
|
|
|
|
|
(market) =>
|
|
|
|
|
market.mint === m.serumMarketExternal.toString()
|
|
|
|
|
)
|
|
|
|
|
: null
|
2023-04-25 05:41:23 -07:00
|
|
|
|
const baseBank = group?.getFirstBankByTokenIndex(
|
2023-04-03 19:48:31 -07:00
|
|
|
|
m.baseTokenIndex
|
|
|
|
|
)
|
2023-04-25 05:41:23 -07:00
|
|
|
|
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()
|
|
|
|
|
}
|
2023-04-03 19:48:31 -07:00
|
|
|
|
const change =
|
2023-04-25 05:41:23 -07:00
|
|
|
|
birdeyeData && price
|
|
|
|
|
? ((price - birdeyeData.data[0].value) /
|
2023-04-03 19:48:31 -07:00
|
|
|
|
birdeyeData.data[0].value) *
|
|
|
|
|
100
|
|
|
|
|
: 0
|
2023-02-03 08:04:29 -08:00
|
|
|
|
return (
|
|
|
|
|
<div
|
2023-05-03 18:41:18 -07:00
|
|
|
|
className={MARKET_LINK_WRAPPER_CLASSES}
|
2023-02-03 08:04:29 -08:00
|
|
|
|
key={m.publicKey.toString()}
|
2023-03-04 12:59:42 -08:00
|
|
|
|
onClick={() => {
|
|
|
|
|
close()
|
|
|
|
|
}}
|
2023-02-03 08:04:29 -08:00
|
|
|
|
>
|
|
|
|
|
<Link
|
2023-05-03 18:41:18 -07:00
|
|
|
|
className={MARKET_LINK_CLASSES}
|
2023-02-03 08:04:29 -08:00
|
|
|
|
href={{
|
|
|
|
|
pathname: '/trade',
|
|
|
|
|
query: { name: m.name },
|
|
|
|
|
}}
|
|
|
|
|
shallow={true}
|
2022-11-30 07:46:20 -08:00
|
|
|
|
>
|
2023-05-03 18:41:18 -07:00
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<MarketLogos market={m} />
|
|
|
|
|
<span>{m.name}</span>
|
|
|
|
|
</div>
|
2023-04-03 19:48:31 -07:00
|
|
|
|
{!loadingPrices ? (
|
2023-04-25 05:41:23 -07:00
|
|
|
|
change ? (
|
|
|
|
|
<Change change={change} suffix="%" />
|
|
|
|
|
) : (
|
|
|
|
|
<span className="text-th-fgd-3">–</span>
|
|
|
|
|
)
|
2023-04-03 19:48:31 -07:00
|
|
|
|
) : (
|
|
|
|
|
<SheenLoader className="mt-0.5">
|
|
|
|
|
<div className="h-3.5 w-12 bg-th-bkg-2" />
|
|
|
|
|
</SheenLoader>
|
|
|
|
|
)}
|
2023-05-03 18:41:18 -07:00
|
|
|
|
</Link>
|
|
|
|
|
<FavoriteMarketButton market={m} />
|
2023-02-03 08:04:29 -08:00
|
|
|
|
</div>
|
|
|
|
|
)
|
2023-02-22 11:04:18 -08:00
|
|
|
|
})}
|
|
|
|
|
</>
|
|
|
|
|
) : null}
|
2022-11-30 07:46:20 -08:00
|
|
|
|
</Popover.Panel>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</Popover>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default MarketSelectDropdown
|