diff --git a/configs/envs/.env.zkevm b/configs/envs/.env.zkevm index bab56010b5..91d7172d3f 100644 --- a/configs/envs/.env.zkevm +++ b/configs/envs/.env.zkevm @@ -37,6 +37,7 @@ NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c18099 NEXT_PUBLIC_HAS_BEACON_CHAIN=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true # NEXT_PUBLIC_AUTH_URL=http://localhost:3000 +NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com diff --git a/lib/api/resources.ts b/lib/api/resources.ts index 0af00fc438..f1b0d4cb3d 100644 --- a/lib/api/resources.ts +++ b/lib/api/resources.ts @@ -59,7 +59,7 @@ import type { TTxsFilters } from 'types/api/txsFilters'; import type { TxStateChanges } from 'types/api/txStateChanges'; import type { VisualizedContract } from 'types/api/visualization'; import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals'; -import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem, ZkEvmL2TxnBatchesResponse, ZkEvmL2TxnBatchTxs } from 'types/api/zkEvml2TxnBatches'; +import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem, ZkEvmL2TxnBatchesResponse, ZkEvmL2TxnBatchTxs } from 'types/api/zkEvmL2TxnBatches'; import type { ArrayElement } from 'types/utils'; import config from 'configs/app'; diff --git a/lib/socket/types.ts b/lib/socket/types.ts index ebc220fe46..4d05a1acb7 100644 --- a/lib/socket/types.ts +++ b/lib/socket/types.ts @@ -6,6 +6,7 @@ import type { SmartContractVerificationResponse } from 'types/api/contract'; import type { RawTracesResponse } from 'types/api/rawTrace'; import type { TokenTransfer } from 'types/api/tokenTransfer'; import type { Transaction } from 'types/api/transaction'; +import type { NewZkEvmBatchSocketResponse } from 'types/api/zkEvmL2TxnBatches'; export type SocketMessageParams = SocketMessage.NewBlock | SocketMessage.BlocksIndexStatus | @@ -30,6 +31,7 @@ SocketMessage.SmartContractWasVerified | SocketMessage.TokenTransfers | SocketMessage.TokenTotalSupply | SocketMessage.ContractVerification | +SocketMessage.NewZkEvmL2Batch | SocketMessage.Unknown; interface SocketMessageParamsGeneric { @@ -64,5 +66,6 @@ export namespace SocketMessage { export type TokenTransfers = SocketMessageParamsGeneric<'token_transfer', {token_transfer: number }>; export type TokenTotalSupply = SocketMessageParamsGeneric<'total_supply', {total_supply: number }>; export type ContractVerification = SocketMessageParamsGeneric<'verification_result', SmartContractVerificationResponse>; + export type NewZkEvmL2Batch = SocketMessageParamsGeneric<'new_zkevm_confirmed_batch', NewZkEvmBatchSocketResponse>; export type Unknown = SocketMessageParamsGeneric; } diff --git a/mocks/zkevmL2txnBatches/zkevmL2txnBatches.ts b/mocks/zkevmL2txnBatches/zkevmL2txnBatches.ts index 6e05950c4d..fc969eaa80 100644 --- a/mocks/zkevmL2txnBatches/zkevmL2txnBatches.ts +++ b/mocks/zkevmL2txnBatches/zkevmL2txnBatches.ts @@ -1,4 +1,4 @@ -import type { ZkEvmL2TxnBatchesResponse } from 'types/api/zkEvml2TxnBatches'; +import type { ZkEvmL2TxnBatchesResponse } from 'types/api/zkEvmL2TxnBatches'; export const txnBatchesData: ZkEvmL2TxnBatchesResponse = { items: [ diff --git a/stubs/zkEvmL2.ts b/stubs/zkEvmL2.ts index 2c7ac9c6c9..d68f1ac9d3 100644 --- a/stubs/zkEvmL2.ts +++ b/stubs/zkEvmL2.ts @@ -1,4 +1,4 @@ -import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem } from 'types/api/zkEvml2TxnBatches'; +import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches'; import { TX_HASH } from './tx'; diff --git a/types/api/zkEvml2TxnBatches.ts b/types/api/zkEvmL2TxnBatches.ts similarity index 93% rename from types/api/zkEvml2TxnBatches.ts rename to types/api/zkEvmL2TxnBatches.ts index ae32f5fc16..e343dddb18 100644 --- a/types/api/zkEvml2TxnBatches.ts +++ b/types/api/zkEvmL2TxnBatches.ts @@ -36,3 +36,5 @@ export type ZkEvmL2TxnBatchTxs = { // API responce doesn't have next_page_params option, but we need to add it to the type for consistency next_page_params: null; } + +export type NewZkEvmBatchSocketResponse = { batch: ZkEvmL2TxnBatch }; diff --git a/ui/home/LatestZkEvmL2Batches.pw.tsx b/ui/home/LatestZkEvmL2Batches.pw.tsx index 3d1663bfed..5a470ac854 100644 --- a/ui/home/LatestZkEvmL2Batches.pw.tsx +++ b/ui/home/LatestZkEvmL2Batches.pw.tsx @@ -1,14 +1,21 @@ -import { test, expect } from '@playwright/experimental-ct-react'; +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, diff --git a/ui/home/LatestZkEvmL2Batches.tsx b/ui/home/LatestZkEvmL2Batches.tsx index db146302c7..b42b4abdc4 100644 --- a/ui/home/LatestZkEvmL2Batches.tsx +++ b/ui/home/LatestZkEvmL2Batches.tsx @@ -1,11 +1,18 @@ 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 { ZkEvmL2TxnBatch } from 'types/api/zkEvmL2TxnBatches'; + import { route } from 'nextjs-routes'; +// import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery 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'; @@ -14,6 +21,7 @@ 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: { @@ -21,6 +29,29 @@ const LatestZkEvmL2Batches = () => { }, }); + // const handleNewBatchMessage: SocketMessage.NewZkEvmL2Batch['handler'] = React.useCallback((payload) => { + // queryClient.setQueryData(getResourceKey('homepage_zkevm_l2_batches'), (prevData: Array | undefined) => { + + // const newData = prevData ? [ ...prevData ] : []; + + // if (newData.some((batch => batch.number === payload.batch.number))) { + // return newData; + // } + + // return [ payload.batch, ...newData ].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) { @@ -33,7 +64,6 @@ const LatestZkEvmL2Batches = () => { content = ( <> - { /* no socket so far */ } { /* */ } { dataToShow.map(((batch, index) => ( { Txn - { /* LINK!!! */ } - { batch.tx_count } + + + { batch.tx_count } + + diff --git a/ui/home/Stats.tsx b/ui/home/Stats.tsx index 5810fb10f0..4fc90f9251 100644 --- a/ui/home/Stats.tsx +++ b/ui/home/Stats.tsx @@ -55,7 +55,7 @@ const Stats = () => { { config.features.zkEvmRollup.isEnabled ? ( { +const ZkEvmBatchEntityL2 = (props: BlockEntity.EntityProps) => { const linkProps = _omit(props, [ 'className' ]); const partsProps = _omit(props, [ 'className', 'onClick' ]); @@ -32,4 +32,4 @@ const BlockEntityL2 = (props: BlockEntity.EntityProps) => { ); }; -export default chakra(BlockEntityL2); +export default chakra(ZkEvmBatchEntityL2); diff --git a/ui/shared/statusTag/ZkEvmL2TxnBatchStatus.tsx b/ui/shared/statusTag/ZkEvmL2TxnBatchStatus.tsx index 837ecbce59..f7601bde46 100644 --- a/ui/shared/statusTag/ZkEvmL2TxnBatchStatus.tsx +++ b/ui/shared/statusTag/ZkEvmL2TxnBatchStatus.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvml2TxnBatches'; +import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches'; import type { StatusTagType } from './StatusTag'; import StatusTag from './StatusTag'; diff --git a/ui/token/TokenVerifiedInfo.tsx b/ui/token/TokenVerifiedInfo.tsx index 5e15db2c6d..ab8446db7b 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 } + { url.host } ); } catch (error) { return null; diff --git a/ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails.tsx b/ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails.tsx index 9c285d4c0f..1e1a3b54d3 100644 --- a/ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails.tsx +++ b/ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails.tsx @@ -1,10 +1,10 @@ -import { Grid, GridItem, Text, Skeleton } from '@chakra-ui/react'; +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 { ZKEVM_L2_TX_BATCH_STATUSES } from 'types/api/zkEvmL2TxnBatches'; +import type { ZkEvmL2TxnBatch } from 'types/api/zkEvmL2TxnBatches'; import { route } from 'nextjs-routes'; @@ -15,6 +15,7 @@ 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'; @@ -58,16 +59,6 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => { return null; } - const sectionGap = ( - - ); - return ( { - { sectionGap } + { return ( - + { fontWeight={ 600 } /> - + - + { timeAgo } - + { - + { item.verify_tx_hash ? ( { /> ) : Pending } - + { item.sequence_tx_hash ? (