@cloak.ag/sdk 1.0.4 → 1.0.6

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
@@ -9,7 +9,9 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
9
  import {
10
10
  Keypair,
11
11
  PublicKey as PublicKey4,
12
- Transaction
12
+ Transaction,
13
+ TransactionInstruction as TransactionInstruction2,
14
+ ComputeBudgetProgram
13
15
  } from "@solana/web3.js";
14
16
 
15
17
  // src/core/types.ts
@@ -1043,6 +1045,8 @@ var RelayService = class {
1043
1045
  *
1044
1046
  * @param params - Withdrawal parameters
1045
1047
  * @param onStatusUpdate - Optional callback for status updates
1048
+ * @param onRequestId - CRITICAL: Callback when request_id is received.
1049
+ * Persist this ID to recover if browser crashes during polling.
1046
1050
  * @returns Transaction signature when completed
1047
1051
  *
1048
1052
  * @example
@@ -1052,11 +1056,12 @@ var RelayService = class {
1052
1056
  * publicInputs: { root, nf, outputs_hash, amount },
1053
1057
  * outputs: [{ recipient: addr, amount: lamports }],
1054
1058
  * feeBps: 50
1055
- * }, (status) => console.log(`Status: ${status}`));
1059
+ * }, (status) => console.log(`Status: ${status}`),
1060
+ * (requestId) => localStorage.setItem('pending_withdraw', requestId));
1056
1061
  * console.log(`Transaction: ${signature}`);
1057
1062
  * ```
1058
1063
  */
1059
- async submitWithdraw(params, onStatusUpdate) {
1064
+ async submitWithdraw(params, onStatusUpdate, onRequestId) {
1060
1065
  const proofBytes = hexToBytes(params.proof);
1061
1066
  const proofBase64 = this.bytesToBase64(proofBytes);
1062
1067
  const requestBody = {
@@ -1097,8 +1102,55 @@ var RelayService = class {
1097
1102
  if (!requestId) {
1098
1103
  throw new Error("Relay response missing request_id");
1099
1104
  }
1105
+ if (onRequestId) {
1106
+ onRequestId(requestId);
1107
+ }
1100
1108
  return this.pollForCompletion(requestId, onStatusUpdate);
1101
1109
  }
1110
+ /**
1111
+ * Resume polling for a withdrawal that was previously started
1112
+ *
1113
+ * Use this after page reload to check status of a pending withdrawal.
1114
+ * The requestId should have been persisted via the onRequestId callback.
1115
+ *
1116
+ * @param requestId - Request ID from a previous submitWithdraw call
1117
+ * @param onStatusUpdate - Optional callback for status updates
1118
+ * @returns Transaction signature if completed, null if still pending/failed
1119
+ *
1120
+ * @example
1121
+ * ```typescript
1122
+ * // On page load, check for pending withdrawal
1123
+ * const pendingId = localStorage.getItem('pending_withdraw');
1124
+ * if (pendingId) {
1125
+ * const result = await relay.resumeWithdraw(pendingId);
1126
+ * if (result.status === 'completed') {
1127
+ * console.log('Withdrawal completed:', result.signature);
1128
+ * localStorage.removeItem('pending_withdraw');
1129
+ * }
1130
+ * }
1131
+ * ```
1132
+ */
1133
+ async resumeWithdraw(requestId, onStatusUpdate) {
1134
+ try {
1135
+ const status = await this.getStatus(requestId);
1136
+ if (onStatusUpdate) {
1137
+ onStatusUpdate(status.status);
1138
+ }
1139
+ if (status.status === "completed") {
1140
+ return { status: "completed", signature: status.txId };
1141
+ } else if (status.status === "failed") {
1142
+ return { status: "failed", error: status.error };
1143
+ } else {
1144
+ const signature = await this.pollForCompletion(requestId, onStatusUpdate);
1145
+ return { status: "completed", signature };
1146
+ }
1147
+ } catch (error) {
1148
+ return {
1149
+ status: "failed",
1150
+ error: error instanceof Error ? error.message : String(error)
1151
+ };
1152
+ }
1153
+ }
1102
1154
  /**
1103
1155
  * Poll for withdrawal completion
1104
1156
  *
@@ -1171,7 +1223,7 @@ var RelayService = class {
1171
1223
  * console.log(`Transaction: ${signature}`);
1172
1224
  * ```
1173
1225
  */
1174
- async submitSwap(params, onStatusUpdate) {
1226
+ async submitSwap(params, onStatusUpdate, onRequestId) {
1175
1227
  const proofBytes = hexToBytes(params.proof);
1176
1228
  const proofBase64 = this.bytesToBase64(proofBytes);
1177
1229
  const requestBody = {
@@ -1213,6 +1265,9 @@ var RelayService = class {
1213
1265
  if (!requestId) {
1214
1266
  throw new Error("Relay response missing request_id");
1215
1267
  }
1268
+ if (onRequestId) {
1269
+ onRequestId(requestId);
1270
+ }
1216
1271
  return this.pollForCompletion(requestId, onStatusUpdate);
1217
1272
  }
1218
1273
  /**
@@ -1449,29 +1504,94 @@ var DepositRecoveryService = class {
1449
1504
  }
1450
1505
  /**
1451
1506
  * Check if a deposit already exists in the indexer
1507
+ * Uses the enhanced deposit lookup endpoint with include_proof=true
1452
1508
  *
1453
1509
  * @private
1454
1510
  */
1455
- async checkExistingDeposit(_commitment) {
1511
+ async checkExistingDeposit(commitment) {
1456
1512
  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
- }
1513
+ const cleanCommit = commitment.replace(/^0x/, "").toLowerCase();
1514
+ const response = await fetch(
1515
+ `${this.apiUrl}/api/v1/deposit/${cleanCommit}?include_proof=true`
1516
+ );
1517
+ if (!response.ok) {
1518
+ if (response.status === 404) {
1519
+ return null;
1468
1520
  }
1521
+ throw new Error(`Failed to check deposit: ${response.status}`);
1469
1522
  }
1470
- return null;
1523
+ const data = await response.json();
1524
+ if (!data.merkle_proof) {
1525
+ const merkleProof = await this.indexer.getMerkleProof(data.leaf_index);
1526
+ return {
1527
+ leafIndex: data.leaf_index,
1528
+ root: merkleProof.root || "",
1529
+ slot: data.slot,
1530
+ merkleProof: {
1531
+ pathElements: merkleProof.pathElements,
1532
+ pathIndices: merkleProof.pathIndices
1533
+ }
1534
+ };
1535
+ }
1536
+ return {
1537
+ leafIndex: data.leaf_index,
1538
+ root: data.merkle_proof.root,
1539
+ slot: data.slot,
1540
+ merkleProof: {
1541
+ pathElements: data.merkle_proof.path_elements,
1542
+ pathIndices: data.merkle_proof.path_indices
1543
+ }
1544
+ };
1471
1545
  } catch (error) {
1472
1546
  return null;
1473
1547
  }
1474
1548
  }
1549
+ /**
1550
+ * Recover deposit by transaction signature
1551
+ * Uses the indexer's signature lookup endpoint
1552
+ */
1553
+ async recoverBySignature(signature) {
1554
+ try {
1555
+ const response = await fetch(
1556
+ `${this.apiUrl}/api/v1/deposit/tx/${signature}?include_proof=true`
1557
+ );
1558
+ if (!response.ok) {
1559
+ if (response.status === 404) {
1560
+ return {
1561
+ success: false,
1562
+ error: "Deposit not found for this transaction signature"
1563
+ };
1564
+ }
1565
+ const errorText = await response.text();
1566
+ return {
1567
+ success: false,
1568
+ error: `Failed to recover deposit: ${errorText}`
1569
+ };
1570
+ }
1571
+ const data = await response.json();
1572
+ if (!data.merkle_proof) {
1573
+ return {
1574
+ success: false,
1575
+ error: "Deposit found but merkle proof not available"
1576
+ };
1577
+ }
1578
+ return {
1579
+ success: true,
1580
+ leafIndex: data.leaf_index,
1581
+ root: data.merkle_proof.root,
1582
+ slot: data.slot,
1583
+ merkleProof: {
1584
+ pathElements: data.merkle_proof.path_elements,
1585
+ pathIndices: data.merkle_proof.path_indices
1586
+ }
1587
+ };
1588
+ } catch (error) {
1589
+ return {
1590
+ success: false,
1591
+ error: error instanceof Error ? error.message : String(error)
1592
+ };
1593
+ }
1594
+ }
1475
1595
  /**
1476
1596
  * Finalize a deposit via server API (alternative recovery method)
1477
1597
  *
@@ -1631,39 +1751,30 @@ function getShieldPoolPDAs(programId, mint) {
1631
1751
 
1632
1752
  // src/utils/proof-generation.ts
1633
1753
  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 };
1754
+ var IS_BROWSER = typeof window !== "undefined" || typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
1755
+ var _nodePath = null;
1756
+ var _nodeFs = null;
1757
+ var _nodeCrypto = null;
1758
+ async function getNodePath() {
1759
+ if (_nodePath) return _nodePath;
1760
+ _nodePath = await import("path");
1761
+ return _nodePath;
1762
+ }
1763
+ async function getNodeFs() {
1764
+ if (_nodeFs) return _nodeFs;
1765
+ _nodeFs = await import("fs");
1766
+ return _nodeFs;
1767
+ }
1768
+ async function getNodeCrypto() {
1769
+ if (_nodeCrypto) return _nodeCrypto;
1770
+ _nodeCrypto = await import("crypto");
1771
+ return _nodeCrypto;
1648
1772
  }
1649
1773
  function joinPath(...parts) {
1650
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1651
- if (!isBrowser && path) {
1652
- return path.join(...parts);
1653
- }
1654
1774
  return parts.join("/").replace(/\/+/g, "/");
1655
1775
  }
1656
1776
  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) {
1777
+ if (IS_BROWSER) {
1667
1778
  try {
1668
1779
  const response = await fetch(filePath, { method: "HEAD" });
1669
1780
  return response.ok;
@@ -1671,13 +1782,21 @@ async function fileExists(filePath) {
1671
1782
  return false;
1672
1783
  }
1673
1784
  }
1674
- return false;
1785
+ try {
1786
+ const nodeFs = globalThis.require?.("fs") || (typeof __require !== "undefined" ? __require("fs") : null);
1787
+ if (nodeFs) {
1788
+ return nodeFs.existsSync(filePath);
1789
+ }
1790
+ const fsModule = await import("fs");
1791
+ return fsModule.existsSync(filePath);
1792
+ } catch {
1793
+ return false;
1794
+ }
1675
1795
  }
1676
1796
  async function generateWithdrawRegularProof(inputs, circuitsPath) {
1677
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1678
1797
  let wasmPath;
1679
1798
  let zkeyPath;
1680
- if (isBrowser) {
1799
+ if (IS_BROWSER) {
1681
1800
  wasmPath = `${circuitsPath}/withdraw_regular_js/withdraw_regular.wasm`;
1682
1801
  zkeyPath = `${circuitsPath}/withdraw_regular_final.zkey`;
1683
1802
  } else {
@@ -1725,10 +1844,9 @@ async function generateWithdrawRegularProof(inputs, circuitsPath) {
1725
1844
  };
1726
1845
  }
1727
1846
  async function generateWithdrawSwapProof(inputs, circuitsPath) {
1728
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1729
1847
  let wasmPath;
1730
1848
  let zkeyPath;
1731
- if (isBrowser) {
1849
+ if (IS_BROWSER) {
1732
1850
  wasmPath = `${circuitsPath}/withdraw_swap_js/withdraw_swap.wasm`;
1733
1851
  zkeyPath = `${circuitsPath}/withdraw_swap_final.zkey`;
1734
1852
  } else {
@@ -1783,8 +1901,7 @@ async function generateWithdrawSwapProof(inputs, circuitsPath) {
1783
1901
  };
1784
1902
  }
1785
1903
  async function areCircuitsAvailable(circuitsPath) {
1786
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1787
- if (isBrowser) {
1904
+ if (IS_BROWSER) {
1788
1905
  return Boolean(circuitsPath && circuitsPath !== "");
1789
1906
  }
1790
1907
  const wasmPath = joinPath(circuitsPath, "build", "withdraw_regular_js", "withdraw_regular.wasm");
@@ -1794,20 +1911,24 @@ async function areCircuitsAvailable(circuitsPath) {
1794
1911
  return wasmExists && zkeyExists;
1795
1912
  }
1796
1913
  async function getDefaultCircuitsPath() {
1797
- const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
1798
- if (isBrowser) {
1914
+ if (IS_BROWSER) {
1915
+ return "/circuits";
1916
+ }
1917
+ if (typeof process === "undefined" || !process.cwd) {
1799
1918
  return "/circuits";
1800
1919
  }
1801
- const { path: path2 } = await loadNodeModules();
1802
- if (!path2 || typeof process === "undefined" || !process.cwd) {
1920
+ let nodePath;
1921
+ try {
1922
+ nodePath = await getNodePath();
1923
+ } catch {
1803
1924
  return "/circuits";
1804
1925
  }
1805
1926
  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")
1927
+ nodePath.resolve(process.cwd(), "../../packages-new/circuits"),
1928
+ nodePath.resolve(process.cwd(), "../packages-new/circuits"),
1929
+ nodePath.resolve(process.cwd(), "packages-new/circuits"),
1930
+ nodePath.resolve(process.cwd(), "../../circuits"),
1931
+ nodePath.resolve(process.cwd(), "../circuits")
1811
1932
  ];
1812
1933
  for (const p of possiblePaths) {
1813
1934
  if (await areCircuitsAvailable(p)) {
@@ -1816,8 +1937,103 @@ async function getDefaultCircuitsPath() {
1816
1937
  }
1817
1938
  return possiblePaths[0];
1818
1939
  }
1940
+ var EXPECTED_CIRCUIT_HASHES = {
1941
+ // SHA-256 of the verification key JSON from withdraw_regular circuit
1942
+ withdraw_regular_vkey: null,
1943
+ // Set to null to skip check during development
1944
+ // SHA-256 of the verification key JSON from withdraw_swap circuit
1945
+ withdraw_swap_vkey: null
1946
+ // Set to null to skip check during development
1947
+ };
1948
+ async function verifyCircuitIntegrity(circuitsPath, circuit) {
1949
+ const expectedHash = circuit === "withdraw_regular" ? EXPECTED_CIRCUIT_HASHES.withdraw_regular_vkey : EXPECTED_CIRCUIT_HASHES.withdraw_swap_vkey;
1950
+ if (!expectedHash) {
1951
+ return {
1952
+ valid: true,
1953
+ circuit,
1954
+ error: "Verification skipped (no expected hash configured)"
1955
+ };
1956
+ }
1957
+ try {
1958
+ const vkeyPath = IS_BROWSER ? `${circuitsPath}/${circuit}_verification_key.json` : joinPath(circuitsPath, "build", `${circuit}_verification_key.json`);
1959
+ let vkeyData;
1960
+ if (IS_BROWSER) {
1961
+ const response = await fetch(vkeyPath);
1962
+ if (!response.ok) {
1963
+ return {
1964
+ valid: false,
1965
+ circuit,
1966
+ error: `Failed to fetch verification key: ${response.status}`
1967
+ };
1968
+ }
1969
+ vkeyData = await response.text();
1970
+ } else {
1971
+ try {
1972
+ const nodeFs = await getNodeFs();
1973
+ vkeyData = nodeFs.readFileSync(vkeyPath, "utf8");
1974
+ } catch (e) {
1975
+ return {
1976
+ valid: false,
1977
+ circuit,
1978
+ error: `Failed to read verification key: ${e}`
1979
+ };
1980
+ }
1981
+ }
1982
+ let computedHash;
1983
+ if (IS_BROWSER) {
1984
+ const encoder = new TextEncoder();
1985
+ const data = encoder.encode(vkeyData);
1986
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
1987
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
1988
+ computedHash = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
1989
+ } else {
1990
+ const nodeCrypto = await getNodeCrypto();
1991
+ computedHash = nodeCrypto.createHash("sha256").update(vkeyData).digest("hex");
1992
+ }
1993
+ if (computedHash === expectedHash) {
1994
+ return {
1995
+ valid: true,
1996
+ circuit,
1997
+ computedHash,
1998
+ expectedHash
1999
+ };
2000
+ } else {
2001
+ return {
2002
+ valid: false,
2003
+ circuit,
2004
+ error: `Verification key hash mismatch! This circuit may produce invalid proofs.`,
2005
+ computedHash,
2006
+ expectedHash
2007
+ };
2008
+ }
2009
+ } catch (error) {
2010
+ return {
2011
+ valid: false,
2012
+ circuit,
2013
+ error: `Circuit verification failed: ${error instanceof Error ? error.message : String(error)}`
2014
+ };
2015
+ }
2016
+ }
2017
+ async function verifyAllCircuits(circuitsPath) {
2018
+ const results = await Promise.all([
2019
+ verifyCircuitIntegrity(circuitsPath, "withdraw_regular"),
2020
+ verifyCircuitIntegrity(circuitsPath, "withdraw_swap")
2021
+ ]);
2022
+ return results;
2023
+ }
1819
2024
 
1820
2025
  // src/core/CloakSDK.ts
2026
+ var COMPUTE_BUDGET_PROGRAM_ID = new PublicKey4("ComputeBudget111111111111111111111111111111");
2027
+ function createSetLoadedAccountsDataSizeLimitInstruction(bytes) {
2028
+ const data = Buffer.alloc(5);
2029
+ data.writeUInt8(4, 0);
2030
+ data.writeUInt32LE(bytes, 1);
2031
+ return new TransactionInstruction2({
2032
+ keys: [],
2033
+ programId: COMPUTE_BUDGET_PROGRAM_ID,
2034
+ data
2035
+ });
2036
+ }
1821
2037
  var CLOAK_PROGRAM_ID = new PublicKey4("c1oak6tetxYnNfvXKFkpn1d98FxtK7B68vBQLYQpWKp");
1822
2038
  var CLOAK_API_URL = "https://api.cloak.ag";
1823
2039
  var CloakSDK = class {
@@ -1930,14 +2146,35 @@ var CloakSDK = class {
1930
2146
  async deposit(connection, amountOrNote, options) {
1931
2147
  try {
1932
2148
  let note;
2149
+ let isNewNote = false;
1933
2150
  if (typeof amountOrNote === "number") {
2151
+ options?.onProgress?.("generating_note", { message: "Generating note with secrets..." });
1934
2152
  note = await generateNote(amountOrNote, this.config.network);
2153
+ isNewNote = true;
1935
2154
  } else {
1936
2155
  note = amountOrNote;
1937
2156
  if (note.depositSignature) {
1938
2157
  throw new Error("Note has already been deposited");
1939
2158
  }
1940
2159
  }
2160
+ if (isNewNote) {
2161
+ await this.storage.saveNote(note);
2162
+ if (options?.onNoteGenerated) {
2163
+ options?.onProgress?.("awaiting_note_acknowledgment", {
2164
+ message: "Waiting for note to be saved before proceeding with deposit..."
2165
+ });
2166
+ try {
2167
+ await options.onNoteGenerated(note);
2168
+ } catch (error) {
2169
+ throw new Error(
2170
+ `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.`
2171
+ );
2172
+ }
2173
+ }
2174
+ options?.onProgress?.("note_saved", {
2175
+ message: "Note saved. Proceeding with on-chain deposit..."
2176
+ });
2177
+ }
1941
2178
  const payerPubkey = this.getPublicKey();
1942
2179
  const balance = await connection.getBalance(payerPubkey);
1943
2180
  const requiredAmount = note.amount + 5e3;
@@ -1957,10 +2194,51 @@ var CloakSDK = class {
1957
2194
  commitment: commitmentBytes
1958
2195
  });
1959
2196
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
2197
+ const priorityFee = options?.priorityFee ?? 1e4;
2198
+ const cuPriceIx = ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee });
2199
+ const dataSizeLimit = options?.loadedAccountsDataSizeLimit ?? 256 * 1024;
2200
+ const dataSizeLimitIx = dataSizeLimit > 0 ? createSetLoadedAccountsDataSizeLimitInstruction(dataSizeLimit) : null;
2201
+ let computeUnits;
2202
+ if (options?.optimizeCU && this.keypair) {
2203
+ options?.onProgress?.("simulating", { message: "Simulating transaction for optimal CU..." });
2204
+ const simCuLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 2e5 });
2205
+ const simTransaction = new Transaction({
2206
+ feePayer: payerPubkey,
2207
+ recentBlockhash: blockhash
2208
+ }).add(simCuLimitIx).add(cuPriceIx);
2209
+ if (dataSizeLimitIx) {
2210
+ simTransaction.add(dataSizeLimitIx);
2211
+ }
2212
+ simTransaction.add(depositIx);
2213
+ try {
2214
+ const simulation = await connection.simulateTransaction(simTransaction, [this.keypair]);
2215
+ if (simulation.value.err) {
2216
+ console.warn("Simulation failed, using default CU:", simulation.value.err);
2217
+ computeUnits = 4e4;
2218
+ } else {
2219
+ const simulatedCU = simulation.value.unitsConsumed ?? 3e4;
2220
+ computeUnits = Math.ceil(simulatedCU * 1.2);
2221
+ console.log(`CU optimization: ${simulatedCU} simulated \u2192 ${computeUnits} limit`);
2222
+ }
2223
+ } catch (simError) {
2224
+ console.warn("Simulation RPC error, using default CU:", simError);
2225
+ computeUnits = 4e4;
2226
+ }
2227
+ } else if (options?.optimizeCU && this.wallet) {
2228
+ console.warn("CU optimization via simulation not available in wallet mode (would require double signing). Using default.");
2229
+ computeUnits = options?.computeUnits ?? 4e4;
2230
+ } else {
2231
+ computeUnits = options?.computeUnits ?? 4e4;
2232
+ }
2233
+ const cuLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits });
1960
2234
  const transaction = new Transaction({
1961
2235
  feePayer: payerPubkey,
1962
2236
  recentBlockhash: blockhash
1963
- }).add(depositIx);
2237
+ }).add(cuLimitIx).add(cuPriceIx);
2238
+ if (dataSizeLimitIx) {
2239
+ transaction.add(dataSizeLimitIx);
2240
+ }
2241
+ transaction.add(depositIx);
1964
2242
  if (!transaction.feePayer) {
1965
2243
  throw new Error("Transaction feePayer is not set");
1966
2244
  }
@@ -2129,6 +2407,16 @@ var CloakSDK = class {
2129
2407
  pathIndices: merkleProof.pathIndices
2130
2408
  }
2131
2409
  });
2410
+ await this.storage.updateNote(note.commitment, {
2411
+ depositSignature: signature,
2412
+ depositSlot,
2413
+ leafIndex,
2414
+ root,
2415
+ merkleProof: {
2416
+ pathElements: merkleProof.pathElements,
2417
+ pathIndices: merkleProof.pathIndices
2418
+ }
2419
+ });
2132
2420
  return {
2133
2421
  note: updatedNote,
2134
2422
  signature,
@@ -2931,6 +3219,63 @@ async function copyNoteToClipboard(note) {
2931
3219
  }
2932
3220
 
2933
3221
  // src/utils/errors.ts
3222
+ var ShieldPoolErrors = {
3223
+ // Root management errors
3224
+ 4096: "Invalid Merkle root",
3225
+ 4097: "Root not found in the roots ring",
3226
+ 4098: "Roots ring is full",
3227
+ // Proof verification errors
3228
+ 4112: "Zero-knowledge proof is invalid",
3229
+ 4113: "Invalid proof size (expected 260 bytes)",
3230
+ 4114: "Invalid public inputs",
3231
+ 4115: "Verification key mismatch",
3232
+ // Nullifier errors
3233
+ 4128: "Double spend detected - this note has already been spent",
3234
+ 4129: "Nullifier shard is full",
3235
+ 4130: "Invalid nullifier",
3236
+ // Transaction validation errors
3237
+ 4144: "Output addresses or amounts don't match the proof",
3238
+ 4145: "Amount conservation failed - outputs + fee must equal input amount",
3239
+ 4146: "Invalid outputs hash",
3240
+ 4147: "Invalid amount (must be greater than zero)",
3241
+ 4148: "Invalid recipient address",
3242
+ 4149: "Commitment already exists in the tree",
3243
+ 4150: "Commitment log is full",
3244
+ // Math errors
3245
+ 4160: "Math overflow occurred",
3246
+ 4161: "Division by zero",
3247
+ // Account errors
3248
+ 4176: "Account validation failed - please check your wallet balance and try again",
3249
+ 4177: "Pool account owner mismatch",
3250
+ 4178: "Treasury account owner mismatch",
3251
+ 4179: "Roots ring account owner mismatch",
3252
+ 4180: "Nullifier shard account owner mismatch",
3253
+ 4181: "Pool account is not writable",
3254
+ 4182: "Treasury account is not writable",
3255
+ 4183: "Recipient account is not writable",
3256
+ 4184: "Insufficient lamports in pool or account",
3257
+ 4185: "Invalid account owner",
3258
+ 4186: "Invalid account size",
3259
+ 4187: "Commitments account is not writable",
3260
+ 4188: "Invalid admin authority",
3261
+ // Instruction errors
3262
+ 4192: "Invalid instruction data length",
3263
+ 4193: "Invalid instruction data format",
3264
+ 4194: "Missing required accounts",
3265
+ 4195: "Invalid instruction tag",
3266
+ // PoW/Scrambler errors
3267
+ 4196: "Invalid miner account",
3268
+ 4197: "Invalid claim account",
3269
+ 4198: "Failed to consume claim",
3270
+ // Groth16 verifier errors
3271
+ 4208: "Invalid G1 point length",
3272
+ 4209: "Invalid G2 point length",
3273
+ 4210: "Invalid public inputs length",
3274
+ 4211: "Public input exceeds field size",
3275
+ 4212: "G1 multiplication failed during proof preparation",
3276
+ 4213: "G1 addition failed during proof preparation",
3277
+ 4214: "Proof verification failed"
3278
+ };
2934
3279
  var PROGRAM_ERRORS = {
2935
3280
  // Nullifier errors
2936
3281
  "NullifierAlreadyUsed": "This note has already been withdrawn. Each note can only be spent once.",
@@ -2959,6 +3304,438 @@ var PROGRAM_ERRORS = {
2959
3304
  "AccountNotFound": "Required account not found.",
2960
3305
  "InvalidInstruction": "Invalid instruction data."
2961
3306
  };
3307
+ var ErrorPatterns = [
3308
+ // Wallet/User action errors
3309
+ {
3310
+ patterns: [
3311
+ "User rejected",
3312
+ "user rejected",
3313
+ "User denied",
3314
+ "user denied",
3315
+ "Transaction cancelled",
3316
+ "Transaction rejected",
3317
+ /code.*4001/i
3318
+ ],
3319
+ result: {
3320
+ title: "Transaction Cancelled",
3321
+ message: "You cancelled the transaction.",
3322
+ category: "wallet",
3323
+ recoverable: true
3324
+ }
3325
+ },
3326
+ {
3327
+ patterns: [
3328
+ "Wallet not connected",
3329
+ "wallet not connected",
3330
+ "Please connect wallet",
3331
+ "No wallet connected"
3332
+ ],
3333
+ result: {
3334
+ title: "Wallet Not Connected",
3335
+ message: "Please connect your wallet to continue.",
3336
+ category: "wallet",
3337
+ suggestion: "Click the wallet button to connect.",
3338
+ recoverable: true
3339
+ }
3340
+ },
3341
+ {
3342
+ patterns: [
3343
+ "SDK not initialized",
3344
+ "sdk not initialized"
3345
+ ],
3346
+ result: {
3347
+ title: "Wallet Connection Required",
3348
+ message: "Your wallet connection was interrupted.",
3349
+ category: "wallet",
3350
+ suggestion: "Please reconnect your wallet and try again.",
3351
+ recoverable: true
3352
+ }
3353
+ },
3354
+ {
3355
+ patterns: [
3356
+ "Wallet does not support signing",
3357
+ "signTransaction"
3358
+ ],
3359
+ result: {
3360
+ title: "Wallet Feature Not Supported",
3361
+ message: "Your wallet doesn't support the required signing method.",
3362
+ category: "wallet",
3363
+ suggestion: "Try using a different wallet like Phantom or Solflare.",
3364
+ recoverable: true
3365
+ }
3366
+ },
3367
+ // Balance errors
3368
+ {
3369
+ patterns: [
3370
+ "insufficient lamports",
3371
+ "Insufficient lamports",
3372
+ "insufficient balance",
3373
+ "Insufficient balance",
3374
+ "Insufficient SOL",
3375
+ "not enough SOL",
3376
+ "Attempt to debit an account but found no record",
3377
+ /0x1$/
3378
+ ],
3379
+ result: {
3380
+ title: "Insufficient Balance",
3381
+ message: "You don't have enough SOL for this transaction.",
3382
+ category: "validation",
3383
+ suggestion: "Add more SOL to your wallet or reduce the amount.",
3384
+ recoverable: true
3385
+ }
3386
+ },
3387
+ {
3388
+ patterns: [
3389
+ "Insufficient funds",
3390
+ "insufficient funds"
3391
+ ],
3392
+ result: {
3393
+ title: "Insufficient Funds",
3394
+ message: "Your wallet doesn't have enough funds for this transaction.",
3395
+ category: "validation",
3396
+ suggestion: "Add more funds to your wallet and try again.",
3397
+ recoverable: true
3398
+ }
3399
+ },
3400
+ // Network errors
3401
+ {
3402
+ patterns: [
3403
+ "fetch failed",
3404
+ "Failed to fetch",
3405
+ "Network error",
3406
+ "network error",
3407
+ "ECONNREFUSED",
3408
+ "ETIMEDOUT",
3409
+ "ENOTFOUND",
3410
+ "NetworkError",
3411
+ "net::ERR",
3412
+ "Failed to load"
3413
+ ],
3414
+ result: {
3415
+ title: "Connection Error",
3416
+ message: "Unable to connect to the network.",
3417
+ category: "network",
3418
+ suggestion: "Check your internet connection and try again.",
3419
+ recoverable: true
3420
+ }
3421
+ },
3422
+ {
3423
+ patterns: [
3424
+ "timeout",
3425
+ "Timeout",
3426
+ "TIMEOUT",
3427
+ "timed out",
3428
+ "Timed out"
3429
+ ],
3430
+ result: {
3431
+ title: "Request Timed Out",
3432
+ message: "The request took too long to complete.",
3433
+ category: "network",
3434
+ suggestion: "The network may be congested. Please try again in a moment.",
3435
+ recoverable: true
3436
+ }
3437
+ },
3438
+ {
3439
+ patterns: [
3440
+ "blockhash not found",
3441
+ "Blockhash not found",
3442
+ "block height exceeded"
3443
+ ],
3444
+ result: {
3445
+ title: "Transaction Expired",
3446
+ message: "The transaction took too long and expired.",
3447
+ category: "network",
3448
+ suggestion: "Please try again. If this persists, the network may be congested.",
3449
+ recoverable: true
3450
+ }
3451
+ },
3452
+ // Service errors (Indexer/Relay)
3453
+ {
3454
+ patterns: [
3455
+ "Indexer",
3456
+ "indexer",
3457
+ /indexer.*unavailable/i,
3458
+ /failed.*indexer/i
3459
+ ],
3460
+ result: {
3461
+ title: "Service Temporarily Unavailable",
3462
+ message: "The privacy service is temporarily unavailable.",
3463
+ category: "service",
3464
+ suggestion: "Please wait a moment and try again.",
3465
+ recoverable: true
3466
+ }
3467
+ },
3468
+ {
3469
+ patterns: [
3470
+ "Relay",
3471
+ "relay",
3472
+ /relay.*unavailable/i,
3473
+ /failed.*relay/i,
3474
+ "failed to submit",
3475
+ "Failed to submit"
3476
+ ],
3477
+ result: {
3478
+ title: "Processing Service Busy",
3479
+ message: "The transaction processing service is busy.",
3480
+ category: "service",
3481
+ suggestion: "Please wait a moment and try again.",
3482
+ recoverable: true
3483
+ }
3484
+ },
3485
+ {
3486
+ patterns: [
3487
+ "429",
3488
+ "Too Many Requests",
3489
+ "rate limit",
3490
+ "Rate limit"
3491
+ ],
3492
+ result: {
3493
+ title: "Too Many Requests",
3494
+ message: "You're making requests too quickly.",
3495
+ category: "service",
3496
+ suggestion: "Please wait a moment before trying again.",
3497
+ recoverable: true
3498
+ }
3499
+ },
3500
+ {
3501
+ patterns: [
3502
+ "503",
3503
+ "Service Unavailable",
3504
+ "502",
3505
+ "Bad Gateway"
3506
+ ],
3507
+ result: {
3508
+ title: "Service Temporarily Unavailable",
3509
+ message: "Our servers are temporarily unavailable.",
3510
+ category: "service",
3511
+ suggestion: "Please try again in a few minutes.",
3512
+ recoverable: true
3513
+ }
3514
+ },
3515
+ // Proof/ZK errors
3516
+ {
3517
+ patterns: [
3518
+ "Circuits not available",
3519
+ "circuits not available",
3520
+ "Failed to load circuit",
3521
+ "WASM",
3522
+ "wasm",
3523
+ /circuit.*not.*found/i
3524
+ ],
3525
+ result: {
3526
+ title: "Loading Error",
3527
+ message: "Failed to load required cryptographic components.",
3528
+ category: "service",
3529
+ suggestion: "Try refreshing the page. If the problem persists, clear your browser cache.",
3530
+ recoverable: true
3531
+ }
3532
+ },
3533
+ {
3534
+ patterns: [
3535
+ "proof generation",
3536
+ "Proof generation",
3537
+ "Failed to generate proof",
3538
+ "proving error"
3539
+ ],
3540
+ result: {
3541
+ title: "Proof Generation Failed",
3542
+ message: "Failed to generate the privacy proof.",
3543
+ category: "service",
3544
+ suggestion: "Try again with a smaller amount or refresh the page.",
3545
+ recoverable: true
3546
+ }
3547
+ },
3548
+ // Validation errors
3549
+ {
3550
+ patterns: [
3551
+ "Invalid amount",
3552
+ "invalid amount",
3553
+ "Amount must be",
3554
+ "amount must be",
3555
+ "Amount too small"
3556
+ ],
3557
+ result: {
3558
+ title: "Invalid Amount",
3559
+ message: "The amount you entered is not valid.",
3560
+ category: "validation",
3561
+ suggestion: "Enter an amount greater than the minimum required.",
3562
+ recoverable: true
3563
+ }
3564
+ },
3565
+ {
3566
+ patterns: [
3567
+ "Invalid recipient",
3568
+ "invalid recipient",
3569
+ "Invalid address",
3570
+ "invalid address",
3571
+ "Invalid Solana address"
3572
+ ],
3573
+ result: {
3574
+ title: "Invalid Address",
3575
+ message: "The recipient address is not valid.",
3576
+ category: "validation",
3577
+ suggestion: "Check the address and make sure it's a valid Solana address.",
3578
+ recoverable: true
3579
+ }
3580
+ },
3581
+ {
3582
+ patterns: [
3583
+ "Invalid token",
3584
+ "invalid token",
3585
+ "Unsupported token"
3586
+ ],
3587
+ result: {
3588
+ title: "Unsupported Token",
3589
+ message: "This token is not supported.",
3590
+ category: "validation",
3591
+ suggestion: "Select a supported token and try again.",
3592
+ recoverable: true
3593
+ }
3594
+ },
3595
+ // Swap errors
3596
+ {
3597
+ patterns: [
3598
+ "Failed to get quote",
3599
+ "failed to get quote",
3600
+ "No route found",
3601
+ "no route found",
3602
+ "Insufficient liquidity",
3603
+ "insufficient liquidity"
3604
+ ],
3605
+ result: {
3606
+ title: "Swap Quote Unavailable",
3607
+ message: "Unable to get a price quote for this swap.",
3608
+ category: "service",
3609
+ suggestion: "Try a different amount or wait for better liquidity.",
3610
+ recoverable: true
3611
+ }
3612
+ },
3613
+ {
3614
+ patterns: [
3615
+ "Slippage",
3616
+ "slippage",
3617
+ "Price impact",
3618
+ "price impact"
3619
+ ],
3620
+ result: {
3621
+ title: "Price Changed",
3622
+ message: "The price changed too much during the transaction.",
3623
+ category: "transaction",
3624
+ suggestion: "Try again or increase your slippage tolerance.",
3625
+ recoverable: true
3626
+ }
3627
+ },
3628
+ // Transaction errors
3629
+ {
3630
+ patterns: [
3631
+ "Double spend",
3632
+ "double spend",
3633
+ "already been spent",
3634
+ "already spent"
3635
+ ],
3636
+ result: {
3637
+ title: "Already Spent",
3638
+ message: "This note has already been used.",
3639
+ category: "transaction",
3640
+ suggestion: "This funds have already been withdrawn.",
3641
+ recoverable: false
3642
+ }
3643
+ },
3644
+ {
3645
+ patterns: [
3646
+ "simulation failed",
3647
+ "Simulation failed",
3648
+ "Transaction simulation"
3649
+ ],
3650
+ result: {
3651
+ title: "Transaction Failed",
3652
+ message: "The transaction could not be completed.",
3653
+ category: "transaction",
3654
+ suggestion: "Please try again. If the problem persists, check your balance.",
3655
+ recoverable: true
3656
+ }
3657
+ },
3658
+ {
3659
+ patterns: [
3660
+ "confirmation timeout",
3661
+ "Confirmation timeout",
3662
+ "not confirmed"
3663
+ ],
3664
+ result: {
3665
+ title: "Confirmation Pending",
3666
+ message: "Transaction confirmation is taking longer than expected.",
3667
+ category: "network",
3668
+ suggestion: "Your transaction may still complete. Check your wallet or try again.",
3669
+ recoverable: true
3670
+ }
3671
+ }
3672
+ ];
3673
+ function parseError(error) {
3674
+ let errorMessage = "";
3675
+ let originalError = "";
3676
+ if (error instanceof Error) {
3677
+ errorMessage = error.message;
3678
+ originalError = error.stack || error.message;
3679
+ } else if (typeof error === "string") {
3680
+ errorMessage = error;
3681
+ originalError = error;
3682
+ } else if (error && typeof error === "object") {
3683
+ const err = error;
3684
+ errorMessage = String(err.message || err.error || err.msg || JSON.stringify(error));
3685
+ originalError = JSON.stringify(error);
3686
+ } else {
3687
+ errorMessage = String(error);
3688
+ originalError = String(error);
3689
+ }
3690
+ for (const { patterns, result } of ErrorPatterns) {
3691
+ for (const pattern of patterns) {
3692
+ if (typeof pattern === "string") {
3693
+ if (errorMessage.includes(pattern)) {
3694
+ return { ...result, originalError };
3695
+ }
3696
+ } else if (pattern instanceof RegExp) {
3697
+ if (pattern.test(errorMessage)) {
3698
+ return { ...result, originalError };
3699
+ }
3700
+ }
3701
+ }
3702
+ }
3703
+ const programError = tryParseProgramError(errorMessage);
3704
+ if (programError) {
3705
+ return { ...programError, originalError };
3706
+ }
3707
+ return {
3708
+ title: "Something Went Wrong",
3709
+ message: "An unexpected error occurred.",
3710
+ category: "unknown",
3711
+ suggestion: "Please try again. If the problem persists, refresh the page.",
3712
+ recoverable: true,
3713
+ originalError
3714
+ };
3715
+ }
3716
+ function tryParseProgramError(message) {
3717
+ const match = message.match(/\{"InstructionError":\[(\d+),\{"Custom":(\d+)\}\]\}/);
3718
+ if (match) {
3719
+ const errorCode = parseInt(match[2]);
3720
+ const friendlyMessage = ShieldPoolErrors[errorCode];
3721
+ if (friendlyMessage) {
3722
+ return {
3723
+ title: "Transaction Failed",
3724
+ message: friendlyMessage,
3725
+ category: "transaction",
3726
+ recoverable: !friendlyMessage.toLowerCase().includes("double spend")
3727
+ };
3728
+ }
3729
+ return {
3730
+ title: "Transaction Failed",
3731
+ message: `Transaction failed with error code ${errorCode}.`,
3732
+ category: "transaction",
3733
+ suggestion: "Please try again or contact support if this persists.",
3734
+ recoverable: true
3735
+ };
3736
+ }
3737
+ return null;
3738
+ }
2962
3739
  function parseTransactionError(error) {
2963
3740
  if (!error) return "An unknown error occurred";
2964
3741
  const errorStr = typeof error === "string" ? error : error.message || error.toString();
@@ -3140,6 +3917,173 @@ function keypairToAdapter(keypair) {
3140
3917
  };
3141
3918
  }
3142
3919
 
3920
+ // src/utils/pending-operations.ts
3921
+ var PENDING_DEPOSITS_KEY = "cloak_pending_deposits";
3922
+ var PENDING_WITHDRAWALS_KEY = "cloak_pending_withdrawals";
3923
+ function getStorage() {
3924
+ if (typeof globalThis !== "undefined" && globalThis.localStorage) {
3925
+ return globalThis.localStorage;
3926
+ }
3927
+ return null;
3928
+ }
3929
+ function savePendingDeposit(deposit) {
3930
+ const storage = getStorage();
3931
+ if (!storage) {
3932
+ console.warn("localStorage not available - pending deposit not persisted");
3933
+ return;
3934
+ }
3935
+ const deposits = loadPendingDeposits();
3936
+ const index = deposits.findIndex((d) => d.note.commitment === deposit.note.commitment);
3937
+ if (index >= 0) {
3938
+ deposits[index] = deposit;
3939
+ } else {
3940
+ deposits.push(deposit);
3941
+ }
3942
+ storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
3943
+ }
3944
+ function loadPendingDeposits() {
3945
+ const storage = getStorage();
3946
+ if (!storage) return [];
3947
+ const stored = storage.getItem(PENDING_DEPOSITS_KEY);
3948
+ if (!stored) return [];
3949
+ try {
3950
+ return JSON.parse(stored);
3951
+ } catch {
3952
+ return [];
3953
+ }
3954
+ }
3955
+ function updatePendingDeposit(commitment, updates) {
3956
+ const storage = getStorage();
3957
+ if (!storage) return;
3958
+ const deposits = loadPendingDeposits();
3959
+ const index = deposits.findIndex((d) => d.note.commitment === commitment);
3960
+ if (index >= 0) {
3961
+ deposits[index] = { ...deposits[index], ...updates };
3962
+ storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
3963
+ }
3964
+ }
3965
+ function removePendingDeposit(commitment) {
3966
+ const storage = getStorage();
3967
+ if (!storage) return;
3968
+ const deposits = loadPendingDeposits();
3969
+ const filtered = deposits.filter((d) => d.note.commitment !== commitment);
3970
+ storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(filtered));
3971
+ }
3972
+ function clearPendingDeposits() {
3973
+ const storage = getStorage();
3974
+ if (storage) {
3975
+ storage.removeItem(PENDING_DEPOSITS_KEY);
3976
+ }
3977
+ }
3978
+ function savePendingWithdrawal(withdrawal) {
3979
+ const storage = getStorage();
3980
+ if (!storage) {
3981
+ console.warn("localStorage not available - pending withdrawal not persisted");
3982
+ return;
3983
+ }
3984
+ const withdrawals = loadPendingWithdrawals();
3985
+ const index = withdrawals.findIndex((w) => w.requestId === withdrawal.requestId);
3986
+ if (index >= 0) {
3987
+ withdrawals[index] = withdrawal;
3988
+ } else {
3989
+ withdrawals.push(withdrawal);
3990
+ }
3991
+ storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
3992
+ }
3993
+ function loadPendingWithdrawals() {
3994
+ const storage = getStorage();
3995
+ if (!storage) return [];
3996
+ const stored = storage.getItem(PENDING_WITHDRAWALS_KEY);
3997
+ if (!stored) return [];
3998
+ try {
3999
+ return JSON.parse(stored);
4000
+ } catch {
4001
+ return [];
4002
+ }
4003
+ }
4004
+ function updatePendingWithdrawal(requestId, updates) {
4005
+ const storage = getStorage();
4006
+ if (!storage) return;
4007
+ const withdrawals = loadPendingWithdrawals();
4008
+ const index = withdrawals.findIndex((w) => w.requestId === requestId);
4009
+ if (index >= 0) {
4010
+ withdrawals[index] = { ...withdrawals[index], ...updates };
4011
+ storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
4012
+ }
4013
+ }
4014
+ function removePendingWithdrawal(requestId) {
4015
+ const storage = getStorage();
4016
+ if (!storage) return;
4017
+ const withdrawals = loadPendingWithdrawals();
4018
+ const filtered = withdrawals.filter((w) => w.requestId !== requestId);
4019
+ storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(filtered));
4020
+ }
4021
+ function clearPendingWithdrawals() {
4022
+ const storage = getStorage();
4023
+ if (storage) {
4024
+ storage.removeItem(PENDING_WITHDRAWALS_KEY);
4025
+ }
4026
+ }
4027
+ function hasPendingOperations() {
4028
+ const deposits = loadPendingDeposits();
4029
+ const withdrawals = loadPendingWithdrawals();
4030
+ const pendingDeposits = deposits.filter(
4031
+ (d) => d.status === "pending" || d.status === "tx_sent"
4032
+ );
4033
+ const pendingWithdrawals = withdrawals.filter(
4034
+ (w) => w.status === "pending" || w.status === "processing"
4035
+ );
4036
+ return pendingDeposits.length > 0 || pendingWithdrawals.length > 0;
4037
+ }
4038
+ function getPendingOperationsSummary() {
4039
+ const deposits = loadPendingDeposits().filter(
4040
+ (d) => d.status === "pending" || d.status === "tx_sent"
4041
+ );
4042
+ const withdrawals = loadPendingWithdrawals().filter(
4043
+ (w) => w.status === "pending" || w.status === "processing"
4044
+ );
4045
+ return {
4046
+ deposits,
4047
+ withdrawals,
4048
+ totalPending: deposits.length + withdrawals.length
4049
+ };
4050
+ }
4051
+ function cleanupStalePendingOperations(maxAgeMs = 24 * 60 * 60 * 1e3) {
4052
+ const now = Date.now();
4053
+ let removedDeposits = 0;
4054
+ let removedWithdrawals = 0;
4055
+ const deposits = loadPendingDeposits();
4056
+ const activeDeposits = deposits.filter((d) => {
4057
+ const age = now - d.startedAt;
4058
+ const isStale = age > maxAgeMs;
4059
+ const isTerminal = d.status === "confirmed" || d.status === "failed";
4060
+ if (isStale || isTerminal) {
4061
+ removedDeposits++;
4062
+ return false;
4063
+ }
4064
+ return true;
4065
+ });
4066
+ const storage = getStorage();
4067
+ if (storage) {
4068
+ storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(activeDeposits));
4069
+ }
4070
+ const withdrawals = loadPendingWithdrawals();
4071
+ const activeWithdrawals = withdrawals.filter((w) => {
4072
+ const age = now - w.startedAt;
4073
+ const isStale = age > maxAgeMs;
4074
+ const isTerminal = w.status === "completed" || w.status === "failed";
4075
+ if (isStale || isTerminal) {
4076
+ removedWithdrawals++;
4077
+ return false;
4078
+ }
4079
+ return true;
4080
+ });
4081
+ if (storage) {
4082
+ storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(activeWithdrawals));
4083
+ }
4084
+ return { removedDeposits, removedWithdrawals };
4085
+ }
4086
+
3143
4087
  // src/index.ts
3144
4088
  var VERSION = "1.0.0";
3145
4089
  export {
@@ -3147,12 +4091,14 @@ export {
3147
4091
  CloakError,
3148
4092
  CloakSDK,
3149
4093
  DepositRecoveryService,
4094
+ EXPECTED_CIRCUIT_HASHES,
3150
4095
  FIXED_FEE_LAMPORTS,
3151
4096
  IndexerService,
3152
4097
  LAMPORTS_PER_SOL,
3153
4098
  LocalStorageAdapter,
3154
4099
  MemoryStorageAdapter,
3155
4100
  RelayService,
4101
+ ShieldPoolErrors,
3156
4102
  VARIABLE_FEE_RATE,
3157
4103
  VERSION,
3158
4104
  bigintToBytes32,
@@ -3160,6 +4106,9 @@ export {
3160
4106
  bytesToHex,
3161
4107
  calculateFee2 as calculateFee,
3162
4108
  calculateRelayFee,
4109
+ cleanupStalePendingOperations,
4110
+ clearPendingDeposits,
4111
+ clearPendingWithdrawals,
3163
4112
  computeCommitment,
3164
4113
  computeMerkleRoot,
3165
4114
  computeNullifier,
@@ -3199,12 +4148,14 @@ export {
3199
4148
  getAddressExplorerUrl,
3200
4149
  getDistributableAmount2 as getDistributableAmount,
3201
4150
  getExplorerUrl,
4151
+ getPendingOperationsSummary,
3202
4152
  getPublicKey,
3203
4153
  getPublicViewKey,
3204
4154
  getRecipientAmount,
3205
4155
  getRpcUrlForNetwork,
3206
4156
  getShieldPoolPDAs,
3207
4157
  getViewKey,
4158
+ hasPendingOperations,
3208
4159
  hexToBigint,
3209
4160
  hexToBytes,
3210
4161
  importKeys,
@@ -3214,7 +4165,10 @@ export {
3214
4165
  isValidSolanaAddress,
3215
4166
  isWithdrawable,
3216
4167
  keypairToAdapter,
4168
+ loadPendingDeposits,
4169
+ loadPendingWithdrawals,
3217
4170
  parseAmount,
4171
+ parseError,
3218
4172
  parseNote,
3219
4173
  parseTransactionError,
3220
4174
  poseidonHash,
@@ -3223,6 +4177,10 @@ export {
3223
4177
  proofToBytes,
3224
4178
  pubkeyToLimbs,
3225
4179
  randomBytes,
4180
+ removePendingDeposit,
4181
+ removePendingWithdrawal,
4182
+ savePendingDeposit,
4183
+ savePendingWithdrawal,
3226
4184
  scanNotesForWallet,
3227
4185
  sendTransaction,
3228
4186
  serializeNote,
@@ -3230,10 +4188,14 @@ export {
3230
4188
  splitTo2Limbs,
3231
4189
  tryDecryptNote,
3232
4190
  updateNoteWithDeposit,
4191
+ updatePendingDeposit,
4192
+ updatePendingWithdrawal,
3233
4193
  validateDepositParams,
3234
4194
  validateNote,
3235
4195
  validateOutputsSum,
3236
4196
  validateTransfers,
3237
4197
  validateWalletConnected,
3238
- validateWithdrawableNote
4198
+ validateWithdrawableNote,
4199
+ verifyAllCircuits,
4200
+ verifyCircuitIntegrity
3239
4201
  };