Commit b5757e40 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Details page: fix line height for rows (#1949)

* refactor block page

* token details

* token instance details

* name domain details

* blob details

* user ops details

* zkEvm and zkSync batch details

* tx details

* address details

* update screenshots
parent 86e33cae
This diff is collapsed.
......@@ -10,7 +10,7 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import { currencyUnits } from 'lib/units';
import CurrencyValue from 'ui/shared/CurrencyValue';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import NativeTokenIcon from 'ui/shared/NativeTokenIcon';
interface Props {
......@@ -66,25 +66,27 @@ const AddressBalance = ({ data, isLoading }: Props) => {
});
return (
<DetailsInfoItem
title={ `${ currencyUnits.ether } balance` }
hint={ `Address balance in ${ currencyUnits.ether }. Doesn't include ERC20, ERC721 and ERC1155 tokens` }
flexWrap="nowrap"
alignSelf="center"
isLoading={ isLoading }
>
<NativeTokenIcon boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<CurrencyValue
value={ data.coin_balance || '0' }
exchangeRate={ data.exchange_rate }
decimals={ String(config.chain.currency.decimals) }
currency={ currencyUnits.ether }
accuracyUsd={ 2 }
accuracy={ 8 }
flexWrap="wrap"
<>
<DetailsInfoItem.Label
hint={ `${ currencyUnits.ether } balance` }
isLoading={ isLoading }
/>
</DetailsInfoItem>
>
Balance
</DetailsInfoItem.Label>
<DetailsInfoItem.Value alignSelf="center" flexWrap="nowrap">
<NativeTokenIcon boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<CurrencyValue
value={ data.coin_balance || '0' }
exchangeRate={ data.exchange_rate }
decimals={ String(config.chain.currency.decimals) }
currency={ currencyUnits.ether }
accuracyUsd={ 2 }
accuracy={ 8 }
flexWrap="wrap"
isLoading={ isLoading }
/>
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -3,7 +3,7 @@ import React from 'react';
import type { Address } from 'types/api/address';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
interface Props {
......@@ -14,46 +14,58 @@ interface Props {
const AddressNameInfo = ({ data, isLoading }: Props) => {
if (data.token) {
return (
<DetailsInfoItem
title="Token name"
hint="Token name and symbol"
isLoading={ isLoading }
>
<TokenEntity
token={ data.token }
<>
<DetailsInfoItem.Label
hint="Token name and symbol"
isLoading={ isLoading }
noIcon
noCopy
/>
</DetailsInfoItem>
>
Token name
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<TokenEntity
token={ data.token }
isLoading={ isLoading }
noIcon
noCopy
/>
</DetailsInfoItem.Value>
</>
);
}
if (data.is_contract && data.name) {
return (
<DetailsInfoItem
title="Contract name"
hint="The name found in the source code of the Contract"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading }>
{ data.name }
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="The name found in the source code of the Contract"
isLoading={ isLoading }
>
Contract name
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }>
{ data.name }
</Skeleton>
</DetailsInfoItem.Value>
</>
);
}
if (data.name) {
return (
<DetailsInfoItem
title="Validator name"
hint="The name of the validator"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading }>
{ data.name }
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="The name of the validator"
isLoading={ isLoading }
>
Validator name
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }>
{ data.name }
</Skeleton>
</DetailsInfoItem.Value>
</>
);
}
......
......@@ -55,7 +55,7 @@ const TokenSelect = ({ onClick }: Props) => {
}
return (
<Flex columnGap={ 3 } mt={{ base: '6px', lg: 0 }}>
<Flex columnGap={ 3 } mt={{ base: 1, lg: 0 }}>
{ isMobile ?
<TokenSelectMobile data={ data } isLoading={ tokensIsFetching === 1 }/> :
<TokenSelectDesktop data={ data } isLoading={ tokensIsFetching === 1 }/>
......
......@@ -4,7 +4,7 @@ import React from 'react';
import type { Blob } from 'types/api/blobs';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
......@@ -30,53 +30,73 @@ const BlobInfo = ({ data, isLoading }: Props) => {
</Skeleton>
</GridItem>
) }
{ data.kzg_proof && (
<DetailsInfoItem
title="Proof"
hint="Zero knowledge proof. Allows for quick verification of commitment"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
{ data.kzg_proof }
<CopyToClipboard text={ data.kzg_proof } isLoading={ isLoading }/>
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Zero knowledge proof. Allows for quick verification of commitment"
isLoading={ isLoading }
>
Proof
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
{ data.kzg_proof }
<CopyToClipboard text={ data.kzg_proof } isLoading={ isLoading }/>
</Skeleton>
</DetailsInfoItem.Value>
</>
) }
{ data.kzg_commitment && (
<DetailsInfoItem
title="Commitment"
hint="Commitment to the data in the blob"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
{ data.kzg_commitment }
<CopyToClipboard text={ data.kzg_commitment } isLoading={ isLoading }/>
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Commitment to the data in the blob"
isLoading={ isLoading }
>
Commitment
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
{ data.kzg_commitment }
<CopyToClipboard text={ data.kzg_commitment } isLoading={ isLoading }/>
</Skeleton>
</DetailsInfoItem.Value>
</>
) }
{ data.blob_data && (
<DetailsInfoItem
title="Size, bytes"
hint="Blob size in bytes"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all">
{ (data.blob_data.replace('0x', '').length / 2).toLocaleString() }
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Blob size in bytes"
isLoading={ isLoading }
>
Size, bytes
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all">
{ (data.blob_data.replace('0x', '').length / 2).toLocaleString() }
</Skeleton>
</DetailsInfoItem.Value>
</>
) }
{ data.blob_data && <DetailsInfoItemDivider/> }
{ data.transaction_hashes[0] && (
<DetailsInfoItem
title="Transaction hash"
hint="Hash of the transaction with this blob"
isLoading={ isLoading }
>
<TxEntity hash={ data.transaction_hashes[0].transaction_hash } isLoading={ isLoading } noIcon noCopy={ false }/>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Hash of the transaction with this blob"
isLoading={ isLoading }
>
Transaction hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<TxEntity hash={ data.transaction_hashes[0].transaction_hash } isLoading={ isLoading } noIcon noCopy={ false }/>
</DetailsInfoItem.Value>
</>
) }
<DetailsSponsoredItem isLoading={ isLoading }/>
{ data.blob_data && (
......
This diff is collapsed.
......@@ -7,7 +7,7 @@ import type { Block } from 'types/api/block';
import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts';
import { space } from 'lib/html-entities';
import { currencyUnits } from 'lib/units';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import IconSvg from 'ui/shared/IconSvg';
import Utilization from 'ui/shared/Utilization/Utilization';
......@@ -33,51 +33,67 @@ const BlockDetailsBlobInfo = ({ data }: Props) => {
<>
{ data.blob_gas_price && (
<DetailsInfoItem
title="Blob gas price"
// eslint-disable-next-line max-len
hint="Price per unit of gas used for for blob deployment. Blob gas is independent of normal gas. Both gas prices can affect the priority of transaction execution."
>
<Text>{ BigNumber(data.blob_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre">
{ space }({ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
// eslint-disable-next-line max-len
hint="Price per unit of gas used for for blob deployment. Blob gas is independent of normal gas. Both gas prices can affect the priority of transaction execution."
>
Blob gas price
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ BigNumber(data.blob_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre">
{ space }({ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text>
</DetailsInfoItem.Value>
</>
) }
{ data.blob_gas_used && (
<DetailsInfoItem
title="Blob gas used"
hint="Actual amount of gas used by the blobs in this block"
>
<Text>{ BigNumber(data.blob_gas_used).toFormat() }</Text>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Actual amount of gas used by the blobs in this block"
>
Blob gas used
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ BigNumber(data.blob_gas_used).toFormat() }</Text>
</DetailsInfoItem.Value>
</>
) }
{ !burntBlobFees.isEqualTo(ZERO) && (
<DetailsInfoItem
title="Blob burnt fees"
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this block` }
>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" mr={ 2 }/>
{ burntBlobFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
{ !blobFees.isEqualTo(ZERO) && (
<Tooltip label="Blob burnt fees / Txn fees * 100%">
<div>
<Utilization ml={ 4 } value={ burntBlobFees.dividedBy(blobFees).toNumber() }/>
</div>
</Tooltip>
) }
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this block` }
>
Blob burnt fees
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" mr={ 2 }/>
{ burntBlobFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
{ !blobFees.isEqualTo(ZERO) && (
<Tooltip label="Blob burnt fees / Txn fees * 100%">
<div>
<Utilization ml={ 4 } value={ burntBlobFees.dividedBy(blobFees).toNumber() }/>
</div>
</Tooltip>
) }
</DetailsInfoItem.Value>
</>
) }
{ data.excess_blob_gas && (
<DetailsInfoItem
title="Excess blob gas"
hint="A running total of blob gas consumed in excess of the target, prior to the block."
>
<Text>{ BigNumber(data.excess_blob_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre">
{ space }({ BigNumber(data.excess_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="A running total of blob gas consumed in excess of the target, prior to the block."
>
Excess blob gas
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ BigNumber(data.excess_blob_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre">
{ space }({ BigNumber(data.excess_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text>
</DetailsInfoItem.Value>
</>
) }
<DetailsInfoItemDivider/>
</>
......
This diff is collapsed.
import { Text } from '@chakra-ui/react';
import React from 'react';
import ContainerWithScrollY from 'ui/shared/ContainerWithScrollY';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
export const TX_ACTIONS_BLOCK_ID = 'tx-actions';
const SCROLL_GRADIENT_HEIGHT = 48;
......@@ -25,26 +26,32 @@ const DetailsActionsWrapper = ({ children, isLoading, type }: Props) => {
}, []);
return (
<DetailsInfoItem
title={ `${ type === 'tx' ? 'Transaction' : 'User operation' } action` }
hint={ `Highlighted events of the ${ type === 'tx' ? 'transaction' : 'user operation' }` }
note={ hasScroll ? 'Scroll to see more' : undefined }
position="relative"
isLoading={ isLoading }
>
<ContainerWithScrollY
containerId={ TX_ACTIONS_BLOCK_ID }
gradientHeight={ SCROLL_GRADIENT_HEIGHT }
hasScroll={ hasScroll }
alignItems="stretch"
rowGap={ 5 }
w="100%"
maxH="200px"
ref={ containerRef }
<>
<DetailsInfoItem.Label
hint={ `Highlighted events of the ${ type === 'tx' ? 'transaction' : 'user operation' }` }
isLoading={ isLoading }
>
{ children }
</ContainerWithScrollY>
</DetailsInfoItem>
<span>{ `${ type === 'tx' ? 'Transaction' : 'User operation' } action` }</span>
{ hasScroll && <Text fontWeight={ 500 } variant="secondary" fontSize="xs" className="note" align="right">Scroll to see more</Text> }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
position="relative"
>
<ContainerWithScrollY
containerId={ TX_ACTIONS_BLOCK_ID }
gradientHeight={ SCROLL_GRADIENT_HEIGHT }
hasScroll={ hasScroll }
alignItems="stretch"
rowGap={ 5 }
w="100%"
maxH="200px"
ref={ containerRef }
>
{ children }
</ContainerWithScrollY>
</DetailsInfoItem.Value>
</>
);
};
......
import { GridItem, Flex, Text, Skeleton } from '@chakra-ui/react';
import type { HTMLChakraProps } from '@chakra-ui/system';
import { chakra, GridItem, Flex, Skeleton } from '@chakra-ui/react';
import React from 'react';
import Hint from 'ui/shared/Hint';
interface Props extends Omit<HTMLChakraProps<'div'>, 'title'> {
title: React.ReactNode;
interface LabelProps {
hint?: string;
children: React.ReactNode;
note?: string;
isLoading?: boolean;
className?: string;
id?: string;
}
const DetailsInfoItem = ({ title, hint, note, children, id, isLoading, ...styles }: Props) => {
const Label = chakra(({ hint, children, isLoading, id, className }: LabelProps) => {
return (
<>
<GridItem py={{ base: 1, lg: 2 }} id={ id } lineHeight={ 5 } { ...styles } _notFirst={{ mt: { base: 3, lg: 0 } }}>
<Flex columnGap={ 2 } alignItems="flex-start">
{ hint && <Hint label={ hint } isLoading={ isLoading }/> }
<Skeleton isLoaded={ !isLoading }>
<Text fontWeight={{ base: 700, lg: 500 }}>
{ title }
{ note && <Text fontWeight={ 500 } variant="secondary" fontSize="xs" className="note" align="right">{ note }</Text> }
</Text>
</Skeleton>
</Flex>
</GridItem>
<GridItem
display="flex"
alignItems="center"
flexWrap="wrap"
rowGap={ 3 }
pl={{ base: 7, lg: 0 }}
py={{ base: 1, lg: 2 }}
lineHeight={ 5 }
whiteSpace="nowrap"
{ ...styles }
>
{ children }
</GridItem>
</>
<GridItem
id={ id }
className={ className }
py={ 1 }
lineHeight={{ base: 5, lg: 6 }}
_notFirst={{ mt: { base: 3, lg: 0 } }}
>
<Flex columnGap={ 2 } alignItems="flex-start">
{ hint && <Hint label={ hint } isLoading={ isLoading } my={{ lg: '2px' }}/> }
<Skeleton isLoaded={ !isLoading } fontWeight={{ base: 700, lg: 500 }}>
{ children }
</Skeleton>
</Flex>
</GridItem>
);
};
});
interface ValueProps {
children: React.ReactNode;
className?: string;
}
export default DetailsInfoItem;
const Value = chakra(({ children, className }: ValueProps) => {
return (
<GridItem
className={ className }
display="flex"
alignItems="center"
flexWrap="wrap"
rowGap={ 3 }
pl={{ base: 7, lg: 0 }}
py={ 1 }
lineHeight={{ base: 5, lg: 6 }}
whiteSpace="nowrap"
>
{ children }
</GridItem>
);
});
export {
Label,
Value,
};
......@@ -5,7 +5,7 @@ import config from 'configs/app';
import * as cookies from 'lib/cookies';
import useIsMobile from 'lib/hooks/useIsMobile';
import AdBanner from 'ui/shared/ad/AdBanner';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
const feature = config.features.adsBanner;
......@@ -30,13 +30,17 @@ const DetailsSponsoredItem = ({ isLoading }: Props) => {
}
return (
<DetailsInfoItem
title="Sponsored"
hint="Sponsored banner advertisement"
isLoading={ isLoading }
>
<AdBanner isLoading={ isLoading }/>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Sponsored banner advertisement"
isLoading={ isLoading }
>
Sponsored
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AdBanner isLoading={ isLoading }/>
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -32,7 +32,7 @@ const PrevNext = ({ className, onClick, prevLabel, nextLabel, isPrevDisabled, is
}
return (
<Box className={ className }>
<Box className={ className } display="flex">
<Tooltip label={ prevLabel }>
<IconButton
aria-label="prev"
......
......@@ -18,7 +18,7 @@ import { TOKEN_COUNTERS } from 'stubs/token';
import type { TokenTabs } from 'ui/pages/Token';
import AppActionButton from 'ui/shared/AppActionButton/AppActionButton';
import useAppActionData from 'ui/shared/AppActionButton/useAppActionData';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import TruncatedValue from 'ui/shared/TruncatedValue';
......@@ -102,77 +102,96 @@ const TokenDetails = ({ tokenQuery }: Props) => {
<Grid
columnGap={ 8 }
rowGap={{ base: 1, lg: 3 }}
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden"
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(728px, auto)' }} overflow="hidden"
>
{ exchangeRate && (
<DetailsInfoItem
title="Price"
hint="Price per token on the exchanges"
alignSelf="center"
isLoading={ tokenQuery.isPlaceholderData }
>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
<span>{ `$${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }` }</span>
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Price per token on the exchanges"
isLoading={ tokenQuery.isPlaceholderData }
>
Price
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
<span>{ `$${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }` }</span>
</Skeleton>
</DetailsInfoItem.Value>
</>
) }
{ marketCap && (
<DetailsInfoItem
title="Fully diluted market cap"
hint="Total supply * Price"
alignSelf="center"
isLoading={ tokenQuery.isPlaceholderData }
>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
<span>{ `$${ BigNumber(marketCap).toFormat() }` }</span>
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Total supply * Price"
isLoading={ tokenQuery.isPlaceholderData }
>
Fully diluted market cap
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
<span>{ `$${ BigNumber(marketCap).toFormat() }` }</span>
</Skeleton>
</DetailsInfoItem.Value>
</>
) }
<DetailsInfoItem
title="Max total supply"
<DetailsInfoItem.Label
hint="The total amount of tokens issued"
isLoading={ tokenQuery.isPlaceholderData }
>
Max total supply
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
alignSelf="center"
wordBreak="break-word"
whiteSpace="pre-wrap"
isLoading={ tokenQuery.isPlaceholderData }
>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } w="100%" display="flex">
<TruncatedValue value={ totalSupplyValue || '0' } maxW="80%" flexShrink={ 0 }/>
<Box flexShrink={ 0 }> </Box>
<TruncatedValue value={ symbol || '' }/>
</Skeleton>
</DetailsInfoItem>
<DetailsInfoItem
title="Holders"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
hint="Number of accounts holding the token"
alignSelf="center"
isLoading={ tokenQuery.isPlaceholderData }
>
Holders
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !tokenCountersQuery.isPlaceholderData }>
{ countersItem('token_holders_count') }
</Skeleton>
</DetailsInfoItem>
<DetailsInfoItem
title="Transfers"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
hint="Number of transfer for the token"
alignSelf="center"
isLoading={ tokenQuery.isPlaceholderData }
>
Transfers
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !tokenCountersQuery.isPlaceholderData }>
{ countersItem('transfers_count') }
</Skeleton>
</DetailsInfoItem>
</DetailsInfoItem.Value>
{ decimals && (
<DetailsInfoItem
title="Decimals"
hint="Number of digits that come after the decimal place when displaying token value"
alignSelf="center"
isLoading={ tokenQuery.isPlaceholderData }
>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } minW={ 6 }>
{ decimals }
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Number of digits that come after the decimal place when displaying token value"
isLoading={ tokenQuery.isPlaceholderData }
>
Decimals
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } minW={ 6 }>
{ decimals }
</Skeleton>
</DetailsInfoItem.Value>
</>
) }
{ type !== 'ERC-20' && (
......@@ -186,14 +205,18 @@ const TokenDetails = ({ tokenQuery }: Props) => {
) }
{ (type !== 'ERC-20' && config.UI.views.nft.marketplaces.length === 0 && appActionData && isActionButtonExperiment) && (
<DetailsInfoItem
title="Dapp"
hint="Link to the dapp"
alignSelf="center"
py={ 1 }
>
<AppActionButton data={ appActionData } height="30px" source="NFT collection"/>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Link to the dapp"
>
Dapp
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
py="1px"
>
<AppActionButton data={ appActionData } height="30px" source="NFT collection"/>
</DetailsInfoItem.Value>
</>
) }
<DetailsSponsoredItem isLoading={ tokenQuery.isPlaceholderData }/>
......
......@@ -5,7 +5,7 @@ import type { AddressMetadataTagFormatted } from 'types/client/addressMetadata';
import config from 'configs/app';
import AppActionButton from 'ui/shared/AppActionButton/AppActionButton';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TextSeparator from 'ui/shared/TextSeparator';
interface Props {
......@@ -23,40 +23,44 @@ const TokenNftMarketplaces = ({ hash, id, isLoading, appActionData, source, isAc
}
return (
<DetailsInfoItem
title="Marketplaces"
hint="Marketplaces trading this NFT"
alignSelf="center"
isLoading={ isLoading }
py={ (appActionData && isActionButtonExperiment) ? 1 : { base: 1, lg: 2 } }
>
<Skeleton isLoaded={ !isLoading } display="flex" columnGap={ 3 } flexWrap="wrap" alignItems="center">
{ config.UI.views.nft.marketplaces.map((item) => {
const hrefTemplate = id ? item.instance_url : item.collection_url;
const href = hrefTemplate.replace('{id}', id || '').replace('{hash}', hash || '');
return (
<Tooltip label={ `View on ${ item.name }` } key={ item.name }>
<Link href={ href } target="_blank">
<Image
src={ item.logo_url }
alt={ `${ item.name } marketplace logo` }
boxSize={ 5 }
borderRadius="full"
/>
</Link>
</Tooltip>
);
}) }
{ (appActionData && isActionButtonExperiment) && (
<>
<TextSeparator color="gray.500" margin={ 0 }/>
<AppActionButton data={ appActionData } height="30px" source={ source }/>
</>
) }
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Marketplaces trading this NFT"
isLoading={ isLoading }
>
Marketplaces
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
py={ (appActionData && isActionButtonExperiment) ? '1px' : '6px' }
>
<Skeleton isLoaded={ !isLoading } display="flex" columnGap={ 3 } flexWrap="wrap" alignItems="center">
{ config.UI.views.nft.marketplaces.map((item) => {
const hrefTemplate = id ? item.instance_url : item.collection_url;
const href = hrefTemplate.replace('{id}', id || '').replace('{hash}', hash || '');
return (
<Tooltip label={ `View on ${ item.name }` } key={ item.name }>
<Link href={ href } target="_blank">
<Image
src={ item.logo_url }
alt={ `${ item.name } marketplace logo` }
boxSize={ 5 }
borderRadius="full"
/>
</Link>
</Tooltip>
);
}) }
{ (appActionData && isActionButtonExperiment) && (
<>
<TextSeparator color="gray.500" margin={ 0 }/>
<AppActionButton data={ appActionData } height="30px" source={ source }/>
</>
) }
</Skeleton>
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -8,7 +8,7 @@ import useFeatureValue from 'lib/growthbook/useFeatureValue';
import AppActionButton from 'ui/shared/AppActionButton/AppActionButton';
import useAppActionData from 'ui/shared/AppActionButton/useAppActionData';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
......@@ -53,31 +53,41 @@ const TokenInstanceDetails = ({ data, token, scrollRef, isLoading }: Props) => {
overflow="hidden"
>
{ data.is_unique && data.owner && (
<DetailsInfoItem
title="Owner"
hint="Current owner of this token instance"
isLoading={ isLoading }
>
<AddressEntity
address={ data.owner }
<>
<DetailsInfoItem.Label
hint="Current owner of this token instance"
isLoading={ isLoading }
/>
</DetailsInfoItem>
>
Owner
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressEntity
address={ data.owner }
isLoading={ isLoading }
/>
</DetailsInfoItem.Value>
</>
) }
<TokenInstanceCreatorAddress hash={ isLoading ? '' : token.address }/>
<DetailsInfoItem
title="Token ID"
<DetailsInfoItem.Label
hint="This token instance unique token ID"
isLoading={ isLoading }
>
Token ID
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Flex alignItems="center" overflow="hidden">
<Skeleton isLoaded={ !isLoading } overflow="hidden" display="inline-block" w="100%">
<HashStringShortenDynamic hash={ data.id }/>
</Skeleton>
<CopyToClipboard text={ data.id } isLoading={ isLoading }/>
</Flex>
</DetailsInfoItem>
</DetailsInfoItem.Value>
<TokenInstanceTransfersCount hash={ isLoading ? '' : token.address } id={ isLoading ? '' : data.id } onClick={ handleCounterItemClick }/>
<TokenNftMarketplaces
isLoading={ isLoading }
hash={ token.address }
......@@ -86,15 +96,18 @@ const TokenInstanceDetails = ({ data, token, scrollRef, isLoading }: Props) => {
source="NFT item"
isActionButtonExperiment={ isActionButtonExperiment }
/>
{ (config.UI.views.nft.marketplaces.length === 0 && appActionData && isActionButtonExperiment) && (
<DetailsInfoItem
title="Dapp"
hint="Link to the dapp"
alignSelf="center"
py={ 1 }
>
<AppActionButton data={ appActionData } height="30px" source="NFT item"/>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Link to the dapp"
>
Dapp
</DetailsInfoItem.Label>
<DetailsInfoItem.Value py="1px">
<AppActionButton data={ appActionData } height="30px" source="NFT item"/>
</DetailsInfoItem.Value>
</>
) }
</Grid>
<NftMedia
......
......@@ -2,7 +2,7 @@ import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import { ADDRESS_INFO } from 'stubs/address';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
interface Props {
......@@ -33,16 +33,20 @@ const TokenInstanceCreatorAddress = ({ hash }: Props) => {
};
return (
<DetailsInfoItem
title="Creator"
hint="Address that deployed this token contract"
isLoading={ addressQuery.isPlaceholderData }
>
<AddressEntity
address={ creatorAddress }
<>
<DetailsInfoItem.Label
hint="Address that deployed this token contract"
isLoading={ addressQuery.isPlaceholderData }
/>
</DetailsInfoItem>
>
Creator
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressEntity
address={ creatorAddress }
isLoading={ addressQuery.isPlaceholderData }
/>
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -5,7 +5,7 @@ import type { TokenInstance } from 'types/api/token';
import type { MetadataAttributes } from 'types/client/token';
import parseMetadata from 'lib/token/parseMetadata';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import LinkExternal from 'ui/shared/links/LinkExternal';
import TruncatedValue from 'ui/shared/TruncatedValue';
......@@ -74,42 +74,55 @@ const TokenInstanceMetadataInfo = ({ data, isLoading }: Props) => {
<>
<DetailsInfoItemDivider/>
{ metadata?.name && (
<DetailsInfoItem
title="Name"
hint="NFT name"
whiteSpace="normal"
wordBreak="break-word"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading }>
{ metadata.name }
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="NFT name"
isLoading={ isLoading }
>
Name
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
whiteSpace="normal"
wordBreak="break-word"
>
<Skeleton isLoaded={ !isLoading }>
{ metadata.name }
</Skeleton>
</DetailsInfoItem.Value>
</>
) }
{ metadata?.description && (
<DetailsInfoItem
title="Description"
hint="NFT description"
whiteSpace="normal"
wordBreak="break-word"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading }>
{ metadata.description }
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="NFT description"
isLoading={ isLoading }
>
Description
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
whiteSpace="normal"
wordBreak="break-word"
>
<Skeleton isLoaded={ !isLoading }>
{ metadata.description }
</Skeleton>
</DetailsInfoItem.Value>
</>
) }
{ metadata?.attributes && (
<DetailsInfoItem
title="Attributes"
hint="NFT attributes"
whiteSpace="normal"
isLoading={ isLoading }
>
<Grid gap={ 2 } templateColumns="repeat(auto-fill,minmax(160px, 1fr))" w="100%">
{ metadata.attributes.map((attribute, index) => <Item key={ index } data={ attribute } isLoading={ isLoading }/>) }
</Grid>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="NFT attributes"
isLoading={ isLoading }
>
Attributes
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Grid gap={ 2 } templateColumns="repeat(auto-fill,minmax(160px, 1fr))" w="100%" whiteSpace="normal">
{ metadata.attributes.map((attribute, index) => <Item key={ index } data={ attribute } isLoading={ isLoading }/>) }
</Grid>
</DetailsInfoItem.Value>
</>
) }
</>
);
......
......@@ -4,7 +4,7 @@ import React from 'react';
import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import LinkInternal from 'ui/shared/links/LinkInternal';
interface Props {
......@@ -37,20 +37,24 @@ const TokenInstanceTransfersCount = ({ hash, id, onClick }: Props) => {
undefined;
return (
<DetailsInfoItem
title="Transfers"
hint="Number of transfer for the token instance"
isLoading={ transfersCountQuery.isPlaceholderData }
>
<Skeleton isLoaded={ !transfersCountQuery.isPlaceholderData } display="inline-block">
<LinkInternal
href={ url }
onClick={ transfersCountQuery.data.transfers_count > 0 ? onClick : undefined }
>
{ transfersCountQuery.data.transfers_count.toLocaleString() }
</LinkInternal>
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Number of transfer for the token instance"
isLoading={ transfersCountQuery.isPlaceholderData }
>
Transfers
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !transfersCountQuery.isPlaceholderData } display="inline-block">
<LinkInternal
href={ url }
onClick={ transfersCountQuery.data.transfers_count > 0 ? onClick : undefined }
>
{ transfersCountQuery.data.transfers_count.toLocaleString() }
</LinkInternal>
</Skeleton>
</DetailsInfoItem.Value>
</>
);
};
......
import { Flex, Link, useBoolean } from '@chakra-ui/react';
import React from 'react';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
interface Props {
......@@ -14,27 +14,31 @@ const TxAllowedPeekers = ({ items }: Props) => {
const [ isExpanded, expand ] = useBoolean(false);
return (
<DetailsInfoItem
title="Allowed peekers"
hint="Smart contracts allowed to interact with confidential data"
>
<Flex flexDir="column" rowGap={ 3 } w="100%">
{ items
.slice(0, isExpanded ? undefined : CUT_LENGTH)
.map((item) => <AddressEntity key={ item } address={{ hash: item, is_contract: true }}/>) }
</Flex>
{ items.length > CUT_LENGTH && (
<Link
display="inline-block"
fontSize="sm"
textDecorationLine="underline"
textDecorationStyle="dashed"
onClick={ expand.toggle }
>
{ isExpanded ? 'Hide' : 'Show all' }
</Link>
) }
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Smart contracts allowed to interact with confidential data"
>
Allowed peekers
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Flex flexDir="column" rowGap={ 3 } w="100%">
{ items
.slice(0, isExpanded ? undefined : CUT_LENGTH)
.map((item) => <AddressEntity key={ item } address={{ hash: item, is_contract: true }}/>) }
</Flex>
{ items.length > CUT_LENGTH && (
<Link
display="inline-block"
fontSize="sm"
textDecorationLine="underline"
textDecorationStyle="dashed"
onClick={ expand.toggle }
>
{ isExpanded ? 'Hide' : 'Show all' }
</Link>
) }
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -8,7 +8,7 @@ import type { ExcludeUndefined } from 'types/utils';
import { currencyUnits } from 'lib/units';
import Tag from 'ui/shared/chakra/Tag';
import CurrencyValue from 'ui/shared/CurrencyValue';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
......@@ -24,85 +24,110 @@ interface Props {
const TxDetailsWrapped = ({ data }: Props) => {
return (
<Grid columnGap={ 8 } rowGap={{ base: 3, lg: 3 }} templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }}>
<DetailsInfoItem
title="Transaction hash"
<DetailsInfoItem.Label
hint="Unique character string (TxID) assigned to every verified transaction"
flexWrap="nowrap"
>
Transaction hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
<TxEntity hash={ data.hash } noIcon noLink noCopy={ false }/>
</DetailsInfoItem>
<DetailsInfoItem
title="Method"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
hint="Transaction method name"
>
Method
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Tag colorScheme="gray">
{ data.method }
</Tag>
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItemDivider/>
<DetailsInfoItem
title={ data.to?.is_contract ? 'Interacted with contract' : 'To' }
<DetailsInfoItem.Label
hint="Address (external or contract) receiving the transaction"
flexWrap={{ base: 'wrap', lg: 'nowrap' }}
columnGap={ 3 }
>
{ data.to?.is_contract ? 'Interacted with contract' : 'To' }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Flex flexWrap="nowrap" alignItems="center" maxW="100%">
<AddressEntity address={ data.to }/>
</Flex>
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItemDivider/>
<DetailsInfoItem
title="Value"
<DetailsInfoItem.Label
hint="Value sent in the native token (and USD) if applicable"
>
Value
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue
value={ data.value }
currency={ currencyUnits.ether }
flexWrap="wrap"
/>
</DetailsInfoItem>
</DetailsInfoItem.Value>
{ data.fee.value !== null && (
<DetailsInfoItem
title="Transaction fee"
hint="Total transaction fee"
>
<CurrencyValue
value={ data.fee.value }
currency={ currencyUnits.ether }
flexWrap="wrap"
/>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Total transaction fee"
>
Transaction fee
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue
value={ data.fee.value }
currency={ currencyUnits.ether }
flexWrap="wrap"
/>
</DetailsInfoItem.Value>
</>
) }
<TxDetailsGasPrice gasPrice={ data.gas_price }/>
{ data.gas_limit && (
<DetailsInfoItem
title="Gas limit"
hint="Maximum amount of gas that can be used by the transaction"
>
{ BigNumber(data.gas_limit).toFormat() }
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Maximum amount of gas that can be used by the transaction"
>
Gas limit
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ BigNumber(data.gas_limit).toFormat() }
</DetailsInfoItem.Value>
</>
) }
<DetailsInfoItemDivider/>
<TxDetailsOther type={ data.type } nonce={ data.nonce } position={ null }/>
<DetailsInfoItem
title="Raw input"
<DetailsInfoItem.Label
hint="Binary data included with the transaction. See logs tab for additional info"
>
Raw input
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<RawInputData hex={ data.raw_input }/>
</DetailsInfoItem>
</DetailsInfoItem.Value>
{ data.decoded_input && (
<DetailsInfoItem
title="Decoded input data"
hint="Decoded input data"
>
<LogDecodedInputData data={ data.decoded_input }/>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Decoded input data"
>
Decoded input data
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<LogDecodedInputData data={ data.decoded_input }/>
</DetailsInfoItem.Value>
</>
) }
</Grid>
);
......
......@@ -7,7 +7,7 @@ import config from 'configs/app';
import { ZERO } from 'lib/consts';
import { currencyUnits } from 'lib/units';
import CurrencyValue from 'ui/shared/CurrencyValue';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import IconSvg from 'ui/shared/IconSvg';
const rollupFeature = config.features.rollup;
......@@ -30,24 +30,28 @@ const TxDetailsBurntFees = ({ data, isLoading }: Props) => {
}
return (
<DetailsInfoItem
title="Burnt fees"
hint={ `
<>
<DetailsInfoItem.Label
hint={ `
Amount of ${ currencyUnits.ether } burned for this transaction. Equals Block Base Fee per Gas * Gas Used
${ data.blob_gas_price && data.blob_gas_used ? ' + Blob Gas Price * Blob Gas Used' : '' }
` }
isLoading={ isLoading }
>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/>
<CurrencyValue
value={ value.toString() }
currency={ currencyUnits.ether }
exchangeRate={ data.exchange_rate }
flexWrap="wrap"
ml={ 2 }
isLoading={ isLoading }
/>
</DetailsInfoItem>
>
Burnt fees
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/>
<CurrencyValue
value={ value.toString() }
currency={ currencyUnits.ether }
exchangeRate={ data.exchange_rate }
flexWrap="wrap"
ml={ 2 }
isLoading={ isLoading }
/>
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -4,7 +4,7 @@ import React from 'react';
import config from 'configs/app';
import { currencyUnits } from 'lib/units';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
interface Props {
txFee: string | null;
......@@ -18,16 +18,20 @@ const TxDetailsFeePerGas = ({ txFee, gasUsed, isLoading }: Props) => {
}
return (
<DetailsInfoItem
title="Fee per gas"
hint="Fee per gas"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
{ BigNumber(txFee).dividedBy(10 ** config.chain.currency.decimals).dividedBy(gasUsed).toFixed() }
{ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ currencyUnits.ether }` }
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Fee per gas"
isLoading={ isLoading }
>
Fee per gas
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
{ BigNumber(txFee).dividedBy(10 ** config.chain.currency.decimals).dividedBy(gasUsed).toFixed() }
{ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ currencyUnits.ether }` }
</Skeleton>
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -5,7 +5,7 @@ import React from 'react';
import config from 'configs/app';
import { WEI, WEI_IN_GWEI } from 'lib/consts';
import { currencyUnits } from 'lib/units';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
interface Props {
gasPrice: string | null;
......@@ -18,18 +18,22 @@ const TxDetailsGasPrice = ({ gasPrice, isLoading }: Props) => {
}
return (
<DetailsInfoItem
title="Gas price"
hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
{ BigNumber(gasPrice).dividedBy(WEI).toFixed() } { currencyUnits.ether }
</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
<span>({ BigNumber(gasPrice).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</span>
</Skeleton>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage"
isLoading={ isLoading }
>
Gas price
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
{ BigNumber(gasPrice).dividedBy(WEI).toFixed() } { currencyUnits.ether }
</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
<span>({ BigNumber(gasPrice).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</span>
</Skeleton>
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -3,47 +3,51 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TextSeparator from 'ui/shared/TextSeparator';
type Props = Pick<Transaction, 'nonce' | 'type' | 'position'>
const TxDetailsOther = ({ nonce, type, position }: Props) => {
return (
<DetailsInfoItem
title="Other"
hint="Other data related to this transaction"
>
{
[
typeof type === 'number' && (
<Box key="type">
<Text as="span" fontWeight="500">Txn type: </Text>
<Text fontWeight="600" as="span">{ type }</Text>
{ type === 2 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-1559)</Text> }
{ type === 3 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-4844)</Text> }
</Box>
),
<Box key="nonce">
<Text as="span" fontWeight="500">Nonce: </Text>
<Text fontWeight="600" as="span">{ nonce }</Text>
</Box>,
position !== null && position !== undefined && (
<Box key="position">
<Text as="span" fontWeight="500">Position: </Text>
<Text fontWeight="600" as="span">{ position }</Text>
</Box>
),
]
.filter(Boolean)
.map((item, index) => (
<>
{ index !== 0 && <TextSeparator/> }
{ item }
</>
))
}
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Other data related to this transaction"
>
Other
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{
[
typeof type === 'number' && (
<Box key="type">
<Text as="span" fontWeight="500">Txn type: </Text>
<Text fontWeight="600" as="span">{ type }</Text>
{ type === 2 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-1559)</Text> }
{ type === 3 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-4844)</Text> }
</Box>
),
<Box key="nonce">
<Text as="span" fontWeight="500">Nonce: </Text>
<Text fontWeight="600" as="span">{ nonce }</Text>
</Box>,
position !== null && position !== undefined && (
<Box key="position">
<Text as="span" fontWeight="500">Position: </Text>
<Text fontWeight="600" as="span">{ position }</Text>
</Box>
),
]
.filter(Boolean)
.map((item, index) => (
<>
{ index !== 0 && <TextSeparator/> }
{ item }
</>
))
}
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -5,7 +5,7 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import { route } from 'nextjs-routes';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal';
......@@ -40,22 +40,24 @@ const TxDetailsTokenTransfers = ({ data, txHash, isOverflow }: Props) => {
}
return (
<DetailsInfoItem
key={ type }
title={ title }
hint={ hint }
position="relative"
>
<Flex
flexDirection="column"
alignItems="flex-start"
rowGap={ 5 }
w="100%"
overflow="hidden"
<React.Fragment key={ type }>
<DetailsInfoItem.Label
hint={ hint }
>
{ items.map((item, index) => <TxDetailsTokenTransfer key={ index } data={ item }/>) }
</Flex>
</DetailsInfoItem>
{ title }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value position="relative">
<Flex
flexDirection="column"
alignItems="flex-start"
rowGap={ 5 }
w="100%"
overflow="hidden"
>
{ items.map((item, index) => <TxDetailsTokenTransfer key={ index } data={ item }/>) }
</Flex>
</DetailsInfoItem.Value>
</React.Fragment>
);
}) }
{ isOverflow && (
......
This diff is collapsed.
......@@ -13,7 +13,7 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
......@@ -61,10 +61,12 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(min-content, 200px) minmax(0, 1fr)' }}
overflow="hidden"
>
<DetailsInfoItem
title="Tx batch number"
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
Tx batch number
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }>
{ data.number }
</Skeleton>
......@@ -76,23 +78,32 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
isPrevDisabled={ data.number === 0 }
isLoading={ isPlaceholderData }
/>
</DetailsInfoItem>
<DetailsInfoItem
title="Status"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
Status
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<VerificationSteps steps={ ZKEVM_L2_TX_BATCH_STATUSES } currentStep={ data.status } isLoading={ isPlaceholderData }/>
</DetailsInfoItem>
<DetailsInfoItem
title="Timestamp"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
Timestamp
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.timestamp ? <DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/> : 'Undefined' }
</DetailsInfoItem>
<DetailsInfoItem
title="Verify tx hash"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
Verify tx hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.verify_tx_hash ? (
<TxEntityL1
isLoading={ isPlaceholderData }
......@@ -100,44 +111,57 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
maxW="100%"
/>
) : <Text>Pending</Text> }
</DetailsInfoItem>
<DetailsInfoItem
title="Transactions"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
Transactions
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }>
<LinkInternal href={ route({ pathname: '/batches/[number]', query: { number: data.number.toString(), tab: 'txs' } }) }>
{ data.transactions.length } transaction{ data.transactions.length === 1 ? '' : 's' }
</LinkInternal>
</Skeleton>
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItemDivider/>
<DetailsInfoItem
title="Global exit root"
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
Global exit root
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexWrap="nowrap"
>
<Skeleton isLoaded={ !isPlaceholderData } overflow="hidden">
<HashStringShortenDynamic hash={ data.global_exit_root }/>
</Skeleton>
<CopyToClipboard text={ data.global_exit_root } isLoading={ isPlaceholderData }/>
</DetailsInfoItem>
<DetailsInfoItem
title="Acc input hash"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
Acc input hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexWrap="nowrap"
>
<Skeleton isLoaded={ !isPlaceholderData } overflow="hidden">
<HashStringShortenDynamic hash={ data.acc_input_hash }/>
</Skeleton>
<CopyToClipboard text={ data.acc_input_hash } isLoading={ isPlaceholderData }/>
</DetailsInfoItem>
<DetailsInfoItem
title="Sequence tx hash"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
Sequence tx hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.sequence_tx_hash ? (
<TxEntityL1
isLoading={ isPlaceholderData }
......@@ -145,17 +169,21 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
maxW="100%"
/>
) : <Text>Pending</Text> }
</DetailsInfoItem>
<DetailsInfoItem
title="State root"
</DetailsInfoItem.Value>
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
State root
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexWrap="nowrap"
>
<Skeleton isLoaded={ !isPlaceholderData } overflow="hidden">
<HashStringShortenDynamic hash={ data.state_root }/>
</Skeleton>
<CopyToClipboard text={ data.state_root } isLoading={ isPlaceholderData }/>
</DetailsInfoItem>
</DetailsInfoItem.Value>
</Grid>
);
};
......
......@@ -16,7 +16,7 @@ import { currencyUnits } from 'lib/units';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
import LinkInternal from 'ui/shared/links/LinkInternal';
......@@ -76,11 +76,13 @@ const ZkSyncL2TxnBatchDetails = ({ query }: Props) => {
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(min-content, 200px) minmax(0, 1fr)' }}
overflow="hidden"
>
<DetailsInfoItem
title="Tx batch number"
<DetailsInfoItem.Label
hint="Batch number indicates the length of batches produced by grouping L2 blocks to be proven on Ethereum."
isLoading={ isPlaceholderData }
>
Tx batch number
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }>
{ data.number }
</Skeleton>
......@@ -92,35 +94,41 @@ const ZkSyncL2TxnBatchDetails = ({ query }: Props) => {
isPrevDisabled={ data.number === 0 }
isLoading={ isPlaceholderData }
/>
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItem
title="Status"
<DetailsInfoItem.Label
hint="Status is the short interpretation of the batch lifecycle"
isLoading={ isPlaceholderData }
>
Status
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES.slice(1) } currentStep={ data.status } isLoading={ isPlaceholderData }/>
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItem
title="Timestamp"
<DetailsInfoItem.Label
hint="Date and time at which batch is produced"
isLoading={ isPlaceholderData }
>
Timestamp
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.timestamp ? <DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/> : 'Undefined' }
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItem
title="Transactions"
<DetailsInfoItem.Label
hint="Number of transactions inside the batch."
isLoading={ isPlaceholderData }
>
Transactions
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }>
<LinkInternal href={ route({ pathname: '/batches/[number]', query: { number: data.number.toString(), tab: 'txs' } }) }>
{ txNum } transaction{ txNum === 1 ? '' : 's' }
</LinkInternal>
</Skeleton>
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItemDivider/>
......@@ -146,31 +154,38 @@ const ZkSyncL2TxnBatchDetails = ({ query }: Props) => {
<>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
<DetailsInfoItem
title="Root hash"
<DetailsInfoItem.Label
hint="L1 batch root is a hash that summarizes batch data and submitted to the L1"
>
Root hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexWrap="nowrap"
alignSelf="flex-start"
>
<TruncatedValue value={ data.root_hash }/>
<CopyToClipboard text={ data.root_hash }/>
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItem
title="L1 gas price"
<DetailsInfoItem.Label
hint="Gas price for the batch settlement transaction on L1"
>
L1 gas price
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text mr={ 1 }>{ BigNumber(data.l1_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether }</Text>
<Text variant="secondary">({ BigNumber(data.l1_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</Text>
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItem
title="L2 fair gas price"
<DetailsInfoItem.Label
hint={ 'The gas price below which the "baseFee" of the batch should not fall' }
>
L2 fair gas price
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text mr={ 1 }>{ BigNumber(data.l2_fair_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether }</Text>
<Text variant="secondary">({ BigNumber(data.l2_fair_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</Text>
</DetailsInfoItem>
</DetailsInfoItem.Value>
</>
) }
</Grid>
......
......@@ -3,7 +3,7 @@ import React from 'react';
import type { ZkSyncBatch } from 'types/api/zkSyncL2';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
......@@ -23,10 +23,13 @@ interface Props {
const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
return (
<>
<DetailsInfoItem
title="Commit tx hash"
<DetailsInfoItem.Label
hint="Hash of L1 tx on which the batch was committed"
isLoading={ isLoading }
>
Commit tx hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexDir="column"
alignItems="flex-start"
>
......@@ -45,12 +48,15 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
) }
</>
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItem
title="Prove tx hash"
<DetailsInfoItem.Label
hint="Hash of L1 tx on which the batch was proven"
isLoading={ isLoading }
>
Prove tx hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexDir="column"
alignItems="flex-start"
>
......@@ -69,12 +75,15 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
) }
</>
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
</DetailsInfoItem>
</DetailsInfoItem.Value>
<DetailsInfoItem
title="Execute tx hash"
<DetailsInfoItem.Label
hint="Hash of L1 tx on which the batch was executed and finalized"
isLoading={ isLoading }
>
Execute tx hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexDir="column"
alignItems="flex-start"
>
......@@ -93,7 +102,7 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
) }
</>
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
</DetailsInfoItem>
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -2,7 +2,7 @@ import React from 'react';
import type { UserOp } from 'types/api/userOps';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import RawInputData from 'ui/shared/RawInputData';
import UserOpCallDataSwitch from './UserOpCallDataSwitch';
......@@ -33,12 +33,16 @@ const UserOpDecodedCallData = ({ data }: Props) => {
) : null;
return (
<DetailsInfoItem
title="Call data"
hint="Data that’s passed to the sender for execution"
>
<RawInputData hex={ callData } rightSlot={ toggler }/>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Data that’s passed to the sender for execution"
>
Call data
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<RawInputData hex={ callData } rightSlot={ toggler }/>
</DetailsInfoItem.Value>
</>
);
};
......
......@@ -2,7 +2,7 @@ import React from 'react';
import type { UserOp } from 'types/api/userOps';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
import UserOpCallDataSwitch from './UserOpCallDataSwitch';
......@@ -33,14 +33,19 @@ const UserOpDecodedCallData = ({ data }: Props) => {
) : null;
return (
<DetailsInfoItem
title="Decoded call data"
hint="Decoded call data"
flexDir={{ base: 'column', lg: 'row' }}
alignItems={{ base: 'flex-start', lg: 'center' }}
>
<LogDecodedInputData data={ callData } rightSlot={ toggler }/>
</DetailsInfoItem>
<>
<DetailsInfoItem.Label
hint="Decoded call data"
>
Decoded call data
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexDir={{ base: 'column', lg: 'row' }}
alignItems={{ base: 'flex-start', lg: 'center' }}
>
<LogDecodedInputData data={ callData } rightSlot={ toggler }/>
</DetailsInfoItem.Value>
</>
);
};
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment