Files
tom goriunovandGitHub c4c96a844b Refactor transaction page: TxInfo → TxDetails, external txs and TAC operation (#3315)
* rename TxInfo to TxDetails

* refactor tx tac operation info

* refactor external txs block
2026-03-02 19:52:34 +01:00

933 lines
36 KiB
TypeScript

import {
Box,
GridItem,
Text,
Flex,
chakra,
VStack,
} from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import { SCROLL_L2_BLOCK_STATUSES } from 'types/api/scrollL2';
import type { Transaction } from 'types/api/transaction';
import { ZKEVM_L2_TX_STATUSES } from 'types/api/transaction';
import { ZKSYNC_L2_TX_BATCH_STATUSES } from 'types/api/zkSyncL2';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import * as arbitrum from 'lib/rollups/arbitrum';
import { formatZkEvmTxStatus, formatZkSyncL2TxnBatchStatus, layerLabels } from 'lib/rollups/utils';
import getConfirmationDuration from 'lib/tx/getConfirmationDuration';
import { currencyUnits } from 'lib/units';
import { Badge } from 'toolkit/chakra/badge';
import { CollapsibleDetails } from 'toolkit/chakra/collapsible';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
import DetailedInfoNativeCoinValue from 'ui/shared/DetailedInfo/DetailedInfoNativeCoinValue';
import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem';
import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressEntityInterop from 'ui/shared/entities/address/AddressEntityInterop';
import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg';
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
import RawInputData from 'ui/shared/RawInputData';
import StatusTag from 'ui/shared/statusTag/StatusTag';
import TxStatus from 'ui/shared/statusTag/TxStatus';
import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization/Utilization';
import GasPriceValue from 'ui/shared/value/GasPriceValue';
import NativeCoinValue from 'ui/shared/value/NativeCoinValue';
import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
import TxDetailsActions from 'ui/tx/details/txDetailsActions/TxDetailsActions';
import TxDetailsBurntFees from 'ui/tx/details/TxDetailsBurntFees';
import TxDetailsFeePerGas from 'ui/tx/details/TxDetailsFeePerGas';
import TxDetailsGasPrice from 'ui/tx/details/TxDetailsGasPrice';
import TxDetailsOther from 'ui/tx/details/TxDetailsOther';
import TxDetailsTokenTransfers from 'ui/tx/details/TxDetailsTokenTransfers';
import TxDetailsWithdrawalStatusOptimistic from 'ui/tx/details/TxDetailsWithdrawalStatusOptimistic';
import TxRevertReason from 'ui/tx/details/TxRevertReason';
import TxAllowedPeekers from 'ui/tx/TxAllowedPeekers';
import TxSocketAlert from 'ui/tx/TxSocketAlert';
import ZkSyncL2TxnBatchHashesInfo from 'ui/txnBatches/zkSyncL2/ZkSyncL2TxnBatchHashesInfo';
import TxDetailsCrossChainMessages from './TxDetailsCrossChainMessages';
import TxDetailsCrossChainTransfers from './TxDetailsCrossChainTransfers';
import TxDetailsGasUsage from './TxDetailsGasUsage';
import TxDetailsInterop from './TxDetailsInterop';
import TxDetailsSetMaxGasLimit from './TxDetailsSetMaxGasLimit';
import TxDetailsTacOperation from './TxDetailsTacOperation';
import TxDetailsTxFee from './TxDetailsTxFee';
import TxDetailsWithdrawalStatusArbitrum from './TxDetailsWithdrawalStatusArbitrum';
import TxHash from './TxHash';
import TxInfoScrollFees from './TxInfoScrollFees';
interface Props {
data: Transaction | undefined;
isLoading: boolean;
socketStatus?: 'close' | 'error';
noTxActions?: boolean;
}
const rollupFeature = config.features.rollup;
const TxDetails = ({ data, isLoading, socketStatus, noTxActions }: Props) => {
const [ isExpanded, setIsExpanded ] = React.useState(false);
const handleCutLinkClick = React.useCallback(() => {
setIsExpanded((flag) => !flag);
}, []);
const showAssociatedL1Tx = React.useCallback(() => {
setIsExpanded(true);
}, []);
if (!data) {
return null;
}
const addressFromTags = [
...data.from.private_tags || [],
...data.from.public_tags || [],
...data.from.watchlist_names || [],
].map((tag) => <Badge key={ tag.label }>{ tag.display_name }</Badge>);
const toAddress = data.to ? data.to : data.created_contract;
const addressToTags = [
...toAddress?.private_tags || [],
...toAddress?.public_tags || [],
...toAddress?.watchlist_names || [],
].map((tag) => <Badge key={ tag.label }>{ tag.display_name }</Badge>);
const executionSuccessBadge = toAddress?.is_contract && data.result === 'success' ? (
<Tooltip content="Contract execution completed">
<chakra.span display="inline-flex" ml={ 2 } mr={ 1 }>
<IconSvg name="status/success" boxSize={ 4 } color={{ _light: 'blackAlpha.800', _dark: 'whiteAlpha.800' }} cursor="pointer"/>
</chakra.span>
</Tooltip>
) : null;
const executionFailedBadge = toAddress?.is_contract && Boolean(data.status) && data.result !== 'success' ? (
<Tooltip content="Error occurred during contract execution">
<chakra.span display="inline-flex" ml={ 2 } mr={ 1 }>
<IconSvg name="status/error" boxSize={ 4 } color="text.error" cursor="pointer"/>
</chakra.span>
</Tooltip>
) : null;
const hasInterop = rollupFeature.isEnabled && rollupFeature.interopEnabled && data.op_interop_messages && data.op_interop_messages.length > 0;
return (
<DetailedInfo.Container templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(215px, auto) minmax(0, 1fr)' }}>
{ config.features.metasuites.isEnabled && (
<>
<Box display="none" as="p" id="meta-suites__tx-info-label" data-status={ data.status } data-ready={ !isLoading }/>
<Box display="none" as="p" id="meta-suites__tx-info-value"/>
<DetailedInfo.ItemDivider display="none" as="p" id="meta-suites__details-info-item-divider"/>
</>
) }
{ socketStatus && (
<GridItem colSpan={{ base: undefined, lg: 2 }} mb={ 2 }>
<TxSocketAlert status={ socketStatus }/>
</GridItem>
) }
{ config.features.tac.isEnabled && <TxDetailsTacOperation isLoading={ isLoading } txHash={ data.hash }/> }
{ data.op_interop_messages ? data.op_interop_messages.map((message) => (
<TxDetailsInterop key={ message.nonce } data={ message } isLoading={ isLoading }/>
)) : null }
{ config.features.crossChainTxs.isEnabled && <TxDetailsCrossChainMessages hash={ data.hash } isLoading={ isLoading }/> }
<TxHash hash={ data.hash } isLoading={ isLoading } status={ data.status }/>
<DetailedInfo.ItemLabel
hint="Current transaction state: Success, Failed (Error), or Pending (In Process)"
isLoading={ isLoading }
>
{
rollupFeature.isEnabled &&
(rollupFeature.type === 'zkEvm' || rollupFeature.type === 'zkSync' || rollupFeature.type === 'arbitrum' || rollupFeature.type === 'scroll') ?
`${ layerLabels.current } status and method` :
'Status and method'
}
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<TxStatus status={ data.status } errorText={ data.status === 'error' ? data.result : undefined } isLoading={ isLoading }/>
{ data.method && (
<Badge colorPalette={ data.method === 'Multicall' ? 'teal' : 'gray' } loading={ isLoading } truncated ml={ 3 }>
{ data.method }
</Badge>
) }
{ data.arbitrum?.contains_message && (
<Skeleton loading={ isLoading } onClick={ showAssociatedL1Tx }>
<Link truncate ml={ 3 }>
{ data.arbitrum?.contains_message === 'incoming' ? 'Incoming message' : 'Outgoing message' }
</Link>
</Skeleton>
) }
</DetailedInfo.ItemValue>
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && data.op_withdrawals && data.op_withdrawals.length > 0 &&
!config.UI.views.tx.hiddenFields?.L1_status && (
<>
<DetailedInfo.ItemLabel
hint="Detailed status progress of the transaction"
>
Withdrawal status
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<Flex flexDir="column" rowGap={ 2 }>
{ data.op_withdrawals.map((withdrawal) => (
<Box key={ withdrawal.nonce }>
<Box mb={ 2 } py={{ base: '5px', lg: 1 }}>
<span>Nonce: </span>
<chakra.span fontWeight={ 600 }>{ withdrawal.nonce }</chakra.span>
</Box>
<TxDetailsWithdrawalStatusOptimistic data={ withdrawal } txHash={ data.hash } from={ data.from }/>
</Box>
)) }
</Flex>
</DetailedInfo.ItemValue>
</>
) }
{ data.zkevm_status && !config.UI.views.tx.hiddenFields?.L1_status && (
<>
<DetailedInfo.ItemLabel
hint={ `Status of the transaction confirmation path to ${ layerLabels.parent }` }
isLoading={ isLoading }
>
Confirmation status
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<VerificationSteps
currentStep={ formatZkEvmTxStatus(data.zkevm_status) }
steps={ ZKEVM_L2_TX_STATUSES.map(formatZkEvmTxStatus) }
isLoading={ isLoading }
/>
</DetailedInfo.ItemValue>
</>
) }
{ data.arbitrum?.status && !config.UI.views.tx.hiddenFields?.L1_status && (
<>
<DetailedInfo.ItemLabel
hint={ `Status of the transaction confirmation path to ${ layerLabels.parent }` }
isLoading={ isLoading }
>
{ layerLabels.parent } status
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<VerificationSteps
currentStep={ arbitrum.VERIFICATION_STEPS_MAP[data.arbitrum.status] }
currentStepPending={ arbitrum.getVerificationStepStatus(data.arbitrum) === 'pending' }
steps={ arbitrum.verificationSteps }
isLoading={ isLoading }
/>
</DetailedInfo.ItemValue>
</>
) }
{ data.revert_reason && (
<>
<DetailedInfo.ItemLabel
hint="The revert reason of the transaction"
>
Revert reason
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue flexWrap="wrap" mt={{ base: '5px', lg: '4px' }}>
<TxRevertReason { ...data.revert_reason }/>
</DetailedInfo.ItemValue>
</>
) }
{ data.zksync && !config.UI.views.tx.hiddenFields?.L1_status && (
<>
<DetailedInfo.ItemLabel
hint="Status is the short interpretation of the batch lifecycle"
isLoading={ isLoading }
>
{ layerLabels.parent } status
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<VerificationSteps
steps={ ZKSYNC_L2_TX_BATCH_STATUSES.map(formatZkSyncL2TxnBatchStatus) }
currentStep={ formatZkSyncL2TxnBatchStatus(data.zksync.status) }
isLoading={ isLoading }
/>
</DetailedInfo.ItemValue>
</>
) }
<DetailedInfo.ItemLabel
hint="Block number containing the transaction"
isLoading={ isLoading }
>
Block
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue multiRow={ Boolean(data.scroll?.l2_block_status) }>
{ data.block_number === null ?
<Text>Pending</Text> : (
<BlockEntity
isLoading={ isLoading }
number={ data.block_number }
noIcon
/>
) }
{ Boolean(data.confirmations) && (
<>
<TextSeparator/>
<Skeleton loading={ isLoading } color="text.secondary">
<span>{ data.confirmations } Block confirmations</span>
</Skeleton>
</>
) }
{ data.scroll?.l2_block_status && (
<>
<TextSeparator/>
<VerificationSteps steps={ SCROLL_L2_BLOCK_STATUSES } currentStep={ data.scroll.l2_block_status } isLoading={ isLoading }/>
</>
) }
</DetailedInfo.ItemValue>
{ data.zkevm_batch_number && !config.UI.views.tx.hiddenFields?.batch && (
<>
<DetailedInfo.ItemLabel
hint="Batch index for this transaction"
isLoading={ isLoading }
>
Txn batch
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<BatchEntityL2
isLoading={ isLoading }
number={ data.zkevm_batch_number }
/>
</DetailedInfo.ItemValue>
</>
) }
{ data.zksync && !config.UI.views.tx.hiddenFields?.batch && (
<>
<DetailedInfo.ItemLabel
hint="Batch number"
isLoading={ isLoading }
>
Batch
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
{ data.zksync.batch_number ? (
<BatchEntityL2
isLoading={ isLoading }
number={ data.zksync.batch_number }
/>
) : <Skeleton loading={ isLoading }>Pending</Skeleton> }
</DetailedInfo.ItemValue>
</>
) }
{ data.arbitrum && !config.UI.views.tx.hiddenFields?.batch && (
<>
<DetailedInfo.ItemLabel
hint="Index of the batch containing this transaction"
isLoading={ isLoading }
>
Batch
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
{ data.arbitrum.batch_number ?
<BatchEntityL2 isLoading={ isLoading } number={ data.arbitrum.batch_number }/> :
<Skeleton loading={ isLoading }>Pending</Skeleton> }
</DetailedInfo.ItemValue>
</>
) }
{ data.timestamp && (
<>
<DetailedInfo.ItemLabel
hint="Date & time of transaction inclusion, including length of time for confirmation"
isLoading={ isLoading }
>
Timestamp
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue multiRow>
<DetailedInfoTimestamp timestamp={ data.timestamp } isLoading={ isLoading }/>
{ data.confirmation_duration && (
<Flex alignItems="center">
<TextSeparator hideBelow="lg"/>
<Skeleton loading={ isLoading } color="text.secondary">
<span>{ getConfirmationDuration(data.confirmation_duration) }</span>
</Skeleton>
</Flex>
) }
</DetailedInfo.ItemValue>
</>
) }
{ data.execution_node && (
<>
<DetailedInfo.ItemLabel
hint="Node that carried out the confidential computation"
isLoading={ isLoading }
>
Kettle
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<AddressEntity
address={ data.execution_node }
href={ route({ pathname: '/txs/kettle/[hash]', query: { hash: data.execution_node.hash } }) }
/>
</DetailedInfo.ItemValue>
</>
) }
{ data.allowed_peekers && data.allowed_peekers.length > 0 && (
<TxAllowedPeekers items={ data.allowed_peekers }/>
) }
<DetailedInfoSponsoredItem isLoading={ isLoading }/>
<DetailedInfo.ItemDivider/>
{ !noTxActions && <TxDetailsActions hash={ data.hash } actions={ data.actions } isTxDataLoading={ isLoading }/> }
<DetailedInfo.ItemLabel
hint="Address (external or contract) sending the transaction"
isLoading={ isLoading }
>
From
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue columnGap={ 3 }>
<AddressEntity
address={ data.from }
isLoading={ isLoading }
/>
{ data.from.name && <Text>{ data.from.name }</Text> }
{ addressFromTags.length > 0 && (
<Flex columnGap={ 3 }>
{ addressFromTags }
</Flex>
) }
</DetailedInfo.ItemValue>
<DetailedInfo.ItemLabel
hint="Address (external or contract) receiving the transaction"
isLoading={ isLoading }
>
{ data.to?.is_contract ? 'Interacted with contract' : 'To' }
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue
flexWrap={{ base: 'wrap', lg: 'nowrap' }}
columnGap={ 3 }
>
{ toAddress ? (
<>
{ data.to && data.to.hash ? (
<Flex flexWrap="nowrap" alignItems="center" maxW="100%">
<AddressEntity
address={ toAddress }
isLoading={ isLoading }
/>
{ executionSuccessBadge }
{ executionFailedBadge }
</Flex>
) : (
<Flex width="100%" whiteSpace="pre" alignItems="center" flexShrink={ 0 }>
<span>[Contract </span>
<AddressEntity
address={ toAddress }
isLoading={ isLoading }
noIcon
/>
<span>created]</span>
{ executionSuccessBadge }
{ executionFailedBadge }
</Flex>
) }
{ addressToTags.length > 0 && (
<Flex columnGap={ 3 }>
{ addressToTags }
</Flex>
) }
</>
) : (
<span>[ Contract creation ]</span>
) }
</DetailedInfo.ItemValue>
{ data.token_transfers && <TxDetailsTokenTransfers data={ data.token_transfers } txHash={ data.hash } isOverflow={ data.token_transfers_overflow }/> }
{ config.features.crossChainTxs.isEnabled && <TxDetailsCrossChainTransfers hash={ data.hash } isLoading={ isLoading }/> }
{ hasInterop && data.op_interop_messages?.some(message => message.target_address_hash) && (
<>
<DetailedInfo.ItemLabel
isLoading={ isLoading }
hint="The target address where this cross-chain transaction is executed"
>
Interop target
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<VStack gap={ 2 } w="100%" overflow="hidden" alignItems="flex-start">
{ data.op_interop_messages
.filter((message) => message.target_address_hash)
.map((message) => {
return message.relay_chain !== undefined ? (
<AddressEntityInterop
chain={ message.relay_chain }
address={{ hash: message.target_address_hash }}
isLoading={ isLoading }
truncation="dynamic"
w="100%"
/>
) : (
<AddressEntity address={{ hash: message.target_address_hash }} isLoading={ isLoading } truncation="dynamic" w="100%"/>
);
}) }
</VStack>
</DetailedInfo.ItemValue>
</>
) }
<DetailedInfo.ItemDivider/>
{ (data.arbitrum?.commitment_transaction.hash || data.arbitrum?.confirmation_transaction.hash) &&
(
<>
{ data.arbitrum?.commitment_transaction.hash && (
<>
<DetailedInfo.ItemLabel
hint={ `${ layerLabels.parent } transaction containing this batch commitment` }
isLoading={ isLoading }
>
Commitment tx
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<TxEntityL1 hash={ data.arbitrum?.commitment_transaction.hash } isLoading={ isLoading }/>
{ data.arbitrum?.commitment_transaction.status === 'finalized' && <StatusTag type="ok" text="Finalized" ml={ 2 }/> }
</DetailedInfo.ItemValue>
</>
) }
{ data.arbitrum?.confirmation_transaction.hash && (
<>
<DetailedInfo.ItemLabel
hint={ `${ layerLabels.parent } transaction containing confirmation of this batch` }
isLoading={ isLoading }
>
Confirmation tx
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<TxEntityL1 hash={ data.arbitrum?.confirmation_transaction.hash } isLoading={ isLoading }/>
{ data.arbitrum?.commitment_transaction.status === 'finalized' && <StatusTag type="ok" text="Finalized" ml={ 2 }/> }
</DetailedInfo.ItemValue>
</>
) }
<DetailedInfo.ItemDivider/>
</>
) }
{ data.zkevm_sequence_hash && (
<>
<DetailedInfo.ItemLabel
isLoading={ isLoading }
>
Sequence tx hash
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue flexWrap="nowrap">
<Skeleton loading={ isLoading } overflow="hidden">
<HashStringShortenDynamic hash={ data.zkevm_sequence_hash }/>
</Skeleton>
<CopyToClipboard text={ data.zkevm_sequence_hash } isLoading={ isLoading }/>
</DetailedInfo.ItemValue>
</>
) }
{ data.zkevm_verify_hash && (
<>
<DetailedInfo.ItemLabel
isLoading={ isLoading }
>
Verify tx hash
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue flexWrap="nowrap">
<Skeleton loading={ isLoading } overflow="hidden">
<HashStringShortenDynamic hash={ data.zkevm_verify_hash }/>
</Skeleton>
<CopyToClipboard text={ data.zkevm_verify_hash } isLoading={ isLoading }/>
</DetailedInfo.ItemValue>
</>
) }
{ (data.zkevm_batch_number || data.zkevm_verify_hash) && <DetailedInfo.ItemDivider/> }
{ !config.UI.views.tx.hiddenFields?.value && (
<>
<DetailedInfo.ItemLabel
hint="Value sent in the native token (and USD) if applicable"
isLoading={ isLoading }
>
Value
</DetailedInfo.ItemLabel>
<DetailedInfoNativeCoinValue
amount={ data.value }
exchangeRate={ data.exchange_rate }
historicalExchangeRate={ data.historic_exchange_rate }
hasExchangeRateToggle
loading={ isLoading }
/>
</>
) }
<TxDetailsTxFee isLoading={ isLoading } data={ data }/>
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && data.operator_fee && (
<>
<DetailedInfo.ItemLabel
hint="A fee set by the chain operator to cover extra costs of additional services"
>
Operator fee
</DetailedInfo.ItemLabel>
<DetailedInfoNativeCoinValue
amount={ data.operator_fee }
exchangeRate={ data.exchange_rate }
historicalExchangeRate={ data.historic_exchange_rate }
hasExchangeRateToggle
loading={ isLoading }
/>
</>
) }
{ rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && data.arbitrum && (
<>
<DetailedInfo.ItemLabel
hint={ `Fee paid to the poster for ${ layerLabels.parent } resources` }
isLoading={ isLoading }
>
Poster fee
</DetailedInfo.ItemLabel>
<DetailedInfoNativeCoinValue
amount={ data.arbitrum.poster_fee }
exchangeRate={ data.exchange_rate }
historicalExchangeRate={ data.historic_exchange_rate }
hasExchangeRateToggle
loading={ isLoading }
/>
<DetailedInfo.ItemLabel
hint={ `Fee paid to the network for ${ layerLabels.current } resources` }
isLoading={ isLoading }
>
Network fee
</DetailedInfo.ItemLabel>
<DetailedInfoNativeCoinValue
amount={ data.arbitrum.network_fee }
exchangeRate={ data.exchange_rate }
historicalExchangeRate={ data.historic_exchange_rate }
hasExchangeRateToggle
loading={ isLoading }
/>
</>
) }
<TxDetailsGasPrice gasPrice={ data.gas_price } gasToken={ data.celo?.gas_token } isLoading={ isLoading }/>
<TxDetailsFeePerGas txFee={ data.fee.value } gasUsed={ data.gas_used } isLoading={ isLoading }/>
{ !config.UI.views.tx.additionalFields?.set_max_gas_limit && <TxDetailsGasUsage isLoading={ isLoading } data={ data }/> }
{ rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && data.arbitrum && data.gas_used && (
<>
<DetailedInfo.ItemLabel
hint={ `${ layerLabels.current } gas set aside for ${ layerLabels.parent } data charges` }
isLoading={ isLoading }
>
Gas used for { layerLabels.parent }
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<Skeleton loading={ isLoading }>{ BigNumber(data.arbitrum.gas_used_for_l1 || 0).toFormat() }</Skeleton>
<TextSeparator/>
<Utilization
ml={ 4 }
value={ BigNumber(data.arbitrum.gas_used_for_l1 || 0).dividedBy(BigNumber(data.gas_used)).toNumber() }
isLoading={ isLoading }
/>
</DetailedInfo.ItemValue>
<DetailedInfo.ItemLabel
hint={ `${ layerLabels.current } gas spent on ${ layerLabels.current } resources` }
isLoading={ isLoading }
>
Gas used for { layerLabels.current }
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<Skeleton loading={ isLoading }>{ BigNumber(data.arbitrum.gas_used_for_l2 || 0).toFormat() }</Skeleton>
<TextSeparator/>
<Utilization
ml={ 4 }
value={ BigNumber(data.arbitrum.gas_used_for_l2 || 0).dividedBy(BigNumber(data.gas_used)).toNumber() }
isLoading={ isLoading }
/>
</DetailedInfo.ItemValue>
</>
) }
{ data.scroll?.l1_gas_used !== undefined && (
<>
<DetailedInfo.ItemLabel
hint={ `Total gas used on ${ layerLabels.parent }` }
isLoading={ isLoading }
>
{ layerLabels.parent } Gas used
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<Skeleton loading={ isLoading }>{ BigNumber(data.scroll?.l1_gas_used || 0).toFormat() }</Skeleton>
</DetailedInfo.ItemValue>
</>
) }
{ !config.UI.views.tx.hiddenFields?.gas_fees &&
(data.base_fee_per_gas || data.max_fee_per_gas || data.max_priority_fee_per_gas) && (
<>
<DetailedInfo.ItemLabel
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 })` }
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue multiRow>
{ data.base_fee_per_gas && (
<NativeCoinValue
amount={ data.base_fee_per_gas }
units="gwei"
unitsTooltip="wei"
noSymbol
loading={ isLoading }
startElement="Base: "
endElement={ (data.max_fee_per_gas || data.max_priority_fee_per_gas) && <TextSeparator/> }
/>
) }
{ data.max_fee_per_gas && (
<NativeCoinValue
amount={ data.max_fee_per_gas }
units="gwei"
unitsTooltip="wei"
noSymbol
loading={ isLoading }
startElement="Max: "
endElement={ data.max_priority_fee_per_gas && <TextSeparator/> }
/>
) }
{ data.max_priority_fee_per_gas && (
<NativeCoinValue
amount={ data.max_priority_fee_per_gas }
units="gwei"
unitsTooltip="wei"
noSymbol
loading={ isLoading }
startElement="Max priority: "
/>
) }
</DetailedInfo.ItemValue>
</>
) }
<TxDetailsBurntFees data={ data } isLoading={ isLoading }/>
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && (
<>
{ data.l1_gas_used && (
<>
<DetailedInfo.ItemLabel
hint={ `${ layerLabels.parent } gas used by transaction` }
isLoading={ isLoading }
>
{ layerLabels.parent } gas used by txn
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<Text>{ BigNumber(data.l1_gas_used).toFormat() }</Text>
</DetailedInfo.ItemValue>
</>
) }
{ data.l1_gas_price && (
<>
<DetailedInfo.ItemLabel
hint={ `${ layerLabels.parent } gas price` }
isLoading={ isLoading }
>
{ layerLabels.parent } gas price
</DetailedInfo.ItemLabel>
<GasPriceValue
amount={ data.l1_gas_price }
asset={ rollupFeature.parentChain.currency?.symbol || currencyUnits.ether }
loading={ isLoading }
/>
</>
) }
{ data.l1_fee && (
<>
<DetailedInfo.ItemLabel
// eslint-disable-next-line max-len
hint={ `${ layerLabels.parent } Data Fee which is used to cover the ${ layerLabels.parent } "security" cost from the batch submission mechanism. In combination with ${ layerLabels.current } execution fee, ${ layerLabels.parent } fee makes the total amount of fees that a transaction pays.` }
isLoading={ isLoading }
>
{ layerLabels.parent } fee
</DetailedInfo.ItemLabel>
<DetailedInfoNativeCoinValue
amount={ data.l1_fee }
asset={ rollupFeature.parentChain.currency?.symbol || currencyUnits.ether }
decimals={ rollupFeature.parentChain.currency?.decimals ?? config.chain.currency.decimals }
exchangeRate={ data.exchange_rate }
historicalExchangeRate={ data.historic_exchange_rate }
hasExchangeRateToggle
loading={ isLoading }
/>
</>
) }
{ data.l1_fee_scalar && (
<>
<DetailedInfo.ItemLabel
hint={ `A Dynamic overhead (fee scalar) premium, which serves as a buffer in case ${ layerLabels.parent } prices rapidly increase.` }
isLoading={ isLoading }
>
{ layerLabels.parent } fee scalar
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<Text>{ data.l1_fee_scalar }</Text>
</DetailedInfo.ItemValue>
</>
) }
</>
) }
<TxInfoScrollFees data={ data } isLoading={ isLoading }/>
<CollapsibleDetails loading={ isLoading } mt={ 6 } gridColumn={{ base: undefined, lg: '1 / 3' }} isExpanded={ isExpanded } onClick={ handleCutLinkClick }>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
<TxDetailsSetMaxGasLimit data={ data }/>
<TxDetailsWithdrawalStatusArbitrum data={ data }/>
{ (data.blob_gas_used || data.max_fee_per_blob_gas || data.blob_gas_price) && (
<>
{ data.blob_gas_used && data.blob_gas_price && (
<>
<DetailedInfo.ItemLabel
hint="Blob fee for this transaction"
>
Blob fee
</DetailedInfo.ItemLabel>
<DetailedInfoNativeCoinValue
amount={ BigNumber(data.blob_gas_used).multipliedBy(data.blob_gas_price).toString() }
noSymbol={ config.UI.views.tx.hiddenFields?.fee_currency }
exchangeRate={ data.exchange_rate }
historicalExchangeRate={ data.historic_exchange_rate }
hasExchangeRateToggle
loading={ isLoading }
/>
</>
) }
{ data.blob_gas_used && (
<>
<DetailedInfo.ItemLabel
hint="Amount of gas used by the blobs in this transaction"
>
Blob gas usage
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
{ BigNumber(data.blob_gas_used).toFormat() }
</DetailedInfo.ItemValue>
</>
) }
{ (data.max_fee_per_blob_gas || data.blob_gas_price) && (
<>
<DetailedInfo.ItemLabel
hint={ `Amount of ${ currencyUnits.ether } used for blobs in this transaction` }
>
{ `Blob gas fees (${ currencyUnits.gwei })` }
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
{ data.blob_gas_price && (
<NativeCoinValue
amount={ data.blob_gas_price }
units="gwei"
unitsTooltip="wei"
noSymbol
loading={ isLoading }
fontWeight="600"
/>
) }
{ (data.max_fee_per_blob_gas && data.blob_gas_price) && <TextSeparator/> }
{ data.max_fee_per_blob_gas && (
<NativeCoinValue
amount={ data.max_fee_per_blob_gas }
units="gwei"
unitsTooltip="wei"
noSymbol
loading={ isLoading }
startElement="Max: "
fontWeight="600"
/>
) }
</DetailedInfo.ItemValue>
</>
) }
<DetailedInfo.ItemDivider/>
</>
) }
<TxDetailsOther nonce={ data.nonce } type={ data.type } position={ data.position } queueIndex={ data.scroll?.queue_index }/>
<DetailedInfo.ItemLabel
hint="Binary data included with the transaction. See logs tab for additional info"
mb={{ base: 1, lg: 0 }}
>
Raw input
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<RawInputData hex={ data.raw_input } defaultDataType={ data.zilliqa?.is_scilla ? 'UTF-8' : 'Hex' }/>
</DetailedInfo.ItemValue>
{ data.decoded_input && (
<>
<DetailedInfo.ItemLabel
hint="Decoded input data"
>
Decoded input data
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue flexWrap="wrap" mt={{ base: '5px', lg: '4px' }}>
<LogDecodedInputData data={ data.decoded_input }/>
</DetailedInfo.ItemValue>
</>
) }
{ data.zksync && <ZkSyncL2TxnBatchHashesInfo data={ data.zksync } isLoading={ isLoading }/> }
</CollapsibleDetails>
</DetailedInfo.Container>
);
};
export default TxDetails;