mango-v4-ui/components/trade/AdvancedMarketHeader.tsx

161 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PerpMarket, Serum3Market } from '@blockworks-foundation/mango-v4'
import { IconButton } from '@components/shared/Button'
import Change from '@components/shared/Change'
import { getOneDayPerpStats } from '@components/stats/PerpMarketsTable'
import { ChartBarIcon } from '@heroicons/react/20/solid'
import { Market } from '@project-serum/serum'
import mangoStore from '@store/mangoStore'
import { useQuery } from '@tanstack/react-query'
import useJupiterMints from 'hooks/useJupiterMints'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useTranslation } from 'next-i18next'
import { useEffect, useMemo } from 'react'
import { Token } from 'types/jupiter'
import { getDecimalCount } from 'utils/numbers'
import MarketSelectDropdown from './MarketSelectDropdown'
import PerpFundingRate from './PerpFundingRate'
type ResponseType = {
prices: [number, number][]
market_caps: [number, number][]
total_volumes: [number, number][]
}
const fetchTokenChange = async (
mangoTokens: Token[],
baseAddress: string
): Promise<ResponseType> => {
let coingeckoId = mangoTokens.find((t) => t.address === baseAddress)
?.extensions?.coingeckoId
if (baseAddress === '3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh') {
coingeckoId = 'bitcoin'
}
const response = await fetch(
`https://api.coingecko.com/api/v3/coins/${coingeckoId}/market_chart?vs_currency=usd&days=1`
)
const data = await response.json()
return data
}
const AdvancedMarketHeader = ({
showChart,
setShowChart,
}: {
showChart?: boolean
setShowChart?: (x: boolean) => void
}) => {
const { t } = useTranslation(['common', 'trade'])
const perpStats = mangoStore((s) => s.perpStats.data)
const { serumOrPerpMarket, price, selectedMarket } = useSelectedMarket()
const selectedMarketName = mangoStore((s) => s.selectedMarket.name)
const { mangoTokens } = useJupiterMints()
useEffect(() => {
if (serumOrPerpMarket instanceof PerpMarket) {
const actions = mangoStore.getState().actions
actions.fetchPerpStats()
}
}, [serumOrPerpMarket])
const spotBaseAddress = useMemo(() => {
const group = mangoStore.getState().group
if (group && selectedMarket && selectedMarket instanceof Serum3Market) {
return group
.getFirstBankByTokenIndex(selectedMarket.baseTokenIndex)
.mint.toString()
}
}, [selectedMarket])
const spotChangeResponse = useQuery(
['coingecko-tokens', spotBaseAddress],
() => fetchTokenChange(mangoTokens, spotBaseAddress!),
{
cacheTime: 1000 * 60 * 15,
staleTime: 1000 * 60 * 10,
retry: 3,
enabled:
!!spotBaseAddress &&
serumOrPerpMarket instanceof Market &&
mangoTokens.length > 0,
refetchOnWindowFocus: false,
}
)
const change = useMemo(() => {
if (!price || !serumOrPerpMarket) return 0
if (serumOrPerpMarket instanceof PerpMarket) {
const changeData = getOneDayPerpStats(perpStats, selectedMarketName)
return changeData.length
? ((price - changeData[0].price) / changeData[0].price) * 100
: 0
} else {
if (!spotChangeResponse.data) return 0
return (
((price - spotChangeResponse.data.prices?.[0][1]) /
spotChangeResponse.data.prices?.[0][1]) *
100
)
}
}, [
spotChangeResponse,
price,
serumOrPerpMarket,
perpStats,
selectedMarketName,
])
return (
<div className="flex flex-col bg-th-bkg-1 md:h-12 md:flex-row md:items-center">
<div className="w-full px-4 md:w-auto md:px-6 md:py-0 lg:pb-0">
<MarketSelectDropdown />
</div>
<div className="border-t border-th-bkg-3 py-2 px-5 md:border-t-0 md:py-0 md:px-0">
<div className="flex items-center justify-between">
<div className="flex items-center">
<div id="trade-step-two" className="flex-col md:ml-6">
<div className="text-xs text-th-fgd-4">
{t('trade:oracle-price')}
</div>
<div className="font-mono text-xs text-th-fgd-2">
{price ? (
`$${price.toFixed(
getDecimalCount(serumOrPerpMarket?.tickSize || 0.01)
)}`
) : (
<span className="text-th-fgd-4"></span>
)}
</div>
</div>
<div className="ml-6 flex-col">
<div className="text-xs text-th-fgd-4">{t('rolling-change')}</div>
<Change change={change} size="small" suffix="%" />
</div>
{serumOrPerpMarket instanceof PerpMarket ? (
<div className="ml-6 flex-col">
<div className="text-xs text-th-fgd-4">
{t('trade:funding-rate')}
</div>
<PerpFundingRate />
</div>
) : null}
</div>
{setShowChart ? (
<IconButton
className={showChart ? 'text-th-active' : 'text-th-fgd-2'}
onClick={() => setShowChart(!showChart)}
hideBg
>
<ChartBarIcon className="h-5 w-5" />
</IconButton>
) : null}
</div>
</div>
</div>
)
}
export default AdvancedMarketHeader