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
...@@ -10,7 +10,7 @@ import AddressCounterItem from 'ui/address/details/AddressCounterItem'; ...@@ -10,7 +10,7 @@ import AddressCounterItem from 'ui/address/details/AddressCounterItem';
import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning'; import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError'; import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
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 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';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
...@@ -92,12 +92,16 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -92,12 +92,16 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden" templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden"
> >
<AddressNameInfo data={ data } isLoading={ addressQuery.isPlaceholderData }/> <AddressNameInfo data={ data } isLoading={ addressQuery.isPlaceholderData }/>
{ data.is_contract && data.creation_tx_hash && data.creator_address_hash && ( { data.is_contract && data.creation_tx_hash && data.creator_address_hash && (
<DetailsInfoItem <>
title="Creator" <DetailsInfoItem.Label
hint="Transaction and address of creation" hint="Transaction and address of creation"
isLoading={ addressQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData }
> >
Creator
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressEntity <AddressEntity
address={{ hash: data.creator_address_hash }} address={{ hash: data.creator_address_hash }}
truncation="constant" truncation="constant"
...@@ -105,48 +109,63 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -105,48 +109,63 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
/> />
<Text whiteSpace="pre"> at txn </Text> <Text whiteSpace="pre"> at txn </Text>
<TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon noCopy={ false }/> <TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon noCopy={ false }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.is_contract && data.implementation_address && ( { data.is_contract && data.implementation_address && (
<DetailsInfoItem <>
title="Implementation" <DetailsInfoItem.Label
hint="Implementation address of the proxy contract" hint="Implementation address of the proxy contract"
columnGap={ 1 }
> >
Implementation
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressEntity <AddressEntity
address={{ hash: data.implementation_address, name: data.implementation_name, is_contract: true }} address={{ hash: data.implementation_address, name: data.implementation_name, is_contract: true }}
isLoading={ addressQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData }
noIcon noIcon
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<AddressBalance data={ data } isLoading={ addressQuery.isPlaceholderData }/> <AddressBalance data={ data } isLoading={ addressQuery.isPlaceholderData }/>
{ data.has_tokens && ( { data.has_tokens && (
<DetailsInfoItem <>
title="Tokens" <DetailsInfoItem.Label
hint="All tokens in the account and total value" hint="All tokens in the account and total value"
alignSelf="center"
py={ 0 }
> >
{ addressQuery.data ? <TokenSelect onClick={ handleCounterItemClick }/> : <Box py="6px">0</Box> } Tokens
</DetailsInfoItem> </DetailsInfoItem.Label>
<DetailsInfoItem.Value py={ addressQuery.data ? 0 : undefined }>
{ addressQuery.data ? <TokenSelect onClick={ handleCounterItemClick }/> : <Box>0</Box> }
</DetailsInfoItem.Value>
</>
) } ) }
{ (config.features.multichainButton.isEnabled || (data.exchange_rate && data.has_tokens)) && ( { (config.features.multichainButton.isEnabled || (data.exchange_rate && data.has_tokens)) && (
<DetailsInfoItem <>
title="Net worth" <DetailsInfoItem.Label
hint="Total net worth in USD of all tokens for the address" hint="Total net worth in USD of all tokens for the address"
alignSelf="center"
isLoading={ addressQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData }
> >
Net worth
</DetailsInfoItem.Label>
<DetailsInfoItem.Value alignSelf="center">
<AddressNetWorth addressData={ addressQuery.data } addressHash={ addressHash } isLoading={ addressQuery.isPlaceholderData }/> <AddressNetWorth addressData={ addressQuery.data } addressHash={ addressHash } isLoading={ addressQuery.isPlaceholderData }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) )
} }
<DetailsInfoItem
title="Transactions" <DetailsInfoItem.Label
hint="Number of transactions related to this address" hint="Number of transactions related to this address"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
> >
Transactions
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ addressQuery.data ? ( { addressQuery.data ? (
<AddressCounterItem <AddressCounterItem
prop="transactions_count" prop="transactions_count"
...@@ -158,13 +177,17 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -158,13 +177,17 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
/> />
) : ) :
0 } 0 }
</DetailsInfoItem> </DetailsInfoItem.Value>
{ data.has_token_transfers && ( { data.has_token_transfers && (
<DetailsInfoItem <>
title="Transfers" <DetailsInfoItem.Label
hint="Number of transfers to/from this address" hint="Number of transfers to/from this address"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
> >
Transfers
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ addressQuery.data ? ( { addressQuery.data ? (
<AddressCounterItem <AddressCounterItem
prop="token_transfers_count" prop="token_transfers_count"
...@@ -176,14 +199,19 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -176,14 +199,19 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
/> />
) : ) :
0 } 0 }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ countersQuery.data?.gas_usage_count && ( { countersQuery.data?.gas_usage_count && (
<DetailsInfoItem <>
title="Gas used" <DetailsInfoItem.Label
hint="Gas used by the address" hint="Gas used by the address"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
> >
Gas used
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ addressQuery.data ? ( { addressQuery.data ? (
<AddressCounterItem <AddressCounterItem
prop="gas_usage_count" prop="gas_usage_count"
...@@ -195,14 +223,19 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -195,14 +223,19 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
/> />
) : ) :
0 } 0 }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.has_validated_blocks && ( { data.has_validated_blocks && (
<DetailsInfoItem <>
title="Blocks validated" <DetailsInfoItem.Label
hint="Number of blocks validated by this validator" hint="Number of blocks validated by this validator"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
> >
Blocks validated
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ addressQuery.data ? ( { addressQuery.data ? (
<AddressCounterItem <AddressCounterItem
prop="validations_count" prop="validations_count"
...@@ -214,22 +247,27 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -214,22 +247,27 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
/> />
) : ) :
0 } 0 }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.block_number_balance_updated_at && ( { data.block_number_balance_updated_at && (
<DetailsInfoItem <>
title="Last balance update" <DetailsInfoItem.Label
hint="Block number in which the address was updated" hint="Block number in which the address was updated"
alignSelf="center"
py={{ base: '2px', lg: 1 }}
isLoading={ addressQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData }
> >
Last balance update
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<BlockEntity <BlockEntity
number={ data.block_number_balance_updated_at } number={ data.block_number_balance_updated_at }
isLoading={ addressQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData }
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<DetailsSponsoredItem isLoading={ addressQuery.isPlaceholderData }/> <DetailsSponsoredItem isLoading={ addressQuery.isPlaceholderData }/>
</Grid> </Grid>
</> </>
......
...@@ -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,13 +66,14 @@ const AddressBalance = ({ data, isLoading }: Props) => { ...@@ -66,13 +66,14 @@ 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 } isLoading={ isLoading }
> >
Balance
</DetailsInfoItem.Label>
<DetailsInfoItem.Value alignSelf="center" flexWrap="nowrap">
<NativeTokenIcon boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/> <NativeTokenIcon boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<CurrencyValue <CurrencyValue
value={ data.coin_balance || '0' } value={ data.coin_balance || '0' }
...@@ -84,7 +85,8 @@ const AddressBalance = ({ data, isLoading }: Props) => { ...@@ -84,7 +85,8 @@ const AddressBalance = ({ data, isLoading }: Props) => {
flexWrap="wrap" flexWrap="wrap"
isLoading={ isLoading } isLoading={ isLoading }
/> />
</DetailsInfoItem> </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 } isLoading={ isLoading }
> >
Token name
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<TokenEntity <TokenEntity
token={ data.token } token={ data.token }
isLoading={ isLoading } isLoading={ isLoading }
noIcon noIcon
noCopy noCopy
/> />
</DetailsInfoItem> </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 }
> >
Contract name
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }> <Skeleton isLoaded={ !isLoading }>
{ data.name } { data.name }
</Skeleton> </Skeleton>
</DetailsInfoItem> </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 }
> >
Validator name
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }> <Skeleton isLoaded={ !isLoading }>
{ data.name } { data.name }
</Skeleton> </Skeleton>
</DetailsInfoItem> </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 }
> >
Proof
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px"> <Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
{ data.kzg_proof } { data.kzg_proof }
<CopyToClipboard text={ data.kzg_proof } isLoading={ isLoading }/> <CopyToClipboard text={ data.kzg_proof } isLoading={ isLoading }/>
</Skeleton> </Skeleton>
</DetailsInfoItem> </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 }
> >
Commitment
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px"> <Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
{ data.kzg_commitment } { data.kzg_commitment }
<CopyToClipboard text={ data.kzg_commitment } isLoading={ isLoading }/> <CopyToClipboard text={ data.kzg_commitment } isLoading={ isLoading }/>
</Skeleton> </Skeleton>
</DetailsInfoItem> </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 }
> >
Size, bytes
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all"> <Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all">
{ (data.blob_data.replace('0x', '').length / 2).toLocaleString() } { (data.blob_data.replace('0x', '').length / 2).toLocaleString() }
</Skeleton> </Skeleton>
</DetailsInfoItem> </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 }
> >
Transaction hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<TxEntity hash={ data.transaction_hashes[0].transaction_hash } isLoading={ isLoading } noIcon noCopy={ false }/> <TxEntity hash={ data.transaction_hashes[0].transaction_hash } isLoading={ isLoading } noIcon noCopy={ false }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<DetailsSponsoredItem isLoading={ isLoading }/> <DetailsSponsoredItem isLoading={ isLoading }/>
{ data.blob_data && ( { data.blob_data && (
......
...@@ -17,7 +17,7 @@ import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; ...@@ -17,7 +17,7 @@ import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
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 DetailsTimestamp from 'ui/shared/DetailsTimestamp'; import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
...@@ -163,11 +163,13 @@ const BlockDetails = ({ query }: Props) => { ...@@ -163,11 +163,13 @@ const BlockDetails = ({ 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={ `${ blockTypeLabel } height` }
hint="The block height of a particular block is defined as the number of blocks preceding it in the blockchain" hint="The block height of a particular block is defined as the number of blocks preceding it in the blockchain"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
{ blockTypeLabel } height
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ data.height } { data.height }
</Skeleton> </Skeleton>
...@@ -180,122 +182,151 @@ const BlockDetails = ({ query }: Props) => { ...@@ -180,122 +182,151 @@ const BlockDetails = ({ query }: Props) => {
isPrevDisabled={ data.height === 0 } isPrevDisabled={ data.height === 0 }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Size" <DetailsInfoItem.Label
hint="Size of the block in bytes" hint="Size of the block in bytes"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Size
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ data.size.toLocaleString() } { data.size.toLocaleString() }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Timestamp" <DetailsInfoItem.Label
hint="Date & time at which block was produced." hint="Date & time at which block was produced."
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Timestamp
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/> <DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Transactions" <DetailsInfoItem.Label
hint="The number of transactions in the block" hint="The number of transactions in the block"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Transactions
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ txsNum } { txsNum }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
{ config.features.beaconChain.isEnabled && Boolean(data.withdrawals_count) && ( { config.features.beaconChain.isEnabled && Boolean(data.withdrawals_count) && (
<DetailsInfoItem <>
title="Withdrawals" <DetailsInfoItem.Label
hint="The number of beacon withdrawals in the block" hint="The number of beacon withdrawals in the block"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Withdrawals
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
<LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'withdrawals' } }) }> <LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'withdrawals' } }) }>
{ data.withdrawals_count } withdrawal{ data.withdrawals_count === 1 ? '' : 's' } { data.withdrawals_count } withdrawal{ data.withdrawals_count === 1 ? '' : 's' }
</LinkInternal> </LinkInternal>
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync && !config.UI.views.block.hiddenFields?.batch && ( { rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync && !config.UI.views.block.hiddenFields?.batch && (
<DetailsInfoItem <>
title="Batch" <DetailsInfoItem.Label
hint="Batch number" hint="Batch number"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
{ data.zksync.batch_number ? ( Batch
<BatchEntityL2 </DetailsInfoItem.Label>
isLoading={ isPlaceholderData } <DetailsInfoItem.Value>
number={ data.zksync.batch_number } { data.zksync.batch_number ?
/> <BatchEntityL2 isLoading={ isPlaceholderData } number={ data.zksync.batch_number }/> :
) : <Skeleton isLoaded={ !isPlaceholderData }>Pending</Skeleton> } <Skeleton isLoaded={ !isPlaceholderData }>Pending</Skeleton> }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync && !config.UI.views.block.hiddenFields?.L1_status && ( { rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync && !config.UI.views.block.hiddenFields?.L1_status && (
<DetailsInfoItem <>
title="Status" <DetailsInfoItem.Label
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 } currentStep={ data.zksync.status } isLoading={ isPlaceholderData }/> <VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES } currentStep={ data.zksync.status } isLoading={ isPlaceholderData }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ !config.UI.views.block.hiddenFields?.miner && ( { !config.UI.views.block.hiddenFields?.miner && (
<DetailsInfoItem <>
title={ verificationTitle } <DetailsInfoItem.Label
hint="A block producer who successfully included the block onto the blockchain" hint="A block producer who successfully included the block onto the blockchain"
columnGap={ 1 }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
{ verificationTitle }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressEntity <AddressEntity
address={ data.miner } address={ data.miner }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
{ /* api doesn't return the block processing time yet */ } </DetailsInfoItem.Value>
{ /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ } </>
</DetailsInfoItem>
) } ) }
{ !rollupFeature.isEnabled && !totalReward.isEqualTo(ZERO) && !config.UI.views.block.hiddenFields?.total_reward && ( { !rollupFeature.isEnabled && !totalReward.isEqualTo(ZERO) && !config.UI.views.block.hiddenFields?.total_reward && (
<DetailsInfoItem <>
title="Block reward" <DetailsInfoItem.Label
hint={ hint={
`For each block, the ${ validatorTitle } is rewarded with a finite amount of ${ config.chain.currency.symbol || 'native token' } `For each block, the ${ validatorTitle } is rewarded with a finite amount of ${ config.chain.currency.symbol || 'native token' }
on top of the fees paid for all transactions in the block` on top of the fees paid for all transactions in the block`
} }
columnGap={ 1 }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Block reward
</DetailsInfoItem.Label>
<DetailsInfoItem.Value columnGap={ 1 }>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ totalReward.dividedBy(WEI).toFixed() } { currencyUnits.ether } { totalReward.dividedBy(WEI).toFixed() } { currencyUnits.ether }
</Skeleton> </Skeleton>
{ rewardBreakDown } { rewardBreakDown }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.rewards { data.rewards
?.filter(({ type }) => type !== 'Validator Reward' && type !== 'Miner Reward') ?.filter(({ type }) => type !== 'Validator Reward' && type !== 'Miner Reward')
.map(({ type, reward }) => ( .map(({ type, reward }) => (
<DetailsInfoItem <React.Fragment key={ type }>
key={ type } <DetailsInfoItem.Label
title={ type }
// is this text correct for validators?
hint={ `Amount of distributed reward. ${ capitalize(validatorTitle) }s receive a static block reward + Tx fees + uncle fees` } hint={ `Amount of distributed reward. ${ capitalize(validatorTitle) }s receive a static block reward + Tx fees + uncle fees` }
> >
{ type }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ BigNumber(reward).dividedBy(WEI).toFixed() } { currencyUnits.ether } { BigNumber(reward).dividedBy(WEI).toFixed() } { currencyUnits.ether }
</DetailsInfoItem> </DetailsInfoItem.Value>
</React.Fragment>
)) ))
} }
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
<DetailsInfoItem <DetailsInfoItem.Label
title="Gas used"
hint="The total gas amount used in the block and its percentage of gas filled in the block" hint="The total gas amount used in the block and its percentage of gas filled in the block"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Gas used
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ BigNumber(data.gas_used || 0).toFormat() } { BigNumber(data.gas_used || 0).toFormat() }
</Skeleton> </Skeleton>
...@@ -311,33 +342,45 @@ const BlockDetails = ({ query }: Props) => { ...@@ -311,33 +342,45 @@ const BlockDetails = ({ query }: Props) => {
<GasUsedToTargetRatio value={ data.gas_target_percentage } isLoading={ isPlaceholderData }/> <GasUsedToTargetRatio value={ data.gas_target_percentage } isLoading={ isPlaceholderData }/>
</> </>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Gas limit" <DetailsInfoItem.Label
hint="Total gas limit provided by all transactions in the block" hint="Total gas limit provided by all transactions in the block"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Gas limit
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ BigNumber(data.gas_limit).toFormat() } { BigNumber(data.gas_limit).toFormat() }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
{ data.minimum_gas_price && ( { data.minimum_gas_price && (
<DetailsInfoItem <>
title="Minimum gas price" <DetailsInfoItem.Label
hint="The minimum gas price a transaction should have in order to be included in this block" hint="The minimum gas price a transaction should have in order to be included in this block"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Minimum gas price
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ BigNumber(data.minimum_gas_price).dividedBy(GWEI).toFormat() } { currencyUnits.gwei } { BigNumber(data.minimum_gas_price).dividedBy(GWEI).toFormat() } { currencyUnits.gwei }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.base_fee_per_gas && ( { data.base_fee_per_gas && (
<DetailsInfoItem <>
title="Base fee per gas" <DetailsInfoItem.Label
hint="Minimum fee required per unit of gas. Fee adjusts based on network congestion" hint="Minimum fee required per unit of gas. Fee adjusts based on network congestion"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Base fee per gas
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ isPlaceholderData ? ( { isPlaceholderData ? (
<Skeleton isLoaded={ !isPlaceholderData } h="20px" maxW="380px" w="100%"/> <Skeleton isLoaded={ !isPlaceholderData } h="20px" maxW="380px" w="100%"/>
) : ( ) : (
...@@ -348,18 +391,22 @@ const BlockDetails = ({ query }: Props) => { ...@@ -348,18 +391,22 @@ const BlockDetails = ({ query }: Props) => {
</Text> </Text>
</> </>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ !config.UI.views.block.hiddenFields?.burnt_fees && !burntFees.isEqualTo(ZERO) && ( { !config.UI.views.block.hiddenFields?.burnt_fees && !burntFees.isEqualTo(ZERO) && (
<DetailsInfoItem <>
title="Burnt fees" <DetailsInfoItem.Label
hint={ hint={
`Amount of ${ config.chain.currency.symbol || 'native token' } burned from transactions included in the block. `Amount of ${ config.chain.currency.symbol || 'native token' } burned from transactions included in the block.
Equals Block Base Fee per Gas * Gas Used` Equals Block Base Fee per Gas * Gas Used`
} }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Burnt fees
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/> <IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/>
<Skeleton isLoaded={ !isPlaceholderData } ml={ 2 }> <Skeleton isLoaded={ !isPlaceholderData } ml={ 2 }>
{ burntFees.dividedBy(WEI).toFixed() } { currencyUnits.ether } { burntFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
...@@ -375,27 +422,25 @@ const BlockDetails = ({ query }: Props) => { ...@@ -375,27 +422,25 @@ const BlockDetails = ({ query }: Props) => {
</Box> </Box>
</Tooltip> </Tooltip>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.priority_fee !== null && BigNumber(data.priority_fee).gt(ZERO) && ( { data.priority_fee !== null && BigNumber(data.priority_fee).gt(ZERO) && (
<DetailsInfoItem <>
title="Priority fee / Tip" <DetailsInfoItem.Label
hint="User-defined tips sent to validator for transaction priority/inclusion" hint="User-defined tips sent to validator for transaction priority/inclusion"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Priority fee / Tip
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ BigNumber(data.priority_fee).dividedBy(WEI).toFixed() } { currencyUnits.ether } { BigNumber(data.priority_fee).dividedBy(WEI).toFixed() } { currencyUnits.ether }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ /* api doesn't support extra data yet */ }
{ /* <DetailsInfoItem
title="Extra data"
hint={ `Any data that can be included by the ${ validatorTitle } in the block` }
>
<Text whiteSpace="pre">{ data.extra_data } </Text>
<Text variant="secondary">(Hex: { data.extra_data })</Text>
</DetailsInfoItem> */ }
{ /* CUT */ } { /* CUT */ }
<GridItem colSpan={{ base: undefined, lg: 2 }}> <GridItem colSpan={{ base: undefined, lg: 2 }}>
...@@ -424,9 +469,13 @@ const BlockDetails = ({ query }: Props) => { ...@@ -424,9 +469,13 @@ const BlockDetails = ({ query }: Props) => {
{ !isPlaceholderData && <BlockDetailsBlobInfo data={ data }/> } { !isPlaceholderData && <BlockDetailsBlobInfo data={ data }/> }
{ data.bitcoin_merged_mining_header && ( { data.bitcoin_merged_mining_header && (
<DetailsInfoItem <>
title="Bitcoin merged mining header" <DetailsInfoItem.Label
hint="Merged-mining field: Bitcoin header" hint="Merged-mining field: Bitcoin header"
>
Bitcoin merged mining header
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexWrap="nowrap" flexWrap="nowrap"
alignSelf="flex-start" alignSelf="flex-start"
> >
...@@ -434,38 +483,54 @@ const BlockDetails = ({ query }: Props) => { ...@@ -434,38 +483,54 @@ const BlockDetails = ({ query }: Props) => {
<HashStringShortenDynamic hash={ data.bitcoin_merged_mining_header }/> <HashStringShortenDynamic hash={ data.bitcoin_merged_mining_header }/>
</Box> </Box>
<CopyToClipboard text={ data.bitcoin_merged_mining_header }/> <CopyToClipboard text={ data.bitcoin_merged_mining_header }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.bitcoin_merged_mining_coinbase_transaction && ( { data.bitcoin_merged_mining_coinbase_transaction && (
<DetailsInfoItem <>
title="Bitcoin merged mining coinbase transaction" <DetailsInfoItem.Label
hint="Merged-mining field: Coinbase transaction" hint="Merged-mining field: Coinbase transaction"
> >
Bitcoin merged mining coinbase transaction
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<RawDataSnippet <RawDataSnippet
data={ data.bitcoin_merged_mining_coinbase_transaction } data={ data.bitcoin_merged_mining_coinbase_transaction }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
showCopy={ false } showCopy={ false }
textareaMaxHeight="100px" textareaMaxHeight="100px"
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.bitcoin_merged_mining_merkle_proof && ( { data.bitcoin_merged_mining_merkle_proof && (
<DetailsInfoItem <>
title="Bitcoin merged mining Merkle proof" <DetailsInfoItem.Label
hint="Merged-mining field: Merkle proof" hint="Merged-mining field: Merkle proof"
> >
Bitcoin merged mining Merkle proof
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<RawDataSnippet <RawDataSnippet
data={ data.bitcoin_merged_mining_merkle_proof } data={ data.bitcoin_merged_mining_merkle_proof }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
showCopy={ false } showCopy={ false }
textareaMaxHeight="100px" textareaMaxHeight="100px"
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.hash_for_merged_mining && ( { data.hash_for_merged_mining && (
<DetailsInfoItem <>
title="Hash for merged mining" <DetailsInfoItem.Label
hint="Merged-mining field: Rootstock block header hash" hint="Merged-mining field: Rootstock block header hash"
>
Hash for merged mining
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexWrap="nowrap" flexWrap="nowrap"
alignSelf="flex-start" alignSelf="flex-start"
> >
...@@ -473,46 +538,54 @@ const BlockDetails = ({ query }: Props) => { ...@@ -473,46 +538,54 @@ const BlockDetails = ({ query }: Props) => {
<HashStringShortenDynamic hash={ data.hash_for_merged_mining }/> <HashStringShortenDynamic hash={ data.hash_for_merged_mining }/>
</Box> </Box>
<CopyToClipboard text={ data.hash_for_merged_mining }/> <CopyToClipboard text={ data.hash_for_merged_mining }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<DetailsInfoItem <DetailsInfoItem.Label
title="Difficulty"
hint={ `Block difficulty for ${ validatorTitle }, used to calibrate block generation time` } hint={ `Block difficulty for ${ validatorTitle }, used to calibrate block generation time` }
> >
<Box whiteSpace="nowrap" overflow="hidden"> Difficulty
</DetailsInfoItem.Label>
<DetailsInfoItem.Value overflow="hidden">
<HashStringShortenDynamic hash={ BigNumber(data.difficulty).toFormat() }/> <HashStringShortenDynamic hash={ BigNumber(data.difficulty).toFormat() }/>
</Box> </DetailsInfoItem.Value>
</DetailsInfoItem>
{ data.total_difficulty && ( { data.total_difficulty && (
<DetailsInfoItem <>
title="Total difficulty" <DetailsInfoItem.Label
hint="Total difficulty of the chain until this block" hint="Total difficulty of the chain until this block"
> >
<Box whiteSpace="nowrap" overflow="hidden"> Total difficulty
</DetailsInfoItem.Label>
<DetailsInfoItem.Value overflow="hidden">
<HashStringShortenDynamic hash={ BigNumber(data.total_difficulty).toFormat() }/> <HashStringShortenDynamic hash={ BigNumber(data.total_difficulty).toFormat() }/>
</Box> </DetailsInfoItem.Value>
</DetailsInfoItem> </>
) } ) }
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
<DetailsInfoItem <DetailsInfoItem.Label
title="Hash"
hint="The SHA256 hash of the block" hint="The SHA256 hash of the block"
flexWrap="nowrap"
> >
<Box overflow="hidden"> Hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
<Box overflow="hidden" >
<HashStringShortenDynamic hash={ data.hash }/> <HashStringShortenDynamic hash={ data.hash }/>
</Box> </Box>
<CopyToClipboard text={ data.hash }/> <CopyToClipboard text={ data.hash }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
{ data.height > 0 && ( { data.height > 0 && (
<DetailsInfoItem <>
title="Parent hash" <DetailsInfoItem.Label
hint="The hash of the block from which this block was generated" hint="The hash of the block from which this block was generated"
flexWrap="nowrap"
> >
Parent hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
<LinkInternal <LinkInternal
href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(data.height - 1) } }) } href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(data.height - 1) } }) }
overflow="hidden" overflow="hidden"
...@@ -523,22 +596,21 @@ const BlockDetails = ({ query }: Props) => { ...@@ -523,22 +596,21 @@ const BlockDetails = ({ query }: Props) => {
/> />
</LinkInternal> </LinkInternal>
<CopyToClipboard text={ data.parent_hash }/> <CopyToClipboard text={ data.parent_hash }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ /* api doesn't support state root yet */ }
{ /* <DetailsInfoItem
title="State root"
hint="The root of the state trie"
>
<Text wordBreak="break-all" whiteSpace="break-spaces">{ data.state_root }</Text>
</DetailsInfoItem> */ }
{ !config.UI.views.block.hiddenFields?.nonce && ( { !config.UI.views.block.hiddenFields?.nonce && (
<DetailsInfoItem <>
title="Nonce" <DetailsInfoItem.Label
hint="Block nonce is a value used during mining to demonstrate proof of work for a block" hint="Block nonce is a value used during mining to demonstrate proof of work for a block"
> >
Nonce
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.nonce } { data.nonce }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
</> </>
) } ) }
......
...@@ -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,30 +33,41 @@ const BlockDetailsBlobInfo = ({ data }: Props) => { ...@@ -33,30 +33,41 @@ 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."
> >
Blob gas price
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ BigNumber(data.blob_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text> <Text>{ BigNumber(data.blob_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre"> <Text variant="secondary" whiteSpace="pre">
{ space }({ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei }) { space }({ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text> </Text>
</DetailsInfoItem> </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"
> >
Blob gas used
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ BigNumber(data.blob_gas_used).toFormat() }</Text> <Text>{ BigNumber(data.blob_gas_used).toFormat() }</Text>
</DetailsInfoItem> </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` }
> >
Blob burnt fees
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" mr={ 2 }/> <IconSvg name="flame" boxSize={ 5 } color="gray.500" mr={ 2 }/>
{ burntBlobFees.dividedBy(WEI).toFixed() } { currencyUnits.ether } { burntBlobFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
{ !blobFees.isEqualTo(ZERO) && ( { !blobFees.isEqualTo(ZERO) && (
...@@ -66,18 +77,23 @@ const BlockDetailsBlobInfo = ({ data }: Props) => { ...@@ -66,18 +77,23 @@ const BlockDetailsBlobInfo = ({ data }: Props) => {
</div> </div>
</Tooltip> </Tooltip>
) } ) }
</DetailsInfoItem> </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."
> >
Excess blob gas
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ BigNumber(data.excess_blob_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text> <Text>{ BigNumber(data.excess_blob_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre"> <Text variant="secondary" whiteSpace="pre">
{ space }({ BigNumber(data.excess_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei }) { space }({ BigNumber(data.excess_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text> </Text>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
</> </>
......
...@@ -8,7 +8,7 @@ import { route } from 'nextjs-routes'; ...@@ -8,7 +8,7 @@ import { route } from 'nextjs-routes';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
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';
import NftEntity from 'ui/shared/entities/nft/NftEntity'; import NftEntity from 'ui/shared/entities/nft/NftEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -30,25 +30,32 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -30,25 +30,32 @@ const NameDomainDetails = ({ query }: Props) => {
return ( return (
<Grid columnGap={ 8 } rowGap={ 3 } templateColumns={{ base: 'minmax(0, 1fr)', lg: 'max-content minmax(728px, auto)' }}> <Grid columnGap={ 8 } rowGap={ 3 } templateColumns={{ base: 'minmax(0, 1fr)', lg: 'max-content minmax(728px, auto)' }}>
{ query.data?.registration_date && ( { query.data?.registration_date && (
<DetailsInfoItem <>
title="Registration date" <DetailsInfoItem.Label
hint="The date the name was registered" hint="The date the name was registered"
isLoading={ isLoading } isLoading={ isLoading }
> >
Registration date
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 }/> <IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 }/>
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="20px"> <Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="20px">
{ dayjs(query.data.registration_date).format('llll') } { dayjs(query.data.registration_date).format('llll') }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ query.data?.expiry_date && ( { query.data?.expiry_date && (
<DetailsInfoItem <>
title="Expiration date" <DetailsInfoItem.Label
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
hint="The date the name expires, upon which there is a 90 day grace period for the owner to renew. After the 90 days, the name is released to the market" hint="The date the name expires, upon which there is a 90 day grace period for the owner to renew. After the 90 days, the name is released to the market"
isLoading={ isLoading } isLoading={ isLoading }
display="inline-block"
> >
Expiration date
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 } mt="-2px"/> <IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 } mt="-2px"/>
{ hasExpired && ( { hasExpired && (
<> <>
...@@ -65,13 +72,19 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -65,13 +72,19 @@ const NameDomainDetails = ({ query }: Props) => {
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline"> <Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline">
<NameDomainExpiryStatus date={ query.data?.expiry_date }/> <NameDomainExpiryStatus date={ query.data?.expiry_date }/>
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ query.data?.registrant && ( { query.data?.registrant && (
<DetailsInfoItem <>
title="Registrant" <DetailsInfoItem.Label
hint="The account that owns the domain name and has the rights to edit its ownership and records" hint="The account that owns the domain name and has the rights to edit its ownership and records"
isLoading={ isLoading } isLoading={ isLoading }
>
Registrant
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
columnGap={ 2 } columnGap={ 2 }
flexWrap="nowrap" flexWrap="nowrap"
> >
...@@ -88,13 +101,19 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -88,13 +101,19 @@ const NameDomainDetails = ({ query }: Props) => {
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/> <IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
</LinkInternal> </LinkInternal>
</Tooltip> </Tooltip>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ query.data?.owner && ( { query.data?.owner && (
<DetailsInfoItem <>
title="Owner" <DetailsInfoItem.Label
hint="The account that owns the rights to edit the records of this domain name" hint="The account that owns the rights to edit the records of this domain name"
isLoading={ isLoading } isLoading={ isLoading }
>
Owner
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
columnGap={ 2 } columnGap={ 2 }
flexWrap="nowrap" flexWrap="nowrap"
> >
...@@ -111,13 +130,19 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -111,13 +130,19 @@ const NameDomainDetails = ({ query }: Props) => {
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/> <IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
</LinkInternal> </LinkInternal>
</Tooltip> </Tooltip>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ query.data?.wrapped_owner && ( { query.data?.wrapped_owner && (
<DetailsInfoItem <>
title="Manager" <DetailsInfoItem.Label
hint="Owner of this NFT domain in NameWrapper contract" hint="Owner of this NFT domain in NameWrapper contract"
isLoading={ isLoading } isLoading={ isLoading }
>
Manager
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
columnGap={ 2 } columnGap={ 2 }
flexWrap="nowrap" flexWrap="nowrap"
> >
...@@ -134,25 +159,36 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -134,25 +159,36 @@ const NameDomainDetails = ({ query }: Props) => {
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/> <IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
</LinkInternal> </LinkInternal>
</Tooltip> </Tooltip>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ query.data?.tokens.map((token) => ( { query.data?.tokens.map((token) => (
<DetailsInfoItem <React.Fragment key={ token.type }>
key={ token.type } <DetailsInfoItem.Label
title={ token.type === 'WRAPPED_DOMAIN_TOKEN' ? 'Wrapped token ID' : 'Token ID' }
hint={ `The ${ token.type === 'WRAPPED_DOMAIN_TOKEN' ? 'wrapped ' : '' }token ID of this domain name NFT` } hint={ `The ${ token.type === 'WRAPPED_DOMAIN_TOKEN' ? 'wrapped ' : '' }token ID of this domain name NFT` }
isLoading={ isLoading } isLoading={ isLoading }
>
{ token.type === 'WRAPPED_DOMAIN_TOKEN' ? 'Wrapped token ID' : 'Token ID' }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
wordBreak="break-all" wordBreak="break-all"
whiteSpace="pre-wrap" whiteSpace="pre-wrap"
> >
<NftEntity hash={ token.contract_hash } id={ token.id } isLoading={ isLoading } noIcon/> <NftEntity hash={ token.contract_hash } id={ token.id } isLoading={ isLoading } noIcon/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</React.Fragment>
)) } )) }
{ otherAddresses.length > 0 && ( { otherAddresses.length > 0 && (
<DetailsInfoItem <>
title="Other addresses" <DetailsInfoItem.Label
hint="Other cryptocurrency addresses added to this domain name" hint="Other cryptocurrency addresses added to this domain name"
isLoading={ isLoading } isLoading={ isLoading }
>
Other addresses
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexDir="column" flexDir="column"
alignItems="flex-start" alignItems="flex-start"
> >
...@@ -167,7 +203,8 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -167,7 +203,8 @@ const NameDomainDetails = ({ query }: Props) => {
/> />
</Flex> </Flex>
)) } )) }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
</Grid> </Grid>
); );
......
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,12 +26,16 @@ const DetailsActionsWrapper = ({ children, isLoading, type }: Props) => { ...@@ -25,12 +26,16 @@ 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 }
position="relative"
isLoading={ isLoading } isLoading={ isLoading }
>
<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 <ContainerWithScrollY
containerId={ TX_ACTIONS_BLOCK_ID } containerId={ TX_ACTIONS_BLOCK_ID }
...@@ -44,7 +49,9 @@ const DetailsActionsWrapper = ({ children, isLoading, type }: Props) => { ...@@ -44,7 +49,9 @@ const DetailsActionsWrapper = ({ children, isLoading, type }: Props) => {
> >
{ children } { children }
</ContainerWithScrollY> </ContainerWithScrollY>
</DetailsInfoItem> </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 }
className={ className }
py={ 1 }
lineHeight={{ base: 5, lg: 6 }}
_notFirst={{ mt: { base: 3, lg: 0 } }}
>
<Flex columnGap={ 2 } alignItems="flex-start"> <Flex columnGap={ 2 } alignItems="flex-start">
{ hint && <Hint label={ hint } isLoading={ isLoading }/> } { hint && <Hint label={ hint } isLoading={ isLoading } my={{ lg: '2px' }}/> }
<Skeleton isLoaded={ !isLoading }> <Skeleton isLoaded={ !isLoading } fontWeight={{ base: 700, lg: 500 }}>
<Text fontWeight={{ base: 700, lg: 500 }}> { children }
{ title }
{ note && <Text fontWeight={ 500 } variant="secondary" fontSize="xs" className="note" align="right">{ note }</Text> }
</Text>
</Skeleton> </Skeleton>
</Flex> </Flex>
</GridItem> </GridItem>
);
});
interface ValueProps {
children: React.ReactNode;
className?: string;
}
const Value = chakra(({ children, className }: ValueProps) => {
return (
<GridItem <GridItem
className={ className }
display="flex" display="flex"
alignItems="center" alignItems="center"
flexWrap="wrap" flexWrap="wrap"
rowGap={ 3 } rowGap={ 3 }
pl={{ base: 7, lg: 0 }} pl={{ base: 7, lg: 0 }}
py={{ base: 1, lg: 2 }} py={ 1 }
lineHeight={ 5 } lineHeight={{ base: 5, lg: 6 }}
whiteSpace="nowrap" whiteSpace="nowrap"
{ ...styles }
> >
{ children } { children }
</GridItem> </GridItem>
</>
); );
}; });
export default DetailsInfoItem; 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 }
> >
Sponsored
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AdBanner isLoading={ isLoading }/> <AdBanner isLoading={ isLoading }/>
</DetailsInfoItem> </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
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block"> <Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
<span>{ `$${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }` }</span> <span>{ `$${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }` }</span>
</Skeleton> </Skeleton>
</DetailsInfoItem> </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
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block"> <Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
<span>{ `$${ BigNumber(marketCap).toFormat() }` }</span> <span>{ `$${ BigNumber(marketCap).toFormat() }` }</span>
</Skeleton> </Skeleton>
</DetailsInfoItem> </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
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } minW={ 6 }> <Skeleton isLoaded={ !tokenQuery.isPlaceholderData } minW={ 6 }>
{ decimals } { decimals }
</Skeleton> </Skeleton>
</DetailsInfoItem> </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>
<DetailsInfoItem.Value
py="1px"
> >
<AppActionButton data={ appActionData } height="30px" source="NFT collection"/> <AppActionButton data={ appActionData } height="30px" source="NFT collection"/>
</DetailsInfoItem> </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,12 +23,15 @@ const TokenNftMarketplaces = ({ hash, id, isLoading, appActionData, source, isAc ...@@ -23,12 +23,15 @@ 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>
<DetailsInfoItem.Value
py={ (appActionData && isActionButtonExperiment) ? '1px' : '6px' }
> >
<Skeleton isLoaded={ !isLoading } display="flex" columnGap={ 3 } flexWrap="wrap" alignItems="center"> <Skeleton isLoaded={ !isLoading } display="flex" columnGap={ 3 } flexWrap="wrap" alignItems="center">
{ config.UI.views.nft.marketplaces.map((item) => { { config.UI.views.nft.marketplaces.map((item) => {
...@@ -56,7 +59,8 @@ const TokenNftMarketplaces = ({ hash, id, isLoading, appActionData, source, isAc ...@@ -56,7 +59,8 @@ const TokenNftMarketplaces = ({ hash, id, isLoading, appActionData, source, isAc
</> </>
) } ) }
</Skeleton> </Skeleton>
</DetailsInfoItem> </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 } isLoading={ isLoading }
> >
Owner
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressEntity <AddressEntity
address={ data.owner } address={ data.owner }
isLoading={ isLoading } isLoading={ isLoading }
/> />
</DetailsInfoItem> </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>
<DetailsInfoItem.Value py="1px">
<AppActionButton data={ appActionData } height="30px" source="NFT item"/> <AppActionButton data={ appActionData } height="30px" source="NFT item"/>
</DetailsInfoItem> </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 } isLoading={ addressQuery.isPlaceholderData }
> >
Creator
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressEntity <AddressEntity
address={ creatorAddress } address={ creatorAddress }
isLoading={ addressQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData }
/> />
</DetailsInfoItem> </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"
isLoading={ isLoading }
>
Name
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
whiteSpace="normal" whiteSpace="normal"
wordBreak="break-word" wordBreak="break-word"
isLoading={ isLoading }
> >
<Skeleton isLoaded={ !isLoading }> <Skeleton isLoaded={ !isLoading }>
{ metadata.name } { metadata.name }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ metadata?.description && ( { metadata?.description && (
<DetailsInfoItem <>
title="Description" <DetailsInfoItem.Label
hint="NFT description" hint="NFT description"
isLoading={ isLoading }
>
Description
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
whiteSpace="normal" whiteSpace="normal"
wordBreak="break-word" wordBreak="break-word"
isLoading={ isLoading }
> >
<Skeleton isLoaded={ !isLoading }> <Skeleton isLoaded={ !isLoading }>
{ metadata.description } { metadata.description }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ metadata?.attributes && ( { metadata?.attributes && (
<DetailsInfoItem <>
title="Attributes" <DetailsInfoItem.Label
hint="NFT attributes" hint="NFT attributes"
whiteSpace="normal"
isLoading={ isLoading } isLoading={ isLoading }
> >
<Grid gap={ 2 } templateColumns="repeat(auto-fill,minmax(160px, 1fr))" w="100%"> 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 }/>) } { metadata.attributes.map((attribute, index) => <Item key={ index } data={ attribute } isLoading={ isLoading }/>) }
</Grid> </Grid>
</DetailsInfoItem> </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,11 +37,14 @@ const TokenInstanceTransfersCount = ({ hash, id, onClick }: Props) => { ...@@ -37,11 +37,14 @@ 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 }
> >
Transfers
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !transfersCountQuery.isPlaceholderData } display="inline-block"> <Skeleton isLoaded={ !transfersCountQuery.isPlaceholderData } display="inline-block">
<LinkInternal <LinkInternal
href={ url } href={ url }
...@@ -50,7 +53,8 @@ const TokenInstanceTransfersCount = ({ hash, id, onClick }: Props) => { ...@@ -50,7 +53,8 @@ const TokenInstanceTransfersCount = ({ hash, id, onClick }: Props) => {
{ transfersCountQuery.data.transfers_count.toLocaleString() } { transfersCountQuery.data.transfers_count.toLocaleString() }
</LinkInternal> </LinkInternal>
</Skeleton> </Skeleton>
</DetailsInfoItem> </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,10 +14,13 @@ const TxAllowedPeekers = ({ items }: Props) => { ...@@ -14,10 +14,13 @@ 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"
> >
Allowed peekers
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Flex flexDir="column" rowGap={ 3 } w="100%"> <Flex flexDir="column" rowGap={ 3 } w="100%">
{ items { items
.slice(0, isExpanded ? undefined : CUT_LENGTH) .slice(0, isExpanded ? undefined : CUT_LENGTH)
...@@ -34,7 +37,8 @@ const TxAllowedPeekers = ({ items }: Props) => { ...@@ -34,7 +37,8 @@ const TxAllowedPeekers = ({ items }: Props) => {
{ isExpanded ? 'Hide' : 'Show all' } { isExpanded ? 'Hide' : 'Show all' }
</Link> </Link>
) } ) }
</DetailsInfoItem> </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"
> >
Transaction fee
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue <CurrencyValue
value={ data.fee.value } value={ data.fee.value }
currency={ currencyUnits.ether } currency={ currencyUnits.ether }
flexWrap="wrap" flexWrap="wrap"
/> />
</DetailsInfoItem> </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"
> >
Gas limit
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ BigNumber(data.gas_limit).toFormat() } { BigNumber(data.gas_limit).toFormat() }
</DetailsInfoItem> </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"
> >
Decoded input data
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<LogDecodedInputData data={ data.decoded_input }/> <LogDecodedInputData data={ data.decoded_input }/>
</DetailsInfoItem> </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,14 +30,17 @@ const TxDetailsBurntFees = ({ data, isLoading }: Props) => { ...@@ -30,14 +30,17 @@ 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 } isLoading={ isLoading }
> >
Burnt fees
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/> <IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/>
<CurrencyValue <CurrencyValue
value={ value.toString() } value={ value.toString() }
...@@ -47,7 +50,8 @@ const TxDetailsBurntFees = ({ data, isLoading }: Props) => { ...@@ -47,7 +50,8 @@ const TxDetailsBurntFees = ({ data, isLoading }: Props) => {
ml={ 2 } ml={ 2 }
isLoading={ isLoading } isLoading={ isLoading }
/> />
</DetailsInfoItem> </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 }
> >
Fee per gas
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } mr={ 1 }> <Skeleton isLoaded={ !isLoading } mr={ 1 }>
{ BigNumber(txFee).dividedBy(10 ** config.chain.currency.decimals).dividedBy(gasUsed).toFixed() } { BigNumber(txFee).dividedBy(10 ** config.chain.currency.decimals).dividedBy(gasUsed).toFixed() }
{ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ currencyUnits.ether }` } { config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ currencyUnits.ether }` }
</Skeleton> </Skeleton>
</DetailsInfoItem> </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 }
> >
Gas price
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } mr={ 1 }> <Skeleton isLoaded={ !isLoading } mr={ 1 }>
{ BigNumber(gasPrice).dividedBy(WEI).toFixed() } { currencyUnits.ether } { BigNumber(gasPrice).dividedBy(WEI).toFixed() } { currencyUnits.ether }
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary"> <Skeleton isLoaded={ !isLoading } color="text_secondary">
<span>({ BigNumber(gasPrice).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</span> <span>({ BigNumber(gasPrice).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</span>
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
); );
}; };
......
...@@ -3,17 +3,20 @@ import React from 'react'; ...@@ -3,17 +3,20 @@ 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>
<DetailsInfoItem.Value>
{ {
[ [
typeof type === 'number' && ( typeof type === 'number' && (
...@@ -43,7 +46,8 @@ const TxDetailsOther = ({ nonce, type, position }: Props) => { ...@@ -43,7 +46,8 @@ const TxDetailsOther = ({ nonce, type, position }: Props) => {
</> </>
)) ))
} }
</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,12 +40,13 @@ const TxDetailsTokenTransfers = ({ data, txHash, isOverflow }: Props) => { ...@@ -40,12 +40,13 @@ 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"
> >
{ title }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value position="relative">
<Flex <Flex
flexDirection="column" flexDirection="column"
alignItems="flex-start" alignItems="flex-start"
...@@ -55,7 +56,8 @@ const TxDetailsTokenTransfers = ({ data, txHash, isOverflow }: Props) => { ...@@ -55,7 +56,8 @@ const TxDetailsTokenTransfers = ({ data, txHash, isOverflow }: Props) => {
> >
{ items.map((item, index) => <TxDetailsTokenTransfer key={ index } data={ item }/>) } { items.map((item, index) => <TxDetailsTokenTransfer key={ index } data={ item }/>) }
</Flex> </Flex>
</DetailsInfoItem> </DetailsInfoItem.Value>
</React.Fragment>
); );
}) } }) }
{ isOverflow && ( { isOverflow && (
......
...@@ -29,7 +29,7 @@ import { currencyUnits } from 'lib/units'; ...@@ -29,7 +29,7 @@ import { currencyUnits } from 'lib/units';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
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 DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem'; import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import DetailsTimestamp from 'ui/shared/DetailsTimestamp'; import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
...@@ -125,12 +125,14 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -125,12 +125,14 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
<TxSocketAlert status={ socketStatus }/> <TxSocketAlert status={ socketStatus }/>
</GridItem> </GridItem>
) } ) }
<DetailsInfoItem
title="Transaction hash" <DetailsInfoItem.Label
hint="Unique character string (TxID) assigned to every verified transaction" hint="Unique character string (TxID) assigned to every verified transaction"
flexWrap="nowrap"
isLoading={ isLoading } isLoading={ isLoading }
> >
Transaction hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
{ data.status === null && <Spinner mr={ 2 } size="sm" flexShrink={ 0 }/> } { data.status === null && <Spinner mr={ 2 } size="sm" flexShrink={ 0 }/> }
<Skeleton isLoaded={ !isLoading } overflow="hidden"> <Skeleton isLoaded={ !isLoading } overflow="hidden">
<HashStringShortenDynamic hash={ data.hash }/> <HashStringShortenDynamic hash={ data.hash }/>
...@@ -143,29 +145,36 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -143,29 +145,36 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
<Box display="none" flexShrink={ 0 } id="meta-suites__tx-explorer-link"/> <Box display="none" flexShrink={ 0 } id="meta-suites__tx-explorer-link"/>
</> </>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title={ <DetailsInfoItem.Label
hint="Current transaction state: Success, Failed (Error), or Pending (In Process)"
isLoading={ isLoading }
>
{
rollupFeature.isEnabled && (rollupFeature.type === 'zkEvm' || rollupFeature.type === 'zkSync') ? rollupFeature.isEnabled && (rollupFeature.type === 'zkEvm' || rollupFeature.type === 'zkSync') ?
'L2 status and method' : 'L2 status and method' :
'Status and method' 'Status and method'
} }
hint="Current transaction state: Success, Failed (Error), or Pending (In Process)" </DetailsInfoItem.Label>
isLoading={ isLoading } <DetailsInfoItem.Value>
>
<TxStatus status={ data.status } errorText={ data.status === 'error' ? data.result : undefined } isLoading={ isLoading }/> <TxStatus status={ data.status } errorText={ data.status === 'error' ? data.result : undefined } isLoading={ isLoading }/>
{ data.method && ( { data.method && (
<Tag colorScheme={ data.method === 'Multicall' ? 'teal' : 'gray' } isLoading={ isLoading } isTruncated ml={ 3 }> <Tag colorScheme={ data.method === 'Multicall' ? 'teal' : 'gray' } isLoading={ isLoading } isTruncated ml={ 3 }>
{ data.method } { data.method }
</Tag> </Tag>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && data.op_withdrawals && data.op_withdrawals.length > 0 && { rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && data.op_withdrawals && data.op_withdrawals.length > 0 &&
!config.UI.views.tx.hiddenFields?.L1_status && ( !config.UI.views.tx.hiddenFields?.L1_status && (
<DetailsInfoItem <>
title="Withdrawal status" <DetailsInfoItem.Label
hint="Detailed status progress of the transaction" hint="Detailed status progress of the transaction"
> >
Withdrawal status
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Flex flexDir="column" rowGap={ 2 }> <Flex flexDir="column" rowGap={ 2 }>
{ data.op_withdrawals.map((withdrawal) => ( { data.op_withdrawals.map((withdrawal) => (
<Box key={ withdrawal.nonce }> <Box key={ withdrawal.nonce }>
...@@ -180,39 +189,58 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -180,39 +189,58 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</Box> </Box>
)) } )) }
</Flex> </Flex>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.zkevm_status && !config.UI.views.tx.hiddenFields?.L1_status && ( { data.zkevm_status && !config.UI.views.tx.hiddenFields?.L1_status && (
<DetailsInfoItem <>
title="Confirmation status" <DetailsInfoItem.Label
hint="Status of the transaction confirmation path to L1" hint="Status of the transaction confirmation path to L1"
isLoading={ isLoading } isLoading={ isLoading }
> >
Confirmation status
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<VerificationSteps currentStep={ data.zkevm_status } steps={ ZKEVM_L2_TX_STATUSES } isLoading={ isLoading }/> <VerificationSteps currentStep={ data.zkevm_status } steps={ ZKEVM_L2_TX_STATUSES } isLoading={ isLoading }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.revert_reason && ( { data.revert_reason && (
<DetailsInfoItem <>
title="Revert reason" <DetailsInfoItem.Label
hint="The revert reason of the transaction" hint="The revert reason of the transaction"
> >
Revert reason
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<TxRevertReason { ...data.revert_reason }/> <TxRevertReason { ...data.revert_reason }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.zksync && !config.UI.views.tx.hiddenFields?.L1_status && ( { data.zksync && !config.UI.views.tx.hiddenFields?.L1_status && (
<DetailsInfoItem <>
title="L1 status" <DetailsInfoItem.Label
hint="Status is the short interpretation of the batch lifecycle" hint="Status is the short interpretation of the batch lifecycle"
isLoading={ isLoading } isLoading={ isLoading }
> >
L1 status
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES } currentStep={ data.zksync.status } isLoading={ isLoading }/> <VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES } currentStep={ data.zksync.status } isLoading={ isLoading }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<DetailsInfoItem
title="Block" <DetailsInfoItem.Label
hint="Block number containing the transaction" hint="Block number containing the transaction"
isLoading={ isLoading } isLoading={ isLoading }
> >
Block
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.block === null ? { data.block === null ?
<Text>Pending</Text> : ( <Text>Pending</Text> : (
<BlockEntity <BlockEntity
...@@ -229,39 +257,53 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -229,39 +257,53 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</Skeleton> </Skeleton>
</> </>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
{ data.zkevm_batch_number && !config.UI.views.tx.hiddenFields?.batch && ( { data.zkevm_batch_number && !config.UI.views.tx.hiddenFields?.batch && (
<DetailsInfoItem <>
title="Tx batch" <DetailsInfoItem.Label
hint="Batch index for this transaction" hint="Batch index for this transaction"
isLoading={ isLoading } isLoading={ isLoading }
> >
Tx batch
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<BatchEntityL2 <BatchEntityL2
isLoading={ isLoading } isLoading={ isLoading }
number={ data.zkevm_batch_number } number={ data.zkevm_batch_number }
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.zksync && !config.UI.views.tx.hiddenFields?.batch && ( { data.zksync && !config.UI.views.tx.hiddenFields?.batch && (
<DetailsInfoItem <>
title="Batch" <DetailsInfoItem.Label
hint="Batch number" hint="Batch number"
isLoading={ isLoading } isLoading={ isLoading }
> >
Batch
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.zksync.batch_number ? ( { data.zksync.batch_number ? (
<BatchEntityL2 <BatchEntityL2
isLoading={ isLoading } isLoading={ isLoading }
number={ data.zksync.batch_number } number={ data.zksync.batch_number }
/> />
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> } ) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.timestamp && ( { data.timestamp && (
<DetailsInfoItem <>
title="Timestamp" <DetailsInfoItem.Label
hint="Date & time of transaction inclusion, including length of time for confirmation" hint="Date & time of transaction inclusion, including length of time for confirmation"
isLoading={ isLoading } isLoading={ isLoading }
> >
Timestamp
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<DetailsTimestamp timestamp={ data.timestamp } isLoading={ isLoading }/> <DetailsTimestamp timestamp={ data.timestamp } isLoading={ isLoading }/>
{ data.confirmation_duration && ( { data.confirmation_duration && (
<> <>
...@@ -271,35 +313,44 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -271,35 +313,44 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</Skeleton> </Skeleton>
</> </>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.execution_node && ( { data.execution_node && (
<DetailsInfoItem <>
title="Kettle" <DetailsInfoItem.Label
hint="Node that carried out the confidential computation" hint="Node that carried out the confidential computation"
isLoading={ isLoading } isLoading={ isLoading }
> >
Kettle
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressEntity <AddressEntity
address={ data.execution_node } address={ data.execution_node }
href={ route({ pathname: '/txs/kettle/[hash]', query: { hash: data.execution_node.hash } }) } href={ route({ pathname: '/txs/kettle/[hash]', query: { hash: data.execution_node.hash } }) }
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.allowed_peekers && data.allowed_peekers.length > 0 && ( { data.allowed_peekers && data.allowed_peekers.length > 0 && (
<TxAllowedPeekers items={ data.allowed_peekers }/> <TxAllowedPeekers items={ data.allowed_peekers }/>
) } ) }
<DetailsSponsoredItem isLoading={ isLoading }/> <DetailsSponsoredItem isLoading={ isLoading }/>
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
<TxDetailsActions hash={ data.hash } actions={ data.actions } isTxDataLoading={ isLoading }/> <TxDetailsActions hash={ data.hash } actions={ data.actions } isTxDataLoading={ isLoading }/>
<DetailsInfoItem <DetailsInfoItem.Label
title="From"
hint="Address (external or contract) sending the transaction" hint="Address (external or contract) sending the transaction"
isLoading={ isLoading } isLoading={ isLoading }
columnGap={ 3 }
> >
From
</DetailsInfoItem.Label>
<DetailsInfoItem.Value columnGap={ 3 }>
<AddressEntity <AddressEntity
address={ data.from } address={ data.from }
isLoading={ isLoading } isLoading={ isLoading }
...@@ -310,11 +361,15 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -310,11 +361,15 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
{ addressFromTags } { addressFromTags }
</Flex> </Flex>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title={ data.to?.is_contract ? 'Interacted with contract' : 'To' } <DetailsInfoItem.Label
hint="Address (external or contract) receiving the transaction" hint="Address (external or contract) receiving the transaction"
isLoading={ isLoading } isLoading={ isLoading }
>
{ data.to?.is_contract ? 'Interacted with contract' : 'To' }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexWrap={{ base: 'wrap', lg: 'nowrap' }} flexWrap={{ base: 'wrap', lg: 'nowrap' }}
columnGap={ 3 } columnGap={ 3 }
> >
...@@ -351,47 +406,56 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -351,47 +406,56 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
) : ( ) : (
<span>[ Contract creation ]</span> <span>[ Contract creation ]</span>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
{ data.token_transfers && <TxDetailsTokenTransfers data={ data.token_transfers } txHash={ data.hash } isOverflow={ data.token_transfers_overflow }/> } { data.token_transfers && <TxDetailsTokenTransfers data={ data.token_transfers } txHash={ data.hash } isOverflow={ data.token_transfers_overflow }/> }
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
{ data.zkevm_sequence_hash && ( { data.zkevm_sequence_hash && (
<DetailsInfoItem <>
title="Sequence tx hash" <DetailsInfoItem.Label
flexWrap="nowrap"
isLoading={ isLoading } isLoading={ isLoading }
> >
Sequence tx hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
<Skeleton isLoaded={ !isLoading } overflow="hidden"> <Skeleton isLoaded={ !isLoading } overflow="hidden">
<HashStringShortenDynamic hash={ data.zkevm_sequence_hash }/> <HashStringShortenDynamic hash={ data.zkevm_sequence_hash }/>
</Skeleton> </Skeleton>
<CopyToClipboard text={ data.zkevm_sequence_hash } isLoading={ isLoading }/> <CopyToClipboard text={ data.zkevm_sequence_hash } isLoading={ isLoading }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.zkevm_verify_hash && ( { data.zkevm_verify_hash && (
<DetailsInfoItem <>
title="Verify tx hash" <DetailsInfoItem.Label
flexWrap="nowrap"
isLoading={ isLoading } isLoading={ isLoading }
> >
Verify tx hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
<Skeleton isLoaded={ !isLoading } overflow="hidden"> <Skeleton isLoaded={ !isLoading } overflow="hidden">
<HashStringShortenDynamic hash={ data.zkevm_verify_hash }/> <HashStringShortenDynamic hash={ data.zkevm_verify_hash }/>
</Skeleton> </Skeleton>
<CopyToClipboard text={ data.zkevm_verify_hash } isLoading={ isLoading }/> <CopyToClipboard text={ data.zkevm_verify_hash } isLoading={ isLoading }/>
</DetailsInfoItem.Value>
</DetailsInfoItem> </>
) } ) }
{ (data.zkevm_batch_number || data.zkevm_verify_hash) && <DetailsInfoItemDivider/> } { (data.zkevm_batch_number || data.zkevm_verify_hash) && <DetailsInfoItemDivider/> }
{ !config.UI.views.tx.hiddenFields?.value && ( { !config.UI.views.tx.hiddenFields?.value && (
<DetailsInfoItem <>
title="Value" <DetailsInfoItem.Label
hint="Value sent in the native token (and USD) if applicable" hint="Value sent in the native token (and USD) if applicable"
isLoading={ isLoading } isLoading={ isLoading }
> >
Value
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue <CurrencyValue
value={ data.value } value={ data.value }
currency={ currencyUnits.ether } currency={ currencyUnits.ether }
...@@ -399,14 +463,19 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -399,14 +463,19 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
isLoading={ isLoading } isLoading={ isLoading }
flexWrap="wrap" flexWrap="wrap"
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ !config.UI.views.tx.hiddenFields?.tx_fee && ( { !config.UI.views.tx.hiddenFields?.tx_fee && (
<DetailsInfoItem <>
title="Transaction fee" <DetailsInfoItem.Label
hint={ data.blob_gas_used ? 'Transaction fee without blob fee' : 'Total transaction fee' } hint={ data.blob_gas_used ? 'Transaction fee without blob fee' : 'Total transaction fee' }
isLoading={ isLoading } isLoading={ isLoading }
> >
Transaction fee
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.stability_fee ? ( { data.stability_fee ? (
<TxFeeStability data={ data.stability_fee } isLoading={ isLoading }/> <TxFeeStability data={ data.stability_fee } isLoading={ isLoading }/>
) : ( ) : (
...@@ -418,27 +487,31 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -418,27 +487,31 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
isLoading={ isLoading } isLoading={ isLoading }
/> />
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<TxDetailsGasPrice gasPrice={ data.gas_price } isLoading={ isLoading }/> <TxDetailsGasPrice gasPrice={ data.gas_price } isLoading={ isLoading }/>
<TxDetailsFeePerGas txFee={ data.fee.value } gasUsed={ data.gas_used } isLoading={ isLoading }/> <TxDetailsFeePerGas txFee={ data.fee.value } gasUsed={ data.gas_used } isLoading={ isLoading }/>
<DetailsInfoItem <DetailsInfoItem.Label
title="Gas usage & limit by txn"
hint="Actual gas amount used by the transaction" hint="Actual gas amount used by the transaction"
isLoading={ isLoading } isLoading={ isLoading }
> >
Gas usage & limit by txn
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading }>{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton> <Skeleton isLoaded={ !isLoading }>{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
<TextSeparator/> <TextSeparator/>
<Skeleton isLoaded={ !isLoading }>{ BigNumber(data.gas_limit).toFormat() }</Skeleton> <Skeleton isLoaded={ !isLoading }>{ BigNumber(data.gas_limit).toFormat() }</Skeleton>
<Utilization ml={ 4 } value={ BigNumber(data.gas_used || 0).dividedBy(BigNumber(data.gas_limit)).toNumber() } isLoading={ isLoading }/> <Utilization ml={ 4 } value={ BigNumber(data.gas_used || 0).dividedBy(BigNumber(data.gas_limit)).toNumber() } isLoading={ isLoading }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
{ !config.UI.views.tx.hiddenFields?.gas_fees && { !config.UI.views.tx.hiddenFields?.gas_fees &&
(data.base_fee_per_gas || data.max_fee_per_gas || data.max_priority_fee_per_gas) && ( (data.base_fee_per_gas || data.max_fee_per_gas || data.max_priority_fee_per_gas) && (
<DetailsInfoItem <>
title={ `Gas fees (${ currencyUnits.gwei })` } <DetailsInfoItem.Label
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
hint={ ` hint={ `
Base Fee refers to the network Base Fee at the time of the block, Base Fee refers to the network Base Fee at the time of the block,
...@@ -447,6 +520,9 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -447,6 +520,9 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
` } ` }
isLoading={ isLoading } isLoading={ isLoading }
> >
{ `Gas fees (${ currencyUnits.gwei })` }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.base_fee_per_gas && ( { data.base_fee_per_gas && (
<Skeleton isLoaded={ !isLoading }> <Skeleton isLoaded={ !isLoading }>
<Text as="span" fontWeight="500">Base: </Text> <Text as="span" fontWeight="500">Base: </Text>
...@@ -467,56 +543,79 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -467,56 +543,79 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
<Text fontWeight="600" as="span">{ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text> <Text fontWeight="600" as="span">{ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Skeleton> </Skeleton>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<TxDetailsBurntFees data={ data } isLoading={ isLoading }/> <TxDetailsBurntFees data={ data } isLoading={ isLoading }/>
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && ( { rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && (
<> <>
{ data.l1_gas_used && ( { data.l1_gas_used && (
<DetailsInfoItem <>
title="L1 gas used by txn" <DetailsInfoItem.Label
hint="L1 gas used by transaction" hint="L1 gas used by transaction"
isLoading={ isLoading } isLoading={ isLoading }
> >
L1 gas used by txn
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ BigNumber(data.l1_gas_used).toFormat() }</Text> <Text>{ BigNumber(data.l1_gas_used).toFormat() }</Text>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.l1_gas_price && ( { data.l1_gas_price && (
<DetailsInfoItem <>
title="L1 gas price" <DetailsInfoItem.Label
hint="L1 gas price" hint="L1 gas price"
isLoading={ isLoading } isLoading={ isLoading }
> >
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>
</>
) } ) }
{ data.l1_fee && ( { data.l1_fee && (
<DetailsInfoItem <>
title="L1 fee" <DetailsInfoItem.Label
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
hint={ `L1 Data Fee which is used to cover the L1 "security" cost from the batch submission mechanism. In combination with L2 execution fee, L1 fee makes the total amount of fees that a transaction pays.` } hint={ `L1 Data Fee which is used to cover the L1 "security" cost from the batch submission mechanism. In combination with L2 execution fee, L1 fee makes the total amount of fees that a transaction pays.` }
isLoading={ isLoading } isLoading={ isLoading }
> >
L1 fee
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue <CurrencyValue
value={ data.l1_fee } value={ data.l1_fee }
currency={ currencyUnits.ether } currency={ currencyUnits.ether }
exchangeRate={ data.exchange_rate } exchangeRate={ data.exchange_rate }
flexWrap="wrap" flexWrap="wrap"
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.l1_fee_scalar && ( { data.l1_fee_scalar && (
<DetailsInfoItem <>
title="L1 fee scalar" <DetailsInfoItem.Label
hint="A Dynamic overhead (fee scalar) premium, which serves as a buffer in case L1 prices rapidly increase." hint="A Dynamic overhead (fee scalar) premium, which serves as a buffer in case L1 prices rapidly increase."
isLoading={ isLoading } isLoading={ isLoading }
> >
L1 fee scalar
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ data.l1_fee_scalar }</Text> <Text>{ data.l1_fee_scalar }</Text>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
</> </>
) } ) }
<GridItem colSpan={{ base: undefined, lg: 2 }}> <GridItem colSpan={{ base: undefined, lg: 2 }}>
<Element name="TxInfo__cutLink"> <Element name="TxInfo__cutLink">
<Skeleton isLoaded={ !isLoading } mt={ 6 } display="inline-block"> <Skeleton isLoaded={ !isLoading } mt={ 6 } display="inline-block">
...@@ -532,16 +631,20 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -532,16 +631,20 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</Skeleton> </Skeleton>
</Element> </Element>
</GridItem> </GridItem>
{ isExpanded && ( { isExpanded && (
<> <>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/> <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
{ (data.blob_gas_used || data.max_fee_per_blob_gas || data.blob_gas_price) && ( { (data.blob_gas_used || data.max_fee_per_blob_gas || data.blob_gas_price) && (
<> <>
{ data.blob_gas_used && data.blob_gas_price && ( { data.blob_gas_used && data.blob_gas_price && (
<DetailsInfoItem <>
title="Blob fee" <DetailsInfoItem.Label
hint="Blob fee for this transaction" hint="Blob fee for this transaction"
> >
Blob fee
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue <CurrencyValue
value={ BigNumber(data.blob_gas_used).multipliedBy(data.blob_gas_price).toString() } value={ BigNumber(data.blob_gas_used).multipliedBy(data.blob_gas_price).toString() }
currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether } currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether }
...@@ -549,21 +652,31 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -549,21 +652,31 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
flexWrap="wrap" flexWrap="wrap"
isLoading={ isLoading } isLoading={ isLoading }
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.blob_gas_used && ( { data.blob_gas_used && (
<DetailsInfoItem <>
title="Blob gas usage" <DetailsInfoItem.Label
hint="Amount of gas used by the blobs in this transaction" hint="Amount of gas used by the blobs in this transaction"
> >
Blob gas usage
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ BigNumber(data.blob_gas_used).toFormat() } { BigNumber(data.blob_gas_used).toFormat() }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ (data.max_fee_per_blob_gas || data.blob_gas_price) && ( { (data.max_fee_per_blob_gas || data.blob_gas_price) && (
<DetailsInfoItem <>
title={ `Blob gas fees (${ currencyUnits.gwei })` } <DetailsInfoItem.Label
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this transaction` } hint={ `Amount of ${ currencyUnits.ether } used for blobs in this transaction` }
> >
{ `Blob gas fees (${ currencyUnits.gwei })` }
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.blob_gas_price && ( { data.blob_gas_price && (
<Text fontWeight="600" as="span">{ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() }</Text> <Text fontWeight="600" as="span">{ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
) } ) }
...@@ -574,26 +687,37 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => { ...@@ -574,26 +687,37 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
<Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text> <Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</> </>
) } ) }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
</> </>
) } ) }
<TxDetailsOther nonce={ data.nonce } type={ data.type } position={ data.position }/> <TxDetailsOther nonce={ data.nonce } type={ data.type } position={ data.position }/>
<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"
> >
Decoded input data
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<LogDecodedInputData data={ data.decoded_input }/> <LogDecodedInputData data={ data.decoded_input }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.zksync && <ZkSyncL2TxnBatchHashesInfo data={ data.zksync } isLoading={ isLoading }/> } { data.zksync && <ZkSyncL2TxnBatchHashesInfo data={ data.zksync } isLoading={ isLoading }/> }
</> </>
) } ) }
......
...@@ -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"
> >
Call data
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<RawInputData hex={ callData } rightSlot={ toggler }/> <RawInputData hex={ callData } rightSlot={ toggler }/>
</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 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"
>
Decoded call data
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexDir={{ base: 'column', lg: 'row' }} flexDir={{ base: 'column', lg: 'row' }}
alignItems={{ base: 'flex-start', lg: 'center' }} alignItems={{ base: 'flex-start', lg: 'center' }}
> >
<LogDecodedInputData data={ callData } rightSlot={ toggler }/> <LogDecodedInputData data={ callData } rightSlot={ toggler }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
); );
}; };
......
...@@ -15,7 +15,7 @@ import { currencyUnits } from 'lib/units'; ...@@ -15,7 +15,7 @@ import { currencyUnits } from 'lib/units';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError'; import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
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 AddressStringOrParam from 'ui/shared/entities/address/AddressStringOrParam'; import AddressStringOrParam from 'ui/shared/entities/address/AddressStringOrParam';
...@@ -68,78 +68,108 @@ const UserOpDetails = ({ query }: Props) => { ...@@ -68,78 +68,108 @@ const UserOpDetails = ({ query }: Props) => {
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(min-content, 220px) minmax(0, 1fr)' }} templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(min-content, 220px) minmax(0, 1fr)' }}
overflow="hidden" overflow="hidden"
> >
<DetailsInfoItem <DetailsInfoItem.Label
title="User operation hash"
hint="Unique character string assigned to every User operation" hint="Unique character string assigned to every User operation"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
User operation hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData } overflow="hidden"> <Skeleton isLoaded={ !isPlaceholderData } overflow="hidden">
<UserOpEntity hash={ data.hash } noIcon noLink noCopy={ false }/> <UserOpEntity hash={ data.hash } noIcon noLink noCopy={ false }/>
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Sender" <DetailsInfoItem.Label
hint="The address of the smart contract account" hint="The address of the smart contract account"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Sender
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressStringOrParam address={ data.sender } isLoading={ isPlaceholderData }/> <AddressStringOrParam address={ data.sender } isLoading={ isPlaceholderData }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Status" <DetailsInfoItem.Label
hint="Current User operation state" hint="Current User operation state"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Status
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<UserOpStatus status={ data.status } isLoading={ isPlaceholderData }/> <UserOpStatus status={ data.status } isLoading={ isPlaceholderData }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
{ data.revert_reason && ( { data.revert_reason && (
<DetailsInfoItem <>
title="Revert reason" <DetailsInfoItem.Label
hint="The revert reason of the User operation" hint="The revert reason of the User operation"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
>
Revert reason
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
wordBreak="break-all" wordBreak="break-all"
whiteSpace="normal" whiteSpace="normal"
> >
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ data.revert_reason } { data.revert_reason }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.timestamp && ( { data.timestamp && (
<DetailsInfoItem <>
title="Timestamp" <DetailsInfoItem.Label
hint="Date and time of User operation" hint="Date and time of User operation"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Timestamp
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/> <DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ !config.UI.views.tx.hiddenFields?.tx_fee && ( { !config.UI.views.tx.hiddenFields?.tx_fee && (
<DetailsInfoItem <>
title="Fee" <DetailsInfoItem.Label
hint="Total User operation fee" hint="Total User operation fee"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Fee
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<CurrencyValue <CurrencyValue
value={ data.fee } value={ data.fee }
currency={ currencyUnits.ether } currency={ currencyUnits.ether }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<DetailsInfoItem
title="Gas limit" <DetailsInfoItem.Label
hint="Gas limit for the User operation" hint="Gas limit for the User operation"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Gas limit
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ BigNumber(data.gas).toFormat() } { BigNumber(data.gas).toFormat() }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Gas used" <DetailsInfoItem.Label
hint="Actual gas amount used by the User operation" hint="Actual gas amount used by the User operation"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Gas used
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ BigNumber(data.gas_used).toFormat() } { BigNumber(data.gas_used).toFormat() }
</Skeleton> </Skeleton>
...@@ -149,28 +179,37 @@ const UserOpDetails = ({ query }: Props) => { ...@@ -149,28 +179,37 @@ const UserOpDetails = ({ query }: Props) => {
value={ BigNumber(data.gas_used).dividedBy(BigNumber(data.gas)).toNumber() } value={ BigNumber(data.gas_used).dividedBy(BigNumber(data.gas)).toNumber() }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Transaction hash" <DetailsInfoItem.Label
hint="Hash of the transaction this User operation belongs to" hint="Hash of the transaction this User operation belongs to"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Transaction hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<TxEntity hash={ data.transaction_hash } isLoading={ isPlaceholderData } noCopy={ false }/> <TxEntity hash={ data.transaction_hash } isLoading={ isPlaceholderData } noCopy={ false }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Block" <DetailsInfoItem.Label
hint="Block number containing this User operation" hint="Block number containing this User operation"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Block
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<BlockEntity number={ data.block_number } isLoading={ isPlaceholderData }/> <BlockEntity number={ data.block_number } isLoading={ isPlaceholderData }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Entry point" <DetailsInfoItem.Label
hint="Contract that executes bundles of User operations" hint="Contract that executes bundles of User operations"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Entry point
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressStringOrParam address={ data.entry_point } isLoading={ isPlaceholderData }/> <AddressStringOrParam address={ data.entry_point } isLoading={ isPlaceholderData }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
{ config.features.txInterpretation.isEnabled && <UserOpDetailsActions hash={ data.hash } isUserOpDataLoading={ isPlaceholderData }/> } { config.features.txInterpretation.isEnabled && <UserOpDetailsActions hash={ data.hash } isUserOpDataLoading={ isPlaceholderData }/> }
...@@ -195,112 +234,158 @@ const UserOpDetails = ({ query }: Props) => { ...@@ -195,112 +234,158 @@ const UserOpDetails = ({ 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="Call gas limit"
hint="Gas limit for execution phase" hint="Gas limit for execution phase"
> >
Call gas limit
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ BigNumber(data.call_gas_limit).toFormat() } { BigNumber(data.call_gas_limit).toFormat() }
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Verification gas limit" <DetailsInfoItem.Label
hint="Gas limit for verification phase" hint="Gas limit for verification phase"
> >
Verification gas limit
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ BigNumber(data.verification_gas_limit).toFormat() } { BigNumber(data.verification_gas_limit).toFormat() }
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Pre-verification gas" <DetailsInfoItem.Label
hint="Gas to compensate the bundler" hint="Gas to compensate the bundler"
> >
Pre-verification gas
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ BigNumber(data.pre_verification_gas).toFormat() } { BigNumber(data.pre_verification_gas).toFormat() }
</DetailsInfoItem> </DetailsInfoItem.Value>
{ !config.UI.views.tx.hiddenFields?.gas_fees && ( { !config.UI.views.tx.hiddenFields?.gas_fees && (
<> <>
<DetailsInfoItem <DetailsInfoItem.Label
title="Max fee per gas"
hint="Maximum fee per gas " hint="Maximum fee per gas "
> >
Max fee per gas
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ BigNumber(data.max_fee_per_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text> <Text>{ BigNumber(data.max_fee_per_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre"> <Text variant="secondary" whiteSpace="pre">
{ space }({ BigNumber(data.max_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei }) { space }({ BigNumber(data.max_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text> </Text>
</DetailsInfoItem><DetailsInfoItem </DetailsInfoItem.Value>
title="Max priority fee per gas"
<DetailsInfoItem.Label
hint="Maximum priority fee per gas" hint="Maximum priority fee per gas"
> >
Max priority fee per gas
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Text>{ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text> <Text>{ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre"> <Text variant="secondary" whiteSpace="pre">
{ space }({ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei }) { space }({ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text> </Text>
</DetailsInfoItem> </DetailsInfoItem.Value>
</> </>
) } ) }
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
{ data.aggregator && ( { data.aggregator && (
<DetailsInfoItem <>
title="Aggregator" <DetailsInfoItem.Label
hint="Helper contract to validate an aggregated signature" hint="Helper contract to validate an aggregated signature"
> >
Aggregator
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressStringOrParam address={ data.aggregator }/> <AddressStringOrParam address={ data.aggregator }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.aggregator_signature && ( { data.aggregator_signature && (
<DetailsInfoItem <>
title="Aggregator signature" <DetailsInfoItem.Label
hint="Aggregator signature" hint="Aggregator signature"
> >
Aggregator signature
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
{ data.aggregator_signature } { data.aggregator_signature }
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<DetailsInfoItem
title="Bundler" <DetailsInfoItem.Label
hint="A node (block builder) that handles User operations" hint="A node (block builder) that handles User operations"
> >
Bundler
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressStringOrParam address={ data.bundler }/> <AddressStringOrParam address={ data.bundler }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
{ data.factory && ( { data.factory && (
<DetailsInfoItem <>
title="Factory" <DetailsInfoItem.Label
hint="Smart contract that deploys new smart contract wallets for users" hint="Smart contract that deploys new smart contract wallets for users"
> >
Factory
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressStringOrParam address={ data.factory }/> <AddressStringOrParam address={ data.factory }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
{ data.paymaster && ( { data.paymaster && (
<DetailsInfoItem <>
title="Paymaster" <DetailsInfoItem.Label
hint="Contract to sponsor the gas fees for User operations" hint="Contract to sponsor the gas fees for User operations"
> >
Paymaster
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<AddressStringOrParam address={ data.paymaster }/> <AddressStringOrParam address={ data.paymaster }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
</>
) } ) }
<DetailsInfoItem
title="Sponsor type" <DetailsInfoItem.Label
hint="Type of the gas fees sponsor" hint="Type of the gas fees sponsor"
> >
Sponsor type
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<UserOpSponsorType sponsorType={ data.sponsor_type }/> <UserOpSponsorType sponsorType={ data.sponsor_type }/>
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
<DetailsInfoItem <DetailsInfoItem.Label
title="Signature"
hint="Used to validate a User operation along with the nonce during verification" hint="Used to validate a User operation along with the nonce during verification"
>
Signature
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
wordBreak="break-all" wordBreak="break-all"
whiteSpace="normal" whiteSpace="normal"
> >
{ data.signature } { data.signature }
</DetailsInfoItem> </DetailsInfoItem.Value>
<DetailsInfoItem
title="Nonce" <DetailsInfoItem.Label
hint="Anti-replay protection; also used as the salt for first-time account creation" hint="Anti-replay protection; also used as the salt for first-time account creation"
>
Nonce
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
wordBreak="break-all" wordBreak="break-all"
whiteSpace="normal" whiteSpace="normal"
> >
{ data.nonce } { data.nonce }
</DetailsInfoItem> </DetailsInfoItem.Value>
<UserOpCallData data={ data }/> <UserOpCallData data={ data }/>
......
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