Commit 555748cd authored by isstuev's avatar isstuev

OP Fault Proofs support

parent 86e33cae
import type { Feature } from './types';
import { getEnvValue } from '../utils';
import rollup from './rollup';
const title = 'Fault proof system';
const config: Feature<{ isEnabled: true }> = (() => {
if (rollup.isEnabled && rollup.type === 'optimistic' && getEnvValue('NEXT_PUBLIC_FAULT_PROOF_ENABLED') === 'true') {
return Object.freeze({
title,
isEnabled: true,
});
}
return Object.freeze({
title,
isEnabled: false,
});
})();
export default config;
...@@ -8,6 +8,7 @@ export { default as bridgedTokens } from './bridgedTokens'; ...@@ -8,6 +8,7 @@ export { default as bridgedTokens } from './bridgedTokens';
export { default as blockchainInteraction } from './blockchainInteraction'; export { default as blockchainInteraction } from './blockchainInteraction';
export { default as csvExport } from './csvExport'; export { default as csvExport } from './csvExport';
export { default as dataAvailability } from './dataAvailability'; export { default as dataAvailability } from './dataAvailability';
export { default as faultProofSystem } from './faultProofSystem';
export { default as gasTracker } from './gasTracker'; export { default as gasTracker } from './gasTracker';
export { default as googleAnalytics } from './googleAnalytics'; export { default as googleAnalytics } from './googleAnalytics';
export { default as graphqlApiDocs } from './graphqlApiDocs'; export { default as graphqlApiDocs } from './graphqlApiDocs';
......
...@@ -61,3 +61,4 @@ NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true ...@@ -61,3 +61,4 @@ NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_ROLLUP_TYPE=optimistic NEXT_PUBLIC_ROLLUP_TYPE=optimistic
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth-sepolia.blockscout.com/ NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth-sepolia.blockscout.com/
NEXT_PUBLIC_FAULT_PROOF_ENABLED=true
...@@ -629,6 +629,16 @@ const schema = yup ...@@ -629,6 +629,16 @@ const schema = yup
NEXT_PUBLIC_GAS_TRACKER_ENABLED: yup.boolean(), NEXT_PUBLIC_GAS_TRACKER_ENABLED: yup.boolean(),
NEXT_PUBLIC_GAS_TRACKER_UNITS: yup.array().transform(replaceQuotes).json().of(yup.string<GasUnit>().oneOf(GAS_UNITS)), NEXT_PUBLIC_GAS_TRACKER_UNITS: yup.array().transform(replaceQuotes).json().of(yup.string<GasUnit>().oneOf(GAS_UNITS)),
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: yup.boolean(), NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: yup.boolean(),
NEXT_PUBLIC_FAULT_PROOF_ENABLED: yup.boolean()
.when('NEXT_PUBLIC_ROLLUP_TYPE', {
is: 'optimistic',
then: (schema) => schema,
otherwise: (schema) => schema.test(
'not-exist',
'NEXT_PUBLIC_FAULT_PROOF_ENABLED can only be used with NEXT_PUBLIC_ROLLUP_TYPE=optimistic',
value => value === undefined,
),
}),
// 6. External services envs // 6. External services envs
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(), NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(),
......
NEXT_PUBLIC_ROLLUP_TYPE=optimistic NEXT_PUBLIC_ROLLUP_TYPE=optimistic
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://example.com NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://example.com
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://example.com NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://example.com
NEXT_PUBLIC_FAULT_PROOF_ENABLED=true
\ No newline at end of file
...@@ -36,6 +36,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will ...@@ -36,6 +36,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
- [Beacon chain](ENVS.md#beacon-chain) - [Beacon chain](ENVS.md#beacon-chain)
- [User operations](ENVS.md#user-operations-feature-erc-4337) - [User operations](ENVS.md#user-operations-feature-erc-4337)
- [Rollup chain](ENVS.md#rollup-chain) - [Rollup chain](ENVS.md#rollup-chain)
- [Fault proof system](ENVS.md#fault-proof-system)
- [Export data to CSV file](ENVS.md#export-data-to-csv-file) - [Export data to CSV file](ENVS.md#export-data-to-csv-file)
- [Google analytics](ENVS.md#google-analytics) - [Google analytics](ENVS.md#google-analytics)
- [Mixpanel analytics](ENVS.md#mixpanel-analytics) - [Mixpanel analytics](ENVS.md#mixpanel-analytics)
...@@ -401,6 +402,14 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi ...@@ -401,6 +402,14 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi
&nbsp; &nbsp;
### Fault proof system
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_FAULT_PROOF_ENABLED | `boolean` | Set to `true` for chains with fault proof system enabled (OP stack only) | - | - | `true` |
&nbsp;
### Export data to CSV file ### Export data to CSV file
| Variable | Type| Description | Compulsoriness | Default value | Example value | | Variable | Type| Description | Compulsoriness | Default value | Example value |
......
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 30 30">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.6" d="M24.24 18.89v-8.582c0-.28-.072-.556-.206-.8a1.576 1.576 0 0 0-.56-.587L16.16 4.476A2.232 2.232 0 0 0 15 4.149c-.408 0-.809.113-1.161.327L6.525 8.92a1.576 1.576 0 0 0-.56.587c-.134.244-.204.52-.205.8v8.581c0 .281.071.557.205.801s.327.446.56.588l7.315 4.445c.352.214.753.327 1.16.327.408 0 .809-.113 1.161-.327l7.315-4.445c.232-.142.425-.345.559-.588.134-.244.204-.52.204-.8Z"/>
<path stroke="transparent" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".2" stroke-width="1.6" d="M24.24 18.89v-8.582c0-.28-.072-.556-.206-.8a1.576 1.576 0 0 0-.56-.587L16.16 4.476A2.232 2.232 0 0 0 15 4.149c-.408 0-.809.113-1.161.327L6.525 8.92a1.576 1.576 0 0 0-.56.587c-.134.244-.204.52-.205.8v8.581c0 .281.071.557.205.801s.327.446.56.588l7.315 4.445c.352.214.753.327 1.16.327.408 0 .809-.113 1.161-.327l7.315-4.445c.232-.142.425-.345.559-.588.134-.244.204-.52.204-.8Z"/>
<path fill="currentColor" fill-rule="evenodd" d="M6.42 8.792a.8.8 0 1 0-.838 1.364l8.618 5.293v9.602a.8.8 0 1 0 1.6 0V15.45l8.617-5.293a.8.8 0 0 0-.837-1.364L15 14.062l-8.58-5.27Z" clip-rule="evenodd"/>
<path fill="transparent" fill-opacity=".2" fill-rule="evenodd" d="M6.42 8.792a.8.8 0 1 0-.838 1.364l8.618 5.293v9.602a.8.8 0 1 0 1.6 0V15.45l8.617-5.293a.8.8 0 0 0-.837-1.364L15 14.062l-8.58-5.27Z" clip-rule="evenodd"/>
<path fill="currentColor" fill-rule="evenodd" d="M15 10.177c.638 0 1.155-.36 1.155-.804 0-.444-.517-.804-1.155-.804-.637 0-1.155.36-1.155.804 0 .444.518.804 1.155.804Zm-2.9 7.735c.497 0 .9-.54.9-1.206 0-.666-.403-1.206-.9-1.206s-.9.54-.9 1.206c0 .666.403 1.206.9 1.206Zm-3 .306c0 .666-.403 1.206-.9 1.206s-.9-.54-.9-1.206c0-.667.403-1.207.9-1.207s.9.54.9 1.207Zm8.458-.404c.496.029.93-.487.968-1.152.04-.665-.331-1.227-.828-1.256-.496-.03-.93.486-.968 1.15-.04.666.331 1.228.828 1.258Zm5.068-3.305c-.039.665-.472 1.18-.969 1.152-.496-.03-.866-.592-.828-1.257.04-.665.473-1.18.97-1.151.496.029.866.591.827 1.256Zm-5.069 7.352c.497.029.93-.487.97-1.152.038-.665-.332-1.227-.829-1.256-.496-.03-.93.486-.969 1.151-.039.665.332 1.227.828 1.257Zm5.07-3.591c-.04.665-.473 1.18-.97 1.151-.496-.029-.866-.591-.828-1.256.04-.665.473-1.18.97-1.152.496.03.866.592.827 1.257Z" clip-rule="evenodd"/>
<path fill="transparent" fill-opacity=".2" fill-rule="evenodd" d="M15 10.177c.638 0 1.155-.36 1.155-.804 0-.444-.517-.804-1.155-.804-.637 0-1.155.36-1.155.804 0 .444.518.804 1.155.804Zm-2.9 7.735c.497 0 .9-.54.9-1.206 0-.666-.403-1.206-.9-1.206s-.9.54-.9 1.206c0 .666.403 1.206.9 1.206Zm-3 .306c0 .666-.403 1.206-.9 1.206s-.9-.54-.9-1.206c0-.667.403-1.207.9-1.207s.9.54.9 1.207Zm8.458-.404c.496.029.93-.487.968-1.152.04-.665-.331-1.227-.828-1.256-.496-.03-.93.486-.968 1.15-.04.666.331 1.228.828 1.258Zm5.068-3.305c-.039.665-.472 1.18-.969 1.152-.496-.03-.866-.592-.828-1.257.04-.665.473-1.18.97-1.151.496.029.866.591.827 1.256Zm-5.069 7.352c.497.029.93-.487.97-1.152.038-.665-.332-1.227-.829-1.256-.496-.03-.93.486-.969 1.151-.039.665.332 1.227.828 1.257Zm5.07-3.591c-.04.665-.473 1.18-.97 1.151-.496-.029-.866-.591-.828-1.256.04-.665.473-1.18.97-1.152.496.03.866.592.827 1.257Z" clip-rule="evenodd"/>
</svg>
...@@ -64,6 +64,7 @@ import type { ...@@ -64,6 +64,7 @@ import type {
OptimisticL2OutputRootsResponse, OptimisticL2OutputRootsResponse,
OptimisticL2TxnBatchesResponse, OptimisticL2TxnBatchesResponse,
OptimisticL2WithdrawalsResponse, OptimisticL2WithdrawalsResponse,
OptimisticL2DisputeGamesResponse,
} from 'types/api/optimisticL2'; } from 'types/api/optimisticL2';
import type { RawTracesResponse } from 'types/api/rawTrace'; import type { RawTracesResponse } from 'types/api/rawTrace';
import type { SearchRedirectResult, SearchResult, SearchResultFilters, SearchResultItem } from 'types/api/search'; import type { SearchRedirectResult, SearchResult, SearchResultFilters, SearchResultItem } from 'types/api/search';
...@@ -650,6 +651,15 @@ export const RESOURCES = { ...@@ -650,6 +651,15 @@ export const RESOURCES = {
path: '/api/v2/optimism/txn-batches/count', path: '/api/v2/optimism/txn-batches/count',
}, },
optimistic_l2_dispute_games: {
path: '/api/v2/optimism/games',
filterFields: [],
},
optimistic_l2_dispute_games_count: {
path: '/api/v2/optimism/games/count',
},
// zkEvm L2 // zkEvm L2
zkevm_l2_deposits: { zkevm_l2_deposits: {
path: '/api/v2/zkevm/deposits', path: '/api/v2/zkevm/deposits',
...@@ -853,6 +863,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' | ...@@ -853,6 +863,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
'token_instance_transfers' | 'token_instance_holders' | 'token_instance_transfers' | 'token_instance_holders' |
'verified_contracts' | 'verified_contracts' |
'optimistic_l2_output_roots' | 'optimistic_l2_withdrawals' | 'optimistic_l2_txn_batches' | 'optimistic_l2_deposits' | 'optimistic_l2_output_roots' | 'optimistic_l2_withdrawals' | 'optimistic_l2_txn_batches' | 'optimistic_l2_deposits' |
'optimistic_l2_dispute_games' |
'shibarium_deposits' | 'shibarium_withdrawals' | 'shibarium_deposits' | 'shibarium_withdrawals' |
'zkevm_l2_deposits' | 'zkevm_l2_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' | 'zksync_l2_txn_batches' | 'zksync_l2_txn_batch_txs' |
...@@ -955,13 +966,12 @@ Q extends 'optimistic_l2_output_roots' ? OptimisticL2OutputRootsResponse : ...@@ -955,13 +966,12 @@ Q extends 'optimistic_l2_output_roots' ? OptimisticL2OutputRootsResponse :
Q extends 'optimistic_l2_withdrawals' ? OptimisticL2WithdrawalsResponse : Q extends 'optimistic_l2_withdrawals' ? OptimisticL2WithdrawalsResponse :
Q extends 'optimistic_l2_deposits' ? OptimisticL2DepositsResponse : Q extends 'optimistic_l2_deposits' ? OptimisticL2DepositsResponse :
Q extends 'optimistic_l2_txn_batches' ? OptimisticL2TxnBatchesResponse : Q extends 'optimistic_l2_txn_batches' ? OptimisticL2TxnBatchesResponse :
Q extends 'optimistic_l2_dispute_games' ? OptimisticL2DisputeGamesResponse :
Q extends 'optimistic_l2_output_roots_count' ? number : Q extends 'optimistic_l2_output_roots_count' ? number :
Q extends 'optimistic_l2_withdrawals_count' ? number : Q extends 'optimistic_l2_withdrawals_count' ? number :
Q extends 'optimistic_l2_deposits_count' ? number : Q extends 'optimistic_l2_deposits_count' ? number :
Q extends 'optimistic_l2_txn_batches_count' ? number : Q extends 'optimistic_l2_txn_batches_count' ? number :
Q extends 'config_backend_version' ? BackendVersionConfig : Q extends 'optimistic_l2_dispute_games_count' ? number :
Q extends 'address_metadata_info' ? AddressMetadataInfo :
Q extends 'address_metadata_tag_types' ? PublicTagTypesResponse :
never; never;
// !!! IMPORTANT !!! // !!! IMPORTANT !!!
// See comment above // See comment above
...@@ -969,6 +979,9 @@ never; ...@@ -969,6 +979,9 @@ never;
/* eslint-disable @typescript-eslint/indent */ /* eslint-disable @typescript-eslint/indent */
export type ResourcePayloadB<Q extends ResourceName> = export type ResourcePayloadB<Q extends ResourceName> =
Q extends 'config_backend_version' ? BackendVersionConfig :
Q extends 'address_metadata_info' ? AddressMetadataInfo :
Q extends 'address_metadata_tag_types' ? PublicTagTypesResponse :
Q extends 'blob' ? Blob : Q extends 'blob' ? Blob :
Q extends 'marketplace_dapps' ? Array<MarketplaceAppOverview> : Q extends 'marketplace_dapps' ? Array<MarketplaceAppOverview> :
Q extends 'marketplace_dapp' ? MarketplaceAppOverview : Q extends 'marketplace_dapp' ? MarketplaceAppOverview :
......
...@@ -96,6 +96,12 @@ export default function useNavItems(): ReturnType { ...@@ -96,6 +96,12 @@ export default function useNavItems(): ReturnType {
icon: 'output_roots', icon: 'output_roots',
isActive: pathname === '/output-roots', isActive: pathname === '/output-roots',
}; };
const rollupDisputeGames = config.features.faultProofSystem.isEnabled ? {
text: 'Dispute games',
nextRoute: { pathname: '/dispute-games' as const },
icon: 'games',
isActive: pathname === '/dispute-games',
} : null;
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
...@@ -109,6 +115,7 @@ export default function useNavItems(): ReturnType { ...@@ -109,6 +115,7 @@ export default function useNavItems(): ReturnType {
[ [
blocks, blocks,
rollupTxnBatches, rollupTxnBatches,
rollupDisputeGames,
rollupFeature.type === 'optimistic' ? rollupOutputRoots : undefined, rollupFeature.type === 'optimistic' ? rollupOutputRoots : undefined,
].filter(Boolean), ].filter(Boolean),
[ [
......
...@@ -35,6 +35,7 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = { ...@@ -35,6 +35,7 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/csv-export': 'Regular page', '/csv-export': 'Regular page',
'/deposits': 'Root page', '/deposits': 'Root page',
'/output-roots': 'Root page', '/output-roots': 'Root page',
'/dispute-games': 'Root page',
'/batches': 'Root page', '/batches': 'Root page',
'/batches/[number]': 'Regular page', '/batches/[number]': 'Regular page',
'/blobs/[hash]': 'Regular page', '/blobs/[hash]': 'Regular page',
......
...@@ -39,6 +39,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = { ...@@ -39,6 +39,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/csv-export': DEFAULT_TEMPLATE, '/csv-export': DEFAULT_TEMPLATE,
'/deposits': DEFAULT_TEMPLATE, '/deposits': DEFAULT_TEMPLATE,
'/output-roots': DEFAULT_TEMPLATE, '/output-roots': DEFAULT_TEMPLATE,
'/dispute-games': DEFAULT_TEMPLATE,
'/batches': DEFAULT_TEMPLATE, '/batches': DEFAULT_TEMPLATE,
'/batches/[number]': DEFAULT_TEMPLATE, '/batches/[number]': DEFAULT_TEMPLATE,
'/blobs/[hash]': DEFAULT_TEMPLATE, '/blobs/[hash]': DEFAULT_TEMPLATE,
......
...@@ -33,6 +33,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = { ...@@ -33,6 +33,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/csv-export': 'export data to CSV', '/csv-export': 'export data to CSV',
'/deposits': 'deposits (L1 > L2)', '/deposits': 'deposits (L1 > L2)',
'/output-roots': 'output roots', '/output-roots': 'output roots',
'/dispute-games': 'dispute games',
'/batches': 'tx batches (L2 blocks)', '/batches': 'tx batches (L2 blocks)',
'/batches/[number]': 'L2 tx batch %number%', '/batches/[number]': 'L2 tx batch %number%',
'/blobs/[hash]': 'blob %hash% details', '/blobs/[hash]': 'blob %hash% details',
......
...@@ -33,6 +33,7 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = { ...@@ -33,6 +33,7 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/csv-export': 'Export data to CSV file', '/csv-export': 'Export data to CSV file',
'/deposits': 'Deposits (L1 > L2)', '/deposits': 'Deposits (L1 > L2)',
'/output-roots': 'Output roots', '/output-roots': 'Output roots',
'/dispute-games': 'Dispute games',
'/batches': 'Tx batches (L2 blocks)', '/batches': 'Tx batches (L2 blocks)',
'/batches/[number]': 'L2 tx batch details', '/batches/[number]': 'L2 tx batch details',
'/blobs/[hash]': 'Blob details', '/blobs/[hash]': 'Blob details',
......
export const data = {
items: [
{
contract_address: '0x5cbe1b88b6357e6a8f0821bea72cc0b88c231f1c',
created_at: '2022-05-27T01:13:48.000000Z',
game_type: 0,
index: 6662,
l2_block_number: 12542890,
resolved_at: null,
status: 'In progress',
},
{
contract_address: '0x5cbe1b88b6357e6a8f0821bea72cc0b88c231f1c',
created_at: '2022-05-27T01:13:48.000000Z',
game_type: 0,
index: 6662,
l2_block_number: 12542890,
resolved_at: '2022-05-27T01:13:48.000000Z',
status: 'Defender wins',
},
],
next_page_params: {
items_count: 50,
index: 8382363,
},
};
...@@ -251,3 +251,13 @@ export const publicTagsSubmit: GetServerSideProps<Props> = async(context) => { ...@@ -251,3 +251,13 @@ export const publicTagsSubmit: GetServerSideProps<Props> = async(context) => {
return base(context); return base(context);
}; };
export const disputeGames: GetServerSideProps<Props> = async(context) => {
if (!config.features.faultProofSystem.isEnabled) {
return {
notFound: true,
};
}
return base(context);
};
...@@ -35,6 +35,7 @@ declare module "nextjs-routes" { ...@@ -35,6 +35,7 @@ declare module "nextjs-routes" {
| StaticRoute<"/contract-verification"> | StaticRoute<"/contract-verification">
| StaticRoute<"/csv-export"> | StaticRoute<"/csv-export">
| StaticRoute<"/deposits"> | StaticRoute<"/deposits">
| StaticRoute<"/dispute-games">
| StaticRoute<"/gas-tracker"> | StaticRoute<"/gas-tracker">
| StaticRoute<"/graphiql"> | StaticRoute<"/graphiql">
| StaticRoute<"/"> | StaticRoute<"/">
......
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
const DisputeGames = dynamic(() => import('ui/pages/OptimisticL2DisputeGames'), { ssr: false });
const Page: NextPage = () => {
return (
<PageNextJs pathname="/dispute-games">
<DisputeGames/>
</PageNextJs>
);
};
export default Page;
export { disputeGames as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -20,6 +20,7 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = { ...@@ -20,6 +20,7 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'optimistic' ], [ 'NEXT_PUBLIC_ROLLUP_TYPE', 'optimistic' ],
[ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ], [ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ],
[ 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL', 'https://localhost:3102' ], [ 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL', 'https://localhost:3102' ],
[ 'NEXT_PUBLIC_FAULT_PROOF_ENABLED', 'true' ],
], ],
shibariumRollup: [ shibariumRollup: [
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'shibarium' ], [ 'NEXT_PUBLIC_ROLLUP_TYPE', 'shibarium' ],
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
| "filter" | "filter"
| "finalized" | "finalized"
| "flame" | "flame"
| "games"
| "gas_xl" | "gas_xl"
| "gas" | "gas"
| "gear_slim" | "gear_slim"
......
import type { import type {
OptimisticL2DepositsItem, OptimisticL2DepositsItem,
OptimisticL2DisputeGamesItem,
OptimisticL2OutputRootsItem, OptimisticL2OutputRootsItem,
OptimisticL2TxnBatchesItem, OptimisticL2TxnBatchesItem,
OptimisticL2WithdrawalsItem, OptimisticL2WithdrawalsItem,
...@@ -45,3 +46,13 @@ export const L2_OUTPUT_ROOTS_ITEM: OptimisticL2OutputRootsItem = { ...@@ -45,3 +46,13 @@ export const L2_OUTPUT_ROOTS_ITEM: OptimisticL2OutputRootsItem = {
l2_output_index: 50655, l2_output_index: 50655,
output_root: TX_HASH, output_root: TX_HASH,
}; };
export const L2_DISPUTE_GAMES_ITEM: OptimisticL2DisputeGamesItem = {
contract_address: ADDRESS_HASH,
created_at: '2023-06-01T15:26:12.000000Z',
game_type: 0,
index: 6594,
l2_block_number: 50655,
resolved_at: null,
status: 'In progress',
};
...@@ -61,15 +61,15 @@ export type OptimisticL2WithdrawalsItem = { ...@@ -61,15 +61,15 @@ export type OptimisticL2WithdrawalsItem = {
'status': string; 'status': string;
} }
export const WITHDRAWAL_STATUSES = [ export type OptimisticL2WithdrawalStatus =
'Waiting for state root', 'Waiting for state root' |
'Ready to prove', 'Ready to prove' |
'In challenge period', 'In challenge period' |
'Ready for relay', 'Waiting a game to resolve' |
'Relayed', 'Ready to prove' |
] as const; 'Proven' |
'Ready for relay' |
export type OptimisticL2WithdrawalStatus = typeof WITHDRAWAL_STATUSES[number]; 'Relayed';
export type OptimisticL2WithdrawalsResponse = { export type OptimisticL2WithdrawalsResponse = {
items: Array<OptimisticL2WithdrawalsItem>; items: Array<OptimisticL2WithdrawalsItem>;
...@@ -78,3 +78,21 @@ export type OptimisticL2WithdrawalsResponse = { ...@@ -78,3 +78,21 @@ export type OptimisticL2WithdrawalsResponse = {
'nonce': string; 'nonce': string;
}; };
} }
export type OptimisticL2DisputeGamesResponse = {
items: Array<OptimisticL2DisputeGamesItem>;
'next_page_params': {
'items_count': number;
'index': number;
};
}
export type OptimisticL2DisputeGamesItem = {
contract_address: string;
created_at: string;
game_type: number;
index: number;
l2_block_number: number;
resolved_at: string | null;
status: string;
}
import { Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { OptimisticL2DisputeGamesItem } from 'types/api/optimisticL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2';
import HashStringShorten from 'ui/shared/HashStringShorten';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
const rollupFeature = config.features.rollup;
type Props = { item: OptimisticL2DisputeGamesItem; isLoading?: boolean };
const OptimisticL2DisputeGamesListItem = ({ item, isLoading }: Props) => {
if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
return null;
}
return (
<ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>Index</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value fontWeight={ 600 } color="text">
<Skeleton isLoaded={ !isLoading } display="inline-block">{ item.index }</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Game type</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">{ item.game_type }</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Address</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value color="text">
<Skeleton isLoaded={ !isLoading } display="flex" overflow="hidden" w="100%" alignItems="center">
<HashStringShorten hash={ item.contract_address } type="long"/>
<CopyToClipboard text={ item.contract_address } ml={ 2 } isLoading={ isLoading }/>
</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L2 block #</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<BlockEntityL2
isLoading={ isLoading }
number={ item.l2_block_number }
fontSize="sm"
lineHeight={ 5 }
noIcon
/>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Age</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">{ dayjs(item.created_at).fromNow() }</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Status</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value color="text">
<Skeleton isLoaded={ !isLoading } display="inline-block">{ item.status }</Skeleton>
</ListItemMobileGrid.Value>
{ item.resolved_at && (
<>
<ListItemMobileGrid.Label isLoading={ isLoading }>Resolution age</ListItemMobileGrid.Label><ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">{ dayjs(item.resolved_at).fromNow() }</Skeleton>
</ListItemMobileGrid.Value>
</>
) }
</ListItemMobileGrid.Container>
);
};
export default OptimisticL2DisputeGamesListItem;
import { Table, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react';
import type { OptimisticL2DisputeGamesItem } from 'types/api/optimisticL2';
import { default as Thead } from 'ui/shared/TheadSticky';
import OptimisticL2DisputeGamesTableItem from './OptimisticL2DisputeGamesTableItem';
type Props = {
items: Array<OptimisticL2DisputeGamesItem>;
top: number;
isLoading?: boolean;
}
const OptimisticL2DisputeGamesTable = ({ items, top, isLoading }: Props) => {
return (
<Table variant="simple" size="sm" style={{ tableLayout: 'auto' }} minW="950px">
<Thead top={ top }>
<Tr>
<Th>Index</Th>
<Th>Game type</Th>
<Th>Address</Th>
<Th>L2 block #</Th>
<Th>Age</Th>
<Th>Status</Th>
<Th>Resolution age</Th>
</Tr>
</Thead>
<Tbody>
{ items.map((item, index) => (
<OptimisticL2DisputeGamesTableItem
key={ String(item.index) + (isLoading ? index : '') }
item={ item }
isLoading={ isLoading }
/>
)) }
</Tbody>
</Table>
);
};
export default OptimisticL2DisputeGamesTable;
import { Flex, Td, Tr, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { OptimisticL2DisputeGamesItem } from 'types/api/optimisticL2';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2';
import HashStringShorten from 'ui/shared/HashStringShorten';
const faultProofSystemFeature = config.features.faultProofSystem;
type Props = { item: OptimisticL2DisputeGamesItem; isLoading?: boolean };
const OptimisticL2DisputeGamesTableItem = ({ item, isLoading }: Props) => {
if (!faultProofSystemFeature.isEnabled) {
return null;
}
return (
<Tr>
<Td verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block">{ item.index }</Skeleton>
</Td>
<Td verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block">{ item.game_type }</Skeleton>
</Td>
<Td verticalAlign="middle">
<Flex overflow="hidden" w="100%" alignItems="center">
<Skeleton isLoaded={ !isLoading }>
<HashStringShorten hash={ item.contract_address } type="long"/>
</Skeleton>
<CopyToClipboard text={ item.contract_address } ml={ 2 } isLoading={ isLoading }/>
</Flex>
</Td>
<Td verticalAlign="middle">
<BlockEntityL2
isLoading={ isLoading }
number={ item.l2_block_number }
fontSize="sm"
lineHeight={ 5 }
noIcon
/>
</Td>
<Td verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block">{ dayjs(item.created_at).fromNow() }</Skeleton>
</Td>
<Td verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block">{ item.status }</Skeleton>
</Td>
<Td>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ item.resolved_at ? dayjs(item.resolved_at).fromNow() : 'N/A' }
</Skeleton>
</Td>
</Tr>
);
};
export default OptimisticL2DisputeGamesTableItem;
import React from 'react';
import { data as disputeGamesData } from 'mocks/l2disputeGames/disputeGames';
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import { test, expect } from 'playwright/lib';
import OptimisticL2DisputeGames from './OptimisticL2DisputeGames';
test('base view +@mobile', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => {
test.slow();
await mockEnvs(ENVS_MAP.optimisticRollup);
await mockTextAd();
await mockApiResponse('optimistic_l2_dispute_games', disputeGamesData);
await mockApiResponse('optimistic_l2_dispute_games_count', 3971111);
const component = await render(<OptimisticL2DisputeGames/>);
await expect(component).toHaveScreenshot();
});
import { Hide, Show, Skeleton, Text } from '@chakra-ui/react';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import { L2_DISPUTE_GAMES_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils';
import OptimisticL2DisputeGamesListItem from 'ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesListItem';
import OptimisticL2DisputeGamesTable from 'ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesTable';
import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
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 OptimisticL2DisputeGames = () => {
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'optimistic_l2_dispute_games',
options: {
placeholderData: generateListStub<'optimistic_l2_dispute_games'>(
L2_DISPUTE_GAMES_ITEM,
50,
{
next_page_params: {
items_count: 50,
index: 9045200,
},
},
),
},
});
const countersQuery = useApiQuery('optimistic_l2_dispute_games_count', {
queryOptions: {
placeholderData: 50617,
},
});
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>
{ data.items.map(((item, index) => (
<OptimisticL2DisputeGamesListItem
key={ item.index + (isPlaceholderData ? String(index) : '') }
item={ item }
isLoading={ isPlaceholderData }
/>
))) }
</Show>
<Hide below="lg" ssr={ false }>
<OptimisticL2DisputeGamesTable items={ data.items } top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
const text = (() => {
if (countersQuery.isError || isError || !data?.items.length) {
return null;
}
return (
<Skeleton isLoaded={ !countersQuery.isPlaceholderData && !isPlaceholderData } display="flex" flexWrap="wrap">
Dispute game index
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[0].index } </Text>to
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[data.items.length - 1].index } </Text>
(total of { countersQuery.data?.toLocaleString() } games)
</Skeleton>
);
})();
const actionBar = <StickyPaginationWithText text={ text } pagination={ pagination }/>;
return (
<>
<PageTitle title="Dispute games" withTextAd/>
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no dispute games."
content={ content }
actionBar={ actionBar }
/>
</>
);
};
export default OptimisticL2DisputeGames;
...@@ -2,7 +2,6 @@ import { Button } from '@chakra-ui/react'; ...@@ -2,7 +2,6 @@ import { Button } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { OptimisticL2WithdrawalStatus } from 'types/api/optimisticL2'; import type { OptimisticL2WithdrawalStatus } from 'types/api/optimisticL2';
import { WITHDRAWAL_STATUSES } from 'types/api/optimisticL2';
import config from 'configs/app'; import config from 'configs/app';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
...@@ -13,10 +12,34 @@ interface Props { ...@@ -13,10 +12,34 @@ interface Props {
l1TxHash: string | undefined; l1TxHash: string | undefined;
} }
const WITHDRAWAL_STATUS_STEPS: Array<OptimisticL2WithdrawalStatus> = [
'Waiting for state root',
'Ready to prove',
'In challenge period',
'Ready for relay',
'Relayed',
];
const WITHDRAWAL_STATUS_ORDER_PROVEN: Array<OptimisticL2WithdrawalStatus> = [
'Waiting for state root',
'Ready to prove',
'Proven',
'Relayed',
];
const WITHDRAWAL_STATUS_ORDER_GAME: Array<OptimisticL2WithdrawalStatus> = [
'Waiting for state root',
'Ready to prove',
'Waiting a game to resolve',
'In challenge period',
'Ready for relay',
'Relayed',
];
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => { const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => {
if (!status || !WITHDRAWAL_STATUSES.includes(status) || !rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') { if (!status || !rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
return null; return null;
} }
...@@ -25,10 +48,14 @@ const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => { ...@@ -25,10 +48,14 @@ const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => {
const steps = (() => { const steps = (() => {
switch (status) { switch (status) {
case 'Ready for relay': case 'Ready for relay':
return WITHDRAWAL_STATUSES.slice(0, -1); return WITHDRAWAL_STATUS_STEPS.slice(0, -1);
case 'Proven':
return WITHDRAWAL_STATUS_ORDER_PROVEN;
case 'Waiting a game to resolve':
return WITHDRAWAL_STATUS_ORDER_GAME;
case 'Relayed': { case 'Relayed': {
if (l1TxHash) { if (l1TxHash) {
return WITHDRAWAL_STATUSES.map((status) => { return WITHDRAWAL_STATUS_STEPS.map((status) => {
return status === 'Relayed' ? { return status === 'Relayed' ? {
content: <TxEntityL1 hash={ l1TxHash } truncation="constant" text="Relayed" noIcon/>, content: <TxEntityL1 hash={ l1TxHash } truncation="constant" text="Relayed" noIcon/>,
label: status, label: status,
...@@ -36,11 +63,11 @@ const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => { ...@@ -36,11 +63,11 @@ const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => {
}); });
} }
return WITHDRAWAL_STATUSES; return WITHDRAWAL_STATUS_STEPS;
} }
default: default:
return WITHDRAWAL_STATUSES; return WITHDRAWAL_STATUS_STEPS;
} }
})(); })();
......
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