@cloak.dev/sdk 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +196 -22
- package/dist/index.d.cts +1054 -920
- package/dist/index.d.ts +1054 -920
- package/dist/index.js +189 -22
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -301,12 +301,15 @@ __export(index_exports, {
|
|
|
301
301
|
MemoryStorageAdapter: () => MemoryStorageAdapter,
|
|
302
302
|
MerkleTree: () => MerkleTree,
|
|
303
303
|
NATIVE_SOL_MINT: () => NATIVE_SOL_MINT,
|
|
304
|
+
RelayInternalError: () => RelayInternalError,
|
|
304
305
|
RelayService: () => RelayService,
|
|
305
306
|
RootNotFoundError: () => RootNotFoundError,
|
|
306
307
|
SCANNER_SUPPORTS_TRANSACT_SWAP: () => SCANNER_SUPPORTS_TRANSACT_SWAP,
|
|
307
308
|
SIGN_IN_MESSAGE: () => SIGN_IN_MESSAGE,
|
|
309
|
+
SanctionsQuoteError: () => SanctionsQuoteError,
|
|
308
310
|
ShieldPoolErrors: () => ShieldPoolErrors,
|
|
309
311
|
SimpleWallet: () => SimpleWallet,
|
|
312
|
+
UtxoAlreadySpentError: () => UtxoAlreadySpentError,
|
|
310
313
|
UtxoWallet: () => UtxoWallet,
|
|
311
314
|
VARIABLE_FEE_DENOMINATOR: () => VARIABLE_FEE_DENOMINATOR,
|
|
312
315
|
VARIABLE_FEE_NUMERATOR: () => VARIABLE_FEE_NUMERATOR,
|
|
@@ -325,6 +328,7 @@ __export(index_exports, {
|
|
|
325
328
|
calculateRelayFee: () => calculateRelayFee,
|
|
326
329
|
chainNoteFromBase64: () => chainNoteFromBase64,
|
|
327
330
|
chainNoteToBase64: () => chainNoteToBase64,
|
|
331
|
+
classifyRelayError: () => classifyRelayError,
|
|
328
332
|
cleanupStalePendingOperations: () => cleanupStalePendingOperations,
|
|
329
333
|
clearPendingDeposits: () => clearPendingDeposits,
|
|
330
334
|
clearPendingWithdrawals: () => clearPendingWithdrawals,
|
|
@@ -437,6 +441,7 @@ __export(index_exports, {
|
|
|
437
441
|
partialWithdraw: () => partialWithdraw,
|
|
438
442
|
poseidonHash: () => poseidonHash,
|
|
439
443
|
preflightCheck: () => preflightCheck,
|
|
444
|
+
preflightNullifiers: () => preflightNullifiers,
|
|
440
445
|
prepareEncryptedOutput: () => prepareEncryptedOutput,
|
|
441
446
|
prepareEncryptedOutputForRecipient: () => prepareEncryptedOutputForRecipient,
|
|
442
447
|
proofToBytes: () => proofToBytes,
|
|
@@ -484,6 +489,7 @@ __export(index_exports, {
|
|
|
484
489
|
validateWithdrawableNote: () => validateWithdrawableNote,
|
|
485
490
|
verifyAllCircuits: () => verifyAllCircuits,
|
|
486
491
|
verifyCircuitIntegrity: () => verifyCircuitIntegrity,
|
|
492
|
+
verifyUtxos: () => verifyUtxos,
|
|
487
493
|
waitForRoot: () => waitForRoot,
|
|
488
494
|
withTiming: () => withTiming
|
|
489
495
|
});
|
|
@@ -1339,6 +1345,71 @@ function parseRelayErrorResponse(responseText) {
|
|
|
1339
1345
|
return { isRootNotFound: false };
|
|
1340
1346
|
}
|
|
1341
1347
|
}
|
|
1348
|
+
var UtxoAlreadySpentError = class extends Error {
|
|
1349
|
+
constructor(message, spentUtxoCommitments = []) {
|
|
1350
|
+
super(message);
|
|
1351
|
+
this.spentUtxoCommitments = spentUtxoCommitments;
|
|
1352
|
+
this.name = "UtxoAlreadySpentError";
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
var SanctionsQuoteError = class extends Error {
|
|
1356
|
+
constructor(message, onChainCode, subKind) {
|
|
1357
|
+
super(message);
|
|
1358
|
+
this.onChainCode = onChainCode;
|
|
1359
|
+
this.subKind = subKind;
|
|
1360
|
+
this.name = "SanctionsQuoteError";
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
var RelayInternalError = class extends Error {
|
|
1364
|
+
constructor(message, relayMessage, httpStatus, cachedTreeFromChain) {
|
|
1365
|
+
super(message);
|
|
1366
|
+
this.relayMessage = relayMessage;
|
|
1367
|
+
this.httpStatus = httpStatus;
|
|
1368
|
+
this.cachedTreeFromChain = cachedTreeFromChain;
|
|
1369
|
+
this.name = "RelayInternalError";
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
function classifyRelayError(responseText, httpStatus) {
|
|
1373
|
+
let inner = "";
|
|
1374
|
+
try {
|
|
1375
|
+
const parsed = JSON.parse(responseText);
|
|
1376
|
+
inner = parsed?.message ?? "";
|
|
1377
|
+
} catch {
|
|
1378
|
+
inner = responseText;
|
|
1379
|
+
}
|
|
1380
|
+
const haystack = inner.toLowerCase();
|
|
1381
|
+
if (haystack.includes("0x1020") || haystack.includes("doublespend")) {
|
|
1382
|
+
return new UtxoAlreadySpentError(
|
|
1383
|
+
"This shielded balance was already spent. The UTXO's nullifier is registered on-chain \u2014 typically means you spent it via another session or device. Run verifyUtxos() to reconcile local state."
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
if (haystack.includes("0x10b0") || haystack.includes("rangequoteexpired")) {
|
|
1387
|
+
return new SanctionsQuoteError(
|
|
1388
|
+
"Range sanctions quote expired before the on-chain program processed the transaction. The relay's quote-expiry window is too short relative to proof-gen + confirmation latency, or on-chain clock drifted. Retry the send; the relay will issue a fresh quote.",
|
|
1389
|
+
4272,
|
|
1390
|
+
"expired"
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
if (haystack.includes("0x10b2") || haystack.includes("rangequotewalletmismatch")) {
|
|
1394
|
+
return new SanctionsQuoteError(
|
|
1395
|
+
"Range sanctions quote's wallet doesn't match the on-chain expected wallet. Indicates a relay config drift \u2014 the sender used for quote signing doesn't match the program's pool.range_signer / fee payer. Relay operators must fix; caller retry will not help.",
|
|
1396
|
+
4274,
|
|
1397
|
+
"wallet_mismatch"
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
if (haystack.includes("0x10b3") || haystack.includes("rangequotemissinged25519")) {
|
|
1401
|
+
return new SanctionsQuoteError(
|
|
1402
|
+
"Transaction reached the on-chain program without the required Ed25519 sanctions quote instruction. This is a relay bug \u2014 the relay constructed the tx without attaching the signed quote. Caller retry will not help.",
|
|
1403
|
+
4275,
|
|
1404
|
+
"missing_ix"
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
return new RelayInternalError(
|
|
1408
|
+
`Relay returned ${httpStatus ?? "an error"}: ${inner || responseText}`,
|
|
1409
|
+
inner || responseText,
|
|
1410
|
+
httpStatus
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1342
1413
|
var ShieldPoolErrors = {
|
|
1343
1414
|
// Root management errors
|
|
1344
1415
|
4096: "Invalid Merkle root",
|
|
@@ -4635,6 +4706,64 @@ async function decryptComplianceMetadataWithMasterKey(encrypted, masterComplianc
|
|
|
4635
4706
|
return decryptWithSharedSecret(payload, sharedSecret);
|
|
4636
4707
|
}
|
|
4637
4708
|
|
|
4709
|
+
// src/utils/verify-utxos.ts
|
|
4710
|
+
init_utxo();
|
|
4711
|
+
async function verifyUtxos(utxos, connection, programId, commitment = "confirmed") {
|
|
4712
|
+
const checkable = [];
|
|
4713
|
+
const skipped = [];
|
|
4714
|
+
for (const utxo of utxos) {
|
|
4715
|
+
if (utxo.commitment === void 0 || utxo.mintAddress === void 0 || utxo.keypair === void 0 || utxo.keypair.privateKey === void 0) {
|
|
4716
|
+
skipped.push(utxo);
|
|
4717
|
+
continue;
|
|
4718
|
+
}
|
|
4719
|
+
try {
|
|
4720
|
+
const nullifierBig = await computeNullifier2(utxo);
|
|
4721
|
+
const nullifierHex = nullifierBig.toString(16).padStart(64, "0");
|
|
4722
|
+
const nullifierBytes = Buffer.from(nullifierHex, "hex");
|
|
4723
|
+
const { pool } = getShieldPoolPDAs(programId, utxo.mintAddress);
|
|
4724
|
+
const [pda] = getNullifierPDA(pool, nullifierBytes, programId);
|
|
4725
|
+
checkable.push({ utxo, pda });
|
|
4726
|
+
} catch {
|
|
4727
|
+
skipped.push(utxo);
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
if (checkable.length === 0) {
|
|
4731
|
+
return { spent: [], unspent: [], skipped };
|
|
4732
|
+
}
|
|
4733
|
+
const CHUNK = 100;
|
|
4734
|
+
const infos = [];
|
|
4735
|
+
for (let i = 0; i < checkable.length; i += CHUNK) {
|
|
4736
|
+
const page = checkable.slice(i, i + CHUNK).map((e) => e.pda);
|
|
4737
|
+
const pageInfos = await connection.getMultipleAccountsInfo(page, commitment);
|
|
4738
|
+
infos.push(...pageInfos);
|
|
4739
|
+
}
|
|
4740
|
+
const spent = [];
|
|
4741
|
+
const unspent = [];
|
|
4742
|
+
for (let i = 0; i < checkable.length; i++) {
|
|
4743
|
+
if (infos[i] !== null) {
|
|
4744
|
+
spent.push(checkable[i].utxo);
|
|
4745
|
+
} else {
|
|
4746
|
+
unspent.push(checkable[i].utxo);
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
return { spent, unspent, skipped };
|
|
4750
|
+
}
|
|
4751
|
+
async function preflightNullifiers(utxos, connection, programId, commitment = "confirmed") {
|
|
4752
|
+
if (utxos.length === 0) return;
|
|
4753
|
+
const result = await verifyUtxos(utxos, connection, programId, commitment);
|
|
4754
|
+
if (result.spent.length > 0) {
|
|
4755
|
+
const allCommitments = result.spent.map(
|
|
4756
|
+
(u) => u.commitment !== void 0 ? u.commitment.toString(16).padStart(64, "0") : "<unknown>"
|
|
4757
|
+
);
|
|
4758
|
+
const preview = allCommitments.slice(0, 5);
|
|
4759
|
+
const extra = allCommitments.length > preview.length ? ` (+${allCommitments.length - preview.length} more)` : "";
|
|
4760
|
+
throw new UtxoAlreadySpentError(
|
|
4761
|
+
`Pre-flight detected ${allCommitments.length} already-spent UTXO(s). First spent commitment(s): ${preview.join(", ")}${extra}. Call verifyUtxos() and remove these from local state before retrying.`,
|
|
4762
|
+
allCommitments
|
|
4763
|
+
);
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
|
|
4638
4767
|
// src/services/risk-oracle.ts
|
|
4639
4768
|
function buildRangeRiskScoreJob(OracleJob, walletAddress) {
|
|
4640
4769
|
return OracleJob.fromObject({
|
|
@@ -4981,10 +5110,15 @@ var import_bs58 = __toESM(require("bs58"), 1);
|
|
|
4981
5110
|
var bs58 = import_bs58.default.default || import_bs58.default;
|
|
4982
5111
|
var TRANSACT_TAG = 0;
|
|
4983
5112
|
var TRANSACT_SWAP_TAG = 1;
|
|
4984
|
-
var DEPOSIT_TAG = 1;
|
|
4985
5113
|
var PROOF_LEN = 256;
|
|
4986
5114
|
var PUBLIC_INPUTS_LEN = 264;
|
|
4987
5115
|
var COMMITMENTS_OFFSET = 168;
|
|
5116
|
+
function isAllZero(b) {
|
|
5117
|
+
for (let i = 0; i < b.length; i++) {
|
|
5118
|
+
if (b[i] !== 0) return false;
|
|
5119
|
+
}
|
|
5120
|
+
return true;
|
|
5121
|
+
}
|
|
4988
5122
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
4989
5123
|
var DEFAULT_MAX_RETRIES = 3;
|
|
4990
5124
|
async function fetchWithRetry(url, options = {}) {
|
|
@@ -5122,24 +5256,20 @@ async function buildMerkleTreeFromRelay(relayUrl, options) {
|
|
|
5122
5256
|
function extractCommitmentsFromInstruction(data) {
|
|
5123
5257
|
if (data.length === 0) return [];
|
|
5124
5258
|
const tag = data[0];
|
|
5125
|
-
if (tag
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
if (
|
|
5138
|
-
|
|
5139
|
-
const commitment = data.slice(1 + 8, 1 + 8 + 32);
|
|
5140
|
-
return [commitment];
|
|
5141
|
-
}
|
|
5142
|
-
return [];
|
|
5259
|
+
if (tag !== TRANSACT_TAG && tag !== TRANSACT_SWAP_TAG) return [];
|
|
5260
|
+
const publicInputsStart = 1 + PROOF_LEN;
|
|
5261
|
+
const publicInputsEnd = publicInputsStart + PUBLIC_INPUTS_LEN;
|
|
5262
|
+
if (data.length < publicInputsEnd) return [];
|
|
5263
|
+
const publicInputs = data.slice(publicInputsStart, publicInputsEnd);
|
|
5264
|
+
const c0 = publicInputs.slice(COMMITMENTS_OFFSET, COMMITMENTS_OFFSET + 32);
|
|
5265
|
+
const c1 = publicInputs.slice(
|
|
5266
|
+
COMMITMENTS_OFFSET + 32,
|
|
5267
|
+
COMMITMENTS_OFFSET + 64
|
|
5268
|
+
);
|
|
5269
|
+
const out = [];
|
|
5270
|
+
if (!isAllZero(c0)) out.push(c0);
|
|
5271
|
+
if (!isAllZero(c1)) out.push(c1);
|
|
5272
|
+
return out;
|
|
5143
5273
|
}
|
|
5144
5274
|
function isBrowser() {
|
|
5145
5275
|
return typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
@@ -6746,6 +6876,7 @@ async function transact(params, options) {
|
|
|
6746
6876
|
if (outputUtxos.length > 2) {
|
|
6747
6877
|
throw new Error("Maximum 2 output UTXOs allowed");
|
|
6748
6878
|
}
|
|
6879
|
+
await preflightNullifiers(inputUtxos, connection, programId);
|
|
6749
6880
|
const mint = inputUtxos.find((u) => u.amount > 0)?.mintAddress ?? outputUtxos.find((u) => u.amount > 0)?.mintAddress ?? NATIVE_SOL_MINT;
|
|
6750
6881
|
const paddedInputs = [...inputUtxos];
|
|
6751
6882
|
while (paddedInputs.length < 2) {
|
|
@@ -7573,10 +7704,46 @@ async function transact(params, options) {
|
|
|
7573
7704
|
}
|
|
7574
7705
|
}
|
|
7575
7706
|
if (lastError) {
|
|
7707
|
+
const classified = classifyRelayError(lastError.message);
|
|
7708
|
+
const isMerkleClass = !(classified instanceof UtxoAlreadySpentError) && !(classified instanceof SanctionsQuoteError) && !isRootNotFoundError2(lastError);
|
|
7709
|
+
if (!IS_BROWSER2 && isMerkleClass && relayUrl) {
|
|
7710
|
+
onProgress?.(
|
|
7711
|
+
"All relay-tree attempts failed. Rebuilding merkle tree from chain as last resort..."
|
|
7712
|
+
);
|
|
7713
|
+
try {
|
|
7714
|
+
const [merkleTreePda] = import_web37.PublicKey.findProgramAddressSync(
|
|
7715
|
+
[Buffer.from("merkle_tree"), mint.toBuffer()],
|
|
7716
|
+
programId
|
|
7717
|
+
);
|
|
7718
|
+
const chainTree = await buildMerkleTreeFromChain(
|
|
7719
|
+
connection,
|
|
7720
|
+
programId,
|
|
7721
|
+
merkleTreePda,
|
|
7722
|
+
onProgress
|
|
7723
|
+
);
|
|
7724
|
+
const chainRootHex = chainTree.root().toString(16).padStart(64, "0");
|
|
7725
|
+
onProgress?.(
|
|
7726
|
+
`Chain-rebuilt tree root: ${chainRootHex.substring(0, 16)}... \u2014 attached to error for caller retry`
|
|
7727
|
+
);
|
|
7728
|
+
throw new RelayInternalError(
|
|
7729
|
+
`All relay-tree attempts failed. Chain-rebuilt tree available (root ${chainRootHex.substring(0, 16)}...). Retry this call with options.cachedMerkleTree = err.cachedTreeFromChain. Original error: ${lastError.message}`,
|
|
7730
|
+
lastError.message,
|
|
7731
|
+
void 0,
|
|
7732
|
+
chainTree
|
|
7733
|
+
);
|
|
7734
|
+
} catch (e) {
|
|
7735
|
+
if (e instanceof RelayInternalError) throw e;
|
|
7736
|
+
onProgress?.(
|
|
7737
|
+
`Chain-replay fallback failed: ${e instanceof Error ? e.message : String(e)}`
|
|
7738
|
+
);
|
|
7739
|
+
}
|
|
7740
|
+
}
|
|
7576
7741
|
if (isRootNotFoundError2(lastError)) {
|
|
7577
|
-
throw new Error(
|
|
7742
|
+
throw new Error(
|
|
7743
|
+
`Transaction failed after ${maxRootRetries + 1} retries: ${lastError.message}`
|
|
7744
|
+
);
|
|
7578
7745
|
}
|
|
7579
|
-
throw
|
|
7746
|
+
throw classified;
|
|
7580
7747
|
}
|
|
7581
7748
|
if (relayUrl && outputCommitments.length >= 2) {
|
|
7582
7749
|
const hasOnChainIndices = commitmentIndices[0] >= 0 && commitmentIndices[1] >= 0;
|
|
@@ -7880,6 +8047,7 @@ async function swapUtxo(params, options) {
|
|
|
7880
8047
|
if (inputUtxos.length > 2) {
|
|
7881
8048
|
throw new Error("Maximum 2 input UTXOs allowed");
|
|
7882
8049
|
}
|
|
8050
|
+
await preflightNullifiers(inputUtxos, connection, programId);
|
|
7883
8051
|
const outputMintAccount = await connection.getAccountInfo(outputMint, {
|
|
7884
8052
|
commitment: "confirmed"
|
|
7885
8053
|
});
|
|
@@ -9703,7 +9871,7 @@ var SimpleWallet = class {
|
|
|
9703
9871
|
};
|
|
9704
9872
|
|
|
9705
9873
|
// src/index.ts
|
|
9706
|
-
var VERSION = "1.
|
|
9874
|
+
var VERSION = "0.1.5";
|
|
9707
9875
|
var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
9708
9876
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9709
9877
|
0 && (module.exports = {
|
|
@@ -9721,12 +9889,15 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
9721
9889
|
MemoryStorageAdapter,
|
|
9722
9890
|
MerkleTree,
|
|
9723
9891
|
NATIVE_SOL_MINT,
|
|
9892
|
+
RelayInternalError,
|
|
9724
9893
|
RelayService,
|
|
9725
9894
|
RootNotFoundError,
|
|
9726
9895
|
SCANNER_SUPPORTS_TRANSACT_SWAP,
|
|
9727
9896
|
SIGN_IN_MESSAGE,
|
|
9897
|
+
SanctionsQuoteError,
|
|
9728
9898
|
ShieldPoolErrors,
|
|
9729
9899
|
SimpleWallet,
|
|
9900
|
+
UtxoAlreadySpentError,
|
|
9730
9901
|
UtxoWallet,
|
|
9731
9902
|
VARIABLE_FEE_DENOMINATOR,
|
|
9732
9903
|
VARIABLE_FEE_NUMERATOR,
|
|
@@ -9745,6 +9916,7 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
9745
9916
|
calculateRelayFee,
|
|
9746
9917
|
chainNoteFromBase64,
|
|
9747
9918
|
chainNoteToBase64,
|
|
9919
|
+
classifyRelayError,
|
|
9748
9920
|
cleanupStalePendingOperations,
|
|
9749
9921
|
clearPendingDeposits,
|
|
9750
9922
|
clearPendingWithdrawals,
|
|
@@ -9857,6 +10029,7 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
9857
10029
|
partialWithdraw,
|
|
9858
10030
|
poseidonHash,
|
|
9859
10031
|
preflightCheck,
|
|
10032
|
+
preflightNullifiers,
|
|
9860
10033
|
prepareEncryptedOutput,
|
|
9861
10034
|
prepareEncryptedOutputForRecipient,
|
|
9862
10035
|
proofToBytes,
|
|
@@ -9904,6 +10077,7 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
9904
10077
|
validateWithdrawableNote,
|
|
9905
10078
|
verifyAllCircuits,
|
|
9906
10079
|
verifyCircuitIntegrity,
|
|
10080
|
+
verifyUtxos,
|
|
9907
10081
|
waitForRoot,
|
|
9908
10082
|
withTiming
|
|
9909
10083
|
});
|