Commit ee5467fa authored by tom goriunov's avatar tom goriunov Committed by GitHub

zkEVM deposits and withdrawals (#1833)

* deposits page

* withdrawals page

* tests

* rename optimism routes

* [skip ci] fix presets
parent 9d6bd245
......@@ -19,8 +19,6 @@ NEXT_PUBLIC_NETWORK_RPC_URL=https://mainnet.optimism.io
# api configuration
NEXT_PUBLIC_API_HOST=optimism.blockscout.com
NEXT_PUBLIC_API_PORT=80
NEXT_PUBLIC_API_PROTOCOL=http
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
......
......@@ -18,8 +18,6 @@ NEXT_PUBLIC_NETWORK_RPC_URL=https://zkevm-rpc.com
# api configuration
NEXT_PUBLIC_API_HOST=zkevm.blockscout.com
NEXT_PUBLIC_API_PORT=80
NEXT_PUBLIC_API_PROTOCOL=http
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
......@@ -47,4 +45,4 @@ NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.co
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
# rollup
NEXT_PUBLIC_ROLLUP_TYPE=zkEvm
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://polygon.blockscout.com
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth.blockscout.com
......@@ -18,8 +18,6 @@ NEXT_PUBLIC_NETWORK_RPC_URL=https://mainnet.era.zksync.io
# api configuration
NEXT_PUBLIC_API_HOST=zksync.blockscout.com
NEXT_PUBLIC_API_PORT=80
NEXT_PUBLIC_API_PROTOCOL=http
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
......
......@@ -21,6 +21,7 @@ export_envs_from_preset() {
"NEXT_PUBLIC_APP_HOST"
"NEXT_PUBLIC_APP_PORT"
"NEXT_PUBLIC_APP_ENV"
"NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL"
)
while IFS='=' read -r name value; do
......
......@@ -98,7 +98,14 @@ import type { ValidatorsCountersResponse, ValidatorsFilters, ValidatorsResponse,
import type { VerifiedContractsSorting } from 'types/api/verifiedContracts';
import type { VisualizedContract } from 'types/api/visualization';
import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals';
import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem, ZkEvmL2TxnBatchesResponse, ZkEvmL2TxnBatchTxs } from 'types/api/zkEvmL2';
import type {
ZkEvmL2DepositsResponse,
ZkEvmL2TxnBatch,
ZkEvmL2TxnBatchesItem,
ZkEvmL2TxnBatchesResponse,
ZkEvmL2TxnBatchTxs,
ZkEvmL2WithdrawalsResponse,
} from 'types/api/zkEvmL2';
import type { ZkSyncBatch, ZkSyncBatchesResponse, ZkSyncBatchTxs } from 'types/api/zkSyncL2';
import type { MarketplaceAppOverview } from 'types/client/marketplace';
import type { ArrayElement } from 'types/utils';
......@@ -600,43 +607,61 @@ export const RESOURCES = {
},
// optimistic L2
l2_deposits: {
optimistic_l2_deposits: {
path: '/api/v2/optimism/deposits',
filterFields: [],
},
l2_deposits_count: {
optimistic_l2_deposits_count: {
path: '/api/v2/optimism/deposits/count',
},
l2_withdrawals: {
optimistic_l2_withdrawals: {
path: '/api/v2/optimism/withdrawals',
filterFields: [],
},
l2_withdrawals_count: {
optimistic_l2_withdrawals_count: {
path: '/api/v2/optimism/withdrawals/count',
},
l2_output_roots: {
optimistic_l2_output_roots: {
path: '/api/v2/optimism/output-roots',
filterFields: [],
},
l2_output_roots_count: {
optimistic_l2_output_roots_count: {
path: '/api/v2/optimism/output-roots/count',
},
l2_txn_batches: {
optimistic_l2_txn_batches: {
path: '/api/v2/optimism/txn-batches',
filterFields: [],
},
l2_txn_batches_count: {
optimistic_l2_txn_batches_count: {
path: '/api/v2/optimism/txn-batches/count',
},
// zkEvm L2
zkevm_l2_deposits: {
path: '/api/v2/zkevm/deposits',
filterFields: [],
},
zkevm_l2_deposits_count: {
path: '/api/v2/zkevm/deposits/count',
},
zkevm_l2_withdrawals: {
path: '/api/v2/zkevm/withdrawals',
filterFields: [],
},
zkevm_l2_withdrawals_count: {
path: '/api/v2/zkevm/withdrawals/count',
},
zkevm_l2_txn_batches: {
path: '/api/v2/zkevm/batches',
filterFields: [],
......@@ -814,9 +839,9 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
'token_transfers' | 'token_holders' | 'token_inventory' | 'tokens' | 'tokens_bridged' |
'token_instance_transfers' | 'token_instance_holders' |
'verified_contracts' |
'l2_output_roots' | 'l2_withdrawals' | 'l2_txn_batches' | 'l2_deposits' |
'optimistic_l2_output_roots' | 'optimistic_l2_withdrawals' | 'optimistic_l2_txn_batches' | 'optimistic_l2_deposits' |
'shibarium_deposits' | 'shibarium_withdrawals' |
'zkevm_l2_txn_batches' | 'zkevm_l2_txn_batch_txs' |
'zkevm_l2_deposits' | 'zkevm_l2_withdrawals' | 'zkevm_l2_txn_batches' | 'zkevm_l2_txn_batch_txs' |
'zksync_l2_txn_batches' | 'zksync_l2_txn_batch_txs' |
'withdrawals' | 'address_withdrawals' | 'block_withdrawals' |
'watchlist' | 'private_tags_address' | 'private_tags_tx' |
......@@ -914,18 +939,14 @@ Q extends 'visualize_sol2uml' ? VisualizedContract :
Q extends 'contract_verification_config' ? SmartContractVerificationConfig :
Q extends 'withdrawals' ? WithdrawalsResponse :
Q extends 'withdrawals_counters' ? WithdrawalsCounters :
Q extends 'l2_output_roots' ? OptimisticL2OutputRootsResponse :
Q extends 'l2_withdrawals' ? OptimisticL2WithdrawalsResponse :
Q extends 'l2_deposits' ? OptimisticL2DepositsResponse :
Q extends 'l2_txn_batches' ? OptimisticL2TxnBatchesResponse :
Q extends 'l2_output_roots_count' ? number :
Q extends 'l2_withdrawals_count' ? number :
Q extends 'l2_deposits_count' ? number :
Q extends 'l2_txn_batches_count' ? number :
Q extends 'zkevm_l2_txn_batches' ? ZkEvmL2TxnBatchesResponse :
Q extends 'zkevm_l2_txn_batches_count' ? number :
Q extends 'zkevm_l2_txn_batch' ? ZkEvmL2TxnBatch :
Q extends 'zkevm_l2_txn_batch_txs' ? ZkEvmL2TxnBatchTxs :
Q extends 'optimistic_l2_output_roots' ? OptimisticL2OutputRootsResponse :
Q extends 'optimistic_l2_withdrawals' ? OptimisticL2WithdrawalsResponse :
Q extends 'optimistic_l2_deposits' ? OptimisticL2DepositsResponse :
Q extends 'optimistic_l2_txn_batches' ? OptimisticL2TxnBatchesResponse :
Q extends 'optimistic_l2_output_roots_count' ? number :
Q extends 'optimistic_l2_withdrawals_count' ? number :
Q extends 'optimistic_l2_deposits_count' ? number :
Q extends 'optimistic_l2_txn_batches_count' ? number :
Q extends 'config_backend_version' ? BackendVersionConfig :
never;
// !!! IMPORTANT !!!
......@@ -943,6 +964,14 @@ Q extends 'shibarium_withdrawals' ? ShibariumWithdrawalsResponse :
Q extends 'shibarium_deposits' ? ShibariumDepositsResponse :
Q extends 'shibarium_withdrawals_count' ? number :
Q extends 'shibarium_deposits_count' ? number :
Q extends 'zkevm_l2_deposits' ? ZkEvmL2DepositsResponse :
Q extends 'zkevm_l2_deposits_count' ? number :
Q extends 'zkevm_l2_withdrawals' ? ZkEvmL2WithdrawalsResponse :
Q extends 'zkevm_l2_withdrawals_count' ? number :
Q extends 'zkevm_l2_txn_batches' ? ZkEvmL2TxnBatchesResponse :
Q extends 'zkevm_l2_txn_batches_count' ? number :
Q extends 'zkevm_l2_txn_batch' ? ZkEvmL2TxnBatch :
Q extends 'zkevm_l2_txn_batch_txs' ? ZkEvmL2TxnBatchTxs :
Q extends 'zksync_l2_txn_batches' ? ZkSyncBatchesResponse :
Q extends 'zksync_l2_txn_batches_count' ? number :
Q extends 'zksync_l2_txn_batch' ? ZkSyncBatch :
......
......@@ -72,45 +72,45 @@ export default function useNavItems(): ReturnType {
icon: 'validator',
isActive: pathname === '/validators',
} : null;
const rollupDeposits = {
text: `Deposits (L1${ rightLineArrow }L2)`,
nextRoute: { pathname: '/deposits' as const },
icon: 'arrows/south-east',
isActive: pathname === '/deposits',
};
const rollupWithdrawals = {
text: `Withdrawals (L2${ rightLineArrow }L1)`,
nextRoute: { pathname: '/withdrawals' as const },
icon: 'arrows/north-east',
isActive: pathname === '/withdrawals',
};
const rollupTxnBatches = {
text: 'Txn batches',
nextRoute: { pathname: '/batches' as const },
icon: 'txn_batches',
isActive: pathname === '/batches',
};
const rollupOutputRoots = {
text: 'Output roots',
nextRoute: { pathname: '/output-roots' as const },
icon: 'output_roots',
isActive: pathname === '/output-roots',
};
const rollupFeature = config.features.rollup;
if (rollupFeature.isEnabled && rollupFeature.type === 'zkEvm') {
if (rollupFeature.isEnabled && (rollupFeature.type === 'optimistic' || rollupFeature.type === 'zkEvm')) {
blockchainNavItems = [
[
txs,
userOps,
blocks,
{
text: 'Txn batches',
nextRoute: { pathname: '/batches' as const },
icon: 'txn_batches',
isActive: pathname === '/batches' || pathname === '/batches/[number]',
},
].filter(Boolean),
[
topAccounts,
validators,
verifiedContracts,
ensLookup,
].filter(Boolean),
];
} else if (rollupFeature.isEnabled && rollupFeature.type === 'optimistic') {
blockchainNavItems = [
[
txs,
// eslint-disable-next-line max-len
{ text: `Deposits (L1${ rightLineArrow }L2)`, nextRoute: { pathname: '/deposits' as const }, icon: 'arrows/south-east', isActive: pathname === '/deposits' },
// eslint-disable-next-line max-len
{ text: `Withdrawals (L2${ rightLineArrow }L1)`, nextRoute: { pathname: '/withdrawals' as const }, icon: 'arrows/north-east', isActive: pathname === '/withdrawals' },
rollupDeposits,
rollupWithdrawals,
],
[
blocks,
// eslint-disable-next-line max-len
{ text: 'Txn batches', nextRoute: { pathname: '/batches' as const }, icon: 'txn_batches', isActive: pathname === '/batches' },
// eslint-disable-next-line max-len
{ text: 'Output roots', nextRoute: { pathname: '/output-roots' as const }, icon: 'output_roots', isActive: pathname === '/output-roots' },
],
rollupTxnBatches,
rollupFeature.type === 'optimistic' ? rollupOutputRoots : undefined,
].filter(Boolean),
[
userOps,
topAccounts,
......@@ -123,10 +123,8 @@ export default function useNavItems(): ReturnType {
blockchainNavItems = [
[
txs,
// eslint-disable-next-line max-len
{ text: `Deposits (L1${ rightLineArrow }L2)`, nextRoute: { pathname: '/deposits' as const }, icon: 'arrows/south-east', isActive: pathname === '/deposits' },
// eslint-disable-next-line max-len
{ text: `Withdrawals (L2${ rightLineArrow }L1)`, nextRoute: { pathname: '/withdrawals' as const }, icon: 'arrows/north-east', isActive: pathname === '/withdrawals' },
rollupDeposits,
rollupWithdrawals,
],
[
blocks,
......@@ -142,12 +140,7 @@ export default function useNavItems(): ReturnType {
txs,
userOps,
blocks,
{
text: 'Txn batches',
nextRoute: { pathname: '/batches' as const },
icon: 'txn_batches',
isActive: pathname === '/batches' || pathname === '/batches/[number]',
},
rollupTxnBatches,
].filter(Boolean),
[
topAccounts,
......
import type { ZkEvmL2DepositsResponse } from 'types/api/zkEvmL2';
export const baseResponse: ZkEvmL2DepositsResponse = {
items: [
{
block_number: 19681943,
index: 182177,
l1_transaction_hash: '0x29074452f976064aca1ca5c6e7c82d890c10454280693e6eca0257ae000c8e85',
l2_transaction_hash: null,
symbol: 'DAI',
timestamp: '2022-04-18T11:08:11.000000Z',
value: '0.003',
},
{
block_number: 19681894,
index: 182176,
l1_transaction_hash: '0x0b7d58c0a6b4695ba28d99df928591fb931c812c0aab6d0093ff5040d2f9bc5e',
l2_transaction_hash: '0x210d9f70f411de1079e32a98473b04345a5ea6ff2340a8511ebc2df641274436',
symbol: 'ETH',
timestamp: '2022-04-18T10:58:23.000000Z',
value: '0.0046651390188845',
},
],
next_page_params: {
items_count: 50,
index: 1,
},
};
import type { ZkEvmL2TxnBatch } from 'types/api/zkEvmL2';
import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesResponse } from 'types/api/zkEvmL2';
export const txnBatchData: ZkEvmL2TxnBatch = {
acc_input_hash: '0x4bf88aabe33713b7817266d7860912c58272d808da7397cdc627ca53b296fad3',
......@@ -13,3 +13,28 @@ export const txnBatchData: ZkEvmL2TxnBatch = {
],
verify_tx_hash: '0x6f7eeaa0eb966e63d127bba6bf8f9046d303c2a1185b542f0b5083f682a0e87f',
};
export const txnBatchesData: ZkEvmL2TxnBatchesResponse = {
items: [
{
timestamp: '2023-06-01T14:46:48.000000Z',
status: 'Finalized',
verify_tx_hash: '0x48139721f792d3a68c3781b4cf50e66e8fc7dbb38adff778e09066ea5be9adb8',
sequence_tx_hash: '0x6aa081e8e33a085e4ec7124fcd8a5f7d36aac0828f176e80d4b70e313a11695b',
number: 5218590,
tx_count: 9,
},
{
timestamp: '2023-06-01T14:46:48.000000Z',
status: 'Unfinalized',
verify_tx_hash: null,
sequence_tx_hash: null,
number: 5218591,
tx_count: 9,
},
],
next_page_params: {
number: 5902834,
items_count: 50,
},
};
import type { ZkEvmL2WithdrawalsResponse } from 'types/api/zkEvmL2';
export const baseResponse: ZkEvmL2WithdrawalsResponse = {
items: [
{
block_number: 11722417,
index: 47040,
l1_transaction_hash: null,
l2_transaction_hash: '0x68c378e412e51553524545ef1d3f00f69496fb37827c0b3b7e0870d245970408',
symbol: 'ETH',
timestamp: '2022-04-18T09:20:37.000000Z',
value: '0.025',
},
{
block_number: 11722480,
index: 47041,
l1_transaction_hash: '0xbf76feb85b8b8f24dacb17f962dd359f82efc512928d7b11ffca92fb812ad6a5',
l2_transaction_hash: '0xfe3c168ac1751b8399f1e819f1d83ee4cf764128bc604d454abee29114dabf49',
symbol: 'ETH',
timestamp: '2022-04-18T09:23:45.000000Z',
value: '4',
},
],
next_page_params: {
items_count: 50,
index: 1,
},
};
import type { ZkEvmL2TxnBatchesResponse } from 'types/api/zkEvmL2';
export const txnBatchesData: ZkEvmL2TxnBatchesResponse = {
items: [
{
timestamp: '2023-06-01T14:46:48.000000Z',
status: 'Finalized',
verify_tx_hash: '0x48139721f792d3a68c3781b4cf50e66e8fc7dbb38adff778e09066ea5be9adb8',
sequence_tx_hash: '0x6aa081e8e33a085e4ec7124fcd8a5f7d36aac0828f176e80d4b70e313a11695b',
number: 5218590,
tx_count: 9,
},
{
timestamp: '2023-06-01T14:46:48.000000Z',
status: 'Unfinalized',
verify_tx_hash: null,
sequence_tx_hash: null,
number: 5218591,
tx_count: 9,
},
],
next_page_params: {
number: 5902834,
items_count: 50,
},
};
......@@ -67,7 +67,7 @@ export const verifiedAddresses: GetServerSideProps<Props> = async(context) => {
};
export const deposits: GetServerSideProps<Props> = async(context) => {
if (!(rollupFeature.isEnabled && (rollupFeature.type === 'optimistic' || rollupFeature.type === 'shibarium'))) {
if (!(rollupFeature.isEnabled && (rollupFeature.type === 'optimistic' || rollupFeature.type === 'shibarium' || rollupFeature.type === 'zkEvm'))) {
return {
notFound: true,
};
......@@ -79,7 +79,7 @@ export const deposits: GetServerSideProps<Props> = async(context) => {
export const withdrawals: GetServerSideProps<Props> = async(context) => {
if (
!config.features.beaconChain.isEnabled &&
!(rollupFeature.isEnabled && (rollupFeature.type === 'optimistic' || rollupFeature.type === 'shibarium'))
!(rollupFeature.isEnabled && (rollupFeature.type === 'optimistic' || rollupFeature.type === 'shibarium' || rollupFeature.type === 'zkEvm'))
) {
return {
notFound: true,
......
......@@ -16,7 +16,11 @@ const Deposits = dynamic(() => {
return import('ui/pages/ShibariumDeposits');
}
throw new Error('Withdrawals feature is not enabled.');
if (rollupFeature.isEnabled && rollupFeature.type === 'zkEvm') {
return import('ui/pages/ZkEvmL2Deposits');
}
throw new Error('Deposits feature is not enabled.');
}, { ssr: false });
const Page: NextPage = () => {
......
......@@ -17,6 +17,10 @@ const Withdrawals = dynamic(() => {
return import('ui/pages/ShibariumWithdrawals');
}
if (rollupFeature.isEnabled && rollupFeature.type === 'zkEvm') {
return import('ui/pages/ZkEvmL2Withdrawals');
}
if (beaconChainFeature.isEnabled) {
return import('ui/pages/BeaconChainWithdrawals');
}
......
......@@ -20,6 +20,10 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'shibarium' ],
[ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ],
],
zkEvmRollup: [
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'zkEvm' ],
[ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ],
],
bridgedTokens: [
[ 'NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS', '[{"id":"1","title":"Ethereum","short_title":"ETH","base_url":"https://eth.blockscout.com/token/"},{"id":"56","title":"Binance Smart Chain","short_title":"BSC","base_url":"https://bscscan.com/token/"},{"id":"99","title":"POA","short_title":"POA","base_url":"https://blockscout.com/poa/core/token/"}]' ],
[ 'NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES', '[{"type":"omni","title":"OmniBridge","short_title":"OMNI"},{"type":"amb","title":"Arbitrary Message Bridge","short_title":"AMB"}]' ],
......
import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2';
import type { ZkEvmL2DepositsItem, ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem, ZkEvmL2WithdrawalsItem } from 'types/api/zkEvmL2';
import { TX_HASH } from './tx';
export const ZKEVM_DEPOSITS_ITEM: ZkEvmL2DepositsItem = {
block_number: 19674901,
index: 181920,
l1_transaction_hash: '0xa74edfa5824a07a5f95ca1145140ed589df7f05bb17796bf18090b14c4566b5d',
l2_transaction_hash: '0x436d1c7ada270466ca0facdb96ecc22934d68d13b8a08f541b8df11b222967b5',
symbol: 'ETH',
timestamp: '2023-06-01T14:46:48.000000Z',
value: '0.13040262',
};
export const ZKEVM_WITHDRAWALS_ITEM: ZkEvmL2WithdrawalsItem = {
block_number: 11692968,
index: 47003,
l1_transaction_hash: '0x230cf46dabea287ac7d0ba83b8ea120bb83c1de58a81d34f44788f0459096c52',
l2_transaction_hash: '0x519d9f025ec47f08a48d708964d177189d2246ddf988686c481f5debcf097e34',
symbol: 'ETH',
timestamp: '2024-04-17T08:51:58.000000Z',
value: '110.35',
};
export const ZKEVM_L2_TXN_BATCHES_ITEM: ZkEvmL2TxnBatchesItem = {
timestamp: '2023-06-01T14:46:48.000000Z',
status: 'Finalized',
......
import type { Transaction } from './transaction';
export type ZkEvmL2DepositsItem = {
block_number: number;
index: number;
l1_transaction_hash: string;
l2_transaction_hash: string | null;
timestamp: string;
value: string;
symbol: string;
}
export type ZkEvmL2DepositsResponse = {
items: Array<ZkEvmL2DepositsItem>;
next_page_params: {
items_count: number;
index: number;
};
}
export type ZkEvmL2WithdrawalsItem = {
block_number: number;
index: number;
l1_transaction_hash: string | null;
l2_transaction_hash: string;
timestamp: string;
value: string;
symbol: string;
}
export type ZkEvmL2WithdrawalsResponse = {
items: Array<ZkEvmL2WithdrawalsItem>;
next_page_params: {
items_count: number;
index: number;
};
}
export type ZkEvmL2TxnBatchesItem = {
number: number;
verify_tx_hash: string | null;
......
import { Skeleton, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { ZkEvmL2DepositsItem } from 'types/api/zkEvmL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
const rollupFeature = config.features.rollup;
type Props = { item: ZkEvmL2DepositsItem; isLoading?: boolean };
const ZkEvmL2DepositsListItem = ({ item, isLoading }: Props) => {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'zkEvm') {
return null;
}
const timeAgo = dayjs(item.timestamp).fromNow();
return (
<ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 block</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<BlockEntityL1
number={ item.block_number }
isLoading={ isLoading }
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 600 }
/>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Index</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ item.index }
</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<TxEntityL1
isLoading={ isLoading }
hash={ item.l1_transaction_hash }
fontSize="sm"
lineHeight={ 5 }
truncation="constant_long"
/>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Age</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">{ timeAgo }</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L2 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
{ item.l2_transaction_hash ? (
<TxEntity
isLoading={ isLoading }
hash={ item.l2_transaction_hash }
fontSize="sm"
lineHeight={ 5 }
truncation="constant_long"
/>
) : (
<chakra.span color="text_secondary">
Pending Claim
</chakra.span>
) }
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Value</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ BigNumber(item.value).toFormat() }
</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Token</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ item.symbol }
</Skeleton>
</ListItemMobileGrid.Value>
</ListItemMobileGrid.Container>
);
};
export default ZkEvmL2DepositsListItem;
import { Table, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react';
import type { ZkEvmL2DepositsItem } from 'types/api/zkEvmL2';
import { default as Thead } from 'ui/shared/TheadSticky';
import ZkEvmL2DepositsTableItem from './ZkEvmL2DepositsTableItem';
type Props = {
items: Array<ZkEvmL2DepositsItem>;
top: number;
isLoading?: boolean;
}
const ZkEvmL2DepositsTable = ({ items, top, isLoading }: Props) => {
return (
<Table variant="simple" size="sm" style={{ tableLayout: 'auto' }} minW="950px">
<Thead top={ top }>
<Tr>
<Th>L1 block</Th>
<Th>Index</Th>
<Th>L1 txn hash</Th>
<Th>Age</Th>
<Th>L2 txn hash</Th>
<Th isNumeric>Value</Th>
<Th>Token</Th>
</Tr>
</Thead>
<Tbody>
{ items.map((item, index) => (
<ZkEvmL2DepositsTableItem key={ String(item.index) + (isLoading ? index : '') } item={ item } isLoading={ isLoading }/>
)) }
</Tbody>
</Table>
);
};
export default ZkEvmL2DepositsTable;
import { Td, Tr, Skeleton, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { ZkEvmL2DepositsItem } from 'types/api/zkEvmL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
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 rollupFeature = config.features.rollup;
type Props = { item: ZkEvmL2DepositsItem; isLoading?: boolean };
const ZkEvmL2DepositsTableItem = ({ item, isLoading }: Props) => {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'zkEvm') {
return null;
}
const timeAgo = dayjs(item.timestamp).fromNow();
return (
<Tr>
<Td verticalAlign="middle">
<BlockEntityL1
number={ item.block_number }
isLoading={ isLoading }
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 600 }
noIcon
/>
</Td>
<Td verticalAlign="middle">
<Skeleton isLoaded={ !isLoading }>
<span>{ item.index }</span>
</Skeleton>
</Td>
<Td verticalAlign="middle">
<TxEntityL1
isLoading={ isLoading }
hash={ item.l1_transaction_hash }
truncation="constant_long"
noIcon
fontSize="sm"
lineHeight={ 5 }
/>
</Td>
<Td verticalAlign="middle" pr={ 12 }>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
<span>{ timeAgo }</span>
</Skeleton>
</Td>
<Td verticalAlign="middle">
{ item.l2_transaction_hash ? (
<TxEntity
isLoading={ isLoading }
hash={ item.l2_transaction_hash }
fontSize="sm"
lineHeight={ 5 }
truncation="constant_long"
noIcon
/>
) : (
<chakra.span color="text_secondary">
Pending Claim
</chakra.span>
) }
</Td>
<Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block">
<span>{ BigNumber(item.value).toFormat() }</span>
</Skeleton>
</Td>
<Td verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block">
<span>{ item.symbol }</span>
</Skeleton>
</Td>
</Tr>
);
};
export default ZkEvmL2DepositsTableItem;
import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import { txnBatchesData } from 'mocks/zkevmL2txnBatches/zkevmL2txnBatches';
import { txnBatchesData } from 'mocks/zkEvm/txnBatches';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
......
......@@ -9,8 +9,8 @@ import * as configs from 'playwright/utils/configs';
import OptimisticL2Deposits from './OptimisticL2Deposits';
const DEPOSITS_API_URL = buildApiUrl('l2_deposits');
const DEPOSITS_COUNT_API_URL = buildApiUrl('l2_deposits_count');
const DEPOSITS_API_URL = buildApiUrl('optimistic_l2_deposits');
const DEPOSITS_COUNT_API_URL = buildApiUrl('optimistic_l2_deposits_count');
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
......
......@@ -14,9 +14,9 @@ import StickyPaginationWithText from 'ui/shared/StickyPaginationWithText';
const OptimisticL2Deposits = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_deposits',
resourceName: 'optimistic_l2_deposits',
options: {
placeholderData: generateListStub<'l2_deposits'>(
placeholderData: generateListStub<'optimistic_l2_deposits'>(
L2_DEPOSIT_ITEM,
50,
{
......@@ -30,7 +30,7 @@ const OptimisticL2Deposits = () => {
},
});
const countersQuery = useApiQuery('l2_deposits_count', {
const countersQuery = useApiQuery('optimistic_l2_deposits_count', {
queryOptions: {
placeholderData: 1927029,
},
......
......@@ -14,8 +14,8 @@ const test = base.extend({
context: contextWithEnvs(configs.featureEnvs.optimisticRollup) as any,
});
const OUTPUT_ROOTS_API_URL = buildApiUrl('l2_output_roots');
const OUTPUT_ROOTS_COUNT_API_URL = buildApiUrl('l2_output_roots_count');
const OUTPUT_ROOTS_API_URL = buildApiUrl('optimistic_l2_output_roots');
const OUTPUT_ROOTS_COUNT_API_URL = buildApiUrl('optimistic_l2_output_roots_count');
test('base view +@mobile', async({ mount, page }) => {
// test on mobile is flaky
......
......@@ -13,9 +13,9 @@ import StickyPaginationWithText from 'ui/shared/StickyPaginationWithText';
const OptimisticL2OutputRoots = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_output_roots',
resourceName: 'optimistic_l2_output_roots',
options: {
placeholderData: generateListStub<'l2_output_roots'>(
placeholderData: generateListStub<'optimistic_l2_output_roots'>(
L2_OUTPUT_ROOTS_ITEM,
50,
{
......@@ -28,7 +28,7 @@ const OptimisticL2OutputRoots = () => {
},
});
const countersQuery = useApiQuery('l2_output_roots_count', {
const countersQuery = useApiQuery('optimistic_l2_output_roots_count', {
queryOptions: {
placeholderData: 50617,
},
......
......@@ -14,8 +14,8 @@ const test = base.extend({
context: contextWithEnvs(configs.featureEnvs.optimisticRollup) as any,
});
const TXN_BATCHES_API_URL = buildApiUrl('l2_txn_batches');
const TXN_BATCHES_COUNT_API_URL = buildApiUrl('l2_txn_batches_count');
const TXN_BATCHES_API_URL = buildApiUrl('optimistic_l2_txn_batches');
const TXN_BATCHES_COUNT_API_URL = buildApiUrl('optimistic_l2_txn_batches_count');
test('base view +@mobile', async({ mount, page }) => {
// test on mobile is flaky
......
......@@ -14,9 +14,9 @@ import OptimisticL2TxnBatchesTable from 'ui/txnBatches/optimisticL2/OptimisticL2
const OptimisticL2TxnBatches = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_txn_batches',
resourceName: 'optimistic_l2_txn_batches',
options: {
placeholderData: generateListStub<'l2_txn_batches'>(
placeholderData: generateListStub<'optimistic_l2_txn_batches'>(
L2_TXN_BATCHES_ITEM,
50,
{
......@@ -29,7 +29,7 @@ const OptimisticL2TxnBatches = () => {
},
});
const countersQuery = useApiQuery('l2_txn_batches_count', {
const countersQuery = useApiQuery('optimistic_l2_txn_batches_count', {
queryOptions: {
placeholderData: 5231746,
},
......
......@@ -14,8 +14,8 @@ const test = base.extend({
context: contextWithEnvs(configs.featureEnvs.optimisticRollup) as any,
});
const WITHDRAWALS_API_URL = buildApiUrl('l2_withdrawals');
const WITHDRAWALS_COUNT_API_URL = buildApiUrl('l2_withdrawals_count');
const WITHDRAWALS_API_URL = buildApiUrl('optimistic_l2_withdrawals');
const WITHDRAWALS_COUNT_API_URL = buildApiUrl('optimistic_l2_withdrawals_count');
test('base view +@mobile', async({ mount, page }) => {
// test on mobile is flaky
......
......@@ -14,9 +14,9 @@ import OptimisticL2WithdrawalsTable from 'ui/withdrawals/optimisticL2/Optimistic
const OptimisticL2Withdrawals = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_withdrawals',
resourceName: 'optimistic_l2_withdrawals',
options: {
placeholderData: generateListStub<'l2_withdrawals'>(
placeholderData: generateListStub<'optimistic_l2_withdrawals'>(
L2_WITHDRAWAL_ITEM,
50,
{
......@@ -29,7 +29,7 @@ const OptimisticL2Withdrawals = () => {
},
});
const countersQuery = useApiQuery('l2_withdrawals_count', {
const countersQuery = useApiQuery('optimistic_l2_withdrawals_count', {
queryOptions: {
placeholderData: 23700,
},
......
import React from 'react';
import * as depositsMock from 'mocks/zkEvm/deposits';
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import { test, expect } from 'playwright/lib';
import ZkEvmL2Deposits from './ZkEvmL2Deposits';
test('base view +@mobile', async({ render, mockApiResponse, mockEnvs }) => {
await mockEnvs(ENVS_MAP.zkEvmRollup);
await mockApiResponse('zkevm_l2_deposits', depositsMock.baseResponse);
await mockApiResponse('zkevm_l2_deposits_count', 3971111);
const component = await render(<ZkEvmL2Deposits/>);
await expect(component).toHaveScreenshot();
});
import { Hide, Show, Skeleton } from '@chakra-ui/react';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import { rightLineArrow, nbsp } from 'lib/html-entities';
import { generateListStub } from 'stubs/utils';
import { ZKEVM_DEPOSITS_ITEM } from 'stubs/zkEvmL2';
import ZkEvmL2DepositsListItem from 'ui/deposits/zkEvmL2/ZkEvmL2DepositsListItem';
import ZkEvmL2DepositsTable from 'ui/deposits/zkEvmL2/ZkEvmL2DepositsTable';
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';
const ZkEvmL2Deposits = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'zkevm_l2_deposits',
options: {
placeholderData: generateListStub<'zkevm_l2_deposits'>(
ZKEVM_DEPOSITS_ITEM,
50,
{ next_page_params: { items_count: 50, index: 1 } },
),
},
});
const countersQuery = useApiQuery('zkevm_l2_deposits_count', {
queryOptions: {
placeholderData: 1927029,
},
});
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>
{ data.items.map(((item, index) => (
<ZkEvmL2DepositsListItem
key={ String(item.index) + (isPlaceholderData ? index : '') }
isLoading={ isPlaceholderData }
item={ item }
/>
))) }
</Show>
<Hide below="lg" ssr={ false }>
<ZkEvmL2DepositsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
const text = (() => {
if (countersQuery.isError) {
return null;
}
return (
<Skeleton
isLoaded={ !countersQuery.isPlaceholderData }
display="inline-block"
>
A total of { countersQuery.data?.toLocaleString() } deposits found
</Skeleton>
);
})();
const actionBar = <StickyPaginationWithText text={ text } pagination={ pagination }/>;
return (
<>
<PageTitle title={ `Deposits (L1${ nbsp }${ rightLineArrow }${ nbsp }L2)` } withTextAd/>
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no deposits."
content={ content }
actionBar={ actionBar }
/>
</>
);
};
export default ZkEvmL2Deposits;
import { test as base, expect, devices } from '@playwright/experimental-ct-react';
import React from 'react';
import { txnBatchData } from 'mocks/zkevmL2txnBatches/zkevmL2txnBatch';
import { txnBatchData } from 'mocks/zkEvm/txnBatches';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
......
import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import { txnBatchesData } from 'mocks/zkevmL2txnBatches/zkevmL2txnBatches';
import { txnBatchesData } from 'mocks/zkEvm/txnBatches';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
......
import React from 'react';
import * as withdrawalsMock from 'mocks/zkEvm/withdrawals';
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import { test, expect } from 'playwright/lib';
import ZkEvmL2Withdrawals from './ZkEvmL2Withdrawals';
test('base view +@mobile', async({ render, mockApiResponse, mockEnvs }) => {
await mockEnvs(ENVS_MAP.zkEvmRollup);
await mockApiResponse('zkevm_l2_withdrawals', withdrawalsMock.baseResponse);
await mockApiResponse('zkevm_l2_withdrawals_count', 3971111);
const component = await render(<ZkEvmL2Withdrawals/>);
await expect(component).toHaveScreenshot();
});
import { Hide, Show, Skeleton } from '@chakra-ui/react';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import { rightLineArrow, nbsp } from 'lib/html-entities';
import { generateListStub } from 'stubs/utils';
import { ZKEVM_WITHDRAWALS_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 ZkEvmL2WithdrawalsListItem from 'ui/withdrawals/zkEvmL2/ZkEvmL2WithdrawalsListItem';
import ZkEvmL2WithdrawalsTable from 'ui/withdrawals/zkEvmL2/ZkEvmL2WithdrawalsTable';
const ZkEvmL2Withdrawals = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'zkevm_l2_withdrawals',
options: {
placeholderData: generateListStub<'zkevm_l2_withdrawals'>(
ZKEVM_WITHDRAWALS_ITEM,
50,
{ next_page_params: { items_count: 50, index: 1 } },
),
},
});
const countersQuery = useApiQuery('zkevm_l2_withdrawals_count', {
queryOptions: {
placeholderData: 1927029,
},
});
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>
{ data.items.map(((item, index) => (
<ZkEvmL2WithdrawalsListItem
key={ String(item.index) + (isPlaceholderData ? index : '') }
isLoading={ isPlaceholderData }
item={ item }
/>
))) }
</Show>
<Hide below="lg" ssr={ false }>
<ZkEvmL2WithdrawalsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
const text = (() => {
if (countersQuery.isError) {
return null;
}
return (
<Skeleton
isLoaded={ !countersQuery.isPlaceholderData }
display="inline-block"
>
A total of { countersQuery.data?.toLocaleString() } withdrawals found
</Skeleton>
);
})();
const actionBar = <StickyPaginationWithText text={ text } pagination={ pagination }/>;
return (
<>
<PageTitle title={ `Withdrawals (L2${ nbsp }${ rightLineArrow }${ nbsp }L1)` } withTextAd/>
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no withdrawals."
content={ content }
actionBar={ actionBar }
/>
</>
);
};
export default ZkEvmL2Withdrawals;
import { Skeleton, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { ZkEvmL2WithdrawalsItem } from 'types/api/zkEvmL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
const rollupFeature = config.features.rollup;
type Props = { item: ZkEvmL2WithdrawalsItem; isLoading?: boolean };
const ZkEvmL2WithdrawalsListItem = ({ item, isLoading }: Props) => {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'zkEvm') {
return null;
}
const timeAgo = dayjs(item.timestamp).fromNow();
return (
<ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>Block</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<BlockEntity
number={ item.block_number }
isLoading={ isLoading }
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 600 }
/>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Index</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ item.index }
</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L2 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<TxEntity
isLoading={ isLoading }
hash={ item.l2_transaction_hash }
fontSize="sm"
lineHeight={ 5 }
truncation="constant_long"
/>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Age</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">{ timeAgo }</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
{ item.l1_transaction_hash ? (
<TxEntityL1
isLoading={ isLoading }
hash={ item.l1_transaction_hash }
fontSize="sm"
lineHeight={ 5 }
truncation="constant_long"
/>
) : (
<chakra.span color="text_secondary">
Pending Claim
</chakra.span>
) }
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Value</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ BigNumber(item.value).toFormat() }
</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Token</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ item.symbol }
</Skeleton>
</ListItemMobileGrid.Value>
</ListItemMobileGrid.Container>
);
};
export default ZkEvmL2WithdrawalsListItem;
import { Table, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react';
import type { ZkEvmL2WithdrawalsItem } from 'types/api/zkEvmL2';
import { default as Thead } from 'ui/shared/TheadSticky';
import ZkEvmL2WithdrawalsTableItem from './ZkEvmL2WithdrawalsTableItem';
type Props = {
items: Array<ZkEvmL2WithdrawalsItem>;
top: number;
isLoading?: boolean;
}
const ZkEvmL2DepositsTable = ({ items, top, isLoading }: Props) => {
return (
<Table variant="simple" size="sm" style={{ tableLayout: 'auto' }} minW="950px">
<Thead top={ top }>
<Tr>
<Th>Block</Th>
<Th>Index</Th>
<Th>L2 txn hash</Th>
<Th>Age</Th>
<Th>L1 txn hash</Th>
<Th isNumeric>Value</Th>
<Th>Token</Th>
</Tr>
</Thead>
<Tbody>
{ items.map((item, index) => (
<ZkEvmL2WithdrawalsTableItem key={ String(item.index) + (isLoading ? index : '') } item={ item } isLoading={ isLoading }/>
)) }
</Tbody>
</Table>
);
};
export default ZkEvmL2DepositsTable;
import { Td, Tr, Skeleton, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { ZkEvmL2WithdrawalsItem } from 'types/api/zkEvmL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
const rollupFeature = config.features.rollup;
type Props = { item: ZkEvmL2WithdrawalsItem; isLoading?: boolean };
const ZkEvmL2WithdrawalsTableItem = ({ item, isLoading }: Props) => {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'zkEvm') {
return null;
}
const timeAgo = dayjs(item.timestamp).fromNow();
return (
<Tr>
<Td verticalAlign="middle">
<BlockEntity
number={ item.block_number }
isLoading={ isLoading }
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 600 }
noIcon
/>
</Td>
<Td verticalAlign="middle">
<Skeleton isLoaded={ !isLoading }>
<span>{ item.index }</span>
</Skeleton>
</Td>
<Td verticalAlign="middle">
<TxEntity
isLoading={ isLoading }
hash={ item.l2_transaction_hash }
fontSize="sm"
lineHeight={ 5 }
truncation="constant_long"
noIcon
/>
</Td>
<Td verticalAlign="middle" pr={ 12 }>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
<span>{ timeAgo }</span>
</Skeleton>
</Td>
<Td verticalAlign="middle">
{ item.l1_transaction_hash ? (
<TxEntityL1
isLoading={ isLoading }
hash={ item.l1_transaction_hash }
truncation="constant_long"
noIcon
fontSize="sm"
lineHeight={ 5 }
/>
) : (
<chakra.span color="text_secondary">
Pending Claim
</chakra.span>
) }
</Td>
<Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block">
<span>{ BigNumber(item.value).toFormat() }</span>
</Skeleton>
</Td>
<Td verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block">
<span>{ item.symbol }</span>
</Skeleton>
</Td>
</Tr>
);
};
export default ZkEvmL2WithdrawalsTableItem;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment