@0xbow/privacy-pools-core-sdk 0.0.0-3dpf8pk

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 (117) hide show
  1. package/README.md +73 -0
  2. package/dist/esm/fetchArtifacts.esm-DTr__iP-.js +18 -0
  3. package/dist/esm/fetchArtifacts.esm-DTr__iP-.js.map +1 -0
  4. package/dist/esm/fetchArtifacts.node-BLw8nwbt.js +31 -0
  5. package/dist/esm/fetchArtifacts.node-BLw8nwbt.js.map +1 -0
  6. package/dist/esm/index-Derz3sX9.js +73457 -0
  7. package/dist/esm/index-Derz3sX9.js.map +1 -0
  8. package/dist/esm/index.mjs +7 -0
  9. package/dist/esm/index.mjs.map +1 -0
  10. package/dist/index.d.mts +1326 -0
  11. package/dist/node/fetchArtifacts.esm-ZwE-hqnx.js +35 -0
  12. package/dist/node/fetchArtifacts.esm-ZwE-hqnx.js.map +1 -0
  13. package/dist/node/fetchArtifacts.node-CY8wLnXd.js +48 -0
  14. package/dist/node/fetchArtifacts.node-CY8wLnXd.js.map +1 -0
  15. package/dist/node/index-B804ILXn.js +80507 -0
  16. package/dist/node/index-B804ILXn.js.map +1 -0
  17. package/dist/node/index.mjs +24 -0
  18. package/dist/node/index.mjs.map +1 -0
  19. package/dist/types/abi/ERC20.d.ts +38 -0
  20. package/dist/types/abi/IEntrypoint.d.ts +823 -0
  21. package/dist/types/abi/IPrivacyPool.d.ts +51 -0
  22. package/dist/types/circuits/circuits.impl.d.ts +120 -0
  23. package/dist/types/circuits/circuits.interface.d.ts +129 -0
  24. package/dist/types/circuits/fetchArtifacts.d.ts +1 -0
  25. package/dist/types/circuits/fetchArtifacts.esm.d.ts +1 -0
  26. package/dist/types/circuits/fetchArtifacts.node.d.ts +1 -0
  27. package/dist/types/circuits/index.d.ts +2 -0
  28. package/dist/types/constants.d.ts +2 -0
  29. package/dist/types/core/account.service.d.ts +355 -0
  30. package/dist/types/core/bruteForce.service.d.ts +61 -0
  31. package/dist/types/core/commitment.service.d.ts +30 -0
  32. package/dist/types/core/contracts.service.d.ts +114 -0
  33. package/dist/types/core/data.service.d.ts +72 -0
  34. package/dist/types/core/sdk.d.ts +45 -0
  35. package/dist/types/core/withdrawal.service.d.ts +32 -0
  36. package/dist/types/crypto.d.ts +67 -0
  37. package/dist/types/dirname.helper.d.ts +2 -0
  38. package/dist/types/errors/account.error.d.ts +10 -0
  39. package/dist/types/errors/base.error.d.ts +53 -0
  40. package/dist/types/errors/data.error.d.ts +7 -0
  41. package/dist/types/errors/events.error.d.ts +9 -0
  42. package/dist/types/exceptions/circuitInitialization.exception.d.ts +3 -0
  43. package/dist/types/exceptions/fetchArtifacts.exception.d.ts +3 -0
  44. package/dist/types/exceptions/index.d.ts +4 -0
  45. package/dist/types/exceptions/invalidRpcUrl.exception.d.ts +3 -0
  46. package/dist/types/exceptions/privacyPool.exception.d.ts +13 -0
  47. package/dist/types/external.d.ts +7 -0
  48. package/dist/types/fetchArtifacts.esm-m-j0Hu4b.js +34 -0
  49. package/dist/types/fetchArtifacts.node-CGJMDWIh.js +47 -0
  50. package/dist/types/filename.helper.d.ts +2 -0
  51. package/dist/types/index-B6kM6ceO.js +80520 -0
  52. package/dist/types/index.d.ts +14 -0
  53. package/dist/types/index.js +23 -0
  54. package/dist/types/interfaces/blockchainProvider.interface.d.ts +12 -0
  55. package/dist/types/interfaces/circuits.interface.d.ts +30 -0
  56. package/dist/types/interfaces/contracts.interface.d.ts +35 -0
  57. package/dist/types/interfaces/index.d.ts +1 -0
  58. package/dist/types/internal.d.ts +6 -0
  59. package/dist/types/keys.d.ts +18 -0
  60. package/dist/types/providers/blockchainProvider.d.ts +8 -0
  61. package/dist/types/providers/index.d.ts +1 -0
  62. package/dist/types/types/account.d.ts +31 -0
  63. package/dist/types/types/commitment.d.ts +48 -0
  64. package/dist/types/types/events.d.ts +72 -0
  65. package/dist/types/types/index.d.ts +4 -0
  66. package/dist/types/types/keys.d.ts +5 -0
  67. package/dist/types/types/rateLimit.d.ts +51 -0
  68. package/dist/types/types/withdrawal.d.ts +30 -0
  69. package/dist/types/utils/logger.d.ts +22 -0
  70. package/package.json +84 -0
  71. package/src/abi/ERC20.ts +222 -0
  72. package/src/abi/IEntrypoint.ts +1059 -0
  73. package/src/abi/IPrivacyPool.ts +576 -0
  74. package/src/circuits/circuits.impl.ts +232 -0
  75. package/src/circuits/circuits.interface.ts +166 -0
  76. package/src/circuits/fetchArtifacts.esm.ts +12 -0
  77. package/src/circuits/fetchArtifacts.node.ts +23 -0
  78. package/src/circuits/fetchArtifacts.ts +7 -0
  79. package/src/circuits/index.ts +2 -0
  80. package/src/constants.ts +3 -0
  81. package/src/core/account.service.ts +1343 -0
  82. package/src/core/bruteForce.service.ts +120 -0
  83. package/src/core/commitment.service.ts +84 -0
  84. package/src/core/contracts.service.ts +442 -0
  85. package/src/core/data.service.ts +608 -0
  86. package/src/core/sdk.ts +92 -0
  87. package/src/core/withdrawal.service.ts +126 -0
  88. package/src/crypto.ts +226 -0
  89. package/src/dirname.helper.ts +4 -0
  90. package/src/errors/account.error.ts +49 -0
  91. package/src/errors/base.error.ts +125 -0
  92. package/src/errors/data.error.ts +34 -0
  93. package/src/errors/events.error.ts +38 -0
  94. package/src/exceptions/circuitInitialization.exception.ts +6 -0
  95. package/src/exceptions/fetchArtifacts.exception.ts +7 -0
  96. package/src/exceptions/index.ts +4 -0
  97. package/src/exceptions/invalidRpcUrl.exception.ts +6 -0
  98. package/src/exceptions/privacyPool.exception.ts +19 -0
  99. package/src/external.ts +13 -0
  100. package/src/filename.helper.ts +4 -0
  101. package/src/index.ts +25 -0
  102. package/src/interfaces/blockchainProvider.interface.ts +13 -0
  103. package/src/interfaces/circuits.interface.ts +34 -0
  104. package/src/interfaces/contracts.interface.ts +66 -0
  105. package/src/interfaces/index.ts +1 -0
  106. package/src/internal.ts +6 -0
  107. package/src/keys.ts +42 -0
  108. package/src/providers/blockchainProvider.ts +26 -0
  109. package/src/providers/index.ts +1 -0
  110. package/src/types/account.ts +35 -0
  111. package/src/types/commitment.ts +50 -0
  112. package/src/types/events.ts +82 -0
  113. package/src/types/index.ts +4 -0
  114. package/src/types/keys.ts +6 -0
  115. package/src/types/rateLimit.ts +66 -0
  116. package/src/types/withdrawal.ts +33 -0
  117. package/src/utils/logger.ts +56 -0
@@ -0,0 +1,608 @@
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";
10
+
11
+ // Event signatures from the contract
12
+ const DEPOSIT_EVENT = parseAbiItem('event Deposited(address indexed _depositor, uint256 _commitment, uint256 _label, uint256 _value, uint256 _merkleRoot)');
13
+ const WITHDRAWAL_EVENT = parseAbiItem('event Withdrawn(address indexed _processooor, uint256 _value, uint256 _spentNullifier, uint256 _newCommitment)');
14
+ const RAGEQUIT_EVENT = parseAbiItem('event Ragequit(address indexed _ragequitter, uint256 _commitment, uint256 _label, uint256 _value)');
15
+
16
+ /**
17
+ * Service responsible for fetching and managing privacy pool events across multiple chains.
18
+ * Handles event retrieval, parsing, and validation for deposits, withdrawals, and ragequits.
19
+ *
20
+ * @remarks
21
+ * This service uses viem's PublicClient to efficiently fetch and process blockchain events.
22
+ * It supports multiple chains and provides robust error handling and validation.
23
+ * All uint256 values from events are handled as bigints, with Hash type assertions for commitment-related fields.
24
+ */
25
+ export class DataService {
26
+ private readonly clients: Map<number, PublicClient> = new Map();
27
+ private readonly logger: Logger;
28
+ private readonly logFetchConfigs: Map<number, LogFetchConfig>;
29
+
30
+ /**
31
+ * Initialize the data service with chain configurations
32
+ *
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).
36
+ * @throws {DataError} If client initialization fails for any chain
37
+ */
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
+ }
53
+
54
+ try {
55
+ for (const config of chainConfigs) {
56
+ if (!config.rpcUrl) {
57
+ throw new Error(`Missing RPC URL for chain ${config.chainId}`);
58
+ }
59
+
60
+ const client = createPublicClient({
61
+ transport: http(config.rpcUrl),
62
+ });
63
+ this.clients.set(config.chainId, client);
64
+ }
65
+ } catch (error) {
66
+ throw new DataError(
67
+ "Failed to initialize PublicClient",
68
+ ErrorCode.NETWORK_ERROR,
69
+ { error: error instanceof Error ? error.message : "Unknown error" },
70
+ );
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Get deposit events for a specific chain
76
+ *
77
+ * @param pool - Pool info containing chainId, address, and deployment block
78
+ * @returns Array of deposit events with properly typed fields (bigint for numbers, Hash for commitments)
79
+ * @throws {DataError} If client is not configured, network error occurs, or event data is invalid
80
+ */
81
+ async getDeposits(pool: PoolInfo): Promise<DepositEvent[]> {
82
+ try {
83
+ const client = this.getClientForChain(pool.chainId);
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
+ );
98
+
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
+ this.logger.debug(`[DataService:getDeposits] Raw logs fetched`, {
121
+ poolAddress: pool.address,
122
+ chainId: pool.chainId,
123
+ fromBlock: fromBlock.toString(),
124
+ toBlock: toBlock.toString(),
125
+ chunkCount: ranges.length,
126
+ rawLogCount: flatLogs.length,
127
+ });
128
+
129
+ const parsed = flatLogs.map((log: unknown) => {
130
+ try {
131
+ const typedLog = log as {
132
+ args?: {
133
+ _depositor?: string;
134
+ _commitment?: bigint;
135
+ _label?: bigint;
136
+ _value?: bigint;
137
+ _merkleRoot?: bigint;
138
+ };
139
+ blockNumber?: bigint;
140
+ transactionHash?: Hex;
141
+ };
142
+
143
+ if (!typedLog.args) {
144
+ throw DataError.invalidLog("deposit", "missing args");
145
+ }
146
+
147
+ const {
148
+ _depositor: depositor,
149
+ _commitment: commitment,
150
+ _label: label,
151
+ _value: value,
152
+ _merkleRoot: precommitment,
153
+ } = typedLog.args;
154
+
155
+ if (
156
+ !depositor ||
157
+ !commitment ||
158
+ !label ||
159
+ !precommitment ||
160
+ !typedLog.blockNumber ||
161
+ !typedLog.transactionHash
162
+ ) {
163
+ throw DataError.invalidLog("deposit", "missing required fields");
164
+ }
165
+
166
+ const result = {
167
+ depositor: depositor.toLowerCase(),
168
+ commitment: commitment as Hash,
169
+ label: label as Hash,
170
+ value: value || BigInt(0),
171
+ precommitment: precommitment as Hash,
172
+ blockNumber: BigInt(typedLog.blockNumber),
173
+ transactionHash: typedLog.transactionHash,
174
+ };
175
+
176
+ this.logger.debug(`[DataService:getDeposits] Parsed deposit event`, {
177
+ poolAddress: pool.address,
178
+ chainId: pool.chainId,
179
+ depositor: result.depositor,
180
+ commitment: result.commitment,
181
+ label: result.label,
182
+ value: result.value.toString(),
183
+ precommitment: result.precommitment,
184
+ blockNumber: result.blockNumber.toString(),
185
+ transactionHash: result.transactionHash,
186
+ });
187
+
188
+ return result;
189
+ } catch (error) {
190
+ this.logger.debug(`[DataService:getDeposits] Failed to parse deposit log`, {
191
+ poolAddress: pool.address,
192
+ chainId: pool.chainId,
193
+ error: error instanceof Error ? error.message : String(error),
194
+ });
195
+ if (error instanceof DataError) throw error;
196
+ throw DataError.invalidLog(
197
+ "deposit",
198
+ error instanceof Error ? error.message : "Unknown error"
199
+ );
200
+ }
201
+ });
202
+
203
+ this.logger.debug(`[DataService:getDeposits] Deposit fetch complete`, {
204
+ poolAddress: pool.address,
205
+ chainId: pool.chainId,
206
+ totalParsed: parsed.length,
207
+ });
208
+
209
+ return parsed;
210
+ } catch (error) {
211
+ if (error instanceof DataError) throw error;
212
+ throw DataError.networkError(
213
+ pool.chainId,
214
+ error instanceof Error ? error : new Error(String(error))
215
+ );
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Get withdrawal events for a specific chain
221
+ *
222
+ * @param pool - Pool info containing chainId, address, and deployment block
223
+ * @param fromBlock - Optional starting block (defaults to pool deployment block)
224
+ * @returns Array of withdrawal events with properly typed fields (bigint for numbers, Hash for commitments)
225
+ * @throws {DataError} If client is not configured, network error occurs, or event data is invalid
226
+ */
227
+ async getWithdrawals(
228
+ pool: PoolInfo,
229
+ fromBlock: bigint = pool.deploymentBlock
230
+ ): Promise<WithdrawalEvent[]> {
231
+ try {
232
+ const client = this.getClientForChain(pool.chainId);
233
+ const chainConfig = this.getConfigForChain(pool.chainId);
234
+ const logConfig = this.getLogFetchConfigForChain(pool.chainId);
235
+
236
+ const startBlock = fromBlock ?? chainConfig.startBlock;
237
+ const toBlock = await this.getCurrentBlock(pool.chainId);
238
+ const ranges = this.generateBlockRanges(
239
+ startBlock,
240
+ toBlock,
241
+ logConfig.blockChunkSize
242
+ );
243
+
244
+ this.logger.debug(
245
+ `Fetching withdrawals in ${ranges.length} chunks for pool ${pool.address}`
246
+ );
247
+
248
+ // Use async.mapLimit for controlled concurrency
249
+ const allLogs = await mapLimit<BlockRange, unknown[]>(
250
+ ranges,
251
+ logConfig.concurrency,
252
+ async (range: BlockRange) => {
253
+ if (logConfig.chunkDelayMs > 0) {
254
+ await this.sleep(logConfig.chunkDelayMs);
255
+ }
256
+ return this.fetchLogsWithRetry(
257
+ client,
258
+ pool.address,
259
+ WITHDRAWAL_EVENT,
260
+ range,
261
+ logConfig
262
+ );
263
+ }
264
+ );
265
+
266
+ // Flatten and parse results
267
+ const flatWithdrawalLogs = allLogs.flat();
268
+
269
+ this.logger.debug(`[DataService:getWithdrawals] Raw logs fetched`, {
270
+ poolAddress: pool.address,
271
+ chainId: pool.chainId,
272
+ fromBlock: startBlock.toString(),
273
+ toBlock: toBlock.toString(),
274
+ chunkCount: ranges.length,
275
+ rawLogCount: flatWithdrawalLogs.length,
276
+ });
277
+
278
+ const parsedWithdrawals = flatWithdrawalLogs.map((log: unknown) => {
279
+ try {
280
+ const typedLog = log as {
281
+ args?: {
282
+ _value?: bigint;
283
+ _spentNullifier?: bigint;
284
+ _newCommitment?: bigint;
285
+ };
286
+ blockNumber?: bigint;
287
+ transactionHash?: Hex;
288
+ };
289
+
290
+ if (!typedLog.args) {
291
+ throw DataError.invalidLog("withdrawal", "missing args");
292
+ }
293
+
294
+ const {
295
+ _value: value,
296
+ _spentNullifier: spentNullifier,
297
+ _newCommitment: newCommitment,
298
+ } = typedLog.args;
299
+
300
+ if (
301
+ !value ||
302
+ !spentNullifier ||
303
+ !newCommitment ||
304
+ !typedLog.blockNumber ||
305
+ !typedLog.transactionHash
306
+ ) {
307
+ throw DataError.invalidLog("withdrawal", "missing required fields");
308
+ }
309
+
310
+ const result = {
311
+ withdrawn: value,
312
+ spentNullifier: spentNullifier as Hash,
313
+ newCommitment: newCommitment as Hash,
314
+ blockNumber: BigInt(typedLog.blockNumber),
315
+ transactionHash: typedLog.transactionHash,
316
+ };
317
+
318
+ this.logger.debug(`[DataService:getWithdrawals] Parsed withdrawal event`, {
319
+ poolAddress: pool.address,
320
+ chainId: pool.chainId,
321
+ withdrawn: result.withdrawn.toString(),
322
+ spentNullifier: result.spentNullifier,
323
+ newCommitment: result.newCommitment,
324
+ blockNumber: result.blockNumber.toString(),
325
+ transactionHash: result.transactionHash,
326
+ });
327
+
328
+ return result;
329
+ } catch (error) {
330
+ this.logger.debug(`[DataService:getWithdrawals] Failed to parse withdrawal log`, {
331
+ poolAddress: pool.address,
332
+ chainId: pool.chainId,
333
+ error: error instanceof Error ? error.message : String(error),
334
+ });
335
+ if (error instanceof DataError) throw error;
336
+ throw DataError.invalidLog(
337
+ "withdrawal",
338
+ error instanceof Error ? error.message : "Unknown error"
339
+ );
340
+ }
341
+ });
342
+
343
+ this.logger.debug(`[DataService:getWithdrawals] Withdrawal fetch complete`, {
344
+ poolAddress: pool.address,
345
+ chainId: pool.chainId,
346
+ totalParsed: parsedWithdrawals.length,
347
+ });
348
+
349
+ return parsedWithdrawals;
350
+ } catch (error) {
351
+ if (error instanceof DataError) throw error;
352
+ throw DataError.networkError(
353
+ pool.chainId,
354
+ error instanceof Error ? error : new Error(String(error))
355
+ );
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Get ragequit events for a specific chain
361
+ *
362
+ * @param pool - Pool info containing chainId, address, and deployment block
363
+ * @param fromBlock - Optional starting block (defaults to pool deployment block)
364
+ * @returns Array of ragequit events with properly typed fields (bigint for numbers, Hash for commitments)
365
+ * @throws {DataError} If client is not configured, network error occurs, or event data is invalid
366
+ */
367
+ async getRagequits(
368
+ pool: PoolInfo,
369
+ fromBlock: bigint = pool.deploymentBlock
370
+ ): Promise<RagequitEvent[]> {
371
+ try {
372
+ const client = this.getClientForChain(pool.chainId);
373
+ const chainConfig = this.getConfigForChain(pool.chainId);
374
+ const logConfig = this.getLogFetchConfigForChain(pool.chainId);
375
+
376
+ const startBlock = fromBlock ?? chainConfig.startBlock;
377
+ const toBlock = await this.getCurrentBlock(pool.chainId);
378
+ const ranges = this.generateBlockRanges(
379
+ startBlock,
380
+ toBlock,
381
+ logConfig.blockChunkSize
382
+ );
383
+
384
+ this.logger.debug(
385
+ `Fetching ragequits in ${ranges.length} chunks for pool ${pool.address}`
386
+ );
387
+
388
+ // Use async.mapLimit for controlled concurrency
389
+ const allLogs = await mapLimit<BlockRange, unknown[]>(
390
+ ranges,
391
+ logConfig.concurrency,
392
+ async (range: BlockRange) => {
393
+ if (logConfig.chunkDelayMs > 0) {
394
+ await this.sleep(logConfig.chunkDelayMs);
395
+ }
396
+ return this.fetchLogsWithRetry(
397
+ client,
398
+ pool.address,
399
+ RAGEQUIT_EVENT,
400
+ range,
401
+ logConfig
402
+ );
403
+ }
404
+ );
405
+
406
+ // Flatten and parse results
407
+ const flatRagequitLogs = allLogs.flat();
408
+
409
+ this.logger.debug(`[DataService:getRagequits] Raw logs fetched`, {
410
+ poolAddress: pool.address,
411
+ chainId: pool.chainId,
412
+ fromBlock: startBlock.toString(),
413
+ toBlock: toBlock.toString(),
414
+ chunkCount: ranges.length,
415
+ rawLogCount: flatRagequitLogs.length,
416
+ });
417
+
418
+ const parsedRagequits = flatRagequitLogs.map((log: unknown) => {
419
+ try {
420
+ const typedLog = log as {
421
+ args?: {
422
+ _ragequitter?: string;
423
+ _commitment?: bigint;
424
+ _label?: bigint;
425
+ _value?: bigint;
426
+ };
427
+ blockNumber?: bigint;
428
+ transactionHash?: Hex;
429
+ };
430
+
431
+ if (!typedLog.args) {
432
+ throw DataError.invalidLog("ragequit", "missing args");
433
+ }
434
+
435
+ const {
436
+ _ragequitter: ragequitter,
437
+ _commitment: commitment,
438
+ _label: label,
439
+ _value: value,
440
+ } = typedLog.args;
441
+
442
+ if (
443
+ !ragequitter ||
444
+ !commitment ||
445
+ !label ||
446
+ !typedLog.blockNumber ||
447
+ !typedLog.transactionHash
448
+ ) {
449
+ throw DataError.invalidLog("ragequit", "missing required fields");
450
+ }
451
+
452
+ const result = {
453
+ ragequitter: ragequitter.toLowerCase(),
454
+ commitment: commitment as Hash,
455
+ label: label as Hash,
456
+ value: value || BigInt(0),
457
+ blockNumber: BigInt(typedLog.blockNumber),
458
+ transactionHash: typedLog.transactionHash,
459
+ };
460
+
461
+ this.logger.debug(`[DataService:getRagequits] Parsed ragequit event`, {
462
+ poolAddress: pool.address,
463
+ chainId: pool.chainId,
464
+ ragequitter: result.ragequitter,
465
+ commitment: result.commitment,
466
+ label: result.label,
467
+ value: result.value.toString(),
468
+ blockNumber: result.blockNumber.toString(),
469
+ transactionHash: result.transactionHash,
470
+ });
471
+
472
+ return result;
473
+ } catch (error) {
474
+ this.logger.debug(`[DataService:getRagequits] Failed to parse ragequit log`, {
475
+ poolAddress: pool.address,
476
+ chainId: pool.chainId,
477
+ error: error instanceof Error ? error.message : String(error),
478
+ });
479
+ if (error instanceof DataError) throw error;
480
+ throw DataError.invalidLog(
481
+ "ragequit",
482
+ error instanceof Error ? error.message : "Unknown error"
483
+ );
484
+ }
485
+ });
486
+
487
+ this.logger.debug(`[DataService:getRagequits] Ragequit fetch complete`, {
488
+ poolAddress: pool.address,
489
+ chainId: pool.chainId,
490
+ totalParsed: parsedRagequits.length,
491
+ });
492
+
493
+ return parsedRagequits;
494
+ } catch (error) {
495
+ if (error instanceof DataError) throw error;
496
+ throw DataError.networkError(
497
+ pool.chainId,
498
+ error instanceof Error ? error : new Error(String(error))
499
+ );
500
+ }
501
+ }
502
+
503
+ /**
504
+ * Gets the current block number for a chain
505
+ */
506
+ private async getCurrentBlock(chainId: number): Promise<bigint> {
507
+ const client = this.getClientForChain(chainId);
508
+ return client.getBlockNumber();
509
+ }
510
+
511
+ /**
512
+ * Generates block ranges for chunked fetching
513
+ */
514
+ private generateBlockRanges(
515
+ fromBlock: bigint,
516
+ toBlock: bigint,
517
+ chunkSize: number
518
+ ): BlockRange[] {
519
+ const ranges: BlockRange[] = [];
520
+ let current = fromBlock;
521
+
522
+ while (current <= toBlock) {
523
+ const end = current + BigInt(chunkSize) - 1n;
524
+ ranges.push({
525
+ fromBlock: current,
526
+ toBlock: end > toBlock ? toBlock : end,
527
+ });
528
+ current = end + 1n;
529
+ }
530
+
531
+ return ranges;
532
+ }
533
+
534
+ /**
535
+ * Fetches logs for a single block range with retry logic
536
+ */
537
+ private async fetchLogsWithRetry<T>(
538
+ client: PublicClient,
539
+ address: string,
540
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
541
+ event: any,
542
+ range: BlockRange,
543
+ logConfig: LogFetchConfig
544
+ ): Promise<T[]> {
545
+ const maxRetries = logConfig.retryOnFailure
546
+ ? logConfig.maxRetries
547
+ : 0;
548
+ let lastError: Error | undefined;
549
+
550
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
551
+ try {
552
+ const logs = await client.getLogs({
553
+ address: address as `0x${string}`,
554
+ event,
555
+ fromBlock: range.fromBlock,
556
+ toBlock: range.toBlock,
557
+ });
558
+ return logs as T[];
559
+ } catch (error) {
560
+ lastError = error instanceof Error ? error : new Error(String(error));
561
+
562
+ if (attempt < maxRetries) {
563
+ const delay =
564
+ logConfig.retryBaseDelayMs * Math.pow(2, attempt);
565
+ this.logger.warn(
566
+ `Log fetch failed, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`,
567
+ { error: lastError.message, range }
568
+ );
569
+ await this.sleep(delay);
570
+ }
571
+ }
572
+ }
573
+
574
+ throw lastError;
575
+ }
576
+
577
+ /**
578
+ * Helper to add delay between requests
579
+ */
580
+ private sleep(ms: number): Promise<void> {
581
+ return new Promise((resolve) => setTimeout(resolve, ms));
582
+ }
583
+
584
+ private getClientForChain(chainId: number): PublicClient {
585
+ const client = this.clients.get(chainId);
586
+ if (!client) {
587
+ throw DataError.chainNotConfigured(chainId);
588
+ }
589
+ return client;
590
+ }
591
+
592
+ private getConfigForChain(chainId: number): ChainConfig {
593
+ const config = this.chainConfigs.find((c) => c.chainId === chainId);
594
+ if (!config) {
595
+ throw DataError.chainNotConfigured(chainId);
596
+ }
597
+ return config;
598
+ }
599
+
600
+ private getLogFetchConfigForChain(chainId: number): LogFetchConfig {
601
+ const config = this.logFetchConfigs.get(chainId);
602
+ if (!config) {
603
+ // Fallback to default if not found (shouldn't happen if constructor is correct)
604
+ return DEFAULT_LOG_FETCH_CONFIG;
605
+ }
606
+ return config;
607
+ }
608
+ }
@@ -0,0 +1,92 @@
1
+ import { CommitmentService } from "./commitment.service.js";
2
+ import { WithdrawalService } from "./withdrawal.service.js";
3
+ import { CircuitsInterface } from "../interfaces/circuits.interface.js";
4
+ import { Commitment, CommitmentProof } from "../types/commitment.js";
5
+ import { WithdrawalProof, WithdrawalProofInput } from "../types/withdrawal.js";
6
+ import { ContractInteractionsService } from "./contracts.service.js";
7
+ import { Hex, Address, Chain } from "viem";
8
+ import { AccountCommitment } from "../types/account.js";
9
+
10
+ /**
11
+ * Main SDK class providing access to all privacy pool functionality.
12
+ * Uses Poseidon hash for all commitment operations.
13
+ */
14
+ export class PrivacyPoolSDK {
15
+ private readonly commitmentService: CommitmentService;
16
+ private readonly withdrawalService: WithdrawalService;
17
+
18
+ constructor(circuits: CircuitsInterface) {
19
+ this.commitmentService = new CommitmentService(circuits);
20
+ this.withdrawalService = new WithdrawalService(circuits);
21
+ }
22
+
23
+ public createContractInstance(
24
+ rpcUrl: string,
25
+ chain: Chain,
26
+ entrypointAddress: Address,
27
+ privateKey: Hex,
28
+ ): ContractInteractionsService {
29
+ return new ContractInteractionsService(
30
+ rpcUrl,
31
+ chain,
32
+ entrypointAddress,
33
+ privateKey,
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Generates a commitment proof.
39
+ *
40
+ * @param value - Value to commit
41
+ * @param label - Label for the commitment
42
+ * @param nullifier - Nullifier for the commitment
43
+ * @param secret - Secret for the commitment
44
+ */
45
+ public async proveCommitment(
46
+ value: bigint,
47
+ label: bigint,
48
+ nullifier: bigint,
49
+ secret: bigint,
50
+ ): Promise<CommitmentProof> {
51
+ return this.commitmentService.proveCommitment(
52
+ value,
53
+ label,
54
+ nullifier,
55
+ secret,
56
+ );
57
+ }
58
+
59
+ /**
60
+ * Verifies a commitment proof.
61
+ *
62
+ * @param proof - The proof to verify
63
+ */
64
+ public async verifyCommitment(proof: CommitmentProof): Promise<boolean> {
65
+ return this.commitmentService.verifyCommitment(proof);
66
+ }
67
+
68
+ /**
69
+ * Generates a withdrawal proof.
70
+ *
71
+ * @param commitment - Commitment to withdraw
72
+ * @param input - Input parameters for the withdrawal
73
+ * @param withdrawal - Withdrawal details
74
+ */
75
+ public async proveWithdrawal(
76
+ commitment: Commitment | AccountCommitment ,
77
+ input: WithdrawalProofInput,
78
+ ): Promise<WithdrawalProof> {
79
+ return await this.withdrawalService.proveWithdrawal(commitment, input);
80
+ }
81
+
82
+ /**
83
+ * Verifies a withdrawal proof.
84
+ *
85
+ * @param withdrawalProof - The withdrawal payload to verify
86
+ */
87
+ public async verifyWithdrawal(
88
+ withdrawalProof: WithdrawalProof,
89
+ ): Promise<boolean> {
90
+ return this.withdrawalService.verifyWithdrawal(withdrawalProof);
91
+ }
92
+ }