add token details page

This commit is contained in:
saml33 2022-10-11 22:59:01 +11:00
parent 99a255ad75
commit db4317400d
13 changed files with 717 additions and 11 deletions

View File

@ -2,6 +2,7 @@ import { Bank, MangoAccount } from '@blockworks-foundation/mango-v4'
import { Transition } from '@headlessui/react'
import {
ChevronDownIcon,
ChevronRightIcon,
EllipsisHorizontalIcon,
QuestionMarkCircleIcon,
} from '@heroicons/react/20/solid'
@ -12,7 +13,6 @@ import { useRouter } from 'next/router'
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'
// import useLocalStorageState from '../hooks/useLocalStorageState'
import { useViewport } from '../hooks/useViewport'
import mangoStore from '@store/mangoStore'
import { formatDecimal, formatFixedDecimals } from '../utils/numbers'
import { breakpoints } from '../utils/theme'
@ -23,7 +23,6 @@ import WithdrawModal from './modals/WithdrawModal'
import { IconButton, LinkButton } from './shared/Button'
import ContentBox from './shared/ContentBox'
import IconDropMenu from './shared/IconDropMenu'
import Change from './shared/Change'
import Tooltip from './shared/Tooltip'
import { formatTokenSymbol } from 'utils/tokens'
@ -40,6 +39,7 @@ const TokenList = () => {
)
const { width } = useViewport()
const showTableView = width ? width > breakpoints.md : false
const router = useRouter()
const banks = useMemo(() => {
if (group) {
@ -76,6 +76,10 @@ const TokenList = () => {
}
}, [connected])
const goToTokenPage = (bank: Bank) => {
router.push(`/token/${bank.name}`, undefined, { shallow: true })
}
return (
<ContentBox hideBorder hidePadding className="-mt-[36px]">
<div className="mb-5 flex items-center justify-end pr-6">
@ -225,6 +229,9 @@ const TokenList = () => {
id={i === 0 ? 'account-step-ten' : ''}
>
<ActionsMenu bank={bank} mangoAccount={mangoAccount} />
<IconButton onClick={() => goToTokenPage(bank)}>
<ChevronRightIcon className="h-5 w-5" />
</IconButton>
</div>
</td>
</tr>
@ -246,7 +253,7 @@ const TokenList = () => {
export default TokenList
const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
const { t } = useTranslation('common')
const { t } = useTranslation(['common', 'token'])
const [showTokenDetails, setShowTokenDetails] = useState(false)
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
@ -256,6 +263,7 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
)
const symbol = bank.name
const oraclePrice = bank.uiPrice
const router = useRouter()
let logoURI
if (jupiterTokens.length) {
@ -281,6 +289,10 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
const unsettled = spotBalances[bank.mint.toString()]?.unsettled || 0.0
const goToTokenPage = (bank: Bank) => {
router.push(`/token/${bank.name}`, undefined, { shallow: true })
}
return (
<div key={symbol} className="border-b border-th-bkg-3 px-6 py-4">
<div className="flex items-center justify-between">
@ -366,6 +378,15 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
</span>
</p>
</div>
<div className="col-span-1">
<LinkButton
className="flex items-center"
onClick={() => goToTokenPage(bank)}
>
{t('token:token-details')}
<ChevronRightIcon className="ml-2 h-5 w-5" />
</LinkButton>
</div>
</div>
</Transition>
</div>
@ -385,7 +406,7 @@ const ActionsMenu = ({
const [showBorrowModal, setShowBorrowModal] = useState(false)
const [selectedToken, setSelectedToken] = useState('')
// const set = mangoStore.getState().set
const router = useRouter()
// const router = useRouter()
// const { asPath } = router
const jupiterTokens = mangoStore((s) => s.jupiterTokens)

View File

@ -0,0 +1,37 @@
import { useMemo } from 'react'
import { formatFixedDecimals } from 'utils/numbers'
interface DailyRangeProps {
high: number
low: number
price: number
}
const DailyRange = ({ high, low, price }: DailyRangeProps) => {
const rangePercent = useMemo(() => {
return ((price - low) * 100) / (high - low)
}, [high, low, price])
return (
<div className="flex items-center justify-between md:block">
<div className="flex items-center">
<span className={`pr-2 font-mono text-th-fgd-2`}>
{formatFixedDecimals(low, true)}
</span>
<div className="mt-[2px] flex h-2 w-32 rounded-sm bg-th-bkg-3">
<div
style={{
width: `${rangePercent}%`,
}}
className="flex rounded-sm bg-th-primary"
></div>
</div>
<span className={`pl-2 font-mono text-th-fgd-2`}>
{formatFixedDecimals(high, true)}
</span>
</div>
</div>
)
}
export default DailyRange

View File

@ -1,6 +1,7 @@
import { Transition } from '@headlessui/react'
import {
ChevronDownIcon,
ChevronRightIcon,
QuestionMarkCircleIcon,
} from '@heroicons/react/20/solid'
import { useTranslation } from 'next-i18next'
@ -11,18 +12,21 @@ import { useViewport } from '../../hooks/useViewport'
import mangoStore from '@store/mangoStore'
import { formatDecimal, formatFixedDecimals } from '../../utils/numbers'
import { breakpoints } from '../../utils/theme'
import { IconButton } from '../shared/Button'
import { IconButton, LinkButton } from '../shared/Button'
import ContentBox from '../shared/ContentBox'
import FlipNumbers from 'react-flip-numbers'
import Tooltip from '@components/shared/Tooltip'
import { Bank } from '@blockworks-foundation/mango-v4'
import { useRouter } from 'next/router'
const TokenList = () => {
const { t } = useTranslation('common')
const TokenStats = () => {
const { t } = useTranslation(['common', 'token'])
const [showTokenDetails, setShowTokenDetails] = useState('')
const group = mangoStore((s) => s.group)
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
const { width } = useViewport()
const showTableView = width ? width > breakpoints.md : false
const router = useRouter()
const banks = useMemo(() => {
if (group) {
@ -55,6 +59,10 @@ const TokenList = () => {
return []
}, [banks])
const goToTokenPage = (bank: Bank) => {
router.push(`/token/${bank.name}`, undefined, { shallow: true })
}
return (
<ContentBox hideBorder hidePadding>
<div className="grid grid-cols-2 gap-x-6 border-b border-th-bkg-3 text-[40px]">
@ -113,7 +121,7 @@ const TokenList = () => {
</div>
</th>
<th>
<div className="flex justify-end">
<div className="flex justify-end text-right">
<Tooltip content={t('asset-weight-desc')}>
<span className="tooltip-underline">
{t('asset-weight')}
@ -204,6 +212,13 @@ const TokenList = () => {
<p>{bank.initLiabWeight.toFixed(2)}</p>
</div>
</td>
<td>
<div className="flex justify-end">
<IconButton onClick={() => goToTokenPage(bank)}>
<ChevronRightIcon className="h-5 w-5" />
</IconButton>
</div>
</td>
</tr>
)
})}
@ -315,6 +330,15 @@ const TokenList = () => {
{bank.initLiabWeight.toFixed(2)}
</p>
</div>
<div className="col-span-1">
<LinkButton
className="flex items-center"
onClick={() => goToTokenPage(bank)}
>
{t('token:token-details')}
<ChevronRightIcon className="ml-2 h-5 w-5" />
</LinkButton>
</div>
</div>
</Transition>
</div>
@ -326,4 +350,4 @@ const TokenList = () => {
)
}
export default TokenList
export default TokenStats

View File

@ -29,6 +29,7 @@
"date-fns": "^2.29.3",
"dayjs": "^1.11.3",
"decimal.js": "^10.4.0",
"html-react-parser": "^3.0.4",
"immer": "^9.0.12",
"jsbi": "^4.3.0",
"lodash": "^4.17.21",

View File

@ -5,7 +5,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, ['common', 'profile'])),
...(await serverSideTranslations(locale, ['common', 'profile', 'token'])),
},
}
}

455
pages/token/[token].tsx Normal file
View File

@ -0,0 +1,455 @@
import Change from '@components/shared/Change'
import DailyRange from '@components/shared/DailyRange'
import mangoStore from '@store/mangoStore'
import { fetchTokenInfo } from 'apis/coingecko'
import type { GetStaticPaths, NextPage } from 'next'
import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import Image from 'next/image'
import { useRouter } from 'next/router'
import { useEffect, useMemo, useState } from 'react'
import FlipNumbers from 'react-flip-numbers'
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import Button, { IconButton, LinkButton } from '@components/shared/Button'
import { ArrowLeftIcon } from '@heroicons/react/20/solid'
import DepositModal from '@components/modals/DepositModal'
import BorrowModal from '@components/modals/BorrowModal'
import parse from 'html-react-parser'
import Link from 'next/link'
import SheenLoader from '@components/shared/SheenLoader'
dayjs.extend(relativeTime)
export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, ['common', 'token'])),
},
}
}
export const getStaticPaths: GetStaticPaths<{ slug: string }> = async () => {
return {
paths: [],
fallback: 'blocking',
}
}
const DEFAULT_COINGECKO_VALUES = {
ath: 0,
atl: 0,
ath_change_percentage: 0,
atl_change_percentage: 0,
ath_date: 0,
atl_date: 0,
high_24h: 0,
circulating_supply: 0,
fully_diluted_valuation: 0,
low_24h: 0,
market_cap: 0,
max_supply: 0,
price_change_percentage_24h: 0,
total_supply: 0,
total_volume: 0,
}
const Token: NextPage = () => {
const { t } = useTranslation(['common', 'token'])
const [showFullDesc, setShowFullDesc] = useState(false)
const [showDepositModal, setShowDepositModal] = useState(false)
const [showBorrowModal, setShowBorrowModal] = useState(false)
const [coingeckoData, setCoingeckoData] = useState<any>(null)
const [loading, setLoading] = useState(true)
const router = useRouter()
const { token } = router.query
const group = mangoStore((s) => s.group)
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
const bank = useMemo(() => {
if (group && token) {
const bank = group.banksMapByName.get(token.toString())
if (bank) {
return bank[0]
} else {
setLoading(false)
}
}
}, [group, token])
const logoURI = useMemo(() => {
if (bank && jupiterTokens.length) {
return jupiterTokens.find((t) => t.address === bank.mint.toString())
?.logoURI
}
}, [bank, jupiterTokens])
const coingeckoId = useMemo(() => {
if (bank && jupiterTokens.length) {
return jupiterTokens.find((t) => t.address === bank.mint.toString())
?.extensions?.coingeckoId
}
}, [bank, jupiterTokens])
const serumMarkets = useMemo(() => {
if (group) {
return Array.from(group.serum3MarketsMapByExternal.values())
}
return []
}, [group])
const handleTrade = () => {
const set = mangoStore.getState().set
const market = serumMarkets.find(
(m) => m.baseTokenIndex === bank?.tokenIndex
)
if (market) {
set((state) => {
state.selectedMarket.current = market
})
router.push('/trade')
}
}
const getCoingeckoData = async (id: string) => {
const response = await fetchTokenInfo(id)
setCoingeckoData(response)
setLoading(false)
}
useEffect(() => {
if (coingeckoId) {
getCoingeckoData(coingeckoId)
}
}, [coingeckoId])
const {
ath,
atl,
ath_change_percentage,
atl_change_percentage,
ath_date,
atl_date,
high_24h,
circulating_supply,
fully_diluted_valuation,
low_24h,
market_cap,
max_supply,
price_change_percentage_24h,
total_supply,
total_volume,
} = coingeckoData ? coingeckoData.market_data : DEFAULT_COINGECKO_VALUES
return (
<div className="pb-20 md:pb-16">
{coingeckoData && bank ? (
<>
<div className="flex flex-col border-b border-th-bkg-3 px-6 py-3 md:flex-row md:items-end md:justify-between">
<div className="mb-4 md:mb-0">
<IconButton
className="mb-2"
onClick={() => router.back()}
hideBg
size="small"
>
<ArrowLeftIcon className="h-6 w-6" />
</IconButton>
<div className="mb-1.5 flex items-center space-x-2">
<Image src={logoURI!} height="24" width="24" />
<h1 className="text-xl">
{coingeckoData.name}{' '}
<span className="text-lg font-normal text-th-fgd-4">
({bank!.name})
</span>
</h1>
</div>
<div className="mb-2 flex items-end space-x-3 text-5xl font-bold text-th-fgd-1">
$
<FlipNumbers
height={48}
width={32}
play
delay={0.05}
duration={1}
numbers={formatDecimal(bank!.uiPrice, 2)}
/>
<Change change={price_change_percentage_24h} />
</div>
<DailyRange
high={high_24h.usd}
low={low_24h.usd}
price={bank!.uiPrice}
/>
</div>
<div className="mb-2 w-full rounded-md bg-th-bkg-2 p-4 md:w-[343px]">
<div className="mb-4 flex justify-between">
<p>{t('balance')}</p>
<p className="font-mono text-th-fgd-2">
{mangoAccount
? formatDecimal(
mangoAccount.getTokenBalanceUi(bank!),
bank!.mintDecimals
)
: 0}
</p>
</div>
<div className="flex space-x-2">
<Button
className="flex-1"
size="small"
disabled={!mangoAccount}
onClick={() => setShowDepositModal(true)}
>
{t('deposit')}
</Button>
<Button
className="flex-1"
size="small"
secondary
disabled={!mangoAccount}
onClick={() => setShowBorrowModal(true)}
>
{t('borrow')}
</Button>
<Button
className="flex-1"
size="small"
secondary
disabled={
!mangoAccount ||
!serumMarkets.find(
(m) => m.baseTokenIndex === bank?.tokenIndex
)
}
onClick={handleTrade}
>
{t('trade')}
</Button>
</div>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2">
<div className="col-span-1 border-b border-r border-th-bkg-3 px-6 py-4 sm:border-b-0">
<h2 className="mb-4 text-base">{t('token:lending')}</h2>
<div className="flex justify-between border-t border-th-bkg-3 py-4">
<p>{t('total-deposits')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(bank!.uiDeposits())}
</p>
</div>
<div className="flex justify-between border-t border-th-bkg-3 py-4">
<p>{t('value')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(
bank!.uiDeposits() * bank!.uiPrice,
true
)}
</p>
</div>
<div className="flex justify-between border-t border-th-bkg-3 pt-4">
<p>{t('deposit-rate')}</p>
<p className="font-mono text-th-green">
{formatDecimal(bank!.getDepositRateUi(), 2, {
fixed: true,
})}
%
</p>
</div>
</div>
<div className="col-span-1 px-6 py-4">
<h2 className="mb-4 text-base">{t('token:borrowing')}</h2>
<div className="flex justify-between border-t border-th-bkg-3 py-4">
<p>{t('total-borrows')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(bank!.uiBorrows())}
</p>
</div>
<div className="flex justify-between border-t border-th-bkg-3 py-4">
<p>{t('value')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(bank!.uiBorrows() * bank!.uiPrice, true)}
</p>
</div>
<div className="flex justify-between border-t border-th-bkg-3 pt-4">
<p>{t('borrow-rate')}</p>
<p className="font-mono text-th-red">
{formatDecimal(bank!.getBorrowRateUi(), 2, {
fixed: true,
})}
%
</p>
</div>
</div>
</div>
<div className="border-y border-th-bkg-3 px-6 py-4 text-center">
<p>
Utilization:{' '}
{bank!.uiDeposits() > 0
? formatDecimal(
(bank!.uiBorrows() / bank!.uiDeposits()) * 100,
1,
{ fixed: true }
)
: '0.0'}
% of deposits have been lent out
</p>
</div>
<div className="border-b border-th-bkg-3 py-4 px-6">
<h2 className="mb-1 text-xl">About {bank!.name}</h2>
<div className="flex items-end">
<p
className={`${
showFullDesc ? 'h-full' : 'h-5'
} max-w-[720px] overflow-hidden`}
>
{parse(coingeckoData.description.en)}
{/* {showFullDesc
? parse(coingeckoData.description.en)
: `${coingeckoData.description.en.substr(
0,
100
)}...`} */}{' '}
</p>
<span
className="default-transition cursor-pointer font-normal underline hover:text-th-fgd-2 md:hover:no-underline"
onClick={() => setShowFullDesc(!showFullDesc)}
>
{showFullDesc ? 'Less' : 'More'}
</span>
</div>
</div>
<div className="grid grid-cols-1 border-b border-th-bkg-3 sm:grid-cols-2">
<div className="col-span-1 border-b border-th-bkg-3 px-6 py-4 sm:col-span-2">
<h2 className="text-base">{bank!.name} Stats</h2>
</div>
<div className="col-span-1 border-r border-th-bkg-3 px-6 py-4">
<div className="flex justify-between pb-4">
<p>{t('token:market-cap')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(market_cap.usd, true)}{' '}
<span className="text-th-fgd-4">
#{coingeckoData.market_cap_rank}
</span>
</p>
</div>
<div className="flex justify-between border-t border-th-bkg-3 py-4">
<p>{t('token:volume')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(total_volume.usd, true)}
</p>
</div>
<div className="flex justify-between border-t border-th-bkg-3 py-4">
<p>{t('token:all-time-high')}</p>
<div className="flex flex-col items-end">
<div className="flex items-center font-mono text-th-fgd-2">
<span className="mr-2">
{formatFixedDecimals(ath.usd, true)}
</span>
<Change change={ath_change_percentage.usd} />
</div>
<p className="text-xs text-th-fgd-4">
{dayjs(ath_date.usd).format('MMM, D, YYYY')} (
{dayjs(ath_date.usd).fromNow()})
</p>
</div>
</div>
<div className="flex justify-between border-b border-t border-th-bkg-3 py-4 sm:border-b-0 sm:pb-0">
<p>{t('token:all-time-low')}</p>
<div className="flex flex-col items-end">
<div className="flex items-center font-mono text-th-fgd-2">
<span className="mr-2">
{formatFixedDecimals(atl.usd, true)}
</span>
<Change change={atl_change_percentage.usd} />
</div>
<p className="text-xs text-th-fgd-4">
{dayjs(atl_date.usd).format('MMM, D, YYYY')} (
{dayjs(atl_date.usd).fromNow()})
</p>
</div>
</div>
</div>
<div className="col-span-1 px-6 pb-4 sm:pt-4">
{fully_diluted_valuation.usd ? (
<div className="flex justify-between pb-4">
<p>{t('token:fdv')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(fully_diluted_valuation.usd, true)}
</p>
</div>
) : null}
<div
className={`flex justify-between ${
fully_diluted_valuation.usd
? 'border-t border-th-bkg-3 py-4'
: 'pb-4'
}`}
>
<p>{t('token:circulating-supply')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(circulating_supply)}
</p>
</div>
<div
className={`flex justify-between border-t border-th-bkg-3 ${
max_supply ? 'py-4' : 'border-b pt-4 sm:pb-4'
}`}
>
<p>{t('token:total-supply')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(total_supply)}
</p>
</div>
{max_supply ? (
<div className="flex justify-between border-t border-th-bkg-3 pt-4">
<p>{t('token:max-supply')}</p>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(max_supply)}
</p>
</div>
) : null}
</div>
</div>
</>
) : loading ? (
<div className="space-y-3 px-6 py-4">
<SheenLoader className="flex flex-1">
<div className="h-32 w-full rounded-lg bg-th-bkg-2" />
</SheenLoader>
<SheenLoader className="flex flex-1">
<div className="h-72 w-full rounded-lg bg-th-bkg-2" />
</SheenLoader>
</div>
) : (
<div className="-mt-8 flex h-screen flex-col items-center justify-center">
<p className="text-3xl">😔</p>
<h2 className="mb-1">{t('token:token-not-found')}</h2>
<p className="mb-2">
{t('token:token-not-found-desc', { token: token })}
</p>
<Link href="/">
<a>{t('token:go-to-account')}</a>
</Link>
</div>
)}
{showDepositModal ? (
<DepositModal
isOpen={showDepositModal}
onClose={() => setShowDepositModal(false)}
token={bank!.name}
/>
) : null}
{showBorrowModal ? (
<BorrowModal
isOpen={showBorrowModal}
onClose={() => setShowBorrowModal(false)}
token={bank!.name}
/>
) : null}
</div>
)
}
export default Token

View File

@ -0,0 +1,16 @@
{
"all-time-high": "All-time High",
"all-time-low": "All-time Low",
"borrowing": "Borrowing",
"circulating-supply": "Circulating Supply",
"fdv": "Fully Diluted Value",
"go-to-account": "Go To Account",
"lending": "Lending",
"market-cap": "Market Cap",
"max-supply": "Max Supply",
"token-details": "Token Details",
"token-not-found": "Token Not Found",
"token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.",
"total-supply": "Total Supply",
"volume": "24h Volume"
}

View File

@ -0,0 +1,16 @@
{
"all-time-high": "All-time High",
"all-time-low": "All-time Low",
"borrowing": "Borrowing",
"circulating-supply": "Circulating Supply",
"fdv": "Fully Diluted Value",
"go-to-account": "Go To Account",
"lending": "Lending",
"market-cap": "Market Cap",
"max-supply": "Max Supply",
"token-details": "Token Details",
"token-not-found": "Token Not Found",
"token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.",
"total-supply": "Total Supply",
"volume": "24h Volume"
}

View File

@ -0,0 +1,16 @@
{
"all-time-high": "All-time High",
"all-time-low": "All-time Low",
"borrowing": "Borrowing",
"circulating-supply": "Circulating Supply",
"fdv": "Fully Diluted Value",
"go-to-account": "Go To Account",
"lending": "Lending",
"market-cap": "Market Cap",
"max-supply": "Max Supply",
"token-details": "Token Details",
"token-not-found": "Token Not Found",
"token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.",
"total-supply": "Total Supply",
"volume": "24h Volume"
}

View File

@ -0,0 +1,16 @@
{
"all-time-high": "All-time High",
"all-time-low": "All-time Low",
"borrowing": "Borrowing",
"circulating-supply": "Circulating Supply",
"fdv": "Fully Diluted Value",
"go-to-account": "Go To Account",
"lending": "Lending",
"market-cap": "Market Cap",
"max-supply": "Max Supply",
"token-details": "Token Details",
"token-not-found": "Token Not Found",
"token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.",
"total-supply": "Total Supply",
"volume": "24h Volume"
}

View File

@ -0,0 +1,16 @@
{
"all-time-high": "All-time High",
"all-time-low": "All-time Low",
"borrowing": "Borrowing",
"circulating-supply": "Circulating Supply",
"fdv": "Fully Diluted Value",
"go-to-account": "Go To Account",
"lending": "Lending",
"market-cap": "Market Cap",
"max-supply": "Max Supply",
"token-details": "Token Details",
"token-not-found": "Token Not Found",
"token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.",
"total-supply": "Total Supply",
"volume": "24h Volume"
}

View File

@ -44,7 +44,8 @@ export const COINGECKO_IDS = [
// { id: 'cope', symbol: 'COPE' },
// { id: 'cardano', symbol: 'ADA' },
{ id: 'msol', symbol: 'MSOL' },
// { id: 'tether', symbol: 'USDT' },
{ id: 'usd-coin', symbol: 'USDC' },
{ id: 'tether', symbol: 'USDT' },
// { id: 'stepn', symbol: 'GMT' },
]

View File

@ -3200,6 +3200,36 @@ dom-helpers@^3.4.0:
dependencies:
"@babel/runtime" "^7.1.2"
dom-serializer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
dependencies:
domelementtype "^2.3.0"
domhandler "^5.0.2"
entities "^4.2.0"
domelementtype@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
domhandler@5.0.3, domhandler@^5.0.1, domhandler@^5.0.2:
version "5.0.3"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
dependencies:
domelementtype "^2.3.0"
domutils@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c"
integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==
dependencies:
dom-serializer "^2.0.0"
domelementtype "^2.3.0"
domhandler "^5.0.1"
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
@ -3290,6 +3320,11 @@ engine.io-parser@~5.0.3:
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
entities@^4.2.0, entities@^4.3.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==
err-code@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
@ -4128,6 +4163,14 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
dependencies:
react-is "^16.7.0"
html-dom-parser@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-3.1.2.tgz#c137c42df80e17d185ff35a806925d96cc73f408"
integrity sha512-mLTtl3pVn3HnqZSZzW3xVs/mJAKrG1yIw3wlp+9bdoZHHLaBRvELdpfShiPVLyjPypq1Fugv2KMDoGHW4lVXnw==
dependencies:
domhandler "5.0.3"
htmlparser2 "8.0.1"
html-parse-stringify@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
@ -4135,6 +4178,26 @@ html-parse-stringify@^3.0.1:
dependencies:
void-elements "3.1.0"
html-react-parser@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-3.0.4.tgz#6a6a115a011dfdadd901ca9d2ed80fa5390647e5"
integrity sha512-va68PSmC7uA6PbOEc9yuw5Mu3OHPXmFKUpkLGvUPdTuNrZ0CJZk1s/8X/FaHjswK/6uZghu2U02tJjussT8+uw==
dependencies:
domhandler "5.0.3"
html-dom-parser "3.1.2"
react-property "2.0.0"
style-to-js "1.1.1"
htmlparser2@8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010"
integrity sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==
dependencies:
domelementtype "^2.3.0"
domhandler "^5.0.2"
domutils "^3.0.1"
entities "^4.3.0"
human-signals@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
@ -4198,6 +4261,11 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.4:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inline-style-parser@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==
internal-slot@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
@ -5605,6 +5673,11 @@ react-number-format@^4.9.2:
dependencies:
prop-types "^15.7.2"
react-property@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-property/-/react-property-2.0.0.tgz#2156ba9d85fa4741faf1918b38efc1eae3c6a136"
integrity sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==
react-qr-reader@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/react-qr-reader/-/react-qr-reader-2.2.1.tgz#dc89046d1c1a1da837a683dd970de5926817d55b"
@ -6201,6 +6274,20 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
style-to-js@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.1.tgz#417786986cda61d4525c80aed9d1123a6a7af9b8"
integrity sha512-RJ18Z9t2B02sYhZtfWKQq5uplVctgvjTfLWT7+Eb1zjUjIrWzX5SdlkwLGQozrqarTmEzJJ/YmdNJCUNI47elg==
dependencies:
style-to-object "0.3.0"
style-to-object@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46"
integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==
dependencies:
inline-style-parser "0.1.1"
styled-jsx@5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.2.tgz#ff230fd593b737e9e68b630a694d460425478729"