@0xbow/privacy-pools-core-sdk 1.0.4 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/esm/{fetchArtifacts.esm-C_DNv-_D.js → fetchArtifacts.esm-DbVRphob.js} +2 -2
  2. package/dist/esm/{fetchArtifacts.esm-C_DNv-_D.js.map → fetchArtifacts.esm-DbVRphob.js.map} +1 -1
  3. package/dist/esm/{fetchArtifacts.node-BBPNjgiT.js → fetchArtifacts.node-D-fJGtzV.js} +2 -2
  4. package/dist/esm/{fetchArtifacts.node-BBPNjgiT.js.map → fetchArtifacts.node-D-fJGtzV.js.map} +1 -1
  5. package/dist/esm/{index-BxzIe_IR.js → index-DkNRxKxP.js} +2889 -109
  6. package/dist/esm/index-DkNRxKxP.js.map +1 -0
  7. package/dist/esm/index.mjs +1 -1
  8. package/dist/index.d.mts +87 -14
  9. package/dist/node/{fetchArtifacts.esm-l-EDo53-.js → fetchArtifacts.esm-BIT-b_1_.js} +2 -2
  10. package/dist/node/{fetchArtifacts.esm-l-EDo53-.js.map → fetchArtifacts.esm-BIT-b_1_.js.map} +1 -1
  11. package/dist/node/{fetchArtifacts.node-DYwQHSF4.js → fetchArtifacts.node-CKwwU50E.js} +2 -2
  12. package/dist/node/{fetchArtifacts.node-DYwQHSF4.js.map → fetchArtifacts.node-CKwwU50E.js.map} +1 -1
  13. package/dist/node/{index-DGsIfUGw.js → index-C3RV9Cri.js} +2889 -109
  14. package/dist/node/index-C3RV9Cri.js.map +1 -0
  15. package/dist/node/index.mjs +1 -1
  16. package/dist/types/core/account.service.d.ts +8 -6
  17. package/dist/types/core/data.service.d.ts +28 -8
  18. package/dist/types/{fetchArtifacts.esm-IMTIZwq7.js → fetchArtifacts.esm-DT5RuODl.js} +1 -1
  19. package/dist/types/{fetchArtifacts.node-BcXsBNCT.js → fetchArtifacts.node-D_iVIPqW.js} +1 -1
  20. package/dist/types/{index-DbuAhDci.js → index-CHy3YamH.js} +2888 -108
  21. package/dist/types/index.js +1 -1
  22. package/dist/types/types/index.d.ts +1 -0
  23. package/dist/types/types/rateLimit.d.ts +51 -0
  24. package/package.json +4 -2
  25. package/src/core/account.service.ts +52 -39
  26. package/src/core/data.service.ts +325 -95
  27. package/src/types/index.ts +1 -0
  28. package/src/types/rateLimit.ts +66 -0
  29. package/dist/esm/index-BxzIe_IR.js.map +0 -1
  30. package/dist/node/index-DGsIfUGw.js.map +0 -1
  31. package/dist/types/utils/concurrency.d.ts +0 -15
  32. package/src/utils/concurrency.ts +0 -32
@@ -1,20 +1,12 @@
1
- import {
2
- type PublicClient,
3
- createPublicClient,
4
- http,
5
- parseAbiItem,
6
- } from "viem";
7
- import {
8
- ChainConfig,
9
- DepositEvent,
10
- WithdrawalEvent,
11
- RagequitEvent,
12
- } from "../types/events.js";
13
- import { PoolInfo } from "../types/account.js";
14
- import { Hash } from "../types/commitment.js";
15
- import { Logger } from "../utils/logger.js";
16
- import { DataError } from "../errors/data.error.js";
17
- import { ErrorCode } from "../errors/base.error.js";
1
+ import {createPublicClient, type Hex, http, parseAbiItem, type PublicClient,} from "viem";
2
+ import {mapLimit} from "async";
3
+ import {ChainConfig, DepositEvent, RagequitEvent, WithdrawalEvent,} from "../types/events.js";
4
+ import {PoolInfo} from "../types/account.js";
5
+ import {Hash} from "../types/commitment.js";
6
+ import {BlockRange, ChainLogFetchConfig, DEFAULT_LOG_FETCH_CONFIG, LogFetchConfig,} from "../types/rateLimit.js";
7
+ import {Logger, LogLevel} from "../utils/logger.js";
8
+ import {DataError} from "../errors/data.error.js";
9
+ import {ErrorCode} from "../errors/base.error.js";
18
10
 
19
11
  // Event signatures from the contract
20
12
  const DEPOSIT_EVENT = parseAbiItem('event Deposited(address indexed _depositor, uint256 _commitment, uint256 _label, uint256 _value, uint256 _merkleRoot)');
@@ -33,15 +25,31 @@ const RAGEQUIT_EVENT = parseAbiItem('event Ragequit(address indexed _ragequitter
33
25
  export class DataService {
34
26
  private readonly clients: Map<number, PublicClient> = new Map();
35
27
  private readonly logger: Logger;
28
+ private readonly logFetchConfigs: Map<number, LogFetchConfig>;
36
29
 
37
30
  /**
38
31
  * Initialize the data service with chain configurations
39
32
  *
40
33
  * @param chainConfigs - Array of chain configurations containing chainId, RPC URL, and API key
34
+ * @param logFetchConfig - Per-chain configuration for rate-limited log fetching as a Map<chainId, config>.
35
+ * Each chain can have its own specific settings (e.g., different block chunk sizes).
41
36
  * @throws {DataError} If client initialization fails for any chain
42
37
  */
43
- constructor(private readonly chainConfigs: ChainConfig[]) {
44
- this.logger = new Logger({ prefix: "Data" });
38
+ constructor(
39
+ private readonly chainConfigs: ChainConfig[],
40
+ logFetchConfig: ChainLogFetchConfig = new Map()
41
+ ) {
42
+ this.logger = new Logger({ prefix: "Data", level: LogLevel.DEBUG });
43
+
44
+ // Initialize per-chain configs with defaults merged with chain-specific overrides
45
+ this.logFetchConfigs = new Map();
46
+ for (const config of chainConfigs) {
47
+ const chainSpecificConfig = logFetchConfig.get(config.chainId);
48
+ this.logFetchConfigs.set(
49
+ config.chainId,
50
+ { ...DEFAULT_LOG_FETCH_CONFIG, ...chainSpecificConfig }
51
+ );
52
+ }
45
53
 
46
54
  try {
47
55
  for (const config of chainConfigs) {
@@ -50,9 +58,7 @@ export class DataService {
50
58
  }
51
59
 
52
60
  const client = createPublicClient({
53
- transport: http(config.rpcUrl, {
54
- timeout: 20_000,
55
- }),
61
+ transport: http(config.rpcUrl),
56
62
  });
57
63
  this.clients.set(config.chainId, client);
58
64
  }
@@ -68,33 +74,64 @@ export class DataService {
68
74
  /**
69
75
  * Get deposit events for a specific chain
70
76
  *
71
- * @param chainId - Chain ID to fetch events from
72
- * @param options - Event filter options including fromBlock, toBlock, and other filters
77
+ * @param pool - Pool info containing chainId, address, and deployment block
73
78
  * @returns Array of deposit events with properly typed fields (bigint for numbers, Hash for commitments)
74
79
  * @throws {DataError} If client is not configured, network error occurs, or event data is invalid
75
80
  */
76
- async getDeposits(
77
- pool: PoolInfo
78
- ): Promise<DepositEvent[]> {
81
+ async getDeposits(pool: PoolInfo): Promise<DepositEvent[]> {
79
82
  try {
80
83
  const client = this.getClientForChain(pool.chainId);
81
- const config = this.getConfigForChain(pool.chainId);
82
-
83
- const logs = await client.getLogs({
84
- address: pool.address,
85
- event: DEPOSIT_EVENT,
86
- fromBlock: pool.deploymentBlock ?? config.startBlock
87
- }).catch(error => {
88
- throw new DataError(
89
- "Failed to fetch deposit logs",
90
- ErrorCode.NETWORK_ERROR,
91
- { error: error instanceof Error ? error.message : "Unknown error" },
92
- );
93
- });
84
+ const chainConfig = this.getConfigForChain(pool.chainId);
85
+ const logConfig = this.getLogFetchConfigForChain(pool.chainId);
86
+
87
+ const fromBlock = pool.deploymentBlock ?? chainConfig.startBlock;
88
+ const toBlock = await this.getCurrentBlock(pool.chainId);
89
+ const ranges = this.generateBlockRanges(
90
+ fromBlock,
91
+ toBlock,
92
+ logConfig.blockChunkSize
93
+ );
94
+
95
+ this.logger.info(
96
+ `Fetching deposits in ${ranges.length} chunks for pool ${pool.address}, chunk size is: ${logConfig.blockChunkSize}`
97
+ );
94
98
 
95
- return logs.map((log) => {
99
+ // Use async.mapLimit for controlled concurrency
100
+ const allLogs = await mapLimit<BlockRange, unknown[]>(
101
+ ranges,
102
+ logConfig.concurrency,
103
+ async (range: BlockRange) => {
104
+ if (logConfig.chunkDelayMs > 0) {
105
+ await this.sleep(logConfig.chunkDelayMs);
106
+ }
107
+ return this.fetchLogsWithRetry(
108
+ client,
109
+ pool.address,
110
+ DEPOSIT_EVENT,
111
+ range,
112
+ logConfig
113
+ );
114
+ }
115
+ );
116
+
117
+ // Flatten and parse results
118
+ const flatLogs = allLogs.flat();
119
+
120
+ return flatLogs.map((log: unknown) => {
96
121
  try {
97
- if (!log.args) {
122
+ const typedLog = log as {
123
+ args?: {
124
+ _depositor?: string;
125
+ _commitment?: bigint;
126
+ _label?: bigint;
127
+ _value?: bigint;
128
+ _merkleRoot?: bigint;
129
+ };
130
+ blockNumber?: bigint;
131
+ transactionHash?: Hex;
132
+ };
133
+
134
+ if (!typedLog.args) {
98
135
  throw DataError.invalidLog("deposit", "missing args");
99
136
  }
100
137
 
@@ -104,9 +141,16 @@ export class DataService {
104
141
  _label: label,
105
142
  _value: value,
106
143
  _merkleRoot: precommitment,
107
- } = log.args;
144
+ } = typedLog.args;
108
145
 
109
- if (!depositor || !commitment || !label || !precommitment || !log.blockNumber || !log.transactionHash) {
146
+ if (
147
+ !depositor ||
148
+ !commitment ||
149
+ !label ||
150
+ !precommitment ||
151
+ !typedLog.blockNumber ||
152
+ !typedLog.transactionHash
153
+ ) {
110
154
  throw DataError.invalidLog("deposit", "missing required fields");
111
155
  }
112
156
 
@@ -116,25 +160,31 @@ export class DataService {
116
160
  label: label as Hash,
117
161
  value: value || BigInt(0),
118
162
  precommitment: precommitment as Hash,
119
- blockNumber: BigInt(log.blockNumber),
120
- transactionHash: log.transactionHash,
163
+ blockNumber: BigInt(typedLog.blockNumber),
164
+ transactionHash: typedLog.transactionHash,
121
165
  };
122
166
  } catch (error) {
123
167
  if (error instanceof DataError) throw error;
124
- throw DataError.invalidLog("deposit", error instanceof Error ? error.message : "Unknown error");
168
+ throw DataError.invalidLog(
169
+ "deposit",
170
+ error instanceof Error ? error.message : "Unknown error"
171
+ );
125
172
  }
126
173
  });
127
174
  } catch (error) {
128
175
  if (error instanceof DataError) throw error;
129
- throw DataError.networkError(pool.chainId, error instanceof Error ? error : new Error(String(error)));
176
+ throw DataError.networkError(
177
+ pool.chainId,
178
+ error instanceof Error ? error : new Error(String(error))
179
+ );
130
180
  }
131
181
  }
132
182
 
133
183
  /**
134
184
  * Get withdrawal events for a specific chain
135
185
  *
136
- * @param chainId - Chain ID to fetch events from
137
- * @param options - Event filter options including fromBlock, toBlock, and other filters
186
+ * @param pool - Pool info containing chainId, address, and deployment block
187
+ * @param fromBlock - Optional starting block (defaults to pool deployment block)
138
188
  * @returns Array of withdrawal events with properly typed fields (bigint for numbers, Hash for commitments)
139
189
  * @throws {DataError} If client is not configured, network error occurs, or event data is invalid
140
190
  */
@@ -144,23 +194,55 @@ export class DataService {
144
194
  ): Promise<WithdrawalEvent[]> {
145
195
  try {
146
196
  const client = this.getClientForChain(pool.chainId);
147
- const config = this.getConfigForChain(pool.chainId);
148
-
149
- const logs = await client.getLogs({
150
- address: pool.address,
151
- event: WITHDRAWAL_EVENT,
152
- fromBlock: fromBlock ?? config.startBlock,
153
- }).catch(error => {
154
- throw new DataError(
155
- "Failed to fetch withdrawal logs",
156
- ErrorCode.NETWORK_ERROR,
157
- { error: error instanceof Error ? error.message : "Unknown error" },
158
- );
159
- });
197
+ const chainConfig = this.getConfigForChain(pool.chainId);
198
+ const logConfig = this.getLogFetchConfigForChain(pool.chainId);
199
+
200
+ const startBlock = fromBlock ?? chainConfig.startBlock;
201
+ const toBlock = await this.getCurrentBlock(pool.chainId);
202
+ const ranges = this.generateBlockRanges(
203
+ startBlock,
204
+ toBlock,
205
+ logConfig.blockChunkSize
206
+ );
160
207
 
161
- return logs.map((log) => {
208
+ this.logger.debug(
209
+ `Fetching withdrawals in ${ranges.length} chunks for pool ${pool.address}`
210
+ );
211
+
212
+ // Use async.mapLimit for controlled concurrency
213
+ const allLogs = await mapLimit<BlockRange, unknown[]>(
214
+ ranges,
215
+ logConfig.concurrency,
216
+ async (range: BlockRange) => {
217
+ if (logConfig.chunkDelayMs > 0) {
218
+ await this.sleep(logConfig.chunkDelayMs);
219
+ }
220
+ return this.fetchLogsWithRetry(
221
+ client,
222
+ pool.address,
223
+ WITHDRAWAL_EVENT,
224
+ range,
225
+ logConfig
226
+ );
227
+ }
228
+ );
229
+
230
+ // Flatten and parse results
231
+ const flatLogs = allLogs.flat();
232
+
233
+ return flatLogs.map((log: unknown) => {
162
234
  try {
163
- if (!log.args) {
235
+ const typedLog = log as {
236
+ args?: {
237
+ _value?: bigint;
238
+ _spentNullifier?: bigint;
239
+ _newCommitment?: bigint;
240
+ };
241
+ blockNumber?: bigint;
242
+ transactionHash?: Hex;
243
+ };
244
+
245
+ if (!typedLog.args) {
164
246
  throw DataError.invalidLog("withdrawal", "missing args");
165
247
  }
166
248
 
@@ -168,9 +250,16 @@ export class DataService {
168
250
  _value: value,
169
251
  _spentNullifier: spentNullifier,
170
252
  _newCommitment: newCommitment,
171
- } = log.args;
253
+ } = typedLog.args;
172
254
 
173
- if (!value || !spentNullifier || !newCommitment || !log.blockNumber || !log.transactionHash) {
255
+ if (
256
+ value === undefined ||
257
+ value === null ||
258
+ !spentNullifier ||
259
+ !newCommitment ||
260
+ !typedLog.blockNumber ||
261
+ !typedLog.transactionHash
262
+ ) {
174
263
  throw DataError.invalidLog("withdrawal", "missing required fields");
175
264
  }
176
265
 
@@ -178,25 +267,31 @@ export class DataService {
178
267
  withdrawn: value,
179
268
  spentNullifier: spentNullifier as Hash,
180
269
  newCommitment: newCommitment as Hash,
181
- blockNumber: BigInt(log.blockNumber),
182
- transactionHash: log.transactionHash,
270
+ blockNumber: BigInt(typedLog.blockNumber),
271
+ transactionHash: typedLog.transactionHash,
183
272
  };
184
273
  } catch (error) {
185
274
  if (error instanceof DataError) throw error;
186
- throw DataError.invalidLog("withdrawal", error instanceof Error ? error.message : "Unknown error");
275
+ throw DataError.invalidLog(
276
+ "withdrawal",
277
+ error instanceof Error ? error.message : "Unknown error"
278
+ );
187
279
  }
188
280
  });
189
281
  } catch (error) {
190
282
  if (error instanceof DataError) throw error;
191
- throw DataError.networkError(pool.chainId, error instanceof Error ? error : new Error(String(error)));
283
+ throw DataError.networkError(
284
+ pool.chainId,
285
+ error instanceof Error ? error : new Error(String(error))
286
+ );
192
287
  }
193
288
  }
194
289
 
195
290
  /**
196
291
  * Get ragequit events for a specific chain
197
292
  *
198
- * @param chainId - Chain ID to fetch events from
199
- * @param options - Event filter options including fromBlock, toBlock, and other filters
293
+ * @param pool - Pool info containing chainId, address, and deployment block
294
+ * @param fromBlock - Optional starting block (defaults to pool deployment block)
200
295
  * @returns Array of ragequit events with properly typed fields (bigint for numbers, Hash for commitments)
201
296
  * @throws {DataError} If client is not configured, network error occurs, or event data is invalid
202
297
  */
@@ -206,23 +301,56 @@ export class DataService {
206
301
  ): Promise<RagequitEvent[]> {
207
302
  try {
208
303
  const client = this.getClientForChain(pool.chainId);
209
- const config = this.getConfigForChain(pool.chainId);
210
-
211
- const logs = await client.getLogs({
212
- address: pool.address,
213
- event: RAGEQUIT_EVENT,
214
- fromBlock: fromBlock ?? config.startBlock,
215
- }).catch(error => {
216
- throw new DataError(
217
- "Failed to fetch ragequit logs",
218
- ErrorCode.NETWORK_ERROR,
219
- { error: error instanceof Error ? error.message : "Unknown error" },
220
- );
221
- });
304
+ const chainConfig = this.getConfigForChain(pool.chainId);
305
+ const logConfig = this.getLogFetchConfigForChain(pool.chainId);
306
+
307
+ const startBlock = fromBlock ?? chainConfig.startBlock;
308
+ const toBlock = await this.getCurrentBlock(pool.chainId);
309
+ const ranges = this.generateBlockRanges(
310
+ startBlock,
311
+ toBlock,
312
+ logConfig.blockChunkSize
313
+ );
314
+
315
+ this.logger.debug(
316
+ `Fetching ragequits in ${ranges.length} chunks for pool ${pool.address}`
317
+ );
318
+
319
+ // Use async.mapLimit for controlled concurrency
320
+ const allLogs = await mapLimit<BlockRange, unknown[]>(
321
+ ranges,
322
+ logConfig.concurrency,
323
+ async (range: BlockRange) => {
324
+ if (logConfig.chunkDelayMs > 0) {
325
+ await this.sleep(logConfig.chunkDelayMs);
326
+ }
327
+ return this.fetchLogsWithRetry(
328
+ client,
329
+ pool.address,
330
+ RAGEQUIT_EVENT,
331
+ range,
332
+ logConfig
333
+ );
334
+ }
335
+ );
336
+
337
+ // Flatten and parse results
338
+ const flatLogs = allLogs.flat();
222
339
 
223
- return logs.map((log) => {
340
+ return flatLogs.map((log: unknown) => {
224
341
  try {
225
- if (!log.args) {
342
+ const typedLog = log as {
343
+ args?: {
344
+ _ragequitter?: string;
345
+ _commitment?: bigint;
346
+ _label?: bigint;
347
+ _value?: bigint;
348
+ };
349
+ blockNumber?: bigint;
350
+ transactionHash?: Hex;
351
+ };
352
+
353
+ if (!typedLog.args) {
226
354
  throw DataError.invalidLog("ragequit", "missing args");
227
355
  }
228
356
 
@@ -231,9 +359,15 @@ export class DataService {
231
359
  _commitment: commitment,
232
360
  _label: label,
233
361
  _value: value,
234
- } = log.args;
362
+ } = typedLog.args;
235
363
 
236
- if (!ragequitter || !commitment || !label || !log.blockNumber || !log.transactionHash) {
364
+ if (
365
+ !ragequitter ||
366
+ !commitment ||
367
+ !label ||
368
+ !typedLog.blockNumber ||
369
+ !typedLog.transactionHash
370
+ ) {
237
371
  throw DataError.invalidLog("ragequit", "missing required fields");
238
372
  }
239
373
 
@@ -242,20 +376,107 @@ export class DataService {
242
376
  commitment: commitment as Hash,
243
377
  label: label as Hash,
244
378
  value: value || BigInt(0),
245
- blockNumber: BigInt(log.blockNumber),
246
- transactionHash: log.transactionHash,
379
+ blockNumber: BigInt(typedLog.blockNumber),
380
+ transactionHash: typedLog.transactionHash,
247
381
  };
248
382
  } catch (error) {
249
383
  if (error instanceof DataError) throw error;
250
- throw DataError.invalidLog("ragequit", error instanceof Error ? error.message : "Unknown error");
384
+ throw DataError.invalidLog(
385
+ "ragequit",
386
+ error instanceof Error ? error.message : "Unknown error"
387
+ );
251
388
  }
252
389
  });
253
390
  } catch (error) {
254
391
  if (error instanceof DataError) throw error;
255
- throw DataError.networkError(pool.chainId, error instanceof Error ? error : new Error(String(error)));
392
+ throw DataError.networkError(
393
+ pool.chainId,
394
+ error instanceof Error ? error : new Error(String(error))
395
+ );
256
396
  }
257
397
  }
258
398
 
399
+ /**
400
+ * Gets the current block number for a chain
401
+ */
402
+ private async getCurrentBlock(chainId: number): Promise<bigint> {
403
+ const client = this.getClientForChain(chainId);
404
+ return client.getBlockNumber();
405
+ }
406
+
407
+ /**
408
+ * Generates block ranges for chunked fetching
409
+ */
410
+ private generateBlockRanges(
411
+ fromBlock: bigint,
412
+ toBlock: bigint,
413
+ chunkSize: number
414
+ ): BlockRange[] {
415
+ const ranges: BlockRange[] = [];
416
+ let current = fromBlock;
417
+
418
+ while (current <= toBlock) {
419
+ const end = current + BigInt(chunkSize) - 1n;
420
+ ranges.push({
421
+ fromBlock: current,
422
+ toBlock: end > toBlock ? toBlock : end,
423
+ });
424
+ current = end + 1n;
425
+ }
426
+
427
+ return ranges;
428
+ }
429
+
430
+ /**
431
+ * Fetches logs for a single block range with retry logic
432
+ */
433
+ private async fetchLogsWithRetry<T>(
434
+ client: PublicClient,
435
+ address: string,
436
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
437
+ event: any,
438
+ range: BlockRange,
439
+ logConfig: LogFetchConfig
440
+ ): Promise<T[]> {
441
+ const maxRetries = logConfig.retryOnFailure
442
+ ? logConfig.maxRetries
443
+ : 0;
444
+ let lastError: Error | undefined;
445
+
446
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
447
+ try {
448
+ const logs = await client.getLogs({
449
+ address: address as `0x${string}`,
450
+ event,
451
+ fromBlock: range.fromBlock,
452
+ toBlock: range.toBlock,
453
+ });
454
+ return logs as T[];
455
+ } catch (error) {
456
+ lastError = error instanceof Error ? error : new Error(String(error));
457
+
458
+ if (attempt < maxRetries) {
459
+ const delay =
460
+ logConfig.retryBaseDelayMs * Math.pow(2, attempt);
461
+ this.logger.warn(
462
+ `Log fetch failed, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`,
463
+ { error: lastError.message, range }
464
+ );
465
+ await this.sleep(delay);
466
+ }
467
+ }
468
+ }
469
+
470
+ throw lastError;
471
+ }
472
+
473
+ /**
474
+ * Helper to add delay between requests
475
+ */
476
+ private sleep(ms: number): Promise<void> {
477
+ return new Promise((resolve) => setTimeout(resolve, ms));
478
+ }
479
+
259
480
  private getClientForChain(chainId: number): PublicClient {
260
481
  const client = this.clients.get(chainId);
261
482
  if (!client) {
@@ -265,10 +486,19 @@ export class DataService {
265
486
  }
266
487
 
267
488
  private getConfigForChain(chainId: number): ChainConfig {
268
- const config = this.chainConfigs.find(c => c.chainId === chainId);
489
+ const config = this.chainConfigs.find((c) => c.chainId === chainId);
269
490
  if (!config) {
270
491
  throw DataError.chainNotConfigured(chainId);
271
492
  }
272
493
  return config;
273
494
  }
495
+
496
+ private getLogFetchConfigForChain(chainId: number): LogFetchConfig {
497
+ const config = this.logFetchConfigs.get(chainId);
498
+ if (!config) {
499
+ // Fallback to default if not found (shouldn't happen if constructor is correct)
500
+ return DEFAULT_LOG_FETCH_CONFIG;
501
+ }
502
+ return config;
503
+ }
274
504
  }
@@ -1,3 +1,4 @@
1
1
  export * from "./commitment.js";
2
2
  export * from "./withdrawal.js";
3
3
  export * from "./keys.js";
4
+ export * from "./rateLimit.js";
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Configuration options for rate-limited log fetching
3
+ */
4
+ export interface LogFetchConfig {
5
+ /**
6
+ * Maximum number of blocks to fetch in a single RPC call.
7
+ * Default: 10000 (typical limit for most RPC providers)
8
+ */
9
+ blockChunkSize: number;
10
+
11
+ /**
12
+ * Maximum number of concurrent log fetch operations.
13
+ * Default: 3
14
+ */
15
+ concurrency: number;
16
+
17
+ /**
18
+ * Delay in milliseconds between chunk requests (for additional throttling).
19
+ * Default: 0 (no delay)
20
+ */
21
+ chunkDelayMs: number;
22
+
23
+ /**
24
+ * Whether to retry failed chunk fetches.
25
+ * Default: true
26
+ */
27
+ retryOnFailure: boolean;
28
+
29
+ /**
30
+ * Maximum number of retries for a failed chunk.
31
+ * Default: 3
32
+ */
33
+ maxRetries: number;
34
+
35
+ /**
36
+ * Base delay for exponential backoff on retries (ms).
37
+ * Default: 1000
38
+ */
39
+ retryBaseDelayMs: number;
40
+ }
41
+
42
+ /**
43
+ * Default log fetch configuration
44
+ */
45
+ export const DEFAULT_LOG_FETCH_CONFIG: LogFetchConfig = {
46
+ blockChunkSize: 10000,
47
+ concurrency: 3,
48
+ chunkDelayMs: 0,
49
+ retryOnFailure: true,
50
+ maxRetries: 3,
51
+ retryBaseDelayMs: 1000,
52
+ };
53
+
54
+ /**
55
+ * Per-chain log fetch configuration map
56
+ * Maps chainId to its specific LogFetchConfig
57
+ */
58
+ export type ChainLogFetchConfig = Map<number, Partial<LogFetchConfig>>;
59
+
60
+ /**
61
+ * Block range for chunked fetching
62
+ */
63
+ export interface BlockRange {
64
+ fromBlock: bigint;
65
+ toBlock: bigint;
66
+ }