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
@@ -10,7 +10,7 @@ import AddressCounterItem from 'ui/address/details/AddressCounterItem';
|
||||
import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning';
|
||||
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
|
||||
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
|
||||
@@ -92,61 +92,80 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
|
||||
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden"
|
||||
>
|
||||
<AddressNameInfo data={ data } isLoading={ addressQuery.isPlaceholderData }/>
|
||||
|
||||
{ data.is_contract && data.creation_tx_hash && data.creator_address_hash && (
|
||||
<DetailsInfoItem
|
||||
title="Creator"
|
||||
hint="Transaction and address of creation"
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
>
|
||||
<AddressEntity
|
||||
address={{ hash: data.creator_address_hash }}
|
||||
truncation="constant"
|
||||
noIcon
|
||||
/>
|
||||
<Text whiteSpace="pre"> at txn </Text>
|
||||
<TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon noCopy={ false }/>
|
||||
</DetailsInfoItem>
|
||||
) }
|
||||
{ data.is_contract && data.implementation_address && (
|
||||
<DetailsInfoItem
|
||||
title="Implementation"
|
||||
hint="Implementation address of the proxy contract"
|
||||
columnGap={ 1 }
|
||||
>
|
||||
<AddressEntity
|
||||
address={{ hash: data.implementation_address, name: data.implementation_name, is_contract: true }}
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Transaction and address of creation"
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
noIcon
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Creator
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressEntity
|
||||
address={{ hash: data.creator_address_hash }}
|
||||
truncation="constant"
|
||||
noIcon
|
||||
/>
|
||||
<Text whiteSpace="pre"> at txn </Text>
|
||||
<TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon noCopy={ false }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.is_contract && data.implementation_address && (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Implementation address of the proxy contract"
|
||||
>
|
||||
Implementation
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressEntity
|
||||
address={{ hash: data.implementation_address, name: data.implementation_name, is_contract: true }}
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
noIcon
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<AddressBalance data={ data } isLoading={ addressQuery.isPlaceholderData }/>
|
||||
|
||||
{ data.has_tokens && (
|
||||
<DetailsInfoItem
|
||||
title="Tokens"
|
||||
hint="All tokens in the account and total value"
|
||||
alignSelf="center"
|
||||
py={ 0 }
|
||||
>
|
||||
{ addressQuery.data ? <TokenSelect onClick={ handleCounterItemClick }/> : <Box py="6px">0</Box> }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="All tokens in the account and total value"
|
||||
>
|
||||
Tokens
|
||||
</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)) && (
|
||||
<DetailsInfoItem
|
||||
title="Net worth"
|
||||
hint="Total net worth in USD of all tokens for the address"
|
||||
alignSelf="center"
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
>
|
||||
<AddressNetWorth addressData={ addressQuery.data } addressHash={ addressHash } isLoading={ addressQuery.isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Total net worth in USD of all tokens for the address"
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
>
|
||||
Net worth
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value alignSelf="center">
|
||||
<AddressNetWorth addressData={ addressQuery.data } addressHash={ addressHash } isLoading={ addressQuery.isPlaceholderData }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
)
|
||||
}
|
||||
<DetailsInfoItem
|
||||
title="Transactions"
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Number of transactions related to this address"
|
||||
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
|
||||
>
|
||||
Transactions
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ addressQuery.data ? (
|
||||
<AddressCounterItem
|
||||
prop="transactions_count"
|
||||
@@ -158,78 +177,97 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
|
||||
/>
|
||||
) :
|
||||
0 }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.has_token_transfers && (
|
||||
<DetailsInfoItem
|
||||
title="Transfers"
|
||||
hint="Number of transfers to/from this address"
|
||||
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
|
||||
>
|
||||
{ addressQuery.data ? (
|
||||
<AddressCounterItem
|
||||
prop="token_transfers_count"
|
||||
query={ countersQuery }
|
||||
address={ data.hash }
|
||||
onClick={ handleCounterItemClick }
|
||||
isAddressQueryLoading={ addressQuery.isPlaceholderData }
|
||||
isDegradedData={ addressQuery.isDegradedData }
|
||||
/>
|
||||
) :
|
||||
0 }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Number of transfers to/from this address"
|
||||
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
|
||||
>
|
||||
Transfers
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ addressQuery.data ? (
|
||||
<AddressCounterItem
|
||||
prop="token_transfers_count"
|
||||
query={ countersQuery }
|
||||
address={ data.hash }
|
||||
onClick={ handleCounterItemClick }
|
||||
isAddressQueryLoading={ addressQuery.isPlaceholderData }
|
||||
isDegradedData={ addressQuery.isDegradedData }
|
||||
/>
|
||||
) :
|
||||
0 }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ countersQuery.data?.gas_usage_count && (
|
||||
<DetailsInfoItem
|
||||
title="Gas used"
|
||||
hint="Gas used by the address"
|
||||
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
|
||||
>
|
||||
{ addressQuery.data ? (
|
||||
<AddressCounterItem
|
||||
prop="gas_usage_count"
|
||||
query={ countersQuery }
|
||||
address={ data.hash }
|
||||
onClick={ handleCounterItemClick }
|
||||
isAddressQueryLoading={ addressQuery.isPlaceholderData }
|
||||
isDegradedData={ addressQuery.isDegradedData }
|
||||
/>
|
||||
) :
|
||||
0 }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Gas used by the address"
|
||||
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
|
||||
>
|
||||
Gas used
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ addressQuery.data ? (
|
||||
<AddressCounterItem
|
||||
prop="gas_usage_count"
|
||||
query={ countersQuery }
|
||||
address={ data.hash }
|
||||
onClick={ handleCounterItemClick }
|
||||
isAddressQueryLoading={ addressQuery.isPlaceholderData }
|
||||
isDegradedData={ addressQuery.isDegradedData }
|
||||
/>
|
||||
) :
|
||||
0 }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.has_validated_blocks && (
|
||||
<DetailsInfoItem
|
||||
title="Blocks validated"
|
||||
hint="Number of blocks validated by this validator"
|
||||
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
|
||||
>
|
||||
{ addressQuery.data ? (
|
||||
<AddressCounterItem
|
||||
prop="validations_count"
|
||||
query={ countersQuery }
|
||||
address={ data.hash }
|
||||
onClick={ handleCounterItemClick }
|
||||
isAddressQueryLoading={ addressQuery.isPlaceholderData }
|
||||
isDegradedData={ addressQuery.isDegradedData }
|
||||
/>
|
||||
) :
|
||||
0 }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Number of blocks validated by this validator"
|
||||
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
|
||||
>
|
||||
Blocks validated
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ addressQuery.data ? (
|
||||
<AddressCounterItem
|
||||
prop="validations_count"
|
||||
query={ countersQuery }
|
||||
address={ data.hash }
|
||||
onClick={ handleCounterItemClick }
|
||||
isAddressQueryLoading={ addressQuery.isPlaceholderData }
|
||||
isDegradedData={ addressQuery.isDegradedData }
|
||||
/>
|
||||
) :
|
||||
0 }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.block_number_balance_updated_at && (
|
||||
<DetailsInfoItem
|
||||
title="Last balance update"
|
||||
hint="Block number in which the address was updated"
|
||||
alignSelf="center"
|
||||
py={{ base: '2px', lg: 1 }}
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
>
|
||||
<BlockEntity
|
||||
number={ data.block_number_balance_updated_at }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Block number in which the address was updated"
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Last balance update
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<BlockEntity
|
||||
number={ data.block_number_balance_updated_at }
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<DetailsSponsoredItem isLoading={ addressQuery.isPlaceholderData }/>
|
||||
</Grid>
|
||||
</>
|
||||
|
||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@@ -10,7 +10,7 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
|
||||
import useSocketMessage from 'lib/socket/useSocketMessage';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import CurrencyValue from 'ui/shared/CurrencyValue';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import NativeTokenIcon from 'ui/shared/NativeTokenIcon';
|
||||
|
||||
interface Props {
|
||||
@@ -66,25 +66,27 @@ const AddressBalance = ({ data, isLoading }: Props) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title={ `${ currencyUnits.ether } balance` }
|
||||
hint={ `Address balance in ${ currencyUnits.ether }. Doesn't include ERC20, ERC721 and ERC1155 tokens` }
|
||||
flexWrap="nowrap"
|
||||
alignSelf="center"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<NativeTokenIcon boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
|
||||
<CurrencyValue
|
||||
value={ data.coin_balance || '0' }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
decimals={ String(config.chain.currency.decimals) }
|
||||
currency={ currencyUnits.ether }
|
||||
accuracyUsd={ 2 }
|
||||
accuracy={ 8 }
|
||||
flexWrap="wrap"
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint={ `${ currencyUnits.ether } balance` }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Balance
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value alignSelf="center" flexWrap="nowrap">
|
||||
<NativeTokenIcon boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
|
||||
<CurrencyValue
|
||||
value={ data.coin_balance || '0' }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
decimals={ String(config.chain.currency.decimals) }
|
||||
currency={ currencyUnits.ether }
|
||||
accuracyUsd={ 2 }
|
||||
accuracy={ 8 }
|
||||
flexWrap="wrap"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
|
||||
import type { Address } from 'types/api/address';
|
||||
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
|
||||
|
||||
interface Props {
|
||||
@@ -14,46 +14,58 @@ interface Props {
|
||||
const AddressNameInfo = ({ data, isLoading }: Props) => {
|
||||
if (data.token) {
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Token name"
|
||||
hint="Token name and symbol"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<TokenEntity
|
||||
token={ data.token }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Token name and symbol"
|
||||
isLoading={ isLoading }
|
||||
noIcon
|
||||
noCopy
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Token name
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<TokenEntity
|
||||
token={ data.token }
|
||||
isLoading={ isLoading }
|
||||
noIcon
|
||||
noCopy
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (data.is_contract && data.name) {
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Contract name"
|
||||
hint="The name found in the source code of the Contract"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
{ data.name }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The name found in the source code of the Contract"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Contract name
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
{ data.name }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (data.name) {
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Validator name"
|
||||
hint="The name of the validator"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
{ data.name }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The name of the validator"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Validator name
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
{ data.name }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ const TokenSelect = ({ onClick }: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex columnGap={ 3 } mt={{ base: '6px', lg: 0 }}>
|
||||
<Flex columnGap={ 3 } mt={{ base: 1, lg: 0 }}>
|
||||
{ isMobile ?
|
||||
<TokenSelectMobile data={ data } isLoading={ tokensIsFetching === 1 }/> :
|
||||
<TokenSelectDesktop data={ data } isLoading={ tokensIsFetching === 1 }/>
|
||||
|
||||
@@ -4,7 +4,7 @@ import React from 'react';
|
||||
import type { Blob } from 'types/api/blobs';
|
||||
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
|
||||
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
@@ -30,53 +30,73 @@ const BlobInfo = ({ data, isLoading }: Props) => {
|
||||
</Skeleton>
|
||||
</GridItem>
|
||||
) }
|
||||
|
||||
{ data.kzg_proof && (
|
||||
<DetailsInfoItem
|
||||
title="Proof"
|
||||
hint="Zero knowledge proof. Allows for quick verification of commitment"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
|
||||
{ data.kzg_proof }
|
||||
<CopyToClipboard text={ data.kzg_proof } isLoading={ isLoading }/>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Zero knowledge proof. Allows for quick verification of commitment"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Proof
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
|
||||
{ data.kzg_proof }
|
||||
<CopyToClipboard text={ data.kzg_proof } isLoading={ isLoading }/>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.kzg_commitment && (
|
||||
<DetailsInfoItem
|
||||
title="Commitment"
|
||||
hint="Commitment to the data in the blob"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
|
||||
{ data.kzg_commitment }
|
||||
<CopyToClipboard text={ data.kzg_commitment } isLoading={ isLoading }/>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Commitment to the data in the blob"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Commitment
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all" lineHeight={ 6 } my="-2px">
|
||||
{ data.kzg_commitment }
|
||||
<CopyToClipboard text={ data.kzg_commitment } isLoading={ isLoading }/>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.blob_data && (
|
||||
<DetailsInfoItem
|
||||
title="Size, bytes"
|
||||
hint="Blob size in bytes"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all">
|
||||
{ (data.blob_data.replace('0x', '').length / 2).toLocaleString() }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Blob size in bytes"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Size, bytes
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="pre-wrap" wordBreak="break-all">
|
||||
{ (data.blob_data.replace('0x', '').length / 2).toLocaleString() }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.blob_data && <DetailsInfoItemDivider/> }
|
||||
|
||||
{ data.transaction_hashes[0] && (
|
||||
<DetailsInfoItem
|
||||
title="Transaction hash"
|
||||
hint="Hash of the transaction with this blob"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<TxEntity hash={ data.transaction_hashes[0].transaction_hash } isLoading={ isLoading } noIcon noCopy={ false }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Hash of the transaction with this blob"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Transaction hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<TxEntity hash={ data.transaction_hashes[0].transaction_hash } isLoading={ isLoading } noIcon noCopy={ false }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<DetailsSponsoredItem isLoading={ isLoading }/>
|
||||
|
||||
{ data.blob_data && (
|
||||
|
||||
@@ -17,7 +17,7 @@ import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
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 DetailsTimestamp from 'ui/shared/DetailsTimestamp';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
@@ -163,11 +163,13 @@ const BlockDetails = ({ query }: Props) => {
|
||||
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(min-content, 200px) minmax(0, 1fr)' }}
|
||||
overflow="hidden"
|
||||
>
|
||||
<DetailsInfoItem
|
||||
title={ `${ blockTypeLabel } height` }
|
||||
<DetailsInfoItem.Label
|
||||
hint="The block height of a particular block is defined as the number of blocks preceding it in the blockchain"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
{ blockTypeLabel } height
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ data.height }
|
||||
</Skeleton>
|
||||
@@ -180,122 +182,151 @@ const BlockDetails = ({ query }: Props) => {
|
||||
isPrevDisabled={ data.height === 0 }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Size"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Size of the block in bytes"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Size
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ data.size.toLocaleString() }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Timestamp"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Date & time at which block was produced."
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Timestamp
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Transactions"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="The number of transactions in the block"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Transactions
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ txsNum }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ config.features.beaconChain.isEnabled && Boolean(data.withdrawals_count) && (
|
||||
<DetailsInfoItem
|
||||
title="Withdrawals"
|
||||
hint="The number of beacon withdrawals in the block"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'withdrawals' } }) }>
|
||||
{ data.withdrawals_count } withdrawal{ data.withdrawals_count === 1 ? '' : 's' }
|
||||
</LinkInternal>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The number of beacon withdrawals in the block"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Withdrawals
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'withdrawals' } }) }>
|
||||
{ data.withdrawals_count } withdrawal{ data.withdrawals_count === 1 ? '' : 's' }
|
||||
</LinkInternal>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync && !config.UI.views.block.hiddenFields?.batch && (
|
||||
<DetailsInfoItem
|
||||
title="Batch"
|
||||
hint="Batch number"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
{ data.zksync.batch_number ? (
|
||||
<BatchEntityL2
|
||||
isLoading={ isPlaceholderData }
|
||||
number={ data.zksync.batch_number }
|
||||
/>
|
||||
) : <Skeleton isLoaded={ !isPlaceholderData }>Pending</Skeleton> }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Batch number"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Batch
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.zksync.batch_number ?
|
||||
<BatchEntityL2 isLoading={ isPlaceholderData } number={ data.zksync.batch_number }/> :
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>Pending</Skeleton> }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync && !config.UI.views.block.hiddenFields?.L1_status && (
|
||||
<DetailsInfoItem
|
||||
title="Status"
|
||||
hint="Status is the short interpretation of the batch lifecycle"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
<VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES } currentStep={ data.zksync.status } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Status is the short interpretation of the batch lifecycle"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Status
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES } currentStep={ data.zksync.status } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ !config.UI.views.block.hiddenFields?.miner && (
|
||||
<DetailsInfoItem
|
||||
title={ verificationTitle }
|
||||
hint="A block producer who successfully included the block onto the blockchain"
|
||||
columnGap={ 1 }
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
<AddressEntity
|
||||
address={ data.miner }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="A block producer who successfully included the block onto the blockchain"
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
{ /* api doesn't return the block processing time yet */ }
|
||||
{ /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ }
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
{ verificationTitle }
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressEntity
|
||||
address={ data.miner }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ !rollupFeature.isEnabled && !totalReward.isEqualTo(ZERO) && !config.UI.views.block.hiddenFields?.total_reward && (
|
||||
<DetailsInfoItem
|
||||
title="Block reward"
|
||||
hint={
|
||||
`For each block, the ${ validatorTitle } is rewarded with a finite amount of ${ config.chain.currency.symbol || 'native token' }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint={
|
||||
`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`
|
||||
}
|
||||
columnGap={ 1 }
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ totalReward.dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
{ rewardBreakDown }
|
||||
</DetailsInfoItem>
|
||||
}
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Block reward
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value columnGap={ 1 }>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ totalReward.dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
{ rewardBreakDown }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.rewards
|
||||
?.filter(({ type }) => type !== 'Validator Reward' && type !== 'Miner Reward')
|
||||
.map(({ type, reward }) => (
|
||||
<DetailsInfoItem
|
||||
key={ type }
|
||||
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` }
|
||||
>
|
||||
{ BigNumber(reward).dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</DetailsInfoItem>
|
||||
<React.Fragment key={ type }>
|
||||
<DetailsInfoItem.Label
|
||||
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 }
|
||||
</DetailsInfoItem.Value>
|
||||
</React.Fragment>
|
||||
))
|
||||
}
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Gas used"
|
||||
<DetailsInfoItem.Label
|
||||
hint="The total gas amount used in the block and its percentage of gas filled in the block"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Gas used
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ BigNumber(data.gas_used || 0).toFormat() }
|
||||
</Skeleton>
|
||||
@@ -311,91 +342,105 @@ const BlockDetails = ({ query }: Props) => {
|
||||
<GasUsedToTargetRatio value={ data.gas_target_percentage } isLoading={ isPlaceholderData }/>
|
||||
</>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Gas limit"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Total gas limit provided by all transactions in the block"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Gas limit
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ BigNumber(data.gas_limit).toFormat() }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
{ data.minimum_gas_price && (
|
||||
<DetailsInfoItem
|
||||
title="Minimum gas price"
|
||||
hint="The minimum gas price a transaction should have in order to be included in this block"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ BigNumber(data.minimum_gas_price).dividedBy(GWEI).toFormat() } { currencyUnits.gwei }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
) }
|
||||
{ data.base_fee_per_gas && (
|
||||
<DetailsInfoItem
|
||||
title="Base fee per gas"
|
||||
hint="Minimum fee required per unit of gas. Fee adjusts based on network congestion"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
{ isPlaceholderData ? (
|
||||
<Skeleton isLoaded={ !isPlaceholderData } h="20px" maxW="380px" w="100%"/>
|
||||
) : (
|
||||
<>
|
||||
<Text>{ BigNumber(data.base_fee_per_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
|
||||
<Text variant="secondary" whiteSpace="pre">
|
||||
{ space }({ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
|
||||
</Text>
|
||||
</>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
) }
|
||||
{ !config.UI.views.block.hiddenFields?.burnt_fees && !burntFees.isEqualTo(ZERO) && (
|
||||
<DetailsInfoItem
|
||||
title="Burnt fees"
|
||||
hint={
|
||||
`Amount of ${ config.chain.currency.symbol || 'native token' } burned from transactions included in the block.
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
Equals Block Base Fee per Gas * Gas Used`
|
||||
}
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/>
|
||||
<Skeleton isLoaded={ !isPlaceholderData } ml={ 2 }>
|
||||
{ burntFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
{ !txFees.isEqualTo(ZERO) && (
|
||||
<Tooltip label="Burnt fees / Txn fees * 100%">
|
||||
<Box>
|
||||
<Utilization
|
||||
ml={ 4 }
|
||||
value={ burntFees.dividedBy(txFees).toNumber() }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
{ data.minimum_gas_price && (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The minimum gas price a transaction should have in order to be included in this block"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Minimum gas price
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ BigNumber(data.minimum_gas_price).dividedBy(GWEI).toFormat() } { currencyUnits.gwei }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.base_fee_per_gas && (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Minimum fee required per unit of gas. Fee adjusts based on network congestion"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Base fee per gas
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ isPlaceholderData ? (
|
||||
<Skeleton isLoaded={ !isPlaceholderData } h="20px" maxW="380px" w="100%"/>
|
||||
) : (
|
||||
<>
|
||||
<Text>{ BigNumber(data.base_fee_per_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
|
||||
<Text variant="secondary" whiteSpace="pre">
|
||||
{ space }({ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
|
||||
</Text>
|
||||
</>
|
||||
) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ !config.UI.views.block.hiddenFields?.burnt_fees && !burntFees.isEqualTo(ZERO) && (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint={
|
||||
`Amount of ${ config.chain.currency.symbol || 'native token' } burned from transactions included in the block.
|
||||
Equals Block Base Fee per Gas * Gas Used`
|
||||
}
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Burnt fees
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/>
|
||||
<Skeleton isLoaded={ !isPlaceholderData } ml={ 2 }>
|
||||
{ burntFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
{ !txFees.isEqualTo(ZERO) && (
|
||||
<Tooltip label="Burnt fees / Txn fees * 100%">
|
||||
<Box>
|
||||
<Utilization
|
||||
ml={ 4 }
|
||||
value={ burntFees.dividedBy(txFees).toNumber() }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.priority_fee !== null && BigNumber(data.priority_fee).gt(ZERO) && (
|
||||
<DetailsInfoItem
|
||||
title="Priority fee / Tip"
|
||||
hint="User-defined tips sent to validator for transaction priority/inclusion"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ BigNumber(data.priority_fee).dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="User-defined tips sent to validator for transaction priority/inclusion"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Priority fee / Tip
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ BigNumber(data.priority_fee).dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
</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 */ }
|
||||
<GridItem colSpan={{ base: undefined, lg: 2 }}>
|
||||
@@ -424,121 +469,148 @@ const BlockDetails = ({ query }: Props) => {
|
||||
{ !isPlaceholderData && <BlockDetailsBlobInfo data={ data }/> }
|
||||
|
||||
{ data.bitcoin_merged_mining_header && (
|
||||
<DetailsInfoItem
|
||||
title="Bitcoin merged mining header"
|
||||
hint="Merged-mining field: Bitcoin header"
|
||||
flexWrap="nowrap"
|
||||
alignSelf="flex-start"
|
||||
>
|
||||
<Box whiteSpace="nowrap" overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.bitcoin_merged_mining_header }/>
|
||||
</Box>
|
||||
<CopyToClipboard text={ data.bitcoin_merged_mining_header }/>
|
||||
</DetailsInfoItem>
|
||||
) }
|
||||
{ data.bitcoin_merged_mining_coinbase_transaction && (
|
||||
<DetailsInfoItem
|
||||
title="Bitcoin merged mining coinbase transaction"
|
||||
hint="Merged-mining field: Coinbase transaction"
|
||||
>
|
||||
<RawDataSnippet
|
||||
data={ data.bitcoin_merged_mining_coinbase_transaction }
|
||||
isLoading={ isPlaceholderData }
|
||||
showCopy={ false }
|
||||
textareaMaxHeight="100px"
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
) }
|
||||
{ data.bitcoin_merged_mining_merkle_proof && (
|
||||
<DetailsInfoItem
|
||||
title="Bitcoin merged mining Merkle proof"
|
||||
hint="Merged-mining field: Merkle proof"
|
||||
>
|
||||
<RawDataSnippet
|
||||
data={ data.bitcoin_merged_mining_merkle_proof }
|
||||
isLoading={ isPlaceholderData }
|
||||
showCopy={ false }
|
||||
textareaMaxHeight="100px"
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
) }
|
||||
{ data.hash_for_merged_mining && (
|
||||
<DetailsInfoItem
|
||||
title="Hash for merged mining"
|
||||
hint="Merged-mining field: Rootstock block header hash"
|
||||
flexWrap="nowrap"
|
||||
alignSelf="flex-start"
|
||||
>
|
||||
<Box whiteSpace="nowrap" overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.hash_for_merged_mining }/>
|
||||
</Box>
|
||||
<CopyToClipboard text={ data.hash_for_merged_mining }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Merged-mining field: Bitcoin header"
|
||||
>
|
||||
Bitcoin merged mining header
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexWrap="nowrap"
|
||||
alignSelf="flex-start"
|
||||
>
|
||||
<Box whiteSpace="nowrap" overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.bitcoin_merged_mining_header }/>
|
||||
</Box>
|
||||
<CopyToClipboard text={ data.bitcoin_merged_mining_header }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Difficulty"
|
||||
{ data.bitcoin_merged_mining_coinbase_transaction && (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Merged-mining field: Coinbase transaction"
|
||||
>
|
||||
Bitcoin merged mining coinbase transaction
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<RawDataSnippet
|
||||
data={ data.bitcoin_merged_mining_coinbase_transaction }
|
||||
isLoading={ isPlaceholderData }
|
||||
showCopy={ false }
|
||||
textareaMaxHeight="100px"
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.bitcoin_merged_mining_merkle_proof && (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Merged-mining field: Merkle proof"
|
||||
>
|
||||
Bitcoin merged mining Merkle proof
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<RawDataSnippet
|
||||
data={ data.bitcoin_merged_mining_merkle_proof }
|
||||
isLoading={ isPlaceholderData }
|
||||
showCopy={ false }
|
||||
textareaMaxHeight="100px"
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.hash_for_merged_mining && (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Merged-mining field: Rootstock block header hash"
|
||||
>
|
||||
Hash for merged mining
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexWrap="nowrap"
|
||||
alignSelf="flex-start"
|
||||
>
|
||||
<Box whiteSpace="nowrap" overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.hash_for_merged_mining }/>
|
||||
</Box>
|
||||
<CopyToClipboard text={ data.hash_for_merged_mining }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint={ `Block difficulty for ${ validatorTitle }, used to calibrate block generation time` }
|
||||
>
|
||||
<Box whiteSpace="nowrap" overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ BigNumber(data.difficulty).toFormat() }/>
|
||||
</Box>
|
||||
</DetailsInfoItem>
|
||||
Difficulty
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ BigNumber(data.difficulty).toFormat() }/>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.total_difficulty && (
|
||||
<DetailsInfoItem
|
||||
title="Total difficulty"
|
||||
hint="Total difficulty of the chain until this block"
|
||||
>
|
||||
<Box whiteSpace="nowrap" overflow="hidden">
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Total difficulty of the chain until this block"
|
||||
>
|
||||
Total difficulty
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ BigNumber(data.total_difficulty).toFormat() }/>
|
||||
</Box>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Hash"
|
||||
<DetailsInfoItem.Label
|
||||
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 }/>
|
||||
</Box>
|
||||
<CopyToClipboard text={ data.hash }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.height > 0 && (
|
||||
<DetailsInfoItem
|
||||
title="Parent hash"
|
||||
hint="The hash of the block from which this block was generated"
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<LinkInternal
|
||||
href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(data.height - 1) } }) }
|
||||
overflow="hidden"
|
||||
whiteSpace="nowrap"
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The hash of the block from which this block was generated"
|
||||
>
|
||||
<HashStringShortenDynamic
|
||||
hash={ data.parent_hash }
|
||||
/>
|
||||
</LinkInternal>
|
||||
<CopyToClipboard text={ data.parent_hash }/>
|
||||
</DetailsInfoItem>
|
||||
Parent hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value flexWrap="nowrap">
|
||||
<LinkInternal
|
||||
href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(data.height - 1) } }) }
|
||||
overflow="hidden"
|
||||
whiteSpace="nowrap"
|
||||
>
|
||||
<HashStringShortenDynamic
|
||||
hash={ data.parent_hash }
|
||||
/>
|
||||
</LinkInternal>
|
||||
<CopyToClipboard text={ data.parent_hash }/>
|
||||
</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 && (
|
||||
<DetailsInfoItem
|
||||
title="Nonce"
|
||||
hint="Block nonce is a value used during mining to demonstrate proof of work for a block"
|
||||
>
|
||||
{ data.nonce }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Block nonce is a value used during mining to demonstrate proof of work for a block"
|
||||
>
|
||||
Nonce
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.nonce }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
</>
|
||||
) }
|
||||
|
||||
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
@@ -7,7 +7,7 @@ import type { Block } from 'types/api/block';
|
||||
import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts';
|
||||
import { space } from 'lib/html-entities';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import Utilization from 'ui/shared/Utilization/Utilization';
|
||||
@@ -33,51 +33,67 @@ const BlockDetailsBlobInfo = ({ data }: Props) => {
|
||||
<>
|
||||
|
||||
{ data.blob_gas_price && (
|
||||
<DetailsInfoItem
|
||||
title="Blob gas price"
|
||||
// eslint-disable-next-line max-len
|
||||
hint="Price per unit of gas used for for blob deployment. Blob gas is independent of normal gas. Both gas prices can affect the priority of transaction execution."
|
||||
>
|
||||
<Text>{ BigNumber(data.blob_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
|
||||
<Text variant="secondary" whiteSpace="pre">
|
||||
{ space }({ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
|
||||
</Text>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
// eslint-disable-next-line max-len
|
||||
hint="Price per unit of gas used for for blob deployment. Blob gas is independent of normal gas. Both gas prices can affect the priority of transaction execution."
|
||||
>
|
||||
Blob gas price
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Text>{ BigNumber(data.blob_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
|
||||
<Text variant="secondary" whiteSpace="pre">
|
||||
{ space }({ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
|
||||
</Text>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
{ data.blob_gas_used && (
|
||||
<DetailsInfoItem
|
||||
title="Blob gas used"
|
||||
hint="Actual amount of gas used by the blobs in this block"
|
||||
>
|
||||
<Text>{ BigNumber(data.blob_gas_used).toFormat() }</Text>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Actual amount of gas used by the blobs in this block"
|
||||
>
|
||||
Blob gas used
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Text>{ BigNumber(data.blob_gas_used).toFormat() }</Text>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
{ !burntBlobFees.isEqualTo(ZERO) && (
|
||||
<DetailsInfoItem
|
||||
title="Blob burnt fees"
|
||||
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this block` }
|
||||
>
|
||||
<IconSvg name="flame" boxSize={ 5 } color="gray.500" mr={ 2 }/>
|
||||
{ burntBlobFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
{ !blobFees.isEqualTo(ZERO) && (
|
||||
<Tooltip label="Blob burnt fees / Txn fees * 100%">
|
||||
<div>
|
||||
<Utilization ml={ 4 } value={ burntBlobFees.dividedBy(blobFees).toNumber() }/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this block` }
|
||||
>
|
||||
Blob burnt fees
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<IconSvg name="flame" boxSize={ 5 } color="gray.500" mr={ 2 }/>
|
||||
{ burntBlobFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
{ !blobFees.isEqualTo(ZERO) && (
|
||||
<Tooltip label="Blob burnt fees / Txn fees * 100%">
|
||||
<div>
|
||||
<Utilization ml={ 4 } value={ burntBlobFees.dividedBy(blobFees).toNumber() }/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
{ data.excess_blob_gas && (
|
||||
<DetailsInfoItem
|
||||
title="Excess blob gas"
|
||||
hint="A running total of blob gas consumed in excess of the target, prior to the block."
|
||||
>
|
||||
<Text>{ BigNumber(data.excess_blob_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
|
||||
<Text variant="secondary" whiteSpace="pre">
|
||||
{ space }({ BigNumber(data.excess_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
|
||||
</Text>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="A running total of blob gas consumed in excess of the target, prior to the block."
|
||||
>
|
||||
Excess blob gas
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Text>{ BigNumber(data.excess_blob_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
|
||||
<Text variant="secondary" whiteSpace="pre">
|
||||
{ space }({ BigNumber(data.excess_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
|
||||
</Text>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
<DetailsInfoItemDivider/>
|
||||
</>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { route } from 'nextjs-routes';
|
||||
|
||||
import type { ResourceError } from 'lib/api/resources';
|
||||
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 NftEntity from 'ui/shared/entities/nft/NftEntity';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
@@ -30,144 +30,181 @@ const NameDomainDetails = ({ query }: Props) => {
|
||||
return (
|
||||
<Grid columnGap={ 8 } rowGap={ 3 } templateColumns={{ base: 'minmax(0, 1fr)', lg: 'max-content minmax(728px, auto)' }}>
|
||||
{ query.data?.registration_date && (
|
||||
<DetailsInfoItem
|
||||
title="Registration date"
|
||||
hint="The date the name was registered"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 }/>
|
||||
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="20px">
|
||||
{ dayjs(query.data.registration_date).format('llll') }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The date the name was registered"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Registration date
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 }/>
|
||||
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="20px">
|
||||
{ dayjs(query.data.registration_date).format('llll') }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ query.data?.expiry_date && (
|
||||
<DetailsInfoItem
|
||||
title="Expiration date"
|
||||
// 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"
|
||||
isLoading={ isLoading }
|
||||
display="inline-block"
|
||||
>
|
||||
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 } mt="-2px"/>
|
||||
{ hasExpired && (
|
||||
<>
|
||||
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px">
|
||||
{ dayjs(query.data.expiry_date).fromNow() }
|
||||
</Skeleton>
|
||||
<TextSeparator color="gray.500"/>
|
||||
</>
|
||||
) }
|
||||
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px">
|
||||
{ dayjs(query.data.expiry_date).format('llll') }
|
||||
</Skeleton>
|
||||
<TextSeparator color="gray.500"/>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline">
|
||||
<NameDomainExpiryStatus date={ query.data?.expiry_date }/>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
// 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"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Expiration date
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 } mt="-2px"/>
|
||||
{ hasExpired && (
|
||||
<>
|
||||
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px">
|
||||
{ dayjs(query.data.expiry_date).fromNow() }
|
||||
</Skeleton>
|
||||
<TextSeparator color="gray.500"/>
|
||||
</>
|
||||
) }
|
||||
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px">
|
||||
{ dayjs(query.data.expiry_date).format('llll') }
|
||||
</Skeleton>
|
||||
<TextSeparator color="gray.500"/>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline">
|
||||
<NameDomainExpiryStatus date={ query.data?.expiry_date }/>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ query.data?.registrant && (
|
||||
<DetailsInfoItem
|
||||
title="Registrant"
|
||||
hint="The account that owns the domain name and has the rights to edit its ownership and records"
|
||||
isLoading={ isLoading }
|
||||
columnGap={ 2 }
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<AddressEntity
|
||||
address={ query.data.registrant }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The account that owns the domain name and has the rights to edit its ownership and records"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
<Tooltip label="Lookup for related domain names">
|
||||
<LinkInternal
|
||||
flexShrink={ 0 }
|
||||
display="inline-flex"
|
||||
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.registrant.hash } }) }
|
||||
>
|
||||
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
|
||||
</LinkInternal>
|
||||
</Tooltip>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Registrant
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
columnGap={ 2 }
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<AddressEntity
|
||||
address={ query.data.registrant }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
<Tooltip label="Lookup for related domain names">
|
||||
<LinkInternal
|
||||
flexShrink={ 0 }
|
||||
display="inline-flex"
|
||||
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.registrant.hash } }) }
|
||||
>
|
||||
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
|
||||
</LinkInternal>
|
||||
</Tooltip>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ query.data?.owner && (
|
||||
<DetailsInfoItem
|
||||
title="Owner"
|
||||
hint="The account that owns the rights to edit the records of this domain name"
|
||||
isLoading={ isLoading }
|
||||
columnGap={ 2 }
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<AddressEntity
|
||||
address={ query.data.owner }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The account that owns the rights to edit the records of this domain name"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
<Tooltip label="Lookup for related domain names">
|
||||
<LinkInternal
|
||||
flexShrink={ 0 }
|
||||
display="inline-flex"
|
||||
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.owner.hash } }) }
|
||||
>
|
||||
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
|
||||
</LinkInternal>
|
||||
</Tooltip>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Owner
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
columnGap={ 2 }
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<AddressEntity
|
||||
address={ query.data.owner }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
<Tooltip label="Lookup for related domain names">
|
||||
<LinkInternal
|
||||
flexShrink={ 0 }
|
||||
display="inline-flex"
|
||||
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.owner.hash } }) }
|
||||
>
|
||||
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
|
||||
</LinkInternal>
|
||||
</Tooltip>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ query.data?.wrapped_owner && (
|
||||
<DetailsInfoItem
|
||||
title="Manager"
|
||||
hint="Owner of this NFT domain in NameWrapper contract"
|
||||
isLoading={ isLoading }
|
||||
columnGap={ 2 }
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<AddressEntity
|
||||
address={ query.data.wrapped_owner }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Owner of this NFT domain in NameWrapper contract"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
<Tooltip label="Lookup for related domain names">
|
||||
<LinkInternal
|
||||
flexShrink={ 0 }
|
||||
display="inline-flex"
|
||||
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.wrapped_owner.hash } }) }
|
||||
>
|
||||
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
|
||||
</LinkInternal>
|
||||
</Tooltip>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Manager
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
columnGap={ 2 }
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<AddressEntity
|
||||
address={ query.data.wrapped_owner }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
<Tooltip label="Lookup for related domain names">
|
||||
<LinkInternal
|
||||
flexShrink={ 0 }
|
||||
display="inline-flex"
|
||||
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.wrapped_owner.hash } }) }
|
||||
>
|
||||
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
|
||||
</LinkInternal>
|
||||
</Tooltip>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ query.data?.tokens.map((token) => (
|
||||
<DetailsInfoItem
|
||||
key={ token.type }
|
||||
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` }
|
||||
isLoading={ isLoading }
|
||||
wordBreak="break-all"
|
||||
whiteSpace="pre-wrap"
|
||||
>
|
||||
<NftEntity hash={ token.contract_hash } id={ token.id } isLoading={ isLoading } noIcon/>
|
||||
</DetailsInfoItem>
|
||||
<React.Fragment key={ token.type }>
|
||||
<DetailsInfoItem.Label
|
||||
hint={ `The ${ token.type === 'WRAPPED_DOMAIN_TOKEN' ? 'wrapped ' : '' }token ID of this domain name NFT` }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
{ token.type === 'WRAPPED_DOMAIN_TOKEN' ? 'Wrapped token ID' : 'Token ID' }
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
wordBreak="break-all"
|
||||
whiteSpace="pre-wrap"
|
||||
>
|
||||
<NftEntity hash={ token.contract_hash } id={ token.id } isLoading={ isLoading } noIcon/>
|
||||
</DetailsInfoItem.Value>
|
||||
</React.Fragment>
|
||||
)) }
|
||||
|
||||
{ otherAddresses.length > 0 && (
|
||||
<DetailsInfoItem
|
||||
title="Other addresses"
|
||||
hint="Other cryptocurrency addresses added to this domain name"
|
||||
isLoading={ isLoading }
|
||||
flexDir="column"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
{ otherAddresses.map(([ type, address ]) => (
|
||||
<Flex key={ type } columnGap={ 2 } minW="0" w="100%" overflow="hidden">
|
||||
<Skeleton isLoaded={ !isLoading }>{ type }</Skeleton>
|
||||
<AddressEntity
|
||||
address={{ hash: address }}
|
||||
isLoading={ isLoading }
|
||||
noLink
|
||||
noIcon
|
||||
/>
|
||||
</Flex>
|
||||
)) }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Other cryptocurrency addresses added to this domain name"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Other addresses
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexDir="column"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
{ otherAddresses.map(([ type, address ]) => (
|
||||
<Flex key={ type } columnGap={ 2 } minW="0" w="100%" overflow="hidden">
|
||||
<Skeleton isLoaded={ !isLoading }>{ type }</Skeleton>
|
||||
<AddressEntity
|
||||
address={{ hash: address }}
|
||||
isLoading={ isLoading }
|
||||
noLink
|
||||
noIcon
|
||||
/>
|
||||
</Flex>
|
||||
)) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
</Grid>
|
||||
);
|
||||
|
||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
@@ -1,7 +1,8 @@
|
||||
import { Text } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import ContainerWithScrollY from 'ui/shared/ContainerWithScrollY';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
|
||||
export const TX_ACTIONS_BLOCK_ID = 'tx-actions';
|
||||
const SCROLL_GRADIENT_HEIGHT = 48;
|
||||
@@ -25,26 +26,32 @@ const DetailsActionsWrapper = ({ children, isLoading, type }: Props) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title={ `${ type === 'tx' ? 'Transaction' : 'User operation' } action` }
|
||||
hint={ `Highlighted events of the ${ type === 'tx' ? 'transaction' : 'user operation' }` }
|
||||
note={ hasScroll ? 'Scroll to see more' : undefined }
|
||||
position="relative"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<ContainerWithScrollY
|
||||
containerId={ TX_ACTIONS_BLOCK_ID }
|
||||
gradientHeight={ SCROLL_GRADIENT_HEIGHT }
|
||||
hasScroll={ hasScroll }
|
||||
alignItems="stretch"
|
||||
rowGap={ 5 }
|
||||
w="100%"
|
||||
maxH="200px"
|
||||
ref={ containerRef }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint={ `Highlighted events of the ${ type === 'tx' ? 'transaction' : 'user operation' }` }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
{ children }
|
||||
</ContainerWithScrollY>
|
||||
</DetailsInfoItem>
|
||||
<span>{ `${ type === 'tx' ? 'Transaction' : 'User operation' } action` }</span>
|
||||
{ hasScroll && <Text fontWeight={ 500 } variant="secondary" fontSize="xs" className="note" align="right">Scroll to see more</Text> }
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
position="relative"
|
||||
>
|
||||
<ContainerWithScrollY
|
||||
containerId={ TX_ACTIONS_BLOCK_ID }
|
||||
gradientHeight={ SCROLL_GRADIENT_HEIGHT }
|
||||
hasScroll={ hasScroll }
|
||||
alignItems="stretch"
|
||||
rowGap={ 5 }
|
||||
w="100%"
|
||||
maxH="200px"
|
||||
ref={ containerRef }
|
||||
>
|
||||
{ children }
|
||||
</ContainerWithScrollY>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,46 +1,59 @@
|
||||
import { GridItem, Flex, Text, Skeleton } from '@chakra-ui/react';
|
||||
import type { HTMLChakraProps } from '@chakra-ui/system';
|
||||
import { chakra, GridItem, Flex, Skeleton } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import Hint from 'ui/shared/Hint';
|
||||
|
||||
interface Props extends Omit<HTMLChakraProps<'div'>, 'title'> {
|
||||
title: React.ReactNode;
|
||||
interface LabelProps {
|
||||
hint?: string;
|
||||
children: React.ReactNode;
|
||||
note?: string;
|
||||
isLoading?: boolean;
|
||||
className?: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const DetailsInfoItem = ({ title, hint, note, children, id, isLoading, ...styles }: Props) => {
|
||||
const Label = chakra(({ hint, children, isLoading, id, className }: LabelProps) => {
|
||||
return (
|
||||
<>
|
||||
<GridItem py={{ base: 1, lg: 2 }} id={ id } lineHeight={ 5 } { ...styles } _notFirst={{ mt: { base: 3, lg: 0 } }}>
|
||||
<Flex columnGap={ 2 } alignItems="flex-start">
|
||||
{ hint && <Hint label={ hint } isLoading={ isLoading }/> }
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Text fontWeight={{ base: 700, lg: 500 }}>
|
||||
{ title }
|
||||
{ note && <Text fontWeight={ 500 } variant="secondary" fontSize="xs" className="note" align="right">{ note }</Text> }
|
||||
</Text>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
</GridItem>
|
||||
<GridItem
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
flexWrap="wrap"
|
||||
rowGap={ 3 }
|
||||
pl={{ base: 7, lg: 0 }}
|
||||
py={{ base: 1, lg: 2 }}
|
||||
lineHeight={ 5 }
|
||||
whiteSpace="nowrap"
|
||||
{ ...styles }
|
||||
>
|
||||
{ children }
|
||||
</GridItem>
|
||||
</>
|
||||
<GridItem
|
||||
id={ id }
|
||||
className={ className }
|
||||
py={ 1 }
|
||||
lineHeight={{ base: 5, lg: 6 }}
|
||||
_notFirst={{ mt: { base: 3, lg: 0 } }}
|
||||
>
|
||||
<Flex columnGap={ 2 } alignItems="flex-start">
|
||||
{ hint && <Hint label={ hint } isLoading={ isLoading } my={{ lg: '2px' }}/> }
|
||||
<Skeleton isLoaded={ !isLoading } fontWeight={{ base: 700, lg: 500 }}>
|
||||
{ children }
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
</GridItem>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default DetailsInfoItem;
|
||||
interface ValueProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Value = chakra(({ children, className }: ValueProps) => {
|
||||
return (
|
||||
<GridItem
|
||||
className={ className }
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
flexWrap="wrap"
|
||||
rowGap={ 3 }
|
||||
pl={{ base: 7, lg: 0 }}
|
||||
py={ 1 }
|
||||
lineHeight={{ base: 5, lg: 6 }}
|
||||
whiteSpace="nowrap"
|
||||
>
|
||||
{ children }
|
||||
</GridItem>
|
||||
);
|
||||
});
|
||||
|
||||
export {
|
||||
Label,
|
||||
Value,
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import config from 'configs/app';
|
||||
import * as cookies from 'lib/cookies';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import AdBanner from 'ui/shared/ad/AdBanner';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
|
||||
const feature = config.features.adsBanner;
|
||||
|
||||
@@ -30,13 +30,17 @@ const DetailsSponsoredItem = ({ isLoading }: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Sponsored"
|
||||
hint="Sponsored banner advertisement"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<AdBanner isLoading={ isLoading }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Sponsored banner advertisement"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Sponsored
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AdBanner isLoading={ isLoading }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const PrevNext = ({ className, onClick, prevLabel, nextLabel, isPrevDisabled, is
|
||||
}
|
||||
|
||||
return (
|
||||
<Box className={ className }>
|
||||
<Box className={ className } display="flex">
|
||||
<Tooltip label={ prevLabel }>
|
||||
<IconButton
|
||||
aria-label="prev"
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -18,7 +18,7 @@ import { TOKEN_COUNTERS } from 'stubs/token';
|
||||
import type { TokenTabs } from 'ui/pages/Token';
|
||||
import AppActionButton from 'ui/shared/AppActionButton/AppActionButton';
|
||||
import useAppActionData from 'ui/shared/AppActionButton/useAppActionData';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
|
||||
import TruncatedValue from 'ui/shared/TruncatedValue';
|
||||
|
||||
@@ -102,77 +102,96 @@ const TokenDetails = ({ tokenQuery }: Props) => {
|
||||
<Grid
|
||||
columnGap={ 8 }
|
||||
rowGap={{ base: 1, lg: 3 }}
|
||||
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden"
|
||||
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(728px, auto)' }} overflow="hidden"
|
||||
>
|
||||
{ exchangeRate && (
|
||||
<DetailsInfoItem
|
||||
title="Price"
|
||||
hint="Price per token on the exchanges"
|
||||
alignSelf="center"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
|
||||
<span>{ `$${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }` }</span>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Price per token on the exchanges"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
Price
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
|
||||
<span>{ `$${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }` }</span>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ marketCap && (
|
||||
<DetailsInfoItem
|
||||
title="Fully diluted market cap"
|
||||
hint="Total supply * Price"
|
||||
alignSelf="center"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
|
||||
<span>{ `$${ BigNumber(marketCap).toFormat() }` }</span>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Total supply * Price"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
Fully diluted market cap
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } display="inline-block">
|
||||
<span>{ `$${ BigNumber(marketCap).toFormat() }` }</span>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
<DetailsInfoItem
|
||||
title="Max total supply"
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="The total amount of tokens issued"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
Max total supply
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
alignSelf="center"
|
||||
wordBreak="break-word"
|
||||
whiteSpace="pre-wrap"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } w="100%" display="flex">
|
||||
<TruncatedValue value={ totalSupplyValue || '0' } maxW="80%" flexShrink={ 0 }/>
|
||||
<Box flexShrink={ 0 }> </Box>
|
||||
<TruncatedValue value={ symbol || '' }/>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Holders"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Number of accounts holding the token"
|
||||
alignSelf="center"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
Holders
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !tokenCountersQuery.isPlaceholderData }>
|
||||
{ countersItem('token_holders_count') }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Transfers"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Number of transfer for the token"
|
||||
alignSelf="center"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
Transfers
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !tokenCountersQuery.isPlaceholderData }>
|
||||
{ countersItem('transfers_count') }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ decimals && (
|
||||
<DetailsInfoItem
|
||||
title="Decimals"
|
||||
hint="Number of digits that come after the decimal place when displaying token value"
|
||||
alignSelf="center"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } minW={ 6 }>
|
||||
{ decimals }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Number of digits that come after the decimal place when displaying token value"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
Decimals
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } minW={ 6 }>
|
||||
{ decimals }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ type !== 'ERC-20' && (
|
||||
@@ -186,14 +205,18 @@ const TokenDetails = ({ tokenQuery }: Props) => {
|
||||
) }
|
||||
|
||||
{ (type !== 'ERC-20' && config.UI.views.nft.marketplaces.length === 0 && appActionData && isActionButtonExperiment) && (
|
||||
<DetailsInfoItem
|
||||
title="Dapp"
|
||||
hint="Link to the dapp"
|
||||
alignSelf="center"
|
||||
py={ 1 }
|
||||
>
|
||||
<AppActionButton data={ appActionData } height="30px" source="NFT collection"/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Link to the dapp"
|
||||
>
|
||||
Dapp
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
py="1px"
|
||||
>
|
||||
<AppActionButton data={ appActionData } height="30px" source="NFT collection"/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<DetailsSponsoredItem isLoading={ tokenQuery.isPlaceholderData }/>
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { AddressMetadataTagFormatted } from 'types/client/addressMetadata';
|
||||
|
||||
import config from 'configs/app';
|
||||
import AppActionButton from 'ui/shared/AppActionButton/AppActionButton';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import TextSeparator from 'ui/shared/TextSeparator';
|
||||
|
||||
interface Props {
|
||||
@@ -23,40 +23,44 @@ const TokenNftMarketplaces = ({ hash, id, isLoading, appActionData, source, isAc
|
||||
}
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Marketplaces"
|
||||
hint="Marketplaces trading this NFT"
|
||||
alignSelf="center"
|
||||
isLoading={ isLoading }
|
||||
py={ (appActionData && isActionButtonExperiment) ? 1 : { base: 1, lg: 2 } }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading } display="flex" columnGap={ 3 } flexWrap="wrap" alignItems="center">
|
||||
{ config.UI.views.nft.marketplaces.map((item) => {
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Marketplaces trading this NFT"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Marketplaces
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
py={ (appActionData && isActionButtonExperiment) ? '1px' : '6px' }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading } display="flex" columnGap={ 3 } flexWrap="wrap" alignItems="center">
|
||||
{ config.UI.views.nft.marketplaces.map((item) => {
|
||||
|
||||
const hrefTemplate = id ? item.instance_url : item.collection_url;
|
||||
const href = hrefTemplate.replace('{id}', id || '').replace('{hash}', hash || '');
|
||||
const hrefTemplate = id ? item.instance_url : item.collection_url;
|
||||
const href = hrefTemplate.replace('{id}', id || '').replace('{hash}', hash || '');
|
||||
|
||||
return (
|
||||
<Tooltip label={ `View on ${ item.name }` } key={ item.name }>
|
||||
<Link href={ href } target="_blank">
|
||||
<Image
|
||||
src={ item.logo_url }
|
||||
alt={ `${ item.name } marketplace logo` }
|
||||
boxSize={ 5 }
|
||||
borderRadius="full"
|
||||
/>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
}) }
|
||||
{ (appActionData && isActionButtonExperiment) && (
|
||||
<>
|
||||
<TextSeparator color="gray.500" margin={ 0 }/>
|
||||
<AppActionButton data={ appActionData } height="30px" source={ source }/>
|
||||
</>
|
||||
) }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
return (
|
||||
<Tooltip label={ `View on ${ item.name }` } key={ item.name }>
|
||||
<Link href={ href } target="_blank">
|
||||
<Image
|
||||
src={ item.logo_url }
|
||||
alt={ `${ item.name } marketplace logo` }
|
||||
boxSize={ 5 }
|
||||
borderRadius="full"
|
||||
/>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
}) }
|
||||
{ (appActionData && isActionButtonExperiment) && (
|
||||
<>
|
||||
<TextSeparator color="gray.500" margin={ 0 }/>
|
||||
<AppActionButton data={ appActionData } height="30px" source={ source }/>
|
||||
</>
|
||||
) }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import useFeatureValue from 'lib/growthbook/useFeatureValue';
|
||||
import AppActionButton from 'ui/shared/AppActionButton/AppActionButton';
|
||||
import useAppActionData from 'ui/shared/AppActionButton/useAppActionData';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
|
||||
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
@@ -53,31 +53,41 @@ const TokenInstanceDetails = ({ data, token, scrollRef, isLoading }: Props) => {
|
||||
overflow="hidden"
|
||||
>
|
||||
{ data.is_unique && data.owner && (
|
||||
<DetailsInfoItem
|
||||
title="Owner"
|
||||
hint="Current owner of this token instance"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<AddressEntity
|
||||
address={ data.owner }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Current owner of this token instance"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Owner
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressEntity
|
||||
address={ data.owner }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<TokenInstanceCreatorAddress hash={ isLoading ? '' : token.address }/>
|
||||
<DetailsInfoItem
|
||||
title="Token ID"
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="This token instance unique token ID"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Token ID
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Flex alignItems="center" overflow="hidden">
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden" display="inline-block" w="100%">
|
||||
<HashStringShortenDynamic hash={ data.id }/>
|
||||
</Skeleton>
|
||||
<CopyToClipboard text={ data.id } isLoading={ isLoading }/>
|
||||
</Flex>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<TokenInstanceTransfersCount hash={ isLoading ? '' : token.address } id={ isLoading ? '' : data.id } onClick={ handleCounterItemClick }/>
|
||||
|
||||
<TokenNftMarketplaces
|
||||
isLoading={ isLoading }
|
||||
hash={ token.address }
|
||||
@@ -86,15 +96,18 @@ const TokenInstanceDetails = ({ data, token, scrollRef, isLoading }: Props) => {
|
||||
source="NFT item"
|
||||
isActionButtonExperiment={ isActionButtonExperiment }
|
||||
/>
|
||||
|
||||
{ (config.UI.views.nft.marketplaces.length === 0 && appActionData && isActionButtonExperiment) && (
|
||||
<DetailsInfoItem
|
||||
title="Dapp"
|
||||
hint="Link to the dapp"
|
||||
alignSelf="center"
|
||||
py={ 1 }
|
||||
>
|
||||
<AppActionButton data={ appActionData } height="30px" source="NFT item"/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Link to the dapp"
|
||||
>
|
||||
Dapp
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value py="1px">
|
||||
<AppActionButton data={ appActionData } height="30px" source="NFT item"/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
</Grid>
|
||||
<NftMedia
|
||||
|
||||
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import { ADDRESS_INFO } from 'stubs/address';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
|
||||
interface Props {
|
||||
@@ -33,16 +33,20 @@ const TokenInstanceCreatorAddress = ({ hash }: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Creator"
|
||||
hint="Address that deployed this token contract"
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
>
|
||||
<AddressEntity
|
||||
address={ creatorAddress }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Address that deployed this token contract"
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Creator
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressEntity
|
||||
address={ creatorAddress }
|
||||
isLoading={ addressQuery.isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { TokenInstance } from 'types/api/token';
|
||||
import type { MetadataAttributes } from 'types/client/token';
|
||||
|
||||
import parseMetadata from 'lib/token/parseMetadata';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
|
||||
import LinkExternal from 'ui/shared/links/LinkExternal';
|
||||
import TruncatedValue from 'ui/shared/TruncatedValue';
|
||||
@@ -74,42 +74,55 @@ const TokenInstanceMetadataInfo = ({ data, isLoading }: Props) => {
|
||||
<>
|
||||
<DetailsInfoItemDivider/>
|
||||
{ metadata?.name && (
|
||||
<DetailsInfoItem
|
||||
title="Name"
|
||||
hint="NFT name"
|
||||
whiteSpace="normal"
|
||||
wordBreak="break-word"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
{ metadata.name }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="NFT name"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Name
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
whiteSpace="normal"
|
||||
wordBreak="break-word"
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
{ metadata.name }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
{ metadata?.description && (
|
||||
<DetailsInfoItem
|
||||
title="Description"
|
||||
hint="NFT description"
|
||||
whiteSpace="normal"
|
||||
wordBreak="break-word"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
{ metadata.description }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="NFT description"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Description
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
whiteSpace="normal"
|
||||
wordBreak="break-word"
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
{ metadata.description }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
{ metadata?.attributes && (
|
||||
<DetailsInfoItem
|
||||
title="Attributes"
|
||||
hint="NFT attributes"
|
||||
whiteSpace="normal"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Grid gap={ 2 } templateColumns="repeat(auto-fill,minmax(160px, 1fr))" w="100%">
|
||||
{ metadata.attributes.map((attribute, index) => <Item key={ index } data={ attribute } isLoading={ isLoading }/>) }
|
||||
</Grid>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="NFT attributes"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Attributes
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Grid gap={ 2 } templateColumns="repeat(auto-fill,minmax(160px, 1fr))" w="100%" whiteSpace="normal">
|
||||
{ metadata.attributes.map((attribute, index) => <Item key={ index } data={ attribute } isLoading={ isLoading }/>) }
|
||||
</Grid>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import React from 'react';
|
||||
import { route } from 'nextjs-routes';
|
||||
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import LinkInternal from 'ui/shared/links/LinkInternal';
|
||||
|
||||
interface Props {
|
||||
@@ -37,20 +37,24 @@ const TokenInstanceTransfersCount = ({ hash, id, onClick }: Props) => {
|
||||
undefined;
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Transfers"
|
||||
hint="Number of transfer for the token instance"
|
||||
isLoading={ transfersCountQuery.isPlaceholderData }
|
||||
>
|
||||
<Skeleton isLoaded={ !transfersCountQuery.isPlaceholderData } display="inline-block">
|
||||
<LinkInternal
|
||||
href={ url }
|
||||
onClick={ transfersCountQuery.data.transfers_count > 0 ? onClick : undefined }
|
||||
>
|
||||
{ transfersCountQuery.data.transfers_count.toLocaleString() }
|
||||
</LinkInternal>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Number of transfer for the token instance"
|
||||
isLoading={ transfersCountQuery.isPlaceholderData }
|
||||
>
|
||||
Transfers
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !transfersCountQuery.isPlaceholderData } display="inline-block">
|
||||
<LinkInternal
|
||||
href={ url }
|
||||
onClick={ transfersCountQuery.data.transfers_count > 0 ? onClick : undefined }
|
||||
>
|
||||
{ transfersCountQuery.data.transfers_count.toLocaleString() }
|
||||
</LinkInternal>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Flex, Link, useBoolean } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
|
||||
interface Props {
|
||||
@@ -14,27 +14,31 @@ const TxAllowedPeekers = ({ items }: Props) => {
|
||||
const [ isExpanded, expand ] = useBoolean(false);
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Allowed peekers"
|
||||
hint="Smart contracts allowed to interact with confidential data"
|
||||
>
|
||||
<Flex flexDir="column" rowGap={ 3 } w="100%">
|
||||
{ items
|
||||
.slice(0, isExpanded ? undefined : CUT_LENGTH)
|
||||
.map((item) => <AddressEntity key={ item } address={{ hash: item, is_contract: true }}/>) }
|
||||
</Flex>
|
||||
{ items.length > CUT_LENGTH && (
|
||||
<Link
|
||||
display="inline-block"
|
||||
fontSize="sm"
|
||||
textDecorationLine="underline"
|
||||
textDecorationStyle="dashed"
|
||||
onClick={ expand.toggle }
|
||||
>
|
||||
{ isExpanded ? 'Hide' : 'Show all' }
|
||||
</Link>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Smart contracts allowed to interact with confidential data"
|
||||
>
|
||||
Allowed peekers
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Flex flexDir="column" rowGap={ 3 } w="100%">
|
||||
{ items
|
||||
.slice(0, isExpanded ? undefined : CUT_LENGTH)
|
||||
.map((item) => <AddressEntity key={ item } address={{ hash: item, is_contract: true }}/>) }
|
||||
</Flex>
|
||||
{ items.length > CUT_LENGTH && (
|
||||
<Link
|
||||
display="inline-block"
|
||||
fontSize="sm"
|
||||
textDecorationLine="underline"
|
||||
textDecorationStyle="dashed"
|
||||
onClick={ expand.toggle }
|
||||
>
|
||||
{ isExpanded ? 'Hide' : 'Show all' }
|
||||
</Link>
|
||||
) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { ExcludeUndefined } from 'types/utils';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import Tag from 'ui/shared/chakra/Tag';
|
||||
import CurrencyValue from 'ui/shared/CurrencyValue';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
@@ -24,85 +24,110 @@ interface Props {
|
||||
const TxDetailsWrapped = ({ data }: Props) => {
|
||||
return (
|
||||
<Grid columnGap={ 8 } rowGap={{ base: 3, lg: 3 }} templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }}>
|
||||
<DetailsInfoItem
|
||||
title="Transaction hash"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Unique character string (TxID) assigned to every verified transaction"
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
Transaction hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value flexWrap="nowrap">
|
||||
<TxEntity hash={ data.hash } noIcon noLink noCopy={ false }/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Method"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Transaction method name"
|
||||
>
|
||||
Method
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Tag colorScheme="gray">
|
||||
{ data.method }
|
||||
</Tag>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title={ data.to?.is_contract ? 'Interacted with contract' : 'To' }
|
||||
<DetailsInfoItem.Label
|
||||
hint="Address (external or contract) receiving the transaction"
|
||||
flexWrap={{ base: 'wrap', lg: 'nowrap' }}
|
||||
columnGap={ 3 }
|
||||
>
|
||||
{ data.to?.is_contract ? 'Interacted with contract' : 'To' }
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Flex flexWrap="nowrap" alignItems="center" maxW="100%">
|
||||
<AddressEntity address={ data.to }/>
|
||||
</Flex>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Value"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Value sent in the native token (and USD) if applicable"
|
||||
>
|
||||
Value
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<CurrencyValue
|
||||
value={ data.value }
|
||||
currency={ currencyUnits.ether }
|
||||
flexWrap="wrap"
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.fee.value !== null && (
|
||||
<DetailsInfoItem
|
||||
title="Transaction fee"
|
||||
hint="Total transaction fee"
|
||||
>
|
||||
<CurrencyValue
|
||||
value={ data.fee.value }
|
||||
currency={ currencyUnits.ether }
|
||||
flexWrap="wrap"
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Total transaction fee"
|
||||
>
|
||||
Transaction fee
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<CurrencyValue
|
||||
value={ data.fee.value }
|
||||
currency={ currencyUnits.ether }
|
||||
flexWrap="wrap"
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<TxDetailsGasPrice gasPrice={ data.gas_price }/>
|
||||
|
||||
{ data.gas_limit && (
|
||||
<DetailsInfoItem
|
||||
title="Gas limit"
|
||||
hint="Maximum amount of gas that can be used by the transaction"
|
||||
>
|
||||
{ BigNumber(data.gas_limit).toFormat() }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Maximum amount of gas that can be used by the transaction"
|
||||
>
|
||||
Gas limit
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ BigNumber(data.gas_limit).toFormat() }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
<TxDetailsOther type={ data.type } nonce={ data.nonce } position={ null }/>
|
||||
<DetailsInfoItem
|
||||
title="Raw input"
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Binary data included with the transaction. See logs tab for additional info"
|
||||
>
|
||||
Raw input
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<RawInputData hex={ data.raw_input }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.decoded_input && (
|
||||
<DetailsInfoItem
|
||||
title="Decoded input data"
|
||||
hint="Decoded input data"
|
||||
>
|
||||
<LogDecodedInputData data={ data.decoded_input }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Decoded input data"
|
||||
>
|
||||
Decoded input data
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<LogDecodedInputData data={ data.decoded_input }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
</Grid>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ import config from 'configs/app';
|
||||
import { ZERO } from 'lib/consts';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import CurrencyValue from 'ui/shared/CurrencyValue';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
|
||||
const rollupFeature = config.features.rollup;
|
||||
@@ -30,24 +30,28 @@ const TxDetailsBurntFees = ({ data, isLoading }: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Burnt fees"
|
||||
hint={ `
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint={ `
|
||||
Amount of ${ currencyUnits.ether } burned for this transaction. Equals Block Base Fee per Gas * Gas Used
|
||||
${ data.blob_gas_price && data.blob_gas_used ? ' + Blob Gas Price * Blob Gas Used' : '' }
|
||||
` }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/>
|
||||
<CurrencyValue
|
||||
value={ value.toString() }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
flexWrap="wrap"
|
||||
ml={ 2 }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Burnt fees
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/>
|
||||
<CurrencyValue
|
||||
value={ value.toString() }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
flexWrap="wrap"
|
||||
ml={ 2 }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import React from 'react';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
|
||||
interface Props {
|
||||
txFee: string | null;
|
||||
@@ -18,16 +18,20 @@ const TxDetailsFeePerGas = ({ txFee, gasUsed, isLoading }: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Fee per gas"
|
||||
hint="Fee per gas"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
|
||||
{ BigNumber(txFee).dividedBy(10 ** config.chain.currency.decimals).dividedBy(gasUsed).toFixed() }
|
||||
{ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ currencyUnits.ether }` }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Fee per gas"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Fee per gas
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
|
||||
{ BigNumber(txFee).dividedBy(10 ** config.chain.currency.decimals).dividedBy(gasUsed).toFixed() }
|
||||
{ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ currencyUnits.ether }` }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
import config from 'configs/app';
|
||||
import { WEI, WEI_IN_GWEI } from 'lib/consts';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
|
||||
interface Props {
|
||||
gasPrice: string | null;
|
||||
@@ -18,18 +18,22 @@ const TxDetailsGasPrice = ({ gasPrice, isLoading }: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Gas price"
|
||||
hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
|
||||
{ BigNumber(gasPrice).dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>({ BigNumber(gasPrice).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</span>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Gas price
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
|
||||
{ BigNumber(gasPrice).dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>({ BigNumber(gasPrice).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</span>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,47 +3,51 @@ import React from 'react';
|
||||
|
||||
import type { Transaction } from 'types/api/transaction';
|
||||
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import TextSeparator from 'ui/shared/TextSeparator';
|
||||
|
||||
type Props = Pick<Transaction, 'nonce' | 'type' | 'position'>
|
||||
|
||||
const TxDetailsOther = ({ nonce, type, position }: Props) => {
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Other"
|
||||
hint="Other data related to this transaction"
|
||||
>
|
||||
{
|
||||
[
|
||||
typeof type === 'number' && (
|
||||
<Box key="type">
|
||||
<Text as="span" fontWeight="500">Txn type: </Text>
|
||||
<Text fontWeight="600" as="span">{ type }</Text>
|
||||
{ type === 2 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-1559)</Text> }
|
||||
{ type === 3 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-4844)</Text> }
|
||||
</Box>
|
||||
),
|
||||
<Box key="nonce">
|
||||
<Text as="span" fontWeight="500">Nonce: </Text>
|
||||
<Text fontWeight="600" as="span">{ nonce }</Text>
|
||||
</Box>,
|
||||
position !== null && position !== undefined && (
|
||||
<Box key="position">
|
||||
<Text as="span" fontWeight="500">Position: </Text>
|
||||
<Text fontWeight="600" as="span">{ position }</Text>
|
||||
</Box>
|
||||
),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.map((item, index) => (
|
||||
<>
|
||||
{ index !== 0 && <TextSeparator/> }
|
||||
{ item }
|
||||
</>
|
||||
))
|
||||
}
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Other data related to this transaction"
|
||||
>
|
||||
Other
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{
|
||||
[
|
||||
typeof type === 'number' && (
|
||||
<Box key="type">
|
||||
<Text as="span" fontWeight="500">Txn type: </Text>
|
||||
<Text fontWeight="600" as="span">{ type }</Text>
|
||||
{ type === 2 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-1559)</Text> }
|
||||
{ type === 3 && <Text fontWeight="400" as="span" ml={ 1 } variant="secondary">(EIP-4844)</Text> }
|
||||
</Box>
|
||||
),
|
||||
<Box key="nonce">
|
||||
<Text as="span" fontWeight="500">Nonce: </Text>
|
||||
<Text fontWeight="600" as="span">{ nonce }</Text>
|
||||
</Box>,
|
||||
position !== null && position !== undefined && (
|
||||
<Box key="position">
|
||||
<Text as="span" fontWeight="500">Position: </Text>
|
||||
<Text fontWeight="600" as="span">{ position }</Text>
|
||||
</Box>
|
||||
),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.map((item, index) => (
|
||||
<>
|
||||
{ index !== 0 && <TextSeparator/> }
|
||||
{ item }
|
||||
</>
|
||||
))
|
||||
}
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
|
||||
|
||||
import { route } from 'nextjs-routes';
|
||||
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import LinkInternal from 'ui/shared/links/LinkInternal';
|
||||
|
||||
@@ -40,22 +40,24 @@ const TxDetailsTokenTransfers = ({ data, txHash, isOverflow }: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
key={ type }
|
||||
title={ title }
|
||||
hint={ hint }
|
||||
position="relative"
|
||||
>
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
alignItems="flex-start"
|
||||
rowGap={ 5 }
|
||||
w="100%"
|
||||
overflow="hidden"
|
||||
<React.Fragment key={ type }>
|
||||
<DetailsInfoItem.Label
|
||||
hint={ hint }
|
||||
>
|
||||
{ items.map((item, index) => <TxDetailsTokenTransfer key={ index } data={ item }/>) }
|
||||
</Flex>
|
||||
</DetailsInfoItem>
|
||||
{ title }
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value position="relative">
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
alignItems="flex-start"
|
||||
rowGap={ 5 }
|
||||
w="100%"
|
||||
overflow="hidden"
|
||||
>
|
||||
{ items.map((item, index) => <TxDetailsTokenTransfer key={ index } data={ item }/>) }
|
||||
</Flex>
|
||||
</DetailsInfoItem.Value>
|
||||
</React.Fragment>
|
||||
);
|
||||
}) }
|
||||
{ isOverflow && (
|
||||
|
||||
@@ -29,7 +29,7 @@ import { currencyUnits } from 'lib/units';
|
||||
import Tag from 'ui/shared/chakra/Tag';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
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 DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
|
||||
import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
|
||||
@@ -125,12 +125,14 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
|
||||
<TxSocketAlert status={ socketStatus }/>
|
||||
</GridItem>
|
||||
) }
|
||||
<DetailsInfoItem
|
||||
title="Transaction hash"
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Unique character string (TxID) assigned to every verified transaction"
|
||||
flexWrap="nowrap"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Transaction hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value flexWrap="nowrap">
|
||||
{ data.status === null && <Spinner mr={ 2 } size="sm" flexShrink={ 0 }/> }
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.hash }/>
|
||||
@@ -143,76 +145,102 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
|
||||
<Box display="none" flexShrink={ 0 } id="meta-suites__tx-explorer-link"/>
|
||||
</>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title={
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Current transaction state: Success, Failed (Error), or Pending (In Process)"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
{
|
||||
rollupFeature.isEnabled && (rollupFeature.type === 'zkEvm' || rollupFeature.type === 'zkSync') ?
|
||||
'L2 status and method' :
|
||||
'Status and method'
|
||||
}
|
||||
hint="Current transaction state: Success, Failed (Error), or Pending (In Process)"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<TxStatus status={ data.status } errorText={ data.status === 'error' ? data.result : undefined } isLoading={ isLoading }/>
|
||||
{ data.method && (
|
||||
<Tag colorScheme={ data.method === 'Multicall' ? 'teal' : 'gray' } isLoading={ isLoading } isTruncated ml={ 3 }>
|
||||
{ data.method }
|
||||
</Tag>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && data.op_withdrawals && data.op_withdrawals.length > 0 &&
|
||||
!config.UI.views.tx.hiddenFields?.L1_status && (
|
||||
<DetailsInfoItem
|
||||
title="Withdrawal status"
|
||||
hint="Detailed status progress of the transaction"
|
||||
>
|
||||
<Flex flexDir="column" rowGap={ 2 }>
|
||||
{ data.op_withdrawals.map((withdrawal) => (
|
||||
<Box key={ withdrawal.nonce }>
|
||||
<Box mb={ 2 }>
|
||||
<span>Nonce: </span>
|
||||
<chakra.span fontWeight={ 600 }>{ withdrawal.nonce }</chakra.span>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Detailed status progress of the transaction"
|
||||
>
|
||||
Withdrawal status
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Flex flexDir="column" rowGap={ 2 }>
|
||||
{ data.op_withdrawals.map((withdrawal) => (
|
||||
<Box key={ withdrawal.nonce }>
|
||||
<Box mb={ 2 }>
|
||||
<span>Nonce: </span>
|
||||
<chakra.span fontWeight={ 600 }>{ withdrawal.nonce }</chakra.span>
|
||||
</Box>
|
||||
<TxDetailsWithdrawalStatus
|
||||
status={ withdrawal.status }
|
||||
l1TxHash={ withdrawal.l1_transaction_hash }
|
||||
/>
|
||||
</Box>
|
||||
<TxDetailsWithdrawalStatus
|
||||
status={ withdrawal.status }
|
||||
l1TxHash={ withdrawal.l1_transaction_hash }
|
||||
/>
|
||||
</Box>
|
||||
)) }
|
||||
</Flex>
|
||||
</DetailsInfoItem>
|
||||
)) }
|
||||
</Flex>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.zkevm_status && !config.UI.views.tx.hiddenFields?.L1_status && (
|
||||
<DetailsInfoItem
|
||||
title="Confirmation status"
|
||||
hint="Status of the transaction confirmation path to L1"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<VerificationSteps currentStep={ data.zkevm_status } steps={ ZKEVM_L2_TX_STATUSES } isLoading={ isLoading }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Status of the transaction confirmation path to L1"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Confirmation status
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<VerificationSteps currentStep={ data.zkevm_status } steps={ ZKEVM_L2_TX_STATUSES } isLoading={ isLoading }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.revert_reason && (
|
||||
<DetailsInfoItem
|
||||
title="Revert reason"
|
||||
hint="The revert reason of the transaction"
|
||||
>
|
||||
<TxRevertReason { ...data.revert_reason }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The revert reason of the transaction"
|
||||
>
|
||||
Revert reason
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<TxRevertReason { ...data.revert_reason }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.zksync && !config.UI.views.tx.hiddenFields?.L1_status && (
|
||||
<DetailsInfoItem
|
||||
title="L1 status"
|
||||
hint="Status is the short interpretation of the batch lifecycle"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES } currentStep={ data.zksync.status } isLoading={ isLoading }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Status is the short interpretation of the batch lifecycle"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
L1 status
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES } currentStep={ data.zksync.status } isLoading={ isLoading }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
<DetailsInfoItem
|
||||
title="Block"
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Block number containing the transaction"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Block
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.block === null ?
|
||||
<Text>Pending</Text> : (
|
||||
<BlockEntity
|
||||
@@ -229,77 +257,100 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
|
||||
</Skeleton>
|
||||
</>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.zkevm_batch_number && !config.UI.views.tx.hiddenFields?.batch && (
|
||||
<DetailsInfoItem
|
||||
title="Tx batch"
|
||||
hint="Batch index for this transaction"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<BatchEntityL2
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Batch index for this transaction"
|
||||
isLoading={ isLoading }
|
||||
number={ data.zkevm_batch_number }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
) }
|
||||
{ data.zksync && !config.UI.views.tx.hiddenFields?.batch && (
|
||||
<DetailsInfoItem
|
||||
title="Batch"
|
||||
hint="Batch number"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
{ data.zksync.batch_number ? (
|
||||
>
|
||||
Tx batch
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<BatchEntityL2
|
||||
isLoading={ isLoading }
|
||||
number={ data.zksync.batch_number }
|
||||
number={ data.zkevm_batch_number }
|
||||
/>
|
||||
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.zksync && !config.UI.views.tx.hiddenFields?.batch && (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Batch number"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Batch
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.zksync.batch_number ? (
|
||||
<BatchEntityL2
|
||||
isLoading={ isLoading }
|
||||
number={ data.zksync.batch_number }
|
||||
/>
|
||||
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.timestamp && (
|
||||
<DetailsInfoItem
|
||||
title="Timestamp"
|
||||
hint="Date & time of transaction inclusion, including length of time for confirmation"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<DetailsTimestamp timestamp={ data.timestamp } isLoading={ isLoading }/>
|
||||
{ data.confirmation_duration && (
|
||||
<>
|
||||
<TextSeparator color="gray.500"/>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>{ getConfirmationDuration(data.confirmation_duration) }</span>
|
||||
</Skeleton>
|
||||
</>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Date & time of transaction inclusion, including length of time for confirmation"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Timestamp
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<DetailsTimestamp timestamp={ data.timestamp } isLoading={ isLoading }/>
|
||||
{ data.confirmation_duration && (
|
||||
<>
|
||||
<TextSeparator color="gray.500"/>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>{ getConfirmationDuration(data.confirmation_duration) }</span>
|
||||
</Skeleton>
|
||||
</>
|
||||
) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.execution_node && (
|
||||
<DetailsInfoItem
|
||||
title="Kettle"
|
||||
hint="Node that carried out the confidential computation"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<AddressEntity
|
||||
address={ data.execution_node }
|
||||
href={ route({ pathname: '/txs/kettle/[hash]', query: { hash: data.execution_node.hash } }) }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Node that carried out the confidential computation"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Kettle
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressEntity
|
||||
address={ data.execution_node }
|
||||
href={ route({ pathname: '/txs/kettle/[hash]', query: { hash: data.execution_node.hash } }) }
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.allowed_peekers && data.allowed_peekers.length > 0 && (
|
||||
<TxAllowedPeekers items={ data.allowed_peekers }/>
|
||||
) }
|
||||
|
||||
<DetailsSponsoredItem isLoading={ isLoading }/>
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
<TxDetailsActions hash={ data.hash } actions={ data.actions } isTxDataLoading={ isLoading }/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="From"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Address (external or contract) sending the transaction"
|
||||
isLoading={ isLoading }
|
||||
columnGap={ 3 }
|
||||
>
|
||||
From
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value columnGap={ 3 }>
|
||||
<AddressEntity
|
||||
address={ data.from }
|
||||
isLoading={ isLoading }
|
||||
@@ -310,11 +361,15 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
|
||||
{ addressFromTags }
|
||||
</Flex>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title={ data.to?.is_contract ? 'Interacted with contract' : 'To' }
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Address (external or contract) receiving the transaction"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
{ data.to?.is_contract ? 'Interacted with contract' : 'To' }
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexWrap={{ base: 'wrap', lg: 'nowrap' }}
|
||||
columnGap={ 3 }
|
||||
>
|
||||
@@ -351,172 +406,216 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
|
||||
) : (
|
||||
<span>[ Contract creation ]</span>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.token_transfers && <TxDetailsTokenTransfers data={ data.token_transfers } txHash={ data.hash } isOverflow={ data.token_transfers_overflow }/> }
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
{ data.zkevm_sequence_hash && (
|
||||
<DetailsInfoItem
|
||||
title="Sequence tx hash"
|
||||
flexWrap="nowrap"
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Sequence tx hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value flexWrap="nowrap">
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.zkevm_sequence_hash }/>
|
||||
</Skeleton>
|
||||
<CopyToClipboard text={ data.zkevm_sequence_hash } isLoading={ isLoading }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.zkevm_sequence_hash }/>
|
||||
</Skeleton>
|
||||
<CopyToClipboard text={ data.zkevm_sequence_hash } isLoading={ isLoading }/>
|
||||
</DetailsInfoItem>
|
||||
) }
|
||||
|
||||
{ data.zkevm_verify_hash && (
|
||||
<DetailsInfoItem
|
||||
title="Verify tx hash"
|
||||
flexWrap="nowrap"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden">
|
||||
|
||||
<HashStringShortenDynamic hash={ data.zkevm_verify_hash }/>
|
||||
</Skeleton>
|
||||
<CopyToClipboard text={ data.zkevm_verify_hash } isLoading={ isLoading }/>
|
||||
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Verify tx hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value flexWrap="nowrap">
|
||||
<Skeleton isLoaded={ !isLoading } overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.zkevm_verify_hash }/>
|
||||
</Skeleton>
|
||||
<CopyToClipboard text={ data.zkevm_verify_hash } isLoading={ isLoading }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ (data.zkevm_batch_number || data.zkevm_verify_hash) && <DetailsInfoItemDivider/> }
|
||||
|
||||
{ !config.UI.views.tx.hiddenFields?.value && (
|
||||
<DetailsInfoItem
|
||||
title="Value"
|
||||
hint="Value sent in the native token (and USD) if applicable"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<CurrencyValue
|
||||
value={ data.value }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Value sent in the native token (and USD) if applicable"
|
||||
isLoading={ isLoading }
|
||||
flexWrap="wrap"
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
) }
|
||||
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
|
||||
<DetailsInfoItem
|
||||
title="Transaction fee"
|
||||
hint={ data.blob_gas_used ? 'Transaction fee without blob fee' : 'Total transaction fee' }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
{ data.stability_fee ? (
|
||||
<TxFeeStability data={ data.stability_fee } isLoading={ isLoading }/>
|
||||
) : (
|
||||
>
|
||||
Value
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<CurrencyValue
|
||||
value={ data.fee.value }
|
||||
currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether }
|
||||
value={ data.value }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
flexWrap="wrap"
|
||||
isLoading={ isLoading }
|
||||
flexWrap="wrap"
|
||||
/>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint={ data.blob_gas_used ? 'Transaction fee without blob fee' : 'Total transaction fee' }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Transaction fee
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.stability_fee ? (
|
||||
<TxFeeStability data={ data.stability_fee } isLoading={ isLoading }/>
|
||||
) : (
|
||||
<CurrencyValue
|
||||
value={ data.fee.value }
|
||||
currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
flexWrap="wrap"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<TxDetailsGasPrice gasPrice={ data.gas_price } isLoading={ isLoading }/>
|
||||
|
||||
<TxDetailsFeePerGas txFee={ data.fee.value } gasUsed={ data.gas_used } isLoading={ isLoading }/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Gas usage & limit by txn"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Actual gas amount used by the transaction"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Gas usage & limit by txn
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isLoading }>{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
|
||||
<TextSeparator/>
|
||||
<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 }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ !config.UI.views.tx.hiddenFields?.gas_fees &&
|
||||
(data.base_fee_per_gas || data.max_fee_per_gas || data.max_priority_fee_per_gas) && (
|
||||
<DetailsInfoItem
|
||||
title={ `Gas fees (${ currencyUnits.gwei })` }
|
||||
// eslint-disable-next-line max-len
|
||||
hint={ `
|
||||
Base Fee refers to the network Base Fee at the time of the block,
|
||||
while Max Fee & Max Priority Fee refer to the max amount a user is willing to pay
|
||||
for their tx & to give to the ${ getNetworkValidatorTitle() } respectively
|
||||
` }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
{ data.base_fee_per_gas && (
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Text as="span" fontWeight="500">Base: </Text>
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
{ (data.max_fee_per_gas || data.max_priority_fee_per_gas) && <TextSeparator/> }
|
||||
</Skeleton>
|
||||
) }
|
||||
{ data.max_fee_per_gas && (
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Text as="span" fontWeight="500">Max: </Text>
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
{ data.max_priority_fee_per_gas && <TextSeparator/> }
|
||||
</Skeleton>
|
||||
) }
|
||||
{ data.max_priority_fee_per_gas && (
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Text as="span" fontWeight="500">Max priority: </Text>
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
</Skeleton>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
// eslint-disable-next-line max-len
|
||||
hint={ `
|
||||
Base Fee refers to the network Base Fee at the time of the block,
|
||||
while Max Fee & Max Priority Fee refer to the max amount a user is willing to pay
|
||||
for their tx & to give to the ${ getNetworkValidatorTitle() } respectively
|
||||
` }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
{ `Gas fees (${ currencyUnits.gwei })` }
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.base_fee_per_gas && (
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Text as="span" fontWeight="500">Base: </Text>
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
{ (data.max_fee_per_gas || data.max_priority_fee_per_gas) && <TextSeparator/> }
|
||||
</Skeleton>
|
||||
) }
|
||||
{ data.max_fee_per_gas && (
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Text as="span" fontWeight="500">Max: </Text>
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
{ data.max_priority_fee_per_gas && <TextSeparator/> }
|
||||
</Skeleton>
|
||||
) }
|
||||
{ data.max_priority_fee_per_gas && (
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Text as="span" fontWeight="500">Max priority: </Text>
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
</Skeleton>
|
||||
) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<TxDetailsBurntFees data={ data } isLoading={ isLoading }/>
|
||||
|
||||
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && (
|
||||
<>
|
||||
{ data.l1_gas_used && (
|
||||
<DetailsInfoItem
|
||||
title="L1 gas used by txn"
|
||||
hint="L1 gas used by transaction"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Text>{ BigNumber(data.l1_gas_used).toFormat() }</Text>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="L1 gas used by transaction"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
L1 gas used by txn
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Text>{ BigNumber(data.l1_gas_used).toFormat() }</Text>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.l1_gas_price && (
|
||||
<DetailsInfoItem
|
||||
title="L1 gas price"
|
||||
hint="L1 gas price"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Text mr={ 1 }>{ BigNumber(data.l1_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether }</Text>
|
||||
<Text variant="secondary">({ BigNumber(data.l1_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</Text>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="L1 gas price"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
L1 gas price
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Text mr={ 1 }>{ BigNumber(data.l1_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether }</Text>
|
||||
<Text variant="secondary">({ BigNumber(data.l1_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</Text>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.l1_fee && (
|
||||
<DetailsInfoItem
|
||||
title="L1 fee"
|
||||
// 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.` }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<CurrencyValue
|
||||
value={ data.l1_fee }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
flexWrap="wrap"
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
// 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.` }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
L1 fee
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<CurrencyValue
|
||||
value={ data.l1_fee }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
flexWrap="wrap"
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.l1_fee_scalar && (
|
||||
<DetailsInfoItem
|
||||
title="L1 fee scalar"
|
||||
hint="A Dynamic overhead (fee scalar) premium, which serves as a buffer in case L1 prices rapidly increase."
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<Text>{ data.l1_fee_scalar }</Text>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="A Dynamic overhead (fee scalar) premium, which serves as a buffer in case L1 prices rapidly increase."
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
L1 fee scalar
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Text>{ data.l1_fee_scalar }</Text>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
</>
|
||||
) }
|
||||
|
||||
<GridItem colSpan={{ base: undefined, lg: 2 }}>
|
||||
<Element name="TxInfo__cutLink">
|
||||
<Skeleton isLoaded={ !isLoading } mt={ 6 } display="inline-block">
|
||||
@@ -532,68 +631,93 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
|
||||
</Skeleton>
|
||||
</Element>
|
||||
</GridItem>
|
||||
|
||||
{ isExpanded && (
|
||||
<>
|
||||
<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.blob_gas_price && (
|
||||
<DetailsInfoItem
|
||||
title="Blob fee"
|
||||
hint="Blob fee for this transaction"
|
||||
>
|
||||
<CurrencyValue
|
||||
value={ BigNumber(data.blob_gas_used).multipliedBy(data.blob_gas_price).toString() }
|
||||
currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
flexWrap="wrap"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Blob fee for this transaction"
|
||||
>
|
||||
Blob fee
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<CurrencyValue
|
||||
value={ BigNumber(data.blob_gas_used).multipliedBy(data.blob_gas_price).toString() }
|
||||
currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
flexWrap="wrap"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.blob_gas_used && (
|
||||
<DetailsInfoItem
|
||||
title="Blob gas usage"
|
||||
hint="Amount of gas used by the blobs in this transaction"
|
||||
>
|
||||
{ BigNumber(data.blob_gas_used).toFormat() }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Amount of gas used by the blobs in this transaction"
|
||||
>
|
||||
Blob gas usage
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ BigNumber(data.blob_gas_used).toFormat() }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ (data.max_fee_per_blob_gas || data.blob_gas_price) && (
|
||||
<DetailsInfoItem
|
||||
title={ `Blob gas fees (${ currencyUnits.gwei })` }
|
||||
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this transaction` }
|
||||
>
|
||||
{ data.blob_gas_price && (
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
) }
|
||||
{ (data.max_fee_per_blob_gas && data.blob_gas_price) && <TextSeparator/> }
|
||||
{ data.max_fee_per_blob_gas && (
|
||||
<>
|
||||
<Text as="span" fontWeight="500" whiteSpace="pre">Max: </Text>
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
</>
|
||||
) }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this transaction` }
|
||||
>
|
||||
{ `Blob gas fees (${ currencyUnits.gwei })` }
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.blob_gas_price && (
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
) }
|
||||
{ (data.max_fee_per_blob_gas && data.blob_gas_price) && <TextSeparator/> }
|
||||
{ data.max_fee_per_blob_gas && (
|
||||
<>
|
||||
<Text as="span" fontWeight="500" whiteSpace="pre">Max: </Text>
|
||||
<Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
|
||||
</>
|
||||
) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
<DetailsInfoItemDivider/>
|
||||
</>
|
||||
) }
|
||||
|
||||
<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"
|
||||
>
|
||||
Raw input
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<RawInputData hex={ data.raw_input }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.decoded_input && (
|
||||
<DetailsInfoItem
|
||||
title="Decoded input data"
|
||||
hint="Decoded input data"
|
||||
>
|
||||
<LogDecodedInputData data={ data.decoded_input }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Decoded input data"
|
||||
>
|
||||
Decoded input data
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<LogDecodedInputData data={ data.decoded_input }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.zksync && <ZkSyncL2TxnBatchHashesInfo data={ data.zksync } isLoading={ isLoading }/> }
|
||||
</>
|
||||
) }
|
||||
|
||||
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
@@ -13,7 +13,7 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
|
||||
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import DataFetchAlert from 'ui/shared/DataFetchAlert';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
|
||||
import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
|
||||
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
|
||||
@@ -61,10 +61,12 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
|
||||
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(min-content, 200px) minmax(0, 1fr)' }}
|
||||
overflow="hidden"
|
||||
>
|
||||
<DetailsInfoItem
|
||||
title="Tx batch number"
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Tx batch number
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ data.number }
|
||||
</Skeleton>
|
||||
@@ -76,23 +78,32 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
|
||||
isPrevDisabled={ data.number === 0 }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Status"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Status
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<VerificationSteps steps={ ZKEVM_L2_TX_BATCH_STATUSES } currentStep={ data.status } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Timestamp"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Timestamp
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.timestamp ? <DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/> : 'Undefined' }
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Verify tx hash"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Verify tx hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.verify_tx_hash ? (
|
||||
<TxEntityL1
|
||||
isLoading={ isPlaceholderData }
|
||||
@@ -100,44 +111,57 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
|
||||
maxW="100%"
|
||||
/>
|
||||
) : <Text>Pending</Text> }
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Transactions"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Transactions
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<LinkInternal href={ route({ pathname: '/batches/[number]', query: { number: data.number.toString(), tab: 'txs' } }) }>
|
||||
{ data.transactions.length } transaction{ data.transactions.length === 1 ? '' : 's' }
|
||||
</LinkInternal>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Global exit root"
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Global exit root
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<Skeleton isLoaded={ !isPlaceholderData } overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.global_exit_root }/>
|
||||
</Skeleton>
|
||||
<CopyToClipboard text={ data.global_exit_root } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Acc input hash"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Acc input hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<Skeleton isLoaded={ !isPlaceholderData } overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.acc_input_hash }/>
|
||||
</Skeleton>
|
||||
<CopyToClipboard text={ data.acc_input_hash } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Sequence tx hash"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Sequence tx hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.sequence_tx_hash ? (
|
||||
<TxEntityL1
|
||||
isLoading={ isPlaceholderData }
|
||||
@@ -145,17 +169,21 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
|
||||
maxW="100%"
|
||||
/>
|
||||
) : <Text>Pending</Text> }
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="State root"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
State root
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexWrap="nowrap"
|
||||
>
|
||||
<Skeleton isLoaded={ !isPlaceholderData } overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ data.state_root }/>
|
||||
</Skeleton>
|
||||
<CopyToClipboard text={ data.state_root } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ import { currencyUnits } from 'lib/units';
|
||||
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import DataFetchAlert from 'ui/shared/DataFetchAlert';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
|
||||
import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
|
||||
import LinkInternal from 'ui/shared/links/LinkInternal';
|
||||
@@ -76,11 +76,13 @@ const ZkSyncL2TxnBatchDetails = ({ query }: Props) => {
|
||||
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(min-content, 200px) minmax(0, 1fr)' }}
|
||||
overflow="hidden"
|
||||
>
|
||||
<DetailsInfoItem
|
||||
title="Tx batch number"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Batch number indicates the length of batches produced by grouping L2 blocks to be proven on Ethereum."
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Tx batch number
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ data.number }
|
||||
</Skeleton>
|
||||
@@ -92,35 +94,41 @@ const ZkSyncL2TxnBatchDetails = ({ query }: Props) => {
|
||||
isPrevDisabled={ data.number === 0 }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Status"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Status is the short interpretation of the batch lifecycle"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Status
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES.slice(1) } currentStep={ data.status } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Timestamp"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Date and time at which batch is produced"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Timestamp
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.timestamp ? <DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/> : 'Undefined' }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Transactions"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Number of transactions inside the batch."
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Transactions
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<LinkInternal href={ route({ pathname: '/batches/[number]', query: { number: data.number.toString(), tab: 'txs' } }) }>
|
||||
{ txNum } transaction{ txNum === 1 ? '' : 's' }
|
||||
</LinkInternal>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
@@ -146,31 +154,38 @@ const ZkSyncL2TxnBatchDetails = ({ query }: Props) => {
|
||||
<>
|
||||
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Root hash"
|
||||
<DetailsInfoItem.Label
|
||||
hint="L1 batch root is a hash that summarizes batch data and submitted to the L1"
|
||||
>
|
||||
Root hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexWrap="nowrap"
|
||||
alignSelf="flex-start"
|
||||
>
|
||||
<TruncatedValue value={ data.root_hash }/>
|
||||
<CopyToClipboard text={ data.root_hash }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="L1 gas price"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Gas price for the batch settlement transaction on L1"
|
||||
>
|
||||
L1 gas price
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Text mr={ 1 }>{ BigNumber(data.l1_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether }</Text>
|
||||
<Text variant="secondary">({ BigNumber(data.l1_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</Text>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="L2 fair gas price"
|
||||
<DetailsInfoItem.Label
|
||||
hint={ 'The gas price below which the "baseFee" of the batch should not fall' }
|
||||
>
|
||||
L2 fair gas price
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Text mr={ 1 }>{ BigNumber(data.l2_fair_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether }</Text>
|
||||
<Text variant="secondary">({ BigNumber(data.l2_fair_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</Text>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
</Grid>
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
|
||||
import type { ZkSyncBatch } from 'types/api/zkSyncL2';
|
||||
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
|
||||
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
|
||||
|
||||
@@ -23,10 +23,13 @@ interface Props {
|
||||
const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<DetailsInfoItem
|
||||
title="Commit tx hash"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Hash of L1 tx on which the batch was committed"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Commit tx hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexDir="column"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
@@ -45,12 +48,15 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
|
||||
) }
|
||||
</>
|
||||
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Prove tx hash"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Hash of L1 tx on which the batch was proven"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Prove tx hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexDir="column"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
@@ -69,12 +75,15 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
|
||||
) }
|
||||
</>
|
||||
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Execute tx hash"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Hash of L1 tx on which the batch was executed and finalized"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Execute tx hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexDir="column"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
@@ -93,7 +102,7 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
|
||||
) }
|
||||
</>
|
||||
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
import type { UserOp } from 'types/api/userOps';
|
||||
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import RawInputData from 'ui/shared/RawInputData';
|
||||
|
||||
import UserOpCallDataSwitch from './UserOpCallDataSwitch';
|
||||
@@ -33,12 +33,16 @@ const UserOpDecodedCallData = ({ data }: Props) => {
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Call data"
|
||||
hint="Data that’s passed to the sender for execution"
|
||||
>
|
||||
<RawInputData hex={ callData } rightSlot={ toggler }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Data that’s passed to the sender for execution"
|
||||
>
|
||||
Call data
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<RawInputData hex={ callData } rightSlot={ toggler }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
import type { UserOp } from 'types/api/userOps';
|
||||
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
|
||||
|
||||
import UserOpCallDataSwitch from './UserOpCallDataSwitch';
|
||||
@@ -33,14 +33,19 @@ const UserOpDecodedCallData = ({ data }: Props) => {
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<DetailsInfoItem
|
||||
title="Decoded call data"
|
||||
hint="Decoded call data"
|
||||
flexDir={{ base: 'column', lg: 'row' }}
|
||||
alignItems={{ base: 'flex-start', lg: 'center' }}
|
||||
>
|
||||
<LogDecodedInputData data={ callData } rightSlot={ toggler }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Decoded call data"
|
||||
>
|
||||
Decoded call data
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
flexDir={{ base: 'column', lg: 'row' }}
|
||||
alignItems={{ base: 'flex-start', lg: 'center' }}
|
||||
>
|
||||
<LogDecodedInputData data={ callData } rightSlot={ toggler }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { currencyUnits } from 'lib/units';
|
||||
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
|
||||
import CurrencyValue from 'ui/shared/CurrencyValue';
|
||||
import DataFetchAlert from 'ui/shared/DataFetchAlert';
|
||||
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
|
||||
import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
|
||||
import AddressStringOrParam from 'ui/shared/entities/address/AddressStringOrParam';
|
||||
@@ -68,78 +68,108 @@ const UserOpDetails = ({ query }: Props) => {
|
||||
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(min-content, 220px) minmax(0, 1fr)' }}
|
||||
overflow="hidden"
|
||||
>
|
||||
<DetailsInfoItem
|
||||
title="User operation hash"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Unique character string assigned to every User operation"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
User operation hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData } overflow="hidden">
|
||||
<UserOpEntity hash={ data.hash } noIcon noLink noCopy={ false }/>
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Sender"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="The address of the smart contract account"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Sender
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressStringOrParam address={ data.sender } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Status"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Current User operation state"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Status
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<UserOpStatus status={ data.status } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.revert_reason && (
|
||||
<DetailsInfoItem
|
||||
title="Revert reason"
|
||||
hint="The revert reason of the User operation"
|
||||
isLoading={ isPlaceholderData }
|
||||
wordBreak="break-all"
|
||||
whiteSpace="normal"
|
||||
>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ data.revert_reason }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="The revert reason of the User operation"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Revert reason
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
wordBreak="break-all"
|
||||
whiteSpace="normal"
|
||||
>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ data.revert_reason }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.timestamp && (
|
||||
<DetailsInfoItem
|
||||
title="Timestamp"
|
||||
hint="Date and time of User operation"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
<DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Date and time of User operation"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Timestamp
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<DetailsTimestamp timestamp={ data.timestamp } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
|
||||
) }
|
||||
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
|
||||
<DetailsInfoItem
|
||||
title="Fee"
|
||||
hint="Total User operation fee"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
<CurrencyValue
|
||||
value={ data.fee }
|
||||
currency={ currencyUnits.ether }
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Total User operation fee"
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
>
|
||||
Fee
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<CurrencyValue
|
||||
value={ data.fee }
|
||||
currency={ currencyUnits.ether }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
<DetailsInfoItem
|
||||
title="Gas limit"
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Gas limit for the User operation"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Gas limit
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ BigNumber(data.gas).toFormat() }
|
||||
</Skeleton>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Gas used"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Actual gas amount used by the User operation"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Gas used
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
{ BigNumber(data.gas_used).toFormat() }
|
||||
</Skeleton>
|
||||
@@ -149,28 +179,37 @@ const UserOpDetails = ({ query }: Props) => {
|
||||
value={ BigNumber(data.gas_used).dividedBy(BigNumber(data.gas)).toNumber() }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Transaction hash"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Hash of the transaction this User operation belongs to"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Transaction hash
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<TxEntity hash={ data.transaction_hash } isLoading={ isPlaceholderData } noCopy={ false }/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Block"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Block number containing this User operation"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Block
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<BlockEntity number={ data.block_number } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Entry point"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Contract that executes bundles of User operations"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
Entry point
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressStringOrParam address={ data.entry_point } isLoading={ isPlaceholderData }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ config.features.txInterpretation.isEnabled && <UserOpDetailsActions hash={ data.hash } isUserOpDataLoading={ isPlaceholderData }/> }
|
||||
|
||||
@@ -195,112 +234,158 @@ const UserOpDetails = ({ query }: Props) => {
|
||||
<>
|
||||
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Call gas limit"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Gas limit for execution phase"
|
||||
>
|
||||
Call gas limit
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ BigNumber(data.call_gas_limit).toFormat() }
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Verification gas limit"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Gas limit for verification phase"
|
||||
>
|
||||
Verification gas limit
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ BigNumber(data.verification_gas_limit).toFormat() }
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Pre-verification gas"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Gas to compensate the bundler"
|
||||
>
|
||||
Pre-verification gas
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ BigNumber(data.pre_verification_gas).toFormat() }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ !config.UI.views.tx.hiddenFields?.gas_fees && (
|
||||
<>
|
||||
<DetailsInfoItem
|
||||
title="Max fee per gas"
|
||||
<DetailsInfoItem.Label
|
||||
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 variant="secondary" whiteSpace="pre">
|
||||
{ space }({ BigNumber(data.max_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
|
||||
</Text>
|
||||
</DetailsInfoItem><DetailsInfoItem
|
||||
title="Max priority fee per gas"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
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 variant="secondary" whiteSpace="pre">
|
||||
{ space }({ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
|
||||
</Text>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
{ data.aggregator && (
|
||||
<DetailsInfoItem
|
||||
title="Aggregator"
|
||||
hint="Helper contract to validate an aggregated signature"
|
||||
>
|
||||
<AddressStringOrParam address={ data.aggregator }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Helper contract to validate an aggregated signature"
|
||||
>
|
||||
Aggregator
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressStringOrParam address={ data.aggregator }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.aggregator_signature && (
|
||||
<DetailsInfoItem
|
||||
title="Aggregator signature"
|
||||
hint="Aggregator signature"
|
||||
>
|
||||
{ data.aggregator_signature }
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Aggregator signature"
|
||||
>
|
||||
Aggregator signature
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.aggregator_signature }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
<DetailsInfoItem
|
||||
title="Bundler"
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="A node (block builder) that handles User operations"
|
||||
>
|
||||
Bundler
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressStringOrParam address={ data.bundler }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
{ data.factory && (
|
||||
<DetailsInfoItem
|
||||
title="Factory"
|
||||
hint="Smart contract that deploys new smart contract wallets for users"
|
||||
>
|
||||
<AddressStringOrParam address={ data.factory }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Smart contract that deploys new smart contract wallets for users"
|
||||
>
|
||||
Factory
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressStringOrParam address={ data.factory }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ data.paymaster && (
|
||||
<DetailsInfoItem
|
||||
title="Paymaster"
|
||||
hint="Contract to sponsor the gas fees for User operations"
|
||||
>
|
||||
<AddressStringOrParam address={ data.paymaster }/>
|
||||
</DetailsInfoItem>
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
hint="Contract to sponsor the gas fees for User operations"
|
||||
>
|
||||
Paymaster
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<AddressStringOrParam address={ data.paymaster }/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
<DetailsInfoItem
|
||||
title="Sponsor type"
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Type of the gas fees sponsor"
|
||||
>
|
||||
Sponsor type
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<UserOpSponsorType sponsorType={ data.sponsor_type }/>
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItemDivider/>
|
||||
|
||||
<DetailsInfoItem
|
||||
title="Signature"
|
||||
<DetailsInfoItem.Label
|
||||
hint="Used to validate a User operation along with the nonce during verification"
|
||||
>
|
||||
Signature
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
wordBreak="break-all"
|
||||
whiteSpace="normal"
|
||||
|
||||
>
|
||||
{ data.signature }
|
||||
</DetailsInfoItem>
|
||||
<DetailsInfoItem
|
||||
title="Nonce"
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<DetailsInfoItem.Label
|
||||
hint="Anti-replay protection; also used as the salt for first-time account creation"
|
||||
>
|
||||
Nonce
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value
|
||||
wordBreak="break-all"
|
||||
whiteSpace="normal"
|
||||
>
|
||||
{ data.nonce }
|
||||
</DetailsInfoItem>
|
||||
</DetailsInfoItem.Value>
|
||||
|
||||
<UserOpCallData data={ data }/>
|
||||
|
||||
|
||||