Skip to content

Commit a57893b

Browse files
authored
Block countdown. Infinite loop if 404 block is in the past. (#3130)
* Block countdown. Infinite loop if 404 block is in the past. Resolves #3124 * fix test
1 parent de32ffd commit a57893b

File tree

6 files changed

+85
-30
lines changed

6 files changed

+85
-30
lines changed

playwright/fixtures/mockRpcResponse.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import type { PublicRpcSchema } from 'viem';
44

55
import { getEnvValue } from 'configs/app/utils';
66

7-
type Params = PublicRpcSchema[number];
7+
type Params = Array<PublicRpcSchema[number]>;
88

99
export type MockRpcResponseFixture = (params: Params) => Promise<void>;
1010

1111
const fixture: TestFixture<MockRpcResponseFixture, { page: Page }> = async({ page }, use) => {
12-
await use(async(rpcMock) => {
12+
await use(async(rpcMocks) => {
1313
const rpcUrl = getEnvValue('NEXT_PUBLIC_NETWORK_RPC_URL');
1414

1515
if (!rpcUrl) {
@@ -27,14 +27,23 @@ const fixture: TestFixture<MockRpcResponseFixture, { page: Page }> = async({ pag
2727
const json = route.request().postDataJSON();
2828
const id = json?.id;
2929

30-
const payload = {
31-
id,
32-
jsonrpc: '2.0',
33-
method: rpcMock.Method,
34-
...(rpcMock.Parameters ? { params: rpcMock.Parameters } : {}),
35-
};
30+
if (id === undefined) {
31+
route.continue();
32+
return;
33+
}
34+
35+
const rpcMock = rpcMocks.find((mock) => {
36+
const payload = {
37+
id,
38+
jsonrpc: '2.0',
39+
method: mock.Method,
40+
...(mock.Parameters ? { params: mock.Parameters } : {}),
41+
};
42+
43+
return isEqual(json, payload);
44+
});
3645

37-
if (isEqual(json, payload) && id !== undefined) {
46+
if (rpcMock) {
3847
return route.fulfill({
3948
status: 200,
4049
json: {

ui/block/useBlockQuery.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type RpcResponseType = GetBlockReturnType<Chain, false, 'latest'> | null;
1919

2020
export type BlockQuery = UseQueryResult<Block, ResourceError<{ status: number }>> & {
2121
isDegradedData: boolean;
22+
isFutureBlock: boolean;
2223
};
2324

2425
interface Params {
@@ -47,6 +48,17 @@ export default function useBlockQuery({ heightOrHash }: Params): BlockQuery {
4748
},
4849
});
4950

51+
const latestBlockQuery = useQuery({
52+
queryKey: [ 'RPC', 'block', 'latest' ],
53+
queryFn: async() => {
54+
if (!publicClient) {
55+
return null;
56+
}
57+
return publicClient.getBlock({ blockTag: 'latest' });
58+
},
59+
enabled: publicClient !== undefined && (apiQuery.isError || apiQuery.errorUpdateCount > 0),
60+
});
61+
5062
const rpcQuery = useQuery<RpcResponseType, unknown, Block | null>({
5163
queryKey: [ 'RPC', 'block', { heightOrHash } ],
5264
queryFn: async() => {
@@ -91,7 +103,7 @@ export default function useBlockQuery({ heightOrHash }: Params): BlockQuery {
91103
};
92104
},
93105
placeholderData: GET_BLOCK,
94-
enabled: publicClient !== undefined && (apiQuery.isError || apiQuery.errorUpdateCount > 0),
106+
enabled: !latestBlockQuery.isPending,
95107
retry: false,
96108
refetchOnMount: false,
97109
});
@@ -120,5 +132,9 @@ export default function useBlockQuery({ heightOrHash }: Params): BlockQuery {
120132
return {
121133
...query,
122134
isDegradedData: isRpcQuery,
135+
isFutureBlock: Boolean(
136+
!heightOrHash.startsWith('0x') &&
137+
latestBlockQuery.data && Number(latestBlockQuery.data.number) < Number(heightOrHash),
138+
),
123139
};
124140
}

ui/pages/Address.pw.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ test('degradation view', async({ render, page, mockRpcResponse, mockApiResponse
4444
await mockApiResponse('general:address_counters', addressCountersMock.forValidator, { pathParams: { hash: addressMock.hash } });
4545
await mockApiResponse('general:address_tabs_counters', null as never, { pathParams: { hash: addressMock.hash }, status: 500 });
4646
await mockApiResponse('general:address_txs', null as never, { pathParams: { hash: addressMock.hash }, status: 500 });
47-
await mockRpcResponse({
47+
await mockRpcResponse([ {
4848
Method: 'eth_getBalance',
4949
Parameters: [ addressMock.hash, 'latest' ],
5050
ReturnType: numberToHex(1234567890123456),
51-
});
51+
} ]);
5252

5353
const component = await render(<Address/>, { hooksConfig });
5454
await page.waitForResponse(config.chain.rpcUrls[0]);

ui/pages/Block.pw.tsx

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,21 @@ test.beforeEach(async({ mockTextAd }) => {
2121

2222
test('degradation view, details tab', async({ render, mockApiResponse, mockRpcResponse, page }) => {
2323
await mockApiResponse('general:block', null as never, { pathParams: { height_or_hash: height }, status: 500 });
24-
await mockRpcResponse({
25-
Method: 'eth_getBlockByNumber',
26-
Parameters: [ numberToHex(Number(height)), false ],
27-
ReturnType: blockMock.rpcBlockBase,
28-
});
24+
await mockRpcResponse([
25+
{
26+
Method: 'eth_getBlockByNumber',
27+
Parameters: [ 'latest', false ],
28+
ReturnType: {
29+
...blockMock.rpcBlockBase,
30+
number: String(Number(height) + 1_000) as `0x${ string }`,
31+
},
32+
},
33+
{
34+
Method: 'eth_getBlockByNumber',
35+
Parameters: [ numberToHex(Number(height)), false ],
36+
ReturnType: blockMock.rpcBlockBase,
37+
},
38+
]);
2939

3040
const component = await render(<Block/>, { hooksConfig });
3141
await page.waitForResponse(config.chain.rpcUrls[0]);
@@ -42,11 +52,21 @@ test('degradation view, txs tab', async({ render, mockApiResponse, mockRpcRespon
4252

4353
await mockApiResponse('general:block', blockMock.base, { pathParams: { height_or_hash: height } });
4454
await mockApiResponse('general:block_txs', null as never, { pathParams: { height_or_hash: height }, status: 500 });
45-
await mockRpcResponse({
46-
Method: 'eth_getBlockByNumber',
47-
Parameters: [ numberToHex(Number(height)), true ],
48-
ReturnType: blockMock.rpcBlockWithTxsInfo,
49-
});
55+
await mockRpcResponse([
56+
{
57+
Method: 'eth_getBlockByNumber',
58+
Parameters: [ 'latest', false ],
59+
ReturnType: {
60+
...blockMock.rpcBlockWithTxsInfo,
61+
number: String(Number(height) + 1_000) as `0x${ string }`,
62+
},
63+
},
64+
{
65+
Method: 'eth_getBlockByNumber',
66+
Parameters: [ numberToHex(Number(height)), true ],
67+
ReturnType: blockMock.rpcBlockWithTxsInfo,
68+
},
69+
]);
5070

5171
const component = await render(<Block/>, { hooksConfig });
5272
await page.waitForResponse(config.chain.rpcUrls[0]);
@@ -64,11 +84,21 @@ test('degradation view, withdrawals tab', async({ render, mockApiResponse, mockR
6484
await mockEnvs(ENVS_MAP.beaconChain);
6585
await mockApiResponse('general:block', blockMock.withWithdrawals, { pathParams: { height_or_hash: height } });
6686
await mockApiResponse('general:block_withdrawals', null as never, { pathParams: { height_or_hash: height }, status: 500 });
67-
await mockRpcResponse({
68-
Method: 'eth_getBlockByNumber',
69-
Parameters: [ numberToHex(Number(height)), false ],
70-
ReturnType: blockMock.rpcBlockBase,
71-
});
87+
await mockRpcResponse([
88+
{
89+
Method: 'eth_getBlockByNumber',
90+
Parameters: [ 'latest', false ],
91+
ReturnType: {
92+
...blockMock.rpcBlockBase,
93+
number: String(Number(height) + 1_000) as `0x${ string }`,
94+
},
95+
},
96+
{
97+
Method: 'eth_getBlockByNumber',
98+
Parameters: [ numberToHex(Number(height)), false ],
99+
ReturnType: blockMock.rpcBlockBase,
100+
},
101+
]);
72102

73103
const component = await render(<Block/>, { hooksConfig });
74104
await page.waitForResponse(config.chain.rpcUrls[0]);

ui/pages/Block.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ const BlockPageContent = () => {
148148
throwOnAbsentParamError(heightOrHash);
149149

150150
if (blockQuery.isError) {
151-
if (!blockQuery.isDegradedData && blockQuery.error.status === 404 && !heightOrHash.startsWith('0x')) {
151+
if (!blockQuery.isDegradedData && blockQuery.error.status === 404 && !heightOrHash.startsWith('0x') && blockQuery.isFutureBlock) {
152152
const url = routeParams({ pathname: '/block/countdown/[height]', query: { height: heightOrHash } }, multichainContext);
153153
router.push(url, undefined, { shallow: true });
154154
return null;

ui/pages/MarketplaceApp.pw.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ const testFn = async({ render, mockAssetResponse, mockEnvs, mockRpcResponse, moc
2222
]);
2323
await mockApiResponse('admin:marketplace_dapp', appsMock[0], { pathParams: { chainId: config.chain.id, dappId: appsMock[0].id } });
2424
await mockAssetResponse(appsMock[0].url, './mocks/apps/app.html');
25-
await mockRpcResponse({
25+
await mockRpcResponse([ {
2626
Method: 'eth_chainId',
2727
ReturnType: numberToHex(Number(config.chain.id)),
28-
});
28+
} ]);
2929

3030
const component = await render(
3131
<Flex flexDirection="column" mx={{ base: 4, lg: 6 }} h="100vh">

0 commit comments

Comments
 (0)