@0xbow/privacy-pools-core-sdk 0.0.0-83f8c2

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-C_DNv-_D.js +18 -0
  3. package/dist/esm/fetchArtifacts.esm-C_DNv-_D.js.map +1 -0
  4. package/dist/esm/fetchArtifacts.node-BBPNjgiT.js +31 -0
  5. package/dist/esm/fetchArtifacts.node-BBPNjgiT.js.map +1 -0
  6. package/dist/esm/index-BxzIe_IR.js +70381 -0
  7. package/dist/esm/index-BxzIe_IR.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 +1224 -0
  11. package/dist/node/fetchArtifacts.esm-l-EDo53-.js +35 -0
  12. package/dist/node/fetchArtifacts.esm-l-EDo53-.js.map +1 -0
  13. package/dist/node/fetchArtifacts.node-DYwQHSF4.js +48 -0
  14. package/dist/node/fetchArtifacts.node-DYwQHSF4.js.map +1 -0
  15. package/dist/node/index-DGsIfUGw.js +77431 -0
  16. package/dist/node/index-DGsIfUGw.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 +347 -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 +52 -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-IMTIZwq7.js +34 -0
  49. package/dist/types/fetchArtifacts.node-BcXsBNCT.js +47 -0
  50. package/dist/types/filename.helper.d.ts +2 -0
  51. package/dist/types/index-DbuAhDci.js +77444 -0
  52. package/dist/types/index.d.ts +13 -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 +3 -0
  66. package/dist/types/types/keys.d.ts +5 -0
  67. package/dist/types/types/withdrawal.d.ts +30 -0
  68. package/dist/types/utils/concurrency.d.ts +15 -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 +1093 -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 +492 -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 +21 -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 +60 -0
  116. package/src/types/withdrawal.ts +33 -0
  117. package/src/utils/logger.ts +56 -0
@@ -0,0 +1,492 @@
1
+ import {
2
+ type PublicClient,
3
+ type Hex,
4
+ createPublicClient,
5
+ http,
6
+ parseAbiItem,
7
+ } from "viem";
8
+ import { mapLimit } from "async";
9
+ import {
10
+ ChainConfig,
11
+ DepositEvent,
12
+ WithdrawalEvent,
13
+ RagequitEvent,
14
+ } from "../types/events.js";
15
+ import { PoolInfo } from "../types/account.js";
16
+ import { Hash } from "../types/commitment.js";
17
+ import {
18
+ LogFetchConfig,
19
+ DEFAULT_LOG_FETCH_CONFIG,
20
+ BlockRange,
21
+ } from "../types/rateLimit.js";
22
+ import { Logger } from "../utils/logger.js";
23
+ import { DataError } from "../errors/data.error.js";
24
+ import { ErrorCode } from "../errors/base.error.js";
25
+
26
+ // Event signatures from the contract
27
+ const DEPOSIT_EVENT = parseAbiItem('event Deposited(address indexed _depositor, uint256 _commitment, uint256 _label, uint256 _value, uint256 _merkleRoot)');
28
+ const WITHDRAWAL_EVENT = parseAbiItem('event Withdrawn(address indexed _processooor, uint256 _value, uint256 _spentNullifier, uint256 _newCommitment)');
29
+ const RAGEQUIT_EVENT = parseAbiItem('event Ragequit(address indexed _ragequitter, uint256 _commitment, uint256 _label, uint256 _value)');
30
+
31
+ /**
32
+ * Service responsible for fetching and managing privacy pool events across multiple chains.
33
+ * Handles event retrieval, parsing, and validation for deposits, withdrawals, and ragequits.
34
+ *
35
+ * @remarks
36
+ * This service uses viem's PublicClient to efficiently fetch and process blockchain events.
37
+ * It supports multiple chains and provides robust error handling and validation.
38
+ * All uint256 values from events are handled as bigints, with Hash type assertions for commitment-related fields.
39
+ */
40
+ export class DataService {
41
+ private readonly clients: Map<number, PublicClient> = new Map();
42
+ private readonly logger: Logger;
43
+ private readonly logFetchConfig: LogFetchConfig;
44
+
45
+ /**
46
+ * Initialize the data service with chain configurations
47
+ *
48
+ * @param chainConfigs - Array of chain configurations containing chainId, RPC URL, and API key
49
+ * @param logFetchConfig - Optional configuration for rate-limited log fetching
50
+ * @throws {DataError} If client initialization fails for any chain
51
+ */
52
+ constructor(
53
+ private readonly chainConfigs: ChainConfig[],
54
+ logFetchConfig: Partial<LogFetchConfig> = {}
55
+ ) {
56
+ this.logger = new Logger({ prefix: "Data" });
57
+ this.logFetchConfig = { ...DEFAULT_LOG_FETCH_CONFIG, ...logFetchConfig };
58
+
59
+ try {
60
+ for (const config of chainConfigs) {
61
+ if (!config.rpcUrl) {
62
+ throw new Error(`Missing RPC URL for chain ${config.chainId}`);
63
+ }
64
+
65
+ const client = createPublicClient({
66
+ transport: http(config.rpcUrl),
67
+ });
68
+ this.clients.set(config.chainId, client);
69
+ }
70
+ } catch (error) {
71
+ throw new DataError(
72
+ "Failed to initialize PublicClient",
73
+ ErrorCode.NETWORK_ERROR,
74
+ { error: error instanceof Error ? error.message : "Unknown error" },
75
+ );
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Get deposit events for a specific chain
81
+ *
82
+ * @param pool - Pool info containing chainId, address, and deployment block
83
+ * @returns Array of deposit events with properly typed fields (bigint for numbers, Hash for commitments)
84
+ * @throws {DataError} If client is not configured, network error occurs, or event data is invalid
85
+ */
86
+ async getDeposits(pool: PoolInfo): Promise<DepositEvent[]> {
87
+ try {
88
+ const client = this.getClientForChain(pool.chainId);
89
+ const config = this.getConfigForChain(pool.chainId);
90
+
91
+ const fromBlock = pool.deploymentBlock ?? config.startBlock;
92
+ const toBlock = await this.getCurrentBlock(pool.chainId);
93
+ const ranges = this.generateBlockRanges(
94
+ fromBlock,
95
+ toBlock,
96
+ this.logFetchConfig.blockChunkSize
97
+ );
98
+
99
+ this.logger.debug(
100
+ `Fetching deposits in ${ranges.length} chunks for pool ${pool.address}`
101
+ );
102
+
103
+ // Use async.mapLimit for controlled concurrency
104
+ const allLogs = await mapLimit<BlockRange, unknown[]>(
105
+ ranges,
106
+ this.logFetchConfig.concurrency,
107
+ async (range: BlockRange) => {
108
+ if (this.logFetchConfig.chunkDelayMs > 0) {
109
+ await this.sleep(this.logFetchConfig.chunkDelayMs);
110
+ }
111
+ return this.fetchLogsWithRetry(
112
+ client,
113
+ pool.address,
114
+ DEPOSIT_EVENT,
115
+ range
116
+ );
117
+ }
118
+ );
119
+
120
+ // Flatten and parse results
121
+ const flatLogs = allLogs.flat();
122
+
123
+ return flatLogs.map((log: unknown) => {
124
+ try {
125
+ const typedLog = log as {
126
+ args?: {
127
+ _depositor?: string;
128
+ _commitment?: bigint;
129
+ _label?: bigint;
130
+ _value?: bigint;
131
+ _merkleRoot?: bigint;
132
+ };
133
+ blockNumber?: bigint;
134
+ transactionHash?: Hex;
135
+ };
136
+
137
+ if (!typedLog.args) {
138
+ throw DataError.invalidLog("deposit", "missing args");
139
+ }
140
+
141
+ const {
142
+ _depositor: depositor,
143
+ _commitment: commitment,
144
+ _label: label,
145
+ _value: value,
146
+ _merkleRoot: precommitment,
147
+ } = typedLog.args;
148
+
149
+ if (
150
+ !depositor ||
151
+ !commitment ||
152
+ !label ||
153
+ !precommitment ||
154
+ !typedLog.blockNumber ||
155
+ !typedLog.transactionHash
156
+ ) {
157
+ throw DataError.invalidLog("deposit", "missing required fields");
158
+ }
159
+
160
+ return {
161
+ depositor: depositor.toLowerCase(),
162
+ commitment: commitment as Hash,
163
+ label: label as Hash,
164
+ value: value || BigInt(0),
165
+ precommitment: precommitment as Hash,
166
+ blockNumber: BigInt(typedLog.blockNumber),
167
+ transactionHash: typedLog.transactionHash,
168
+ };
169
+ } catch (error) {
170
+ if (error instanceof DataError) throw error;
171
+ throw DataError.invalidLog(
172
+ "deposit",
173
+ error instanceof Error ? error.message : "Unknown error"
174
+ );
175
+ }
176
+ });
177
+ } catch (error) {
178
+ if (error instanceof DataError) throw error;
179
+ throw DataError.networkError(
180
+ pool.chainId,
181
+ error instanceof Error ? error : new Error(String(error))
182
+ );
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Get withdrawal events for a specific chain
188
+ *
189
+ * @param pool - Pool info containing chainId, address, and deployment block
190
+ * @param fromBlock - Optional starting block (defaults to pool deployment block)
191
+ * @returns Array of withdrawal events with properly typed fields (bigint for numbers, Hash for commitments)
192
+ * @throws {DataError} If client is not configured, network error occurs, or event data is invalid
193
+ */
194
+ async getWithdrawals(
195
+ pool: PoolInfo,
196
+ fromBlock: bigint = pool.deploymentBlock
197
+ ): Promise<WithdrawalEvent[]> {
198
+ try {
199
+ const client = this.getClientForChain(pool.chainId);
200
+ const config = this.getConfigForChain(pool.chainId);
201
+
202
+ const startBlock = fromBlock ?? config.startBlock;
203
+ const toBlock = await this.getCurrentBlock(pool.chainId);
204
+ const ranges = this.generateBlockRanges(
205
+ startBlock,
206
+ toBlock,
207
+ this.logFetchConfig.blockChunkSize
208
+ );
209
+
210
+ this.logger.debug(
211
+ `Fetching withdrawals in ${ranges.length} chunks for pool ${pool.address}`
212
+ );
213
+
214
+ // Use async.mapLimit for controlled concurrency
215
+ const allLogs = await mapLimit<BlockRange, unknown[]>(
216
+ ranges,
217
+ this.logFetchConfig.concurrency,
218
+ async (range: BlockRange) => {
219
+ if (this.logFetchConfig.chunkDelayMs > 0) {
220
+ await this.sleep(this.logFetchConfig.chunkDelayMs);
221
+ }
222
+ return this.fetchLogsWithRetry(
223
+ client,
224
+ pool.address,
225
+ WITHDRAWAL_EVENT,
226
+ range
227
+ );
228
+ }
229
+ );
230
+
231
+ // Flatten and parse results
232
+ const flatLogs = allLogs.flat();
233
+
234
+ return flatLogs.map((log: unknown) => {
235
+ try {
236
+ const typedLog = log as {
237
+ args?: {
238
+ _value?: bigint;
239
+ _spentNullifier?: bigint;
240
+ _newCommitment?: bigint;
241
+ };
242
+ blockNumber?: bigint;
243
+ transactionHash?: Hex;
244
+ };
245
+
246
+ if (!typedLog.args) {
247
+ throw DataError.invalidLog("withdrawal", "missing args");
248
+ }
249
+
250
+ const {
251
+ _value: value,
252
+ _spentNullifier: spentNullifier,
253
+ _newCommitment: newCommitment,
254
+ } = typedLog.args;
255
+
256
+ if (
257
+ !value ||
258
+ !spentNullifier ||
259
+ !newCommitment ||
260
+ !typedLog.blockNumber ||
261
+ !typedLog.transactionHash
262
+ ) {
263
+ throw DataError.invalidLog("withdrawal", "missing required fields");
264
+ }
265
+
266
+ return {
267
+ withdrawn: value,
268
+ spentNullifier: spentNullifier as Hash,
269
+ newCommitment: newCommitment as Hash,
270
+ blockNumber: BigInt(typedLog.blockNumber),
271
+ transactionHash: typedLog.transactionHash,
272
+ };
273
+ } catch (error) {
274
+ if (error instanceof DataError) throw error;
275
+ throw DataError.invalidLog(
276
+ "withdrawal",
277
+ error instanceof Error ? error.message : "Unknown error"
278
+ );
279
+ }
280
+ });
281
+ } catch (error) {
282
+ if (error instanceof DataError) throw error;
283
+ throw DataError.networkError(
284
+ pool.chainId,
285
+ error instanceof Error ? error : new Error(String(error))
286
+ );
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Get ragequit events for a specific chain
292
+ *
293
+ * @param pool - Pool info containing chainId, address, and deployment block
294
+ * @param fromBlock - Optional starting block (defaults to pool deployment block)
295
+ * @returns Array of ragequit events with properly typed fields (bigint for numbers, Hash for commitments)
296
+ * @throws {DataError} If client is not configured, network error occurs, or event data is invalid
297
+ */
298
+ async getRagequits(
299
+ pool: PoolInfo,
300
+ fromBlock: bigint = pool.deploymentBlock
301
+ ): Promise<RagequitEvent[]> {
302
+ try {
303
+ const client = this.getClientForChain(pool.chainId);
304
+ const config = this.getConfigForChain(pool.chainId);
305
+
306
+ const startBlock = fromBlock ?? config.startBlock;
307
+ const toBlock = await this.getCurrentBlock(pool.chainId);
308
+ const ranges = this.generateBlockRanges(
309
+ startBlock,
310
+ toBlock,
311
+ this.logFetchConfig.blockChunkSize
312
+ );
313
+
314
+ this.logger.debug(
315
+ `Fetching ragequits in ${ranges.length} chunks for pool ${pool.address}`
316
+ );
317
+
318
+ // Use async.mapLimit for controlled concurrency
319
+ const allLogs = await mapLimit<BlockRange, unknown[]>(
320
+ ranges,
321
+ this.logFetchConfig.concurrency,
322
+ async (range: BlockRange) => {
323
+ if (this.logFetchConfig.chunkDelayMs > 0) {
324
+ await this.sleep(this.logFetchConfig.chunkDelayMs);
325
+ }
326
+ return this.fetchLogsWithRetry(
327
+ client,
328
+ pool.address,
329
+ RAGEQUIT_EVENT,
330
+ range
331
+ );
332
+ }
333
+ );
334
+
335
+ // Flatten and parse results
336
+ const flatLogs = allLogs.flat();
337
+
338
+ return flatLogs.map((log: unknown) => {
339
+ try {
340
+ const typedLog = log as {
341
+ args?: {
342
+ _ragequitter?: string;
343
+ _commitment?: bigint;
344
+ _label?: bigint;
345
+ _value?: bigint;
346
+ };
347
+ blockNumber?: bigint;
348
+ transactionHash?: Hex;
349
+ };
350
+
351
+ if (!typedLog.args) {
352
+ throw DataError.invalidLog("ragequit", "missing args");
353
+ }
354
+
355
+ const {
356
+ _ragequitter: ragequitter,
357
+ _commitment: commitment,
358
+ _label: label,
359
+ _value: value,
360
+ } = typedLog.args;
361
+
362
+ if (
363
+ !ragequitter ||
364
+ !commitment ||
365
+ !label ||
366
+ !typedLog.blockNumber ||
367
+ !typedLog.transactionHash
368
+ ) {
369
+ throw DataError.invalidLog("ragequit", "missing required fields");
370
+ }
371
+
372
+ return {
373
+ ragequitter: ragequitter.toLowerCase(),
374
+ commitment: commitment as Hash,
375
+ label: label as Hash,
376
+ value: value || BigInt(0),
377
+ blockNumber: BigInt(typedLog.blockNumber),
378
+ transactionHash: typedLog.transactionHash,
379
+ };
380
+ } catch (error) {
381
+ if (error instanceof DataError) throw error;
382
+ throw DataError.invalidLog(
383
+ "ragequit",
384
+ error instanceof Error ? error.message : "Unknown error"
385
+ );
386
+ }
387
+ });
388
+ } catch (error) {
389
+ if (error instanceof DataError) throw error;
390
+ throw DataError.networkError(
391
+ pool.chainId,
392
+ error instanceof Error ? error : new Error(String(error))
393
+ );
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Gets the current block number for a chain
399
+ */
400
+ private async getCurrentBlock(chainId: number): Promise<bigint> {
401
+ const client = this.getClientForChain(chainId);
402
+ return client.getBlockNumber();
403
+ }
404
+
405
+ /**
406
+ * Generates block ranges for chunked fetching
407
+ */
408
+ private generateBlockRanges(
409
+ fromBlock: bigint,
410
+ toBlock: bigint,
411
+ chunkSize: number
412
+ ): BlockRange[] {
413
+ const ranges: BlockRange[] = [];
414
+ let current = fromBlock;
415
+
416
+ while (current <= toBlock) {
417
+ const end = current + BigInt(chunkSize) - 1n;
418
+ ranges.push({
419
+ fromBlock: current,
420
+ toBlock: end > toBlock ? toBlock : end,
421
+ });
422
+ current = end + 1n;
423
+ }
424
+
425
+ return ranges;
426
+ }
427
+
428
+ /**
429
+ * Fetches logs for a single block range with retry logic
430
+ */
431
+ private async fetchLogsWithRetry<T>(
432
+ client: PublicClient,
433
+ address: string,
434
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
435
+ event: any,
436
+ range: BlockRange
437
+ ): Promise<T[]> {
438
+ const maxRetries = this.logFetchConfig.retryOnFailure
439
+ ? this.logFetchConfig.maxRetries
440
+ : 0;
441
+ let lastError: Error | undefined;
442
+
443
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
444
+ try {
445
+ const logs = await client.getLogs({
446
+ address: address as `0x${string}`,
447
+ event,
448
+ fromBlock: range.fromBlock,
449
+ toBlock: range.toBlock,
450
+ });
451
+ return logs as T[];
452
+ } catch (error) {
453
+ lastError = error instanceof Error ? error : new Error(String(error));
454
+
455
+ if (attempt < maxRetries) {
456
+ const delay =
457
+ this.logFetchConfig.retryBaseDelayMs * Math.pow(2, attempt);
458
+ this.logger.warn(
459
+ `Log fetch failed, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`,
460
+ { error: lastError.message, range }
461
+ );
462
+ await this.sleep(delay);
463
+ }
464
+ }
465
+ }
466
+
467
+ throw lastError;
468
+ }
469
+
470
+ /**
471
+ * Helper to add delay between requests
472
+ */
473
+ private sleep(ms: number): Promise<void> {
474
+ return new Promise((resolve) => setTimeout(resolve, ms));
475
+ }
476
+
477
+ private getClientForChain(chainId: number): PublicClient {
478
+ const client = this.clients.get(chainId);
479
+ if (!client) {
480
+ throw DataError.chainNotConfigured(chainId);
481
+ }
482
+ return client;
483
+ }
484
+
485
+ private getConfigForChain(chainId: number): ChainConfig {
486
+ const config = this.chainConfigs.find((c) => c.chainId === chainId);
487
+ if (!config) {
488
+ throw DataError.chainNotConfigured(chainId);
489
+ }
490
+ return config;
491
+ }
492
+ }
@@ -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
+ }
@@ -0,0 +1,126 @@
1
+ import * as snarkjs from "snarkjs";
2
+ import { ProofError } from "../errors/base.error.js";
3
+ import {
4
+ CircuitName,
5
+ CircuitsInterface,
6
+ } from "../interfaces/circuits.interface.js";
7
+ import { WithdrawalProof, WithdrawalProofInput } from "../types/withdrawal.js";
8
+ import { AccountCommitment, Commitment } from "../index.js";
9
+
10
+ /**
11
+ * Service responsible for handling withdrawal-related operations.
12
+ */
13
+ export class WithdrawalService {
14
+ constructor(private readonly circuits: CircuitsInterface) {}
15
+
16
+ /**
17
+ * Generates a withdrawal proof.
18
+ *
19
+ * @param commitment - Commitment to withdraw
20
+ * @param input - Input parameters for the withdrawal
21
+ * @param withdrawal - Withdrawal details
22
+ * @returns Promise resolving to withdrawal payload
23
+ * @throws {ProofError} If proof generation fails
24
+ */
25
+ public async proveWithdrawal(
26
+ commitment: Commitment | AccountCommitment,
27
+ input: WithdrawalProofInput
28
+ ): Promise<WithdrawalProof> {
29
+ try {
30
+ const inputSignals = this.prepareInputSignals(commitment, input);
31
+
32
+ const wasm = await this.circuits.getWasm(CircuitName.Withdraw);
33
+ const zkey = await this.circuits.getProvingKey(CircuitName.Withdraw);
34
+
35
+ const { proof, publicSignals } = await snarkjs.groth16.fullProve(
36
+ inputSignals,
37
+ wasm,
38
+ zkey,
39
+ );
40
+
41
+ return {
42
+ proof,
43
+ publicSignals,
44
+ };
45
+ } catch (error) {
46
+ throw ProofError.generationFailed({
47
+ error: error instanceof Error ? error.message : "Unknown error",
48
+ });
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Verifies a withdrawal proof.
54
+ *
55
+ * @param withdrawalPayload - The withdrawal payload to verify
56
+ * @returns Promise resolving to boolean indicating proof validity
57
+ * @throws {ProofError} If verification fails
58
+ */
59
+ public async verifyWithdrawal(
60
+ withdrawalPayload: WithdrawalProof,
61
+ ): Promise<boolean> {
62
+ try {
63
+ const vkeyBin = await this.circuits.getVerificationKey(
64
+ CircuitName.Withdraw,
65
+ );
66
+ const vkey = JSON.parse(new TextDecoder("utf-8").decode(vkeyBin));
67
+ return await snarkjs.groth16.verify(
68
+ vkey,
69
+ withdrawalPayload.publicSignals,
70
+ withdrawalPayload.proof,
71
+ );
72
+ } catch (error) {
73
+ throw ProofError.verificationFailed({
74
+ error: error instanceof Error ? error.message : "Unknown error",
75
+ });
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Prepares input signals for the withdrawal circuit.
81
+ */
82
+ private prepareInputSignals(
83
+ commitment: Commitment | AccountCommitment,
84
+ input: WithdrawalProofInput
85
+ ): Record<string, bigint | bigint[] | string> {
86
+ let existingValue: bigint;
87
+ let existingNullifier: bigint;
88
+ let existingSecret: bigint;
89
+ let label: bigint;
90
+ if ("preimage" in commitment) {
91
+ existingValue = commitment.preimage.value;
92
+ existingNullifier = commitment.preimage.precommitment.nullifier;
93
+ existingSecret = commitment.preimage.precommitment.secret;
94
+ label = commitment.preimage.label;
95
+ } else {
96
+ existingValue = commitment.value;
97
+ existingNullifier = commitment.nullifier;
98
+ existingSecret = commitment.secret;
99
+ label = commitment.label;
100
+ }
101
+
102
+ return {
103
+ // Public signals
104
+ withdrawnValue: input.withdrawalAmount,
105
+ stateRoot: input.stateRoot,
106
+ stateTreeDepth: input.stateTreeDepth,
107
+ ASPRoot: input.aspRoot,
108
+ ASPTreeDepth: input.aspTreeDepth,
109
+ context: input.context,
110
+
111
+ // Private signals
112
+ label,
113
+ existingValue,
114
+ existingNullifier,
115
+ existingSecret,
116
+ newNullifier: input.newNullifier,
117
+ newSecret: input.newSecret,
118
+
119
+ // Merkle Proofs
120
+ stateSiblings: input.stateMerkleProof.siblings,
121
+ stateIndex: BigInt(input.stateMerkleProof.index),
122
+ ASPSiblings: input.aspMerkleProof.siblings,
123
+ ASPIndex: BigInt(input.aspMerkleProof.index),
124
+ };
125
+ }
126
+ }