@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/README.md +39 -4
- package/dist/index.cjs +1002 -91
- package/dist/index.d.cts +280 -6
- package/dist/index.d.ts +280 -6
- package/dist/index.js +983 -90
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,12 +34,14 @@ __export(index_exports, {
|
|
|
34
34
|
CloakError: () => CloakError,
|
|
35
35
|
CloakSDK: () => CloakSDK,
|
|
36
36
|
DepositRecoveryService: () => DepositRecoveryService,
|
|
37
|
+
EXPECTED_CIRCUIT_HASHES: () => EXPECTED_CIRCUIT_HASHES,
|
|
37
38
|
FIXED_FEE_LAMPORTS: () => FIXED_FEE_LAMPORTS,
|
|
38
39
|
IndexerService: () => IndexerService,
|
|
39
40
|
LAMPORTS_PER_SOL: () => LAMPORTS_PER_SOL,
|
|
40
41
|
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
41
42
|
MemoryStorageAdapter: () => MemoryStorageAdapter,
|
|
42
43
|
RelayService: () => RelayService,
|
|
44
|
+
ShieldPoolErrors: () => ShieldPoolErrors,
|
|
43
45
|
VARIABLE_FEE_RATE: () => VARIABLE_FEE_RATE,
|
|
44
46
|
VERSION: () => VERSION,
|
|
45
47
|
bigintToBytes32: () => bigintToBytes32,
|
|
@@ -47,6 +49,9 @@ __export(index_exports, {
|
|
|
47
49
|
bytesToHex: () => bytesToHex,
|
|
48
50
|
calculateFee: () => calculateFee2,
|
|
49
51
|
calculateRelayFee: () => calculateRelayFee,
|
|
52
|
+
cleanupStalePendingOperations: () => cleanupStalePendingOperations,
|
|
53
|
+
clearPendingDeposits: () => clearPendingDeposits,
|
|
54
|
+
clearPendingWithdrawals: () => clearPendingWithdrawals,
|
|
50
55
|
computeCommitment: () => computeCommitment,
|
|
51
56
|
computeMerkleRoot: () => computeMerkleRoot,
|
|
52
57
|
computeNullifier: () => computeNullifier,
|
|
@@ -86,12 +91,14 @@ __export(index_exports, {
|
|
|
86
91
|
getAddressExplorerUrl: () => getAddressExplorerUrl,
|
|
87
92
|
getDistributableAmount: () => getDistributableAmount2,
|
|
88
93
|
getExplorerUrl: () => getExplorerUrl,
|
|
94
|
+
getPendingOperationsSummary: () => getPendingOperationsSummary,
|
|
89
95
|
getPublicKey: () => getPublicKey,
|
|
90
96
|
getPublicViewKey: () => getPublicViewKey,
|
|
91
97
|
getRecipientAmount: () => getRecipientAmount,
|
|
92
98
|
getRpcUrlForNetwork: () => getRpcUrlForNetwork,
|
|
93
99
|
getShieldPoolPDAs: () => getShieldPoolPDAs,
|
|
94
100
|
getViewKey: () => getViewKey,
|
|
101
|
+
hasPendingOperations: () => hasPendingOperations,
|
|
95
102
|
hexToBigint: () => hexToBigint,
|
|
96
103
|
hexToBytes: () => hexToBytes,
|
|
97
104
|
importKeys: () => importKeys,
|
|
@@ -101,7 +108,10 @@ __export(index_exports, {
|
|
|
101
108
|
isValidSolanaAddress: () => isValidSolanaAddress,
|
|
102
109
|
isWithdrawable: () => isWithdrawable,
|
|
103
110
|
keypairToAdapter: () => keypairToAdapter,
|
|
111
|
+
loadPendingDeposits: () => loadPendingDeposits,
|
|
112
|
+
loadPendingWithdrawals: () => loadPendingWithdrawals,
|
|
104
113
|
parseAmount: () => parseAmount,
|
|
114
|
+
parseError: () => parseError,
|
|
105
115
|
parseNote: () => parseNote,
|
|
106
116
|
parseTransactionError: () => parseTransactionError,
|
|
107
117
|
poseidonHash: () => poseidonHash,
|
|
@@ -110,6 +120,10 @@ __export(index_exports, {
|
|
|
110
120
|
proofToBytes: () => proofToBytes,
|
|
111
121
|
pubkeyToLimbs: () => pubkeyToLimbs,
|
|
112
122
|
randomBytes: () => randomBytes,
|
|
123
|
+
removePendingDeposit: () => removePendingDeposit,
|
|
124
|
+
removePendingWithdrawal: () => removePendingWithdrawal,
|
|
125
|
+
savePendingDeposit: () => savePendingDeposit,
|
|
126
|
+
savePendingWithdrawal: () => savePendingWithdrawal,
|
|
113
127
|
scanNotesForWallet: () => scanNotesForWallet,
|
|
114
128
|
sendTransaction: () => sendTransaction,
|
|
115
129
|
serializeNote: () => serializeNote,
|
|
@@ -117,12 +131,16 @@ __export(index_exports, {
|
|
|
117
131
|
splitTo2Limbs: () => splitTo2Limbs,
|
|
118
132
|
tryDecryptNote: () => tryDecryptNote,
|
|
119
133
|
updateNoteWithDeposit: () => updateNoteWithDeposit,
|
|
134
|
+
updatePendingDeposit: () => updatePendingDeposit,
|
|
135
|
+
updatePendingWithdrawal: () => updatePendingWithdrawal,
|
|
120
136
|
validateDepositParams: () => validateDepositParams,
|
|
121
137
|
validateNote: () => validateNote,
|
|
122
138
|
validateOutputsSum: () => validateOutputsSum,
|
|
123
139
|
validateTransfers: () => validateTransfers,
|
|
124
140
|
validateWalletConnected: () => validateWalletConnected,
|
|
125
|
-
validateWithdrawableNote: () => validateWithdrawableNote
|
|
141
|
+
validateWithdrawableNote: () => validateWithdrawableNote,
|
|
142
|
+
verifyAllCircuits: () => verifyAllCircuits,
|
|
143
|
+
verifyCircuitIntegrity: () => verifyCircuitIntegrity
|
|
126
144
|
});
|
|
127
145
|
module.exports = __toCommonJS(index_exports);
|
|
128
146
|
|
|
@@ -289,14 +307,14 @@ function randomBytes(length) {
|
|
|
289
307
|
} catch {
|
|
290
308
|
}
|
|
291
309
|
try {
|
|
292
|
-
const
|
|
293
|
-
if (
|
|
294
|
-
const buffer =
|
|
310
|
+
const nodeCrypto2 = require("crypto");
|
|
311
|
+
if (nodeCrypto2?.randomBytes) {
|
|
312
|
+
const buffer = nodeCrypto2.randomBytes(length);
|
|
295
313
|
bytes.set(buffer);
|
|
296
314
|
return bytes;
|
|
297
315
|
}
|
|
298
|
-
if (
|
|
299
|
-
|
|
316
|
+
if (nodeCrypto2?.webcrypto?.getRandomValues) {
|
|
317
|
+
nodeCrypto2.webcrypto.getRandomValues(bytes);
|
|
300
318
|
return bytes;
|
|
301
319
|
}
|
|
302
320
|
} catch {
|
|
@@ -418,8 +436,8 @@ function generateMasterSeed() {
|
|
|
418
436
|
cryptoObj.getRandomValues(seed);
|
|
419
437
|
} else {
|
|
420
438
|
try {
|
|
421
|
-
const
|
|
422
|
-
const buffer =
|
|
439
|
+
const nodeCrypto2 = require("crypto");
|
|
440
|
+
const buffer = nodeCrypto2.randomBytes(32);
|
|
423
441
|
seed.set(buffer);
|
|
424
442
|
} catch {
|
|
425
443
|
throw new Error("No secure random number generator available");
|
|
@@ -1160,6 +1178,8 @@ var RelayService = class {
|
|
|
1160
1178
|
*
|
|
1161
1179
|
* @param params - Withdrawal parameters
|
|
1162
1180
|
* @param onStatusUpdate - Optional callback for status updates
|
|
1181
|
+
* @param onRequestId - CRITICAL: Callback when request_id is received.
|
|
1182
|
+
* Persist this ID to recover if browser crashes during polling.
|
|
1163
1183
|
* @returns Transaction signature when completed
|
|
1164
1184
|
*
|
|
1165
1185
|
* @example
|
|
@@ -1169,11 +1189,12 @@ var RelayService = class {
|
|
|
1169
1189
|
* publicInputs: { root, nf, outputs_hash, amount },
|
|
1170
1190
|
* outputs: [{ recipient: addr, amount: lamports }],
|
|
1171
1191
|
* feeBps: 50
|
|
1172
|
-
* }, (status) => console.log(`Status: ${status}`)
|
|
1192
|
+
* }, (status) => console.log(`Status: ${status}`),
|
|
1193
|
+
* (requestId) => localStorage.setItem('pending_withdraw', requestId));
|
|
1173
1194
|
* console.log(`Transaction: ${signature}`);
|
|
1174
1195
|
* ```
|
|
1175
1196
|
*/
|
|
1176
|
-
async submitWithdraw(params, onStatusUpdate) {
|
|
1197
|
+
async submitWithdraw(params, onStatusUpdate, onRequestId) {
|
|
1177
1198
|
const proofBytes = hexToBytes(params.proof);
|
|
1178
1199
|
const proofBase64 = this.bytesToBase64(proofBytes);
|
|
1179
1200
|
const requestBody = {
|
|
@@ -1214,8 +1235,55 @@ var RelayService = class {
|
|
|
1214
1235
|
if (!requestId) {
|
|
1215
1236
|
throw new Error("Relay response missing request_id");
|
|
1216
1237
|
}
|
|
1238
|
+
if (onRequestId) {
|
|
1239
|
+
onRequestId(requestId);
|
|
1240
|
+
}
|
|
1217
1241
|
return this.pollForCompletion(requestId, onStatusUpdate);
|
|
1218
1242
|
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Resume polling for a withdrawal that was previously started
|
|
1245
|
+
*
|
|
1246
|
+
* Use this after page reload to check status of a pending withdrawal.
|
|
1247
|
+
* The requestId should have been persisted via the onRequestId callback.
|
|
1248
|
+
*
|
|
1249
|
+
* @param requestId - Request ID from a previous submitWithdraw call
|
|
1250
|
+
* @param onStatusUpdate - Optional callback for status updates
|
|
1251
|
+
* @returns Transaction signature if completed, null if still pending/failed
|
|
1252
|
+
*
|
|
1253
|
+
* @example
|
|
1254
|
+
* ```typescript
|
|
1255
|
+
* // On page load, check for pending withdrawal
|
|
1256
|
+
* const pendingId = localStorage.getItem('pending_withdraw');
|
|
1257
|
+
* if (pendingId) {
|
|
1258
|
+
* const result = await relay.resumeWithdraw(pendingId);
|
|
1259
|
+
* if (result.status === 'completed') {
|
|
1260
|
+
* console.log('Withdrawal completed:', result.signature);
|
|
1261
|
+
* localStorage.removeItem('pending_withdraw');
|
|
1262
|
+
* }
|
|
1263
|
+
* }
|
|
1264
|
+
* ```
|
|
1265
|
+
*/
|
|
1266
|
+
async resumeWithdraw(requestId, onStatusUpdate) {
|
|
1267
|
+
try {
|
|
1268
|
+
const status = await this.getStatus(requestId);
|
|
1269
|
+
if (onStatusUpdate) {
|
|
1270
|
+
onStatusUpdate(status.status);
|
|
1271
|
+
}
|
|
1272
|
+
if (status.status === "completed") {
|
|
1273
|
+
return { status: "completed", signature: status.txId };
|
|
1274
|
+
} else if (status.status === "failed") {
|
|
1275
|
+
return { status: "failed", error: status.error };
|
|
1276
|
+
} else {
|
|
1277
|
+
const signature = await this.pollForCompletion(requestId, onStatusUpdate);
|
|
1278
|
+
return { status: "completed", signature };
|
|
1279
|
+
}
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
return {
|
|
1282
|
+
status: "failed",
|
|
1283
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1219
1287
|
/**
|
|
1220
1288
|
* Poll for withdrawal completion
|
|
1221
1289
|
*
|
|
@@ -1288,7 +1356,7 @@ var RelayService = class {
|
|
|
1288
1356
|
* console.log(`Transaction: ${signature}`);
|
|
1289
1357
|
* ```
|
|
1290
1358
|
*/
|
|
1291
|
-
async submitSwap(params, onStatusUpdate) {
|
|
1359
|
+
async submitSwap(params, onStatusUpdate, onRequestId) {
|
|
1292
1360
|
const proofBytes = hexToBytes(params.proof);
|
|
1293
1361
|
const proofBase64 = this.bytesToBase64(proofBytes);
|
|
1294
1362
|
const requestBody = {
|
|
@@ -1330,6 +1398,9 @@ var RelayService = class {
|
|
|
1330
1398
|
if (!requestId) {
|
|
1331
1399
|
throw new Error("Relay response missing request_id");
|
|
1332
1400
|
}
|
|
1401
|
+
if (onRequestId) {
|
|
1402
|
+
onRequestId(requestId);
|
|
1403
|
+
}
|
|
1333
1404
|
return this.pollForCompletion(requestId, onStatusUpdate);
|
|
1334
1405
|
}
|
|
1335
1406
|
/**
|
|
@@ -1566,29 +1637,94 @@ var DepositRecoveryService = class {
|
|
|
1566
1637
|
}
|
|
1567
1638
|
/**
|
|
1568
1639
|
* Check if a deposit already exists in the indexer
|
|
1640
|
+
* Uses the enhanced deposit lookup endpoint with include_proof=true
|
|
1569
1641
|
*
|
|
1570
1642
|
* @private
|
|
1571
1643
|
*/
|
|
1572
|
-
async checkExistingDeposit(
|
|
1644
|
+
async checkExistingDeposit(commitment) {
|
|
1573
1645
|
try {
|
|
1574
|
-
const
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
return null;
|
|
1582
|
-
} catch (e) {
|
|
1583
|
-
continue;
|
|
1584
|
-
}
|
|
1646
|
+
const cleanCommit = commitment.replace(/^0x/, "").toLowerCase();
|
|
1647
|
+
const response = await fetch(
|
|
1648
|
+
`${this.apiUrl}/api/v1/deposit/${cleanCommit}?include_proof=true`
|
|
1649
|
+
);
|
|
1650
|
+
if (!response.ok) {
|
|
1651
|
+
if (response.status === 404) {
|
|
1652
|
+
return null;
|
|
1585
1653
|
}
|
|
1654
|
+
throw new Error(`Failed to check deposit: ${response.status}`);
|
|
1586
1655
|
}
|
|
1587
|
-
|
|
1656
|
+
const data = await response.json();
|
|
1657
|
+
if (!data.merkle_proof) {
|
|
1658
|
+
const merkleProof = await this.indexer.getMerkleProof(data.leaf_index);
|
|
1659
|
+
return {
|
|
1660
|
+
leafIndex: data.leaf_index,
|
|
1661
|
+
root: merkleProof.root || "",
|
|
1662
|
+
slot: data.slot,
|
|
1663
|
+
merkleProof: {
|
|
1664
|
+
pathElements: merkleProof.pathElements,
|
|
1665
|
+
pathIndices: merkleProof.pathIndices
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
return {
|
|
1670
|
+
leafIndex: data.leaf_index,
|
|
1671
|
+
root: data.merkle_proof.root,
|
|
1672
|
+
slot: data.slot,
|
|
1673
|
+
merkleProof: {
|
|
1674
|
+
pathElements: data.merkle_proof.path_elements,
|
|
1675
|
+
pathIndices: data.merkle_proof.path_indices
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1588
1678
|
} catch (error) {
|
|
1589
1679
|
return null;
|
|
1590
1680
|
}
|
|
1591
1681
|
}
|
|
1682
|
+
/**
|
|
1683
|
+
* Recover deposit by transaction signature
|
|
1684
|
+
* Uses the indexer's signature lookup endpoint
|
|
1685
|
+
*/
|
|
1686
|
+
async recoverBySignature(signature) {
|
|
1687
|
+
try {
|
|
1688
|
+
const response = await fetch(
|
|
1689
|
+
`${this.apiUrl}/api/v1/deposit/tx/${signature}?include_proof=true`
|
|
1690
|
+
);
|
|
1691
|
+
if (!response.ok) {
|
|
1692
|
+
if (response.status === 404) {
|
|
1693
|
+
return {
|
|
1694
|
+
success: false,
|
|
1695
|
+
error: "Deposit not found for this transaction signature"
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
const errorText = await response.text();
|
|
1699
|
+
return {
|
|
1700
|
+
success: false,
|
|
1701
|
+
error: `Failed to recover deposit: ${errorText}`
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
const data = await response.json();
|
|
1705
|
+
if (!data.merkle_proof) {
|
|
1706
|
+
return {
|
|
1707
|
+
success: false,
|
|
1708
|
+
error: "Deposit found but merkle proof not available"
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
return {
|
|
1712
|
+
success: true,
|
|
1713
|
+
leafIndex: data.leaf_index,
|
|
1714
|
+
root: data.merkle_proof.root,
|
|
1715
|
+
slot: data.slot,
|
|
1716
|
+
merkleProof: {
|
|
1717
|
+
pathElements: data.merkle_proof.path_elements,
|
|
1718
|
+
pathIndices: data.merkle_proof.path_indices
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
} catch (error) {
|
|
1722
|
+
return {
|
|
1723
|
+
success: false,
|
|
1724
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1592
1728
|
/**
|
|
1593
1729
|
* Finalize a deposit via server API (alternative recovery method)
|
|
1594
1730
|
*
|
|
@@ -1744,39 +1880,15 @@ function getShieldPoolPDAs(programId, mint) {
|
|
|
1744
1880
|
|
|
1745
1881
|
// src/utils/proof-generation.ts
|
|
1746
1882
|
var snarkjs = __toESM(require("snarkjs"), 1);
|
|
1747
|
-
var
|
|
1748
|
-
var fs = null;
|
|
1749
|
-
async function loadNodeModules() {
|
|
1750
|
-
const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
|
|
1751
|
-
if (!isBrowser && typeof process !== "undefined" && process.versions?.node) {
|
|
1752
|
-
if (!path) {
|
|
1753
|
-
path = await import("path");
|
|
1754
|
-
}
|
|
1755
|
-
if (!fs) {
|
|
1756
|
-
fs = await import("fs");
|
|
1757
|
-
}
|
|
1758
|
-
return { path, fs };
|
|
1759
|
-
}
|
|
1760
|
-
return { path: null, fs: null };
|
|
1761
|
-
}
|
|
1883
|
+
var IS_BROWSER = typeof window !== "undefined" || typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
1762
1884
|
function joinPath(...parts) {
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
return path.join(...parts);
|
|
1885
|
+
if (IS_BROWSER) {
|
|
1886
|
+
return parts.join("/").replace(/\/+/g, "/");
|
|
1766
1887
|
}
|
|
1767
1888
|
return parts.join("/").replace(/\/+/g, "/");
|
|
1768
1889
|
}
|
|
1769
1890
|
async function fileExists(filePath) {
|
|
1770
|
-
|
|
1771
|
-
if (fs2) {
|
|
1772
|
-
try {
|
|
1773
|
-
return fs2.existsSync(filePath);
|
|
1774
|
-
} catch {
|
|
1775
|
-
return false;
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
|
|
1779
|
-
if (isBrowser) {
|
|
1891
|
+
if (IS_BROWSER) {
|
|
1780
1892
|
try {
|
|
1781
1893
|
const response = await fetch(filePath, { method: "HEAD" });
|
|
1782
1894
|
return response.ok;
|
|
@@ -1784,18 +1896,26 @@ async function fileExists(filePath) {
|
|
|
1784
1896
|
return false;
|
|
1785
1897
|
}
|
|
1786
1898
|
}
|
|
1787
|
-
|
|
1899
|
+
try {
|
|
1900
|
+
const nodeFs2 = globalThis.require?.("fs") || (typeof require !== "undefined" ? require("fs") : null);
|
|
1901
|
+
if (nodeFs2) {
|
|
1902
|
+
return nodeFs2.existsSync(filePath);
|
|
1903
|
+
}
|
|
1904
|
+
const fsModule = await import("fs");
|
|
1905
|
+
return fsModule.existsSync(filePath);
|
|
1906
|
+
} catch {
|
|
1907
|
+
return false;
|
|
1908
|
+
}
|
|
1788
1909
|
}
|
|
1789
|
-
async function generateWithdrawRegularProof(inputs,
|
|
1790
|
-
const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
|
|
1910
|
+
async function generateWithdrawRegularProof(inputs, circuitsPath2) {
|
|
1791
1911
|
let wasmPath;
|
|
1792
1912
|
let zkeyPath;
|
|
1793
|
-
if (
|
|
1794
|
-
wasmPath = `${
|
|
1795
|
-
zkeyPath = `${
|
|
1913
|
+
if (IS_BROWSER) {
|
|
1914
|
+
wasmPath = `${circuitsPath2}/withdraw_regular_js/withdraw_regular.wasm`;
|
|
1915
|
+
zkeyPath = `${circuitsPath2}/withdraw_regular_final.zkey`;
|
|
1796
1916
|
} else {
|
|
1797
|
-
wasmPath = joinPath(
|
|
1798
|
-
zkeyPath = joinPath(
|
|
1917
|
+
wasmPath = joinPath(circuitsPath2, "build", "withdraw_regular_js", "withdraw_regular.wasm");
|
|
1918
|
+
zkeyPath = joinPath(circuitsPath2, "build", "withdraw_regular_final.zkey");
|
|
1799
1919
|
const wasmExists = await fileExists(wasmPath);
|
|
1800
1920
|
const zkeyExists = await fileExists(zkeyPath);
|
|
1801
1921
|
if (!wasmExists) {
|
|
@@ -1837,16 +1957,15 @@ async function generateWithdrawRegularProof(inputs, circuitsPath) {
|
|
|
1837
1957
|
// Not used, kept for compatibility
|
|
1838
1958
|
};
|
|
1839
1959
|
}
|
|
1840
|
-
async function generateWithdrawSwapProof(inputs,
|
|
1841
|
-
const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
|
|
1960
|
+
async function generateWithdrawSwapProof(inputs, circuitsPath2) {
|
|
1842
1961
|
let wasmPath;
|
|
1843
1962
|
let zkeyPath;
|
|
1844
|
-
if (
|
|
1845
|
-
wasmPath = `${
|
|
1846
|
-
zkeyPath = `${
|
|
1963
|
+
if (IS_BROWSER) {
|
|
1964
|
+
wasmPath = `${circuitsPath2}/withdraw_swap_js/withdraw_swap.wasm`;
|
|
1965
|
+
zkeyPath = `${circuitsPath2}/withdraw_swap_final.zkey`;
|
|
1847
1966
|
} else {
|
|
1848
|
-
wasmPath = joinPath(
|
|
1849
|
-
zkeyPath = joinPath(
|
|
1967
|
+
wasmPath = joinPath(circuitsPath2, "build", "withdraw_swap_js", "withdraw_swap.wasm");
|
|
1968
|
+
zkeyPath = joinPath(circuitsPath2, "build", "withdraw_swap_final.zkey");
|
|
1850
1969
|
const wasmExists = await fileExists(wasmPath);
|
|
1851
1970
|
const zkeyExists = await fileExists(zkeyPath);
|
|
1852
1971
|
if (!wasmExists) {
|
|
@@ -1895,32 +2014,35 @@ async function generateWithdrawSwapProof(inputs, circuitsPath) {
|
|
|
1895
2014
|
// Not used, kept for compatibility
|
|
1896
2015
|
};
|
|
1897
2016
|
}
|
|
1898
|
-
async function areCircuitsAvailable(
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
return Boolean(circuitsPath && circuitsPath !== "");
|
|
2017
|
+
async function areCircuitsAvailable(circuitsPath2) {
|
|
2018
|
+
if (IS_BROWSER) {
|
|
2019
|
+
return Boolean(circuitsPath2 && circuitsPath2 !== "");
|
|
1902
2020
|
}
|
|
1903
|
-
const wasmPath = joinPath(
|
|
1904
|
-
const zkeyPath = joinPath(
|
|
2021
|
+
const wasmPath = joinPath(circuitsPath2, "build", "withdraw_regular_js", "withdraw_regular.wasm");
|
|
2022
|
+
const zkeyPath = joinPath(circuitsPath2, "build", "withdraw_regular_final.zkey");
|
|
1905
2023
|
const wasmExists = await fileExists(wasmPath);
|
|
1906
2024
|
const zkeyExists = await fileExists(zkeyPath);
|
|
1907
2025
|
return wasmExists && zkeyExists;
|
|
1908
2026
|
}
|
|
1909
2027
|
async function getDefaultCircuitsPath() {
|
|
1910
|
-
|
|
1911
|
-
if (isBrowser) {
|
|
2028
|
+
if (IS_BROWSER) {
|
|
1912
2029
|
return "/circuits";
|
|
1913
2030
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
2031
|
+
if (typeof process === "undefined" || !process.cwd) {
|
|
2032
|
+
return "/circuits";
|
|
2033
|
+
}
|
|
2034
|
+
let nodePath;
|
|
2035
|
+
try {
|
|
2036
|
+
nodePath = eval("require")("path");
|
|
2037
|
+
} catch {
|
|
1916
2038
|
return "/circuits";
|
|
1917
2039
|
}
|
|
1918
2040
|
const possiblePaths = [
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
2041
|
+
nodePath.resolve(process.cwd(), "../../packages-new/circuits"),
|
|
2042
|
+
nodePath.resolve(process.cwd(), "../packages-new/circuits"),
|
|
2043
|
+
nodePath.resolve(process.cwd(), "packages-new/circuits"),
|
|
2044
|
+
nodePath.resolve(process.cwd(), "../../circuits"),
|
|
2045
|
+
nodePath.resolve(process.cwd(), "../circuits")
|
|
1924
2046
|
];
|
|
1925
2047
|
for (const p of possiblePaths) {
|
|
1926
2048
|
if (await areCircuitsAvailable(p)) {
|
|
@@ -1929,6 +2051,90 @@ async function getDefaultCircuitsPath() {
|
|
|
1929
2051
|
}
|
|
1930
2052
|
return possiblePaths[0];
|
|
1931
2053
|
}
|
|
2054
|
+
var EXPECTED_CIRCUIT_HASHES = {
|
|
2055
|
+
// SHA-256 of the verification key JSON from withdraw_regular circuit
|
|
2056
|
+
withdraw_regular_vkey: null,
|
|
2057
|
+
// Set to null to skip check during development
|
|
2058
|
+
// SHA-256 of the verification key JSON from withdraw_swap circuit
|
|
2059
|
+
withdraw_swap_vkey: null
|
|
2060
|
+
// Set to null to skip check during development
|
|
2061
|
+
};
|
|
2062
|
+
async function verifyCircuitIntegrity(circuitsPath, circuit) {
|
|
2063
|
+
const expectedHash = circuit === "withdraw_regular" ? EXPECTED_CIRCUIT_HASHES.withdraw_regular_vkey : EXPECTED_CIRCUIT_HASHES.withdraw_swap_vkey;
|
|
2064
|
+
if (!expectedHash) {
|
|
2065
|
+
return {
|
|
2066
|
+
valid: true,
|
|
2067
|
+
circuit,
|
|
2068
|
+
error: "Verification skipped (no expected hash configured)"
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
try {
|
|
2072
|
+
const vkeyPath = IS_BROWSER ? `${circuitsPath}/${circuit}_verification_key.json` : joinPath(circuitsPath, "build", `${circuit}_verification_key.json`);
|
|
2073
|
+
let vkeyData;
|
|
2074
|
+
if (IS_BROWSER) {
|
|
2075
|
+
const response = await fetch(vkeyPath);
|
|
2076
|
+
if (!response.ok) {
|
|
2077
|
+
return {
|
|
2078
|
+
valid: false,
|
|
2079
|
+
circuit,
|
|
2080
|
+
error: `Failed to fetch verification key: ${response.status}`
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
vkeyData = await response.text();
|
|
2084
|
+
} else {
|
|
2085
|
+
try {
|
|
2086
|
+
const nodeFs = eval("require")("fs");
|
|
2087
|
+
vkeyData = nodeFs.readFileSync(vkeyPath, "utf8");
|
|
2088
|
+
} catch (e) {
|
|
2089
|
+
return {
|
|
2090
|
+
valid: false,
|
|
2091
|
+
circuit,
|
|
2092
|
+
error: `Failed to read verification key: ${e}`
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
let computedHash;
|
|
2097
|
+
if (IS_BROWSER) {
|
|
2098
|
+
const encoder = new TextEncoder();
|
|
2099
|
+
const data = encoder.encode(vkeyData);
|
|
2100
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
2101
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
2102
|
+
computedHash = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2103
|
+
} else {
|
|
2104
|
+
const nodeCrypto = eval("require")("crypto");
|
|
2105
|
+
computedHash = nodeCrypto.createHash("sha256").update(vkeyData).digest("hex");
|
|
2106
|
+
}
|
|
2107
|
+
if (computedHash === expectedHash) {
|
|
2108
|
+
return {
|
|
2109
|
+
valid: true,
|
|
2110
|
+
circuit,
|
|
2111
|
+
computedHash,
|
|
2112
|
+
expectedHash
|
|
2113
|
+
};
|
|
2114
|
+
} else {
|
|
2115
|
+
return {
|
|
2116
|
+
valid: false,
|
|
2117
|
+
circuit,
|
|
2118
|
+
error: `Verification key hash mismatch! This circuit may produce invalid proofs.`,
|
|
2119
|
+
computedHash,
|
|
2120
|
+
expectedHash
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
} catch (error) {
|
|
2124
|
+
return {
|
|
2125
|
+
valid: false,
|
|
2126
|
+
circuit,
|
|
2127
|
+
error: `Circuit verification failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
async function verifyAllCircuits(circuitsPath2) {
|
|
2132
|
+
const results = await Promise.all([
|
|
2133
|
+
verifyCircuitIntegrity(circuitsPath2, "withdraw_regular"),
|
|
2134
|
+
verifyCircuitIntegrity(circuitsPath2, "withdraw_swap")
|
|
2135
|
+
]);
|
|
2136
|
+
return results;
|
|
2137
|
+
}
|
|
1932
2138
|
|
|
1933
2139
|
// src/core/CloakSDK.ts
|
|
1934
2140
|
var CLOAK_PROGRAM_ID = new import_web35.PublicKey("c1oak6tetxYnNfvXKFkpn1d98FxtK7B68vBQLYQpWKp");
|
|
@@ -2043,14 +2249,35 @@ var CloakSDK = class {
|
|
|
2043
2249
|
async deposit(connection, amountOrNote, options) {
|
|
2044
2250
|
try {
|
|
2045
2251
|
let note;
|
|
2252
|
+
let isNewNote = false;
|
|
2046
2253
|
if (typeof amountOrNote === "number") {
|
|
2254
|
+
options?.onProgress?.("generating_note", { message: "Generating note with secrets..." });
|
|
2047
2255
|
note = await generateNote(amountOrNote, this.config.network);
|
|
2256
|
+
isNewNote = true;
|
|
2048
2257
|
} else {
|
|
2049
2258
|
note = amountOrNote;
|
|
2050
2259
|
if (note.depositSignature) {
|
|
2051
2260
|
throw new Error("Note has already been deposited");
|
|
2052
2261
|
}
|
|
2053
2262
|
}
|
|
2263
|
+
if (isNewNote) {
|
|
2264
|
+
await this.storage.saveNote(note);
|
|
2265
|
+
if (options?.onNoteGenerated) {
|
|
2266
|
+
options?.onProgress?.("awaiting_note_acknowledgment", {
|
|
2267
|
+
message: "Waiting for note to be saved before proceeding with deposit..."
|
|
2268
|
+
});
|
|
2269
|
+
try {
|
|
2270
|
+
await options.onNoteGenerated(note);
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
throw new Error(
|
|
2273
|
+
`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.`
|
|
2274
|
+
);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
options?.onProgress?.("note_saved", {
|
|
2278
|
+
message: "Note saved. Proceeding with on-chain deposit..."
|
|
2279
|
+
});
|
|
2280
|
+
}
|
|
2054
2281
|
const payerPubkey = this.getPublicKey();
|
|
2055
2282
|
const balance = await connection.getBalance(payerPubkey);
|
|
2056
2283
|
const requiredAmount = note.amount + 5e3;
|
|
@@ -2242,6 +2469,16 @@ var CloakSDK = class {
|
|
|
2242
2469
|
pathIndices: merkleProof.pathIndices
|
|
2243
2470
|
}
|
|
2244
2471
|
});
|
|
2472
|
+
await this.storage.updateNote(note.commitment, {
|
|
2473
|
+
depositSignature: signature,
|
|
2474
|
+
depositSlot,
|
|
2475
|
+
leafIndex,
|
|
2476
|
+
root,
|
|
2477
|
+
merkleProof: {
|
|
2478
|
+
pathElements: merkleProof.pathElements,
|
|
2479
|
+
pathIndices: merkleProof.pathIndices
|
|
2480
|
+
}
|
|
2481
|
+
});
|
|
2245
2482
|
return {
|
|
2246
2483
|
note: updatedNote,
|
|
2247
2484
|
signature,
|
|
@@ -2343,8 +2580,8 @@ var CloakSDK = class {
|
|
|
2343
2580
|
}
|
|
2344
2581
|
}
|
|
2345
2582
|
const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
|
|
2346
|
-
const
|
|
2347
|
-
const useDirectProof = await areCircuitsAvailable(
|
|
2583
|
+
const circuitsPath2 = isBrowser ? "/circuits" : typeof process !== "undefined" && process.env?.CIRCUITS_PATH || await getDefaultCircuitsPath();
|
|
2584
|
+
const useDirectProof = await areCircuitsAvailable(circuitsPath2);
|
|
2348
2585
|
let proofHex;
|
|
2349
2586
|
let finalPublicInputs;
|
|
2350
2587
|
if (useDirectProof) {
|
|
@@ -2406,7 +2643,7 @@ var CloakSDK = class {
|
|
|
2406
2643
|
rem
|
|
2407
2644
|
};
|
|
2408
2645
|
options?.onProgress?.("proof_generating");
|
|
2409
|
-
const proofResult = await generateWithdrawRegularProof(proofInputs,
|
|
2646
|
+
const proofResult = await generateWithdrawRegularProof(proofInputs, circuitsPath2);
|
|
2410
2647
|
options?.onProgress?.("proof_complete");
|
|
2411
2648
|
proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
|
|
2412
2649
|
finalPublicInputs = {
|
|
@@ -2417,7 +2654,7 @@ var CloakSDK = class {
|
|
|
2417
2654
|
};
|
|
2418
2655
|
} else {
|
|
2419
2656
|
throw new Error(
|
|
2420
|
-
`Circuits not available at ${
|
|
2657
|
+
`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.`
|
|
2421
2658
|
);
|
|
2422
2659
|
}
|
|
2423
2660
|
const signature = await this.relay.submitWithdraw(
|
|
@@ -2614,8 +2851,8 @@ var CloakSDK = class {
|
|
|
2614
2851
|
throw new Error("Merkle proof is invalid: missing path elements");
|
|
2615
2852
|
}
|
|
2616
2853
|
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;
|
|
2617
|
-
const
|
|
2618
|
-
const useDirectProof = await areCircuitsAvailable(
|
|
2854
|
+
const circuitsPath2 = envCircuitsPath || await getDefaultCircuitsPath();
|
|
2855
|
+
const useDirectProof = await areCircuitsAvailable(circuitsPath2);
|
|
2619
2856
|
let proofHex;
|
|
2620
2857
|
let finalPublicInputs;
|
|
2621
2858
|
if (useDirectProof) {
|
|
@@ -2658,7 +2895,7 @@ var CloakSDK = class {
|
|
|
2658
2895
|
rem
|
|
2659
2896
|
};
|
|
2660
2897
|
options?.onProgress?.("proof_generating");
|
|
2661
|
-
const proofResult = await generateWithdrawSwapProof(proofInputs,
|
|
2898
|
+
const proofResult = await generateWithdrawSwapProof(proofInputs, circuitsPath2);
|
|
2662
2899
|
options?.onProgress?.("proof_complete");
|
|
2663
2900
|
proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
|
|
2664
2901
|
finalPublicInputs = {
|
|
@@ -2669,7 +2906,7 @@ var CloakSDK = class {
|
|
|
2669
2906
|
};
|
|
2670
2907
|
} else {
|
|
2671
2908
|
throw new Error(
|
|
2672
|
-
`Circuits not available at ${
|
|
2909
|
+
`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.`
|
|
2673
2910
|
);
|
|
2674
2911
|
}
|
|
2675
2912
|
const signature = await this.relay.submitSwap(
|
|
@@ -3044,6 +3281,63 @@ async function copyNoteToClipboard(note) {
|
|
|
3044
3281
|
}
|
|
3045
3282
|
|
|
3046
3283
|
// src/utils/errors.ts
|
|
3284
|
+
var ShieldPoolErrors = {
|
|
3285
|
+
// Root management errors
|
|
3286
|
+
4096: "Invalid Merkle root",
|
|
3287
|
+
4097: "Root not found in the roots ring",
|
|
3288
|
+
4098: "Roots ring is full",
|
|
3289
|
+
// Proof verification errors
|
|
3290
|
+
4112: "Zero-knowledge proof is invalid",
|
|
3291
|
+
4113: "Invalid proof size (expected 260 bytes)",
|
|
3292
|
+
4114: "Invalid public inputs",
|
|
3293
|
+
4115: "Verification key mismatch",
|
|
3294
|
+
// Nullifier errors
|
|
3295
|
+
4128: "Double spend detected - this note has already been spent",
|
|
3296
|
+
4129: "Nullifier shard is full",
|
|
3297
|
+
4130: "Invalid nullifier",
|
|
3298
|
+
// Transaction validation errors
|
|
3299
|
+
4144: "Output addresses or amounts don't match the proof",
|
|
3300
|
+
4145: "Amount conservation failed - outputs + fee must equal input amount",
|
|
3301
|
+
4146: "Invalid outputs hash",
|
|
3302
|
+
4147: "Invalid amount (must be greater than zero)",
|
|
3303
|
+
4148: "Invalid recipient address",
|
|
3304
|
+
4149: "Commitment already exists in the tree",
|
|
3305
|
+
4150: "Commitment log is full",
|
|
3306
|
+
// Math errors
|
|
3307
|
+
4160: "Math overflow occurred",
|
|
3308
|
+
4161: "Division by zero",
|
|
3309
|
+
// Account errors
|
|
3310
|
+
4176: "Account validation failed - please check your wallet balance and try again",
|
|
3311
|
+
4177: "Pool account owner mismatch",
|
|
3312
|
+
4178: "Treasury account owner mismatch",
|
|
3313
|
+
4179: "Roots ring account owner mismatch",
|
|
3314
|
+
4180: "Nullifier shard account owner mismatch",
|
|
3315
|
+
4181: "Pool account is not writable",
|
|
3316
|
+
4182: "Treasury account is not writable",
|
|
3317
|
+
4183: "Recipient account is not writable",
|
|
3318
|
+
4184: "Insufficient lamports in pool or account",
|
|
3319
|
+
4185: "Invalid account owner",
|
|
3320
|
+
4186: "Invalid account size",
|
|
3321
|
+
4187: "Commitments account is not writable",
|
|
3322
|
+
4188: "Invalid admin authority",
|
|
3323
|
+
// Instruction errors
|
|
3324
|
+
4192: "Invalid instruction data length",
|
|
3325
|
+
4193: "Invalid instruction data format",
|
|
3326
|
+
4194: "Missing required accounts",
|
|
3327
|
+
4195: "Invalid instruction tag",
|
|
3328
|
+
// PoW/Scrambler errors
|
|
3329
|
+
4196: "Invalid miner account",
|
|
3330
|
+
4197: "Invalid claim account",
|
|
3331
|
+
4198: "Failed to consume claim",
|
|
3332
|
+
// Groth16 verifier errors
|
|
3333
|
+
4208: "Invalid G1 point length",
|
|
3334
|
+
4209: "Invalid G2 point length",
|
|
3335
|
+
4210: "Invalid public inputs length",
|
|
3336
|
+
4211: "Public input exceeds field size",
|
|
3337
|
+
4212: "G1 multiplication failed during proof preparation",
|
|
3338
|
+
4213: "G1 addition failed during proof preparation",
|
|
3339
|
+
4214: "Proof verification failed"
|
|
3340
|
+
};
|
|
3047
3341
|
var PROGRAM_ERRORS = {
|
|
3048
3342
|
// Nullifier errors
|
|
3049
3343
|
"NullifierAlreadyUsed": "This note has already been withdrawn. Each note can only be spent once.",
|
|
@@ -3072,6 +3366,438 @@ var PROGRAM_ERRORS = {
|
|
|
3072
3366
|
"AccountNotFound": "Required account not found.",
|
|
3073
3367
|
"InvalidInstruction": "Invalid instruction data."
|
|
3074
3368
|
};
|
|
3369
|
+
var ErrorPatterns = [
|
|
3370
|
+
// Wallet/User action errors
|
|
3371
|
+
{
|
|
3372
|
+
patterns: [
|
|
3373
|
+
"User rejected",
|
|
3374
|
+
"user rejected",
|
|
3375
|
+
"User denied",
|
|
3376
|
+
"user denied",
|
|
3377
|
+
"Transaction cancelled",
|
|
3378
|
+
"Transaction rejected",
|
|
3379
|
+
/code.*4001/i
|
|
3380
|
+
],
|
|
3381
|
+
result: {
|
|
3382
|
+
title: "Transaction Cancelled",
|
|
3383
|
+
message: "You cancelled the transaction.",
|
|
3384
|
+
category: "wallet",
|
|
3385
|
+
recoverable: true
|
|
3386
|
+
}
|
|
3387
|
+
},
|
|
3388
|
+
{
|
|
3389
|
+
patterns: [
|
|
3390
|
+
"Wallet not connected",
|
|
3391
|
+
"wallet not connected",
|
|
3392
|
+
"Please connect wallet",
|
|
3393
|
+
"No wallet connected"
|
|
3394
|
+
],
|
|
3395
|
+
result: {
|
|
3396
|
+
title: "Wallet Not Connected",
|
|
3397
|
+
message: "Please connect your wallet to continue.",
|
|
3398
|
+
category: "wallet",
|
|
3399
|
+
suggestion: "Click the wallet button to connect.",
|
|
3400
|
+
recoverable: true
|
|
3401
|
+
}
|
|
3402
|
+
},
|
|
3403
|
+
{
|
|
3404
|
+
patterns: [
|
|
3405
|
+
"SDK not initialized",
|
|
3406
|
+
"sdk not initialized"
|
|
3407
|
+
],
|
|
3408
|
+
result: {
|
|
3409
|
+
title: "Wallet Connection Required",
|
|
3410
|
+
message: "Your wallet connection was interrupted.",
|
|
3411
|
+
category: "wallet",
|
|
3412
|
+
suggestion: "Please reconnect your wallet and try again.",
|
|
3413
|
+
recoverable: true
|
|
3414
|
+
}
|
|
3415
|
+
},
|
|
3416
|
+
{
|
|
3417
|
+
patterns: [
|
|
3418
|
+
"Wallet does not support signing",
|
|
3419
|
+
"signTransaction"
|
|
3420
|
+
],
|
|
3421
|
+
result: {
|
|
3422
|
+
title: "Wallet Feature Not Supported",
|
|
3423
|
+
message: "Your wallet doesn't support the required signing method.",
|
|
3424
|
+
category: "wallet",
|
|
3425
|
+
suggestion: "Try using a different wallet like Phantom or Solflare.",
|
|
3426
|
+
recoverable: true
|
|
3427
|
+
}
|
|
3428
|
+
},
|
|
3429
|
+
// Balance errors
|
|
3430
|
+
{
|
|
3431
|
+
patterns: [
|
|
3432
|
+
"insufficient lamports",
|
|
3433
|
+
"Insufficient lamports",
|
|
3434
|
+
"insufficient balance",
|
|
3435
|
+
"Insufficient balance",
|
|
3436
|
+
"Insufficient SOL",
|
|
3437
|
+
"not enough SOL",
|
|
3438
|
+
"Attempt to debit an account but found no record",
|
|
3439
|
+
/0x1$/
|
|
3440
|
+
],
|
|
3441
|
+
result: {
|
|
3442
|
+
title: "Insufficient Balance",
|
|
3443
|
+
message: "You don't have enough SOL for this transaction.",
|
|
3444
|
+
category: "validation",
|
|
3445
|
+
suggestion: "Add more SOL to your wallet or reduce the amount.",
|
|
3446
|
+
recoverable: true
|
|
3447
|
+
}
|
|
3448
|
+
},
|
|
3449
|
+
{
|
|
3450
|
+
patterns: [
|
|
3451
|
+
"Insufficient funds",
|
|
3452
|
+
"insufficient funds"
|
|
3453
|
+
],
|
|
3454
|
+
result: {
|
|
3455
|
+
title: "Insufficient Funds",
|
|
3456
|
+
message: "Your wallet doesn't have enough funds for this transaction.",
|
|
3457
|
+
category: "validation",
|
|
3458
|
+
suggestion: "Add more funds to your wallet and try again.",
|
|
3459
|
+
recoverable: true
|
|
3460
|
+
}
|
|
3461
|
+
},
|
|
3462
|
+
// Network errors
|
|
3463
|
+
{
|
|
3464
|
+
patterns: [
|
|
3465
|
+
"fetch failed",
|
|
3466
|
+
"Failed to fetch",
|
|
3467
|
+
"Network error",
|
|
3468
|
+
"network error",
|
|
3469
|
+
"ECONNREFUSED",
|
|
3470
|
+
"ETIMEDOUT",
|
|
3471
|
+
"ENOTFOUND",
|
|
3472
|
+
"NetworkError",
|
|
3473
|
+
"net::ERR",
|
|
3474
|
+
"Failed to load"
|
|
3475
|
+
],
|
|
3476
|
+
result: {
|
|
3477
|
+
title: "Connection Error",
|
|
3478
|
+
message: "Unable to connect to the network.",
|
|
3479
|
+
category: "network",
|
|
3480
|
+
suggestion: "Check your internet connection and try again.",
|
|
3481
|
+
recoverable: true
|
|
3482
|
+
}
|
|
3483
|
+
},
|
|
3484
|
+
{
|
|
3485
|
+
patterns: [
|
|
3486
|
+
"timeout",
|
|
3487
|
+
"Timeout",
|
|
3488
|
+
"TIMEOUT",
|
|
3489
|
+
"timed out",
|
|
3490
|
+
"Timed out"
|
|
3491
|
+
],
|
|
3492
|
+
result: {
|
|
3493
|
+
title: "Request Timed Out",
|
|
3494
|
+
message: "The request took too long to complete.",
|
|
3495
|
+
category: "network",
|
|
3496
|
+
suggestion: "The network may be congested. Please try again in a moment.",
|
|
3497
|
+
recoverable: true
|
|
3498
|
+
}
|
|
3499
|
+
},
|
|
3500
|
+
{
|
|
3501
|
+
patterns: [
|
|
3502
|
+
"blockhash not found",
|
|
3503
|
+
"Blockhash not found",
|
|
3504
|
+
"block height exceeded"
|
|
3505
|
+
],
|
|
3506
|
+
result: {
|
|
3507
|
+
title: "Transaction Expired",
|
|
3508
|
+
message: "The transaction took too long and expired.",
|
|
3509
|
+
category: "network",
|
|
3510
|
+
suggestion: "Please try again. If this persists, the network may be congested.",
|
|
3511
|
+
recoverable: true
|
|
3512
|
+
}
|
|
3513
|
+
},
|
|
3514
|
+
// Service errors (Indexer/Relay)
|
|
3515
|
+
{
|
|
3516
|
+
patterns: [
|
|
3517
|
+
"Indexer",
|
|
3518
|
+
"indexer",
|
|
3519
|
+
/indexer.*unavailable/i,
|
|
3520
|
+
/failed.*indexer/i
|
|
3521
|
+
],
|
|
3522
|
+
result: {
|
|
3523
|
+
title: "Service Temporarily Unavailable",
|
|
3524
|
+
message: "The privacy service is temporarily unavailable.",
|
|
3525
|
+
category: "service",
|
|
3526
|
+
suggestion: "Please wait a moment and try again.",
|
|
3527
|
+
recoverable: true
|
|
3528
|
+
}
|
|
3529
|
+
},
|
|
3530
|
+
{
|
|
3531
|
+
patterns: [
|
|
3532
|
+
"Relay",
|
|
3533
|
+
"relay",
|
|
3534
|
+
/relay.*unavailable/i,
|
|
3535
|
+
/failed.*relay/i,
|
|
3536
|
+
"failed to submit",
|
|
3537
|
+
"Failed to submit"
|
|
3538
|
+
],
|
|
3539
|
+
result: {
|
|
3540
|
+
title: "Processing Service Busy",
|
|
3541
|
+
message: "The transaction processing service is busy.",
|
|
3542
|
+
category: "service",
|
|
3543
|
+
suggestion: "Please wait a moment and try again.",
|
|
3544
|
+
recoverable: true
|
|
3545
|
+
}
|
|
3546
|
+
},
|
|
3547
|
+
{
|
|
3548
|
+
patterns: [
|
|
3549
|
+
"429",
|
|
3550
|
+
"Too Many Requests",
|
|
3551
|
+
"rate limit",
|
|
3552
|
+
"Rate limit"
|
|
3553
|
+
],
|
|
3554
|
+
result: {
|
|
3555
|
+
title: "Too Many Requests",
|
|
3556
|
+
message: "You're making requests too quickly.",
|
|
3557
|
+
category: "service",
|
|
3558
|
+
suggestion: "Please wait a moment before trying again.",
|
|
3559
|
+
recoverable: true
|
|
3560
|
+
}
|
|
3561
|
+
},
|
|
3562
|
+
{
|
|
3563
|
+
patterns: [
|
|
3564
|
+
"503",
|
|
3565
|
+
"Service Unavailable",
|
|
3566
|
+
"502",
|
|
3567
|
+
"Bad Gateway"
|
|
3568
|
+
],
|
|
3569
|
+
result: {
|
|
3570
|
+
title: "Service Temporarily Unavailable",
|
|
3571
|
+
message: "Our servers are temporarily unavailable.",
|
|
3572
|
+
category: "service",
|
|
3573
|
+
suggestion: "Please try again in a few minutes.",
|
|
3574
|
+
recoverable: true
|
|
3575
|
+
}
|
|
3576
|
+
},
|
|
3577
|
+
// Proof/ZK errors
|
|
3578
|
+
{
|
|
3579
|
+
patterns: [
|
|
3580
|
+
"Circuits not available",
|
|
3581
|
+
"circuits not available",
|
|
3582
|
+
"Failed to load circuit",
|
|
3583
|
+
"WASM",
|
|
3584
|
+
"wasm",
|
|
3585
|
+
/circuit.*not.*found/i
|
|
3586
|
+
],
|
|
3587
|
+
result: {
|
|
3588
|
+
title: "Loading Error",
|
|
3589
|
+
message: "Failed to load required cryptographic components.",
|
|
3590
|
+
category: "service",
|
|
3591
|
+
suggestion: "Try refreshing the page. If the problem persists, clear your browser cache.",
|
|
3592
|
+
recoverable: true
|
|
3593
|
+
}
|
|
3594
|
+
},
|
|
3595
|
+
{
|
|
3596
|
+
patterns: [
|
|
3597
|
+
"proof generation",
|
|
3598
|
+
"Proof generation",
|
|
3599
|
+
"Failed to generate proof",
|
|
3600
|
+
"proving error"
|
|
3601
|
+
],
|
|
3602
|
+
result: {
|
|
3603
|
+
title: "Proof Generation Failed",
|
|
3604
|
+
message: "Failed to generate the privacy proof.",
|
|
3605
|
+
category: "service",
|
|
3606
|
+
suggestion: "Try again with a smaller amount or refresh the page.",
|
|
3607
|
+
recoverable: true
|
|
3608
|
+
}
|
|
3609
|
+
},
|
|
3610
|
+
// Validation errors
|
|
3611
|
+
{
|
|
3612
|
+
patterns: [
|
|
3613
|
+
"Invalid amount",
|
|
3614
|
+
"invalid amount",
|
|
3615
|
+
"Amount must be",
|
|
3616
|
+
"amount must be",
|
|
3617
|
+
"Amount too small"
|
|
3618
|
+
],
|
|
3619
|
+
result: {
|
|
3620
|
+
title: "Invalid Amount",
|
|
3621
|
+
message: "The amount you entered is not valid.",
|
|
3622
|
+
category: "validation",
|
|
3623
|
+
suggestion: "Enter an amount greater than the minimum required.",
|
|
3624
|
+
recoverable: true
|
|
3625
|
+
}
|
|
3626
|
+
},
|
|
3627
|
+
{
|
|
3628
|
+
patterns: [
|
|
3629
|
+
"Invalid recipient",
|
|
3630
|
+
"invalid recipient",
|
|
3631
|
+
"Invalid address",
|
|
3632
|
+
"invalid address",
|
|
3633
|
+
"Invalid Solana address"
|
|
3634
|
+
],
|
|
3635
|
+
result: {
|
|
3636
|
+
title: "Invalid Address",
|
|
3637
|
+
message: "The recipient address is not valid.",
|
|
3638
|
+
category: "validation",
|
|
3639
|
+
suggestion: "Check the address and make sure it's a valid Solana address.",
|
|
3640
|
+
recoverable: true
|
|
3641
|
+
}
|
|
3642
|
+
},
|
|
3643
|
+
{
|
|
3644
|
+
patterns: [
|
|
3645
|
+
"Invalid token",
|
|
3646
|
+
"invalid token",
|
|
3647
|
+
"Unsupported token"
|
|
3648
|
+
],
|
|
3649
|
+
result: {
|
|
3650
|
+
title: "Unsupported Token",
|
|
3651
|
+
message: "This token is not supported.",
|
|
3652
|
+
category: "validation",
|
|
3653
|
+
suggestion: "Select a supported token and try again.",
|
|
3654
|
+
recoverable: true
|
|
3655
|
+
}
|
|
3656
|
+
},
|
|
3657
|
+
// Swap errors
|
|
3658
|
+
{
|
|
3659
|
+
patterns: [
|
|
3660
|
+
"Failed to get quote",
|
|
3661
|
+
"failed to get quote",
|
|
3662
|
+
"No route found",
|
|
3663
|
+
"no route found",
|
|
3664
|
+
"Insufficient liquidity",
|
|
3665
|
+
"insufficient liquidity"
|
|
3666
|
+
],
|
|
3667
|
+
result: {
|
|
3668
|
+
title: "Swap Quote Unavailable",
|
|
3669
|
+
message: "Unable to get a price quote for this swap.",
|
|
3670
|
+
category: "service",
|
|
3671
|
+
suggestion: "Try a different amount or wait for better liquidity.",
|
|
3672
|
+
recoverable: true
|
|
3673
|
+
}
|
|
3674
|
+
},
|
|
3675
|
+
{
|
|
3676
|
+
patterns: [
|
|
3677
|
+
"Slippage",
|
|
3678
|
+
"slippage",
|
|
3679
|
+
"Price impact",
|
|
3680
|
+
"price impact"
|
|
3681
|
+
],
|
|
3682
|
+
result: {
|
|
3683
|
+
title: "Price Changed",
|
|
3684
|
+
message: "The price changed too much during the transaction.",
|
|
3685
|
+
category: "transaction",
|
|
3686
|
+
suggestion: "Try again or increase your slippage tolerance.",
|
|
3687
|
+
recoverable: true
|
|
3688
|
+
}
|
|
3689
|
+
},
|
|
3690
|
+
// Transaction errors
|
|
3691
|
+
{
|
|
3692
|
+
patterns: [
|
|
3693
|
+
"Double spend",
|
|
3694
|
+
"double spend",
|
|
3695
|
+
"already been spent",
|
|
3696
|
+
"already spent"
|
|
3697
|
+
],
|
|
3698
|
+
result: {
|
|
3699
|
+
title: "Already Spent",
|
|
3700
|
+
message: "This note has already been used.",
|
|
3701
|
+
category: "transaction",
|
|
3702
|
+
suggestion: "This funds have already been withdrawn.",
|
|
3703
|
+
recoverable: false
|
|
3704
|
+
}
|
|
3705
|
+
},
|
|
3706
|
+
{
|
|
3707
|
+
patterns: [
|
|
3708
|
+
"simulation failed",
|
|
3709
|
+
"Simulation failed",
|
|
3710
|
+
"Transaction simulation"
|
|
3711
|
+
],
|
|
3712
|
+
result: {
|
|
3713
|
+
title: "Transaction Failed",
|
|
3714
|
+
message: "The transaction could not be completed.",
|
|
3715
|
+
category: "transaction",
|
|
3716
|
+
suggestion: "Please try again. If the problem persists, check your balance.",
|
|
3717
|
+
recoverable: true
|
|
3718
|
+
}
|
|
3719
|
+
},
|
|
3720
|
+
{
|
|
3721
|
+
patterns: [
|
|
3722
|
+
"confirmation timeout",
|
|
3723
|
+
"Confirmation timeout",
|
|
3724
|
+
"not confirmed"
|
|
3725
|
+
],
|
|
3726
|
+
result: {
|
|
3727
|
+
title: "Confirmation Pending",
|
|
3728
|
+
message: "Transaction confirmation is taking longer than expected.",
|
|
3729
|
+
category: "network",
|
|
3730
|
+
suggestion: "Your transaction may still complete. Check your wallet or try again.",
|
|
3731
|
+
recoverable: true
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3734
|
+
];
|
|
3735
|
+
function parseError(error) {
|
|
3736
|
+
let errorMessage = "";
|
|
3737
|
+
let originalError = "";
|
|
3738
|
+
if (error instanceof Error) {
|
|
3739
|
+
errorMessage = error.message;
|
|
3740
|
+
originalError = error.stack || error.message;
|
|
3741
|
+
} else if (typeof error === "string") {
|
|
3742
|
+
errorMessage = error;
|
|
3743
|
+
originalError = error;
|
|
3744
|
+
} else if (error && typeof error === "object") {
|
|
3745
|
+
const err = error;
|
|
3746
|
+
errorMessage = String(err.message || err.error || err.msg || JSON.stringify(error));
|
|
3747
|
+
originalError = JSON.stringify(error);
|
|
3748
|
+
} else {
|
|
3749
|
+
errorMessage = String(error);
|
|
3750
|
+
originalError = String(error);
|
|
3751
|
+
}
|
|
3752
|
+
for (const { patterns, result } of ErrorPatterns) {
|
|
3753
|
+
for (const pattern of patterns) {
|
|
3754
|
+
if (typeof pattern === "string") {
|
|
3755
|
+
if (errorMessage.includes(pattern)) {
|
|
3756
|
+
return { ...result, originalError };
|
|
3757
|
+
}
|
|
3758
|
+
} else if (pattern instanceof RegExp) {
|
|
3759
|
+
if (pattern.test(errorMessage)) {
|
|
3760
|
+
return { ...result, originalError };
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
const programError = tryParseProgramError(errorMessage);
|
|
3766
|
+
if (programError) {
|
|
3767
|
+
return { ...programError, originalError };
|
|
3768
|
+
}
|
|
3769
|
+
return {
|
|
3770
|
+
title: "Something Went Wrong",
|
|
3771
|
+
message: "An unexpected error occurred.",
|
|
3772
|
+
category: "unknown",
|
|
3773
|
+
suggestion: "Please try again. If the problem persists, refresh the page.",
|
|
3774
|
+
recoverable: true,
|
|
3775
|
+
originalError
|
|
3776
|
+
};
|
|
3777
|
+
}
|
|
3778
|
+
function tryParseProgramError(message) {
|
|
3779
|
+
const match = message.match(/\{"InstructionError":\[(\d+),\{"Custom":(\d+)\}\]\}/);
|
|
3780
|
+
if (match) {
|
|
3781
|
+
const errorCode = parseInt(match[2]);
|
|
3782
|
+
const friendlyMessage = ShieldPoolErrors[errorCode];
|
|
3783
|
+
if (friendlyMessage) {
|
|
3784
|
+
return {
|
|
3785
|
+
title: "Transaction Failed",
|
|
3786
|
+
message: friendlyMessage,
|
|
3787
|
+
category: "transaction",
|
|
3788
|
+
recoverable: !friendlyMessage.toLowerCase().includes("double spend")
|
|
3789
|
+
};
|
|
3790
|
+
}
|
|
3791
|
+
return {
|
|
3792
|
+
title: "Transaction Failed",
|
|
3793
|
+
message: `Transaction failed with error code ${errorCode}.`,
|
|
3794
|
+
category: "transaction",
|
|
3795
|
+
suggestion: "Please try again or contact support if this persists.",
|
|
3796
|
+
recoverable: true
|
|
3797
|
+
};
|
|
3798
|
+
}
|
|
3799
|
+
return null;
|
|
3800
|
+
}
|
|
3075
3801
|
function parseTransactionError(error) {
|
|
3076
3802
|
if (!error) return "An unknown error occurred";
|
|
3077
3803
|
const errorStr = typeof error === "string" ? error : error.message || error.toString();
|
|
@@ -3251,6 +3977,173 @@ function keypairToAdapter(keypair) {
|
|
|
3251
3977
|
};
|
|
3252
3978
|
}
|
|
3253
3979
|
|
|
3980
|
+
// src/utils/pending-operations.ts
|
|
3981
|
+
var PENDING_DEPOSITS_KEY = "cloak_pending_deposits";
|
|
3982
|
+
var PENDING_WITHDRAWALS_KEY = "cloak_pending_withdrawals";
|
|
3983
|
+
function getStorage() {
|
|
3984
|
+
if (typeof globalThis !== "undefined" && globalThis.localStorage) {
|
|
3985
|
+
return globalThis.localStorage;
|
|
3986
|
+
}
|
|
3987
|
+
return null;
|
|
3988
|
+
}
|
|
3989
|
+
function savePendingDeposit(deposit) {
|
|
3990
|
+
const storage = getStorage();
|
|
3991
|
+
if (!storage) {
|
|
3992
|
+
console.warn("localStorage not available - pending deposit not persisted");
|
|
3993
|
+
return;
|
|
3994
|
+
}
|
|
3995
|
+
const deposits = loadPendingDeposits();
|
|
3996
|
+
const index = deposits.findIndex((d) => d.note.commitment === deposit.note.commitment);
|
|
3997
|
+
if (index >= 0) {
|
|
3998
|
+
deposits[index] = deposit;
|
|
3999
|
+
} else {
|
|
4000
|
+
deposits.push(deposit);
|
|
4001
|
+
}
|
|
4002
|
+
storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
|
|
4003
|
+
}
|
|
4004
|
+
function loadPendingDeposits() {
|
|
4005
|
+
const storage = getStorage();
|
|
4006
|
+
if (!storage) return [];
|
|
4007
|
+
const stored = storage.getItem(PENDING_DEPOSITS_KEY);
|
|
4008
|
+
if (!stored) return [];
|
|
4009
|
+
try {
|
|
4010
|
+
return JSON.parse(stored);
|
|
4011
|
+
} catch {
|
|
4012
|
+
return [];
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
function updatePendingDeposit(commitment, updates) {
|
|
4016
|
+
const storage = getStorage();
|
|
4017
|
+
if (!storage) return;
|
|
4018
|
+
const deposits = loadPendingDeposits();
|
|
4019
|
+
const index = deposits.findIndex((d) => d.note.commitment === commitment);
|
|
4020
|
+
if (index >= 0) {
|
|
4021
|
+
deposits[index] = { ...deposits[index], ...updates };
|
|
4022
|
+
storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
function removePendingDeposit(commitment) {
|
|
4026
|
+
const storage = getStorage();
|
|
4027
|
+
if (!storage) return;
|
|
4028
|
+
const deposits = loadPendingDeposits();
|
|
4029
|
+
const filtered = deposits.filter((d) => d.note.commitment !== commitment);
|
|
4030
|
+
storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(filtered));
|
|
4031
|
+
}
|
|
4032
|
+
function clearPendingDeposits() {
|
|
4033
|
+
const storage = getStorage();
|
|
4034
|
+
if (storage) {
|
|
4035
|
+
storage.removeItem(PENDING_DEPOSITS_KEY);
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
function savePendingWithdrawal(withdrawal) {
|
|
4039
|
+
const storage = getStorage();
|
|
4040
|
+
if (!storage) {
|
|
4041
|
+
console.warn("localStorage not available - pending withdrawal not persisted");
|
|
4042
|
+
return;
|
|
4043
|
+
}
|
|
4044
|
+
const withdrawals = loadPendingWithdrawals();
|
|
4045
|
+
const index = withdrawals.findIndex((w) => w.requestId === withdrawal.requestId);
|
|
4046
|
+
if (index >= 0) {
|
|
4047
|
+
withdrawals[index] = withdrawal;
|
|
4048
|
+
} else {
|
|
4049
|
+
withdrawals.push(withdrawal);
|
|
4050
|
+
}
|
|
4051
|
+
storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
|
|
4052
|
+
}
|
|
4053
|
+
function loadPendingWithdrawals() {
|
|
4054
|
+
const storage = getStorage();
|
|
4055
|
+
if (!storage) return [];
|
|
4056
|
+
const stored = storage.getItem(PENDING_WITHDRAWALS_KEY);
|
|
4057
|
+
if (!stored) return [];
|
|
4058
|
+
try {
|
|
4059
|
+
return JSON.parse(stored);
|
|
4060
|
+
} catch {
|
|
4061
|
+
return [];
|
|
4062
|
+
}
|
|
4063
|
+
}
|
|
4064
|
+
function updatePendingWithdrawal(requestId, updates) {
|
|
4065
|
+
const storage = getStorage();
|
|
4066
|
+
if (!storage) return;
|
|
4067
|
+
const withdrawals = loadPendingWithdrawals();
|
|
4068
|
+
const index = withdrawals.findIndex((w) => w.requestId === requestId);
|
|
4069
|
+
if (index >= 0) {
|
|
4070
|
+
withdrawals[index] = { ...withdrawals[index], ...updates };
|
|
4071
|
+
storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
function removePendingWithdrawal(requestId) {
|
|
4075
|
+
const storage = getStorage();
|
|
4076
|
+
if (!storage) return;
|
|
4077
|
+
const withdrawals = loadPendingWithdrawals();
|
|
4078
|
+
const filtered = withdrawals.filter((w) => w.requestId !== requestId);
|
|
4079
|
+
storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(filtered));
|
|
4080
|
+
}
|
|
4081
|
+
function clearPendingWithdrawals() {
|
|
4082
|
+
const storage = getStorage();
|
|
4083
|
+
if (storage) {
|
|
4084
|
+
storage.removeItem(PENDING_WITHDRAWALS_KEY);
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
function hasPendingOperations() {
|
|
4088
|
+
const deposits = loadPendingDeposits();
|
|
4089
|
+
const withdrawals = loadPendingWithdrawals();
|
|
4090
|
+
const pendingDeposits = deposits.filter(
|
|
4091
|
+
(d) => d.status === "pending" || d.status === "tx_sent"
|
|
4092
|
+
);
|
|
4093
|
+
const pendingWithdrawals = withdrawals.filter(
|
|
4094
|
+
(w) => w.status === "pending" || w.status === "processing"
|
|
4095
|
+
);
|
|
4096
|
+
return pendingDeposits.length > 0 || pendingWithdrawals.length > 0;
|
|
4097
|
+
}
|
|
4098
|
+
function getPendingOperationsSummary() {
|
|
4099
|
+
const deposits = loadPendingDeposits().filter(
|
|
4100
|
+
(d) => d.status === "pending" || d.status === "tx_sent"
|
|
4101
|
+
);
|
|
4102
|
+
const withdrawals = loadPendingWithdrawals().filter(
|
|
4103
|
+
(w) => w.status === "pending" || w.status === "processing"
|
|
4104
|
+
);
|
|
4105
|
+
return {
|
|
4106
|
+
deposits,
|
|
4107
|
+
withdrawals,
|
|
4108
|
+
totalPending: deposits.length + withdrawals.length
|
|
4109
|
+
};
|
|
4110
|
+
}
|
|
4111
|
+
function cleanupStalePendingOperations(maxAgeMs = 24 * 60 * 60 * 1e3) {
|
|
4112
|
+
const now = Date.now();
|
|
4113
|
+
let removedDeposits = 0;
|
|
4114
|
+
let removedWithdrawals = 0;
|
|
4115
|
+
const deposits = loadPendingDeposits();
|
|
4116
|
+
const activeDeposits = deposits.filter((d) => {
|
|
4117
|
+
const age = now - d.startedAt;
|
|
4118
|
+
const isStale = age > maxAgeMs;
|
|
4119
|
+
const isTerminal = d.status === "confirmed" || d.status === "failed";
|
|
4120
|
+
if (isStale || isTerminal) {
|
|
4121
|
+
removedDeposits++;
|
|
4122
|
+
return false;
|
|
4123
|
+
}
|
|
4124
|
+
return true;
|
|
4125
|
+
});
|
|
4126
|
+
const storage = getStorage();
|
|
4127
|
+
if (storage) {
|
|
4128
|
+
storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(activeDeposits));
|
|
4129
|
+
}
|
|
4130
|
+
const withdrawals = loadPendingWithdrawals();
|
|
4131
|
+
const activeWithdrawals = withdrawals.filter((w) => {
|
|
4132
|
+
const age = now - w.startedAt;
|
|
4133
|
+
const isStale = age > maxAgeMs;
|
|
4134
|
+
const isTerminal = w.status === "completed" || w.status === "failed";
|
|
4135
|
+
if (isStale || isTerminal) {
|
|
4136
|
+
removedWithdrawals++;
|
|
4137
|
+
return false;
|
|
4138
|
+
}
|
|
4139
|
+
return true;
|
|
4140
|
+
});
|
|
4141
|
+
if (storage) {
|
|
4142
|
+
storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(activeWithdrawals));
|
|
4143
|
+
}
|
|
4144
|
+
return { removedDeposits, removedWithdrawals };
|
|
4145
|
+
}
|
|
4146
|
+
|
|
3254
4147
|
// src/index.ts
|
|
3255
4148
|
var VERSION = "1.0.0";
|
|
3256
4149
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -3259,12 +4152,14 @@ var VERSION = "1.0.0";
|
|
|
3259
4152
|
CloakError,
|
|
3260
4153
|
CloakSDK,
|
|
3261
4154
|
DepositRecoveryService,
|
|
4155
|
+
EXPECTED_CIRCUIT_HASHES,
|
|
3262
4156
|
FIXED_FEE_LAMPORTS,
|
|
3263
4157
|
IndexerService,
|
|
3264
4158
|
LAMPORTS_PER_SOL,
|
|
3265
4159
|
LocalStorageAdapter,
|
|
3266
4160
|
MemoryStorageAdapter,
|
|
3267
4161
|
RelayService,
|
|
4162
|
+
ShieldPoolErrors,
|
|
3268
4163
|
VARIABLE_FEE_RATE,
|
|
3269
4164
|
VERSION,
|
|
3270
4165
|
bigintToBytes32,
|
|
@@ -3272,6 +4167,9 @@ var VERSION = "1.0.0";
|
|
|
3272
4167
|
bytesToHex,
|
|
3273
4168
|
calculateFee,
|
|
3274
4169
|
calculateRelayFee,
|
|
4170
|
+
cleanupStalePendingOperations,
|
|
4171
|
+
clearPendingDeposits,
|
|
4172
|
+
clearPendingWithdrawals,
|
|
3275
4173
|
computeCommitment,
|
|
3276
4174
|
computeMerkleRoot,
|
|
3277
4175
|
computeNullifier,
|
|
@@ -3311,12 +4209,14 @@ var VERSION = "1.0.0";
|
|
|
3311
4209
|
getAddressExplorerUrl,
|
|
3312
4210
|
getDistributableAmount,
|
|
3313
4211
|
getExplorerUrl,
|
|
4212
|
+
getPendingOperationsSummary,
|
|
3314
4213
|
getPublicKey,
|
|
3315
4214
|
getPublicViewKey,
|
|
3316
4215
|
getRecipientAmount,
|
|
3317
4216
|
getRpcUrlForNetwork,
|
|
3318
4217
|
getShieldPoolPDAs,
|
|
3319
4218
|
getViewKey,
|
|
4219
|
+
hasPendingOperations,
|
|
3320
4220
|
hexToBigint,
|
|
3321
4221
|
hexToBytes,
|
|
3322
4222
|
importKeys,
|
|
@@ -3326,7 +4226,10 @@ var VERSION = "1.0.0";
|
|
|
3326
4226
|
isValidSolanaAddress,
|
|
3327
4227
|
isWithdrawable,
|
|
3328
4228
|
keypairToAdapter,
|
|
4229
|
+
loadPendingDeposits,
|
|
4230
|
+
loadPendingWithdrawals,
|
|
3329
4231
|
parseAmount,
|
|
4232
|
+
parseError,
|
|
3330
4233
|
parseNote,
|
|
3331
4234
|
parseTransactionError,
|
|
3332
4235
|
poseidonHash,
|
|
@@ -3335,6 +4238,10 @@ var VERSION = "1.0.0";
|
|
|
3335
4238
|
proofToBytes,
|
|
3336
4239
|
pubkeyToLimbs,
|
|
3337
4240
|
randomBytes,
|
|
4241
|
+
removePendingDeposit,
|
|
4242
|
+
removePendingWithdrawal,
|
|
4243
|
+
savePendingDeposit,
|
|
4244
|
+
savePendingWithdrawal,
|
|
3338
4245
|
scanNotesForWallet,
|
|
3339
4246
|
sendTransaction,
|
|
3340
4247
|
serializeNote,
|
|
@@ -3342,10 +4249,14 @@ var VERSION = "1.0.0";
|
|
|
3342
4249
|
splitTo2Limbs,
|
|
3343
4250
|
tryDecryptNote,
|
|
3344
4251
|
updateNoteWithDeposit,
|
|
4252
|
+
updatePendingDeposit,
|
|
4253
|
+
updatePendingWithdrawal,
|
|
3345
4254
|
validateDepositParams,
|
|
3346
4255
|
validateNote,
|
|
3347
4256
|
validateOutputsSum,
|
|
3348
4257
|
validateTransfers,
|
|
3349
4258
|
validateWalletConnected,
|
|
3350
|
-
validateWithdrawableNote
|
|
4259
|
+
validateWithdrawableNote,
|
|
4260
|
+
verifyAllCircuits,
|
|
4261
|
+
verifyCircuitIntegrity
|
|
3351
4262
|
});
|