Skip to content

Commit cbffbed

Browse files
fix: get logs batching
1 parent 098f10d commit cbffbed

File tree

9 files changed

+105
-71
lines changed

9 files changed

+105
-71
lines changed

src/adapters/ethers/resources/interop/address.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,7 @@ export function formatInteropEvmChain(chainId: bigint): Hex {
1313
const chainRef = toBeArray(chainId);
1414
const chainRefLength = getBytes(toBeHex(chainRef.length, 1));
1515

16-
const payload = concat([
17-
PREFIX_EVM_CHAIN,
18-
chainRefLength,
19-
chainRef,
20-
new Uint8Array([0]),
21-
]);
16+
const payload = concat([PREFIX_EVM_CHAIN, chainRefLength, chainRef, new Uint8Array([0])]);
2217

2318
return hexlify(payload) as Hex;
2419
}

src/adapters/ethers/resources/interop/context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ export async function commonCtx(
4242
attributes: AttributesResource,
4343
): Promise<BuildCtx> {
4444
const sender = (await client.signer.getAddress()) as Address;
45-
const chainId = BigInt((await client.l2.getNetwork()).chainId);
46-
const dstChainId = BigInt((await dstProvider.getNetwork()).chainId);
45+
const chainId = (await client.l2.getNetwork()).chainId;
46+
const dstChainId = (await dstProvider.getNetwork()).chainId;
4747

4848
const {
4949
bridgehub,

src/adapters/ethers/resources/interop/index.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
createInteropFinalizationServices,
3434
type InteropFinalizationServices,
3535
} from './services/finalization';
36+
import type { DestinationLogsQueryOptions } from './services/finalization/data-fetchers';
3637

3738
const { wrap, toResult } = createErrorHandlers('interop');
3839

@@ -72,7 +73,7 @@ function resolveWaitableInput(waitableInput: InteropWaitable): {
7273
const input = waitableInput as { waitable?: InteropWaitableBase };
7374
return {
7475
dstProvider: resolveDstProvider(waitableInput.dstChain),
75-
waitable: input.waitable ? input.waitable : waitableInput as InteropHandle<unknown>,
76+
waitable: input.waitable ? input.waitable : (waitableInput as InteropHandle<unknown>),
7677
};
7778
}
7879

@@ -97,7 +98,7 @@ export interface InteropResource {
9798
{ ok: true; value: InteropHandle<TransactionRequest> } | { ok: false; error: unknown }
9899
>;
99100

100-
status(h: InteropWaitable): Promise<InteropStatus>;
101+
status(h: InteropWaitable, opts?: DestinationLogsQueryOptions): Promise<InteropStatus>;
101102

102103
wait(
103104
h: InteropWaitable,
@@ -109,10 +110,14 @@ export interface InteropResource {
109110
opts?: { pollMs?: number; timeoutMs?: number },
110111
): Promise<{ ok: true; value: InteropFinalizationInfo } | { ok: false; error: unknown }>;
111112

112-
finalize(h: InteropWaitable | InteropFinalizationInfo): Promise<InteropFinalizationResult>;
113+
finalize(
114+
h: InteropWaitable | InteropFinalizationInfo,
115+
opts?: DestinationLogsQueryOptions,
116+
): Promise<InteropFinalizationResult>;
113117

114118
tryFinalize(
115119
h: InteropWaitable | InteropFinalizationInfo,
120+
opts?: DestinationLogsQueryOptions,
116121
): Promise<{ ok: true; value: InteropFinalizationResult } | { ok: false; error: unknown }>;
117122
}
118123

@@ -296,9 +301,12 @@ export function createInteropResource(
296301
toResult<InteropHandle<TransactionRequest>>(OP_INTEROP.tryCreate, () => create(params));
297302

298303
// status → non-blocking lifecycle inspection
299-
const status = (h: InteropWaitable): Promise<InteropStatus> => {
304+
const status = (
305+
h: InteropWaitable,
306+
opts?: DestinationLogsQueryOptions,
307+
): Promise<InteropStatus> => {
300308
const { dstProvider, waitable } = resolveWaitableInput(h);
301-
return wrap(OP_INTEROP.status, () => svc.status(dstProvider, waitable), {
309+
return wrap(OP_INTEROP.status, () => svc.status(dstProvider, waitable, opts), {
302310
message: 'Internal error while checking interop status.',
303311
ctx: { where: 'interop.status' },
304312
});
@@ -331,6 +339,7 @@ export function createInteropResource(
331339
// returns finalization metadata for UI / explorers.
332340
const finalize = (
333341
h: InteropWaitable | InteropFinalizationInfo,
342+
opts?: DestinationLogsQueryOptions,
334343
): Promise<InteropFinalizationResult> =>
335344
wrap(
336345
OP_INTEROP.finalize,
@@ -345,21 +354,23 @@ export function createInteropResource(
345354
});
346355
}
347356
const dstProvider = resolveDstProvider(h.dstChain);
348-
return svc.finalize(dstProvider, h);
357+
return svc.finalize(dstProvider, h, opts);
349358
}
350359

351360
const { dstProvider, waitable } = resolveWaitableInput(h);
352361
const info = await svc.wait(dstProvider, waitable);
353-
return svc.finalize(dstProvider, info);
362+
return svc.finalize(dstProvider, info, opts);
354363
},
355364
{
356365
message: 'Failed to finalize/execute interop bundle on destination.',
357366
ctx: { where: 'interop.finalize' },
358367
},
359368
);
360369

361-
const tryFinalize = (h: InteropWaitable | InteropFinalizationInfo) =>
362-
toResult<InteropFinalizationResult>(OP_INTEROP.tryFinalize, () => finalize(h));
370+
const tryFinalize = (
371+
h: InteropWaitable | InteropFinalizationInfo,
372+
opts?: DestinationLogsQueryOptions,
373+
) => toResult<InteropFinalizationResult>(OP_INTEROP.tryFinalize, () => finalize(h, opts));
363374

364375
return {
365376
quote,

src/adapters/ethers/resources/interop/services/erc20.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,3 @@ export async function resolveErc20AssetIds(
6262

6363
return assetIds;
6464
}
65-

src/adapters/ethers/resources/interop/services/finalization/bundle.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { Contract, type AbstractProvider, type TransactionResponse, type TransactionReceipt } from 'ethers';
1+
import {
2+
Contract,
3+
type AbstractProvider,
4+
type TransactionResponse,
5+
type TransactionReceipt,
6+
} from 'ethers';
27
import type { Hex } from '../../../../../../core/types/primitives';
38
import type { InteropFinalizationInfo } from '../../../../../../core/types/flows/interop';
49
import type { EthersClient } from '../../../../client';
@@ -10,7 +15,7 @@ import IInteropHandlerAbi from '../../../../../../core/internal/abis/IInteropHan
1015
import { getTopics } from './topics';
1116
import type { InteropPhase } from '../../../../../../core/types/flows/interop';
1217
import type { InteropTopics } from '../../../../../../core/resources/interop/events';
13-
import { getDestinationLogs } from './data-fetchers';
18+
import { getDestinationLogs, type DestinationLogsQueryOptions } from './data-fetchers';
1419

1520
const { wrap } = createErrorHandlers('interop');
1621

@@ -19,13 +24,16 @@ export async function getBundleStatus(
1924
dstProvider: AbstractProvider,
2025
topics: InteropTopics,
2126
bundleHash: Hex,
27+
opts?: DestinationLogsQueryOptions,
2228
): Promise<{ phase: InteropPhase; dstExecTxHash?: Hex }> {
2329
const { interopHandler } = await client.ensureAddresses();
2430
// Single call: filter only by bundleHash (topic1), then classify via topic0 locally.
25-
const bundleLogs = await getDestinationLogs(dstProvider, interopHandler, [
26-
null,
27-
bundleHash,
28-
]);
31+
const bundleLogs = await getDestinationLogs(
32+
dstProvider,
33+
interopHandler,
34+
[null, bundleHash],
35+
opts,
36+
);
2937

3038
const findLastByTopic = (eventTopic: Hex) =>
3139
bundleLogs.findLast((log) => log.topics[0].toLowerCase() === eventTopic.toLowerCase());
@@ -53,11 +61,12 @@ export async function executeBundle(
5361
client: EthersClient,
5462
dstProvider: AbstractProvider,
5563
info: InteropFinalizationInfo,
64+
opts?: DestinationLogsQueryOptions,
5665
): Promise<{ hash: Hex; wait: () => Promise<TransactionReceipt> }> {
5766
const { topics } = getTopics();
5867
const { bundleHash, encodedData, proof } = info;
5968

60-
const dstStatus = await getBundleStatus(client, dstProvider, topics, bundleHash);
69+
const dstStatus = await getBundleStatus(client, dstProvider, topics, bundleHash, opts);
6170

6271
if (['EXECUTED', 'UNBUNDLED'].includes(dstStatus.phase)) {
6372
throw createError('STATE', {

src/adapters/ethers/resources/interop/services/finalization/data-fetchers.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@ import { InteropRootStorageABI } from '../../../../../../core/abi';
88
import { L2_INTEROP_ROOT_STORAGE_ADDRESS } from '../../../../../../core/constants';
99

1010
const { wrap } = createErrorHandlers('interop');
11-
const DEFAULT_LOG_CHUNK_SIZE = 1_000;
11+
const DEFAULT_BLOCKS_RANGE_SIZE = 10_000;
1212
const DEFAULT_MAX_BLOCKS_BACK = 20_000;
13+
const SAFE_BLOCKS_RANGE_SIZE = 1_000;
14+
15+
export interface DestinationLogsQueryOptions {
16+
maxBlocksBack?: number;
17+
logChunkSize?: number;
18+
}
1319

1420
// Server returns an error if the there is a block range limit and the requested range exceeds it.
1521
// The error returned in such case is UNKNOWN_ERROR with a message containing "query exceeds max block range {limit}".
@@ -48,15 +54,19 @@ export async function getDestinationLogs(
4854
dstProvider: AbstractProvider,
4955
address: Address,
5056
topics: Array<Hex | null>,
57+
opts?: DestinationLogsQueryOptions,
5158
): Promise<Log[]> {
59+
const maxBlocksBack = opts?.maxBlocksBack ?? DEFAULT_MAX_BLOCKS_BACK;
60+
const initialChunkSize = opts?.logChunkSize ?? DEFAULT_BLOCKS_RANGE_SIZE;
61+
5262
return await wrap(
5363
OP_INTEROP.svc.status.dstLogs,
5464
async () => {
5565
const currentBlock = await dstProvider.getBlockNumber();
56-
const minBlock = Math.max(0, currentBlock - DEFAULT_MAX_BLOCKS_BACK);
66+
const minBlock = Math.max(0, currentBlock - maxBlocksBack);
5767

5868
let toBlock = currentBlock;
59-
let chunkSize = DEFAULT_LOG_CHUNK_SIZE;
69+
let chunkSize = initialChunkSize;
6070

6171
while (toBlock >= minBlock) {
6272
const fromBlock = Math.max(minBlock, toBlock - chunkSize + 1);
@@ -84,16 +94,23 @@ export async function getDestinationLogs(
8494
const serverLimit = parseMaxBlockRangeLimit(error);
8595
// If we can't determine the server limit, rethrow the error.
8696
if (serverLimit == null) {
87-
throw error;
97+
// In case the error message cannot be parsed or a different error message format is returned by
98+
// a provider, try once again with a small chunk size.
99+
if (chunkSize > SAFE_BLOCKS_RANGE_SIZE) {
100+
chunkSize = SAFE_BLOCKS_RANGE_SIZE;
101+
} else {
102+
throw error;
103+
}
104+
} else {
105+
chunkSize = Math.min(chunkSize, serverLimit);
88106
}
89-
chunkSize = serverLimit;
90107
}
91108
}
92109

93110
return [];
94111
},
95112
{
96-
ctx: { address },
113+
ctx: { address, maxBlocksBack, logChunkSize: initialChunkSize },
97114
message: 'Failed to query destination bundle lifecycle logs.',
98115
},
99116
);

src/adapters/ethers/resources/interop/services/finalization/index.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ import type { EthersClient } from '../../../../client';
99
import { executeBundle } from './bundle';
1010
import { waitForFinalization } from './polling';
1111
import { getStatus } from './status';
12+
import type { DestinationLogsQueryOptions } from './data-fetchers';
1213

1314
export interface InteropFinalizationServices {
14-
status(dstProvider: AbstractProvider, input: InteropWaitable): Promise<InteropStatus>;
15+
status(
16+
dstProvider: AbstractProvider,
17+
input: InteropWaitable,
18+
opts?: DestinationLogsQueryOptions,
19+
): Promise<InteropStatus>;
1520
wait(
1621
dstProvider: AbstractProvider,
1722
input: InteropWaitable,
@@ -20,23 +25,24 @@ export interface InteropFinalizationServices {
2025
finalize(
2126
dstProvider: AbstractProvider,
2227
info: InteropFinalizationInfo,
28+
opts?: DestinationLogsQueryOptions,
2329
): Promise<InteropFinalizationResult>;
2430
}
2531

2632
export function createInteropFinalizationServices(
2733
client: EthersClient,
2834
): InteropFinalizationServices {
2935
return {
30-
status(dstProvider, input) {
31-
return getStatus(client, dstProvider, input);
36+
status(dstProvider, input, opts) {
37+
return getStatus(client, dstProvider, input, opts);
3238
},
3339

3440
wait(dstProvider, input, opts) {
3541
return waitForFinalization(client, dstProvider, input, opts);
3642
},
3743

38-
async finalize(dstProvider, info) {
39-
const execResult = await executeBundle(client, dstProvider, info);
44+
async finalize(dstProvider, info, opts) {
45+
const execResult = await executeBundle(client, dstProvider, info, opts);
4046
await execResult.wait();
4147

4248
return {

src/adapters/ethers/resources/interop/services/finalization/status.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ import { getTopics } from './topics';
1414
import { decodeInteropBundleSent } from './decoders';
1515
import { getSourceReceipt } from './data-fetchers';
1616
import { getBundleStatus } from './bundle';
17+
import type { DestinationLogsQueryOptions } from './data-fetchers';
1718

1819
export async function getStatus(
1920
client: EthersClient,
2021
dstProvider: AbstractProvider,
2122
input: InteropWaitable,
23+
opts?: DestinationLogsQueryOptions,
2224
): Promise<InteropStatus> {
2325
const { topics, centerIface } = getTopics();
2426
const baseIds = resolveIdsFromWaitable(input);
@@ -51,12 +53,7 @@ export async function getStatus(
5153
};
5254
}
5355

54-
const dstInfo = await getBundleStatus(
55-
client,
56-
dstProvider,
57-
topics,
58-
enrichedIds.bundleHash,
59-
);
56+
const dstInfo = await getBundleStatus(client, dstProvider, topics, enrichedIds.bundleHash, opts);
6057

6158
return {
6259
phase: dstInfo.phase,

0 commit comments

Comments
 (0)