add token details page
This commit is contained in:
parent
99a255ad75
commit
db4317400d
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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'])),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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' },
|
||||
]
|
||||
|
||||
|
|
87
yarn.lock
87
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue