@hula-privacy/mixer 0.2.0 → 0.3.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,2587 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ DOMAIN_ENCRYPTION: () => DOMAIN_ENCRYPTION,
34
+ DOMAIN_NULLIFIER: () => DOMAIN_NULLIFIER,
35
+ DOMAIN_OWNER: () => DOMAIN_OWNER,
36
+ DOMAIN_VIEWING: () => DOMAIN_VIEWING,
37
+ FIELD_PRIME: () => FIELD_PRIME,
38
+ HulaWallet: () => HulaWallet,
39
+ MAX_LEAVES: () => MAX_LEAVES,
40
+ MERKLE_TREE_DEPTH: () => MERKLE_TREE_DEPTH,
41
+ MERKLE_TREE_SEED: () => MERKLE_TREE_SEED,
42
+ NULLIFIER_SEED: () => NULLIFIER_SEED,
43
+ NUM_INPUT_UTXOS: () => NUM_INPUT_UTXOS,
44
+ NUM_OUTPUT_UTXOS: () => NUM_OUTPUT_UTXOS,
45
+ POOL_SEED: () => POOL_SEED,
46
+ PROGRAM_ID: () => PROGRAM_ID,
47
+ PROOF_SIZE: () => PROOF_SIZE,
48
+ RelayerClient: () => RelayerClient,
49
+ TOKEN_2022_PROGRAM_ID: () => TOKEN_2022_PROGRAM_ID,
50
+ VAULT_SEED: () => VAULT_SEED,
51
+ bigIntToBytes: () => bigIntToBytes,
52
+ bigIntToBytes32: () => bigIntToBytes32,
53
+ buildTransaction: () => buildTransaction,
54
+ buildTransactionAccounts: () => buildTransactionAccounts,
55
+ bytesToBigInt: () => bytesToBigInt,
56
+ bytesToHex: () => bytesToHex,
57
+ calculateBalance: () => calculateBalance,
58
+ computeCommitment: () => computeCommitment,
59
+ computeMerklePathFromLeaves: () => computeMerklePathFromLeaves,
60
+ computeMerkleRoot: () => computeMerkleRoot,
61
+ computeNullifier: () => computeNullifier,
62
+ computeNullifierFromKeys: () => computeNullifierFromKeys,
63
+ computeZeros: () => computeZeros,
64
+ createDummyUTXO: () => createDummyUTXO,
65
+ createUTXO: () => createUTXO,
66
+ decryptNote: () => decryptNote,
67
+ deriveKeys: () => deriveKeys,
68
+ deriveSpendingKeyFromSignature: () => deriveSpendingKeyFromSignature,
69
+ deserializeEncryptedNote: () => deserializeEncryptedNote,
70
+ deserializeUTXO: () => deserializeUTXO,
71
+ encryptNote: () => encryptNote,
72
+ fetchMerklePath: () => fetchMerklePath,
73
+ fetchMerkleRoot: () => fetchMerkleRoot,
74
+ formatCommitment: () => formatCommitment,
75
+ generateProof: () => generateProof,
76
+ generateProofInMemory: () => generateProofInMemory,
77
+ generateSpendingKey: () => generateSpendingKey,
78
+ getCircuitPaths: () => getCircuitPaths,
79
+ getCurrentTreeIndex: () => getCurrentTreeIndex,
80
+ getEmptyTreeRoot: () => getEmptyTreeRoot,
81
+ getKeyDerivationMessage: () => getKeyDerivationMessage,
82
+ getMerkleTreePDA: () => getMerkleTreePDA,
83
+ getNextLeafIndex: () => getNextLeafIndex,
84
+ getNullifierPDA: () => getNullifierPDA,
85
+ getPoolPDA: () => getPoolPDA,
86
+ getPoseidon: () => getPoseidon,
87
+ getRelayerClient: () => getRelayerClient,
88
+ getVaultPDA: () => getVaultPDA,
89
+ getZeroAtLevel: () => getZeroAtLevel,
90
+ hexToBytes: () => hexToBytes,
91
+ initHulaSDK: () => initHulaSDK,
92
+ initPoseidon: () => initPoseidon,
93
+ isPoseidonInitialized: () => isPoseidonInitialized,
94
+ parseProof: () => parseProof,
95
+ poseidonHash: () => poseidonHash,
96
+ pubkeyToBigInt: () => pubkeyToBigInt,
97
+ scanNotesForUTXOs: () => scanNotesForUTXOs,
98
+ selectUTXOs: () => selectUTXOs,
99
+ serializeEncryptedNote: () => serializeEncryptedNote,
100
+ serializeUTXO: () => serializeUTXO,
101
+ setCircuitPaths: () => setCircuitPaths,
102
+ setDefaultRelayerUrl: () => setDefaultRelayerUrl,
103
+ syncUTXOs: () => syncUTXOs,
104
+ toAnchorPublicInputs: () => toAnchorPublicInputs,
105
+ verifyCircuitFiles: () => verifyCircuitFiles,
106
+ verifyMerklePath: () => verifyMerklePath
107
+ });
108
+ module.exports = __toCommonJS(index_exports);
109
+
110
+ // src/wallet.ts
111
+ var import_web34 = require("@solana/web3.js");
112
+ var import_anchor2 = require("@coral-xyz/anchor");
113
+
114
+ // src/idl.ts
115
+ var HulaPrivacyIdl = {
116
+ "address": "tnJ9AAxNau3BRBTe7NzXpv8DeYyiogvGd8YPnCiuarA",
117
+ "metadata": {
118
+ "name": "hula_privacy",
119
+ "version": "0.1.0",
120
+ "spec": "0.1.0",
121
+ "description": "Privacy wallet for Solana using ZK proofs"
122
+ },
123
+ "instructions": [
124
+ {
125
+ "name": "initialize_new_tree",
126
+ "docs": [
127
+ "Initialize a new merkle tree when the current one is full"
128
+ ],
129
+ "discriminator": [
130
+ 42,
131
+ 154,
132
+ 29,
133
+ 105,
134
+ 242,
135
+ 162,
136
+ 191,
137
+ 224
138
+ ],
139
+ "accounts": [
140
+ {
141
+ "name": "payer",
142
+ "docs": [
143
+ "The payer for account creation"
144
+ ],
145
+ "writable": true,
146
+ "signer": true
147
+ },
148
+ {
149
+ "name": "pool",
150
+ "docs": [
151
+ "Global privacy pool account"
152
+ ],
153
+ "writable": true,
154
+ "pda": {
155
+ "seeds": [
156
+ {
157
+ "kind": "const",
158
+ "value": [
159
+ 104,
160
+ 117,
161
+ 108,
162
+ 97,
163
+ 95,
164
+ 112,
165
+ 111,
166
+ 111,
167
+ 108
168
+ ]
169
+ }
170
+ ]
171
+ }
172
+ },
173
+ {
174
+ "name": "current_tree",
175
+ "docs": [
176
+ "The current (full) merkle tree"
177
+ ],
178
+ "pda": {
179
+ "seeds": [
180
+ {
181
+ "kind": "const",
182
+ "value": [
183
+ 109,
184
+ 101,
185
+ 114,
186
+ 107,
187
+ 108,
188
+ 101,
189
+ 95,
190
+ 116,
191
+ 114,
192
+ 101,
193
+ 101
194
+ ]
195
+ },
196
+ {
197
+ "kind": "account",
198
+ "path": "pool.current_tree_index",
199
+ "account": "PrivacyPool"
200
+ }
201
+ ]
202
+ }
203
+ },
204
+ {
205
+ "name": "new_tree",
206
+ "docs": [
207
+ "New merkle tree account (PDA with tree_index)"
208
+ ],
209
+ "writable": true,
210
+ "pda": {
211
+ "seeds": [
212
+ {
213
+ "kind": "const",
214
+ "value": [
215
+ 109,
216
+ 101,
217
+ 114,
218
+ 107,
219
+ 108,
220
+ 101,
221
+ 95,
222
+ 116,
223
+ 114,
224
+ 101,
225
+ 101
226
+ ]
227
+ },
228
+ {
229
+ "kind": "arg",
230
+ "path": "tree_index"
231
+ }
232
+ ]
233
+ }
234
+ },
235
+ {
236
+ "name": "system_program",
237
+ "docs": [
238
+ "System program"
239
+ ],
240
+ "address": "11111111111111111111111111111111"
241
+ }
242
+ ],
243
+ "args": [
244
+ {
245
+ "name": "tree_index",
246
+ "type": "u32"
247
+ }
248
+ ]
249
+ },
250
+ {
251
+ "name": "initialize_pool",
252
+ "docs": [
253
+ "Initialize the global privacy pool"
254
+ ],
255
+ "discriminator": [
256
+ 95,
257
+ 180,
258
+ 10,
259
+ 172,
260
+ 84,
261
+ 174,
262
+ 232,
263
+ 40
264
+ ],
265
+ "accounts": [
266
+ {
267
+ "name": "authority",
268
+ "docs": [
269
+ "The authority creating the pool (pays for account creation)"
270
+ ],
271
+ "writable": true,
272
+ "signer": true
273
+ },
274
+ {
275
+ "name": "pool",
276
+ "docs": [
277
+ "Global privacy pool account (PDA)"
278
+ ],
279
+ "writable": true,
280
+ "pda": {
281
+ "seeds": [
282
+ {
283
+ "kind": "const",
284
+ "value": [
285
+ 104,
286
+ 117,
287
+ 108,
288
+ 97,
289
+ 95,
290
+ 112,
291
+ 111,
292
+ 111,
293
+ 108
294
+ ]
295
+ }
296
+ ]
297
+ }
298
+ },
299
+ {
300
+ "name": "merkle_tree",
301
+ "docs": [
302
+ "Merkle tree account (PDA with tree index 0)"
303
+ ],
304
+ "writable": true,
305
+ "pda": {
306
+ "seeds": [
307
+ {
308
+ "kind": "const",
309
+ "value": [
310
+ 109,
311
+ 101,
312
+ 114,
313
+ 107,
314
+ 108,
315
+ 101,
316
+ 95,
317
+ 116,
318
+ 114,
319
+ 101,
320
+ 101
321
+ ]
322
+ },
323
+ {
324
+ "kind": "const",
325
+ "value": [
326
+ 0,
327
+ 0,
328
+ 0,
329
+ 0
330
+ ]
331
+ }
332
+ ]
333
+ }
334
+ },
335
+ {
336
+ "name": "system_program",
337
+ "docs": [
338
+ "System program"
339
+ ],
340
+ "address": "11111111111111111111111111111111"
341
+ }
342
+ ],
343
+ "args": []
344
+ },
345
+ {
346
+ "name": "transact",
347
+ "docs": [
348
+ "Execute a private transaction"
349
+ ],
350
+ "discriminator": [
351
+ 217,
352
+ 149,
353
+ 130,
354
+ 143,
355
+ 221,
356
+ 52,
357
+ 252,
358
+ 119
359
+ ],
360
+ "accounts": [
361
+ {
362
+ "name": "payer",
363
+ "writable": true,
364
+ "signer": true
365
+ },
366
+ {
367
+ "name": "mint"
368
+ },
369
+ {
370
+ "name": "pool",
371
+ "writable": true,
372
+ "pda": {
373
+ "seeds": [
374
+ {
375
+ "kind": "const",
376
+ "value": [
377
+ 104,
378
+ 117,
379
+ 108,
380
+ 97,
381
+ 95,
382
+ 112,
383
+ 111,
384
+ 111,
385
+ 108
386
+ ]
387
+ }
388
+ ]
389
+ }
390
+ },
391
+ {
392
+ "name": "input_tree",
393
+ "docs": [
394
+ "Input tree - where the input UTXOs come from (for merkle root verification)",
395
+ "If None, defaults to current tree"
396
+ ],
397
+ "pda": {
398
+ "seeds": [
399
+ {
400
+ "kind": "const",
401
+ "value": [
402
+ 109,
403
+ 101,
404
+ 114,
405
+ 107,
406
+ 108,
407
+ 101,
408
+ 95,
409
+ 116,
410
+ 114,
411
+ 101,
412
+ 101
413
+ ]
414
+ },
415
+ {
416
+ "kind": "arg",
417
+ "path": "input_tree_index.unwrap_or(pool.current_tree_index)"
418
+ }
419
+ ]
420
+ }
421
+ },
422
+ {
423
+ "name": "merkle_tree",
424
+ "docs": [
425
+ "Output tree - where new commitments will be inserted (current active tree)"
426
+ ],
427
+ "writable": true,
428
+ "pda": {
429
+ "seeds": [
430
+ {
431
+ "kind": "const",
432
+ "value": [
433
+ 109,
434
+ 101,
435
+ 114,
436
+ 107,
437
+ 108,
438
+ 101,
439
+ 95,
440
+ 116,
441
+ 114,
442
+ 101,
443
+ 101
444
+ ]
445
+ },
446
+ {
447
+ "kind": "account",
448
+ "path": "pool.current_tree_index",
449
+ "account": "PrivacyPool"
450
+ }
451
+ ]
452
+ }
453
+ },
454
+ {
455
+ "name": "vault",
456
+ "docs": [
457
+ "Vault for this mint (created if needed)"
458
+ ],
459
+ "writable": true,
460
+ "pda": {
461
+ "seeds": [
462
+ {
463
+ "kind": "const",
464
+ "value": [
465
+ 118,
466
+ 97,
467
+ 117,
468
+ 108,
469
+ 116
470
+ ]
471
+ },
472
+ {
473
+ "kind": "account",
474
+ "path": "mint"
475
+ }
476
+ ]
477
+ }
478
+ },
479
+ {
480
+ "name": "depositor_token_account",
481
+ "docs": [
482
+ "Depositor's token account (optional)"
483
+ ],
484
+ "writable": true,
485
+ "optional": true
486
+ },
487
+ {
488
+ "name": "depositor",
489
+ "signer": true,
490
+ "optional": true
491
+ },
492
+ {
493
+ "name": "recipient_token_account",
494
+ "docs": [
495
+ "Recipient's token account (optional)"
496
+ ],
497
+ "writable": true,
498
+ "optional": true
499
+ },
500
+ {
501
+ "name": "fee_recipient_token_account",
502
+ "docs": [
503
+ "Fee recipient's token account (optional)"
504
+ ],
505
+ "writable": true,
506
+ "optional": true
507
+ },
508
+ {
509
+ "name": "nullifier_account_0",
510
+ "docs": [
511
+ "Nullifier account 0 (optional - for first input UTXO)"
512
+ ],
513
+ "writable": true,
514
+ "optional": true
515
+ },
516
+ {
517
+ "name": "nullifier_account_1",
518
+ "docs": [
519
+ "Nullifier account 1 (optional - for second input UTXO)"
520
+ ],
521
+ "writable": true,
522
+ "optional": true
523
+ },
524
+ {
525
+ "name": "token_program",
526
+ "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
527
+ },
528
+ {
529
+ "name": "system_program",
530
+ "address": "11111111111111111111111111111111"
531
+ }
532
+ ],
533
+ "args": [
534
+ {
535
+ "name": "input_tree_index",
536
+ "type": {
537
+ "option": "u32"
538
+ }
539
+ },
540
+ {
541
+ "name": "proof",
542
+ "type": {
543
+ "array": [
544
+ "u8",
545
+ 256
546
+ ]
547
+ }
548
+ },
549
+ {
550
+ "name": "public_inputs",
551
+ "type": {
552
+ "defined": {
553
+ "name": "PublicInputs"
554
+ }
555
+ }
556
+ },
557
+ {
558
+ "name": "encrypted_notes",
559
+ "type": {
560
+ "vec": "bytes"
561
+ }
562
+ }
563
+ ]
564
+ }
565
+ ],
566
+ "accounts": [
567
+ {
568
+ "name": "MerkleTreeAccount",
569
+ "discriminator": [
570
+ 147,
571
+ 200,
572
+ 34,
573
+ 248,
574
+ 131,
575
+ 187,
576
+ 248,
577
+ 253
578
+ ]
579
+ },
580
+ {
581
+ "name": "PrivacyPool",
582
+ "discriminator": [
583
+ 133,
584
+ 184,
585
+ 191,
586
+ 79,
587
+ 252,
588
+ 142,
589
+ 190,
590
+ 150
591
+ ]
592
+ }
593
+ ],
594
+ "errors": [
595
+ {
596
+ "code": 6e3,
597
+ "name": "Groth16MulFailed",
598
+ "msg": "Groth16 multiplication failed"
599
+ },
600
+ {
601
+ "code": 6001,
602
+ "name": "Groth16AddFailed",
603
+ "msg": "Groth16 addition failed"
604
+ },
605
+ {
606
+ "code": 6002,
607
+ "name": "Groth16PairingFailed",
608
+ "msg": "Groth16 pairing failed"
609
+ },
610
+ {
611
+ "code": 6003,
612
+ "name": "InvalidPublicInputs",
613
+ "msg": "Invalid public inputs"
614
+ },
615
+ {
616
+ "code": 6004,
617
+ "name": "ProofVerificationFailed",
618
+ "msg": "Proof verification failed"
619
+ },
620
+ {
621
+ "code": 6005,
622
+ "name": "PoolPaused",
623
+ "msg": "Pool is paused"
624
+ },
625
+ {
626
+ "code": 6006,
627
+ "name": "InvalidMint",
628
+ "msg": "Invalid mint"
629
+ },
630
+ {
631
+ "code": 6007,
632
+ "name": "Unauthorized",
633
+ "msg": "Unauthorized"
634
+ },
635
+ {
636
+ "code": 6008,
637
+ "name": "TreeFull",
638
+ "msg": "Merkle tree is full"
639
+ },
640
+ {
641
+ "code": 6009,
642
+ "name": "InvalidRoot",
643
+ "msg": "Invalid merkle root"
644
+ },
645
+ {
646
+ "code": 6010,
647
+ "name": "NullifierAlreadySpent",
648
+ "msg": "Nullifier already spent"
649
+ },
650
+ {
651
+ "code": 6011,
652
+ "name": "NullifierAlreadyUsed",
653
+ "msg": "Nullifier already used - double spend attempt"
654
+ },
655
+ {
656
+ "code": 6012,
657
+ "name": "MissingNullifierAccount",
658
+ "msg": "Missing nullifier account"
659
+ },
660
+ {
661
+ "code": 6013,
662
+ "name": "InvalidNullifierAccount",
663
+ "msg": "Invalid nullifier account"
664
+ },
665
+ {
666
+ "code": 6014,
667
+ "name": "InvalidPublicAmount",
668
+ "msg": "Invalid public amount"
669
+ },
670
+ {
671
+ "code": 6015,
672
+ "name": "ArithmeticOverflow",
673
+ "msg": "Arithmetic overflow"
674
+ },
675
+ {
676
+ "code": 6016,
677
+ "name": "InvalidRecipient",
678
+ "msg": "Invalid recipient"
679
+ },
680
+ {
681
+ "code": 6017,
682
+ "name": "InvalidFee",
683
+ "msg": "Invalid fee"
684
+ },
685
+ {
686
+ "code": 6018,
687
+ "name": "TreeNotFull",
688
+ "msg": "Tree is not full yet"
689
+ },
690
+ {
691
+ "code": 6019,
692
+ "name": "InvalidTreeIndex",
693
+ "msg": "Invalid tree index"
694
+ }
695
+ ],
696
+ "types": [
697
+ {
698
+ "name": "MerkleTreeAccount",
699
+ "docs": [
700
+ "Incremental Merkle Tree for UTXO commitments",
701
+ "Uses a sparse representation - only stores filled subtrees"
702
+ ],
703
+ "type": {
704
+ "kind": "struct",
705
+ "fields": [
706
+ {
707
+ "name": "pool",
708
+ "docs": [
709
+ "The privacy pool this tree belongs to"
710
+ ],
711
+ "type": "pubkey"
712
+ },
713
+ {
714
+ "name": "tree_index",
715
+ "docs": [
716
+ "Tree index (which tree in the sequence)"
717
+ ],
718
+ "type": "u32"
719
+ },
720
+ {
721
+ "name": "next_index",
722
+ "docs": [
723
+ "Current number of leaves inserted"
724
+ ],
725
+ "type": "u32"
726
+ },
727
+ {
728
+ "name": "root",
729
+ "docs": [
730
+ "Current root of the tree"
731
+ ],
732
+ "type": {
733
+ "array": [
734
+ "u8",
735
+ 32
736
+ ]
737
+ }
738
+ },
739
+ {
740
+ "name": "filled_subtrees",
741
+ "docs": [
742
+ "Filled subtrees at each level (for incremental insertion)",
743
+ "filled_subtrees[i] = the rightmost filled node at level i"
744
+ ],
745
+ "type": {
746
+ "array": [
747
+ {
748
+ "array": [
749
+ "u8",
750
+ 32
751
+ ]
752
+ },
753
+ 10
754
+ ]
755
+ }
756
+ },
757
+ {
758
+ "name": "root_history",
759
+ "docs": [
760
+ "Historical roots for verification (ring buffer)",
761
+ "Allows verifying proofs against recent roots"
762
+ ],
763
+ "type": {
764
+ "array": [
765
+ {
766
+ "array": [
767
+ "u8",
768
+ 32
769
+ ]
770
+ },
771
+ 32
772
+ ]
773
+ }
774
+ },
775
+ {
776
+ "name": "root_history_index",
777
+ "docs": [
778
+ "Current position in root history ring buffer"
779
+ ],
780
+ "type": "u8"
781
+ },
782
+ {
783
+ "name": "bump",
784
+ "docs": [
785
+ "Bump seed for PDA"
786
+ ],
787
+ "type": "u8"
788
+ }
789
+ ]
790
+ }
791
+ },
792
+ {
793
+ "name": "PrivacyPool",
794
+ "docs": [
795
+ "Global Privacy Pool Account",
796
+ "Single pool for all tokens - vaults are created per mint lazily"
797
+ ],
798
+ "type": {
799
+ "kind": "struct",
800
+ "fields": [
801
+ {
802
+ "name": "authority",
803
+ "docs": [
804
+ "Authority that can update pool settings"
805
+ ],
806
+ "type": "pubkey"
807
+ },
808
+ {
809
+ "name": "commitment_count",
810
+ "docs": [
811
+ "Total number of commitments across all trees"
812
+ ],
813
+ "type": "u64"
814
+ },
815
+ {
816
+ "name": "merkle_root",
817
+ "docs": [
818
+ "Current merkle root (of the active tree)"
819
+ ],
820
+ "type": {
821
+ "array": [
822
+ "u8",
823
+ 32
824
+ ]
825
+ }
826
+ },
827
+ {
828
+ "name": "bump",
829
+ "docs": [
830
+ "Pool bump seed"
831
+ ],
832
+ "type": "u8"
833
+ },
834
+ {
835
+ "name": "paused",
836
+ "docs": [
837
+ "Whether the pool is paused"
838
+ ],
839
+ "type": "bool"
840
+ },
841
+ {
842
+ "name": "current_tree_index",
843
+ "docs": [
844
+ "Current active tree index (0-based)"
845
+ ],
846
+ "type": "u32"
847
+ },
848
+ {
849
+ "name": "_reserved",
850
+ "docs": [
851
+ "Reserved for future use"
852
+ ],
853
+ "type": "bytes"
854
+ }
855
+ ]
856
+ }
857
+ },
858
+ {
859
+ "name": "PublicInputs",
860
+ "docs": [
861
+ "Public inputs for the transaction circuit"
862
+ ],
863
+ "type": {
864
+ "kind": "struct",
865
+ "fields": [
866
+ {
867
+ "name": "merkle_root",
868
+ "type": {
869
+ "array": [
870
+ "u8",
871
+ 32
872
+ ]
873
+ }
874
+ },
875
+ {
876
+ "name": "nullifiers",
877
+ "type": {
878
+ "array": [
879
+ {
880
+ "array": [
881
+ "u8",
882
+ 32
883
+ ]
884
+ },
885
+ 2
886
+ ]
887
+ }
888
+ },
889
+ {
890
+ "name": "output_commitments",
891
+ "type": {
892
+ "array": [
893
+ {
894
+ "array": [
895
+ "u8",
896
+ 32
897
+ ]
898
+ },
899
+ 2
900
+ ]
901
+ }
902
+ },
903
+ {
904
+ "name": "public_deposit",
905
+ "type": "u64"
906
+ },
907
+ {
908
+ "name": "public_withdraw",
909
+ "type": "u64"
910
+ },
911
+ {
912
+ "name": "recipient",
913
+ "type": "pubkey"
914
+ },
915
+ {
916
+ "name": "mint_token_address",
917
+ "type": "pubkey"
918
+ },
919
+ {
920
+ "name": "fee",
921
+ "type": "u64"
922
+ }
923
+ ]
924
+ }
925
+ }
926
+ ],
927
+ "constants": [
928
+ {
929
+ "name": "MERKLE_TREE_SEED",
930
+ "type": "bytes",
931
+ "value": "[109, 101, 114, 107, 108, 101, 95, 116, 114, 101, 101]"
932
+ },
933
+ {
934
+ "name": "NULLIFIER_SEED",
935
+ "type": "bytes",
936
+ "value": "[110, 117, 108, 108, 105, 102, 105, 101, 114]"
937
+ },
938
+ {
939
+ "name": "POOL_SEED",
940
+ "type": "bytes",
941
+ "value": "[104, 117, 108, 97, 95, 112, 111, 111, 108]"
942
+ },
943
+ {
944
+ "name": "VAULT_SEED",
945
+ "type": "bytes",
946
+ "value": "[118, 97, 117, 108, 116]"
947
+ }
948
+ ]
949
+ };
950
+ var idl_default = HulaPrivacyIdl;
951
+
952
+ // src/crypto.ts
953
+ var import_circomlibjs = require("circomlibjs");
954
+ var nacl = __toESM(require("tweetnacl"));
955
+ var import_sha256 = require("@noble/hashes/sha256");
956
+
957
+ // src/constants.ts
958
+ var import_web3 = require("@solana/web3.js");
959
+ var PROGRAM_ID = new import_web3.PublicKey("tnJ9AAxNau3BRBTe7NzXpv8DeYyiogvGd8YPnCiuarA");
960
+ var TOKEN_2022_PROGRAM_ID = new import_web3.PublicKey(
961
+ "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
962
+ );
963
+ var POOL_SEED = Buffer.from("hula_pool");
964
+ var VAULT_SEED = Buffer.from("vault");
965
+ var MERKLE_TREE_SEED = Buffer.from("merkle_tree");
966
+ var NULLIFIER_SEED = Buffer.from("nullifier");
967
+ var NUM_INPUT_UTXOS = 2;
968
+ var NUM_OUTPUT_UTXOS = 2;
969
+ var MERKLE_TREE_DEPTH = 10;
970
+ var MAX_LEAVES = 1 << MERKLE_TREE_DEPTH;
971
+ var PROOF_SIZE = 256;
972
+ var DOMAIN_OWNER = 0n;
973
+ var DOMAIN_VIEWING = 1n;
974
+ var DOMAIN_NULLIFIER = 2n;
975
+ var DOMAIN_ENCRYPTION = 3n;
976
+ var FIELD_PRIME = BigInt(
977
+ "21888242871839275222246405745257275088548364400416034343698204186575808495617"
978
+ );
979
+ function getPoolPDA() {
980
+ return import_web3.PublicKey.findProgramAddressSync([POOL_SEED], PROGRAM_ID);
981
+ }
982
+ function getMerkleTreePDA(treeIndex = 0) {
983
+ const treeIndexBuffer = Buffer.alloc(4);
984
+ treeIndexBuffer.writeUInt32LE(treeIndex, 0);
985
+ return import_web3.PublicKey.findProgramAddressSync(
986
+ [MERKLE_TREE_SEED, treeIndexBuffer],
987
+ PROGRAM_ID
988
+ );
989
+ }
990
+ function getVaultPDA(mint) {
991
+ return import_web3.PublicKey.findProgramAddressSync(
992
+ [VAULT_SEED, mint.toBytes()],
993
+ PROGRAM_ID
994
+ );
995
+ }
996
+ function getNullifierPDA(nullifier) {
997
+ return import_web3.PublicKey.findProgramAddressSync(
998
+ [NULLIFIER_SEED, nullifier],
999
+ PROGRAM_ID
1000
+ );
1001
+ }
1002
+
1003
+ // src/crypto.ts
1004
+ var poseidonInstance = null;
1005
+ async function initPoseidon() {
1006
+ if (!poseidonInstance) {
1007
+ poseidonInstance = await (0, import_circomlibjs.buildPoseidon)();
1008
+ }
1009
+ }
1010
+ function isPoseidonInitialized() {
1011
+ return poseidonInstance !== null;
1012
+ }
1013
+ function getPoseidon() {
1014
+ if (!poseidonInstance) {
1015
+ throw new Error("Poseidon not initialized. Call initPoseidon() first.");
1016
+ }
1017
+ return poseidonInstance;
1018
+ }
1019
+ function poseidonHash(inputs) {
1020
+ const poseidon = getPoseidon();
1021
+ const hash = poseidon(inputs.map((x) => poseidon.F.e(x)));
1022
+ return poseidon.F.toObject(hash);
1023
+ }
1024
+ function bytesToBigInt(bytes) {
1025
+ let result = 0n;
1026
+ for (let i = 0; i < bytes.length; i++) {
1027
+ result = (result << 8n) + BigInt(bytes[i]);
1028
+ }
1029
+ return result;
1030
+ }
1031
+ function bigIntToBytes(value, length) {
1032
+ const bytes = new Uint8Array(length);
1033
+ let v = value;
1034
+ for (let i = length - 1; i >= 0; i--) {
1035
+ bytes[i] = Number(v & 0xffn);
1036
+ v >>= 8n;
1037
+ }
1038
+ return bytes;
1039
+ }
1040
+ function bigIntToBytes32(value) {
1041
+ return bigIntToBytes(value, 32);
1042
+ }
1043
+ function hexToBytes(hex) {
1044
+ const cleaned = hex.startsWith("0x") ? hex.slice(2) : hex;
1045
+ const bytes = new Uint8Array(cleaned.length / 2);
1046
+ for (let i = 0; i < bytes.length; i++) {
1047
+ bytes[i] = parseInt(cleaned.substr(i * 2, 2), 16);
1048
+ }
1049
+ return bytes;
1050
+ }
1051
+ function bytesToHex(bytes) {
1052
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1053
+ }
1054
+ function pubkeyToBigInt(pubkey) {
1055
+ return bytesToBigInt(pubkey.toBytes());
1056
+ }
1057
+ function generateSpendingKey() {
1058
+ const randomBytes2 = nacl.randomBytes(32);
1059
+ return bytesToBigInt(randomBytes2) % 2n ** 253n;
1060
+ }
1061
+ function deriveKeys(spendingKey) {
1062
+ const owner = poseidonHash([spendingKey, DOMAIN_OWNER]);
1063
+ const viewingKey = poseidonHash([spendingKey, DOMAIN_VIEWING]);
1064
+ const nullifierKey = poseidonHash([spendingKey, DOMAIN_NULLIFIER]);
1065
+ const encryptionSeed = poseidonHash([spendingKey, DOMAIN_ENCRYPTION]);
1066
+ const seedBytes = bigIntToBytes(encryptionSeed, 32);
1067
+ const hashedSeed = (0, import_sha256.sha256)(seedBytes);
1068
+ const encryptionKeyPair = nacl.box.keyPair.fromSecretKey(hashedSeed);
1069
+ return {
1070
+ spendingKey,
1071
+ owner,
1072
+ viewingKey,
1073
+ nullifierKey,
1074
+ encryptionKeyPair
1075
+ };
1076
+ }
1077
+ function deriveSpendingKeyFromSignature(signature) {
1078
+ if (signature.length < 64) {
1079
+ throw new Error("Signature must be at least 64 bytes");
1080
+ }
1081
+ const hash = (0, import_sha256.sha256)(signature);
1082
+ return bytesToBigInt(hash) % FIELD_PRIME;
1083
+ }
1084
+ function encryptNote(noteData, recipientEncryptionPubKey) {
1085
+ const mintPrefix = noteData.mintTokenAddress.toString(16).padStart(64, "0").slice(0, 16);
1086
+ const payload = {
1087
+ v: noteData.value.toString(),
1088
+ // value
1089
+ m: mintPrefix,
1090
+ // mint (first 8 bytes as hex)
1091
+ s: noteData.secret.toString()
1092
+ // secret
1093
+ // leafIndex omitted - recipient queries relayer with computed commitment
1094
+ };
1095
+ const message = new TextEncoder().encode(JSON.stringify(payload));
1096
+ const nonce = nacl.randomBytes(24);
1097
+ const ephemeralKeyPair = nacl.box.keyPair();
1098
+ const ciphertext = nacl.box(
1099
+ message,
1100
+ nonce,
1101
+ recipientEncryptionPubKey,
1102
+ ephemeralKeyPair.secretKey
1103
+ );
1104
+ return {
1105
+ ephemeralPubKey: ephemeralKeyPair.publicKey,
1106
+ ciphertext,
1107
+ nonce
1108
+ };
1109
+ }
1110
+ function decryptNote(encryptedNote, encryptionSecretKey) {
1111
+ try {
1112
+ const decrypted = nacl.box.open(
1113
+ encryptedNote.ciphertext,
1114
+ encryptedNote.nonce,
1115
+ encryptedNote.ephemeralPubKey,
1116
+ encryptionSecretKey
1117
+ );
1118
+ if (!decrypted) return null;
1119
+ const noteData = JSON.parse(new TextDecoder().decode(decrypted));
1120
+ if (noteData.v !== void 0) {
1121
+ return {
1122
+ value: BigInt(noteData.v),
1123
+ mintPrefix: noteData.m,
1124
+ // First 8 bytes as hex string
1125
+ secret: BigInt(noteData.s)
1126
+ };
1127
+ } else {
1128
+ return {
1129
+ value: BigInt(noteData.value),
1130
+ mintPrefix: BigInt(noteData.mintTokenAddress).toString(16).padStart(64, "0").slice(0, 16),
1131
+ secret: BigInt(noteData.secret)
1132
+ };
1133
+ }
1134
+ } catch {
1135
+ return null;
1136
+ }
1137
+ }
1138
+ function serializeEncryptedNote(note) {
1139
+ const totalLength = 32 + 24 + 2 + note.ciphertext.length;
1140
+ const buffer = new Uint8Array(totalLength);
1141
+ buffer.set(note.ephemeralPubKey, 0);
1142
+ buffer.set(note.nonce, 32);
1143
+ buffer[56] = note.ciphertext.length >> 8 & 255;
1144
+ buffer[57] = note.ciphertext.length & 255;
1145
+ buffer.set(note.ciphertext, 58);
1146
+ return buffer;
1147
+ }
1148
+ function deserializeEncryptedNote(data) {
1149
+ const ephemeralPubKey = data.slice(0, 32);
1150
+ const nonce = data.slice(32, 56);
1151
+ const ciphertextLength = data[56] << 8 | data[57];
1152
+ const ciphertext = data.slice(58, 58 + ciphertextLength);
1153
+ return { ephemeralPubKey, nonce, ciphertext };
1154
+ }
1155
+ function formatCommitment(value, length = 16) {
1156
+ if (value instanceof Uint8Array) {
1157
+ return `0x${bytesToHex(value).slice(0, length)}...`;
1158
+ }
1159
+ return `0x${value.toString(16).padStart(64, "0").slice(0, length)}...`;
1160
+ }
1161
+
1162
+ // src/api.ts
1163
+ var DEFAULT_API_URL = "http://localhost:3001";
1164
+ var RelayerClient = class {
1165
+ baseUrl;
1166
+ constructor(baseUrl = DEFAULT_API_URL) {
1167
+ this.baseUrl = baseUrl.replace(/\/$/, "");
1168
+ }
1169
+ async fetch(path2, init) {
1170
+ const response = await fetch(`${this.baseUrl}${path2}`, {
1171
+ ...init,
1172
+ headers: {
1173
+ "Content-Type": "application/json",
1174
+ ...init?.headers
1175
+ }
1176
+ });
1177
+ if (!response.ok) {
1178
+ const errorData = await response.json().catch(() => ({ error: "Unknown error" }));
1179
+ throw new Error(errorData.error || `HTTP ${response.status}`);
1180
+ }
1181
+ return response.json();
1182
+ }
1183
+ // ============================================================================
1184
+ // Health & Stats
1185
+ // ============================================================================
1186
+ /**
1187
+ * Health check
1188
+ */
1189
+ async health() {
1190
+ return this.fetch("/health");
1191
+ }
1192
+ /**
1193
+ * Get protocol stats
1194
+ */
1195
+ async getStats() {
1196
+ return this.fetch("/api/stats");
1197
+ }
1198
+ // ============================================================================
1199
+ // Pool
1200
+ // ============================================================================
1201
+ /**
1202
+ * Get pool data
1203
+ */
1204
+ async getPool() {
1205
+ return this.fetch("/api/pool");
1206
+ }
1207
+ // ============================================================================
1208
+ // Trees
1209
+ // ============================================================================
1210
+ /**
1211
+ * Get all merkle trees
1212
+ */
1213
+ async getTrees() {
1214
+ return this.fetch("/api/trees");
1215
+ }
1216
+ /**
1217
+ * Get tree by index
1218
+ */
1219
+ async getTree(treeIndex) {
1220
+ return this.fetch(`/api/trees/${treeIndex}`);
1221
+ }
1222
+ // ============================================================================
1223
+ // Leaves / Commitments
1224
+ // ============================================================================
1225
+ /**
1226
+ * Get leaves with cursor pagination
1227
+ */
1228
+ async getLeaves(cursor, limit, treeIndex) {
1229
+ const params = new URLSearchParams();
1230
+ if (cursor) params.set("cursor", cursor);
1231
+ if (limit) params.set("limit", String(limit));
1232
+ if (treeIndex !== void 0) params.set("treeIndex", String(treeIndex));
1233
+ return this.fetch(`/api/leaves?${params}`);
1234
+ }
1235
+ /**
1236
+ * Get leaves after a specific index (for syncing)
1237
+ */
1238
+ async getLeavesAfter(treeIndex, afterLeafIndex, limit) {
1239
+ const params = new URLSearchParams();
1240
+ params.set("treeIndex", String(treeIndex));
1241
+ params.set("afterLeafIndex", String(afterLeafIndex));
1242
+ if (limit) params.set("limit", String(limit));
1243
+ return this.fetch(`/api/leaves/after?${params}`);
1244
+ }
1245
+ /**
1246
+ * Get all leaves for a tree (auto-paginated)
1247
+ */
1248
+ async getAllLeavesForTree(treeIndex) {
1249
+ const allLeaves = [];
1250
+ let cursor;
1251
+ while (true) {
1252
+ const response = await this.getLeaves(cursor, 100, treeIndex);
1253
+ allLeaves.push(...response.items);
1254
+ if (!response.hasMore || !response.nextCursor) break;
1255
+ cursor = response.nextCursor;
1256
+ }
1257
+ return allLeaves;
1258
+ }
1259
+ /**
1260
+ * Get all commitment values for a tree as bigints
1261
+ */
1262
+ async getCommitmentsForTree(treeIndex) {
1263
+ const leaves = await this.getAllLeavesForTree(treeIndex);
1264
+ leaves.sort((a, b) => a.leafIndex - b.leafIndex);
1265
+ return leaves.map((l) => {
1266
+ const commitment = l.commitment.startsWith("0x") ? l.commitment : `0x${l.commitment}`;
1267
+ return BigInt(commitment);
1268
+ });
1269
+ }
1270
+ // ============================================================================
1271
+ // Transactions
1272
+ // ============================================================================
1273
+ /**
1274
+ * Get transactions with cursor pagination
1275
+ */
1276
+ async getTransactions(cursor, limit, mint) {
1277
+ const params = new URLSearchParams();
1278
+ if (cursor) params.set("cursor", cursor);
1279
+ if (limit) params.set("limit", String(limit));
1280
+ if (mint) params.set("mint", mint);
1281
+ return this.fetch(`/api/transactions?${params}`);
1282
+ }
1283
+ /**
1284
+ * Get transaction by signature
1285
+ */
1286
+ async getTransaction(signature) {
1287
+ return this.fetch(`/api/transactions/${signature}`);
1288
+ }
1289
+ // ============================================================================
1290
+ // Nullifiers
1291
+ // ============================================================================
1292
+ /**
1293
+ * Check if nullifier is spent
1294
+ */
1295
+ async checkNullifier(nullifier) {
1296
+ return this.fetch(`/api/nullifiers/check?nullifier=${encodeURIComponent(nullifier)}`);
1297
+ }
1298
+ /**
1299
+ * Check multiple nullifiers at once
1300
+ */
1301
+ async checkNullifiersBatch(nullifiers) {
1302
+ return this.fetch("/api/nullifiers/check-batch", {
1303
+ method: "POST",
1304
+ body: JSON.stringify({ nullifiers })
1305
+ });
1306
+ }
1307
+ // ============================================================================
1308
+ // Encrypted Notes
1309
+ // ============================================================================
1310
+ /**
1311
+ * Get encrypted notes with cursor pagination
1312
+ */
1313
+ async getNotes(cursor, limit, afterSlot) {
1314
+ const params = new URLSearchParams();
1315
+ if (cursor) params.set("cursor", cursor);
1316
+ if (limit) params.set("limit", String(limit));
1317
+ if (afterSlot) params.set("afterSlot", afterSlot);
1318
+ return this.fetch(`/api/notes?${params}`);
1319
+ }
1320
+ /**
1321
+ * Get all notes (auto-paginated)
1322
+ */
1323
+ async getAllNotes(afterSlot) {
1324
+ const allNotes = [];
1325
+ let cursor;
1326
+ while (true) {
1327
+ const response = await this.getNotes(cursor, 100, afterSlot);
1328
+ allNotes.push(...response.items);
1329
+ if (!response.hasMore || !response.nextCursor) break;
1330
+ cursor = response.nextCursor;
1331
+ }
1332
+ return allNotes;
1333
+ }
1334
+ };
1335
+ var defaultClient = null;
1336
+ function getRelayerClient(url) {
1337
+ if (url) {
1338
+ return new RelayerClient(url);
1339
+ }
1340
+ if (!defaultClient) {
1341
+ defaultClient = new RelayerClient();
1342
+ }
1343
+ return defaultClient;
1344
+ }
1345
+ function setDefaultRelayerUrl(url) {
1346
+ defaultClient = new RelayerClient(url);
1347
+ }
1348
+
1349
+ // src/utxo.ts
1350
+ var import_web32 = require("@solana/web3.js");
1351
+ function computeCommitment(value, mintTokenAddress, owner, secret) {
1352
+ return poseidonHash([value, mintTokenAddress, owner, secret]);
1353
+ }
1354
+ function computeNullifier(spendingKey, commitment, leafIndex) {
1355
+ const nullifierKey = poseidonHash([spendingKey, DOMAIN_NULLIFIER]);
1356
+ return poseidonHash([nullifierKey, commitment, BigInt(leafIndex)]);
1357
+ }
1358
+ function computeNullifierFromKeys(keys, commitment, leafIndex) {
1359
+ return poseidonHash([keys.nullifierKey, commitment, BigInt(leafIndex)]);
1360
+ }
1361
+ function createUTXO(value, mintTokenAddress, owner, leafIndex, treeIndex = 0) {
1362
+ const secret = generateSpendingKey();
1363
+ const commitment = computeCommitment(value, mintTokenAddress, owner, secret);
1364
+ return {
1365
+ value,
1366
+ mintTokenAddress,
1367
+ owner,
1368
+ secret,
1369
+ commitment,
1370
+ leafIndex,
1371
+ treeIndex
1372
+ };
1373
+ }
1374
+ function createDummyUTXO() {
1375
+ return {
1376
+ value: 0n,
1377
+ mintTokenAddress: 0n,
1378
+ owner: 0n,
1379
+ secret: 0n,
1380
+ commitment: 0n,
1381
+ leafIndex: 0,
1382
+ treeIndex: 0
1383
+ };
1384
+ }
1385
+ function serializeUTXO(utxo, mint, createdTx, spent = false, spentTx) {
1386
+ return {
1387
+ value: utxo.value.toString(),
1388
+ mintTokenAddress: utxo.mintTokenAddress.toString(),
1389
+ owner: utxo.owner.toString(),
1390
+ secret: utxo.secret.toString(),
1391
+ commitment: utxo.commitment.toString(),
1392
+ leafIndex: utxo.leafIndex,
1393
+ treeIndex: utxo.treeIndex,
1394
+ mint,
1395
+ spent,
1396
+ spentTx,
1397
+ createdTx,
1398
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1399
+ };
1400
+ }
1401
+ function deserializeUTXO(data) {
1402
+ return {
1403
+ value: BigInt(data.value),
1404
+ mintTokenAddress: BigInt(data.mintTokenAddress),
1405
+ owner: BigInt(data.owner),
1406
+ secret: BigInt(data.secret),
1407
+ commitment: BigInt(data.commitment),
1408
+ leafIndex: data.leafIndex,
1409
+ treeIndex: data.treeIndex
1410
+ };
1411
+ }
1412
+ function scanNotesForUTXOs(notes, walletKeys, commitments) {
1413
+ const foundUTXOs = [];
1414
+ for (const note of notes) {
1415
+ try {
1416
+ const noteBytes = hexToBytes(note.encryptedData);
1417
+ const encryptedNote = deserializeEncryptedNote(noteBytes);
1418
+ const decrypted = decryptNote(encryptedNote, walletKeys.encryptionKeyPair.secretKey);
1419
+ if (!decrypted) continue;
1420
+ const mintAddress = note.mint;
1421
+ const mintTokenAddress = pubkeyToBigInt(new import_web32.PublicKey(mintAddress));
1422
+ const commitment = computeCommitment(
1423
+ decrypted.value,
1424
+ mintTokenAddress,
1425
+ walletKeys.owner,
1426
+ decrypted.secret
1427
+ );
1428
+ let foundLeafIndex;
1429
+ const treeCommitments = commitments.get(note.treeIndex);
1430
+ if (treeCommitments) {
1431
+ for (const [leafIndex, onChainCommitment] of treeCommitments.entries()) {
1432
+ if (onChainCommitment === commitment) {
1433
+ foundLeafIndex = leafIndex;
1434
+ break;
1435
+ }
1436
+ }
1437
+ }
1438
+ if (foundLeafIndex === void 0) {
1439
+ continue;
1440
+ }
1441
+ foundUTXOs.push({
1442
+ value: decrypted.value.toString(),
1443
+ mintTokenAddress: mintTokenAddress.toString(),
1444
+ owner: walletKeys.owner.toString(),
1445
+ secret: decrypted.secret.toString(),
1446
+ commitment: commitment.toString(),
1447
+ leafIndex: foundLeafIndex,
1448
+ treeIndex: note.treeIndex,
1449
+ mint: note.mint,
1450
+ spent: false,
1451
+ createdTx: note.txSignature,
1452
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1453
+ });
1454
+ } catch (err) {
1455
+ }
1456
+ }
1457
+ return foundUTXOs;
1458
+ }
1459
+ async function syncUTXOs(walletKeys, existingUTXOs, relayerUrl, afterSlot) {
1460
+ const client = getRelayerClient(relayerUrl);
1461
+ const notes = await client.getAllNotes(afterSlot);
1462
+ const pool = await client.getPool();
1463
+ const commitments = /* @__PURE__ */ new Map();
1464
+ for (let treeIndex = 0; treeIndex <= pool.currentTreeIndex; treeIndex++) {
1465
+ const leaves = await client.getCommitmentsForTree(treeIndex);
1466
+ const treeMap = /* @__PURE__ */ new Map();
1467
+ leaves.forEach((commitment, index) => {
1468
+ treeMap.set(index, commitment);
1469
+ });
1470
+ commitments.set(treeIndex, treeMap);
1471
+ }
1472
+ const newUTXOs = scanNotesForUTXOs(
1473
+ notes.map((n) => ({
1474
+ encryptedData: n.encryptedData,
1475
+ treeIndex: n.treeIndex,
1476
+ mint: n.mint,
1477
+ txSignature: n.txSignature
1478
+ })),
1479
+ walletKeys,
1480
+ commitments
1481
+ );
1482
+ const allUTXOs = [...newUTXOs, ...existingUTXOs];
1483
+ const unspentUTXOs = allUTXOs.filter((u) => !u.spent);
1484
+ const nullifiersToCheck = [];
1485
+ const utxoByNullifier = /* @__PURE__ */ new Map();
1486
+ for (const utxo of unspentUTXOs) {
1487
+ const commitment = BigInt(utxo.commitment);
1488
+ const nullifier = computeNullifier(
1489
+ walletKeys.spendingKey,
1490
+ commitment,
1491
+ utxo.leafIndex
1492
+ );
1493
+ const nullifierStr = nullifier.toString(16).padStart(64, "0");
1494
+ nullifiersToCheck.push(nullifierStr);
1495
+ utxoByNullifier.set(nullifierStr, utxo.commitment);
1496
+ }
1497
+ const spentUTXOs = [];
1498
+ if (nullifiersToCheck.length > 0) {
1499
+ const batchSize = 50;
1500
+ for (let i = 0; i < nullifiersToCheck.length; i += batchSize) {
1501
+ const batch = nullifiersToCheck.slice(i, i + batchSize);
1502
+ const result = await client.checkNullifiersBatch(batch);
1503
+ for (const r of result.results) {
1504
+ if (r.spent) {
1505
+ const utxoId = utxoByNullifier.get(r.nullifier);
1506
+ if (utxoId) {
1507
+ spentUTXOs.push(utxoId);
1508
+ }
1509
+ }
1510
+ }
1511
+ }
1512
+ }
1513
+ return { newUTXOs, spentUTXOs };
1514
+ }
1515
+ function selectUTXOs(utxos, targetAmount, mint) {
1516
+ const filtered = mint !== void 0 ? utxos.filter((u) => u.mintTokenAddress === mint) : utxos;
1517
+ const sorted = [...filtered].sort((a, b) => {
1518
+ if (a.value > b.value) return -1;
1519
+ if (a.value < b.value) return 1;
1520
+ return 0;
1521
+ });
1522
+ const selected = [];
1523
+ let total = 0n;
1524
+ for (const utxo of sorted) {
1525
+ if (total >= targetAmount) break;
1526
+ selected.push(utxo);
1527
+ total += utxo.value;
1528
+ }
1529
+ if (total < targetAmount) {
1530
+ throw new Error(
1531
+ `Insufficient balance. Need ${targetAmount}, have ${total}`
1532
+ );
1533
+ }
1534
+ return selected;
1535
+ }
1536
+ function calculateBalance(utxos, mint) {
1537
+ const filtered = mint !== void 0 ? utxos.filter((u) => u.mintTokenAddress === mint) : utxos;
1538
+ return filtered.reduce((sum, u) => sum + u.value, 0n);
1539
+ }
1540
+
1541
+ // src/transaction.ts
1542
+ var import_web33 = require("@solana/web3.js");
1543
+ var import_spl_token = require("@solana/spl-token");
1544
+ var import_anchor = require("@coral-xyz/anchor");
1545
+
1546
+ // src/merkle.ts
1547
+ var ZEROS = [];
1548
+ function computeZeros() {
1549
+ if (!isPoseidonInitialized()) {
1550
+ throw new Error("Poseidon not initialized. Call initPoseidon() first.");
1551
+ }
1552
+ if (ZEROS.length > 0) {
1553
+ return ZEROS;
1554
+ }
1555
+ const zeros = [0n];
1556
+ const poseidon = getPoseidon();
1557
+ for (let i = 0; i < MERKLE_TREE_DEPTH; i++) {
1558
+ const hash = poseidon([zeros[i], zeros[i]]);
1559
+ zeros.push(poseidon.F.toObject(hash));
1560
+ }
1561
+ ZEROS = zeros;
1562
+ return zeros;
1563
+ }
1564
+ function getZeroAtLevel(level) {
1565
+ if (ZEROS.length === 0) {
1566
+ computeZeros();
1567
+ }
1568
+ return ZEROS[level];
1569
+ }
1570
+ function getEmptyTreeRoot() {
1571
+ if (ZEROS.length === 0) {
1572
+ computeZeros();
1573
+ }
1574
+ return ZEROS[MERKLE_TREE_DEPTH];
1575
+ }
1576
+ function computeMerklePathFromLeaves(leafIndex, leaves) {
1577
+ if (!isPoseidonInitialized()) {
1578
+ throw new Error("Poseidon not initialized. Call initPoseidon() first.");
1579
+ }
1580
+ const zeros = computeZeros();
1581
+ const pathElements = [];
1582
+ const pathIndices = [];
1583
+ const poseidon = getPoseidon();
1584
+ let currentLevel = [...leaves];
1585
+ const treeSize = 1 << MERKLE_TREE_DEPTH;
1586
+ while (currentLevel.length < treeSize) {
1587
+ currentLevel.push(zeros[0]);
1588
+ }
1589
+ let targetIndex = leafIndex;
1590
+ for (let level = 0; level < MERKLE_TREE_DEPTH; level++) {
1591
+ const isLeft = (targetIndex & 1) === 0;
1592
+ pathIndices.push(isLeft ? 0 : 1);
1593
+ const siblingIndex = isLeft ? targetIndex + 1 : targetIndex - 1;
1594
+ pathElements.push(currentLevel[siblingIndex]);
1595
+ const nextLevel = [];
1596
+ for (let i = 0; i < currentLevel.length; i += 2) {
1597
+ const left = currentLevel[i];
1598
+ const right = currentLevel[i + 1] ?? zeros[level];
1599
+ const hash = poseidon([left, right]);
1600
+ nextLevel.push(poseidon.F.toObject(hash));
1601
+ }
1602
+ currentLevel = nextLevel;
1603
+ targetIndex = Math.floor(targetIndex / 2);
1604
+ }
1605
+ return { pathElements, pathIndices, root: currentLevel[0] };
1606
+ }
1607
+ function computeMerkleRoot(leaves) {
1608
+ if (leaves.length === 0) {
1609
+ return getEmptyTreeRoot();
1610
+ }
1611
+ const { root } = computeMerklePathFromLeaves(0, leaves);
1612
+ return root;
1613
+ }
1614
+ function verifyMerklePath(leafValue, path2) {
1615
+ if (!isPoseidonInitialized()) {
1616
+ throw new Error("Poseidon not initialized. Call initPoseidon() first.");
1617
+ }
1618
+ let current = leafValue;
1619
+ const poseidon = getPoseidon();
1620
+ for (let i = 0; i < path2.pathElements.length; i++) {
1621
+ const sibling = path2.pathElements[i];
1622
+ const isLeft = path2.pathIndices[i] === 0;
1623
+ const left = isLeft ? current : sibling;
1624
+ const right = isLeft ? sibling : current;
1625
+ const hash = poseidon([left, right]);
1626
+ current = poseidon.F.toObject(hash);
1627
+ }
1628
+ return current === path2.root;
1629
+ }
1630
+ async function fetchMerklePath(treeIndex, leafIndex, relayerUrl) {
1631
+ const client = getRelayerClient(relayerUrl);
1632
+ const leaves = await client.getCommitmentsForTree(treeIndex);
1633
+ if (leafIndex >= leaves.length) {
1634
+ throw new Error(`Leaf index ${leafIndex} not found in tree ${treeIndex} (has ${leaves.length} leaves)`);
1635
+ }
1636
+ return computeMerklePathFromLeaves(leafIndex, leaves);
1637
+ }
1638
+ async function fetchMerkleRoot(treeIndex, relayerUrl) {
1639
+ const client = getRelayerClient(relayerUrl);
1640
+ const tree = await client.getTree(treeIndex);
1641
+ const rootStr = tree.root.startsWith("0x") ? tree.root : `0x${tree.root}`;
1642
+ return BigInt(rootStr);
1643
+ }
1644
+ async function getNextLeafIndex(treeIndex, relayerUrl) {
1645
+ const client = getRelayerClient(relayerUrl);
1646
+ const tree = await client.getTree(treeIndex);
1647
+ return tree.nextIndex;
1648
+ }
1649
+ async function getCurrentTreeIndex(relayerUrl) {
1650
+ const client = getRelayerClient(relayerUrl);
1651
+ const pool = await client.getPool();
1652
+ return pool.currentTreeIndex;
1653
+ }
1654
+
1655
+ // src/proof.ts
1656
+ var import_fs = require("fs");
1657
+ var import_child_process = require("child_process");
1658
+ var import_path = __toESM(require("path"));
1659
+ var circuitWasmPath = null;
1660
+ var circuitZkeyPath = null;
1661
+ function setCircuitPaths(wasmPath, zkeyPath) {
1662
+ circuitWasmPath = wasmPath;
1663
+ circuitZkeyPath = zkeyPath;
1664
+ }
1665
+ function getCircuitPaths() {
1666
+ if (circuitWasmPath && circuitZkeyPath) {
1667
+ return { wasmPath: circuitWasmPath, zkeyPath: circuitZkeyPath };
1668
+ }
1669
+ const possibleBasePaths = [
1670
+ import_path.default.join(process.cwd(), "circuits", "build"),
1671
+ import_path.default.join(process.cwd(), "..", "circuits", "build"),
1672
+ import_path.default.join(process.cwd(), "assets")
1673
+ ];
1674
+ for (const basePath of possibleBasePaths) {
1675
+ const wasmPath = import_path.default.join(basePath, "transaction_js", "transaction.wasm");
1676
+ const zkeyPath = import_path.default.join(basePath, "keys", "transaction_final.zkey");
1677
+ if ((0, import_fs.existsSync)(wasmPath) && (0, import_fs.existsSync)(zkeyPath)) {
1678
+ return { wasmPath, zkeyPath };
1679
+ }
1680
+ const altWasmPath = import_path.default.join(basePath, "transaction.wasm");
1681
+ const altZkeyPath = import_path.default.join(basePath, "transaction_final.zkey");
1682
+ if ((0, import_fs.existsSync)(altWasmPath) && (0, import_fs.existsSync)(altZkeyPath)) {
1683
+ return { wasmPath: altWasmPath, zkeyPath: altZkeyPath };
1684
+ }
1685
+ }
1686
+ throw new Error(
1687
+ "Circuit files not found. Either:\n1. Run 'make' in circuits/ directory to build them, or\n2. Call setCircuitPaths() with the correct paths"
1688
+ );
1689
+ }
1690
+ function verifyCircuitFiles() {
1691
+ const { wasmPath, zkeyPath } = getCircuitPaths();
1692
+ if (!(0, import_fs.existsSync)(wasmPath)) {
1693
+ throw new Error(`Circuit WASM not found: ${wasmPath}`);
1694
+ }
1695
+ if (!(0, import_fs.existsSync)(zkeyPath)) {
1696
+ throw new Error(`Circuit zkey not found: ${zkeyPath}`);
1697
+ }
1698
+ }
1699
+ function toBigEndianBytes(decimalStr) {
1700
+ let hex = BigInt(decimalStr).toString(16);
1701
+ hex = hex.padStart(64, "0");
1702
+ const bytes = new Uint8Array(32);
1703
+ for (let i = 0; i < 32; i++) {
1704
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
1705
+ }
1706
+ return bytes;
1707
+ }
1708
+ function parseProof(proofJson) {
1709
+ const proof = new Uint8Array(PROOF_SIZE);
1710
+ const pi_a_x = toBigEndianBytes(proofJson.pi_a[0]);
1711
+ const pi_a_y = toBigEndianBytes(proofJson.pi_a[1]);
1712
+ proof.set(pi_a_x, 0);
1713
+ proof.set(pi_a_y, 32);
1714
+ const pi_b_x0 = toBigEndianBytes(proofJson.pi_b[0][1]);
1715
+ const pi_b_x1 = toBigEndianBytes(proofJson.pi_b[0][0]);
1716
+ const pi_b_y0 = toBigEndianBytes(proofJson.pi_b[1][1]);
1717
+ const pi_b_y1 = toBigEndianBytes(proofJson.pi_b[1][0]);
1718
+ proof.set(pi_b_x0, 64);
1719
+ proof.set(pi_b_x1, 96);
1720
+ proof.set(pi_b_y0, 128);
1721
+ proof.set(pi_b_y1, 160);
1722
+ const pi_c_x = toBigEndianBytes(proofJson.pi_c[0]);
1723
+ const pi_c_y = toBigEndianBytes(proofJson.pi_c[1]);
1724
+ proof.set(pi_c_x, 192);
1725
+ proof.set(pi_c_y, 224);
1726
+ return proof;
1727
+ }
1728
+ async function generateProof(circuitInputs) {
1729
+ verifyCircuitFiles();
1730
+ const { wasmPath, zkeyPath } = getCircuitPaths();
1731
+ const tempDir = import_path.default.dirname(zkeyPath);
1732
+ const timestamp = Date.now();
1733
+ const inputPath = import_path.default.join(tempDir, `temp_input_${timestamp}.json`);
1734
+ const witnessPath = import_path.default.join(tempDir, `temp_witness_${timestamp}.wtns`);
1735
+ const proofPath = import_path.default.join(tempDir, `temp_proof_${timestamp}.json`);
1736
+ const publicPath = import_path.default.join(tempDir, `temp_public_${timestamp}.json`);
1737
+ try {
1738
+ (0, import_fs.writeFileSync)(inputPath, JSON.stringify(circuitInputs));
1739
+ const witnessGenScript = import_path.default.join(
1740
+ import_path.default.dirname(wasmPath),
1741
+ "generate_witness.cjs"
1742
+ );
1743
+ if (!(0, import_fs.existsSync)(witnessGenScript)) {
1744
+ throw new Error(`Witness generation script not found: ${witnessGenScript}`);
1745
+ }
1746
+ (0, import_child_process.execSync)(`node ${witnessGenScript} ${wasmPath} ${inputPath} ${witnessPath}`, {
1747
+ stdio: "pipe",
1748
+ cwd: tempDir
1749
+ });
1750
+ (0, import_child_process.execSync)(
1751
+ `npx snarkjs groth16 prove ${zkeyPath} ${witnessPath} ${proofPath} ${publicPath}`,
1752
+ {
1753
+ stdio: "pipe",
1754
+ cwd: tempDir
1755
+ }
1756
+ );
1757
+ const proofJson = JSON.parse((0, import_fs.readFileSync)(proofPath, "utf-8"));
1758
+ const publicSignals = JSON.parse((0, import_fs.readFileSync)(publicPath, "utf-8"));
1759
+ const proof = parseProof(proofJson);
1760
+ return { proof, publicSignals };
1761
+ } finally {
1762
+ const cleanup = (filePath) => {
1763
+ try {
1764
+ if ((0, import_fs.existsSync)(filePath)) (0, import_fs.unlinkSync)(filePath);
1765
+ } catch {
1766
+ }
1767
+ };
1768
+ cleanup(inputPath);
1769
+ cleanup(witnessPath);
1770
+ cleanup(proofPath);
1771
+ cleanup(publicPath);
1772
+ }
1773
+ }
1774
+ async function generateProofInMemory(circuitInputs) {
1775
+ verifyCircuitFiles();
1776
+ const { wasmPath, zkeyPath } = getCircuitPaths();
1777
+ const snarkjs = await import("snarkjs");
1778
+ const { proof: proofData, publicSignals } = await snarkjs.groth16.fullProve(
1779
+ circuitInputs,
1780
+ wasmPath,
1781
+ zkeyPath
1782
+ );
1783
+ const proof = parseProof(proofData);
1784
+ return { proof, publicSignals };
1785
+ }
1786
+
1787
+ // src/transaction.ts
1788
+ async function buildTransaction(request, walletKeys, relayerUrl) {
1789
+ const client = getRelayerClient(relayerUrl);
1790
+ const pool = await client.getPool();
1791
+ const currentTreeIndex = pool.currentTreeIndex;
1792
+ const depositAmount = request.depositAmount ?? 0n;
1793
+ const withdrawAmount = request.withdrawAmount ?? 0n;
1794
+ const fee = request.fee ?? 0n;
1795
+ const inputUtxos = request.inputUtxos ?? [];
1796
+ const outputs = request.outputs ?? [];
1797
+ if (depositAmount === 0n && inputUtxos.length === 0) {
1798
+ throw new Error("Either depositAmount or inputUtxos is required");
1799
+ }
1800
+ if (withdrawAmount > 0n && !request.recipient) {
1801
+ throw new Error("recipient is required when withdrawing");
1802
+ }
1803
+ let inputTreeIndex = currentTreeIndex;
1804
+ if (inputUtxos.length > 0) {
1805
+ inputTreeIndex = inputUtxos[0].treeIndex;
1806
+ for (const utxo of inputUtxos) {
1807
+ if (utxo.treeIndex !== inputTreeIndex) {
1808
+ throw new Error("All input UTXOs must be from the same tree");
1809
+ }
1810
+ }
1811
+ }
1812
+ const leaves = await client.getCommitmentsForTree(inputTreeIndex);
1813
+ let merkleRoot;
1814
+ if (inputUtxos.length > 0) {
1815
+ merkleRoot = await fetchMerkleRoot(inputTreeIndex, relayerUrl);
1816
+ } else {
1817
+ merkleRoot = await fetchMerkleRoot(currentTreeIndex, relayerUrl);
1818
+ }
1819
+ const nextLeafIndex = await getNextLeafIndex(currentTreeIndex, relayerUrl);
1820
+ const totalInputValue = inputUtxos.reduce((sum, u) => sum + u.value, 0n);
1821
+ const totalAvailable = totalInputValue + depositAmount;
1822
+ const mintBigInt = pubkeyToBigInt(request.mint);
1823
+ const outputUtxos = [];
1824
+ let totalOutputValue = 0n;
1825
+ let outputIndex = 0;
1826
+ for (const output of outputs) {
1827
+ const owner = output.owner === "self" ? walletKeys.owner : output.owner;
1828
+ const utxo = createUTXO(
1829
+ output.amount,
1830
+ mintBigInt,
1831
+ owner,
1832
+ nextLeafIndex + outputIndex,
1833
+ currentTreeIndex
1834
+ );
1835
+ outputUtxos.push(utxo);
1836
+ totalOutputValue += output.amount;
1837
+ outputIndex++;
1838
+ }
1839
+ const totalOut = withdrawAmount + fee + totalOutputValue;
1840
+ const changeAmount = totalAvailable - totalOut;
1841
+ if (changeAmount < 0n) {
1842
+ throw new Error(
1843
+ `Insufficient value. Have ${totalAvailable} (inputs: ${totalInputValue}, deposit: ${depositAmount}), need ${totalOut} (outputs: ${totalOutputValue}, withdraw: ${withdrawAmount}, fee: ${fee})`
1844
+ );
1845
+ }
1846
+ if (changeAmount > 0n) {
1847
+ if (outputUtxos.length >= NUM_OUTPUT_UTXOS) {
1848
+ throw new Error("No output slot available for change");
1849
+ }
1850
+ const changeUtxo = createUTXO(
1851
+ changeAmount,
1852
+ mintBigInt,
1853
+ walletKeys.owner,
1854
+ nextLeafIndex + outputIndex,
1855
+ currentTreeIndex
1856
+ );
1857
+ outputUtxos.push(changeUtxo);
1858
+ totalOutputValue += changeAmount;
1859
+ }
1860
+ while (outputUtxos.length < NUM_OUTPUT_UTXOS) {
1861
+ outputUtxos.push(createDummyUTXO());
1862
+ }
1863
+ const circuitInputs = buildCircuitInputs(
1864
+ merkleRoot,
1865
+ inputUtxos,
1866
+ outputUtxos,
1867
+ walletKeys,
1868
+ leaves,
1869
+ depositAmount,
1870
+ withdrawAmount,
1871
+ request.recipient ?? import_web33.PublicKey.default,
1872
+ request.mint,
1873
+ fee
1874
+ );
1875
+ const { proof } = await generateProof(circuitInputs);
1876
+ const nullifiers = circuitInputs.nullifiers.map((n) => BigInt(n));
1877
+ const publicInputs = {
1878
+ merkleRoot: Array.from(bigIntToBytes32(merkleRoot)),
1879
+ nullifiers: nullifiers.map((n) => Array.from(bigIntToBytes32(n))),
1880
+ outputCommitments: outputUtxos.map((u) => Array.from(bigIntToBytes32(u.commitment))),
1881
+ publicDeposit: depositAmount,
1882
+ publicWithdraw: withdrawAmount,
1883
+ recipient: request.recipient ?? import_web33.PublicKey.default,
1884
+ mintTokenAddress: request.mint,
1885
+ fee
1886
+ };
1887
+ const encryptedNotes = [];
1888
+ for (let i = 0; i < outputs.length; i++) {
1889
+ const output = outputs[i];
1890
+ const utxo = outputUtxos[i];
1891
+ if (output.encryptionPubKey && utxo.value > 0n) {
1892
+ const encrypted = encryptNote(
1893
+ {
1894
+ value: utxo.value,
1895
+ mintTokenAddress: utxo.mintTokenAddress,
1896
+ secret: utxo.secret
1897
+ // leafIndex omitted - recipient queries relayer with computed commitment
1898
+ },
1899
+ output.encryptionPubKey
1900
+ );
1901
+ encryptedNotes.push(Buffer.from(serializeEncryptedNote(encrypted)));
1902
+ }
1903
+ }
1904
+ if (changeAmount > 0n) {
1905
+ const changeIndex = outputs.length;
1906
+ const changeUtxo = outputUtxos[changeIndex];
1907
+ const encrypted = encryptNote(
1908
+ {
1909
+ value: changeUtxo.value,
1910
+ mintTokenAddress: changeUtxo.mintTokenAddress,
1911
+ secret: changeUtxo.secret
1912
+ // leafIndex omitted - we already know it locally
1913
+ },
1914
+ walletKeys.encryptionKeyPair.publicKey
1915
+ );
1916
+ encryptedNotes.push(Buffer.from(serializeEncryptedNote(encrypted)));
1917
+ }
1918
+ return {
1919
+ proof,
1920
+ publicInputs,
1921
+ encryptedNotes,
1922
+ outputUtxos: outputUtxos.filter((u) => u.value > 0n),
1923
+ nullifiers,
1924
+ inputTreeIndex,
1925
+ outputTreeIndex: currentTreeIndex
1926
+ };
1927
+ }
1928
+ function buildCircuitInputs(merkleRoot, inputUtxos, outputUtxos, walletKeys, leaves, depositAmount, withdrawAmount, recipient, mint, fee) {
1929
+ const inputSpendingKeys = [];
1930
+ const inputValues = [];
1931
+ const inputSecrets = [];
1932
+ const inputLeafIndices = [];
1933
+ const inputPathElements = [];
1934
+ const inputPathIndices = [];
1935
+ const nullifiers = [];
1936
+ for (let i = 0; i < NUM_INPUT_UTXOS; i++) {
1937
+ if (i < inputUtxos.length) {
1938
+ const utxo = inputUtxos[i];
1939
+ inputSpendingKeys.push(walletKeys.spendingKey.toString());
1940
+ inputValues.push(utxo.value.toString());
1941
+ inputSecrets.push(utxo.secret.toString());
1942
+ inputLeafIndices.push(utxo.leafIndex.toString());
1943
+ const { pathElements, pathIndices } = computeMerklePathFromLeaves(
1944
+ utxo.leafIndex,
1945
+ leaves
1946
+ );
1947
+ inputPathElements.push(pathElements.map((e) => e.toString()));
1948
+ inputPathIndices.push(pathIndices);
1949
+ const nullifier = computeNullifier(
1950
+ walletKeys.spendingKey,
1951
+ utxo.commitment,
1952
+ utxo.leafIndex
1953
+ );
1954
+ nullifiers.push(nullifier.toString());
1955
+ } else {
1956
+ inputSpendingKeys.push("0");
1957
+ inputValues.push("0");
1958
+ inputSecrets.push("0");
1959
+ inputLeafIndices.push("0");
1960
+ inputPathElements.push(Array(MERKLE_TREE_DEPTH).fill("0"));
1961
+ inputPathIndices.push(Array(MERKLE_TREE_DEPTH).fill(0));
1962
+ nullifiers.push("0");
1963
+ }
1964
+ }
1965
+ return {
1966
+ merkleRoot: merkleRoot.toString(),
1967
+ nullifiers,
1968
+ outputCommitments: outputUtxos.map((u) => u.commitment.toString()),
1969
+ publicDeposit: depositAmount.toString(),
1970
+ publicWithdraw: withdrawAmount.toString(),
1971
+ recipient: pubkeyToBigInt(recipient).toString(),
1972
+ mintTokenAddress: pubkeyToBigInt(mint).toString(),
1973
+ fee: fee.toString(),
1974
+ inputSpendingKeys,
1975
+ inputValues,
1976
+ inputSecrets,
1977
+ inputLeafIndices,
1978
+ inputPathElements,
1979
+ inputPathIndices,
1980
+ outputValues: outputUtxos.map((u) => u.value.toString()),
1981
+ outputOwners: outputUtxos.map((u) => u.owner.toString()),
1982
+ outputSecrets: outputUtxos.map((u) => u.secret.toString())
1983
+ };
1984
+ }
1985
+ function buildTransactionAccounts(builtTx, payer, mint, depositAmount, withdrawAmount, recipient) {
1986
+ const [poolPda] = getPoolPDA();
1987
+ const [vaultPda] = getVaultPDA(mint);
1988
+ const [inputTreePda] = getMerkleTreePDA(builtTx.inputTreeIndex);
1989
+ const [outputTreePda] = getMerkleTreePDA(builtTx.outputTreeIndex);
1990
+ const preInstructions = [];
1991
+ let depositorTokenAccount = null;
1992
+ let depositor = null;
1993
+ if (depositAmount > 0n) {
1994
+ depositorTokenAccount = (0, import_spl_token.getAssociatedTokenAddressSync)(
1995
+ mint,
1996
+ payer,
1997
+ false,
1998
+ TOKEN_2022_PROGRAM_ID
1999
+ );
2000
+ depositor = payer;
2001
+ }
2002
+ let recipientTokenAccount = null;
2003
+ if (withdrawAmount > 0n && recipient) {
2004
+ recipientTokenAccount = (0, import_spl_token.getAssociatedTokenAddressSync)(
2005
+ mint,
2006
+ recipient,
2007
+ false,
2008
+ TOKEN_2022_PROGRAM_ID
2009
+ );
2010
+ const createAtaIx = (0, import_spl_token.createAssociatedTokenAccountIdempotentInstruction)(
2011
+ payer,
2012
+ recipientTokenAccount,
2013
+ recipient,
2014
+ mint,
2015
+ TOKEN_2022_PROGRAM_ID
2016
+ );
2017
+ preInstructions.push(createAtaIx);
2018
+ }
2019
+ const nullifierPdas = [];
2020
+ for (const nullifier of builtTx.nullifiers) {
2021
+ if (nullifier === 0n) {
2022
+ nullifierPdas.push(null);
2023
+ } else {
2024
+ const nullifierBytes = bigIntToBytes32(nullifier);
2025
+ const [nullifierPda] = getNullifierPDA(nullifierBytes);
2026
+ nullifierPdas.push(nullifierPda);
2027
+ }
2028
+ }
2029
+ const inputTreeIndex = builtTx.inputTreeIndex !== builtTx.outputTreeIndex ? builtTx.inputTreeIndex : null;
2030
+ return {
2031
+ accounts: {
2032
+ payer,
2033
+ mint,
2034
+ pool: poolPda,
2035
+ inputTree: inputTreePda,
2036
+ merkleTree: outputTreePda,
2037
+ vault: vaultPda,
2038
+ depositorTokenAccount,
2039
+ depositor,
2040
+ recipientTokenAccount,
2041
+ feeRecipientTokenAccount: null,
2042
+ // TODO: Add fee recipient support
2043
+ nullifierAccount0: nullifierPdas[0] ?? null,
2044
+ nullifierAccount1: nullifierPdas[1] ?? null,
2045
+ tokenProgram: TOKEN_2022_PROGRAM_ID,
2046
+ systemProgram: import_web33.SystemProgram.programId
2047
+ },
2048
+ preInstructions,
2049
+ inputTreeIndex
2050
+ };
2051
+ }
2052
+ function toAnchorPublicInputs(builtTx) {
2053
+ return {
2054
+ merkleRoot: builtTx.publicInputs.merkleRoot,
2055
+ nullifiers: builtTx.publicInputs.nullifiers,
2056
+ outputCommitments: builtTx.publicInputs.outputCommitments,
2057
+ publicDeposit: new import_anchor.BN(builtTx.publicInputs.publicDeposit.toString()),
2058
+ publicWithdraw: new import_anchor.BN(builtTx.publicInputs.publicWithdraw.toString()),
2059
+ recipient: builtTx.publicInputs.recipient,
2060
+ mintTokenAddress: builtTx.publicInputs.mintTokenAddress,
2061
+ fee: new import_anchor.BN(builtTx.publicInputs.fee.toString())
2062
+ };
2063
+ }
2064
+
2065
+ // src/wallet.ts
2066
+ var COMPUTE_UNITS = 4e5;
2067
+ var COMPUTE_UNIT_PRICE = 1;
2068
+ var HulaWallet = class _HulaWallet {
2069
+ connection;
2070
+ relayerUrl;
2071
+ programId;
2072
+ keys;
2073
+ signer;
2074
+ utxos = [];
2075
+ lastSyncedSlot = "0";
2076
+ initialized = false;
2077
+ constructor(connection, relayerUrl, programId, keys, signer) {
2078
+ this.connection = connection;
2079
+ this.relayerUrl = relayerUrl;
2080
+ this.programId = programId;
2081
+ this.keys = keys;
2082
+ this.signer = signer;
2083
+ }
2084
+ /**
2085
+ * Create a new wallet with a random spending key
2086
+ */
2087
+ static async create(config) {
2088
+ await initPoseidon();
2089
+ const connection = new import_web34.Connection(config.rpcUrl, "confirmed");
2090
+ const relayerUrl = config.relayerUrl;
2091
+ const programId = config.programId ?? new import_web34.PublicKey("tnJ9AAxNau3BRBTe7NzXpv8DeYyiogvGd8YPnCiuarA");
2092
+ setDefaultRelayerUrl(relayerUrl);
2093
+ const spendingKey = generateSpendingKey();
2094
+ const keys = deriveKeys(spendingKey);
2095
+ const wallet = new _HulaWallet(connection, relayerUrl, programId, keys);
2096
+ wallet.initialized = true;
2097
+ return wallet;
2098
+ }
2099
+ /**
2100
+ * Create a wallet from a Solana keypair
2101
+ *
2102
+ * Derives the spending key deterministically from the keypair's secret key.
2103
+ */
2104
+ static async fromKeypair(keypair, config) {
2105
+ await initPoseidon();
2106
+ const connection = new import_web34.Connection(config.rpcUrl, "confirmed");
2107
+ const relayerUrl = config.relayerUrl;
2108
+ const programId = config.programId ?? new import_web34.PublicKey("tnJ9AAxNau3BRBTe7NzXpv8DeYyiogvGd8YPnCiuarA");
2109
+ setDefaultRelayerUrl(relayerUrl);
2110
+ const spendingKey = deriveSpendingKeyFromSignature(keypair.secretKey);
2111
+ const keys = deriveKeys(spendingKey);
2112
+ const wallet = new _HulaWallet(connection, relayerUrl, programId, keys, keypair);
2113
+ wallet.initialized = true;
2114
+ return wallet;
2115
+ }
2116
+ /**
2117
+ * Create a wallet from an existing spending key
2118
+ */
2119
+ static async fromSpendingKey(spendingKey, config) {
2120
+ await initPoseidon();
2121
+ const connection = new import_web34.Connection(config.rpcUrl, "confirmed");
2122
+ const relayerUrl = config.relayerUrl;
2123
+ const programId = config.programId ?? new import_web34.PublicKey("tnJ9AAxNau3BRBTe7NzXpv8DeYyiogvGd8YPnCiuarA");
2124
+ setDefaultRelayerUrl(relayerUrl);
2125
+ const keys = deriveKeys(spendingKey);
2126
+ const wallet = new _HulaWallet(connection, relayerUrl, programId, keys);
2127
+ wallet.initialized = true;
2128
+ return wallet;
2129
+ }
2130
+ /**
2131
+ * Create a wallet from a Solana signature (deterministic derivation)
2132
+ *
2133
+ * This allows recovery as long as the user can sign with their Solana wallet.
2134
+ */
2135
+ static async fromSignature(signature, config) {
2136
+ await initPoseidon();
2137
+ const connection = new import_web34.Connection(config.rpcUrl, "confirmed");
2138
+ const relayerUrl = config.relayerUrl;
2139
+ const programId = config.programId ?? new import_web34.PublicKey("tnJ9AAxNau3BRBTe7NzXpv8DeYyiogvGd8YPnCiuarA");
2140
+ setDefaultRelayerUrl(relayerUrl);
2141
+ const spendingKey = deriveSpendingKeyFromSignature(signature);
2142
+ const keys = deriveKeys(spendingKey);
2143
+ const wallet = new _HulaWallet(connection, relayerUrl, programId, keys);
2144
+ wallet.initialized = true;
2145
+ return wallet;
2146
+ }
2147
+ // ============================================================================
2148
+ // Getters
2149
+ // ============================================================================
2150
+ /**
2151
+ * Get wallet's owner hash (public identifier)
2152
+ */
2153
+ get owner() {
2154
+ return this.keys.owner;
2155
+ }
2156
+ /**
2157
+ * Get wallet's owner hash as hex string
2158
+ */
2159
+ getOwnerHash() {
2160
+ return "0x" + this.keys.owner.toString(16).padStart(64, "0");
2161
+ }
2162
+ /**
2163
+ * Get wallet's encryption public key as hex string
2164
+ */
2165
+ getEncryptionPublicKey() {
2166
+ return "0x" + Buffer.from(this.keys.encryptionKeyPair.publicKey).toString("hex");
2167
+ }
2168
+ /**
2169
+ * Get wallet's encryption public key as bytes
2170
+ */
2171
+ get encryptionPublicKey() {
2172
+ return this.keys.encryptionKeyPair.publicKey;
2173
+ }
2174
+ /**
2175
+ * Get spending key (CAREFUL - this is sensitive!)
2176
+ */
2177
+ get spendingKey() {
2178
+ return this.keys.spendingKey;
2179
+ }
2180
+ /**
2181
+ * Get all wallet keys
2182
+ */
2183
+ get walletKeys() {
2184
+ return this.keys;
2185
+ }
2186
+ /**
2187
+ * Get the connection
2188
+ */
2189
+ getConnection() {
2190
+ return this.connection;
2191
+ }
2192
+ /**
2193
+ * Get the signer keypair (if available)
2194
+ */
2195
+ getSigner() {
2196
+ return this.signer;
2197
+ }
2198
+ /**
2199
+ * Set the signer keypair
2200
+ */
2201
+ setSigner(signer) {
2202
+ this.signer = signer;
2203
+ }
2204
+ // ============================================================================
2205
+ // UTXO Management
2206
+ // ============================================================================
2207
+ /**
2208
+ * Sync wallet with relayer
2209
+ *
2210
+ * Fetches new encrypted notes and checks for spent UTXOs.
2211
+ */
2212
+ async sync(onProgress) {
2213
+ if (!this.initialized) {
2214
+ throw new Error("Wallet not initialized");
2215
+ }
2216
+ onProgress?.({ stage: "notes", current: 0, total: 100 });
2217
+ try {
2218
+ const { newUTXOs, spentUTXOs } = await syncUTXOs(
2219
+ this.keys,
2220
+ this.utxos,
2221
+ this.relayerUrl,
2222
+ this.lastSyncedSlot !== "0" ? this.lastSyncedSlot : void 0
2223
+ );
2224
+ this.utxos.push(...newUTXOs);
2225
+ for (const spentId of spentUTXOs) {
2226
+ const utxo = this.utxos.find((u) => u.commitment === spentId);
2227
+ if (utxo) {
2228
+ utxo.spent = true;
2229
+ }
2230
+ }
2231
+ const client = getRelayerClient(this.relayerUrl);
2232
+ const stats = await client.getStats();
2233
+ this.lastSyncedSlot = Date.now().toString();
2234
+ onProgress?.({ stage: "complete", current: 100, total: 100 });
2235
+ return {
2236
+ newUtxos: newUTXOs.length,
2237
+ spentUtxos: spentUTXOs.length,
2238
+ errors: []
2239
+ };
2240
+ } catch (err) {
2241
+ const errorMsg = err instanceof Error ? err.message : "Sync failed";
2242
+ onProgress?.({ stage: "complete", current: 100, total: 100 });
2243
+ return {
2244
+ newUtxos: 0,
2245
+ spentUtxos: 0,
2246
+ errors: [errorMsg]
2247
+ };
2248
+ }
2249
+ }
2250
+ /**
2251
+ * Get all unspent UTXOs
2252
+ */
2253
+ getUnspentUTXOs() {
2254
+ return this.utxos.filter((u) => !u.spent).map(deserializeUTXO);
2255
+ }
2256
+ /**
2257
+ * Get all UTXOs (including spent)
2258
+ */
2259
+ getAllUTXOs() {
2260
+ return [...this.utxos];
2261
+ }
2262
+ /**
2263
+ * Get balance for a specific mint
2264
+ */
2265
+ getBalance(mint) {
2266
+ const mintBigInt = pubkeyToBigInt(mint);
2267
+ const unspent = this.getUnspentUTXOs();
2268
+ return calculateBalance(unspent, mintBigInt);
2269
+ }
2270
+ /**
2271
+ * Import UTXOs (for wallet recovery or manual import)
2272
+ */
2273
+ importUTXOs(utxos) {
2274
+ this.utxos.push(...utxos);
2275
+ }
2276
+ /**
2277
+ * Export UTXOs for backup
2278
+ */
2279
+ exportUTXOs() {
2280
+ return [...this.utxos];
2281
+ }
2282
+ // ============================================================================
2283
+ // Transaction Building
2284
+ // ============================================================================
2285
+ /**
2286
+ * Deposit tokens into the privacy pool
2287
+ *
2288
+ * @param mint - Token mint address
2289
+ * @param amount - Amount in raw token units
2290
+ * @returns Transaction signature
2291
+ */
2292
+ async deposit(mint, amount) {
2293
+ if (!this.signer) {
2294
+ throw new Error("No signer available. Use fromKeypair() or setSigner() to set a signer.");
2295
+ }
2296
+ const builtTx = await buildTransaction(
2297
+ {
2298
+ mint,
2299
+ depositAmount: amount,
2300
+ outputs: []
2301
+ // Change will be created automatically
2302
+ },
2303
+ this.keys,
2304
+ this.relayerUrl
2305
+ );
2306
+ const { accounts, preInstructions, inputTreeIndex } = buildTransactionAccounts(
2307
+ builtTx,
2308
+ this.signer.publicKey,
2309
+ mint,
2310
+ amount,
2311
+ 0n
2312
+ );
2313
+ const allPreInstructions = [
2314
+ import_web34.ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNITS }),
2315
+ import_web34.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }),
2316
+ ...preInstructions
2317
+ ];
2318
+ const wallet = new import_anchor2.Wallet(this.signer);
2319
+ const provider = new import_anchor2.AnchorProvider(this.connection, wallet, { commitment: "confirmed" });
2320
+ const program = new import_anchor2.Program(idl_default, provider);
2321
+ const publicInputs = toAnchorPublicInputs(builtTx);
2322
+ const signature = await program.methods.transact(
2323
+ inputTreeIndex,
2324
+ Array.from(builtTx.proof),
2325
+ publicInputs,
2326
+ builtTx.encryptedNotes
2327
+ ).accounts(accounts).preInstructions(allPreInstructions).signers([this.signer]).rpc();
2328
+ return signature;
2329
+ }
2330
+ /**
2331
+ * Transfer tokens privately to another user
2332
+ *
2333
+ * @param mint - Token mint address
2334
+ * @param amount - Amount in raw token units
2335
+ * @param recipientOwner - Recipient's owner hash (bigint)
2336
+ * @param recipientEncryptionPubKey - Recipient's encryption public key
2337
+ * @returns Transaction signature
2338
+ */
2339
+ async transfer(mint, amount, recipientOwner, recipientEncryptionPubKey) {
2340
+ if (!this.signer) {
2341
+ throw new Error("No signer available. Use fromKeypair() or setSigner() to set a signer.");
2342
+ }
2343
+ const mintBigInt = pubkeyToBigInt(mint);
2344
+ const unspent = this.getUnspentUTXOs().filter(
2345
+ (u) => u.mintTokenAddress === mintBigInt
2346
+ );
2347
+ const inputUtxos = selectUTXOs(unspent, amount, mintBigInt);
2348
+ const builtTx = await buildTransaction(
2349
+ {
2350
+ mint,
2351
+ inputUtxos,
2352
+ outputs: [
2353
+ {
2354
+ owner: recipientOwner,
2355
+ amount,
2356
+ encryptionPubKey: recipientEncryptionPubKey
2357
+ }
2358
+ ]
2359
+ },
2360
+ this.keys,
2361
+ this.relayerUrl
2362
+ );
2363
+ const { accounts, preInstructions, inputTreeIndex } = buildTransactionAccounts(
2364
+ builtTx,
2365
+ this.signer.publicKey,
2366
+ mint,
2367
+ 0n,
2368
+ 0n
2369
+ );
2370
+ const allPreInstructions = [
2371
+ import_web34.ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNITS }),
2372
+ import_web34.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }),
2373
+ ...preInstructions
2374
+ ];
2375
+ const wallet = new import_anchor2.Wallet(this.signer);
2376
+ const provider = new import_anchor2.AnchorProvider(this.connection, wallet, { commitment: "confirmed" });
2377
+ const program = new import_anchor2.Program(idl_default, provider);
2378
+ const publicInputs = toAnchorPublicInputs(builtTx);
2379
+ const signature = await program.methods.transact(
2380
+ inputTreeIndex,
2381
+ Array.from(builtTx.proof),
2382
+ publicInputs,
2383
+ builtTx.encryptedNotes
2384
+ ).accounts(accounts).preInstructions(allPreInstructions).signers([this.signer]).rpc();
2385
+ for (const utxo of inputUtxos) {
2386
+ const serialized = this.utxos.find((u) => u.commitment === utxo.commitment.toString());
2387
+ if (serialized) {
2388
+ serialized.spent = true;
2389
+ serialized.spentTx = signature;
2390
+ }
2391
+ }
2392
+ return signature;
2393
+ }
2394
+ /**
2395
+ * Withdraw tokens from the privacy pool to a public address
2396
+ *
2397
+ * @param mint - Token mint address
2398
+ * @param amount - Amount in raw token units
2399
+ * @param recipient - Public key to receive the tokens
2400
+ * @returns Transaction signature
2401
+ */
2402
+ async withdraw(mint, amount, recipient) {
2403
+ if (!this.signer) {
2404
+ throw new Error("No signer available. Use fromKeypair() or setSigner() to set a signer.");
2405
+ }
2406
+ const mintBigInt = pubkeyToBigInt(mint);
2407
+ const unspent = this.getUnspentUTXOs().filter(
2408
+ (u) => u.mintTokenAddress === mintBigInt
2409
+ );
2410
+ const inputUtxos = selectUTXOs(unspent, amount, mintBigInt);
2411
+ const builtTx = await buildTransaction(
2412
+ {
2413
+ mint,
2414
+ inputUtxos,
2415
+ withdrawAmount: amount,
2416
+ recipient
2417
+ },
2418
+ this.keys,
2419
+ this.relayerUrl
2420
+ );
2421
+ const { accounts, preInstructions, inputTreeIndex } = buildTransactionAccounts(
2422
+ builtTx,
2423
+ this.signer.publicKey,
2424
+ mint,
2425
+ 0n,
2426
+ amount,
2427
+ recipient
2428
+ );
2429
+ const allPreInstructions = [
2430
+ import_web34.ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNITS }),
2431
+ import_web34.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }),
2432
+ ...preInstructions
2433
+ ];
2434
+ const wallet = new import_anchor2.Wallet(this.signer);
2435
+ const provider = new import_anchor2.AnchorProvider(this.connection, wallet, { commitment: "confirmed" });
2436
+ const program = new import_anchor2.Program(idl_default, provider);
2437
+ const publicInputs = toAnchorPublicInputs(builtTx);
2438
+ const signature = await program.methods.transact(
2439
+ inputTreeIndex,
2440
+ Array.from(builtTx.proof),
2441
+ publicInputs,
2442
+ builtTx.encryptedNotes
2443
+ ).accounts(accounts).preInstructions(allPreInstructions).signers([this.signer]).rpc();
2444
+ for (const utxo of inputUtxos) {
2445
+ const serialized = this.utxos.find((u) => u.commitment === utxo.commitment.toString());
2446
+ if (serialized) {
2447
+ serialized.spent = true;
2448
+ serialized.spentTx = signature;
2449
+ }
2450
+ }
2451
+ return signature;
2452
+ }
2453
+ /**
2454
+ * Build a custom transaction
2455
+ */
2456
+ async buildTransaction(request) {
2457
+ const tx = await buildTransaction(request, this.keys, this.relayerUrl);
2458
+ const { preInstructions } = buildTransactionAccounts(
2459
+ tx,
2460
+ import_web34.PublicKey.default,
2461
+ request.mint,
2462
+ request.depositAmount ?? 0n,
2463
+ request.withdrawAmount ?? 0n,
2464
+ request.recipient
2465
+ );
2466
+ return { transaction: tx, instructions: preInstructions };
2467
+ }
2468
+ // ============================================================================
2469
+ // Transaction Submission
2470
+ // ============================================================================
2471
+ /**
2472
+ * Submit a transaction using an Anchor program
2473
+ *
2474
+ * @param program - Anchor program instance (any type to avoid IDL complexity)
2475
+ * @param payer - Keypair for signing
2476
+ * @param builtTx - Built transaction from buildTransaction()
2477
+ * @param mint - Token mint
2478
+ * @param depositAmount - Amount being deposited (if any)
2479
+ * @param withdrawAmount - Amount being withdrawn (if any)
2480
+ * @param recipient - Recipient for withdrawal (if any)
2481
+ */
2482
+ async submitTransaction(program, payer, builtTx, mint, depositAmount = 0n, withdrawAmount = 0n, recipient) {
2483
+ const { accounts, preInstructions, inputTreeIndex } = buildTransactionAccounts(
2484
+ builtTx,
2485
+ payer.publicKey,
2486
+ mint,
2487
+ depositAmount,
2488
+ withdrawAmount,
2489
+ recipient
2490
+ );
2491
+ const publicInputs = toAnchorPublicInputs(builtTx);
2492
+ const tx = await program.methods.transact(
2493
+ inputTreeIndex,
2494
+ Array.from(builtTx.proof),
2495
+ publicInputs,
2496
+ builtTx.encryptedNotes
2497
+ ).accounts(accounts).preInstructions(preInstructions).signers([payer]).rpc();
2498
+ for (const _utxo of builtTx.outputUtxos) {
2499
+ }
2500
+ return tx;
2501
+ }
2502
+ };
2503
+ function getKeyDerivationMessage() {
2504
+ return new TextEncoder().encode("Hula Privacy Wallet Key Derivation v1");
2505
+ }
2506
+ async function initHulaSDK() {
2507
+ if (!isPoseidonInitialized()) {
2508
+ await initPoseidon();
2509
+ }
2510
+ }
2511
+ // Annotate the CommonJS export names for ESM import in node:
2512
+ 0 && (module.exports = {
2513
+ DOMAIN_ENCRYPTION,
2514
+ DOMAIN_NULLIFIER,
2515
+ DOMAIN_OWNER,
2516
+ DOMAIN_VIEWING,
2517
+ FIELD_PRIME,
2518
+ HulaWallet,
2519
+ MAX_LEAVES,
2520
+ MERKLE_TREE_DEPTH,
2521
+ MERKLE_TREE_SEED,
2522
+ NULLIFIER_SEED,
2523
+ NUM_INPUT_UTXOS,
2524
+ NUM_OUTPUT_UTXOS,
2525
+ POOL_SEED,
2526
+ PROGRAM_ID,
2527
+ PROOF_SIZE,
2528
+ RelayerClient,
2529
+ TOKEN_2022_PROGRAM_ID,
2530
+ VAULT_SEED,
2531
+ bigIntToBytes,
2532
+ bigIntToBytes32,
2533
+ buildTransaction,
2534
+ buildTransactionAccounts,
2535
+ bytesToBigInt,
2536
+ bytesToHex,
2537
+ calculateBalance,
2538
+ computeCommitment,
2539
+ computeMerklePathFromLeaves,
2540
+ computeMerkleRoot,
2541
+ computeNullifier,
2542
+ computeNullifierFromKeys,
2543
+ computeZeros,
2544
+ createDummyUTXO,
2545
+ createUTXO,
2546
+ decryptNote,
2547
+ deriveKeys,
2548
+ deriveSpendingKeyFromSignature,
2549
+ deserializeEncryptedNote,
2550
+ deserializeUTXO,
2551
+ encryptNote,
2552
+ fetchMerklePath,
2553
+ fetchMerkleRoot,
2554
+ formatCommitment,
2555
+ generateProof,
2556
+ generateProofInMemory,
2557
+ generateSpendingKey,
2558
+ getCircuitPaths,
2559
+ getCurrentTreeIndex,
2560
+ getEmptyTreeRoot,
2561
+ getKeyDerivationMessage,
2562
+ getMerkleTreePDA,
2563
+ getNextLeafIndex,
2564
+ getNullifierPDA,
2565
+ getPoolPDA,
2566
+ getPoseidon,
2567
+ getRelayerClient,
2568
+ getVaultPDA,
2569
+ getZeroAtLevel,
2570
+ hexToBytes,
2571
+ initHulaSDK,
2572
+ initPoseidon,
2573
+ isPoseidonInitialized,
2574
+ parseProof,
2575
+ poseidonHash,
2576
+ pubkeyToBigInt,
2577
+ scanNotesForUTXOs,
2578
+ selectUTXOs,
2579
+ serializeEncryptedNote,
2580
+ serializeUTXO,
2581
+ setCircuitPaths,
2582
+ setDefaultRelayerUrl,
2583
+ syncUTXOs,
2584
+ toAnchorPublicInputs,
2585
+ verifyCircuitFiles,
2586
+ verifyMerklePath
2587
+ });