Skip to content

Commit

Permalink
Merge pull request #623 from oasisprotocol/csillag/token-dashboard
Browse files Browse the repository at this point in the history
Implement top part of token dashboard
  • Loading branch information
csillag authored Jun 30, 2023
2 parents 764a54f + ddcbbcc commit 9227d9a
Show file tree
Hide file tree
Showing 51 changed files with 676 additions and 61 deletions.
1 change: 1 addition & 0 deletions .changelog/623.2.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Properly display creator info for contracts.
1 change: 1 addition & 0 deletions .changelog/623.3.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When displaying a contract that describes a token, link to the token dashboard
1 change: 1 addition & 0 deletions .changelog/623.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add token dashboard
4 changes: 2 additions & 2 deletions src/app/components/Account/AccountLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const AccountLink: FC<{
alwaysTrim?: boolean
plain?: boolean
}> = ({ scope, address, alwaysTrim, plain }) => {
const { isMobile } = useScreenSize()
const { isTablet } = useScreenSize()
const to = RouteUtils.getAccountRoute(scope, address)
return (
<Typography
Expand All @@ -26,7 +26,7 @@ export const AccountLink: FC<{
: { color: COLORS.brandDark, fontWeight: 700 }
}
>
{alwaysTrim || isMobile ? (
{alwaysTrim || isTablet ? (
<TrimLinkLabel label={address} to={to} />
) : plain ? (
address
Expand Down
51 changes: 51 additions & 0 deletions src/app/components/Account/ContractCreatorInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { FC } from 'react'
import { SearchScope } from '../../../types/searchScope'
import { Trans, useTranslation } from 'react-i18next'
import { TransactionLink } from '../Transactions/TransactionLink'
import { Layer, useGetRuntimeTransactionsTxHash } from '../../../oasis-indexer/api'
import { AppErrors } from '../../../types/errors'
import { AccountLink } from './AccountLink'
import Box from '@mui/material/Box'

const TxSender: FC<{ scope: SearchScope; txHash: string }> = ({ scope, txHash }) => {
const { t } = useTranslation()
if (scope.layer === Layer.consensus) {
throw AppErrors.UnsupportedLayer
}
const query = useGetRuntimeTransactionsTxHash(scope.network, scope.layer, txHash)
const tx = query.data?.data.transactions[0]
const senderAddress = tx?.sender_0_eth || tx?.sender_0
return senderAddress ? (
<AccountLink scope={scope} address={senderAddress} alwaysTrim />
) : (
t('common.missing')
)
}

export const ContractCreatorInfo: FC<{ scope: SearchScope; address: string | undefined }> = ({
scope,
address,
}) => {
const { t } = useTranslation()
return address === undefined ? (
t('common.missing')
) : (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 3,
}}
>
<TxSender scope={scope} txHash={address} />
&nbsp;
<Trans
t={t}
i18nKey={'contract.createdAt'}
components={{
TransactionLink: <TransactionLink scope={scope} hash={address} alwaysTrim />,
}}
/>
</Box>
)
}
55 changes: 40 additions & 15 deletions src/app/components/Account/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ import { CopyToClipboard } from '../../components/CopyToClipboard'
import { JazzIcon } from '../../components/JazzIcon'
import { CoinGeckoReferral } from '../../components/CoinGeckoReferral'
import { TextSkeleton } from '../../components/Skeleton'
import { type RuntimeAccount } from '../../../oasis-indexer/api'
import { EvmToken, type RuntimeAccount } from '../../../oasis-indexer/api'
import { TokenPills } from './TokenPills'
import { AccountLink } from './AccountLink'
import { RouteUtils } from '../../utils/route-utils'
import { accountTransactionsContainerId } from '../../pages/AccountDetailsPage/AccountTransactionsCard'
import Link from '@mui/material/Link'
import { DashboardLink } from '../../pages/DashboardPage/DashboardLink'
import { DashboardLink } from '../../pages/ParatimeDashboardPage/DashboardLink'
import { getNameForTicker, Ticker } from '../../../types/ticker'
import { TokenPriceInfo } from '../../../coin-gecko/api'
import { TransactionLink } from '../Transactions/TransactionLink'
import { ContractCreatorInfo } from './ContractCreatorInfo'
import { ContractVerificationIcon } from '../ContractVerificationIcon'
import { TokenLink } from '../Tokens/TokenLink'

export const StyledAvatarContainer = styled('dt')(({ theme }) => ({
'&&': {
Expand Down Expand Up @@ -50,17 +51,17 @@ export const FiatMoneyAmountBox = styled(Box)(() => ({

type AccountProps = {
account?: RuntimeAccount
token?: EvmToken
isLoading: boolean
tokenPriceInfo: TokenPriceInfo
showLayer?: boolean
}

export const Account: FC<AccountProps> = ({ account, isLoading, tokenPriceInfo, showLayer }) => {
export const Account: FC<AccountProps> = ({ account, token, isLoading, tokenPriceInfo, showLayer }) => {
const { t } = useTranslation()
const { isMobile } = useScreenSize()
const balance = account?.balances[0]?.balance ?? '0'
const balance = account?.balances[0]?.balance
const address = account ? account.address_eth ?? account.address : undefined
const creationTxHash = account?.evm_contract?.eth_creation_tx ?? account?.evm_contract?.creation_tx

const transactionsLabel = account ? account.stats.num_txns.toLocaleString() : ''
const transactionsAnchor = account
Expand All @@ -70,14 +71,15 @@ export const Account: FC<AccountProps> = ({ account, isLoading, tokenPriceInfo,
)}#${accountTransactionsContainerId}`
: undefined

const token = account?.ticker || Ticker.ROSE
const tickerName = getNameForTicker(t, token)
const nativeToken = account?.ticker || Ticker.ROSE
const nativeTickerName = getNameForTicker(t, nativeToken)
const {
isLoading: isPriceLoading,
price: tokenFiatValue,
isFree: isTokenFree,
hasUsedCoinGecko,
} = tokenPriceInfo
const contract = account?.evm_contract

return (
<>
Expand All @@ -100,7 +102,20 @@ export const Account: FC<AccountProps> = ({ account, isLoading, tokenPriceInfo,
<CopyToClipboard value={address!} />
</dd>

{account?.evm_contract && (
{token && (
<>
<dt>{t('common.token')}</dt>
<dd>
<TokenLink
scope={account}
address={token.eth_contract_addr || token.contract_addr}
name={token.name}
/>
</dd>
</>
)}

{contract && (
<>
<dt>{t('contract.verification.title')}</dt>
<dd>
Expand All @@ -111,17 +126,25 @@ export const Account: FC<AccountProps> = ({ account, isLoading, tokenPriceInfo,
</dd>
</>
)}
{creationTxHash && (

{contract && (
<>
<dt>{t('common.createdAt')}</dt>
<dt>{t('contract.creator')}</dt>
<dd>
<TransactionLink scope={account} hash={creationTxHash} />
<ContractCreatorInfo
scope={account}
address={contract.eth_creation_tx || contract.creation_tx}
/>
</dd>
</>
)}

<dt>{t('common.balance')}</dt>
<dd>{t('common.valueInToken', { value: balance, ticker: tickerName })}</dd>
<dd>
{balance === undefined
? t('common.missing')
: t('common.valueInToken', { value: balance, ticker: nativeTickerName })}
</dd>

<dt>{t('common.tokens')}</dt>
<dd>
Expand Down Expand Up @@ -159,10 +182,12 @@ export const Account: FC<AccountProps> = ({ account, isLoading, tokenPriceInfo,
</dd>

<dt>{t('account.totalReceived')}</dt>
<dd>{t('common.valueInToken', { value: account.stats.total_received, ticker: tickerName })}</dd>
<dd>
{t('common.valueInToken', { value: account.stats.total_received, ticker: nativeTickerName })}
</dd>

<dt>{t('account.totalSent')}</dt>
<dd>{t('common.valueInToken', { value: account.stats.total_sent, ticker: tickerName })}</dd>
<dd>{t('common.valueInToken', { value: account.stats.total_sent, ticker: nativeTickerName })}</dd>
</StyledDescriptionList>
)}
</>
Expand Down
10 changes: 7 additions & 3 deletions src/app/components/ContractVerificationIcon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const StyledBox = styled(Box, {
return {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '28px',
fontSize: '12px',
backgroundColor: statusBgColor[status],
Expand All @@ -47,9 +46,14 @@ const StyledBox = styled(Box, {
type ContractVerificationIconProps = {
verified: boolean
address_eth: string
noLink?: boolean
}

export const ContractVerificationIcon: FC<ContractVerificationIconProps> = ({ verified, address_eth }) => {
export const ContractVerificationIcon: FC<ContractVerificationIconProps> = ({
verified,
address_eth,
noLink = false,
}) => {
const { t } = useTranslation()
const status: VerificationStatus = verified ? 'verified' : 'unverified'
const statusLabel: Record<VerificationStatus, string> = {
Expand All @@ -65,7 +69,7 @@ export const ContractVerificationIcon: FC<ContractVerificationIconProps> = ({ ve
{statusIcon[status]}
</StyledBox>
&nbsp; &nbsp;
{verified && (
{verified && !noLink && (
<Typography component="span" sx={{ fontSize: '12px', color: COLORS.brandExtraDark }}>
<Trans
t={t}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FC, memo } from 'react'
import { Select, SelectOptionBase } from '../../../components/Select'
import { Select, SelectOptionBase } from '../../Select'
import { ChartDuration } from '../../../utils/chart-utils'
import { useTranslation } from 'react-i18next'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type SnapshotCardProps = PropsWithChildren & {
label?: string
title: ReactNode
withContentPadding?: boolean
withConstantHeight?: boolean
}

export const SnapshotCard: FC<SnapshotCardProps> = ({
Expand All @@ -39,12 +40,13 @@ export const SnapshotCard: FC<SnapshotCardProps> = ({
title,
label,
withContentPadding = true,
withConstantHeight = false,
}) => {
return (
<StyledCard>
<CardHeader component="h5" title={title} sx={{ pb: 0, pl: 4, pt: 4 }} />
<StyledCardContent withContentPadding={withContentPadding}>{children}</StyledCardContent>
{(badge || label) && (
{(badge || label || withConstantHeight) && (
<CardActions sx={{ minHeight: 60 }}>
<Box
sx={{
Expand Down
4 changes: 2 additions & 2 deletions src/app/components/Tokens/TokenDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'
import { TokenLink } from './TokenLink'
import { CopyToClipboard } from '../CopyToClipboard'
import { AccountLink } from '../Account/AccountLink'
import { DashboardLink } from '../../pages/DashboardPage/DashboardLink'
import { DashboardLink } from '../../pages/ParatimeDashboardPage/DashboardLink'
import { LongDataDisplay } from '../LongDataDisplay'

export const TokenDetails: FC<{
Expand Down Expand Up @@ -48,7 +48,7 @@ export const TokenDetails: FC<{
<dt>{t(isMobile ? 'tokens.holdersCount_short' : 'tokens.holdersCount')}</dt>
<dd>{token.num_holders.toLocaleString()}</dd>

<dt>{t('tokens.supply')}</dt>
<dt>{t('tokens.totalSupply')}</dt>
<dd>
<LongDataDisplay data={token.total_supply || t('common.missing')} threshold={100} fontWeight={400} />
</dd>
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/Tokens/TokenList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const TokenList = (props: TokensProps) => {
content: t('tokens.holdersCount'),
align: TableCellAlign.Right,
},
{ key: 'supply', content: t('tokens.supply'), align: TableCellAlign.Right },
{ key: 'supply', content: t('tokens.totalSupply'), align: TableCellAlign.Right },
{ key: 'ticker', content: t('common.ticker'), align: TableCellAlign.Right },
]

Expand Down
16 changes: 13 additions & 3 deletions src/app/pages/AccountDetailsPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@ import { RouterTabs } from '../../components/RouterTabs'
import { TokenPriceInfo, useTokenPrice } from '../../../coin-gecko/api'
import { Ticker } from '../../../types/ticker'

import { EvmTokenType, RuntimeAccount } from '../../../oasis-indexer/api'
import { EvmToken, EvmTokenType, RuntimeAccount } from '../../../oasis-indexer/api'
import { accountTokenContainerId } from './AccountTokensCard'
import { useAccount } from './hook'
import { useRequiredScopeParam } from '../../hooks/useScopeParam'
import { showEmptyAccountDetails } from '../../../config'
import { CardEmptyState } from './CardEmptyState'
import { contractCodeContainerId } from './ContractCodeCard'
import { useTokenInfo } from '../TokenDashboardPage/hook'

export const AccountDetailsPage: FC = () => {
const { t } = useTranslation()

const scope = useRequiredScopeParam()
const address = useLoaderData() as string
const { account, isLoading, isError } = useAccount(scope, address)
const { token } = useTokenInfo(scope, address)

const tokenPriceInfo = useTokenPrice(account?.ticker || Ticker.ROSE)
const isContract = !!account?.evm_contract
Expand All @@ -46,6 +48,7 @@ export const AccountDetailsPage: FC = () => {
isLoading={isLoading}
isError={isError}
account={account}
token={token}
tokenPriceInfo={tokenPriceInfo}
/>
</SubPageCard>
Expand All @@ -66,13 +69,20 @@ export const AccountDetailsView: FC<{
isLoading: boolean
isError: boolean
account: RuntimeAccount | undefined
token?: EvmToken
tokenPriceInfo: TokenPriceInfo
showLayer?: boolean
}> = ({ isLoading, isError, account, tokenPriceInfo, showLayer }) => {
}> = ({ isLoading, isError, account, token, tokenPriceInfo, showLayer }) => {
const { t } = useTranslation()
return isError ? (
<CardEmptyState label={t('account.cantLoadDetails')} />
) : (
<Account account={account} isLoading={isLoading} tokenPriceInfo={tokenPriceInfo} showLayer={showLayer} />
<Account
account={account}
token={token}
isLoading={isLoading}
tokenPriceInfo={tokenPriceInfo}
showLayer={showLayer}
/>
)
}
2 changes: 1 addition & 1 deletion src/app/pages/BlockDetailPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { transactionsContainerId } from './TransactionsCard'
import { BlockLink, BlockHashLink } from '../../components/Blocks/BlockLink'
import { RouteUtils } from '../../utils/route-utils'
import { useRequiredScopeParam } from '../../hooks/useScopeParam'
import { DashboardLink } from '../DashboardPage/DashboardLink'
import { DashboardLink } from '../ParatimeDashboardPage/DashboardLink'

export const BlockDetailPage: FC = () => {
const { t } = useTranslation()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import startOfMonth from 'date-fns/startOfMonth'
import { SnapshotCard } from './SnapshotCard'
import { SnapshotCard } from '../../components/Snapshots/SnapshotCard'
import { BarChart } from '../../components/charts/BarChart'
import {
useGetLayerStatsActiveAccounts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Box from '@mui/material/Box'
import Typography from '@mui/material/Typography'
import OfflineBoltIcon from '@mui/icons-material/OfflineBolt'
import InfoIcon from '@mui/icons-material/Info'
import { SnapshotCard } from './SnapshotCard'
import { SnapshotCard } from '../../components/Snapshots/SnapshotCard'
import { COLORS } from '../../../styles/theme/colors'
import { Layer, useGetRuntimeStatus } from '../../../oasis-indexer/api'
import { AppErrors } from '../../../types/errors'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Typography from '@mui/material/Typography'
import { useScreenSize } from '../../hooks/useScreensize'
import { useTheme } from '@mui/material/styles'
import { styled } from '@mui/material/styles'
import { DurationSelect } from './DurationSelect'
import { DurationSelect } from '../../components/Snapshots/DurationSelect'
import { TransactionsChartCard } from './TransactionsChartCard'
import { RosePriceCard } from './RosePriceCard'
import { Nodes } from './Nodes'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
import Box from '@mui/material/Box'
import { styled } from '@mui/material/styles'
import { CoinGeckoReferral } from '../../components/CoinGeckoReferral'
import { SnapshotCard } from './SnapshotCard'
import { SnapshotCard } from '../../components/Snapshots/SnapshotCard'
import { useGetRosePrice } from '../../../coin-gecko/api'
import { COLORS } from '../../../styles/theme/colors'
import Typography from '@mui/material/Typography'
Expand Down
File renamed without changes.
Loading

0 comments on commit 9227d9a

Please sign in to comment.