@cloak.dev/sdk 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,375 +1,4 @@
1
- import { PublicKey, Transaction, Connection, TransactionInstruction, Keypair, SendOptions, AddressLookupTableAccount, VersionedTransaction } from '@solana/web3.js';
2
-
3
- type ComplianceTxType = "deposit" | "withdraw" | "send" | "swap";
4
- interface TransactionMetadata {
5
- amount: number;
6
- recipient: string;
7
- timestamp: number;
8
- txType: ComplianceTxType;
9
- commitment: string;
10
- signature?: string;
11
- outputMint?: string;
12
- }
13
- interface EncryptedMetadataBundle {
14
- encrypted_user: string;
15
- encrypted_compliance: string;
16
- user_pubkey: string;
17
- commitment: string;
18
- timestamp: number;
19
- tx_type?: ComplianceTxType;
20
- wallet_signature?: string;
21
- viewing_key?: string;
22
- }
23
-
24
- /**
25
- * Supported Solana networks
26
- */
27
- type Network = "localnet" | "devnet" | "mainnet" | "testnet";
28
- /**
29
- * Minimal wallet adapter interface
30
- * Compatible with @solana/wallet-adapter-base
31
- */
32
- interface WalletAdapter {
33
- publicKey: PublicKey | null;
34
- signTransaction?<T extends Transaction>(transaction: T): Promise<T>;
35
- signAllTransactions?<T extends Transaction>(transactions: T[]): Promise<T[]>;
36
- sendTransaction?(transaction: Transaction, connection: any, options?: any): Promise<string>;
37
- }
38
- /**
39
- * Cloak-specific error with categorization
40
- */
41
- declare class CloakError extends Error {
42
- category: "network" | "indexer" | "prover" | "relay" | "validation" | "wallet" | "environment" | "service";
43
- retryable: boolean;
44
- originalError?: Error | undefined;
45
- constructor(message: string, category: "network" | "indexer" | "prover" | "relay" | "validation" | "wallet" | "environment" | "service", retryable?: boolean, originalError?: Error | undefined);
46
- }
47
- /**
48
- * Cloak Note - Represents a private transaction commitment
49
- *
50
- * A note contains all the information needed to withdraw funds from the Cloak protocol.
51
- * Keep this safe and secret - anyone with access to this note can withdraw the funds!
52
- */
53
- interface CloakNote {
54
- /** Protocol version */
55
- version: string;
56
- /** Amount in lamports */
57
- amount: number;
58
- /** Commitment hash (hex) */
59
- commitment: string;
60
- /** Spending secret key (hex, 64 chars) */
61
- sk_spend: string;
62
- /** Randomness value (hex, 64 chars) */
63
- r: string;
64
- /** Transaction signature from deposit (optional until deposited) */
65
- depositSignature?: string;
66
- /** Solana slot when deposited (optional until deposited) */
67
- depositSlot?: number;
68
- /** Index in the Merkle tree (optional until deposited) */
69
- leafIndex?: number;
70
- /** Historical Merkle root at time of deposit (optional until deposited) */
71
- root?: string;
72
- /** Merkle proof at time of deposit (optional until deposited) */
73
- merkleProof?: MerkleProof;
74
- /** Creation timestamp */
75
- timestamp: number;
76
- /** Network where this note was created */
77
- network: Network;
78
- }
79
- /**
80
- * Merkle proof for a leaf in the commitment tree
81
- */
82
- interface MerkleProof {
83
- /** Sibling hashes along the path (hex strings) */
84
- pathElements: string[];
85
- /** Path directions (0 = left, 1 = right) */
86
- pathIndices: number[];
87
- /** Optional root for backward compatibility */
88
- root?: string;
89
- }
90
- /**
91
- * Transfer recipient - used in privateTransfer
92
- */
93
- interface Transfer {
94
- /** Recipient's Solana public key */
95
- recipient: PublicKey;
96
- /** Amount to send in lamports */
97
- amount: number;
98
- }
99
- /**
100
- * Type-safe array with maximum length constraint
101
- * Used to enforce 1-5 recipients in privateTransfer
102
- */
103
- type MaxLengthArray<T, Max extends number, A extends T[] = []> = A['length'] extends Max ? A : A | MaxLengthArray<T, Max, [T, ...A]>;
104
- /**
105
- * Result from a private transfer
106
- */
107
- interface TransferResult {
108
- /** Solana transaction signature */
109
- signature: string;
110
- /** Recipients and amounts that were sent */
111
- outputs: Array<{
112
- recipient: string;
113
- amount: number;
114
- }>;
115
- /** Nullifier used (prevents double-spending) */
116
- nullifier: string;
117
- /** Merkle root that was proven against */
118
- root: string;
119
- }
120
- /**
121
- * Result from a deposit operation
122
- */
123
- interface DepositResult {
124
- /** The created note (save this securely!) */
125
- note: CloakNote;
126
- /** Solana transaction signature */
127
- signature: string;
128
- /** Leaf index in the Merkle tree */
129
- leafIndex: number;
130
- /** Current Merkle root */
131
- root: string;
132
- }
133
- /**
134
- * Configuration for the Cloak SDK
135
- */
136
- interface CloakConfig {
137
- /** Solana network */
138
- network?: Network;
139
- /**
140
- * Keypair bytes for signing (deprecated - use wallet instead)
141
- * @deprecated Use wallet parameter for better integration
142
- */
143
- keypairBytes?: Uint8Array;
144
- /**
145
- * Wallet adapter for signing transactions
146
- * Required unless using keypairBytes
147
- */
148
- wallet?: WalletAdapter;
149
- /**
150
- * Cloak key pair for v2.0 features (note scanning, encryption)
151
- * Optional but recommended for full functionality
152
- */
153
- cloakKeys?: any;
154
- /** Optional: Proof generation timeout in milliseconds (default: 5 minutes) */
155
- proofTimeout?: number;
156
- /** Optional: Program ID (defaults to Cloak mainnet program) */
157
- programId?: PublicKey;
158
- /** Optional: Pool account address (auto-derived from program ID if not provided) */
159
- poolAddress?: PublicKey;
160
- /** Optional: Merkle tree account address (auto-derived if not provided) */
161
- merkleTreeAddress?: PublicKey;
162
- /** Optional: Treasury account address (auto-derived if not provided) */
163
- treasuryAddress?: PublicKey;
164
- /**
165
- * Enable debug logging with structured output similar to Rust tracing.
166
- *
167
- * When enabled, logs SDK operations with timestamps, module paths,
168
- * and key-value pairs for context:
169
- *
170
- * ```
171
- * 2026-01-23T00:51:46.489000Z INFO cloak::sdk: 📥 Deposit completed signature=42XB... leaf_index=757
172
- * ```
173
- *
174
- * Can also be enabled via environment variable:
175
- * - `CLOAK_DEBUG=1`
176
- * - `DEBUG=cloak:*`
177
- *
178
- * Default: false
179
- */
180
- debug?: boolean;
181
- }
182
- /**
183
- * Deposit progress status
184
- */
185
- type DepositStatus = "generating_note" | "awaiting_note_acknowledgment" | "note_saved" | "creating_transaction" | "simulating" | "sending" | "confirming" | "submitting_to_indexer" | "fetching_proof" | "complete";
186
- /**
187
- * Options for deposit operation
188
- */
189
- interface DepositOptions {
190
- /** Optional callback for progress updates with detailed status */
191
- onProgress?: (status: DepositStatus | string, details?: {
192
- message?: string;
193
- step?: number;
194
- totalSteps?: number;
195
- retryAttempt?: number;
196
- }) => void;
197
- /** Callback when transaction is sent (before confirmation) */
198
- onTransactionSent?: (signature: string) => void;
199
- /** Callback when transaction is confirmed */
200
- onConfirmed?: (signature: string, slot: number) => void;
201
- /**
202
- * CRITICAL: Callback when note is generated, BEFORE any on-chain transaction.
203
- *
204
- * This is your chance to safely persist the note. If you don't save the note
205
- * and the deposit succeeds but the browser crashes, your funds could be lost!
206
- *
207
- * The callback receives the note with all secrets needed for withdrawal.
208
- * The deposit will NOT proceed until this callback completes.
209
- *
210
- * @example
211
- * ```typescript
212
- * onNoteGenerated: async (note) => {
213
- * // Save to secure storage BEFORE deposit proceeds
214
- * await localStorage.setItem(`pending_note_${note.commitment}`, JSON.stringify(note));
215
- * // Or show user a modal to copy/download the note
216
- * }
217
- * ```
218
- */
219
- onNoteGenerated?: (note: CloakNote) => Promise<void> | void;
220
- /**
221
- * If true, require user to acknowledge the note before proceeding with deposit.
222
- * When enabled with onNoteGenerated, the deposit will wait for the callback to complete.
223
- * Default: true when onNoteGenerated is provided
224
- */
225
- requireNoteAcknowledgment?: boolean;
226
- /** Skip simulation (default: false) */
227
- skipPreflight?: boolean;
228
- /**
229
- * Compute units to request.
230
- * - If `optimizeCU` is true, this is ignored and CU is determined via simulation
231
- * - Otherwise defaults to 40,000 (suitable for typical deposits)
232
- */
233
- computeUnits?: number;
234
- /**
235
- * Enable simulation-based CU optimization.
236
- * When true, simulates the transaction first to determine actual CU usage,
237
- * then sets an optimal limit (simulated + 20% buffer).
238
- *
239
- * **Note: Only works in keypair mode (Node.js/scripts).**
240
- * In wallet/browser mode, this is ignored because simulation would require
241
- * the user to sign twice (simulation + actual tx) which is bad UX.
242
- *
243
- * Trade-offs:
244
- * - ✅ Optimal block scheduling priority
245
- * - ❌ Adds ~200-500ms latency (extra RPC call)
246
- * - ❌ Only available in keypair mode
247
- *
248
- * Default: false (uses fixed 40K CU which is ~75% efficient for typical deposits)
249
- */
250
- optimizeCU?: boolean;
251
- /** Priority fee in micro-lamports (default: 10,000) */
252
- priorityFee?: number;
253
- /**
254
- * Loaded accounts data size limit in bytes.
255
- *
256
- * Without this, Solana defaults to 64MB which incurs CU overhead
257
- * (charged at 8 CU per 32KB). Setting a lower limit improves priority.
258
- *
259
- * **Note for Cloak deposits:**
260
- * The Shield Pool program is ~104KB, so the minimum practical limit is ~128KB.
261
- *
262
- * Default: 256 * 1024 (256KB) - provides safety margin above program size.
263
- * Set to 0 to disable (use Solana 64MB default).
264
- *
265
- * @see https://www.anza.xyz/blog/cu-optimization-with-setloadedaccountsdatasizelimit
266
- */
267
- loadedAccountsDataSizeLimit?: number;
268
- /**
269
- * Optional: Encrypt output for specific recipient's view key
270
- * If not provided, encrypts for the wallet's own view key (for self-scanning)
271
- */
272
- recipientViewKey?: string;
273
- /**
274
- * Skip privacy warning on testnet (default: false)
275
- * Warning: Only skip if you understand the privacy limitations
276
- */
277
- skipPrivacyWarning?: boolean;
278
- }
279
- /**
280
- * Options for private transfer/withdraw operation
281
- */
282
- interface TransferOptions {
283
- /**
284
- * Optional callback for progress updates
285
- * Note: relayFeeBps is automatically calculated from protocol fees
286
- */
287
- onProgress?: (status: string) => void;
288
- /** Optional callback for proof generation progress (0-100) */
289
- onProofProgress?: (percent: number) => void;
290
- /**
291
- * Metadata bundle for compliance tracking.
292
- * On first transaction, should include viewing_key for compliance registration.
293
- */
294
- metadataBundle?: {
295
- encrypted_user: string;
296
- encrypted_compliance: string;
297
- user_pubkey: string;
298
- commitment: string;
299
- timestamp: number;
300
- tx_type?: ComplianceTxType;
301
- viewing_key?: string;
302
- };
303
- }
304
- /**
305
- * Options for withdrawal (convenience method with single recipient)
306
- */
307
- interface WithdrawOptions extends TransferOptions {
308
- /** Whether to withdraw full amount minus fees (default: true) */
309
- withdrawAll?: boolean;
310
- /** Specific amount to withdraw in lamports (if not withdrawing all) */
311
- amount?: number;
312
- }
313
- /**
314
- * Merkle root response from indexer
315
- */
316
- interface MerkleRootResponse {
317
- root: string;
318
- next_index: number;
319
- }
320
- /**
321
- * Transaction status from relay service
322
- */
323
- interface TxStatus {
324
- status: "pending" | "processing" | "completed" | "failed";
325
- txId?: string;
326
- error?: string;
327
- }
328
- /**
329
- * Swap parameters for token swaps
330
- */
331
- interface SwapParams {
332
- /** Output token mint address */
333
- output_mint: string;
334
- /** Slippage tolerance in basis points (e.g., 100 = 1%) */
335
- slippage_bps: number;
336
- /** Minimum output amount in token's smallest unit */
337
- min_output_amount: number;
338
- }
339
- /**
340
- * Options for swap operation
341
- */
342
- interface SwapOptions extends TransferOptions {
343
- /** Output token mint address */
344
- outputMint: string;
345
- /** Slippage tolerance in basis points (default: 100 = 1%) */
346
- slippageBps?: number;
347
- /** Minimum output amount (will be calculated from quote if not provided) */
348
- minOutputAmount?: number;
349
- /** Optional callback to get swap quote */
350
- getQuote?: (amountLamports: number, outputMint: string, slippageBps: number) => Promise<{
351
- outAmount: number;
352
- minOutputAmount: number;
353
- }>;
354
- /**
355
- * Recipient's associated token account for the output token.
356
- * If not provided, will be computed automatically (requires @solana/spl-token).
357
- * For browser environments, it's recommended to compute this in the frontend
358
- * where @solana/spl-token is properly bundled.
359
- */
360
- recipientAta?: string;
361
- }
362
- /**
363
- * Result from a swap operation
364
- */
365
- interface SwapResult extends TransferResult {
366
- /** Output token mint address */
367
- outputMint: string;
368
- /** Minimum output amount that was guaranteed */
369
- minOutputAmount: number;
370
- /** Actual output amount received (may be higher than min) */
371
- actualOutputAmount?: number;
372
- }
1
+ import { PublicKey, Transaction, Connection, AddressLookupTableAccount, TransactionInstruction, Keypair, SendOptions, VersionedTransaction } from '@solana/web3.js';
373
2
 
374
3
  /**
375
4
  * Cloak Key Hierarchy (v2.0)
@@ -455,681 +84,350 @@ declare function exportKeys(keys: CloakKeyPair): string;
455
84
  declare function importKeys(exported: string): CloakKeyPair;
456
85
 
457
86
  /**
458
- * Storage Interface
459
- *
460
- * Defines a pluggable storage interface for notes and keys.
461
- * Applications can implement their own storage (localStorage, IndexedDB, file system, etc.)
87
+ * Supported Solana networks.
462
88
  */
463
-
89
+ type Network = "localnet" | "devnet" | "mainnet" | "testnet";
464
90
  /**
465
- * Storage adapter interface
466
- *
467
- * Implement this interface to provide custom storage for notes and keys.
468
- * The SDK will use this adapter for all persistence operations.
91
+ * Minimal wallet adapter interface — compatible with `@solana/wallet-adapter-base`.
469
92
  */
470
- interface StorageAdapter {
471
- /**
472
- * Save a note
473
- */
474
- saveNote(note: CloakNote): Promise<void> | void;
475
- /**
476
- * Load all notes
477
- */
478
- loadAllNotes(): Promise<CloakNote[]> | CloakNote[];
479
- /**
480
- * Update a note
481
- */
482
- updateNote(commitment: string, updates: Partial<CloakNote>): Promise<void> | void;
483
- /**
484
- * Delete a note
485
- */
486
- deleteNote(commitment: string): Promise<void> | void;
487
- /**
488
- * Clear all notes
489
- */
490
- clearAllNotes(): Promise<void> | void;
491
- /**
492
- * Save wallet keys
493
- */
494
- saveKeys(keys: CloakKeyPair): Promise<void> | void;
495
- /**
496
- * Load wallet keys
497
- */
498
- loadKeys(): Promise<CloakKeyPair | null> | CloakKeyPair | null;
499
- /**
500
- * Delete wallet keys
501
- */
502
- deleteKeys(): Promise<void> | void;
93
+ interface WalletAdapter {
94
+ publicKey: PublicKey | null;
95
+ signTransaction?<T extends Transaction>(transaction: T): Promise<T>;
96
+ signAllTransactions?<T extends Transaction>(transactions: T[]): Promise<T[]>;
97
+ sendTransaction?(transaction: Transaction, connection: any, options?: any): Promise<string>;
503
98
  }
504
99
  /**
505
- * In-memory storage adapter (default, no persistence)
506
- *
507
- * Useful for testing or when storage is handled externally
100
+ * Cloak-specific error with categorization.
508
101
  */
509
- declare class MemoryStorageAdapter implements StorageAdapter {
510
- private notes;
511
- private keys;
512
- saveNote(note: CloakNote): void;
513
- loadAllNotes(): CloakNote[];
514
- updateNote(commitment: string, updates: Partial<CloakNote>): void;
515
- deleteNote(commitment: string): void;
516
- clearAllNotes(): void;
517
- saveKeys(keys: CloakKeyPair): void;
518
- loadKeys(): CloakKeyPair | null;
519
- deleteKeys(): void;
102
+ declare class CloakError extends Error {
103
+ category: "network" | "indexer" | "prover" | "relay" | "validation" | "wallet" | "environment" | "service";
104
+ retryable: boolean;
105
+ originalError?: Error | undefined;
106
+ constructor(message: string, category: "network" | "indexer" | "prover" | "relay" | "validation" | "wallet" | "environment" | "service", retryable?: boolean, originalError?: Error | undefined);
520
107
  }
521
108
  /**
522
- * Browser localStorage adapter (optional, for browser environments)
523
- *
524
- * Only use this if you're in a browser environment and want localStorage persistence.
525
- * Import from a separate browser-specific module.
109
+ * Merkle proof for a leaf in the commitment tree.
526
110
  */
527
- declare class LocalStorageAdapter implements StorageAdapter {
528
- private notesKey;
529
- private keysKey;
530
- constructor(notesKey?: string, keysKey?: string);
531
- private getStorage;
532
- saveNote(note: CloakNote): void;
533
- loadAllNotes(): CloakNote[];
534
- updateNote(commitment: string, updates: Partial<CloakNote>): void;
535
- deleteNote(commitment: string): void;
536
- clearAllNotes(): void;
537
- saveKeys(keys: CloakKeyPair): void;
538
- loadKeys(): CloakKeyPair | null;
539
- deleteKeys(): void;
111
+ interface MerkleProof {
112
+ /** Sibling hashes along the path (hex strings) */
113
+ pathElements: string[];
114
+ /** Path directions (0 = left, 1 = right) */
115
+ pathIndices: number[];
116
+ /** Optional root for backward compatibility */
117
+ root?: string;
540
118
  }
541
-
542
- /** Default Cloak Program ID on Solana */
543
- declare const CLOAK_PROGRAM_ID: PublicKey;
544
119
  /**
545
- * Main Cloak SDK
120
+ * Snapshot of the active SDK configuration, returned by `CloakSDK.getConfig()`.
546
121
  *
547
- * Provides high-level API for interacting with the Cloak protocol,
548
- * including deposits, withdrawals, and private transfers.
122
+ * This type is intentionally narrower than the constructor input
123
+ * (`CloakSDKOptions` in `core/CloakSDK.ts`): it omits `keypairBytes`,
124
+ * `wallet`, and `storage` because those are consumed into private SDK
125
+ * state (`this.keypair`, `this.wallet`, `this.storage`) at construction
126
+ * time and are never persisted on the config object. The type system
127
+ * therefore enforces the same invariant exercised at runtime by
128
+ * `cloak-sdk.test.ts`.
549
129
  *
550
- * Supports two modes:
551
- * 1. Node.js mode with keypairBytes - for scripts and backend services
552
- * 2. Wallet adapter mode - for browser applications with wallet integration
130
+ * If you're configuring the SDK at construction, use `CloakSDKOptions`.
553
131
  */
554
- declare class CloakSDK {
555
- private config;
556
- private keypair?;
557
- private wallet?;
558
- private cloakKeys?;
559
- private relay;
560
- private storage;
561
- /**
562
- * Create a new Cloak SDK client
563
- *
564
- * @param config - Client configuration
565
- *
566
- * @example Node.js mode (with keypair)
567
- * ```typescript
568
- * const sdk = new CloakSDK({
569
- * keypairBytes: keypair.secretKey,
570
- * network: "devnet"
571
- * });
572
- * ```
573
- *
574
- * @example Browser mode (with wallet adapter)
575
- * ```typescript
576
- * const sdk = new CloakSDK({
577
- * wallet: walletAdapter,
578
- * network: "devnet"
579
- * });
580
- * ```
581
- */
582
- constructor(config: {
583
- /** Keypair bytes for signing (Node.js mode) */
584
- keypairBytes?: Uint8Array;
585
- /** Wallet adapter for signing (Browser mode) */
586
- wallet?: WalletAdapter;
587
- network?: Network;
588
- cloakKeys?: CloakKeyPair;
589
- storage?: StorageAdapter;
590
- programId?: PublicKey;
591
- relayUrl?: string;
592
- /** Enable debug logging with structured output */
593
- debug?: boolean;
594
- });
595
- /**
596
- * Get the public key for deposits (from keypair or wallet)
597
- */
598
- getPublicKey(): PublicKey;
599
- /**
600
- * Check if the SDK is using a wallet adapter
601
- */
602
- isWalletMode(): boolean;
603
- /**
604
- * Deposit SOL into the Cloak protocol
605
- *
606
- * Creates a new note (or uses a provided one), submits a deposit transaction,
607
- * and registers with the indexer.
608
- *
609
- * @param connection - Solana connection
610
- * @param payer - Payer wallet with sendTransaction method
611
- * @param amountOrNote - Amount in lamports OR an existing note to deposit
612
- * @param options - Optional configuration
613
- * @returns Deposit result with note and transaction info
614
- *
615
- * @example
616
- * ```typescript
617
- * // Generate and deposit in one step
618
- * const result = await client.deposit(
619
- * connection,
620
- * wallet,
621
- * 1_000_000_000,
622
- * {
623
- * onProgress: (status) => console.log(status)
624
- * }
625
- * );
626
- *
627
- * // Or deposit a pre-generated note
628
- * const note = client.generateNote(1_000_000_000);
629
- * const result = await client.deposit(connection, wallet, note);
630
- * ```
631
- */
632
- deposit(connection: Connection, amountOrNote: number | CloakNote, options?: DepositOptions): Promise<DepositResult>;
633
- /**
634
- * Private transfer with up to 5 recipients
635
- *
636
- * Handles the complete private transfer flow:
637
- * 1. If note is not deposited, deposits it first and waits for confirmation
638
- * 2. Generates a zero-knowledge proof
639
- * 3. Submits the withdrawal via relay service to recipients
640
- *
641
- * This is the main method for performing private transfers - it handles everything!
642
- *
643
- * @param connection - Solana connection (required for deposit if not already deposited)
644
- * @param payer - Payer wallet (required for deposit if not already deposited)
645
- * @param note - Note to spend (can be deposited or not)
646
- * @param recipients - Array of 1-5 recipients with amounts
647
- * @param options - Optional configuration
648
- * @returns Transfer result with signature and outputs
649
- *
650
- * @example
651
- * ```typescript
652
- * // Create a note (not deposited yet)
653
- * const note = client.generateNote(1_000_000_000);
654
- *
655
- * // privateTransfer handles the full flow: deposit + withdraw
656
- * const result = await client.privateTransfer(
657
- * connection,
658
- * wallet,
659
- * note,
660
- * [
661
- * { recipient: new PublicKey("..."), amount: 500_000_000 },
662
- * { recipient: new PublicKey("..."), amount: 492_500_000 }
663
- * ],
664
- * {
665
- * relayFeeBps: 50, // 0.5%
666
- * onProgress: (status) => console.log(status),
667
- * onProofProgress: (pct) => console.log(`Proof: ${pct}%`)
668
- * }
669
- * );
670
- * console.log(`Success! TX: ${result.signature}`);
671
- * ```
672
- */
673
- privateTransfer(connection: Connection, note: CloakNote, recipients: MaxLengthArray<Transfer, 5>, options?: TransferOptions): Promise<TransferResult>;
132
+ interface CloakConfig {
133
+ /** Network the SDK was constructed for. Defaults to `"mainnet"`. */
134
+ network?: Network;
135
+ /** Cloak key pair for note scanning / encryption (when provided). */
136
+ cloakKeys?: CloakKeyPair;
137
+ /** Resolved program ID (defaults to the mainnet shield-pool). */
138
+ programId?: PublicKey;
139
+ /** Pool PDA derived from `programId` + native SOL mint. */
140
+ poolAddress?: PublicKey;
141
+ /** Merkle-tree PDA. */
142
+ merkleTreeAddress?: PublicKey;
143
+ /** Treasury PDA. */
144
+ treasuryAddress?: PublicKey;
674
145
  /**
675
- * Withdraw to a single recipient
146
+ * Whether structured debug logging was enabled at construction.
676
147
  *
677
- * Convenience method for withdrawing to one address.
678
- * Handles the complete flow: deposits if needed, then withdraws.
679
- *
680
- * @param connection - Solana connection
681
- * @param payer - Payer wallet
682
- * @param note - Note to spend
683
- * @param recipient - Recipient address
684
- * @param options - Optional configuration
685
- * @returns Transfer result
686
- *
687
- * @example
688
- * ```typescript
689
- * const note = client.generateNote(1_000_000_000);
690
- * const result = await client.withdraw(
691
- * connection,
692
- * wallet,
693
- * note,
694
- * new PublicKey("..."),
695
- * { withdrawAll: true }
696
- * );
697
- * ```
148
+ * Can also be enabled via env: `CLOAK_DEBUG=1` or `DEBUG=cloak:*`.
698
149
  */
699
- withdraw(connection: Connection, note: CloakNote, recipient: PublicKey, options?: WithdrawOptions): Promise<TransferResult>;
150
+ debug?: boolean;
151
+ }
152
+ /**
153
+ * Merkle root response from the relay/indexer.
154
+ */
155
+ interface MerkleRootResponse {
156
+ root: string;
157
+ next_index: number;
158
+ }
159
+ /**
160
+ * Transaction status returned by the relay.
161
+ */
162
+ interface TxStatus {
163
+ status: "pending" | "processing" | "completed" | "failed";
164
+ txId?: string;
165
+ error?: string;
166
+ }
167
+
168
+ /**
169
+ * Storage Interface
170
+ *
171
+ * Pluggable storage for Cloak wallet keys (master seed / spend / view keys).
172
+ *
173
+ * As of `0.1.6`, note persistence is the consumer's responsibility: the OLD
174
+ * `CloakNote` model was retired (see `CloakSDK` jsdoc), and UTXO persistence
175
+ * in the new flow is owned by callers.
176
+ */
177
+
178
+ interface StorageAdapter {
179
+ /** Save wallet keys. */
180
+ saveKeys(keys: CloakKeyPair): Promise<void> | void;
181
+ /** Load wallet keys (`null` when none stored). */
182
+ loadKeys(): Promise<CloakKeyPair | null> | CloakKeyPair | null;
183
+ /** Delete wallet keys. */
184
+ deleteKeys(): Promise<void> | void;
185
+ }
186
+ /**
187
+ * In-memory storage adapter (default; no persistence). Useful for tests or
188
+ * when storage is handled externally.
189
+ */
190
+ declare class MemoryStorageAdapter implements StorageAdapter {
191
+ private keys;
192
+ saveKeys(keys: CloakKeyPair): void;
193
+ loadKeys(): CloakKeyPair | null;
194
+ deleteKeys(): void;
195
+ }
196
+ /**
197
+ * Browser `localStorage` adapter — only safe in browser environments.
198
+ *
199
+ * **Auto-purge on construction:** the first time any `LocalStorageAdapter`
200
+ * is constructed in a given browser, it removes the legacy `cloak_notes`
201
+ * localStorage entry (pre-0.1.6 plaintext `r` + `sk_spend` blob) and writes
202
+ * a sentinel under {@link LEGACY_PURGE_SENTINEL_KEY} so subsequent
203
+ * constructions skip the work. This closes the upgrade-time security gap
204
+ * without requiring every consumer to explicitly call
205
+ * {@link LocalStorageAdapter.purgeLegacyNoteStorage}. Consumers using a
206
+ * non-default legacy key can still call the static helper with their own
207
+ * key name.
208
+ */
209
+ declare class LocalStorageAdapter implements StorageAdapter {
210
+ private keysKey;
211
+ constructor(keysKey?: string);
212
+ private getStorage;
213
+ saveKeys(keys: CloakKeyPair): void;
214
+ loadKeys(): CloakKeyPair | null;
215
+ deleteKeys(): void;
700
216
  /**
701
- * Send SOL privately to multiple recipients
702
- *
703
- * Convenience method that wraps privateTransfer with a simpler API.
704
- * Handles the complete flow: deposits if needed, then sends to recipients.
705
- *
706
- * @param connection - Solana connection
707
- * @param note - Note to spend
708
- * @param recipients - Array of 1-5 recipients with amounts
709
- * @param options - Optional configuration
710
- * @returns Transfer result
711
- *
712
- * @example
713
- * ```typescript
714
- * const note = client.generateNote(1_000_000_000);
715
- * const result = await client.send(
716
- * connection,
717
- * note,
718
- * [
719
- * { recipient: new PublicKey("..."), amount: 500_000_000 },
720
- * { recipient: new PublicKey("..."), amount: 492_500_000 }
721
- * ]
722
- * );
723
- * ```
217
+ * Sentinel key set after the auto-purge runs. Persists across browser
218
+ * sessions so the purge never re-runs on the same origin.
724
219
  */
725
- send(connection: Connection, note: CloakNote, recipients: MaxLengthArray<Transfer, 5>, options?: TransferOptions): Promise<TransferResult>;
220
+ static readonly LEGACY_PURGE_SENTINEL_KEY = "cloak_purged_v0_1_5";
726
221
  /**
727
- * Swap SOL for tokens privately
728
- *
729
- * Withdraws SOL from a note and swaps it for tokens via the relay service.
730
- * Handles the complete flow: deposits if needed, generates proof, and submits swap.
731
- *
732
- * @param connection - Solana connection
733
- * @param note - Note to spend
734
- * @param recipient - Recipient address (will receive tokens)
735
- * @param options - Swap configuration
736
- * @returns Swap result with transaction signature
737
- *
738
- * @example
739
- * ```typescript
740
- * const note = client.generateNote(1_000_000_000);
741
- * const result = await client.swap(
742
- * connection,
743
- * note,
744
- * new PublicKey("..."), // recipient
745
- * {
746
- * outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
747
- * slippageBps: 100, // 1%
748
- * getQuote: async (amount, mint, slippage) => {
749
- * // Fetch quote from your swap API
750
- * const quote = await fetchSwapQuote(amount, mint, slippage);
751
- * return {
752
- * outAmount: quote.outAmount,
753
- * minOutputAmount: quote.minOutputAmount
754
- * };
755
- * }
756
- * }
757
- * );
758
- * ```
222
+ * Default localStorage key the legacy `<= 0.1.5` SDK used for the
223
+ * plaintext `CloakNote[]` blob.
759
224
  */
760
- swap(connection: Connection, note: CloakNote, recipient: PublicKey, options: SwapOptions): Promise<SwapResult>;
225
+ static readonly LEGACY_NOTES_KEY = "cloak_notes";
761
226
  /**
762
- * Generate a new note without depositing
763
- *
764
- * @param amountLamports - Amount for the note
765
- * @param useWalletKeys - Whether to use wallet keys (v2.0 recommended)
766
- * @returns New note (not yet deposited)
227
+ * Runs the legacy-purge exactly once per browser origin. Invoked
228
+ * automatically by the constructor; exposed as a static for tests and
229
+ * for callers who want to force the check before the first adapter
230
+ * instantiation.
767
231
  */
768
- generateNote(amountLamports: number, useWalletKeys?: boolean): Promise<CloakNote>;
232
+ static runLegacyPurgeOnce(): void;
769
233
  /**
770
- * Parse a note from JSON string
234
+ * Manual cleanup helper for callers that stored notes under a non-default
235
+ * key. The auto-purge in the constructor only handles the canonical
236
+ * `cloak_notes` slot; pass the custom key here.
771
237
  *
772
- * @param jsonString - JSON representation
773
- * @returns Parsed note
774
- */
775
- parseNote(jsonString: string): CloakNote;
776
- /**
777
- * Export a note to JSON string
238
+ * Pre-0.1.6 versions stored the user's `CloakNote[]` (including
239
+ * `r` randomness and `sk_spend` secret-key hex) as JSON plaintext.
240
+ * Those notes are no longer usable (the OLD `withdraw_regular.circom`
241
+ * flow is incompatible with the deployed UTXO program) but the plaintext
242
+ * would otherwise linger indefinitely.
778
243
  *
779
- * @param note - Note to export
780
- * @param pretty - Format with indentation
781
- * @returns JSON string
782
- */
783
- exportNote(note: CloakNote, pretty?: boolean): string;
784
- /**
785
- * Check if a note is ready for withdrawal
244
+ * Idempotent; safe to call when the key doesn't exist or when running
245
+ * outside a browser (no-op). Does NOT update the auto-purge sentinel.
786
246
  *
787
- * @param note - Note to check
788
- * @returns True if withdrawable
247
+ * @param notesKey - localStorage key to remove (default `"cloak_notes"`,
248
+ * matching the pre-0.1.6 default).
789
249
  */
790
- isWithdrawable(note: CloakNote): boolean;
250
+ static purgeLegacyNoteStorage(notesKey?: string): void;
251
+ }
252
+
253
+ /** Default Cloak Program ID on Solana mainnet. */
254
+ declare const CLOAK_PROGRAM_ID: PublicKey;
255
+ /**
256
+ * Constructor options for {@link CloakSDK}.
257
+ *
258
+ * Contains *all* the settings a caller can supply at construction time —
259
+ * including the secret-bearing fields (`keypairBytes`, `wallet`, `cloakKeys`).
260
+ * Those secrets are consumed into private SDK state and are NOT persisted on
261
+ * the snapshot returned by {@link CloakSDK.getConfig}; see
262
+ * {@link CloakConfig} for the (narrower) snapshot shape.
263
+ */
264
+ interface CloakSDKOptions {
265
+ /** Keypair bytes for signing (Node.js mode). Consumed into a `Keypair`; not retained. */
266
+ keypairBytes?: Uint8Array;
267
+ /** Wallet adapter for signing (browser mode). */
268
+ wallet?: WalletAdapter;
269
+ /** Network the SDK targets. Defaults to `"mainnet"`. */
270
+ network?: Network;
271
+ /** Cloak key pair for note scanning / encryption. */
272
+ cloakKeys?: CloakKeyPair;
273
+ /** Storage adapter for wallet keys (defaults to in-memory). */
274
+ storage?: StorageAdapter;
275
+ /** Program ID override (defaults to the mainnet shield-pool). */
276
+ programId?: PublicKey;
277
+ /** Relay URL override (defaults to `https://api.cloak.ag`). */
278
+ relayUrl?: string;
279
+ /** Enable structured debug logging. Also via env `CLOAK_DEBUG=1` / `DEBUG=cloak:*`. */
280
+ debug?: boolean;
281
+ }
282
+ /**
283
+ * Lightweight Cloak client.
284
+ *
285
+ * Holds program/relay/storage configuration and exposes a small set of
286
+ * read-only chain helpers. **All transaction-emitting methods (deposit,
287
+ * privateTransfer, withdraw, send, swap) were removed in 0.1.6** — they
288
+ * shipped a legacy `[discriminator: 1, amount: u64, commitment: 32]` deposit
289
+ * instruction (`createDepositInstruction`) that the on-chain program no
290
+ * longer understands. Tag `1` is now `TransactSwap`, not `Deposit`, so every
291
+ * call hit `0x1063 MissingAccounts`. The OLD note model
292
+ * (3-input `Poseidon(amount, r0, r1, pk_spend)` for `withdraw_regular.circom`)
293
+ * is also incompatible with the deployed `transaction.circom` UTXO circuit.
294
+ *
295
+ * The supported flow is the functional UTXO API:
296
+ *
297
+ * ```ts
298
+ * import {
299
+ * transact,
300
+ * createUtxo,
301
+ * createZeroUtxo,
302
+ * generateUtxoKeypair,
303
+ * CLOAK_PROGRAM_ID,
304
+ * NATIVE_SOL_MINT,
305
+ * } from "@cloak.dev/sdk";
306
+ *
307
+ * const outputKeypair = await generateUtxoKeypair();
308
+ * const outputUtxo = await createUtxo(amountLamports, outputKeypair);
309
+ * const result = await transact(
310
+ * {
311
+ * inputUtxos: [await createZeroUtxo(), await createZeroUtxo()],
312
+ * outputUtxos: [outputUtxo, await createZeroUtxo()],
313
+ * externalAmount: amountLamports, // positive = deposit
314
+ * depositor: payer.publicKey,
315
+ * },
316
+ * {
317
+ * connection,
318
+ * programId: CLOAK_PROGRAM_ID,
319
+ * relayUrl: "https://api.cloak.ag",
320
+ * depositorKeypair: payer,
321
+ * },
322
+ * );
323
+ * ```
324
+ *
325
+ * See `transfer`, `partialWithdraw`, `fullWithdraw`, `swapUtxo` for the
326
+ * non-deposit flows.
327
+ */
328
+ declare class CloakSDK {
329
+ private config;
330
+ private keypair?;
331
+ private wallet?;
332
+ private cloakKeys?;
333
+ private relay;
334
+ private storage;
335
+ constructor(config: CloakSDKOptions);
336
+ /** Public key of the configured signer (keypair or wallet). */
337
+ getPublicKey(): PublicKey;
338
+ /** True when the SDK was constructed with a wallet adapter (browser). */
339
+ isWalletMode(): boolean;
791
340
  /**
792
- * Get Merkle proof for a leaf index directly from on-chain state
793
- *
794
- * @param connection - Solana connection
795
- * @param leafIndex - Leaf index in tree
796
- * @returns Merkle proof computed from on-chain data
341
+ * Compute a Merkle proof for `leafIndex` from on-chain state. No relay /
342
+ * indexer round-trip required.
797
343
  */
798
344
  getMerkleProof(connection: Connection, leafIndex: number): Promise<MerkleProof>;
799
- /**
800
- * Get current Merkle root directly from on-chain state
801
- *
802
- * @param connection - Solana connection
803
- * @returns Current root hash from on-chain tree
804
- */
345
+ /** Read the current SOL-pool Merkle root from on-chain state. */
805
346
  getCurrentRoot(connection: Connection): Promise<string>;
806
- /**
807
- * Get transaction status from relay service
808
- *
809
- * @param requestId - Request ID from previous submission
810
- * @returns Current status
811
- */
347
+ /** Poll relay for the status of a previously-submitted request. */
812
348
  getTransactionStatus(requestId: string): Promise<TxStatus>;
813
- /**
814
- * Fetch and decrypt this user's transaction metadata history.
815
- * DEPRECATED: This functionality is no longer supported
816
- */
817
- getTransactionMetadata(_options?: {
818
- _after?: number;
819
- _before?: number;
820
- }): Promise<TransactionMetadata[]>;
821
- /**
822
- * Load all notes from storage
823
- *
824
- * @returns Array of saved notes
825
- */
826
- loadNotes(): Promise<CloakNote[]>;
827
- /**
828
- * Save a note to storage
829
- *
830
- * @param note - Note to save
831
- */
832
- saveNote(note: CloakNote): Promise<void>;
833
- /**
834
- * Find a note by its commitment
835
- *
836
- * @param commitment - Commitment hash
837
- * @returns Note if found
838
- */
839
- findNote(commitment: string): Promise<CloakNote | undefined>;
840
- /**
841
- * Import wallet keys from JSON
842
- *
843
- * @param keysJson - JSON string containing keys
844
- */
349
+ /** Import wallet keys from JSON; persists to the configured storage adapter. */
845
350
  importWalletKeys(keysJson: string): Promise<void>;
846
351
  /**
847
- * Export wallet keys to JSON
848
- *
849
- * WARNING: This exports secret keys! Store securely.
352
+ * Export wallet keys to JSON.
850
353
  *
851
- * @returns JSON string with keys
354
+ * WARNING: this exports secret keys. Store securely.
852
355
  */
853
356
  exportWalletKeys(): string;
854
357
  /**
855
- * Get the configuration
856
- */
857
- getConfig(): CloakConfig;
858
- /**
859
- * Wrap errors with better categorization and user-friendly messages
358
+ * Snapshot of the active SDK configuration.
860
359
  *
861
- * @private
360
+ * The return type ({@link CloakConfig}) is structurally narrower than the
361
+ * constructor input ({@link CloakSDKOptions}): `keypairBytes`, `wallet`,
362
+ * and `storage` are intentionally absent from the snapshot. The secrets
363
+ * are consumed into private SDK state at construction time and are not
364
+ * re-exposable through this method — the type system enforces it. To
365
+ * sign, pass a `Keypair` / `WalletAdapter` directly to the standalone
366
+ * `transact` / `partialWithdraw` / `fullWithdraw` / `swapUtxo` helpers.
862
367
  */
863
- private wrapError;
368
+ getConfig(): CloakConfig;
369
+ }
370
+
371
+ type ComplianceTxType = "deposit" | "withdraw" | "send" | "swap";
372
+ interface TransactionMetadata {
373
+ amount: number;
374
+ recipient: string;
375
+ timestamp: number;
376
+ txType: ComplianceTxType;
377
+ commitment: string;
378
+ signature?: string;
379
+ outputMint?: string;
380
+ }
381
+ interface EncryptedMetadataBundle {
382
+ encrypted_user: string;
383
+ encrypted_compliance: string;
384
+ user_pubkey: string;
385
+ commitment: string;
386
+ timestamp: number;
387
+ tx_type?: ComplianceTxType;
388
+ wallet_signature?: string;
389
+ viewing_key?: string;
864
390
  }
865
391
 
392
+ interface ViewingKeyPair {
393
+ privateKey: Uint8Array;
394
+ publicKey: Uint8Array;
395
+ }
396
+ /** Zcash-style expanded spend key: ask (auth), nsk (nullifier/nk), ovk (outgoing view) */
397
+ interface ExpandedSpendKey {
398
+ ask: Uint8Array;
399
+ nsk: Uint8Array;
400
+ ovk: Uint8Array;
401
+ }
866
402
  /**
867
- * Serialize a note to JSON string
868
- *
869
- * @param note - Note to serialize
870
- * @param pretty - Whether to format with indentation (default: false)
871
- * @returns JSON string
872
- *
873
- * @example
874
- * ```typescript
875
- * const json = serializeNote(note, true);
876
- * console.log(json);
877
- * // Or save to file, copy to clipboard, etc.
878
- * ```
403
+ * Expand spend key into (ask, nsk, ovk) — Zcash-style.
404
+ * - ask: spend authorization (future)
405
+ * - nsk: base for incoming viewing (nk) — chain note decryption
406
+ * - ovk: outgoing viewing (future)
879
407
  */
880
- declare function serializeNote(note: CloakNote, pretty?: boolean): string;
408
+ declare function expandSpendKey(skSpend: Uint8Array): ExpandedSpendKey;
881
409
  /**
882
- * Export note as downloadable JSON (browser only)
410
+ * Derive chain note viewing key from nk (Zcash-style IVK component).
411
+ * Used for trial-decrypting incoming chain notes.
883
412
  *
884
- * @param note - Note to export
885
- * @param filename - Optional custom filename
413
+ * @param nk 32-byte nk (from expandSpendKey(...).nsk — the incoming view base)
886
414
  */
887
- declare function downloadNote(note: CloakNote, filename?: string): void;
415
+ declare function deriveViewingKeyFromNk(nk: Uint8Array): ViewingKeyPair;
888
416
  /**
889
- * Copy note to clipboard as JSON (browser only)
890
- *
891
- * @param note - Note to copy
892
- * @returns Promise that resolves when copied
417
+ * Derive diversifier for per-output chain note encryption (Phase 3).
418
+ * d = BLAKE3("cloak_div_v1" || nk || commitmentHex || outputIndex)[0:11]
893
419
  */
894
- declare function copyNoteToClipboard(note: CloakNote): Promise<void>;
895
-
420
+ declare function deriveDiversifier(nk: Uint8Array, commitmentHex: string, outputIndex: number): Uint8Array;
896
421
  /**
897
- * Fee calculation utilities for Cloak Protocol
898
- *
899
- * The protocol charges a fixed fee plus a variable percentage fee
900
- * to prevent sybil attacks and cover operational costs.
901
- *
902
- * IMPORTANT: These constants are the single source of truth for fee calculations.
903
- * When updating fees, only change these values - all other code should import from here.
422
+ * Derive per-output viewing key pair from nk + diversifier (Phase 3).
423
+ * sk_d = BLAKE3("cloak_sk_d_v1" || nk || d) → clamp → X25519 keypair
904
424
  */
905
- /** Lamports per SOL */
906
- declare const LAMPORTS_PER_SOL = 1000000000;
907
- /** Fixed fee: 0.005 SOL (5M lamports) */
908
- declare const FIXED_FEE_LAMPORTS = 5000000;
909
- /** Variable fee numerator (3 = 0.3%) */
910
- declare const VARIABLE_FEE_NUMERATOR = 3;
911
- /** Variable fee denominator (1000 = divide by 1000) */
912
- declare const VARIABLE_FEE_DENOMINATOR = 1000;
913
- /** Variable fee rate as decimal: 0.3% = 0.003 */
914
- declare const VARIABLE_FEE_RATE: number;
915
- /** Minimum deposit amount to prevent dust/UX footguns (0.01 SOL). */
916
- declare const MIN_DEPOSIT_LAMPORTS = 10000000;
425
+ declare function deriveDiversifiedViewingKey(nk: Uint8Array, diversifier: Uint8Array): ViewingKeyPair;
917
426
  /**
918
- * Calculate the total protocol fee for a given amount
919
- *
920
- * Formula: FIXED_FEE + floor((amount * VARIABLE_FEE_NUMERATOR) / VARIABLE_FEE_DENOMINATOR)
921
- *
922
- * @param amountLamports - Amount in lamports
923
- * @returns Total fee in lamports
924
- *
925
- * @example
926
- * ```typescript
927
- * const fee = calculateFee(1_000_000_000); // 1 SOL
928
- * // Returns: 5_000_000 (fixed) + 3_000_000 (0.3%) = 8_000_000 lamports
929
- * ```
427
+ * Derive chain note viewing key from spend key (Zcash-style).
428
+ * Expands to (ask, nsk, ovk) and uses nsk (nk) for chain note decryption.
930
429
  */
931
- declare function calculateFee(amountLamports: number): number;
932
- /**
933
- * Bigint version of calculateFee (for UTXO flows that use bigint lamports).
934
- */
935
- declare function calculateFeeBigint(amountLamports: bigint): bigint;
936
- /**
937
- * Returns true if a withdrawal amount can pay fees and still leave >0 lamports.
938
- * (On-chain uses the same integer math and enforces this as well.)
939
- */
940
- declare function isWithdrawAmountSufficient(amountLamports: bigint): boolean;
941
- /**
942
- * Calculate the distributable amount after protocol fees
943
- *
944
- * This is the amount available to send to recipients.
945
- *
946
- * @param amountLamports - Total note amount in lamports
947
- * @returns Amount available for recipients in lamports
948
- *
949
- * @example
950
- * ```typescript
951
- * const distributable = getDistributableAmount(1_000_000_000);
952
- * // Returns: 1_000_000_000 - 7_500_000 = 992_500_000 lamports
953
- * ```
954
- */
955
- declare function getDistributableAmount(amountLamports: number): number;
956
- /**
957
- * Format lamports as SOL string
958
- *
959
- * @param lamports - Amount in lamports
960
- * @param decimals - Number of decimal places (default: 9)
961
- * @returns Formatted string (e.g., "1.000000000")
962
- *
963
- * @example
964
- * ```typescript
965
- * formatAmount(1_000_000_000); // "1.000000000"
966
- * formatAmount(1_500_000_000); // "1.500000000"
967
- * formatAmount(123_456_789, 4); // "0.1235"
968
- * ```
969
- */
970
- declare function formatAmount(lamports: number, decimals?: number): string;
971
- /**
972
- * Parse SOL string to lamports
973
- *
974
- * @param sol - SOL amount as string (e.g., "1.5")
975
- * @returns Amount in lamports
976
- * @throws Error if invalid format
977
- *
978
- * @example
979
- * ```typescript
980
- * parseAmount("1.5"); // 1_500_000_000
981
- * parseAmount("0.001"); // 1_000_000
982
- * ```
983
- */
984
- declare function parseAmount(sol: string): number;
985
- /**
986
- * Validate that outputs sum equals expected amount
987
- *
988
- * @param outputs - Array of output amounts
989
- * @param expectedTotal - Expected total amount
990
- * @returns True if amounts match
991
- */
992
- declare function validateOutputsSum(outputs: Array<{
993
- amount: number;
994
- }>, expectedTotal: number): boolean;
995
- /**
996
- * Calculate relay fee from basis points
997
- *
998
- * @param amountLamports - Amount in lamports
999
- * @param feeBps - Fee in basis points (100 bps = 1%)
1000
- * @returns Relay fee in lamports
1001
- *
1002
- * @example
1003
- * ```typescript
1004
- * calculateRelayFee(1_000_000_000, 50); // 0.5% = 5_000_000 lamports
1005
- * ```
1006
- */
1007
- declare function calculateRelayFee(amountLamports: number, feeBps: number): number;
1008
-
1009
- /**
1010
- * Note Manager
1011
- *
1012
- * Standalone note management - no browser dependencies.
1013
- * Storage is handled externally via StorageAdapter.
1014
- *
1015
- * Core functionality:
1016
- * - Generate notes (v1.0 and v2.0)
1017
- * - Parse and validate notes
1018
- * - Note utilities (formatting, fees, etc.)
1019
- * - Key management (without storage)
1020
- */
1021
-
1022
- /**
1023
- * Generate a new note without wallet keys (legacy v1.0)
1024
- * Uses Poseidon hashing to match circuit implementation
1025
- * @deprecated Use generateNoteFromWallet instead for enhanced security
1026
- */
1027
- declare function generateNote(amountLamports: number, network?: Network): Promise<CloakNote>;
1028
- /**
1029
- * Generate a note using wallet's spend key (v2.0 recommended)
1030
- * Uses Poseidon hashing to match circuit implementation
1031
- */
1032
- declare function generateNoteFromWallet(amountLamports: number, keys: CloakKeyPair, network?: Network): Promise<CloakNote>;
1033
- /**
1034
- * Parse and validate a note from JSON string
1035
- */
1036
- declare function parseNote(jsonString: string): CloakNote;
1037
- /**
1038
- * Export note to JSON string
1039
- */
1040
- declare function exportNote(note: CloakNote, pretty?: boolean): string;
1041
- /**
1042
- * Check if a note is withdrawable (has been deposited)
1043
- * Note: merkleProof is optional - it may be fetched lazily at withdrawal time
1044
- */
1045
- declare function isWithdrawable(note: CloakNote): boolean;
1046
- /**
1047
- * Update note with deposit information
1048
- * Returns a new note object with deposit info added
1049
- */
1050
- declare function updateNoteWithDeposit(note: CloakNote, depositInfo: {
1051
- signature: string;
1052
- slot: number;
1053
- leafIndex: number;
1054
- root: string;
1055
- merkleProof?: {
1056
- pathElements: string[];
1057
- pathIndices: number[];
1058
- };
1059
- }): CloakNote;
1060
- /**
1061
- * Find note by commitment from an array
1062
- */
1063
- declare function findNoteByCommitment(notes: CloakNote[], commitment: string): CloakNote | undefined;
1064
- /**
1065
- * Filter notes by network
1066
- */
1067
- declare function filterNotesByNetwork(notes: CloakNote[], network: Network): CloakNote[];
1068
- /**
1069
- * Filter notes that can be withdrawn
1070
- */
1071
- declare function filterWithdrawableNotes(notes: CloakNote[]): CloakNote[];
1072
- /**
1073
- * Export keys to JSON string
1074
- * WARNING: This exports secret keys! Store securely.
1075
- */
1076
- declare function exportWalletKeys(keys: CloakKeyPair): string;
1077
- /**
1078
- * Import keys from JSON string
1079
- */
1080
- declare function importWalletKeys(keysJson: string): CloakKeyPair;
1081
- /**
1082
- * Get public view key from keys
1083
- */
1084
- declare function getPublicViewKey(keys: CloakKeyPair): string;
1085
- /**
1086
- * Get view key from keys
1087
- */
1088
- declare function getViewKey(keys: CloakKeyPair): ViewKey;
1089
- /**
1090
- * Get recipient amount after fees
1091
- */
1092
- declare function getRecipientAmount(amountLamports: number): number;
1093
-
1094
- interface ViewingKeyPair {
1095
- privateKey: Uint8Array;
1096
- publicKey: Uint8Array;
1097
- }
1098
- /** Zcash-style expanded spend key: ask (auth), nsk (nullifier/nk), ovk (outgoing view) */
1099
- interface ExpandedSpendKey {
1100
- ask: Uint8Array;
1101
- nsk: Uint8Array;
1102
- ovk: Uint8Array;
1103
- }
1104
- /**
1105
- * Expand spend key into (ask, nsk, ovk) — Zcash-style.
1106
- * - ask: spend authorization (future)
1107
- * - nsk: base for incoming viewing (nk) — chain note decryption
1108
- * - ovk: outgoing viewing (future)
1109
- */
1110
- declare function expandSpendKey(skSpend: Uint8Array): ExpandedSpendKey;
1111
- /**
1112
- * Derive chain note viewing key from nk (Zcash-style IVK component).
1113
- * Used for trial-decrypting incoming chain notes.
1114
- *
1115
- * @param nk 32-byte nk (from expandSpendKey(...).nsk — the incoming view base)
1116
- */
1117
- declare function deriveViewingKeyFromNk(nk: Uint8Array): ViewingKeyPair;
1118
- /**
1119
- * Derive diversifier for per-output chain note encryption (Phase 3).
1120
- * d = BLAKE3("cloak_div_v1" || nk || commitmentHex || outputIndex)[0:11]
1121
- */
1122
- declare function deriveDiversifier(nk: Uint8Array, commitmentHex: string, outputIndex: number): Uint8Array;
1123
- /**
1124
- * Derive per-output viewing key pair from nk + diversifier (Phase 3).
1125
- * sk_d = BLAKE3("cloak_sk_d_v1" || nk || d) → clamp → X25519 keypair
1126
- */
1127
- declare function deriveDiversifiedViewingKey(nk: Uint8Array, diversifier: Uint8Array): ViewingKeyPair;
1128
- /**
1129
- * Derive chain note viewing key from spend key (Zcash-style).
1130
- * Expands to (ask, nsk, ovk) and uses nsk (nk) for chain note decryption.
1131
- */
1132
- declare function deriveViewingKeyFromSpendKey(skSpend: Uint8Array): ViewingKeyPair;
430
+ declare function deriveViewingKeyFromSpendKey(skSpend: Uint8Array): ViewingKeyPair;
1133
431
  /**
1134
432
  * Derive chain note viewing key from UTXO private key (Zcash-style).
1135
433
  * New UTXO (new keypair) => new viewing key.
@@ -1243,137 +541,6 @@ declare function computeMerkleRoot(leaf: bigint, pathElements: bigint[], pathInd
1243
541
  * Convert hex string to bigint
1244
542
  */
1245
543
  declare function hexToBigint$1(hex: string): bigint;
1246
- /**
1247
- * Compute commitment = Poseidon(amount, r0, r1, pk_spend)
1248
- * where pk_spend = Poseidon(sk0, sk1)
1249
- * (matching withdraw_regular.circom)
1250
- *
1251
- * This is the test-style function that takes bigints directly
1252
- *
1253
- * @param amount - Amount as bigint
1254
- * @param r - Randomness as bigint
1255
- * @param sk_spend - Spending secret key as bigint
1256
- * @returns Commitment hash as bigint
1257
- */
1258
- declare function computeCommitment$1(amount: bigint, r: bigint, sk_spend: bigint): Promise<bigint>;
1259
- /**
1260
- * Generate a Poseidon commitment for a note
1261
- *
1262
- * Formula: Poseidon(amount, r0, r1, pk_spend)
1263
- * where pk_spend = Poseidon(sk0, sk1)
1264
- *
1265
- * This matches the withdraw_regular.circom circuit
1266
- *
1267
- * @param amountLamports - Amount in lamports
1268
- * @param r - Randomness bytes (32 bytes)
1269
- * @param skSpend - Spending secret key bytes (32 bytes)
1270
- * @returns Commitment hash as bigint
1271
- */
1272
- declare function generateCommitmentAsync(amountLamports: number, r: Uint8Array, skSpend: Uint8Array): Promise<bigint>;
1273
- /**
1274
- * Generate a Poseidon commitment for a note (sync wrapper)
1275
- * Returns bytes instead of bigint for backward compatibility
1276
- *
1277
- * @deprecated Use generateCommitmentAsync instead
1278
- */
1279
- declare function generateCommitment(_amountLamports: number, _r: Uint8Array, _skSpend: Uint8Array): Uint8Array;
1280
- /**
1281
- * Compute nullifier = Poseidon(sk0, sk1, leaf_index)
1282
- * (matching withdraw_regular.circom)
1283
- *
1284
- * This is the test-style function that takes bigint directly
1285
- *
1286
- * @param sk_spend - Spending secret key as bigint
1287
- * @param leafIndex - Index in the Merkle tree as bigint
1288
- * @returns Nullifier as bigint
1289
- */
1290
- declare function computeNullifier$1(sk_spend: bigint, leafIndex: bigint): Promise<bigint>;
1291
- /**
1292
- * Compute nullifier from spending key and leaf index
1293
- *
1294
- * Formula: Poseidon(sk0, sk1, leaf_index)
1295
- *
1296
- * This matches the circuit's nullifier computation
1297
- *
1298
- * @param skSpend - Spending secret key bytes (32 bytes) or hex string
1299
- * @param leafIndex - Index in the Merkle tree
1300
- * @returns Nullifier as bigint
1301
- */
1302
- declare function computeNullifierAsync(skSpend: Uint8Array | string, leafIndex: number): Promise<bigint>;
1303
- /**
1304
- * Compute nullifier (sync wrapper for backward compatibility)
1305
- * @deprecated Use computeNullifierAsync instead
1306
- */
1307
- declare function computeNullifierSync(_skSpend: Uint8Array, _leafIndex: number): Uint8Array;
1308
- /**
1309
- * Compute outputs hash from recipients and amounts
1310
- *
1311
- * Formula: Chain of Poseidon(prev_hash, addr_lo, addr_hi, amount) for each active output
1312
- *
1313
- * This matches the withdraw_regular.circom circuit's outputs hash computation
1314
- *
1315
- * @param outputs - Array of {recipient: PublicKey, amount: number}
1316
- * @returns Outputs hash as bigint
1317
- */
1318
- declare function computeOutputsHashAsync(outputs: Array<{
1319
- recipient: PublicKey;
1320
- amount: number;
1321
- }>): Promise<bigint>;
1322
- /**
1323
- * Compute outputs hash for withdraw_regular circuit
1324
- * outputs_hash = chain of Poseidon(prev_hash, addr_lo, addr_hi, amount) for each active output
1325
- *
1326
- * This is the test-style function that takes raw limbs
1327
- *
1328
- * @param outAddr - Array of [lo, hi] limb pairs for each address (5x2 array)
1329
- * @param outAmount - Array of amounts as bigints (5 elements)
1330
- * @param outFlags - Array of flags (1 = active, 0 = inactive) (5 elements)
1331
- * @returns Outputs hash as bigint
1332
- */
1333
- declare function computeOutputsHash(outAddr: bigint[][], outAmount: bigint[], outFlags: number[]): Promise<bigint>;
1334
- /**
1335
- * Compute outputs hash (sync wrapper for backward compatibility)
1336
- * @deprecated Use computeOutputsHashAsync instead
1337
- */
1338
- declare function computeOutputsHashSync(_outputs: Array<{
1339
- recipient: PublicKey;
1340
- amount: number;
1341
- }>): Uint8Array;
1342
- /**
1343
- * Compute outputs hash for withdraw_swap circuit
1344
- * outputs_hash = Poseidon(input_mint limbs, output_mint limbs, recipient_ata limbs, min_output_amount, public_amount)
1345
- *
1346
- * This is the test-style function that takes raw limbs
1347
- *
1348
- * @param inputMintLimbs - Input mint address as [lo, hi] limbs
1349
- * @param outputMintLimbs - Output mint address as [lo, hi] limbs
1350
- * @param recipientAtaLimbs - Recipient ATA as [lo, hi] limbs
1351
- * @param minOutputAmount - Minimum output amount as bigint
1352
- * @param publicAmount - Public amount as bigint
1353
- * @returns Outputs hash as bigint
1354
- */
1355
- declare function computeSwapOutputsHash(inputMintLimbs: [bigint, bigint], outputMintLimbs: [bigint, bigint], recipientAtaLimbs: [bigint, bigint], minOutputAmount: bigint, publicAmount: bigint): Promise<bigint>;
1356
- /**
1357
- * Compute outputs hash for swap transactions
1358
- *
1359
- * Formula: Poseidon(input_mint_lo, input_mint_hi, output_mint_lo, output_mint_hi,
1360
- * recipient_ata_lo, recipient_ata_hi, min_output_amount, public_amount)
1361
- *
1362
- * This matches the withdraw_swap.circom circuit
1363
- *
1364
- * @param inputMint - Input token mint address (SOL = SystemProgram)
1365
- * @param outputMint - Output token mint address
1366
- * @param recipientAta - Recipient's associated token account
1367
- * @param minOutputAmount - Minimum output amount in token's smallest unit
1368
- * @param amount - Note amount in lamports (public_amount)
1369
- * @returns Outputs hash as bigint
1370
- */
1371
- declare function computeSwapOutputsHashAsync(inputMint: PublicKey, outputMint: PublicKey, recipientAta: PublicKey, minOutputAmount: number, amount: number): Promise<bigint>;
1372
- /**
1373
- * Compute swap outputs hash (sync wrapper for backward compatibility)
1374
- * @deprecated Use computeSwapOutputsHashAsync instead
1375
- */
1376
- declare function computeSwapOutputsHashSync(_outputMint: PublicKey, _recipientAta: PublicKey, _minOutputAmount: number, _amount: number): Uint8Array;
1377
544
  /**
1378
545
  * Convert bigint to 32-byte big-endian Uint8Array
1379
546
  */
@@ -1436,57 +603,145 @@ declare function proofToBytes(proof: Groth16Proof): Uint8Array;
1436
603
  declare function buildPublicInputsBytes(root: bigint, nullifier: bigint, outputsHash: bigint, publicAmount: bigint): Uint8Array;
1437
604
 
1438
605
  /**
1439
- * Validate a Solana public key
606
+ * Fee calculation utilities for Cloak Protocol
1440
607
  *
1441
- * @param address - Address string to validate
1442
- * @returns True if valid Solana address
1443
- */
1444
- declare function isValidSolanaAddress(address: string): boolean;
1445
- /**
1446
- * Validate a Cloak note structure
608
+ * The protocol charges a fixed fee plus a variable percentage fee
609
+ * to prevent sybil attacks and cover operational costs.
1447
610
  *
1448
- * @param note - Note object to validate
1449
- * @throws Error if invalid
611
+ * IMPORTANT: These constants are the single source of truth for fee calculations.
612
+ * When updating fees, only change these values - all other code should import from here.
1450
613
  */
1451
- declare function validateNote(note: any): asserts note is CloakNote;
614
+ /** Lamports per SOL */
615
+ declare const LAMPORTS_PER_SOL = 1000000000;
616
+ /** Fixed fee: 0.005 SOL (5M lamports) */
617
+ declare const FIXED_FEE_LAMPORTS = 5000000;
618
+ /** Variable fee numerator (3 = 0.3%) */
619
+ declare const VARIABLE_FEE_NUMERATOR = 3;
620
+ /** Variable fee denominator (1000 = divide by 1000) */
621
+ declare const VARIABLE_FEE_DENOMINATOR = 1000;
622
+ /** Variable fee rate as decimal: 0.3% = 0.003 */
623
+ declare const VARIABLE_FEE_RATE: number;
624
+ /** Minimum deposit amount to prevent dust/UX footguns (0.01 SOL). */
625
+ declare const MIN_DEPOSIT_LAMPORTS = 10000000;
1452
626
  /**
1453
- * Validate that a note is ready for withdrawal
627
+ * Calculate the total protocol fee for a given amount
1454
628
  *
1455
- * @param note - Note to validate
1456
- * @throws Error if note cannot be used for withdrawal
1457
- */
1458
- declare function validateWithdrawableNote(note: CloakNote): void;
1459
- /**
1460
- * Validate transfer recipients
629
+ * Formula: FIXED_FEE + floor((amount * VARIABLE_FEE_NUMERATOR) / VARIABLE_FEE_DENOMINATOR)
1461
630
  *
1462
- * @param recipients - Array of transfers to validate
1463
- * @param totalAmount - Total amount available
1464
- * @throws Error if invalid
1465
- */
1466
- declare function validateTransfers(recipients: Array<{
1467
- recipient: PublicKey;
1468
- amount: number;
1469
- }>, totalAmount: number): void;
1470
-
1471
- /**
1472
- * Network Utilities
631
+ * @param amountLamports - Amount in lamports
632
+ * @returns Total fee in lamports
1473
633
  *
1474
- * Helper functions for network detection and configuration
634
+ * @example
635
+ * ```typescript
636
+ * const fee = calculateFee(1_000_000_000); // 1 SOL
637
+ * // Returns: 5_000_000 (fixed) + 3_000_000 (0.3%) = 8_000_000 lamports
638
+ * ```
1475
639
  */
1476
-
640
+ declare function calculateFee(amountLamports: number): number;
1477
641
  /**
1478
- * Detect network from RPC URL
1479
- *
1480
- * Attempts to detect the Solana network from common RPC URL patterns.
1481
- * Falls back to devnet if unable to detect.
642
+ * Bigint version of calculateFee (for UTXO flows that use bigint lamports).
1482
643
  */
1483
- declare function detectNetworkFromRpcUrl(rpcUrl?: string): Network;
644
+ declare function calculateFeeBigint(amountLamports: bigint): bigint;
1484
645
  /**
1485
- * Get the standard RPC URL for a network
646
+ * Returns true if a withdrawal amount can pay fees and still leave >0 lamports.
647
+ * (On-chain uses the same integer math and enforces this as well.)
1486
648
  */
1487
- declare function getRpcUrlForNetwork(network: Network): string;
649
+ declare function isWithdrawAmountSufficient(amountLamports: bigint): boolean;
1488
650
  /**
1489
- * Validate RPC URL format
651
+ * Calculate the distributable amount after protocol fees
652
+ *
653
+ * This is the amount available to send to recipients.
654
+ *
655
+ * @param amountLamports - Total note amount in lamports
656
+ * @returns Amount available for recipients in lamports
657
+ *
658
+ * @example
659
+ * ```typescript
660
+ * const distributable = getDistributableAmount(1_000_000_000);
661
+ * // Returns: 1_000_000_000 - 7_500_000 = 992_500_000 lamports
662
+ * ```
663
+ */
664
+ declare function getDistributableAmount(amountLamports: number): number;
665
+ /**
666
+ * Format lamports as SOL string
667
+ *
668
+ * @param lamports - Amount in lamports
669
+ * @param decimals - Number of decimal places (default: 9)
670
+ * @returns Formatted string (e.g., "1.000000000")
671
+ *
672
+ * @example
673
+ * ```typescript
674
+ * formatAmount(1_000_000_000); // "1.000000000"
675
+ * formatAmount(1_500_000_000); // "1.500000000"
676
+ * formatAmount(123_456_789, 4); // "0.1235"
677
+ * ```
678
+ */
679
+ declare function formatAmount(lamports: number, decimals?: number): string;
680
+ /**
681
+ * Parse SOL string to lamports
682
+ *
683
+ * @param sol - SOL amount as string (e.g., "1.5")
684
+ * @returns Amount in lamports
685
+ * @throws Error if invalid format
686
+ *
687
+ * @example
688
+ * ```typescript
689
+ * parseAmount("1.5"); // 1_500_000_000
690
+ * parseAmount("0.001"); // 1_000_000
691
+ * ```
692
+ */
693
+ declare function parseAmount(sol: string): number;
694
+ /**
695
+ * Validate that outputs sum equals expected amount
696
+ *
697
+ * @param outputs - Array of output amounts
698
+ * @param expectedTotal - Expected total amount
699
+ * @returns True if amounts match
700
+ */
701
+ declare function validateOutputsSum(outputs: Array<{
702
+ amount: number;
703
+ }>, expectedTotal: number): boolean;
704
+ /**
705
+ * Calculate relay fee from basis points
706
+ *
707
+ * @param amountLamports - Amount in lamports
708
+ * @param feeBps - Fee in basis points (100 bps = 1%)
709
+ * @returns Relay fee in lamports
710
+ *
711
+ * @example
712
+ * ```typescript
713
+ * calculateRelayFee(1_000_000_000, 50); // 0.5% = 5_000_000 lamports
714
+ * ```
715
+ */
716
+ declare function calculateRelayFee(amountLamports: number, feeBps: number): number;
717
+
718
+ /**
719
+ * Validate that a string is a valid Solana public key.
720
+ *
721
+ * @param address - Address string to validate
722
+ * @returns True if valid Solana address
723
+ */
724
+ declare function isValidSolanaAddress(address: string): boolean;
725
+
726
+ /**
727
+ * Network Utilities
728
+ *
729
+ * Helper functions for network detection and configuration
730
+ */
731
+
732
+ /**
733
+ * Detect network from RPC URL
734
+ *
735
+ * Attempts to detect the Solana network from common RPC URL patterns.
736
+ * Falls back to devnet if unable to detect.
737
+ */
738
+ declare function detectNetworkFromRpcUrl(rpcUrl?: string): Network;
739
+ /**
740
+ * Get the standard RPC URL for a network
741
+ */
742
+ declare function getRpcUrlForNetwork(network: Network): string;
743
+ /**
744
+ * Validate RPC URL format
1490
745
  */
1491
746
  declare function isValidRpcUrl(url: string): boolean;
1492
747
  /**
@@ -1498,6 +753,85 @@ declare function getExplorerUrl(signature: string, network?: Network): string;
1498
753
  */
1499
754
  declare function getAddressExplorerUrl(address: string, network?: Network): string;
1500
755
 
756
+ /**
757
+ * Merkle Tree implementation for computing proofs
758
+ *
759
+ * This tree stores all leaves and can compute the correct Merkle path
760
+ * for any leaf index.
761
+ */
762
+ declare const MERKLE_TREE_HEIGHT = 32;
763
+ /**
764
+ * Merkle Tree class that stores all leaves and computes proofs
765
+ */
766
+ declare class MerkleTree {
767
+ private levels;
768
+ private capacity;
769
+ private layers;
770
+ private zeros;
771
+ private poseidon;
772
+ /**
773
+ * Create a new Merkle tree
774
+ * @param levels Number of levels in the tree (height)
775
+ * @param leaves Initial leaves to insert
776
+ * @param zeros Precomputed zero values for each level
777
+ * @param poseidon Pre-initialized Poseidon instance (avoids async in constructor)
778
+ */
779
+ constructor(levels: number, leaves: bigint[], zeros: bigint[], poseidon: any);
780
+ /**
781
+ * Create a new MerkleTree with async initialization
782
+ */
783
+ static create(levels?: number, leaves?: bigint[]): Promise<MerkleTree>;
784
+ /**
785
+ * Rebuild the tree from the leaves (synchronous — poseidon must be initialized)
786
+ */
787
+ private _rebuildSync;
788
+ /**
789
+ * Get the tree root
790
+ */
791
+ root(): bigint;
792
+ /**
793
+ * Get the number of leaves in the tree
794
+ */
795
+ get length(): number;
796
+ /**
797
+ * Insert a new leaf into the tree
798
+ */
799
+ insert(leaf: bigint): void;
800
+ /**
801
+ * Insert multiple leaves
802
+ */
803
+ bulkInsert(leaves: bigint[]): void;
804
+ /**
805
+ * Update the path from a leaf to the root
806
+ */
807
+ private _updatePath;
808
+ /**
809
+ * Get the Merkle path for a leaf at the given index
810
+ * @param index Leaf index
811
+ * @returns pathElements and pathIndices for the proof
812
+ */
813
+ path(index: number): {
814
+ pathElements: bigint[];
815
+ pathIndices: number;
816
+ };
817
+ /**
818
+ * Find the index of a leaf in the tree
819
+ * @param leaf Leaf value to find
820
+ * @returns Index if found, -1 otherwise
821
+ */
822
+ indexOf(leaf: bigint): number;
823
+ /**
824
+ * Get all leaves
825
+ */
826
+ leaves(): bigint[];
827
+ }
828
+ /**
829
+ * Build a MerkleTree from commitment values
830
+ * @param commitments Array of commitment bigints
831
+ * @param height Tree height (default: 32)
832
+ */
833
+ declare function buildMerkleTree(commitments: bigint[], height?: number): Promise<MerkleTree>;
834
+
1501
835
  /**
1502
836
  * Error Utilities
1503
837
  *
@@ -1529,6 +863,79 @@ declare function parseRelayErrorResponse(responseText: string): {
1529
863
  isRootNotFound: boolean;
1530
864
  currentRoot?: string;
1531
865
  };
866
+ /**
867
+ * Raised when a spend attempt hits 0x1020 DoubleSpend on-chain — the input
868
+ * UTXO's nullifier PDA already exists, meaning the UTXO was spent earlier
869
+ * (often via another device / session the user forgot about).
870
+ *
871
+ * Recovery path: the SDK's pre-flight nullifier check catches this BEFORE
872
+ * proof generation via `verifyUtxos()`. If thrown after submission (unlikely
873
+ * once pre-flight is wired in), the caller should call `verifyUtxos()` to
874
+ * reconcile local state with chain.
875
+ */
876
+ declare class UtxoAlreadySpentError extends Error {
877
+ readonly spentUtxoCommitments: string[];
878
+ constructor(message: string, spentUtxoCommitments?: string[]);
879
+ }
880
+ /**
881
+ * Base class for Range / sanctions-quote mismatches from the on-chain
882
+ * program. The relay signs quotes; the program verifies them. When they
883
+ * don't line up we hit one of three errors:
884
+ *
885
+ * 0x10B0 = QuoteExpired — quote's expiry_ts < on-chain clock
886
+ * 0x10B2 = WalletMismatch — quote wallet != expected_wallet
887
+ * 0x10B3 = MissingEd25519 — no withdraw-auth / range-quote ix at all
888
+ *
889
+ * These are structural mismatches the caller can't fix on their own —
890
+ * they indicate a relay / program version skew or a config drift
891
+ * (e.g. pool not initialized with the expected range_signer).
892
+ */
893
+ declare class SanctionsQuoteError extends Error {
894
+ readonly onChainCode: number;
895
+ readonly subKind: "expired" | "wallet_mismatch" | "missing_ix";
896
+ constructor(message: string, onChainCode: number, subKind: "expired" | "wallet_mismatch" | "missing_ix");
897
+ }
898
+ /**
899
+ * Generic catch-all for relay 500s that don't map to any structured class.
900
+ * Preserves the raw relay error text in `relayMessage` so the caller can
901
+ * log it without having to string-match.
902
+ */
903
+ declare class RelayInternalError extends Error {
904
+ readonly relayMessage?: string | undefined;
905
+ readonly httpStatus?: number | undefined;
906
+ /**
907
+ * Optional pre-built merkle tree rebuilt from chain (Node only).
908
+ * Populated by the chain-replay fallback in `transact()` when the
909
+ * relay-tree path fails after all retries. Callers can retry the
910
+ * spend with `options.cachedMerkleTree = err.cachedTreeFromChain`
911
+ * without paying the chain-replay cost a second time.
912
+ *
913
+ * Typed via `import type` so there's no runtime coupling between
914
+ * this module and the MerkleTree implementation.
915
+ */
916
+ readonly cachedTreeFromChain?: MerkleTree | undefined;
917
+ constructor(message: string, relayMessage?: string | undefined, httpStatus?: number | undefined,
918
+ /**
919
+ * Optional pre-built merkle tree rebuilt from chain (Node only).
920
+ * Populated by the chain-replay fallback in `transact()` when the
921
+ * relay-tree path fails after all retries. Callers can retry the
922
+ * spend with `options.cachedMerkleTree = err.cachedTreeFromChain`
923
+ * without paying the chain-replay cost a second time.
924
+ *
925
+ * Typed via `import type` so there's no runtime coupling between
926
+ * this module and the MerkleTree implementation.
927
+ */
928
+ cachedTreeFromChain?: MerkleTree | undefined);
929
+ }
930
+ /**
931
+ * Classify a relay error (response body text + HTTP status) into one of the
932
+ * structured error types above, or fall back to RelayInternalError.
933
+ *
934
+ * Order matters — more specific first. RootNotFoundError is handled by the
935
+ * caller separately (existing code path) since it carries `current_root` in
936
+ * the response body.
937
+ */
938
+ declare function classifyRelayError(responseText: string, httpStatus?: number): Error;
1532
939
  /**
1533
940
  * Shield Pool Program Error Codes
1534
941
  *
@@ -1578,1228 +985,871 @@ declare function createCloakError(error: unknown, _context: string): CloakError;
1578
985
  declare function formatErrorForLogging(error: unknown): string;
1579
986
 
1580
987
  /**
1581
- * Structured Logger for Cloak SDK
1582
- *
1583
- * Provides structured logging similar to Rust tracing format:
1584
- * 2026-01-23T00:51:46.489317Z INFO cloak::module: 📥 Message key=value
988
+ * UTXO (Unspent Transaction Output) Model
1585
989
  *
1586
- * Enable via:
1587
- * - SDK config: new CloakSDK({ debug: true })
1588
- * - Environment: CLOAK_DEBUG=1 or DEBUG=cloak:*
1589
- */
1590
- type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR";
1591
- /**
1592
- * Enable or disable debug logging globally
1593
- */
1594
- declare function setDebugMode(enabled: boolean): void;
1595
- /**
1596
- * Check if debug mode is enabled
990
+ * This module implements the UTXO model for the Cloak privacy protocol.
991
+ * UTXOs allow for:
992
+ * - Shield-to-shield transfers (privacy preserved)
993
+ * - Partial withdrawals (change stays shielded)
994
+ * - Combining multiple small deposits
995
+ * - Multi-token support (SOL + any SPL token)
1597
996
  */
1598
- declare function isDebugEnabled(): boolean;
997
+
998
+ declare const NATIVE_SOL_MINT: PublicKey;
1599
999
  /**
1600
- * Logger interface
1000
+ * UTXO Keypair for ownership verification
1001
+ *
1002
+ * Uses Poseidon hash for key derivation:
1003
+ * - privateKey: random field element (kept secret)
1004
+ * - publicKey: Poseidon(privateKey, 0) - can be shared
1601
1005
  */
1602
- interface Logger {
1603
- debug: (message: string, kvPairs?: Record<string, unknown>) => void;
1604
- info: (message: string, kvPairs?: Record<string, unknown>) => void;
1605
- warn: (message: string, kvPairs?: Record<string, unknown>) => void;
1606
- error: (message: string, kvPairs?: Record<string, unknown>) => void;
1006
+ interface UtxoKeypair {
1007
+ /** Private key (random 252-bit field element) */
1008
+ privateKey: bigint;
1009
+ /** Public key derived as Poseidon(privateKey, 0) */
1010
+ publicKey: bigint;
1607
1011
  }
1608
1012
  /**
1609
- * Create a logger for a specific module
1610
- *
1611
- * @param module - Module name (e.g., "cloak::sdk", "cloak::indexer")
1612
- * @returns Logger instance with debug, info, warn, error methods
1013
+ * UTXO represents an unspent transaction output
1613
1014
  *
1614
- * @example
1615
- * ```typescript
1616
- * const log = createLogger("cloak::deposit");
1617
- * log.info("Depositing", { amount: 100000000 });
1618
- * // Output: 2026-01-23T00:51:46.489000Z INFO cloak::deposit: Depositing amount=100000000
1619
- * ```
1015
+ * Contains all information needed to spend the UTXO:
1016
+ * - amount: Value in the smallest unit (lamports for SOL)
1017
+ * - keypair: Ownership keys
1018
+ * - blinding: Randomness for hiding
1019
+ * - mintAddress: Token type (SOL or SPL token)
1020
+ * - index: Leaf index in Merkle tree (set after deposit/creation)
1620
1021
  */
1621
- declare function createLogger(module: string): Logger;
1622
- /**
1623
- * Measure and return duration of an async operation
1624
- *
1625
- * @param _logger - Logger instance (reserved for future debug logging)
1626
- * @param _operation - Operation name (reserved for future debug logging)
1627
- * @param fn - Async function to measure
1628
- * @returns Result and duration in milliseconds
1629
- */
1630
- declare function withTiming<T>(_logger: Logger, _operation: string, fn: () => Promise<T>): Promise<{
1631
- result: T;
1632
- durationMs: number;
1633
- }>;
1634
- /**
1635
- * Format lamports as SOL with proper decimals
1636
- */
1637
- declare function formatSol(lamports: number | bigint): string;
1638
- /**
1639
- * Truncate a string (useful for signatures, hashes)
1640
- */
1641
- declare function truncate(str: string, len?: number): string;
1022
+ interface Utxo {
1023
+ /** Amount in the smallest unit (lamports for SOL, token units for SPL) */
1024
+ amount: bigint;
1025
+ /** Keypair for ownership */
1026
+ keypair: UtxoKeypair;
1027
+ /** Random blinding factor */
1028
+ blinding: bigint;
1029
+ /** Token mint address (NATIVE_SOL_MINT for SOL) */
1030
+ mintAddress: PublicKey;
1031
+ /** Leaf index in Merkle tree (set after the UTXO is in the tree) */
1032
+ index?: number;
1033
+ /** Commitment hash (computed from amount, pubkey, blinding, mint) */
1034
+ commitment?: bigint;
1035
+ /** Nullifier hash (computed when spending) */
1036
+ nullifier?: bigint;
1037
+ /**
1038
+ * Sibling commitment at level 0 of the Merkle tree.
1039
+ * Required for Merkle proofs since siblings aren't stored on-chain.
1040
+ * This is the commitment of the other output in the same transaction.
1041
+ */
1042
+ siblingCommitment?: bigint;
1043
+ }
1642
1044
  /**
1643
- * Default SDK logger instance
1045
+ * Encrypted UTXO note for recipient discovery
1046
+ *
1047
+ * When creating an output UTXO for someone else, the data is encrypted
1048
+ * so only the recipient can discover and spend it.
1644
1049
  */
1645
- declare const sdkLogger: Logger;
1646
-
1050
+ interface EncryptedNote {
1051
+ /** Encrypted UTXO data (ECIES/ChaCha20 encrypted) */
1052
+ ciphertext: Uint8Array;
1053
+ /** Ephemeral public key for ECDH */
1054
+ ephemeralPubkey: Uint8Array;
1055
+ /** MAC for integrity verification */
1056
+ mac: Uint8Array;
1057
+ }
1647
1058
  /**
1648
- * Result from submitting a withdrawal that includes the request ID
1649
- * for potential recovery/resume scenarios
1059
+ * Transaction parameters for a UTXO transaction
1650
1060
  */
1651
- interface WithdrawSubmissionResult {
1652
- /** The relay request ID - persist this for recovery */
1653
- requestId: string;
1654
- /** The final transaction signature */
1655
- signature: string;
1061
+ interface TransactParams {
1062
+ /** Input UTXOs to spend (max 2) */
1063
+ inputUtxos: Utxo[];
1064
+ /** Output UTXOs to create (max 2) */
1065
+ outputUtxos: Utxo[];
1066
+ /** External recipient for withdrawal (optional) */
1067
+ recipient?: PublicKey;
1068
+ /** Net external amount: positive = deposit, negative = withdraw */
1069
+ externalAmount?: bigint;
1070
+ /** Depositor address (for deposits, the source of funds) */
1071
+ depositor?: PublicKey;
1656
1072
  }
1657
1073
  /**
1658
- * Relay Service Client
1659
- *
1660
- * Handles submission of withdrawal transactions through a relay service
1661
- * that pays for transaction fees and submits the transaction on-chain.
1074
+ * Result from a UTXO transaction
1662
1075
  */
1663
- declare class RelayService {
1664
- private baseUrl;
1665
- /**
1666
- * Create a new Relay Service client
1667
- *
1668
- * @param baseUrl - Relay service base URL
1669
- */
1670
- constructor(baseUrl: string);
1671
- /**
1672
- * Submit a withdrawal transaction via relay
1673
- *
1674
- * The relay service will validate the proof, pay for transaction fees,
1675
- * and submit the transaction on-chain.
1676
- *
1677
- * @param params - Withdrawal parameters
1678
- * @param onStatusUpdate - Optional callback for status updates
1679
- * @param onRequestId - CRITICAL: Callback when request_id is received.
1680
- * Persist this ID to recover if browser crashes during polling.
1681
- * @returns Transaction signature when completed
1682
- *
1683
- * @example
1684
- * ```typescript
1685
- * const signature = await relay.submitWithdraw({
1686
- * proof: proofHex,
1687
- * publicInputs: { root, nf, outputs_hash, amount },
1688
- * outputs: [{ recipient: addr, amount: lamports }],
1689
- * feeBps: 50
1690
- * }, (status) => console.log(`Status: ${status}`),
1691
- * (requestId) => localStorage.setItem('pending_withdraw', requestId));
1692
- * console.log(`Transaction: ${signature}`);
1693
- * ```
1694
- */
1695
- submitWithdraw(params: {
1696
- proof: string;
1697
- publicInputs: {
1698
- root: string;
1699
- nf: string;
1700
- outputs_hash: string;
1701
- amount: number;
1702
- };
1703
- outputs: Array<{
1704
- recipient: string;
1705
- amount: number;
1706
- }>;
1707
- feeBps: number;
1708
- metadataBundle?: EncryptedMetadataBundle;
1709
- }, onStatusUpdate?: (status: string) => void, onRequestId?: (requestId: string) => void): Promise<string>;
1710
- /**
1711
- * Resume polling for a withdrawal that was previously started
1712
- *
1713
- * Use this after page reload to check status of a pending withdrawal.
1714
- * The requestId should have been persisted via the onRequestId callback.
1715
- *
1716
- * @param requestId - Request ID from a previous submitWithdraw call
1717
- * @param onStatusUpdate - Optional callback for status updates
1718
- * @returns Transaction signature if completed, null if still pending/failed
1719
- *
1720
- * @example
1721
- * ```typescript
1722
- * // On page load, check for pending withdrawal
1723
- * const pendingId = localStorage.getItem('pending_withdraw');
1724
- * if (pendingId) {
1725
- * const result = await relay.resumeWithdraw(pendingId);
1726
- * if (result.status === 'completed') {
1727
- * console.log('Withdrawal completed:', result.signature);
1728
- * localStorage.removeItem('pending_withdraw');
1729
- * }
1730
- * }
1731
- * ```
1732
- */
1733
- resumeWithdraw(requestId: string, onStatusUpdate?: (status: string) => void): Promise<{
1734
- status: 'completed' | 'failed' | 'pending';
1735
- signature?: string;
1736
- error?: string;
1737
- }>;
1738
- /**
1739
- * Poll for withdrawal completion
1740
- *
1741
- * @param requestId - Request ID from relay service
1742
- * @param onStatusUpdate - Optional callback for status updates
1743
- * @returns Transaction signature when completed
1744
- */
1745
- private pollForCompletion;
1076
+ interface TransactResult {
1077
+ /** Transaction signature */
1078
+ signature: string;
1079
+ /** Nullifiers of spent inputs */
1080
+ inputNullifiers: bigint[];
1081
+ /** Commitments of created outputs */
1082
+ outputCommitments: bigint[];
1083
+ /** New Merkle root after the transaction */
1084
+ newRoot: string;
1085
+ /** Merkle tree indices where output commitments were inserted */
1086
+ commitmentIndices: [number, number];
1746
1087
  /**
1747
- * Submit a swap transaction via relay
1748
- *
1749
- * Similar to submitWithdraw but includes swap parameters for token swaps.
1750
- * The relay service will validate the proof, execute the swap, pay for fees,
1751
- * and submit the transaction on-chain.
1752
- *
1753
- * @param params - Swap parameters
1754
- * @param onStatusUpdate - Optional callback for status updates
1755
- * @returns Transaction signature when completed
1756
- *
1757
- * @example
1758
- * ```typescript
1759
- * const signature = await relay.submitSwap({
1760
- * proof: proofHex,
1761
- * publicInputs: { root, nf, outputs_hash, amount },
1762
- * outputs: [{ recipient: addr, amount: lamports }],
1763
- * feeBps: 50,
1764
- * swap: {
1765
- * output_mint: tokenMint.toBase58(),
1766
- * slippage_bps: 100,
1767
- * min_output_amount: minAmount
1768
- * }
1769
- * }, (status) => console.log(`Status: ${status}`));
1770
- * console.log(`Transaction: ${signature}`);
1771
- * ```
1088
+ * Sibling commitments for each output.
1089
+ * For output[0], sibling is output[1] and vice versa.
1090
+ * These are needed for Merkle proofs since siblings aren't stored on-chain.
1772
1091
  */
1773
- submitSwap(params: {
1774
- proof: string;
1775
- publicInputs: {
1776
- root: string;
1777
- nf: string;
1778
- outputs_hash: string;
1779
- amount: number;
1780
- };
1781
- outputs: Array<{
1782
- recipient: string;
1783
- amount: number;
1784
- }>;
1785
- feeBps: number;
1786
- swap: {
1787
- output_mint: string;
1788
- slippage_bps: number;
1789
- min_output_amount: number;
1790
- };
1791
- metadataBundle?: EncryptedMetadataBundle;
1792
- }, onStatusUpdate?: (status: string) => void, onRequestId?: (requestId: string) => void): Promise<string>;
1092
+ siblingCommitments: [bigint, bigint];
1793
1093
  /**
1794
- * Get transaction status
1795
- *
1796
- * @param requestId - Request ID from previous submission
1797
- * @returns Current status
1798
- *
1799
- * @example
1800
- * ```typescript
1801
- * const status = await relay.getStatus(requestId);
1802
- * console.log(`Status: ${status.status}`);
1803
- * if (status.status === 'completed') {
1804
- * console.log(`TX: ${status.txId}`);
1805
- * }
1806
- * ```
1094
+ * Left sibling from before the transaction (for odd-index handling).
1095
+ * When the first output ends up at an odd index due to concurrent access,
1096
+ * its sibling at index-1 comes from a previous transaction.
1097
+ * This value captures subtrees[0] before our transaction to enable
1098
+ * correct Merkle proof computation for odd indices.
1807
1099
  */
1808
- getStatus(requestId: string): Promise<TxStatus>;
1100
+ preTransactionLeftSibling?: bigint;
1809
1101
  /**
1810
- * Register viewing key with relay for compliance decryption.
1811
- * This is a one-time operation - the viewing key is stored by the relay.
1812
- *
1813
- * The user must sign a one-time relay challenge proving wallet ownership.
1814
- *
1815
- * @param userPubkey - User's wallet public key (base58)
1816
- * @param viewingKey - Viewing key private key (32 bytes, hex encoded)
1817
- * @param signMessage - Function to sign challenge message bytes
1818
- *
1819
- * @example
1820
- * ```typescript
1821
- * await relay.registerViewingKey(
1822
- * userPublicKey.toBase58(),
1823
- * viewingKeyHex,
1824
- * signatureBase64
1825
- * );
1826
- * ```
1102
+ * The actual output UTXOs created by this transaction.
1103
+ * These have all the data needed to spend them later (keypair, blinding, etc).
1104
+ * The UTXOs are already updated with their indices and sibling commitments.
1827
1105
  */
1828
- registerViewingKey(userPubkey: string, viewingKey: string, signMessage: (message: Uint8Array) => Promise<Uint8Array>): Promise<boolean>;
1106
+ outputUtxos: Utxo[];
1829
1107
  /**
1830
- * Fetch relay compliance master public key.
1831
- * DEPRECATED: This endpoint is no longer used
1108
+ * Whether the viewing key was registered with the relay for compliance.
1109
+ * True on first transaction when metadataBundle includes viewing_key.
1110
+ * Once registered, subsequent transactions don't need to send metadataBundle.
1832
1111
  */
1833
- getCompliancePublicKey(): Promise<Uint8Array>;
1112
+ viewingKeyRegistered?: boolean;
1834
1113
  /**
1835
- * Convert bytes to base64 string
1114
+ * The Merkle tree used/built for this transaction, with output commitments inserted.
1115
+ * Pass this as `cachedMerkleTree` in the next transaction to skip relay fetching
1116
+ * and avoid needing `waitForCommitmentIndex`.
1836
1117
  */
1837
- private bytesToBase64;
1118
+ merkleTree?: MerkleTree;
1838
1119
  /**
1839
- * Sleep utility
1120
+ * Address lookup table used for v0 transaction (when chain notes or risk oracle require it).
1121
+ * Pass this as `addressLookupTableAccounts` in the next deposit to avoid creating a new ALT.
1840
1122
  */
1841
- private sleep;
1842
- private computeViewingKeyIdentifier;
1123
+ addressLookupTableAccounts?: AddressLookupTableAccount[];
1843
1124
  }
1844
-
1845
- /**
1846
- * Risk Oracle Service
1847
- *
1848
- * Self-contained module for fetching Switchboard risk quote instructions
1849
- * using the Range Risk API. Eliminates the need for an external web endpoint
1850
- * (e.g. `/api/risk-quote`) by calling Switchboard directly from the SDK.
1851
- *
1852
- * Uses dynamic imports for @switchboard-xyz packages so they are only loaded
1853
- * when fetchRiskQuoteIx is actually called — not at SDK import time. This
1854
- * prevents the heavy Node.js-only Switchboard code from being bundled into
1855
- * browser builds (Next.js, etc.).
1856
- *
1857
- * Usage:
1858
- * const { instruction, queue } = await fetchRiskQuoteIx(connection, wallet, rangeApiKey);
1859
- */
1860
-
1861
- /**
1862
- * Fetch a Switchboard risk quote instruction directly from Range + Switchboard.
1863
- *
1864
- * This removes the need for an external `/api/risk-quote` endpoint. The SDK
1865
- * calls Switchboard's crossbar oracle network directly with the Range API key.
1866
- *
1867
- * @param connection - Solana RPC connection
1868
- * @param wallet - Depositor's public key (the wallet being risk-scored)
1869
- * @param rangeApiKey - Range.org API key for the risk score lookup
1870
- * @returns The Ed25519 sig-verify instruction to prepend at index 0, plus the queue pubkey
1871
- */
1872
- declare function fetchRiskQuoteIx(connection: Connection, wallet: PublicKey, rangeApiKey: string): Promise<{
1873
- instruction: TransactionInstruction;
1874
- queue: PublicKey;
1875
- }>;
1876
-
1877
- /**
1878
- * Encrypted Output Helpers
1879
- *
1880
- * Functions for creating encrypted outputs that enable note scanning
1881
- */
1882
-
1883
- /**
1884
- * Prepare encrypted output for scanning by wallet owner
1885
- *
1886
- * @param note - Note to encrypt
1887
- * @param cloakKeys - Wallet's Cloak keys (for self-encryption)
1888
- * @returns Base64-encoded encrypted output
1889
- */
1890
- declare function prepareEncryptedOutput(note: CloakNote, cloakKeys: CloakKeyPair): string;
1891
- /**
1892
- * Prepare encrypted output for a specific recipient
1893
- *
1894
- * @param note - Note to encrypt
1895
- * @param recipientPvkHex - Recipient's public view key (hex)
1896
- * @returns Base64-encoded encrypted output
1897
- */
1898
- declare function prepareEncryptedOutputForRecipient(note: CloakNote, recipientPvkHex: string): string;
1899
- /**
1900
- * Simple base64 encoding for v1.0 notes (no encryption)
1901
- *
1902
- * @param note - Note to encode
1903
- * @returns Base64-encoded note data
1904
- */
1905
- declare function encodeNoteSimple(note: CloakNote): string;
1906
-
1907
- /**
1908
- * Wallet Integration Helpers
1909
- *
1910
- * Helper functions for working with Solana Wallet Adapters
1911
- */
1912
-
1913
- /**
1914
- * Validate wallet is connected and has public key
1915
- */
1916
- declare function validateWalletConnected(wallet: WalletAdapter | Keypair): void;
1917
- /**
1918
- * Get public key from wallet or keypair
1919
- */
1920
- declare function getPublicKey(wallet: WalletAdapter | Keypair): PublicKey;
1921
- /**
1922
- * Send transaction using wallet adapter or keypair
1923
- */
1924
- declare function sendTransaction(transaction: Transaction, wallet: WalletAdapter | Keypair, connection: Connection, options?: SendOptions): Promise<string>;
1925
- /**
1926
- * Sign transaction using wallet adapter or keypair
1927
- */
1928
- declare function signTransaction<T extends Transaction>(transaction: T, wallet: WalletAdapter | Keypair): Promise<T>;
1929
- /**
1930
- * Create a keypair adapter for testing
1931
- */
1932
- declare function keypairToAdapter(keypair: Keypair): WalletAdapter;
1933
-
1934
- /**
1935
- * Create a deposit instruction
1936
- *
1937
- * Deposits SOL into the Cloak protocol by creating a commitment.
1938
- *
1939
- * Instruction format:
1940
- * - Byte 0: Discriminant (0x01 for Deposit, per ShieldPoolInstruction enum)
1941
- * - Bytes 1-8: Amount (u64, little-endian)
1942
- * - Bytes 9-40: Commitment (32 bytes)
1943
- *
1944
- * @param params - Deposit parameters
1945
- * @returns Transaction instruction
1946
- *
1947
- * @example
1948
- * ```typescript
1949
- * const instruction = createDepositInstruction({
1950
- * programId: CLOAK_PROGRAM_ID,
1951
- * payer: wallet.publicKey,
1952
- * pool: POOL_ADDRESS,
1953
- * commitments: COMMITMENTS_ADDRESS,
1954
- * amount: 1_000_000_000, // 1 SOL
1955
- * commitment: commitmentBytes
1956
- * });
1957
- * ```
1958
- */
1959
- declare function createDepositInstruction(params: {
1960
- programId: PublicKey;
1961
- payer: PublicKey;
1962
- pool: PublicKey;
1963
- merkleTree: PublicKey;
1964
- amount: number;
1965
- commitment: Uint8Array;
1966
- }): TransactionInstruction;
1967
1125
  /**
1968
- * Deposit instruction parameters for type safety
1126
+ * Generate a random field element suitable for BN254
1969
1127
  */
1970
- interface DepositInstructionParams {
1971
- programId: PublicKey;
1972
- payer: PublicKey;
1973
- pool: PublicKey;
1974
- merkleTree: PublicKey;
1975
- amount: number;
1976
- commitment: Uint8Array;
1977
- }
1128
+ declare function randomFieldElement(): bigint;
1978
1129
  /**
1979
- * Validate deposit instruction parameters
1130
+ * Generate a new UTXO keypair
1980
1131
  *
1981
- * @param params - Parameters to validate
1982
- * @throws Error if invalid
1132
+ * @returns A new keypair with random private key and derived public key
1983
1133
  */
1984
- declare function validateDepositParams(params: DepositInstructionParams): void;
1985
-
1134
+ declare function generateUtxoKeypair(): Promise<UtxoKeypair>;
1986
1135
  /**
1987
- * Program Derived Address (PDA) utilities for Shield Pool
1136
+ * Derive a deterministic UTXO keypair from a wallet's spend key.
1137
+ * Use this for deposits and change outputs so all chain notes use the same nk,
1138
+ * enabling the compliance scan (scanTransactions + toComplianceReport) to decrypt them.
1988
1139
  *
1989
- * These functions derive deterministic addresses from the program ID and seeds.
1990
- * This matches the behavior in tooling/test/src/shared.rs::get_pda_addresses()
1140
+ * @param skSpend - 32-byte spend key (from wallet key derivation)
1141
+ * @returns UtxoKeypair with deterministic private/public keys
1991
1142
  */
1992
-
1993
- interface ShieldPoolPDAs {
1994
- pool: PublicKey;
1995
- merkleTree: PublicKey;
1996
- vaultAuthority: PublicKey;
1997
- treasury: PublicKey;
1998
- }
1143
+ declare function deriveUtxoKeypairFromSpendKey(skSpend: Uint8Array): Promise<UtxoKeypair>;
1999
1144
  /**
2000
- * Derive all Shield Pool PDAs from program ID + pool mint
2001
- *
2002
- * Seeds match the on-chain mint-scoped program:
2003
- * - pool: [b"pool", mint]
2004
- * - merkle_tree: [b"merkle_tree", mint]
2005
- * - treasury: [b"treasury", mint]
2006
- * - vault_authority: [b"vault_authority", mint]
1145
+ * Derive public key from private key
2007
1146
  *
2008
- * @param programId - Optional program ID (defaults to CLOAK_PROGRAM_ID)
2009
- * @param mint - Pool mint (defaults to NATIVE_SOL_MINT / WSOL sentinel)
1147
+ * @param privateKey The private key
1148
+ * @returns The corresponding public key
2010
1149
  */
2011
- declare function getShieldPoolPDAs(programId?: PublicKey, mint?: PublicKey): ShieldPoolPDAs;
1150
+ declare function derivePublicKey(privateKey: bigint): Promise<bigint>;
2012
1151
  /**
2013
- * Derive the nullifier PDA for a specific pool + nullifier hash.
2014
- *
2015
- * With the new PDA-per-nullifier design, each nullifier has its own PDA
2016
- * instead of being stored in a shared shard. The PDA is created when
2017
- * the nullifier is used during withdrawal.
2018
- *
2019
- * Seeds: ["nullifier", pool_pubkey, nullifier_hash]
1152
+ * Create a new UTXO
2020
1153
  *
2021
- * @param poolPubkey - Pool PDA that scopes nullifier domain
2022
- * @param nullifier - 32-byte nullifier hash
2023
- * @param programId - Optional program ID (defaults to CLOAK_PROGRAM_ID)
2024
- * @returns [PublicKey, bump] - The nullifier PDA and its bump seed
1154
+ * @param amount Amount in smallest unit
1155
+ * @param keypair Owner's keypair
1156
+ * @param mintAddress Token mint (defaults to native SOL)
1157
+ * @returns A new UTXO (commitment will be computed)
2025
1158
  */
2026
- declare function getNullifierPDA(poolPubkey: PublicKey, nullifier: Uint8Array | Buffer, programId?: PublicKey): [PublicKey, number];
1159
+ declare function createUtxo(amount: bigint, keypair: UtxoKeypair, mintAddress?: PublicKey): Promise<Utxo>;
2027
1160
  /**
2028
- * Derive the swap state PDA for a given pool + nullifier.
1161
+ * Create a zero/padding UTXO
2029
1162
  *
2030
- * Seeds: ["swap_state", pool_pubkey, nullifier_hash]
1163
+ * Zero UTXOs are used as padding when we need fewer than 2 inputs/outputs.
1164
+ * They have zero amount and don't require valid Merkle proofs.
1165
+ * Uses salt-based keypair (privateKey = salt) with blinding=0 to match the circuit
1166
+ * test. By default, salt is random to avoid cross-process collisions under concurrency.
1167
+ * Callers can pass an explicit salt for deterministic behavior.
2031
1168
  *
2032
- * @param poolPubkey - Pool PDA used for swap state domain separation
2033
- * @param nullifier - 32-byte nullifier hash
2034
- * @param programId - Optional program ID (defaults to CLOAK_PROGRAM_ID)
2035
- * @returns [PublicKey, bump] - The swap state PDA and its bump seed
1169
+ * @param mintAddress Token mint (defaults to native SOL)
1170
+ * @param salt Optional salt; if omitted, uses timestamp+counter for uniqueness
2036
1171
  */
2037
- declare function getSwapStatePDA(poolPubkey: PublicKey, nullifier: Uint8Array | Buffer, programId?: PublicKey): [PublicKey, number];
2038
-
1172
+ declare function createZeroUtxo(mintAddress?: PublicKey, salt?: bigint): Promise<Utxo>;
2039
1173
  /**
2040
- * On-chain Merkle proof computation
1174
+ * Convert PublicKey to field element for circuit
2041
1175
  *
2042
- * Computes Merkle proofs directly from on-chain tree state,
2043
- * eliminating the need for an indexer for proof generation.
1176
+ * @param pubkey Solana PublicKey
1177
+ * @returns Field element representation
2044
1178
  */
2045
-
1179
+ declare function pubkeyToFieldElement(pubkey: PublicKey): bigint;
2046
1180
  /**
2047
- * Merkle proof result
1181
+ * Compute the commitment for a UTXO
1182
+ *
1183
+ * commitment = Poseidon(amount, pubkey, blinding, mintAddress)
1184
+ *
1185
+ * @param utxo The UTXO to compute commitment for
1186
+ * @returns The commitment as a bigint
2048
1187
  */
2049
- interface OnchainMerkleProof {
2050
- pathElements: string[];
2051
- pathIndices: number[];
2052
- root: string;
2053
- }
2054
- interface MerkleTreeState {
2055
- nextIndex: number;
2056
- root: string;
2057
- subtrees: string[];
2058
- }
1188
+ declare function computeCommitment(utxo: Utxo): Promise<bigint>;
2059
1189
  /**
2060
- * Read the Merkle tree state from on-chain account
2061
- * @param forceConfirmed - If true, forces "confirmed" commitment level for fresh data
2062
- * @param relayUrl - Optional relay URL to query for current root (bypasses RPC caching)
2063
- * @param mint - Optional pool mint used when querying relay /merkle-root
1190
+ * Compute the signature for nullifier derivation
1191
+ *
1192
+ * signature = Poseidon(privateKey, commitment, pathIndex)
1193
+ *
1194
+ * @param privateKey Owner's private key
1195
+ * @param commitment The UTXO commitment
1196
+ * @param pathIndex Merkle tree path index
1197
+ * @returns The signature
2064
1198
  */
2065
- declare function readMerkleTreeState(connection: Connection, merkleTreePDA: PublicKey, forceConfirmed?: boolean, relayUrl?: string,
2066
- /** When true, skip relay root and use chain directly (avoids stale relay root causing ProofInvalid) */
2067
- skipRelayRoot?: boolean, mint?: PublicKey): Promise<MerkleTreeState>;
1199
+ declare function computeSignature(privateKey: bigint, commitment: bigint, pathIndex: bigint): Promise<bigint>;
2068
1200
  /**
2069
- * Compute Merkle proof for a leaf at a given index using on-chain state
1201
+ * Compute the nullifier for a UTXO
2070
1202
  *
2071
- * This works by:
2072
- * 1. Reading the subtrees (frontier) from the on-chain account
2073
- * 2. For each level, determining the sibling:
2074
- * - If index is odd: sibling is the stored subtree (left sibling)
2075
- * - If index is even: sibling is either a subsequent subtree or zero value
1203
+ * nullifier = Poseidon(commitment, pathIndex, signature)
2076
1204
  *
2077
- * Note: This works best for recent leaves where siblings are zero values.
2078
- * For older leaves with non-zero siblings that aren't stored in subtrees,
2079
- * an indexer may still be needed.
1205
+ * The nullifier is a unique identifier that gets recorded on-chain
1206
+ * to prevent double-spending.
2080
1207
  *
2081
- * @param connection - Solana connection
2082
- * @param merkleTreePDA - PDA of the merkle tree account
2083
- * @param leafIndex - Index of the leaf to compute proof for
2084
- * @param forceConfirmed - If true, forces "confirmed" commitment level for fresh data
2085
- * @returns Merkle proof with path elements and indices
1208
+ * @param utxo The UTXO being spent
1209
+ * @returns The nullifier
2086
1210
  */
2087
- declare function computeProofFromChain(connection: Connection, merkleTreePDA: PublicKey, leafIndex: number, forceConfirmed?: boolean): Promise<OnchainMerkleProof>;
1211
+ declare function computeNullifier(utxo: Utxo): Promise<bigint>;
2088
1212
  /**
2089
- * Compute proof for the most recently inserted leaf
2090
- *
2091
- * This is the most reliable case - immediately after your deposit,
2092
- * all siblings to the right are zero values, and siblings to the left
2093
- * are stored in the subtrees.
1213
+ * Serialize a UTXO to bytes for encryption
2094
1214
  */
2095
- declare function computeProofForLatestDeposit(connection: Connection, merkleTreePDA: PublicKey): Promise<OnchainMerkleProof & {
2096
- leafIndex: number;
2097
- }>;
2098
-
1215
+ declare function serializeUtxo(utxo: Utxo): Uint8Array;
2099
1216
  /**
2100
- * Merkle Tree implementation for computing proofs
2101
- *
2102
- * This tree stores all leaves and can compute the correct Merkle path
2103
- * for any leaf index.
1217
+ * Deserialize a UTXO from bytes
2104
1218
  */
2105
- declare const MERKLE_TREE_HEIGHT = 32;
1219
+ declare function deserializeUtxo(bytes: Uint8Array): Promise<Utxo>;
2106
1220
  /**
2107
- * Merkle Tree class that stores all leaves and computes proofs
1221
+ * Convert bigint to hex string (64 chars, padded)
2108
1222
  */
2109
- declare class MerkleTree {
2110
- private levels;
2111
- private capacity;
2112
- private layers;
2113
- private zeros;
2114
- private poseidon;
2115
- /**
2116
- * Create a new Merkle tree
2117
- * @param levels Number of levels in the tree (height)
2118
- * @param leaves Initial leaves to insert
2119
- * @param zeros Precomputed zero values for each level
2120
- * @param poseidon Pre-initialized Poseidon instance (avoids async in constructor)
2121
- */
2122
- constructor(levels: number, leaves: bigint[], zeros: bigint[], poseidon: any);
2123
- /**
2124
- * Create a new MerkleTree with async initialization
2125
- */
2126
- static create(levels?: number, leaves?: bigint[]): Promise<MerkleTree>;
2127
- /**
2128
- * Rebuild the tree from the leaves (synchronous — poseidon must be initialized)
2129
- */
2130
- private _rebuildSync;
2131
- /**
2132
- * Get the tree root
2133
- */
2134
- root(): bigint;
2135
- /**
2136
- * Get the number of leaves in the tree
2137
- */
2138
- get length(): number;
2139
- /**
2140
- * Insert a new leaf into the tree
2141
- */
2142
- insert(leaf: bigint): void;
2143
- /**
2144
- * Insert multiple leaves
2145
- */
2146
- bulkInsert(leaves: bigint[]): void;
2147
- /**
2148
- * Update the path from a leaf to the root
2149
- */
2150
- private _updatePath;
2151
- /**
2152
- * Get the Merkle path for a leaf at the given index
2153
- * @param index Leaf index
2154
- * @returns pathElements and pathIndices for the proof
2155
- */
2156
- path(index: number): {
2157
- pathElements: bigint[];
2158
- pathIndices: number;
2159
- };
2160
- /**
2161
- * Find the index of a leaf in the tree
2162
- * @param leaf Leaf value to find
2163
- * @returns Index if found, -1 otherwise
2164
- */
2165
- indexOf(leaf: bigint): number;
2166
- /**
2167
- * Get all leaves
2168
- */
2169
- leaves(): bigint[];
2170
- }
1223
+ declare function bigintToHex(value: bigint): string;
2171
1224
  /**
2172
- * Build a MerkleTree from commitment values
2173
- * @param commitments Array of commitment bigints
2174
- * @param height Tree height (default: 32)
1225
+ * Convert hex string to bigint
2175
1226
  */
2176
- declare function buildMerkleTree(commitments: bigint[], height?: number): Promise<MerkleTree>;
2177
-
1227
+ declare function hexToBigint(hex: string): bigint;
2178
1228
  /**
2179
- * Relay client utilities for fetching data from the relay service
1229
+ * Convert bigint to 32-byte array (big-endian for circuits)
2180
1230
  */
2181
-
2182
- interface CommitmentEntry {
2183
- index: number;
2184
- commitment: string;
2185
- }
2186
- interface CommitmentsResponse {
2187
- commitments: CommitmentEntry[];
2188
- count: number;
2189
- target_reached?: boolean;
2190
- canonical?: boolean;
2191
- canonical_reason?: string | null;
2192
- }
2193
- interface FetchCommitmentsOptions {
2194
- mint?: PublicKey;
2195
- timeoutMs?: number;
2196
- maxRetries?: number;
2197
- sync?: boolean;
2198
- repair?: boolean;
2199
- requireTarget?: boolean;
2200
- requireCanonical?: boolean;
2201
- fromIndex?: number;
2202
- limit?: number;
2203
- waitForIndex?: number;
2204
- }
1231
+ declare function bigintToBytes32(value: bigint): Uint8Array;
2205
1232
  /**
2206
- * Fetch all commitments from the relay
2207
- * @param relayUrl Base URL of the relay service (may include cache-busting query params)
2208
- * @param options Timeout and retry options
2209
- * @returns Array of commitment entries
1233
+ * Check if two UTXOs are equal (same commitment)
2210
1234
  */
2211
- declare function fetchCommitments(relayUrl: string, options?: FetchCommitmentsOptions): Promise<CommitmentEntry[]>;
1235
+ declare function utxoEquals(a: Utxo, b: Utxo): Promise<boolean>;
2212
1236
  /**
2213
- * Build a Merkle tree from relay commitments
2214
- * @param relayUrl Base URL of the relay service
2215
- * @param options When sync is true, triggers on-demand sync on relay before fetching (for freshness when relay is behind chain)
2216
- * @returns MerkleTree instance with all commitments loaded
2217
- *
2218
- * Supports sparse indices by zero-filling missing leaves, including missing prefixes.
2219
- * Non-contiguous gaps (e.g. 0,1,2,3,22,23,30,31,32,33) are reconstructed with
2220
- * zero leaves to match the on-chain Merkle tree structure.
1237
+ * Calculate total amount from an array of UTXOs
2221
1238
  */
2222
- declare function buildMerkleTreeFromRelay(relayUrl: string, options?: {
2223
- mint?: PublicKey;
2224
- sync?: boolean;
2225
- repair?: boolean;
2226
- requireTarget?: boolean;
2227
- requireCanonical?: boolean;
2228
- timeoutMs?: number;
2229
- maxRetries?: number;
2230
- waitForIndex?: number;
2231
- }): Promise<MerkleTree>;
2232
- declare function buildMerkleTreeFromChain(connection: Connection, programId: PublicKey, merkleTree: PublicKey, onProgress?: (message: string) => void): Promise<MerkleTree>;
1239
+ declare function sumUtxoAmounts(utxos: Utxo[]): bigint;
2233
1240
  /**
2234
- * Pre-flight root validation
2235
- *
2236
- * Validates that a Merkle root is still valid before generating a proof.
2237
- * This prevents wasted time generating proofs that will fail on-chain.
1241
+ * Select UTXOs to cover a target amount
2238
1242
  *
2239
- * @param relayUrl Relay service URL
2240
- * @param rootHex Root to validate (hex string, with or without 0x prefix)
2241
- * @returns Validation result with status and helpful message
1243
+ * @param available Available UTXOs (sorted by amount descending)
1244
+ * @param targetAmount Target amount to cover
1245
+ * @returns Selected UTXOs that cover the target, or null if insufficient
2242
1246
  */
2243
- declare function validateRoot(relayUrl: string, rootHex: string): Promise<{
2244
- valid: boolean;
2245
- currentRoot?: string;
2246
- message: string;
2247
- isRecent: boolean;
2248
- }>;
1247
+ declare function selectUtxos(available: Utxo[], targetAmount: bigint): Utxo[] | null;
1248
+
2249
1249
  /**
2250
- * Wait for root to be valid
1250
+ * UTXO verification reconcile local UTXO state with on-chain nullifier state.
2251
1251
  *
2252
- * Polls the relay until the given root is accepted or timeout.
2253
- * Useful when you know a transaction is being processed but root hasn't updated yet.
1252
+ * Shipped in 0.1.5 as the structured building block for self-repair flows.
1253
+ * The web app previously composed this out of lower-level SDK primitives
1254
+ * (`computeNullifier` + `getNullifierPDA` + `getMultipleAccountsInfo`); now
1255
+ * it's a first-class SDK export so callers don't re-implement the same
1256
+ * ~40 lines across tools.
2254
1257
  *
2255
- * @param relayUrl Relay service URL
2256
- * @param rootHex Root to wait for
2257
- * @param timeoutMs Maximum time to wait (default: 30000ms)
2258
- * @param pollIntervalMs Polling interval (default: 1000ms)
2259
- * @returns Whether root became valid in time
1258
+ * Two entry points:
1259
+ *
1260
+ * 1. `verifyUtxos(utxos, conn, programId)` pure function. Takes a UTXO
1261
+ * set, does a single batched on-chain lookup of the derived nullifier
1262
+ * PDAs, returns `{ spent, unspent }`. Caller decides what to do.
1263
+ *
1264
+ * 2. `preflightNullifiers(utxos, conn, programId)` — thin wrapper that
1265
+ * throws `UtxoAlreadySpentError` if ANY input is already spent.
1266
+ * Intended for use at the top of spend entry points (transact,
1267
+ * fullWithdraw, swap) so the SDK fails fast with a structured error
1268
+ * instead of burning proof-gen + relay retries on a doomed tx.
1269
+ *
1270
+ * Root-caused from a production incident 2026-04-24: a user's cloak.ag
1271
+ * backup contained a UTXO that had been spent via another session. The
1272
+ * recovery tool generated a valid proof against the correct merkle tree
1273
+ * and submitted it; relay's simulation returned 0x1020 DoubleSpend after
1274
+ * 6 retries over ~60s. Pre-flight catches this class in ~200ms.
2260
1275
  */
2261
- declare function waitForRoot(relayUrl: string, rootHex: string, timeoutMs?: number, pollIntervalMs?: number): Promise<{
2262
- success: boolean;
2263
- currentRoot?: string;
2264
- waitedMs: number;
2265
- }>;
1276
+
1277
+ /** Result of a batch verification — disjoint partition of the input set. */
1278
+ interface VerifyUtxosResult {
1279
+ /** Inputs whose nullifier PDA exists on-chain (already spent). */
1280
+ spent: Utxo[];
1281
+ /** Inputs whose nullifier PDA is absent on-chain (safe to spend). */
1282
+ unspent: Utxo[];
1283
+ /** Inputs we couldn't check — missing commitment / index / mint. */
1284
+ skipped: Utxo[];
1285
+ }
2266
1286
  /**
2267
- * Smart pre-flight check with automatic retry suggestion
1287
+ * Check the on-chain nullifier PDA for each UTXO and partition by state.
2268
1288
  *
2269
- * Checks root validity and provides actionable guidance:
2270
- * - If root is current: proceed
2271
- * - If root is old: suggest fetching fresh Merkle proofs
2272
- * - If root is very old: suggest full tree rebuild
1289
+ * Only UTXOs with `commitment`, `mintAddress`, AND a non-null keypair can
1290
+ * be verified the nullifier derivation needs all three. UTXOs lacking any
1291
+ * of these go into `skipped` and the caller must decide how to treat them.
1292
+ * (In practice a missing `commitment` means the UTXO hasn't landed on-chain
1293
+ * yet — those can't be spent anyway.)
2273
1294
  *
2274
- * @param relayUrl Relay service URL
2275
- * @param rootHex Root to check
2276
- * @returns Action recommendation
1295
+ * Uses a single batched `getMultipleAccountsInfo` call for efficiency —
1296
+ * one round-trip regardless of UTXO count (up to Solana's 100-key cap,
1297
+ * beyond which we page).
1298
+ *
1299
+ * Typical latency: ~150-300ms on a healthy RPC.
2277
1300
  */
2278
- declare function preflightCheck(relayUrl: string, rootHex: string): Promise<{
2279
- shouldProceed: boolean;
2280
- action: "proceed" | "refresh_proofs" | "rebuild_tree" | "wait";
2281
- message: string;
2282
- estimatedWaitMs?: number;
2283
- }>;
1301
+ declare function verifyUtxos(utxos: Utxo[], connection: Connection, programId: PublicKey, commitment?: "processed" | "confirmed" | "finalized"): Promise<VerifyUtxosResult>;
1302
+ /**
1303
+ * Pre-flight gate: throw `UtxoAlreadySpentError` if any input is already
1304
+ * spent on-chain. Called at the top of spend entry points in core/transact.
1305
+ *
1306
+ * Browser-safe: uses one batched RPC call.
1307
+ */
1308
+ declare function preflightNullifiers(utxos: Utxo[], connection: Connection, programId: PublicKey, commitment?: "processed" | "confirmed" | "finalized"): Promise<void>;
2284
1309
 
2285
1310
  /**
2286
- * Direct Circom WASM Proof Generation
1311
+ * Structured Logger for Cloak SDK
2287
1312
  *
2288
- * This module provides direct proof generation using snarkjs and Circom WASM,
2289
- * matching the approach used in services-new/tests/src/proof.ts
1313
+ * Provides structured logging similar to Rust tracing format:
1314
+ * 2026-01-23T00:51:46.489317Z INFO cloak::module: 📥 Message key=value
2290
1315
  *
2291
- * This uses pinned S3-hosted circuit artifacts and does not require
2292
- * a backend prover service.
1316
+ * Enable via:
1317
+ * - SDK config: new CloakSDK({ debug: true })
1318
+ * - Environment: CLOAK_DEBUG=1 or DEBUG=cloak:*
2293
1319
  */
2294
-
1320
+ type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR";
2295
1321
  /**
2296
- * Default URL for fetching circuit artifacts
2297
- * Hosted in S3 and versioned by build.
1322
+ * Enable or disable debug logging globally
2298
1323
  */
2299
- declare const DEFAULT_CIRCUITS_URL = "https://cloak-circuits.s3.us-east-1.amazonaws.com/circuits/0.1.0";
2300
- interface WithdrawRegularInputs {
2301
- root: bigint;
2302
- nullifier: bigint;
2303
- outputs_hash: bigint;
2304
- public_amount: bigint;
2305
- amount: bigint;
2306
- leaf_index: bigint;
2307
- sk: [bigint, bigint];
2308
- r: [bigint, bigint];
2309
- pathElements: bigint[];
2310
- pathIndices: number[];
2311
- num_outputs: number;
2312
- out_addr: bigint[][];
2313
- out_amount: bigint[];
2314
- out_flags: number[];
2315
- var_fee: bigint;
2316
- rem: bigint;
2317
- }
2318
- interface WithdrawSwapInputs {
2319
- sk_spend: bigint;
2320
- r: bigint;
2321
- amount: bigint;
2322
- leaf_index: bigint;
2323
- path_elements: bigint[];
2324
- path_indices: number[];
2325
- root: bigint;
2326
- nullifier: bigint;
2327
- outputs_hash: bigint;
2328
- public_amount: bigint;
2329
- input_mint: bigint[];
2330
- output_mint: bigint[];
2331
- recipient_ata: bigint[];
2332
- min_output_amount: bigint;
2333
- var_fee: bigint;
2334
- rem: bigint;
2335
- }
2336
- interface ProofResult {
2337
- proof: Groth16Proof;
2338
- publicSignals: string[];
2339
- proofBytes: Uint8Array;
2340
- publicInputsBytes: Uint8Array;
1324
+ declare function setDebugMode(enabled: boolean): void;
1325
+ /**
1326
+ * Check if debug mode is enabled
1327
+ */
1328
+ declare function isDebugEnabled(): boolean;
1329
+ /**
1330
+ * Logger interface
1331
+ */
1332
+ interface Logger {
1333
+ debug: (message: string, kvPairs?: Record<string, unknown>) => void;
1334
+ info: (message: string, kvPairs?: Record<string, unknown>) => void;
1335
+ warn: (message: string, kvPairs?: Record<string, unknown>) => void;
1336
+ error: (message: string, kvPairs?: Record<string, unknown>) => void;
2341
1337
  }
2342
1338
  /**
2343
- * Generate Groth16 proof for regular withdrawal using Circom WASM
1339
+ * Create a logger for a specific module
2344
1340
  *
2345
- * This matches the approach in services-new/tests/src/proof.ts
1341
+ * @param module - Module name (e.g., "cloak::sdk", "cloak::indexer")
1342
+ * @returns Logger instance with debug, info, warn, error methods
2346
1343
  *
2347
- * @param inputs - Circuit inputs
2348
- * @param circuitsPath - Ignored. Proof generation always uses pinned S3 circuits.
1344
+ * @example
1345
+ * ```typescript
1346
+ * const log = createLogger("cloak::deposit");
1347
+ * log.info("Depositing", { amount: 100000000 });
1348
+ * // Output: 2026-01-23T00:51:46.489000Z INFO cloak::deposit: Depositing amount=100000000
1349
+ * ```
2349
1350
  */
2350
- declare function generateWithdrawRegularProof(inputs: WithdrawRegularInputs, circuitsPath: string): Promise<ProofResult>;
1351
+ declare function createLogger(module: string): Logger;
2351
1352
  /**
2352
- * Generate Groth16 proof for swap withdrawal using Circom WASM
2353
- *
2354
- * This matches the approach in services-new/tests/src/proof.ts
1353
+ * Measure and return duration of an async operation
2355
1354
  *
2356
- * @param inputs - Circuit inputs
2357
- * @param circuitsPath - Ignored. Proof generation always uses pinned S3 circuits.
1355
+ * @param _logger - Logger instance (reserved for future debug logging)
1356
+ * @param _operation - Operation name (reserved for future debug logging)
1357
+ * @param fn - Async function to measure
1358
+ * @returns Result and duration in milliseconds
2358
1359
  */
2359
- declare function generateWithdrawSwapProof(inputs: WithdrawSwapInputs, circuitsPath: string): Promise<ProofResult>;
1360
+ declare function withTiming<T>(_logger: Logger, _operation: string, fn: () => Promise<T>): Promise<{
1361
+ result: T;
1362
+ durationMs: number;
1363
+ }>;
2360
1364
  /**
2361
- * Check if circuits are available from the pinned S3 source.
1365
+ * Format lamports as SOL with proper decimals
2362
1366
  */
2363
- declare function areCircuitsAvailable(circuitsPath: string): Promise<boolean>;
1367
+ declare function formatSol(lamports: number | bigint): string;
2364
1368
  /**
2365
- * Get default circuits URL.
1369
+ * Truncate a string (useful for signatures, hashes)
2366
1370
  */
2367
- declare function getDefaultCircuitsPath(): Promise<string>;
1371
+ declare function truncate(str: string, len?: number): string;
2368
1372
  /**
2369
- * Pinned circuit artifact hashes (SHA-256).
2370
- *
2371
- * These hashes must be updated whenever the trusted circuit artifacts change.
1373
+ * Default SDK logger instance
2372
1374
  */
2373
- declare const EXPECTED_CIRCUIT_HASHES: {
2374
- withdraw_regular_wasm: string;
2375
- withdraw_regular_zkey: string;
2376
- withdraw_swap_wasm: string;
2377
- withdraw_swap_zkey: string;
2378
- };
1375
+ declare const sdkLogger: Logger;
1376
+
2379
1377
  /**
2380
- * Circuit verification result
1378
+ * Result from submitting a withdrawal that includes the request ID
1379
+ * for potential recovery/resume scenarios
2381
1380
  */
2382
- interface CircuitVerificationResult {
2383
- /** Whether verification passed */
2384
- valid: boolean;
2385
- /** Which circuit was checked */
2386
- circuit: 'withdraw_regular' | 'withdraw_swap';
2387
- /** Error message if verification failed */
2388
- error?: string;
2389
- computed?: {
2390
- wasm: string;
2391
- zkey: string;
2392
- };
2393
- expected?: {
2394
- wasm: string;
2395
- zkey: string;
2396
- };
1381
+ interface WithdrawSubmissionResult {
1382
+ /** The relay request ID - persist this for recovery */
1383
+ requestId: string;
1384
+ /** The final transaction signature */
1385
+ signature: string;
1386
+ }
1387
+ /**
1388
+ * Relay Service Client
1389
+ *
1390
+ * Handles submission of withdrawal transactions through a relay service
1391
+ * that pays for transaction fees and submits the transaction on-chain.
1392
+ */
1393
+ declare class RelayService {
1394
+ private baseUrl;
1395
+ /**
1396
+ * Create a new Relay Service client
1397
+ *
1398
+ * @param baseUrl - Relay service base URL
1399
+ */
1400
+ constructor(baseUrl: string);
1401
+ /**
1402
+ * Submit a withdrawal transaction via relay
1403
+ *
1404
+ * The relay service will validate the proof, pay for transaction fees,
1405
+ * and submit the transaction on-chain.
1406
+ *
1407
+ * @param params - Withdrawal parameters
1408
+ * @param onStatusUpdate - Optional callback for status updates
1409
+ * @param onRequestId - CRITICAL: Callback when request_id is received.
1410
+ * Persist this ID to recover if browser crashes during polling.
1411
+ * @returns Transaction signature when completed
1412
+ *
1413
+ * @example
1414
+ * ```typescript
1415
+ * const signature = await relay.submitWithdraw({
1416
+ * proof: proofHex,
1417
+ * publicInputs: { root, nf, outputs_hash, amount },
1418
+ * outputs: [{ recipient: addr, amount: lamports }],
1419
+ * feeBps: 50
1420
+ * }, (status) => console.log(`Status: ${status}`),
1421
+ * (requestId) => localStorage.setItem('pending_withdraw', requestId));
1422
+ * console.log(`Transaction: ${signature}`);
1423
+ * ```
1424
+ */
1425
+ submitWithdraw(params: {
1426
+ proof: string;
1427
+ publicInputs: {
1428
+ root: string;
1429
+ nf: string;
1430
+ outputs_hash: string;
1431
+ amount: number;
1432
+ };
1433
+ outputs: Array<{
1434
+ recipient: string;
1435
+ amount: number;
1436
+ }>;
1437
+ feeBps: number;
1438
+ metadataBundle?: EncryptedMetadataBundle;
1439
+ }, onStatusUpdate?: (status: string) => void, onRequestId?: (requestId: string) => void): Promise<string>;
1440
+ /**
1441
+ * Resume polling for a withdrawal that was previously started
1442
+ *
1443
+ * Use this after page reload to check status of a pending withdrawal.
1444
+ * The requestId should have been persisted via the onRequestId callback.
1445
+ *
1446
+ * @param requestId - Request ID from a previous submitWithdraw call
1447
+ * @param onStatusUpdate - Optional callback for status updates
1448
+ * @returns Transaction signature if completed, null if still pending/failed
1449
+ *
1450
+ * @example
1451
+ * ```typescript
1452
+ * // On page load, check for pending withdrawal
1453
+ * const pendingId = localStorage.getItem('pending_withdraw');
1454
+ * if (pendingId) {
1455
+ * const result = await relay.resumeWithdraw(pendingId);
1456
+ * if (result.status === 'completed') {
1457
+ * console.log('Withdrawal completed:', result.signature);
1458
+ * localStorage.removeItem('pending_withdraw');
1459
+ * }
1460
+ * }
1461
+ * ```
1462
+ */
1463
+ resumeWithdraw(requestId: string, onStatusUpdate?: (status: string) => void): Promise<{
1464
+ status: 'completed' | 'failed' | 'pending';
1465
+ signature?: string;
1466
+ error?: string;
1467
+ }>;
1468
+ /**
1469
+ * Poll for withdrawal completion
1470
+ *
1471
+ * @param requestId - Request ID from relay service
1472
+ * @param onStatusUpdate - Optional callback for status updates
1473
+ * @returns Transaction signature when completed
1474
+ */
1475
+ private pollForCompletion;
1476
+ /**
1477
+ * Submit a swap transaction via relay
1478
+ *
1479
+ * Similar to submitWithdraw but includes swap parameters for token swaps.
1480
+ * The relay service will validate the proof, execute the swap, pay for fees,
1481
+ * and submit the transaction on-chain.
1482
+ *
1483
+ * @param params - Swap parameters
1484
+ * @param onStatusUpdate - Optional callback for status updates
1485
+ * @returns Transaction signature when completed
1486
+ *
1487
+ * @example
1488
+ * ```typescript
1489
+ * const signature = await relay.submitSwap({
1490
+ * proof: proofHex,
1491
+ * publicInputs: { root, nf, outputs_hash, amount },
1492
+ * outputs: [{ recipient: addr, amount: lamports }],
1493
+ * feeBps: 50,
1494
+ * swap: {
1495
+ * output_mint: tokenMint.toBase58(),
1496
+ * slippage_bps: 100,
1497
+ * min_output_amount: minAmount
1498
+ * }
1499
+ * }, (status) => console.log(`Status: ${status}`));
1500
+ * console.log(`Transaction: ${signature}`);
1501
+ * ```
1502
+ */
1503
+ submitSwap(params: {
1504
+ proof: string;
1505
+ publicInputs: {
1506
+ root: string;
1507
+ nf: string;
1508
+ outputs_hash: string;
1509
+ amount: number;
1510
+ };
1511
+ outputs: Array<{
1512
+ recipient: string;
1513
+ amount: number;
1514
+ }>;
1515
+ feeBps: number;
1516
+ swap: {
1517
+ output_mint: string;
1518
+ slippage_bps: number;
1519
+ min_output_amount: number;
1520
+ };
1521
+ metadataBundle?: EncryptedMetadataBundle;
1522
+ }, onStatusUpdate?: (status: string) => void, onRequestId?: (requestId: string) => void): Promise<string>;
1523
+ /**
1524
+ * Get transaction status
1525
+ *
1526
+ * @param requestId - Request ID from previous submission
1527
+ * @returns Current status
1528
+ *
1529
+ * @example
1530
+ * ```typescript
1531
+ * const status = await relay.getStatus(requestId);
1532
+ * console.log(`Status: ${status.status}`);
1533
+ * if (status.status === 'completed') {
1534
+ * console.log(`TX: ${status.txId}`);
1535
+ * }
1536
+ * ```
1537
+ */
1538
+ getStatus(requestId: string): Promise<TxStatus>;
1539
+ /**
1540
+ * Register viewing key with relay for compliance decryption.
1541
+ * This is a one-time operation - the viewing key is stored by the relay.
1542
+ *
1543
+ * The user must sign a one-time relay challenge proving wallet ownership.
1544
+ *
1545
+ * @param userPubkey - User's wallet public key (base58)
1546
+ * @param viewingKey - Viewing key private key (32 bytes, hex encoded)
1547
+ * @param signMessage - Function to sign challenge message bytes
1548
+ *
1549
+ * @example
1550
+ * ```typescript
1551
+ * await relay.registerViewingKey(
1552
+ * userPublicKey.toBase58(),
1553
+ * viewingKeyHex,
1554
+ * signatureBase64
1555
+ * );
1556
+ * ```
1557
+ */
1558
+ registerViewingKey(userPubkey: string, viewingKey: string, signMessage: (message: Uint8Array) => Promise<Uint8Array>): Promise<boolean>;
1559
+ /**
1560
+ * Fetch relay compliance master public key.
1561
+ * DEPRECATED: This endpoint is no longer used
1562
+ */
1563
+ getCompliancePublicKey(): Promise<Uint8Array>;
1564
+ /**
1565
+ * Convert bytes to base64 string
1566
+ */
1567
+ private bytesToBase64;
1568
+ /**
1569
+ * Sleep utility
1570
+ */
1571
+ private sleep;
1572
+ private computeViewingKeyIdentifier;
2397
1573
  }
1574
+
2398
1575
  /**
2399
- * Verify circuit integrity by checking verification key hashes
2400
- *
2401
- * This function fetches the verification key and computes its hash,
2402
- * then compares against the expected hash embedded in the SDK.
2403
- *
2404
- * IMPORTANT: If the hashes don't match, the circuit may produce proofs
2405
- * that will be rejected by the on-chain verifier!
2406
- *
2407
- * @param circuitsPath - Ignored. Verification always uses pinned S3 circuits.
2408
- * @param circuit - Which circuit to verify ('withdraw_regular' or 'withdraw_swap')
2409
- * @returns Verification result
1576
+ * Risk Oracle Service
2410
1577
  *
2411
- * @example
2412
- * ```typescript
2413
- * const result = await verifyCircuitIntegrity('https://cloak-circuits.s3.us-east-1.amazonaws.com/circuits/0.1.0', 'withdraw_regular');
2414
- * if (!result.valid) {
2415
- * console.error('Circuit verification failed:', result.error);
2416
- * // Don't proceed with proof generation!
2417
- * }
2418
- * ```
2419
- */
2420
- declare function verifyCircuitIntegrity(circuitsPath: string, circuit: 'withdraw_regular' | 'withdraw_swap'): Promise<CircuitVerificationResult>;
2421
- /**
2422
- * Verify all circuits before use
1578
+ * Self-contained module for fetching Switchboard risk quote instructions
1579
+ * using the Range Risk API. Eliminates the need for an external web endpoint
1580
+ * (e.g. `/api/risk-quote`) by calling Switchboard directly from the SDK.
2423
1581
  *
2424
- * Call this at SDK initialization to ensure circuits are valid.
1582
+ * Uses dynamic imports for @switchboard-xyz packages so they are only loaded
1583
+ * when fetchRiskQuoteIx is actually called — not at SDK import time. This
1584
+ * prevents the heavy Node.js-only Switchboard code from being bundled into
1585
+ * browser builds (Next.js, etc.).
2425
1586
  *
2426
- * @param circuitsPath - Ignored. Verification always uses pinned S3 circuits.
2427
- * @returns Array of verification results (one per circuit)
1587
+ * Usage:
1588
+ * const { instruction, queue } = await fetchRiskQuoteIx(connection, wallet, rangeApiKey);
2428
1589
  */
2429
- declare function verifyAllCircuits(circuitsPath: string): Promise<CircuitVerificationResult[]>;
2430
1590
 
2431
1591
  /**
2432
- * Pending Operations Manager
2433
- *
2434
- * Utility for persisting pending deposit/withdrawal operations in browser storage.
2435
- * This enables recovery if the browser crashes or user navigates away mid-operation.
1592
+ * Fetch a Switchboard risk quote instruction directly from Range + Switchboard.
2436
1593
  *
2437
- * IMPORTANT: This uses localStorage by default which has security implications.
2438
- * Notes contain sensitive spending keys - consider using more secure storage
2439
- * in production (e.g., encrypted IndexedDB, secure enclave).
2440
- */
2441
-
2442
- /**
2443
- * Pending deposit record
2444
- */
2445
- interface PendingDeposit {
2446
- /** The note (contains spending secrets!) */
2447
- note: CloakNote;
2448
- /** When the deposit was initiated */
2449
- startedAt: number;
2450
- /** Transaction signature if available */
2451
- txSignature?: string;
2452
- /** Status of the deposit */
2453
- status: "pending" | "tx_sent" | "confirmed" | "failed";
2454
- /** Error message if failed */
2455
- error?: string;
2456
- }
2457
- /**
2458
- * Pending withdrawal record
2459
- */
2460
- interface PendingWithdrawal {
2461
- /** The relay request ID (for resumption) */
2462
- requestId: string;
2463
- /** The note commitment being withdrawn */
2464
- commitment: string;
2465
- /** The nullifier being used */
2466
- nullifier: string;
2467
- /** When the withdrawal was initiated */
2468
- startedAt: number;
2469
- /** Status of the withdrawal */
2470
- status: "pending" | "processing" | "completed" | "failed";
2471
- /** Transaction signature if completed */
2472
- txSignature?: string;
2473
- /** Error message if failed */
2474
- error?: string;
2475
- }
2476
- /**
2477
- * Save a pending deposit
2478
- * Call this BEFORE sending the on-chain transaction to ensure note is persisted
2479
- */
2480
- declare function savePendingDeposit(deposit: PendingDeposit): void;
2481
- /**
2482
- * Load all pending deposits
2483
- */
2484
- declare function loadPendingDeposits(): PendingDeposit[];
2485
- /**
2486
- * Update a pending deposit status
2487
- */
2488
- declare function updatePendingDeposit(commitment: string, updates: Partial<PendingDeposit>): void;
2489
- /**
2490
- * Remove a pending deposit (e.g., after successful confirmation)
2491
- */
2492
- declare function removePendingDeposit(commitment: string): void;
2493
- /**
2494
- * Clear all pending deposits
2495
- */
2496
- declare function clearPendingDeposits(): void;
2497
- /**
2498
- * Save a pending withdrawal
2499
- * Call this when you receive the request_id from the relay
2500
- */
2501
- declare function savePendingWithdrawal(withdrawal: PendingWithdrawal): void;
2502
- /**
2503
- * Load all pending withdrawals
2504
- */
2505
- declare function loadPendingWithdrawals(): PendingWithdrawal[];
2506
- /**
2507
- * Update a pending withdrawal status
2508
- */
2509
- declare function updatePendingWithdrawal(requestId: string, updates: Partial<PendingWithdrawal>): void;
2510
- /**
2511
- * Remove a pending withdrawal (e.g., after successful completion)
2512
- */
2513
- declare function removePendingWithdrawal(requestId: string): void;
2514
- /**
2515
- * Clear all pending withdrawals
2516
- */
2517
- declare function clearPendingWithdrawals(): void;
2518
- /**
2519
- * Check if there are any pending operations that need recovery
2520
- * Call this on page load to determine if recovery UI should be shown
2521
- */
2522
- declare function hasPendingOperations(): boolean;
2523
- /**
2524
- * Get summary of pending operations for recovery UI
2525
- */
2526
- declare function getPendingOperationsSummary(): {
2527
- deposits: PendingDeposit[];
2528
- withdrawals: PendingWithdrawal[];
2529
- totalPending: number;
2530
- };
2531
- /**
2532
- * Clean up stale pending operations
2533
- * Call this periodically to remove old failed/completed operations
1594
+ * This removes the need for an external `/api/risk-quote` endpoint. The SDK
1595
+ * calls Switchboard's crossbar oracle network directly with the Range API key.
2534
1596
  *
2535
- * @param maxAgeMs Maximum age in milliseconds before an operation is removed (default: 24 hours)
1597
+ * @param connection - Solana RPC connection
1598
+ * @param wallet - Depositor's public key (the wallet being risk-scored)
1599
+ * @param rangeApiKey - Range.org API key for the risk score lookup
1600
+ * @returns The Ed25519 sig-verify instruction to prepend at index 0, plus the queue pubkey
2536
1601
  */
2537
- declare function cleanupStalePendingOperations(maxAgeMs?: number): {
2538
- removedDeposits: number;
2539
- removedWithdrawals: number;
2540
- };
1602
+ declare function fetchRiskQuoteIx(connection: Connection, wallet: PublicKey, rangeApiKey: string): Promise<{
1603
+ instruction: TransactionInstruction;
1604
+ queue: PublicKey;
1605
+ }>;
2541
1606
 
2542
1607
  /**
2543
- * UTXO (Unspent Transaction Output) Model
1608
+ * Wallet Integration Helpers
2544
1609
  *
2545
- * This module implements the UTXO model for the Cloak privacy protocol.
2546
- * UTXOs allow for:
2547
- * - Shield-to-shield transfers (privacy preserved)
2548
- * - Partial withdrawals (change stays shielded)
2549
- * - Combining multiple small deposits
2550
- * - Multi-token support (SOL + any SPL token)
1610
+ * Helper functions for working with Solana Wallet Adapters
2551
1611
  */
2552
1612
 
2553
- declare const NATIVE_SOL_MINT: PublicKey;
2554
- /**
2555
- * UTXO Keypair for ownership verification
2556
- *
2557
- * Uses Poseidon hash for key derivation:
2558
- * - privateKey: random field element (kept secret)
2559
- * - publicKey: Poseidon(privateKey, 0) - can be shared
2560
- */
2561
- interface UtxoKeypair {
2562
- /** Private key (random 252-bit field element) */
2563
- privateKey: bigint;
2564
- /** Public key derived as Poseidon(privateKey, 0) */
2565
- publicKey: bigint;
2566
- }
2567
- /**
2568
- * UTXO represents an unspent transaction output
2569
- *
2570
- * Contains all information needed to spend the UTXO:
2571
- * - amount: Value in the smallest unit (lamports for SOL)
2572
- * - keypair: Ownership keys
2573
- * - blinding: Randomness for hiding
2574
- * - mintAddress: Token type (SOL or SPL token)
2575
- * - index: Leaf index in Merkle tree (set after deposit/creation)
2576
- */
2577
- interface Utxo {
2578
- /** Amount in the smallest unit (lamports for SOL, token units for SPL) */
2579
- amount: bigint;
2580
- /** Keypair for ownership */
2581
- keypair: UtxoKeypair;
2582
- /** Random blinding factor */
2583
- blinding: bigint;
2584
- /** Token mint address (NATIVE_SOL_MINT for SOL) */
2585
- mintAddress: PublicKey;
2586
- /** Leaf index in Merkle tree (set after the UTXO is in the tree) */
2587
- index?: number;
2588
- /** Commitment hash (computed from amount, pubkey, blinding, mint) */
2589
- commitment?: bigint;
2590
- /** Nullifier hash (computed when spending) */
2591
- nullifier?: bigint;
2592
- /**
2593
- * Sibling commitment at level 0 of the Merkle tree.
2594
- * Required for Merkle proofs since siblings aren't stored on-chain.
2595
- * This is the commitment of the other output in the same transaction.
2596
- */
2597
- siblingCommitment?: bigint;
2598
- }
2599
- /**
2600
- * Encrypted UTXO note for recipient discovery
2601
- *
2602
- * When creating an output UTXO for someone else, the data is encrypted
2603
- * so only the recipient can discover and spend it.
2604
- */
2605
- interface EncryptedNote {
2606
- /** Encrypted UTXO data (ECIES/ChaCha20 encrypted) */
2607
- ciphertext: Uint8Array;
2608
- /** Ephemeral public key for ECDH */
2609
- ephemeralPubkey: Uint8Array;
2610
- /** MAC for integrity verification */
2611
- mac: Uint8Array;
2612
- }
2613
- /**
2614
- * Transaction parameters for a UTXO transaction
2615
- */
2616
- interface TransactParams {
2617
- /** Input UTXOs to spend (max 2) */
2618
- inputUtxos: Utxo[];
2619
- /** Output UTXOs to create (max 2) */
2620
- outputUtxos: Utxo[];
2621
- /** External recipient for withdrawal (optional) */
2622
- recipient?: PublicKey;
2623
- /** Net external amount: positive = deposit, negative = withdraw */
2624
- externalAmount?: bigint;
2625
- /** Depositor address (for deposits, the source of funds) */
2626
- depositor?: PublicKey;
2627
- }
2628
1613
  /**
2629
- * Result from a UTXO transaction
1614
+ * Validate wallet is connected and has public key
2630
1615
  */
2631
- interface TransactResult {
2632
- /** Transaction signature */
2633
- signature: string;
2634
- /** Nullifiers of spent inputs */
2635
- inputNullifiers: bigint[];
2636
- /** Commitments of created outputs */
2637
- outputCommitments: bigint[];
2638
- /** New Merkle root after the transaction */
2639
- newRoot: string;
2640
- /** Merkle tree indices where output commitments were inserted */
2641
- commitmentIndices: [number, number];
2642
- /**
2643
- * Sibling commitments for each output.
2644
- * For output[0], sibling is output[1] and vice versa.
2645
- * These are needed for Merkle proofs since siblings aren't stored on-chain.
2646
- */
2647
- siblingCommitments: [bigint, bigint];
2648
- /**
2649
- * Left sibling from before the transaction (for odd-index handling).
2650
- * When the first output ends up at an odd index due to concurrent access,
2651
- * its sibling at index-1 comes from a previous transaction.
2652
- * This value captures subtrees[0] before our transaction to enable
2653
- * correct Merkle proof computation for odd indices.
2654
- */
2655
- preTransactionLeftSibling?: bigint;
2656
- /**
2657
- * The actual output UTXOs created by this transaction.
2658
- * These have all the data needed to spend them later (keypair, blinding, etc).
2659
- * The UTXOs are already updated with their indices and sibling commitments.
2660
- */
2661
- outputUtxos: Utxo[];
2662
- /**
2663
- * Whether the viewing key was registered with the relay for compliance.
2664
- * True on first transaction when metadataBundle includes viewing_key.
2665
- * Once registered, subsequent transactions don't need to send metadataBundle.
2666
- */
2667
- viewingKeyRegistered?: boolean;
2668
- /**
2669
- * The Merkle tree used/built for this transaction, with output commitments inserted.
2670
- * Pass this as `cachedMerkleTree` in the next transaction to skip relay fetching
2671
- * and avoid needing `waitForCommitmentIndex`.
2672
- */
2673
- merkleTree?: MerkleTree;
2674
- /**
2675
- * Address lookup table used for v0 transaction (when chain notes or risk oracle require it).
2676
- * Pass this as `addressLookupTableAccounts` in the next deposit to avoid creating a new ALT.
2677
- */
2678
- addressLookupTableAccounts?: AddressLookupTableAccount[];
2679
- }
1616
+ declare function validateWalletConnected(wallet: WalletAdapter | Keypair): void;
2680
1617
  /**
2681
- * Generate a random field element suitable for BN254
1618
+ * Get public key from wallet or keypair
2682
1619
  */
2683
- declare function randomFieldElement(): bigint;
1620
+ declare function getPublicKey(wallet: WalletAdapter | Keypair): PublicKey;
2684
1621
  /**
2685
- * Generate a new UTXO keypair
2686
- *
2687
- * @returns A new keypair with random private key and derived public key
1622
+ * Send transaction using wallet adapter or keypair
2688
1623
  */
2689
- declare function generateUtxoKeypair(): Promise<UtxoKeypair>;
1624
+ declare function sendTransaction(transaction: Transaction, wallet: WalletAdapter | Keypair, connection: Connection, options?: SendOptions): Promise<string>;
2690
1625
  /**
2691
- * Derive a deterministic UTXO keypair from a wallet's spend key.
2692
- * Use this for deposits and change outputs so all chain notes use the same nk,
2693
- * enabling the compliance scan (scanTransactions + toComplianceReport) to decrypt them.
2694
- *
2695
- * @param skSpend - 32-byte spend key (from wallet key derivation)
2696
- * @returns UtxoKeypair with deterministic private/public keys
1626
+ * Sign transaction using wallet adapter or keypair
2697
1627
  */
2698
- declare function deriveUtxoKeypairFromSpendKey(skSpend: Uint8Array): Promise<UtxoKeypair>;
1628
+ declare function signTransaction<T extends Transaction>(transaction: T, wallet: WalletAdapter | Keypair): Promise<T>;
2699
1629
  /**
2700
- * Derive public key from private key
2701
- *
2702
- * @param privateKey The private key
2703
- * @returns The corresponding public key
1630
+ * Create a keypair adapter for testing
2704
1631
  */
2705
- declare function derivePublicKey(privateKey: bigint): Promise<bigint>;
1632
+ declare function keypairToAdapter(keypair: Keypair): WalletAdapter;
1633
+
2706
1634
  /**
2707
- * Create a new UTXO
1635
+ * Program Derived Address (PDA) utilities for Shield Pool
2708
1636
  *
2709
- * @param amount Amount in smallest unit
2710
- * @param keypair Owner's keypair
2711
- * @param mintAddress Token mint (defaults to native SOL)
2712
- * @returns A new UTXO (commitment will be computed)
1637
+ * These functions derive deterministic addresses from the program ID and seeds.
1638
+ * This matches the behavior in tooling/test/src/shared.rs::get_pda_addresses()
2713
1639
  */
2714
- declare function createUtxo(amount: bigint, keypair: UtxoKeypair, mintAddress?: PublicKey): Promise<Utxo>;
1640
+
1641
+ interface ShieldPoolPDAs {
1642
+ pool: PublicKey;
1643
+ merkleTree: PublicKey;
1644
+ vaultAuthority: PublicKey;
1645
+ treasury: PublicKey;
1646
+ }
2715
1647
  /**
2716
- * Create a zero/padding UTXO
1648
+ * Derive all Shield Pool PDAs from program ID + pool mint
2717
1649
  *
2718
- * Zero UTXOs are used as padding when we need fewer than 2 inputs/outputs.
2719
- * They have zero amount and don't require valid Merkle proofs.
2720
- * Uses salt-based keypair (privateKey = salt) with blinding=0 to match the circuit
2721
- * test. By default, salt is random to avoid cross-process collisions under concurrency.
2722
- * Callers can pass an explicit salt for deterministic behavior.
1650
+ * Seeds match the on-chain mint-scoped program:
1651
+ * - pool: [b"pool", mint]
1652
+ * - merkle_tree: [b"merkle_tree", mint]
1653
+ * - treasury: [b"treasury", mint]
1654
+ * - vault_authority: [b"vault_authority", mint]
2723
1655
  *
2724
- * @param mintAddress Token mint (defaults to native SOL)
2725
- * @param salt Optional salt; if omitted, uses timestamp+counter for uniqueness
1656
+ * @param programId - Optional program ID (defaults to CLOAK_PROGRAM_ID)
1657
+ * @param mint - Pool mint (defaults to NATIVE_SOL_MINT / WSOL sentinel)
2726
1658
  */
2727
- declare function createZeroUtxo(mintAddress?: PublicKey, salt?: bigint): Promise<Utxo>;
1659
+ declare function getShieldPoolPDAs(programId?: PublicKey, mint?: PublicKey): ShieldPoolPDAs;
2728
1660
  /**
2729
- * Convert PublicKey to field element for circuit
1661
+ * Derive the nullifier PDA for a specific pool + nullifier hash.
2730
1662
  *
2731
- * @param pubkey Solana PublicKey
2732
- * @returns Field element representation
1663
+ * With the new PDA-per-nullifier design, each nullifier has its own PDA
1664
+ * instead of being stored in a shared shard. The PDA is created when
1665
+ * the nullifier is used during withdrawal.
1666
+ *
1667
+ * Seeds: ["nullifier", pool_pubkey, nullifier_hash]
1668
+ *
1669
+ * @param poolPubkey - Pool PDA that scopes nullifier domain
1670
+ * @param nullifier - 32-byte nullifier hash
1671
+ * @param programId - Optional program ID (defaults to CLOAK_PROGRAM_ID)
1672
+ * @returns [PublicKey, bump] - The nullifier PDA and its bump seed
2733
1673
  */
2734
- declare function pubkeyToFieldElement(pubkey: PublicKey): bigint;
1674
+ declare function getNullifierPDA(poolPubkey: PublicKey, nullifier: Uint8Array | Buffer, programId?: PublicKey): [PublicKey, number];
2735
1675
  /**
2736
- * Compute the commitment for a UTXO
1676
+ * Derive the swap state PDA for a given pool + nullifier.
2737
1677
  *
2738
- * commitment = Poseidon(amount, pubkey, blinding, mintAddress)
1678
+ * Seeds: ["swap_state", pool_pubkey, nullifier_hash]
2739
1679
  *
2740
- * @param utxo The UTXO to compute commitment for
2741
- * @returns The commitment as a bigint
1680
+ * @param poolPubkey - Pool PDA used for swap state domain separation
1681
+ * @param nullifier - 32-byte nullifier hash
1682
+ * @param programId - Optional program ID (defaults to CLOAK_PROGRAM_ID)
1683
+ * @returns [PublicKey, bump] - The swap state PDA and its bump seed
2742
1684
  */
2743
- declare function computeCommitment(utxo: Utxo): Promise<bigint>;
1685
+ declare function getSwapStatePDA(poolPubkey: PublicKey, nullifier: Uint8Array | Buffer, programId?: PublicKey): [PublicKey, number];
1686
+
2744
1687
  /**
2745
- * Compute the signature for nullifier derivation
2746
- *
2747
- * signature = Poseidon(privateKey, commitment, pathIndex)
1688
+ * On-chain Merkle proof computation
2748
1689
  *
2749
- * @param privateKey Owner's private key
2750
- * @param commitment The UTXO commitment
2751
- * @param pathIndex Merkle tree path index
2752
- * @returns The signature
1690
+ * Computes Merkle proofs directly from on-chain tree state,
1691
+ * eliminating the need for an indexer for proof generation.
2753
1692
  */
2754
- declare function computeSignature(privateKey: bigint, commitment: bigint, pathIndex: bigint): Promise<bigint>;
1693
+
2755
1694
  /**
2756
- * Compute the nullifier for a UTXO
1695
+ * Merkle proof result
1696
+ */
1697
+ interface OnchainMerkleProof {
1698
+ pathElements: string[];
1699
+ pathIndices: number[];
1700
+ root: string;
1701
+ }
1702
+ interface MerkleTreeState {
1703
+ nextIndex: number;
1704
+ root: string;
1705
+ subtrees: string[];
1706
+ }
1707
+ /**
1708
+ * Read the Merkle tree state from on-chain account
1709
+ * @param forceConfirmed - If true, forces "confirmed" commitment level for fresh data
1710
+ * @param relayUrl - Optional relay URL to query for current root (bypasses RPC caching)
1711
+ * @param mint - Optional pool mint used when querying relay /merkle-root
1712
+ */
1713
+ declare function readMerkleTreeState(connection: Connection, merkleTreePDA: PublicKey, forceConfirmed?: boolean, relayUrl?: string,
1714
+ /** When true, skip relay root and use chain directly (avoids stale relay root causing ProofInvalid) */
1715
+ skipRelayRoot?: boolean, mint?: PublicKey): Promise<MerkleTreeState>;
1716
+ /**
1717
+ * Compute Merkle proof for a leaf at a given index using on-chain state
2757
1718
  *
2758
- * nullifier = Poseidon(commitment, pathIndex, signature)
1719
+ * This works by:
1720
+ * 1. Reading the subtrees (frontier) from the on-chain account
1721
+ * 2. For each level, determining the sibling:
1722
+ * - If index is odd: sibling is the stored subtree (left sibling)
1723
+ * - If index is even: sibling is either a subsequent subtree or zero value
2759
1724
  *
2760
- * The nullifier is a unique identifier that gets recorded on-chain
2761
- * to prevent double-spending.
1725
+ * Note: This works best for recent leaves where siblings are zero values.
1726
+ * For older leaves with non-zero siblings that aren't stored in subtrees,
1727
+ * an indexer may still be needed.
2762
1728
  *
2763
- * @param utxo The UTXO being spent
2764
- * @returns The nullifier
2765
- */
2766
- declare function computeNullifier(utxo: Utxo): Promise<bigint>;
2767
- /**
2768
- * Serialize a UTXO to bytes for encryption
1729
+ * @param connection - Solana connection
1730
+ * @param merkleTreePDA - PDA of the merkle tree account
1731
+ * @param leafIndex - Index of the leaf to compute proof for
1732
+ * @param forceConfirmed - If true, forces "confirmed" commitment level for fresh data
1733
+ * @returns Merkle proof with path elements and indices
2769
1734
  */
2770
- declare function serializeUtxo(utxo: Utxo): Uint8Array;
1735
+ declare function computeProofFromChain(connection: Connection, merkleTreePDA: PublicKey, leafIndex: number, forceConfirmed?: boolean): Promise<OnchainMerkleProof>;
2771
1736
  /**
2772
- * Deserialize a UTXO from bytes
1737
+ * Compute proof for the most recently inserted leaf
1738
+ *
1739
+ * This is the most reliable case - immediately after your deposit,
1740
+ * all siblings to the right are zero values, and siblings to the left
1741
+ * are stored in the subtrees.
2773
1742
  */
2774
- declare function deserializeUtxo(bytes: Uint8Array): Promise<Utxo>;
1743
+ declare function computeProofForLatestDeposit(connection: Connection, merkleTreePDA: PublicKey): Promise<OnchainMerkleProof & {
1744
+ leafIndex: number;
1745
+ }>;
1746
+
2775
1747
  /**
2776
- * Convert bigint to hex string (64 chars, padded)
1748
+ * Relay client utilities for fetching data from the relay service
2777
1749
  */
2778
- declare function bigintToHex(value: bigint): string;
1750
+
1751
+ interface CommitmentEntry {
1752
+ index: number;
1753
+ commitment: string;
1754
+ }
1755
+ interface CommitmentsResponse {
1756
+ commitments: CommitmentEntry[];
1757
+ count: number;
1758
+ target_reached?: boolean;
1759
+ canonical?: boolean;
1760
+ canonical_reason?: string | null;
1761
+ }
1762
+ interface FetchCommitmentsOptions {
1763
+ mint?: PublicKey;
1764
+ timeoutMs?: number;
1765
+ maxRetries?: number;
1766
+ sync?: boolean;
1767
+ repair?: boolean;
1768
+ requireTarget?: boolean;
1769
+ requireCanonical?: boolean;
1770
+ fromIndex?: number;
1771
+ limit?: number;
1772
+ waitForIndex?: number;
1773
+ }
2779
1774
  /**
2780
- * Convert hex string to bigint
1775
+ * Fetch all commitments from the relay
1776
+ * @param relayUrl Base URL of the relay service (may include cache-busting query params)
1777
+ * @param options Timeout and retry options
1778
+ * @returns Array of commitment entries
2781
1779
  */
2782
- declare function hexToBigint(hex: string): bigint;
1780
+ declare function fetchCommitments(relayUrl: string, options?: FetchCommitmentsOptions): Promise<CommitmentEntry[]>;
2783
1781
  /**
2784
- * Convert bigint to 32-byte array (big-endian for circuits)
1782
+ * Build a Merkle tree from relay commitments
1783
+ * @param relayUrl Base URL of the relay service
1784
+ * @param options When sync is true, triggers on-demand sync on relay before fetching (for freshness when relay is behind chain)
1785
+ * @returns MerkleTree instance with all commitments loaded
1786
+ *
1787
+ * Supports sparse indices by zero-filling missing leaves, including missing prefixes.
1788
+ * Non-contiguous gaps (e.g. 0,1,2,3,22,23,30,31,32,33) are reconstructed with
1789
+ * zero leaves to match the on-chain Merkle tree structure.
2785
1790
  */
2786
- declare function bigintToBytes32(value: bigint): Uint8Array;
1791
+ declare function buildMerkleTreeFromRelay(relayUrl: string, options?: {
1792
+ mint?: PublicKey;
1793
+ sync?: boolean;
1794
+ repair?: boolean;
1795
+ requireTarget?: boolean;
1796
+ requireCanonical?: boolean;
1797
+ timeoutMs?: number;
1798
+ maxRetries?: number;
1799
+ waitForIndex?: number;
1800
+ }): Promise<MerkleTree>;
1801
+ declare function buildMerkleTreeFromChain(connection: Connection, programId: PublicKey, merkleTree: PublicKey, onProgress?: (message: string) => void): Promise<MerkleTree>;
2787
1802
  /**
2788
- * Check if two UTXOs are equal (same commitment)
1803
+ * Pre-flight root validation
1804
+ *
1805
+ * Validates that a Merkle root is still valid before generating a proof.
1806
+ * This prevents wasted time generating proofs that will fail on-chain.
1807
+ *
1808
+ * @param relayUrl Relay service URL
1809
+ * @param rootHex Root to validate (hex string, with or without 0x prefix)
1810
+ * @returns Validation result with status and helpful message
2789
1811
  */
2790
- declare function utxoEquals(a: Utxo, b: Utxo): Promise<boolean>;
1812
+ declare function validateRoot(relayUrl: string, rootHex: string): Promise<{
1813
+ valid: boolean;
1814
+ currentRoot?: string;
1815
+ message: string;
1816
+ isRecent: boolean;
1817
+ }>;
2791
1818
  /**
2792
- * Calculate total amount from an array of UTXOs
1819
+ * Wait for root to be valid
1820
+ *
1821
+ * Polls the relay until the given root is accepted or timeout.
1822
+ * Useful when you know a transaction is being processed but root hasn't updated yet.
1823
+ *
1824
+ * @param relayUrl Relay service URL
1825
+ * @param rootHex Root to wait for
1826
+ * @param timeoutMs Maximum time to wait (default: 30000ms)
1827
+ * @param pollIntervalMs Polling interval (default: 1000ms)
1828
+ * @returns Whether root became valid in time
2793
1829
  */
2794
- declare function sumUtxoAmounts(utxos: Utxo[]): bigint;
1830
+ declare function waitForRoot(relayUrl: string, rootHex: string, timeoutMs?: number, pollIntervalMs?: number): Promise<{
1831
+ success: boolean;
1832
+ currentRoot?: string;
1833
+ waitedMs: number;
1834
+ }>;
2795
1835
  /**
2796
- * Select UTXOs to cover a target amount
1836
+ * Smart pre-flight check with automatic retry suggestion
2797
1837
  *
2798
- * @param available Available UTXOs (sorted by amount descending)
2799
- * @param targetAmount Target amount to cover
2800
- * @returns Selected UTXOs that cover the target, or null if insufficient
1838
+ * Checks root validity and provides actionable guidance:
1839
+ * - If root is current: proceed
1840
+ * - If root is old: suggest fetching fresh Merkle proofs
1841
+ * - If root is very old: suggest full tree rebuild
1842
+ *
1843
+ * @param relayUrl Relay service URL
1844
+ * @param rootHex Root to check
1845
+ * @returns Action recommendation
2801
1846
  */
2802
- declare function selectUtxos(available: Utxo[], targetAmount: bigint): Utxo[] | null;
1847
+ declare function preflightCheck(relayUrl: string, rootHex: string): Promise<{
1848
+ shouldProceed: boolean;
1849
+ action: "proceed" | "refresh_proofs" | "rebuild_tree" | "wait";
1850
+ message: string;
1851
+ estimatedWaitMs?: number;
1852
+ }>;
2803
1853
 
2804
1854
  /**
2805
1855
  * UTXO Transaction Methods
@@ -3424,10 +2474,25 @@ declare class SimpleWallet {
3424
2474
  * Cloak SDK - TypeScript SDK for Private Transactions on Solana
3425
2475
  *
3426
2476
  * @packageDocumentation
2477
+ *
2478
+ * # 0.1.6 — breaking changes
2479
+ *
2480
+ * The legacy `CloakNote` API (`CloakSDK.deposit/privateTransfer/withdraw/
2481
+ * send/swap`, `createDepositInstruction`, `generateNote`, `withdraw_regular`
2482
+ * proofs, etc.) was removed because every code path eventually emitted a
2483
+ * legacy `[discriminator: 1, amount: u64, commitment: 32]` instruction that
2484
+ * the deployed shield-pool program no longer accepts (tag `1` is now
2485
+ * `TransactSwap`, not `Deposit`). The OLD 3-input commitment scheme is
2486
+ * also incompatible with the deployed `transaction.circom` UTXO circuit.
2487
+ *
2488
+ * Use the functional UTXO API instead — `transact`, `transfer`,
2489
+ * `partialWithdraw`, `fullWithdraw`, `swapUtxo`, `createUtxo`,
2490
+ * `createZeroUtxo`, `generateUtxoKeypair`. The `CloakSDK` class is kept as
2491
+ * a thin config + read-only chain helper.
3427
2492
  */
3428
2493
 
3429
- declare const VERSION = "1.0.0";
3430
- /** True when scanner supports TransactSwap (tag 1). Check this to verify the correct SDK bundle is loaded. */
2494
+ declare const VERSION = "0.1.6";
2495
+ /** True when scanner supports TransactSwap (tag 1). */
3431
2496
  declare const SCANNER_SUPPORTS_TRANSACT_SWAP = true;
3432
2497
 
3433
- export { CLOAK_PROGRAM_ID, type ChainNoteTxType, type CircuitVerificationResult, type CloakConfig, CloakError, type CloakKeyPair, type CloakNote, CloakSDK, type CommitmentEntry, type CommitmentsResponse, type CompactChainNote, type ComplianceReport, type ComplianceTxType, DEFAULT_CIRCUITS_URL, DEFAULT_TRANSACTION_CIRCUITS_URL, type DepositInstructionParams, type DepositOptions, type DepositResult, type DepositStatus, EXPECTED_CIRCUIT_HASHES, type EncryptedMetadataBundle, type EncryptedNote$1 as EncryptedNote, type ErrorCategory, type ExpandedSpendKey, FIXED_FEE_LAMPORTS, type Groth16Proof, LAMPORTS_PER_SOL, LocalStorageAdapter, type LogLevel, type Logger, MERKLE_TREE_HEIGHT, MIN_DEPOSIT_LAMPORTS, type MasterKey, type MaxLengthArray, MemoryStorageAdapter, type MerkleProof, type MerkleRootResponse, MerkleTree, NATIVE_SOL_MINT, type Network, type NoteData, type OnchainMerkleProof, type PendingDeposit, type PendingWithdrawal, type ProofResult, RelayService, type RiskQuoteInstructionResponse, RootNotFoundError, SCANNER_SUPPORTS_TRANSACT_SWAP, SIGN_IN_MESSAGE, type ScanOptions, type ScanResult, type ScanSummary, type ScannedTransaction, ShieldPoolErrors, type ShieldPoolPDAs, SimpleWallet, type SpendKey, type StorageAdapter, type SwapOptions, type SwapParams, type SwapResult, type TransactOptions, type TransactParams, type TransactResult, type TransactionMetadata, type Transfer, type TransferOptions, type TransferResult, type TxStatus, type UserFriendlyError, type Utxo, type EncryptedNote as UtxoEncryptedNote, type UtxoKeypair, type UtxoSwapParams, type UtxoSwapResult, UtxoWallet, VARIABLE_FEE_DENOMINATOR, VARIABLE_FEE_NUMERATOR, VARIABLE_FEE_RATE, VERSION, type ViewKey, type ViewingKeyPair, type WalletAdapter, type WalletUtxo, type WithdrawOptions, type WithdrawRegularInputs, type WithdrawSubmissionResult, type WithdrawSwapInputs, areCircuitsAvailable, bigintToBytes32$1 as bigintToBytes32, bigintToHex, buildMerkleTree, buildMerkleTreeFromChain, buildMerkleTreeFromRelay, buildPublicInputsBytes, bytesToHex, calculateFee, calculateFeeBigint, calculateRelayFee, chainNoteFromBase64, chainNoteToBase64, cleanupStalePendingOperations, clearPendingDeposits, clearPendingWithdrawals, computeChainNoteHash, computeCommitment$1 as computeCommitment, computeExtDataHash, computeMerkleRoot, computeNullifier$1 as computeNullifier, computeNullifierAsync, computeNullifierSync, computeOutputsHash, computeOutputsHashAsync, computeOutputsHashSync, computeProofForLatestDeposit, computeProofFromChain, computeSignature, computeSwapOutputsHash, computeSwapOutputsHashAsync, computeSwapOutputsHashSync, computeCommitment as computeUtxoCommitment, computeNullifier as computeUtxoNullifier, copyNoteToClipboard, createCloakError, createDepositInstruction, createLogger, createUtxo, createZeroUtxo, decryptCompactChainNote, decryptComplianceMetadataWithMasterKey, decryptTransactionMetadata, deriveDiversifiedViewingKey, deriveDiversifier, derivePublicKey, deriveSpendKey, deriveUserCompliancePublicKey, deriveUserComplianceScalar, deriveUtxoKeypairFromSpendKey, deriveViewKey, deriveViewingKeyFromNk, deriveViewingKeyFromSpendKey, deriveViewingKeyFromUtxoPrivateKey, deserializeUtxo, detectNetworkFromRpcUrl, downloadNote, encodeNoteSimple, encryptCompactChainNote, encryptNoteForRecipient, encryptTransactionMetadata, encryptTransactionMetadataBundle, expandSpendKey, exportKeys, exportNote, exportWalletKeys, fetchCommitments, fetchRiskQuoteInstruction, fetchRiskQuoteIx, filterNotesByNetwork, filterWithdrawableNotes, findNoteByCommitment, formatAmount, formatComplianceCsv, formatErrorForLogging, formatSol, fullWithdraw, generateCloakKeys, generateCommitment, generateCommitmentAsync, generateMasterSeed, generateNote, generateNoteFromWallet, generateUtxoKeypair, generateViewingKeyPair, generateWithdrawRegularProof, generateWithdrawSwapProof, getAddressExplorerUrl, getCircuitsPath, getDefaultCircuitsPath, getDistributableAmount, getExplorerUrl, getNkFromUtxoPrivateKey, getNullifierPDA, getPendingOperationsSummary, getPublicKey, getPublicViewKey, getRecipientAmount, getRpcUrlForNetwork, getShieldPoolPDAs, getSwapStatePDA, getViewKey, hasPendingOperations, hexToBigint$1 as hexToBigint, hexToBytes, importKeys, importWalletKeys, isDebugEnabled, isRootNotFoundError, isValidHex, isValidRpcUrl, isValidSolanaAddress, isWithdrawAmountSufficient, isWithdrawable, keypairToAdapter, loadPendingDeposits, loadPendingWithdrawals, parseAmount, parseError, parseNote, parseRelayErrorResponse, parseTransactionError, partialWithdraw, poseidonHash, preflightCheck, prepareEncryptedOutput, prepareEncryptedOutputForRecipient, proofToBytes, pubkeyToFieldElement, pubkeyToLimbs, randomBytes, randomFieldElement, readMerkleTreeState, registerViewingKey, removePendingDeposit, removePendingWithdrawal, savePendingDeposit, savePendingWithdrawal, scanNotesForWallet, scanTransactions, sdkLogger, selectUtxos, sendTransaction, serializeNote, serializeUtxo, setCircuitsPath, setDebugMode, signTransaction, splitTo2Limbs, sumUtxoAmounts, swapUtxo, swapWithChange, toComplianceReport, transact, transfer, truncate, tryDecryptNote, updateNoteWithDeposit, updatePendingDeposit, updatePendingWithdrawal, bigintToBytes32 as utxoBigintToBytes32, utxoEquals, hexToBigint as utxoHexToBigint, validateDepositParams, validateNote, validateOutputsSum, validateRoot, validateTransfers, validateWalletConnected, validateWithdrawableNote, verifyAllCircuits, verifyCircuitIntegrity, waitForRoot, withTiming };
2498
+ export { CLOAK_PROGRAM_ID, type ChainNoteTxType, type CloakConfig, CloakError, type CloakKeyPair, CloakSDK, type CloakSDKOptions, type CommitmentEntry, type CommitmentsResponse, type CompactChainNote, type ComplianceReport, type ComplianceTxType, DEFAULT_TRANSACTION_CIRCUITS_URL, type EncryptedMetadataBundle, type EncryptedNote$1 as EncryptedNote, type ErrorCategory, type ExpandedSpendKey, FIXED_FEE_LAMPORTS, type Groth16Proof, LAMPORTS_PER_SOL, LocalStorageAdapter, type LogLevel, type Logger, MERKLE_TREE_HEIGHT, MIN_DEPOSIT_LAMPORTS, type MasterKey, MemoryStorageAdapter, type MerkleProof, type MerkleRootResponse, MerkleTree, NATIVE_SOL_MINT, type Network, type NoteData, type OnchainMerkleProof, RelayInternalError, RelayService, type RiskQuoteInstructionResponse, RootNotFoundError, SCANNER_SUPPORTS_TRANSACT_SWAP, SIGN_IN_MESSAGE, SanctionsQuoteError, type ScanOptions, type ScanResult, type ScanSummary, type ScannedTransaction, ShieldPoolErrors, type ShieldPoolPDAs, SimpleWallet, type SpendKey, type StorageAdapter, type TransactOptions, type TransactParams, type TransactResult, type TransactionMetadata, type TxStatus, type UserFriendlyError, type Utxo, UtxoAlreadySpentError, type EncryptedNote as UtxoEncryptedNote, type UtxoKeypair, type UtxoSwapParams, type UtxoSwapResult, UtxoWallet, VARIABLE_FEE_DENOMINATOR, VARIABLE_FEE_NUMERATOR, VARIABLE_FEE_RATE, VERSION, type VerifyUtxosResult, type ViewKey, type ViewingKeyPair, type WalletAdapter, type WalletUtxo, type WithdrawSubmissionResult, bigintToBytes32$1 as bigintToBytes32, bigintToHex, buildMerkleTree, buildMerkleTreeFromChain, buildMerkleTreeFromRelay, buildPublicInputsBytes, bytesToHex, calculateFee, calculateFeeBigint, calculateRelayFee, chainNoteFromBase64, chainNoteToBase64, classifyRelayError, computeChainNoteHash, computeExtDataHash, computeMerkleRoot, computeProofForLatestDeposit, computeProofFromChain, computeSignature, computeCommitment as computeUtxoCommitment, computeNullifier as computeUtxoNullifier, createCloakError, createLogger, createUtxo, createZeroUtxo, decryptCompactChainNote, decryptComplianceMetadataWithMasterKey, decryptTransactionMetadata, deriveDiversifiedViewingKey, deriveDiversifier, derivePublicKey, deriveSpendKey, deriveUserCompliancePublicKey, deriveUserComplianceScalar, deriveUtxoKeypairFromSpendKey, deriveViewKey, deriveViewingKeyFromNk, deriveViewingKeyFromSpendKey, deriveViewingKeyFromUtxoPrivateKey, deserializeUtxo, detectNetworkFromRpcUrl, encryptCompactChainNote, encryptNoteForRecipient, encryptTransactionMetadata, encryptTransactionMetadataBundle, expandSpendKey, exportKeys, fetchCommitments, fetchRiskQuoteInstruction, fetchRiskQuoteIx, formatAmount, formatComplianceCsv, formatErrorForLogging, formatSol, fullWithdraw, generateCloakKeys, generateMasterSeed, generateUtxoKeypair, generateViewingKeyPair, getAddressExplorerUrl, getCircuitsPath, getDistributableAmount, getExplorerUrl, getNkFromUtxoPrivateKey, getNullifierPDA, getPublicKey, getRpcUrlForNetwork, getShieldPoolPDAs, getSwapStatePDA, hexToBigint$1 as hexToBigint, hexToBytes, importKeys, isDebugEnabled, isRootNotFoundError, isValidHex, isValidRpcUrl, isValidSolanaAddress, isWithdrawAmountSufficient, keypairToAdapter, parseAmount, parseError, parseRelayErrorResponse, parseTransactionError, partialWithdraw, poseidonHash, preflightCheck, preflightNullifiers, proofToBytes, pubkeyToFieldElement, pubkeyToLimbs, randomBytes, randomFieldElement, readMerkleTreeState, registerViewingKey, scanNotesForWallet, scanTransactions, sdkLogger, selectUtxos, sendTransaction, serializeUtxo, setCircuitsPath, setDebugMode, signTransaction, splitTo2Limbs, sumUtxoAmounts, swapUtxo, swapWithChange, toComplianceReport, transact, transfer, truncate, tryDecryptNote, bigintToBytes32 as utxoBigintToBytes32, utxoEquals, hexToBigint as utxoHexToBigint, validateOutputsSum, validateRoot, validateWalletConnected, verifyUtxos, waitForRoot, withTiming };