@@ -43,9 +45,9 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum
{ capitalize(getNetworkValidatorTitle()) } |
Txn |
Gas used |
- { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.total_reward &&
+ { !isRollup && !config.UI.views.block.hiddenFields?.total_reward &&
Reward { config.chain.currency.symbol } | }
- { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.burnt_fees &&
+ { !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees &&
Burnt fees { config.chain.currency.symbol } | }
diff --git a/ui/blocks/BlocksTableItem.tsx b/ui/blocks/BlocksTableItem.tsx
index 4676d82155..17c0710bbc 100644
--- a/ui/blocks/BlocksTableItem.tsx
+++ b/ui/blocks/BlocksTableItem.tsx
@@ -26,6 +26,8 @@ interface Props {
enableTimeIncrement?: boolean;
}
+const isRollup = config.features.optimisticRollup.isEnabled || config.features.zkEvmRollup.isEnabled;
+
const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
const totalReward = getBlockTotalReward(data);
const burntFees = BigNumber(data.burnt_fees || 0);
@@ -82,7 +84,7 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
) : data.tx_count }
- { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.total_reward && (
+ { !isRollup && !config.UI.views.block.hiddenFields?.total_reward && (
{ BigNumber(data.gas_used || 0).toFormat() }
@@ -109,7 +111,7 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
{ totalReward.toFixed(8) }
|
- { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.burnt_fees && (
+ { !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees && (
diff --git a/ui/home/LatestBlocks.pw.tsx b/ui/home/LatestBlocks.pw.tsx
index 0e397533ea..8450ba1aa0 100644
--- a/ui/home/LatestBlocks.pw.tsx
+++ b/ui/home/LatestBlocks.pw.tsx
@@ -42,7 +42,7 @@ test('default view +@mobile +@dark-mode', async({ mount, page }) => {
const testL2 = test.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- context: contextWithEnvs(configs.featureEnvs.rollup) as any,
+ context: contextWithEnvs(configs.featureEnvs.optimisticRollup) as any,
});
testL2('L2 view', async({ mount, page }) => {
diff --git a/ui/home/LatestBlocks.tsx b/ui/home/LatestBlocks.tsx
index f37799a68f..301963a6e2 100644
--- a/ui/home/LatestBlocks.tsx
+++ b/ui/home/LatestBlocks.tsx
@@ -24,7 +24,7 @@ const LatestBlocks = () => {
const isMobile = useIsMobile();
// const blocksMaxCount = isMobile ? 2 : 3;
let blocksMaxCount: number;
- if (config.features.rollup.isEnabled || config.UI.views.block.hiddenFields?.total_reward) {
+ if (config.features.optimisticRollup.isEnabled || config.UI.views.block.hiddenFields?.total_reward) {
blocksMaxCount = isMobile ? 4 : 5;
} else {
blocksMaxCount = isMobile ? 2 : 3;
diff --git a/ui/home/LatestBlocksItem.tsx b/ui/home/LatestBlocksItem.tsx
index d59856105e..4dde7218db 100644
--- a/ui/home/LatestBlocksItem.tsx
+++ b/ui/home/LatestBlocksItem.tsx
@@ -59,14 +59,14 @@ const LatestBlocksItem = ({ block, isLoading }: Props) => {
Txn
{ block.tx_count }
- { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.total_reward && (
+ { !config.features.optimisticRollup.isEnabled && !config.UI.views.block.hiddenFields?.total_reward && (
<>
Reward
{ totalReward.dp(10).toFixed() }
>
) }
- { !config.features.rollup.isEnabled && (
+ { !config.features.optimisticRollup.isEnabled && (
<>
{ getNetworkValidatorTitle() }
{
diff --git a/ui/home/LatestDepositsItem.tsx b/ui/home/LatestDepositsItem.tsx
index 4ef9e6d349..c6f8fdc7b8 100644
--- a/ui/home/LatestDepositsItem.tsx
+++ b/ui/home/LatestDepositsItem.tsx
@@ -15,7 +15,7 @@ import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
type Props = {
item: L2DepositsItem;
diff --git a/ui/home/LatestTxsItem.tsx b/ui/home/LatestTxsItem.tsx
index 13b0b2ae16..9d04d7ea25 100644
--- a/ui/home/LatestTxsItem.tsx
+++ b/ui/home/LatestTxsItem.tsx
@@ -17,9 +17,9 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
+import TxStatus from 'ui/shared/statusTag/TxStatus';
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
-import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxType from 'ui/txs/TxType';
diff --git a/ui/home/LatestTxsItemMobile.tsx b/ui/home/LatestTxsItemMobile.tsx
index 0e375b4e4d..b6ced44217 100644
--- a/ui/home/LatestTxsItemMobile.tsx
+++ b/ui/home/LatestTxsItemMobile.tsx
@@ -16,9 +16,9 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
+import TxStatus from 'ui/shared/statusTag/TxStatus';
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
-import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxType from 'ui/txs/TxType';
diff --git a/ui/home/LatestZkEvmL2Batches.pw.tsx b/ui/home/LatestZkEvmL2Batches.pw.tsx
new file mode 100644
index 0000000000..5a470ac854
--- /dev/null
+++ b/ui/home/LatestZkEvmL2Batches.pw.tsx
@@ -0,0 +1,32 @@
+import { test as base, expect } from '@playwright/experimental-ct-react';
+import React from 'react';
+
+import { txnBatchesData } from 'mocks/zkevmL2txnBatches/zkevmL2txnBatches';
+import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
+import TestApp from 'playwright/TestApp';
+import buildApiUrl from 'playwright/utils/buildApiUrl';
+import * as configs from 'playwright/utils/configs';
+
+import LatestZkEvmL2Batches from './LatestZkEvmL2Batches';
+
+const BATCHES_API_URL = buildApiUrl('homepage_zkevm_l2_batches');
+
+const test = base.extend({
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ context: contextWithEnvs(configs.featureEnvs.zkRollup) as any,
+});
+
+test('default view +@mobile +@dark-mode', async({ mount, page }) => {
+ await page.route(BATCHES_API_URL, (route) => route.fulfill({
+ status: 200,
+ body: JSON.stringify(txnBatchesData),
+ }));
+
+ const component = await mount(
+
+
+ ,
+ );
+
+ await expect(component).toHaveScreenshot();
+});
diff --git a/ui/home/LatestZkEvmL2Batches.tsx b/ui/home/LatestZkEvmL2Batches.tsx
new file mode 100644
index 0000000000..e6a24214b7
--- /dev/null
+++ b/ui/home/LatestZkEvmL2Batches.tsx
@@ -0,0 +1,90 @@
+import { Box, Heading, Flex, Text, VStack } from '@chakra-ui/react';
+import { useQueryClient } from '@tanstack/react-query';
+import { AnimatePresence } from 'framer-motion';
+import React from 'react';
+
+import type { SocketMessage } from 'lib/socket/types';
+import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
+
+import { route } from 'nextjs-routes';
+
+import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
+import useIsMobile from 'lib/hooks/useIsMobile';
+import useSocketChannel from 'lib/socket/useSocketChannel';
+import useSocketMessage from 'lib/socket/useSocketMessage';
+import { ZKEVM_L2_TXN_BATCHES_ITEM } from 'stubs/zkEvmL2';
+import LinkInternal from 'ui/shared/LinkInternal';
+
+import LatestZkevmL2BatchItem from './LatestZkevmL2BatchItem';
+
+const LatestZkEvmL2Batches = () => {
+ const isMobile = useIsMobile();
+ const batchesMaxCount = isMobile ? 2 : 5;
+ const queryClient = useQueryClient();
+
+ const { data, isPlaceholderData, isError } = useApiQuery('homepage_zkevm_l2_batches', {
+ queryOptions: {
+ placeholderData: { items: Array(batchesMaxCount).fill(ZKEVM_L2_TXN_BATCHES_ITEM) },
+ },
+ });
+
+ const handleNewBatchMessage: SocketMessage.NewZkEvmL2Batch['handler'] = React.useCallback((payload) => {
+ queryClient.setQueryData(getResourceKey('homepage_zkevm_l2_batches'), (prevData: { items: Array } | undefined) => {
+ const newItems = prevData?.items ? [ ...prevData.items ] : [];
+
+ if (newItems.some((batch => batch.number === payload.batch.number))) {
+ return { items: newItems };
+ }
+
+ return { items: [ payload.batch, ...newItems ].sort((b1, b2) => b2.number - b1.number).slice(0, batchesMaxCount) };
+ });
+ }, [ queryClient, batchesMaxCount ]);
+
+ const channel = useSocketChannel({
+ topic: 'zkevm_batches:new_zkevm_confirmed_batch',
+ isDisabled: isPlaceholderData || isError,
+ });
+ useSocketMessage({
+ channel,
+ event: 'new_zkevm_confirmed_batch',
+ handler: handleNewBatchMessage,
+ });
+
+ let content;
+
+ if (isError) {
+ content = No data. Please reload page.;
+ }
+
+ if (data) {
+ const dataToShow = data.items.slice(0, batchesMaxCount);
+
+ content = (
+ <>
+
+
+ { dataToShow.map(((batch, index) => (
+
+ ))) }
+
+
+
+ View all batches
+
+ >
+ );
+ }
+
+ return (
+
+ Latest batches
+ { content }
+
+ );
+};
+
+export default LatestZkEvmL2Batches;
diff --git a/ui/home/LatestZkevmL2BatchItem.tsx b/ui/home/LatestZkevmL2BatchItem.tsx
new file mode 100644
index 0000000000..bad789425e
--- /dev/null
+++ b/ui/home/LatestZkevmL2BatchItem.tsx
@@ -0,0 +1,74 @@
+import {
+ Box,
+ Flex,
+ Skeleton,
+} from '@chakra-ui/react';
+import { motion } from 'framer-motion';
+import React from 'react';
+
+import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
+
+import { route } from 'nextjs-routes';
+
+import BlockTimestamp from 'ui/blocks/BlockTimestamp';
+import ZkEvmBatchEntityL2 from 'ui/shared/entities/block/ZkEvmBatchEntityL2';
+import LinkInternal from 'ui/shared/LinkInternal';
+import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus';
+
+type Props = {
+ batch: ZkEvmL2TxnBatchesItem;
+ isLoading?: boolean;
+}
+
+const LatestZkevmL2BatchItem = ({ batch, isLoading }: Props) => {
+ return (
+
+
+
+
+
+
+
+ Txn
+
+
+ { batch.tx_count }
+
+
+
+
+
+
+ );
+};
+
+export default LatestZkevmL2BatchItem;
diff --git a/ui/home/Stats.tsx b/ui/home/Stats.tsx
index c7b317e538..b3fecac9f4 100644
--- a/ui/home/Stats.tsx
+++ b/ui/home/Stats.tsx
@@ -10,6 +10,7 @@ import clockIcon from 'icons/clock-light.svg';
import bitcoinIcon from 'icons/coins/bitcoin.svg';
import gasIcon from 'icons/gas.svg';
import txIcon from 'icons/transactions.svg';
+import batchesIcon from 'icons/txn_batches.svg';
import walletIcon from 'icons/wallet.svg';
import useApiQuery from 'lib/api/useApiQuery';
import { WEI } from 'lib/consts';
@@ -28,7 +29,14 @@ const Stats = () => {
},
});
- if (isError) {
+ const zkEvmLatestBatchQuery = useApiQuery('homepage_zkevm_latest_batch', {
+ queryOptions: {
+ placeholderData: 12345,
+ enabled: config.features.zkEvmRollup.isEnabled,
+ },
+ });
+
+ if (isError || zkEvmLatestBatchQuery.isError) {
return null;
}
@@ -48,13 +56,23 @@ const Stats = () => {
content = (
<>
-
+ { config.features.zkEvmRollup.isEnabled ? (
+
+ ) : (
+
+ ) }
{ hasAvgBlockTime && (
{
const hasAccount = useHasAccount();
- if (config.features.rollup.isEnabled || hasAccount) {
+ if (config.features.optimisticRollup.isEnabled || hasAccount) {
const tabs = [
{ id: 'txn', title: 'Latest txn', component: },
- config.features.rollup.isEnabled && { id: 'deposits', title: 'Deposits (L1→L2 txn)', component: },
+ config.features.optimisticRollup.isEnabled && { id: 'deposits', title: 'Deposits (L1→L2 txn)', component: },
hasAccount && { id: 'watchlist', title: 'Watch list', component: },
].filter(Boolean);
return (
diff --git a/ui/home/__screenshots__/LatestZkEvmL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png b/ui/home/__screenshots__/LatestZkEvmL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png
new file mode 100644
index 0000000000..fcf75937b4
Binary files /dev/null and b/ui/home/__screenshots__/LatestZkEvmL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png differ
diff --git a/ui/home/__screenshots__/LatestZkEvmL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png b/ui/home/__screenshots__/LatestZkEvmL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png
new file mode 100644
index 0000000000..1493485338
Binary files /dev/null and b/ui/home/__screenshots__/LatestZkEvmL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png differ
diff --git a/ui/home/__screenshots__/LatestZkEvmL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png b/ui/home/__screenshots__/LatestZkEvmL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png
new file mode 100644
index 0000000000..db7592d367
Binary files /dev/null and b/ui/home/__screenshots__/LatestZkEvmL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png differ
diff --git a/ui/l2Deposits/DepositsListItem.tsx b/ui/l2Deposits/DepositsListItem.tsx
index e57839ecb8..7e4caefc57 100644
--- a/ui/l2Deposits/DepositsListItem.tsx
+++ b/ui/l2Deposits/DepositsListItem.tsx
@@ -12,7 +12,7 @@ import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
type Props = { item: L2DepositsItem; isLoading?: boolean };
diff --git a/ui/l2Deposits/DepositsTableItem.tsx b/ui/l2Deposits/DepositsTableItem.tsx
index 32bd7eeb2b..99ba9ad465 100644
--- a/ui/l2Deposits/DepositsTableItem.tsx
+++ b/ui/l2Deposits/DepositsTableItem.tsx
@@ -11,7 +11,7 @@ import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
type Props = { item: L2DepositsItem; isLoading?: boolean };
diff --git a/ui/l2OutputRoots/OutputRootsListItem.tsx b/ui/l2OutputRoots/OutputRootsListItem.tsx
index c39996c6f1..4a4a9337d0 100644
--- a/ui/l2OutputRoots/OutputRootsListItem.tsx
+++ b/ui/l2OutputRoots/OutputRootsListItem.tsx
@@ -11,7 +11,7 @@ import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
type Props = { item: L2OutputRootsItem; isLoading?: boolean };
diff --git a/ui/l2OutputRoots/OutputRootsTableItem.tsx b/ui/l2OutputRoots/OutputRootsTableItem.tsx
index bbc011fdb8..e64b613a82 100644
--- a/ui/l2OutputRoots/OutputRootsTableItem.tsx
+++ b/ui/l2OutputRoots/OutputRootsTableItem.tsx
@@ -10,7 +10,7 @@ import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
type Props = { item: L2OutputRootsItem; isLoading?: boolean };
diff --git a/ui/l2TxnBatches/TxnBatchesListItem.tsx b/ui/l2TxnBatches/TxnBatchesListItem.tsx
index 35e2a0426a..3efba7b03e 100644
--- a/ui/l2TxnBatches/TxnBatchesListItem.tsx
+++ b/ui/l2TxnBatches/TxnBatchesListItem.tsx
@@ -13,7 +13,7 @@ import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
type Props = { item: L2TxnBatchesItem; isLoading?: boolean };
diff --git a/ui/l2TxnBatches/TxnBatchesTableItem.tsx b/ui/l2TxnBatches/TxnBatchesTableItem.tsx
index 0dc151d449..8409b8e736 100644
--- a/ui/l2TxnBatches/TxnBatchesTableItem.tsx
+++ b/ui/l2TxnBatches/TxnBatchesTableItem.tsx
@@ -12,7 +12,7 @@ import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkInternal from 'ui/shared/LinkInternal';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
type Props = { item: L2TxnBatchesItem; isLoading?: boolean };
diff --git a/ui/l2Withdrawals/WithdrawalsListItem.tsx b/ui/l2Withdrawals/WithdrawalsListItem.tsx
index 7076255f58..585b3f7688 100644
--- a/ui/l2Withdrawals/WithdrawalsListItem.tsx
+++ b/ui/l2Withdrawals/WithdrawalsListItem.tsx
@@ -11,7 +11,7 @@ import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkExternal from 'ui/shared/LinkExternal';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
type Props = { item: L2WithdrawalsItem; isLoading?: boolean };
diff --git a/ui/l2Withdrawals/WithdrawalsTableItem.tsx b/ui/l2Withdrawals/WithdrawalsTableItem.tsx
index 9f4bc8fc25..6df6254116 100644
--- a/ui/l2Withdrawals/WithdrawalsTableItem.tsx
+++ b/ui/l2Withdrawals/WithdrawalsTableItem.tsx
@@ -10,7 +10,7 @@ import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkExternal from 'ui/shared/LinkExternal';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
type Props = { item: L2WithdrawalsItem; isLoading?: boolean };
diff --git a/ui/pages/Home.tsx b/ui/pages/Home.tsx
index dd5424b7f6..dfd6c958af 100644
--- a/ui/pages/Home.tsx
+++ b/ui/pages/Home.tsx
@@ -4,6 +4,7 @@ import React from 'react';
import config from 'configs/app';
import ChainIndicators from 'ui/home/indicators/ChainIndicators';
import LatestBlocks from 'ui/home/LatestBlocks';
+import LatestZkEvmL2Batches from 'ui/home/LatestZkEvmL2Batches';
import Stats from 'ui/home/Stats';
import Transactions from 'ui/home/Transactions';
import AdBanner from 'ui/shared/ad/AdBanner';
@@ -43,7 +44,7 @@ const Home = () => {
-
+ { config.features.zkEvmRollup.isEnabled ? : }
diff --git a/ui/pages/L2Deposits.pw.tsx b/ui/pages/L2Deposits.pw.tsx
index e915c9fa3d..94c62178b8 100644
--- a/ui/pages/L2Deposits.pw.tsx
+++ b/ui/pages/L2Deposits.pw.tsx
@@ -14,7 +14,7 @@ const DEPOSITS_COUNT_API_URL = buildApiUrl('l2_deposits_count');
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- context: contextWithEnvs(configs.featureEnvs.rollup) as any,
+ context: contextWithEnvs(configs.featureEnvs.optimisticRollup) as any,
});
test('base view +@mobile', async({ mount, page }) => {
diff --git a/ui/pages/L2OutputRoots.pw.tsx b/ui/pages/L2OutputRoots.pw.tsx
index 34136616a1..d4f85eec48 100644
--- a/ui/pages/L2OutputRoots.pw.tsx
+++ b/ui/pages/L2OutputRoots.pw.tsx
@@ -11,7 +11,7 @@ import OutputRoots from './L2OutputRoots';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- context: contextWithEnvs(configs.featureEnvs.rollup) as any,
+ context: contextWithEnvs(configs.featureEnvs.optimisticRollup) as any,
});
const OUTPUT_ROOTS_API_URL = buildApiUrl('l2_output_roots');
diff --git a/ui/pages/L2TxnBatches.pw.tsx b/ui/pages/L2TxnBatches.pw.tsx
index 6e199d1e07..4216bb0c3b 100644
--- a/ui/pages/L2TxnBatches.pw.tsx
+++ b/ui/pages/L2TxnBatches.pw.tsx
@@ -11,7 +11,7 @@ import L2TxnBatches from './L2TxnBatches';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- context: contextWithEnvs(configs.featureEnvs.rollup) as any,
+ context: contextWithEnvs(configs.featureEnvs.optimisticRollup) as any,
});
const TXN_BATCHES_API_URL = buildApiUrl('l2_txn_batches');
diff --git a/ui/pages/L2Withdrawals.pw.tsx b/ui/pages/L2Withdrawals.pw.tsx
index aceccdc935..5ea8399a3f 100644
--- a/ui/pages/L2Withdrawals.pw.tsx
+++ b/ui/pages/L2Withdrawals.pw.tsx
@@ -11,7 +11,7 @@ import L2Withdrawals from './L2Withdrawals';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- context: contextWithEnvs(configs.featureEnvs.rollup) as any,
+ context: contextWithEnvs(configs.featureEnvs.optimisticRollup) as any,
});
const WITHDRAWALS_API_URL = buildApiUrl('l2_withdrawals');
diff --git a/ui/pages/ZkEvmL2TxnBatch.pw.tsx b/ui/pages/ZkEvmL2TxnBatch.pw.tsx
new file mode 100644
index 0000000000..852ecc3afb
--- /dev/null
+++ b/ui/pages/ZkEvmL2TxnBatch.pw.tsx
@@ -0,0 +1,70 @@
+import { test as base, expect, devices } from '@playwright/experimental-ct-react';
+import React from 'react';
+
+import { txnBatchData } from 'mocks/zkevmL2txnBatches/zkevmL2txnBatch';
+import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
+import TestApp from 'playwright/TestApp';
+import buildApiUrl from 'playwright/utils/buildApiUrl';
+import * as configs from 'playwright/utils/configs';
+
+import ZkEvmL2TxnBatch from './ZkEvmL2TxnBatch';
+
+const test = base.extend({
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ context: contextWithEnvs(configs.featureEnvs.zkRollup) as any,
+});
+
+const hooksConfig = {
+ router: {
+ query: { number: '5' },
+ },
+};
+
+const BATCH_API_URL = buildApiUrl('zkevm_l2_txn_batch', { number: '5' });
+
+test('base view', async({ mount, page }) => {
+ test.slow();
+ await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
+ status: 200,
+ body: '',
+ }));
+
+ await page.route(BATCH_API_URL, (route) => route.fulfill({
+ status: 200,
+ body: JSON.stringify(txnBatchData),
+ }));
+
+ const component = await mount(
+
+
+ ,
+ { hooksConfig },
+ );
+
+ await expect(component).toHaveScreenshot();
+});
+
+test.describe('mobile', () => {
+ test.use({ viewport: devices['iPhone 13 Pro'].viewport });
+ test('base view', async({ mount, page }) => {
+ test.slow();
+ await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
+ status: 200,
+ body: '',
+ }));
+
+ await page.route(BATCH_API_URL, (route) => route.fulfill({
+ status: 200,
+ body: JSON.stringify(txnBatchData),
+ }));
+
+ const component = await mount(
+
+
+ ,
+ { hooksConfig },
+ );
+
+ await expect(component).toHaveScreenshot();
+ });
+});
diff --git a/ui/pages/ZkEvmL2TxnBatch.tsx b/ui/pages/ZkEvmL2TxnBatch.tsx
new file mode 100644
index 0000000000..e2cb9811ff
--- /dev/null
+++ b/ui/pages/ZkEvmL2TxnBatch.tsx
@@ -0,0 +1,86 @@
+import { useRouter } from 'next/router';
+import React from 'react';
+
+import type { RoutedTab } from 'ui/shared/Tabs/types';
+
+import useApiQuery from 'lib/api/useApiQuery';
+import { useAppContext } from 'lib/contexts/app';
+import getQueryParamString from 'lib/router/getQueryParamString';
+import { TX_ZKEVM_L2 } from 'stubs/tx';
+import { generateListStub } from 'stubs/utils';
+import { ZKEVM_L2_TXN_BATCH } from 'stubs/zkEvmL2';
+import TextAd from 'ui/shared/ad/TextAd';
+import PageTitle from 'ui/shared/Page/PageTitle';
+import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
+import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
+import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
+import TxsContent from 'ui/txs/TxsContent';
+import ZkEvmL2TxnBatchDetails from 'ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails';
+
+const ZkEvmL2TxnBatch = () => {
+ const router = useRouter();
+ const appProps = useAppContext();
+ const number = getQueryParamString(router.query.number);
+ const tab = getQueryParamString(router.query.tab);
+
+ const batchQuery = useApiQuery('zkevm_l2_txn_batch', {
+ pathParams: { number },
+ queryOptions: {
+ enabled: Boolean(number),
+ placeholderData: ZKEVM_L2_TXN_BATCH,
+ },
+ });
+
+ const batchTxsQuery = useQueryWithPages({
+ resourceName: 'zkevm_l2_txn_batch_txs',
+ pathParams: { number },
+ options: {
+ enabled: Boolean(!batchQuery.isPlaceholderData && batchQuery.data?.number && tab === 'txs'),
+ // there is no pagination in zkevm_l2_txn_batch_txs
+ placeholderData: generateListStub<'zkevm_l2_txn_batch_txs'>(TX_ZKEVM_L2, 50, { next_page_params: null }),
+ },
+ });
+
+ if (!number) {
+ throw new Error('Tx batch not found', { cause: { status: 404 } });
+ }
+
+ if (batchQuery.isError) {
+ throw new Error(undefined, { cause: batchQuery.error });
+ }
+
+ const tabs: Array = React.useMemo(() => ([
+ { id: 'index', title: 'Details', component: },
+ { id: 'txs', title: 'Transactions', component: },
+ ].filter(Boolean)), [ batchQuery, batchTxsQuery ]);
+
+ const backLink = React.useMemo(() => {
+ const hasGoBackLink = appProps.referrer && appProps.referrer.includes('/zkevm_l2_txn_batches');
+
+ if (!hasGoBackLink) {
+ return;
+ }
+
+ return {
+ label: 'Back to tx batches list',
+ url: appProps.referrer,
+ };
+ }, [ appProps.referrer ]);
+
+ return (
+ <>
+
+
+ { batchQuery.isPlaceholderData ? : (
+
+ ) }
+ >
+ );
+};
+
+export default ZkEvmL2TxnBatch;
diff --git a/ui/pages/ZkEvmL2TxnBatches.pw.tsx b/ui/pages/ZkEvmL2TxnBatches.pw.tsx
new file mode 100644
index 0000000000..8c403a4620
--- /dev/null
+++ b/ui/pages/ZkEvmL2TxnBatches.pw.tsx
@@ -0,0 +1,43 @@
+import { test as base, expect } from '@playwright/experimental-ct-react';
+import React from 'react';
+
+import { txnBatchesData } from 'mocks/zkevmL2txnBatches/zkevmL2txnBatches';
+import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
+import TestApp from 'playwright/TestApp';
+import buildApiUrl from 'playwright/utils/buildApiUrl';
+import * as configs from 'playwright/utils/configs';
+
+import ZkEvmL2TxnBatches from './ZkEvmL2TxnBatches';
+
+const test = base.extend({
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ context: contextWithEnvs(configs.featureEnvs.zkRollup) as any,
+});
+
+const BATCHES_API_URL = buildApiUrl('zkevm_l2_txn_batches');
+const BATCHES_COUNTERS_API_URL = buildApiUrl('zkevm_l2_txn_batches_count');
+
+test('base view +@mobile', async({ mount, page }) => {
+ await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
+ status: 200,
+ body: '',
+ }));
+
+ await page.route(BATCHES_API_URL, (route) => route.fulfill({
+ status: 200,
+ body: JSON.stringify(txnBatchesData),
+ }));
+
+ await page.route(BATCHES_COUNTERS_API_URL, (route) => route.fulfill({
+ status: 200,
+ body: '9927',
+ }));
+
+ const component = await mount(
+
+
+ ,
+ );
+
+ await expect(component).toHaveScreenshot();
+});
diff --git a/ui/pages/ZkEvmL2TxnBatches.tsx b/ui/pages/ZkEvmL2TxnBatches.tsx
new file mode 100644
index 0000000000..562be180c9
--- /dev/null
+++ b/ui/pages/ZkEvmL2TxnBatches.tsx
@@ -0,0 +1,83 @@
+import { Hide, Show, Skeleton, Text } from '@chakra-ui/react';
+import React from 'react';
+
+import useApiQuery from 'lib/api/useApiQuery';
+import { generateListStub } from 'stubs/utils';
+import { ZKEVM_L2_TXN_BATCHES_ITEM } from 'stubs/zkEvmL2';
+import DataListDisplay from 'ui/shared/DataListDisplay';
+import PageTitle from 'ui/shared/Page/PageTitle';
+import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
+import StickyPaginationWithText from 'ui/shared/StickyPaginationWithText';
+import ZkEvmTxnBatchesListItem from 'ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesListItem';
+import ZkEvmTxnBatchesTable from 'ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesTable';
+
+const ZkEvmL2TxnBatches = () => {
+ const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
+ resourceName: 'zkevm_l2_txn_batches',
+ options: {
+ placeholderData: generateListStub<'zkevm_l2_txn_batches'>(
+ ZKEVM_L2_TXN_BATCHES_ITEM,
+ 50,
+ {
+ next_page_params: {
+ items_count: 50,
+ number: 9045200,
+ },
+ },
+ ),
+ },
+ });
+
+ const countersQuery = useApiQuery('zkevm_l2_txn_batches_count', {
+ queryOptions: {
+ placeholderData: 5231746,
+ },
+ });
+
+ const content = data?.items ? (
+ <>
+
+ { data.items.map(((item, index) => (
+
+ ))) }
+
+
+ >
+ ) : null;
+
+ const text = (() => {
+ if (countersQuery.isError || isError || !data?.items.length) {
+ return null;
+ }
+
+ return (
+
+ Tx batch
+ #{ data.items[0].number } to
+ #{ data.items[data.items.length - 1].number }
+ (total of { countersQuery.data?.toLocaleString() } batches)
+
+ );
+ })();
+
+ const actionBar = ;
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default ZkEvmL2TxnBatches;
diff --git a/ui/pages/__screenshots__/L2Withdrawals.pw.tsx_default_base-view-mobile-1.png b/ui/pages/__screenshots__/L2Withdrawals.pw.tsx_default_base-view-mobile-1.png
index 61bb5ddbb9..1d1532ab8c 100644
Binary files a/ui/pages/__screenshots__/L2Withdrawals.pw.tsx_default_base-view-mobile-1.png and b/ui/pages/__screenshots__/L2Withdrawals.pw.tsx_default_base-view-mobile-1.png differ
diff --git a/ui/pages/__screenshots__/ZkEvmL2TxnBatch.pw.tsx_default_base-view-1.png b/ui/pages/__screenshots__/ZkEvmL2TxnBatch.pw.tsx_default_base-view-1.png
new file mode 100644
index 0000000000..c59f29f714
Binary files /dev/null and b/ui/pages/__screenshots__/ZkEvmL2TxnBatch.pw.tsx_default_base-view-1.png differ
diff --git a/ui/pages/__screenshots__/ZkEvmL2TxnBatch.pw.tsx_default_mobile-base-view-1.png b/ui/pages/__screenshots__/ZkEvmL2TxnBatch.pw.tsx_default_mobile-base-view-1.png
new file mode 100644
index 0000000000..b0fd12db39
Binary files /dev/null and b/ui/pages/__screenshots__/ZkEvmL2TxnBatch.pw.tsx_default_mobile-base-view-1.png differ
diff --git a/ui/pages/__screenshots__/ZkEvmL2TxnBatches.pw.tsx_default_base-view-mobile-1.png b/ui/pages/__screenshots__/ZkEvmL2TxnBatches.pw.tsx_default_base-view-mobile-1.png
new file mode 100644
index 0000000000..99c8067c46
Binary files /dev/null and b/ui/pages/__screenshots__/ZkEvmL2TxnBatches.pw.tsx_default_base-view-mobile-1.png differ
diff --git a/ui/pages/__screenshots__/ZkEvmL2TxnBatches.pw.tsx_mobile_base-view-mobile-1.png b/ui/pages/__screenshots__/ZkEvmL2TxnBatches.pw.tsx_mobile_base-view-mobile-1.png
new file mode 100644
index 0000000000..8052eb9ef9
Binary files /dev/null and b/ui/pages/__screenshots__/ZkEvmL2TxnBatches.pw.tsx_mobile_base-view-mobile-1.png differ
diff --git a/ui/shared/DetailsInfoItem.tsx b/ui/shared/DetailsInfoItem.tsx
index 18c64f6c2e..1aa81cae90 100644
--- a/ui/shared/DetailsInfoItem.tsx
+++ b/ui/shared/DetailsInfoItem.tsx
@@ -6,7 +6,7 @@ import Hint from 'ui/shared/Hint';
interface Props extends Omit, 'title'> {
title: React.ReactNode;
- hint: string;
+ hint?: string;
children: React.ReactNode;
note?: string;
isLoading?: boolean;
@@ -17,7 +17,7 @@ const DetailsInfoItem = ({ title, hint, note, children, id, isLoading, ...styles
<>
-
+ { hint && }
{ title }
diff --git a/ui/shared/LinkExternal.tsx b/ui/shared/LinkExternal.tsx
index 2ffd5f4d23..64401dab4a 100644
--- a/ui/shared/LinkExternal.tsx
+++ b/ui/shared/LinkExternal.tsx
@@ -17,8 +17,6 @@ const LinkExternal = ({ href, children, className, isLoading, variant }: Props)
const styleProps: ChakraProps = (() => {
const commonProps = {
- fontSize: 'sm',
- lineHeight: 5,
display: 'inline-block',
alignItems: 'center',
};
diff --git a/ui/shared/entities/address/AddressEntityL1.tsx b/ui/shared/entities/address/AddressEntityL1.tsx
index 877721614c..5df57f35d7 100644
--- a/ui/shared/entities/address/AddressEntityL1.tsx
+++ b/ui/shared/entities/address/AddressEntityL1.tsx
@@ -7,7 +7,7 @@ import config from 'configs/app';
import * as AddressEntity from './AddressEntity';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
const AddressEntityL1 = (props: AddressEntity.EntityProps) => {
if (!feature.isEnabled) {
diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_external-link-1.png b/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_external-link-1.png
index eadd0597c5..5d824ab018 100644
Binary files a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_external-link-1.png and b/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_external-link-1.png differ
diff --git a/ui/shared/entities/block/BlockEntityL1.tsx b/ui/shared/entities/block/BlockEntityL1.tsx
index 3275f62f12..6816ac8e8a 100644
--- a/ui/shared/entities/block/BlockEntityL1.tsx
+++ b/ui/shared/entities/block/BlockEntityL1.tsx
@@ -8,7 +8,7 @@ import config from 'configs/app';
import * as BlockEntity from './BlockEntity';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
const BlockEntityL1 = (props: BlockEntity.EntityProps) => {
const linkProps = _omit(props, [ 'className' ]);
diff --git a/ui/shared/entities/block/BlockEntityL2.tsx b/ui/shared/entities/block/BlockEntityL2.tsx
index 8cbdf811c6..aa11730764 100644
--- a/ui/shared/entities/block/BlockEntityL2.tsx
+++ b/ui/shared/entities/block/BlockEntityL2.tsx
@@ -7,7 +7,7 @@ import txBatchIcon from 'icons/txn_batches_slim.svg';
import * as BlockEntity from './BlockEntity';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup;
const BlockEntityL2 = (props: BlockEntity.EntityProps) => {
const linkProps = _omit(props, [ 'className' ]);
diff --git a/ui/shared/entities/block/ZkEvmBatchEntityL2.tsx b/ui/shared/entities/block/ZkEvmBatchEntityL2.tsx
new file mode 100644
index 0000000000..5ad926c1da
--- /dev/null
+++ b/ui/shared/entities/block/ZkEvmBatchEntityL2.tsx
@@ -0,0 +1,35 @@
+import { chakra } from '@chakra-ui/react';
+import _omit from 'lodash/omit';
+import React from 'react';
+
+import { route } from 'nextjs-routes';
+
+import config from 'configs/app';
+import txBatchIcon from 'icons/txn_batches_slim.svg';
+
+import * as BlockEntity from './BlockEntity';
+
+const feature = config.features.zkEvmRollup;
+
+const ZkEvmBatchEntityL2 = (props: BlockEntity.EntityProps) => {
+ const linkProps = _omit(props, [ 'className' ]);
+ const partsProps = _omit(props, [ 'className', 'onClick' ]);
+
+ if (!feature.isEnabled) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default chakra(ZkEvmBatchEntityL2);
diff --git a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_dark-color-mode_external-link-dark-mode-1.png b/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_dark-color-mode_external-link-dark-mode-1.png
index 75c402e818..af7a2cbe1a 100644
Binary files a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_dark-color-mode_external-link-dark-mode-1.png and b/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_dark-color-mode_external-link-dark-mode-1.png differ
diff --git a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_external-link-dark-mode-1.png b/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_external-link-dark-mode-1.png
index 089ee7e92b..40b249a491 100644
Binary files a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_external-link-dark-mode-1.png and b/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_external-link-dark-mode-1.png differ
diff --git a/ui/shared/entities/tx/TxEntityL1.tsx b/ui/shared/entities/tx/TxEntityL1.tsx
index 90f39afaa7..3b72f1894c 100644
--- a/ui/shared/entities/tx/TxEntityL1.tsx
+++ b/ui/shared/entities/tx/TxEntityL1.tsx
@@ -8,7 +8,7 @@ import config from 'configs/app';
import * as TxEntity from './TxEntity';
-const feature = config.features.rollup;
+const feature = config.features.optimisticRollup.isEnabled ? config.features.optimisticRollup : config.features.zkEvmRollup;
const TxEntityL1 = (props: TxEntity.EntityProps) => {
const partsProps = _omit(props, [ 'className', 'onClick' ]);
diff --git a/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_external-link-1.png b/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_external-link-1.png
index 7dca66fece..be6e12356d 100644
Binary files a/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_external-link-1.png and b/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_external-link-1.png differ
diff --git a/ui/shared/statusTag/StatusTag.pw.tsx b/ui/shared/statusTag/StatusTag.pw.tsx
new file mode 100644
index 0000000000..d289c54b5c
--- /dev/null
+++ b/ui/shared/statusTag/StatusTag.pw.tsx
@@ -0,0 +1,37 @@
+import { test, expect } from '@playwright/experimental-ct-react';
+import React from 'react';
+
+import TestApp from 'playwright/TestApp';
+
+import StatusTag from './StatusTag';
+
+test('ok status', async({ page, mount }) => {
+ await mount(
+
+
+ ,
+ );
+
+ await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 75, height: 30 } });
+});
+
+test('error status', async({ page, mount }) => {
+ await mount(
+
+
+ ,
+ );
+
+ await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 75, height: 30 } });
+
+});
+
+test('pending status', async({ page, mount }) => {
+ await mount(
+
+
+ ,
+ );
+
+ await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 75, height: 30 } });
+});
diff --git a/ui/shared/TxStatus.tsx b/ui/shared/statusTag/StatusTag.tsx
similarity index 73%
rename from ui/shared/TxStatus.tsx
rename to ui/shared/statusTag/StatusTag.tsx
index a77e0133cf..1075b1dfe7 100644
--- a/ui/shared/TxStatus.tsx
+++ b/ui/shared/statusTag/StatusTag.tsx
@@ -1,37 +1,34 @@
import { TagLabel, TagLeftIcon, Tooltip } from '@chakra-ui/react';
import React from 'react';
-import type { Transaction } from 'types/api/transaction';
-
import errorIcon from 'icons/status/error.svg';
import pendingIcon from 'icons/status/pending.svg';
import successIcon from 'icons/status/success.svg';
import Tag from 'ui/shared/chakra/Tag';
+export type StatusTagType = 'ok' | 'error' | 'pending';
+
export interface Props {
- status: Transaction['status'];
+ type: 'ok' | 'error' | 'pending';
+ text: string;
errorText?: string | null;
isLoading?: boolean;
}
-const TxStatus = ({ status, errorText, isLoading }: Props) => {
- let label;
+const StatusTag = ({ type, text, errorText, isLoading }: Props) => {
let icon;
let colorScheme;
- switch (status) {
+ switch (type) {
case 'ok':
- label = 'Success';
icon = successIcon;
colorScheme = 'green';
break;
case 'error':
- label = 'Failed';
icon = errorIcon;
colorScheme = 'red';
break;
- case null:
- label = 'Pending';
+ case 'pending':
icon = pendingIcon;
// FIXME: it's not gray on mockups
// need to implement new color scheme or redefine colors here
@@ -43,10 +40,10 @@ const TxStatus = ({ status, errorText, isLoading }: Props) => {
- { label }
+ { text }
);
};
-export default TxStatus;
+export default StatusTag;
diff --git a/ui/shared/statusTag/TxStatus.tsx b/ui/shared/statusTag/TxStatus.tsx
new file mode 100644
index 0000000000..9fcfec58d4
--- /dev/null
+++ b/ui/shared/statusTag/TxStatus.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+
+import type { Transaction } from 'types/api/transaction';
+
+import type { StatusTagType } from './StatusTag';
+import StatusTag from './StatusTag';
+
+export interface Props {
+ status: Transaction['status'];
+ errorText?: string | null;
+ isLoading?: boolean;
+}
+
+const TxStatus = ({ status, errorText, isLoading }: Props) => {
+ let text;
+ let type: StatusTagType;
+
+ switch (status) {
+ case 'ok':
+ text = 'Success';
+ type = 'ok';
+ break;
+ case 'error':
+ text = 'Failed';
+ type = 'error';
+ break;
+ case null:
+ text = 'Pending';
+ type = 'pending';
+ break;
+ }
+
+ return ;
+};
+
+export default TxStatus;
diff --git a/ui/shared/statusTag/ZkEvmL2TxnBatchStatus.tsx b/ui/shared/statusTag/ZkEvmL2TxnBatchStatus.tsx
new file mode 100644
index 0000000000..f7601bde46
--- /dev/null
+++ b/ui/shared/statusTag/ZkEvmL2TxnBatchStatus.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+
+import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
+
+import type { StatusTagType } from './StatusTag';
+import StatusTag from './StatusTag';
+
+export interface Props {
+ status: ZkEvmL2TxnBatchesItem['status'];
+ isLoading?: boolean;
+}
+
+const ZkEvmL2TxnBatchStatus = ({ status, isLoading }: Props) => {
+ let type: StatusTagType;
+
+ switch (status) {
+ case 'L1 Sequence Confirmed':
+ case 'Finalized':
+ type = 'ok';
+ break;
+ default:
+ type = 'pending';
+ break;
+ }
+
+ return ;
+};
+
+export default ZkEvmL2TxnBatchStatus;
diff --git a/ui/shared/statusTag/__screenshots__/StatusTag.pw.tsx_default_error-status-1.png b/ui/shared/statusTag/__screenshots__/StatusTag.pw.tsx_default_error-status-1.png
new file mode 100644
index 0000000000..456a05197e
Binary files /dev/null and b/ui/shared/statusTag/__screenshots__/StatusTag.pw.tsx_default_error-status-1.png differ
diff --git a/ui/shared/statusTag/__screenshots__/StatusTag.pw.tsx_default_ok-status-1.png b/ui/shared/statusTag/__screenshots__/StatusTag.pw.tsx_default_ok-status-1.png
new file mode 100644
index 0000000000..1f91068cef
Binary files /dev/null and b/ui/shared/statusTag/__screenshots__/StatusTag.pw.tsx_default_ok-status-1.png differ
diff --git a/ui/shared/statusTag/__screenshots__/StatusTag.pw.tsx_default_pending-status-1.png b/ui/shared/statusTag/__screenshots__/StatusTag.pw.tsx_default_pending-status-1.png
new file mode 100644
index 0000000000..d5d9f0cec1
Binary files /dev/null and b/ui/shared/statusTag/__screenshots__/StatusTag.pw.tsx_default_pending-status-1.png differ
diff --git a/ui/shared/verificationSteps/VerificationStep.tsx b/ui/shared/verificationSteps/VerificationStep.tsx
new file mode 100644
index 0000000000..efc9a294e8
--- /dev/null
+++ b/ui/shared/verificationSteps/VerificationStep.tsx
@@ -0,0 +1,26 @@
+import { Text, Icon, HStack } from '@chakra-ui/react';
+import React from 'react';
+
+import arrowIcon from 'icons/arrows/east.svg';
+import finalizedIcon from 'icons/finalized.svg';
+import unfinalizedIcon from 'icons/unfinalized.svg';
+
+type Props = {
+ step: string;
+ isLast: boolean;
+ isPassed: boolean;
+}
+
+const VerificationStep = ({ step, isLast, isPassed }: Props) => {
+ const stepColor = isPassed ? 'green.500' : 'text_secondary';
+
+ return (
+
+
+ { step }
+ { !isLast && }
+
+ );
+};
+
+export default VerificationStep;
diff --git a/ui/shared/verificationSteps/VerificationSteps.pw.tsx b/ui/shared/verificationSteps/VerificationSteps.pw.tsx
new file mode 100644
index 0000000000..21e2f23518
--- /dev/null
+++ b/ui/shared/verificationSteps/VerificationSteps.pw.tsx
@@ -0,0 +1,33 @@
+import { Box } from '@chakra-ui/react';
+import { test, expect } from '@playwright/experimental-ct-react';
+import React from 'react';
+
+import { ZKEVM_L2_TX_STATUSES } from 'types/api/transaction';
+
+import TestApp from 'playwright/TestApp';
+
+import VerificationSteps from './VerificationSteps';
+
+test('first step +@mobile +@dark-mode', async({ mount }) => {
+
+ const component = await mount(
+
+
+
+
+ ,
+ );
+
+ await expect(component).toHaveScreenshot();
+});
+
+test('second status', async({ mount }) => {
+
+ const component = await mount(
+
+
+ ,
+ );
+
+ await expect(component).toHaveScreenshot();
+});
diff --git a/ui/shared/verificationSteps/VerificationSteps.tsx b/ui/shared/verificationSteps/VerificationSteps.tsx
new file mode 100644
index 0000000000..a07ec743c8
--- /dev/null
+++ b/ui/shared/verificationSteps/VerificationSteps.tsx
@@ -0,0 +1,30 @@
+import { Skeleton } from '@chakra-ui/react';
+import React from 'react';
+
+import VerificationStep from './VerificationStep';
+
+export interface Props {
+ step: T;
+ steps: Array;
+ isLoading?: boolean;
+}
+
+const VerificationSteps = ({ step, steps, isLoading }: Props) => {
+ const currentStepIndex = steps.indexOf(step);
+
+ return (
+
+ { steps.map((step, index) => (
+
+ )) }
+
+ );
+};
+
+export default VerificationSteps;
diff --git a/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_dark-color-mode_first-step-mobile-dark-mode-1.png b/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_dark-color-mode_first-step-mobile-dark-mode-1.png
new file mode 100644
index 0000000000..1b723b918c
Binary files /dev/null and b/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_dark-color-mode_first-step-mobile-dark-mode-1.png differ
diff --git a/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_default_first-step-mobile-dark-mode-1.png b/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_default_first-step-mobile-dark-mode-1.png
new file mode 100644
index 0000000000..ec5715c72c
Binary files /dev/null and b/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_default_first-step-mobile-dark-mode-1.png differ
diff --git a/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_default_second-status-1.png b/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_default_second-status-1.png
new file mode 100644
index 0000000000..bed6995133
Binary files /dev/null and b/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_default_second-status-1.png differ
diff --git a/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_mobile_first-step-mobile-dark-mode-1.png b/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_mobile_first-step-mobile-dark-mode-1.png
new file mode 100644
index 0000000000..cf24c70de8
Binary files /dev/null and b/ui/shared/verificationSteps/__screenshots__/VerificationSteps.pw.tsx_mobile_first-step-mobile-dark-mode-1.png differ
diff --git a/ui/token/TokenVerifiedInfo.tsx b/ui/token/TokenVerifiedInfo.tsx
index 13765829dc..136929caf9 100644
--- a/ui/token/TokenVerifiedInfo.tsx
+++ b/ui/token/TokenVerifiedInfo.tsx
@@ -40,7 +40,7 @@ const TokenVerifiedInfo = ({ verifiedInfoQuery }: Props) => {
try {
const url = new URL(data.projectWebsite);
return (
-
+
{ url.host }
);
diff --git a/ui/tokenInstance/__screenshots__/TokenInstanceMetadata.pw.tsx_default_base-view-mobile-1.png b/ui/tokenInstance/__screenshots__/TokenInstanceMetadata.pw.tsx_default_base-view-mobile-1.png
index 10b3d019cc..e8099dc08a 100644
Binary files a/ui/tokenInstance/__screenshots__/TokenInstanceMetadata.pw.tsx_default_base-view-mobile-1.png and b/ui/tokenInstance/__screenshots__/TokenInstanceMetadata.pw.tsx_default_base-view-mobile-1.png differ
diff --git a/ui/tokenInstance/__screenshots__/TokenInstanceMetadata.pw.tsx_mobile_base-view-mobile-1.png b/ui/tokenInstance/__screenshots__/TokenInstanceMetadata.pw.tsx_mobile_base-view-mobile-1.png
index fe13eeb41b..a14681595e 100644
Binary files a/ui/tokenInstance/__screenshots__/TokenInstanceMetadata.pw.tsx_mobile_base-view-mobile-1.png and b/ui/tokenInstance/__screenshots__/TokenInstanceMetadata.pw.tsx_mobile_base-view-mobile-1.png differ
diff --git a/ui/tokenInstance/details/TokenInstanceMetadataInfo.tsx b/ui/tokenInstance/details/TokenInstanceMetadataInfo.tsx
index 17720a0aaa..85dd53856c 100644
--- a/ui/tokenInstance/details/TokenInstanceMetadataInfo.tsx
+++ b/ui/tokenInstance/details/TokenInstanceMetadataInfo.tsx
@@ -33,6 +33,8 @@ const Item = ({ data, isLoading }: ItemProps) => {
w="100%"
overflow="hidden"
href={ data.value }
+ fontSize="sm"
+ lineHeight={ 5 }
>
diff --git a/ui/tx/TxDetails.pw.tsx b/ui/tx/TxDetails.pw.tsx
index 8b2a19aa2b..2c59d2a55c 100644
--- a/ui/tx/TxDetails.pw.tsx
+++ b/ui/tx/TxDetails.pw.tsx
@@ -155,7 +155,7 @@ test('with actions uniswap +@mobile +@dark-mode', async({ mount, page }) => {
const l2Test = test.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- context: contextWithEnvs(configs.featureEnvs.rollup) as any,
+ context: contextWithEnvs(configs.featureEnvs.optimisticRollup) as any,
});
l2Test('l2', async({ mount, page }) => {
diff --git a/ui/tx/TxDetails.tsx b/ui/tx/TxDetails.tsx
index 06e979f2a6..fbbbd29da2 100644
--- a/ui/tx/TxDetails.tsx
+++ b/ui/tx/TxDetails.tsx
@@ -17,6 +17,8 @@ import BigNumber from 'bignumber.js';
import React from 'react';
import { scroller, Element } from 'react-scroll';
+import { ZKEVM_L2_TX_STATUSES } from 'types/api/transaction';
+
import { route } from 'nextjs-routes';
import config from 'configs/app';
@@ -38,13 +40,15 @@ import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
+import ZkEvmBatchEntityL2 from 'ui/shared/entities/block/ZkEvmBatchEntityL2';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
import RawInputData from 'ui/shared/RawInputData';
+import TxStatus from 'ui/shared/statusTag/TxStatus';
import TextSeparator from 'ui/shared/TextSeparator';
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
-import TxStatus from 'ui/shared/TxStatus';
import Utilization from 'ui/shared/Utilization/Utilization';
+import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
import TxDetailsActions from 'ui/tx/details/TxDetailsActions';
import TxDetailsFeePerGas from 'ui/tx/details/TxDetailsFeePerGas';
import TxDetailsGasPrice from 'ui/tx/details/TxDetailsGasPrice';
@@ -141,7 +145,7 @@ const TxDetails = () => {
@@ -152,6 +156,15 @@ const TxDetails = () => {
) }
+ { data.zkevm_status && (
+
+
+
+ ) }
{ data.revert_reason && (
{
>
) }
+ { data.zkevm_batch_number && (
+
+
+
+ ) }
{ data.timestamp && (
{
+ { data.zkevm_sequence_hash && (
+
+
+
+
+
+
+ ) }
+ { data.zkevm_verify_hash && (
+
+
+
+
+
+
+
+
+ ) }
+
+ { (data.zkevm_batch_number || data.zkevm_verify_hash) && }
+
{ !config.UI.views.tx.hiddenFields?.value && (
{
) }
) }
- { data.tx_burnt_fee && !config.UI.views.tx.hiddenFields?.burnt_fees && !config.features.rollup.isEnabled && (
+ { data.tx_burnt_fee && !config.UI.views.tx.hiddenFields?.burnt_fees && !config.features.optimisticRollup.isEnabled && (
{
/>
) }
- { config.features.rollup.isEnabled && (
+ { config.features.optimisticRollup.isEnabled && (
<>
{ data.l1_gas_used && (
void;
@@ -34,7 +35,7 @@ export default function useFetchTxInfo({ onTxStatusUpdate, updateDelay }: Params
queryOptions: {
enabled: Boolean(hash),
refetchOnMount: false,
- placeholderData: TX,
+ placeholderData: config.features.zkEvmRollup.isEnabled ? TX_ZKEVM_L2 : TX,
},
});
const { data, isError, isLoading } = queryResult;
diff --git a/ui/txs/TxsContent.tsx b/ui/txs/TxsContent.tsx
index db2e82f199..fb81ae48b3 100644
--- a/ui/txs/TxsContent.tsx
+++ b/ui/txs/TxsContent.tsx
@@ -15,7 +15,8 @@ import TxsTable from './TxsTable';
import useTxsSort from './useTxsSort';
type Props = {
- query: QueryWithPagesResult<'txs_validated' | 'txs_pending'> | QueryWithPagesResult<'txs_watchlist'> | QueryWithPagesResult<'block_txs'>;
+ // eslint-disable-next-line max-len
+ query: QueryWithPagesResult<'txs_validated' | 'txs_pending'> | QueryWithPagesResult<'txs_watchlist'> | QueryWithPagesResult<'block_txs'> | QueryWithPagesResult<'zkevm_l2_txn_batch_txs'>;
showBlockInfo?: boolean;
showSocketInfo?: boolean;
socketInfoAlert?: string;
diff --git a/ui/txs/TxsListItem.tsx b/ui/txs/TxsListItem.tsx
index 329532a06b..4d1b72eb69 100644
--- a/ui/txs/TxsListItem.tsx
+++ b/ui/txs/TxsListItem.tsx
@@ -19,9 +19,9 @@ import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
+import TxStatus from 'ui/shared/statusTag/TxStatus';
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
-import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxType from 'ui/txs/TxType';
diff --git a/ui/txs/TxsTableItem.tsx b/ui/txs/TxsTableItem.tsx
index b0b52fe13b..42443476ef 100644
--- a/ui/txs/TxsTableItem.tsx
+++ b/ui/txs/TxsTableItem.tsx
@@ -23,9 +23,9 @@ import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag';
+import TxStatus from 'ui/shared/statusTag/TxStatus';
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
-import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxType from './TxType';
diff --git a/ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails.tsx b/ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails.tsx
new file mode 100644
index 0000000000..dd0f6fbcbf
--- /dev/null
+++ b/ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails.tsx
@@ -0,0 +1,175 @@
+import { Grid, Text, Skeleton } from '@chakra-ui/react';
+import type { UseQueryResult } from '@tanstack/react-query';
+import { useRouter } from 'next/router';
+import React from 'react';
+
+import { ZKEVM_L2_TX_BATCH_STATUSES } from 'types/api/zkEvmL2TxnBatches';
+import type { ZkEvmL2TxnBatch } from 'types/api/zkEvmL2TxnBatches';
+
+import { route } from 'nextjs-routes';
+
+import clockIcon from 'icons/clock.svg';
+import type { ResourceError } from 'lib/api/resources';
+import dayjs from 'lib/date/dayjs';
+import Icon from 'ui/shared/chakra/Icon';
+import CopyToClipboard from 'ui/shared/CopyToClipboard';
+import DataFetchAlert from 'ui/shared/DataFetchAlert';
+import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
+import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
+import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
+import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
+import LinkInternal from 'ui/shared/LinkInternal';
+import PrevNext from 'ui/shared/PrevNext';
+import TextSeparator from 'ui/shared/TextSeparator';
+import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
+
+interface Props {
+ query: UseQueryResult;
+}
+
+const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
+ const router = useRouter();
+
+ const { data, isPlaceholderData, isError, error } = query;
+
+ const handlePrevNextClick = React.useCallback((direction: 'prev' | 'next') => {
+ if (!data) {
+ return;
+ }
+
+ const increment = direction === 'next' ? +1 : -1;
+ const nextId = String(data.number + increment);
+
+ router.push({ pathname: '/zkevm-l2-txn-batch/[number]', query: { number: nextId } }, undefined);
+ }, [ data, router ]);
+
+ if (isError) {
+ if (error?.status === 404) {
+ throw Error('Tx Batch not found', { cause: error as unknown as Error });
+ }
+
+ if (error?.status === 422) {
+ throw Error('Invalid tx batch number', { cause: error as unknown as Error });
+ }
+
+ return ;
+ }
+
+ if (!data) {
+ return null;
+ }
+
+ return (
+
+
+
+ { data.number }
+
+
+
+
+
+
+
+
+
+ { dayjs(data.timestamp).fromNow() }
+
+
+
+ { dayjs(data.timestamp).format('llll') }
+
+
+
+ { data.verify_tx_hash ? (
+
+ ) : pending }
+
+
+
+
+ { data.transactions.length } transaction{ data.transactions.length === 1 ? '' : 's' }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { data.sequence_tx_hash ? (
+
+ ) : Pending }
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ZkEvmL2TxnBatchDetails;
diff --git a/ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesListItem.tsx b/ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesListItem.tsx
new file mode 100644
index 0000000000..4b99a2e6c3
--- /dev/null
+++ b/ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesListItem.tsx
@@ -0,0 +1,94 @@
+import { Skeleton, Text } from '@chakra-ui/react';
+import React from 'react';
+
+import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
+
+import { route } from 'nextjs-routes';
+
+import config from 'configs/app';
+import dayjs from 'lib/date/dayjs';
+import ZkEvmBatchEntityL2 from 'ui/shared/entities/block/ZkEvmBatchEntityL2';
+import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
+import LinkInternal from 'ui/shared/LinkInternal';
+import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
+import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus';
+
+const feature = config.features.zkEvmRollup;
+
+type Props = { item: ZkEvmL2TxnBatchesItem; isLoading?: boolean };
+
+const ZkEvmTxnBatchesListItem = ({ item, isLoading }: Props) => {
+ const timeAgo = dayjs(item.timestamp).fromNow();
+
+ if (!feature.isEnabled) {
+ return null;
+ }
+
+ return (
+
+
+ Batch #
+
+
+
+
+ Status
+
+
+
+
+ Age
+
+ { timeAgo }
+
+
+ Txn count
+
+
+
+ { item.tx_count }
+
+
+
+
+ Verify Tx Has
+
+ { item.verify_tx_hash ? (
+
+ ) : Pending }
+
+
+ Sequence hash
+
+ { item.sequence_tx_hash ? (
+
+ ) : Pending }
+
+
+
+ );
+};
+
+export default ZkEvmTxnBatchesListItem;
diff --git a/ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesTable.tsx b/ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesTable.tsx
new file mode 100644
index 0000000000..fabbc0ae70
--- /dev/null
+++ b/ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesTable.tsx
@@ -0,0 +1,42 @@
+import { Table, Tbody, Th, Tr } from '@chakra-ui/react';
+import React from 'react';
+
+import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
+
+import { default as Thead } from 'ui/shared/TheadSticky';
+
+import ZkEvmTxnBatchesTableItem from './ZkEvmTxnBatchesTableItem';
+
+type Props = {
+ items: Array;
+ top: number;
+ isLoading?: boolean;
+}
+
+const TxnBatchesTable = ({ items, top, isLoading }: Props) => {
+ return (
+
+
+
+ Batch # |
+ Status |
+ Age |
+ Txn count |
+ Verify Tx Has |
+ Sequence hash |
+
+
+
+ { items.map((item, index) => (
+
+ )) }
+
+
+ );
+};
+
+export default TxnBatchesTable;
diff --git a/ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesTableItem.tsx b/ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesTableItem.tsx
new file mode 100644
index 0000000000..3485414e71
--- /dev/null
+++ b/ui/zkEvmL2TxnBatches/ZkEvmTxnBatchesTableItem.tsx
@@ -0,0 +1,81 @@
+import { Td, Tr, Text, Skeleton } from '@chakra-ui/react';
+import React from 'react';
+
+import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
+
+import { route } from 'nextjs-routes';
+
+import config from 'configs/app';
+import dayjs from 'lib/date/dayjs';
+import ZkEvmBatchEntityL2 from 'ui/shared/entities/block/ZkEvmBatchEntityL2';
+import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
+import LinkInternal from 'ui/shared/LinkInternal';
+import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus';
+
+const feature = config.features.zkEvmRollup;
+
+type Props = { item: ZkEvmL2TxnBatchesItem; isLoading?: boolean };
+
+const TxnBatchesTableItem = ({ item, isLoading }: Props) => {
+ const timeAgo = dayjs(item.timestamp).fromNow();
+
+ if (!feature.isEnabled) {
+ return null;
+ }
+
+ return (
+
+
+
+ |
+
+
+ |
+
+
+ { timeAgo }
+
+ |
+
+
+
+ { item.tx_count }
+
+
+ |
+
+ { item.verify_tx_hash ? (
+
+ ) : Pending }
+ |
+
+ { item.sequence_tx_hash ? (
+
+ ) : Pending }
+ |
+
+ );
+};
+
+export default TxnBatchesTableItem;
|