Merge pull request #270 from blockworks-foundation/mobile-tweaks
market selection on mobile
This commit is contained in:
commit
57732372d4
|
@ -91,7 +91,7 @@ const MarketNavItem: FunctionComponent<MarketNavItemProps> = ({
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</button>
|
</button>
|
||||||
<div className="ml-1">
|
<div className="ml-1 hidden sm:block">
|
||||||
<FavoriteMarketButton market={market} />
|
<FavoriteMarketButton market={market} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useMemo } from 'react'
|
import { useEffect, useMemo } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { formatUsdValue, perpContractPrecision, usdFormatter } from '../utils'
|
import { formatUsdValue, perpContractPrecision, usdFormatter } from '../utils'
|
||||||
import { Table, Td, Th, TrBody, TrHead } from './TableElements'
|
import { Table, Td, Th, TrBody, TrHead } from './TableElements'
|
||||||
|
@ -6,18 +6,29 @@ import { useViewport } from '../hooks/useViewport'
|
||||||
import { breakpoints } from './TradePageGrid'
|
import { breakpoints } from './TradePageGrid'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import useMangoStore from '../stores/useMangoStore'
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
import MobileTableHeader from './mobile/MobileTableHeader'
|
|
||||||
import { ExpandableRow } from './TableElements'
|
|
||||||
import { FavoriteMarketButton } from './TradeNavMenu'
|
import { FavoriteMarketButton } from './TradeNavMenu'
|
||||||
import { useSortableData } from '../hooks/useSortableData'
|
import { useSortableData } from '../hooks/useSortableData'
|
||||||
import { LinkButton } from './Button'
|
import { LinkButton } from './Button'
|
||||||
import { ArrowSmDownIcon } from '@heroicons/react/solid'
|
import { ArrowSmDownIcon } from '@heroicons/react/solid'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { AreaChart, Area, XAxis, YAxis } from 'recharts'
|
||||||
|
import { InformationCircleIcon } from '@heroicons/react/outline'
|
||||||
|
import Tooltip from './Tooltip'
|
||||||
|
|
||||||
const MarketsTable = ({ isPerpMarket }) => {
|
const MarketsTable = ({ isPerpMarket }) => {
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const { width } = useViewport()
|
const { width } = useViewport()
|
||||||
const isMobile = width ? width < breakpoints.md : false
|
const isMobile = width ? width < breakpoints.md : false
|
||||||
const marketsInfo = useMangoStore((s) => s.marketsInfo)
|
const marketsInfo = useMangoStore((s) => s.marketsInfo)
|
||||||
|
const actions = useMangoStore((s) => s.actions)
|
||||||
|
const coingeckoPrices = useMangoStore((s) => s.coingeckoPrices)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (coingeckoPrices.length === 0) {
|
||||||
|
actions.fetchCoingeckoPrices()
|
||||||
|
}
|
||||||
|
}, [coingeckoPrices])
|
||||||
|
|
||||||
const perpMarketsInfo = useMemo(
|
const perpMarketsInfo = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -49,7 +60,9 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
className="flex items-center font-normal no-underline"
|
className="flex items-center font-normal no-underline"
|
||||||
onClick={() => requestSort('name')}
|
onClick={() => requestSort('name')}
|
||||||
>
|
>
|
||||||
<span className="font-normal text-th-fgd-3">{t('market')}</span>
|
<span className="text-left font-normal text-th-fgd-3">
|
||||||
|
{t('market')}
|
||||||
|
</span>
|
||||||
<ArrowSmDownIcon
|
<ArrowSmDownIcon
|
||||||
className={`default-transition ml-1 h-4 w-4 flex-shrink-0 ${
|
className={`default-transition ml-1 h-4 w-4 flex-shrink-0 ${
|
||||||
sortConfig?.key === 'name'
|
sortConfig?.key === 'name'
|
||||||
|
@ -66,7 +79,9 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
className="flex items-center font-normal no-underline"
|
className="flex items-center font-normal no-underline"
|
||||||
onClick={() => requestSort('last')}
|
onClick={() => requestSort('last')}
|
||||||
>
|
>
|
||||||
<span className="font-normal text-th-fgd-3">{t('price')}</span>
|
<span className="text-left font-normal text-th-fgd-3">
|
||||||
|
{t('price')}
|
||||||
|
</span>
|
||||||
<ArrowSmDownIcon
|
<ArrowSmDownIcon
|
||||||
className={`default-transition ml-1 h-4 w-4 flex-shrink-0 ${
|
className={`default-transition ml-1 h-4 w-4 flex-shrink-0 ${
|
||||||
sortConfig?.key === 'last'
|
sortConfig?.key === 'last'
|
||||||
|
@ -83,7 +98,7 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
className="flex items-center font-normal no-underline"
|
className="flex items-center font-normal no-underline"
|
||||||
onClick={() => requestSort('change24h')}
|
onClick={() => requestSort('change24h')}
|
||||||
>
|
>
|
||||||
<span className="font-normal text-th-fgd-3">
|
<span className="text-left font-normal text-th-fgd-3">
|
||||||
{t('rolling-change')}
|
{t('rolling-change')}
|
||||||
</span>
|
</span>
|
||||||
<ArrowSmDownIcon
|
<ArrowSmDownIcon
|
||||||
|
@ -102,7 +117,7 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
className="flex items-center font-normal no-underline"
|
className="flex items-center font-normal no-underline"
|
||||||
onClick={() => requestSort('volumeUsd24h')}
|
onClick={() => requestSort('volumeUsd24h')}
|
||||||
>
|
>
|
||||||
<span className="font-normal text-th-fgd-3">
|
<span className="text-left font-normal text-th-fgd-3">
|
||||||
{t('daily-volume')}
|
{t('daily-volume')}
|
||||||
</span>
|
</span>
|
||||||
<ArrowSmDownIcon
|
<ArrowSmDownIcon
|
||||||
|
@ -123,7 +138,7 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
className="flex items-center font-normal no-underline"
|
className="flex items-center font-normal no-underline"
|
||||||
onClick={() => requestSort('funding1h')}
|
onClick={() => requestSort('funding1h')}
|
||||||
>
|
>
|
||||||
<span className="font-normal text-th-fgd-3">
|
<span className="text-left font-normal text-th-fgd-3">
|
||||||
{t('average-funding')}
|
{t('average-funding')}
|
||||||
</span>
|
</span>
|
||||||
<ArrowSmDownIcon
|
<ArrowSmDownIcon
|
||||||
|
@ -142,7 +157,7 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
className="flex items-center no-underline"
|
className="flex items-center no-underline"
|
||||||
onClick={() => requestSort('openInterestUsd')}
|
onClick={() => requestSort('openInterestUsd')}
|
||||||
>
|
>
|
||||||
<span className="font-normal text-th-fgd-3">
|
<span className="text-left font-normal text-th-fgd-3">
|
||||||
{t('open-interest')}
|
{t('open-interest')}
|
||||||
</span>
|
</span>
|
||||||
<ArrowSmDownIcon
|
<ArrowSmDownIcon
|
||||||
|
@ -178,7 +193,10 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
const fundingApr = funding1h
|
const fundingApr = funding1h
|
||||||
? (funding1h * 24 * 365).toFixed(2)
|
? (funding1h * 24 * 365).toFixed(2)
|
||||||
: '-'
|
: '-'
|
||||||
|
const coingeckoData = coingeckoPrices.find(
|
||||||
|
(asset) => asset.symbol === baseSymbol
|
||||||
|
)
|
||||||
|
const chartData = coingeckoData ? coingeckoData.prices : undefined
|
||||||
return (
|
return (
|
||||||
<TrBody key={name} className="hover:bg-th-bkg-3">
|
<TrBody key={name} className="hover:bg-th-bkg-3">
|
||||||
<Td>
|
<Td>
|
||||||
|
@ -197,12 +215,36 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td className="flex items-center">
|
||||||
|
<div className="w-20">
|
||||||
{last ? (
|
{last ? (
|
||||||
formatUsdValue(last)
|
formatUsdValue(last)
|
||||||
) : (
|
) : (
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
<span className="text-th-fgd-4">{t('unavailable')}</span>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="pl-6">
|
||||||
|
{chartData !== undefined ? (
|
||||||
|
<AreaChart width={104} height={40} data={chartData}>
|
||||||
|
<Area
|
||||||
|
isAnimationActive={false}
|
||||||
|
type="monotone"
|
||||||
|
dataKey="1"
|
||||||
|
stroke="#FF9C24"
|
||||||
|
fill="#FF9C24"
|
||||||
|
fillOpacity={0.1}
|
||||||
|
/>
|
||||||
|
<XAxis dataKey="0" hide />
|
||||||
|
<YAxis
|
||||||
|
domain={['dataMin', 'dataMax']}
|
||||||
|
dataKey="1"
|
||||||
|
hide
|
||||||
|
/>
|
||||||
|
</AreaChart>
|
||||||
|
) : (
|
||||||
|
t('unavailable')
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<span
|
<span
|
||||||
|
@ -211,7 +253,7 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
{change24h || change24h === 0 ? (
|
{change24h || change24h === 0 ? (
|
||||||
`${(change24h * 100).toFixed(2)}%`
|
`${(change24h * 100).toFixed(2)}%`
|
||||||
) : (
|
) : (
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
<span className="text-th-fgd-4">{t('unavailable')}</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</Td>
|
</Td>
|
||||||
|
@ -219,7 +261,7 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
{volumeUsd24h ? (
|
{volumeUsd24h ? (
|
||||||
usdFormatter(volumeUsd24h, 0)
|
usdFormatter(volumeUsd24h, 0)
|
||||||
) : (
|
) : (
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
<span className="text-th-fgd-4">{t('unavailable')}</span>
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
{isPerpMarket ? (
|
{isPerpMarket ? (
|
||||||
|
@ -231,7 +273,9 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
<span className="text-xs text-th-fgd-3">{`(${fundingApr}% APR)`}</span>
|
<span className="text-xs text-th-fgd-3">{`(${fundingApr}% APR)`}</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
<span className="text-th-fgd-4">
|
||||||
|
{t('unavailable')}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
|
@ -249,7 +293,9 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
<span className="text-th-fgd-4">
|
||||||
|
{t('unavailable')}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
</>
|
</>
|
||||||
|
@ -265,128 +311,89 @@ const MarketsTable = ({ isPerpMarket }) => {
|
||||||
</tbody>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
) : (
|
) : (
|
||||||
<div className="mb-4 border-b border-th-bkg-4">
|
items.map((market) => {
|
||||||
<MobileTableHeader
|
const { baseSymbol, change24h, funding1h, last, name } = market
|
||||||
colOneHeader={t('asset')}
|
|
||||||
colTwoHeader={`${t('price')}/${t('rolling-change')}`}
|
|
||||||
/>
|
|
||||||
{items.map((market, index) => {
|
|
||||||
const {
|
|
||||||
baseSymbol,
|
|
||||||
change24h,
|
|
||||||
funding1h,
|
|
||||||
high24h,
|
|
||||||
last,
|
|
||||||
low24h,
|
|
||||||
name,
|
|
||||||
openInterest,
|
|
||||||
volumeUsd24h,
|
|
||||||
} = market
|
|
||||||
const fundingApr = funding1h ? (funding1h * 24 * 365).toFixed(2) : '-'
|
const fundingApr = funding1h ? (funding1h * 24 * 365).toFixed(2) : '-'
|
||||||
|
const coingeckoData = coingeckoPrices.find(
|
||||||
|
(asset) => asset.symbol === baseSymbol
|
||||||
|
)
|
||||||
|
const chartData = coingeckoData ? coingeckoData.prices : undefined
|
||||||
return (
|
return (
|
||||||
<ExpandableRow
|
<button
|
||||||
buttonTemplate={
|
className="mb-2.5 w-full rounded-lg bg-th-bkg-2 p-4 pb-2.5 md:bg-th-bkg-3"
|
||||||
<div className="flex w-full items-center justify-between text-th-fgd-1">
|
onClick={() =>
|
||||||
<div className="flex items-center text-th-fgd-1">
|
router.push(`/?name=${name}`, undefined, {
|
||||||
|
shallow: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
key={name}
|
||||||
|
>
|
||||||
|
<div className="mb-1 flex justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="mb-2 flex items-center font-bold text-th-fgd-3">
|
||||||
<img
|
<img
|
||||||
alt=""
|
alt=""
|
||||||
width="20"
|
width="24"
|
||||||
height="20"
|
height="24"
|
||||||
src={`/assets/icons/${baseSymbol.toLowerCase()}.svg`}
|
src={`/assets/icons/${baseSymbol.toLowerCase()}.svg`}
|
||||||
className={`mr-2.5`}
|
className="mr-2"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{market.baseSymbol}
|
{name}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-2.5 text-right text-th-fgd-1">
|
{chartData !== undefined ? (
|
||||||
<div>{formatUsdValue(last)}</div>
|
<AreaChart width={144} height={40} data={chartData}>
|
||||||
<div className="text-th-fgd-4">|</div>
|
<Area
|
||||||
|
isAnimationActive={false}
|
||||||
|
type="monotone"
|
||||||
|
dataKey="1"
|
||||||
|
stroke="#FF9C24"
|
||||||
|
fill="#FF9C24"
|
||||||
|
fillOpacity={0.1}
|
||||||
|
/>
|
||||||
|
<XAxis dataKey="0" hide />
|
||||||
|
<YAxis domain={['dataMin', 'dataMax']} dataKey="1" hide />
|
||||||
|
</AreaChart>
|
||||||
|
) : (
|
||||||
|
t('unavailable')
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<p className="mb-0 text-xl font-bold">
|
||||||
|
{last ? (
|
||||||
|
formatUsdValue(last)
|
||||||
|
) : (
|
||||||
|
<span className="text-th-fgd-4">{t('unavailable')}</span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
<div
|
<div
|
||||||
className={
|
className={change24h >= 0 ? 'text-th-green' : 'text-th-red'}
|
||||||
change24h >= 0 ? 'text-th-green' : 'text-th-red'
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{change24h || change24h === 0 ? (
|
{change24h || change24h === 0 ? (
|
||||||
`${(change24h * 100).toFixed(2)}%`
|
`${(change24h * 100).toFixed(2)}%`
|
||||||
) : (
|
) : (
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
<span className="text-th-fgd-4">{t('unavailable')}</span>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
key={`${name}${index}`}
|
|
||||||
panelTemplate={
|
|
||||||
<>
|
|
||||||
<div className="grid grid-flow-row grid-cols-2 gap-4 pb-4">
|
|
||||||
<div className="text-left">
|
|
||||||
<div className="pb-0.5 text-xs text-th-fgd-3">
|
|
||||||
{t('daily-low')}
|
|
||||||
</div>
|
|
||||||
{low24h ? (
|
|
||||||
formatUsdValue(low24h)
|
|
||||||
) : (
|
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="text-left">
|
|
||||||
<div className="pb-0.5 text-xs text-th-fgd-3">
|
|
||||||
{t('daily-high')}
|
|
||||||
</div>
|
|
||||||
{high24h ? (
|
|
||||||
formatUsdValue(high24h)
|
|
||||||
) : (
|
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isPerpMarket ? (
|
{isPerpMarket ? (
|
||||||
<>
|
funding1h ? (
|
||||||
<div className="text-left">
|
<Tooltip content={t('average-funding')}>
|
||||||
<div className="pb-0.5 text-xs text-th-fgd-3">
|
<div className="mt-1 flex items-center justify-end text-th-fgd-3">
|
||||||
{t('daily-volume')}
|
<span className="text-xs">{`${fundingApr}% APR`}</span>
|
||||||
|
<InformationCircleIcon className="ml-1 h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
{volumeUsd24h ? (
|
</Tooltip>
|
||||||
usdFormatter(volumeUsd24h, 0)
|
|
||||||
) : (
|
) : (
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
<span className="text-th-fgd-4">{t('unavailable')}</span>
|
||||||
)}
|
)
|
||||||
</div>
|
|
||||||
<div className="text-left">
|
|
||||||
<div className="pb-0.5 text-xs text-th-fgd-3">
|
|
||||||
{t('average-funding')}
|
|
||||||
</div>
|
|
||||||
{funding1h ? (
|
|
||||||
`${funding1h.toLocaleString(undefined, {
|
|
||||||
maximumSignificantDigits: 3,
|
|
||||||
})}% (${fundingApr}% APR)`
|
|
||||||
) : (
|
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="text-left">
|
|
||||||
<div className="pb-0.5 text-xs text-th-fgd-3">
|
|
||||||
{t('open-interest')}
|
|
||||||
</div>
|
|
||||||
{openInterest ? (
|
|
||||||
`${openInterest.toLocaleString()} ${
|
|
||||||
market.baseSymbol
|
|
||||||
}`
|
|
||||||
) : (
|
|
||||||
<span className="text-th-fgd-4">Unavailable</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})
|
||||||
)
|
)
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MarketsTable
|
export default MarketsTable as any
|
||||||
|
|
|
@ -523,14 +523,6 @@ export default function Orderbook({ depth = 8 }) {
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between pb-2.5">
|
<div className="flex items-center justify-between pb-2.5">
|
||||||
<div className="relative flex">
|
<div className="relative flex">
|
||||||
<Tooltip
|
|
||||||
content={
|
|
||||||
displayCumulativeSize
|
|
||||||
? t('tooltip-display-step')
|
|
||||||
: t('tooltip-display-cumulative')
|
|
||||||
}
|
|
||||||
className="py-1 text-xs"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDisplayCumulativeSize(!displayCumulativeSize)
|
setDisplayCumulativeSize(!displayCumulativeSize)
|
||||||
|
@ -543,7 +535,6 @@ export default function Orderbook({ depth = 8 }) {
|
||||||
<CumulativeSizeIcon className="h-5 w-5" />
|
<CumulativeSizeIcon className="h-5 w-5" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<GroupSize
|
<GroupSize
|
||||||
|
|
|
@ -103,10 +103,10 @@ const SwitchMarketDropdown = () => {
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<Popover.Panel
|
<Popover.Panel
|
||||||
className="thin-scroll absolute left-0 top-14 z-10 max-h-[75vh] w-72 transform overflow-y-auto rounded-b-md rounded-tl-md bg-th-bkg-3 p-4"
|
className="thin-scroll absolute left-0 top-14 z-10 max-h-[50vh] w-72 transform overflow-y-auto rounded-b-md rounded-tl-md bg-th-bkg-3 p-4 sm:max-h-[75vh]"
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<div className="pb-2.5">
|
<div className="hidden pb-2.5 sm:block">
|
||||||
<Input
|
<Input
|
||||||
onChange={(e) => onSearch(e.target.value)}
|
onChange={(e) => onSearch(e.target.value)}
|
||||||
prefix={<SearchIcon className="h-4 w-4 text-th-fgd-3" />}
|
prefix={<SearchIcon className="h-4 w-4 text-th-fgd-3" />}
|
||||||
|
@ -134,7 +134,7 @@ const SwitchMarketDropdown = () => {
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="flex justify-between py-1.5">
|
<div className="flex justify-between py-1.5">
|
||||||
<h4 className="text-xs font-normal">{t('futures')}</h4>
|
<h4 className="text-xs font-normal">{t('futures')}</h4>
|
||||||
<p className="mb-0 text-xs text-th-fgd-3">
|
<p className="mb-0 hidden text-xs text-th-fgd-3 sm:block">
|
||||||
{t('favorite')}
|
{t('favorite')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -148,7 +148,7 @@ const SwitchMarketDropdown = () => {
|
||||||
))}
|
))}
|
||||||
<div className="flex justify-between py-1.5">
|
<div className="flex justify-between py-1.5">
|
||||||
<h4 className="text-xs font-normal">{t('spot')}</h4>
|
<h4 className="text-xs font-normal">{t('spot')}</h4>
|
||||||
<p className="mb-0 text-xs text-th-fgd-3">
|
<p className="mb-0 hidden text-xs text-th-fgd-3 sm:block">
|
||||||
{t('favorite')}
|
{t('favorite')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -36,12 +36,12 @@ const BottomBar = () => {
|
||||||
<div className="default-transition grid grid-cols-5 grid-rows-1 bg-th-bkg-3 py-2.5">
|
<div className="default-transition grid grid-cols-5 grid-rows-1 bg-th-bkg-3 py-2.5">
|
||||||
<Link
|
<Link
|
||||||
href={{
|
href={{
|
||||||
pathname: '/select',
|
pathname: '/markets',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
asPath === '/select' ? 'text-th-primary' : 'text-th-fgd-3'
|
asPath === '/markets' ? 'text-th-primary' : 'text-th-fgd-3'
|
||||||
} default-transition col-span-1 flex cursor-pointer flex-col items-center hover:text-th-primary`}
|
} default-transition col-span-1 flex cursor-pointer flex-col items-center hover:text-th-primary`}
|
||||||
>
|
>
|
||||||
<BtcMonoIcon className="mb-1 h-4 w-4" />
|
<BtcMonoIcon className="mb-1 h-4 w-4" />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import { Disclosure } from '@headlessui/react'
|
import { Disclosure } from '@headlessui/react'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import { SwitchHorizontalIcon, XIcon } from '@heroicons/react/outline'
|
import { XIcon } from '@heroicons/react/outline'
|
||||||
import useMangoStore from '../../stores/useMangoStore'
|
import useMangoStore from '../../stores/useMangoStore'
|
||||||
import { getWeights, PerpMarket } from '@blockworks-foundation/mango-client'
|
import { getWeights, PerpMarket } from '@blockworks-foundation/mango-client'
|
||||||
import { CandlesIcon } from '../icons'
|
import { CandlesIcon } from '../icons'
|
||||||
|
@ -16,8 +16,8 @@ import RecentMarketTrades from '../RecentMarketTrades'
|
||||||
import FloatingElement from '../FloatingElement'
|
import FloatingElement from '../FloatingElement'
|
||||||
import Swipeable from './Swipeable'
|
import Swipeable from './Swipeable'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import Link from 'next/link'
|
|
||||||
import { useWallet } from '@solana/wallet-adapter-react'
|
import { useWallet } from '@solana/wallet-adapter-react'
|
||||||
|
import SwitchMarketDropdown from 'components/SwitchMarketDropdown'
|
||||||
|
|
||||||
const TVChartContainer = dynamic(
|
const TVChartContainer = dynamic(
|
||||||
() => import('../../components/TradingView/index'),
|
() => import('../../components/TradingView/index'),
|
||||||
|
@ -31,9 +31,6 @@ const MobileTradePage = () => {
|
||||||
const selectedMarket = useMangoStore((s) => s.selectedMarket.current)
|
const selectedMarket = useMangoStore((s) => s.selectedMarket.current)
|
||||||
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
|
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
|
||||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||||
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
|
|
||||||
const baseSymbol = marketConfig.baseSymbol
|
|
||||||
const isPerpMarket = marketConfig.kind === 'perp'
|
|
||||||
|
|
||||||
const handleChangeViewIndex = (index) => {
|
const handleChangeViewIndex = (index) => {
|
||||||
setViewIndex(index)
|
setViewIndex(index)
|
||||||
|
@ -57,30 +54,10 @@ const MobileTradePage = () => {
|
||||||
<div className="px-2 pb-14 pt-4">
|
<div className="px-2 pb-14 pt-4">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<img
|
<SwitchMarketDropdown />
|
||||||
alt=""
|
|
||||||
width="30"
|
|
||||||
height="30"
|
|
||||||
src={`/assets/icons/${baseSymbol.toLowerCase()}.svg`}
|
|
||||||
className="mr-2"
|
|
||||||
/>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="pr-0.5 text-xl font-semibold">{baseSymbol}</div>
|
|
||||||
<span className="text-xl text-th-fgd-4">
|
|
||||||
{isPerpMarket ? '-' : '/'}
|
|
||||||
</span>
|
|
||||||
<div className="pl-0.5 text-xl font-semibold">
|
|
||||||
{isPerpMarket ? 'PERP' : groupConfig.quoteSymbol}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span className="ml-2 rounded border border-th-primary px-1 py-0.5 text-xs text-th-primary">
|
<span className="ml-2 rounded border border-th-primary px-1 py-0.5 text-xs text-th-primary">
|
||||||
{initLeverage}x
|
{initLeverage}x
|
||||||
</span>
|
</span>
|
||||||
<Link href="/select">
|
|
||||||
<div className="ml-2 flex h-10 w-10 items-center justify-center">
|
|
||||||
<SwitchHorizontalIcon className="h-5 w-5" />
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
<Disclosure>
|
<Disclosure>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
|
|
|
@ -15,11 +15,7 @@ import {
|
||||||
InformationCircleIcon,
|
InformationCircleIcon,
|
||||||
} from '@heroicons/react/outline'
|
} from '@heroicons/react/outline'
|
||||||
import { notify } from '../../utils/notifications'
|
import { notify } from '../../utils/notifications'
|
||||||
import {
|
import { calculateTradePrice, getDecimalCount } from '../../utils'
|
||||||
calculateTradePrice,
|
|
||||||
getDecimalCount,
|
|
||||||
percentFormat,
|
|
||||||
} from '../../utils'
|
|
||||||
import { floorToDecimal } from '../../utils/index'
|
import { floorToDecimal } from '../../utils/index'
|
||||||
import useMangoStore, { Orderbook } from '../../stores/useMangoStore'
|
import useMangoStore, { Orderbook } from '../../stores/useMangoStore'
|
||||||
import Button, { LinkButton } from '../Button'
|
import Button, { LinkButton } from '../Button'
|
||||||
|
@ -79,7 +75,7 @@ export default function AdvancedTradeForm({
|
||||||
const [spotMargin, setSpotMargin] = useState(defaultSpotMargin)
|
const [spotMargin, setSpotMargin] = useState(defaultSpotMargin)
|
||||||
const [positionSizePercent, setPositionSizePercent] = useState('')
|
const [positionSizePercent, setPositionSizePercent] = useState('')
|
||||||
const [insufficientSol, setInsufficientSol] = useState(false)
|
const [insufficientSol, setInsufficientSol] = useState(false)
|
||||||
const { takerFee, makerFee } = useFees()
|
const { takerFee } = useFees()
|
||||||
const { totalMsrm } = useSrmAccount()
|
const { totalMsrm } = useSrmAccount()
|
||||||
|
|
||||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||||
|
@ -947,7 +943,7 @@ export default function AdvancedTradeForm({
|
||||||
</div>
|
</div>
|
||||||
) : null
|
) : null
|
||||||
) : null}
|
) : null}
|
||||||
<div className="flex-wrap sm:flex">
|
<div className="mt-1 flex-wrap sm:flex">
|
||||||
{isLimitOrder ? (
|
{isLimitOrder ? (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="mr-4 mt-3">
|
<div className="mr-4 mt-3">
|
||||||
|
@ -989,6 +985,7 @@ export default function AdvancedTradeForm({
|
||||||
auto updating the reduceOnly state when doing a market order:
|
auto updating the reduceOnly state when doing a market order:
|
||||||
&& showReduceOnly(perpAccount?.basePosition.toNumber())
|
&& showReduceOnly(perpAccount?.basePosition.toNumber())
|
||||||
*/}
|
*/}
|
||||||
|
<div className="flex">
|
||||||
{marketConfig.kind === 'perp' || isLuna ? (
|
{marketConfig.kind === 'perp' || isLuna ? (
|
||||||
<div className="mr-4 mt-3">
|
<div className="mr-4 mt-3">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
@ -1025,6 +1022,7 @@ export default function AdvancedTradeForm({
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
</div>
|
||||||
{marketConfig.kind === 'spot' ? (
|
{marketConfig.kind === 'spot' ? (
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
@ -1050,7 +1048,7 @@ export default function AdvancedTradeForm({
|
||||||
<div className="text-xs">{t('slippage-warning')}</div>
|
<div className="text-xs">{t('slippage-warning')}</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className={`mt-3 flex`}>
|
<div className={`mt-4 flex`}>
|
||||||
{canTrade ? (
|
{canTrade ? (
|
||||||
<button
|
<button
|
||||||
disabled={disabledTradeButton}
|
disabled={disabledTradeButton}
|
||||||
|
@ -1174,18 +1172,7 @@ export default function AdvancedTradeForm({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : null}
|
||||||
<div className="mt-2.5 flex flex-col items-center justify-center px-6 text-xs text-th-fgd-4 md:flex-row">
|
|
||||||
<div>
|
|
||||||
{t('maker-fee')}: {percentFormat.format(makerFee)}{' '}
|
|
||||||
</div>
|
|
||||||
<span className="hidden md:block md:px-1">|</span>
|
|
||||||
<div>
|
|
||||||
{' '}
|
|
||||||
{t('taker-fee')}: {percentFormat.format(takerFee)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useTranslation } from 'next-i18next'
|
||||||
import MarketsTable from '../components/MarketsTable'
|
import MarketsTable from '../components/MarketsTable'
|
||||||
import Tabs from '../components/Tabs'
|
import Tabs from '../components/Tabs'
|
||||||
|
|
||||||
const TABS = ['perp', 'spot']
|
const TABS = ['futures', 'spot']
|
||||||
|
|
||||||
export async function getStaticProps({ locale }) {
|
export async function getStaticProps({ locale }) {
|
||||||
return {
|
return {
|
||||||
|
@ -18,15 +18,13 @@ export async function getStaticProps({ locale }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Markets() {
|
export default function Markets() {
|
||||||
const [activeTab, setActiveTab] = useState('perp')
|
const [activeTab, setActiveTab] = useState('futures')
|
||||||
const { t } = useTranslation(['common'])
|
const { t } = useTranslation(['common'])
|
||||||
|
|
||||||
const handleTabChange = (tabName) => {
|
const handleTabChange = (tabName) => {
|
||||||
setActiveTab(tabName)
|
setActiveTab(tabName)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPerp = activeTab === 'perp'
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all`}>
|
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all`}>
|
||||||
<TopBar />
|
<TopBar />
|
||||||
|
@ -35,13 +33,14 @@ export default function Markets() {
|
||||||
<h1>{t('markets')}</h1>
|
<h1>{t('markets')}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:rounded-lg md:bg-th-bkg-2 md:p-6">
|
<div className="md:rounded-lg md:bg-th-bkg-2 md:p-6">
|
||||||
<Tabs activeTab={activeTab} onChange={handleTabChange} tabs={TABS} />
|
<div className="mb-0 sm:mb-6">
|
||||||
<h2 className="mb-4">
|
<Tabs
|
||||||
{isPerp
|
activeTab={activeTab}
|
||||||
? `${t('perp')} ${t('markets')}`
|
onChange={handleTabChange}
|
||||||
: `${t('spot')} ${t('markets')}`}
|
tabs={TABS}
|
||||||
</h2>
|
/>
|
||||||
<MarketsTable isPerpMarket={activeTab === 'perp'} />
|
</div>
|
||||||
|
<MarketsTable isPerpMarket={activeTab === 'futures'} />
|
||||||
</div>
|
</div>
|
||||||
</PageBodyContainer>
|
</PageBodyContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|
109
pages/select.tsx
109
pages/select.tsx
|
@ -1,109 +0,0 @@
|
||||||
import { useTranslation } from 'next-i18next'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { ChevronRightIcon } from '@heroicons/react/solid'
|
|
||||||
import useMangoStore from '../stores/useMangoStore'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import { formatUsdValue } from '../utils'
|
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
|
||||||
import PageBodyContainer from '../components/PageBodyContainer'
|
|
||||||
import TopBar from '../components/TopBar'
|
|
||||||
|
|
||||||
export async function getStaticProps({ locale }) {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
...(await serverSideTranslations(locale, [
|
|
||||||
'common',
|
|
||||||
'tv-chart',
|
|
||||||
'profile',
|
|
||||||
])),
|
|
||||||
// Will be passed to the page component as props
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SelectMarket = () => {
|
|
||||||
const { t } = useTranslation('common')
|
|
||||||
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
|
|
||||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
|
||||||
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
|
|
||||||
|
|
||||||
const [markets, setMarkets] = useState<any[]>([])
|
|
||||||
useEffect(() => {
|
|
||||||
const markets: any[] = []
|
|
||||||
const allMarkets =
|
|
||||||
groupConfig?.spotMarkets && groupConfig?.perpMarkets
|
|
||||||
? [...groupConfig.spotMarkets, ...groupConfig.perpMarkets]
|
|
||||||
: []
|
|
||||||
allMarkets.forEach((market) => {
|
|
||||||
const base = market.name.slice(0, -5)
|
|
||||||
const found = markets.find((b) => b.baseAsset === base)
|
|
||||||
if (!found) {
|
|
||||||
markets.push({ baseAsset: base, markets: [market] })
|
|
||||||
} else {
|
|
||||||
found.markets.push(market)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
setMarkets(markets)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (!mangoCache) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all`}>
|
|
||||||
<TopBar />
|
|
||||||
<PageBodyContainer>
|
|
||||||
<div className="py-4 text-2xl font-bold text-th-fgd-1">
|
|
||||||
{t('markets')}
|
|
||||||
</div>
|
|
||||||
{markets.map((mkt) => {
|
|
||||||
return (
|
|
||||||
<div key={mkt.baseAsset}>
|
|
||||||
<div className="flex items-center justify-between bg-th-bkg-3 px-2.5 py-2">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<img
|
|
||||||
alt=""
|
|
||||||
src={`/assets/icons/${mkt.baseAsset.toLowerCase()}.svg`}
|
|
||||||
className={`mr-2.5 h-5 w-auto`}
|
|
||||||
/>
|
|
||||||
<span className="text-th-fgd-2">{mkt.baseAsset}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="divide-y divide-th-bkg-4">
|
|
||||||
{mangoGroup
|
|
||||||
? mkt.markets.map((m) => (
|
|
||||||
<div
|
|
||||||
className={`flex items-center justify-between px-2.5 text-xs`}
|
|
||||||
key={m.name}
|
|
||||||
>
|
|
||||||
<Link href={`/?name=${m.name}`} key={m.name}>
|
|
||||||
<a className="default-transition flex h-12 w-full cursor-pointer items-center justify-between text-th-fgd-2 hover:text-th-primary">
|
|
||||||
{m.name}
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="w-20 text-right">
|
|
||||||
{formatUsdValue(
|
|
||||||
mangoGroup
|
|
||||||
.getPrice(m.marketIndex, mangoCache)
|
|
||||||
.toNumber()
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<ChevronRightIcon className="ml-1 h-4 w-5 text-th-fgd-2" />
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
: null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
{/* spacer so last market can be selected albeit bottom bar overlay */}
|
|
||||||
<p className="flex h-12 md:hidden"></p>
|
|
||||||
</PageBodyContainer>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SelectMarket
|
|
|
@ -431,6 +431,7 @@
|
||||||
"trigger-price": "Trigger Price",
|
"trigger-price": "Trigger Price",
|
||||||
"try-again": "Try again",
|
"try-again": "Try again",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
|
"unavailable": "Unavailable",
|
||||||
"unrealized-pnl": "Unrealized PnL",
|
"unrealized-pnl": "Unrealized PnL",
|
||||||
"unsettled": "Unsettled",
|
"unsettled": "Unsettled",
|
||||||
"unsettled-balance": "Redeemable Value",
|
"unsettled-balance": "Redeemable Value",
|
||||||
|
|
|
@ -431,6 +431,7 @@
|
||||||
"trigger-price": "Precio de activación",
|
"trigger-price": "Precio de activación",
|
||||||
"try-again": "Inténtalo de nuevo",
|
"try-again": "Inténtalo de nuevo",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
|
"unavailable": "Unavailable",
|
||||||
"unrealized-pnl": "PnL no realizado",
|
"unrealized-pnl": "PnL no realizado",
|
||||||
"unsettled": "Inestable",
|
"unsettled": "Inestable",
|
||||||
"unsettled-balance": "Saldo pendiente",
|
"unsettled-balance": "Saldo pendiente",
|
||||||
|
|
|
@ -431,6 +431,7 @@
|
||||||
"trigger-price": "触发价格",
|
"trigger-price": "触发价格",
|
||||||
"try-again": "请再试一次",
|
"try-again": "请再试一次",
|
||||||
"type": "类型",
|
"type": "类型",
|
||||||
|
"unavailable": "无资料",
|
||||||
"unrealized-pnl": "未实现盈亏",
|
"unrealized-pnl": "未实现盈亏",
|
||||||
"unsettled": "未结清",
|
"unsettled": "未结清",
|
||||||
"unsettled-balance": "可领取价值",
|
"unsettled-balance": "可领取价值",
|
||||||
|
|
|
@ -431,6 +431,7 @@
|
||||||
"trigger-price": "觸發價格",
|
"trigger-price": "觸發價格",
|
||||||
"try-again": "請再試一次",
|
"try-again": "請再試一次",
|
||||||
"type": "類型",
|
"type": "類型",
|
||||||
|
"unavailable": "無資料",
|
||||||
"unrealized-pnl": "未實現盈虧",
|
"unrealized-pnl": "未實現盈虧",
|
||||||
"unsettled": "未結清",
|
"unsettled": "未結清",
|
||||||
"unsettled-balance": "可領取價值",
|
"unsettled-balance": "可領取價值",
|
||||||
|
|
|
@ -40,6 +40,7 @@ import { getProfilePicture, ProfilePicture } from '@solflare-wallet/pfp'
|
||||||
import { decodeBook } from '../hooks/useHydrateStore'
|
import { decodeBook } from '../hooks/useHydrateStore'
|
||||||
import { IOrderLineAdapter } from '../public/charting_library/charting_library'
|
import { IOrderLineAdapter } from '../public/charting_library/charting_library'
|
||||||
import { Wallet } from '@solana/wallet-adapter-react'
|
import { Wallet } from '@solana/wallet-adapter-react'
|
||||||
|
import { coingeckoIds } from 'utils/tokens'
|
||||||
import { getTokenAccountsByMint } from 'utils/tokens'
|
import { getTokenAccountsByMint } from 'utils/tokens'
|
||||||
import { getParsedNftAccountsByOwner } from 'utils/getParsedNftAccountsByOwner'
|
import { getParsedNftAccountsByOwner } from 'utils/getParsedNftAccountsByOwner'
|
||||||
|
|
||||||
|
@ -264,6 +265,7 @@ export type MangoStore = {
|
||||||
deleteAlert: (id: string) => void
|
deleteAlert: (id: string) => void
|
||||||
loadAlerts: (pk: PublicKey) => void
|
loadAlerts: (pk: PublicKey) => void
|
||||||
fetchMarketsInfo: () => void
|
fetchMarketsInfo: () => void
|
||||||
|
fetchCoingeckoPrices: () => void
|
||||||
}
|
}
|
||||||
alerts: {
|
alerts: {
|
||||||
activeAlerts: Array<Alert>
|
activeAlerts: Array<Alert>
|
||||||
|
@ -277,6 +279,7 @@ export type MangoStore = {
|
||||||
tradingView: {
|
tradingView: {
|
||||||
orderLines: Map<string, IOrderLineAdapter>
|
orderLines: Map<string, IOrderLineAdapter>
|
||||||
}
|
}
|
||||||
|
coingeckoPrices: any[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const useMangoStore = create<
|
const useMangoStore = create<
|
||||||
|
@ -408,6 +411,7 @@ const useMangoStore = create<
|
||||||
tradingView: {
|
tradingView: {
|
||||||
orderLines: new Map(),
|
orderLines: new Map(),
|
||||||
},
|
},
|
||||||
|
coingeckoPrices: [],
|
||||||
set: (fn) => set(produce(fn)),
|
set: (fn) => set(produce(fn)),
|
||||||
actions: {
|
actions: {
|
||||||
async fetchWalletTokens(wallet: Wallet) {
|
async fetchWalletTokens(wallet: Wallet) {
|
||||||
|
@ -1125,6 +1129,29 @@ const useMangoStore = create<
|
||||||
console.log('ERORR: Unable to load all market info')
|
console.log('ERORR: Unable to load all market info')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async fetchCoingeckoPrices() {
|
||||||
|
const set = get().set
|
||||||
|
try {
|
||||||
|
const promises: any = []
|
||||||
|
for (const asset of coingeckoIds) {
|
||||||
|
promises.push(
|
||||||
|
fetch(
|
||||||
|
`https://api.coingecko.com/api/v3/coins/${asset.id}/market_chart?vs_currency=usd&days=2`
|
||||||
|
).then((res) => res.json())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await Promise.all(promises)
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
data[i].symbol = coingeckoIds[i].symbol
|
||||||
|
}
|
||||||
|
set((state) => {
|
||||||
|
state.coingeckoPrices = data
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.log('ERORR: Unable to load Coingecko prices')
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -23,6 +23,23 @@ export function parseTokenAccountData(data: Buffer): {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const coingeckoIds = [
|
||||||
|
{ id: 'bitcoin', symbol: 'BTC' },
|
||||||
|
{ id: 'ethereum', symbol: 'ETH' },
|
||||||
|
{ id: 'solana', symbol: 'SOL' },
|
||||||
|
{ id: 'mango-markets', symbol: 'MNGO' },
|
||||||
|
{ id: 'binancecoin', symbol: 'BNB' },
|
||||||
|
{ id: 'serum', symbol: 'SRM' },
|
||||||
|
{ id: 'raydium', symbol: 'RAY' },
|
||||||
|
{ id: 'ftx-token', symbol: 'FTT' },
|
||||||
|
{ id: 'avalanche-2', symbol: 'AVAX' },
|
||||||
|
{ id: 'terra-luna', symbol: 'LUNA' },
|
||||||
|
{ id: 'cope', symbol: 'COPE' },
|
||||||
|
{ id: 'cardano', symbol: 'ADA' },
|
||||||
|
{ id: 'msol', symbol: 'MSOL' },
|
||||||
|
{ id: 'tether', symbol: 'USDT' },
|
||||||
|
]
|
||||||
|
|
||||||
export async function getTokenAccountsByMint(
|
export async function getTokenAccountsByMint(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
mint: string
|
mint: string
|
||||||
|
|
Loading…
Reference in New Issue