@cloak.dev/sdk 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +173 -0
- package/README.md +183 -96
- package/dist/index.cjs +296 -2256
- package/dist/index.d.cts +443 -1512
- package/dist/index.d.ts +443 -1512
- package/dist/index.js +252 -2161
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -36,8 +36,8 @@ __export(utxo_exports, {
|
|
|
36
36
|
NATIVE_SOL_MINT: () => NATIVE_SOL_MINT,
|
|
37
37
|
bigintToBytes32: () => bigintToBytes322,
|
|
38
38
|
bigintToHex: () => bigintToHex,
|
|
39
|
-
computeCommitment: () =>
|
|
40
|
-
computeNullifier: () =>
|
|
39
|
+
computeCommitment: () => computeCommitment,
|
|
40
|
+
computeNullifier: () => computeNullifier,
|
|
41
41
|
computeSignature: () => computeSignature,
|
|
42
42
|
createUtxo: () => createUtxo,
|
|
43
43
|
createZeroUtxo: () => createZeroUtxo,
|
|
@@ -112,7 +112,7 @@ async function createUtxo(amount, keypair, mintAddress = NATIVE_SOL_MINT) {
|
|
|
112
112
|
blinding,
|
|
113
113
|
mintAddress
|
|
114
114
|
};
|
|
115
|
-
utxo.commitment = await
|
|
115
|
+
utxo.commitment = await computeCommitment(utxo);
|
|
116
116
|
return utxo;
|
|
117
117
|
}
|
|
118
118
|
async function createZeroUtxo(mintAddress = NATIVE_SOL_MINT, salt) {
|
|
@@ -127,7 +127,7 @@ async function createZeroUtxo(mintAddress = NATIVE_SOL_MINT, salt) {
|
|
|
127
127
|
blinding: BigInt(0),
|
|
128
128
|
mintAddress
|
|
129
129
|
};
|
|
130
|
-
utxo.commitment = await
|
|
130
|
+
utxo.commitment = await computeCommitment(utxo);
|
|
131
131
|
utxo.index = 0;
|
|
132
132
|
return utxo;
|
|
133
133
|
}
|
|
@@ -139,7 +139,7 @@ function pubkeyToFieldElement(pubkey) {
|
|
|
139
139
|
}
|
|
140
140
|
return value % FIELD_MODULUS;
|
|
141
141
|
}
|
|
142
|
-
async function
|
|
142
|
+
async function computeCommitment(utxo) {
|
|
143
143
|
const poseidon3 = await getPoseidon2();
|
|
144
144
|
const mintField = pubkeyToFieldElement(utxo.mintAddress);
|
|
145
145
|
const hash = poseidon3([
|
|
@@ -155,12 +155,12 @@ async function computeSignature(privateKey, commitment, pathIndex) {
|
|
|
155
155
|
const hash = poseidon3([privateKey, commitment, pathIndex]);
|
|
156
156
|
return poseidon3.F.toObject(hash);
|
|
157
157
|
}
|
|
158
|
-
async function
|
|
158
|
+
async function computeNullifier(utxo) {
|
|
159
159
|
if (utxo.index === void 0) {
|
|
160
160
|
throw new Error("UTXO must have an index to compute nullifier");
|
|
161
161
|
}
|
|
162
162
|
const poseidon3 = await getPoseidon2();
|
|
163
|
-
const commitment = utxo.commitment ?? await
|
|
163
|
+
const commitment = utxo.commitment ?? await computeCommitment(utxo);
|
|
164
164
|
const pathIndex = BigInt(utxo.index);
|
|
165
165
|
const signature = await computeSignature(
|
|
166
166
|
utxo.keypair.privateKey,
|
|
@@ -202,7 +202,7 @@ async function deserializeUtxo(bytes) {
|
|
|
202
202
|
const blindingBytes = bytes.slice(40, 72);
|
|
203
203
|
const blinding = bytesToBigint(blindingBytes);
|
|
204
204
|
const mintBytes = bytes.slice(72, 104);
|
|
205
|
-
const mintAddress = new
|
|
205
|
+
const mintAddress = new import_web3.PublicKey(mintBytes);
|
|
206
206
|
const index = view.getUint32(104, true);
|
|
207
207
|
const utxo = {
|
|
208
208
|
amount,
|
|
@@ -211,7 +211,7 @@ async function deserializeUtxo(bytes) {
|
|
|
211
211
|
mintAddress,
|
|
212
212
|
index: index > 0 ? index : void 0
|
|
213
213
|
};
|
|
214
|
-
utxo.commitment = await
|
|
214
|
+
utxo.commitment = await computeCommitment(utxo);
|
|
215
215
|
return utxo;
|
|
216
216
|
}
|
|
217
217
|
function bigintToBytes(value, length) {
|
|
@@ -247,8 +247,8 @@ function bigintToBytes322(value) {
|
|
|
247
247
|
return result;
|
|
248
248
|
}
|
|
249
249
|
async function utxoEquals(a, b) {
|
|
250
|
-
const commitmentA = a.commitment ?? await
|
|
251
|
-
const commitmentB = b.commitment ?? await
|
|
250
|
+
const commitmentA = a.commitment ?? await computeCommitment(a);
|
|
251
|
+
const commitmentB = b.commitment ?? await computeCommitment(b);
|
|
252
252
|
return commitmentA === commitmentB;
|
|
253
253
|
}
|
|
254
254
|
function sumUtxoAmounts(utxos) {
|
|
@@ -270,16 +270,16 @@ function selectUtxos(available, targetAmount) {
|
|
|
270
270
|
}
|
|
271
271
|
return selected;
|
|
272
272
|
}
|
|
273
|
-
var
|
|
273
|
+
var import_web3, import_circomlibjs2, import_blake32, FIELD_MODULUS, UTXO_KEY_DOMAIN, NATIVE_SOL_MINT, poseidonInstance;
|
|
274
274
|
var init_utxo = __esm({
|
|
275
275
|
"src/core/utxo.ts"() {
|
|
276
276
|
"use strict";
|
|
277
|
-
|
|
277
|
+
import_web3 = require("@solana/web3.js");
|
|
278
278
|
import_circomlibjs2 = require("circomlibjs");
|
|
279
279
|
import_blake32 = require("@noble/hashes/blake3");
|
|
280
280
|
FIELD_MODULUS = BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
281
281
|
UTXO_KEY_DOMAIN = new TextEncoder().encode("cloak_utxo_priv_v1");
|
|
282
|
-
NATIVE_SOL_MINT = new
|
|
282
|
+
NATIVE_SOL_MINT = new import_web3.PublicKey("So11111111111111111111111111111111111111112");
|
|
283
283
|
poseidonInstance = null;
|
|
284
284
|
}
|
|
285
285
|
});
|
|
@@ -290,9 +290,7 @@ __export(index_exports, {
|
|
|
290
290
|
CLOAK_PROGRAM_ID: () => CLOAK_PROGRAM_ID,
|
|
291
291
|
CloakError: () => CloakError,
|
|
292
292
|
CloakSDK: () => CloakSDK,
|
|
293
|
-
DEFAULT_CIRCUITS_URL: () => DEFAULT_CIRCUITS_URL,
|
|
294
293
|
DEFAULT_TRANSACTION_CIRCUITS_URL: () => DEFAULT_TRANSACTION_CIRCUITS_URL,
|
|
295
|
-
EXPECTED_CIRCUIT_HASHES: () => EXPECTED_CIRCUIT_HASHES,
|
|
296
294
|
FIXED_FEE_LAMPORTS: () => FIXED_FEE_LAMPORTS,
|
|
297
295
|
LAMPORTS_PER_SOL: () => LAMPORTS_PER_SOL,
|
|
298
296
|
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
@@ -315,7 +313,6 @@ __export(index_exports, {
|
|
|
315
313
|
VARIABLE_FEE_NUMERATOR: () => VARIABLE_FEE_NUMERATOR,
|
|
316
314
|
VARIABLE_FEE_RATE: () => VARIABLE_FEE_RATE,
|
|
317
315
|
VERSION: () => VERSION,
|
|
318
|
-
areCircuitsAvailable: () => areCircuitsAvailable,
|
|
319
316
|
bigintToBytes32: () => bigintToBytes32,
|
|
320
317
|
bigintToHex: () => bigintToHex,
|
|
321
318
|
buildMerkleTree: () => buildMerkleTree,
|
|
@@ -329,30 +326,15 @@ __export(index_exports, {
|
|
|
329
326
|
chainNoteFromBase64: () => chainNoteFromBase64,
|
|
330
327
|
chainNoteToBase64: () => chainNoteToBase64,
|
|
331
328
|
classifyRelayError: () => classifyRelayError,
|
|
332
|
-
cleanupStalePendingOperations: () => cleanupStalePendingOperations,
|
|
333
|
-
clearPendingDeposits: () => clearPendingDeposits,
|
|
334
|
-
clearPendingWithdrawals: () => clearPendingWithdrawals,
|
|
335
329
|
computeChainNoteHash: () => computeChainNoteHash,
|
|
336
|
-
computeCommitment: () => computeCommitment,
|
|
337
330
|
computeExtDataHash: () => computeExtDataHash,
|
|
338
331
|
computeMerkleRoot: () => computeMerkleRoot,
|
|
339
|
-
computeNullifier: () => computeNullifier,
|
|
340
|
-
computeNullifierAsync: () => computeNullifierAsync,
|
|
341
|
-
computeNullifierSync: () => computeNullifierSync,
|
|
342
|
-
computeOutputsHash: () => computeOutputsHash,
|
|
343
|
-
computeOutputsHashAsync: () => computeOutputsHashAsync,
|
|
344
|
-
computeOutputsHashSync: () => computeOutputsHashSync,
|
|
345
332
|
computeProofForLatestDeposit: () => computeProofForLatestDeposit,
|
|
346
333
|
computeProofFromChain: () => computeProofFromChain,
|
|
347
334
|
computeSignature: () => computeSignature,
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
computeSwapOutputsHashSync: () => computeSwapOutputsHashSync,
|
|
351
|
-
computeUtxoCommitment: () => computeCommitment2,
|
|
352
|
-
computeUtxoNullifier: () => computeNullifier2,
|
|
353
|
-
copyNoteToClipboard: () => copyNoteToClipboard,
|
|
335
|
+
computeUtxoCommitment: () => computeCommitment,
|
|
336
|
+
computeUtxoNullifier: () => computeNullifier,
|
|
354
337
|
createCloakError: () => createCloakError,
|
|
355
|
-
createDepositInstruction: () => createDepositInstruction,
|
|
356
338
|
createLogger: () => createLogger,
|
|
357
339
|
createUtxo: () => createUtxo,
|
|
358
340
|
createZeroUtxo: () => createZeroUtxo,
|
|
@@ -372,78 +354,52 @@ __export(index_exports, {
|
|
|
372
354
|
deriveViewingKeyFromUtxoPrivateKey: () => deriveViewingKeyFromUtxoPrivateKey,
|
|
373
355
|
deserializeUtxo: () => deserializeUtxo,
|
|
374
356
|
detectNetworkFromRpcUrl: () => detectNetworkFromRpcUrl,
|
|
375
|
-
downloadNote: () => downloadNote,
|
|
376
|
-
encodeNoteSimple: () => encodeNoteSimple,
|
|
377
357
|
encryptCompactChainNote: () => encryptCompactChainNote,
|
|
378
358
|
encryptNoteForRecipient: () => encryptNoteForRecipient,
|
|
379
359
|
encryptTransactionMetadata: () => encryptTransactionMetadata,
|
|
380
360
|
encryptTransactionMetadataBundle: () => encryptTransactionMetadataBundle,
|
|
381
361
|
expandSpendKey: () => expandSpendKey,
|
|
382
362
|
exportKeys: () => exportKeys,
|
|
383
|
-
exportNote: () => exportNote,
|
|
384
|
-
exportWalletKeys: () => exportWalletKeys,
|
|
385
363
|
fetchCommitments: () => fetchCommitments,
|
|
386
364
|
fetchRiskQuoteInstruction: () => fetchRiskQuoteInstruction,
|
|
387
365
|
fetchRiskQuoteIx: () => fetchRiskQuoteIx,
|
|
388
|
-
filterNotesByNetwork: () => filterNotesByNetwork,
|
|
389
|
-
filterWithdrawableNotes: () => filterWithdrawableNotes,
|
|
390
|
-
findNoteByCommitment: () => findNoteByCommitment,
|
|
391
366
|
formatAmount: () => formatAmount,
|
|
392
367
|
formatComplianceCsv: () => formatComplianceCsv,
|
|
393
368
|
formatErrorForLogging: () => formatErrorForLogging,
|
|
394
369
|
formatSol: () => formatSol,
|
|
395
370
|
fullWithdraw: () => fullWithdraw,
|
|
396
371
|
generateCloakKeys: () => generateCloakKeys,
|
|
397
|
-
generateCommitment: () => generateCommitment,
|
|
398
|
-
generateCommitmentAsync: () => generateCommitmentAsync,
|
|
399
372
|
generateMasterSeed: () => generateMasterSeed,
|
|
400
|
-
generateNote: () => generateNote,
|
|
401
|
-
generateNoteFromWallet: () => generateNoteFromWallet,
|
|
402
373
|
generateUtxoKeypair: () => generateUtxoKeypair,
|
|
403
374
|
generateViewingKeyPair: () => generateViewingKeyPair,
|
|
404
|
-
generateWithdrawRegularProof: () => generateWithdrawRegularProof,
|
|
405
|
-
generateWithdrawSwapProof: () => generateWithdrawSwapProof,
|
|
406
375
|
getAddressExplorerUrl: () => getAddressExplorerUrl,
|
|
407
376
|
getCircuitsPath: () => getCircuitsPath,
|
|
408
|
-
getDefaultCircuitsPath: () => getDefaultCircuitsPath,
|
|
409
377
|
getDistributableAmount: () => getDistributableAmount,
|
|
410
378
|
getExplorerUrl: () => getExplorerUrl,
|
|
411
379
|
getNkFromUtxoPrivateKey: () => getNkFromUtxoPrivateKey,
|
|
412
380
|
getNullifierPDA: () => getNullifierPDA,
|
|
413
|
-
getPendingOperationsSummary: () => getPendingOperationsSummary,
|
|
414
381
|
getPublicKey: () => getPublicKey,
|
|
415
|
-
getPublicViewKey: () => getPublicViewKey,
|
|
416
|
-
getRecipientAmount: () => getRecipientAmount,
|
|
417
382
|
getRpcUrlForNetwork: () => getRpcUrlForNetwork,
|
|
418
383
|
getShieldPoolPDAs: () => getShieldPoolPDAs,
|
|
419
384
|
getSwapStatePDA: () => getSwapStatePDA,
|
|
420
|
-
getViewKey: () => getViewKey,
|
|
421
|
-
hasPendingOperations: () => hasPendingOperations,
|
|
422
385
|
hexToBigint: () => hexToBigint,
|
|
423
386
|
hexToBytes: () => hexToBytes,
|
|
424
387
|
importKeys: () => importKeys,
|
|
425
|
-
importWalletKeys: () => importWalletKeys,
|
|
426
388
|
isDebugEnabled: () => isDebugEnabled,
|
|
427
389
|
isRootNotFoundError: () => isRootNotFoundError,
|
|
428
390
|
isValidHex: () => isValidHex,
|
|
429
391
|
isValidRpcUrl: () => isValidRpcUrl,
|
|
430
392
|
isValidSolanaAddress: () => isValidSolanaAddress,
|
|
431
393
|
isWithdrawAmountSufficient: () => isWithdrawAmountSufficient,
|
|
432
|
-
isWithdrawable: () => isWithdrawable,
|
|
433
394
|
keypairToAdapter: () => keypairToAdapter,
|
|
434
|
-
loadPendingDeposits: () => loadPendingDeposits,
|
|
435
|
-
loadPendingWithdrawals: () => loadPendingWithdrawals,
|
|
436
395
|
parseAmount: () => parseAmount,
|
|
437
396
|
parseError: () => parseError,
|
|
438
|
-
parseNote: () => parseNote,
|
|
439
397
|
parseRelayErrorResponse: () => parseRelayErrorResponse,
|
|
440
398
|
parseTransactionError: () => parseTransactionError,
|
|
441
399
|
partialWithdraw: () => partialWithdraw,
|
|
442
400
|
poseidonHash: () => poseidonHash,
|
|
443
401
|
preflightCheck: () => preflightCheck,
|
|
444
402
|
preflightNullifiers: () => preflightNullifiers,
|
|
445
|
-
prepareEncryptedOutput: () => prepareEncryptedOutput,
|
|
446
|
-
prepareEncryptedOutputForRecipient: () => prepareEncryptedOutputForRecipient,
|
|
447
403
|
proofToBytes: () => proofToBytes,
|
|
448
404
|
pubkeyToFieldElement: () => pubkeyToFieldElement,
|
|
449
405
|
pubkeyToLimbs: () => pubkeyToLimbs,
|
|
@@ -451,16 +407,11 @@ __export(index_exports, {
|
|
|
451
407
|
randomFieldElement: () => randomFieldElement,
|
|
452
408
|
readMerkleTreeState: () => readMerkleTreeState,
|
|
453
409
|
registerViewingKey: () => registerViewingKey,
|
|
454
|
-
removePendingDeposit: () => removePendingDeposit,
|
|
455
|
-
removePendingWithdrawal: () => removePendingWithdrawal,
|
|
456
|
-
savePendingDeposit: () => savePendingDeposit,
|
|
457
|
-
savePendingWithdrawal: () => savePendingWithdrawal,
|
|
458
410
|
scanNotesForWallet: () => scanNotesForWallet,
|
|
459
411
|
scanTransactions: () => scanTransactions,
|
|
460
412
|
sdkLogger: () => sdkLogger,
|
|
461
413
|
selectUtxos: () => selectUtxos,
|
|
462
414
|
sendTransaction: () => sendTransaction,
|
|
463
|
-
serializeNote: () => serializeNote,
|
|
464
415
|
serializeUtxo: () => serializeUtxo,
|
|
465
416
|
setCircuitsPath: () => setCircuitsPath,
|
|
466
417
|
setDebugMode: () => setDebugMode,
|
|
@@ -474,21 +425,12 @@ __export(index_exports, {
|
|
|
474
425
|
transfer: () => transfer,
|
|
475
426
|
truncate: () => truncate,
|
|
476
427
|
tryDecryptNote: () => tryDecryptNote,
|
|
477
|
-
updateNoteWithDeposit: () => updateNoteWithDeposit,
|
|
478
|
-
updatePendingDeposit: () => updatePendingDeposit,
|
|
479
|
-
updatePendingWithdrawal: () => updatePendingWithdrawal,
|
|
480
428
|
utxoBigintToBytes32: () => bigintToBytes322,
|
|
481
429
|
utxoEquals: () => utxoEquals,
|
|
482
430
|
utxoHexToBigint: () => hexToBigint2,
|
|
483
|
-
validateDepositParams: () => validateDepositParams,
|
|
484
|
-
validateNote: () => validateNote,
|
|
485
431
|
validateOutputsSum: () => validateOutputsSum,
|
|
486
432
|
validateRoot: () => validateRoot,
|
|
487
|
-
validateTransfers: () => validateTransfers,
|
|
488
433
|
validateWalletConnected: () => validateWalletConnected,
|
|
489
|
-
validateWithdrawableNote: () => validateWithdrawableNote,
|
|
490
|
-
verifyAllCircuits: () => verifyAllCircuits,
|
|
491
|
-
verifyCircuitIntegrity: () => verifyCircuitIntegrity,
|
|
492
434
|
verifyUtxos: () => verifyUtxos,
|
|
493
435
|
waitForRoot: () => waitForRoot,
|
|
494
436
|
withTiming: () => withTiming
|
|
@@ -496,7 +438,7 @@ __export(index_exports, {
|
|
|
496
438
|
module.exports = __toCommonJS(index_exports);
|
|
497
439
|
|
|
498
440
|
// src/core/CloakSDK.ts
|
|
499
|
-
var
|
|
441
|
+
var import_web33 = require("@solana/web3.js");
|
|
500
442
|
|
|
501
443
|
// src/core/types.ts
|
|
502
444
|
var CloakError = class extends Error {
|
|
@@ -553,79 +495,6 @@ function hexToBigint(hex) {
|
|
|
553
495
|
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
554
496
|
return BigInt("0x" + cleanHex);
|
|
555
497
|
}
|
|
556
|
-
async function computeCommitment(amount, r, sk_spend) {
|
|
557
|
-
const [sk0, sk1] = splitTo2Limbs(sk_spend);
|
|
558
|
-
const [r0, r1] = splitTo2Limbs(r);
|
|
559
|
-
const pk_spend = await poseidonHash([sk0, sk1]);
|
|
560
|
-
return await poseidonHash([amount, r0, r1, pk_spend]);
|
|
561
|
-
}
|
|
562
|
-
async function generateCommitmentAsync(amountLamports, r, skSpend) {
|
|
563
|
-
const amount = BigInt(amountLamports);
|
|
564
|
-
const rValue = hexToBigint(bytesToHex(r));
|
|
565
|
-
const skValue = hexToBigint(bytesToHex(skSpend));
|
|
566
|
-
return await computeCommitment(amount, rValue, skValue);
|
|
567
|
-
}
|
|
568
|
-
function generateCommitment(_amountLamports, _r, _skSpend) {
|
|
569
|
-
throw new Error("generateCommitment is deprecated. Use generateCommitmentAsync instead.");
|
|
570
|
-
}
|
|
571
|
-
async function computeNullifier(sk_spend, leafIndex) {
|
|
572
|
-
const [sk0, sk1] = splitTo2Limbs(sk_spend);
|
|
573
|
-
return await poseidonHash([sk0, sk1, leafIndex]);
|
|
574
|
-
}
|
|
575
|
-
async function computeNullifierAsync(skSpend, leafIndex) {
|
|
576
|
-
const skValue = typeof skSpend === "string" ? hexToBigint(skSpend) : hexToBigint(bytesToHex(skSpend));
|
|
577
|
-
return await computeNullifier(skValue, BigInt(leafIndex));
|
|
578
|
-
}
|
|
579
|
-
function computeNullifierSync(_skSpend, _leafIndex) {
|
|
580
|
-
throw new Error("computeNullifierSync is deprecated. Use computeNullifierAsync instead.");
|
|
581
|
-
}
|
|
582
|
-
async function computeOutputsHashAsync(outputs) {
|
|
583
|
-
let hash = 0n;
|
|
584
|
-
for (const output of outputs) {
|
|
585
|
-
const [lo, hi] = pubkeyToLimbs(output.recipient);
|
|
586
|
-
hash = await poseidonHash([hash, lo, hi, BigInt(output.amount)]);
|
|
587
|
-
}
|
|
588
|
-
return hash;
|
|
589
|
-
}
|
|
590
|
-
async function computeOutputsHash(outAddr, outAmount, outFlags) {
|
|
591
|
-
let hash = 0n;
|
|
592
|
-
for (let i = 0; i < 5; i++) {
|
|
593
|
-
if (outFlags[i] === 1) {
|
|
594
|
-
hash = await poseidonHash([hash, outAddr[i][0], outAddr[i][1], outAmount[i]]);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
return hash;
|
|
598
|
-
}
|
|
599
|
-
function computeOutputsHashSync(_outputs) {
|
|
600
|
-
throw new Error("computeOutputsHashSync is deprecated. Use computeOutputsHashAsync instead.");
|
|
601
|
-
}
|
|
602
|
-
async function computeSwapOutputsHash(inputMintLimbs, outputMintLimbs, recipientAtaLimbs, minOutputAmount, publicAmount) {
|
|
603
|
-
return await poseidonHash([
|
|
604
|
-
inputMintLimbs[0],
|
|
605
|
-
inputMintLimbs[1],
|
|
606
|
-
outputMintLimbs[0],
|
|
607
|
-
outputMintLimbs[1],
|
|
608
|
-
recipientAtaLimbs[0],
|
|
609
|
-
recipientAtaLimbs[1],
|
|
610
|
-
minOutputAmount,
|
|
611
|
-
publicAmount
|
|
612
|
-
]);
|
|
613
|
-
}
|
|
614
|
-
async function computeSwapOutputsHashAsync(inputMint, outputMint, recipientAta, minOutputAmount, amount) {
|
|
615
|
-
const inputMintLimbs = pubkeyToLimbs(inputMint);
|
|
616
|
-
const outputMintLimbs = pubkeyToLimbs(outputMint);
|
|
617
|
-
const recipientAtaLimbs = pubkeyToLimbs(recipientAta);
|
|
618
|
-
return await computeSwapOutputsHash(
|
|
619
|
-
inputMintLimbs,
|
|
620
|
-
outputMintLimbs,
|
|
621
|
-
recipientAtaLimbs,
|
|
622
|
-
BigInt(minOutputAmount),
|
|
623
|
-
BigInt(amount)
|
|
624
|
-
);
|
|
625
|
-
}
|
|
626
|
-
function computeSwapOutputsHashSync(_outputMint, _recipientAta, _minOutputAmount, _amount) {
|
|
627
|
-
throw new Error("computeSwapOutputsHashSync is deprecated. Use computeSwapOutputsHashAsync instead.");
|
|
628
|
-
}
|
|
629
498
|
function bigintToBytes32(n) {
|
|
630
499
|
const hex = n.toString(16).padStart(64, "0");
|
|
631
500
|
const bytes = new Uint8Array(32);
|
|
@@ -898,223 +767,11 @@ function importKeys(exported) {
|
|
|
898
767
|
return generateCloakKeys(masterSeed);
|
|
899
768
|
}
|
|
900
769
|
|
|
901
|
-
// src/utils/network.ts
|
|
902
|
-
function detectNetworkFromRpcUrl(rpcUrl) {
|
|
903
|
-
const url = rpcUrl || process.env.NEXT_PUBLIC_SOLANA_RPC_URL || "";
|
|
904
|
-
const lowerUrl = url.toLowerCase();
|
|
905
|
-
if (lowerUrl.includes("mainnet") || lowerUrl.includes("api.mainnet-beta") || lowerUrl.includes("mainnet-beta")) {
|
|
906
|
-
return "mainnet";
|
|
907
|
-
}
|
|
908
|
-
if (lowerUrl.includes("testnet") || lowerUrl.includes("api.testnet")) {
|
|
909
|
-
return "testnet";
|
|
910
|
-
}
|
|
911
|
-
if (lowerUrl.includes("devnet") || lowerUrl.includes("api.devnet")) {
|
|
912
|
-
return "devnet";
|
|
913
|
-
}
|
|
914
|
-
if (lowerUrl.includes("localhost") || lowerUrl.includes("127.0.0.1") || lowerUrl.includes("local")) {
|
|
915
|
-
return "localnet";
|
|
916
|
-
}
|
|
917
|
-
return "mainnet";
|
|
918
|
-
}
|
|
919
|
-
function getRpcUrlForNetwork(network) {
|
|
920
|
-
switch (network) {
|
|
921
|
-
case "mainnet":
|
|
922
|
-
return "https://api.mainnet-beta.solana.com";
|
|
923
|
-
case "devnet":
|
|
924
|
-
return "https://api.devnet.solana.com";
|
|
925
|
-
case "localnet":
|
|
926
|
-
return "http://localhost:8899";
|
|
927
|
-
default:
|
|
928
|
-
return "https://api.mainnet-beta.solana.com";
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
function isValidRpcUrl(url) {
|
|
932
|
-
try {
|
|
933
|
-
const parsed = new URL(url);
|
|
934
|
-
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
935
|
-
} catch {
|
|
936
|
-
return false;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
function getExplorerUrl(signature, network = "devnet") {
|
|
940
|
-
const cluster = network === "mainnet" ? "" : `?cluster=${network}`;
|
|
941
|
-
return `https://explorer.solana.com/tx/${signature}${cluster}`;
|
|
942
|
-
}
|
|
943
|
-
function getAddressExplorerUrl(address, network = "devnet") {
|
|
944
|
-
const cluster = network === "mainnet" ? "" : `?cluster=${network}`;
|
|
945
|
-
return `https://explorer.solana.com/address/${address}${cluster}`;
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
// src/utils/fees.ts
|
|
949
|
-
var LAMPORTS_PER_SOL = 1e9;
|
|
950
|
-
var FIXED_FEE_LAMPORTS = 5e6;
|
|
951
|
-
var VARIABLE_FEE_NUMERATOR = 3;
|
|
952
|
-
var VARIABLE_FEE_DENOMINATOR = 1e3;
|
|
953
|
-
var VARIABLE_FEE_RATE = VARIABLE_FEE_NUMERATOR / VARIABLE_FEE_DENOMINATOR;
|
|
954
|
-
var MIN_DEPOSIT_LAMPORTS = 1e7;
|
|
955
|
-
function calculateFee(amountLamports) {
|
|
956
|
-
const variableFee = Math.floor(amountLamports * VARIABLE_FEE_NUMERATOR / VARIABLE_FEE_DENOMINATOR);
|
|
957
|
-
return FIXED_FEE_LAMPORTS + variableFee;
|
|
958
|
-
}
|
|
959
|
-
function calculateFeeBigint(amountLamports) {
|
|
960
|
-
const fixed = BigInt(FIXED_FEE_LAMPORTS);
|
|
961
|
-
const variable = amountLamports * BigInt(VARIABLE_FEE_NUMERATOR) / BigInt(VARIABLE_FEE_DENOMINATOR);
|
|
962
|
-
return fixed + variable;
|
|
963
|
-
}
|
|
964
|
-
function isWithdrawAmountSufficient(amountLamports) {
|
|
965
|
-
return amountLamports > calculateFeeBigint(amountLamports);
|
|
966
|
-
}
|
|
967
|
-
function getDistributableAmount(amountLamports) {
|
|
968
|
-
return amountLamports - calculateFee(amountLamports);
|
|
969
|
-
}
|
|
970
|
-
function formatAmount(lamports, decimals = 9) {
|
|
971
|
-
return (lamports / LAMPORTS_PER_SOL).toFixed(decimals);
|
|
972
|
-
}
|
|
973
|
-
function parseAmount(sol) {
|
|
974
|
-
const num = parseFloat(sol);
|
|
975
|
-
if (isNaN(num) || num < 0) {
|
|
976
|
-
throw new Error(`Invalid SOL amount: ${sol}`);
|
|
977
|
-
}
|
|
978
|
-
return Math.floor(num * LAMPORTS_PER_SOL);
|
|
979
|
-
}
|
|
980
|
-
function validateOutputsSum(outputs, expectedTotal) {
|
|
981
|
-
const sum = outputs.reduce((acc, out) => acc + out.amount, 0);
|
|
982
|
-
return sum === expectedTotal;
|
|
983
|
-
}
|
|
984
|
-
function calculateRelayFee(amountLamports, feeBps) {
|
|
985
|
-
if (feeBps < 0 || feeBps > 1e4) {
|
|
986
|
-
throw new Error("Fee basis points must be between 0 and 10000");
|
|
987
|
-
}
|
|
988
|
-
return Math.floor(amountLamports * feeBps / 1e4);
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
// src/core/note-manager.ts
|
|
992
|
-
async function generateNote(amountLamports, network) {
|
|
993
|
-
const actualNetwork = network || detectNetworkFromRpcUrl();
|
|
994
|
-
const skSpend = randomBytes(32);
|
|
995
|
-
const rBytes = randomBytes(32);
|
|
996
|
-
const commitmentBigint = await generateCommitmentAsync(amountLamports, rBytes, skSpend);
|
|
997
|
-
const commitmentHex = commitmentBigint.toString(16).padStart(64, "0");
|
|
998
|
-
const skSpendHex = bytesToHex(skSpend);
|
|
999
|
-
const rHex = bytesToHex(rBytes);
|
|
1000
|
-
return {
|
|
1001
|
-
version: "1.0",
|
|
1002
|
-
amount: amountLamports,
|
|
1003
|
-
commitment: commitmentHex,
|
|
1004
|
-
sk_spend: skSpendHex,
|
|
1005
|
-
r: rHex,
|
|
1006
|
-
timestamp: Date.now(),
|
|
1007
|
-
network: actualNetwork
|
|
1008
|
-
};
|
|
1009
|
-
}
|
|
1010
|
-
async function generateNoteFromWallet(amountLamports, keys, network) {
|
|
1011
|
-
const actualNetwork = network || detectNetworkFromRpcUrl();
|
|
1012
|
-
const rBytes = randomBytes(32);
|
|
1013
|
-
const sk_spend = hexToBytes(keys.spend.sk_spend_hex);
|
|
1014
|
-
const commitmentBigint = await generateCommitmentAsync(amountLamports, rBytes, sk_spend);
|
|
1015
|
-
const commitmentHex = commitmentBigint.toString(16).padStart(64, "0");
|
|
1016
|
-
return {
|
|
1017
|
-
version: "2.0",
|
|
1018
|
-
amount: amountLamports,
|
|
1019
|
-
commitment: commitmentHex,
|
|
1020
|
-
sk_spend: keys.spend.sk_spend_hex,
|
|
1021
|
-
r: bytesToHex(rBytes),
|
|
1022
|
-
timestamp: Date.now(),
|
|
1023
|
-
network: actualNetwork
|
|
1024
|
-
};
|
|
1025
|
-
}
|
|
1026
|
-
function parseNote(jsonString) {
|
|
1027
|
-
const note = JSON.parse(jsonString);
|
|
1028
|
-
if (!note.version || !note.amount || !note.commitment || !note.sk_spend || !note.r) {
|
|
1029
|
-
throw new CloakError(
|
|
1030
|
-
"Invalid note format: missing required fields",
|
|
1031
|
-
"validation",
|
|
1032
|
-
false
|
|
1033
|
-
);
|
|
1034
|
-
}
|
|
1035
|
-
if (!/^[0-9a-f]{64}$/i.test(note.commitment)) {
|
|
1036
|
-
throw new CloakError("Invalid commitment format", "validation", false);
|
|
1037
|
-
}
|
|
1038
|
-
if (!/^[0-9a-f]{64}$/i.test(note.sk_spend)) {
|
|
1039
|
-
throw new CloakError("Invalid sk_spend format", "validation", false);
|
|
1040
|
-
}
|
|
1041
|
-
if (!/^[0-9a-f]{64}$/i.test(note.r)) {
|
|
1042
|
-
throw new CloakError("Invalid r format", "validation", false);
|
|
1043
|
-
}
|
|
1044
|
-
return note;
|
|
1045
|
-
}
|
|
1046
|
-
function exportNote(note, pretty = false) {
|
|
1047
|
-
return pretty ? JSON.stringify(note, null, 2) : JSON.stringify(note);
|
|
1048
|
-
}
|
|
1049
|
-
function isWithdrawable(note) {
|
|
1050
|
-
return !!(note.depositSignature && note.leafIndex !== void 0 && note.root);
|
|
1051
|
-
}
|
|
1052
|
-
function updateNoteWithDeposit(note, depositInfo) {
|
|
1053
|
-
const updated = {
|
|
1054
|
-
...note,
|
|
1055
|
-
depositSignature: depositInfo.signature,
|
|
1056
|
-
depositSlot: depositInfo.slot,
|
|
1057
|
-
leafIndex: depositInfo.leafIndex,
|
|
1058
|
-
root: depositInfo.root
|
|
1059
|
-
};
|
|
1060
|
-
if (depositInfo.merkleProof) {
|
|
1061
|
-
updated.merkleProof = depositInfo.merkleProof;
|
|
1062
|
-
}
|
|
1063
|
-
return updated;
|
|
1064
|
-
}
|
|
1065
|
-
function findNoteByCommitment(notes, commitment) {
|
|
1066
|
-
return notes.find((n) => n.commitment === commitment);
|
|
1067
|
-
}
|
|
1068
|
-
function filterNotesByNetwork(notes, network) {
|
|
1069
|
-
return notes.filter((n) => n.network === network);
|
|
1070
|
-
}
|
|
1071
|
-
function filterWithdrawableNotes(notes) {
|
|
1072
|
-
return notes.filter(isWithdrawable);
|
|
1073
|
-
}
|
|
1074
|
-
function exportWalletKeys(keys) {
|
|
1075
|
-
return exportKeys(keys);
|
|
1076
|
-
}
|
|
1077
|
-
function importWalletKeys(keysJson) {
|
|
1078
|
-
return importKeys(keysJson);
|
|
1079
|
-
}
|
|
1080
|
-
function getPublicViewKey(keys) {
|
|
1081
|
-
return keys.view.pvk_hex;
|
|
1082
|
-
}
|
|
1083
|
-
function getViewKey(keys) {
|
|
1084
|
-
return keys.view;
|
|
1085
|
-
}
|
|
1086
|
-
var calculateFee2 = calculateFee;
|
|
1087
|
-
function getDistributableAmount2(amountLamports) {
|
|
1088
|
-
return amountLamports - calculateFee2(amountLamports);
|
|
1089
|
-
}
|
|
1090
|
-
function getRecipientAmount(amountLamports) {
|
|
1091
|
-
return getDistributableAmount2(amountLamports);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
770
|
// src/core/storage.ts
|
|
1095
771
|
var MemoryStorageAdapter = class {
|
|
1096
772
|
constructor() {
|
|
1097
|
-
this.notes = /* @__PURE__ */ new Map();
|
|
1098
773
|
this.keys = null;
|
|
1099
774
|
}
|
|
1100
|
-
saveNote(note) {
|
|
1101
|
-
this.notes.set(note.commitment, note);
|
|
1102
|
-
}
|
|
1103
|
-
loadAllNotes() {
|
|
1104
|
-
return Array.from(this.notes.values());
|
|
1105
|
-
}
|
|
1106
|
-
updateNote(commitment, updates) {
|
|
1107
|
-
const existing = this.notes.get(commitment);
|
|
1108
|
-
if (existing) {
|
|
1109
|
-
this.notes.set(commitment, { ...existing, ...updates });
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
deleteNote(commitment) {
|
|
1113
|
-
this.notes.delete(commitment);
|
|
1114
|
-
}
|
|
1115
|
-
clearAllNotes() {
|
|
1116
|
-
this.notes.clear();
|
|
1117
|
-
}
|
|
1118
775
|
saveKeys(keys) {
|
|
1119
776
|
this.keys = keys;
|
|
1120
777
|
}
|
|
@@ -1125,10 +782,10 @@ var MemoryStorageAdapter = class {
|
|
|
1125
782
|
this.keys = null;
|
|
1126
783
|
}
|
|
1127
784
|
};
|
|
1128
|
-
var
|
|
1129
|
-
constructor(
|
|
1130
|
-
this.notesKey = notesKey;
|
|
785
|
+
var _LocalStorageAdapter = class _LocalStorageAdapter {
|
|
786
|
+
constructor(keysKey = "cloak_wallet_keys") {
|
|
1131
787
|
this.keysKey = keysKey;
|
|
788
|
+
_LocalStorageAdapter.runLegacyPurgeOnce();
|
|
1132
789
|
}
|
|
1133
790
|
getStorage() {
|
|
1134
791
|
if (typeof globalThis !== "undefined" && globalThis.localStorage) {
|
|
@@ -1136,47 +793,6 @@ var LocalStorageAdapter = class {
|
|
|
1136
793
|
}
|
|
1137
794
|
return null;
|
|
1138
795
|
}
|
|
1139
|
-
saveNote(note) {
|
|
1140
|
-
const storage = this.getStorage();
|
|
1141
|
-
if (!storage) throw new Error("localStorage not available");
|
|
1142
|
-
const notes = this.loadAllNotes();
|
|
1143
|
-
notes.push(note);
|
|
1144
|
-
storage.setItem(this.notesKey, JSON.stringify(notes));
|
|
1145
|
-
}
|
|
1146
|
-
loadAllNotes() {
|
|
1147
|
-
const storage = this.getStorage();
|
|
1148
|
-
if (!storage) return [];
|
|
1149
|
-
const stored = storage.getItem(this.notesKey);
|
|
1150
|
-
if (!stored) return [];
|
|
1151
|
-
try {
|
|
1152
|
-
return JSON.parse(stored);
|
|
1153
|
-
} catch {
|
|
1154
|
-
return [];
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
updateNote(commitment, updates) {
|
|
1158
|
-
const storage = this.getStorage();
|
|
1159
|
-
if (!storage) return;
|
|
1160
|
-
const notes = this.loadAllNotes();
|
|
1161
|
-
const index = notes.findIndex((n) => n.commitment === commitment);
|
|
1162
|
-
if (index !== -1) {
|
|
1163
|
-
notes[index] = { ...notes[index], ...updates };
|
|
1164
|
-
storage.setItem(this.notesKey, JSON.stringify(notes));
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
deleteNote(commitment) {
|
|
1168
|
-
const storage = this.getStorage();
|
|
1169
|
-
if (!storage) return;
|
|
1170
|
-
const notes = this.loadAllNotes();
|
|
1171
|
-
const filtered = notes.filter((n) => n.commitment !== commitment);
|
|
1172
|
-
storage.setItem(this.notesKey, JSON.stringify(filtered));
|
|
1173
|
-
}
|
|
1174
|
-
clearAllNotes() {
|
|
1175
|
-
const storage = this.getStorage();
|
|
1176
|
-
if (storage) {
|
|
1177
|
-
storage.removeItem(this.notesKey);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
796
|
saveKeys(keys) {
|
|
1181
797
|
const storage = this.getStorage();
|
|
1182
798
|
if (!storage) throw new Error("localStorage not available");
|
|
@@ -1199,126 +815,64 @@ var LocalStorageAdapter = class {
|
|
|
1199
815
|
storage.removeItem(this.keysKey);
|
|
1200
816
|
}
|
|
1201
817
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
return false;
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
function validateNote(note) {
|
|
1215
|
-
if (!note || typeof note !== "object") {
|
|
1216
|
-
throw new Error("Note must be an object");
|
|
1217
|
-
}
|
|
1218
|
-
const requiredFields = ["version", "amount", "commitment", "sk_spend", "r", "timestamp", "network"];
|
|
1219
|
-
for (const field of requiredFields) {
|
|
1220
|
-
if (!(field in note)) {
|
|
1221
|
-
throw new Error(`Missing required field: ${field}`);
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
if (typeof note.version !== "string") {
|
|
1225
|
-
throw new Error("Version must be a string");
|
|
1226
|
-
}
|
|
1227
|
-
if (typeof note.amount !== "number" || note.amount <= 0) {
|
|
1228
|
-
throw new Error("Amount must be a positive number");
|
|
1229
|
-
}
|
|
1230
|
-
if (!isValidHex(note.commitment, 32)) {
|
|
1231
|
-
throw new Error("Invalid commitment format (expected 64 hex characters)");
|
|
1232
|
-
}
|
|
1233
|
-
if (!isValidHex(note.sk_spend, 32)) {
|
|
1234
|
-
throw new Error("Invalid sk_spend format (expected 64 hex characters)");
|
|
1235
|
-
}
|
|
1236
|
-
if (!isValidHex(note.r, 32)) {
|
|
1237
|
-
throw new Error("Invalid r format (expected 64 hex characters)");
|
|
1238
|
-
}
|
|
1239
|
-
if (typeof note.timestamp !== "number" || note.timestamp <= 0) {
|
|
1240
|
-
throw new Error("Timestamp must be a positive number");
|
|
1241
|
-
}
|
|
1242
|
-
if (!["localnet", "devnet", "testnet", "mainnet"].includes(note.network)) {
|
|
1243
|
-
throw new Error("Network must be localnet, devnet, testnet, or mainnet");
|
|
1244
|
-
}
|
|
1245
|
-
if (note.depositSignature !== void 0 && typeof note.depositSignature !== "string") {
|
|
1246
|
-
throw new Error("Deposit signature must be a string");
|
|
1247
|
-
}
|
|
1248
|
-
if (note.depositSlot !== void 0 && typeof note.depositSlot !== "number") {
|
|
1249
|
-
throw new Error("Deposit slot must be a number");
|
|
1250
|
-
}
|
|
1251
|
-
if (note.leafIndex !== void 0) {
|
|
1252
|
-
if (typeof note.leafIndex !== "number" || note.leafIndex < 0) {
|
|
1253
|
-
throw new Error("Leaf index must be a non-negative number");
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
if (note.root !== void 0 && !isValidHex(note.root, 32)) {
|
|
1257
|
-
throw new Error("Invalid root format (expected 64 hex characters)");
|
|
1258
|
-
}
|
|
1259
|
-
if (note.merkleProof !== void 0) {
|
|
1260
|
-
if (!Array.isArray(note.merkleProof.pathElements)) {
|
|
1261
|
-
throw new Error("Merkle proof pathElements must be an array");
|
|
818
|
+
/**
|
|
819
|
+
* Runs the legacy-purge exactly once per browser origin. Invoked
|
|
820
|
+
* automatically by the constructor; exposed as a static for tests and
|
|
821
|
+
* for callers who want to force the check before the first adapter
|
|
822
|
+
* instantiation.
|
|
823
|
+
*/
|
|
824
|
+
static runLegacyPurgeOnce() {
|
|
825
|
+
if (typeof globalThis === "undefined" || !globalThis.localStorage) {
|
|
826
|
+
return;
|
|
1262
827
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
828
|
+
const storage = globalThis.localStorage;
|
|
829
|
+
if (storage.getItem(_LocalStorageAdapter.LEGACY_PURGE_SENTINEL_KEY)) {
|
|
830
|
+
return;
|
|
1265
831
|
}
|
|
1266
|
-
|
|
1267
|
-
|
|
832
|
+
storage.removeItem(_LocalStorageAdapter.LEGACY_NOTES_KEY);
|
|
833
|
+
try {
|
|
834
|
+
storage.setItem(_LocalStorageAdapter.LEGACY_PURGE_SENTINEL_KEY, "1");
|
|
835
|
+
} catch {
|
|
1268
836
|
}
|
|
1269
837
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
}
|
|
1291
|
-
if (!note.merkleProof) {
|
|
1292
|
-
throw new Error("Note must have Merkle proof for withdrawal");
|
|
1293
|
-
}
|
|
1294
|
-
if (note.merkleProof.pathElements.length === 0) {
|
|
1295
|
-
throw new Error("Merkle proof is empty");
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
function validateTransfers(recipients, totalAmount) {
|
|
1299
|
-
if (recipients.length === 0) {
|
|
1300
|
-
throw new Error("At least one recipient is required");
|
|
1301
|
-
}
|
|
1302
|
-
if (recipients.length > 5) {
|
|
1303
|
-
throw new Error("Maximum 5 recipients allowed");
|
|
1304
|
-
}
|
|
1305
|
-
for (let i = 0; i < recipients.length; i++) {
|
|
1306
|
-
const transfer2 = recipients[i];
|
|
1307
|
-
const isPublicKeyLike = transfer2.recipient && typeof transfer2.recipient.toBase58 === "function" && typeof transfer2.recipient.toBuffer === "function";
|
|
1308
|
-
if (!isPublicKeyLike) {
|
|
1309
|
-
throw new Error(`Recipient ${i} must be a PublicKey (got ${typeof transfer2.recipient})`);
|
|
1310
|
-
}
|
|
1311
|
-
if (typeof transfer2.amount !== "number" || transfer2.amount <= 0) {
|
|
1312
|
-
throw new Error(`Recipient ${i} amount must be a positive number`);
|
|
838
|
+
/**
|
|
839
|
+
* Manual cleanup helper for callers that stored notes under a non-default
|
|
840
|
+
* key. The auto-purge in the constructor only handles the canonical
|
|
841
|
+
* `cloak_notes` slot; pass the custom key here.
|
|
842
|
+
*
|
|
843
|
+
* Pre-0.1.6 versions stored the user's `CloakNote[]` (including
|
|
844
|
+
* `r` randomness and `sk_spend` secret-key hex) as JSON plaintext.
|
|
845
|
+
* Those notes are no longer usable (the OLD `withdraw_regular.circom`
|
|
846
|
+
* flow is incompatible with the deployed UTXO program) but the plaintext
|
|
847
|
+
* would otherwise linger indefinitely.
|
|
848
|
+
*
|
|
849
|
+
* Idempotent; safe to call when the key doesn't exist or when running
|
|
850
|
+
* outside a browser (no-op). Does NOT update the auto-purge sentinel.
|
|
851
|
+
*
|
|
852
|
+
* @param notesKey - localStorage key to remove (default `"cloak_notes"`,
|
|
853
|
+
* matching the pre-0.1.6 default).
|
|
854
|
+
*/
|
|
855
|
+
static purgeLegacyNoteStorage(notesKey = _LocalStorageAdapter.LEGACY_NOTES_KEY) {
|
|
856
|
+
if (typeof globalThis === "undefined" || !globalThis.localStorage) {
|
|
857
|
+
return;
|
|
1313
858
|
}
|
|
859
|
+
globalThis.localStorage.removeItem(notesKey);
|
|
1314
860
|
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
861
|
+
};
|
|
862
|
+
/**
|
|
863
|
+
* Sentinel key set after the auto-purge runs. Persists across browser
|
|
864
|
+
* sessions so the purge never re-runs on the same origin.
|
|
865
|
+
*/
|
|
866
|
+
_LocalStorageAdapter.LEGACY_PURGE_SENTINEL_KEY = "cloak_purged_v0_1_5";
|
|
867
|
+
/**
|
|
868
|
+
* Default localStorage key the legacy `<= 0.1.5` SDK used for the
|
|
869
|
+
* plaintext `CloakNote[]` blob.
|
|
870
|
+
*/
|
|
871
|
+
_LocalStorageAdapter.LEGACY_NOTES_KEY = "cloak_notes";
|
|
872
|
+
var LocalStorageAdapter = _LocalStorageAdapter;
|
|
873
|
+
|
|
874
|
+
// src/services/RelayService.ts
|
|
875
|
+
var import_sha2 = require("@noble/hashes/sha2");
|
|
1322
876
|
|
|
1323
877
|
// src/utils/errors.ts
|
|
1324
878
|
var RootNotFoundError = class extends Error {
|
|
@@ -2214,7 +1768,6 @@ function formatErrorForLogging(error) {
|
|
|
2214
1768
|
}
|
|
2215
1769
|
|
|
2216
1770
|
// src/services/RelayService.ts
|
|
2217
|
-
var import_sha2 = require("@noble/hashes/sha2");
|
|
2218
1771
|
var RelayService = class {
|
|
2219
1772
|
/**
|
|
2220
1773
|
* Create a new Relay Service client
|
|
@@ -2583,88 +2136,24 @@ var RelayService = class {
|
|
|
2583
2136
|
}
|
|
2584
2137
|
};
|
|
2585
2138
|
|
|
2586
|
-
// src/solana/instructions.ts
|
|
2587
|
-
var import_web32 = require("@solana/web3.js");
|
|
2588
|
-
function createDepositInstruction(params) {
|
|
2589
|
-
if (params.commitment.length !== 32) {
|
|
2590
|
-
throw new Error(
|
|
2591
|
-
`Invalid commitment length: ${params.commitment.length} (expected 32 bytes)`
|
|
2592
|
-
);
|
|
2593
|
-
}
|
|
2594
|
-
if (params.amount <= 0) {
|
|
2595
|
-
throw new Error("Amount must be positive");
|
|
2596
|
-
}
|
|
2597
|
-
const discriminant = new Uint8Array([1]);
|
|
2598
|
-
const amountBytes = new Uint8Array(8);
|
|
2599
|
-
new DataView(amountBytes.buffer).setBigUint64(
|
|
2600
|
-
0,
|
|
2601
|
-
BigInt(params.amount),
|
|
2602
|
-
true
|
|
2603
|
-
// little-endian
|
|
2604
|
-
);
|
|
2605
|
-
const data = new Uint8Array(41);
|
|
2606
|
-
data.set(discriminant, 0);
|
|
2607
|
-
data.set(amountBytes, 1);
|
|
2608
|
-
data.set(params.commitment, 9);
|
|
2609
|
-
return new import_web32.TransactionInstruction({
|
|
2610
|
-
programId: params.programId,
|
|
2611
|
-
keys: [
|
|
2612
|
-
// Account 0: Payer (signer, writable) - pays for transaction
|
|
2613
|
-
{ pubkey: params.payer, isSigner: true, isWritable: true },
|
|
2614
|
-
// Account 1: Pool (writable) - receives SOL
|
|
2615
|
-
{ pubkey: params.pool, isSigner: false, isWritable: true },
|
|
2616
|
-
// Account 2: System Program (readonly) - for transfers
|
|
2617
|
-
{ pubkey: import_web32.SystemProgram.programId, isSigner: false, isWritable: false },
|
|
2618
|
-
// Account 3: Merkle Tree (writable) - stores on-chain Merkle tree
|
|
2619
|
-
{ pubkey: params.merkleTree, isSigner: false, isWritable: true }
|
|
2620
|
-
],
|
|
2621
|
-
data: Buffer.from(data)
|
|
2622
|
-
});
|
|
2623
|
-
}
|
|
2624
|
-
function validateDepositParams(params) {
|
|
2625
|
-
if (!(params.programId instanceof import_web32.PublicKey)) {
|
|
2626
|
-
throw new Error("programId must be a PublicKey");
|
|
2627
|
-
}
|
|
2628
|
-
if (!(params.payer instanceof import_web32.PublicKey)) {
|
|
2629
|
-
throw new Error("payer must be a PublicKey");
|
|
2630
|
-
}
|
|
2631
|
-
if (!(params.pool instanceof import_web32.PublicKey)) {
|
|
2632
|
-
throw new Error("pool must be a PublicKey");
|
|
2633
|
-
}
|
|
2634
|
-
if (!(params.merkleTree instanceof import_web32.PublicKey)) {
|
|
2635
|
-
throw new Error("merkleTree must be a PublicKey");
|
|
2636
|
-
}
|
|
2637
|
-
if (typeof params.amount !== "number" || params.amount <= 0) {
|
|
2638
|
-
throw new Error("amount must be a positive number");
|
|
2639
|
-
}
|
|
2640
|
-
if (!(params.commitment instanceof Uint8Array)) {
|
|
2641
|
-
throw new Error("commitment must be a Uint8Array");
|
|
2642
|
-
}
|
|
2643
|
-
if (params.commitment.length !== 32) {
|
|
2644
|
-
throw new Error(
|
|
2645
|
-
`commitment must be 32 bytes (got ${params.commitment.length})`
|
|
2646
|
-
);
|
|
2647
|
-
}
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
2139
|
// src/utils/pda.ts
|
|
2651
|
-
var
|
|
2140
|
+
var import_web32 = require("@solana/web3.js");
|
|
2652
2141
|
init_utxo();
|
|
2653
2142
|
function getShieldPoolPDAs(programId, mint = NATIVE_SOL_MINT) {
|
|
2654
2143
|
const pid = programId || CLOAK_PROGRAM_ID;
|
|
2655
|
-
const [pool] =
|
|
2144
|
+
const [pool] = import_web32.PublicKey.findProgramAddressSync(
|
|
2656
2145
|
[Buffer.from("pool"), mint.toBuffer()],
|
|
2657
2146
|
pid
|
|
2658
2147
|
);
|
|
2659
|
-
const [merkleTree] =
|
|
2148
|
+
const [merkleTree] = import_web32.PublicKey.findProgramAddressSync(
|
|
2660
2149
|
[Buffer.from("merkle_tree"), mint.toBuffer()],
|
|
2661
2150
|
pid
|
|
2662
2151
|
);
|
|
2663
|
-
const [treasury] =
|
|
2152
|
+
const [treasury] = import_web32.PublicKey.findProgramAddressSync(
|
|
2664
2153
|
[Buffer.from("treasury"), mint.toBuffer()],
|
|
2665
2154
|
pid
|
|
2666
2155
|
);
|
|
2667
|
-
const [vaultAuthority] =
|
|
2156
|
+
const [vaultAuthority] = import_web32.PublicKey.findProgramAddressSync(
|
|
2668
2157
|
[Buffer.from("vault_authority"), mint.toBuffer()],
|
|
2669
2158
|
pid
|
|
2670
2159
|
);
|
|
@@ -2680,7 +2169,7 @@ function getNullifierPDA(poolPubkey, nullifier, programId) {
|
|
|
2680
2169
|
if (nullifier.length !== 32) {
|
|
2681
2170
|
throw new Error(`Nullifier must be 32 bytes, got ${nullifier.length}`);
|
|
2682
2171
|
}
|
|
2683
|
-
return
|
|
2172
|
+
return import_web32.PublicKey.findProgramAddressSync(
|
|
2684
2173
|
[Buffer.from("nullifier"), poolPubkey.toBuffer(), Buffer.from(nullifier)],
|
|
2685
2174
|
pid
|
|
2686
2175
|
);
|
|
@@ -2690,270 +2179,26 @@ function getSwapStatePDA(poolPubkey, nullifier, programId) {
|
|
|
2690
2179
|
if (nullifier.length !== 32) {
|
|
2691
2180
|
throw new Error(`Nullifier must be 32 bytes, got ${nullifier.length}`);
|
|
2692
2181
|
}
|
|
2693
|
-
return
|
|
2182
|
+
return import_web32.PublicKey.findProgramAddressSync(
|
|
2694
2183
|
[Buffer.from("swap_state"), poolPubkey.toBuffer(), Buffer.from(nullifier)],
|
|
2695
2184
|
pid
|
|
2696
2185
|
);
|
|
2697
2186
|
}
|
|
2698
2187
|
|
|
2699
|
-
// src/
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
var
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
}
|
|
2708
|
-
function resolveCircuitsUrl(circuitsPath2) {
|
|
2709
|
-
if (circuitsPath2.trim() !== "" && circuitsPath2.trim() !== DEFAULT_CIRCUITS_URL) {
|
|
2710
|
-
return DEFAULT_CIRCUITS_URL;
|
|
2188
|
+
// src/core/CloakSDK.ts
|
|
2189
|
+
init_utxo();
|
|
2190
|
+
|
|
2191
|
+
// src/utils/logger.ts
|
|
2192
|
+
var globalDebugEnabled = false;
|
|
2193
|
+
function checkEnvDebug() {
|
|
2194
|
+
if (typeof process !== "undefined" && process.env) {
|
|
2195
|
+
return !!(process.env.CLOAK_DEBUG === "1" || process.env.CLOAK_DEBUG === "true" || process.env.DEBUG?.includes("cloak"));
|
|
2711
2196
|
}
|
|
2712
|
-
return
|
|
2197
|
+
return false;
|
|
2713
2198
|
}
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
|
2718
|
-
}
|
|
2719
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
2720
|
-
return new Uint8Array(arrayBuffer);
|
|
2721
|
-
}
|
|
2722
|
-
async function sha256Hex(data) {
|
|
2723
|
-
if (IS_BROWSER) {
|
|
2724
|
-
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
2725
|
-
const buffer = bytes.buffer.slice(
|
|
2726
|
-
bytes.byteOffset,
|
|
2727
|
-
bytes.byteOffset + bytes.byteLength
|
|
2728
|
-
);
|
|
2729
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
|
|
2730
|
-
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2731
|
-
}
|
|
2732
|
-
const nodeCrypto = await getNodeCrypto();
|
|
2733
|
-
return nodeCrypto.createHash("sha256").update(data).digest("hex");
|
|
2734
|
-
}
|
|
2735
|
-
var _nodeCrypto = null;
|
|
2736
|
-
function nodeRequire(moduleName) {
|
|
2737
|
-
if (IS_BROWSER) {
|
|
2738
|
-
throw new Error(`Node.js ${moduleName} module not available in browser/React Native`);
|
|
2739
|
-
}
|
|
2740
|
-
try {
|
|
2741
|
-
const requireFunc = new Function("moduleName", "return require(moduleName)");
|
|
2742
|
-
return requireFunc(moduleName);
|
|
2743
|
-
} catch {
|
|
2744
|
-
throw new Error(`Cannot load Node.js module ${moduleName} synchronously in ESM.`);
|
|
2745
|
-
}
|
|
2746
|
-
}
|
|
2747
|
-
async function getNodeCrypto() {
|
|
2748
|
-
if (_nodeCrypto) return _nodeCrypto;
|
|
2749
|
-
_nodeCrypto = nodeRequire("crypto");
|
|
2750
|
-
return _nodeCrypto;
|
|
2751
|
-
}
|
|
2752
|
-
async function generateWithdrawRegularProof(inputs, circuitsPath2) {
|
|
2753
|
-
const circuitsUrl = resolveCircuitsUrl(circuitsPath2);
|
|
2754
|
-
let wasmInput;
|
|
2755
|
-
let zkeyInput;
|
|
2756
|
-
if (IS_BROWSER) {
|
|
2757
|
-
wasmInput = `${circuitsUrl}/withdraw_regular_js/withdraw_regular.wasm`;
|
|
2758
|
-
zkeyInput = `${circuitsUrl}/withdraw_regular_final.zkey`;
|
|
2759
|
-
} else {
|
|
2760
|
-
const wasmUrl = `${circuitsUrl}/withdraw_regular_js/withdraw_regular.wasm`;
|
|
2761
|
-
const zkeyUrl = `${circuitsUrl}/withdraw_regular_final.zkey`;
|
|
2762
|
-
const [wasmData, zkeyData] = await Promise.all([
|
|
2763
|
-
fetchFileAsBuffer(wasmUrl),
|
|
2764
|
-
fetchFileAsBuffer(zkeyUrl)
|
|
2765
|
-
]);
|
|
2766
|
-
wasmInput = wasmData;
|
|
2767
|
-
zkeyInput = zkeyData;
|
|
2768
|
-
}
|
|
2769
|
-
const verification = await verifyCircuitIntegrity(circuitsUrl, "withdraw_regular");
|
|
2770
|
-
if (!verification.valid) {
|
|
2771
|
-
throw new Error(
|
|
2772
|
-
`withdraw_regular circuit integrity verification failed: ${verification.error ?? "hash mismatch"}`
|
|
2773
|
-
);
|
|
2774
|
-
}
|
|
2775
|
-
const circuitInputs = {
|
|
2776
|
-
// Public signals
|
|
2777
|
-
root: inputs.root.toString(),
|
|
2778
|
-
nullifier: inputs.nullifier.toString(),
|
|
2779
|
-
outputs_hash: inputs.outputs_hash.toString(),
|
|
2780
|
-
public_amount: inputs.public_amount.toString(),
|
|
2781
|
-
// Private common inputs
|
|
2782
|
-
amount: inputs.amount.toString(),
|
|
2783
|
-
leaf_index: inputs.leaf_index.toString(),
|
|
2784
|
-
sk: inputs.sk.map((x) => x.toString()),
|
|
2785
|
-
r: inputs.r.map((x) => x.toString()),
|
|
2786
|
-
pathElements: inputs.pathElements.map((x) => x.toString()),
|
|
2787
|
-
pathIndices: inputs.pathIndices,
|
|
2788
|
-
// Outputs
|
|
2789
|
-
num_outputs: inputs.num_outputs,
|
|
2790
|
-
out_addr: inputs.out_addr.map((addr) => addr.map((x) => x.toString())),
|
|
2791
|
-
out_amount: inputs.out_amount.map((x) => x.toString()),
|
|
2792
|
-
out_flags: inputs.out_flags,
|
|
2793
|
-
// Fee calculation
|
|
2794
|
-
var_fee: inputs.var_fee.toString(),
|
|
2795
|
-
rem: inputs.rem.toString()
|
|
2796
|
-
};
|
|
2797
|
-
const { proof, publicSignals } = await snarkjs.groth16.fullProve(circuitInputs, wasmInput, zkeyInput);
|
|
2798
|
-
const proofBytes = proofToBytes(proof);
|
|
2799
|
-
return {
|
|
2800
|
-
proof,
|
|
2801
|
-
publicSignals,
|
|
2802
|
-
proofBytes,
|
|
2803
|
-
publicInputsBytes: new Uint8Array(0)
|
|
2804
|
-
// Not used, kept for compatibility
|
|
2805
|
-
};
|
|
2806
|
-
}
|
|
2807
|
-
async function generateWithdrawSwapProof(inputs, circuitsPath2) {
|
|
2808
|
-
const circuitsUrl = resolveCircuitsUrl(circuitsPath2);
|
|
2809
|
-
let wasmInput;
|
|
2810
|
-
let zkeyInput;
|
|
2811
|
-
if (IS_BROWSER) {
|
|
2812
|
-
wasmInput = `${circuitsUrl}/withdraw_swap_js/withdraw_swap.wasm`;
|
|
2813
|
-
zkeyInput = `${circuitsUrl}/withdraw_swap_final.zkey`;
|
|
2814
|
-
} else {
|
|
2815
|
-
const wasmUrl = `${circuitsUrl}/withdraw_swap_js/withdraw_swap.wasm`;
|
|
2816
|
-
const zkeyUrl = `${circuitsUrl}/withdraw_swap_final.zkey`;
|
|
2817
|
-
const [wasmData, zkeyData] = await Promise.all([
|
|
2818
|
-
fetchFileAsBuffer(wasmUrl),
|
|
2819
|
-
fetchFileAsBuffer(zkeyUrl)
|
|
2820
|
-
]);
|
|
2821
|
-
wasmInput = wasmData;
|
|
2822
|
-
zkeyInput = zkeyData;
|
|
2823
|
-
}
|
|
2824
|
-
const verification = await verifyCircuitIntegrity(circuitsUrl, "withdraw_swap");
|
|
2825
|
-
if (!verification.valid) {
|
|
2826
|
-
throw new Error(
|
|
2827
|
-
`withdraw_swap circuit integrity verification failed: ${verification.error ?? "hash mismatch"}`
|
|
2828
|
-
);
|
|
2829
|
-
}
|
|
2830
|
-
const sk = splitTo2Limbs(inputs.sk_spend);
|
|
2831
|
-
const r = splitTo2Limbs(inputs.r);
|
|
2832
|
-
const circuitInputs = {
|
|
2833
|
-
// Public signals (must be provided for witness generation)
|
|
2834
|
-
root: inputs.root.toString(),
|
|
2835
|
-
nullifier: inputs.nullifier.toString(),
|
|
2836
|
-
outputs_hash: inputs.outputs_hash.toString(),
|
|
2837
|
-
public_amount: inputs.public_amount.toString(),
|
|
2838
|
-
// Private inputs
|
|
2839
|
-
sk: sk.map((x) => x.toString()),
|
|
2840
|
-
r: r.map((x) => x.toString()),
|
|
2841
|
-
amount: inputs.amount.toString(),
|
|
2842
|
-
leaf_index: inputs.leaf_index.toString(),
|
|
2843
|
-
pathElements: inputs.path_elements.map((x) => x.toString()),
|
|
2844
|
-
pathIndices: inputs.path_indices,
|
|
2845
|
-
input_mint: inputs.input_mint.map((x) => x.toString()),
|
|
2846
|
-
output_mint: inputs.output_mint.map((x) => x.toString()),
|
|
2847
|
-
recipient_ata: inputs.recipient_ata.map((x) => x.toString()),
|
|
2848
|
-
min_output_amount: inputs.min_output_amount.toString(),
|
|
2849
|
-
// Note: fee is computed off-chain and validated on-chain:
|
|
2850
|
-
// - fixed_fee is 5000000 (0.005 SOL)
|
|
2851
|
-
// - var_fee and rem are computed from amount * 3 / 1000 (0.3%)
|
|
2852
|
-
var_fee: inputs.var_fee.toString(),
|
|
2853
|
-
rem: inputs.rem.toString()
|
|
2854
|
-
};
|
|
2855
|
-
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
|
|
2856
|
-
circuitInputs,
|
|
2857
|
-
wasmInput,
|
|
2858
|
-
zkeyInput
|
|
2859
|
-
);
|
|
2860
|
-
const proofBytes = proofToBytes(proof);
|
|
2861
|
-
return {
|
|
2862
|
-
proof,
|
|
2863
|
-
publicSignals,
|
|
2864
|
-
proofBytes,
|
|
2865
|
-
publicInputsBytes: new Uint8Array(0)
|
|
2866
|
-
// Not used, kept for compatibility
|
|
2867
|
-
};
|
|
2868
|
-
}
|
|
2869
|
-
async function areCircuitsAvailable(circuitsPath2) {
|
|
2870
|
-
if (!circuitsPath2 || circuitsPath2 === "") {
|
|
2871
|
-
return false;
|
|
2872
|
-
}
|
|
2873
|
-
return isUrl(resolveCircuitsUrl(circuitsPath2));
|
|
2874
|
-
}
|
|
2875
|
-
async function getDefaultCircuitsPath() {
|
|
2876
|
-
return DEFAULT_CIRCUITS_URL;
|
|
2877
|
-
}
|
|
2878
|
-
var EXPECTED_CIRCUIT_HASHES = {
|
|
2879
|
-
withdraw_regular_wasm: "b80c364c926332111945ef437b07a71720fae774e08206cb11b6171f2a2420a3",
|
|
2880
|
-
withdraw_regular_zkey: "6dbca2612f7cc257b897d93384ae9dd09c1617fd1668f8287efb1b124488d144",
|
|
2881
|
-
withdraw_swap_wasm: "a296badc0047c211a4724c96a4e64a77cd75fbde7cfa4cf23af3640a723e5686",
|
|
2882
|
-
withdraw_swap_zkey: "95251954feaac80396651d12a0df3288630fa75db9410aebecb8aaee17a4f6ea"
|
|
2883
|
-
};
|
|
2884
|
-
async function verifyCircuitIntegrity(circuitsPath2, circuit) {
|
|
2885
|
-
const circuitsUrl = resolveCircuitsUrl(circuitsPath2);
|
|
2886
|
-
if (SKIP_CIRCUIT_VERIFY_ENV) {
|
|
2887
|
-
return {
|
|
2888
|
-
valid: true,
|
|
2889
|
-
circuit,
|
|
2890
|
-
error: "Verification skipped (CLOAK_SKIP_CIRCUIT_INTEGRITY_CHECK=1)"
|
|
2891
|
-
};
|
|
2892
|
-
}
|
|
2893
|
-
const expected = circuit === "withdraw_regular" ? {
|
|
2894
|
-
wasm: EXPECTED_CIRCUIT_HASHES.withdraw_regular_wasm,
|
|
2895
|
-
zkey: EXPECTED_CIRCUIT_HASHES.withdraw_regular_zkey
|
|
2896
|
-
} : {
|
|
2897
|
-
wasm: EXPECTED_CIRCUIT_HASHES.withdraw_swap_wasm,
|
|
2898
|
-
zkey: EXPECTED_CIRCUIT_HASHES.withdraw_swap_zkey
|
|
2899
|
-
};
|
|
2900
|
-
const wasmPath = `${circuitsUrl}/${circuit}_js/${circuit}.wasm`;
|
|
2901
|
-
const zkeyPath = `${circuitsUrl}/${circuit}_final.zkey`;
|
|
2902
|
-
try {
|
|
2903
|
-
const [wasmBytes, zkeyBytes] = await Promise.all([
|
|
2904
|
-
fetchFileAsBuffer(wasmPath),
|
|
2905
|
-
fetchFileAsBuffer(zkeyPath)
|
|
2906
|
-
]);
|
|
2907
|
-
const computed = {
|
|
2908
|
-
wasm: await sha256Hex(wasmBytes),
|
|
2909
|
-
zkey: await sha256Hex(zkeyBytes)
|
|
2910
|
-
};
|
|
2911
|
-
if (computed.wasm === expected.wasm && computed.zkey === expected.zkey) {
|
|
2912
|
-
return {
|
|
2913
|
-
valid: true,
|
|
2914
|
-
circuit,
|
|
2915
|
-
computed,
|
|
2916
|
-
expected
|
|
2917
|
-
};
|
|
2918
|
-
}
|
|
2919
|
-
return {
|
|
2920
|
-
valid: false,
|
|
2921
|
-
circuit,
|
|
2922
|
-
error: `Circuit artifact hash mismatch for ${circuit}`,
|
|
2923
|
-
computed,
|
|
2924
|
-
expected
|
|
2925
|
-
};
|
|
2926
|
-
} catch (error) {
|
|
2927
|
-
return {
|
|
2928
|
-
valid: false,
|
|
2929
|
-
circuit,
|
|
2930
|
-
error: `Circuit verification failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2931
|
-
};
|
|
2932
|
-
}
|
|
2933
|
-
}
|
|
2934
|
-
async function verifyAllCircuits(circuitsPath2) {
|
|
2935
|
-
const circuitsUrl = resolveCircuitsUrl(circuitsPath2);
|
|
2936
|
-
const results = await Promise.all([
|
|
2937
|
-
verifyCircuitIntegrity(circuitsUrl, "withdraw_regular"),
|
|
2938
|
-
verifyCircuitIntegrity(circuitsUrl, "withdraw_swap")
|
|
2939
|
-
]);
|
|
2940
|
-
return results;
|
|
2941
|
-
}
|
|
2942
|
-
|
|
2943
|
-
// src/core/CloakSDK.ts
|
|
2944
|
-
init_utxo();
|
|
2945
|
-
|
|
2946
|
-
// src/utils/logger.ts
|
|
2947
|
-
var globalDebugEnabled = false;
|
|
2948
|
-
function checkEnvDebug() {
|
|
2949
|
-
if (typeof process !== "undefined" && process.env) {
|
|
2950
|
-
return !!(process.env.CLOAK_DEBUG === "1" || process.env.CLOAK_DEBUG === "true" || process.env.DEBUG?.includes("cloak"));
|
|
2951
|
-
}
|
|
2952
|
-
return false;
|
|
2953
|
-
}
|
|
2954
|
-
globalDebugEnabled = checkEnvDebug();
|
|
2955
|
-
function setDebugMode(enabled) {
|
|
2956
|
-
globalDebugEnabled = enabled;
|
|
2199
|
+
globalDebugEnabled = checkEnvDebug();
|
|
2200
|
+
function setDebugMode(enabled) {
|
|
2201
|
+
globalDebugEnabled = enabled;
|
|
2957
2202
|
}
|
|
2958
2203
|
function isDebugEnabled() {
|
|
2959
2204
|
return globalDebugEnabled;
|
|
@@ -3159,50 +2404,22 @@ async function computeProofInternal(leafIndex, _nextIndex, subtrees) {
|
|
|
3159
2404
|
}
|
|
3160
2405
|
|
|
3161
2406
|
// src/core/CloakSDK.ts
|
|
3162
|
-
var
|
|
3163
|
-
function createSetLoadedAccountsDataSizeLimitInstruction(bytes) {
|
|
3164
|
-
const data = Buffer.alloc(5);
|
|
3165
|
-
data.writeUInt8(4, 0);
|
|
3166
|
-
data.writeUInt32LE(bytes, 1);
|
|
3167
|
-
return new import_web35.TransactionInstruction({
|
|
3168
|
-
keys: [],
|
|
3169
|
-
programId: COMPUTE_BUDGET_PROGRAM_ID,
|
|
3170
|
-
data
|
|
3171
|
-
});
|
|
3172
|
-
}
|
|
3173
|
-
var CLOAK_PROGRAM_ID = new import_web35.PublicKey("zh1eLd6rSphLejbFfJEneUwzHRfMKxgzrgkfwA6qRkW");
|
|
2407
|
+
var CLOAK_PROGRAM_ID = new import_web33.PublicKey("zh1eLd6rSphLejbFfJEneUwzHRfMKxgzrgkfwA6qRkW");
|
|
3174
2408
|
var CLOAK_API_URL = "https://api.cloak.ag";
|
|
3175
2409
|
var CloakSDK = class {
|
|
3176
|
-
/**
|
|
3177
|
-
* Create a new Cloak SDK client
|
|
3178
|
-
*
|
|
3179
|
-
* @param config - Client configuration
|
|
3180
|
-
*
|
|
3181
|
-
* @example Node.js mode (with keypair)
|
|
3182
|
-
* ```typescript
|
|
3183
|
-
* const sdk = new CloakSDK({
|
|
3184
|
-
* keypairBytes: keypair.secretKey,
|
|
3185
|
-
* network: "devnet"
|
|
3186
|
-
* });
|
|
3187
|
-
* ```
|
|
3188
|
-
*
|
|
3189
|
-
* @example Browser mode (with wallet adapter)
|
|
3190
|
-
* ```typescript
|
|
3191
|
-
* const sdk = new CloakSDK({
|
|
3192
|
-
* wallet: walletAdapter,
|
|
3193
|
-
* network: "devnet"
|
|
3194
|
-
* });
|
|
3195
|
-
* ```
|
|
3196
|
-
*/
|
|
3197
2410
|
constructor(config) {
|
|
3198
2411
|
if (!config.keypairBytes && !config.wallet) {
|
|
3199
|
-
throw new
|
|
2412
|
+
throw new CloakError(
|
|
2413
|
+
"Must provide either keypairBytes (Node.js) or wallet (Browser)",
|
|
2414
|
+
"validation",
|
|
2415
|
+
false
|
|
2416
|
+
);
|
|
3200
2417
|
}
|
|
3201
2418
|
if (config.debug) {
|
|
3202
2419
|
setDebugMode(true);
|
|
3203
2420
|
}
|
|
3204
2421
|
if (config.keypairBytes) {
|
|
3205
|
-
this.keypair =
|
|
2422
|
+
this.keypair = import_web33.Keypair.fromSecretKey(config.keypairBytes);
|
|
3206
2423
|
}
|
|
3207
2424
|
this.wallet = config.wallet;
|
|
3208
2425
|
this.cloakKeys = config.cloakKeys;
|
|
@@ -3216,902 +2433,42 @@ var CloakSDK = class {
|
|
|
3216
2433
|
}
|
|
3217
2434
|
}
|
|
3218
2435
|
const programId = config.programId || CLOAK_PROGRAM_ID;
|
|
3219
|
-
const { pool, merkleTree, treasury } = getShieldPoolPDAs(
|
|
3220
|
-
programId,
|
|
3221
|
-
NATIVE_SOL_MINT
|
|
3222
|
-
);
|
|
2436
|
+
const { pool, merkleTree, treasury } = getShieldPoolPDAs(programId, NATIVE_SOL_MINT);
|
|
3223
2437
|
this.config = {
|
|
3224
2438
|
network: config.network || "mainnet",
|
|
3225
|
-
keypairBytes: config.keypairBytes,
|
|
3226
2439
|
cloakKeys: config.cloakKeys,
|
|
3227
2440
|
programId,
|
|
3228
|
-
poolAddress: pool,
|
|
3229
|
-
merkleTreeAddress: merkleTree,
|
|
3230
|
-
treasuryAddress: treasury,
|
|
3231
|
-
debug: config.debug
|
|
3232
|
-
};
|
|
3233
|
-
}
|
|
3234
|
-
/**
|
|
3235
|
-
* Get the public key for deposits (from keypair or wallet)
|
|
3236
|
-
*/
|
|
3237
|
-
getPublicKey() {
|
|
3238
|
-
if (this.keypair) {
|
|
3239
|
-
return this.keypair.publicKey;
|
|
3240
|
-
}
|
|
3241
|
-
if (this.wallet?.publicKey) {
|
|
3242
|
-
return this.wallet.publicKey;
|
|
3243
|
-
}
|
|
3244
|
-
throw new Error("No public key available - wallet not connected or keypair not provided");
|
|
3245
|
-
}
|
|
3246
|
-
/**
|
|
3247
|
-
* Check if the SDK is using a wallet adapter
|
|
3248
|
-
*/
|
|
3249
|
-
isWalletMode() {
|
|
3250
|
-
return !!this.wallet && !this.keypair;
|
|
3251
|
-
}
|
|
3252
|
-
/**
|
|
3253
|
-
* Deposit SOL into the Cloak protocol
|
|
3254
|
-
*
|
|
3255
|
-
* Creates a new note (or uses a provided one), submits a deposit transaction,
|
|
3256
|
-
* and registers with the indexer.
|
|
3257
|
-
*
|
|
3258
|
-
* @param connection - Solana connection
|
|
3259
|
-
* @param payer - Payer wallet with sendTransaction method
|
|
3260
|
-
* @param amountOrNote - Amount in lamports OR an existing note to deposit
|
|
3261
|
-
* @param options - Optional configuration
|
|
3262
|
-
* @returns Deposit result with note and transaction info
|
|
3263
|
-
*
|
|
3264
|
-
* @example
|
|
3265
|
-
* ```typescript
|
|
3266
|
-
* // Generate and deposit in one step
|
|
3267
|
-
* const result = await client.deposit(
|
|
3268
|
-
* connection,
|
|
3269
|
-
* wallet,
|
|
3270
|
-
* 1_000_000_000,
|
|
3271
|
-
* {
|
|
3272
|
-
* onProgress: (status) => console.log(status)
|
|
3273
|
-
* }
|
|
3274
|
-
* );
|
|
3275
|
-
*
|
|
3276
|
-
* // Or deposit a pre-generated note
|
|
3277
|
-
* const note = client.generateNote(1_000_000_000);
|
|
3278
|
-
* const result = await client.deposit(connection, wallet, note);
|
|
3279
|
-
* ```
|
|
3280
|
-
*/
|
|
3281
|
-
async deposit(connection, amountOrNote, options) {
|
|
3282
|
-
try {
|
|
3283
|
-
let note;
|
|
3284
|
-
let isNewNote = false;
|
|
3285
|
-
if (typeof amountOrNote === "number") {
|
|
3286
|
-
options?.onProgress?.("generating_note", { message: "Generating note with secrets..." });
|
|
3287
|
-
note = await generateNote(amountOrNote, this.config.network);
|
|
3288
|
-
isNewNote = true;
|
|
3289
|
-
} else {
|
|
3290
|
-
note = amountOrNote;
|
|
3291
|
-
if (note.depositSignature) {
|
|
3292
|
-
throw new Error("Note has already been deposited");
|
|
3293
|
-
}
|
|
3294
|
-
}
|
|
3295
|
-
if (isNewNote) {
|
|
3296
|
-
await this.storage.saveNote(note);
|
|
3297
|
-
if (options?.onNoteGenerated) {
|
|
3298
|
-
options?.onProgress?.("awaiting_note_acknowledgment", {
|
|
3299
|
-
message: "Waiting for note to be saved before proceeding with deposit..."
|
|
3300
|
-
});
|
|
3301
|
-
try {
|
|
3302
|
-
await options.onNoteGenerated(note);
|
|
3303
|
-
} catch (error) {
|
|
3304
|
-
throw new Error(
|
|
3305
|
-
`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.`
|
|
3306
|
-
);
|
|
3307
|
-
}
|
|
3308
|
-
}
|
|
3309
|
-
options?.onProgress?.("note_saved", {
|
|
3310
|
-
message: "Note saved. Proceeding with on-chain deposit..."
|
|
3311
|
-
});
|
|
3312
|
-
}
|
|
3313
|
-
const payerPubkey = this.getPublicKey();
|
|
3314
|
-
const balance = await connection.getBalance(payerPubkey);
|
|
3315
|
-
const requiredAmount = note.amount + 5e3;
|
|
3316
|
-
if (balance < requiredAmount) {
|
|
3317
|
-
throw new Error(
|
|
3318
|
-
`Insufficient balance. Required: ${requiredAmount} lamports (${note.amount} + fees), Available: ${balance} lamports`
|
|
3319
|
-
);
|
|
3320
|
-
}
|
|
3321
|
-
const commitmentBytes = hexToBytes(note.commitment);
|
|
3322
|
-
const programId = this.config.programId || CLOAK_PROGRAM_ID;
|
|
3323
|
-
const depositIx = createDepositInstruction({
|
|
3324
|
-
programId,
|
|
3325
|
-
payer: payerPubkey,
|
|
3326
|
-
pool: this.config.poolAddress,
|
|
3327
|
-
merkleTree: this.config.merkleTreeAddress,
|
|
3328
|
-
amount: note.amount,
|
|
3329
|
-
commitment: commitmentBytes
|
|
3330
|
-
});
|
|
3331
|
-
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
|
|
3332
|
-
const priorityFee = options?.priorityFee ?? 1e4;
|
|
3333
|
-
const cuPriceIx = import_web35.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee });
|
|
3334
|
-
const dataSizeLimit = options?.loadedAccountsDataSizeLimit ?? 256 * 1024;
|
|
3335
|
-
const dataSizeLimitIx = dataSizeLimit > 0 ? createSetLoadedAccountsDataSizeLimitInstruction(dataSizeLimit) : null;
|
|
3336
|
-
let computeUnits;
|
|
3337
|
-
if (options?.optimizeCU && this.keypair) {
|
|
3338
|
-
options?.onProgress?.("simulating", { message: "Simulating transaction for optimal CU..." });
|
|
3339
|
-
const simCuLimitIx = import_web35.ComputeBudgetProgram.setComputeUnitLimit({ units: 2e5 });
|
|
3340
|
-
const simTransaction = new import_web35.Transaction({
|
|
3341
|
-
feePayer: payerPubkey,
|
|
3342
|
-
recentBlockhash: blockhash
|
|
3343
|
-
}).add(simCuLimitIx).add(cuPriceIx);
|
|
3344
|
-
if (dataSizeLimitIx) {
|
|
3345
|
-
simTransaction.add(dataSizeLimitIx);
|
|
3346
|
-
}
|
|
3347
|
-
simTransaction.add(depositIx);
|
|
3348
|
-
try {
|
|
3349
|
-
const simulation = await connection.simulateTransaction(simTransaction, [this.keypair]);
|
|
3350
|
-
if (simulation.value.err) {
|
|
3351
|
-
console.warn("Simulation failed, using default CU:", simulation.value.err);
|
|
3352
|
-
computeUnits = 4e4;
|
|
3353
|
-
} else {
|
|
3354
|
-
const simulatedCU = simulation.value.unitsConsumed ?? 3e4;
|
|
3355
|
-
computeUnits = Math.ceil(simulatedCU * 1.2);
|
|
3356
|
-
console.log(`CU optimization: ${simulatedCU} simulated \u2192 ${computeUnits} limit`);
|
|
3357
|
-
}
|
|
3358
|
-
} catch (simError) {
|
|
3359
|
-
console.warn("Simulation RPC error, using default CU:", simError);
|
|
3360
|
-
computeUnits = 4e4;
|
|
3361
|
-
}
|
|
3362
|
-
} else if (options?.optimizeCU && this.wallet) {
|
|
3363
|
-
console.warn("CU optimization via simulation not available in wallet mode (would require double signing). Using default.");
|
|
3364
|
-
computeUnits = options?.computeUnits ?? 4e4;
|
|
3365
|
-
} else {
|
|
3366
|
-
computeUnits = options?.computeUnits ?? 4e4;
|
|
3367
|
-
}
|
|
3368
|
-
const cuLimitIx = import_web35.ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits });
|
|
3369
|
-
const transaction = new import_web35.Transaction({
|
|
3370
|
-
feePayer: payerPubkey,
|
|
3371
|
-
recentBlockhash: blockhash
|
|
3372
|
-
}).add(cuLimitIx).add(cuPriceIx);
|
|
3373
|
-
if (dataSizeLimitIx) {
|
|
3374
|
-
transaction.add(dataSizeLimitIx);
|
|
3375
|
-
}
|
|
3376
|
-
transaction.add(depositIx);
|
|
3377
|
-
if (!transaction.feePayer) {
|
|
3378
|
-
throw new Error("Transaction feePayer is not set");
|
|
3379
|
-
}
|
|
3380
|
-
if (!transaction.recentBlockhash) {
|
|
3381
|
-
throw new Error("Transaction recentBlockhash is not set");
|
|
3382
|
-
}
|
|
3383
|
-
let signature;
|
|
3384
|
-
if (this.wallet) {
|
|
3385
|
-
if (!this.wallet.publicKey) {
|
|
3386
|
-
throw new Error("Wallet not connected - publicKey is null");
|
|
3387
|
-
}
|
|
3388
|
-
if (this.wallet.signTransaction) {
|
|
3389
|
-
try {
|
|
3390
|
-
const signedTransaction = await this.wallet.signTransaction(transaction);
|
|
3391
|
-
const rawTransaction = signedTransaction.serialize();
|
|
3392
|
-
signature = await connection.sendRawTransaction(rawTransaction, {
|
|
3393
|
-
skipPreflight: options?.skipPreflight || false,
|
|
3394
|
-
preflightCommitment: "confirmed",
|
|
3395
|
-
maxRetries: 3
|
|
3396
|
-
});
|
|
3397
|
-
} catch (signError) {
|
|
3398
|
-
if (this.wallet.sendTransaction) {
|
|
3399
|
-
signature = await this.wallet.sendTransaction(transaction, connection, {
|
|
3400
|
-
skipPreflight: options?.skipPreflight || false,
|
|
3401
|
-
preflightCommitment: "confirmed",
|
|
3402
|
-
maxRetries: 3
|
|
3403
|
-
});
|
|
3404
|
-
} else {
|
|
3405
|
-
throw signError;
|
|
3406
|
-
}
|
|
3407
|
-
}
|
|
3408
|
-
} else if (this.wallet.sendTransaction) {
|
|
3409
|
-
signature = await this.wallet.sendTransaction(transaction, connection, {
|
|
3410
|
-
skipPreflight: options?.skipPreflight || false,
|
|
3411
|
-
preflightCommitment: "confirmed",
|
|
3412
|
-
maxRetries: 3
|
|
3413
|
-
});
|
|
3414
|
-
} else {
|
|
3415
|
-
throw new Error("Wallet adapter must provide either sendTransaction or signTransaction");
|
|
3416
|
-
}
|
|
3417
|
-
} else if (this.keypair) {
|
|
3418
|
-
signature = await connection.sendTransaction(transaction, [this.keypair], {
|
|
3419
|
-
skipPreflight: options?.skipPreflight || false,
|
|
3420
|
-
preflightCommitment: "confirmed",
|
|
3421
|
-
maxRetries: 3
|
|
3422
|
-
});
|
|
3423
|
-
} else {
|
|
3424
|
-
throw new Error("No signing method available - provide keypair or wallet");
|
|
3425
|
-
}
|
|
3426
|
-
const confirmation = await connection.confirmTransaction({
|
|
3427
|
-
signature,
|
|
3428
|
-
blockhash,
|
|
3429
|
-
lastValidBlockHeight
|
|
3430
|
-
});
|
|
3431
|
-
if (confirmation.value.err) {
|
|
3432
|
-
throw new Error(
|
|
3433
|
-
`Transaction failed: ${JSON.stringify(confirmation.value.err)}`
|
|
3434
|
-
);
|
|
3435
|
-
}
|
|
3436
|
-
const txDetails = await connection.getTransaction(signature, {
|
|
3437
|
-
commitment: "confirmed",
|
|
3438
|
-
maxSupportedTransactionVersion: 0
|
|
3439
|
-
});
|
|
3440
|
-
const depositSlot = txDetails?.slot ?? 0;
|
|
3441
|
-
let leafIndex = null;
|
|
3442
|
-
let root = null;
|
|
3443
|
-
try {
|
|
3444
|
-
if (txDetails?.meta?.logMessages) {
|
|
3445
|
-
for (const log of txDetails.meta.logMessages) {
|
|
3446
|
-
if (log.includes("Program data:")) {
|
|
3447
|
-
try {
|
|
3448
|
-
const parts = log.split("Program data:");
|
|
3449
|
-
if (parts.length > 1) {
|
|
3450
|
-
const base64Data = parts[1].trim();
|
|
3451
|
-
const eventData = Buffer.from(base64Data, "base64");
|
|
3452
|
-
if (eventData.length >= 81 && eventData[0] === 1) {
|
|
3453
|
-
const parsedLeafIndex = Number(eventData.readBigUInt64LE(1));
|
|
3454
|
-
const loggedCommitment = eventData.slice(9, 41);
|
|
3455
|
-
if (Buffer.compare(loggedCommitment, Buffer.from(commitmentBytes)) === 0) {
|
|
3456
|
-
leafIndex = parsedLeafIndex;
|
|
3457
|
-
const loggedRoot = eventData.slice(49, 81);
|
|
3458
|
-
root = Buffer.from(loggedRoot).toString("hex");
|
|
3459
|
-
break;
|
|
3460
|
-
}
|
|
3461
|
-
}
|
|
3462
|
-
}
|
|
3463
|
-
} catch (e) {
|
|
3464
|
-
continue;
|
|
3465
|
-
}
|
|
3466
|
-
}
|
|
3467
|
-
}
|
|
3468
|
-
}
|
|
3469
|
-
} catch (e) {
|
|
3470
|
-
}
|
|
3471
|
-
if (leafIndex === null) {
|
|
3472
|
-
const programId2 = this.config.programId || CLOAK_PROGRAM_ID;
|
|
3473
|
-
const mintForSOL = NATIVE_SOL_MINT;
|
|
3474
|
-
const { merkleTree } = getShieldPoolPDAs(programId2, mintForSOL);
|
|
3475
|
-
const merkleTreeAccount = await connection.getAccountInfo(merkleTree);
|
|
3476
|
-
if (!merkleTreeAccount) {
|
|
3477
|
-
throw new Error("Failed to fetch merkle tree account");
|
|
3478
|
-
}
|
|
3479
|
-
const nextIndex = Number(merkleTreeAccount.data.readBigUInt64LE(32));
|
|
3480
|
-
leafIndex = nextIndex - 1;
|
|
3481
|
-
const rootBytes = merkleTreeAccount.data.slice(1064, 1096);
|
|
3482
|
-
root = Buffer.from(rootBytes).toString("hex");
|
|
3483
|
-
}
|
|
3484
|
-
if (leafIndex === null || root === null) {
|
|
3485
|
-
throw new Error("Failed to get leaf index and root from transaction logs or on-chain state");
|
|
3486
|
-
}
|
|
3487
|
-
let merkleProof;
|
|
3488
|
-
try {
|
|
3489
|
-
const programId2 = this.config.programId || CLOAK_PROGRAM_ID;
|
|
3490
|
-
const mintForSOL = NATIVE_SOL_MINT;
|
|
3491
|
-
const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId2, mintForSOL);
|
|
3492
|
-
const chainProof = await computeProofFromChain(connection, merkleTreePDA, leafIndex);
|
|
3493
|
-
merkleProof = {
|
|
3494
|
-
pathElements: chainProof.pathElements,
|
|
3495
|
-
pathIndices: chainProof.pathIndices
|
|
3496
|
-
};
|
|
3497
|
-
root = chainProof.root;
|
|
3498
|
-
} catch (proofError) {
|
|
3499
|
-
console.warn("[SDK] Could not compute proof from chain, will fetch at withdrawal:", proofError);
|
|
3500
|
-
}
|
|
3501
|
-
const updatedNote = updateNoteWithDeposit(note, {
|
|
3502
|
-
signature,
|
|
3503
|
-
slot: depositSlot,
|
|
3504
|
-
leafIndex,
|
|
3505
|
-
root,
|
|
3506
|
-
merkleProof
|
|
3507
|
-
});
|
|
3508
|
-
await this.storage.updateNote(note.commitment, {
|
|
3509
|
-
depositSignature: signature,
|
|
3510
|
-
depositSlot,
|
|
3511
|
-
leafIndex,
|
|
3512
|
-
root,
|
|
3513
|
-
merkleProof
|
|
3514
|
-
});
|
|
3515
|
-
return {
|
|
3516
|
-
note: updatedNote,
|
|
3517
|
-
signature,
|
|
3518
|
-
leafIndex,
|
|
3519
|
-
root
|
|
3520
|
-
};
|
|
3521
|
-
} catch (error) {
|
|
3522
|
-
throw this.wrapError(error, "Deposit failed");
|
|
3523
|
-
}
|
|
3524
|
-
}
|
|
3525
|
-
/**
|
|
3526
|
-
* Private transfer with up to 5 recipients
|
|
3527
|
-
*
|
|
3528
|
-
* Handles the complete private transfer flow:
|
|
3529
|
-
* 1. If note is not deposited, deposits it first and waits for confirmation
|
|
3530
|
-
* 2. Generates a zero-knowledge proof
|
|
3531
|
-
* 3. Submits the withdrawal via relay service to recipients
|
|
3532
|
-
*
|
|
3533
|
-
* This is the main method for performing private transfers - it handles everything!
|
|
3534
|
-
*
|
|
3535
|
-
* @param connection - Solana connection (required for deposit if not already deposited)
|
|
3536
|
-
* @param payer - Payer wallet (required for deposit if not already deposited)
|
|
3537
|
-
* @param note - Note to spend (can be deposited or not)
|
|
3538
|
-
* @param recipients - Array of 1-5 recipients with amounts
|
|
3539
|
-
* @param options - Optional configuration
|
|
3540
|
-
* @returns Transfer result with signature and outputs
|
|
3541
|
-
*
|
|
3542
|
-
* @example
|
|
3543
|
-
* ```typescript
|
|
3544
|
-
* // Create a note (not deposited yet)
|
|
3545
|
-
* const note = client.generateNote(1_000_000_000);
|
|
3546
|
-
*
|
|
3547
|
-
* // privateTransfer handles the full flow: deposit + withdraw
|
|
3548
|
-
* const result = await client.privateTransfer(
|
|
3549
|
-
* connection,
|
|
3550
|
-
* wallet,
|
|
3551
|
-
* note,
|
|
3552
|
-
* [
|
|
3553
|
-
* { recipient: new PublicKey("..."), amount: 500_000_000 },
|
|
3554
|
-
* { recipient: new PublicKey("..."), amount: 492_500_000 }
|
|
3555
|
-
* ],
|
|
3556
|
-
* {
|
|
3557
|
-
* relayFeeBps: 50, // 0.5%
|
|
3558
|
-
* onProgress: (status) => console.log(status),
|
|
3559
|
-
* onProofProgress: (pct) => console.log(`Proof: ${pct}%`)
|
|
3560
|
-
* }
|
|
3561
|
-
* );
|
|
3562
|
-
* console.log(`Success! TX: ${result.signature}`);
|
|
3563
|
-
* ```
|
|
3564
|
-
*/
|
|
3565
|
-
async privateTransfer(connection, note, recipients, options) {
|
|
3566
|
-
if (!isWithdrawable(note)) {
|
|
3567
|
-
const depositResult = await this.deposit(connection, note, {
|
|
3568
|
-
skipPreflight: false
|
|
3569
|
-
});
|
|
3570
|
-
note = depositResult.note;
|
|
3571
|
-
}
|
|
3572
|
-
const protocolFee = note.amount - getDistributableAmount(note.amount);
|
|
3573
|
-
const feeBps = Math.ceil(protocolFee * 1e4 / note.amount);
|
|
3574
|
-
const distributableAmount = getDistributableAmount(note.amount);
|
|
3575
|
-
validateTransfers(recipients, distributableAmount);
|
|
3576
|
-
if (!note.leafIndex && note.leafIndex !== 0) {
|
|
3577
|
-
throw new Error("Note must have a leaf index (note must be deposited)");
|
|
3578
|
-
}
|
|
3579
|
-
if (!isValidHex(note.r, 32)) {
|
|
3580
|
-
throw new Error("Note r must be 64 hex characters (32 bytes)");
|
|
3581
|
-
}
|
|
3582
|
-
if (!isValidHex(note.sk_spend, 32)) {
|
|
3583
|
-
throw new Error("Note sk_spend must be 64 hex characters (32 bytes)");
|
|
3584
|
-
}
|
|
3585
|
-
const nullifier = await computeNullifierAsync(note.sk_spend, note.leafIndex);
|
|
3586
|
-
const nullifierHex = nullifier.toString(16).padStart(64, "0");
|
|
3587
|
-
const outputsHash = await computeOutputsHashAsync(recipients);
|
|
3588
|
-
const outputsHashHex = outputsHash.toString(16).padStart(64, "0");
|
|
3589
|
-
const circuitsPath2 = await getDefaultCircuitsPath();
|
|
3590
|
-
const useDirectProof = await areCircuitsAvailable(circuitsPath2);
|
|
3591
|
-
if (!useDirectProof) {
|
|
3592
|
-
throw new Error(
|
|
3593
|
-
`Circuits not available at ${circuitsPath2}. Make sure circuits are built and accessible. Circuit artifacts are pinned to S3 and must be reachable from this environment.`
|
|
3594
|
-
);
|
|
3595
|
-
}
|
|
3596
|
-
const MAX_PROOF_RETRIES = 3;
|
|
3597
|
-
let lastError = null;
|
|
3598
|
-
for (let attempt = 0; attempt < MAX_PROOF_RETRIES; attempt++) {
|
|
3599
|
-
try {
|
|
3600
|
-
if (attempt > 0) {
|
|
3601
|
-
options?.onProgress?.(`proof_refresh_${attempt + 1}`);
|
|
3602
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3603
|
-
}
|
|
3604
|
-
let merkleProof;
|
|
3605
|
-
let merkleRoot;
|
|
3606
|
-
const hasStoredProof = note.merkleProof && note.root && note.merkleProof.pathElements && note.merkleProof.pathElements.length > 0;
|
|
3607
|
-
if (attempt === 0 && hasStoredProof) {
|
|
3608
|
-
merkleProof = note.merkleProof;
|
|
3609
|
-
merkleRoot = note.root;
|
|
3610
|
-
} else {
|
|
3611
|
-
const programId = this.config.programId || CLOAK_PROGRAM_ID;
|
|
3612
|
-
const mintForSOL = NATIVE_SOL_MINT;
|
|
3613
|
-
const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, mintForSOL);
|
|
3614
|
-
const chainProof = await computeProofFromChain(
|
|
3615
|
-
connection,
|
|
3616
|
-
merkleTreePDA,
|
|
3617
|
-
note.leafIndex
|
|
3618
|
-
);
|
|
3619
|
-
merkleProof = {
|
|
3620
|
-
pathElements: chainProof.pathElements,
|
|
3621
|
-
pathIndices: chainProof.pathIndices,
|
|
3622
|
-
root: chainProof.root
|
|
3623
|
-
};
|
|
3624
|
-
merkleRoot = chainProof.root;
|
|
3625
|
-
note.merkleProof = merkleProof;
|
|
3626
|
-
note.root = merkleRoot;
|
|
3627
|
-
}
|
|
3628
|
-
if (!merkleProof || !merkleRoot) {
|
|
3629
|
-
throw new Error("Failed to get Merkle proof from chain");
|
|
3630
|
-
}
|
|
3631
|
-
if (!merkleProof.pathElements || merkleProof.pathElements.length === 0) {
|
|
3632
|
-
throw new Error("Merkle proof is invalid: missing path elements");
|
|
3633
|
-
}
|
|
3634
|
-
if (merkleProof.pathElements.length !== merkleProof.pathIndices.length) {
|
|
3635
|
-
throw new Error("Merkle proof is invalid: path elements and indices length mismatch");
|
|
3636
|
-
}
|
|
3637
|
-
for (let i = 0; i < merkleProof.pathIndices.length; i++) {
|
|
3638
|
-
const idx = merkleProof.pathIndices[i];
|
|
3639
|
-
if (idx !== 0 && idx !== 1) {
|
|
3640
|
-
throw new Error(`Merkle proof path index at position ${i} must be 0 or 1, got ${idx}`);
|
|
3641
|
-
}
|
|
3642
|
-
}
|
|
3643
|
-
if (!isValidHex(merkleRoot, 32)) {
|
|
3644
|
-
throw new Error("Merkle root must be 64 hex characters (32 bytes)");
|
|
3645
|
-
}
|
|
3646
|
-
for (let i = 0; i < merkleProof.pathElements.length; i++) {
|
|
3647
|
-
const element = merkleProof.pathElements[i];
|
|
3648
|
-
if (typeof element !== "string" || !isValidHex(element, 32)) {
|
|
3649
|
-
throw new Error(`Merkle proof path element at position ${i} must be 64 hex characters (32 bytes)`);
|
|
3650
|
-
}
|
|
3651
|
-
}
|
|
3652
|
-
const sk_spend_bigint = BigInt("0x" + note.sk_spend);
|
|
3653
|
-
const r_bigint = BigInt("0x" + note.r);
|
|
3654
|
-
const root_bigint = BigInt("0x" + merkleRoot);
|
|
3655
|
-
const nullifier_bigint = BigInt("0x" + nullifierHex);
|
|
3656
|
-
const outputs_hash_bigint = BigInt("0x" + outputsHashHex);
|
|
3657
|
-
const amount_bigint = BigInt(note.amount);
|
|
3658
|
-
const pathElements = merkleProof.pathElements.map((p) => BigInt("0x" + p));
|
|
3659
|
-
const outAddr = [];
|
|
3660
|
-
for (let i = 0; i < 5; i++) {
|
|
3661
|
-
if (i < recipients.length) {
|
|
3662
|
-
const [lo, hi] = pubkeyToLimbs(recipients[i].recipient.toBytes());
|
|
3663
|
-
outAddr.push([lo, hi]);
|
|
3664
|
-
} else {
|
|
3665
|
-
outAddr.push([0n, 0n]);
|
|
3666
|
-
}
|
|
3667
|
-
}
|
|
3668
|
-
const t = amount_bigint * 3n;
|
|
3669
|
-
const varFee = t / 1000n;
|
|
3670
|
-
const rem = t % 1000n;
|
|
3671
|
-
const outAmount = [];
|
|
3672
|
-
const outFlags = [];
|
|
3673
|
-
for (let i = 0; i < 5; i++) {
|
|
3674
|
-
if (i < recipients.length) {
|
|
3675
|
-
outAmount.push(BigInt(recipients[i].amount));
|
|
3676
|
-
outFlags.push(1);
|
|
3677
|
-
} else {
|
|
3678
|
-
outAmount.push(0n);
|
|
3679
|
-
outFlags.push(0);
|
|
3680
|
-
}
|
|
3681
|
-
}
|
|
3682
|
-
const sk = splitTo2Limbs(sk_spend_bigint);
|
|
3683
|
-
const r = splitTo2Limbs(r_bigint);
|
|
3684
|
-
if (attempt === 0) {
|
|
3685
|
-
const computedCommitment = await computeCommitment(amount_bigint, r_bigint, sk_spend_bigint);
|
|
3686
|
-
const noteCommitment = BigInt("0x" + note.commitment);
|
|
3687
|
-
if (computedCommitment !== noteCommitment) {
|
|
3688
|
-
throw new Error(
|
|
3689
|
-
`Commitment mismatch! Computed: ${computedCommitment.toString(16)}, Note: ${note.commitment}. This means the note's sk_spend, r, or amount doesn't match what was deposited.`
|
|
3690
|
-
);
|
|
3691
|
-
}
|
|
3692
|
-
}
|
|
3693
|
-
const proofInputs = {
|
|
3694
|
-
root: root_bigint,
|
|
3695
|
-
nullifier: nullifier_bigint,
|
|
3696
|
-
outputs_hash: outputs_hash_bigint,
|
|
3697
|
-
public_amount: amount_bigint,
|
|
3698
|
-
amount: amount_bigint,
|
|
3699
|
-
leaf_index: BigInt(note.leafIndex),
|
|
3700
|
-
sk,
|
|
3701
|
-
r,
|
|
3702
|
-
pathElements,
|
|
3703
|
-
pathIndices: merkleProof.pathIndices,
|
|
3704
|
-
num_outputs: recipients.length,
|
|
3705
|
-
out_addr: outAddr,
|
|
3706
|
-
out_amount: outAmount,
|
|
3707
|
-
out_flags: outFlags,
|
|
3708
|
-
var_fee: varFee,
|
|
3709
|
-
rem
|
|
3710
|
-
};
|
|
3711
|
-
options?.onProgress?.("proof_generating");
|
|
3712
|
-
const proofResult = await generateWithdrawRegularProof(proofInputs, circuitsPath2);
|
|
3713
|
-
options?.onProgress?.("proof_complete");
|
|
3714
|
-
const proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
|
|
3715
|
-
const finalPublicInputs = {
|
|
3716
|
-
root: merkleRoot,
|
|
3717
|
-
nf: nullifierHex,
|
|
3718
|
-
outputs_hash: outputsHashHex,
|
|
3719
|
-
amount: note.amount
|
|
3720
|
-
};
|
|
3721
|
-
const metadataBundle = options?.metadataBundle;
|
|
3722
|
-
const signature = await this.relay.submitWithdraw(
|
|
3723
|
-
{
|
|
3724
|
-
proof: proofHex,
|
|
3725
|
-
publicInputs: finalPublicInputs,
|
|
3726
|
-
outputs: recipients.map((r2) => ({
|
|
3727
|
-
recipient: r2.recipient.toBase58(),
|
|
3728
|
-
amount: r2.amount
|
|
3729
|
-
})),
|
|
3730
|
-
feeBps,
|
|
3731
|
-
// Use calculated protocol fee BPS
|
|
3732
|
-
metadataBundle
|
|
3733
|
-
},
|
|
3734
|
-
options?.onProgress
|
|
3735
|
-
);
|
|
3736
|
-
return {
|
|
3737
|
-
signature,
|
|
3738
|
-
outputs: recipients.map((r2) => ({
|
|
3739
|
-
recipient: r2.recipient.toBase58(),
|
|
3740
|
-
amount: r2.amount
|
|
3741
|
-
})),
|
|
3742
|
-
nullifier: nullifierHex,
|
|
3743
|
-
root: merkleRoot
|
|
3744
|
-
};
|
|
3745
|
-
} catch (error) {
|
|
3746
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3747
|
-
if (error instanceof RootNotFoundError) {
|
|
3748
|
-
console.warn(
|
|
3749
|
-
`[Cloak SDK] Merkle root expired (attempt ${attempt + 1}/${MAX_PROOF_RETRIES}). Regenerating proof with fresh root...`
|
|
3750
|
-
);
|
|
3751
|
-
continue;
|
|
3752
|
-
}
|
|
3753
|
-
throw error;
|
|
3754
|
-
}
|
|
3755
|
-
}
|
|
3756
|
-
throw new Error(
|
|
3757
|
-
`Withdrawal failed after ${MAX_PROOF_RETRIES} proof refresh attempts. The Merkle tree is updating too rapidly. Last error: ${lastError?.message}`
|
|
3758
|
-
);
|
|
3759
|
-
}
|
|
3760
|
-
/**
|
|
3761
|
-
* Withdraw to a single recipient
|
|
3762
|
-
*
|
|
3763
|
-
* Convenience method for withdrawing to one address.
|
|
3764
|
-
* Handles the complete flow: deposits if needed, then withdraws.
|
|
3765
|
-
*
|
|
3766
|
-
* @param connection - Solana connection
|
|
3767
|
-
* @param payer - Payer wallet
|
|
3768
|
-
* @param note - Note to spend
|
|
3769
|
-
* @param recipient - Recipient address
|
|
3770
|
-
* @param options - Optional configuration
|
|
3771
|
-
* @returns Transfer result
|
|
3772
|
-
*
|
|
3773
|
-
* @example
|
|
3774
|
-
* ```typescript
|
|
3775
|
-
* const note = client.generateNote(1_000_000_000);
|
|
3776
|
-
* const result = await client.withdraw(
|
|
3777
|
-
* connection,
|
|
3778
|
-
* wallet,
|
|
3779
|
-
* note,
|
|
3780
|
-
* new PublicKey("..."),
|
|
3781
|
-
* { withdrawAll: true }
|
|
3782
|
-
* );
|
|
3783
|
-
* ```
|
|
3784
|
-
*/
|
|
3785
|
-
async withdraw(connection, note, recipient, options) {
|
|
3786
|
-
const withdrawAll = options?.withdrawAll ?? true;
|
|
3787
|
-
const amount = withdrawAll ? getDistributableAmount(note.amount) : options?.amount || note.amount;
|
|
3788
|
-
if (!withdrawAll && !options?.amount) {
|
|
3789
|
-
throw new Error("Must specify amount or set withdrawAll: true");
|
|
3790
|
-
}
|
|
3791
|
-
return this.privateTransfer(
|
|
3792
|
-
connection,
|
|
3793
|
-
note,
|
|
3794
|
-
[{ recipient, amount }],
|
|
3795
|
-
options
|
|
3796
|
-
);
|
|
3797
|
-
}
|
|
3798
|
-
/**
|
|
3799
|
-
* Send SOL privately to multiple recipients
|
|
3800
|
-
*
|
|
3801
|
-
* Convenience method that wraps privateTransfer with a simpler API.
|
|
3802
|
-
* Handles the complete flow: deposits if needed, then sends to recipients.
|
|
3803
|
-
*
|
|
3804
|
-
* @param connection - Solana connection
|
|
3805
|
-
* @param note - Note to spend
|
|
3806
|
-
* @param recipients - Array of 1-5 recipients with amounts
|
|
3807
|
-
* @param options - Optional configuration
|
|
3808
|
-
* @returns Transfer result
|
|
3809
|
-
*
|
|
3810
|
-
* @example
|
|
3811
|
-
* ```typescript
|
|
3812
|
-
* const note = client.generateNote(1_000_000_000);
|
|
3813
|
-
* const result = await client.send(
|
|
3814
|
-
* connection,
|
|
3815
|
-
* note,
|
|
3816
|
-
* [
|
|
3817
|
-
* { recipient: new PublicKey("..."), amount: 500_000_000 },
|
|
3818
|
-
* { recipient: new PublicKey("..."), amount: 492_500_000 }
|
|
3819
|
-
* ]
|
|
3820
|
-
* );
|
|
3821
|
-
* ```
|
|
3822
|
-
*/
|
|
3823
|
-
async send(connection, note, recipients, options) {
|
|
3824
|
-
return this.privateTransfer(connection, note, recipients, options);
|
|
3825
|
-
}
|
|
3826
|
-
/**
|
|
3827
|
-
* Swap SOL for tokens privately
|
|
3828
|
-
*
|
|
3829
|
-
* Withdraws SOL from a note and swaps it for tokens via the relay service.
|
|
3830
|
-
* Handles the complete flow: deposits if needed, generates proof, and submits swap.
|
|
3831
|
-
*
|
|
3832
|
-
* @param connection - Solana connection
|
|
3833
|
-
* @param note - Note to spend
|
|
3834
|
-
* @param recipient - Recipient address (will receive tokens)
|
|
3835
|
-
* @param options - Swap configuration
|
|
3836
|
-
* @returns Swap result with transaction signature
|
|
3837
|
-
*
|
|
3838
|
-
* @example
|
|
3839
|
-
* ```typescript
|
|
3840
|
-
* const note = client.generateNote(1_000_000_000);
|
|
3841
|
-
* const result = await client.swap(
|
|
3842
|
-
* connection,
|
|
3843
|
-
* note,
|
|
3844
|
-
* new PublicKey("..."), // recipient
|
|
3845
|
-
* {
|
|
3846
|
-
* outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
|
|
3847
|
-
* slippageBps: 100, // 1%
|
|
3848
|
-
* getQuote: async (amount, mint, slippage) => {
|
|
3849
|
-
* // Fetch quote from your swap API
|
|
3850
|
-
* const quote = await fetchSwapQuote(amount, mint, slippage);
|
|
3851
|
-
* return {
|
|
3852
|
-
* outAmount: quote.outAmount,
|
|
3853
|
-
* minOutputAmount: quote.minOutputAmount
|
|
3854
|
-
* };
|
|
3855
|
-
* }
|
|
3856
|
-
* }
|
|
3857
|
-
* );
|
|
3858
|
-
* ```
|
|
3859
|
-
*/
|
|
3860
|
-
async swap(connection, note, recipient, options) {
|
|
3861
|
-
try {
|
|
3862
|
-
if (!isWithdrawable(note)) {
|
|
3863
|
-
const depositResult = await this.deposit(connection, note, {
|
|
3864
|
-
skipPreflight: false
|
|
3865
|
-
});
|
|
3866
|
-
note = depositResult.note;
|
|
3867
|
-
}
|
|
3868
|
-
const variableFee = Math.floor(note.amount * 3 / 1e3);
|
|
3869
|
-
const feeBps = note.amount === 0 ? 0 : Math.min(Math.floor((variableFee * 1e4 + note.amount - 1) / note.amount), 65535);
|
|
3870
|
-
const withdrawAmountLamports = getDistributableAmount(note.amount);
|
|
3871
|
-
if (withdrawAmountLamports <= 0) {
|
|
3872
|
-
throw new Error("Amount too small after fees");
|
|
3873
|
-
}
|
|
3874
|
-
let minOutputAmount;
|
|
3875
|
-
if (options.minOutputAmount !== void 0) {
|
|
3876
|
-
minOutputAmount = options.minOutputAmount;
|
|
3877
|
-
} else if (options.getQuote) {
|
|
3878
|
-
const quote = await options.getQuote(
|
|
3879
|
-
withdrawAmountLamports,
|
|
3880
|
-
options.outputMint,
|
|
3881
|
-
options.slippageBps || 100
|
|
3882
|
-
);
|
|
3883
|
-
minOutputAmount = quote.minOutputAmount;
|
|
3884
|
-
} else {
|
|
3885
|
-
throw new Error(
|
|
3886
|
-
"Must provide either minOutputAmount or getQuote function"
|
|
3887
|
-
);
|
|
3888
|
-
}
|
|
3889
|
-
let recipientAta;
|
|
3890
|
-
if (options.recipientAta) {
|
|
3891
|
-
recipientAta = new import_web35.PublicKey(options.recipientAta);
|
|
3892
|
-
} else {
|
|
3893
|
-
try {
|
|
3894
|
-
const splTokenModule = await import("@solana/spl-token");
|
|
3895
|
-
const getAssociatedTokenAddress = splTokenModule.getAssociatedTokenAddress;
|
|
3896
|
-
if (!getAssociatedTokenAddress) {
|
|
3897
|
-
throw new Error("getAssociatedTokenAddress not found");
|
|
3898
|
-
}
|
|
3899
|
-
const outputMint2 = new import_web35.PublicKey(options.outputMint);
|
|
3900
|
-
recipientAta = await getAssociatedTokenAddress(outputMint2, recipient);
|
|
3901
|
-
} catch (error) {
|
|
3902
|
-
throw new Error(
|
|
3903
|
-
`Failed to get associated token account: ${error instanceof Error ? error.message : String(error)}. Please install @solana/spl-token or provide recipientAta in options.`
|
|
3904
|
-
);
|
|
3905
|
-
}
|
|
3906
|
-
}
|
|
3907
|
-
if (!note.leafIndex && note.leafIndex !== 0) {
|
|
3908
|
-
throw new Error("Note must have a leaf index (note must be deposited)");
|
|
3909
|
-
}
|
|
3910
|
-
const nullifier = await computeNullifierAsync(note.sk_spend, note.leafIndex);
|
|
3911
|
-
const nullifierHex = nullifier.toString(16).padStart(64, "0");
|
|
3912
|
-
const inputMint = new import_web35.PublicKey("11111111111111111111111111111111");
|
|
3913
|
-
const outputMint = new import_web35.PublicKey(options.outputMint);
|
|
3914
|
-
const outputsHash = await computeSwapOutputsHashAsync(
|
|
3915
|
-
inputMint,
|
|
3916
|
-
outputMint,
|
|
3917
|
-
recipientAta,
|
|
3918
|
-
minOutputAmount,
|
|
3919
|
-
note.amount
|
|
3920
|
-
);
|
|
3921
|
-
const outputsHashHex = outputsHash.toString(16).padStart(64, "0");
|
|
3922
|
-
const circuitsPath2 = await getDefaultCircuitsPath();
|
|
3923
|
-
const useDirectProof = await areCircuitsAvailable(circuitsPath2);
|
|
3924
|
-
if (!useDirectProof) {
|
|
3925
|
-
throw new Error(
|
|
3926
|
-
`Circuits not available at ${circuitsPath2}. Make sure circuits are built and accessible. Circuit artifacts are pinned to S3 and must be reachable from this environment.`
|
|
3927
|
-
);
|
|
3928
|
-
}
|
|
3929
|
-
const MAX_PROOF_RETRIES = 3;
|
|
3930
|
-
let lastError = null;
|
|
3931
|
-
for (let attempt = 0; attempt < MAX_PROOF_RETRIES; attempt++) {
|
|
3932
|
-
try {
|
|
3933
|
-
if (attempt > 0) {
|
|
3934
|
-
options?.onProgress?.(`proof_refresh_${attempt + 1}`);
|
|
3935
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3936
|
-
}
|
|
3937
|
-
const programId = this.config.programId || CLOAK_PROGRAM_ID;
|
|
3938
|
-
const mintForSOL = NATIVE_SOL_MINT;
|
|
3939
|
-
const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, mintForSOL);
|
|
3940
|
-
const chainProof = await computeProofFromChain(connection, merkleTreePDA, note.leafIndex);
|
|
3941
|
-
const merkleProof = {
|
|
3942
|
-
pathElements: chainProof.pathElements,
|
|
3943
|
-
pathIndices: chainProof.pathIndices
|
|
3944
|
-
};
|
|
3945
|
-
const merkleRoot = chainProof.root;
|
|
3946
|
-
if (!merkleRoot) {
|
|
3947
|
-
throw new Error("Failed to get Merkle root from chain");
|
|
3948
|
-
}
|
|
3949
|
-
if (!merkleProof.pathElements || merkleProof.pathElements.length === 0) {
|
|
3950
|
-
throw new Error("Merkle proof is invalid: missing path elements");
|
|
3951
|
-
}
|
|
3952
|
-
const sk_spend_bigint = BigInt("0x" + note.sk_spend);
|
|
3953
|
-
const r_bigint = BigInt("0x" + note.r);
|
|
3954
|
-
const root_bigint = BigInt("0x" + merkleRoot);
|
|
3955
|
-
const nullifier_bigint = BigInt("0x" + nullifierHex);
|
|
3956
|
-
const outputs_hash_bigint = BigInt("0x" + outputsHashHex);
|
|
3957
|
-
const amount_bigint = BigInt(note.amount);
|
|
3958
|
-
const pathElements = merkleProof.pathElements.map((p) => BigInt("0x" + p));
|
|
3959
|
-
const inputMintLimbs = pubkeyToLimbs(inputMint.toBytes());
|
|
3960
|
-
const outputMintLimbs = pubkeyToLimbs(outputMint.toBytes());
|
|
3961
|
-
const recipientAtaLimbs = pubkeyToLimbs(recipientAta.toBytes());
|
|
3962
|
-
if (attempt === 0) {
|
|
3963
|
-
const computedCommitment = await computeCommitment(amount_bigint, r_bigint, sk_spend_bigint);
|
|
3964
|
-
const noteCommitment = BigInt("0x" + note.commitment);
|
|
3965
|
-
if (computedCommitment !== noteCommitment) {
|
|
3966
|
-
throw new Error(
|
|
3967
|
-
`Commitment mismatch! Computed: ${computedCommitment.toString(16)}, Note: ${note.commitment}. This means the note's sk_spend, r, or amount doesn't match what was deposited.`
|
|
3968
|
-
);
|
|
3969
|
-
}
|
|
3970
|
-
}
|
|
3971
|
-
const t = amount_bigint * 3n;
|
|
3972
|
-
const varFee = t / 1000n;
|
|
3973
|
-
const rem = t % 1000n;
|
|
3974
|
-
const proofInputs = {
|
|
3975
|
-
sk_spend: sk_spend_bigint,
|
|
3976
|
-
r: r_bigint,
|
|
3977
|
-
amount: amount_bigint,
|
|
3978
|
-
leaf_index: BigInt(note.leafIndex),
|
|
3979
|
-
path_elements: pathElements,
|
|
3980
|
-
path_indices: merkleProof.pathIndices,
|
|
3981
|
-
root: root_bigint,
|
|
3982
|
-
nullifier: nullifier_bigint,
|
|
3983
|
-
outputs_hash: outputs_hash_bigint,
|
|
3984
|
-
public_amount: amount_bigint,
|
|
3985
|
-
input_mint: inputMintLimbs,
|
|
3986
|
-
output_mint: outputMintLimbs,
|
|
3987
|
-
recipient_ata: recipientAtaLimbs,
|
|
3988
|
-
min_output_amount: BigInt(minOutputAmount),
|
|
3989
|
-
var_fee: varFee,
|
|
3990
|
-
rem
|
|
3991
|
-
};
|
|
3992
|
-
options?.onProgress?.("proof_generating");
|
|
3993
|
-
const proofResult = await generateWithdrawSwapProof(proofInputs, circuitsPath2);
|
|
3994
|
-
options?.onProgress?.("proof_complete");
|
|
3995
|
-
const proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
|
|
3996
|
-
const finalPublicInputs = {
|
|
3997
|
-
root: merkleRoot,
|
|
3998
|
-
nf: nullifierHex,
|
|
3999
|
-
outputs_hash: outputsHashHex,
|
|
4000
|
-
amount: note.amount
|
|
4001
|
-
};
|
|
4002
|
-
const metadataBundle = options?.metadataBundle;
|
|
4003
|
-
const signature = await this.relay.submitSwap(
|
|
4004
|
-
{
|
|
4005
|
-
proof: proofHex,
|
|
4006
|
-
publicInputs: finalPublicInputs,
|
|
4007
|
-
outputs: [
|
|
4008
|
-
{
|
|
4009
|
-
recipient: recipient.toBase58(),
|
|
4010
|
-
amount: withdrawAmountLamports
|
|
4011
|
-
}
|
|
4012
|
-
],
|
|
4013
|
-
feeBps,
|
|
4014
|
-
swap: {
|
|
4015
|
-
output_mint: options.outputMint,
|
|
4016
|
-
slippage_bps: options.slippageBps || 100,
|
|
4017
|
-
min_output_amount: minOutputAmount
|
|
4018
|
-
},
|
|
4019
|
-
metadataBundle
|
|
4020
|
-
},
|
|
4021
|
-
options.onProgress
|
|
4022
|
-
);
|
|
4023
|
-
return {
|
|
4024
|
-
signature,
|
|
4025
|
-
outputs: [
|
|
4026
|
-
{
|
|
4027
|
-
recipient: recipient.toBase58(),
|
|
4028
|
-
amount: withdrawAmountLamports
|
|
4029
|
-
}
|
|
4030
|
-
],
|
|
4031
|
-
nullifier: nullifierHex,
|
|
4032
|
-
root: merkleRoot,
|
|
4033
|
-
outputMint: options.outputMint,
|
|
4034
|
-
minOutputAmount
|
|
4035
|
-
};
|
|
4036
|
-
} catch (error) {
|
|
4037
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
4038
|
-
if (error instanceof RootNotFoundError) {
|
|
4039
|
-
console.warn(
|
|
4040
|
-
`[Cloak SDK] Merkle root expired (attempt ${attempt + 1}/${MAX_PROOF_RETRIES}). Regenerating swap proof with fresh root...`
|
|
4041
|
-
);
|
|
4042
|
-
continue;
|
|
4043
|
-
}
|
|
4044
|
-
throw error;
|
|
4045
|
-
}
|
|
4046
|
-
}
|
|
4047
|
-
throw new Error(
|
|
4048
|
-
`Swap failed after ${MAX_PROOF_RETRIES} proof refresh attempts. The Merkle tree is updating too rapidly. Last error: ${lastError?.message}`
|
|
4049
|
-
);
|
|
4050
|
-
} catch (error) {
|
|
4051
|
-
throw this.wrapError(error, "Swap failed");
|
|
4052
|
-
}
|
|
4053
|
-
}
|
|
4054
|
-
/**
|
|
4055
|
-
* Generate a new note without depositing
|
|
4056
|
-
*
|
|
4057
|
-
* @param amountLamports - Amount for the note
|
|
4058
|
-
* @param useWalletKeys - Whether to use wallet keys (v2.0 recommended)
|
|
4059
|
-
* @returns New note (not yet deposited)
|
|
4060
|
-
*/
|
|
4061
|
-
async generateNote(amountLamports, useWalletKeys = false) {
|
|
4062
|
-
if (useWalletKeys && this.cloakKeys) {
|
|
4063
|
-
return await generateNoteFromWallet(amountLamports, this.cloakKeys, this.config.network);
|
|
4064
|
-
} else if (useWalletKeys) {
|
|
4065
|
-
const keys = generateCloakKeys();
|
|
4066
|
-
this.cloakKeys = keys;
|
|
4067
|
-
const result = this.storage.saveKeys(keys);
|
|
4068
|
-
if (result instanceof Promise) {
|
|
4069
|
-
result.catch(() => {
|
|
4070
|
-
});
|
|
4071
|
-
}
|
|
4072
|
-
return await generateNoteFromWallet(amountLamports, keys, this.config.network);
|
|
4073
|
-
}
|
|
4074
|
-
return await generateNote(amountLamports, this.config.network);
|
|
4075
|
-
}
|
|
4076
|
-
/**
|
|
4077
|
-
* Parse a note from JSON string
|
|
4078
|
-
*
|
|
4079
|
-
* @param jsonString - JSON representation
|
|
4080
|
-
* @returns Parsed note
|
|
4081
|
-
*/
|
|
4082
|
-
parseNote(jsonString) {
|
|
4083
|
-
return parseNote2(jsonString);
|
|
2441
|
+
poolAddress: pool,
|
|
2442
|
+
merkleTreeAddress: merkleTree,
|
|
2443
|
+
treasuryAddress: treasury,
|
|
2444
|
+
debug: config.debug
|
|
2445
|
+
};
|
|
4084
2446
|
}
|
|
4085
|
-
/**
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
2447
|
+
/** Public key of the configured signer (keypair or wallet). */
|
|
2448
|
+
getPublicKey() {
|
|
2449
|
+
if (this.keypair) {
|
|
2450
|
+
return this.keypair.publicKey;
|
|
2451
|
+
}
|
|
2452
|
+
if (this.wallet?.publicKey) {
|
|
2453
|
+
return this.wallet.publicKey;
|
|
2454
|
+
}
|
|
2455
|
+
throw new CloakError(
|
|
2456
|
+
"No public key available - wallet not connected and no keypair provided",
|
|
2457
|
+
"wallet",
|
|
2458
|
+
false
|
|
2459
|
+
);
|
|
4094
2460
|
}
|
|
4095
|
-
/**
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
* @param note - Note to check
|
|
4099
|
-
* @returns True if withdrawable
|
|
4100
|
-
*/
|
|
4101
|
-
isWithdrawable(note) {
|
|
4102
|
-
return isWithdrawable(note);
|
|
2461
|
+
/** True when the SDK was constructed with a wallet adapter (browser). */
|
|
2462
|
+
isWalletMode() {
|
|
2463
|
+
return !!this.wallet && !this.keypair;
|
|
4103
2464
|
}
|
|
4104
2465
|
/**
|
|
4105
|
-
*
|
|
4106
|
-
*
|
|
4107
|
-
* @param connection - Solana connection
|
|
4108
|
-
* @param leafIndex - Leaf index in tree
|
|
4109
|
-
* @returns Merkle proof computed from on-chain data
|
|
2466
|
+
* Compute a Merkle proof for `leafIndex` from on-chain state. No relay /
|
|
2467
|
+
* indexer round-trip required.
|
|
4110
2468
|
*/
|
|
4111
2469
|
async getMerkleProof(connection, leafIndex) {
|
|
4112
2470
|
const programId = this.config.programId || CLOAK_PROGRAM_ID;
|
|
4113
|
-
const
|
|
4114
|
-
const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, mintForSOL);
|
|
2471
|
+
const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, NATIVE_SOL_MINT);
|
|
4115
2472
|
const chainProof = await computeProofFromChain(connection, merkleTreePDA, leafIndex);
|
|
4116
2473
|
return {
|
|
4117
2474
|
pathElements: chainProof.pathElements,
|
|
@@ -4119,72 +2476,20 @@ var CloakSDK = class {
|
|
|
4119
2476
|
root: chainProof.root
|
|
4120
2477
|
};
|
|
4121
2478
|
}
|
|
4122
|
-
/**
|
|
4123
|
-
* Get current Merkle root directly from on-chain state
|
|
4124
|
-
*
|
|
4125
|
-
* @param connection - Solana connection
|
|
4126
|
-
* @returns Current root hash from on-chain tree
|
|
4127
|
-
*/
|
|
2479
|
+
/** Read the current SOL-pool Merkle root from on-chain state. */
|
|
4128
2480
|
async getCurrentRoot(connection) {
|
|
4129
2481
|
const programId = this.config.programId || CLOAK_PROGRAM_ID;
|
|
4130
|
-
const
|
|
4131
|
-
const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, mintForSOL);
|
|
2482
|
+
const { merkleTree: merkleTreePDA } = getShieldPoolPDAs(programId, NATIVE_SOL_MINT);
|
|
4132
2483
|
const state = await readMerkleTreeState(connection, merkleTreePDA);
|
|
4133
2484
|
return state.root;
|
|
4134
2485
|
}
|
|
4135
|
-
/**
|
|
4136
|
-
* Get transaction status from relay service
|
|
4137
|
-
*
|
|
4138
|
-
* @param requestId - Request ID from previous submission
|
|
4139
|
-
* @returns Current status
|
|
4140
|
-
*/
|
|
2486
|
+
/** Poll relay for the status of a previously-submitted request. */
|
|
4141
2487
|
async getTransactionStatus(requestId) {
|
|
4142
2488
|
return this.relay.getStatus(requestId);
|
|
4143
2489
|
}
|
|
4144
|
-
/**
|
|
4145
|
-
* Fetch and decrypt this user's transaction metadata history.
|
|
4146
|
-
* DEPRECATED: This functionality is no longer supported
|
|
4147
|
-
*/
|
|
4148
|
-
async getTransactionMetadata(_options) {
|
|
4149
|
-
throw new Error("getTransactionMetadata is deprecated - use compliance export API instead");
|
|
4150
|
-
}
|
|
4151
|
-
/**
|
|
4152
|
-
* Load all notes from storage
|
|
4153
|
-
*
|
|
4154
|
-
* @returns Array of saved notes
|
|
4155
|
-
*/
|
|
4156
|
-
async loadNotes() {
|
|
4157
|
-
const notes = this.storage.loadAllNotes();
|
|
4158
|
-
return Array.isArray(notes) ? notes : await notes;
|
|
4159
|
-
}
|
|
4160
|
-
/**
|
|
4161
|
-
* Save a note to storage
|
|
4162
|
-
*
|
|
4163
|
-
* @param note - Note to save
|
|
4164
|
-
*/
|
|
4165
|
-
async saveNote(note) {
|
|
4166
|
-
const result = this.storage.saveNote(note);
|
|
4167
|
-
if (result instanceof Promise) {
|
|
4168
|
-
await result;
|
|
4169
|
-
}
|
|
4170
|
-
}
|
|
4171
|
-
/**
|
|
4172
|
-
* Find a note by its commitment
|
|
4173
|
-
*
|
|
4174
|
-
* @param commitment - Commitment hash
|
|
4175
|
-
* @returns Note if found
|
|
4176
|
-
*/
|
|
4177
|
-
async findNote(commitment) {
|
|
4178
|
-
const notes = await this.loadNotes();
|
|
4179
|
-
return findNoteByCommitment(notes, commitment);
|
|
4180
|
-
}
|
|
4181
|
-
/**
|
|
4182
|
-
* Import wallet keys from JSON
|
|
4183
|
-
*
|
|
4184
|
-
* @param keysJson - JSON string containing keys
|
|
4185
|
-
*/
|
|
2490
|
+
/** Import wallet keys from JSON; persists to the configured storage adapter. */
|
|
4186
2491
|
async importWalletKeys(keysJson) {
|
|
4187
|
-
const keys =
|
|
2492
|
+
const keys = importKeys(keysJson);
|
|
4188
2493
|
this.cloakKeys = keys;
|
|
4189
2494
|
const result = this.storage.saveKeys(keys);
|
|
4190
2495
|
if (result instanceof Promise) {
|
|
@@ -4192,134 +2497,32 @@ var CloakSDK = class {
|
|
|
4192
2497
|
}
|
|
4193
2498
|
}
|
|
4194
2499
|
/**
|
|
4195
|
-
* Export wallet keys to JSON
|
|
4196
|
-
*
|
|
4197
|
-
* WARNING:
|
|
4198
|
-
*
|
|
4199
|
-
* @returns JSON string with keys
|
|
2500
|
+
* Export wallet keys to JSON.
|
|
2501
|
+
*
|
|
2502
|
+
* WARNING: this exports secret keys. Store securely.
|
|
4200
2503
|
*/
|
|
4201
2504
|
exportWalletKeys() {
|
|
4202
2505
|
if (!this.cloakKeys) {
|
|
4203
2506
|
throw new CloakError("No wallet keys available", "wallet", false);
|
|
4204
2507
|
}
|
|
4205
|
-
return
|
|
2508
|
+
return exportKeys(this.cloakKeys);
|
|
4206
2509
|
}
|
|
4207
2510
|
/**
|
|
4208
|
-
*
|
|
2511
|
+
* Snapshot of the active SDK configuration.
|
|
2512
|
+
*
|
|
2513
|
+
* The return type ({@link CloakConfig}) is structurally narrower than the
|
|
2514
|
+
* constructor input ({@link CloakSDKOptions}): `keypairBytes`, `wallet`,
|
|
2515
|
+
* and `storage` are intentionally absent from the snapshot. The secrets
|
|
2516
|
+
* are consumed into private SDK state at construction time and are not
|
|
2517
|
+
* re-exposable through this method — the type system enforces it. To
|
|
2518
|
+
* sign, pass a `Keypair` / `WalletAdapter` directly to the standalone
|
|
2519
|
+
* `transact` / `partialWithdraw` / `fullWithdraw` / `swapUtxo` helpers.
|
|
4209
2520
|
*/
|
|
4210
2521
|
getConfig() {
|
|
4211
2522
|
return { ...this.config };
|
|
4212
2523
|
}
|
|
4213
|
-
// Note: scanNotes was removed - it required the indexer to store encrypted outputs.
|
|
4214
|
-
// Users should maintain their own note storage. This is more privacy-preserving.
|
|
4215
|
-
/**
|
|
4216
|
-
* Wrap errors with better categorization and user-friendly messages
|
|
4217
|
-
*
|
|
4218
|
-
* @private
|
|
4219
|
-
*/
|
|
4220
|
-
wrapError(error, context) {
|
|
4221
|
-
if (error instanceof CloakError) {
|
|
4222
|
-
return error;
|
|
4223
|
-
}
|
|
4224
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4225
|
-
if (errorMessage.includes("duplicate key") || errorMessage.includes("already deposited")) {
|
|
4226
|
-
return new CloakError(
|
|
4227
|
-
"This note was already deposited. Generate a new note to continue.",
|
|
4228
|
-
"validation",
|
|
4229
|
-
false,
|
|
4230
|
-
error instanceof Error ? error : void 0
|
|
4231
|
-
);
|
|
4232
|
-
}
|
|
4233
|
-
if (errorMessage.includes("insufficient funds") || errorMessage.includes("insufficient lamports")) {
|
|
4234
|
-
return new CloakError(
|
|
4235
|
-
"Insufficient funds for this transaction. Please check your wallet balance.",
|
|
4236
|
-
"wallet",
|
|
4237
|
-
false,
|
|
4238
|
-
error instanceof Error ? error : void 0
|
|
4239
|
-
);
|
|
4240
|
-
}
|
|
4241
|
-
if (errorMessage.includes("Merkle tree") || errorMessage.includes("merkle")) {
|
|
4242
|
-
return new CloakError(
|
|
4243
|
-
"Failed to read on-chain Merkle tree state. Please try again.",
|
|
4244
|
-
"network",
|
|
4245
|
-
true,
|
|
4246
|
-
error instanceof Error ? error : void 0
|
|
4247
|
-
);
|
|
4248
|
-
}
|
|
4249
|
-
if (errorMessage.includes("timeout") || errorMessage.includes("timed out")) {
|
|
4250
|
-
return new CloakError(
|
|
4251
|
-
"Network timeout. Please check your connection and try again.",
|
|
4252
|
-
"network",
|
|
4253
|
-
true,
|
|
4254
|
-
error instanceof Error ? error : void 0
|
|
4255
|
-
);
|
|
4256
|
-
}
|
|
4257
|
-
if (errorMessage.includes("not connected") || errorMessage.includes("wallet")) {
|
|
4258
|
-
return new CloakError(
|
|
4259
|
-
"Wallet not connected. Please connect your wallet first.",
|
|
4260
|
-
"wallet",
|
|
4261
|
-
false,
|
|
4262
|
-
error instanceof Error ? error : void 0
|
|
4263
|
-
);
|
|
4264
|
-
}
|
|
4265
|
-
if (errorMessage.includes("proof") && (errorMessage.includes("failed") || errorMessage.includes("error"))) {
|
|
4266
|
-
return new CloakError(
|
|
4267
|
-
"Zero-knowledge proof generation failed. This is usually temporary - please try again.",
|
|
4268
|
-
"prover",
|
|
4269
|
-
true,
|
|
4270
|
-
error instanceof Error ? error : void 0
|
|
4271
|
-
);
|
|
4272
|
-
}
|
|
4273
|
-
if (errorMessage.includes("relay") || errorMessage.includes("withdraw")) {
|
|
4274
|
-
return new CloakError(
|
|
4275
|
-
"Relay service error. Please try again later.",
|
|
4276
|
-
"relay",
|
|
4277
|
-
true,
|
|
4278
|
-
error instanceof Error ? error : void 0
|
|
4279
|
-
);
|
|
4280
|
-
}
|
|
4281
|
-
return new CloakError(
|
|
4282
|
-
`${context}: ${errorMessage}`,
|
|
4283
|
-
"network",
|
|
4284
|
-
false,
|
|
4285
|
-
error instanceof Error ? error : void 0
|
|
4286
|
-
);
|
|
4287
|
-
}
|
|
4288
2524
|
};
|
|
4289
2525
|
|
|
4290
|
-
// src/core/note.ts
|
|
4291
|
-
function serializeNote(note, pretty = false) {
|
|
4292
|
-
validateNote(note);
|
|
4293
|
-
return pretty ? JSON.stringify(note, null, 2) : JSON.stringify(note);
|
|
4294
|
-
}
|
|
4295
|
-
function downloadNote(note, filename) {
|
|
4296
|
-
const g = globalThis;
|
|
4297
|
-
const doc = g?.document;
|
|
4298
|
-
const URL_ = g?.URL;
|
|
4299
|
-
const Blob_ = g?.Blob;
|
|
4300
|
-
if (!doc || !URL_ || !Blob_) {
|
|
4301
|
-
throw new Error("downloadNote is only available in browser environments");
|
|
4302
|
-
}
|
|
4303
|
-
const json = serializeNote(note, true);
|
|
4304
|
-
const blob = new Blob_([json], { type: "application/json" });
|
|
4305
|
-
const url = URL_.createObjectURL(blob);
|
|
4306
|
-
const defaultFilename = `cloak-note-${note.commitment.slice(0, 8)}.json`;
|
|
4307
|
-
const link = doc.createElement("a");
|
|
4308
|
-
link.href = url;
|
|
4309
|
-
link.download = filename || defaultFilename;
|
|
4310
|
-
link.click();
|
|
4311
|
-
URL_.revokeObjectURL(url);
|
|
4312
|
-
}
|
|
4313
|
-
async function copyNoteToClipboard(note) {
|
|
4314
|
-
const g = globalThis;
|
|
4315
|
-
const nav = g?.navigator;
|
|
4316
|
-
if (!nav || !nav.clipboard) {
|
|
4317
|
-
throw new Error("Clipboard API not available");
|
|
4318
|
-
}
|
|
4319
|
-
const json = serializeNote(note, true);
|
|
4320
|
-
await nav.clipboard.writeText(json);
|
|
4321
|
-
}
|
|
4322
|
-
|
|
4323
2526
|
// src/core/compliance-keys.ts
|
|
4324
2527
|
var import_tweetnacl2 = __toESM(require("tweetnacl"), 1);
|
|
4325
2528
|
var import_blake33 = require("@noble/hashes/blake3");
|
|
@@ -4706,6 +2909,107 @@ async function decryptComplianceMetadataWithMasterKey(encrypted, masterComplianc
|
|
|
4706
2909
|
return decryptWithSharedSecret(payload, sharedSecret);
|
|
4707
2910
|
}
|
|
4708
2911
|
|
|
2912
|
+
// src/utils/fees.ts
|
|
2913
|
+
var LAMPORTS_PER_SOL = 1e9;
|
|
2914
|
+
var FIXED_FEE_LAMPORTS = 5e6;
|
|
2915
|
+
var VARIABLE_FEE_NUMERATOR = 3;
|
|
2916
|
+
var VARIABLE_FEE_DENOMINATOR = 1e3;
|
|
2917
|
+
var VARIABLE_FEE_RATE = VARIABLE_FEE_NUMERATOR / VARIABLE_FEE_DENOMINATOR;
|
|
2918
|
+
var MIN_DEPOSIT_LAMPORTS = 1e7;
|
|
2919
|
+
function calculateFee(amountLamports) {
|
|
2920
|
+
const variableFee = Math.floor(amountLamports * VARIABLE_FEE_NUMERATOR / VARIABLE_FEE_DENOMINATOR);
|
|
2921
|
+
return FIXED_FEE_LAMPORTS + variableFee;
|
|
2922
|
+
}
|
|
2923
|
+
function calculateFeeBigint(amountLamports) {
|
|
2924
|
+
const fixed = BigInt(FIXED_FEE_LAMPORTS);
|
|
2925
|
+
const variable = amountLamports * BigInt(VARIABLE_FEE_NUMERATOR) / BigInt(VARIABLE_FEE_DENOMINATOR);
|
|
2926
|
+
return fixed + variable;
|
|
2927
|
+
}
|
|
2928
|
+
function isWithdrawAmountSufficient(amountLamports) {
|
|
2929
|
+
return amountLamports > calculateFeeBigint(amountLamports);
|
|
2930
|
+
}
|
|
2931
|
+
function getDistributableAmount(amountLamports) {
|
|
2932
|
+
return amountLamports - calculateFee(amountLamports);
|
|
2933
|
+
}
|
|
2934
|
+
function formatAmount(lamports, decimals = 9) {
|
|
2935
|
+
return (lamports / LAMPORTS_PER_SOL).toFixed(decimals);
|
|
2936
|
+
}
|
|
2937
|
+
function parseAmount(sol) {
|
|
2938
|
+
const num = parseFloat(sol);
|
|
2939
|
+
if (isNaN(num) || num < 0) {
|
|
2940
|
+
throw new Error(`Invalid SOL amount: ${sol}`);
|
|
2941
|
+
}
|
|
2942
|
+
return Math.floor(num * LAMPORTS_PER_SOL);
|
|
2943
|
+
}
|
|
2944
|
+
function validateOutputsSum(outputs, expectedTotal) {
|
|
2945
|
+
const sum = outputs.reduce((acc, out) => acc + out.amount, 0);
|
|
2946
|
+
return sum === expectedTotal;
|
|
2947
|
+
}
|
|
2948
|
+
function calculateRelayFee(amountLamports, feeBps) {
|
|
2949
|
+
if (feeBps < 0 || feeBps > 1e4) {
|
|
2950
|
+
throw new Error("Fee basis points must be between 0 and 10000");
|
|
2951
|
+
}
|
|
2952
|
+
return Math.floor(amountLamports * feeBps / 1e4);
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
// src/utils/validation.ts
|
|
2956
|
+
var import_web34 = require("@solana/web3.js");
|
|
2957
|
+
function isValidSolanaAddress(address) {
|
|
2958
|
+
try {
|
|
2959
|
+
new import_web34.PublicKey(address);
|
|
2960
|
+
return true;
|
|
2961
|
+
} catch {
|
|
2962
|
+
return false;
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
// src/utils/network.ts
|
|
2967
|
+
function detectNetworkFromRpcUrl(rpcUrl) {
|
|
2968
|
+
const url = rpcUrl || process.env.NEXT_PUBLIC_SOLANA_RPC_URL || "";
|
|
2969
|
+
const lowerUrl = url.toLowerCase();
|
|
2970
|
+
if (lowerUrl.includes("mainnet") || lowerUrl.includes("api.mainnet-beta") || lowerUrl.includes("mainnet-beta")) {
|
|
2971
|
+
return "mainnet";
|
|
2972
|
+
}
|
|
2973
|
+
if (lowerUrl.includes("testnet") || lowerUrl.includes("api.testnet")) {
|
|
2974
|
+
return "testnet";
|
|
2975
|
+
}
|
|
2976
|
+
if (lowerUrl.includes("devnet") || lowerUrl.includes("api.devnet")) {
|
|
2977
|
+
return "devnet";
|
|
2978
|
+
}
|
|
2979
|
+
if (lowerUrl.includes("localhost") || lowerUrl.includes("127.0.0.1") || lowerUrl.includes("local")) {
|
|
2980
|
+
return "localnet";
|
|
2981
|
+
}
|
|
2982
|
+
return "mainnet";
|
|
2983
|
+
}
|
|
2984
|
+
function getRpcUrlForNetwork(network) {
|
|
2985
|
+
switch (network) {
|
|
2986
|
+
case "mainnet":
|
|
2987
|
+
return "https://api.mainnet-beta.solana.com";
|
|
2988
|
+
case "devnet":
|
|
2989
|
+
return "https://api.devnet.solana.com";
|
|
2990
|
+
case "localnet":
|
|
2991
|
+
return "http://localhost:8899";
|
|
2992
|
+
default:
|
|
2993
|
+
return "https://api.mainnet-beta.solana.com";
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
function isValidRpcUrl(url) {
|
|
2997
|
+
try {
|
|
2998
|
+
const parsed = new URL(url);
|
|
2999
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
3000
|
+
} catch {
|
|
3001
|
+
return false;
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
function getExplorerUrl(signature, network = "devnet") {
|
|
3005
|
+
const cluster = network === "mainnet" ? "" : `?cluster=${network}`;
|
|
3006
|
+
return `https://explorer.solana.com/tx/${signature}${cluster}`;
|
|
3007
|
+
}
|
|
3008
|
+
function getAddressExplorerUrl(address, network = "devnet") {
|
|
3009
|
+
const cluster = network === "mainnet" ? "" : `?cluster=${network}`;
|
|
3010
|
+
return `https://explorer.solana.com/address/${address}${cluster}`;
|
|
3011
|
+
}
|
|
3012
|
+
|
|
4709
3013
|
// src/utils/verify-utxos.ts
|
|
4710
3014
|
init_utxo();
|
|
4711
3015
|
async function verifyUtxos(utxos, connection, programId, commitment = "confirmed") {
|
|
@@ -4717,7 +3021,7 @@ async function verifyUtxos(utxos, connection, programId, commitment = "confirmed
|
|
|
4717
3021
|
continue;
|
|
4718
3022
|
}
|
|
4719
3023
|
try {
|
|
4720
|
-
const nullifierBig = await
|
|
3024
|
+
const nullifierBig = await computeNullifier(utxo);
|
|
4721
3025
|
const nullifierHex = nullifierBig.toString(16).padStart(64, "0");
|
|
4722
3026
|
const nullifierBytes = Buffer.from(nullifierHex, "hex");
|
|
4723
3027
|
const { pool } = getShieldPoolPDAs(programId, utxo.mintAddress);
|
|
@@ -4835,49 +3139,10 @@ async function fetchRiskQuoteIx(connection, wallet, rangeApiKey) {
|
|
|
4835
3139
|
};
|
|
4836
3140
|
}
|
|
4837
3141
|
|
|
4838
|
-
// src/helpers/encrypted-output.ts
|
|
4839
|
-
function prepareEncryptedOutput(note, cloakKeys) {
|
|
4840
|
-
const noteData = {
|
|
4841
|
-
amount: note.amount,
|
|
4842
|
-
r: note.r,
|
|
4843
|
-
sk_spend: note.sk_spend,
|
|
4844
|
-
commitment: note.commitment
|
|
4845
|
-
};
|
|
4846
|
-
const encrypted = encryptNoteForRecipient(noteData, cloakKeys.view.pvk);
|
|
4847
|
-
return btoa(JSON.stringify(encrypted));
|
|
4848
|
-
}
|
|
4849
|
-
function prepareEncryptedOutputForRecipient(note, recipientPvkHex) {
|
|
4850
|
-
const noteData = {
|
|
4851
|
-
amount: note.amount,
|
|
4852
|
-
r: note.r,
|
|
4853
|
-
sk_spend: note.sk_spend,
|
|
4854
|
-
commitment: note.commitment
|
|
4855
|
-
};
|
|
4856
|
-
const recipientPvk = hexToBytes(recipientPvkHex);
|
|
4857
|
-
const encrypted = encryptNoteForRecipient(noteData, recipientPvk);
|
|
4858
|
-
return btoa(JSON.stringify(encrypted));
|
|
4859
|
-
}
|
|
4860
|
-
function encodeNoteSimple(note) {
|
|
4861
|
-
const data = {
|
|
4862
|
-
amount: note.amount,
|
|
4863
|
-
r: note.r,
|
|
4864
|
-
sk_spend: note.sk_spend,
|
|
4865
|
-
commitment: note.commitment
|
|
4866
|
-
};
|
|
4867
|
-
const json = JSON.stringify(data);
|
|
4868
|
-
if (typeof Buffer !== "undefined") {
|
|
4869
|
-
return Buffer.from(json).toString("base64");
|
|
4870
|
-
} else if (typeof btoa !== "undefined") {
|
|
4871
|
-
return btoa(json);
|
|
4872
|
-
} else {
|
|
4873
|
-
throw new Error("No base64 encoding method available");
|
|
4874
|
-
}
|
|
4875
|
-
}
|
|
4876
|
-
|
|
4877
3142
|
// src/helpers/wallet-integration.ts
|
|
4878
|
-
var
|
|
3143
|
+
var import_web35 = require("@solana/web3.js");
|
|
4879
3144
|
function validateWalletConnected(wallet) {
|
|
4880
|
-
if (wallet instanceof
|
|
3145
|
+
if (wallet instanceof import_web35.Keypair) {
|
|
4881
3146
|
return;
|
|
4882
3147
|
}
|
|
4883
3148
|
if (!wallet.publicKey) {
|
|
@@ -4889,7 +3154,7 @@ function validateWalletConnected(wallet) {
|
|
|
4889
3154
|
}
|
|
4890
3155
|
}
|
|
4891
3156
|
function getPublicKey(wallet) {
|
|
4892
|
-
if (wallet instanceof
|
|
3157
|
+
if (wallet instanceof import_web35.Keypair) {
|
|
4893
3158
|
return wallet.publicKey;
|
|
4894
3159
|
}
|
|
4895
3160
|
if (!wallet.publicKey) {
|
|
@@ -4902,7 +3167,7 @@ function getPublicKey(wallet) {
|
|
|
4902
3167
|
return wallet.publicKey;
|
|
4903
3168
|
}
|
|
4904
3169
|
async function sendTransaction(transaction, wallet, connection, options) {
|
|
4905
|
-
if (wallet instanceof
|
|
3170
|
+
if (wallet instanceof import_web35.Keypair) {
|
|
4906
3171
|
return await connection.sendTransaction(transaction, [wallet], options);
|
|
4907
3172
|
}
|
|
4908
3173
|
if (wallet.sendTransaction) {
|
|
@@ -4919,7 +3184,7 @@ async function sendTransaction(transaction, wallet, connection, options) {
|
|
|
4919
3184
|
}
|
|
4920
3185
|
}
|
|
4921
3186
|
async function signTransaction(transaction, wallet) {
|
|
4922
|
-
if (wallet instanceof
|
|
3187
|
+
if (wallet instanceof import_web35.Keypair) {
|
|
4923
3188
|
transaction.sign(wallet);
|
|
4924
3189
|
return transaction;
|
|
4925
3190
|
}
|
|
@@ -5383,180 +3648,13 @@ async function preflightCheck(relayUrl, rootHex) {
|
|
|
5383
3648
|
};
|
|
5384
3649
|
}
|
|
5385
3650
|
|
|
5386
|
-
// src/utils/pending-operations.ts
|
|
5387
|
-
var PENDING_DEPOSITS_KEY = "cloak_pending_deposits";
|
|
5388
|
-
var PENDING_WITHDRAWALS_KEY = "cloak_pending_withdrawals";
|
|
5389
|
-
function getStorage() {
|
|
5390
|
-
if (typeof globalThis !== "undefined" && globalThis.localStorage) {
|
|
5391
|
-
return globalThis.localStorage;
|
|
5392
|
-
}
|
|
5393
|
-
return null;
|
|
5394
|
-
}
|
|
5395
|
-
function savePendingDeposit(deposit) {
|
|
5396
|
-
const storage = getStorage();
|
|
5397
|
-
if (!storage) {
|
|
5398
|
-
console.warn("localStorage not available - pending deposit not persisted");
|
|
5399
|
-
return;
|
|
5400
|
-
}
|
|
5401
|
-
const deposits = loadPendingDeposits();
|
|
5402
|
-
const index = deposits.findIndex((d) => d.note.commitment === deposit.note.commitment);
|
|
5403
|
-
if (index >= 0) {
|
|
5404
|
-
deposits[index] = deposit;
|
|
5405
|
-
} else {
|
|
5406
|
-
deposits.push(deposit);
|
|
5407
|
-
}
|
|
5408
|
-
storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
|
|
5409
|
-
}
|
|
5410
|
-
function loadPendingDeposits() {
|
|
5411
|
-
const storage = getStorage();
|
|
5412
|
-
if (!storage) return [];
|
|
5413
|
-
const stored = storage.getItem(PENDING_DEPOSITS_KEY);
|
|
5414
|
-
if (!stored) return [];
|
|
5415
|
-
try {
|
|
5416
|
-
return JSON.parse(stored);
|
|
5417
|
-
} catch {
|
|
5418
|
-
return [];
|
|
5419
|
-
}
|
|
5420
|
-
}
|
|
5421
|
-
function updatePendingDeposit(commitment, updates) {
|
|
5422
|
-
const storage = getStorage();
|
|
5423
|
-
if (!storage) return;
|
|
5424
|
-
const deposits = loadPendingDeposits();
|
|
5425
|
-
const index = deposits.findIndex((d) => d.note.commitment === commitment);
|
|
5426
|
-
if (index >= 0) {
|
|
5427
|
-
deposits[index] = { ...deposits[index], ...updates };
|
|
5428
|
-
storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(deposits));
|
|
5429
|
-
}
|
|
5430
|
-
}
|
|
5431
|
-
function removePendingDeposit(commitment) {
|
|
5432
|
-
const storage = getStorage();
|
|
5433
|
-
if (!storage) return;
|
|
5434
|
-
const deposits = loadPendingDeposits();
|
|
5435
|
-
const filtered = deposits.filter((d) => d.note.commitment !== commitment);
|
|
5436
|
-
storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(filtered));
|
|
5437
|
-
}
|
|
5438
|
-
function clearPendingDeposits() {
|
|
5439
|
-
const storage = getStorage();
|
|
5440
|
-
if (storage) {
|
|
5441
|
-
storage.removeItem(PENDING_DEPOSITS_KEY);
|
|
5442
|
-
}
|
|
5443
|
-
}
|
|
5444
|
-
function savePendingWithdrawal(withdrawal) {
|
|
5445
|
-
const storage = getStorage();
|
|
5446
|
-
if (!storage) {
|
|
5447
|
-
console.warn("localStorage not available - pending withdrawal not persisted");
|
|
5448
|
-
return;
|
|
5449
|
-
}
|
|
5450
|
-
const withdrawals = loadPendingWithdrawals();
|
|
5451
|
-
const index = withdrawals.findIndex((w) => w.requestId === withdrawal.requestId);
|
|
5452
|
-
if (index >= 0) {
|
|
5453
|
-
withdrawals[index] = withdrawal;
|
|
5454
|
-
} else {
|
|
5455
|
-
withdrawals.push(withdrawal);
|
|
5456
|
-
}
|
|
5457
|
-
storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
|
|
5458
|
-
}
|
|
5459
|
-
function loadPendingWithdrawals() {
|
|
5460
|
-
const storage = getStorage();
|
|
5461
|
-
if (!storage) return [];
|
|
5462
|
-
const stored = storage.getItem(PENDING_WITHDRAWALS_KEY);
|
|
5463
|
-
if (!stored) return [];
|
|
5464
|
-
try {
|
|
5465
|
-
return JSON.parse(stored);
|
|
5466
|
-
} catch {
|
|
5467
|
-
return [];
|
|
5468
|
-
}
|
|
5469
|
-
}
|
|
5470
|
-
function updatePendingWithdrawal(requestId, updates) {
|
|
5471
|
-
const storage = getStorage();
|
|
5472
|
-
if (!storage) return;
|
|
5473
|
-
const withdrawals = loadPendingWithdrawals();
|
|
5474
|
-
const index = withdrawals.findIndex((w) => w.requestId === requestId);
|
|
5475
|
-
if (index >= 0) {
|
|
5476
|
-
withdrawals[index] = { ...withdrawals[index], ...updates };
|
|
5477
|
-
storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(withdrawals));
|
|
5478
|
-
}
|
|
5479
|
-
}
|
|
5480
|
-
function removePendingWithdrawal(requestId) {
|
|
5481
|
-
const storage = getStorage();
|
|
5482
|
-
if (!storage) return;
|
|
5483
|
-
const withdrawals = loadPendingWithdrawals();
|
|
5484
|
-
const filtered = withdrawals.filter((w) => w.requestId !== requestId);
|
|
5485
|
-
storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(filtered));
|
|
5486
|
-
}
|
|
5487
|
-
function clearPendingWithdrawals() {
|
|
5488
|
-
const storage = getStorage();
|
|
5489
|
-
if (storage) {
|
|
5490
|
-
storage.removeItem(PENDING_WITHDRAWALS_KEY);
|
|
5491
|
-
}
|
|
5492
|
-
}
|
|
5493
|
-
function hasPendingOperations() {
|
|
5494
|
-
const deposits = loadPendingDeposits();
|
|
5495
|
-
const withdrawals = loadPendingWithdrawals();
|
|
5496
|
-
const pendingDeposits = deposits.filter(
|
|
5497
|
-
(d) => d.status === "pending" || d.status === "tx_sent"
|
|
5498
|
-
);
|
|
5499
|
-
const pendingWithdrawals = withdrawals.filter(
|
|
5500
|
-
(w) => w.status === "pending" || w.status === "processing"
|
|
5501
|
-
);
|
|
5502
|
-
return pendingDeposits.length > 0 || pendingWithdrawals.length > 0;
|
|
5503
|
-
}
|
|
5504
|
-
function getPendingOperationsSummary() {
|
|
5505
|
-
const deposits = loadPendingDeposits().filter(
|
|
5506
|
-
(d) => d.status === "pending" || d.status === "tx_sent"
|
|
5507
|
-
);
|
|
5508
|
-
const withdrawals = loadPendingWithdrawals().filter(
|
|
5509
|
-
(w) => w.status === "pending" || w.status === "processing"
|
|
5510
|
-
);
|
|
5511
|
-
return {
|
|
5512
|
-
deposits,
|
|
5513
|
-
withdrawals,
|
|
5514
|
-
totalPending: deposits.length + withdrawals.length
|
|
5515
|
-
};
|
|
5516
|
-
}
|
|
5517
|
-
function cleanupStalePendingOperations(maxAgeMs = 24 * 60 * 60 * 1e3) {
|
|
5518
|
-
const now = Date.now();
|
|
5519
|
-
let removedDeposits = 0;
|
|
5520
|
-
let removedWithdrawals = 0;
|
|
5521
|
-
const deposits = loadPendingDeposits();
|
|
5522
|
-
const activeDeposits = deposits.filter((d) => {
|
|
5523
|
-
const age = now - d.startedAt;
|
|
5524
|
-
const isStale = age > maxAgeMs;
|
|
5525
|
-
const isTerminal = d.status === "confirmed" || d.status === "failed";
|
|
5526
|
-
if (isStale || isTerminal) {
|
|
5527
|
-
removedDeposits++;
|
|
5528
|
-
return false;
|
|
5529
|
-
}
|
|
5530
|
-
return true;
|
|
5531
|
-
});
|
|
5532
|
-
const storage = getStorage();
|
|
5533
|
-
if (storage) {
|
|
5534
|
-
storage.setItem(PENDING_DEPOSITS_KEY, JSON.stringify(activeDeposits));
|
|
5535
|
-
}
|
|
5536
|
-
const withdrawals = loadPendingWithdrawals();
|
|
5537
|
-
const activeWithdrawals = withdrawals.filter((w) => {
|
|
5538
|
-
const age = now - w.startedAt;
|
|
5539
|
-
const isStale = age > maxAgeMs;
|
|
5540
|
-
const isTerminal = w.status === "completed" || w.status === "failed";
|
|
5541
|
-
if (isStale || isTerminal) {
|
|
5542
|
-
removedWithdrawals++;
|
|
5543
|
-
return false;
|
|
5544
|
-
}
|
|
5545
|
-
return true;
|
|
5546
|
-
});
|
|
5547
|
-
if (storage) {
|
|
5548
|
-
storage.setItem(PENDING_WITHDRAWALS_KEY, JSON.stringify(activeWithdrawals));
|
|
5549
|
-
}
|
|
5550
|
-
return { removedDeposits, removedWithdrawals };
|
|
5551
|
-
}
|
|
5552
|
-
|
|
5553
3651
|
// src/index.ts
|
|
5554
3652
|
init_utxo();
|
|
5555
3653
|
|
|
5556
3654
|
// src/core/transact.ts
|
|
5557
|
-
var
|
|
3655
|
+
var import_web36 = require("@solana/web3.js");
|
|
5558
3656
|
var import_spl_token = require("@solana/spl-token");
|
|
5559
|
-
var
|
|
3657
|
+
var snarkjs = __toESM(require("snarkjs"), 1);
|
|
5560
3658
|
init_utxo();
|
|
5561
3659
|
|
|
5562
3660
|
// src/core/chain-note.ts
|
|
@@ -5707,8 +3805,8 @@ function chainNoteFromBase64(base64) {
|
|
|
5707
3805
|
// src/core/transact.ts
|
|
5708
3806
|
var import_circomlibjs4 = require("circomlibjs");
|
|
5709
3807
|
var import_tweetnacl4 = __toESM(require("tweetnacl"), 1);
|
|
5710
|
-
var
|
|
5711
|
-
var DEFAULT_TRANSACTION_CIRCUITS_URL = "https://
|
|
3808
|
+
var IS_BROWSER = typeof window !== "undefined" || typeof globalThis.document !== "undefined";
|
|
3809
|
+
var DEFAULT_TRANSACTION_CIRCUITS_URL = "https://storage.googleapis.com/cloak-circuits/circuits/0.1.0";
|
|
5712
3810
|
var DEFAULT_CLOAK_RELAY_URL = "https://api.cloak.ag";
|
|
5713
3811
|
function resolveDefaultTransactionCircuitsPath() {
|
|
5714
3812
|
return DEFAULT_TRANSACTION_CIRCUITS_URL;
|
|
@@ -5720,7 +3818,7 @@ async function resolveTransactionCircuitFiles() {
|
|
|
5720
3818
|
const baseNorm = base.endsWith("/") ? base : `${base}/`;
|
|
5721
3819
|
const wasmUrl = `${baseNorm}transaction_js/transaction.wasm`;
|
|
5722
3820
|
const zkeyUrl = `${baseNorm}transaction_final.zkey`;
|
|
5723
|
-
if (
|
|
3821
|
+
if (IS_BROWSER) {
|
|
5724
3822
|
return { wasmPath: wasmUrl, zkeyPath: zkeyUrl };
|
|
5725
3823
|
}
|
|
5726
3824
|
if (_remoteTxCircuitsCache?.base === baseNorm) {
|
|
@@ -5857,7 +3955,7 @@ async function resolveAddressLookupTableAccounts(connection, relayUrl, altAddres
|
|
|
5857
3955
|
const fetched = await Promise.all(
|
|
5858
3956
|
altAddresses.map(async (addr) => {
|
|
5859
3957
|
try {
|
|
5860
|
-
const pubkey = new
|
|
3958
|
+
const pubkey = new import_web36.PublicKey(addr);
|
|
5861
3959
|
const result = await connection.getAddressLookupTable(pubkey);
|
|
5862
3960
|
return result.value ?? null;
|
|
5863
3961
|
} catch {
|
|
@@ -6019,7 +4117,7 @@ async function generateTransactionProof(inputs, onProgress) {
|
|
|
6019
4117
|
onProgress?.(10);
|
|
6020
4118
|
const { wasmPath: wasmInput, zkeyPath: zkeyInput } = await resolveTransactionCircuitFiles();
|
|
6021
4119
|
onProgress?.(30);
|
|
6022
|
-
const { proof, publicSignals } = await
|
|
4120
|
+
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
|
|
6023
4121
|
inputs,
|
|
6024
4122
|
wasmInput,
|
|
6025
4123
|
zkeyInput
|
|
@@ -6099,7 +4197,7 @@ function buildPublicInputsBytesFromSignals(publicSignals) {
|
|
|
6099
4197
|
}
|
|
6100
4198
|
var TRANSACT_DISCRIMINATOR = 0;
|
|
6101
4199
|
function deriveNullifierPDA(programId, poolPDA, nullifier) {
|
|
6102
|
-
return
|
|
4200
|
+
return import_web36.PublicKey.findProgramAddressSync(
|
|
6103
4201
|
[Buffer.from("nullifier"), poolPDA.toBuffer(), Buffer.from(nullifier)],
|
|
6104
4202
|
programId
|
|
6105
4203
|
);
|
|
@@ -6142,7 +4240,7 @@ function buildTransactInstruction(programId, payer, poolPDA, treasuryPDA, merkle
|
|
|
6142
4240
|
// 4. nullifier PDA 0 (writable)
|
|
6143
4241
|
{ pubkey: nullifierPDA1, isSigner: false, isWritable: true },
|
|
6144
4242
|
// 5. nullifier PDA 1 (writable)
|
|
6145
|
-
{ pubkey:
|
|
4243
|
+
{ pubkey: import_web36.SystemProgram.programId, isSigner: false, isWritable: false }
|
|
6146
4244
|
// 6. system_program
|
|
6147
4245
|
];
|
|
6148
4246
|
if (splAccounts) {
|
|
@@ -6151,23 +4249,23 @@ function buildTransactInstruction(programId, payer, poolPDA, treasuryPDA, merkle
|
|
|
6151
4249
|
accounts.push({ pubkey: splAccounts.tokenProgram, isSigner: false, isWritable: false });
|
|
6152
4250
|
if (splAccounts.payerAta) {
|
|
6153
4251
|
if (enableRiskCheck) {
|
|
6154
|
-
accounts.push({ pubkey:
|
|
4252
|
+
accounts.push({ pubkey: import_web36.SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false });
|
|
6155
4253
|
}
|
|
6156
4254
|
accounts.push({ pubkey: splAccounts.payerAta, isSigner: false, isWritable: true });
|
|
6157
4255
|
} else if (enableRiskCheck && !recipient) {
|
|
6158
|
-
accounts.push({ pubkey:
|
|
4256
|
+
accounts.push({ pubkey: import_web36.SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false });
|
|
6159
4257
|
}
|
|
6160
4258
|
} else {
|
|
6161
4259
|
if (recipient) {
|
|
6162
4260
|
accounts.push({ pubkey: recipient, isSigner: false, isWritable: true });
|
|
6163
4261
|
if (enableRiskCheck) {
|
|
6164
|
-
accounts.push({ pubkey:
|
|
4262
|
+
accounts.push({ pubkey: import_web36.SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false });
|
|
6165
4263
|
}
|
|
6166
4264
|
} else if (enableRiskCheck) {
|
|
6167
|
-
accounts.push({ pubkey:
|
|
4265
|
+
accounts.push({ pubkey: import_web36.SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false });
|
|
6168
4266
|
}
|
|
6169
4267
|
}
|
|
6170
|
-
return new
|
|
4268
|
+
return new import_web36.TransactionInstruction({
|
|
6171
4269
|
programId,
|
|
6172
4270
|
keys: accounts,
|
|
6173
4271
|
data
|
|
@@ -6175,10 +4273,10 @@ function buildTransactInstruction(programId, payer, poolPDA, treasuryPDA, merkle
|
|
|
6175
4273
|
}
|
|
6176
4274
|
function getCommonALTAddresses() {
|
|
6177
4275
|
return [
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
4276
|
+
import_web36.SystemProgram.programId,
|
|
4277
|
+
import_web36.SYSVAR_SLOT_HASHES_PUBKEY,
|
|
4278
|
+
import_web36.SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
4279
|
+
import_web36.ComputeBudgetProgram.programId
|
|
6182
4280
|
];
|
|
6183
4281
|
}
|
|
6184
4282
|
function dedupePubkeys(addresses) {
|
|
@@ -6228,23 +4326,23 @@ async function createEphemeralALT(connection, depositor, onProgress, additionalA
|
|
|
6228
4326
|
for (let altAttempt = 0; altAttempt < MAX_ALT_CREATE_RETRIES; altAttempt++) {
|
|
6229
4327
|
try {
|
|
6230
4328
|
const slot = await connection.getSlot("finalized");
|
|
6231
|
-
const [createIx, altAddress] =
|
|
4329
|
+
const [createIx, altAddress] = import_web36.AddressLookupTableProgram.createLookupTable({
|
|
6232
4330
|
authority: depositor.publicKey,
|
|
6233
4331
|
payer: depositor.publicKey,
|
|
6234
4332
|
recentSlot: slot
|
|
6235
4333
|
});
|
|
6236
|
-
const extendIx =
|
|
4334
|
+
const extendIx = import_web36.AddressLookupTableProgram.extendLookupTable({
|
|
6237
4335
|
payer: depositor.publicKey,
|
|
6238
4336
|
authority: depositor.publicKey,
|
|
6239
4337
|
lookupTable: altAddress,
|
|
6240
4338
|
addresses: addressesForCreate
|
|
6241
4339
|
});
|
|
6242
|
-
const tx = new
|
|
4340
|
+
const tx = new import_web36.Transaction().add(createIx).add(extendIx);
|
|
6243
4341
|
const { blockhash } = await connection.getLatestBlockhash();
|
|
6244
4342
|
tx.recentBlockhash = blockhash;
|
|
6245
4343
|
tx.feePayer = depositor.publicKey;
|
|
6246
4344
|
if (depositor.keypair) {
|
|
6247
|
-
await (0,
|
|
4345
|
+
await (0, import_web36.sendAndConfirmTransaction)(connection, tx, [depositor.keypair], { commitment: "confirmed" });
|
|
6248
4346
|
} else if (depositor.signTransaction) {
|
|
6249
4347
|
const signedTx = await depositor.signTransaction(tx);
|
|
6250
4348
|
const sig = await connection.sendRawTransaction(signedTx.serialize());
|
|
@@ -6405,8 +4503,8 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
|
|
|
6405
4503
|
if (riskQuoteIx) {
|
|
6406
4504
|
baseInstructions.unshift(riskQuoteIx);
|
|
6407
4505
|
}
|
|
6408
|
-
const cuLimitIx =
|
|
6409
|
-
const cuPriceIx =
|
|
4506
|
+
const cuLimitIx = import_web36.ComputeBudgetProgram.setComputeUnitLimit({ units: 4e5 });
|
|
4507
|
+
const cuPriceIx = import_web36.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1e5 });
|
|
6410
4508
|
const fullInstructions = [...baseInstructions, cuLimitIx, cuPriceIx, transactIx];
|
|
6411
4509
|
const compactInstructions = [...baseInstructions, cuLimitIx, transactIx];
|
|
6412
4510
|
const minimalInstructions = [...baseInstructions, transactIx];
|
|
@@ -6417,7 +4515,7 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
|
|
|
6417
4515
|
if (useV0) {
|
|
6418
4516
|
const estimateV0Size = (ixs) => {
|
|
6419
4517
|
try {
|
|
6420
|
-
const messageV0 = new
|
|
4518
|
+
const messageV0 = new import_web36.TransactionMessage({
|
|
6421
4519
|
payerKey: depositor.publicKey,
|
|
6422
4520
|
recentBlockhash: blockhash,
|
|
6423
4521
|
instructions: ixs
|
|
@@ -6477,12 +4575,12 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
|
|
|
6477
4575
|
}
|
|
6478
4576
|
let transportAttempt = 0;
|
|
6479
4577
|
while (true) {
|
|
6480
|
-
const messageV0 = new
|
|
4578
|
+
const messageV0 = new import_web36.TransactionMessage({
|
|
6481
4579
|
payerKey: depositor.publicKey,
|
|
6482
4580
|
recentBlockhash: blockhash,
|
|
6483
4581
|
instructions: ixs
|
|
6484
4582
|
}).compileToV0Message(addressLookupTableAccounts);
|
|
6485
|
-
const versionedTx = new
|
|
4583
|
+
const versionedTx = new import_web36.VersionedTransaction(messageV0);
|
|
6486
4584
|
try {
|
|
6487
4585
|
if (depositor.keypair) {
|
|
6488
4586
|
onProgress?.("Signing and sending transaction...");
|
|
@@ -6615,7 +4713,7 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
|
|
|
6615
4713
|
let legacyTransportAttempt = 0;
|
|
6616
4714
|
while (true) {
|
|
6617
4715
|
try {
|
|
6618
|
-
const tx = new
|
|
4716
|
+
const tx = new import_web36.Transaction();
|
|
6619
4717
|
for (const ix of instructions) {
|
|
6620
4718
|
tx.add(ix);
|
|
6621
4719
|
}
|
|
@@ -6623,7 +4721,7 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
|
|
|
6623
4721
|
tx.feePayer = depositor.publicKey;
|
|
6624
4722
|
if (depositor.keypair) {
|
|
6625
4723
|
onProgress?.("Signing and sending transaction...");
|
|
6626
|
-
signature = await (0,
|
|
4724
|
+
signature = await (0, import_web36.sendAndConfirmTransaction)(
|
|
6627
4725
|
connection,
|
|
6628
4726
|
tx,
|
|
6629
4727
|
[depositor.keypair],
|
|
@@ -6797,7 +4895,7 @@ async function fetchRiskQuoteInstruction(riskQuoteUrl, wallet, options) {
|
|
|
6797
4895
|
const signerB58 = signerKey;
|
|
6798
4896
|
const signatureBuf = Buffer.from(signatureB64, "base64");
|
|
6799
4897
|
const messageArr = hexToBytes2(messageHex);
|
|
6800
|
-
const publicKeyBytes = new
|
|
4898
|
+
const publicKeyBytes = new import_web36.PublicKey(signerB58).toBytes();
|
|
6801
4899
|
if (signatureBuf.length !== 64 || messageArr.length !== 33 && messageArr.length !== 41 && messageArr.length !== 50 || publicKeyBytes.length !== 32) {
|
|
6802
4900
|
throw new Error(
|
|
6803
4901
|
`Invalid risk quote response: invalid lengths (sig=${signatureBuf.length}, msg=${messageArr.length}, pk=${publicKeyBytes.length})`
|
|
@@ -6806,7 +4904,7 @@ async function fetchRiskQuoteInstruction(riskQuoteUrl, wallet, options) {
|
|
|
6806
4904
|
const signature = new Uint8Array(signatureBuf);
|
|
6807
4905
|
const message = new Uint8Array(messageArr);
|
|
6808
4906
|
const publicKey = new Uint8Array(publicKeyBytes);
|
|
6809
|
-
return
|
|
4907
|
+
return import_web36.Ed25519Program.createInstructionWithPublicKey({
|
|
6810
4908
|
publicKey,
|
|
6811
4909
|
message,
|
|
6812
4910
|
signature
|
|
@@ -6819,10 +4917,10 @@ async function fetchRiskQuoteInstruction(riskQuoteUrl, wallet, options) {
|
|
|
6819
4917
|
`Invalid risk quote response: expected relay format { signature, message, signer_pubkey } or { instruction }. Received keys: ${keys}`
|
|
6820
4918
|
);
|
|
6821
4919
|
}
|
|
6822
|
-
return new
|
|
6823
|
-
programId: new
|
|
4920
|
+
return new import_web36.TransactionInstruction({
|
|
4921
|
+
programId: new import_web36.PublicKey(raw.programId),
|
|
6824
4922
|
keys: raw.keys.map((k) => ({
|
|
6825
|
-
pubkey: new
|
|
4923
|
+
pubkey: new import_web36.PublicKey(k.pubkey),
|
|
6826
4924
|
isSigner: k.isSigner,
|
|
6827
4925
|
isWritable: k.isWritable
|
|
6828
4926
|
})),
|
|
@@ -7192,7 +5290,7 @@ async function transact(params, options) {
|
|
|
7192
5290
|
const outputCommitments = [];
|
|
7193
5291
|
for (let i = 0; i < paddedOutputs.length; i++) {
|
|
7194
5292
|
const utxo = paddedOutputs[i];
|
|
7195
|
-
const commitment = await
|
|
5293
|
+
const commitment = await computeCommitment(utxo);
|
|
7196
5294
|
outputCommitments.push(commitment);
|
|
7197
5295
|
}
|
|
7198
5296
|
onProgress?.("Computing external data hash...");
|
|
@@ -7273,7 +5371,7 @@ async function transact(params, options) {
|
|
|
7273
5371
|
}
|
|
7274
5372
|
inputNullifiers = [];
|
|
7275
5373
|
for (const utxo of paddedInputs) {
|
|
7276
|
-
inputNullifiers.push(await
|
|
5374
|
+
inputNullifiers.push(await computeNullifier(utxo));
|
|
7277
5375
|
}
|
|
7278
5376
|
if (nextIndex !== null) {
|
|
7279
5377
|
onProgress?.(`[Submit ${submissionAttempts + 1}/${maxRootRetries + 1}] Root: ${rootHex.substring(0, 16)}..., nextIndex: ${nextIndex}`);
|
|
@@ -7706,12 +5804,12 @@ async function transact(params, options) {
|
|
|
7706
5804
|
if (lastError) {
|
|
7707
5805
|
const classified = classifyRelayError(lastError.message);
|
|
7708
5806
|
const isMerkleClass = !(classified instanceof UtxoAlreadySpentError) && !(classified instanceof SanctionsQuoteError) && !isRootNotFoundError2(lastError);
|
|
7709
|
-
if (!
|
|
5807
|
+
if (!IS_BROWSER && isMerkleClass && relayUrl) {
|
|
7710
5808
|
onProgress?.(
|
|
7711
5809
|
"All relay-tree attempts failed. Rebuilding merkle tree from chain as last resort..."
|
|
7712
5810
|
);
|
|
7713
5811
|
try {
|
|
7714
|
-
const [merkleTreePda] =
|
|
5812
|
+
const [merkleTreePda] = import_web36.PublicKey.findProgramAddressSync(
|
|
7715
5813
|
[Buffer.from("merkle_tree"), mint.toBuffer()],
|
|
7716
5814
|
programId
|
|
7717
5815
|
);
|
|
@@ -8355,7 +6453,7 @@ async function swapUtxo(params, options) {
|
|
|
8355
6453
|
let inputNullifiers = [];
|
|
8356
6454
|
const outputCommitments = [];
|
|
8357
6455
|
for (const utxo of paddedOutputs) {
|
|
8358
|
-
outputCommitments.push(await
|
|
6456
|
+
outputCommitments.push(await computeCommitment(utxo));
|
|
8359
6457
|
}
|
|
8360
6458
|
const chainNoteTimestamp = BigInt(Date.now());
|
|
8361
6459
|
onProgress?.("Computing external data hash...");
|
|
@@ -8435,7 +6533,7 @@ async function swapUtxo(params, options) {
|
|
|
8435
6533
|
}
|
|
8436
6534
|
inputNullifiers = [];
|
|
8437
6535
|
for (const utxo of paddedInputs) {
|
|
8438
|
-
inputNullifiers.push(await
|
|
6536
|
+
inputNullifiers.push(await computeNullifier(utxo));
|
|
8439
6537
|
}
|
|
8440
6538
|
if (merkleState) {
|
|
8441
6539
|
onProgress?.(`[Attempt ${attempt + 1}/${maxRootRetries + 1}] Root: ${rootHex.substring(0, 16)}..., nextIndex: ${merkleState.nextIndex}`);
|
|
@@ -8779,7 +6877,7 @@ async function swapWithChange(inputUtxos, swapAmount, outputMint, recipientAta,
|
|
|
8779
6877
|
|
|
8780
6878
|
// src/core/scanner.ts
|
|
8781
6879
|
var import_bs582 = __toESM(require("bs58"), 1);
|
|
8782
|
-
var
|
|
6880
|
+
var import_web37 = require("@solana/web3.js");
|
|
8783
6881
|
var import_spl_token2 = require("@solana/spl-token");
|
|
8784
6882
|
var bs582 = import_bs582.default.default || import_bs582.default;
|
|
8785
6883
|
var TRANSACT_TAG2 = 0;
|
|
@@ -8866,7 +6964,7 @@ function parseSwapOutputMint(data) {
|
|
|
8866
6964
|
const mintBytes = data.slice(SWAP_OUTPUT_MINT_OFFSET, end);
|
|
8867
6965
|
if (mintBytes.every((byte) => byte === 0)) return void 0;
|
|
8868
6966
|
try {
|
|
8869
|
-
return new
|
|
6967
|
+
return new import_web37.PublicKey(mintBytes).toBase58();
|
|
8870
6968
|
} catch {
|
|
8871
6969
|
return void 0;
|
|
8872
6970
|
}
|
|
@@ -8956,7 +7054,7 @@ function parseSwapRecipientAta(data) {
|
|
|
8956
7054
|
const recipientAtaBytes = data.slice(recipientAtaStart, recipientAtaEnd);
|
|
8957
7055
|
if (recipientAtaBytes.every((byte) => byte === 0)) return void 0;
|
|
8958
7056
|
try {
|
|
8959
|
-
return new
|
|
7057
|
+
return new import_web37.PublicKey(recipientAtaBytes).toBase58();
|
|
8960
7058
|
} catch {
|
|
8961
7059
|
return void 0;
|
|
8962
7060
|
}
|
|
@@ -9271,8 +7369,8 @@ async function scanTransactions(opts) {
|
|
|
9271
7369
|
if (onChainAta) {
|
|
9272
7370
|
try {
|
|
9273
7371
|
const expectedAta = (0, import_spl_token2.getAssociatedTokenAddressSync)(
|
|
9274
|
-
new
|
|
9275
|
-
new
|
|
7372
|
+
new import_web37.PublicKey(asset.mint),
|
|
7373
|
+
new import_web37.PublicKey(walletPublicKey)
|
|
9276
7374
|
).toBase58();
|
|
9277
7375
|
if (onChainAta === expectedAta) {
|
|
9278
7376
|
isOurs = true;
|
|
@@ -9575,7 +7673,7 @@ function formatComplianceCsv(report) {
|
|
|
9575
7673
|
}
|
|
9576
7674
|
|
|
9577
7675
|
// src/core/wallet.ts
|
|
9578
|
-
var
|
|
7676
|
+
var import_web38 = require("@solana/web3.js");
|
|
9579
7677
|
init_utxo();
|
|
9580
7678
|
var UtxoWallet = class _UtxoWallet {
|
|
9581
7679
|
constructor(viewingKey) {
|
|
@@ -9788,7 +7886,7 @@ var UtxoWallet = class _UtxoWallet {
|
|
|
9788
7886
|
data.viewingKey ? new Uint8Array(data.viewingKey) : void 0
|
|
9789
7887
|
);
|
|
9790
7888
|
for (const w of data.wallets) {
|
|
9791
|
-
const mint = new
|
|
7889
|
+
const mint = new import_web38.PublicKey(w.mint);
|
|
9792
7890
|
for (const u of w.utxos) {
|
|
9793
7891
|
wallet.addUtxo({
|
|
9794
7892
|
amount: BigInt(u.amount),
|
|
@@ -9871,16 +7969,14 @@ var SimpleWallet = class {
|
|
|
9871
7969
|
};
|
|
9872
7970
|
|
|
9873
7971
|
// src/index.ts
|
|
9874
|
-
var VERSION = "0.1.
|
|
7972
|
+
var VERSION = "0.1.6";
|
|
9875
7973
|
var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
9876
7974
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9877
7975
|
0 && (module.exports = {
|
|
9878
7976
|
CLOAK_PROGRAM_ID,
|
|
9879
7977
|
CloakError,
|
|
9880
7978
|
CloakSDK,
|
|
9881
|
-
DEFAULT_CIRCUITS_URL,
|
|
9882
7979
|
DEFAULT_TRANSACTION_CIRCUITS_URL,
|
|
9883
|
-
EXPECTED_CIRCUIT_HASHES,
|
|
9884
7980
|
FIXED_FEE_LAMPORTS,
|
|
9885
7981
|
LAMPORTS_PER_SOL,
|
|
9886
7982
|
LocalStorageAdapter,
|
|
@@ -9903,7 +7999,6 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
9903
7999
|
VARIABLE_FEE_NUMERATOR,
|
|
9904
8000
|
VARIABLE_FEE_RATE,
|
|
9905
8001
|
VERSION,
|
|
9906
|
-
areCircuitsAvailable,
|
|
9907
8002
|
bigintToBytes32,
|
|
9908
8003
|
bigintToHex,
|
|
9909
8004
|
buildMerkleTree,
|
|
@@ -9917,30 +8012,15 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
9917
8012
|
chainNoteFromBase64,
|
|
9918
8013
|
chainNoteToBase64,
|
|
9919
8014
|
classifyRelayError,
|
|
9920
|
-
cleanupStalePendingOperations,
|
|
9921
|
-
clearPendingDeposits,
|
|
9922
|
-
clearPendingWithdrawals,
|
|
9923
8015
|
computeChainNoteHash,
|
|
9924
|
-
computeCommitment,
|
|
9925
8016
|
computeExtDataHash,
|
|
9926
8017
|
computeMerkleRoot,
|
|
9927
|
-
computeNullifier,
|
|
9928
|
-
computeNullifierAsync,
|
|
9929
|
-
computeNullifierSync,
|
|
9930
|
-
computeOutputsHash,
|
|
9931
|
-
computeOutputsHashAsync,
|
|
9932
|
-
computeOutputsHashSync,
|
|
9933
8018
|
computeProofForLatestDeposit,
|
|
9934
8019
|
computeProofFromChain,
|
|
9935
8020
|
computeSignature,
|
|
9936
|
-
computeSwapOutputsHash,
|
|
9937
|
-
computeSwapOutputsHashAsync,
|
|
9938
|
-
computeSwapOutputsHashSync,
|
|
9939
8021
|
computeUtxoCommitment,
|
|
9940
8022
|
computeUtxoNullifier,
|
|
9941
|
-
copyNoteToClipboard,
|
|
9942
8023
|
createCloakError,
|
|
9943
|
-
createDepositInstruction,
|
|
9944
8024
|
createLogger,
|
|
9945
8025
|
createUtxo,
|
|
9946
8026
|
createZeroUtxo,
|
|
@@ -9960,78 +8040,52 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
9960
8040
|
deriveViewingKeyFromUtxoPrivateKey,
|
|
9961
8041
|
deserializeUtxo,
|
|
9962
8042
|
detectNetworkFromRpcUrl,
|
|
9963
|
-
downloadNote,
|
|
9964
|
-
encodeNoteSimple,
|
|
9965
8043
|
encryptCompactChainNote,
|
|
9966
8044
|
encryptNoteForRecipient,
|
|
9967
8045
|
encryptTransactionMetadata,
|
|
9968
8046
|
encryptTransactionMetadataBundle,
|
|
9969
8047
|
expandSpendKey,
|
|
9970
8048
|
exportKeys,
|
|
9971
|
-
exportNote,
|
|
9972
|
-
exportWalletKeys,
|
|
9973
8049
|
fetchCommitments,
|
|
9974
8050
|
fetchRiskQuoteInstruction,
|
|
9975
8051
|
fetchRiskQuoteIx,
|
|
9976
|
-
filterNotesByNetwork,
|
|
9977
|
-
filterWithdrawableNotes,
|
|
9978
|
-
findNoteByCommitment,
|
|
9979
8052
|
formatAmount,
|
|
9980
8053
|
formatComplianceCsv,
|
|
9981
8054
|
formatErrorForLogging,
|
|
9982
8055
|
formatSol,
|
|
9983
8056
|
fullWithdraw,
|
|
9984
8057
|
generateCloakKeys,
|
|
9985
|
-
generateCommitment,
|
|
9986
|
-
generateCommitmentAsync,
|
|
9987
8058
|
generateMasterSeed,
|
|
9988
|
-
generateNote,
|
|
9989
|
-
generateNoteFromWallet,
|
|
9990
8059
|
generateUtxoKeypair,
|
|
9991
8060
|
generateViewingKeyPair,
|
|
9992
|
-
generateWithdrawRegularProof,
|
|
9993
|
-
generateWithdrawSwapProof,
|
|
9994
8061
|
getAddressExplorerUrl,
|
|
9995
8062
|
getCircuitsPath,
|
|
9996
|
-
getDefaultCircuitsPath,
|
|
9997
8063
|
getDistributableAmount,
|
|
9998
8064
|
getExplorerUrl,
|
|
9999
8065
|
getNkFromUtxoPrivateKey,
|
|
10000
8066
|
getNullifierPDA,
|
|
10001
|
-
getPendingOperationsSummary,
|
|
10002
8067
|
getPublicKey,
|
|
10003
|
-
getPublicViewKey,
|
|
10004
|
-
getRecipientAmount,
|
|
10005
8068
|
getRpcUrlForNetwork,
|
|
10006
8069
|
getShieldPoolPDAs,
|
|
10007
8070
|
getSwapStatePDA,
|
|
10008
|
-
getViewKey,
|
|
10009
|
-
hasPendingOperations,
|
|
10010
8071
|
hexToBigint,
|
|
10011
8072
|
hexToBytes,
|
|
10012
8073
|
importKeys,
|
|
10013
|
-
importWalletKeys,
|
|
10014
8074
|
isDebugEnabled,
|
|
10015
8075
|
isRootNotFoundError,
|
|
10016
8076
|
isValidHex,
|
|
10017
8077
|
isValidRpcUrl,
|
|
10018
8078
|
isValidSolanaAddress,
|
|
10019
8079
|
isWithdrawAmountSufficient,
|
|
10020
|
-
isWithdrawable,
|
|
10021
8080
|
keypairToAdapter,
|
|
10022
|
-
loadPendingDeposits,
|
|
10023
|
-
loadPendingWithdrawals,
|
|
10024
8081
|
parseAmount,
|
|
10025
8082
|
parseError,
|
|
10026
|
-
parseNote,
|
|
10027
8083
|
parseRelayErrorResponse,
|
|
10028
8084
|
parseTransactionError,
|
|
10029
8085
|
partialWithdraw,
|
|
10030
8086
|
poseidonHash,
|
|
10031
8087
|
preflightCheck,
|
|
10032
8088
|
preflightNullifiers,
|
|
10033
|
-
prepareEncryptedOutput,
|
|
10034
|
-
prepareEncryptedOutputForRecipient,
|
|
10035
8089
|
proofToBytes,
|
|
10036
8090
|
pubkeyToFieldElement,
|
|
10037
8091
|
pubkeyToLimbs,
|
|
@@ -10039,16 +8093,11 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
10039
8093
|
randomFieldElement,
|
|
10040
8094
|
readMerkleTreeState,
|
|
10041
8095
|
registerViewingKey,
|
|
10042
|
-
removePendingDeposit,
|
|
10043
|
-
removePendingWithdrawal,
|
|
10044
|
-
savePendingDeposit,
|
|
10045
|
-
savePendingWithdrawal,
|
|
10046
8096
|
scanNotesForWallet,
|
|
10047
8097
|
scanTransactions,
|
|
10048
8098
|
sdkLogger,
|
|
10049
8099
|
selectUtxos,
|
|
10050
8100
|
sendTransaction,
|
|
10051
|
-
serializeNote,
|
|
10052
8101
|
serializeUtxo,
|
|
10053
8102
|
setCircuitsPath,
|
|
10054
8103
|
setDebugMode,
|
|
@@ -10062,21 +8111,12 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
10062
8111
|
transfer,
|
|
10063
8112
|
truncate,
|
|
10064
8113
|
tryDecryptNote,
|
|
10065
|
-
updateNoteWithDeposit,
|
|
10066
|
-
updatePendingDeposit,
|
|
10067
|
-
updatePendingWithdrawal,
|
|
10068
8114
|
utxoBigintToBytes32,
|
|
10069
8115
|
utxoEquals,
|
|
10070
8116
|
utxoHexToBigint,
|
|
10071
|
-
validateDepositParams,
|
|
10072
|
-
validateNote,
|
|
10073
8117
|
validateOutputsSum,
|
|
10074
8118
|
validateRoot,
|
|
10075
|
-
validateTransfers,
|
|
10076
8119
|
validateWalletConnected,
|
|
10077
|
-
validateWithdrawableNote,
|
|
10078
|
-
verifyAllCircuits,
|
|
10079
|
-
verifyCircuitIntegrity,
|
|
10080
8120
|
verifyUtxos,
|
|
10081
8121
|
waitForRoot,
|
|
10082
8122
|
withTiming
|