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