mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
OP Fault Proofs support
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
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';
|
||||
export { default as blockchainInteraction } from './blockchainInteraction';
|
||||
export { default as csvExport } from './csvExport';
|
||||
export { default as dataAvailability } from './dataAvailability';
|
||||
export { default as faultProofSystem } from './faultProofSystem';
|
||||
export { default as gasTracker } from './gasTracker';
|
||||
export { default as googleAnalytics } from './googleAnalytics';
|
||||
export { default as graphqlApiDocs } from './graphqlApiDocs';
|
||||
|
||||
@@ -61,3 +61,4 @@ NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
|
||||
NEXT_PUBLIC_ROLLUP_TYPE=optimistic
|
||||
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_FAULT_PROOF_ENABLED=true
|
||||
|
||||
@@ -629,6 +629,16 @@ const schema = yup
|
||||
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_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
|
||||
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
NEXT_PUBLIC_ROLLUP_TYPE=optimistic
|
||||
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
|
||||
@@ -36,6 +36,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
|
||||
- [Beacon chain](ENVS.md#beacon-chain)
|
||||
- [User operations](ENVS.md#user-operations-feature-erc-4337)
|
||||
- [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)
|
||||
- [Google analytics](ENVS.md#google-analytics)
|
||||
- [Mixpanel analytics](ENVS.md#mixpanel-analytics)
|
||||
@@ -401,6 +402,14 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi
|
||||
|
||||
|
||||
|
||||
### 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` |
|
||||
|
||||
|
||||
|
||||
### Export data to CSV file
|
||||
|
||||
| Variable | Type| Description | Compulsoriness | Default value | Example value |
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
+16
-3
@@ -64,6 +64,7 @@ import type {
|
||||
OptimisticL2OutputRootsResponse,
|
||||
OptimisticL2TxnBatchesResponse,
|
||||
OptimisticL2WithdrawalsResponse,
|
||||
OptimisticL2DisputeGamesResponse,
|
||||
} from 'types/api/optimisticL2';
|
||||
import type { RawTracesResponse } from 'types/api/rawTrace';
|
||||
import type { SearchRedirectResult, SearchResult, SearchResultFilters, SearchResultItem } from 'types/api/search';
|
||||
@@ -650,6 +651,15 @@ export const RESOURCES = {
|
||||
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_deposits: {
|
||||
path: '/api/v2/zkevm/deposits',
|
||||
@@ -853,6 +863,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
|
||||
'token_instance_transfers' | 'token_instance_holders' |
|
||||
'verified_contracts' |
|
||||
'optimistic_l2_output_roots' | 'optimistic_l2_withdrawals' | 'optimistic_l2_txn_batches' | 'optimistic_l2_deposits' |
|
||||
'optimistic_l2_dispute_games' |
|
||||
'shibarium_deposits' | 'shibarium_withdrawals' |
|
||||
'zkevm_l2_deposits' | 'zkevm_l2_withdrawals' | 'zkevm_l2_txn_batches' | 'zkevm_l2_txn_batch_txs' |
|
||||
'zksync_l2_txn_batches' | 'zksync_l2_txn_batch_txs' |
|
||||
@@ -955,13 +966,12 @@ Q extends 'optimistic_l2_output_roots' ? OptimisticL2OutputRootsResponse :
|
||||
Q extends 'optimistic_l2_withdrawals' ? OptimisticL2WithdrawalsResponse :
|
||||
Q extends 'optimistic_l2_deposits' ? OptimisticL2DepositsResponse :
|
||||
Q extends 'optimistic_l2_txn_batches' ? OptimisticL2TxnBatchesResponse :
|
||||
Q extends 'optimistic_l2_dispute_games' ? OptimisticL2DisputeGamesResponse :
|
||||
Q extends 'optimistic_l2_output_roots_count' ? number :
|
||||
Q extends 'optimistic_l2_withdrawals_count' ? number :
|
||||
Q extends 'optimistic_l2_deposits_count' ? number :
|
||||
Q extends 'optimistic_l2_txn_batches_count' ? number :
|
||||
Q extends 'config_backend_version' ? BackendVersionConfig :
|
||||
Q extends 'address_metadata_info' ? AddressMetadataInfo :
|
||||
Q extends 'address_metadata_tag_types' ? PublicTagTypesResponse :
|
||||
Q extends 'optimistic_l2_dispute_games_count' ? number :
|
||||
never;
|
||||
// !!! IMPORTANT !!!
|
||||
// See comment above
|
||||
@@ -969,6 +979,9 @@ never;
|
||||
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
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 'marketplace_dapps' ? Array<MarketplaceAppOverview> :
|
||||
Q extends 'marketplace_dapp' ? MarketplaceAppOverview :
|
||||
|
||||
@@ -96,6 +96,12 @@ export default function useNavItems(): ReturnType {
|
||||
icon: '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;
|
||||
|
||||
@@ -109,6 +115,7 @@ export default function useNavItems(): ReturnType {
|
||||
[
|
||||
blocks,
|
||||
rollupTxnBatches,
|
||||
rollupDisputeGames,
|
||||
rollupFeature.type === 'optimistic' ? rollupOutputRoots : undefined,
|
||||
].filter(Boolean),
|
||||
[
|
||||
|
||||
@@ -35,6 +35,7 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
|
||||
'/csv-export': 'Regular page',
|
||||
'/deposits': 'Root page',
|
||||
'/output-roots': 'Root page',
|
||||
'/dispute-games': 'Root page',
|
||||
'/batches': 'Root page',
|
||||
'/batches/[number]': 'Regular page',
|
||||
'/blobs/[hash]': 'Regular page',
|
||||
|
||||
@@ -39,6 +39,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
|
||||
'/csv-export': DEFAULT_TEMPLATE,
|
||||
'/deposits': DEFAULT_TEMPLATE,
|
||||
'/output-roots': DEFAULT_TEMPLATE,
|
||||
'/dispute-games': DEFAULT_TEMPLATE,
|
||||
'/batches': DEFAULT_TEMPLATE,
|
||||
'/batches/[number]': DEFAULT_TEMPLATE,
|
||||
'/blobs/[hash]': DEFAULT_TEMPLATE,
|
||||
|
||||
@@ -33,6 +33,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
|
||||
'/csv-export': 'export data to CSV',
|
||||
'/deposits': 'deposits (L1 > L2)',
|
||||
'/output-roots': 'output roots',
|
||||
'/dispute-games': 'dispute games',
|
||||
'/batches': 'tx batches (L2 blocks)',
|
||||
'/batches/[number]': 'L2 tx batch %number%',
|
||||
'/blobs/[hash]': 'blob %hash% details',
|
||||
|
||||
@@ -33,6 +33,7 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
|
||||
'/csv-export': 'Export data to CSV file',
|
||||
'/deposits': 'Deposits (L1 > L2)',
|
||||
'/output-roots': 'Output roots',
|
||||
'/dispute-games': 'Dispute games',
|
||||
'/batches': 'Tx batches (L2 blocks)',
|
||||
'/batches/[number]': 'L2 tx batch details',
|
||||
'/blobs/[hash]': 'Blob details',
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
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) => {
|
||||
|
||||
return base(context);
|
||||
};
|
||||
|
||||
export const disputeGames: GetServerSideProps<Props> = async(context) => {
|
||||
if (!config.features.faultProofSystem.isEnabled) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
return base(context);
|
||||
};
|
||||
|
||||
Vendored
+1
@@ -35,6 +35,7 @@ declare module "nextjs-routes" {
|
||||
| StaticRoute<"/contract-verification">
|
||||
| StaticRoute<"/csv-export">
|
||||
| StaticRoute<"/deposits">
|
||||
| StaticRoute<"/dispute-games">
|
||||
| StaticRoute<"/gas-tracker">
|
||||
| StaticRoute<"/graphiql">
|
||||
| StaticRoute<"/">
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
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]>> = {
|
||||
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'optimistic' ],
|
||||
[ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ],
|
||||
[ 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL', 'https://localhost:3102' ],
|
||||
[ 'NEXT_PUBLIC_FAULT_PROOF_ENABLED', 'true' ],
|
||||
],
|
||||
shibariumRollup: [
|
||||
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'shibarium' ],
|
||||
|
||||
Vendored
+1
@@ -61,6 +61,7 @@
|
||||
| "filter"
|
||||
| "finalized"
|
||||
| "flame"
|
||||
| "games"
|
||||
| "gas_xl"
|
||||
| "gas"
|
||||
| "gear_slim"
|
||||
|
||||
+11
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
OptimisticL2DepositsItem,
|
||||
OptimisticL2DisputeGamesItem,
|
||||
OptimisticL2OutputRootsItem,
|
||||
OptimisticL2TxnBatchesItem,
|
||||
OptimisticL2WithdrawalsItem,
|
||||
@@ -45,3 +46,13 @@ export const L2_OUTPUT_ROOTS_ITEM: OptimisticL2OutputRootsItem = {
|
||||
l2_output_index: 50655,
|
||||
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 = {
|
||||
'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 OptimisticL2WithdrawalStatus =
|
||||
'Waiting for state root' |
|
||||
'Ready to prove' |
|
||||
'In challenge period' |
|
||||
'Waiting a game to resolve' |
|
||||
'Ready to prove' |
|
||||
'Proven' |
|
||||
'Ready for relay' |
|
||||
'Relayed';
|
||||
|
||||
export type OptimisticL2WithdrawalsResponse = {
|
||||
items: Array<OptimisticL2WithdrawalsItem>;
|
||||
@@ -78,3 +78,21 @@ export type OptimisticL2WithdrawalsResponse = {
|
||||
'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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
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;
|
||||
@@ -0,0 +1,43 @@
|
||||
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;
|
||||
@@ -0,0 +1,61 @@
|
||||
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;
|
||||
@@ -0,0 +1,19 @@
|
||||
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();
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
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;
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -2,7 +2,6 @@ import { Button } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { OptimisticL2WithdrawalStatus } from 'types/api/optimisticL2';
|
||||
import { WITHDRAWAL_STATUSES } from 'types/api/optimisticL2';
|
||||
|
||||
import config from 'configs/app';
|
||||
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
|
||||
@@ -13,10 +12,34 @@ interface Props {
|
||||
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 TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => {
|
||||
if (!status || !WITHDRAWAL_STATUSES.includes(status) || !rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
|
||||
if (!status || !rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -25,10 +48,14 @@ const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => {
|
||||
const steps = (() => {
|
||||
switch (status) {
|
||||
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': {
|
||||
if (l1TxHash) {
|
||||
return WITHDRAWAL_STATUSES.map((status) => {
|
||||
return WITHDRAWAL_STATUS_STEPS.map((status) => {
|
||||
return status === 'Relayed' ? {
|
||||
content: <TxEntityL1 hash={ l1TxHash } truncation="constant" text="Relayed" noIcon/>,
|
||||
label: status,
|
||||
@@ -36,11 +63,11 @@ const TxDetailsWithdrawalStatus = ({ status, l1TxHash }: Props) => {
|
||||
});
|
||||
}
|
||||
|
||||
return WITHDRAWAL_STATUSES;
|
||||
return WITHDRAWAL_STATUS_STEPS;
|
||||
}
|
||||
|
||||
default:
|
||||
return WITHDRAWAL_STATUSES;
|
||||
return WITHDRAWAL_STATUS_STEPS;
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user