-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[W.I.P.] Start to build token dashboard
- Loading branch information
Showing
12 changed files
with
422 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add token dashboard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { FC } from 'react' | ||
import Card from '@mui/material/Card' | ||
import { useRequiredScopeParam } from '../../hooks/useScopeParam' | ||
import { useLoaderData } from 'react-router-dom' | ||
import { useTokenInfo } from './hook' | ||
import { useAccount } from '../AccountDetailsPage/hook' | ||
import { TextSkeleton } from '../../components/Skeleton' | ||
import { StyledDescriptionList } from '../../components/StyledDescriptionList' | ||
import { useScreenSize } from '../../hooks/useScreensize' | ||
import { useTranslation } from 'react-i18next' | ||
import { AccountLink } from '../../components/Account/AccountLink' | ||
import { CopyToClipboard } from '../../components/CopyToClipboard' | ||
import { ContractVerificationIcon } from '../../components/ContractVerificationIcon' | ||
import { getTokenTypeName } from './TokenTypeCard' | ||
import { getNameForTicker, Ticker } from '../../../types/ticker' | ||
|
||
export const TokenDetailsCard: FC = () => { | ||
const { t } = useTranslation() | ||
const scope = useRequiredScopeParam() | ||
const address = useLoaderData() as string | ||
const { isMobile } = useScreenSize() | ||
|
||
const { token, isLoading: tokenIsLoading } = useTokenInfo(scope, address) | ||
const { account, isLoading: accountIsLoading } = useAccount(scope, address) | ||
const isLoading = tokenIsLoading || accountIsLoading | ||
|
||
const contract = account?.evm_contract | ||
|
||
const transactionsLabel = account ? account.stats.num_txns.toLocaleString() : '' | ||
const balance = account?.balances[0]?.balance || '0' | ||
const nativeToken = account?.ticker || Ticker.ROSE | ||
const tickerName = getNameForTicker(t, nativeToken) | ||
|
||
return ( | ||
<Card> | ||
{isLoading && <TextSkeleton numberOfRows={8} />} | ||
{account && token && contract && ( | ||
<StyledDescriptionList titleWidth={isMobile ? '100px' : '200px'}> | ||
<dt>{t('common.token')}</dt> | ||
<dd>{token.name}</dd> | ||
|
||
<dt>{t(isMobile ? 'common.smartContract_short' : 'common.smartContract')}</dt> | ||
<dd> | ||
<AccountLink scope={account} address={address!} /> | ||
<CopyToClipboard value={address!} /> | ||
</dd> | ||
|
||
<dt>{t('contract.verification.title')}</dt> | ||
<dd> | ||
<ContractVerificationIcon | ||
verified={!!account?.evm_contract?.verification} | ||
address_eth={account.address_eth!} | ||
/> | ||
</dd> | ||
|
||
<dt>{t('common.type')} </dt> | ||
<dd>{getTokenTypeName(t, token.type)} </dd> | ||
|
||
<dt>{t('contract.creator')}</dt> | ||
<dd> {contract.creation_tx ? contract.creation_tx : t('common.missing')}</dd> | ||
|
||
<dt>{t('common.balance')} </dt> | ||
<dd>{t('common.valueInToken', { value: balance, ticker: tickerName })}</dd> | ||
|
||
<dt>{t('common.transactions')}</dt> | ||
<dd>{transactionsLabel}</dd> | ||
</StyledDescriptionList> | ||
)} | ||
</Card> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { FC } from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import Box from '@mui/material/Box' | ||
import Typography from '@mui/material/Typography' | ||
import { SnapshotCard } from '../../components/Snapshots/SnapshotCard' | ||
import { COLORS } from '../../../styles/theme/colors' | ||
import { useRequiredScopeParam } from '../../hooks/useScopeParam' | ||
import { useTokenInfo } from './hook' | ||
import { useLoaderData } from 'react-router-dom' | ||
|
||
export const TokenHoldersCard: FC = () => { | ||
const { t } = useTranslation() | ||
const scope = useRequiredScopeParam() | ||
|
||
const address = useLoaderData() as string | ||
|
||
const { token, isFetched } = useTokenInfo(scope, address) | ||
|
||
const title = t('tokens.holders') | ||
return ( | ||
<SnapshotCard title={title}> | ||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}> | ||
{isFetched && ( | ||
<> | ||
<Typography | ||
component="span" | ||
sx={{ | ||
fontSize: '48px', | ||
fontWeight: 700, | ||
color: COLORS.brandDark, | ||
}} | ||
> | ||
{t('tokens.holdersValue', { value: token?.num_holders })} | ||
</Typography> | ||
</> | ||
)} | ||
</Box> | ||
</SnapshotCard> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { FC } from 'react' | ||
import Box from '@mui/material/Box' | ||
import Grid from '@mui/material/Grid' | ||
import Typography from '@mui/material/Typography' | ||
import { useScreenSize } from '../../hooks/useScreensize' | ||
import { useTheme } from '@mui/material/styles' | ||
import { styled } from '@mui/material/styles' | ||
import { useTranslation } from 'react-i18next' | ||
import { AppendMobileSearch } from '../../components/AppendMobileSearch' | ||
import { useRequiredScopeParam } from '../../hooks/useScopeParam' | ||
import { TokenSupplyCard } from './TokenSupplyCard' | ||
import { TokenHoldersCard } from './TokenHoldersCard' | ||
import { TokenTypeCard } from './TokenTypeCard' | ||
import { TokenTotalTransactionsCard } from './TokenTotalTransactionsCard' | ||
|
||
const StyledGrid = styled(Grid)(() => ({ | ||
display: 'flex', | ||
})) | ||
|
||
export const TokenSnapshot: FC = () => { | ||
const { t } = useTranslation() | ||
|
||
const scope = useRequiredScopeParam() | ||
const theme = useTheme() | ||
const { isMobile } = useScreenSize() | ||
|
||
return ( | ||
<> | ||
<Grid container sx={{ display: 'flex', alignItems: 'center', gap: 3, mb: 4 }}> | ||
<Grid item xs={12} sx={{ px: isMobile ? 4 : 0 }}> | ||
<AppendMobileSearch scope={scope}> | ||
<Box sx={{ display: 'flex', flexDirection: isMobile ? 'column' : 'row', mb: 2 }}> | ||
<Typography | ||
variant="h3" | ||
sx={{ color: theme.palette.layout.main, fontWeight: 700, mr: 3, mb: isMobile ? 4 : 0 }} | ||
> | ||
{t('tokenSnapshot.header')} | ||
</Typography> | ||
</Box> | ||
</AppendMobileSearch> | ||
</Grid> | ||
</Grid> | ||
|
||
<Grid container rowSpacing={1} columnSpacing={4} columns={22}> | ||
<StyledGrid item xs={22} md={5}> | ||
<TokenTotalTransactionsCard /> | ||
</StyledGrid> | ||
<StyledGrid item xs={22} md={6}> | ||
<TokenSupplyCard /> | ||
</StyledGrid> | ||
<StyledGrid item xs={22} md={5}> | ||
<TokenHoldersCard /> | ||
</StyledGrid> | ||
<StyledGrid item xs={22} md={6}> | ||
<TokenTypeCard /> | ||
</StyledGrid> | ||
</Grid> | ||
</> | ||
) | ||
} | ||
// <TransactionsChartCard chartDuration={chartDuration} /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { FC } from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import Box from '@mui/material/Box' | ||
import Typography from '@mui/material/Typography' | ||
import { SnapshotCard } from '../../components/Snapshots/SnapshotCard' | ||
import { COLORS } from '../../../styles/theme/colors' | ||
import { useRequiredScopeParam } from '../../hooks/useScopeParam' | ||
import { useTokenInfo } from './hook' | ||
import { useLoaderData } from 'react-router-dom' | ||
|
||
export const TokenSupplyCard: FC = () => { | ||
const { t } = useTranslation() | ||
const scope = useRequiredScopeParam() | ||
|
||
const address = useLoaderData() as string | ||
|
||
const { token, isFetched } = useTokenInfo(scope, address) | ||
|
||
const title = t('tokens.totalSupply') | ||
const supplyString = token?.total_supply || '0' | ||
const supplyNumber = parseInt(supplyString) | ||
|
||
return ( | ||
<SnapshotCard title={title} label={token?.symbol}> | ||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}> | ||
{isFetched && ( | ||
<> | ||
<Typography | ||
component="span" | ||
sx={{ | ||
fontSize: '48px', | ||
fontWeight: 700, | ||
color: COLORS.brandDark, | ||
}} | ||
> | ||
{t('tokens.totalSupplyValue', { value: supplyNumber })} | ||
</Typography> | ||
</> | ||
)} | ||
</Box> | ||
</SnapshotCard> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { FC } from 'react' | ||
import { useRequiredScopeParam } from '../../hooks/useScopeParam' | ||
import { useLoaderData } from 'react-router-dom' | ||
import Card from '@mui/material/Card' | ||
import CardHeader from '@mui/material/CardHeader' | ||
import { useTokenInfo } from './hook' | ||
import Skeleton from '@mui/material/Skeleton' | ||
import { ContractVerificationIcon } from '../../components/ContractVerificationIcon' | ||
import { useAccount } from '../AccountDetailsPage/hook' | ||
|
||
const TitleSkeleton: FC = () => <Skeleton variant="text" sx={{ display: 'inline-block', width: '100%' }} /> | ||
|
||
export const TokenTitleCard: FC = () => { | ||
const scope = useRequiredScopeParam() | ||
const address = useLoaderData() as string | ||
|
||
const { isLoading, token } = useTokenInfo(scope, address) | ||
const { account } = useAccount(scope, address) | ||
|
||
const title = isLoading ? <TitleSkeleton /> : token?.name | ||
const subTitle = isLoading ? null : ` (${token?.symbol})` || null | ||
|
||
const addressEth = account?.address_eth | ||
|
||
return ( | ||
<Card> | ||
<CardHeader | ||
disableTypography | ||
component="h2" | ||
title={title} | ||
subheader={subTitle} | ||
action={ | ||
<> | ||
{addressEth && ( | ||
<ContractVerificationIcon | ||
verified={!!account?.evm_contract?.verification} | ||
address_eth={addressEth} | ||
/> | ||
)} | ||
{/*<Link*/} | ||
{/* component={RouterLink}*/} | ||
{/* to={RouteUtils.getLatestTransactionsRoute(scope)}*/} | ||
{/* sx={{ color: COLORS.brandExtraDark }}*/} | ||
{/*>*/} | ||
{/* {t('common.viewAll')}*/} | ||
{/*</Link>*/} | ||
</> | ||
} | ||
/> | ||
</Card> | ||
) | ||
} |
40 changes: 40 additions & 0 deletions
40
src/app/pages/TokenDashboardPage/TokenTotalTransactionsCard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { FC } from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import Box from '@mui/material/Box' | ||
import Typography from '@mui/material/Typography' | ||
import { SnapshotCard } from '../../components/Snapshots/SnapshotCard' | ||
import { COLORS } from '../../../styles/theme/colors' | ||
import { useRequiredScopeParam } from '../../hooks/useScopeParam' | ||
import { useLoaderData } from 'react-router-dom' | ||
import { useAccount } from '../AccountDetailsPage/hook' | ||
|
||
export const TokenTotalTransactionsCard: FC = () => { | ||
const { t } = useTranslation() | ||
const scope = useRequiredScopeParam() | ||
|
||
const address = useLoaderData() as string | ||
|
||
const { isFetched, account } = useAccount(scope, address) | ||
const value = account?.stats.num_txns | ||
|
||
return ( | ||
<SnapshotCard title={t('totalTransactions.header')}> | ||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}> | ||
{isFetched && ( | ||
<> | ||
<Typography | ||
component="span" | ||
sx={{ | ||
fontSize: '48px', | ||
fontWeight: 700, | ||
color: COLORS.brandDark, | ||
}} | ||
> | ||
{t('totalTransactions.value', { value })} | ||
</Typography> | ||
</> | ||
)} | ||
</Box> | ||
</SnapshotCard> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { FC } from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import Box from '@mui/material/Box' | ||
import Typography from '@mui/material/Typography' | ||
import { SnapshotCard } from '../../components/Snapshots/SnapshotCard' | ||
import { COLORS } from '../../../styles/theme/colors' | ||
import { useRequiredScopeParam } from '../../hooks/useScopeParam' | ||
import { useTokenInfo } from './hook' | ||
import { useLoaderData } from 'react-router-dom' | ||
import { EvmTokenType } from '../../../oasis-indexer/api' | ||
import { TFunction } from 'i18next' | ||
import { exhaustedTypeWarning } from '../../../types/errors' | ||
|
||
export const getTokenTypeName = (t: TFunction, type: EvmTokenType): string => { | ||
switch (type) { | ||
case 'ERC20': | ||
return t('account.ERC20') | ||
default: | ||
void exhaustedTypeWarning('Unknown token type', type) | ||
return '???' | ||
} | ||
} | ||
|
||
export const TokenTypeCard: FC = () => { | ||
const { t } = useTranslation() | ||
const scope = useRequiredScopeParam() | ||
|
||
const address = useLoaderData() as string | ||
|
||
const { token, isFetched } = useTokenInfo(scope, address) | ||
|
||
return ( | ||
<SnapshotCard title={t('tokens.type')}> | ||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}> | ||
{isFetched && ( | ||
<> | ||
<Typography | ||
component="span" | ||
sx={{ | ||
fontSize: '48px', | ||
fontWeight: 700, | ||
color: COLORS.brandDark, | ||
}} | ||
> | ||
{token?.type ? getTokenTypeName(t, token.type) : '-'} | ||
</Typography> | ||
</> | ||
)} | ||
</Box> | ||
</SnapshotCard> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Layer, useGetRuntimeEvmTokensAddress } from '../../../oasis-indexer/api' | ||
import { AppErrors } from '../../../types/errors' | ||
import { SearchScope } from '../../../types/searchScope' | ||
|
||
export const useTokenInfo = (scope: SearchScope, address: string) => { | ||
const { network, layer } = scope | ||
if (layer === Layer.consensus) { | ||
// There can be no ERC-20 or ERC-721 tokens on consensus | ||
throw AppErrors.UnsupportedLayer | ||
} | ||
const query = useGetRuntimeEvmTokensAddress(network, layer, address!) | ||
const token = query.data?.data | ||
const { isLoading, isError, isFetched } = query | ||
return { token, isLoading, isError, isFetched } | ||
} |
Oops, something went wrong.