@cloak.dev/sdk 0.1.8 → 0.2.0

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.
@@ -0,0 +1,594 @@
1
+ // src/core/utxo.ts
2
+ import { PublicKey } from "@solana/web3.js";
3
+ import { buildPoseidon as buildPoseidon2 } from "circomlibjs";
4
+ import { blake3 } from "@noble/hashes/blake3";
5
+
6
+ // src/utils/crypto.ts
7
+ import { buildPoseidon } from "circomlibjs";
8
+
9
+ // src/core/types.ts
10
+ var CloakError = class extends Error {
11
+ constructor(message, category, retryable = false, originalError) {
12
+ super(message);
13
+ this.category = category;
14
+ this.retryable = retryable;
15
+ this.originalError = originalError;
16
+ this.name = "CloakError";
17
+ }
18
+ };
19
+
20
+ // src/utils/crypto.ts
21
+ var poseidon = null;
22
+ async function getPoseidon() {
23
+ if (!poseidon) {
24
+ poseidon = await buildPoseidon();
25
+ }
26
+ return poseidon;
27
+ }
28
+ async function poseidonHash(inputs) {
29
+ const p = await getPoseidon();
30
+ const hash = p(inputs.map((x) => p.F.e(x)));
31
+ return p.F.toObject(hash);
32
+ }
33
+ function splitTo2Limbs(value) {
34
+ const mask = (1n << 128n) - 1n;
35
+ const lo = value & mask;
36
+ const hi = value >> 128n;
37
+ return [lo, hi];
38
+ }
39
+ function pubkeyToLimbs(pubkey) {
40
+ const bytes = typeof pubkey.toBytes === "function" ? pubkey.toBytes() : pubkey;
41
+ const value = BigInt("0x" + Buffer.from(bytes).toString("hex"));
42
+ return splitTo2Limbs(value);
43
+ }
44
+ async function computeMerkleRoot(leaf, pathElements, pathIndices) {
45
+ let current = leaf;
46
+ for (let i = 0; i < pathElements.length; i++) {
47
+ if (pathIndices[i] === 0) {
48
+ current = await poseidonHash([current, pathElements[i]]);
49
+ } else {
50
+ current = await poseidonHash([pathElements[i], current]);
51
+ }
52
+ }
53
+ return current;
54
+ }
55
+ function hexToBigint(hex) {
56
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
57
+ return BigInt("0x" + cleanHex);
58
+ }
59
+ async function computeCommitment(amount, r, sk_spend) {
60
+ const [sk0, sk1] = splitTo2Limbs(sk_spend);
61
+ const [r0, r1] = splitTo2Limbs(r);
62
+ const pk_spend = await poseidonHash([sk0, sk1]);
63
+ return await poseidonHash([amount, r0, r1, pk_spend]);
64
+ }
65
+ async function generateCommitmentAsync(amountLamports, r, skSpend) {
66
+ const amount = BigInt(amountLamports);
67
+ const rValue = hexToBigint(bytesToHex(r));
68
+ const skValue = hexToBigint(bytesToHex(skSpend));
69
+ return await computeCommitment(amount, rValue, skValue);
70
+ }
71
+ function generateCommitment(_amountLamports, _r, _skSpend) {
72
+ throw new Error("generateCommitment is deprecated. Use generateCommitmentAsync instead.");
73
+ }
74
+ async function computeNullifier(sk_spend, leafIndex) {
75
+ const [sk0, sk1] = splitTo2Limbs(sk_spend);
76
+ return await poseidonHash([sk0, sk1, leafIndex]);
77
+ }
78
+ async function computeNullifierAsync(skSpend, leafIndex) {
79
+ const skValue = typeof skSpend === "string" ? hexToBigint(skSpend) : hexToBigint(bytesToHex(skSpend));
80
+ return await computeNullifier(skValue, BigInt(leafIndex));
81
+ }
82
+ function computeNullifierSync(_skSpend, _leafIndex) {
83
+ throw new Error("computeNullifierSync is deprecated. Use computeNullifierAsync instead.");
84
+ }
85
+ async function computeOutputsHashAsync(outputs) {
86
+ let hash = 0n;
87
+ for (const output of outputs) {
88
+ const [lo, hi] = pubkeyToLimbs(output.recipient);
89
+ hash = await poseidonHash([hash, lo, hi, BigInt(output.amount)]);
90
+ }
91
+ return hash;
92
+ }
93
+ async function computeOutputsHash(outAddr, outAmount, outFlags) {
94
+ let hash = 0n;
95
+ for (let i = 0; i < 5; i++) {
96
+ if (outFlags[i] === 1) {
97
+ hash = await poseidonHash([hash, outAddr[i][0], outAddr[i][1], outAmount[i]]);
98
+ }
99
+ }
100
+ return hash;
101
+ }
102
+ function computeOutputsHashSync(_outputs) {
103
+ throw new Error("computeOutputsHashSync is deprecated. Use computeOutputsHashAsync instead.");
104
+ }
105
+ async function computeSwapOutputsHash(inputMintLimbs, outputMintLimbs, recipientAtaLimbs, minOutputAmount, publicAmount) {
106
+ return await poseidonHash([
107
+ inputMintLimbs[0],
108
+ inputMintLimbs[1],
109
+ outputMintLimbs[0],
110
+ outputMintLimbs[1],
111
+ recipientAtaLimbs[0],
112
+ recipientAtaLimbs[1],
113
+ minOutputAmount,
114
+ publicAmount
115
+ ]);
116
+ }
117
+ async function computeSwapOutputsHashAsync(inputMint, outputMint, recipientAta, minOutputAmount, amount) {
118
+ const inputMintLimbs = pubkeyToLimbs(inputMint);
119
+ const outputMintLimbs = pubkeyToLimbs(outputMint);
120
+ const recipientAtaLimbs = pubkeyToLimbs(recipientAta);
121
+ return await computeSwapOutputsHash(
122
+ inputMintLimbs,
123
+ outputMintLimbs,
124
+ recipientAtaLimbs,
125
+ BigInt(minOutputAmount),
126
+ BigInt(amount)
127
+ );
128
+ }
129
+ function computeSwapOutputsHashSync(_outputMint, _recipientAta, _minOutputAmount, _amount) {
130
+ throw new Error("computeSwapOutputsHashSync is deprecated. Use computeSwapOutputsHashAsync instead.");
131
+ }
132
+ function bigintToBytes32(n) {
133
+ const hex = n.toString(16).padStart(64, "0");
134
+ const bytes = new Uint8Array(32);
135
+ for (let i = 0; i < 32; i++) {
136
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
137
+ }
138
+ return bytes;
139
+ }
140
+ function hexToBytes(hex) {
141
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
142
+ const bytes = new Uint8Array(cleanHex.length / 2);
143
+ for (let i = 0; i < cleanHex.length; i += 2) {
144
+ bytes[i / 2] = parseInt(cleanHex.substr(i, 2), 16);
145
+ }
146
+ return bytes;
147
+ }
148
+ function bytesToHex(bytes, prefix = false) {
149
+ const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
150
+ return prefix ? `0x${hex}` : hex;
151
+ }
152
+ var InsecureRandomnessError = class extends CloakError {
153
+ constructor(message, originalError) {
154
+ super(message, "environment", false, originalError);
155
+ this.name = "InsecureRandomnessError";
156
+ }
157
+ };
158
+ var GET_RANDOM_VALUES_MAX_BYTES = 65536;
159
+ function describeFailure(err) {
160
+ if (err instanceof Error) {
161
+ return err.message ? `${err.name}: ${err.message}` : err.name;
162
+ }
163
+ return String(err);
164
+ }
165
+ function resolveWebCrypto() {
166
+ const g = globalThis;
167
+ const cryptoObj = g?.crypto ?? g?.self?.crypto ?? g?.window?.crypto;
168
+ return cryptoObj && typeof cryptoObj.getRandomValues === "function" ? cryptoObj : void 0;
169
+ }
170
+ function resolveNodeCrypto() {
171
+ const proc = globalThis?.process;
172
+ if (typeof proc?.getBuiltinModule !== "function") return void 0;
173
+ return proc.getBuiltinModule("node:crypto");
174
+ }
175
+ function randomBytes(length) {
176
+ if (!Number.isInteger(length) || length < 0) {
177
+ throw new InsecureRandomnessError(
178
+ `randomBytes(length) requires a non-negative integer length, received ${String(length)}`
179
+ );
180
+ }
181
+ const bytes = new Uint8Array(length);
182
+ if (length === 0) return bytes;
183
+ const failures = [];
184
+ const webCrypto = resolveWebCrypto();
185
+ if (webCrypto) {
186
+ try {
187
+ for (let offset = 0; offset < length; offset += GET_RANDOM_VALUES_MAX_BYTES) {
188
+ const end = Math.min(offset + GET_RANDOM_VALUES_MAX_BYTES, length);
189
+ webCrypto.getRandomValues(bytes.subarray(offset, end));
190
+ }
191
+ return bytes;
192
+ } catch (err) {
193
+ failures.push(`globalThis.crypto.getRandomValues -> ${describeFailure(err)}`);
194
+ }
195
+ } else {
196
+ failures.push("globalThis.crypto.getRandomValues -> not available in this runtime");
197
+ }
198
+ try {
199
+ const nodeCrypto = resolveNodeCrypto();
200
+ if (nodeCrypto && typeof nodeCrypto.randomFillSync === "function") {
201
+ nodeCrypto.randomFillSync(bytes);
202
+ return bytes;
203
+ }
204
+ failures.push(
205
+ nodeCrypto ? "node:crypto.randomFillSync -> not a function on the resolved node:crypto module" : "node:crypto -> not available (process.getBuiltinModule missing; needs Node >= 20.16)"
206
+ );
207
+ } catch (err) {
208
+ failures.push(`node:crypto.randomFillSync -> ${describeFailure(err)}`);
209
+ }
210
+ throw new InsecureRandomnessError(
211
+ `No cryptographically secure random source is available; refusing to produce ${length} bytes of secret material. Cloak fails closed here because note spend keys, blindings and nonces derived from a predictable generator are permanently compromised. Attempted sources: ${failures.join(" | ")}. Fix the runtime (Node >= 18, a browser/worker with Web Crypto, or install a crypto.getRandomValues polyfill such as react-native-get-random-values).`
212
+ );
213
+ }
214
+ function isValidHex(hex, expectedLength) {
215
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
216
+ if (!/^[0-9a-f]*$/i.test(cleanHex)) {
217
+ return false;
218
+ }
219
+ if (cleanHex.length % 2 !== 0) {
220
+ return false;
221
+ }
222
+ if (expectedLength !== void 0) {
223
+ return cleanHex.length === expectedLength * 2;
224
+ }
225
+ return true;
226
+ }
227
+ var BN254_MODULUS = BigInt("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47");
228
+ function proofToBytes(proof) {
229
+ const pi_a_x = BigInt(proof.pi_a[0]);
230
+ const pi_a_y = BigInt(proof.pi_a[1]);
231
+ const pi_a_y_neg = (BN254_MODULUS - pi_a_y) % BN254_MODULUS;
232
+ const pi_a_x_le = bigintToBytes32LE(pi_a_x);
233
+ const pi_a_y_neg_le = bigintToBytes32LE(pi_a_y_neg);
234
+ const pi_a_le = new Uint8Array(64);
235
+ pi_a_le.set(pi_a_x_le, 0);
236
+ pi_a_le.set(pi_a_y_neg_le, 32);
237
+ const pi_a_be = convertEndianness32(pi_a_le);
238
+ const pi_b_x1 = BigInt(proof.pi_b[0][0]);
239
+ const pi_b_x2 = BigInt(proof.pi_b[0][1]);
240
+ const pi_b_y1 = BigInt(proof.pi_b[1][0]);
241
+ const pi_b_y2 = BigInt(proof.pi_b[1][1]);
242
+ const pi_b_x1_le = bigintToBytes32LE(pi_b_x1);
243
+ const pi_b_x2_le = bigintToBytes32LE(pi_b_x2);
244
+ const pi_b_y1_le = bigintToBytes32LE(pi_b_y1);
245
+ const pi_b_y2_le = bigintToBytes32LE(pi_b_y2);
246
+ const pi_b_le = new Uint8Array(128);
247
+ pi_b_le.set(pi_b_x1_le, 0);
248
+ pi_b_le.set(pi_b_x2_le, 32);
249
+ pi_b_le.set(pi_b_y1_le, 64);
250
+ pi_b_le.set(pi_b_y2_le, 96);
251
+ const pi_b_be = convertEndianness64(pi_b_le);
252
+ const pi_c_x = BigInt(proof.pi_c[0]);
253
+ const pi_c_y = BigInt(proof.pi_c[1]);
254
+ const pi_c_x_le = bigintToBytes32LE(pi_c_x);
255
+ const pi_c_y_le = bigintToBytes32LE(pi_c_y);
256
+ const pi_c_le = new Uint8Array(64);
257
+ pi_c_le.set(pi_c_x_le, 0);
258
+ pi_c_le.set(pi_c_y_le, 32);
259
+ const pi_c_be = convertEndianness32(pi_c_le);
260
+ const result = new Uint8Array(256);
261
+ result.set(pi_a_be, 0);
262
+ result.set(pi_b_be, 64);
263
+ result.set(pi_c_be, 192);
264
+ return result;
265
+ }
266
+ function bigintToBytes32LE(n) {
267
+ const bytes = new Uint8Array(32);
268
+ let value = n % BN254_MODULUS;
269
+ if (value < 0) {
270
+ value = (value + BN254_MODULUS) % BN254_MODULUS;
271
+ }
272
+ for (let i = 0; i < 32; i++) {
273
+ bytes[i] = Number(value & BigInt(255));
274
+ value = value >> BigInt(8);
275
+ }
276
+ return bytes;
277
+ }
278
+ function convertEndianness32(bytes) {
279
+ if (bytes.length !== 64) {
280
+ throw new Error("convertEndianness32 expects 64 bytes");
281
+ }
282
+ const result = new Uint8Array(64);
283
+ for (let i = 0; i < 32; i++) {
284
+ result[i] = bytes[31 - i];
285
+ }
286
+ for (let i = 0; i < 32; i++) {
287
+ result[32 + i] = bytes[63 - i];
288
+ }
289
+ return result;
290
+ }
291
+ function convertEndianness64(bytes) {
292
+ if (bytes.length !== 128) {
293
+ throw new Error("convertEndianness64 expects 128 bytes");
294
+ }
295
+ const result = new Uint8Array(128);
296
+ for (let i = 0; i < 64; i++) {
297
+ result[i] = bytes[63 - i];
298
+ }
299
+ for (let i = 0; i < 64; i++) {
300
+ result[64 + i] = bytes[127 - i];
301
+ }
302
+ return result;
303
+ }
304
+ function buildPublicInputsBytes(root, nullifier, outputsHash, publicAmount) {
305
+ const result = new Uint8Array(104);
306
+ result.set(bigintToBytes32(root), 0);
307
+ result.set(bigintToBytes32(nullifier), 32);
308
+ result.set(bigintToBytes32(outputsHash), 64);
309
+ const amountBytes = new Uint8Array(8);
310
+ let amt = publicAmount;
311
+ for (let i = 7; i >= 0; i--) {
312
+ amountBytes[i] = Number(amt & 0xffn);
313
+ amt = amt >> 8n;
314
+ }
315
+ result.set(amountBytes, 96);
316
+ return result;
317
+ }
318
+
319
+ // src/core/utxo.ts
320
+ var FIELD_MODULUS = BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
321
+ var KEYPAIR_DOMAIN_TAG = BigInt("5423839527465210463");
322
+ var UTXO_KEY_DOMAIN = new TextEncoder().encode("cloak_utxo_priv_v1");
323
+ var NATIVE_SOL_MINT = new PublicKey("So11111111111111111111111111111111111111112");
324
+ var poseidonInstance = null;
325
+ async function getPoseidon2() {
326
+ if (!poseidonInstance) {
327
+ poseidonInstance = await buildPoseidon2();
328
+ }
329
+ return poseidonInstance;
330
+ }
331
+ function randomFieldElement() {
332
+ const bytes = randomBytes(32);
333
+ let value = BigInt(0);
334
+ for (let i = 0; i < 32; i++) {
335
+ value = value << BigInt(8) | BigInt(bytes[i]);
336
+ }
337
+ return value % (FIELD_MODULUS >> BigInt(4));
338
+ }
339
+ async function generateUtxoKeypair() {
340
+ const poseidon2 = await getPoseidon2();
341
+ const privateKey = randomFieldElement();
342
+ const publicKeyHash = poseidon2([privateKey], KEYPAIR_DOMAIN_TAG);
343
+ const publicKey = poseidon2.F.toObject(publicKeyHash);
344
+ return { privateKey, publicKey };
345
+ }
346
+ async function deriveUtxoKeypairFromSpendKey(skSpend) {
347
+ if (!skSpend || !(skSpend instanceof Uint8Array) || skSpend.length !== 32) {
348
+ throw new Error("skSpend must be 32 bytes");
349
+ }
350
+ const preimage = new Uint8Array(UTXO_KEY_DOMAIN.length + skSpend.length);
351
+ preimage.set(UTXO_KEY_DOMAIN, 0);
352
+ preimage.set(skSpend, UTXO_KEY_DOMAIN.length);
353
+ const hash = blake3(preimage);
354
+ let value = BigInt(0);
355
+ for (let i = 0; i < 32; i++) {
356
+ value = value << BigInt(8) | BigInt(hash[i]);
357
+ }
358
+ const privateKey = value % (FIELD_MODULUS >> BigInt(4));
359
+ const publicKey = await derivePublicKey(privateKey);
360
+ return { privateKey, publicKey };
361
+ }
362
+ async function derivePublicKey(privateKey) {
363
+ const poseidon2 = await getPoseidon2();
364
+ const hash = poseidon2([privateKey], KEYPAIR_DOMAIN_TAG);
365
+ return poseidon2.F.toObject(hash);
366
+ }
367
+ async function createUtxo(amount, keypair, mintAddress = NATIVE_SOL_MINT) {
368
+ const blinding = randomFieldElement();
369
+ const utxo = {
370
+ amount,
371
+ keypair,
372
+ blinding,
373
+ mintAddress
374
+ };
375
+ utxo.commitment = await computeCommitment2(utxo);
376
+ return utxo;
377
+ }
378
+ async function createZeroUtxo(mintAddress = NATIVE_SOL_MINT, salt) {
379
+ const poseidon2 = await getPoseidon2();
380
+ const effectiveSalt = salt ?? randomFieldElement();
381
+ const publicKeyHash = poseidon2([effectiveSalt], KEYPAIR_DOMAIN_TAG);
382
+ const publicKey = poseidon2.F.toObject(publicKeyHash);
383
+ const keypair = { privateKey: effectiveSalt, publicKey };
384
+ const utxo = {
385
+ amount: BigInt(0),
386
+ keypair,
387
+ blinding: BigInt(0),
388
+ mintAddress
389
+ };
390
+ utxo.commitment = await computeCommitment2(utxo);
391
+ utxo.index = 0;
392
+ return utxo;
393
+ }
394
+ function pubkeyToFieldElement(pubkey) {
395
+ const bytes = pubkey.toBytes();
396
+ let value = BigInt(0);
397
+ for (let i = 0; i < 32; i++) {
398
+ value = value << BigInt(8) | BigInt(bytes[i]);
399
+ }
400
+ return value % FIELD_MODULUS;
401
+ }
402
+ function pubkeyToFieldLimbs(pubkey) {
403
+ const bytes = pubkey.toBytes();
404
+ let hi = BigInt(0);
405
+ let lo = BigInt(0);
406
+ for (let i = 0; i < 16; i++) {
407
+ hi = hi << BigInt(8) | BigInt(bytes[i]);
408
+ }
409
+ for (let i = 16; i < 32; i++) {
410
+ lo = lo << BigInt(8) | BigInt(bytes[i]);
411
+ }
412
+ return [hi, lo];
413
+ }
414
+ async function computeCommitment2(utxo) {
415
+ const poseidon2 = await getPoseidon2();
416
+ const mintField = pubkeyToFieldElement(utxo.mintAddress);
417
+ const hash = poseidon2([
418
+ utxo.amount,
419
+ utxo.keypair.publicKey,
420
+ utxo.blinding,
421
+ mintField
422
+ ]);
423
+ return poseidon2.F.toObject(hash);
424
+ }
425
+ async function computeSignature(privateKey, commitment, pathIndex) {
426
+ const poseidon2 = await getPoseidon2();
427
+ const hash = poseidon2([privateKey, commitment, pathIndex]);
428
+ return poseidon2.F.toObject(hash);
429
+ }
430
+ async function computeNullifier2(utxo) {
431
+ if (utxo.index === void 0) {
432
+ throw new Error("UTXO must have an index to compute nullifier");
433
+ }
434
+ const poseidon2 = await getPoseidon2();
435
+ const commitment = utxo.commitment ?? await computeCommitment2(utxo);
436
+ const pathIndex = BigInt(utxo.index);
437
+ const signature = await computeSignature(
438
+ utxo.keypair.privateKey,
439
+ commitment,
440
+ pathIndex
441
+ );
442
+ const hash = poseidon2([commitment, pathIndex, signature]);
443
+ return poseidon2.F.toObject(hash);
444
+ }
445
+ function serializeUtxo(utxo) {
446
+ const buffer = new ArrayBuffer(128);
447
+ const view = new DataView(buffer);
448
+ const amountBytes = bigintToBytes(utxo.amount, 8);
449
+ for (let i = 0; i < 8; i++) {
450
+ view.setUint8(i, amountBytes[i]);
451
+ }
452
+ const privkeyBytes = bigintToBytes(utxo.keypair.privateKey, 32);
453
+ for (let i = 0; i < 32; i++) {
454
+ view.setUint8(8 + i, privkeyBytes[i]);
455
+ }
456
+ const blindingBytes = bigintToBytes(utxo.blinding, 32);
457
+ for (let i = 0; i < 32; i++) {
458
+ view.setUint8(40 + i, blindingBytes[i]);
459
+ }
460
+ const mintBytes = utxo.mintAddress.toBytes();
461
+ for (let i = 0; i < 32; i++) {
462
+ view.setUint8(72 + i, mintBytes[i]);
463
+ }
464
+ view.setUint32(104, utxo.index ?? 0, true);
465
+ return new Uint8Array(buffer);
466
+ }
467
+ async function deserializeUtxo(bytes) {
468
+ const view = new DataView(bytes.buffer, bytes.byteOffset);
469
+ const amountBytes = bytes.slice(0, 8);
470
+ const amount = bytesToBigint(amountBytes);
471
+ const privkeyBytes = bytes.slice(8, 40);
472
+ const privateKey = bytesToBigint(privkeyBytes);
473
+ const publicKey = await derivePublicKey(privateKey);
474
+ const blindingBytes = bytes.slice(40, 72);
475
+ const blinding = bytesToBigint(blindingBytes);
476
+ const mintBytes = bytes.slice(72, 104);
477
+ const mintAddress = new PublicKey(mintBytes);
478
+ const index = view.getUint32(104, true);
479
+ const utxo = {
480
+ amount,
481
+ keypair: { privateKey, publicKey },
482
+ blinding,
483
+ mintAddress,
484
+ index: index > 0 ? index : void 0
485
+ };
486
+ utxo.commitment = await computeCommitment2(utxo);
487
+ return utxo;
488
+ }
489
+ function bigintToBytes(value, length) {
490
+ const result = new Uint8Array(length);
491
+ let remaining = value;
492
+ for (let i = 0; i < length; i++) {
493
+ result[i] = Number(remaining & BigInt(255));
494
+ remaining >>= BigInt(8);
495
+ }
496
+ return result;
497
+ }
498
+ function bytesToBigint(bytes) {
499
+ let result = BigInt(0);
500
+ for (let i = bytes.length - 1; i >= 0; i--) {
501
+ result = result << BigInt(8) | BigInt(bytes[i]);
502
+ }
503
+ return result;
504
+ }
505
+ function bigintToHex(value) {
506
+ return value.toString(16).padStart(64, "0");
507
+ }
508
+ function hexToBigint2(hex) {
509
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
510
+ return BigInt("0x" + cleanHex);
511
+ }
512
+ function bigintToBytes322(value) {
513
+ const result = new Uint8Array(32);
514
+ let remaining = value;
515
+ for (let i = 31; i >= 0; i--) {
516
+ result[i] = Number(remaining & BigInt(255));
517
+ remaining >>= BigInt(8);
518
+ }
519
+ return result;
520
+ }
521
+ async function utxoEquals(a, b) {
522
+ const commitmentA = a.commitment ?? await computeCommitment2(a);
523
+ const commitmentB = b.commitment ?? await computeCommitment2(b);
524
+ return commitmentA === commitmentB;
525
+ }
526
+ function sumUtxoAmounts(utxos) {
527
+ return utxos.reduce((sum, utxo) => sum + utxo.amount, BigInt(0));
528
+ }
529
+ function selectUtxos(available, targetAmount) {
530
+ const sorted = [...available].sort(
531
+ (a, b) => Number(b.amount - a.amount)
532
+ );
533
+ const selected = [];
534
+ let total = BigInt(0);
535
+ for (const utxo of sorted) {
536
+ if (total >= targetAmount) break;
537
+ selected.push(utxo);
538
+ total += utxo.amount;
539
+ }
540
+ if (total < targetAmount) {
541
+ return null;
542
+ }
543
+ return selected;
544
+ }
545
+
546
+ export {
547
+ CloakError,
548
+ poseidonHash,
549
+ splitTo2Limbs,
550
+ pubkeyToLimbs,
551
+ computeMerkleRoot,
552
+ hexToBigint,
553
+ computeCommitment,
554
+ generateCommitmentAsync,
555
+ generateCommitment,
556
+ computeNullifier,
557
+ computeNullifierAsync,
558
+ computeNullifierSync,
559
+ computeOutputsHashAsync,
560
+ computeOutputsHash,
561
+ computeOutputsHashSync,
562
+ computeSwapOutputsHash,
563
+ computeSwapOutputsHashAsync,
564
+ computeSwapOutputsHashSync,
565
+ bigintToBytes32,
566
+ hexToBytes,
567
+ bytesToHex,
568
+ InsecureRandomnessError,
569
+ randomBytes,
570
+ isValidHex,
571
+ BN254_MODULUS,
572
+ proofToBytes,
573
+ buildPublicInputsBytes,
574
+ NATIVE_SOL_MINT,
575
+ randomFieldElement,
576
+ generateUtxoKeypair,
577
+ deriveUtxoKeypairFromSpendKey,
578
+ derivePublicKey,
579
+ createUtxo,
580
+ createZeroUtxo,
581
+ pubkeyToFieldElement,
582
+ pubkeyToFieldLimbs,
583
+ computeCommitment2,
584
+ computeSignature,
585
+ computeNullifier2,
586
+ serializeUtxo,
587
+ deserializeUtxo,
588
+ bigintToHex,
589
+ hexToBigint2,
590
+ bigintToBytes322,
591
+ utxoEquals,
592
+ sumUtxoAmounts,
593
+ selectUtxos
594
+ };