@cloak.ag/sdk 1.0.4 → 1.0.5

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
@@ -172,14 +172,14 @@ function randomBytes(length) {
172
172
  } catch {
173
173
  }
174
174
  try {
175
- const nodeCrypto = __require("crypto");
176
- if (nodeCrypto?.randomBytes) {
177
- const buffer = nodeCrypto.randomBytes(length);
175
+ const nodeCrypto2 = __require("crypto");
176
+ if (nodeCrypto2?.randomBytes) {
177
+ const buffer = nodeCrypto2.randomBytes(length);
178
178
  bytes.set(buffer);
179
179
  return bytes;
180
180
  }
181
- if (nodeCrypto?.webcrypto?.getRandomValues) {
182
- nodeCrypto.webcrypto.getRandomValues(bytes);
181
+ if (nodeCrypto2?.webcrypto?.getRandomValues) {
182
+ nodeCrypto2.webcrypto.getRandomValues(bytes);
183
183
  return bytes;
184
184
  }
185
185
  } catch {
@@ -301,8 +301,8 @@ function generateMasterSeed() {
301
301
  cryptoObj.getRandomValues(seed);
302
302
  } else {
303
303
  try {
304
- const nodeCrypto = __require("crypto");
305
- const buffer = nodeCrypto.randomBytes(32);
304
+ const nodeCrypto2 = __require("crypto");
305
+ const buffer = nodeCrypto2.randomBytes(32);
306
306
  seed.set(buffer);
307
307
  } catch {
308
308
  throw new Error("No secure random number generator available");
@@ -1043,6 +1043,8 @@ var RelayService = class {
1043
1043
  *
1044
1044
  * @param params - Withdrawal parameters
1045
1045
  * @param onStatusUpdate - Optional callback for status updates
1046
+ * @param onRequestId - CRITICAL: Callback when request_id is received.
1047
+ * Persist this ID to recover if browser crashes during polling.
1046
1048
  * @returns Transaction signature when completed
1047
1049
  *
1048
1050
  * @example
@@ -1052,11 +1054,12 @@ var RelayService = class {
1052
1054
  * publicInputs: { root, nf, outputs_hash, amount },
1053
1055
  * outputs: [{ recipient: addr, amount: lamports }],
1054
1056
  * feeBps: 50
1055
- * }, (status) => console.log(`Status: ${status}`));
1057
+ * }, (status) => console.log(`Status: ${status}`),
1058
+ * (requestId) => localStorage.setItem('pending_withdraw', requestId));
1056
1059
  * console.log(`Transaction: ${signature}`);
1057
1060
  * ```
1058
1061
  */
1059
- async submitWithdraw(params, onStatusUpdate) {
1062
+ async submitWithdraw(params, onStatusUpdate, onRequestId) {
1060
1063
  const proofBytes = hexToBytes(params.proof);
1061
1064
  const proofBase64 = this.bytesToBase64(proofBytes);
1062
1065
  const requestBody = {
@@ -1097,8 +1100,55 @@ var RelayService = class {
1097
1100
  if (!requestId) {
1098
1101
  throw new Error("Relay response missing request_id");
1099
1102
  }
1103
+ if (onRequestId) {
1104
+ onRequestId(requestId);
1105
+ }
1100
1106
  return this.pollForCompletion(requestId, onStatusUpdate);
1101
1107
  }
1108
+ /**
1109
+ * Resume polling for a withdrawal that was previously started
1110
+ *
1111
+ * Use this after page reload to check status of a pending withdrawal.
1112
+ * The requestId should have been persisted via the onRequestId callback.
1113
+ *
1114
+ * @param requestId - Request ID from a previous submitWithdraw call
1115
+ * @param onStatusUpdate - Optional callback for status updates
1116
+ * @returns Transaction signature if completed, null if still pending/failed
1117
+ *
1118
+ * @example
1119
+ * ```typescript
1120
+ * // On page load, check for pending withdrawal
1121
+ * const pendingId = localStorage.getItem('pending_withdraw');
1122
+ * if (pendingId) {
1123
+ * const result = await relay.resumeWithdraw(pendingId);
1124
+ * if (result.status === 'completed') {
1125
+ * console.log('Withdrawal completed:', result.signature);
1126
+ * localStorage.removeItem('pending_withdraw');
1127
+ * }
1128
+ * }
1129
+ * ```
1130
+ */
1131
+ async resumeWithdraw(requestId, onStatusUpdate) {
1132
+ try {
1133
+ const status = await this.getStatus(requestId);
1134
+ if (onStatusUpdate) {
1135
+ onStatusUpdate(status.status);
1136
+ }
1137
+ if (status.status === "completed") {
1138
+ return { status: "completed", signature: status.txId };
1139
+ } else if (status.status === "failed") {
1140
+ return { status: "failed", error: status.error };
1141
+ } else {
1142
+ const signature = await this.pollForCompletion(requestId, onStatusUpdate);
1143
+ return { status: "completed", signature };
1144
+ }
1145
+ } catch (error) {
1146
+ return {
1147
+ status: "failed",
1148
+ error: error instanceof Error ? error.message : String(error)
1149
+ };
1150
+ }
1151
+ }
1102
1152
  /**
1103
1153
  * Poll for withdrawal completion
1104
1154
  *
@@ -1171,7 +1221,7 @@ var RelayService = class {
1171
1221
  * console.log(`Transaction: ${signature}`);
1172
1222
  * ```
1173
1223
  */
1174
- async submitSwap(params, onStatusUpdate) {
1224
+ async submitSwap(params, onStatusUpdate, onRequestId) {
1175
1225
  const proofBytes = hexToBytes(params.proof);
1176
1226
  const proofBase64 = this.bytesToBase64(proofBytes);
1177
1227
  const requestBody = {
@@ -1213,6 +1263,9 @@ var RelayService = class {
1213
1263
  if (!requestId) {
1214
1264
  throw new Error("Relay response missing request_id");
1215
1265
  }
1266
+ if (onRequestId) {
1267
+ onRequestId(requestId);
1268
+ }
1216
1269
  return this.pollForCompletion(requestId, onStatusUpdate);
1217
1270
  }
1218
1271
  /**
@@ -1449,29 +1502,94 @@ var DepositRecoveryService = class {
1449
1502
  }
1450
1503
  /**
1451
1504
  * Check if a deposit already exists in the indexer
1505
+ * Uses the enhanced deposit lookup endpoint with include_proof=true
1452
1506
  *
1453
1507
  * @private
1454
1508
  */
1455
- async checkExistingDeposit(_commitment) {
1509
+ async checkExistingDeposit(commitment) {
1456
1510
  try {
1457
- const { next_index } = await this.indexer.getMerkleRoot();
1458
- const batchSize = 100;
1459
- for (let i = 0; i < next_index; i += batchSize) {
1460
- const end = Math.min(i + batchSize - 1, next_index - 1);
1461
- const { notes } = await this.indexer.getNotesRange(i, end, batchSize);
1462
- for (let j = 0; j < notes.length; j++) {
1463
- try {
1464
- return null;
1465
- } catch (e) {
1466
- continue;
1467
- }
1511
+ const cleanCommit = commitment.replace(/^0x/, "").toLowerCase();
1512
+ const response = await fetch(
1513
+ `${this.apiUrl}/api/v1/deposit/${cleanCommit}?include_proof=true`
1514
+ );
1515
+ if (!response.ok) {
1516
+ if (response.status === 404) {
1517
+ return null;
1468
1518
  }
1519
+ throw new Error(`Failed to check deposit: ${response.status}`);
1469
1520
  }
1470
- return null;
1521
+ const data = await response.json();
1522
+ if (!data.merkle_proof) {
1523
+ const merkleProof = await this.indexer.getMerkleProof(data.leaf_index);
1524
+ return {
1525
+ leafIndex: data.leaf_index,
1526
+ root: merkleProof.root || "",
1527
+ slot: data.slot,
1528
+ merkleProof: {
1529
+ pathElements: merkleProof.pathElements,
1530
+ pathIndices: merkleProof.pathIndices
1531
+ }
1532
+ };
1533
+ }
1534
+ return {
1535
+ leafIndex: data.leaf_index,
1536
+ root: data.merkle_proof.root,
1537
+ slot: data.slot,
1538
+ merkleProof: {
1539
+ pathElements: data.merkle_proof.path_elements,
1540
+ pathIndices: data.merkle_proof.path_indices
1541
+ }
1542
+ };
1471
1543
  } catch (error) {
1472
1544
  return null;
1473
1545
  }
1474
1546
  }
1547
+ /**
1548
+ * Recover deposit by transaction signature
1549
+ * Uses the indexer's signature lookup endpoint
1550
+ */
1551
+ async recoverBySignature(signature) {
1552
+ try {
1553
+ const response = await fetch(
1554
+ `${this.apiUrl}/api/v1/deposit/tx/${signature}?include_proof=true`
1555
+ );
1556
+ if (!response.ok) {
1557
+ if (response.status === 404) {
1558
+ return {
1559
+ success: false,
1560
+ error: "Deposit not found for this transaction signature"
1561
+ };
1562
+ }
1563
+ const errorText = await response.text();
1564
+ return {
1565
+ success: false,
1566
+ error: `Failed to recover deposit: ${errorText}`
1567
+ };
1568
+ }
1569
+ const data = await response.json();
1570
+ if (!data.merkle_proof) {
1571
+ return {
1572
+ success: false,
1573
+ error: "Deposit found but merkle proof not available"
1574
+ };
1575
+ }
1576
+ return {
1577
+ success: true,
1578
+ leafIndex: data.leaf_index,
1579
+ root: data.merkle_proof.root,
1580
+ slot: data.slot,
1581
+ merkleProof: {
1582
+ pathElements: data.merkle_proof.path_elements,
1583
+ pathIndices: data.merkle_proof.path_indices
1584
+ }
1585
+ };
1586
+ } catch (error) {
1587
+ return {
1588
+ success: false,
1589
+ error: error instanceof Error ? error.message : String(error)
1590
+ };
1591
+ }
1592
+ }
1475
1593
  /**
1476
1594
  * Finalize a deposit via server API (alternative recovery method)
1477
1595
  *
@@ -1631,39 +1749,15 @@ function getShieldPoolPDAs(programId, mint) {
1631
1749
 
1632
1750
  // src/utils/proof-generation.ts
1633
1751
  import * as snarkjs from "snarkjs";
1634
- var path = null;
1635
- var fs = null;
1636
- async function loadNodeModules() {
1637
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1638
- if (!isBrowser && typeof process !== "undefined" && process.versions?.node) {
1639
- if (!path) {
1640
- path = await import("path");
1641
- }
1642
- if (!fs) {
1643
- fs = await import("fs");
1644
- }
1645
- return { path, fs };
1646
- }
1647
- return { path: null, fs: null };
1648
- }
1752
+ var IS_BROWSER = typeof window !== "undefined" || typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
1649
1753
  function joinPath(...parts) {
1650
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1651
- if (!isBrowser && path) {
1652
- return path.join(...parts);
1754
+ if (IS_BROWSER) {
1755
+ return parts.join("/").replace(/\/+/g, "/");
1653
1756
  }
1654
1757
  return parts.join("/").replace(/\/+/g, "/");
1655
1758
  }
1656
1759
  async function fileExists(filePath) {
1657
- const { fs: fs2 } = await loadNodeModules();
1658
- if (fs2) {
1659
- try {
1660
- return fs2.existsSync(filePath);
1661
- } catch {
1662
- return false;
1663
- }
1664
- }
1665
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1666
- if (isBrowser) {
1760
+ if (IS_BROWSER) {
1667
1761
  try {
1668
1762
  const response = await fetch(filePath, { method: "HEAD" });
1669
1763
  return response.ok;
@@ -1671,18 +1765,26 @@ async function fileExists(filePath) {
1671
1765
  return false;
1672
1766
  }
1673
1767
  }
1674
- return false;
1768
+ try {
1769
+ const nodeFs2 = globalThis.require?.("fs") || (typeof __require !== "undefined" ? __require("fs") : null);
1770
+ if (nodeFs2) {
1771
+ return nodeFs2.existsSync(filePath);
1772
+ }
1773
+ const fsModule = await import("fs");
1774
+ return fsModule.existsSync(filePath);
1775
+ } catch {
1776
+ return false;
1777
+ }
1675
1778
  }
1676
- async function generateWithdrawRegularProof(inputs, circuitsPath) {
1677
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1779
+ async function generateWithdrawRegularProof(inputs, circuitsPath2) {
1678
1780
  let wasmPath;
1679
1781
  let zkeyPath;
1680
- if (isBrowser) {
1681
- wasmPath = `${circuitsPath}/withdraw_regular_js/withdraw_regular.wasm`;
1682
- zkeyPath = `${circuitsPath}/withdraw_regular_final.zkey`;
1782
+ if (IS_BROWSER) {
1783
+ wasmPath = `${circuitsPath2}/withdraw_regular_js/withdraw_regular.wasm`;
1784
+ zkeyPath = `${circuitsPath2}/withdraw_regular_final.zkey`;
1683
1785
  } else {
1684
- wasmPath = joinPath(circuitsPath, "build", "withdraw_regular_js", "withdraw_regular.wasm");
1685
- zkeyPath = joinPath(circuitsPath, "build", "withdraw_regular_final.zkey");
1786
+ wasmPath = joinPath(circuitsPath2, "build", "withdraw_regular_js", "withdraw_regular.wasm");
1787
+ zkeyPath = joinPath(circuitsPath2, "build", "withdraw_regular_final.zkey");
1686
1788
  const wasmExists = await fileExists(wasmPath);
1687
1789
  const zkeyExists = await fileExists(zkeyPath);
1688
1790
  if (!wasmExists) {
@@ -1724,16 +1826,15 @@ async function generateWithdrawRegularProof(inputs, circuitsPath) {
1724
1826
  // Not used, kept for compatibility
1725
1827
  };
1726
1828
  }
1727
- async function generateWithdrawSwapProof(inputs, circuitsPath) {
1728
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1829
+ async function generateWithdrawSwapProof(inputs, circuitsPath2) {
1729
1830
  let wasmPath;
1730
1831
  let zkeyPath;
1731
- if (isBrowser) {
1732
- wasmPath = `${circuitsPath}/withdraw_swap_js/withdraw_swap.wasm`;
1733
- zkeyPath = `${circuitsPath}/withdraw_swap_final.zkey`;
1832
+ if (IS_BROWSER) {
1833
+ wasmPath = `${circuitsPath2}/withdraw_swap_js/withdraw_swap.wasm`;
1834
+ zkeyPath = `${circuitsPath2}/withdraw_swap_final.zkey`;
1734
1835
  } else {
1735
- wasmPath = joinPath(circuitsPath, "build", "withdraw_swap_js", "withdraw_swap.wasm");
1736
- zkeyPath = joinPath(circuitsPath, "build", "withdraw_swap_final.zkey");
1836
+ wasmPath = joinPath(circuitsPath2, "build", "withdraw_swap_js", "withdraw_swap.wasm");
1837
+ zkeyPath = joinPath(circuitsPath2, "build", "withdraw_swap_final.zkey");
1737
1838
  const wasmExists = await fileExists(wasmPath);
1738
1839
  const zkeyExists = await fileExists(zkeyPath);
1739
1840
  if (!wasmExists) {
@@ -1782,32 +1883,35 @@ async function generateWithdrawSwapProof(inputs, circuitsPath) {
1782
1883
  // Not used, kept for compatibility
1783
1884
  };
1784
1885
  }
1785
- async function areCircuitsAvailable(circuitsPath) {
1786
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1787
- if (isBrowser) {
1788
- return Boolean(circuitsPath && circuitsPath !== "");
1886
+ async function areCircuitsAvailable(circuitsPath2) {
1887
+ if (IS_BROWSER) {
1888
+ return Boolean(circuitsPath2 && circuitsPath2 !== "");
1789
1889
  }
1790
- const wasmPath = joinPath(circuitsPath, "build", "withdraw_regular_js", "withdraw_regular.wasm");
1791
- const zkeyPath = joinPath(circuitsPath, "build", "withdraw_regular_final.zkey");
1890
+ const wasmPath = joinPath(circuitsPath2, "build", "withdraw_regular_js", "withdraw_regular.wasm");
1891
+ const zkeyPath = joinPath(circuitsPath2, "build", "withdraw_regular_final.zkey");
1792
1892
  const wasmExists = await fileExists(wasmPath);
1793
1893
  const zkeyExists = await fileExists(zkeyPath);
1794
1894
  return wasmExists && zkeyExists;
1795
1895
  }
1796
1896
  async function getDefaultCircuitsPath() {
1797
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1798
- if (isBrowser) {
1897
+ if (IS_BROWSER) {
1799
1898
  return "/circuits";
1800
1899
  }
1801
- const { path: path2 } = await loadNodeModules();
1802
- if (!path2 || typeof process === "undefined" || !process.cwd) {
1900
+ if (typeof process === "undefined" || !process.cwd) {
1901
+ return "/circuits";
1902
+ }
1903
+ let nodePath;
1904
+ try {
1905
+ nodePath = eval("require")("path");
1906
+ } catch {
1803
1907
  return "/circuits";
1804
1908
  }
1805
1909
  const possiblePaths = [
1806
- path2.resolve(process.cwd(), "../../packages-new/circuits"),
1807
- path2.resolve(process.cwd(), "../packages-new/circuits"),
1808
- path2.resolve(process.cwd(), "packages-new/circuits"),
1809
- path2.resolve(process.cwd(), "../../circuits"),
1810
- path2.resolve(process.cwd(), "../circuits")
1910
+ nodePath.resolve(process.cwd(), "../../packages-new/circuits"),
1911
+ nodePath.resolve(process.cwd(), "../packages-new/circuits"),
1912
+ nodePath.resolve(process.cwd(), "packages-new/circuits"),
1913
+ nodePath.resolve(process.cwd(), "../../circuits"),
1914
+ nodePath.resolve(process.cwd(), "../circuits")
1811
1915
  ];
1812
1916
  for (const p of possiblePaths) {
1813
1917
  if (await areCircuitsAvailable(p)) {
@@ -1816,6 +1920,90 @@ async function getDefaultCircuitsPath() {
1816
1920
  }
1817
1921
  return possiblePaths[0];
1818
1922
  }
1923
+ var EXPECTED_CIRCUIT_HASHES = {
1924
+ // SHA-256 of the verification key JSON from withdraw_regular circuit
1925
+ withdraw_regular_vkey: null,
1926
+ // Set to null to skip check during development
1927
+ // SHA-256 of the verification key JSON from withdraw_swap circuit
1928
+ withdraw_swap_vkey: null
1929
+ // Set to null to skip check during development
1930
+ };
1931
+ async function verifyCircuitIntegrity(circuitsPath, circuit) {
1932
+ const expectedHash = circuit === "withdraw_regular" ? EXPECTED_CIRCUIT_HASHES.withdraw_regular_vkey : EXPECTED_CIRCUIT_HASHES.withdraw_swap_vkey;
1933
+ if (!expectedHash) {
1934
+ return {
1935
+ valid: true,
1936
+ circuit,
1937
+ error: "Verification skipped (no expected hash configured)"
1938
+ };
1939
+ }
1940
+ try {
1941
+ const vkeyPath = IS_BROWSER ? `${circuitsPath}/${circuit}_verification_key.json` : joinPath(circuitsPath, "build", `${circuit}_verification_key.json`);
1942
+ let vkeyData;
1943
+ if (IS_BROWSER) {
1944
+ const response = await fetch(vkeyPath);
1945
+ if (!response.ok) {
1946
+ return {
1947
+ valid: false,
1948
+ circuit,
1949
+ error: `Failed to fetch verification key: ${response.status}`
1950
+ };
1951
+ }
1952
+ vkeyData = await response.text();
1953
+ } else {
1954
+ try {
1955
+ const nodeFs = eval("require")("fs");
1956
+ vkeyData = nodeFs.readFileSync(vkeyPath, "utf8");
1957
+ } catch (e) {
1958
+ return {
1959
+ valid: false,
1960
+ circuit,
1961
+ error: `Failed to read verification key: ${e}`
1962
+ };
1963
+ }
1964
+ }
1965
+ let computedHash;
1966
+ if (IS_BROWSER) {
1967
+ const encoder = new TextEncoder();
1968
+ const data = encoder.encode(vkeyData);
1969
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
1970
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
1971
+ computedHash = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
1972
+ } else {
1973
+ const nodeCrypto = eval("require")("crypto");
1974
+ computedHash = nodeCrypto.createHash("sha256").update(vkeyData).digest("hex");
1975
+ }
1976
+ if (computedHash === expectedHash) {
1977
+ return {
1978
+ valid: true,
1979
+ circuit,
1980
+ computedHash,
1981
+ expectedHash
1982
+ };
1983
+ } else {
1984
+ return {
1985
+ valid: false,
1986
+ circuit,
1987
+ error: `Verification key hash mismatch! This circuit may produce invalid proofs.`,
1988
+ computedHash,
1989
+ expectedHash
1990
+ };
1991
+ }
1992
+ } catch (error) {
1993
+ return {
1994
+ valid: false,
1995
+ circuit,
1996
+ error: `Circuit verification failed: ${error instanceof Error ? error.message : String(error)}`
1997
+ };
1998
+ }
1999
+ }
2000
+ async function verifyAllCircuits(circuitsPath2) {
2001
+ const results = await Promise.all([
2002
+ verifyCircuitIntegrity(circuitsPath2, "withdraw_regular"),
2003
+ verifyCircuitIntegrity(circuitsPath2, "withdraw_swap")
2004
+ ]);
2005
+ return results;
2006
+ }
1819
2007
 
1820
2008
  // src/core/CloakSDK.ts
1821
2009
  var CLOAK_PROGRAM_ID = new PublicKey4("c1oak6tetxYnNfvXKFkpn1d98FxtK7B68vBQLYQpWKp");
@@ -1930,14 +2118,35 @@ var CloakSDK = class {
1930
2118
  async deposit(connection, amountOrNote, options) {
1931
2119
  try {
1932
2120
  let note;
2121
+ let isNewNote = false;
1933
2122
  if (typeof amountOrNote === "number") {
2123
+ options?.onProgress?.("generating_note", { message: "Generating note with secrets..." });
1934
2124
  note = await generateNote(amountOrNote, this.config.network);
2125
+ isNewNote = true;
1935
2126
  } else {
1936
2127
  note = amountOrNote;
1937
2128
  if (note.depositSignature) {
1938
2129
  throw new Error("Note has already been deposited");
1939
2130
  }
1940
2131
  }
2132
+ if (isNewNote) {
2133
+ await this.storage.saveNote(note);
2134
+ if (options?.onNoteGenerated) {
2135
+ options?.onProgress?.("awaiting_note_acknowledgment", {
2136
+ message: "Waiting for note to be saved before proceeding with deposit..."
2137
+ });
2138
+ try {
2139
+ await options.onNoteGenerated(note);
2140
+ } catch (error) {
2141
+ throw new Error(
2142
+ `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.`
2143
+ );
2144
+ }
2145
+ }
2146
+ options?.onProgress?.("note_saved", {
2147
+ message: "Note saved. Proceeding with on-chain deposit..."
2148
+ });
2149
+ }
1941
2150
  const payerPubkey = this.getPublicKey();
1942
2151
  const balance = await connection.getBalance(payerPubkey);
1943
2152
  const requiredAmount = note.amount + 5e3;
@@ -2129,6 +2338,16 @@ var CloakSDK = class {
2129
2338
  pathIndices: merkleProof.pathIndices
2130
2339
  }
2131
2340
  });
2341
+ await this.storage.updateNote(note.commitment, {
2342
+ depositSignature: signature,
2343
+ depositSlot,
2344
+ leafIndex,
2345
+ root,
2346
+ merkleProof: {
2347
+ pathElements: merkleProof.pathElements,
2348
+ pathIndices: merkleProof.pathIndices
2349
+ }
2350
+ });
2132
2351
  return {
2133
2352
  note: updatedNote,
2134
2353
  signature,
@@ -2230,8 +2449,8 @@ var CloakSDK = class {
2230
2449
  }
2231
2450
  }
2232
2451
  const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
2233
- const circuitsPath = isBrowser ? "/circuits" : typeof process !== "undefined" && process.env?.CIRCUITS_PATH || await getDefaultCircuitsPath();
2234
- const useDirectProof = await areCircuitsAvailable(circuitsPath);
2452
+ const circuitsPath2 = isBrowser ? "/circuits" : typeof process !== "undefined" && process.env?.CIRCUITS_PATH || await getDefaultCircuitsPath();
2453
+ const useDirectProof = await areCircuitsAvailable(circuitsPath2);
2235
2454
  let proofHex;
2236
2455
  let finalPublicInputs;
2237
2456
  if (useDirectProof) {
@@ -2293,7 +2512,7 @@ var CloakSDK = class {
2293
2512
  rem
2294
2513
  };
2295
2514
  options?.onProgress?.("proof_generating");
2296
- const proofResult = await generateWithdrawRegularProof(proofInputs, circuitsPath);
2515
+ const proofResult = await generateWithdrawRegularProof(proofInputs, circuitsPath2);
2297
2516
  options?.onProgress?.("proof_complete");
2298
2517
  proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
2299
2518
  finalPublicInputs = {
@@ -2304,7 +2523,7 @@ var CloakSDK = class {
2304
2523
  };
2305
2524
  } else {
2306
2525
  throw new Error(
2307
- `Circuits not available at ${circuitsPath}. Make sure circuits are built and accessible. In browser, circuits should be served from /circuits. In Node.js, set CIRCUITS_PATH environment variable or ensure circuits are in the expected location.`
2526
+ `Circuits not available at ${circuitsPath2}. Make sure circuits are built and accessible. In browser, circuits should be served from /circuits. In Node.js, set CIRCUITS_PATH environment variable or ensure circuits are in the expected location.`
2308
2527
  );
2309
2528
  }
2310
2529
  const signature = await this.relay.submitWithdraw(
@@ -2501,8 +2720,8 @@ var CloakSDK = class {
2501
2720
  throw new Error("Merkle proof is invalid: missing path elements");
2502
2721
  }
2503
2722
  const envCircuitsPath = typeof window !== "undefined" ? typeof process !== "undefined" && process.env?.NEXT_PUBLIC_CIRCUITS_PATH || void 0 : typeof process !== "undefined" && process.env?.CIRCUITS_PATH || void 0;
2504
- const circuitsPath = envCircuitsPath || await getDefaultCircuitsPath();
2505
- const useDirectProof = await areCircuitsAvailable(circuitsPath);
2723
+ const circuitsPath2 = envCircuitsPath || await getDefaultCircuitsPath();
2724
+ const useDirectProof = await areCircuitsAvailable(circuitsPath2);
2506
2725
  let proofHex;
2507
2726
  let finalPublicInputs;
2508
2727
  if (useDirectProof) {
@@ -2545,7 +2764,7 @@ var CloakSDK = class {
2545
2764
  rem
2546
2765
  };
2547
2766
  options?.onProgress?.("proof_generating");
2548
- const proofResult = await generateWithdrawSwapProof(proofInputs, circuitsPath);
2767
+ const proofResult = await generateWithdrawSwapProof(proofInputs, circuitsPath2);
2549
2768
  options?.onProgress?.("proof_complete");
2550
2769
  proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
2551
2770
  finalPublicInputs = {
@@ -2556,7 +2775,7 @@ var CloakSDK = class {
2556
2775
  };
2557
2776
  } else {
2558
2777
  throw new Error(
2559
- `Circuits not available at ${circuitsPath}. Make sure circuits are built and accessible. In browser, circuits should be served from /circuits. In Node.js, set CIRCUITS_PATH environment variable or ensure circuits are in the expected location.`
2778
+ `Circuits not available at ${circuitsPath2}. Make sure circuits are built and accessible. In browser, circuits should be served from /circuits. In Node.js, set CIRCUITS_PATH environment variable or ensure circuits are in the expected location.`
2560
2779
  );
2561
2780
  }
2562
2781
  const signature = await this.relay.submitSwap(
@@ -2931,6 +3150,63 @@ async function copyNoteToClipboard(note) {
2931
3150
  }
2932
3151
 
2933
3152
  // src/utils/errors.ts
3153
+ var ShieldPoolErrors = {
3154
+ // Root management errors
3155
+ 4096: "Invalid Merkle root",
3156
+ 4097: "Root not found in the roots ring",
3157
+ 4098: "Roots ring is full",
3158
+ // Proof verification errors
3159
+ 4112: "Zero-knowledge proof is invalid",
3160
+ 4113: "Invalid proof size (expected 260 bytes)",
3161
+ 4114: "Invalid public inputs",
3162
+ 4115: "Verification key mismatch",
3163
+ // Nullifier errors
3164
+ 4128: "Double spend detected - this note has already been spent",
3165
+ 4129: "Nullifier shard is full",
3166
+ 4130: "Invalid nullifier",
3167
+ // Transaction validation errors
3168
+ 4144: "Output addresses or amounts don't match the proof",
3169
+ 4145: "Amount conservation failed - outputs + fee must equal input amount",
3170
+ 4146: "Invalid outputs hash",
3171
+ 4147: "Invalid amount (must be greater than zero)",
3172
+ 4148: "Invalid recipient address",
3173
+ 4149: "Commitment already exists in the tree",
3174
+ 4150: "Commitment log is full",
3175
+ // Math errors
3176
+ 4160: "Math overflow occurred",
3177
+ 4161: "Division by zero",
3178
+ // Account errors
3179
+ 4176: "Account validation failed - please check your wallet balance and try again",
3180
+ 4177: "Pool account owner mismatch",
3181
+ 4178: "Treasury account owner mismatch",
3182
+ 4179: "Roots ring account owner mismatch",
3183
+ 4180: "Nullifier shard account owner mismatch",
3184
+ 4181: "Pool account is not writable",
3185
+ 4182: "Treasury account is not writable",
3186
+ 4183: "Recipient account is not writable",
3187
+ 4184: "Insufficient lamports in pool or account",
3188
+ 4185: "Invalid account owner",
3189
+ 4186: "Invalid account size",
3190
+ 4187: "Commitments account is not writable",
3191
+ 4188: "Invalid admin authority",
3192
+ // Instruction errors
3193
+ 4192: "Invalid instruction data length",
3194
+ 4193: "Invalid instruction data format",
3195
+ 4194: "Missing required accounts",
3196
+ 4195: "Invalid instruction tag",
3197
+ // PoW/Scrambler errors
3198
+ 4196: "Invalid miner account",
3199
+ 4197: "Invalid claim account",
3200
+ 4198: "Failed to consume claim",
3201
+ // Groth16 verifier errors
3202
+ 4208: "Invalid G1 point length",
3203
+ 4209: "Invalid G2 point length",
3204
+ 4210: "Invalid public inputs length",
3205
+ 4211: "Public input exceeds field size",
3206
+ 4212: "G1 multiplication failed during proof preparation",
3207
+ 4213: "G1 addition failed during proof preparation",
3208
+ 4214: "Proof verification failed"
3209
+ };
2934
3210
  var PROGRAM_ERRORS = {
2935
3211
  // Nullifier errors
2936
3212
  "NullifierAlreadyUsed": "This note has already been withdrawn. Each note can only be spent once.",
@@ -2959,6 +3235,438 @@ var PROGRAM_ERRORS = {
2959
3235
  "AccountNotFound": "Required account not found.",
2960
3236
  "InvalidInstruction": "Invalid instruction data."
2961
3237
  };
3238
+ var ErrorPatterns = [
3239
+ // Wallet/User action errors
3240
+ {
3241
+ patterns: [
3242
+ "User rejected",
3243
+ "user rejected",
3244
+ "User denied",
3245
+ "user denied",
3246
+ "Transaction cancelled",
3247
+ "Transaction rejected",
3248
+ /code.*4001/i
3249
+ ],
3250
+ result: {
3251
+ title: "Transaction Cancelled",
3252
+ message: "You cancelled the transaction.",
3253
+ category: "wallet",
3254
+ recoverable: true
3255
+ }
3256
+ },
3257
+ {
3258
+ patterns: [
3259
+ "Wallet not connected",
3260
+ "wallet not connected",
3261
+ "Please connect wallet",
3262
+ "No wallet connected"
3263
+ ],
3264
+ result: {
3265
+ title: "Wallet Not Connected",
3266
+ message: "Please connect your wallet to continue.",
3267
+ category: "wallet",
3268
+ suggestion: "Click the wallet button to connect.",
3269
+ recoverable: true
3270
+ }
3271
+ },
3272
+ {
3273
+ patterns: [
3274
+ "SDK not initialized",
3275
+ "sdk not initialized"
3276
+ ],
3277
+ result: {
3278
+ title: "Wallet Connection Required",
3279
+ message: "Your wallet connection was interrupted.",
3280
+ category: "wallet",
3281
+ suggestion: "Please reconnect your wallet and try again.",
3282
+ recoverable: true
3283
+ }
3284
+ },
3285
+ {
3286
+ patterns: [
3287
+ "Wallet does not support signing",
3288
+ "signTransaction"
3289
+ ],
3290
+ result: {
3291
+ title: "Wallet Feature Not Supported",
3292
+ message: "Your wallet doesn't support the required signing method.",
3293
+ category: "wallet",
3294
+ suggestion: "Try using a different wallet like Phantom or Solflare.",
3295
+ recoverable: true
3296
+ }
3297
+ },
3298
+ // Balance errors
3299
+ {
3300
+ patterns: [
3301
+ "insufficient lamports",
3302
+ "Insufficient lamports",
3303
+ "insufficient balance",
3304
+ "Insufficient balance",
3305
+ "Insufficient SOL",
3306
+ "not enough SOL",
3307
+ "Attempt to debit an account but found no record",
3308
+ /0x1$/
3309
+ ],
3310
+ result: {
3311
+ title: "Insufficient Balance",
3312
+ message: "You don't have enough SOL for this transaction.",
3313
+ category: "validation",
3314
+ suggestion: "Add more SOL to your wallet or reduce the amount.",
3315
+ recoverable: true
3316
+ }
3317
+ },
3318
+ {
3319
+ patterns: [
3320
+ "Insufficient funds",
3321
+ "insufficient funds"
3322
+ ],
3323
+ result: {
3324
+ title: "Insufficient Funds",
3325
+ message: "Your wallet doesn't have enough funds for this transaction.",
3326
+ category: "validation",
3327
+ suggestion: "Add more funds to your wallet and try again.",
3328
+ recoverable: true
3329
+ }
3330
+ },
3331
+ // Network errors
3332
+ {
3333
+ patterns: [
3334
+ "fetch failed",
3335
+ "Failed to fetch",
3336
+ "Network error",
3337
+ "network error",
3338
+ "ECONNREFUSED",
3339
+ "ETIMEDOUT",
3340
+ "ENOTFOUND",
3341
+ "NetworkError",
3342
+ "net::ERR",
3343
+ "Failed to load"
3344
+ ],
3345
+ result: {
3346
+ title: "Connection Error",
3347
+ message: "Unable to connect to the network.",
3348
+ category: "network",
3349
+ suggestion: "Check your internet connection and try again.",
3350
+ recoverable: true
3351
+ }
3352
+ },
3353
+ {
3354
+ patterns: [
3355
+ "timeout",
3356
+ "Timeout",
3357
+ "TIMEOUT",
3358
+ "timed out",
3359
+ "Timed out"
3360
+ ],
3361
+ result: {
3362
+ title: "Request Timed Out",
3363
+ message: "The request took too long to complete.",
3364
+ category: "network",
3365
+ suggestion: "The network may be congested. Please try again in a moment.",
3366
+ recoverable: true
3367
+ }
3368
+ },
3369
+ {
3370
+ patterns: [
3371
+ "blockhash not found",
3372
+ "Blockhash not found",
3373
+ "block height exceeded"
3374
+ ],
3375
+ result: {
3376
+ title: "Transaction Expired",
3377
+ message: "The transaction took too long and expired.",
3378
+ category: "network",
3379
+ suggestion: "Please try again. If this persists, the network may be congested.",
3380
+ recoverable: true
3381
+ }
3382
+ },
3383
+ // Service errors (Indexer/Relay)
3384
+ {
3385
+ patterns: [
3386
+ "Indexer",
3387
+ "indexer",
3388
+ /indexer.*unavailable/i,
3389
+ /failed.*indexer/i
3390
+ ],
3391
+ result: {
3392
+ title: "Service Temporarily Unavailable",
3393
+ message: "The privacy service is temporarily unavailable.",
3394
+ category: "service",
3395
+ suggestion: "Please wait a moment and try again.",
3396
+ recoverable: true
3397
+ }
3398
+ },
3399
+ {
3400
+ patterns: [
3401
+ "Relay",
3402
+ "relay",
3403
+ /relay.*unavailable/i,
3404
+ /failed.*relay/i,
3405
+ "failed to submit",
3406
+ "Failed to submit"
3407
+ ],
3408
+ result: {
3409
+ title: "Processing Service Busy",
3410
+ message: "The transaction processing service is busy.",
3411
+ category: "service",
3412
+ suggestion: "Please wait a moment and try again.",
3413
+ recoverable: true
3414
+ }
3415
+ },
3416
+ {
3417
+ patterns: [
3418
+ "429",
3419
+ "Too Many Requests",
3420
+ "rate limit",
3421
+ "Rate limit"
3422
+ ],
3423
+ result: {
3424
+ title: "Too Many Requests",
3425
+ message: "You're making requests too quickly.",
3426
+ category: "service",
3427
+ suggestion: "Please wait a moment before trying again.",
3428
+ recoverable: true
3429
+ }
3430
+ },
3431
+ {
3432
+ patterns: [
3433
+ "503",
3434
+ "Service Unavailable",
3435
+ "502",
3436
+ "Bad Gateway"
3437
+ ],
3438
+ result: {
3439
+ title: "Service Temporarily Unavailable",
3440
+ message: "Our servers are temporarily unavailable.",
3441
+ category: "service",
3442
+ suggestion: "Please try again in a few minutes.",
3443
+ recoverable: true
3444
+ }
3445
+ },
3446
+ // Proof/ZK errors
3447
+ {
3448
+ patterns: [
3449
+ "Circuits not available",
3450
+ "circuits not available",
3451
+ "Failed to load circuit",
3452
+ "WASM",
3453
+ "wasm",
3454
+ /circuit.*not.*found/i
3455
+ ],
3456
+ result: {
3457
+ title: "Loading Error",
3458
+ message: "Failed to load required cryptographic components.",
3459
+ category: "service",
3460
+ suggestion: "Try refreshing the page. If the problem persists, clear your browser cache.",
3461
+ recoverable: true
3462
+ }
3463
+ },
3464
+ {
3465
+ patterns: [
3466
+ "proof generation",
3467
+ "Proof generation",
3468
+ "Failed to generate proof",
3469
+ "proving error"
3470
+ ],
3471
+ result: {
3472
+ title: "Proof Generation Failed",
3473
+ message: "Failed to generate the privacy proof.",
3474
+ category: "service",
3475
+ suggestion: "Try again with a smaller amount or refresh the page.",
3476
+ recoverable: true
3477
+ }
3478
+ },
3479
+ // Validation errors
3480
+ {
3481
+ patterns: [
3482
+ "Invalid amount",
3483
+ "invalid amount",
3484
+ "Amount must be",
3485
+ "amount must be",
3486
+ "Amount too small"
3487
+ ],
3488
+ result: {
3489
+ title: "Invalid Amount",
3490
+ message: "The amount you entered is not valid.",
3491
+ category: "validation",
3492
+ suggestion: "Enter an amount greater than the minimum required.",
3493
+ recoverable: true
3494
+ }
3495
+ },
3496
+ {
3497
+ patterns: [
3498
+ "Invalid recipient",
3499
+ "invalid recipient",
3500
+ "Invalid address",
3501
+ "invalid address",
3502
+ "Invalid Solana address"
3503
+ ],
3504
+ result: {
3505
+ title: "Invalid Address",
3506
+ message: "The recipient address is not valid.",
3507
+ category: "validation",
3508
+ suggestion: "Check the address and make sure it's a valid Solana address.",
3509
+ recoverable: true
3510
+ }
3511
+ },
3512
+ {
3513
+ patterns: [
3514
+ "Invalid token",
3515
+ "invalid token",
3516
+ "Unsupported token"
3517
+ ],
3518
+ result: {
3519
+ title: "Unsupported Token",
3520
+ message: "This token is not supported.",
3521
+ category: "validation",
3522
+ suggestion: "Select a supported token and try again.",
3523
+ recoverable: true
3524
+ }
3525
+ },
3526
+ // Swap errors
3527
+ {
3528
+ patterns: [
3529
+ "Failed to get quote",
3530
+ "failed to get quote",
3531
+ "No route found",
3532
+ "no route found",
3533
+ "Insufficient liquidity",
3534
+ "insufficient liquidity"
3535
+ ],
3536
+ result: {
3537
+ title: "Swap Quote Unavailable",
3538
+ message: "Unable to get a price quote for this swap.",
3539
+ category: "service",
3540
+ suggestion: "Try a different amount or wait for better liquidity.",
3541
+ recoverable: true
3542
+ }
3543
+ },
3544
+ {
3545
+ patterns: [
3546
+ "Slippage",
3547
+ "slippage",
3548
+ "Price impact",
3549
+ "price impact"
3550
+ ],
3551
+ result: {
3552
+ title: "Price Changed",
3553
+ message: "The price changed too much during the transaction.",
3554
+ category: "transaction",
3555
+ suggestion: "Try again or increase your slippage tolerance.",
3556
+ recoverable: true
3557
+ }
3558
+ },
3559
+ // Transaction errors
3560
+ {
3561
+ patterns: [
3562
+ "Double spend",
3563
+ "double spend",
3564
+ "already been spent",
3565
+ "already spent"
3566
+ ],
3567
+ result: {
3568
+ title: "Already Spent",
3569
+ message: "This note has already been used.",
3570
+ category: "transaction",
3571
+ suggestion: "This funds have already been withdrawn.",
3572
+ recoverable: false
3573
+ }
3574
+ },
3575
+ {
3576
+ patterns: [
3577
+ "simulation failed",
3578
+ "Simulation failed",
3579
+ "Transaction simulation"
3580
+ ],
3581
+ result: {
3582
+ title: "Transaction Failed",
3583
+ message: "The transaction could not be completed.",
3584
+ category: "transaction",
3585
+ suggestion: "Please try again. If the problem persists, check your balance.",
3586
+ recoverable: true
3587
+ }
3588
+ },
3589
+ {
3590
+ patterns: [
3591
+ "confirmation timeout",
3592
+ "Confirmation timeout",
3593
+ "not confirmed"
3594
+ ],
3595
+ result: {
3596
+ title: "Confirmation Pending",
3597
+ message: "Transaction confirmation is taking longer than expected.",
3598
+ category: "network",
3599
+ suggestion: "Your transaction may still complete. Check your wallet or try again.",
3600
+ recoverable: true
3601
+ }
3602
+ }
3603
+ ];
3604
+ function parseError(error) {
3605
+ let errorMessage = "";
3606
+ let originalError = "";
3607
+ if (error instanceof Error) {
3608
+ errorMessage = error.message;
3609
+ originalError = error.stack || error.message;
3610
+ } else if (typeof error === "string") {
3611
+ errorMessage = error;
3612
+ originalError = error;
3613
+ } else if (error && typeof error === "object") {
3614
+ const err = error;
3615
+ errorMessage = String(err.message || err.error || err.msg || JSON.stringify(error));
3616
+ originalError = JSON.stringify(error);
3617
+ } else {
3618
+ errorMessage = String(error);
3619
+ originalError = String(error);
3620
+ }
3621
+ for (const { patterns, result } of ErrorPatterns) {
3622
+ for (const pattern of patterns) {
3623
+ if (typeof pattern === "string") {
3624
+ if (errorMessage.includes(pattern)) {
3625
+ return { ...result, originalError };
3626
+ }
3627
+ } else if (pattern instanceof RegExp) {
3628
+ if (pattern.test(errorMessage)) {
3629
+ return { ...result, originalError };
3630
+ }
3631
+ }
3632
+ }
3633
+ }
3634
+ const programError = tryParseProgramError(errorMessage);
3635
+ if (programError) {
3636
+ return { ...programError, originalError };
3637
+ }
3638
+ return {
3639
+ title: "Something Went Wrong",
3640
+ message: "An unexpected error occurred.",
3641
+ category: "unknown",
3642
+ suggestion: "Please try again. If the problem persists, refresh the page.",
3643
+ recoverable: true,
3644
+ originalError
3645
+ };
3646
+ }
3647
+ function tryParseProgramError(message) {
3648
+ const match = message.match(/\{"InstructionError":\[(\d+),\{"Custom":(\d+)\}\]\}/);
3649
+ if (match) {
3650
+ const errorCode = parseInt(match[2]);
3651
+ const friendlyMessage = ShieldPoolErrors[errorCode];
3652
+ if (friendlyMessage) {
3653
+ return {
3654
+ title: "Transaction Failed",
3655
+ message: friendlyMessage,
3656
+ category: "transaction",
3657
+ recoverable: !friendlyMessage.toLowerCase().includes("double spend")
3658
+ };
3659
+ }
3660
+ return {
3661
+ title: "Transaction Failed",
3662
+ message: `Transaction failed with error code ${errorCode}.`,
3663
+ category: "transaction",
3664
+ suggestion: "Please try again or contact support if this persists.",
3665
+ recoverable: true
3666
+ };
3667
+ }
3668
+ return null;
3669
+ }
2962
3670
  function parseTransactionError(error) {
2963
3671
  if (!error) return "An unknown error occurred";
2964
3672
  const errorStr = typeof error === "string" ? error : error.message || error.toString();
@@ -3140,6 +3848,173 @@ function keypairToAdapter(keypair) {
3140
3848
  };
3141
3849
  }
3142
3850
 
3851
+ // src/utils/pending-operations.ts
3852
+ var PENDING_DEPOSITS_KEY = "cloak_pending_deposits";
3853
+ var PENDING_WITHDRAWALS_KEY = "cloak_pending_withdrawals";
3854
+ function getStorage() {
3855
+ if (typeof globalThis !== "undefined" && globalThis.localStorage) {
3856
+ return globalThis.localStorage;
3857
+ }
3858
+ return null;
3859
+ }
3860
+ function savePendingDeposit(deposit) {
3861
+ const storage = getStorage();
3862
+ if (!storage) {
3863
+ console.warn("localStorage not available - pending deposit not persisted");
3864
+ return;
3865
+ }
3866
+ const deposits = loadPendingDeposits();
3867
+ const index = deposits.findIndex((d) => d.note.commitment === deposit.note.commitment);
3868
+ if (index >= 0) {
3869
+ deposits[index] = deposit;
3870
+ } else {
3871
+ deposits.push(deposit);
3872
+ }
3873
+ storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
3874
+ }
3875
+ function loadPendingDeposits() {
3876
+ const storage = getStorage();
3877
+ if (!storage) return [];
3878
+ const stored = storage.getItem(PENDING_DEPOSITS_KEY);
3879
+ if (!stored) return [];
3880
+ try {
3881
+ return JSON.parse(stored);
3882
+ } catch {
3883
+ return [];
3884
+ }
3885
+ }
3886
+ function updatePendingDeposit(commitment, updates) {
3887
+ const storage = getStorage();
3888
+ if (!storage) return;
3889
+ const deposits = loadPendingDeposits();
3890
+ const index = deposits.findIndex((d) => d.note.commitment === commitment);
3891
+ if (index >= 0) {
3892
+ deposits[index] = { ...deposits[index], ...updates };
3893
+ storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
3894
+ }
3895
+ }
3896
+ function removePendingDeposit(commitment) {
3897
+ const storage = getStorage();
3898
+ if (!storage) return;
3899
+ const deposits = loadPendingDeposits();
3900
+ const filtered = deposits.filter((d) => d.note.commitment !== commitment);
3901
+ storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(filtered));
3902
+ }
3903
+ function clearPendingDeposits() {
3904
+ const storage = getStorage();
3905
+ if (storage) {
3906
+ storage.removeItem(PENDING_DEPOSITS_KEY);
3907
+ }
3908
+ }
3909
+ function savePendingWithdrawal(withdrawal) {
3910
+ const storage = getStorage();
3911
+ if (!storage) {
3912
+ console.warn("localStorage not available - pending withdrawal not persisted");
3913
+ return;
3914
+ }
3915
+ const withdrawals = loadPendingWithdrawals();
3916
+ const index = withdrawals.findIndex((w) => w.requestId === withdrawal.requestId);
3917
+ if (index >= 0) {
3918
+ withdrawals[index] = withdrawal;
3919
+ } else {
3920
+ withdrawals.push(withdrawal);
3921
+ }
3922
+ storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
3923
+ }
3924
+ function loadPendingWithdrawals() {
3925
+ const storage = getStorage();
3926
+ if (!storage) return [];
3927
+ const stored = storage.getItem(PENDING_WITHDRAWALS_KEY);
3928
+ if (!stored) return [];
3929
+ try {
3930
+ return JSON.parse(stored);
3931
+ } catch {
3932
+ return [];
3933
+ }
3934
+ }
3935
+ function updatePendingWithdrawal(requestId, updates) {
3936
+ const storage = getStorage();
3937
+ if (!storage) return;
3938
+ const withdrawals = loadPendingWithdrawals();
3939
+ const index = withdrawals.findIndex((w) => w.requestId === requestId);
3940
+ if (index >= 0) {
3941
+ withdrawals[index] = { ...withdrawals[index], ...updates };
3942
+ storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
3943
+ }
3944
+ }
3945
+ function removePendingWithdrawal(requestId) {
3946
+ const storage = getStorage();
3947
+ if (!storage) return;
3948
+ const withdrawals = loadPendingWithdrawals();
3949
+ const filtered = withdrawals.filter((w) => w.requestId !== requestId);
3950
+ storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(filtered));
3951
+ }
3952
+ function clearPendingWithdrawals() {
3953
+ const storage = getStorage();
3954
+ if (storage) {
3955
+ storage.removeItem(PENDING_WITHDRAWALS_KEY);
3956
+ }
3957
+ }
3958
+ function hasPendingOperations() {
3959
+ const deposits = loadPendingDeposits();
3960
+ const withdrawals = loadPendingWithdrawals();
3961
+ const pendingDeposits = deposits.filter(
3962
+ (d) => d.status === "pending" || d.status === "tx_sent"
3963
+ );
3964
+ const pendingWithdrawals = withdrawals.filter(
3965
+ (w) => w.status === "pending" || w.status === "processing"
3966
+ );
3967
+ return pendingDeposits.length > 0 || pendingWithdrawals.length > 0;
3968
+ }
3969
+ function getPendingOperationsSummary() {
3970
+ const deposits = loadPendingDeposits().filter(
3971
+ (d) => d.status === "pending" || d.status === "tx_sent"
3972
+ );
3973
+ const withdrawals = loadPendingWithdrawals().filter(
3974
+ (w) => w.status === "pending" || w.status === "processing"
3975
+ );
3976
+ return {
3977
+ deposits,
3978
+ withdrawals,
3979
+ totalPending: deposits.length + withdrawals.length
3980
+ };
3981
+ }
3982
+ function cleanupStalePendingOperations(maxAgeMs = 24 * 60 * 60 * 1e3) {
3983
+ const now = Date.now();
3984
+ let removedDeposits = 0;
3985
+ let removedWithdrawals = 0;
3986
+ const deposits = loadPendingDeposits();
3987
+ const activeDeposits = deposits.filter((d) => {
3988
+ const age = now - d.startedAt;
3989
+ const isStale = age > maxAgeMs;
3990
+ const isTerminal = d.status === "confirmed" || d.status === "failed";
3991
+ if (isStale || isTerminal) {
3992
+ removedDeposits++;
3993
+ return false;
3994
+ }
3995
+ return true;
3996
+ });
3997
+ const storage = getStorage();
3998
+ if (storage) {
3999
+ storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(activeDeposits));
4000
+ }
4001
+ const withdrawals = loadPendingWithdrawals();
4002
+ const activeWithdrawals = withdrawals.filter((w) => {
4003
+ const age = now - w.startedAt;
4004
+ const isStale = age > maxAgeMs;
4005
+ const isTerminal = w.status === "completed" || w.status === "failed";
4006
+ if (isStale || isTerminal) {
4007
+ removedWithdrawals++;
4008
+ return false;
4009
+ }
4010
+ return true;
4011
+ });
4012
+ if (storage) {
4013
+ storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(activeWithdrawals));
4014
+ }
4015
+ return { removedDeposits, removedWithdrawals };
4016
+ }
4017
+
3143
4018
  // src/index.ts
3144
4019
  var VERSION = "1.0.0";
3145
4020
  export {
@@ -3147,12 +4022,14 @@ export {
3147
4022
  CloakError,
3148
4023
  CloakSDK,
3149
4024
  DepositRecoveryService,
4025
+ EXPECTED_CIRCUIT_HASHES,
3150
4026
  FIXED_FEE_LAMPORTS,
3151
4027
  IndexerService,
3152
4028
  LAMPORTS_PER_SOL,
3153
4029
  LocalStorageAdapter,
3154
4030
  MemoryStorageAdapter,
3155
4031
  RelayService,
4032
+ ShieldPoolErrors,
3156
4033
  VARIABLE_FEE_RATE,
3157
4034
  VERSION,
3158
4035
  bigintToBytes32,
@@ -3160,6 +4037,9 @@ export {
3160
4037
  bytesToHex,
3161
4038
  calculateFee2 as calculateFee,
3162
4039
  calculateRelayFee,
4040
+ cleanupStalePendingOperations,
4041
+ clearPendingDeposits,
4042
+ clearPendingWithdrawals,
3163
4043
  computeCommitment,
3164
4044
  computeMerkleRoot,
3165
4045
  computeNullifier,
@@ -3199,12 +4079,14 @@ export {
3199
4079
  getAddressExplorerUrl,
3200
4080
  getDistributableAmount2 as getDistributableAmount,
3201
4081
  getExplorerUrl,
4082
+ getPendingOperationsSummary,
3202
4083
  getPublicKey,
3203
4084
  getPublicViewKey,
3204
4085
  getRecipientAmount,
3205
4086
  getRpcUrlForNetwork,
3206
4087
  getShieldPoolPDAs,
3207
4088
  getViewKey,
4089
+ hasPendingOperations,
3208
4090
  hexToBigint,
3209
4091
  hexToBytes,
3210
4092
  importKeys,
@@ -3214,7 +4096,10 @@ export {
3214
4096
  isValidSolanaAddress,
3215
4097
  isWithdrawable,
3216
4098
  keypairToAdapter,
4099
+ loadPendingDeposits,
4100
+ loadPendingWithdrawals,
3217
4101
  parseAmount,
4102
+ parseError,
3218
4103
  parseNote,
3219
4104
  parseTransactionError,
3220
4105
  poseidonHash,
@@ -3223,6 +4108,10 @@ export {
3223
4108
  proofToBytes,
3224
4109
  pubkeyToLimbs,
3225
4110
  randomBytes,
4111
+ removePendingDeposit,
4112
+ removePendingWithdrawal,
4113
+ savePendingDeposit,
4114
+ savePendingWithdrawal,
3226
4115
  scanNotesForWallet,
3227
4116
  sendTransaction,
3228
4117
  serializeNote,
@@ -3230,10 +4119,14 @@ export {
3230
4119
  splitTo2Limbs,
3231
4120
  tryDecryptNote,
3232
4121
  updateNoteWithDeposit,
4122
+ updatePendingDeposit,
4123
+ updatePendingWithdrawal,
3233
4124
  validateDepositParams,
3234
4125
  validateNote,
3235
4126
  validateOutputsSum,
3236
4127
  validateTransfers,
3237
4128
  validateWalletConnected,
3238
- validateWithdrawableNote
4129
+ validateWithdrawableNote,
4130
+ verifyAllCircuits,
4131
+ verifyCircuitIntegrity
3239
4132
  };