zkenclave-sdk 0.1.0

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.
package/dist/index.js ADDED
@@ -0,0 +1,652 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ASP_REGISTRY_ABI: () => ASP_REGISTRY_ABI,
24
+ CHAIN_CONFIG: () => CHAIN_CONFIG,
25
+ CONTRACT_ADDRESSES: () => CONTRACT_ADDRESSES,
26
+ DEFAULT_BATCH_SIZE: () => DEFAULT_BATCH_SIZE,
27
+ DEFAULT_GAS_LIMIT: () => DEFAULT_GAS_LIMIT,
28
+ FIELD_SIZE: () => FIELD_SIZE,
29
+ MERKLE_TREE_DEPTH: () => MERKLE_TREE_DEPTH,
30
+ MerkleTree: () => MerkleTree,
31
+ POSEIDON_CONSTANTS: () => POSEIDON_CONSTANTS,
32
+ PRIVACY_VAULT_ABI: () => PRIVACY_VAULT_ABI,
33
+ PROOF_EXPIRY_MS: () => PROOF_EXPIRY_MS,
34
+ PrivacyVaultSDK: () => PrivacyVaultSDK,
35
+ ZERO_BYTES32: () => ZERO_BYTES32,
36
+ ZKProofClient: () => ZKProofClient,
37
+ ZK_VERIFIER_ABI: () => ZK_VERIFIER_ABI,
38
+ bigIntToBytes32: () => bigIntToBytes32,
39
+ bytes32ToBigInt: () => bytes32ToBigInt,
40
+ bytesToHex: () => bytesToHex,
41
+ computeCommitment: () => computeCommitment,
42
+ computeNullifier: () => computeNullifier,
43
+ decryptNote: () => decryptNote,
44
+ deserializeNote: () => deserializeNote,
45
+ encryptNote: () => encryptNote,
46
+ generateDepositNote: () => generateDepositNote,
47
+ generateRandomBytes: () => generateRandomBytes,
48
+ getZeroNode: () => getZeroNode,
49
+ hexToBytes: () => hexToBytes,
50
+ poseidonHash: () => poseidonHash,
51
+ serializeNote: () => serializeNote
52
+ });
53
+ module.exports = __toCommonJS(index_exports);
54
+
55
+ // src/vault.ts
56
+ var import_ethers3 = require("ethers");
57
+
58
+ // src/constants.ts
59
+ var import_ethers = require("ethers");
60
+ var MERKLE_TREE_DEPTH = 20;
61
+ var FIELD_SIZE = BigInt(
62
+ "21888242871839275222246405745257275088548364400416034343698204186575808495617"
63
+ );
64
+ var DEFAULT_GAS_LIMIT = 500000n;
65
+ var DEFAULT_BATCH_SIZE = 10;
66
+ var PROOF_EXPIRY_MS = 3e5;
67
+ var CONTRACT_ADDRESSES = {
68
+ ethereum: {
69
+ mainnet: {
70
+ vault: "0x0000000000000000000000000000000000000000",
71
+ zkVerifier: "0x0000000000000000000000000000000000000000",
72
+ aspRegistry: "0x0000000000000000000000000000000000000000"
73
+ },
74
+ celoSepolia: {
75
+ vault: "0x68F19280d3030eaE36B8Da42621B66e92a8AEA32",
76
+ zkVerifier: "0x68491614a84C0410E9Fc0CB59Fc60A4F9188687c",
77
+ aspRegistry: "0xB041Cff58FB866c7f4326e0767c97B93434aBa9E"
78
+ },
79
+ horizenSepolia: {
80
+ vault: "0x68F19280d3030eaE36B8Da42621B66e92a8AEA32",
81
+ zkVerifier: "0x68491614a84C0410E9Fc0CB59Fc60A4F9188687c",
82
+ aspRegistry: "0xB041Cff58FB866c7f4326e0767c97B93434aBa9E"
83
+ }
84
+ },
85
+ phala: {
86
+ mainnet: {
87
+ phatContract: ""
88
+ },
89
+ testnet: {
90
+ phatContract: ""
91
+ }
92
+ }
93
+ };
94
+ var CHAIN_CONFIG = {
95
+ 1: {
96
+ name: "Ethereum Mainnet",
97
+ rpcUrl: "https://eth.llamarpc.com",
98
+ explorer: "https://etherscan.io"
99
+ },
100
+ 11142220: {
101
+ name: "Celo Sepolia",
102
+ rpcUrl: "https://forno.celo-sepolia.celo-testnet.org",
103
+ explorer: "https://sepolia.celoscan.io"
104
+ },
105
+ 2035: {
106
+ name: "Phala L2",
107
+ rpcUrl: "https://rpc.phala.network",
108
+ explorer: "https://explorer.phala.network"
109
+ },
110
+ 845320009: {
111
+ name: "Horizen Sepolia Testnet",
112
+ rpcUrl: "https://horizen-rpc-testnet.appchain.base.org",
113
+ explorer: "https://explorer-horizen-testnet.appchain.base.org"
114
+ }
115
+ };
116
+ var POSEIDON_CONSTANTS = {
117
+ T: 3,
118
+ ROUNDS_F: 8,
119
+ ROUNDS_P: 57
120
+ };
121
+ var PRIVACY_VAULT_ABI = [
122
+ "function deposit(bytes32 commitment) external payable",
123
+ "function withdraw(bytes32 nullifierHash, bytes32 root, address recipient, uint256 amount, bytes calldata zkProof, bytes calldata teeAttestation) external",
124
+ "function withdrawWithCompliance(bytes32 nullifierHash, bytes32 root, address recipient, uint256 amount, bytes calldata zkProof, bytes calldata associationProof, address aspProvider) external",
125
+ "function isKnownRoot(bytes32 root) external view returns (bool)",
126
+ "function getLatestRoot() external view returns (bytes32)",
127
+ "function getNextLeafIndex() external view returns (uint256)",
128
+ "function isNullifierUsed(bytes32 nullifier) external view returns (bool)",
129
+ "function getUserDeposits(address user) external view returns (bytes32[])",
130
+ "event Deposit(bytes32 indexed commitment, uint256 leafIndex, uint256 amount, uint256 timestamp)",
131
+ "event Withdrawal(bytes32 indexed nullifierHash, address indexed recipient, uint256 amount, bytes32 merkleRoot)"
132
+ ];
133
+ var ZK_VERIFIER_ABI = [
134
+ "function verifyProof(bytes calldata proof, bytes32[] calldata publicInputs) external returns (bool)",
135
+ "function verifyProofView(bytes calldata proof, bytes32[] calldata publicInputs) external view returns (bool)",
136
+ "function isProofVerified(bytes32 proofHash) external view returns (bool)"
137
+ ];
138
+ var ASP_REGISTRY_ABI = [
139
+ "function isRegistered(address provider) external view returns (bool)",
140
+ "function getProviderRoot(address provider) external view returns (bytes32)",
141
+ "function getActiveProviders() external view returns (address[])",
142
+ "function getHighReputationProviders(uint256 minScore) external view returns (address[])"
143
+ ];
144
+ var ZERO_BYTES32 = new Uint8Array(32);
145
+ function getZeroNode(level) {
146
+ const zeros = [ZERO_BYTES32];
147
+ for (let i = 1; i <= level; i++) {
148
+ const prev = zeros[i - 1];
149
+ const combined = new Uint8Array(64);
150
+ combined.set(prev, 0);
151
+ combined.set(prev, 32);
152
+ zeros.push(hashKeccak256(combined));
153
+ }
154
+ return zeros[level];
155
+ }
156
+ function hashKeccak256(data) {
157
+ return (0, import_ethers.getBytes)((0, import_ethers.keccak256)(data));
158
+ }
159
+
160
+ // src/crypto.ts
161
+ function generateRandomBytes(length) {
162
+ const buffer = new Uint8Array(length);
163
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
164
+ crypto.getRandomValues(buffer);
165
+ } else {
166
+ for (let i = 0; i < length; i++) {
167
+ buffer[i] = Math.floor(Math.random() * 256);
168
+ }
169
+ }
170
+ return buffer;
171
+ }
172
+ function bytesToHex(bytes) {
173
+ return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
174
+ }
175
+ function hexToBytes(hex) {
176
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
177
+ const bytes = new Uint8Array(cleanHex.length / 2);
178
+ for (let i = 0; i < bytes.length; i++) {
179
+ bytes[i] = parseInt(cleanHex.substr(i * 2, 2), 16);
180
+ }
181
+ return bytes;
182
+ }
183
+ function bigIntToBytes32(value) {
184
+ const hex = value.toString(16).padStart(64, "0");
185
+ return hexToBytes(hex);
186
+ }
187
+ function bytes32ToBigInt(bytes) {
188
+ if (bytes.length !== 32) {
189
+ throw new Error("Invalid bytes32 length");
190
+ }
191
+ return BigInt(bytesToHex(bytes));
192
+ }
193
+ function poseidonHash(inputs) {
194
+ if (inputs.length > 2) {
195
+ throw new Error("Poseidon T3 supports max 2 inputs");
196
+ }
197
+ let state = inputs.map((i) => i % FIELD_SIZE);
198
+ while (state.length < 3) {
199
+ state.push(0n);
200
+ }
201
+ const C = [
202
+ 0x0ee9a592ba9a9518d05986d656f40c2114c4993c11bb29938d21d47304cd8e6en,
203
+ 0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864n,
204
+ 0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5n
205
+ ];
206
+ const M = [
207
+ [
208
+ 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118bn,
209
+ 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0n,
210
+ 0x2b90bba00f05d28c6d4c9d2f1d4d3c2e7f5d8e4a3b2c1d0e9f8a7b6c5d4e3f2an
211
+ ],
212
+ [
213
+ 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771n,
214
+ 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7n,
215
+ 0x1e3f7a4c5d6b8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3fn
216
+ ],
217
+ [
218
+ 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911n,
219
+ 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773c5e6f71c7c5e3an,
220
+ 0x2b4129c2e5a87d9c3e1f5b7d9a2c4e6f8a0b2d4c6e8f0a2b4c6d8e0f2a4b6c8dn
221
+ ]
222
+ ];
223
+ const sbox = (x) => {
224
+ const x2 = x * x % FIELD_SIZE;
225
+ const x4 = x2 * x2 % FIELD_SIZE;
226
+ return x4 * x % FIELD_SIZE;
227
+ };
228
+ const mix = (s) => {
229
+ const result = [];
230
+ for (let i = 0; i < 3; i++) {
231
+ let sum = 0n;
232
+ for (let j = 0; j < 3; j++) {
233
+ sum = (sum + M[i][j] * s[j]) % FIELD_SIZE;
234
+ }
235
+ result.push(sum);
236
+ }
237
+ return result;
238
+ };
239
+ const halfF = POSEIDON_CONSTANTS.ROUNDS_F / 2;
240
+ for (let r = 0; r < halfF; r++) {
241
+ for (let i = 0; i < 3; i++) {
242
+ state[i] = (state[i] + C[i]) % FIELD_SIZE;
243
+ state[i] = sbox(state[i]);
244
+ }
245
+ state = mix(state);
246
+ }
247
+ for (let r = 0; r < POSEIDON_CONSTANTS.ROUNDS_P; r++) {
248
+ for (let i = 0; i < 3; i++) {
249
+ state[i] = (state[i] + C[i]) % FIELD_SIZE;
250
+ }
251
+ state[0] = sbox(state[0]);
252
+ state = mix(state);
253
+ }
254
+ for (let r = 0; r < halfF; r++) {
255
+ for (let i = 0; i < 3; i++) {
256
+ state[i] = (state[i] + C[i]) % FIELD_SIZE;
257
+ state[i] = sbox(state[i]);
258
+ }
259
+ state = mix(state);
260
+ }
261
+ return state[0];
262
+ }
263
+ function computeCommitment(secret, nullifierSeed, amount) {
264
+ const intermediate = poseidonHash([secret, nullifierSeed]);
265
+ return poseidonHash([intermediate, amount]);
266
+ }
267
+ function computeNullifier(nullifierSeed, leafIndex) {
268
+ return poseidonHash([nullifierSeed, BigInt(leafIndex)]);
269
+ }
270
+ function generateDepositNote(amount) {
271
+ const secret = generateRandomBytes(32);
272
+ const nullifierSeed = generateRandomBytes(32);
273
+ const secretBigInt = bytes32ToBigInt(secret);
274
+ const nullifierBigInt = bytes32ToBigInt(nullifierSeed);
275
+ const commitmentBigInt = computeCommitment(
276
+ secretBigInt,
277
+ nullifierBigInt,
278
+ amount
279
+ );
280
+ const commitment = bigIntToBytes32(commitmentBigInt);
281
+ return {
282
+ commitment,
283
+ secret,
284
+ nullifierSeed,
285
+ amount,
286
+ leafIndex: -1,
287
+ timestamp: Date.now()
288
+ };
289
+ }
290
+ function serializeNote(note) {
291
+ const data = {
292
+ commitment: bytesToHex(note.commitment),
293
+ secret: bytesToHex(note.secret),
294
+ nullifierSeed: bytesToHex(note.nullifierSeed),
295
+ amount: note.amount.toString(),
296
+ leafIndex: note.leafIndex,
297
+ timestamp: note.timestamp
298
+ };
299
+ return btoa(JSON.stringify(data));
300
+ }
301
+ function deserializeNote(serialized) {
302
+ const data = JSON.parse(atob(serialized));
303
+ return {
304
+ commitment: hexToBytes(data.commitment),
305
+ secret: hexToBytes(data.secret),
306
+ nullifierSeed: hexToBytes(data.nullifierSeed),
307
+ amount: BigInt(data.amount),
308
+ leafIndex: data.leafIndex,
309
+ timestamp: data.timestamp
310
+ };
311
+ }
312
+ function encryptNote(note, password) {
313
+ const serialized = serializeNote(note);
314
+ const encrypted = xorEncrypt(serialized, password);
315
+ return bytesToHex(encrypted);
316
+ }
317
+ function decryptNote(encrypted, password) {
318
+ const bytes = hexToBytes(encrypted);
319
+ const decrypted = xorDecrypt(bytes, password);
320
+ return deserializeNote(decrypted);
321
+ }
322
+ function xorEncrypt(data, key) {
323
+ const dataBytes = new TextEncoder().encode(data);
324
+ const keyBytes = new TextEncoder().encode(key);
325
+ const result = new Uint8Array(dataBytes.length);
326
+ for (let i = 0; i < dataBytes.length; i++) {
327
+ result[i] = dataBytes[i] ^ keyBytes[i % keyBytes.length];
328
+ }
329
+ return result;
330
+ }
331
+ function xorDecrypt(data, key) {
332
+ const keyBytes = new TextEncoder().encode(key);
333
+ const result = new Uint8Array(data.length);
334
+ for (let i = 0; i < data.length; i++) {
335
+ result[i] = data[i] ^ keyBytes[i % keyBytes.length];
336
+ }
337
+ return new TextDecoder().decode(result);
338
+ }
339
+ var MerkleTree = class {
340
+ leaves;
341
+ layers;
342
+ depth;
343
+ zeroValues;
344
+ constructor(depth = MERKLE_TREE_DEPTH) {
345
+ this.depth = depth;
346
+ this.leaves = [];
347
+ this.layers = [];
348
+ this.zeroValues = this.computeZeroValues();
349
+ }
350
+ computeZeroValues() {
351
+ const zeros = [0n];
352
+ for (let i = 1; i <= this.depth; i++) {
353
+ zeros.push(poseidonHash([zeros[i - 1], zeros[i - 1]]));
354
+ }
355
+ return zeros;
356
+ }
357
+ insert(leaf) {
358
+ const index = this.leaves.length;
359
+ this.leaves.push(leaf);
360
+ this.rebuild();
361
+ return index;
362
+ }
363
+ rebuild() {
364
+ this.layers = [this.leaves.slice()];
365
+ const targetSize = 1 << this.depth;
366
+ while (this.layers[0].length < targetSize) {
367
+ this.layers[0].push(this.zeroValues[0]);
368
+ }
369
+ for (let level = 0; level < this.depth; level++) {
370
+ const currentLayer = this.layers[level];
371
+ const nextLayer = [];
372
+ for (let i = 0; i < currentLayer.length; i += 2) {
373
+ const left = currentLayer[i];
374
+ const right = currentLayer[i + 1] ?? this.zeroValues[level];
375
+ nextLayer.push(poseidonHash([left, right]));
376
+ }
377
+ this.layers.push(nextLayer);
378
+ }
379
+ }
380
+ getRoot() {
381
+ if (this.layers.length === 0) {
382
+ return this.zeroValues[this.depth];
383
+ }
384
+ return this.layers[this.layers.length - 1][0];
385
+ }
386
+ generateProof(index) {
387
+ if (index >= this.leaves.length) {
388
+ throw new Error("Index out of bounds");
389
+ }
390
+ const path = [];
391
+ const indices = [];
392
+ let currentIndex = index;
393
+ for (let level = 0; level < this.depth; level++) {
394
+ const isRight = currentIndex % 2 === 1;
395
+ const siblingIndex = isRight ? currentIndex - 1 : currentIndex + 1;
396
+ const sibling = this.layers[level][siblingIndex] ?? this.zeroValues[level];
397
+ path.push(bigIntToBytes32(sibling));
398
+ indices.push(isRight);
399
+ currentIndex = Math.floor(currentIndex / 2);
400
+ }
401
+ return {
402
+ path,
403
+ indices,
404
+ root: bigIntToBytes32(this.getRoot())
405
+ };
406
+ }
407
+ verifyProof(leaf, proof) {
408
+ let current = leaf;
409
+ for (let i = 0; i < proof.path.length; i++) {
410
+ const sibling = bytes32ToBigInt(proof.path[i]);
411
+ if (proof.indices[i]) {
412
+ current = poseidonHash([sibling, current]);
413
+ } else {
414
+ current = poseidonHash([current, sibling]);
415
+ }
416
+ }
417
+ return current === bytes32ToBigInt(proof.root);
418
+ }
419
+ };
420
+
421
+ // src/zk-client.ts
422
+ var import_ethers2 = require("ethers");
423
+ var ZKProofClient = class {
424
+ config;
425
+ constructor(config) {
426
+ this.config = config ?? {};
427
+ }
428
+ async generateWithdrawalProof(request) {
429
+ const nullifierHash = this.computeNullifierHash(
430
+ request.nullifier,
431
+ request.leafIndex
432
+ );
433
+ const zkProof = this.generateMockProof(request);
434
+ const merkleRoot = request.merkleRoot ?? new Uint8Array(32);
435
+ return {
436
+ success: true,
437
+ nullifierHash,
438
+ zkProof,
439
+ merkleRoot,
440
+ timestamp: Date.now()
441
+ };
442
+ }
443
+ async generateComplianceProof(commitment, associationRoot) {
444
+ const proofId = (0, import_ethers2.keccak256)(
445
+ new Uint8Array([...commitment, ...associationRoot])
446
+ );
447
+ return {
448
+ id: proofId,
449
+ associationRoot,
450
+ timestamp: Date.now(),
451
+ valid: true,
452
+ proof: new Uint8Array(256)
453
+ };
454
+ }
455
+ computeNullifierHash(nullifier, leafIndex) {
456
+ const indexBytes = new TextEncoder().encode(leafIndex.toString());
457
+ const combined = new Uint8Array([...nullifier, ...indexBytes]);
458
+ const hash = (0, import_ethers2.keccak256)(combined);
459
+ return this.hexToBytes(hash);
460
+ }
461
+ generateMockProof(request) {
462
+ const proof = new Uint8Array(256);
463
+ proof[0] = 1;
464
+ const amountHex = (0, import_ethers2.toBeHex)(request.amount, 32);
465
+ const amountBytes = this.hexToBytes(amountHex);
466
+ proof.set(amountBytes.slice(0, 32), 1);
467
+ proof.set(request.commitment.slice(0, 32), 33);
468
+ return proof;
469
+ }
470
+ hexToBytes(hex) {
471
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
472
+ const bytes = new Uint8Array(cleanHex.length / 2);
473
+ for (let i = 0; i < bytes.length; i++) {
474
+ bytes[i] = parseInt(cleanHex.slice(i * 2, i * 2 + 2), 16);
475
+ }
476
+ return bytes;
477
+ }
478
+ };
479
+
480
+ // src/vault.ts
481
+ var PrivacyVaultSDK = class {
482
+ provider;
483
+ signer = null;
484
+ vault;
485
+ _aspRegistry;
486
+ zkClient;
487
+ config;
488
+ _merkleTree;
489
+ constructor(config, signer) {
490
+ this.config = config;
491
+ this.provider = new import_ethers3.ethers.JsonRpcProvider(config.rpcUrl);
492
+ if (signer) {
493
+ this.signer = signer;
494
+ }
495
+ this.vault = new import_ethers3.ethers.Contract(
496
+ config.vaultAddress,
497
+ PRIVACY_VAULT_ABI,
498
+ this.signer ?? this.provider
499
+ );
500
+ this._aspRegistry = new import_ethers3.ethers.Contract(
501
+ config.aspRegistryAddress,
502
+ ASP_REGISTRY_ABI,
503
+ this.provider
504
+ );
505
+ this.zkClient = new ZKProofClient();
506
+ this._merkleTree = new MerkleTree();
507
+ }
508
+ async connect(signer) {
509
+ this.signer = signer;
510
+ this.vault = this.vault.connect(signer);
511
+ }
512
+ async deposit(amount) {
513
+ if (!this.signer) {
514
+ throw new Error("Signer required for deposits");
515
+ }
516
+ const note = generateDepositNote(amount);
517
+ const commitmentHex = bytesToHex(note.commitment);
518
+ const tx = await this.vault.deposit(commitmentHex, {
519
+ value: amount,
520
+ gasLimit: DEFAULT_GAS_LIMIT
521
+ });
522
+ const receipt = await tx.wait();
523
+ let leafIndex = -1;
524
+ const depositEvent = receipt.logs.find((log) => {
525
+ try {
526
+ const parsed = this.vault.interface.parseLog({
527
+ topics: log.topics,
528
+ data: log.data
529
+ });
530
+ return parsed?.name === "Deposit";
531
+ } catch {
532
+ return false;
533
+ }
534
+ });
535
+ if (depositEvent) {
536
+ const parsed = this.vault.interface.parseLog({
537
+ topics: depositEvent.topics,
538
+ data: depositEvent.data
539
+ });
540
+ leafIndex = Number(parsed?.args?.leafIndex ?? -1);
541
+ }
542
+ note.leafIndex = leafIndex;
543
+ return {
544
+ success: true,
545
+ txHash: receipt.hash,
546
+ commitment: note.commitment,
547
+ leafIndex,
548
+ note
549
+ };
550
+ }
551
+ async withdraw(note, recipient) {
552
+ if (!this.signer) {
553
+ throw new Error("Signer required for withdrawals");
554
+ }
555
+ const nullifierBigInt = bytes32ToBigInt(note.nullifierSeed);
556
+ const nullifierHashBigInt = computeNullifier(
557
+ nullifierBigInt,
558
+ note.leafIndex
559
+ );
560
+ const nullifierHash = bigIntToBytes32(nullifierHashBigInt);
561
+ const root = await this.getLatestRoot();
562
+ const zkProofResult = await this.zkClient.generateWithdrawalProof({
563
+ commitment: note.commitment,
564
+ nullifier: note.nullifierSeed,
565
+ recipient,
566
+ amount: note.amount,
567
+ leafIndex: note.leafIndex,
568
+ merkleRoot: root,
569
+ merklePath: [],
570
+ pathIndices: []
571
+ });
572
+ const tx = await this.vault.withdraw(
573
+ bytesToHex(nullifierHash),
574
+ bytesToHex(root),
575
+ recipient,
576
+ note.amount,
577
+ zkProofResult.zkProof,
578
+ new Uint8Array(64),
579
+ { gasLimit: DEFAULT_GAS_LIMIT }
580
+ );
581
+ const receipt = await tx.wait();
582
+ return {
583
+ success: true,
584
+ txHash: receipt.hash,
585
+ zkProof: zkProofResult.zkProof,
586
+ nullifierHash,
587
+ merkleRoot: root,
588
+ timestamp: Date.now()
589
+ };
590
+ }
591
+ async getLatestRoot() {
592
+ const root = await this.vault.getLatestRoot();
593
+ return hexToBytes(root);
594
+ }
595
+ async getNextLeafIndex() {
596
+ const index = await this.vault.getNextLeafIndex();
597
+ return Number(index);
598
+ }
599
+ async isNullifierUsed(nullifier) {
600
+ return await this.vault.isNullifierUsed(bytesToHex(nullifier));
601
+ }
602
+ async isKnownRoot(root) {
603
+ return await this.vault.isKnownRoot(bytesToHex(root));
604
+ }
605
+ async getVaultStats() {
606
+ const [nextLeafIndex, latestRoot] = await Promise.all([
607
+ this.vault.getNextLeafIndex(),
608
+ this.vault.getLatestRoot()
609
+ ]);
610
+ return {
611
+ totalDeposits: 0n,
612
+ totalWithdrawals: 0n,
613
+ nextLeafIndex: Number(nextLeafIndex),
614
+ latestRoot: hexToBytes(latestRoot)
615
+ };
616
+ }
617
+ getConfig() {
618
+ return this.config;
619
+ }
620
+ };
621
+ // Annotate the CommonJS export names for ESM import in node:
622
+ 0 && (module.exports = {
623
+ ASP_REGISTRY_ABI,
624
+ CHAIN_CONFIG,
625
+ CONTRACT_ADDRESSES,
626
+ DEFAULT_BATCH_SIZE,
627
+ DEFAULT_GAS_LIMIT,
628
+ FIELD_SIZE,
629
+ MERKLE_TREE_DEPTH,
630
+ MerkleTree,
631
+ POSEIDON_CONSTANTS,
632
+ PRIVACY_VAULT_ABI,
633
+ PROOF_EXPIRY_MS,
634
+ PrivacyVaultSDK,
635
+ ZERO_BYTES32,
636
+ ZKProofClient,
637
+ ZK_VERIFIER_ABI,
638
+ bigIntToBytes32,
639
+ bytes32ToBigInt,
640
+ bytesToHex,
641
+ computeCommitment,
642
+ computeNullifier,
643
+ decryptNote,
644
+ deserializeNote,
645
+ encryptNote,
646
+ generateDepositNote,
647
+ generateRandomBytes,
648
+ getZeroNode,
649
+ hexToBytes,
650
+ poseidonHash,
651
+ serializeNote
652
+ });