Commit 5552cd6d authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into tom2drum/issue-2029

parents fed42c9c 6095da71
......@@ -16,7 +16,7 @@ const L2WithdrawalUrl = getEnvValue('NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL');
const title = 'Rollup (L2) chain';
const config: Feature<{ type: RollupType; L1BaseUrl: string; L2WithdrawalUrl?: string }> = (() => {
const config: Feature<{ type: RollupType; L1BaseUrl: string; L2WithdrawalUrl?: string; homepage: { showLatestBlocks: boolean } }> = (() => {
if (type && L1BaseUrl) {
return Object.freeze({
......@@ -25,6 +25,9 @@ const config: Feature<{ type: RollupType; L1BaseUrl: string; L2WithdrawalUrl?: s
type,
L1BaseUrl: stripTrailingSlash(L1BaseUrl),
L2WithdrawalUrl,
homepage: {
showLatestBlocks: getEnvValue('NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS') === 'true',
},
});
}
......
......@@ -287,6 +287,17 @@ const rollupSchema = yup
then: (schema) => schema.test(urlTest).required(),
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL can be used only if NEXT_PUBLIC_ROLLUP_TYPE is set to \'optimistic\' '),
}),
NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS: yup
.boolean()
.when('NEXT_PUBLIC_ROLLUP_TYPE', {
is: (value: string) => value,
then: (schema) => schema,
otherwise: (schema) => schema.test(
'not-exist',
'NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS cannot not be used if NEXT_PUBLIC_ROLLUP_TYPE is not defined',
value => value === undefined,
),
}),
});
const adButlerConfigSchema = yup
......
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_FAULT_PROOF_ENABLED=true
\ No newline at end of file
NEXT_PUBLIC_FAULT_PROOF_ENABLED=true
NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS=true
\ No newline at end of file
......@@ -434,6 +434,7 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi
| NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL | `string` | URL for L2 -> L1 withdrawals (Optimistic stack only) | Required for `optimistic` rollups | - | `https://app.optimism.io/bridge/withdraw` | v1.24.0+ |
| NEXT_PUBLIC_FAULT_PROOF_ENABLED | `boolean` | Set to `true` for chains with fault proof system enabled (Optimistic stack only) | - | - | `true` | v1.31.0+ |
| NEXT_PUBLIC_HAS_MUD_FRAMEWORK | `boolean` | Set to `true` for instances that use MUD framework (Optimistic stack only) | - | - | `true` | v1.33.0+ |
| NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS | `boolean` | Set to `true` to display "Latest blocks" widget instead of "Latest batches" on the home page | - | - | `true` | v1.36.0+ |
&nbsp;
......
......@@ -40,8 +40,8 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/deposits': '%network_name% deposits (L1 > L2)',
'/output-roots': '%network_name% output roots',
'/dispute-games': '%network_name% dispute games',
'/batches': '%network_name% tx batches (L2 blocks)',
'/batches/[number]': '%network_name% L2 tx batch %number%',
'/batches': '%network_name% txn batches',
'/batches/[number]': '%network_name% L2 txn batch %number%',
'/blobs/[hash]': '%network_name% blob %hash% details',
'/ops': 'User operations on %network_name% - %network_name% explorer',
'/op/[hash]': '%network_name% user operation %hash%',
......
......@@ -38,8 +38,8 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/deposits': 'Deposits (L1 > L2)',
'/output-roots': 'Output roots',
'/dispute-games': 'Dispute games',
'/batches': 'Tx batches (L2 blocks)',
'/batches/[number]': 'L2 tx batch details',
'/batches': 'Txn batches',
'/batches/[number]': 'L2 txn batch details',
'/blobs/[hash]': 'Blob details',
'/ops': 'User operations',
'/op/[hash]': 'User operation details',
......
import _get from 'lodash/get';
import React from 'react';
import config from 'configs/app';
......@@ -24,9 +25,10 @@ export default function useAddOrSwitchChain() {
const errorObj = getErrorObj(error);
const code = errorObj && 'code' in errorObj ? errorObj.code : undefined;
const originalErrorCode = _get(errorObj, 'data.originalError.code');
// This error code indicates that the chain has not been added to Wallet.
if (code === 4902) {
if (code === 4902 || originalErrorCode === 4902) {
const params = [ {
chainId: hexadecimalChainId,
chainName: config.chain.name,
......
......@@ -69,6 +69,17 @@ const abiItem: AbiFunction = {
name: 'internalProposals',
type: 'tuple[]',
},
// ARRAY OF TUPLES WITHOUT NAMES
{
components: [
{ type: 'address' },
{ type: 'uint256' },
],
internalType: 'struct SharingPercentage[]',
name: '_sharingPercentages',
type: 'tuple[]',
},
],
};
......@@ -115,6 +126,10 @@ const result = [
},
},
],
[
[ '0xfD36176C63dA52E783a347DE3544B0b44C7054a6', 0 ],
[ '0xC9534cB913150aD3e98D792857689B55e2404212', 3500 ],
],
];
const onSettle = () => {};
......
......@@ -23,8 +23,20 @@ const ItemTuple = ({ abiParameter, data, mode, level }: Props) => {
<span> { '{' }</span>
</p>
{ 'components' in abiParameter && abiParameter.components.map((component, index) => {
const dataObj = typeof data === 'object' && data !== null ? data : undefined;
const itemData = dataObj && component.name && component.name in dataObj ? dataObj[component.name as keyof typeof dataObj] : undefined;
const itemData = (() => {
if (typeof data !== 'object' || data === null) {
return;
}
if (Array.isArray(data)) {
return data[index];
}
if (component.name && component.name in data) {
return data[component.name as keyof typeof data];
}
})();
return (
<Item
key={ index }
......
......@@ -108,7 +108,7 @@ const ArbitrumL2TxnBatch = () => {
}
return {
label: 'Back to tx batches list',
label: 'Back to txn batches list',
url: appProps.referrer,
};
}, [ appProps.referrer ]);
......@@ -117,7 +117,7 @@ const ArbitrumL2TxnBatch = () => {
<>
<TextAd mb={ 6 }/>
<PageTitle
title={ `Tx batch #${ number }` }
title={ `Txn batch #${ number }` }
backLink={ backLink }
/>
{ batchQuery.isPlaceholderData ?
......
......@@ -2,7 +2,6 @@ import { Hide, Show, Skeleton, Text } from '@chakra-ui/react';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import { nbsp } from 'lib/html-entities';
import { ARBITRUM_L2_TXN_BATCHES_ITEM } from 'stubs/arbitrumL2';
import { generateListStub } from 'stubs/utils';
import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
......@@ -60,7 +59,7 @@ const ArbitrumL2TxnBatches = () => {
return (
<Skeleton isLoaded={ !countersQuery.isPlaceholderData && !isPlaceholderData } display="flex" flexWrap="wrap">
Tx batch (L2 block)
Txn batch
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[0].number } </Text>to
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[data.items.length - 1].number } </Text>
(total of { countersQuery.data?.toLocaleString() } batches)
......@@ -72,11 +71,11 @@ const ArbitrumL2TxnBatches = () => {
return (
<>
<PageTitle title={ `Tx batches (L2${ nbsp }blocks)` } withTextAd/>
<PageTitle title="Txn batches" withTextAd/>
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no tx batches."
emptyText="There are no txn batches."
content={ content }
actionBar={ actionBar }
/>
......
......@@ -14,6 +14,20 @@ import AdBanner from 'ui/shared/ad/AdBanner';
const rollupFeature = config.features.rollup;
const Home = () => {
const leftWidget = (() => {
if (rollupFeature.isEnabled && !rollupFeature.homepage.showLatestBlocks) {
switch (rollupFeature.type) {
case 'zkEvm':
return <LatestZkEvmL2Batches/>;
case 'arbitrum':
return <LatestArbitrumL2Batches/>;
}
}
return <LatestBlocks/>;
})();
return (
<Box as="main">
<HeroBanner/>
......@@ -23,9 +37,7 @@ const Home = () => {
</Flex>
<AdBanner mt={ 6 } mx="auto" display={{ base: 'flex', lg: 'none' }} justifyContent="center"/>
<Flex mt={ 8 } direction={{ base: 'column', lg: 'row' }} columnGap={ 12 } rowGap={ 6 }>
{ rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' && <LatestZkEvmL2Batches/> }
{ rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && <LatestArbitrumL2Batches/> }
{ !(rollupFeature.isEnabled && (rollupFeature.type === 'arbitrum' || rollupFeature.type === 'zkEvm')) && <LatestBlocks/> }
{ leftWidget }
<Box flexGrow={ 1 }>
<Transactions/>
</Box>
......
......@@ -106,7 +106,7 @@ const OptimisticL2TxnBatch = () => {
}
return {
label: 'Back to tx batches list',
label: 'Back to txn batches list',
url: appProps.referrer,
};
}, [ appProps.referrer ]);
......
......@@ -2,7 +2,6 @@ import { Hide, Show, Skeleton, Text } from '@chakra-ui/react';
import React from 'react';
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 { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
......@@ -60,7 +59,7 @@ const OptimisticL2TxnBatches = () => {
return (
<Skeleton isLoaded={ !countersQuery.isPlaceholderData && !isPlaceholderData } display="flex" flexWrap="wrap">
Tx batch (L2 block)
Txn batch
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[0].internal_id } </Text>to
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[data.items.length - 1].internal_id } </Text>
(total of { countersQuery.data?.toLocaleString() } batches)
......@@ -72,11 +71,11 @@ const OptimisticL2TxnBatches = () => {
return (
<>
<PageTitle title={ `Tx batches (L2${ nbsp }blocks)` } withTextAd/>
<PageTitle title="Txn batches" withTextAd/>
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no tx batches."
emptyText="There are no txn batches."
content={ content }
actionBar={ actionBar }
/>
......
......@@ -59,7 +59,7 @@ const ZkEvmL2TxnBatch = () => {
}
return {
label: 'Back to tx batches list',
label: 'Back to txn batches list',
url: appProps.referrer,
};
}, [ appProps.referrer ]);
......@@ -68,7 +68,7 @@ const ZkEvmL2TxnBatch = () => {
<>
<TextAd mb={ 6 }/>
<PageTitle
title={ `Tx batch #${ number }` }
title={ `Txn batch #${ number }` }
backLink={ backLink }
/>
{ batchQuery.isPlaceholderData ? <TabsSkeleton tabs={ tabs }/> : (
......
......@@ -59,7 +59,7 @@ const ZkEvmL2TxnBatches = () => {
return (
<Skeleton isLoaded={ !countersQuery.isPlaceholderData && !isPlaceholderData } display="flex" flexWrap="wrap">
Tx batch
Txn batch
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[0].number } </Text>to
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[data.items.length - 1].number } </Text>
(total of { countersQuery.data?.toLocaleString() } batches)
......@@ -71,11 +71,11 @@ const ZkEvmL2TxnBatches = () => {
return (
<>
<PageTitle title="Tx batches" withTextAd/>
<PageTitle title="Txn batches" withTextAd/>
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no tx batches."
emptyText="There are no txn batches."
content={ content }
actionBar={ actionBar }
/>
......
......@@ -80,7 +80,7 @@ const ZkSyncL2TxnBatch = () => {
}
return {
label: 'Back to tx batches list',
label: 'Back to txn batches list',
url: appProps.referrer,
};
}, [ appProps.referrer ]);
......@@ -89,7 +89,7 @@ const ZkSyncL2TxnBatch = () => {
<>
<TextAd mb={ 6 }/>
<PageTitle
title={ `Tx batch #${ number }` }
title={ `Txn batch #${ number }` }
backLink={ backLink }
/>
{ batchQuery.isPlaceholderData ?
......
......@@ -59,7 +59,7 @@ const ZkSyncL2TxnBatches = () => {
return (
<Skeleton isLoaded={ !countersQuery.isPlaceholderData && !isPlaceholderData } display="flex" flexWrap="wrap">
Tx batch
Txn batch
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[0].number } </Text>to
<Text fontWeight={ 600 } whiteSpace="pre"> #{ data.items[data.items.length - 1].number } </Text>
(total of { countersQuery.data?.toLocaleString() } batches)
......@@ -71,11 +71,11 @@ const ZkSyncL2TxnBatches = () => {
return (
<>
<PageTitle title="Tx batches" withTextAd/>
<PageTitle title="Txn batches" withTextAd/>
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no tx batches."
emptyText="There are no txn batches."
content={ content }
actionBar={ actionBar }
/>
......
......@@ -151,6 +151,7 @@ const SearchBar = ({ isHomepage }: Props) => {
<PopoverContent
w={ `${ menuWidth.current }px` }
ref={ menuRef }
overflow="hidden"
>
<PopoverBody
p={ 0 }
......
import { InputGroup, Input, InputLeftElement, chakra, useColorModeValue, forwardRef, InputRightElement } from '@chakra-ui/react';
import { InputGroup, Input, InputLeftElement, chakra, useColorModeValue, forwardRef, InputRightElement, Center } from '@chakra-ui/react';
import throttle from 'lodash/throttle';
import React from 'react';
import type { ChangeEvent, FormEvent, FocusEvent } from 'react';
......@@ -62,9 +62,69 @@ const SearchBarInput = (
};
}, [ isMobile, handleScroll ]);
const handleKeyPress = React.useCallback((event: KeyboardEvent) => {
if (isMobile) {
return;
}
switch (event.key) {
case '/': {
if ([ 'INPUT', 'TEXTAREA' ].includes((event.target as HTMLElement).tagName)) {
break;
}
if (!isSuggestOpen) {
event.preventDefault();
innerRef.current?.querySelector('input')?.focus();
onFocus?.();
}
break;
}
case 'Escape': {
if (isSuggestOpen) {
innerRef.current?.querySelector('input')?.blur();
onHide?.();
}
break;
}
}
}, [ isMobile, isSuggestOpen, onFocus, onHide ]);
React.useEffect(() => {
window.addEventListener('keydown', handleKeyPress);
return () => {
window.removeEventListener('keydown', handleKeyPress);
};
}, [ handleKeyPress ]);
const bgColor = useColorModeValue('white', 'black');
const transformMobile = scrollDirection !== 'down' ? 'translateY(0)' : 'translateY(-100%)';
const rightElement = (() => {
if (value) {
return <ClearButton onClick={ onClear }/>;
}
if (isMobile) {
return null;
}
return (
<Center
boxSize="20px"
my="2px"
mr={{ base: 1, lg: isHomepage ? 2 : 1 }}
borderRadius="sm"
borderWidth="1px"
borderColor="gray.400"
color="gray.400"
display={{ base: 'none', lg: 'flex' }}
>
/
</Center>
);
})();
return (
<chakra.form
ref={ innerRef }
......@@ -111,11 +171,9 @@ const SearchBarInput = (
color={ useColorModeValue('black', 'white') }
value={ value }
/>
{ value && (
<InputRightElement top={{ base: 2, lg: isHomepage ? 3 : 2 }} right={ 2 }>
<ClearButton onClick={ onClear }/>
</InputRightElement>
) }
<InputRightElement top={{ base: 2, lg: isHomepage ? 3 : 2 }} right={ 2 }>
{ rightElement }
</InputRightElement>
</InputGroup>
</chakra.form>
);
......
......@@ -306,7 +306,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
hint="Batch index for this transaction"
isLoading={ isLoading }
>
Tx batch
Txn batch
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<BatchEntityL2
......
......@@ -77,7 +77,7 @@ const ArbitrumL2TxnBatchDetails = ({ query }: Props) => {
isLoading={ isPlaceholderData }
hint="Batch number indicates the length of batches produced by grouping L2 blocks to be proven on L1"
>
Tx batch number
Txn batch number
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }>
......@@ -86,8 +86,8 @@ const ArbitrumL2TxnBatchDetails = ({ query }: Props) => {
<PrevNext
ml={ 6 }
onClick={ handlePrevNextClick }
prevLabel="View previous tx batch"
nextLabel="View next tx batch"
prevLabel="View previous txn batch"
nextLabel="View next txn batch"
isPrevDisabled={ data.number === 0 }
isLoading={ isPlaceholderData }
/>
......
......@@ -47,7 +47,7 @@ const OptimisticL2TxnBatchBlobCelestia = ({ blobs, isLoading }: Props) => {
<Icon as={ celeniumIcon } boxSize={ 5 }/>
<LinkExternal href={ getCeleniumUrl(blob) }>Blob page</LinkExternal>
</GridItem>
<GridItem fontWeight={ 600 }>Hight</GridItem>
<GridItem fontWeight={ 600 }>Height</GridItem>
<GridItem colSpan={ 2 }>
{ blob.height }
</GridItem>
......
......@@ -75,8 +75,8 @@ const OptimisticL2TxnBatchDetails = ({ query }: Props) => {
<PrevNext
ml={ 6 }
onClick={ handlePrevNextClick }
prevLabel="View previous tx batch"
nextLabel="View next tx batch"
prevLabel="View previous txn batch"
nextLabel="View next txn batch"
isPrevDisabled={ data.internal_id === 0 }
isLoading={ isPlaceholderData }
/>
......
......@@ -68,7 +68,7 @@ const OptimisticL2TxnBatchesListItem = ({ item, isLoading }: Props) => {
</LinkInternal>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L2 blocks</ListItemMobileGrid.Label>
<ListItemMobileGrid.Label isLoading={ isLoading }>Txn</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<LinkInternal
href={ route({ pathname: '/batches/[number]', query: { number: item.internal_id.toString(), tab: 'txs' } }) }
......
......@@ -64,7 +64,7 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
<DetailsInfoItem.Label
isLoading={ isPlaceholderData }
>
Tx batch number
Txn batch number
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }>
......@@ -73,8 +73,8 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
<PrevNext
ml={ 6 }
onClick={ handlePrevNextClick }
prevLabel="View previous tx batch"
nextLabel="View next tx batch"
prevLabel="View previous txn batch"
nextLabel="View next txn batch"
isPrevDisabled={ data.number === 0 }
isLoading={ isPlaceholderData }
/>
......
......@@ -80,7 +80,7 @@ const ZkSyncL2TxnBatchDetails = ({ query }: Props) => {
hint="Batch number indicates the length of batches produced by grouping L2 blocks to be proven on Ethereum."
isLoading={ isPlaceholderData }
>
Tx batch number
Txn batch number
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }>
......@@ -89,8 +89,8 @@ const ZkSyncL2TxnBatchDetails = ({ query }: Props) => {
<PrevNext
ml={ 6 }
onClick={ handlePrevNextClick }
prevLabel="View previous tx batch"
nextLabel="View next tx batch"
prevLabel="View previous txn batch"
nextLabel="View next txn batch"
isPrevDisabled={ data.number === 0 }
isLoading={ isPlaceholderData }
/>
......
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