@cloak.dev/sdk 0.1.5 → 0.1.7

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 CHANGED
@@ -24,10 +24,7 @@ import {
24
24
  // src/core/CloakSDK.ts
25
25
  import {
26
26
  Keypair,
27
- PublicKey as PublicKey4,
28
- Transaction,
29
- TransactionInstruction as TransactionInstruction2,
30
- ComputeBudgetProgram
27
+ PublicKey as PublicKey2
31
28
  } from "@solana/web3.js";
32
29
 
33
30
  // src/core/types.ts
@@ -85,79 +82,6 @@ function hexToBigint2(hex) {
85
82
  const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
86
83
  return BigInt("0x" + cleanHex);
87
84
  }
88
- async function computeCommitment2(amount, r, sk_spend) {
89
- const [sk0, sk1] = splitTo2Limbs(sk_spend);
90
- const [r0, r1] = splitTo2Limbs(r);
91
- const pk_spend = await poseidonHash([sk0, sk1]);
92
- return await poseidonHash([amount, r0, r1, pk_spend]);
93
- }
94
- async function generateCommitmentAsync(amountLamports, r, skSpend) {
95
- const amount = BigInt(amountLamports);
96
- const rValue = hexToBigint2(bytesToHex(r));
97
- const skValue = hexToBigint2(bytesToHex(skSpend));
98
- return await computeCommitment2(amount, rValue, skValue);
99
- }
100
- function generateCommitment(_amountLamports, _r, _skSpend) {
101
- throw new Error("generateCommitment is deprecated. Use generateCommitmentAsync instead.");
102
- }
103
- async function computeNullifier2(sk_spend, leafIndex) {
104
- const [sk0, sk1] = splitTo2Limbs(sk_spend);
105
- return await poseidonHash([sk0, sk1, leafIndex]);
106
- }
107
- async function computeNullifierAsync(skSpend, leafIndex) {
108
- const skValue = typeof skSpend === "string" ? hexToBigint2(skSpend) : hexToBigint2(bytesToHex(skSpend));
109
- return await computeNullifier2(skValue, BigInt(leafIndex));
110
- }
111
- function computeNullifierSync(_skSpend, _leafIndex) {
112
- throw new Error("computeNullifierSync is deprecated. Use computeNullifierAsync instead.");
113
- }
114
- async function computeOutputsHashAsync(outputs) {
115
- let hash = 0n;
116
- for (const output of outputs) {
117
- const [lo, hi] = pubkeyToLimbs(output.recipient);
118
- hash = await poseidonHash([hash, lo, hi, BigInt(output.amount)]);
119
- }
120
- return hash;
121
- }
122
- async function computeOutputsHash(outAddr, outAmount, outFlags) {
123
- let hash = 0n;
124
- for (let i = 0; i < 5; i++) {
125
- if (outFlags[i] === 1) {
126
- hash = await poseidonHash([hash, outAddr[i][0], outAddr[i][1], outAmount[i]]);
127
- }
128
- }
129
- return hash;
130
- }
131
- function computeOutputsHashSync(_outputs) {
132
- throw new Error("computeOutputsHashSync is deprecated. Use computeOutputsHashAsync instead.");
133
- }
134
- async function computeSwapOutputsHash(inputMintLimbs, outputMintLimbs, recipientAtaLimbs, minOutputAmount, publicAmount) {
135
- return await poseidonHash([
136
- inputMintLimbs[0],
137
- inputMintLimbs[1],
138
- outputMintLimbs[0],
139
- outputMintLimbs[1],
140
- recipientAtaLimbs[0],
141
- recipientAtaLimbs[1],
142
- minOutputAmount,
143
- publicAmount
144
- ]);
145
- }
146
- async function computeSwapOutputsHashAsync(inputMint, outputMint, recipientAta, minOutputAmount, amount) {
147
- const inputMintLimbs = pubkeyToLimbs(inputMint);
148
- const outputMintLimbs = pubkeyToLimbs(outputMint);
149
- const recipientAtaLimbs = pubkeyToLimbs(recipientAta);
150
- return await computeSwapOutputsHash(
151
- inputMintLimbs,
152
- outputMintLimbs,
153
- recipientAtaLimbs,
154
- BigInt(minOutputAmount),
155
- BigInt(amount)
156
- );
157
- }
158
- function computeSwapOutputsHashSync(_outputMint, _recipientAta, _minOutputAmount, _amount) {
159
- throw new Error("computeSwapOutputsHashSync is deprecated. Use computeSwapOutputsHashAsync instead.");
160
- }
161
85
  function bigintToBytes322(n) {
162
86
  const hex = n.toString(16).padStart(64, "0");
163
87
  const bytes = new Uint8Array(32);
@@ -430,223 +354,11 @@ function importKeys(exported) {
430
354
  return generateCloakKeys(masterSeed);
431
355
  }
432
356
 
433
- // src/utils/network.ts
434
- function detectNetworkFromRpcUrl(rpcUrl) {
435
- const url = rpcUrl || process.env.NEXT_PUBLIC_SOLANA_RPC_URL || "";
436
- const lowerUrl = url.toLowerCase();
437
- if (lowerUrl.includes("mainnet") || lowerUrl.includes("api.mainnet-beta") || lowerUrl.includes("mainnet-beta")) {
438
- return "mainnet";
439
- }
440
- if (lowerUrl.includes("testnet") || lowerUrl.includes("api.testnet")) {
441
- return "testnet";
442
- }
443
- if (lowerUrl.includes("devnet") || lowerUrl.includes("api.devnet")) {
444
- return "devnet";
445
- }
446
- if (lowerUrl.includes("localhost") || lowerUrl.includes("127.0.0.1") || lowerUrl.includes("local")) {
447
- return "localnet";
448
- }
449
- return "mainnet";
450
- }
451
- function getRpcUrlForNetwork(network) {
452
- switch (network) {
453
- case "mainnet":
454
- return "https://api.mainnet-beta.solana.com";
455
- case "devnet":
456
- return "https://api.devnet.solana.com";
457
- case "localnet":
458
- return "http://localhost:8899";
459
- default:
460
- return "https://api.mainnet-beta.solana.com";
461
- }
462
- }
463
- function isValidRpcUrl(url) {
464
- try {
465
- const parsed = new URL(url);
466
- return parsed.protocol === "http:" || parsed.protocol === "https:";
467
- } catch {
468
- return false;
469
- }
470
- }
471
- function getExplorerUrl(signature, network = "devnet") {
472
- const cluster = network === "mainnet" ? "" : `?cluster=${network}`;
473
- return `https://explorer.solana.com/tx/${signature}${cluster}`;
474
- }
475
- function getAddressExplorerUrl(address, network = "devnet") {
476
- const cluster = network === "mainnet" ? "" : `?cluster=${network}`;
477
- return `https://explorer.solana.com/address/${address}${cluster}`;
478
- }
479
-
480
- // src/utils/fees.ts
481
- var LAMPORTS_PER_SOL = 1e9;
482
- var FIXED_FEE_LAMPORTS = 5e6;
483
- var VARIABLE_FEE_NUMERATOR = 3;
484
- var VARIABLE_FEE_DENOMINATOR = 1e3;
485
- var VARIABLE_FEE_RATE = VARIABLE_FEE_NUMERATOR / VARIABLE_FEE_DENOMINATOR;
486
- var MIN_DEPOSIT_LAMPORTS = 1e7;
487
- function calculateFee(amountLamports) {
488
- const variableFee = Math.floor(amountLamports * VARIABLE_FEE_NUMERATOR / VARIABLE_FEE_DENOMINATOR);
489
- return FIXED_FEE_LAMPORTS + variableFee;
490
- }
491
- function calculateFeeBigint(amountLamports) {
492
- const fixed = BigInt(FIXED_FEE_LAMPORTS);
493
- const variable = amountLamports * BigInt(VARIABLE_FEE_NUMERATOR) / BigInt(VARIABLE_FEE_DENOMINATOR);
494
- return fixed + variable;
495
- }
496
- function isWithdrawAmountSufficient(amountLamports) {
497
- return amountLamports > calculateFeeBigint(amountLamports);
498
- }
499
- function getDistributableAmount(amountLamports) {
500
- return amountLamports - calculateFee(amountLamports);
501
- }
502
- function formatAmount(lamports, decimals = 9) {
503
- return (lamports / LAMPORTS_PER_SOL).toFixed(decimals);
504
- }
505
- function parseAmount(sol) {
506
- const num = parseFloat(sol);
507
- if (isNaN(num) || num < 0) {
508
- throw new Error(`Invalid SOL amount: ${sol}`);
509
- }
510
- return Math.floor(num * LAMPORTS_PER_SOL);
511
- }
512
- function validateOutputsSum(outputs, expectedTotal) {
513
- const sum = outputs.reduce((acc, out) => acc + out.amount, 0);
514
- return sum === expectedTotal;
515
- }
516
- function calculateRelayFee(amountLamports, feeBps) {
517
- if (feeBps < 0 || feeBps > 1e4) {
518
- throw new Error("Fee basis points must be between 0 and 10000");
519
- }
520
- return Math.floor(amountLamports * feeBps / 1e4);
521
- }
522
-
523
- // src/core/note-manager.ts
524
- async function generateNote(amountLamports, network) {
525
- const actualNetwork = network || detectNetworkFromRpcUrl();
526
- const skSpend = randomBytes(32);
527
- const rBytes = randomBytes(32);
528
- const commitmentBigint = await generateCommitmentAsync(amountLamports, rBytes, skSpend);
529
- const commitmentHex = commitmentBigint.toString(16).padStart(64, "0");
530
- const skSpendHex = bytesToHex(skSpend);
531
- const rHex = bytesToHex(rBytes);
532
- return {
533
- version: "1.0",
534
- amount: amountLamports,
535
- commitment: commitmentHex,
536
- sk_spend: skSpendHex,
537
- r: rHex,
538
- timestamp: Date.now(),
539
- network: actualNetwork
540
- };
541
- }
542
- async function generateNoteFromWallet(amountLamports, keys, network) {
543
- const actualNetwork = network || detectNetworkFromRpcUrl();
544
- const rBytes = randomBytes(32);
545
- const sk_spend = hexToBytes(keys.spend.sk_spend_hex);
546
- const commitmentBigint = await generateCommitmentAsync(amountLamports, rBytes, sk_spend);
547
- const commitmentHex = commitmentBigint.toString(16).padStart(64, "0");
548
- return {
549
- version: "2.0",
550
- amount: amountLamports,
551
- commitment: commitmentHex,
552
- sk_spend: keys.spend.sk_spend_hex,
553
- r: bytesToHex(rBytes),
554
- timestamp: Date.now(),
555
- network: actualNetwork
556
- };
557
- }
558
- function parseNote(jsonString) {
559
- const note = JSON.parse(jsonString);
560
- if (!note.version || !note.amount || !note.commitment || !note.sk_spend || !note.r) {
561
- throw new CloakError(
562
- "Invalid note format: missing required fields",
563
- "validation",
564
- false
565
- );
566
- }
567
- if (!/^[0-9a-f]{64}$/i.test(note.commitment)) {
568
- throw new CloakError("Invalid commitment format", "validation", false);
569
- }
570
- if (!/^[0-9a-f]{64}$/i.test(note.sk_spend)) {
571
- throw new CloakError("Invalid sk_spend format", "validation", false);
572
- }
573
- if (!/^[0-9a-f]{64}$/i.test(note.r)) {
574
- throw new CloakError("Invalid r format", "validation", false);
575
- }
576
- return note;
577
- }
578
- function exportNote(note, pretty = false) {
579
- return pretty ? JSON.stringify(note, null, 2) : JSON.stringify(note);
580
- }
581
- function isWithdrawable(note) {
582
- return !!(note.depositSignature && note.leafIndex !== void 0 && note.root);
583
- }
584
- function updateNoteWithDeposit(note, depositInfo) {
585
- const updated = {
586
- ...note,
587
- depositSignature: depositInfo.signature,
588
- depositSlot: depositInfo.slot,
589
- leafIndex: depositInfo.leafIndex,
590
- root: depositInfo.root
591
- };
592
- if (depositInfo.merkleProof) {
593
- updated.merkleProof = depositInfo.merkleProof;
594
- }
595
- return updated;
596
- }
597
- function findNoteByCommitment(notes, commitment) {
598
- return notes.find((n) => n.commitment === commitment);
599
- }
600
- function filterNotesByNetwork(notes, network) {
601
- return notes.filter((n) => n.network === network);
602
- }
603
- function filterWithdrawableNotes(notes) {
604
- return notes.filter(isWithdrawable);
605
- }
606
- function exportWalletKeys(keys) {
607
- return exportKeys(keys);
608
- }
609
- function importWalletKeys(keysJson) {
610
- return importKeys(keysJson);
611
- }
612
- function getPublicViewKey(keys) {
613
- return keys.view.pvk_hex;
614
- }
615
- function getViewKey(keys) {
616
- return keys.view;
617
- }
618
- var calculateFee2 = calculateFee;
619
- function getDistributableAmount2(amountLamports) {
620
- return amountLamports - calculateFee2(amountLamports);
621
- }
622
- function getRecipientAmount(amountLamports) {
623
- return getDistributableAmount2(amountLamports);
624
- }
625
-
626
357
  // src/core/storage.ts
627
358
  var MemoryStorageAdapter = class {
628
359
  constructor() {
629
- this.notes = /* @__PURE__ */ new Map();
630
360
  this.keys = null;
631
361
  }
632
- saveNote(note) {
633
- this.notes.set(note.commitment, note);
634
- }
635
- loadAllNotes() {
636
- return Array.from(this.notes.values());
637
- }
638
- updateNote(commitment, updates) {
639
- const existing = this.notes.get(commitment);
640
- if (existing) {
641
- this.notes.set(commitment, { ...existing, ...updates });
642
- }
643
- }
644
- deleteNote(commitment) {
645
- this.notes.delete(commitment);
646
- }
647
- clearAllNotes() {
648
- this.notes.clear();
649
- }
650
362
  saveKeys(keys) {
651
363
  this.keys = keys;
652
364
  }
@@ -657,10 +369,10 @@ var MemoryStorageAdapter = class {
657
369
  this.keys = null;
658
370
  }
659
371
  };
660
- var LocalStorageAdapter = class {
661
- constructor(notesKey = "cloak_notes", keysKey = "cloak_wallet_keys") {
662
- this.notesKey = notesKey;
372
+ var _LocalStorageAdapter = class _LocalStorageAdapter {
373
+ constructor(keysKey = "cloak_wallet_keys") {
663
374
  this.keysKey = keysKey;
375
+ _LocalStorageAdapter.runLegacyPurgeOnce();
664
376
  }
665
377
  getStorage() {
666
378
  if (typeof globalThis !== "undefined" && globalThis.localStorage) {
@@ -668,47 +380,6 @@ var LocalStorageAdapter = class {
668
380
  }
669
381
  return null;
670
382
  }
671
- saveNote(note) {
672
- const storage = this.getStorage();
673
- if (!storage) throw new Error("localStorage not available");
674
- const notes = this.loadAllNotes();
675
- notes.push(note);
676
- storage.setItem(this.notesKey, JSON.stringify(notes));
677
- }
678
- loadAllNotes() {
679
- const storage = this.getStorage();
680
- if (!storage) return [];
681
- const stored = storage.getItem(this.notesKey);
682
- if (!stored) return [];
683
- try {
684
- return JSON.parse(stored);
685
- } catch {
686
- return [];
687
- }
688
- }
689
- updateNote(commitment, updates) {
690
- const storage = this.getStorage();
691
- if (!storage) return;
692
- const notes = this.loadAllNotes();
693
- const index = notes.findIndex((n) => n.commitment === commitment);
694
- if (index !== -1) {
695
- notes[index] = { ...notes[index], ...updates };
696
- storage.setItem(this.notesKey, JSON.stringify(notes));
697
- }
698
- }
699
- deleteNote(commitment) {
700
- const storage = this.getStorage();
701
- if (!storage) return;
702
- const notes = this.loadAllNotes();
703
- const filtered = notes.filter((n) => n.commitment !== commitment);
704
- storage.setItem(this.notesKey, JSON.stringify(filtered));
705
- }
706
- clearAllNotes() {
707
- const storage = this.getStorage();
708
- if (storage) {
709
- storage.removeItem(this.notesKey);
710
- }
711
- }
712
383
  saveKeys(keys) {
713
384
  const storage = this.getStorage();
714
385
  if (!storage) throw new Error("localStorage not available");
@@ -731,126 +402,64 @@ var LocalStorageAdapter = class {
731
402
  storage.removeItem(this.keysKey);
732
403
  }
733
404
  }
734
- };
735
-
736
- // src/utils/validation.ts
737
- import { PublicKey } from "@solana/web3.js";
738
- function isValidSolanaAddress(address) {
739
- try {
740
- new PublicKey(address);
741
- return true;
742
- } catch {
743
- return false;
744
- }
745
- }
746
- function validateNote(note) {
747
- if (!note || typeof note !== "object") {
748
- throw new Error("Note must be an object");
749
- }
750
- const requiredFields = ["version", "amount", "commitment", "sk_spend", "r", "timestamp", "network"];
751
- for (const field of requiredFields) {
752
- if (!(field in note)) {
753
- throw new Error(`Missing required field: ${field}`);
754
- }
755
- }
756
- if (typeof note.version !== "string") {
757
- throw new Error("Version must be a string");
758
- }
759
- if (typeof note.amount !== "number" || note.amount <= 0) {
760
- throw new Error("Amount must be a positive number");
761
- }
762
- if (!isValidHex(note.commitment, 32)) {
763
- throw new Error("Invalid commitment format (expected 64 hex characters)");
764
- }
765
- if (!isValidHex(note.sk_spend, 32)) {
766
- throw new Error("Invalid sk_spend format (expected 64 hex characters)");
767
- }
768
- if (!isValidHex(note.r, 32)) {
769
- throw new Error("Invalid r format (expected 64 hex characters)");
770
- }
771
- if (typeof note.timestamp !== "number" || note.timestamp <= 0) {
772
- throw new Error("Timestamp must be a positive number");
773
- }
774
- if (!["localnet", "devnet", "testnet", "mainnet"].includes(note.network)) {
775
- throw new Error("Network must be localnet, devnet, testnet, or mainnet");
776
- }
777
- if (note.depositSignature !== void 0 && typeof note.depositSignature !== "string") {
778
- throw new Error("Deposit signature must be a string");
779
- }
780
- if (note.depositSlot !== void 0 && typeof note.depositSlot !== "number") {
781
- throw new Error("Deposit slot must be a number");
782
- }
783
- if (note.leafIndex !== void 0) {
784
- if (typeof note.leafIndex !== "number" || note.leafIndex < 0) {
785
- throw new Error("Leaf index must be a non-negative number");
786
- }
787
- }
788
- if (note.root !== void 0 && !isValidHex(note.root, 32)) {
789
- throw new Error("Invalid root format (expected 64 hex characters)");
790
- }
791
- if (note.merkleProof !== void 0) {
792
- if (!Array.isArray(note.merkleProof.pathElements)) {
793
- throw new Error("Merkle proof pathElements must be an array");
405
+ /**
406
+ * Runs the legacy-purge exactly once per browser origin. Invoked
407
+ * automatically by the constructor; exposed as a static for tests and
408
+ * for callers who want to force the check before the first adapter
409
+ * instantiation.
410
+ */
411
+ static runLegacyPurgeOnce() {
412
+ if (typeof globalThis === "undefined" || !globalThis.localStorage) {
413
+ return;
794
414
  }
795
- if (!Array.isArray(note.merkleProof.pathIndices)) {
796
- throw new Error("Merkle proof pathIndices must be an array");
415
+ const storage = globalThis.localStorage;
416
+ if (storage.getItem(_LocalStorageAdapter.LEGACY_PURGE_SENTINEL_KEY)) {
417
+ return;
797
418
  }
798
- if (note.merkleProof.pathElements.length !== note.merkleProof.pathIndices.length) {
799
- throw new Error("Merkle proof pathElements and pathIndices must have same length");
419
+ storage.removeItem(_LocalStorageAdapter.LEGACY_NOTES_KEY);
420
+ try {
421
+ storage.setItem(_LocalStorageAdapter.LEGACY_PURGE_SENTINEL_KEY, "1");
422
+ } catch {
800
423
  }
801
424
  }
802
- }
803
- function parseNote2(jsonString) {
804
- let parsed;
805
- try {
806
- parsed = JSON.parse(jsonString);
807
- } catch (error) {
808
- throw new Error("Invalid JSON format");
809
- }
810
- validateNote(parsed);
811
- return parsed;
812
- }
813
- function validateWithdrawableNote(note) {
814
- if (!note.depositSignature) {
815
- throw new Error("Note must be deposited before withdrawal (missing depositSignature)");
816
- }
817
- if (note.leafIndex === void 0) {
818
- throw new Error("Note must be deposited before withdrawal (missing leafIndex)");
819
- }
820
- if (!note.root) {
821
- throw new Error("Note must have historical root for withdrawal");
822
- }
823
- if (!note.merkleProof) {
824
- throw new Error("Note must have Merkle proof for withdrawal");
825
- }
826
- if (note.merkleProof.pathElements.length === 0) {
827
- throw new Error("Merkle proof is empty");
828
- }
829
- }
830
- function validateTransfers(recipients, totalAmount) {
831
- if (recipients.length === 0) {
832
- throw new Error("At least one recipient is required");
833
- }
834
- if (recipients.length > 5) {
835
- throw new Error("Maximum 5 recipients allowed");
836
- }
837
- for (let i = 0; i < recipients.length; i++) {
838
- const transfer2 = recipients[i];
839
- const isPublicKeyLike = transfer2.recipient && typeof transfer2.recipient.toBase58 === "function" && typeof transfer2.recipient.toBuffer === "function";
840
- if (!isPublicKeyLike) {
841
- throw new Error(`Recipient ${i} must be a PublicKey (got ${typeof transfer2.recipient})`);
842
- }
843
- if (typeof transfer2.amount !== "number" || transfer2.amount <= 0) {
844
- throw new Error(`Recipient ${i} amount must be a positive number`);
425
+ /**
426
+ * Manual cleanup helper for callers that stored notes under a non-default
427
+ * key. The auto-purge in the constructor only handles the canonical
428
+ * `cloak_notes` slot; pass the custom key here.
429
+ *
430
+ * Pre-0.1.6 versions stored the user's `CloakNote[]` (including
431
+ * `r` randomness and `sk_spend` secret-key hex) as JSON plaintext.
432
+ * Those notes are no longer usable (the OLD `withdraw_regular.circom`
433
+ * flow is incompatible with the deployed UTXO program) but the plaintext
434
+ * would otherwise linger indefinitely.
435
+ *
436
+ * Idempotent; safe to call when the key doesn't exist or when running
437
+ * outside a browser (no-op). Does NOT update the auto-purge sentinel.
438
+ *
439
+ * @param notesKey - localStorage key to remove (default `"cloak_notes"`,
440
+ * matching the pre-0.1.6 default).
441
+ */
442
+ static purgeLegacyNoteStorage(notesKey = _LocalStorageAdapter.LEGACY_NOTES_KEY) {
443
+ if (typeof globalThis === "undefined" || !globalThis.localStorage) {
444
+ return;
845
445
  }
446
+ globalThis.localStorage.removeItem(notesKey);
846
447
  }
847
- const sum = recipients.reduce((acc, t) => acc + t.amount, 0);
848
- if (sum !== totalAmount) {
849
- throw new Error(
850
- `Recipients sum (${sum}) does not match expected total (${totalAmount})`
851
- );
852
- }
853
- }
448
+ };
449
+ /**
450
+ * Sentinel key set after the auto-purge runs. Persists across browser
451
+ * sessions so the purge never re-runs on the same origin.
452
+ */
453
+ _LocalStorageAdapter.LEGACY_PURGE_SENTINEL_KEY = "cloak_purged_v0_1_5";
454
+ /**
455
+ * Default localStorage key the legacy `<= 0.1.5` SDK used for the
456
+ * plaintext `CloakNote[]` blob.
457
+ */
458
+ _LocalStorageAdapter.LEGACY_NOTES_KEY = "cloak_notes";
459
+ var LocalStorageAdapter = _LocalStorageAdapter;
460
+
461
+ // src/services/RelayService.ts
462
+ import { sha256 } from "@noble/hashes/sha2";
854
463
 
855
464
  // src/utils/errors.ts
856
465
  var RootNotFoundError = class extends Error {
@@ -1746,7 +1355,6 @@ function formatErrorForLogging(error) {
1746
1355
  }
1747
1356
 
1748
1357
  // src/services/RelayService.ts
1749
- import { sha256 } from "@noble/hashes/sha2";
1750
1358
  var RelayService = class {
1751
1359
  /**
1752
1360
  * Create a new Relay Service client
@@ -2115,91 +1723,23 @@ var RelayService = class {
2115
1723
  }
2116
1724
  };
2117
1725
 
2118
- // src/solana/instructions.ts
2119
- import {
2120
- PublicKey as PublicKey2,
2121
- SystemProgram,
2122
- TransactionInstruction
2123
- } from "@solana/web3.js";
2124
- function createDepositInstruction(params) {
2125
- if (params.commitment.length !== 32) {
2126
- throw new Error(
2127
- `Invalid commitment length: ${params.commitment.length} (expected 32 bytes)`
2128
- );
2129
- }
2130
- if (params.amount <= 0) {
2131
- throw new Error("Amount must be positive");
2132
- }
2133
- const discriminant = new Uint8Array([1]);
2134
- const amountBytes = new Uint8Array(8);
2135
- new DataView(amountBytes.buffer).setBigUint64(
2136
- 0,
2137
- BigInt(params.amount),
2138
- true
2139
- // little-endian
2140
- );
2141
- const data = new Uint8Array(41);
2142
- data.set(discriminant, 0);
2143
- data.set(amountBytes, 1);
2144
- data.set(params.commitment, 9);
2145
- return new TransactionInstruction({
2146
- programId: params.programId,
2147
- keys: [
2148
- // Account 0: Payer (signer, writable) - pays for transaction
2149
- { pubkey: params.payer, isSigner: true, isWritable: true },
2150
- // Account 1: Pool (writable) - receives SOL
2151
- { pubkey: params.pool, isSigner: false, isWritable: true },
2152
- // Account 2: System Program (readonly) - for transfers
2153
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
2154
- // Account 3: Merkle Tree (writable) - stores on-chain Merkle tree
2155
- { pubkey: params.merkleTree, isSigner: false, isWritable: true }
2156
- ],
2157
- data: Buffer.from(data)
2158
- });
2159
- }
2160
- function validateDepositParams(params) {
2161
- if (!(params.programId instanceof PublicKey2)) {
2162
- throw new Error("programId must be a PublicKey");
2163
- }
2164
- if (!(params.payer instanceof PublicKey2)) {
2165
- throw new Error("payer must be a PublicKey");
2166
- }
2167
- if (!(params.pool instanceof PublicKey2)) {
2168
- throw new Error("pool must be a PublicKey");
2169
- }
2170
- if (!(params.merkleTree instanceof PublicKey2)) {
2171
- throw new Error("merkleTree must be a PublicKey");
2172
- }
2173
- if (typeof params.amount !== "number" || params.amount <= 0) {
2174
- throw new Error("amount must be a positive number");
2175
- }
2176
- if (!(params.commitment instanceof Uint8Array)) {
2177
- throw new Error("commitment must be a Uint8Array");
2178
- }
2179
- if (params.commitment.length !== 32) {
2180
- throw new Error(
2181
- `commitment must be 32 bytes (got ${params.commitment.length})`
2182
- );
2183
- }
2184
- }
2185
-
2186
1726
  // src/utils/pda.ts
2187
- import { PublicKey as PublicKey3 } from "@solana/web3.js";
1727
+ import { PublicKey } from "@solana/web3.js";
2188
1728
  function getShieldPoolPDAs(programId, mint = NATIVE_SOL_MINT) {
2189
1729
  const pid = programId || CLOAK_PROGRAM_ID;
2190
- const [pool] = PublicKey3.findProgramAddressSync(
1730
+ const [pool] = PublicKey.findProgramAddressSync(
2191
1731
  [Buffer.from("pool"), mint.toBuffer()],
2192
1732
  pid
2193
1733
  );
2194
- const [merkleTree] = PublicKey3.findProgramAddressSync(
1734
+ const [merkleTree] = PublicKey.findProgramAddressSync(
2195
1735
  [Buffer.from("merkle_tree"), mint.toBuffer()],
2196
1736
  pid
2197
1737
  );
2198
- const [treasury] = PublicKey3.findProgramAddressSync(
1738
+ const [treasury] = PublicKey.findProgramAddressSync(
2199
1739
  [Buffer.from("treasury"), mint.toBuffer()],
2200
1740
  pid
2201
1741
  );
2202
- const [vaultAuthority] = PublicKey3.findProgramAddressSync(
1742
+ const [vaultAuthority] = PublicKey.findProgramAddressSync(
2203
1743
  [Buffer.from("vault_authority"), mint.toBuffer()],
2204
1744
  pid
2205
1745
  );
@@ -2215,7 +1755,7 @@ function getNullifierPDA(poolPubkey, nullifier, programId) {
2215
1755
  if (nullifier.length !== 32) {
2216
1756
  throw new Error(`Nullifier must be 32 bytes, got ${nullifier.length}`);
2217
1757
  }
2218
- return PublicKey3.findProgramAddressSync(
1758
+ return PublicKey.findProgramAddressSync(
2219
1759
  [Buffer.from("nullifier"), poolPubkey.toBuffer(), Buffer.from(nullifier)],
2220
1760
  pid
2221
1761
  );
@@ -2225,270 +1765,26 @@ function getSwapStatePDA(poolPubkey, nullifier, programId) {
2225
1765
  if (nullifier.length !== 32) {
2226
1766
  throw new Error(`Nullifier must be 32 bytes, got ${nullifier.length}`);
2227
1767
  }
2228
- return PublicKey3.findProgramAddressSync(
1768
+ return PublicKey.findProgramAddressSync(
2229
1769
  [Buffer.from("swap_state"), poolPubkey.toBuffer(), Buffer.from(nullifier)],
2230
1770
  pid
2231
1771
  );
2232
1772
  }
2233
1773
 
2234
- // src/utils/proof-generation.ts
2235
- import * as snarkjs from "snarkjs";
2236
- var IS_REACT_NATIVE = typeof navigator !== "undefined" && navigator.product === "ReactNative";
2237
- var IS_BROWSER = IS_REACT_NATIVE || typeof window !== "undefined" || typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
2238
- var DEFAULT_CIRCUITS_URL = "https://cloak-circuits.s3.us-east-1.amazonaws.com/circuits/0.1.0";
2239
- var SKIP_CIRCUIT_VERIFY_ENV = typeof process !== "undefined" && process.env && process.env.CLOAK_SKIP_CIRCUIT_INTEGRITY_CHECK === "1";
2240
- function isUrl(path) {
2241
- return path.startsWith("http://") || path.startsWith("https://");
2242
- }
2243
- function resolveCircuitsUrl(circuitsPath2) {
2244
- if (circuitsPath2.trim() !== "" && circuitsPath2.trim() !== DEFAULT_CIRCUITS_URL) {
2245
- return DEFAULT_CIRCUITS_URL;
1774
+ // src/utils/logger.ts
1775
+ var globalDebugEnabled = false;
1776
+ function checkEnvDebug() {
1777
+ if (typeof process !== "undefined" && process.env) {
1778
+ return !!(process.env.CLOAK_DEBUG === "1" || process.env.CLOAK_DEBUG === "true" || process.env.DEBUG?.includes("cloak"));
2246
1779
  }
2247
- return DEFAULT_CIRCUITS_URL;
1780
+ return false;
2248
1781
  }
2249
- async function fetchFileAsBuffer(url) {
2250
- const response = await fetch(url);
2251
- if (!response.ok) {
2252
- throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
2253
- }
2254
- const arrayBuffer = await response.arrayBuffer();
2255
- return new Uint8Array(arrayBuffer);
1782
+ globalDebugEnabled = checkEnvDebug();
1783
+ function setDebugMode(enabled) {
1784
+ globalDebugEnabled = enabled;
2256
1785
  }
2257
- async function sha256Hex(data) {
2258
- if (IS_BROWSER) {
2259
- const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
2260
- const buffer = bytes.buffer.slice(
2261
- bytes.byteOffset,
2262
- bytes.byteOffset + bytes.byteLength
2263
- );
2264
- const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
2265
- return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
2266
- }
2267
- const nodeCrypto = await getNodeCrypto();
2268
- return nodeCrypto.createHash("sha256").update(data).digest("hex");
2269
- }
2270
- var _nodeCrypto = null;
2271
- function nodeRequire(moduleName) {
2272
- if (IS_BROWSER) {
2273
- throw new Error(`Node.js ${moduleName} module not available in browser/React Native`);
2274
- }
2275
- try {
2276
- const requireFunc = new Function("moduleName", "return require(moduleName)");
2277
- return requireFunc(moduleName);
2278
- } catch {
2279
- throw new Error(`Cannot load Node.js module ${moduleName} synchronously in ESM.`);
2280
- }
2281
- }
2282
- async function getNodeCrypto() {
2283
- if (_nodeCrypto) return _nodeCrypto;
2284
- _nodeCrypto = nodeRequire("crypto");
2285
- return _nodeCrypto;
2286
- }
2287
- async function generateWithdrawRegularProof(inputs, circuitsPath2) {
2288
- const circuitsUrl = resolveCircuitsUrl(circuitsPath2);
2289
- let wasmInput;
2290
- let zkeyInput;
2291
- if (IS_BROWSER) {
2292
- wasmInput = `${circuitsUrl}/withdraw_regular_js/withdraw_regular.wasm`;
2293
- zkeyInput = `${circuitsUrl}/withdraw_regular_final.zkey`;
2294
- } else {
2295
- const wasmUrl = `${circuitsUrl}/withdraw_regular_js/withdraw_regular.wasm`;
2296
- const zkeyUrl = `${circuitsUrl}/withdraw_regular_final.zkey`;
2297
- const [wasmData, zkeyData] = await Promise.all([
2298
- fetchFileAsBuffer(wasmUrl),
2299
- fetchFileAsBuffer(zkeyUrl)
2300
- ]);
2301
- wasmInput = wasmData;
2302
- zkeyInput = zkeyData;
2303
- }
2304
- const verification = await verifyCircuitIntegrity(circuitsUrl, "withdraw_regular");
2305
- if (!verification.valid) {
2306
- throw new Error(
2307
- `withdraw_regular circuit integrity verification failed: ${verification.error ?? "hash mismatch"}`
2308
- );
2309
- }
2310
- const circuitInputs = {
2311
- // Public signals
2312
- root: inputs.root.toString(),
2313
- nullifier: inputs.nullifier.toString(),
2314
- outputs_hash: inputs.outputs_hash.toString(),
2315
- public_amount: inputs.public_amount.toString(),
2316
- // Private common inputs
2317
- amount: inputs.amount.toString(),
2318
- leaf_index: inputs.leaf_index.toString(),
2319
- sk: inputs.sk.map((x) => x.toString()),
2320
- r: inputs.r.map((x) => x.toString()),
2321
- pathElements: inputs.pathElements.map((x) => x.toString()),
2322
- pathIndices: inputs.pathIndices,
2323
- // Outputs
2324
- num_outputs: inputs.num_outputs,
2325
- out_addr: inputs.out_addr.map((addr) => addr.map((x) => x.toString())),
2326
- out_amount: inputs.out_amount.map((x) => x.toString()),
2327
- out_flags: inputs.out_flags,
2328
- // Fee calculation
2329
- var_fee: inputs.var_fee.toString(),
2330
- rem: inputs.rem.toString()
2331
- };
2332
- const { proof, publicSignals } = await snarkjs.groth16.fullProve(circuitInputs, wasmInput, zkeyInput);
2333
- const proofBytes = proofToBytes(proof);
2334
- return {
2335
- proof,
2336
- publicSignals,
2337
- proofBytes,
2338
- publicInputsBytes: new Uint8Array(0)
2339
- // Not used, kept for compatibility
2340
- };
2341
- }
2342
- async function generateWithdrawSwapProof(inputs, circuitsPath2) {
2343
- const circuitsUrl = resolveCircuitsUrl(circuitsPath2);
2344
- let wasmInput;
2345
- let zkeyInput;
2346
- if (IS_BROWSER) {
2347
- wasmInput = `${circuitsUrl}/withdraw_swap_js/withdraw_swap.wasm`;
2348
- zkeyInput = `${circuitsUrl}/withdraw_swap_final.zkey`;
2349
- } else {
2350
- const wasmUrl = `${circuitsUrl}/withdraw_swap_js/withdraw_swap.wasm`;
2351
- const zkeyUrl = `${circuitsUrl}/withdraw_swap_final.zkey`;
2352
- const [wasmData, zkeyData] = await Promise.all([
2353
- fetchFileAsBuffer(wasmUrl),
2354
- fetchFileAsBuffer(zkeyUrl)
2355
- ]);
2356
- wasmInput = wasmData;
2357
- zkeyInput = zkeyData;
2358
- }
2359
- const verification = await verifyCircuitIntegrity(circuitsUrl, "withdraw_swap");
2360
- if (!verification.valid) {
2361
- throw new Error(
2362
- `withdraw_swap circuit integrity verification failed: ${verification.error ?? "hash mismatch"}`
2363
- );
2364
- }
2365
- const sk = splitTo2Limbs(inputs.sk_spend);
2366
- const r = splitTo2Limbs(inputs.r);
2367
- const circuitInputs = {
2368
- // Public signals (must be provided for witness generation)
2369
- root: inputs.root.toString(),
2370
- nullifier: inputs.nullifier.toString(),
2371
- outputs_hash: inputs.outputs_hash.toString(),
2372
- public_amount: inputs.public_amount.toString(),
2373
- // Private inputs
2374
- sk: sk.map((x) => x.toString()),
2375
- r: r.map((x) => x.toString()),
2376
- amount: inputs.amount.toString(),
2377
- leaf_index: inputs.leaf_index.toString(),
2378
- pathElements: inputs.path_elements.map((x) => x.toString()),
2379
- pathIndices: inputs.path_indices,
2380
- input_mint: inputs.input_mint.map((x) => x.toString()),
2381
- output_mint: inputs.output_mint.map((x) => x.toString()),
2382
- recipient_ata: inputs.recipient_ata.map((x) => x.toString()),
2383
- min_output_amount: inputs.min_output_amount.toString(),
2384
- // Note: fee is computed off-chain and validated on-chain:
2385
- // - fixed_fee is 5000000 (0.005 SOL)
2386
- // - var_fee and rem are computed from amount * 3 / 1000 (0.3%)
2387
- var_fee: inputs.var_fee.toString(),
2388
- rem: inputs.rem.toString()
2389
- };
2390
- const { proof, publicSignals } = await snarkjs.groth16.fullProve(
2391
- circuitInputs,
2392
- wasmInput,
2393
- zkeyInput
2394
- );
2395
- const proofBytes = proofToBytes(proof);
2396
- return {
2397
- proof,
2398
- publicSignals,
2399
- proofBytes,
2400
- publicInputsBytes: new Uint8Array(0)
2401
- // Not used, kept for compatibility
2402
- };
2403
- }
2404
- async function areCircuitsAvailable(circuitsPath2) {
2405
- if (!circuitsPath2 || circuitsPath2 === "") {
2406
- return false;
2407
- }
2408
- return isUrl(resolveCircuitsUrl(circuitsPath2));
2409
- }
2410
- async function getDefaultCircuitsPath() {
2411
- return DEFAULT_CIRCUITS_URL;
2412
- }
2413
- var EXPECTED_CIRCUIT_HASHES = {
2414
- withdraw_regular_wasm: "b80c364c926332111945ef437b07a71720fae774e08206cb11b6171f2a2420a3",
2415
- withdraw_regular_zkey: "6dbca2612f7cc257b897d93384ae9dd09c1617fd1668f8287efb1b124488d144",
2416
- withdraw_swap_wasm: "a296badc0047c211a4724c96a4e64a77cd75fbde7cfa4cf23af3640a723e5686",
2417
- withdraw_swap_zkey: "95251954feaac80396651d12a0df3288630fa75db9410aebecb8aaee17a4f6ea"
2418
- };
2419
- async function verifyCircuitIntegrity(circuitsPath2, circuit) {
2420
- const circuitsUrl = resolveCircuitsUrl(circuitsPath2);
2421
- if (SKIP_CIRCUIT_VERIFY_ENV) {
2422
- return {
2423
- valid: true,
2424
- circuit,
2425
- error: "Verification skipped (CLOAK_SKIP_CIRCUIT_INTEGRITY_CHECK=1)"
2426
- };
2427
- }
2428
- const expected = circuit === "withdraw_regular" ? {
2429
- wasm: EXPECTED_CIRCUIT_HASHES.withdraw_regular_wasm,
2430
- zkey: EXPECTED_CIRCUIT_HASHES.withdraw_regular_zkey
2431
- } : {
2432
- wasm: EXPECTED_CIRCUIT_HASHES.withdraw_swap_wasm,
2433
- zkey: EXPECTED_CIRCUIT_HASHES.withdraw_swap_zkey
2434
- };
2435
- const wasmPath = `${circuitsUrl}/${circuit}_js/${circuit}.wasm`;
2436
- const zkeyPath = `${circuitsUrl}/${circuit}_final.zkey`;
2437
- try {
2438
- const [wasmBytes, zkeyBytes] = await Promise.all([
2439
- fetchFileAsBuffer(wasmPath),
2440
- fetchFileAsBuffer(zkeyPath)
2441
- ]);
2442
- const computed = {
2443
- wasm: await sha256Hex(wasmBytes),
2444
- zkey: await sha256Hex(zkeyBytes)
2445
- };
2446
- if (computed.wasm === expected.wasm && computed.zkey === expected.zkey) {
2447
- return {
2448
- valid: true,
2449
- circuit,
2450
- computed,
2451
- expected
2452
- };
2453
- }
2454
- return {
2455
- valid: false,
2456
- circuit,
2457
- error: `Circuit artifact hash mismatch for ${circuit}`,
2458
- computed,
2459
- expected
2460
- };
2461
- } catch (error) {
2462
- return {
2463
- valid: false,
2464
- circuit,
2465
- error: `Circuit verification failed: ${error instanceof Error ? error.message : String(error)}`
2466
- };
2467
- }
2468
- }
2469
- async function verifyAllCircuits(circuitsPath2) {
2470
- const circuitsUrl = resolveCircuitsUrl(circuitsPath2);
2471
- const results = await Promise.all([
2472
- verifyCircuitIntegrity(circuitsUrl, "withdraw_regular"),
2473
- verifyCircuitIntegrity(circuitsUrl, "withdraw_swap")
2474
- ]);
2475
- return results;
2476
- }
2477
-
2478
- // src/utils/logger.ts
2479
- var globalDebugEnabled = false;
2480
- function checkEnvDebug() {
2481
- if (typeof process !== "undefined" && process.env) {
2482
- return !!(process.env.CLOAK_DEBUG === "1" || process.env.CLOAK_DEBUG === "true" || process.env.DEBUG?.includes("cloak"));
2483
- }
2484
- return false;
2485
- }
2486
- globalDebugEnabled = checkEnvDebug();
2487
- function setDebugMode(enabled) {
2488
- globalDebugEnabled = enabled;
2489
- }
2490
- function isDebugEnabled() {
2491
- return globalDebugEnabled;
1786
+ function isDebugEnabled() {
1787
+ return globalDebugEnabled;
2492
1788
  }
2493
1789
  var LOG_COLORS = {
2494
1790
  DEBUG: "\x1B[90m",
@@ -2691,44 +1987,16 @@ async function computeProofInternal(leafIndex, _nextIndex, subtrees) {
2691
1987
  }
2692
1988
 
2693
1989
  // src/core/CloakSDK.ts
2694
- var COMPUTE_BUDGET_PROGRAM_ID = new PublicKey4("ComputeBudget111111111111111111111111111111");
2695
- function createSetLoadedAccountsDataSizeLimitInstruction(bytes) {
2696
- const data = Buffer.alloc(5);
2697
- data.writeUInt8(4, 0);
2698
- data.writeUInt32LE(bytes, 1);
2699
- return new TransactionInstruction2({
2700
- keys: [],
2701
- programId: COMPUTE_BUDGET_PROGRAM_ID,
2702
- data
2703
- });
2704
- }
2705
- var CLOAK_PROGRAM_ID = new PublicKey4("zh1eLd6rSphLejbFfJEneUwzHRfMKxgzrgkfwA6qRkW");
1990
+ var CLOAK_PROGRAM_ID = new PublicKey2("zh1eLd6rSphLejbFfJEneUwzHRfMKxgzrgkfwA6qRkW");
2706
1991
  var CLOAK_API_URL = "https://api.cloak.ag";
2707
1992
  var CloakSDK = class {
2708
- /**
2709
- * Create a new Cloak SDK client
2710
- *
2711
- * @param config - Client configuration
2712
- *
2713
- * @example Node.js mode (with keypair)
2714
- * ```typescript
2715
- * const sdk = new CloakSDK({
2716
- * keypairBytes: keypair.secretKey,
2717
- * network: "devnet"
2718
- * });
2719
- * ```
2720
- *
2721
- * @example Browser mode (with wallet adapter)
2722
- * ```typescript
2723
- * const sdk = new CloakSDK({
2724
- * wallet: walletAdapter,
2725
- * network: "devnet"
2726
- * });
2727
- * ```
2728
- */
2729
1993
  constructor(config) {
2730
1994
  if (!config.keypairBytes && !config.wallet) {
2731
- throw new Error("Must provide either keypairBytes (Node.js) or wallet (Browser)");
1995
+ throw new CloakError(
1996
+ "Must provide either keypairBytes (Node.js) or wallet (Browser)",
1997
+ "validation",
1998
+ false
1999
+ );
2732
2000
  }
2733
2001
  if (config.debug) {
2734
2002
  setDebugMode(true);
@@ -2748,13 +2016,9 @@ var CloakSDK = class {
2748
2016
  }
2749
2017
  }
2750
2018
  const programId = config.programId || CLOAK_PROGRAM_ID;
2751
- const { pool, merkleTree, treasury } = getShieldPoolPDAs(
2752
- programId,
2753
- NATIVE_SOL_MINT
2754
- );
2019
+ const { pool, merkleTree, treasury } = getShieldPoolPDAs(programId, NATIVE_SOL_MINT);
2755
2020
  this.config = {
2756
2021
  network: config.network || "mainnet",
2757
- keypairBytes: config.keypairBytes,
2758
2022
  cloakKeys: config.cloakKeys,
2759
2023
  programId,
2760
2024
  poolAddress: pool,
@@ -2763,887 +2027,31 @@ var CloakSDK = class {
2763
2027
  debug: config.debug
2764
2028
  };
2765
2029
  }
2766
- /**
2767
- * Get the public key for deposits (from keypair or wallet)
2768
- */
2769
- getPublicKey() {
2770
- if (this.keypair) {
2771
- return this.keypair.publicKey;
2772
- }
2773
- if (this.wallet?.publicKey) {
2774
- return this.wallet.publicKey;
2775
- }
2776
- throw new Error("No public key available - wallet not connected or keypair not provided");
2777
- }
2778
- /**
2779
- * Check if the SDK is using a wallet adapter
2780
- */
2781
- isWalletMode() {
2782
- return !!this.wallet && !this.keypair;
2783
- }
2784
- /**
2785
- * Deposit SOL into the Cloak protocol
2786
- *
2787
- * Creates a new note (or uses a provided one), submits a deposit transaction,
2788
- * and registers with the indexer.
2789
- *
2790
- * @param connection - Solana connection
2791
- * @param payer - Payer wallet with sendTransaction method
2792
- * @param amountOrNote - Amount in lamports OR an existing note to deposit
2793
- * @param options - Optional configuration
2794
- * @returns Deposit result with note and transaction info
2795
- *
2796
- * @example
2797
- * ```typescript
2798
- * // Generate and deposit in one step
2799
- * const result = await client.deposit(
2800
- * connection,
2801
- * wallet,
2802
- * 1_000_000_000,
2803
- * {
2804
- * onProgress: (status) => console.log(status)
2805
- * }
2806
- * );
2807
- *
2808
- * // Or deposit a pre-generated note
2809
- * const note = client.generateNote(1_000_000_000);
2810
- * const result = await client.deposit(connection, wallet, note);
2811
- * ```
2812
- */
2813
- async deposit(connection, amountOrNote, options) {
2814
- try {
2815
- let note;
2816
- let isNewNote = false;
2817
- if (typeof amountOrNote === "number") {
2818
- options?.onProgress?.("generating_note", { message: "Generating note with secrets..." });
2819
- note = await generateNote(amountOrNote, this.config.network);
2820
- isNewNote = true;
2821
- } else {
2822
- note = amountOrNote;
2823
- if (note.depositSignature) {
2824
- throw new Error("Note has already been deposited");
2825
- }
2826
- }
2827
- if (isNewNote) {
2828
- await this.storage.saveNote(note);
2829
- if (options?.onNoteGenerated) {
2830
- options?.onProgress?.("awaiting_note_acknowledgment", {
2831
- message: "Waiting for note to be saved before proceeding with deposit..."
2832
- });
2833
- try {
2834
- await options.onNoteGenerated(note);
2835
- } catch (error) {
2836
- throw new Error(
2837
- `Failed to save note before deposit: ${error instanceof Error ? error.message : String(error)}. For your safety, the deposit has been aborted. Your funds are safe.`
2838
- );
2839
- }
2840
- }
2841
- options?.onProgress?.("note_saved", {
2842
- message: "Note saved. Proceeding with on-chain deposit..."
2843
- });
2844
- }
2845
- const payerPubkey = this.getPublicKey();
2846
- const balance = await connection.getBalance(payerPubkey);
2847
- const requiredAmount = note.amount + 5e3;
2848
- if (balance < requiredAmount) {
2849
- throw new Error(
2850
- `Insufficient balance. Required: ${requiredAmount} lamports (${note.amount} + fees), Available: ${balance} lamports`
2851
- );
2852
- }
2853
- const commitmentBytes = hexToBytes(note.commitment);
2854
- const programId = this.config.programId || CLOAK_PROGRAM_ID;
2855
- const depositIx = createDepositInstruction({
2856
- programId,
2857
- payer: payerPubkey,
2858
- pool: this.config.poolAddress,
2859
- merkleTree: this.config.merkleTreeAddress,
2860
- amount: note.amount,
2861
- commitment: commitmentBytes
2862
- });
2863
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
2864
- const priorityFee = options?.priorityFee ?? 1e4;
2865
- const cuPriceIx = ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee });
2866
- const dataSizeLimit = options?.loadedAccountsDataSizeLimit ?? 256 * 1024;
2867
- const dataSizeLimitIx = dataSizeLimit > 0 ? createSetLoadedAccountsDataSizeLimitInstruction(dataSizeLimit) : null;
2868
- let computeUnits;
2869
- if (options?.optimizeCU && this.keypair) {
2870
- options?.onProgress?.("simulating", { message: "Simulating transaction for optimal CU..." });
2871
- const simCuLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 2e5 });
2872
- const simTransaction = new Transaction({
2873
- feePayer: payerPubkey,
2874
- recentBlockhash: blockhash
2875
- }).add(simCuLimitIx).add(cuPriceIx);
2876
- if (dataSizeLimitIx) {
2877
- simTransaction.add(dataSizeLimitIx);
2878
- }
2879
- simTransaction.add(depositIx);
2880
- try {
2881
- const simulation = await connection.simulateTransaction(simTransaction, [this.keypair]);
2882
- if (simulation.value.err) {
2883
- console.warn("Simulation failed, using default CU:", simulation.value.err);
2884
- computeUnits = 4e4;
2885
- } else {
2886
- const simulatedCU = simulation.value.unitsConsumed ?? 3e4;
2887
- computeUnits = Math.ceil(simulatedCU * 1.2);
2888
- console.log(`CU optimization: ${simulatedCU} simulated \u2192 ${computeUnits} limit`);
2889
- }
2890
- } catch (simError) {
2891
- console.warn("Simulation RPC error, using default CU:", simError);
2892
- computeUnits = 4e4;
2893
- }
2894
- } else if (options?.optimizeCU && this.wallet) {
2895
- console.warn("CU optimization via simulation not available in wallet mode (would require double signing). Using default.");
2896
- computeUnits = options?.computeUnits ?? 4e4;
2897
- } else {
2898
- computeUnits = options?.computeUnits ?? 4e4;
2899
- }
2900
- const cuLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits });
2901
- const transaction = new Transaction({
2902
- feePayer: payerPubkey,
2903
- recentBlockhash: blockhash
2904
- }).add(cuLimitIx).add(cuPriceIx);
2905
- if (dataSizeLimitIx) {
2906
- transaction.add(dataSizeLimitIx);
2907
- }
2908
- transaction.add(depositIx);
2909
- if (!transaction.feePayer) {
2910
- throw new Error("Transaction feePayer is not set");
2911
- }
2912
- if (!transaction.recentBlockhash) {
2913
- throw new Error("Transaction recentBlockhash is not set");
2914
- }
2915
- let signature;
2916
- if (this.wallet) {
2917
- if (!this.wallet.publicKey) {
2918
- throw new Error("Wallet not connected - publicKey is null");
2919
- }
2920
- if (this.wallet.signTransaction) {
2921
- try {
2922
- const signedTransaction = await this.wallet.signTransaction(transaction);
2923
- const rawTransaction = signedTransaction.serialize();
2924
- signature = await connection.sendRawTransaction(rawTransaction, {
2925
- skipPreflight: options?.skipPreflight || false,
2926
- preflightCommitment: "confirmed",
2927
- maxRetries: 3
2928
- });
2929
- } catch (signError) {
2930
- if (this.wallet.sendTransaction) {
2931
- signature = await this.wallet.sendTransaction(transaction, connection, {
2932
- skipPreflight: options?.skipPreflight || false,
2933
- preflightCommitment: "confirmed",
2934
- maxRetries: 3
2935
- });
2936
- } else {
2937
- throw signError;
2938
- }
2939
- }
2940
- } else if (this.wallet.sendTransaction) {
2941
- signature = await this.wallet.sendTransaction(transaction, connection, {
2942
- skipPreflight: options?.skipPreflight || false,
2943
- preflightCommitment: "confirmed",
2944
- maxRetries: 3
2945
- });
2946
- } else {
2947
- throw new Error("Wallet adapter must provide either sendTransaction or signTransaction");
2948
- }
2949
- } else if (this.keypair) {
2950
- signature = await connection.sendTransaction(transaction, [this.keypair], {
2951
- skipPreflight: options?.skipPreflight || false,
2952
- preflightCommitment: "confirmed",
2953
- maxRetries: 3
2954
- });
2955
- } else {
2956
- throw new Error("No signing method available - provide keypair or wallet");
2957
- }
2958
- const confirmation = await connection.confirmTransaction({
2959
- signature,
2960
- blockhash,
2961
- lastValidBlockHeight
2962
- });
2963
- if (confirmation.value.err) {
2964
- throw new Error(
2965
- `Transaction failed: ${JSON.stringify(confirmation.value.err)}`
2966
- );
2967
- }
2968
- const txDetails = await connection.getTransaction(signature, {
2969
- commitment: "confirmed",
2970
- maxSupportedTransactionVersion: 0
2971
- });
2972
- const depositSlot = txDetails?.slot ?? 0;
2973
- let leafIndex = null;
2974
- let root = null;
2975
- try {
2976
- if (txDetails?.meta?.logMessages) {
2977
- for (const log of txDetails.meta.logMessages) {
2978
- if (log.includes("Program data:")) {
2979
- try {
2980
- const parts = log.split("Program data:");
2981
- if (parts.length > 1) {
2982
- const base64Data = parts[1].trim();
2983
- const eventData = Buffer.from(base64Data, "base64");
2984
- if (eventData.length >= 81 && eventData[0] === 1) {
2985
- const parsedLeafIndex = Number(eventData.readBigUInt64LE(1));
2986
- const loggedCommitment = eventData.slice(9, 41);
2987
- if (Buffer.compare(loggedCommitment, Buffer.from(commitmentBytes)) === 0) {
2988
- leafIndex = parsedLeafIndex;
2989
- const loggedRoot = eventData.slice(49, 81);
2990
- root = Buffer.from(loggedRoot).toString("hex");
2991
- break;
2992
- }
2993
- }
2994
- }
2995
- } catch (e) {
2996
- continue;
2997
- }
2998
- }
2999
- }
3000
- }
3001
- } catch (e) {
3002
- }
3003
- if (leafIndex === null) {
3004
- const programId2 = this.config.programId || CLOAK_PROGRAM_ID;
3005
- const mintForSOL = NATIVE_SOL_MINT;
3006
- const { merkleTree } = getShieldPoolPDAs(programId2, mintForSOL);
3007
- const merkleTreeAccount = await connection.getAccountInfo(merkleTree);
3008
- if (!merkleTreeAccount) {
3009
- throw new Error("Failed to fetch merkle tree account");
3010
- }
3011
- const nextIndex = Number(merkleTreeAccount.data.readBigUInt64LE(32));
3012
- leafIndex = nextIndex - 1;
3013
- const rootBytes = merkleTreeAccount.data.slice(1064, 1096);
3014
- root = Buffer.from(rootBytes).toString("hex");
3015
- }
3016
- if (leafIndex === null || root === null) {
3017
- throw new Error("Failed to get leaf index and root from transaction logs or on-chain state");
3018
- }
3019
- let merkleProof;
3020
- try {
3021
- const programId2 = this.config.programId || CLOAK_PROGRAM_ID;
3022
- const mintForSOL = NATIVE_SOL_MINT;
3023
- const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId2, mintForSOL);
3024
- const chainProof = await computeProofFromChain(connection, merkleTreePDA, leafIndex);
3025
- merkleProof = {
3026
- pathElements: chainProof.pathElements,
3027
- pathIndices: chainProof.pathIndices
3028
- };
3029
- root = chainProof.root;
3030
- } catch (proofError) {
3031
- console.warn("[SDK] Could not compute proof from chain, will fetch at withdrawal:", proofError);
3032
- }
3033
- const updatedNote = updateNoteWithDeposit(note, {
3034
- signature,
3035
- slot: depositSlot,
3036
- leafIndex,
3037
- root,
3038
- merkleProof
3039
- });
3040
- await this.storage.updateNote(note.commitment, {
3041
- depositSignature: signature,
3042
- depositSlot,
3043
- leafIndex,
3044
- root,
3045
- merkleProof
3046
- });
3047
- return {
3048
- note: updatedNote,
3049
- signature,
3050
- leafIndex,
3051
- root
3052
- };
3053
- } catch (error) {
3054
- throw this.wrapError(error, "Deposit failed");
3055
- }
3056
- }
3057
- /**
3058
- * Private transfer with up to 5 recipients
3059
- *
3060
- * Handles the complete private transfer flow:
3061
- * 1. If note is not deposited, deposits it first and waits for confirmation
3062
- * 2. Generates a zero-knowledge proof
3063
- * 3. Submits the withdrawal via relay service to recipients
3064
- *
3065
- * This is the main method for performing private transfers - it handles everything!
3066
- *
3067
- * @param connection - Solana connection (required for deposit if not already deposited)
3068
- * @param payer - Payer wallet (required for deposit if not already deposited)
3069
- * @param note - Note to spend (can be deposited or not)
3070
- * @param recipients - Array of 1-5 recipients with amounts
3071
- * @param options - Optional configuration
3072
- * @returns Transfer result with signature and outputs
3073
- *
3074
- * @example
3075
- * ```typescript
3076
- * // Create a note (not deposited yet)
3077
- * const note = client.generateNote(1_000_000_000);
3078
- *
3079
- * // privateTransfer handles the full flow: deposit + withdraw
3080
- * const result = await client.privateTransfer(
3081
- * connection,
3082
- * wallet,
3083
- * note,
3084
- * [
3085
- * { recipient: new PublicKey("..."), amount: 500_000_000 },
3086
- * { recipient: new PublicKey("..."), amount: 492_500_000 }
3087
- * ],
3088
- * {
3089
- * relayFeeBps: 50, // 0.5%
3090
- * onProgress: (status) => console.log(status),
3091
- * onProofProgress: (pct) => console.log(`Proof: ${pct}%`)
3092
- * }
3093
- * );
3094
- * console.log(`Success! TX: ${result.signature}`);
3095
- * ```
3096
- */
3097
- async privateTransfer(connection, note, recipients, options) {
3098
- if (!isWithdrawable(note)) {
3099
- const depositResult = await this.deposit(connection, note, {
3100
- skipPreflight: false
3101
- });
3102
- note = depositResult.note;
3103
- }
3104
- const protocolFee = note.amount - getDistributableAmount(note.amount);
3105
- const feeBps = Math.ceil(protocolFee * 1e4 / note.amount);
3106
- const distributableAmount = getDistributableAmount(note.amount);
3107
- validateTransfers(recipients, distributableAmount);
3108
- if (!note.leafIndex && note.leafIndex !== 0) {
3109
- throw new Error("Note must have a leaf index (note must be deposited)");
3110
- }
3111
- if (!isValidHex(note.r, 32)) {
3112
- throw new Error("Note r must be 64 hex characters (32 bytes)");
3113
- }
3114
- if (!isValidHex(note.sk_spend, 32)) {
3115
- throw new Error("Note sk_spend must be 64 hex characters (32 bytes)");
3116
- }
3117
- const nullifier = await computeNullifierAsync(note.sk_spend, note.leafIndex);
3118
- const nullifierHex = nullifier.toString(16).padStart(64, "0");
3119
- const outputsHash = await computeOutputsHashAsync(recipients);
3120
- const outputsHashHex = outputsHash.toString(16).padStart(64, "0");
3121
- const circuitsPath2 = await getDefaultCircuitsPath();
3122
- const useDirectProof = await areCircuitsAvailable(circuitsPath2);
3123
- if (!useDirectProof) {
3124
- throw new Error(
3125
- `Circuits not available at ${circuitsPath2}. Make sure circuits are built and accessible. Circuit artifacts are pinned to S3 and must be reachable from this environment.`
3126
- );
3127
- }
3128
- const MAX_PROOF_RETRIES = 3;
3129
- let lastError = null;
3130
- for (let attempt = 0; attempt < MAX_PROOF_RETRIES; attempt++) {
3131
- try {
3132
- if (attempt > 0) {
3133
- options?.onProgress?.(`proof_refresh_${attempt + 1}`);
3134
- await new Promise((resolve) => setTimeout(resolve, 1e3));
3135
- }
3136
- let merkleProof;
3137
- let merkleRoot;
3138
- const hasStoredProof = note.merkleProof && note.root && note.merkleProof.pathElements && note.merkleProof.pathElements.length > 0;
3139
- if (attempt === 0 && hasStoredProof) {
3140
- merkleProof = note.merkleProof;
3141
- merkleRoot = note.root;
3142
- } else {
3143
- const programId = this.config.programId || CLOAK_PROGRAM_ID;
3144
- const mintForSOL = NATIVE_SOL_MINT;
3145
- const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, mintForSOL);
3146
- const chainProof = await computeProofFromChain(
3147
- connection,
3148
- merkleTreePDA,
3149
- note.leafIndex
3150
- );
3151
- merkleProof = {
3152
- pathElements: chainProof.pathElements,
3153
- pathIndices: chainProof.pathIndices,
3154
- root: chainProof.root
3155
- };
3156
- merkleRoot = chainProof.root;
3157
- note.merkleProof = merkleProof;
3158
- note.root = merkleRoot;
3159
- }
3160
- if (!merkleProof || !merkleRoot) {
3161
- throw new Error("Failed to get Merkle proof from chain");
3162
- }
3163
- if (!merkleProof.pathElements || merkleProof.pathElements.length === 0) {
3164
- throw new Error("Merkle proof is invalid: missing path elements");
3165
- }
3166
- if (merkleProof.pathElements.length !== merkleProof.pathIndices.length) {
3167
- throw new Error("Merkle proof is invalid: path elements and indices length mismatch");
3168
- }
3169
- for (let i = 0; i < merkleProof.pathIndices.length; i++) {
3170
- const idx = merkleProof.pathIndices[i];
3171
- if (idx !== 0 && idx !== 1) {
3172
- throw new Error(`Merkle proof path index at position ${i} must be 0 or 1, got ${idx}`);
3173
- }
3174
- }
3175
- if (!isValidHex(merkleRoot, 32)) {
3176
- throw new Error("Merkle root must be 64 hex characters (32 bytes)");
3177
- }
3178
- for (let i = 0; i < merkleProof.pathElements.length; i++) {
3179
- const element = merkleProof.pathElements[i];
3180
- if (typeof element !== "string" || !isValidHex(element, 32)) {
3181
- throw new Error(`Merkle proof path element at position ${i} must be 64 hex characters (32 bytes)`);
3182
- }
3183
- }
3184
- const sk_spend_bigint = BigInt("0x" + note.sk_spend);
3185
- const r_bigint = BigInt("0x" + note.r);
3186
- const root_bigint = BigInt("0x" + merkleRoot);
3187
- const nullifier_bigint = BigInt("0x" + nullifierHex);
3188
- const outputs_hash_bigint = BigInt("0x" + outputsHashHex);
3189
- const amount_bigint = BigInt(note.amount);
3190
- const pathElements = merkleProof.pathElements.map((p) => BigInt("0x" + p));
3191
- const outAddr = [];
3192
- for (let i = 0; i < 5; i++) {
3193
- if (i < recipients.length) {
3194
- const [lo, hi] = pubkeyToLimbs(recipients[i].recipient.toBytes());
3195
- outAddr.push([lo, hi]);
3196
- } else {
3197
- outAddr.push([0n, 0n]);
3198
- }
3199
- }
3200
- const t = amount_bigint * 3n;
3201
- const varFee = t / 1000n;
3202
- const rem = t % 1000n;
3203
- const outAmount = [];
3204
- const outFlags = [];
3205
- for (let i = 0; i < 5; i++) {
3206
- if (i < recipients.length) {
3207
- outAmount.push(BigInt(recipients[i].amount));
3208
- outFlags.push(1);
3209
- } else {
3210
- outAmount.push(0n);
3211
- outFlags.push(0);
3212
- }
3213
- }
3214
- const sk = splitTo2Limbs(sk_spend_bigint);
3215
- const r = splitTo2Limbs(r_bigint);
3216
- if (attempt === 0) {
3217
- const computedCommitment = await computeCommitment2(amount_bigint, r_bigint, sk_spend_bigint);
3218
- const noteCommitment = BigInt("0x" + note.commitment);
3219
- if (computedCommitment !== noteCommitment) {
3220
- throw new Error(
3221
- `Commitment mismatch! Computed: ${computedCommitment.toString(16)}, Note: ${note.commitment}. This means the note's sk_spend, r, or amount doesn't match what was deposited.`
3222
- );
3223
- }
3224
- }
3225
- const proofInputs = {
3226
- root: root_bigint,
3227
- nullifier: nullifier_bigint,
3228
- outputs_hash: outputs_hash_bigint,
3229
- public_amount: amount_bigint,
3230
- amount: amount_bigint,
3231
- leaf_index: BigInt(note.leafIndex),
3232
- sk,
3233
- r,
3234
- pathElements,
3235
- pathIndices: merkleProof.pathIndices,
3236
- num_outputs: recipients.length,
3237
- out_addr: outAddr,
3238
- out_amount: outAmount,
3239
- out_flags: outFlags,
3240
- var_fee: varFee,
3241
- rem
3242
- };
3243
- options?.onProgress?.("proof_generating");
3244
- const proofResult = await generateWithdrawRegularProof(proofInputs, circuitsPath2);
3245
- options?.onProgress?.("proof_complete");
3246
- const proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
3247
- const finalPublicInputs = {
3248
- root: merkleRoot,
3249
- nf: nullifierHex,
3250
- outputs_hash: outputsHashHex,
3251
- amount: note.amount
3252
- };
3253
- const metadataBundle = options?.metadataBundle;
3254
- const signature = await this.relay.submitWithdraw(
3255
- {
3256
- proof: proofHex,
3257
- publicInputs: finalPublicInputs,
3258
- outputs: recipients.map((r2) => ({
3259
- recipient: r2.recipient.toBase58(),
3260
- amount: r2.amount
3261
- })),
3262
- feeBps,
3263
- // Use calculated protocol fee BPS
3264
- metadataBundle
3265
- },
3266
- options?.onProgress
3267
- );
3268
- return {
3269
- signature,
3270
- outputs: recipients.map((r2) => ({
3271
- recipient: r2.recipient.toBase58(),
3272
- amount: r2.amount
3273
- })),
3274
- nullifier: nullifierHex,
3275
- root: merkleRoot
3276
- };
3277
- } catch (error) {
3278
- lastError = error instanceof Error ? error : new Error(String(error));
3279
- if (error instanceof RootNotFoundError) {
3280
- console.warn(
3281
- `[Cloak SDK] Merkle root expired (attempt ${attempt + 1}/${MAX_PROOF_RETRIES}). Regenerating proof with fresh root...`
3282
- );
3283
- continue;
3284
- }
3285
- throw error;
3286
- }
3287
- }
3288
- throw new Error(
3289
- `Withdrawal failed after ${MAX_PROOF_RETRIES} proof refresh attempts. The Merkle tree is updating too rapidly. Last error: ${lastError?.message}`
3290
- );
3291
- }
3292
- /**
3293
- * Withdraw to a single recipient
3294
- *
3295
- * Convenience method for withdrawing to one address.
3296
- * Handles the complete flow: deposits if needed, then withdraws.
3297
- *
3298
- * @param connection - Solana connection
3299
- * @param payer - Payer wallet
3300
- * @param note - Note to spend
3301
- * @param recipient - Recipient address
3302
- * @param options - Optional configuration
3303
- * @returns Transfer result
3304
- *
3305
- * @example
3306
- * ```typescript
3307
- * const note = client.generateNote(1_000_000_000);
3308
- * const result = await client.withdraw(
3309
- * connection,
3310
- * wallet,
3311
- * note,
3312
- * new PublicKey("..."),
3313
- * { withdrawAll: true }
3314
- * );
3315
- * ```
3316
- */
3317
- async withdraw(connection, note, recipient, options) {
3318
- const withdrawAll = options?.withdrawAll ?? true;
3319
- const amount = withdrawAll ? getDistributableAmount(note.amount) : options?.amount || note.amount;
3320
- if (!withdrawAll && !options?.amount) {
3321
- throw new Error("Must specify amount or set withdrawAll: true");
3322
- }
3323
- return this.privateTransfer(
3324
- connection,
3325
- note,
3326
- [{ recipient, amount }],
3327
- options
3328
- );
3329
- }
3330
- /**
3331
- * Send SOL privately to multiple recipients
3332
- *
3333
- * Convenience method that wraps privateTransfer with a simpler API.
3334
- * Handles the complete flow: deposits if needed, then sends to recipients.
3335
- *
3336
- * @param connection - Solana connection
3337
- * @param note - Note to spend
3338
- * @param recipients - Array of 1-5 recipients with amounts
3339
- * @param options - Optional configuration
3340
- * @returns Transfer result
3341
- *
3342
- * @example
3343
- * ```typescript
3344
- * const note = client.generateNote(1_000_000_000);
3345
- * const result = await client.send(
3346
- * connection,
3347
- * note,
3348
- * [
3349
- * { recipient: new PublicKey("..."), amount: 500_000_000 },
3350
- * { recipient: new PublicKey("..."), amount: 492_500_000 }
3351
- * ]
3352
- * );
3353
- * ```
3354
- */
3355
- async send(connection, note, recipients, options) {
3356
- return this.privateTransfer(connection, note, recipients, options);
3357
- }
3358
- /**
3359
- * Swap SOL for tokens privately
3360
- *
3361
- * Withdraws SOL from a note and swaps it for tokens via the relay service.
3362
- * Handles the complete flow: deposits if needed, generates proof, and submits swap.
3363
- *
3364
- * @param connection - Solana connection
3365
- * @param note - Note to spend
3366
- * @param recipient - Recipient address (will receive tokens)
3367
- * @param options - Swap configuration
3368
- * @returns Swap result with transaction signature
3369
- *
3370
- * @example
3371
- * ```typescript
3372
- * const note = client.generateNote(1_000_000_000);
3373
- * const result = await client.swap(
3374
- * connection,
3375
- * note,
3376
- * new PublicKey("..."), // recipient
3377
- * {
3378
- * outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
3379
- * slippageBps: 100, // 1%
3380
- * getQuote: async (amount, mint, slippage) => {
3381
- * // Fetch quote from your swap API
3382
- * const quote = await fetchSwapQuote(amount, mint, slippage);
3383
- * return {
3384
- * outAmount: quote.outAmount,
3385
- * minOutputAmount: quote.minOutputAmount
3386
- * };
3387
- * }
3388
- * }
3389
- * );
3390
- * ```
3391
- */
3392
- async swap(connection, note, recipient, options) {
3393
- try {
3394
- if (!isWithdrawable(note)) {
3395
- const depositResult = await this.deposit(connection, note, {
3396
- skipPreflight: false
3397
- });
3398
- note = depositResult.note;
3399
- }
3400
- const variableFee = Math.floor(note.amount * 3 / 1e3);
3401
- const feeBps = note.amount === 0 ? 0 : Math.min(Math.floor((variableFee * 1e4 + note.amount - 1) / note.amount), 65535);
3402
- const withdrawAmountLamports = getDistributableAmount(note.amount);
3403
- if (withdrawAmountLamports <= 0) {
3404
- throw new Error("Amount too small after fees");
3405
- }
3406
- let minOutputAmount;
3407
- if (options.minOutputAmount !== void 0) {
3408
- minOutputAmount = options.minOutputAmount;
3409
- } else if (options.getQuote) {
3410
- const quote = await options.getQuote(
3411
- withdrawAmountLamports,
3412
- options.outputMint,
3413
- options.slippageBps || 100
3414
- );
3415
- minOutputAmount = quote.minOutputAmount;
3416
- } else {
3417
- throw new Error(
3418
- "Must provide either minOutputAmount or getQuote function"
3419
- );
3420
- }
3421
- let recipientAta;
3422
- if (options.recipientAta) {
3423
- recipientAta = new PublicKey4(options.recipientAta);
3424
- } else {
3425
- try {
3426
- const splTokenModule = await import("@solana/spl-token");
3427
- const getAssociatedTokenAddress = splTokenModule.getAssociatedTokenAddress;
3428
- if (!getAssociatedTokenAddress) {
3429
- throw new Error("getAssociatedTokenAddress not found");
3430
- }
3431
- const outputMint2 = new PublicKey4(options.outputMint);
3432
- recipientAta = await getAssociatedTokenAddress(outputMint2, recipient);
3433
- } catch (error) {
3434
- throw new Error(
3435
- `Failed to get associated token account: ${error instanceof Error ? error.message : String(error)}. Please install @solana/spl-token or provide recipientAta in options.`
3436
- );
3437
- }
3438
- }
3439
- if (!note.leafIndex && note.leafIndex !== 0) {
3440
- throw new Error("Note must have a leaf index (note must be deposited)");
3441
- }
3442
- const nullifier = await computeNullifierAsync(note.sk_spend, note.leafIndex);
3443
- const nullifierHex = nullifier.toString(16).padStart(64, "0");
3444
- const inputMint = new PublicKey4("11111111111111111111111111111111");
3445
- const outputMint = new PublicKey4(options.outputMint);
3446
- const outputsHash = await computeSwapOutputsHashAsync(
3447
- inputMint,
3448
- outputMint,
3449
- recipientAta,
3450
- minOutputAmount,
3451
- note.amount
3452
- );
3453
- const outputsHashHex = outputsHash.toString(16).padStart(64, "0");
3454
- const circuitsPath2 = await getDefaultCircuitsPath();
3455
- const useDirectProof = await areCircuitsAvailable(circuitsPath2);
3456
- if (!useDirectProof) {
3457
- throw new Error(
3458
- `Circuits not available at ${circuitsPath2}. Make sure circuits are built and accessible. Circuit artifacts are pinned to S3 and must be reachable from this environment.`
3459
- );
3460
- }
3461
- const MAX_PROOF_RETRIES = 3;
3462
- let lastError = null;
3463
- for (let attempt = 0; attempt < MAX_PROOF_RETRIES; attempt++) {
3464
- try {
3465
- if (attempt > 0) {
3466
- options?.onProgress?.(`proof_refresh_${attempt + 1}`);
3467
- await new Promise((resolve) => setTimeout(resolve, 1e3));
3468
- }
3469
- const programId = this.config.programId || CLOAK_PROGRAM_ID;
3470
- const mintForSOL = NATIVE_SOL_MINT;
3471
- const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, mintForSOL);
3472
- const chainProof = await computeProofFromChain(connection, merkleTreePDA, note.leafIndex);
3473
- const merkleProof = {
3474
- pathElements: chainProof.pathElements,
3475
- pathIndices: chainProof.pathIndices
3476
- };
3477
- const merkleRoot = chainProof.root;
3478
- if (!merkleRoot) {
3479
- throw new Error("Failed to get Merkle root from chain");
3480
- }
3481
- if (!merkleProof.pathElements || merkleProof.pathElements.length === 0) {
3482
- throw new Error("Merkle proof is invalid: missing path elements");
3483
- }
3484
- const sk_spend_bigint = BigInt("0x" + note.sk_spend);
3485
- const r_bigint = BigInt("0x" + note.r);
3486
- const root_bigint = BigInt("0x" + merkleRoot);
3487
- const nullifier_bigint = BigInt("0x" + nullifierHex);
3488
- const outputs_hash_bigint = BigInt("0x" + outputsHashHex);
3489
- const amount_bigint = BigInt(note.amount);
3490
- const pathElements = merkleProof.pathElements.map((p) => BigInt("0x" + p));
3491
- const inputMintLimbs = pubkeyToLimbs(inputMint.toBytes());
3492
- const outputMintLimbs = pubkeyToLimbs(outputMint.toBytes());
3493
- const recipientAtaLimbs = pubkeyToLimbs(recipientAta.toBytes());
3494
- if (attempt === 0) {
3495
- const computedCommitment = await computeCommitment2(amount_bigint, r_bigint, sk_spend_bigint);
3496
- const noteCommitment = BigInt("0x" + note.commitment);
3497
- if (computedCommitment !== noteCommitment) {
3498
- throw new Error(
3499
- `Commitment mismatch! Computed: ${computedCommitment.toString(16)}, Note: ${note.commitment}. This means the note's sk_spend, r, or amount doesn't match what was deposited.`
3500
- );
3501
- }
3502
- }
3503
- const t = amount_bigint * 3n;
3504
- const varFee = t / 1000n;
3505
- const rem = t % 1000n;
3506
- const proofInputs = {
3507
- sk_spend: sk_spend_bigint,
3508
- r: r_bigint,
3509
- amount: amount_bigint,
3510
- leaf_index: BigInt(note.leafIndex),
3511
- path_elements: pathElements,
3512
- path_indices: merkleProof.pathIndices,
3513
- root: root_bigint,
3514
- nullifier: nullifier_bigint,
3515
- outputs_hash: outputs_hash_bigint,
3516
- public_amount: amount_bigint,
3517
- input_mint: inputMintLimbs,
3518
- output_mint: outputMintLimbs,
3519
- recipient_ata: recipientAtaLimbs,
3520
- min_output_amount: BigInt(minOutputAmount),
3521
- var_fee: varFee,
3522
- rem
3523
- };
3524
- options?.onProgress?.("proof_generating");
3525
- const proofResult = await generateWithdrawSwapProof(proofInputs, circuitsPath2);
3526
- options?.onProgress?.("proof_complete");
3527
- const proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
3528
- const finalPublicInputs = {
3529
- root: merkleRoot,
3530
- nf: nullifierHex,
3531
- outputs_hash: outputsHashHex,
3532
- amount: note.amount
3533
- };
3534
- const metadataBundle = options?.metadataBundle;
3535
- const signature = await this.relay.submitSwap(
3536
- {
3537
- proof: proofHex,
3538
- publicInputs: finalPublicInputs,
3539
- outputs: [
3540
- {
3541
- recipient: recipient.toBase58(),
3542
- amount: withdrawAmountLamports
3543
- }
3544
- ],
3545
- feeBps,
3546
- swap: {
3547
- output_mint: options.outputMint,
3548
- slippage_bps: options.slippageBps || 100,
3549
- min_output_amount: minOutputAmount
3550
- },
3551
- metadataBundle
3552
- },
3553
- options.onProgress
3554
- );
3555
- return {
3556
- signature,
3557
- outputs: [
3558
- {
3559
- recipient: recipient.toBase58(),
3560
- amount: withdrawAmountLamports
3561
- }
3562
- ],
3563
- nullifier: nullifierHex,
3564
- root: merkleRoot,
3565
- outputMint: options.outputMint,
3566
- minOutputAmount
3567
- };
3568
- } catch (error) {
3569
- lastError = error instanceof Error ? error : new Error(String(error));
3570
- if (error instanceof RootNotFoundError) {
3571
- console.warn(
3572
- `[Cloak SDK] Merkle root expired (attempt ${attempt + 1}/${MAX_PROOF_RETRIES}). Regenerating swap proof with fresh root...`
3573
- );
3574
- continue;
3575
- }
3576
- throw error;
3577
- }
3578
- }
3579
- throw new Error(
3580
- `Swap failed after ${MAX_PROOF_RETRIES} proof refresh attempts. The Merkle tree is updating too rapidly. Last error: ${lastError?.message}`
3581
- );
3582
- } catch (error) {
3583
- throw this.wrapError(error, "Swap failed");
3584
- }
3585
- }
3586
- /**
3587
- * Generate a new note without depositing
3588
- *
3589
- * @param amountLamports - Amount for the note
3590
- * @param useWalletKeys - Whether to use wallet keys (v2.0 recommended)
3591
- * @returns New note (not yet deposited)
3592
- */
3593
- async generateNote(amountLamports, useWalletKeys = false) {
3594
- if (useWalletKeys && this.cloakKeys) {
3595
- return await generateNoteFromWallet(amountLamports, this.cloakKeys, this.config.network);
3596
- } else if (useWalletKeys) {
3597
- const keys = generateCloakKeys();
3598
- this.cloakKeys = keys;
3599
- const result = this.storage.saveKeys(keys);
3600
- if (result instanceof Promise) {
3601
- result.catch(() => {
3602
- });
3603
- }
3604
- return await generateNoteFromWallet(amountLamports, keys, this.config.network);
2030
+ /** Public key of the configured signer (keypair or wallet). */
2031
+ getPublicKey() {
2032
+ if (this.keypair) {
2033
+ return this.keypair.publicKey;
3605
2034
  }
3606
- return await generateNote(amountLamports, this.config.network);
3607
- }
3608
- /**
3609
- * Parse a note from JSON string
3610
- *
3611
- * @param jsonString - JSON representation
3612
- * @returns Parsed note
3613
- */
3614
- parseNote(jsonString) {
3615
- return parseNote2(jsonString);
3616
- }
3617
- /**
3618
- * Export a note to JSON string
3619
- *
3620
- * @param note - Note to export
3621
- * @param pretty - Format with indentation
3622
- * @returns JSON string
3623
- */
3624
- exportNote(note, pretty = false) {
3625
- return pretty ? JSON.stringify(note, null, 2) : JSON.stringify(note);
2035
+ if (this.wallet?.publicKey) {
2036
+ return this.wallet.publicKey;
2037
+ }
2038
+ throw new CloakError(
2039
+ "No public key available - wallet not connected and no keypair provided",
2040
+ "wallet",
2041
+ false
2042
+ );
3626
2043
  }
3627
- /**
3628
- * Check if a note is ready for withdrawal
3629
- *
3630
- * @param note - Note to check
3631
- * @returns True if withdrawable
3632
- */
3633
- isWithdrawable(note) {
3634
- return isWithdrawable(note);
2044
+ /** True when the SDK was constructed with a wallet adapter (browser). */
2045
+ isWalletMode() {
2046
+ return !!this.wallet && !this.keypair;
3635
2047
  }
3636
2048
  /**
3637
- * Get Merkle proof for a leaf index directly from on-chain state
3638
- *
3639
- * @param connection - Solana connection
3640
- * @param leafIndex - Leaf index in tree
3641
- * @returns Merkle proof computed from on-chain data
2049
+ * Compute a Merkle proof for `leafIndex` from on-chain state. No relay /
2050
+ * indexer round-trip required.
3642
2051
  */
3643
2052
  async getMerkleProof(connection, leafIndex) {
3644
2053
  const programId = this.config.programId || CLOAK_PROGRAM_ID;
3645
- const mintForSOL = NATIVE_SOL_MINT;
3646
- const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, mintForSOL);
2054
+ const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, NATIVE_SOL_MINT);
3647
2055
  const chainProof = await computeProofFromChain(connection, merkleTreePDA, leafIndex);
3648
2056
  return {
3649
2057
  pathElements: chainProof.pathElements,
@@ -3651,72 +2059,20 @@ var CloakSDK = class {
3651
2059
  root: chainProof.root
3652
2060
  };
3653
2061
  }
3654
- /**
3655
- * Get current Merkle root directly from on-chain state
3656
- *
3657
- * @param connection - Solana connection
3658
- * @returns Current root hash from on-chain tree
3659
- */
2062
+ /** Read the current SOL-pool Merkle root from on-chain state. */
3660
2063
  async getCurrentRoot(connection) {
3661
2064
  const programId = this.config.programId || CLOAK_PROGRAM_ID;
3662
- const mintForSOL = NATIVE_SOL_MINT;
3663
- const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, mintForSOL);
2065
+ const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, NATIVE_SOL_MINT);
3664
2066
  const state = await readMerkleTreeState(connection, merkleTreePDA);
3665
2067
  return state.root;
3666
2068
  }
3667
- /**
3668
- * Get transaction status from relay service
3669
- *
3670
- * @param requestId - Request ID from previous submission
3671
- * @returns Current status
3672
- */
2069
+ /** Poll relay for the status of a previously-submitted request. */
3673
2070
  async getTransactionStatus(requestId) {
3674
2071
  return this.relay.getStatus(requestId);
3675
2072
  }
3676
- /**
3677
- * Fetch and decrypt this user's transaction metadata history.
3678
- * DEPRECATED: This functionality is no longer supported
3679
- */
3680
- async getTransactionMetadata(_options) {
3681
- throw new Error("getTransactionMetadata is deprecated - use compliance export API instead");
3682
- }
3683
- /**
3684
- * Load all notes from storage
3685
- *
3686
- * @returns Array of saved notes
3687
- */
3688
- async loadNotes() {
3689
- const notes = this.storage.loadAllNotes();
3690
- return Array.isArray(notes) ? notes : await notes;
3691
- }
3692
- /**
3693
- * Save a note to storage
3694
- *
3695
- * @param note - Note to save
3696
- */
3697
- async saveNote(note) {
3698
- const result = this.storage.saveNote(note);
3699
- if (result instanceof Promise) {
3700
- await result;
3701
- }
3702
- }
3703
- /**
3704
- * Find a note by its commitment
3705
- *
3706
- * @param commitment - Commitment hash
3707
- * @returns Note if found
3708
- */
3709
- async findNote(commitment) {
3710
- const notes = await this.loadNotes();
3711
- return findNoteByCommitment(notes, commitment);
3712
- }
3713
- /**
3714
- * Import wallet keys from JSON
3715
- *
3716
- * @param keysJson - JSON string containing keys
3717
- */
2073
+ /** Import wallet keys from JSON; persists to the configured storage adapter. */
3718
2074
  async importWalletKeys(keysJson) {
3719
- const keys = importWalletKeys(keysJson);
2075
+ const keys = importKeys(keysJson);
3720
2076
  this.cloakKeys = keys;
3721
2077
  const result = this.storage.saveKeys(keys);
3722
2078
  if (result instanceof Promise) {
@@ -3724,134 +2080,32 @@ var CloakSDK = class {
3724
2080
  }
3725
2081
  }
3726
2082
  /**
3727
- * Export wallet keys to JSON
3728
- *
3729
- * WARNING: This exports secret keys! Store securely.
3730
- *
3731
- * @returns JSON string with keys
2083
+ * Export wallet keys to JSON.
2084
+ *
2085
+ * WARNING: this exports secret keys. Store securely.
3732
2086
  */
3733
2087
  exportWalletKeys() {
3734
2088
  if (!this.cloakKeys) {
3735
2089
  throw new CloakError("No wallet keys available", "wallet", false);
3736
2090
  }
3737
- return exportWalletKeys(this.cloakKeys);
2091
+ return exportKeys(this.cloakKeys);
3738
2092
  }
3739
2093
  /**
3740
- * Get the configuration
2094
+ * Snapshot of the active SDK configuration.
2095
+ *
2096
+ * The return type ({@link CloakConfig}) is structurally narrower than the
2097
+ * constructor input ({@link CloakSDKOptions}): `keypairBytes`, `wallet`,
2098
+ * and `storage` are intentionally absent from the snapshot. The secrets
2099
+ * are consumed into private SDK state at construction time and are not
2100
+ * re-exposable through this method — the type system enforces it. To
2101
+ * sign, pass a `Keypair` / `WalletAdapter` directly to the standalone
2102
+ * `transact` / `partialWithdraw` / `fullWithdraw` / `swapUtxo` helpers.
3741
2103
  */
3742
2104
  getConfig() {
3743
2105
  return { ...this.config };
3744
2106
  }
3745
- // Note: scanNotes was removed - it required the indexer to store encrypted outputs.
3746
- // Users should maintain their own note storage. This is more privacy-preserving.
3747
- /**
3748
- * Wrap errors with better categorization and user-friendly messages
3749
- *
3750
- * @private
3751
- */
3752
- wrapError(error, context) {
3753
- if (error instanceof CloakError) {
3754
- return error;
3755
- }
3756
- const errorMessage = error instanceof Error ? error.message : String(error);
3757
- if (errorMessage.includes("duplicate key") || errorMessage.includes("already deposited")) {
3758
- return new CloakError(
3759
- "This note was already deposited. Generate a new note to continue.",
3760
- "validation",
3761
- false,
3762
- error instanceof Error ? error : void 0
3763
- );
3764
- }
3765
- if (errorMessage.includes("insufficient funds") || errorMessage.includes("insufficient lamports")) {
3766
- return new CloakError(
3767
- "Insufficient funds for this transaction. Please check your wallet balance.",
3768
- "wallet",
3769
- false,
3770
- error instanceof Error ? error : void 0
3771
- );
3772
- }
3773
- if (errorMessage.includes("Merkle tree") || errorMessage.includes("merkle")) {
3774
- return new CloakError(
3775
- "Failed to read on-chain Merkle tree state. Please try again.",
3776
- "network",
3777
- true,
3778
- error instanceof Error ? error : void 0
3779
- );
3780
- }
3781
- if (errorMessage.includes("timeout") || errorMessage.includes("timed out")) {
3782
- return new CloakError(
3783
- "Network timeout. Please check your connection and try again.",
3784
- "network",
3785
- true,
3786
- error instanceof Error ? error : void 0
3787
- );
3788
- }
3789
- if (errorMessage.includes("not connected") || errorMessage.includes("wallet")) {
3790
- return new CloakError(
3791
- "Wallet not connected. Please connect your wallet first.",
3792
- "wallet",
3793
- false,
3794
- error instanceof Error ? error : void 0
3795
- );
3796
- }
3797
- if (errorMessage.includes("proof") && (errorMessage.includes("failed") || errorMessage.includes("error"))) {
3798
- return new CloakError(
3799
- "Zero-knowledge proof generation failed. This is usually temporary - please try again.",
3800
- "prover",
3801
- true,
3802
- error instanceof Error ? error : void 0
3803
- );
3804
- }
3805
- if (errorMessage.includes("relay") || errorMessage.includes("withdraw")) {
3806
- return new CloakError(
3807
- "Relay service error. Please try again later.",
3808
- "relay",
3809
- true,
3810
- error instanceof Error ? error : void 0
3811
- );
3812
- }
3813
- return new CloakError(
3814
- `${context}: ${errorMessage}`,
3815
- "network",
3816
- false,
3817
- error instanceof Error ? error : void 0
3818
- );
3819
- }
3820
2107
  };
3821
2108
 
3822
- // src/core/note.ts
3823
- function serializeNote(note, pretty = false) {
3824
- validateNote(note);
3825
- return pretty ? JSON.stringify(note, null, 2) : JSON.stringify(note);
3826
- }
3827
- function downloadNote(note, filename) {
3828
- const g = globalThis;
3829
- const doc = g?.document;
3830
- const URL_ = g?.URL;
3831
- const Blob_ = g?.Blob;
3832
- if (!doc || !URL_ || !Blob_) {
3833
- throw new Error("downloadNote is only available in browser environments");
3834
- }
3835
- const json = serializeNote(note, true);
3836
- const blob = new Blob_([json], { type: "application/json" });
3837
- const url = URL_.createObjectURL(blob);
3838
- const defaultFilename = `cloak-note-${note.commitment.slice(0, 8)}.json`;
3839
- const link = doc.createElement("a");
3840
- link.href = url;
3841
- link.download = filename || defaultFilename;
3842
- link.click();
3843
- URL_.revokeObjectURL(url);
3844
- }
3845
- async function copyNoteToClipboard(note) {
3846
- const g = globalThis;
3847
- const nav = g?.navigator;
3848
- if (!nav || !nav.clipboard) {
3849
- throw new Error("Clipboard API not available");
3850
- }
3851
- const json = serializeNote(note, true);
3852
- await nav.clipboard.writeText(json);
3853
- }
3854
-
3855
2109
  // src/core/compliance-keys.ts
3856
2110
  import nacl2 from "tweetnacl";
3857
2111
  import { blake3 as blake32 } from "@noble/hashes/blake3";
@@ -4238,6 +2492,107 @@ async function decryptComplianceMetadataWithMasterKey(encrypted, masterComplianc
4238
2492
  return decryptWithSharedSecret(payload, sharedSecret);
4239
2493
  }
4240
2494
 
2495
+ // src/utils/fees.ts
2496
+ var LAMPORTS_PER_SOL = 1e9;
2497
+ var FIXED_FEE_LAMPORTS = 5e6;
2498
+ var VARIABLE_FEE_NUMERATOR = 3;
2499
+ var VARIABLE_FEE_DENOMINATOR = 1e3;
2500
+ var VARIABLE_FEE_RATE = VARIABLE_FEE_NUMERATOR / VARIABLE_FEE_DENOMINATOR;
2501
+ var MIN_DEPOSIT_LAMPORTS = 1e7;
2502
+ function calculateFee(amountLamports) {
2503
+ const variableFee = Math.floor(amountLamports * VARIABLE_FEE_NUMERATOR / VARIABLE_FEE_DENOMINATOR);
2504
+ return FIXED_FEE_LAMPORTS + variableFee;
2505
+ }
2506
+ function calculateFeeBigint(amountLamports) {
2507
+ const fixed = BigInt(FIXED_FEE_LAMPORTS);
2508
+ const variable = amountLamports * BigInt(VARIABLE_FEE_NUMERATOR) / BigInt(VARIABLE_FEE_DENOMINATOR);
2509
+ return fixed + variable;
2510
+ }
2511
+ function isWithdrawAmountSufficient(amountLamports) {
2512
+ return amountLamports > calculateFeeBigint(amountLamports);
2513
+ }
2514
+ function getDistributableAmount(amountLamports) {
2515
+ return amountLamports - calculateFee(amountLamports);
2516
+ }
2517
+ function formatAmount(lamports, decimals = 9) {
2518
+ return (lamports / LAMPORTS_PER_SOL).toFixed(decimals);
2519
+ }
2520
+ function parseAmount(sol) {
2521
+ const num = parseFloat(sol);
2522
+ if (isNaN(num) || num < 0) {
2523
+ throw new Error(`Invalid SOL amount: ${sol}`);
2524
+ }
2525
+ return Math.floor(num * LAMPORTS_PER_SOL);
2526
+ }
2527
+ function validateOutputsSum(outputs, expectedTotal) {
2528
+ const sum = outputs.reduce((acc, out) => acc + out.amount, 0);
2529
+ return sum === expectedTotal;
2530
+ }
2531
+ function calculateRelayFee(amountLamports, feeBps) {
2532
+ if (feeBps < 0 || feeBps > 1e4) {
2533
+ throw new Error("Fee basis points must be between 0 and 10000");
2534
+ }
2535
+ return Math.floor(amountLamports * feeBps / 1e4);
2536
+ }
2537
+
2538
+ // src/utils/validation.ts
2539
+ import { PublicKey as PublicKey3 } from "@solana/web3.js";
2540
+ function isValidSolanaAddress(address) {
2541
+ try {
2542
+ new PublicKey3(address);
2543
+ return true;
2544
+ } catch {
2545
+ return false;
2546
+ }
2547
+ }
2548
+
2549
+ // src/utils/network.ts
2550
+ function detectNetworkFromRpcUrl(rpcUrl) {
2551
+ const url = rpcUrl || process.env.NEXT_PUBLIC_SOLANA_RPC_URL || "";
2552
+ const lowerUrl = url.toLowerCase();
2553
+ if (lowerUrl.includes("mainnet") || lowerUrl.includes("api.mainnet-beta") || lowerUrl.includes("mainnet-beta")) {
2554
+ return "mainnet";
2555
+ }
2556
+ if (lowerUrl.includes("testnet") || lowerUrl.includes("api.testnet")) {
2557
+ return "testnet";
2558
+ }
2559
+ if (lowerUrl.includes("devnet") || lowerUrl.includes("api.devnet")) {
2560
+ return "devnet";
2561
+ }
2562
+ if (lowerUrl.includes("localhost") || lowerUrl.includes("127.0.0.1") || lowerUrl.includes("local")) {
2563
+ return "localnet";
2564
+ }
2565
+ return "mainnet";
2566
+ }
2567
+ function getRpcUrlForNetwork(network) {
2568
+ switch (network) {
2569
+ case "mainnet":
2570
+ return "https://api.mainnet-beta.solana.com";
2571
+ case "devnet":
2572
+ return "https://api.devnet.solana.com";
2573
+ case "localnet":
2574
+ return "http://localhost:8899";
2575
+ default:
2576
+ return "https://api.mainnet-beta.solana.com";
2577
+ }
2578
+ }
2579
+ function isValidRpcUrl(url) {
2580
+ try {
2581
+ const parsed = new URL(url);
2582
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
2583
+ } catch {
2584
+ return false;
2585
+ }
2586
+ }
2587
+ function getExplorerUrl(signature, network = "devnet") {
2588
+ const cluster = network === "mainnet" ? "" : `?cluster=${network}`;
2589
+ return `https://explorer.solana.com/tx/${signature}${cluster}`;
2590
+ }
2591
+ function getAddressExplorerUrl(address, network = "devnet") {
2592
+ const cluster = network === "mainnet" ? "" : `?cluster=${network}`;
2593
+ return `https://explorer.solana.com/address/${address}${cluster}`;
2594
+ }
2595
+
4241
2596
  // src/utils/verify-utxos.ts
4242
2597
  async function verifyUtxos(utxos, connection, programId, commitment = "confirmed") {
4243
2598
  const checkable = [];
@@ -4366,45 +2721,6 @@ async function fetchRiskQuoteIx(connection, wallet, rangeApiKey) {
4366
2721
  };
4367
2722
  }
4368
2723
 
4369
- // src/helpers/encrypted-output.ts
4370
- function prepareEncryptedOutput(note, cloakKeys) {
4371
- const noteData = {
4372
- amount: note.amount,
4373
- r: note.r,
4374
- sk_spend: note.sk_spend,
4375
- commitment: note.commitment
4376
- };
4377
- const encrypted = encryptNoteForRecipient(noteData, cloakKeys.view.pvk);
4378
- return btoa(JSON.stringify(encrypted));
4379
- }
4380
- function prepareEncryptedOutputForRecipient(note, recipientPvkHex) {
4381
- const noteData = {
4382
- amount: note.amount,
4383
- r: note.r,
4384
- sk_spend: note.sk_spend,
4385
- commitment: note.commitment
4386
- };
4387
- const recipientPvk = hexToBytes(recipientPvkHex);
4388
- const encrypted = encryptNoteForRecipient(noteData, recipientPvk);
4389
- return btoa(JSON.stringify(encrypted));
4390
- }
4391
- function encodeNoteSimple(note) {
4392
- const data = {
4393
- amount: note.amount,
4394
- r: note.r,
4395
- sk_spend: note.sk_spend,
4396
- commitment: note.commitment
4397
- };
4398
- const json = JSON.stringify(data);
4399
- if (typeof Buffer !== "undefined") {
4400
- return Buffer.from(json).toString("base64");
4401
- } else if (typeof btoa !== "undefined") {
4402
- return btoa(json);
4403
- } else {
4404
- throw new Error("No base64 encoding method available");
4405
- }
4406
- }
4407
-
4408
2724
  // src/helpers/wallet-integration.ts
4409
2725
  import {
4410
2726
  Keypair as Keypair2
@@ -4916,181 +3232,14 @@ async function preflightCheck(relayUrl, rootHex) {
4916
3232
  };
4917
3233
  }
4918
3234
 
4919
- // src/utils/pending-operations.ts
4920
- var PENDING_DEPOSITS_KEY = "cloak_pending_deposits";
4921
- var PENDING_WITHDRAWALS_KEY = "cloak_pending_withdrawals";
4922
- function getStorage() {
4923
- if (typeof globalThis !== "undefined" && globalThis.localStorage) {
4924
- return globalThis.localStorage;
4925
- }
4926
- return null;
4927
- }
4928
- function savePendingDeposit(deposit) {
4929
- const storage = getStorage();
4930
- if (!storage) {
4931
- console.warn("localStorage not available - pending deposit not persisted");
4932
- return;
4933
- }
4934
- const deposits = loadPendingDeposits();
4935
- const index = deposits.findIndex((d) => d.note.commitment === deposit.note.commitment);
4936
- if (index >= 0) {
4937
- deposits[index] = deposit;
4938
- } else {
4939
- deposits.push(deposit);
4940
- }
4941
- storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
4942
- }
4943
- function loadPendingDeposits() {
4944
- const storage = getStorage();
4945
- if (!storage) return [];
4946
- const stored = storage.getItem(PENDING_DEPOSITS_KEY);
4947
- if (!stored) return [];
4948
- try {
4949
- return JSON.parse(stored);
4950
- } catch {
4951
- return [];
4952
- }
4953
- }
4954
- function updatePendingDeposit(commitment, updates) {
4955
- const storage = getStorage();
4956
- if (!storage) return;
4957
- const deposits = loadPendingDeposits();
4958
- const index = deposits.findIndex((d) => d.note.commitment === commitment);
4959
- if (index >= 0) {
4960
- deposits[index] = { ...deposits[index], ...updates };
4961
- storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
4962
- }
4963
- }
4964
- function removePendingDeposit(commitment) {
4965
- const storage = getStorage();
4966
- if (!storage) return;
4967
- const deposits = loadPendingDeposits();
4968
- const filtered = deposits.filter((d) => d.note.commitment !== commitment);
4969
- storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(filtered));
4970
- }
4971
- function clearPendingDeposits() {
4972
- const storage = getStorage();
4973
- if (storage) {
4974
- storage.removeItem(PENDING_DEPOSITS_KEY);
4975
- }
4976
- }
4977
- function savePendingWithdrawal(withdrawal) {
4978
- const storage = getStorage();
4979
- if (!storage) {
4980
- console.warn("localStorage not available - pending withdrawal not persisted");
4981
- return;
4982
- }
4983
- const withdrawals = loadPendingWithdrawals();
4984
- const index = withdrawals.findIndex((w) => w.requestId === withdrawal.requestId);
4985
- if (index >= 0) {
4986
- withdrawals[index] = withdrawal;
4987
- } else {
4988
- withdrawals.push(withdrawal);
4989
- }
4990
- storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
4991
- }
4992
- function loadPendingWithdrawals() {
4993
- const storage = getStorage();
4994
- if (!storage) return [];
4995
- const stored = storage.getItem(PENDING_WITHDRAWALS_KEY);
4996
- if (!stored) return [];
4997
- try {
4998
- return JSON.parse(stored);
4999
- } catch {
5000
- return [];
5001
- }
5002
- }
5003
- function updatePendingWithdrawal(requestId, updates) {
5004
- const storage = getStorage();
5005
- if (!storage) return;
5006
- const withdrawals = loadPendingWithdrawals();
5007
- const index = withdrawals.findIndex((w) => w.requestId === requestId);
5008
- if (index >= 0) {
5009
- withdrawals[index] = { ...withdrawals[index], ...updates };
5010
- storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
5011
- }
5012
- }
5013
- function removePendingWithdrawal(requestId) {
5014
- const storage = getStorage();
5015
- if (!storage) return;
5016
- const withdrawals = loadPendingWithdrawals();
5017
- const filtered = withdrawals.filter((w) => w.requestId !== requestId);
5018
- storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(filtered));
5019
- }
5020
- function clearPendingWithdrawals() {
5021
- const storage = getStorage();
5022
- if (storage) {
5023
- storage.removeItem(PENDING_WITHDRAWALS_KEY);
5024
- }
5025
- }
5026
- function hasPendingOperations() {
5027
- const deposits = loadPendingDeposits();
5028
- const withdrawals = loadPendingWithdrawals();
5029
- const pendingDeposits = deposits.filter(
5030
- (d) => d.status === "pending" || d.status === "tx_sent"
5031
- );
5032
- const pendingWithdrawals = withdrawals.filter(
5033
- (w) => w.status === "pending" || w.status === "processing"
5034
- );
5035
- return pendingDeposits.length > 0 || pendingWithdrawals.length > 0;
5036
- }
5037
- function getPendingOperationsSummary() {
5038
- const deposits = loadPendingDeposits().filter(
5039
- (d) => d.status === "pending" || d.status === "tx_sent"
5040
- );
5041
- const withdrawals = loadPendingWithdrawals().filter(
5042
- (w) => w.status === "pending" || w.status === "processing"
5043
- );
5044
- return {
5045
- deposits,
5046
- withdrawals,
5047
- totalPending: deposits.length + withdrawals.length
5048
- };
5049
- }
5050
- function cleanupStalePendingOperations(maxAgeMs = 24 * 60 * 60 * 1e3) {
5051
- const now = Date.now();
5052
- let removedDeposits = 0;
5053
- let removedWithdrawals = 0;
5054
- const deposits = loadPendingDeposits();
5055
- const activeDeposits = deposits.filter((d) => {
5056
- const age = now - d.startedAt;
5057
- const isStale = age > maxAgeMs;
5058
- const isTerminal = d.status === "confirmed" || d.status === "failed";
5059
- if (isStale || isTerminal) {
5060
- removedDeposits++;
5061
- return false;
5062
- }
5063
- return true;
5064
- });
5065
- const storage = getStorage();
5066
- if (storage) {
5067
- storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(activeDeposits));
5068
- }
5069
- const withdrawals = loadPendingWithdrawals();
5070
- const activeWithdrawals = withdrawals.filter((w) => {
5071
- const age = now - w.startedAt;
5072
- const isStale = age > maxAgeMs;
5073
- const isTerminal = w.status === "completed" || w.status === "failed";
5074
- if (isStale || isTerminal) {
5075
- removedWithdrawals++;
5076
- return false;
5077
- }
5078
- return true;
5079
- });
5080
- if (storage) {
5081
- storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(activeWithdrawals));
5082
- }
5083
- return { removedDeposits, removedWithdrawals };
5084
- }
5085
-
5086
3235
  // src/core/transact.ts
5087
3236
  import {
5088
- PublicKey as PublicKey6,
5089
- Transaction as Transaction3,
5090
- TransactionInstruction as TransactionInstruction3,
3237
+ PublicKey as PublicKey5,
3238
+ Transaction as Transaction2,
3239
+ TransactionInstruction,
5091
3240
  sendAndConfirmTransaction,
5092
- SystemProgram as SystemProgram2,
5093
- ComputeBudgetProgram as ComputeBudgetProgram2,
3241
+ SystemProgram,
3242
+ ComputeBudgetProgram,
5094
3243
  SYSVAR_INSTRUCTIONS_PUBKEY,
5095
3244
  SYSVAR_SLOT_HASHES_PUBKEY,
5096
3245
  VersionedTransaction,
@@ -5103,7 +3252,7 @@ import {
5103
3252
  getAssociatedTokenAddressSync,
5104
3253
  TOKEN_PROGRAM_ID
5105
3254
  } from "@solana/spl-token";
5106
- import * as snarkjs2 from "snarkjs";
3255
+ import * as snarkjs from "snarkjs";
5107
3256
 
5108
3257
  // src/core/chain-note.ts
5109
3258
  var NOTE_VERSION = 2;
@@ -5253,8 +3402,8 @@ function chainNoteFromBase64(base64) {
5253
3402
  // src/core/transact.ts
5254
3403
  import { buildPoseidon as buildPoseidon3 } from "circomlibjs";
5255
3404
  import nacl4 from "tweetnacl";
5256
- var IS_BROWSER2 = typeof window !== "undefined" || typeof globalThis.document !== "undefined";
5257
- var DEFAULT_TRANSACTION_CIRCUITS_URL = "https://cloak-circuits.s3.us-east-1.amazonaws.com/circuits/0.1.0";
3405
+ var IS_BROWSER = typeof window !== "undefined" || typeof globalThis.document !== "undefined";
3406
+ var DEFAULT_TRANSACTION_CIRCUITS_URL = "https://storage.googleapis.com/cloak-circuits/circuits/0.1.0";
5258
3407
  var DEFAULT_CLOAK_RELAY_URL = "https://api.cloak.ag";
5259
3408
  function resolveDefaultTransactionCircuitsPath() {
5260
3409
  return DEFAULT_TRANSACTION_CIRCUITS_URL;
@@ -5266,7 +3415,7 @@ async function resolveTransactionCircuitFiles() {
5266
3415
  const baseNorm = base.endsWith("/") ? base : `${base}/`;
5267
3416
  const wasmUrl = `${baseNorm}transaction_js/transaction.wasm`;
5268
3417
  const zkeyUrl = `${baseNorm}transaction_final.zkey`;
5269
- if (IS_BROWSER2) {
3418
+ if (IS_BROWSER) {
5270
3419
  return { wasmPath: wasmUrl, zkeyPath: zkeyUrl };
5271
3420
  }
5272
3421
  if (_remoteTxCircuitsCache?.base === baseNorm) {
@@ -5403,7 +3552,7 @@ async function resolveAddressLookupTableAccounts(connection, relayUrl, altAddres
5403
3552
  const fetched = await Promise.all(
5404
3553
  altAddresses.map(async (addr) => {
5405
3554
  try {
5406
- const pubkey = new PublicKey6(addr);
3555
+ const pubkey = new PublicKey5(addr);
5407
3556
  const result = await connection.getAddressLookupTable(pubkey);
5408
3557
  return result.value ?? null;
5409
3558
  } catch {
@@ -5565,7 +3714,7 @@ async function generateTransactionProof(inputs, onProgress) {
5565
3714
  onProgress?.(10);
5566
3715
  const { wasmPath: wasmInput, zkeyPath: zkeyInput } = await resolveTransactionCircuitFiles();
5567
3716
  onProgress?.(30);
5568
- const { proof, publicSignals } = await snarkjs2.groth16.fullProve(
3717
+ const { proof, publicSignals } = await snarkjs.groth16.fullProve(
5569
3718
  inputs,
5570
3719
  wasmInput,
5571
3720
  zkeyInput
@@ -5645,7 +3794,7 @@ function buildPublicInputsBytesFromSignals(publicSignals) {
5645
3794
  }
5646
3795
  var TRANSACT_DISCRIMINATOR = 0;
5647
3796
  function deriveNullifierPDA(programId, poolPDA, nullifier) {
5648
- return PublicKey6.findProgramAddressSync(
3797
+ return PublicKey5.findProgramAddressSync(
5649
3798
  [Buffer.from("nullifier"), poolPDA.toBuffer(), Buffer.from(nullifier)],
5650
3799
  programId
5651
3800
  );
@@ -5688,7 +3837,7 @@ function buildTransactInstruction(programId, payer, poolPDA, treasuryPDA, merkle
5688
3837
  // 4. nullifier PDA 0 (writable)
5689
3838
  { pubkey: nullifierPDA1, isSigner: false, isWritable: true },
5690
3839
  // 5. nullifier PDA 1 (writable)
5691
- { pubkey: SystemProgram2.programId, isSigner: false, isWritable: false }
3840
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
5692
3841
  // 6. system_program
5693
3842
  ];
5694
3843
  if (splAccounts) {
@@ -5713,7 +3862,7 @@ function buildTransactInstruction(programId, payer, poolPDA, treasuryPDA, merkle
5713
3862
  accounts.push({ pubkey: SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false });
5714
3863
  }
5715
3864
  }
5716
- return new TransactionInstruction3({
3865
+ return new TransactionInstruction({
5717
3866
  programId,
5718
3867
  keys: accounts,
5719
3868
  data
@@ -5721,10 +3870,10 @@ function buildTransactInstruction(programId, payer, poolPDA, treasuryPDA, merkle
5721
3870
  }
5722
3871
  function getCommonALTAddresses() {
5723
3872
  return [
5724
- SystemProgram2.programId,
3873
+ SystemProgram.programId,
5725
3874
  SYSVAR_SLOT_HASHES_PUBKEY,
5726
3875
  SYSVAR_INSTRUCTIONS_PUBKEY,
5727
- ComputeBudgetProgram2.programId
3876
+ ComputeBudgetProgram.programId
5728
3877
  ];
5729
3878
  }
5730
3879
  function dedupePubkeys(addresses) {
@@ -5785,7 +3934,7 @@ async function createEphemeralALT(connection, depositor, onProgress, additionalA
5785
3934
  lookupTable: altAddress,
5786
3935
  addresses: addressesForCreate
5787
3936
  });
5788
- const tx = new Transaction3().add(createIx).add(extendIx);
3937
+ const tx = new Transaction2().add(createIx).add(extendIx);
5789
3938
  const { blockhash } = await connection.getLatestBlockhash();
5790
3939
  tx.recentBlockhash = blockhash;
5791
3940
  tx.feePayer = depositor.publicKey;
@@ -5951,8 +4100,8 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
5951
4100
  if (riskQuoteIx) {
5952
4101
  baseInstructions.unshift(riskQuoteIx);
5953
4102
  }
5954
- const cuLimitIx = ComputeBudgetProgram2.setComputeUnitLimit({ units: 4e5 });
5955
- const cuPriceIx = ComputeBudgetProgram2.setComputeUnitPrice({ microLamports: 1e5 });
4103
+ const cuLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 4e5 });
4104
+ const cuPriceIx = ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1e5 });
5956
4105
  const fullInstructions = [...baseInstructions, cuLimitIx, cuPriceIx, transactIx];
5957
4106
  const compactInstructions = [...baseInstructions, cuLimitIx, transactIx];
5958
4107
  const minimalInstructions = [...baseInstructions, transactIx];
@@ -6161,7 +4310,7 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
6161
4310
  let legacyTransportAttempt = 0;
6162
4311
  while (true) {
6163
4312
  try {
6164
- const tx = new Transaction3();
4313
+ const tx = new Transaction2();
6165
4314
  for (const ix of instructions) {
6166
4315
  tx.add(ix);
6167
4316
  }
@@ -6343,7 +4492,7 @@ async function fetchRiskQuoteInstruction(riskQuoteUrl, wallet, options) {
6343
4492
  const signerB58 = signerKey;
6344
4493
  const signatureBuf = Buffer.from(signatureB64, "base64");
6345
4494
  const messageArr = hexToBytes2(messageHex);
6346
- const publicKeyBytes = new PublicKey6(signerB58).toBytes();
4495
+ const publicKeyBytes = new PublicKey5(signerB58).toBytes();
6347
4496
  if (signatureBuf.length !== 64 || messageArr.length !== 33 && messageArr.length !== 41 && messageArr.length !== 50 || publicKeyBytes.length !== 32) {
6348
4497
  throw new Error(
6349
4498
  `Invalid risk quote response: invalid lengths (sig=${signatureBuf.length}, msg=${messageArr.length}, pk=${publicKeyBytes.length})`
@@ -6365,10 +4514,10 @@ async function fetchRiskQuoteInstruction(riskQuoteUrl, wallet, options) {
6365
4514
  `Invalid risk quote response: expected relay format { signature, message, signer_pubkey } or { instruction }. Received keys: ${keys}`
6366
4515
  );
6367
4516
  }
6368
- return new TransactionInstruction3({
6369
- programId: new PublicKey6(raw.programId),
4517
+ return new TransactionInstruction({
4518
+ programId: new PublicKey5(raw.programId),
6370
4519
  keys: raw.keys.map((k) => ({
6371
- pubkey: new PublicKey6(k.pubkey),
4520
+ pubkey: new PublicKey5(k.pubkey),
6372
4521
  isSigner: k.isSigner,
6373
4522
  isWritable: k.isWritable
6374
4523
  })),
@@ -7252,12 +5401,12 @@ async function transact(params, options) {
7252
5401
  if (lastError) {
7253
5402
  const classified = classifyRelayError(lastError.message);
7254
5403
  const isMerkleClass = !(classified instanceof UtxoAlreadySpentError) && !(classified instanceof SanctionsQuoteError) && !isRootNotFoundError2(lastError);
7255
- if (!IS_BROWSER2 && isMerkleClass && relayUrl) {
5404
+ if (!IS_BROWSER && isMerkleClass && relayUrl) {
7256
5405
  onProgress?.(
7257
5406
  "All relay-tree attempts failed. Rebuilding merkle tree from chain as last resort..."
7258
5407
  );
7259
5408
  try {
7260
- const [merkleTreePda] = PublicKey6.findProgramAddressSync(
5409
+ const [merkleTreePda] = PublicKey5.findProgramAddressSync(
7261
5410
  [Buffer.from("merkle_tree"), mint.toBuffer()],
7262
5411
  programId
7263
5412
  );
@@ -8326,7 +6475,7 @@ async function swapWithChange(inputUtxos, swapAmount, outputMint, recipientAta,
8326
6475
  // src/core/scanner.ts
8327
6476
  import _bs582 from "bs58";
8328
6477
  import {
8329
- PublicKey as PublicKey7
6478
+ PublicKey as PublicKey6
8330
6479
  } from "@solana/web3.js";
8331
6480
  import { getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2 } from "@solana/spl-token";
8332
6481
  var bs582 = _bs582.default || _bs582;
@@ -8414,7 +6563,7 @@ function parseSwapOutputMint(data) {
8414
6563
  const mintBytes = data.slice(SWAP_OUTPUT_MINT_OFFSET, end);
8415
6564
  if (mintBytes.every((byte) => byte === 0)) return void 0;
8416
6565
  try {
8417
- return new PublicKey7(mintBytes).toBase58();
6566
+ return new PublicKey6(mintBytes).toBase58();
8418
6567
  } catch {
8419
6568
  return void 0;
8420
6569
  }
@@ -8504,7 +6653,7 @@ function parseSwapRecipientAta(data) {
8504
6653
  const recipientAtaBytes = data.slice(recipientAtaStart, recipientAtaEnd);
8505
6654
  if (recipientAtaBytes.every((byte) => byte === 0)) return void 0;
8506
6655
  try {
8507
- return new PublicKey7(recipientAtaBytes).toBase58();
6656
+ return new PublicKey6(recipientAtaBytes).toBase58();
8508
6657
  } catch {
8509
6658
  return void 0;
8510
6659
  }
@@ -8819,8 +6968,8 @@ async function scanTransactions(opts) {
8819
6968
  if (onChainAta) {
8820
6969
  try {
8821
6970
  const expectedAta = getAssociatedTokenAddressSync2(
8822
- new PublicKey7(asset.mint),
8823
- new PublicKey7(walletPublicKey)
6971
+ new PublicKey6(asset.mint),
6972
+ new PublicKey6(walletPublicKey)
8824
6973
  ).toBase58();
8825
6974
  if (onChainAta === expectedAta) {
8826
6975
  isOurs = true;
@@ -9123,7 +7272,7 @@ function formatComplianceCsv(report) {
9123
7272
  }
9124
7273
 
9125
7274
  // src/core/wallet.ts
9126
- import { PublicKey as PublicKey8 } from "@solana/web3.js";
7275
+ import { PublicKey as PublicKey7 } from "@solana/web3.js";
9127
7276
  var UtxoWallet = class _UtxoWallet {
9128
7277
  constructor(viewingKey) {
9129
7278
  this.wallets = /* @__PURE__ */ new Map();
@@ -9335,7 +7484,7 @@ var UtxoWallet = class _UtxoWallet {
9335
7484
  data.viewingKey ? new Uint8Array(data.viewingKey) : void 0
9336
7485
  );
9337
7486
  for (const w of data.wallets) {
9338
- const mint = new PublicKey8(w.mint);
7487
+ const mint = new PublicKey7(w.mint);
9339
7488
  for (const u of w.utxos) {
9340
7489
  wallet.addUtxo({
9341
7490
  amount: BigInt(u.amount),
@@ -9418,15 +7567,13 @@ var SimpleWallet = class {
9418
7567
  };
9419
7568
 
9420
7569
  // src/index.ts
9421
- var VERSION = "0.1.5";
7570
+ var VERSION = "0.1.6";
9422
7571
  var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
9423
7572
  export {
9424
7573
  CLOAK_PROGRAM_ID,
9425
7574
  CloakError,
9426
7575
  CloakSDK,
9427
- DEFAULT_CIRCUITS_URL,
9428
7576
  DEFAULT_TRANSACTION_CIRCUITS_URL,
9429
- EXPECTED_CIRCUIT_HASHES,
9430
7577
  FIXED_FEE_LAMPORTS,
9431
7578
  LAMPORTS_PER_SOL,
9432
7579
  LocalStorageAdapter,
@@ -9449,7 +7596,6 @@ export {
9449
7596
  VARIABLE_FEE_NUMERATOR,
9450
7597
  VARIABLE_FEE_RATE,
9451
7598
  VERSION,
9452
- areCircuitsAvailable,
9453
7599
  bigintToBytes322 as bigintToBytes32,
9454
7600
  bigintToHex,
9455
7601
  buildMerkleTree,
@@ -9463,30 +7609,15 @@ export {
9463
7609
  chainNoteFromBase64,
9464
7610
  chainNoteToBase64,
9465
7611
  classifyRelayError,
9466
- cleanupStalePendingOperations,
9467
- clearPendingDeposits,
9468
- clearPendingWithdrawals,
9469
7612
  computeChainNoteHash,
9470
- computeCommitment2 as computeCommitment,
9471
7613
  computeExtDataHash,
9472
7614
  computeMerkleRoot,
9473
- computeNullifier2 as computeNullifier,
9474
- computeNullifierAsync,
9475
- computeNullifierSync,
9476
- computeOutputsHash,
9477
- computeOutputsHashAsync,
9478
- computeOutputsHashSync,
9479
7615
  computeProofForLatestDeposit,
9480
7616
  computeProofFromChain,
9481
7617
  computeSignature,
9482
- computeSwapOutputsHash,
9483
- computeSwapOutputsHashAsync,
9484
- computeSwapOutputsHashSync,
9485
7618
  computeCommitment as computeUtxoCommitment,
9486
7619
  computeNullifier as computeUtxoNullifier,
9487
- copyNoteToClipboard,
9488
7620
  createCloakError,
9489
- createDepositInstruction,
9490
7621
  createLogger,
9491
7622
  createUtxo,
9492
7623
  createZeroUtxo,
@@ -9506,78 +7637,52 @@ export {
9506
7637
  deriveViewingKeyFromUtxoPrivateKey,
9507
7638
  deserializeUtxo,
9508
7639
  detectNetworkFromRpcUrl,
9509
- downloadNote,
9510
- encodeNoteSimple,
9511
7640
  encryptCompactChainNote,
9512
7641
  encryptNoteForRecipient,
9513
7642
  encryptTransactionMetadata,
9514
7643
  encryptTransactionMetadataBundle,
9515
7644
  expandSpendKey,
9516
7645
  exportKeys,
9517
- exportNote,
9518
- exportWalletKeys,
9519
7646
  fetchCommitments,
9520
7647
  fetchRiskQuoteInstruction,
9521
7648
  fetchRiskQuoteIx,
9522
- filterNotesByNetwork,
9523
- filterWithdrawableNotes,
9524
- findNoteByCommitment,
9525
7649
  formatAmount,
9526
7650
  formatComplianceCsv,
9527
7651
  formatErrorForLogging,
9528
7652
  formatSol,
9529
7653
  fullWithdraw,
9530
7654
  generateCloakKeys,
9531
- generateCommitment,
9532
- generateCommitmentAsync,
9533
7655
  generateMasterSeed,
9534
- generateNote,
9535
- generateNoteFromWallet,
9536
7656
  generateUtxoKeypair,
9537
7657
  generateViewingKeyPair,
9538
- generateWithdrawRegularProof,
9539
- generateWithdrawSwapProof,
9540
7658
  getAddressExplorerUrl,
9541
7659
  getCircuitsPath,
9542
- getDefaultCircuitsPath,
9543
7660
  getDistributableAmount,
9544
7661
  getExplorerUrl,
9545
7662
  getNkFromUtxoPrivateKey,
9546
7663
  getNullifierPDA,
9547
- getPendingOperationsSummary,
9548
7664
  getPublicKey,
9549
- getPublicViewKey,
9550
- getRecipientAmount,
9551
7665
  getRpcUrlForNetwork,
9552
7666
  getShieldPoolPDAs,
9553
7667
  getSwapStatePDA,
9554
- getViewKey,
9555
- hasPendingOperations,
9556
7668
  hexToBigint2 as hexToBigint,
9557
7669
  hexToBytes,
9558
7670
  importKeys,
9559
- importWalletKeys,
9560
7671
  isDebugEnabled,
9561
7672
  isRootNotFoundError,
9562
7673
  isValidHex,
9563
7674
  isValidRpcUrl,
9564
7675
  isValidSolanaAddress,
9565
7676
  isWithdrawAmountSufficient,
9566
- isWithdrawable,
9567
7677
  keypairToAdapter,
9568
- loadPendingDeposits,
9569
- loadPendingWithdrawals,
9570
7678
  parseAmount,
9571
7679
  parseError,
9572
- parseNote,
9573
7680
  parseRelayErrorResponse,
9574
7681
  parseTransactionError,
9575
7682
  partialWithdraw,
9576
7683
  poseidonHash,
9577
7684
  preflightCheck,
9578
7685
  preflightNullifiers,
9579
- prepareEncryptedOutput,
9580
- prepareEncryptedOutputForRecipient,
9581
7686
  proofToBytes,
9582
7687
  pubkeyToFieldElement,
9583
7688
  pubkeyToLimbs,
@@ -9585,16 +7690,11 @@ export {
9585
7690
  randomFieldElement,
9586
7691
  readMerkleTreeState,
9587
7692
  registerViewingKey,
9588
- removePendingDeposit,
9589
- removePendingWithdrawal,
9590
- savePendingDeposit,
9591
- savePendingWithdrawal,
9592
7693
  scanNotesForWallet,
9593
7694
  scanTransactions,
9594
7695
  sdkLogger,
9595
7696
  selectUtxos,
9596
7697
  sendTransaction,
9597
- serializeNote,
9598
7698
  serializeUtxo,
9599
7699
  setCircuitsPath,
9600
7700
  setDebugMode,
@@ -9608,21 +7708,12 @@ export {
9608
7708
  transfer,
9609
7709
  truncate,
9610
7710
  tryDecryptNote,
9611
- updateNoteWithDeposit,
9612
- updatePendingDeposit,
9613
- updatePendingWithdrawal,
9614
7711
  bigintToBytes32 as utxoBigintToBytes32,
9615
7712
  utxoEquals,
9616
7713
  hexToBigint as utxoHexToBigint,
9617
- validateDepositParams,
9618
- validateNote,
9619
7714
  validateOutputsSum,
9620
7715
  validateRoot,
9621
- validateTransfers,
9622
7716
  validateWalletConnected,
9623
- validateWithdrawableNote,
9624
- verifyAllCircuits,
9625
- verifyCircuitIntegrity,
9626
7717
  verifyUtxos,
9627
7718
  waitForRoot,
9628
7719
  withTiming