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

Refactor rollup features (#1599)

* refactor ENVs and app feature

* rename l2 into optimisticL2

* [skip ci] fix validator schema
parent e7a9bc6a
......@@ -13,7 +13,7 @@ export { default as marketplace } from './marketplace';
export { default as mixpanel } from './mixpanel';
export { default as nameService } from './nameService';
export { default as restApiDocs } from './restApiDocs';
export { default as optimisticRollup } from './optimisticRollup';
export { default as rollup } from './rollup';
export { default as safe } from './safe';
export { default as sentry } from './sentry';
export { default as sol2uml } from './sol2uml';
......@@ -23,4 +23,3 @@ export { default as txInterpretation } from './txInterpretation';
export { default as userOps } from './userOps';
export { default as verifiedTokens } from './verifiedTokens';
export { default as web3Wallet } from './web3Wallet';
export { default as zkEvmRollup } from './zkEvmRollup';
import type { Feature } from './types';
import type { RollupType } from 'types/client/rollup';
import { ROLLUP_TYPES } from 'types/client/rollup';
import { getEnvValue } from '../utils';
const type = (() => {
const envValue = getEnvValue('NEXT_PUBLIC_ROLLUP_TYPE');
return ROLLUP_TYPES.find((type) => type === envValue);
})();
const L1BaseUrl = getEnvValue('NEXT_PUBLIC_ROLLUP_L1_BASE_URL');
const L2WithdrawalUrl = getEnvValue('NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL');
const title = 'Rollup (L2) chain';
const config: Feature<{ L1BaseUrl: string; withdrawalUrl: string }> = (() => {
const L1BaseUrl = getEnvValue('NEXT_PUBLIC_L1_BASE_URL');
const withdrawalUrl = getEnvValue('NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL');
const config: Feature<{ type: RollupType; L1BaseUrl: string; L2WithdrawalUrl?: string }> = (() => {
if (
getEnvValue('NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK') === 'true' &&
L1BaseUrl &&
withdrawalUrl
) {
if (type && L1BaseUrl) {
return Object.freeze({
title,
isEnabled: true,
type,
L1BaseUrl,
withdrawalUrl,
L2WithdrawalUrl,
});
}
......
import type { Feature } from './types';
import { getEnvValue } from '../utils';
const title = 'ZkEVM rollup (L2) chain';
const config: Feature<{ L1BaseUrl: string; withdrawalUrl?: string }> = (() => {
const L1BaseUrl = getEnvValue('NEXT_PUBLIC_L1_BASE_URL');
const isZkEvm = getEnvValue('NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK') === 'true';
if (isZkEvm && L1BaseUrl) {
return Object.freeze({
title,
isEnabled: true,
L1BaseUrl,
});
}
return Object.freeze({
title,
isEnabled: false,
});
})();
export default config;
......@@ -44,8 +44,6 @@ NEXT_PUBLIC_APP_INSTANCE=jest
NEXT_PUBLIC_APP_ENV=testing
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK=false
NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK=false
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3100
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
......
......@@ -47,6 +47,6 @@ NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/base-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_STATS_API_HOST=https://stats-optimism-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK=true
NEXT_PUBLIC_L1_BASE_URL=https://blockscout-main.k8s-dev.blockscout.com
NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_ROLLUP_TYPE=optimistic
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://blockscout-main.k8s-dev.blockscout.com
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw
......@@ -41,8 +41,6 @@ NEXT_PUBLIC_APP_ENV=testing
NEXT_PUBLIC_APP_INSTANCE=pw
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK=false
NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK=false
NEXT_PUBLIC_AD_BANNER_PROVIDER=slise
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3100
......
......@@ -47,5 +47,5 @@ NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
# rollup
NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK=true
NEXT_PUBLIC_L1_BASE_URL=https://polygon.blockscout.com
NEXT_PUBLIC_ROLLUP_TYPE=zkEvm
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://polygon.blockscout.com
......@@ -15,6 +15,7 @@ import type { ContractCodeIde } from '../../../types/client/contract';
import type { MarketplaceAppOverview } from '../../../types/client/marketplace';
import { NAVIGATION_LINK_IDS } from '../../../types/client/navigation-items';
import type { NavItemExternal, NavigationLinkId } from '../../../types/client/navigation-items';
import { ROLLUP_TYPES } from '../../../types/client/rollup';
import type { BridgedTokenChain, TokenBridge } from '../../../types/client/token';
import { PROVIDERS as TX_INTERPRETATION_PROVIDERS } from '../../../types/client/txInterpretation';
import type { WalletType } from '../../../types/client/wallets';
......@@ -114,23 +115,20 @@ const beaconChainSchema = yup
const rollupSchema = yup
.object()
.shape({
NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK: yup.boolean(),
NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL: yup
NEXT_PUBLIC_ROLLUP_TYPE: yup.string().oneOf(ROLLUP_TYPES),
NEXT_PUBLIC_ROLLUP_L1_BASE_URL: yup
.string()
.when('NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK', {
.when('NEXT_PUBLIC_ROLLUP_TYPE', {
is: (value: string) => value,
then: (schema) => schema.test(urlTest).required(),
// eslint-disable-next-line max-len
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL cannot not be used if NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK is not set to "true"'),
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL cannot not be used if NEXT_PUBLIC_ROLLUP_TYPE is not defined'),
}),
NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK: yup.boolean(),
NEXT_PUBLIC_L1_BASE_URL: yup
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL: yup
.string()
.when([ 'NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK', 'NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK' ], {
is: (isOptimistic?: boolean, isZk?: boolean) => isOptimistic || isZk,
.when('NEXT_PUBLIC_ROLLUP_TYPE', {
is: (value: string) => value === 'optimistic',
then: (schema) => schema.test(urlTest).required(),
// eslint-disable-next-line max-len
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_L1_BASE_URL cannot not be used if NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK or NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK is not set to "true"'),
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL cannot not be used if NEXT_PUBLIC_ROLLUP_TYPE is not defined'),
}),
});
......
NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK=true
NEXT_PUBLIC_L1_BASE_URL=https://example.com
NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL=https://example.com
\ No newline at end of file
NEXT_PUBLIC_ROLLUP_TYPE=optimistic
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://example.com
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://example.com
\ No newline at end of file
......@@ -191,9 +191,9 @@ frontend:
NEXT_PUBLIC_VISUALIZE_API_HOST: https://visualizer-test.k8s-dev.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST: https://contracts-info-test.k8s-dev.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST: https://admin-rs-test.k8s-dev.blockscout.com
NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK: "true"
NEXT_PUBLIC_L1_BASE_URL: https://eth-goerli.blockscout.com/
NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_ROLLUP_TYPE: optimistic
NEXT_PUBLIC_ROLLUP_L1_BASE_URL: https://eth-goerli.blockscout.com/
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
envFromSecret:
NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID
......
......@@ -65,9 +65,9 @@ frontend:
NEXT_PUBLIC_VISUALIZE_API_HOST: https://visualizer-optimism-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST: https://contracts-info-test.k8s-dev.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST: https://admin-rs-test.k8s-dev.blockscout.com
NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK: "true"
NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_L1_BASE_URL: https://blockscout-main.k8s-dev.blockscout.com
NEXT_PUBLIC_ROLLUP_TYPE=optimistic
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://blockscout-main.k8s-dev.blockscout.com
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
NEXT_PUBLIC_USE_NEXT_JS_PROXY: true
envFromSecret:
......
# Deprecated environment variables
| Variable | Type | Description | Compulsoriness | Default value | Example value | Deprecated in version | Comment |
| --- | --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK | `boolean` | Set to true for optimistic L2 solutions | Required | - | `true` | v1.24.0 | Replaced by NEXT_PUBLIC_ROLLUP_TYPE |
| NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK | `boolean` | Set to true for zkevm L2 solutions | Required | - | `true` | v1.24.0 | Replaced by NEXT_PUBLIC_ROLLUP_TYPE |
| NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL | `string` | URL for optimistic L2 -> L1 withdrawals | Required | - | `https://app.optimism.io/bridge/withdraw` | v1.24.0 | Renamed to NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL |
| NEXT_PUBLIC_L1_BASE_URL | `string` | Blockscout base URL for L1 network | Required | - | `'http://eth-goerli.blockscout.com'` | v1.24.0 | Renamed to NEXT_PUBLIC_ROLLUP_L1_BASE_URL |
......@@ -34,8 +34,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
- [Text ads](ENVS.md#text-ads)
- [Beacon chain](ENVS.md#beacon-chain)
- [User operations](ENVS.md#user-operations-feature-erc-4337)
- [Optimistic rollup (L2) chain](ENVS.md#optimistic-rollup-l2-chain)
- [ZkEvm rollup (L2) chain](NVS.md#zkevm-rollup-l2-chain)
- [Rollup chain](ENVS.md#rollup-chain)
- [Export data to CSV file](ENVS.md#export-data-to-csv-file)
- [Google analytics](ENVS.md#google-analytics)
- [Mixpanel analytics](ENVS.md#mixpanel-analytics)
......@@ -365,22 +364,13 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi
&nbsp;
### Optimistic rollup (L2) chain
### Rollup chain
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK | `boolean` | Set to true for optimistic L2 solutions | Required | - | `true` |
| NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL | `string` | URL for optimistic L2 -> L1 withdrawals | Required | - | `https://app.optimism.io/bridge/withdraw` |
| NEXT_PUBLIC_L1_BASE_URL | `string` | Blockscout base URL for L1 network | Required | - | `'http://eth-goerli.blockscout.com'` |
&nbsp;
### ZkEvm rollup (L2) chain
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK | `boolean` | Set to true for zkevm L2 solutions | Required | - | `true` |
| NEXT_PUBLIC_L1_BASE_URL | `string` | Blockscout base URL for L1 network | Required | - | `'http://eth-goerli.blockscout.com'` |
| NEXT_PUBLIC_ROLLUP_TYPE | `'optimistic' \| 'zkEvm' ` | Rollup chain type | Required | - | `'optimistic'` |
| NEXT_PUBLIC_ROLLUP_L1_BASE_URL | `string` | Blockscout base URL for L1 network | Required | - | `'http://eth-goerli.blockscout.com'` |
| NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL | `string` | URL for L2 -> L1 withdrawals | - | - | `https://app.optimism.io/bridge/withdraw` |
&nbsp;
......
......@@ -47,11 +47,14 @@ import type {
} from 'types/api/ens';
import type { IndexingStatus } from 'types/api/indexingStatus';
import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
import type { L2DepositsResponse, L2DepositsItem } from 'types/api/l2Deposits';
import type { L2OutputRootsResponse } from 'types/api/l2OutputRoots';
import type { L2TxnBatchesResponse } from 'types/api/l2TxnBatches';
import type { L2WithdrawalsResponse } from 'types/api/l2Withdrawals';
import type { LogsResponseTx, LogsResponseAddress } from 'types/api/log';
import type {
OptimisticL2DepositsResponse,
OptimisticL2DepositsItem,
OptimisticL2OutputRootsResponse,
OptimisticL2TxnBatchesResponse,
OptimisticL2WithdrawalsResponse,
} from 'types/api/optimisticL2';
import type { RawTracesResponse } from 'types/api/rawTrace';
import type { SearchRedirectResult, SearchResult, SearchResultFilters, SearchResultItem } from 'types/api/search';
import type { Counters, StatsCharts, StatsChart, HomeStats } from 'types/api/stats';
......@@ -81,7 +84,7 @@ import type { UserOpsResponse, UserOp, UserOpsFilters, UserOpsAccount } from 'ty
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/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem, ZkEvmL2TxnBatchesResponse, ZkEvmL2TxnBatchTxs } from 'types/api/zkEvmL2';
import type { ArrayElement } from 'types/utils';
import config from 'configs/app';
......@@ -688,7 +691,7 @@ Q extends 'homepage_chart_market' ? ChartMarketResponse :
Q extends 'homepage_blocks' ? Array<Block> :
Q extends 'homepage_txs' ? Array<Transaction> :
Q extends 'homepage_txs_watchlist' ? Array<Transaction> :
Q extends 'homepage_deposits' ? Array<L2DepositsItem> :
Q extends 'homepage_deposits' ? Array<OptimisticL2DepositsItem> :
Q extends 'homepage_zkevm_l2_batches' ? { items: Array<ZkEvmL2TxnBatchesItem> } :
Q extends 'homepage_indexing_status' ? IndexingStatus :
Q extends 'homepage_zkevm_latest_batch' ? number :
......@@ -752,10 +755,10 @@ 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' ? L2OutputRootsResponse :
Q extends 'l2_withdrawals' ? L2WithdrawalsResponse :
Q extends 'l2_deposits' ? L2DepositsResponse :
Q extends 'l2_txn_batches' ? L2TxnBatchesResponse :
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 :
......
......@@ -67,7 +67,9 @@ export default function useNavItems(): ReturnType {
isActive: pathname === '/name-domains' || pathname === '/name-domains/[name]',
} : null;
if (config.features.zkEvmRollup.isEnabled) {
const rollupFeature = config.features.rollup;
if (rollupFeature.isEnabled && rollupFeature.type === 'zkEvm') {
blockchainNavItems = [
[
txs,
......@@ -86,7 +88,7 @@ export default function useNavItems(): ReturnType {
ensLookup,
].filter(Boolean),
];
} else if (config.features.optimisticRollup.isEnabled) {
} else if (rollupFeature.isEnabled && rollupFeature.type === 'optimistic') {
blockchainNavItems = [
[
txs,
......
......@@ -6,7 +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';
import type { NewZkEvmBatchSocketResponse } from 'types/api/zkEvmL2';
export type SocketMessageParams = SocketMessage.NewBlock |
SocketMessage.BlocksIndexStatus |
......
import type { ZkEvmL2TxnBatch } from 'types/api/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatch } from 'types/api/zkEvmL2';
export const txnBatchData: ZkEvmL2TxnBatch = {
acc_input_hash: '0x4bf88aabe33713b7817266d7860912c58272d808da7397cdc627ca53b296fad3',
......
import type { ZkEvmL2TxnBatchesResponse } from 'types/api/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatchesResponse } from 'types/api/zkEvmL2';
export const txnBatchesData: ZkEvmL2TxnBatchesResponse = {
items: [
......
import type { GetServerSideProps } from 'next';
import config from 'configs/app';
const rollupFeature = config.features.rollup;
export type Props = {
cookies: string;
......@@ -49,7 +50,10 @@ export const verifiedAddresses: GetServerSideProps<Props> = async(context) => {
};
export const withdrawals: GetServerSideProps<Props> = async(context) => {
if (!config.features.beaconChain.isEnabled && !config.features.optimisticRollup.isEnabled) {
if (
!config.features.beaconChain.isEnabled &&
!(rollupFeature.isEnabled && rollupFeature.type === 'optimistic')
) {
return {
notFound: true,
};
......@@ -59,7 +63,7 @@ export const withdrawals: GetServerSideProps<Props> = async(context) => {
};
export const rollup: GetServerSideProps<Props> = async(context) => {
if (!config.features.optimisticRollup.isEnabled && !config.features.zkEvmRollup.isEnabled) {
if (!config.features.rollup.isEnabled) {
return {
notFound: true,
};
......@@ -69,7 +73,7 @@ export const rollup: GetServerSideProps<Props> = async(context) => {
};
export const optimisticRollup: GetServerSideProps<Props> = async(context) => {
if (!config.features.optimisticRollup.isEnabled) {
if (!(rollupFeature.isEnabled && rollupFeature.type === 'optimistic')) {
return {
notFound: true,
};
......@@ -79,7 +83,7 @@ export const optimisticRollup: GetServerSideProps<Props> = async(context) => {
};
export const zkEvmRollup: GetServerSideProps<Props> = async(context) => {
if (!config.features.zkEvmRollup.isEnabled) {
if (!(rollupFeature.isEnabled && rollupFeature.type === 'zkEvm')) {
return {
notFound: true,
};
......
......@@ -38,9 +38,9 @@ declare module "nextjs-routes" {
| StaticRoute<"/login">
| DynamicRoute<"/name-domains/[name]", { "name": string }>
| StaticRoute<"/name-domains">
| StaticRoute<"/output-roots">
| DynamicRoute<"/op/[hash]", { "hash": string }>
| StaticRoute<"/ops">
| StaticRoute<"/output-roots">
| StaticRoute<"/search-results">
| StaticRoute<"/stats">
| DynamicRoute<"/token/[hash]", { "hash": string }>
......
......@@ -5,12 +5,19 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
import config from 'configs/app';
const rollupFeature = config.features.rollup;
const Batches = dynamic(() => {
if (config.features.zkEvmRollup.isEnabled) {
if (!rollupFeature.isEnabled) {
throw new Error('Rollup feature is not enabled.');
}
switch (rollupFeature.type) {
case 'zkEvm':
return import('ui/pages/ZkEvmL2TxnBatches');
case 'optimistic':
return import('ui/pages/OptimisticL2TxnBatches');
}
return import('ui/pages/L2TxnBatches');
}, { ssr: false });
const Page: NextPage = () => {
......
......@@ -4,7 +4,7 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
const Deposits = dynamic(() => import('ui/pages/L2Deposits'), { ssr: false });
const Deposits = dynamic(() => import('ui/pages/OptimisticL2Deposits'), { ssr: false });
const Page: NextPage = () => {
return (
......
......@@ -4,7 +4,7 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
const OutputRoots = dynamic(() => import('ui/pages/L2OutputRoots'), { ssr: false });
const OutputRoots = dynamic(() => import('ui/pages/OptimisticL2OutputRoots'), { ssr: false });
const Page: NextPage = () => {
return (
......
......@@ -5,12 +5,18 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
import config from 'configs/app';
const rollupFeature = config.features.rollup;
const beaconChainFeature = config.features.beaconChain;
const Withdrawals = dynamic(() => {
if (config.features.optimisticRollup.isEnabled) {
return import('ui/pages/L2Withdrawals');
if (rollupFeature.isEnabled && rollupFeature.type === 'optimistic') {
return import('ui/pages/OptimisticL2Withdrawals');
}
return import('ui/pages/Withdrawals');
if (beaconChainFeature.isEnabled) {
return import('ui/pages/BeaconChainWithdrawals');
}
throw new Error('Withdrawals feature is not enabled.');
}, { ssr: false });
const Page: NextPage = () => {
......
......@@ -15,9 +15,9 @@ export const featureEnvs = {
{ name: 'NEXT_PUBLIC_HAS_BEACON_CHAIN', value: 'true' },
],
optimisticRollup: [
{ name: 'NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK', value: 'true' },
{ name: 'NEXT_PUBLIC_L1_BASE_URL', value: 'https://localhost:3101' },
{ name: 'NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL', value: 'https://localhost:3102' },
{ name: 'NEXT_PUBLIC_ROLLUP_TYPE', value: 'optimistic' },
{ name: 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', value: 'https://localhost:3101' },
{ name: 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL', value: 'https://localhost:3102' },
],
bridgedTokens: [
{
......@@ -32,9 +32,9 @@ export const featureEnvs = {
txInterpretation: [
{ name: 'NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER', value: 'blockscout' },
],
zkRollup: [
{ name: 'NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK', value: 'true' },
{ name: 'NEXT_PUBLIC_L1_BASE_URL', value: 'https://localhost:3101' },
zkEvmRollup: [
{ name: 'NEXT_PUBLIC_ROLLUP_TYPE', value: 'zkEvm' },
{ name: 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', value: 'https://localhost:3101' },
],
userOps: [
{ name: 'NEXT_PUBLIC_HAS_USER_OPS', value: 'true' },
......
import type { L2DepositsItem } from 'types/api/l2Deposits';
import type { L2OutputRootsItem } from 'types/api/l2OutputRoots';
import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches';
import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals';
import type {
OptimisticL2DepositsItem,
OptimisticL2OutputRootsItem,
OptimisticL2TxnBatchesItem,
OptimisticL2WithdrawalsItem,
} from 'types/api/optimisticL2';
import { ADDRESS_HASH, ADDRESS_PARAMS } from './addressParams';
import { TX_HASH } from './tx';
export const L2_DEPOSIT_ITEM: L2DepositsItem = {
export const L2_DEPOSIT_ITEM: OptimisticL2DepositsItem = {
l1_block_number: 9045233,
l1_block_timestamp: '2023-05-22T18:00:36.000000Z',
l1_tx_hash: TX_HASH,
......@@ -15,7 +17,7 @@ export const L2_DEPOSIT_ITEM: L2DepositsItem = {
l2_tx_hash: TX_HASH,
};
export const L2_WITHDRAWAL_ITEM: L2WithdrawalsItem = {
export const L2_WITHDRAWAL_ITEM: OptimisticL2WithdrawalsItem = {
challenge_period_end: null,
from: ADDRESS_PARAMS,
l1_tx_hash: TX_HASH,
......@@ -26,7 +28,7 @@ export const L2_WITHDRAWAL_ITEM: L2WithdrawalsItem = {
status: 'Ready to prove',
};
export const L2_TXN_BATCHES_ITEM: L2TxnBatchesItem = {
export const L2_TXN_BATCHES_ITEM: OptimisticL2TxnBatchesItem = {
epoch_number: 9103513,
l1_timestamp: '2023-06-01T14:46:48.000000Z',
l1_tx_hashes: [
......@@ -36,7 +38,7 @@ export const L2_TXN_BATCHES_ITEM: L2TxnBatchesItem = {
tx_count: 9,
};
export const L2_OUTPUT_ROOTS_ITEM: L2OutputRootsItem = {
export const L2_OUTPUT_ROOTS_ITEM: OptimisticL2OutputRootsItem = {
l1_block_number: 9103684,
l1_timestamp: '2023-06-01T15:26:12.000000Z',
l1_tx_hash: TX_HASH,
......
import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2';
import { TX_HASH } from './tx';
......
export type L2DepositsItem = {
l1_block_number: number;
l1_tx_hash: string;
l1_block_timestamp: string;
l1_tx_origin: string;
l2_tx_gas_limit: string;
l2_tx_hash: string;
}
export type L2DepositsResponse = {
items: Array<L2DepositsItem>;
next_page_params: {
items_count: number;
l1_block_number: number;
tx_hash: string;
};
}
export type L2OutputRootsItem = {
l1_block_number: number;
l1_timestamp: string;
l1_tx_hash: string;
l2_block_number: number;
l2_output_index: number;
output_root: string;
}
export type L2OutputRootsResponse = {
items: Array<L2OutputRootsItem>;
next_page_params: {
index: number;
items_count: number;
};
}
export type L2TxnBatchesItem = {
epoch_number: number;
l1_tx_hashes: Array<string>;
l1_timestamp: string;
l2_block_number: number;
tx_count: number;
}
export type L2TxnBatchesResponse = {
items: Array<L2TxnBatchesItem>;
next_page_params: {
block_number: number;
items_count: number;
};
}
import type { AddressParam } from './addressParams';
export type L2WithdrawalsItem = {
'challenge_period_end': string | null;
'from': AddressParam | null;
'l1_tx_hash': string | null;
'l2_timestamp': string | null;
'l2_tx_hash': string;
'msg_nonce': number;
'msg_nonce_version': number;
'status': string;
}
export const WITHDRAWAL_STATUSES = [
'Waiting for state root',
'Ready to prove',
'In challenge period',
'Ready for relay',
'Relayed',
] as const;
export type L2WithdrawalStatus = typeof WITHDRAWAL_STATUSES[number];
export type L2WithdrawalsResponse = {
items: Array<L2WithdrawalsItem>;
'next_page_params': {
'items_count': number;
'nonce': string;
};
}
import type { AddressParam } from './addressParams';
export type OptimisticL2DepositsItem = {
l1_block_number: number;
l1_tx_hash: string;
l1_block_timestamp: string;
l1_tx_origin: string;
l2_tx_gas_limit: string;
l2_tx_hash: string;
}
export type OptimisticL2DepositsResponse = {
items: Array<OptimisticL2DepositsItem>;
next_page_params: {
items_count: number;
l1_block_number: number;
tx_hash: string;
};
}
export type OptimisticL2OutputRootsItem = {
l1_block_number: number;
l1_timestamp: string;
l1_tx_hash: string;
l2_block_number: number;
l2_output_index: number;
output_root: string;
}
export type OptimisticL2OutputRootsResponse = {
items: Array<OptimisticL2OutputRootsItem>;
next_page_params: {
index: number;
items_count: number;
};
}
export type OptimisticL2TxnBatchesItem = {
epoch_number: number;
l1_tx_hashes: Array<string>;
l1_timestamp: string;
l2_block_number: number;
tx_count: number;
}
export type OptimisticL2TxnBatchesResponse = {
items: Array<OptimisticL2TxnBatchesItem>;
next_page_params: {
block_number: number;
items_count: number;
};
}
export type OptimisticL2WithdrawalsItem = {
'challenge_period_end': string | null;
'from': AddressParam | null;
'l1_tx_hash': string | null;
'l2_timestamp': string | null;
'l2_tx_hash': string;
'msg_nonce': number;
'msg_nonce_version': number;
'status': string;
}
export const WITHDRAWAL_STATUSES = [
'Waiting for state root',
'Ready to prove',
'In challenge period',
'Ready for relay',
'Relayed',
] as const;
export type OptimisticL2WithdrawalStatus = typeof WITHDRAWAL_STATUSES[number];
export type OptimisticL2WithdrawalsResponse = {
items: Array<OptimisticL2WithdrawalsItem>;
'next_page_params': {
'items_count': number;
'nonce': string;
};
}
......@@ -2,7 +2,7 @@ import type { AddressParam } from './addressParams';
import type { BlockTransactionsResponse } from './block';
import type { DecodedInput } from './decodedInput';
import type { Fee } from './fee';
import type { L2WithdrawalStatus } from './l2Withdrawals';
import type { OptimisticL2WithdrawalStatus } from './optimisticL2';
import type { TokenInfo } from './token';
import type { TokenTransfer } from './tokenTransfer';
import type { TxAction } from './txAction';
......@@ -17,7 +17,7 @@ type WrappedTransactionFields = 'decoded_input' | 'fee' | 'gas_limit' | 'gas_pri
export interface OpWithdrawal {
l1_transaction_hash: string;
nonce: number;
status: L2WithdrawalStatus;
status: OptimisticL2WithdrawalStatus;
}
export type Transaction = {
......
import type { ArrayElement } from 'types/utils';
export const ROLLUP_TYPES = [
'optimistic',
'zkEvm',
] as const;
export type RollupType = ArrayElement<typeof ROLLUP_TYPES>;
......@@ -9,8 +9,8 @@ import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import WithdrawalsListItem from 'ui/withdrawals/WithdrawalsListItem';
import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable';
import BeaconChainWithdrawalsListItem from 'ui/withdrawals/beaconChain/BeaconChainWithdrawalsListItem';
import BeaconChainWithdrawalsTable from 'ui/withdrawals/beaconChain/BeaconChainWithdrawalsTable';
const AddressWithdrawals = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>}) => {
const router = useRouter();
......@@ -32,7 +32,7 @@ const AddressWithdrawals = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
<>
<Show below="lg" ssr={ false }>
{ data.items.map((item, index) => (
<WithdrawalsListItem
<BeaconChainWithdrawalsListItem
key={ item.index + Number(isPlaceholderData ? index : '') }
item={ item }
view="address"
......@@ -41,7 +41,7 @@ const AddressWithdrawals = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
)) }
</Show>
<Hide below="lg" ssr={ false }>
<WithdrawalsTable items={ data.items } view="address" top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
<BeaconChainWithdrawalsTable items={ data.items } view="address" top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null ;
......
......@@ -34,7 +34,7 @@ interface Props {
query: BlockQuery;
}
const isRollup = config.features.optimisticRollup.isEnabled || config.features.zkEvmRollup.isEnabled;
const rollupFeature = config.features.rollup;
const BlockDetails = ({ query }: Props) => {
const [ isExpanded, setIsExpanded ] = React.useState(false);
......@@ -73,7 +73,7 @@ const BlockDetails = ({ query }: Props) => {
const validatorTitle = getNetworkValidatorTitle();
const rewardBreakDown = (() => {
if (isRollup || totalReward.isEqualTo(ZERO) || txFees.isEqualTo(ZERO) || burntFees.isEqualTo(ZERO)) {
if (rollupFeature.isEnabled || totalReward.isEqualTo(ZERO) || txFees.isEqualTo(ZERO) || burntFees.isEqualTo(ZERO)) {
return null;
}
......@@ -107,7 +107,7 @@ const BlockDetails = ({ query }: Props) => {
})();
const verificationTitle = (() => {
if (config.features.zkEvmRollup.isEnabled) {
if (rollupFeature.isEnabled && rollupFeature.type === 'zkEvm') {
return 'Sequenced by';
}
......@@ -205,7 +205,7 @@ const BlockDetails = ({ query }: Props) => {
{ /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ }
</DetailsInfoItem>
) }
{ !isRollup && !totalReward.isEqualTo(ZERO) && !config.UI.views.block.hiddenFields?.total_reward && (
{ !rollupFeature.isEnabled && !totalReward.isEqualTo(ZERO) && !config.UI.views.block.hiddenFields?.total_reward && (
<DetailsInfoItem
title="Block reward"
hint={
......
......@@ -3,8 +3,8 @@ import React from 'react';
import DataListDisplay from 'ui/shared/DataListDisplay';
import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import WithdrawalsList from 'ui/withdrawals/WithdrawalsList';
import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable';
import BeaconChainWithdrawalsList from 'ui/withdrawals/beaconChain/BeaconChainWithdrawalsList';
import BeaconChainWithdrawalsTable from 'ui/withdrawals/beaconChain/BeaconChainWithdrawalsTable';
type Props = {
blockWithdrawalsQuery: QueryWithPagesResult<'block_withdrawals'>;
......@@ -14,14 +14,14 @@ const BlockWithdrawals = ({ blockWithdrawalsQuery }: Props) => {
const content = blockWithdrawalsQuery.data?.items ? (
<>
<Show below="lg" ssr={ false }>
<WithdrawalsList
<BeaconChainWithdrawalsList
items={ blockWithdrawalsQuery.data.items }
isLoading={ blockWithdrawalsQuery.isPlaceholderData }
view="block"
/>
</Show>
<Hide below="lg" ssr={ false }>
<WithdrawalsTable
<BeaconChainWithdrawalsTable
items={ blockWithdrawalsQuery.data.items }
isLoading={ blockWithdrawalsQuery.isPlaceholderData }
top={ blockWithdrawalsQuery.pagination.isVisible ? 80 : 0 }
......
......@@ -28,7 +28,7 @@ interface Props {
enableTimeIncrement?: boolean;
}
const isRollup = config.features.optimisticRollup.isEnabled || config.features.zkEvmRollup.isEnabled;
const isRollup = config.features.rollup.isEnabled;
const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
const totalReward = getBlockTotalReward(data);
......
......@@ -28,7 +28,7 @@ const GAS_COL_WEIGHT = 33;
const REWARD_COL_WEIGHT = 22;
const FEES_COL_WEIGHT = 22;
const isRollup = config.features.optimisticRollup.isEnabled || config.features.zkEvmRollup.isEnabled;
const isRollup = config.features.rollup.isEnabled;
const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum, socketInfoAlert }: Props) => {
......
......@@ -25,7 +25,7 @@ interface Props {
enableTimeIncrement?: boolean;
}
const isRollup = config.features.optimisticRollup.isEnabled || config.features.zkEvmRollup.isEnabled;
const isRollup = config.features.rollup.isEnabled;
const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
const totalReward = getBlockTotalReward(data);
......
......@@ -2,7 +2,7 @@ import { Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits';
import type { OptimisticL2DepositsItem } from 'types/api/optimisticL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
......@@ -12,14 +12,14 @@ 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.optimisticRollup;
const rollupFeature = config.features.rollup;
type Props = { item: L2DepositsItem; isLoading?: boolean };
type Props = { item: OptimisticL2DepositsItem; isLoading?: boolean };
const DepositsListItem = ({ item, isLoading }: Props) => {
const OptimisticDepositsListItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.l1_block_timestamp).fromNow();
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
return null;
}
......@@ -80,4 +80,4 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
);
};
export default DepositsListItem;
export default OptimisticDepositsListItem;
import { Table, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits';
import type { OptimisticL2DepositsItem } from 'types/api/optimisticL2';
import { default as Thead } from 'ui/shared/TheadSticky';
import DepositsTableItem from './DepositsTableItem';
import OptimisticDepositsTableItem from './OptimisticDepositsTableItem';
type Props = {
items: Array<L2DepositsItem>;
items: Array<OptimisticL2DepositsItem>;
top: number;
isLoading?: boolean;
}
const DepositsTable = ({ items, top, isLoading }: Props) => {
const OptimisticDepositsTable = ({ items, top, isLoading }: Props) => {
return (
<Table variant="simple" size="sm" style={{ tableLayout: 'auto' }} minW="950px">
<Thead top={ top }>
......@@ -28,11 +28,11 @@ const DepositsTable = ({ items, top, isLoading }: Props) => {
</Thead>
<Tbody>
{ items.map((item, index) => (
<DepositsTableItem key={ item.l2_tx_hash + (isLoading ? index : '') } item={ item } isLoading={ isLoading }/>
<OptimisticDepositsTableItem key={ item.l2_tx_hash + (isLoading ? index : '') } item={ item } isLoading={ isLoading }/>
)) }
</Tbody>
</Table>
);
};
export default DepositsTable;
export default OptimisticDepositsTable;
......@@ -2,7 +2,7 @@ import { Td, Tr, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits';
import type { OptimisticL2DepositsItem } from 'types/api/optimisticL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
......@@ -11,14 +11,14 @@ 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.optimisticRollup;
const rollupFeature = config.features.rollup;
type Props = { item: L2DepositsItem; isLoading?: boolean };
type Props = { item: OptimisticL2DepositsItem; isLoading?: boolean };
const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
const OptimisticDepositsTableItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.l1_block_timestamp).fromNow();
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
return null;
}
......@@ -71,4 +71,4 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
);
};
export default WithdrawalsTableItem;
export default OptimisticDepositsTableItem;
......@@ -24,7 +24,7 @@ const LatestBlocks = () => {
const isMobile = useIsMobile();
// const blocksMaxCount = isMobile ? 2 : 3;
let blocksMaxCount: number;
if (config.features.optimisticRollup.isEnabled || config.UI.views.block.hiddenFields?.total_reward) {
if (config.features.rollup.isEnabled || config.UI.views.block.hiddenFields?.total_reward) {
blocksMaxCount = isMobile ? 4 : 5;
} else {
blocksMaxCount = isMobile ? 2 : 3;
......
......@@ -59,14 +59,14 @@ const LatestBlocksItem = ({ block, isLoading }: Props) => {
<Skeleton isLoaded={ !isLoading }>Txn</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ block.tx_count }</span></Skeleton>
{ !config.features.optimisticRollup.isEnabled && !config.UI.views.block.hiddenFields?.total_reward && (
{ !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.total_reward && (
<>
<Skeleton isLoaded={ !isLoading }>Reward</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ totalReward.dp(10).toFixed() }</span></Skeleton>
</>
) }
{ !config.features.optimisticRollup.isEnabled && !config.UI.views.block.hiddenFields?.miner && (
{ !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.miner && (
<>
<Skeleton isLoaded={ !isLoading } textTransform="capitalize">{ getNetworkValidatorTitle() }</Skeleton>
<AddressEntity
......
......@@ -6,7 +6,7 @@ import {
} from '@chakra-ui/react';
import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits';
import type { OptimisticL2DepositsItem } from 'types/api/optimisticL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
......@@ -15,18 +15,18 @@ 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.optimisticRollup;
const feature = config.features.rollup;
type Props = {
item: L2DepositsItem;
item: OptimisticL2DepositsItem;
isLoading?: boolean;
}
const LatestTxsItem = ({ item, isLoading }: Props) => {
const LatestDepositsItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.l1_block_timestamp).fromNow();
const isMobile = useIsMobile();
if (!feature.isEnabled) {
if (!feature.isEnabled || feature.type !== 'optimistic') {
return null;
}
......@@ -116,4 +116,4 @@ const LatestTxsItem = ({ item, isLoading }: Props) => {
);
};
export default React.memo(LatestTxsItem);
export default React.memo(LatestDepositsItem);
......@@ -13,7 +13,7 @@ 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,
context: contextWithEnvs(configs.featureEnvs.zkEvmRollup) as any,
});
test('default view +@mobile +@dark-mode', async({ mount, page }) => {
......
......@@ -4,7 +4,7 @@ import { AnimatePresence } from 'framer-motion';
import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2';
import { route } from 'nextjs-routes';
......
......@@ -6,12 +6,12 @@ import {
import { motion } from 'framer-motion';
import React from 'react';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2';
import { route } from 'nextjs-routes';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import ZkEvmBatchEntityL2 from 'ui/shared/entities/block/ZkEvmBatchEntityL2';
import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
import LinkInternal from 'ui/shared/LinkInternal';
import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus';
......@@ -35,7 +35,7 @@ const LatestZkevmL2BatchItem = ({ batch, isLoading }: Props) => {
p={ 6 }
>
<Flex alignItems="center" overflow="hidden" w="100%" mb={ 3 }>
<ZkEvmBatchEntityL2
<BatchEntityL2
isLoading={ isLoading }
number={ batch.number }
tailLength={ 2 }
......
......@@ -15,6 +15,7 @@ import StatsItem from './StatsItem';
const hasGasTracker = config.UI.homepage.showGasTracker;
const hasAvgBlockTime = config.UI.homepage.showAvgBlockTime;
const rollupFeature = config.features.rollup;
const Stats = () => {
const { data, isPlaceholderData, isError, dataUpdatedAt } = useApiQuery('homepage_stats', {
......@@ -32,7 +33,7 @@ const Stats = () => {
const zkEvmLatestBatchQuery = useApiQuery('homepage_zkevm_latest_batch', {
queryOptions: {
placeholderData: 12345,
enabled: config.features.zkEvmRollup.isEnabled,
enabled: rollupFeature.isEnabled && rollupFeature.type === 'zkEvm',
},
});
......@@ -68,7 +69,7 @@ const Stats = () => {
content = (
<>
{ config.features.zkEvmRollup.isEnabled ? (
{ rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' ? (
<StatsItem
icon="txn_batches"
title="Latest batch"
......
......@@ -7,13 +7,14 @@ import LatestDeposits from 'ui/home/LatestDeposits';
import LatestTxs from 'ui/home/LatestTxs';
import LatestWatchlistTxs from 'ui/home/LatestWatchlistTxs';
import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll';
const rollupFeature = config.features.rollup;
const TransactionsHome = () => {
const hasAccount = useHasAccount();
if (config.features.optimisticRollup.isEnabled || hasAccount) {
if ((rollupFeature.isEnabled && rollupFeature.type === 'optimistic') || hasAccount) {
const tabs = [
{ id: 'txn', title: 'Latest txn', component: <LatestTxs/> },
config.features.optimisticRollup.isEnabled && { id: 'deposits', title: 'Deposits (L1→L2 txn)', component: <LatestDeposits/> },
rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && { id: 'deposits', title: 'Deposits (L1→L2 txn)', component: <LatestDeposits/> },
hasAccount && { id: 'watchlist', title: 'Watch list', component: <LatestWatchlistTxs/> },
].filter(Boolean);
return (
......
import { Flex, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { L2OutputRootsItem } from 'types/api/l2OutputRoots';
import type { OptimisticL2OutputRootsItem } from 'types/api/optimisticL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
......@@ -11,14 +11,14 @@ 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.optimisticRollup;
const rollupFeature = config.features.rollup;
type Props = { item: L2OutputRootsItem; isLoading?: boolean };
type Props = { item: OptimisticL2OutputRootsItem; isLoading?: boolean };
const OutputRootsListItem = ({ item, isLoading }: Props) => {
const OptimisticL2OutputRootsListItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.l1_timestamp).fromNow();
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
return null;
}
......@@ -72,4 +72,4 @@ const OutputRootsListItem = ({ item, isLoading }: Props) => {
);
};
export default OutputRootsListItem;
export default OptimisticL2OutputRootsListItem;
import { Table, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react';
import type { L2OutputRootsItem } from 'types/api/l2OutputRoots';
import type { OptimisticL2OutputRootsItem } from 'types/api/optimisticL2';
import { default as Thead } from 'ui/shared/TheadSticky';
import OutputRootsTableItem from './OutputRootsTableItem';
import OptimisticL2OutputRootsTableItem from './OptimisticL2OutputRootsTableItem';
type Props = {
items: Array<L2OutputRootsItem>;
items: Array<OptimisticL2OutputRootsItem>;
top: number;
isLoading?: boolean;
}
const OutputRootsTable = ({ items, top, isLoading }: Props) => {
const OptimisticL2OutputRootsTable = ({ items, top, isLoading }: Props) => {
return (
<Table variant="simple" size="sm" minW="900px">
<Thead top={ top }>
......@@ -27,11 +27,15 @@ const OutputRootsTable = ({ items, top, isLoading }: Props) => {
</Thead>
<Tbody>
{ items.map((item, index) => (
<OutputRootsTableItem key={ item.l2_output_index + (Number(isLoading ? index : '') ? String(index) : '') } item={ item } isLoading={ isLoading }/>
<OptimisticL2OutputRootsTableItem
key={ item.l2_output_index + (Number(isLoading ? index : '') ? String(index) : '') }
item={ item }
isLoading={ isLoading }
/>
)) }
</Tbody>
</Table>
);
};
export default OutputRootsTable;
export default OptimisticL2OutputRootsTable;
import { Flex, Td, Tr, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { L2OutputRootsItem } from 'types/api/l2OutputRoots';
import type { OptimisticL2OutputRootsItem } from 'types/api/optimisticL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
......@@ -10,14 +10,14 @@ 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.optimisticRollup;
const rollupFeature = config.features.rollup;
type Props = { item: L2OutputRootsItem; isLoading?: boolean };
type Props = { item: OptimisticL2OutputRootsItem; isLoading?: boolean };
const OutputRootsTableItem = ({ item, isLoading }: Props) => {
const OptimisticL2OutputRootsTableItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.l1_timestamp).fromNow();
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
return null;
}
......@@ -58,4 +58,4 @@ const OutputRootsTableItem = ({ item, isLoading }: Props) => {
);
};
export default OutputRootsTableItem;
export default OptimisticL2OutputRootsTableItem;
......@@ -7,7 +7,7 @@ import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import Withdrawals from './Withdrawals';
import BeaconChainWithdrawals from './BeaconChainWithdrawals';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
......@@ -35,7 +35,7 @@ test('base view +@mobile', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Withdrawals/>
<BeaconChainWithdrawals/>
</TestApp>,
);
......
......@@ -12,8 +12,8 @@ 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 WithdrawalsListItem from 'ui/withdrawals/WithdrawalsListItem';
import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable';
import BeaconChainWithdrawalsListItem from 'ui/withdrawals/beaconChain/BeaconChainWithdrawalsListItem';
import BeaconChainWithdrawalsTable from 'ui/withdrawals/beaconChain/BeaconChainWithdrawalsTable';
const feature = config.features.beaconChain;
......@@ -41,7 +41,7 @@ const Withdrawals = () => {
<>
<Show below="lg" ssr={ false }>
{ data.items.map(((item, index) => (
<WithdrawalsListItem
<BeaconChainWithdrawalsListItem
key={ item.index + (isPlaceholderData ? String(index) : '') }
item={ item }
view="list"
......@@ -50,7 +50,7 @@ const Withdrawals = () => {
))) }
</Show>
<Hide below="lg" ssr={ false }>
<WithdrawalsTable items={ data.items } view="list" top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
<BeaconChainWithdrawalsTable items={ data.items } view="list" top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
......
......@@ -12,6 +12,8 @@ import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import SearchBar from 'ui/snippets/searchBar/SearchBar';
import WalletMenuDesktop from 'ui/snippets/walletMenu/WalletMenuDesktop';
const rollupFeature = config.features.rollup;
const Home = () => {
return (
<Box as="main">
......@@ -44,7 +46,7 @@ const Home = () => {
<ChainIndicators/>
<AdBanner mt={{ base: 6, lg: 8 }} mx="auto" display="flex" justifyContent="center"/>
<Flex mt={ 8 } direction={{ base: 'column', lg: 'row' }} columnGap={ 12 } rowGap={ 8 }>
{ config.features.zkEvmRollup.isEnabled ? <LatestZkEvmL2Batches/> : <LatestBlocks/> }
{ rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' ? <LatestZkEvmL2Batches/> : <LatestBlocks/> }
<Box flexGrow={ 1 }>
<Transactions/>
</Box>
......
......@@ -7,7 +7,7 @@ import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import L2Deposits from './L2Deposits';
import OptimisticL2Deposits from './OptimisticL2Deposits';
const DEPOSITS_API_URL = buildApiUrl('l2_deposits');
const DEPOSITS_COUNT_API_URL = buildApiUrl('l2_deposits_count');
......@@ -40,7 +40,7 @@ test('base view +@mobile', async({ mount, page }) => {
const component = await mount(
<TestApp>
<L2Deposits/>
<OptimisticL2Deposits/>
</TestApp>,
);
......
......@@ -5,14 +5,14 @@ import useApiQuery from 'lib/api/useApiQuery';
import { rightLineArrow, nbsp } from 'lib/html-entities';
import { L2_DEPOSIT_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils';
import DepositsListItem from 'ui/l2Deposits/DepositsListItem';
import DepositsTable from 'ui/l2Deposits/DepositsTable';
import OptimisticDepositsListItem from 'ui/deposits/optimisticL2/OptimisticDepositsListItem';
import OptimisticDepositsTable from 'ui/deposits/optimisticL2/OptimisticDepositsTable';
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 L2Deposits = () => {
const OptimisticL2Deposits = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_deposits',
options: {
......@@ -40,7 +40,7 @@ const L2Deposits = () => {
<>
<Show below="lg" ssr={ false }>
{ data.items.map(((item, index) => (
<DepositsListItem
<OptimisticDepositsListItem
key={ item.l2_tx_hash + (isPlaceholderData ? index : '') }
isLoading={ isPlaceholderData }
item={ item }
......@@ -48,7 +48,7 @@ const L2Deposits = () => {
))) }
</Show>
<Hide below="lg" ssr={ false }>
<DepositsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
<OptimisticDepositsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
......@@ -84,4 +84,4 @@ const L2Deposits = () => {
);
};
export default L2Deposits;
export default OptimisticL2Deposits;
......@@ -7,7 +7,7 @@ import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import OutputRoots from './L2OutputRoots';
import OptimisticL2OutputRoots from './OptimisticL2OutputRoots';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
......@@ -40,7 +40,7 @@ test('base view +@mobile', async({ mount, page }) => {
const component = await mount(
<TestApp>
<OutputRoots/>
<OptimisticL2OutputRoots/>
</TestApp>,
);
......
......@@ -4,14 +4,14 @@ import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import { L2_OUTPUT_ROOTS_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils';
import OutputRootsListItem from 'ui/l2OutputRoots/OutputRootsListItem';
import OutputRootsTable from 'ui/l2OutputRoots/OutputRootsTable';
import OptimisticL2OutputRootsListItem from 'ui/outputRoots/optimisticL2/OptimisticL2OutputRootsListItem';
import OptimisticL2OutputRootsTable from 'ui/outputRoots/optimisticL2/OptimisticL2OutputRootsTable';
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 L2OutputRoots = () => {
const OptimisticL2OutputRoots = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_output_roots',
options: {
......@@ -38,7 +38,7 @@ const L2OutputRoots = () => {
<>
<Show below="lg" ssr={ false }>
{ data.items.map(((item, index) => (
<OutputRootsListItem
<OptimisticL2OutputRootsListItem
key={ item.l2_output_index + (isPlaceholderData ? String(index) : '') }
item={ item }
isLoading={ isPlaceholderData }
......@@ -46,7 +46,7 @@ const L2OutputRoots = () => {
))) }
</Show>
<Hide below="lg" ssr={ false }>
<OutputRootsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
<OptimisticL2OutputRootsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
......@@ -82,4 +82,4 @@ const L2OutputRoots = () => {
);
};
export default L2OutputRoots;
export default OptimisticL2OutputRoots;
......@@ -7,7 +7,7 @@ import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import L2TxnBatches from './L2TxnBatches';
import OptimisticL2TxnBatches from './OptimisticL2TxnBatches';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
......@@ -40,7 +40,7 @@ test('base view +@mobile', async({ mount, page }) => {
const component = await mount(
<TestApp>
<L2TxnBatches/>
<OptimisticL2TxnBatches/>
</TestApp>,
);
......
......@@ -5,14 +5,14 @@ import useApiQuery from 'lib/api/useApiQuery';
import { nbsp } from 'lib/html-entities';
import { L2_TXN_BATCHES_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils';
import TxnBatchesListItem from 'ui/l2TxnBatches/TxnBatchesListItem';
import TxnBatchesTable from 'ui/l2TxnBatches/TxnBatchesTable';
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 OptimisticL2TxnBatchesListItem from 'ui/txnBatches/optimisticL2/OptimisticL2TxnBatchesListItem';
import OptimisticL2TxnBatchesTable from 'ui/txnBatches/optimisticL2/OptimisticL2TxnBatchesTable';
const L2TxnBatches = () => {
const OptimisticL2TxnBatches = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_txn_batches',
options: {
......@@ -39,14 +39,16 @@ const L2TxnBatches = () => {
<>
<Show below="lg" ssr={ false }>
{ data.items.map(((item, index) => (
<TxnBatchesListItem
<OptimisticL2TxnBatchesListItem
key={ item.l2_block_number + (isPlaceholderData ? String(index) : '') }
item={ item }
isLoading={ isPlaceholderData }
/>
))) }
</Show>
<Hide below="lg" ssr={ false }><TxnBatchesTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/></Hide>
<Hide below="lg" ssr={ false }>
<OptimisticL2TxnBatchesTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
......@@ -81,4 +83,4 @@ const L2TxnBatches = () => {
);
};
export default L2TxnBatches;
export default OptimisticL2TxnBatches;
......@@ -7,7 +7,7 @@ import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import L2Withdrawals from './L2Withdrawals';
import OptimisticL2Withdrawals from './OptimisticL2Withdrawals';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
......@@ -40,7 +40,7 @@ test('base view +@mobile', async({ mount, page }) => {
const component = await mount(
<TestApp>
<L2Withdrawals/>
<OptimisticL2Withdrawals/>
</TestApp>,
);
......
......@@ -5,14 +5,14 @@ import useApiQuery from 'lib/api/useApiQuery';
import { rightLineArrow, nbsp } from 'lib/html-entities';
import { L2_WITHDRAWAL_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils';
import WithdrawalsListItem from 'ui/l2Withdrawals/WithdrawalsListItem';
import WithdrawalsTable from 'ui/l2Withdrawals/WithdrawalsTable';
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 OptimisticL2WithdrawalsListItem from 'ui/withdrawals/optimisticL2/OptimisticL2WithdrawalsListItem';
import OptimisticL2WithdrawalsTable from 'ui/withdrawals/optimisticL2/OptimisticL2WithdrawalsTable';
const L2Withdrawals = () => {
const OptimisticL2Withdrawals = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_withdrawals',
options: {
......@@ -38,14 +38,14 @@ const L2Withdrawals = () => {
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>{ data.items.map(((item, index) => (
<WithdrawalsListItem
<OptimisticL2WithdrawalsListItem
key={ item.l2_tx_hash + (isPlaceholderData ? index : '') }
item={ item }
isLoading={ isPlaceholderData }
/>
))) }</Show>
<Hide below="lg" ssr={ false }>
<WithdrawalsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
<OptimisticL2WithdrawalsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
......@@ -81,4 +81,4 @@ const L2Withdrawals = () => {
);
};
export default L2Withdrawals;
export default OptimisticL2Withdrawals;
......@@ -11,7 +11,7 @@ import ZkEvmL2TxnBatch from './ZkEvmL2TxnBatch';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.zkRollup) as any,
context: contextWithEnvs(configs.featureEnvs.zkEvmRollup) as any,
});
const hooksConfig = {
......
......@@ -16,8 +16,8 @@ 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 ZkEvmL2TxnBatchDetails from 'ui/txnBatches/zkEvmL2/ZkEvmL2TxnBatchDetails';
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';
import ZkEvmL2TxnBatchDetails from 'ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails';
const ZkEvmL2TxnBatch = () => {
const router = useRouter();
......
......@@ -11,7 +11,7 @@ import ZkEvmL2TxnBatches from './ZkEvmL2TxnBatches';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.zkRollup) as any,
context: contextWithEnvs(configs.featureEnvs.zkEvmRollup) as any,
});
const BATCHES_API_URL = buildApiUrl('zkevm_l2_txn_batches');
......
......@@ -8,8 +8,8 @@ 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';
import ZkEvmTxnBatchesListItem from 'ui/txnBatches/zkEvmL2/ZkEvmTxnBatchesListItem';
import ZkEvmTxnBatchesTable from 'ui/txnBatches/zkEvmL2/ZkEvmTxnBatchesTable';
const ZkEvmL2TxnBatches = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
......
......@@ -7,14 +7,14 @@ import config from 'configs/app';
import * as AddressEntity from './AddressEntity';
const feature = config.features.optimisticRollup;
const rollupFeature = config.features.rollup;
const AddressEntityL1 = (props: AddressEntity.EntityProps) => {
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled) {
return null;
}
const defaultHref = feature.L1BaseUrl + route({
const defaultHref = rollupFeature.L1BaseUrl + route({
pathname: '/address/[hash]',
query: {
...props.query,
......
......@@ -8,13 +8,13 @@ import config from 'configs/app';
import * as BlockEntity from './BlockEntity';
const feature = config.features.zkEvmRollup;
const rollupFeature = config.features.rollup;
const ZkEvmBatchEntityL2 = (props: BlockEntity.EntityProps) => {
const BatchEntityL2 = (props: BlockEntity.EntityProps) => {
const linkProps = _omit(props, [ 'className' ]);
const partsProps = _omit(props, [ 'className', 'onClick' ]);
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled) {
return null;
}
......@@ -31,4 +31,4 @@ const ZkEvmBatchEntityL2 = (props: BlockEntity.EntityProps) => {
);
};
export default chakra(ZkEvmBatchEntityL2);
export default chakra(BatchEntityL2);
......@@ -8,13 +8,13 @@ import config from 'configs/app';
import * as BlockEntity from './BlockEntity';
const feature = config.features.optimisticRollup;
const rollupFeature = config.features.rollup;
const BlockEntityL1 = (props: BlockEntity.EntityProps) => {
const linkProps = _omit(props, [ 'className' ]);
const partsProps = _omit(props, [ 'className', 'onClick' ]);
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled) {
return null;
}
......@@ -24,7 +24,7 @@ const BlockEntityL1 = (props: BlockEntity.EntityProps) => {
<BlockEntity.Link
{ ...linkProps }
isExternal
href={ feature.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: props.hash ?? String(props.number) } }) }
href={ rollupFeature.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: props.hash ?? String(props.number) } }) }
>
<BlockEntity.Content { ...partsProps }/>
</BlockEntity.Link>
......
......@@ -6,13 +6,13 @@ import config from 'configs/app';
import * as BlockEntity from './BlockEntity';
const feature = config.features.optimisticRollup;
const rollupFeature = config.features.rollup;
const BlockEntityL2 = (props: BlockEntity.EntityProps) => {
const linkProps = _omit(props, [ 'className' ]);
const partsProps = _omit(props, [ 'className', 'onClick' ]);
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled) {
return null;
}
......
......@@ -8,13 +8,13 @@ import config from 'configs/app';
import * as TxEntity from './TxEntity';
const feature = config.features.optimisticRollup.isEnabled ? config.features.optimisticRollup : config.features.zkEvmRollup;
const rollupFeature = config.features.rollup;
const TxEntityL1 = (props: TxEntity.EntityProps) => {
const partsProps = _omit(props, [ 'className', 'onClick' ]);
const linkProps = _omit(props, [ 'className' ]);
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled) {
return null;
}
......@@ -24,7 +24,7 @@ const TxEntityL1 = (props: TxEntity.EntityProps) => {
<TxEntity.Link
{ ...linkProps }
isExternal
href={ feature.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: props.hash } }) }
href={ rollupFeature.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: props.hash } }) }
>
<TxEntity.Content { ...partsProps }/>
</TxEntity.Link>
......
import React from 'react';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2';
import type { StatusTagType } from './StatusTag';
import StatusTag from './StatusTag';
......
......@@ -2,7 +2,7 @@ import { Box } from '@chakra-ui/react';
import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import type { L2WithdrawalStatus } from 'types/api/l2Withdrawals';
import type { OptimisticL2WithdrawalStatus } from 'types/api/optimisticL2';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
......@@ -10,7 +10,7 @@ import * as configs from 'playwright/utils/configs';
import TxDetailsWithdrawalStatus from './TxDetailsWithdrawalStatus';
const statuses: Array<L2WithdrawalStatus> = [
const statuses: Array<OptimisticL2WithdrawalStatus> = [
'Waiting for state root',
'Ready for relay',
'Relayed',
......
import { Button } from '@chakra-ui/react';
import React from 'react';
import type { L2WithdrawalStatus } from 'types/api/l2Withdrawals';
import { WITHDRAWAL_STATUSES } from 'types/api/l2Withdrawals';
import type { OptimisticL2WithdrawalStatus } from 'types/api/optimisticL2';
import { WITHDRAWAL_STATUSES } from 'types/api/optimisticL2';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
interface Props {
status: L2WithdrawalStatus | undefined;
status: OptimisticL2WithdrawalStatus | undefined;
l1TxHash: string | undefined;
}
......@@ -55,7 +55,7 @@ const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => {
return (
<VerificationSteps
steps={ steps as unknown as Array<L2WithdrawalStatus> }
steps={ steps as unknown as Array<OptimisticL2WithdrawalStatus> }
currentStep={ status }
rightSlot={ rightSlot }
my={ hasClaimButton ? '-6px' : 0 }
......
......@@ -33,8 +33,8 @@ import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import ZkEvmBatchEntityL2 from 'ui/shared/entities/block/ZkEvmBatchEntityL2';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg';
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
......@@ -53,6 +53,9 @@ import TxDetailsWithdrawalStatus from 'ui/tx/details/TxDetailsWithdrawalStatus';
import TxRevertReason from 'ui/tx/details/TxRevertReason';
import TxAllowedPeekers from 'ui/tx/TxAllowedPeekers';
import TxSocketAlert from 'ui/tx/TxSocketAlert';
const rollupFeature = config.features.rollup;
interface Props {
data: Transaction | undefined;
isLoading: boolean;
......@@ -123,7 +126,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
<CopyToClipboard text={ data.hash } isLoading={ isLoading }/>
</DetailsInfoItem>
<DetailsInfoItem
title={ config.features.zkEvmRollup.isEnabled ? 'L2 status and method' : 'Status and method' }
title={ rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' ? 'L2 status and method' : 'Status and method' }
hint="Current transaction state: Success, Failed (Error), or Pending (In Process)"
isLoading={ isLoading }
>
......@@ -134,7 +137,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</Tag>
) }
</DetailsInfoItem>
{ config.features.optimisticRollup.isEnabled && data.op_withdrawals && data.op_withdrawals.length > 0 && (
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && data.op_withdrawals && data.op_withdrawals.length > 0 && (
<DetailsInfoItem
title="Withdrawal status"
hint="Detailed status progress of the transaction"
......@@ -200,7 +203,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
hint="Batch index for this transaction"
isLoading={ isLoading }
>
<ZkEvmBatchEntityL2
<BatchEntityL2
isLoading={ isLoading }
number={ data.zkevm_batch_number }
/>
......@@ -419,7 +422,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
) }
</DetailsInfoItem>
) }
{ data.tx_burnt_fee && !config.UI.views.tx.hiddenFields?.burnt_fees && !config.features.optimisticRollup.isEnabled && (
{ data.tx_burnt_fee && !config.UI.views.tx.hiddenFields?.burnt_fees && !(rollupFeature.isEnabled && rollupFeature.type === 'optimistic') && (
<DetailsInfoItem
title="Burnt fees"
hint={ `Amount of ${ currencyUnits.ether } burned for this transaction. Equals Block Base Fee per Gas * Gas Used` }
......@@ -434,7 +437,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
/>
</DetailsInfoItem>
) }
{ config.features.optimisticRollup.isEnabled && (
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && (
<>
{ data.l1_gas_used && (
<DetailsInfoItem
......
......@@ -18,6 +18,8 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import { TX, TX_ZKEVM_L2 } from 'stubs/tx';
const rollupFeature = config.features.rollup;
export type TxQuery = UseQueryResult<Transaction, ResourceError<{ status: number }>> & {
socketStatus: 'close' | 'error' | undefined;
setRefetchOnError: {
......@@ -46,7 +48,7 @@ export default function useTxQuery(params?: Params): TxQuery {
queryOptions: {
enabled: Boolean(hash) && params?.isEnabled !== false,
refetchOnMount: false,
placeholderData: config.features.zkEvmRollup.isEnabled ? TX_ZKEVM_L2 : TX,
placeholderData: rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' ? TX_ZKEVM_L2 : TX,
retry: (failureCount, error) => {
if (isRefetchEnabled) {
return false;
......
import { Skeleton, VStack } from '@chakra-ui/react';
import React from 'react';
import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches';
import type { OptimisticL2TxnBatchesItem } from 'types/api/optimisticL2';
import { route } from 'nextjs-routes';
......@@ -13,14 +13,14 @@ 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.optimisticRollup;
const rollupFeature = config.features.rollup;
type Props = { item: L2TxnBatchesItem; isLoading?: boolean };
type Props = { item: OptimisticL2TxnBatchesItem; isLoading?: boolean };
const TxnBatchesListItem = ({ item, isLoading }: Props) => {
const OptimisticL2TxnBatchesListItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.l1_timestamp).fromNow();
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
return null;
}
......@@ -87,4 +87,4 @@ const TxnBatchesListItem = ({ item, isLoading }: Props) => {
);
};
export default TxnBatchesListItem;
export default OptimisticL2TxnBatchesListItem;
import { Table, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react';
import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches';
import type { OptimisticL2TxnBatchesItem } from 'types/api/optimisticL2';
import { default as Thead } from 'ui/shared/TheadSticky';
import TxnBatchesTableItem from './TxnBatchesTableItem';
import OptimisticL2TxnBatchesTableItem from './OptimisticL2TxnBatchesTableItem';
type Props = {
items: Array<L2TxnBatchesItem>;
items: Array<OptimisticL2TxnBatchesItem>;
top: number;
isLoading?: boolean;
}
const TxnBatchesTable = ({ items, top, isLoading }: Props) => {
const OptimisticL2TxnBatchesTable = ({ items, top, isLoading }: Props) => {
return (
<Table variant="simple" size="sm" minW="850px">
<Thead top={ top }>
......@@ -27,7 +27,7 @@ const TxnBatchesTable = ({ items, top, isLoading }: Props) => {
</Thead>
<Tbody>
{ items.map((item, index) => (
<TxnBatchesTableItem
<OptimisticL2TxnBatchesTableItem
key={ item.l2_block_number + (isLoading ? String(index) : '') }
item={ item }
isLoading={ isLoading }
......@@ -38,4 +38,4 @@ const TxnBatchesTable = ({ items, top, isLoading }: Props) => {
);
};
export default TxnBatchesTable;
export default OptimisticL2TxnBatchesTable;
import { Td, Tr, VStack, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches';
import type { OptimisticL2TxnBatchesItem } from 'types/api/optimisticL2';
import { route } from 'nextjs-routes';
......@@ -12,14 +12,14 @@ 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.optimisticRollup;
const rollupFeature = config.features.rollup;
type Props = { item: L2TxnBatchesItem; isLoading?: boolean };
type Props = { item: OptimisticL2TxnBatchesItem; isLoading?: boolean };
const TxnBatchesTableItem = ({ item, isLoading }: Props) => {
const OptimisticL2TxnBatchesTableItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.l1_timestamp).fromNow();
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
return null;
}
......@@ -78,4 +78,4 @@ const TxnBatchesTableItem = ({ item, isLoading }: Props) => {
);
};
export default TxnBatchesTableItem;
export default OptimisticL2TxnBatchesTableItem;
......@@ -3,8 +3,8 @@ 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/zkEvmL2';
import type { ZkEvmL2TxnBatch } from 'types/api/zkEvmL2';
import { route } from 'nextjs-routes';
......
import { Skeleton, Text } from '@chakra-ui/react';
import React from 'react';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2';
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 BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
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;
const rollupFeature = config.features.rollup;
type Props = { item: ZkEvmL2TxnBatchesItem; isLoading?: boolean };
const ZkEvmTxnBatchesListItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.timestamp).fromNow();
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'zkEvm') {
return null;
}
......@@ -29,7 +29,7 @@ const ZkEvmTxnBatchesListItem = ({ item, isLoading }: Props) => {
<ListItemMobileGrid.Label isLoading={ isLoading }>Batch #</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<ZkEvmBatchEntityL2
<BatchEntityL2
isLoading={ isLoading }
number={ item.number }
fontSize="sm"
......
import { Table, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2';
import { default as Thead } from 'ui/shared/TheadSticky';
......
import { Td, Tr, Text, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2TxnBatches';
import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2';
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 BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
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;
const rollupFeature = config.features.rollup;
type Props = { item: ZkEvmL2TxnBatchesItem; isLoading?: boolean };
const TxnBatchesTableItem = ({ item, isLoading }: Props) => {
const timeAgo = dayjs(item.timestamp).fromNow();
if (!feature.isEnabled) {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'zkEvm') {
return null;
}
return (
<Tr>
<Td verticalAlign="middle">
<ZkEvmBatchEntityL2
<BatchEntityL2
isLoading={ isLoading }
number={ item.number }
fontSize="sm"
......
......@@ -7,7 +7,7 @@ import type { WithdrawalsItem } from 'types/api/withdrawals';
import useLazyRenderedList from 'lib/hooks/useLazyRenderedList';
import WithdrawalsListItem from './WithdrawalsListItem';
import BeaconChainWithdrawalsListItem from './BeaconChainWithdrawalsListItem';
type Props = {
isLoading?: boolean;
......@@ -34,7 +34,7 @@ const WithdrawalsList = ({ items, view, isLoading }: Props) => {
switch (view) {
case 'address': {
return (
<WithdrawalsListItem
<BeaconChainWithdrawalsListItem
key={ key }
item={ item as AddressWithdrawalsItem }
view={ view }
......@@ -44,7 +44,7 @@ const WithdrawalsList = ({ items, view, isLoading }: Props) => {
}
case 'block': {
return (
<WithdrawalsListItem
<BeaconChainWithdrawalsListItem
key={ key }
item={ item as BlockWithdrawalsItem }
view={ view }
......@@ -54,7 +54,7 @@ const WithdrawalsList = ({ items, view, isLoading }: Props) => {
}
case 'list': {
return (
<WithdrawalsListItem
<BeaconChainWithdrawalsListItem
key={ key }
item={ item as WithdrawalsItem }
view={ view }
......
......@@ -26,7 +26,7 @@ type Props = ({
view: 'block';
}) & { isLoading?: boolean };
const WithdrawalsListItem = ({ item, isLoading, view }: Props) => {
const BeaconChainWithdrawalsListItem = ({ item, isLoading, view }: Props) => {
if (!feature.isEnabled) {
return null;
}
......@@ -87,4 +87,4 @@ const WithdrawalsListItem = ({ item, isLoading, view }: Props) => {
);
};
export default WithdrawalsListItem;
export default BeaconChainWithdrawalsListItem;
......@@ -9,7 +9,7 @@ import config from 'configs/app';
import useLazyRenderedList from 'lib/hooks/useLazyRenderedList';
import { default as Thead } from 'ui/shared/TheadSticky';
import WithdrawalsTableItem from './WithdrawalsTableItem';
import BeaconChainWithdrawalsTableItem from './BeaconChainWithdrawalsTableItem';
const feature = config.features.beaconChain;
......@@ -27,7 +27,7 @@ const feature = config.features.beaconChain;
view: 'block';
});
const WithdrawalsTable = ({ items, isLoading, top, view }: Props) => {
const BeaconChainWithdrawalsTable = ({ items, isLoading, top, view }: Props) => {
const { cutRef, renderedItemsNum } = useLazyRenderedList(items, !isLoading);
if (!feature.isEnabled) {
......@@ -48,13 +48,13 @@ const WithdrawalsTable = ({ items, isLoading, top, view }: Props) => {
</Thead>
<Tbody>
{ view === 'list' && (items as Array<WithdrawalsItem>).slice(0, renderedItemsNum).map((item, index) => (
<WithdrawalsTableItem key={ item.index + (isLoading ? String(index) : '') } item={ item } view="list" isLoading={ isLoading }/>
<BeaconChainWithdrawalsTableItem key={ item.index + (isLoading ? String(index) : '') } item={ item } view="list" isLoading={ isLoading }/>
)) }
{ view === 'address' && (items as Array<AddressWithdrawalsItem>).slice(0, renderedItemsNum).map((item, index) => (
<WithdrawalsTableItem key={ item.index + (isLoading ? String(index) : '') } item={ item } view="address" isLoading={ isLoading }/>
<BeaconChainWithdrawalsTableItem key={ item.index + (isLoading ? String(index) : '') } item={ item } view="address" isLoading={ isLoading }/>
)) }
{ view === 'block' && (items as Array<BlockWithdrawalsItem>).slice(0, renderedItemsNum).map((item, index) => (
<WithdrawalsTableItem key={ item.index + (isLoading ? String(index) : '') } item={ item } view="block" isLoading={ isLoading }/>
<BeaconChainWithdrawalsTableItem key={ item.index + (isLoading ? String(index) : '') } item={ item } view="block" isLoading={ isLoading }/>
)) }
<tr ref={ cutRef }/>
</Tbody>
......@@ -62,4 +62,4 @@ const WithdrawalsTable = ({ items, isLoading, top, view }: Props) => {
);
};
export default WithdrawalsTable;
export default BeaconChainWithdrawalsTable;
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