@cloak.dev/sdk 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +421 -278
- package/dist/chunk-YX5SCAMR.js +594 -0
- package/dist/index.cjs +6201 -1652
- package/dist/index.d.cts +2705 -447
- package/dist/index.d.ts +2705 -447
- package/dist/index.js +5729 -1609
- package/dist/{utxo-LSTVI4HH.js → utxo-PFJT3ETR.js} +7 -5
- package/package.json +4 -4
- package/CHANGELOG.md +0 -173
- package/dist/chunk-2SOX3JNO.js +0 -255
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,376 @@
|
|
|
1
1
|
import { PublicKey, Transaction, Connection, AddressLookupTableAccount, TransactionInstruction, Keypair, SendOptions, VersionedTransaction } from '@solana/web3.js';
|
|
2
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
|
+
}
|
|
373
|
+
|
|
3
374
|
/**
|
|
4
375
|
* Cloak Key Hierarchy (v2.0)
|
|
5
376
|
*
|
|
@@ -84,310 +455,641 @@ declare function exportKeys(keys: CloakKeyPair): string;
|
|
|
84
455
|
declare function importKeys(exported: string): CloakKeyPair;
|
|
85
456
|
|
|
86
457
|
/**
|
|
87
|
-
*
|
|
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.)
|
|
88
462
|
*/
|
|
89
|
-
|
|
463
|
+
|
|
90
464
|
/**
|
|
91
|
-
*
|
|
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.
|
|
92
469
|
*/
|
|
93
|
-
interface
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* In-memory storage adapter (default, no persistence)
|
|
506
|
+
*
|
|
507
|
+
* Useful for testing or when storage is handled externally
|
|
508
|
+
*/
|
|
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;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
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.
|
|
526
|
+
*/
|
|
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;
|
|
98
540
|
}
|
|
541
|
+
|
|
542
|
+
/** Default Cloak Program ID on Solana */
|
|
543
|
+
declare const CLOAK_PROGRAM_ID: PublicKey;
|
|
544
|
+
/**
|
|
545
|
+
* Main Cloak SDK
|
|
546
|
+
*
|
|
547
|
+
* Provides high-level API for interacting with the Cloak protocol,
|
|
548
|
+
* including deposits, withdrawals, and private transfers.
|
|
549
|
+
*
|
|
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
|
|
553
|
+
*/
|
|
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>;
|
|
674
|
+
/**
|
|
675
|
+
* Withdraw to a single recipient
|
|
676
|
+
*
|
|
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
|
+
* ```
|
|
698
|
+
*/
|
|
699
|
+
withdraw(connection: Connection, note: CloakNote, recipient: PublicKey, options?: WithdrawOptions): Promise<TransferResult>;
|
|
700
|
+
/**
|
|
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
|
+
* ```
|
|
724
|
+
*/
|
|
725
|
+
send(connection: Connection, note: CloakNote, recipients: MaxLengthArray<Transfer, 5>, options?: TransferOptions): Promise<TransferResult>;
|
|
726
|
+
/**
|
|
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
|
+
* ```
|
|
759
|
+
*/
|
|
760
|
+
swap(connection: Connection, note: CloakNote, recipient: PublicKey, options: SwapOptions): Promise<SwapResult>;
|
|
761
|
+
/**
|
|
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)
|
|
767
|
+
*/
|
|
768
|
+
generateNote(amountLamports: number, useWalletKeys?: boolean): Promise<CloakNote>;
|
|
769
|
+
/**
|
|
770
|
+
* Parse a note from JSON string
|
|
771
|
+
*
|
|
772
|
+
* @param jsonString - JSON representation
|
|
773
|
+
* @returns Parsed note
|
|
774
|
+
*/
|
|
775
|
+
parseNote(jsonString: string): CloakNote;
|
|
776
|
+
/**
|
|
777
|
+
* Export a note to JSON string
|
|
778
|
+
*
|
|
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
|
|
786
|
+
*
|
|
787
|
+
* @param note - Note to check
|
|
788
|
+
* @returns True if withdrawable
|
|
789
|
+
*/
|
|
790
|
+
isWithdrawable(note: CloakNote): boolean;
|
|
791
|
+
/**
|
|
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
|
|
797
|
+
*/
|
|
798
|
+
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
|
+
*/
|
|
805
|
+
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
|
+
*/
|
|
812
|
+
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
|
+
*/
|
|
845
|
+
importWalletKeys(keysJson: string): Promise<void>;
|
|
846
|
+
/**
|
|
847
|
+
* Export wallet keys to JSON
|
|
848
|
+
*
|
|
849
|
+
* WARNING: This exports secret keys! Store securely.
|
|
850
|
+
*
|
|
851
|
+
* @returns JSON string with keys
|
|
852
|
+
*/
|
|
853
|
+
exportWalletKeys(): string;
|
|
854
|
+
/**
|
|
855
|
+
* Get the configuration
|
|
856
|
+
*/
|
|
857
|
+
getConfig(): CloakConfig;
|
|
858
|
+
/**
|
|
859
|
+
* Wrap errors with better categorization and user-friendly messages
|
|
860
|
+
*
|
|
861
|
+
* @private
|
|
862
|
+
*/
|
|
863
|
+
private wrapError;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/**
|
|
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
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
declare function serializeNote(note: CloakNote, pretty?: boolean): string;
|
|
881
|
+
/**
|
|
882
|
+
* Export note as downloadable JSON (browser only)
|
|
883
|
+
*
|
|
884
|
+
* @param note - Note to export
|
|
885
|
+
* @param filename - Optional custom filename
|
|
886
|
+
*/
|
|
887
|
+
declare function downloadNote(note: CloakNote, filename?: string): void;
|
|
888
|
+
/**
|
|
889
|
+
* Copy note to clipboard as JSON (browser only)
|
|
890
|
+
*
|
|
891
|
+
* @param note - Note to copy
|
|
892
|
+
* @returns Promise that resolves when copied
|
|
893
|
+
*/
|
|
894
|
+
declare function copyNoteToClipboard(note: CloakNote): Promise<void>;
|
|
895
|
+
|
|
896
|
+
/**
|
|
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.
|
|
904
|
+
*/
|
|
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;
|
|
917
|
+
/**
|
|
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
|
+
* ```
|
|
930
|
+
*/
|
|
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;
|
|
99
1041
|
/**
|
|
100
|
-
*
|
|
1042
|
+
* Check if a note is withdrawable (has been deposited)
|
|
1043
|
+
* Note: merkleProof is optional - it may be fetched lazily at withdrawal time
|
|
101
1044
|
*/
|
|
102
|
-
declare
|
|
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);
|
|
107
|
-
}
|
|
1045
|
+
declare function isWithdrawable(note: CloakNote): boolean;
|
|
108
1046
|
/**
|
|
109
|
-
*
|
|
1047
|
+
* Update note with deposit information
|
|
1048
|
+
* Returns a new note object with deposit info added
|
|
110
1049
|
*/
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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;
|
|
119
1060
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
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`.
|
|
129
|
-
*
|
|
130
|
-
* If you're configuring the SDK at construction, use `CloakSDKOptions`.
|
|
1061
|
+
* Find note by commitment from an array
|
|
131
1062
|
*/
|
|
132
|
-
|
|
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;
|
|
145
|
-
/**
|
|
146
|
-
* Whether structured debug logging was enabled at construction.
|
|
147
|
-
*
|
|
148
|
-
* Can also be enabled via env: `CLOAK_DEBUG=1` or `DEBUG=cloak:*`.
|
|
149
|
-
*/
|
|
150
|
-
debug?: boolean;
|
|
151
|
-
}
|
|
1063
|
+
declare function findNoteByCommitment(notes: CloakNote[], commitment: string): CloakNote | undefined;
|
|
152
1064
|
/**
|
|
153
|
-
*
|
|
1065
|
+
* Filter notes by network
|
|
154
1066
|
*/
|
|
155
|
-
|
|
156
|
-
root: string;
|
|
157
|
-
next_index: number;
|
|
158
|
-
}
|
|
1067
|
+
declare function filterNotesByNetwork(notes: CloakNote[], network: Network): CloakNote[];
|
|
159
1068
|
/**
|
|
160
|
-
*
|
|
1069
|
+
* Filter notes that can be withdrawn
|
|
161
1070
|
*/
|
|
162
|
-
|
|
163
|
-
status: "pending" | "processing" | "completed" | "failed";
|
|
164
|
-
txId?: string;
|
|
165
|
-
error?: string;
|
|
166
|
-
}
|
|
167
|
-
|
|
1071
|
+
declare function filterWithdrawableNotes(notes: CloakNote[]): CloakNote[];
|
|
168
1072
|
/**
|
|
169
|
-
*
|
|
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.
|
|
1073
|
+
* Export keys to JSON string
|
|
1074
|
+
* WARNING: This exports secret keys! Store securely.
|
|
176
1075
|
*/
|
|
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
|
-
}
|
|
1076
|
+
declare function exportWalletKeys(keys: CloakKeyPair): string;
|
|
186
1077
|
/**
|
|
187
|
-
*
|
|
188
|
-
* when storage is handled externally.
|
|
1078
|
+
* Import keys from JSON string
|
|
189
1079
|
*/
|
|
190
|
-
declare
|
|
191
|
-
private keys;
|
|
192
|
-
saveKeys(keys: CloakKeyPair): void;
|
|
193
|
-
loadKeys(): CloakKeyPair | null;
|
|
194
|
-
deleteKeys(): void;
|
|
195
|
-
}
|
|
1080
|
+
declare function importWalletKeys(keysJson: string): CloakKeyPair;
|
|
196
1081
|
/**
|
|
197
|
-
*
|
|
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.
|
|
1082
|
+
* Get public view key from keys
|
|
208
1083
|
*/
|
|
209
|
-
declare
|
|
210
|
-
private keysKey;
|
|
211
|
-
constructor(keysKey?: string);
|
|
212
|
-
private getStorage;
|
|
213
|
-
saveKeys(keys: CloakKeyPair): void;
|
|
214
|
-
loadKeys(): CloakKeyPair | null;
|
|
215
|
-
deleteKeys(): void;
|
|
216
|
-
/**
|
|
217
|
-
* Sentinel key set after the auto-purge runs. Persists across browser
|
|
218
|
-
* sessions so the purge never re-runs on the same origin.
|
|
219
|
-
*/
|
|
220
|
-
static readonly LEGACY_PURGE_SENTINEL_KEY = "cloak_purged_v0_1_5";
|
|
221
|
-
/**
|
|
222
|
-
* Default localStorage key the legacy `<= 0.1.5` SDK used for the
|
|
223
|
-
* plaintext `CloakNote[]` blob.
|
|
224
|
-
*/
|
|
225
|
-
static readonly LEGACY_NOTES_KEY = "cloak_notes";
|
|
226
|
-
/**
|
|
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.
|
|
231
|
-
*/
|
|
232
|
-
static runLegacyPurgeOnce(): void;
|
|
233
|
-
/**
|
|
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.
|
|
237
|
-
*
|
|
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.
|
|
243
|
-
*
|
|
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.
|
|
246
|
-
*
|
|
247
|
-
* @param notesKey - localStorage key to remove (default `"cloak_notes"`,
|
|
248
|
-
* matching the pre-0.1.6 default).
|
|
249
|
-
*/
|
|
250
|
-
static purgeLegacyNoteStorage(notesKey?: string): void;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/** Default Cloak Program ID on Solana mainnet. */
|
|
254
|
-
declare const CLOAK_PROGRAM_ID: PublicKey;
|
|
1084
|
+
declare function getPublicViewKey(keys: CloakKeyPair): string;
|
|
255
1085
|
/**
|
|
256
|
-
*
|
|
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.
|
|
1086
|
+
* Get view key from keys
|
|
263
1087
|
*/
|
|
264
|
-
|
|
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
|
-
}
|
|
1088
|
+
declare function getViewKey(keys: CloakKeyPair): ViewKey;
|
|
282
1089
|
/**
|
|
283
|
-
*
|
|
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.
|
|
1090
|
+
* Get recipient amount after fees
|
|
327
1091
|
*/
|
|
328
|
-
declare
|
|
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;
|
|
340
|
-
/**
|
|
341
|
-
* Compute a Merkle proof for `leafIndex` from on-chain state. No relay /
|
|
342
|
-
* indexer round-trip required.
|
|
343
|
-
*/
|
|
344
|
-
getMerkleProof(connection: Connection, leafIndex: number): Promise<MerkleProof>;
|
|
345
|
-
/** Read the current SOL-pool Merkle root from on-chain state. */
|
|
346
|
-
getCurrentRoot(connection: Connection): Promise<string>;
|
|
347
|
-
/** Poll relay for the status of a previously-submitted request. */
|
|
348
|
-
getTransactionStatus(requestId: string): Promise<TxStatus>;
|
|
349
|
-
/** Import wallet keys from JSON; persists to the configured storage adapter. */
|
|
350
|
-
importWalletKeys(keysJson: string): Promise<void>;
|
|
351
|
-
/**
|
|
352
|
-
* Export wallet keys to JSON.
|
|
353
|
-
*
|
|
354
|
-
* WARNING: this exports secret keys. Store securely.
|
|
355
|
-
*/
|
|
356
|
-
exportWalletKeys(): string;
|
|
357
|
-
/**
|
|
358
|
-
* Snapshot of the active SDK configuration.
|
|
359
|
-
*
|
|
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.
|
|
367
|
-
*/
|
|
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;
|
|
390
|
-
}
|
|
1092
|
+
declare function getRecipientAmount(amountLamports: number): number;
|
|
391
1093
|
|
|
392
1094
|
interface ViewingKeyPair {
|
|
393
1095
|
privateKey: Uint8Array;
|
|
@@ -542,186 +1244,246 @@ declare function computeMerkleRoot(leaf: bigint, pathElements: bigint[], pathInd
|
|
|
542
1244
|
*/
|
|
543
1245
|
declare function hexToBigint$1(hex: string): bigint;
|
|
544
1246
|
/**
|
|
545
|
-
*
|
|
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
|
|
546
1257
|
*/
|
|
547
|
-
declare function
|
|
1258
|
+
declare function computeCommitment$1(amount: bigint, r: bigint, sk_spend: bigint): Promise<bigint>;
|
|
548
1259
|
/**
|
|
549
|
-
*
|
|
1260
|
+
* Generate a Poseidon commitment for a note
|
|
550
1261
|
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
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
|
|
553
1271
|
*/
|
|
554
|
-
declare function
|
|
1272
|
+
declare function generateCommitmentAsync(amountLamports: number, r: Uint8Array, skSpend: Uint8Array): Promise<bigint>;
|
|
555
1273
|
/**
|
|
556
|
-
*
|
|
1274
|
+
* Generate a Poseidon commitment for a note (sync wrapper)
|
|
1275
|
+
* Returns bytes instead of bigint for backward compatibility
|
|
557
1276
|
*
|
|
558
|
-
* @
|
|
559
|
-
* @param prefix - Whether to include 0x prefix (default: false)
|
|
560
|
-
* @returns Hex-encoded string
|
|
1277
|
+
* @deprecated Use generateCommitmentAsync instead
|
|
561
1278
|
*/
|
|
562
|
-
declare function
|
|
1279
|
+
declare function generateCommitment(_amountLamports: number, _r: Uint8Array, _skSpend: Uint8Array): Uint8Array;
|
|
563
1280
|
/**
|
|
564
|
-
*
|
|
1281
|
+
* Compute nullifier = Poseidon(sk0, sk1, leaf_index)
|
|
1282
|
+
* (matching withdraw_regular.circom)
|
|
565
1283
|
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
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
|
|
568
1289
|
*/
|
|
569
|
-
declare function
|
|
1290
|
+
declare function computeNullifier$1(sk_spend: bigint, leafIndex: bigint): Promise<bigint>;
|
|
570
1291
|
/**
|
|
571
|
-
*
|
|
1292
|
+
* Compute nullifier from spending key and leaf index
|
|
572
1293
|
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
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
|
|
576
1301
|
*/
|
|
577
|
-
declare function
|
|
1302
|
+
declare function computeNullifierAsync(skSpend: Uint8Array | string, leafIndex: number): Promise<bigint>;
|
|
578
1303
|
/**
|
|
579
|
-
*
|
|
1304
|
+
* Compute nullifier (sync wrapper for backward compatibility)
|
|
1305
|
+
* @deprecated Use computeNullifierAsync instead
|
|
580
1306
|
*/
|
|
581
|
-
|
|
582
|
-
pi_a: string[];
|
|
583
|
-
pi_b: string[][];
|
|
584
|
-
pi_c: string[];
|
|
585
|
-
protocol: string;
|
|
586
|
-
curve: string;
|
|
587
|
-
}
|
|
1307
|
+
declare function computeNullifierSync(_skSpend: Uint8Array, _leafIndex: number): Uint8Array;
|
|
588
1308
|
/**
|
|
589
|
-
*
|
|
1309
|
+
* Compute outputs hash from recipients and amounts
|
|
590
1310
|
*
|
|
591
|
-
*
|
|
592
|
-
* - Proof_a: Must be NEGATED (as G1 point) and converted from LE to BE
|
|
593
|
-
* - Proof_b: Converted from LE to BE (reversing each 64-byte chunk)
|
|
594
|
-
* - Proof_c: Converted from LE to BE (reversing each 32-byte chunk)
|
|
1311
|
+
* Formula: Chain of Poseidon(prev_hash, addr_lo, addr_hi, amount) for each active output
|
|
595
1312
|
*
|
|
596
|
-
*
|
|
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
|
|
597
1317
|
*/
|
|
598
|
-
declare function
|
|
1318
|
+
declare function computeOutputsHashAsync(outputs: Array<{
|
|
1319
|
+
recipient: PublicKey;
|
|
1320
|
+
amount: number;
|
|
1321
|
+
}>): Promise<bigint>;
|
|
599
1322
|
/**
|
|
600
|
-
*
|
|
601
|
-
*
|
|
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
|
|
602
1332
|
*/
|
|
603
|
-
declare function
|
|
604
|
-
|
|
1333
|
+
declare function computeOutputsHash(outAddr: bigint[][], outAmount: bigint[], outFlags: number[]): Promise<bigint>;
|
|
605
1334
|
/**
|
|
606
|
-
*
|
|
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)
|
|
607
1345
|
*
|
|
608
|
-
*
|
|
609
|
-
* to prevent sybil attacks and cover operational costs.
|
|
1346
|
+
* This is the test-style function that takes raw limbs
|
|
610
1347
|
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
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
|
|
613
1354
|
*/
|
|
614
|
-
|
|
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;
|
|
1355
|
+
declare function computeSwapOutputsHash(inputMintLimbs: [bigint, bigint], outputMintLimbs: [bigint, bigint], recipientAtaLimbs: [bigint, bigint], minOutputAmount: bigint, publicAmount: bigint): Promise<bigint>;
|
|
626
1356
|
/**
|
|
627
|
-
*
|
|
1357
|
+
* Compute outputs hash for swap transactions
|
|
628
1358
|
*
|
|
629
|
-
* Formula:
|
|
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)
|
|
630
1361
|
*
|
|
631
|
-
*
|
|
632
|
-
* @returns Total fee in lamports
|
|
1362
|
+
* This matches the withdraw_swap.circom circuit
|
|
633
1363
|
*
|
|
634
|
-
* @
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
*
|
|
638
|
-
*
|
|
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
|
|
639
1370
|
*/
|
|
640
|
-
declare function
|
|
1371
|
+
declare function computeSwapOutputsHashAsync(inputMint: PublicKey, outputMint: PublicKey, recipientAta: PublicKey, minOutputAmount: number, amount: number): Promise<bigint>;
|
|
641
1372
|
/**
|
|
642
|
-
*
|
|
1373
|
+
* Compute swap outputs hash (sync wrapper for backward compatibility)
|
|
1374
|
+
* @deprecated Use computeSwapOutputsHashAsync instead
|
|
643
1375
|
*/
|
|
644
|
-
declare function
|
|
1376
|
+
declare function computeSwapOutputsHashSync(_outputMint: PublicKey, _recipientAta: PublicKey, _minOutputAmount: number, _amount: number): Uint8Array;
|
|
645
1377
|
/**
|
|
646
|
-
*
|
|
647
|
-
* (On-chain uses the same integer math and enforces this as well.)
|
|
1378
|
+
* Convert bigint to 32-byte big-endian Uint8Array
|
|
648
1379
|
*/
|
|
649
|
-
declare function
|
|
1380
|
+
declare function bigintToBytes32$1(n: bigint): Uint8Array;
|
|
650
1381
|
/**
|
|
651
|
-
*
|
|
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
|
|
1382
|
+
* Convert hex string to Uint8Array
|
|
657
1383
|
*
|
|
658
|
-
* @
|
|
659
|
-
*
|
|
660
|
-
* const distributable = getDistributableAmount(1_000_000_000);
|
|
661
|
-
* // Returns: 1_000_000_000 - 7_500_000 = 992_500_000 lamports
|
|
662
|
-
* ```
|
|
1384
|
+
* @param hex - Hex string (with or without 0x prefix)
|
|
1385
|
+
* @returns Decoded bytes
|
|
663
1386
|
*/
|
|
664
|
-
declare function
|
|
1387
|
+
declare function hexToBytes(hex: string): Uint8Array;
|
|
665
1388
|
/**
|
|
666
|
-
*
|
|
1389
|
+
* Convert Uint8Array to hex string
|
|
667
1390
|
*
|
|
668
|
-
* @param
|
|
669
|
-
* @param
|
|
670
|
-
* @returns
|
|
1391
|
+
* @param bytes - Bytes to encode
|
|
1392
|
+
* @param prefix - Whether to include 0x prefix (default: false)
|
|
1393
|
+
* @returns Hex-encoded string
|
|
1394
|
+
*/
|
|
1395
|
+
declare function bytesToHex(bytes: Uint8Array, prefix?: boolean): string;
|
|
1396
|
+
/**
|
|
1397
|
+
* Thrown when no cryptographically secure randomness source is reachable.
|
|
671
1398
|
*
|
|
672
|
-
*
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
* formatAmount(1_500_000_000); // "1.500000000"
|
|
676
|
-
* formatAmount(123_456_789, 4); // "0.1235"
|
|
677
|
-
* ```
|
|
1399
|
+
* `randomBytes` backs note spend keys, blindings, salts and nonces. A predictable
|
|
1400
|
+
* value there is unrecoverable — it deanonymizes and can drain the note — so the
|
|
1401
|
+
* SDK fails closed instead of degrading to a non-cryptographic generator.
|
|
678
1402
|
*/
|
|
679
|
-
declare
|
|
1403
|
+
declare class InsecureRandomnessError extends CloakError {
|
|
1404
|
+
constructor(message: string, originalError?: Error);
|
|
1405
|
+
}
|
|
680
1406
|
/**
|
|
681
|
-
*
|
|
1407
|
+
* Generate cryptographically secure random bytes.
|
|
682
1408
|
*
|
|
683
|
-
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
1409
|
+
* Source order: `globalThis.crypto.getRandomValues` first (works in every runtime
|
|
1410
|
+
* this SDK supports, under both ESM and CJS), then `node:crypto.randomFillSync`.
|
|
1411
|
+
* There is deliberately **no** insecure fallback: if neither source is usable this
|
|
1412
|
+
* throws {@link InsecureRandomnessError} naming every source that was tried and why
|
|
1413
|
+
* it failed.
|
|
686
1414
|
*
|
|
687
|
-
* @
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
* parseAmount("0.001"); // 1_000_000
|
|
691
|
-
* ```
|
|
1415
|
+
* @param length - Number of bytes to generate
|
|
1416
|
+
* @returns Random bytes
|
|
1417
|
+
* @throws {InsecureRandomnessError} If no cryptographically secure source is available
|
|
692
1418
|
*/
|
|
693
|
-
declare function
|
|
1419
|
+
declare function randomBytes(length: number): Uint8Array;
|
|
694
1420
|
/**
|
|
695
|
-
* Validate
|
|
1421
|
+
* Validate a hex string format
|
|
696
1422
|
*
|
|
697
|
-
* @param
|
|
698
|
-
* @param
|
|
699
|
-
* @returns True if
|
|
1423
|
+
* @param hex - Hex string to validate
|
|
1424
|
+
* @param expectedLength - Expected length in bytes (optional)
|
|
1425
|
+
* @returns True if valid hex string
|
|
700
1426
|
*/
|
|
701
|
-
declare function
|
|
702
|
-
amount: number;
|
|
703
|
-
}>, expectedTotal: number): boolean;
|
|
1427
|
+
declare function isValidHex(hex: string, expectedLength?: number): boolean;
|
|
704
1428
|
/**
|
|
705
|
-
*
|
|
1429
|
+
* Groth16 proof structure from snarkjs
|
|
1430
|
+
*/
|
|
1431
|
+
interface Groth16Proof {
|
|
1432
|
+
pi_a: string[];
|
|
1433
|
+
pi_b: string[][];
|
|
1434
|
+
pi_c: string[];
|
|
1435
|
+
protocol: string;
|
|
1436
|
+
curve: string;
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Convert snarkjs Groth16 proof to 256-byte format for Solana (pinocchio-groth16 format)
|
|
706
1440
|
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
*
|
|
1441
|
+
* Based on pinocchio-groth16/src/proof_parser.rs convert_proof function:
|
|
1442
|
+
* - Proof_a: Must be NEGATED (as G1 point) and converted from LE to BE
|
|
1443
|
+
* - Proof_b: Converted from LE to BE (reversing each 64-byte chunk)
|
|
1444
|
+
* - Proof_c: Converted from LE to BE (reversing each 32-byte chunk)
|
|
710
1445
|
*
|
|
711
|
-
*
|
|
712
|
-
* ```typescript
|
|
713
|
-
* calculateRelayFee(1_000_000_000, 50); // 0.5% = 5_000_000 lamports
|
|
714
|
-
* ```
|
|
1446
|
+
* Format: pi_a (64) + pi_b (128) + pi_c (64) = 256 bytes
|
|
715
1447
|
*/
|
|
716
|
-
declare function
|
|
1448
|
+
declare function proofToBytes(proof: Groth16Proof): Uint8Array;
|
|
1449
|
+
/**
|
|
1450
|
+
* Build public inputs bytes for on-chain verification
|
|
1451
|
+
* Format: root (32) + nullifier (32) + outputs_hash (32) + public_amount (8) = 104 bytes
|
|
1452
|
+
*/
|
|
1453
|
+
declare function buildPublicInputsBytes(root: bigint, nullifier: bigint, outputsHash: bigint, publicAmount: bigint): Uint8Array;
|
|
717
1454
|
|
|
718
1455
|
/**
|
|
719
|
-
* Validate
|
|
1456
|
+
* Validate a Solana public key
|
|
720
1457
|
*
|
|
721
1458
|
* @param address - Address string to validate
|
|
722
1459
|
* @returns True if valid Solana address
|
|
723
1460
|
*/
|
|
724
1461
|
declare function isValidSolanaAddress(address: string): boolean;
|
|
1462
|
+
/**
|
|
1463
|
+
* Validate a Cloak note structure
|
|
1464
|
+
*
|
|
1465
|
+
* @param note - Note object to validate
|
|
1466
|
+
* @throws Error if invalid
|
|
1467
|
+
*/
|
|
1468
|
+
declare function validateNote(note: any): asserts note is CloakNote;
|
|
1469
|
+
/**
|
|
1470
|
+
* Validate that a note is ready for withdrawal
|
|
1471
|
+
*
|
|
1472
|
+
* @param note - Note to validate
|
|
1473
|
+
* @throws Error if note cannot be used for withdrawal
|
|
1474
|
+
*/
|
|
1475
|
+
declare function validateWithdrawableNote(note: CloakNote): void;
|
|
1476
|
+
/**
|
|
1477
|
+
* Validate transfer recipients
|
|
1478
|
+
*
|
|
1479
|
+
* @param recipients - Array of transfers to validate
|
|
1480
|
+
* @param totalAmount - Total amount available
|
|
1481
|
+
* @throws Error if invalid
|
|
1482
|
+
*/
|
|
1483
|
+
declare function validateTransfers(recipients: Array<{
|
|
1484
|
+
recipient: PublicKey;
|
|
1485
|
+
amount: number;
|
|
1486
|
+
}>, totalAmount: number): void;
|
|
725
1487
|
|
|
726
1488
|
/**
|
|
727
1489
|
* Network Utilities
|
|
@@ -927,6 +1689,67 @@ declare class RelayInternalError extends Error {
|
|
|
927
1689
|
*/
|
|
928
1690
|
cachedTreeFromChain?: MerkleTree | undefined);
|
|
929
1691
|
}
|
|
1692
|
+
/**
|
|
1693
|
+
* Raised when a submission's outcome could NOT be established as "landed" from chain state.
|
|
1694
|
+
*
|
|
1695
|
+
* This is the failure side of settlement verification (see `core/settlement.ts`). It exists
|
|
1696
|
+
* because the campaign's worst outcome was not a failed transaction — it was an AMBIGUOUS one
|
|
1697
|
+
* whose error text carried no signature (REL-A-10: seven POSTs, the transaction landed, the SDK
|
|
1698
|
+
* threw `RelayInternalError`, and the user had nothing to look up). Losing the signature is what
|
|
1699
|
+
* turns a landed transaction into a support ticket.
|
|
1700
|
+
*
|
|
1701
|
+
* `outcome` is deliberately unambiguous for the caller:
|
|
1702
|
+
*
|
|
1703
|
+
* "landed" the transaction DID land — the input nullifier PDAs exist — but the relay never
|
|
1704
|
+
* returned a usable response, so the SDK cannot hand back commitment indices.
|
|
1705
|
+
* DO NOT RETRY: a retry spends nothing and fails 0x1020. Rescan to recover notes.
|
|
1706
|
+
* "not-landed" provably nothing landed. The inputs are unspent; retrying is safe.
|
|
1707
|
+
* "unknown" the evidence is contradictory or unavailable. Check `signature` on an
|
|
1708
|
+
* independent RPC before doing anything else.
|
|
1709
|
+
* "failed" the transaction is on chain and failed during execution. Inputs are unspent.
|
|
1710
|
+
*/
|
|
1711
|
+
declare class SettlementVerificationError extends Error {
|
|
1712
|
+
/** What the chain evidence supports. See the class doc — each value implies a different action. */
|
|
1713
|
+
readonly outcome: "landed" | "not-landed" | "unknown" | "failed";
|
|
1714
|
+
/**
|
|
1715
|
+
* The signature to look up, when one is known. `null` means no counterparty ever gave the SDK
|
|
1716
|
+
* one — which is itself part of the report, not something to paper over.
|
|
1717
|
+
*/
|
|
1718
|
+
readonly signature: string | null;
|
|
1719
|
+
/** The verifier's own statement of what was and was not proven. */
|
|
1720
|
+
readonly reason: string;
|
|
1721
|
+
/** True when the input nullifier PDAs were observed on chain. */
|
|
1722
|
+
readonly nullifiersSpent: boolean;
|
|
1723
|
+
/** The underlying relay/RPC error, when the failure started as one. */
|
|
1724
|
+
readonly cause?: unknown | undefined;
|
|
1725
|
+
constructor(message: string,
|
|
1726
|
+
/** What the chain evidence supports. See the class doc — each value implies a different action. */
|
|
1727
|
+
outcome: "landed" | "not-landed" | "unknown" | "failed",
|
|
1728
|
+
/**
|
|
1729
|
+
* The signature to look up, when one is known. `null` means no counterparty ever gave the SDK
|
|
1730
|
+
* one — which is itself part of the report, not something to paper over.
|
|
1731
|
+
*/
|
|
1732
|
+
signature: string | null,
|
|
1733
|
+
/** The verifier's own statement of what was and was not proven. */
|
|
1734
|
+
reason: string,
|
|
1735
|
+
/** True when the input nullifier PDAs were observed on chain. */
|
|
1736
|
+
nullifiersSpent?: boolean,
|
|
1737
|
+
/** The underlying relay/RPC error, when the failure started as one. */
|
|
1738
|
+
cause?: unknown | undefined);
|
|
1739
|
+
/** True when retrying this exact spend is safe (nothing was consumed on chain). */
|
|
1740
|
+
get safeToRetry(): boolean;
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
* Pull the signature out of a relay error body.
|
|
1744
|
+
*
|
|
1745
|
+
* The relay's `SubmissionOutcomeUnknown` response is a 503 whose JSON carries
|
|
1746
|
+
* `{"code":"submission_outcome_unknown","retryable":true,"signature":"<sig>"}` (relay
|
|
1747
|
+
* `src/error.rs`). The SDK used to funnel that body into a generic retry and then throw an error
|
|
1748
|
+
* built from a LATER attempt's message, dropping the one field the user needs.
|
|
1749
|
+
*/
|
|
1750
|
+
declare function parseRelayErrorSignature(responseText: string): string | null;
|
|
1751
|
+
/** True when a relay error body is the relay's own "I do not know if this landed" report. */
|
|
1752
|
+
declare function isSubmissionOutcomeUnknownResponse(responseText: string): boolean;
|
|
930
1753
|
/**
|
|
931
1754
|
* Classify a relay error (response body text + HTTP status) into one of the
|
|
932
1755
|
* structured error types above, or fall back to RelayInternalError.
|
|
@@ -1307,6 +2130,141 @@ declare function verifyUtxos(utxos: Utxo[], connection: Connection, programId: P
|
|
|
1307
2130
|
*/
|
|
1308
2131
|
declare function preflightNullifiers(utxos: Utxo[], connection: Connection, programId: PublicKey, commitment?: "processed" | "confirmed" | "finalized"): Promise<void>;
|
|
1309
2132
|
|
|
2133
|
+
/**
|
|
2134
|
+
* Settlement verification — decide from CHAIN STATE whether a submission landed.
|
|
2135
|
+
*
|
|
2136
|
+
* Why this module exists (adversarial campaign, tier 2):
|
|
2137
|
+
*
|
|
2138
|
+
* X-S-01B A hostile relay answered `/transact` with a well-formed success body carrying a
|
|
2139
|
+
* phantom signature and commitment indices [4242, 4243]. The SDK returned SUCCESS.
|
|
2140
|
+
* Real-RPC `getSignatureStatuses(searchTransactionHistory)` -> null, and BOTH input
|
|
2141
|
+
* nullifier PDAs read `exists=false`. Nothing had landed.
|
|
2142
|
+
* X-S-03a A hostile RPC swallowed `sendTransaction` and fabricated
|
|
2143
|
+
* `{err: null, confirmationStatus: "finalized"}`. `confirmTransaction` was satisfied and
|
|
2144
|
+
* the SDK returned a signature for a transaction that was never forwarded. (A control
|
|
2145
|
+
* deposit landed honestly through the same proxy, so account reads were NOT tampered
|
|
2146
|
+
* with — only the submission and its status were.)
|
|
2147
|
+
*
|
|
2148
|
+
* Both defects have the same shape: the SDK reported success on the strength of what its
|
|
2149
|
+
* counterparty SAID, never on the strength of what the chain SHOWS.
|
|
2150
|
+
*
|
|
2151
|
+
* The ground truth used here is the one the campaign itself used to prove nothing landed: the
|
|
2152
|
+
* input NULLIFIER PDAs. Their addresses are derived locally from the proof's own public inputs
|
|
2153
|
+
* (`["nullifier", pool, nullifier]`), and the program creates one per non-zero input nullifier on
|
|
2154
|
+
* every landed transact — including deposits, whose padding slots still emit real nullifiers
|
|
2155
|
+
* (transaction.circom: "the slot still emits a real nullifier ... and consumed on-chain";
|
|
2156
|
+
* shield-pool/src/instructions/transact.rs skips only all-zero nullifiers).
|
|
2157
|
+
*
|
|
2158
|
+
* Nullifier presence is strictly stronger evidence than a signature status:
|
|
2159
|
+
*
|
|
2160
|
+
* - it is ACCOUNT STATE, so it survives an RPC that has no signature history for the slot
|
|
2161
|
+
* (Surfpool forks, pruned nodes, a load-balanced endpoint that missed the write); and
|
|
2162
|
+
* - the nullifier and the output commitments are fields of the SAME proof's public inputs, so
|
|
2163
|
+
* the only transaction that can create these PDAs is one that also appended exactly our
|
|
2164
|
+
* output commitments. "Nullifiers exist" therefore means "our outputs are in the tree".
|
|
2165
|
+
*
|
|
2166
|
+
* The signature status is still checked, because it is the only signal that can prove a
|
|
2167
|
+
* DEFINITIVE FAILURE (`err != null`) and because a signature the RPC has never heard of is the
|
|
2168
|
+
* fingerprint of a phantom. It is corroboration, never the sole basis for success.
|
|
2169
|
+
*
|
|
2170
|
+
* LIMIT, stated plainly: a single RPC endpoint that lies about ACCOUNT READS as well cannot be
|
|
2171
|
+
* caught by any single-endpoint client, and this module does not pretend to. It closes the two
|
|
2172
|
+
* observed attacks — a lying relay, and an RPC that lies only about submission/status — and it
|
|
2173
|
+
* downgrades every unproven outcome to an explicit, signature-carrying "unknown" instead of
|
|
2174
|
+
* silently reporting success.
|
|
2175
|
+
*/
|
|
2176
|
+
|
|
2177
|
+
/**
|
|
2178
|
+
* The RPC surface settlement verification needs, as a structural type rather than a hard
|
|
2179
|
+
* dependency on `Connection`. A real `web3.js` Connection satisfies it; so does a test stub, which
|
|
2180
|
+
* is what lets the phantom-relay / lying-RPC regressions be reproduced without a validator.
|
|
2181
|
+
*/
|
|
2182
|
+
interface SettlementConnection {
|
|
2183
|
+
getSignatureStatuses(signatures: string[], config?: {
|
|
2184
|
+
searchTransactionHistory?: boolean;
|
|
2185
|
+
}): Promise<{
|
|
2186
|
+
value: Array<{
|
|
2187
|
+
err: unknown | null;
|
|
2188
|
+
confirmationStatus?: string | null;
|
|
2189
|
+
} | null>;
|
|
2190
|
+
}>;
|
|
2191
|
+
getMultipleAccountsInfo(publicKeys: PublicKey[], commitmentOrConfig?: any): Promise<Array<unknown | null>>;
|
|
2192
|
+
}
|
|
2193
|
+
/**
|
|
2194
|
+
* What the chain says about a submission.
|
|
2195
|
+
*
|
|
2196
|
+
* - `landed` the input nullifier PDAs exist: the proof was consumed, so its output
|
|
2197
|
+
* commitments are in the tree. This is the ONLY status that may be reported as
|
|
2198
|
+
* success.
|
|
2199
|
+
* - `failed` the signature is on chain and carries an execution error. Nothing was applied;
|
|
2200
|
+
* the inputs are still spendable.
|
|
2201
|
+
* - `not-landed` no nullifier PDA exists and the signature (if any) is unknown to the RPC even
|
|
2202
|
+
* with `searchTransactionHistory`. The phantom-signature fingerprint.
|
|
2203
|
+
* - `unknown` the evidence is contradictory or incomplete — most importantly the case where
|
|
2204
|
+
* a status claims confirmation while no nullifier PDA exists. NEVER report this
|
|
2205
|
+
* as success and NEVER silently retry it: the caller must surface the signature.
|
|
2206
|
+
*/
|
|
2207
|
+
type SettlementStatus = "landed" | "failed" | "not-landed" | "unknown";
|
|
2208
|
+
interface SettlementVerdict {
|
|
2209
|
+
status: SettlementStatus;
|
|
2210
|
+
/** The signature that was checked, when one was supplied and syntactically usable. */
|
|
2211
|
+
signature: string | null;
|
|
2212
|
+
/** Human-readable statement of what was and was not proven. Safe to put in an error message. */
|
|
2213
|
+
reason: string;
|
|
2214
|
+
/** True when every checkable input nullifier PDA was present on chain. */
|
|
2215
|
+
nullifiersSpent: boolean;
|
|
2216
|
+
/** What `getSignatureStatuses` reported for `signature`. */
|
|
2217
|
+
signatureStatus: "finalized" | "confirmed" | "processed" | "absent" | "error" | "unchecked";
|
|
2218
|
+
}
|
|
2219
|
+
interface ConfirmSettlementParams {
|
|
2220
|
+
connection: SettlementConnection;
|
|
2221
|
+
programId: PublicKey;
|
|
2222
|
+
mint: PublicKey;
|
|
2223
|
+
/** The proof's public input nullifiers. All-zero entries are padding and are skipped. */
|
|
2224
|
+
inputNullifiers: bigint[];
|
|
2225
|
+
/** The signature the counterparty reported, if it reported one. */
|
|
2226
|
+
signature?: string | null;
|
|
2227
|
+
/** Total time to wait for evidence to appear before giving a verdict. */
|
|
2228
|
+
timeoutMs?: number;
|
|
2229
|
+
pollIntervalMs?: number;
|
|
2230
|
+
onProgress?: (status: string) => void;
|
|
2231
|
+
}
|
|
2232
|
+
declare function isPlausibleSignature(value: string | null | undefined): value is string;
|
|
2233
|
+
/**
|
|
2234
|
+
* Derive the nullifier PDA for each non-zero input nullifier. Zero entries are the program's own
|
|
2235
|
+
* padding sentinel (`transact.rs`: "Skip zero nullifiers (padding inputs)") and create no account.
|
|
2236
|
+
*/
|
|
2237
|
+
declare function deriveInputNullifierPdas(programId: PublicKey, mint: PublicKey, inputNullifiers: bigint[]): PublicKey[];
|
|
2238
|
+
/**
|
|
2239
|
+
* Establish, from chain state, whether a submission landed.
|
|
2240
|
+
*
|
|
2241
|
+
* Polls until either side of the question is answered or `timeoutMs` elapses. Every RPC error is
|
|
2242
|
+
* absorbed into the verdict rather than thrown: "I could not check" is `unknown`, which the caller
|
|
2243
|
+
* must surface — it is never success.
|
|
2244
|
+
*/
|
|
2245
|
+
declare function confirmTransactSettlement(params: ConfirmSettlementParams): Promise<SettlementVerdict>;
|
|
2246
|
+
/**
|
|
2247
|
+
* Gate a DIRECT (self-signed) submission on chain state — X-S-03a.
|
|
2248
|
+
*
|
|
2249
|
+
* `connection.confirmTransaction` is not proof of anything: the campaign's proxy swallowed
|
|
2250
|
+
* `sendTransaction` and answered the follow-up status poll with a fabricated
|
|
2251
|
+
* `{err: null, confirmationStatus: "finalized"}`, and the SDK handed the caller a signature for a
|
|
2252
|
+
* transaction that was never forwarded. A control deposit landed honestly through the same proxy,
|
|
2253
|
+
* so this was not broken plumbing — it was the client believing a status field.
|
|
2254
|
+
*
|
|
2255
|
+
* Throws `SettlementVerificationError` unless the input nullifier PDAs prove the transaction was
|
|
2256
|
+
* applied. The signature is always carried on the error so the caller can look it up.
|
|
2257
|
+
*/
|
|
2258
|
+
declare function assertDirectSubmissionLanded(params: {
|
|
2259
|
+
connection: SettlementConnection;
|
|
2260
|
+
programId: PublicKey;
|
|
2261
|
+
mint: PublicKey;
|
|
2262
|
+
nullifiers: Array<Uint8Array | bigint>;
|
|
2263
|
+
signature: string;
|
|
2264
|
+
timeoutMs?: number;
|
|
2265
|
+
onProgress?: (status: string) => void;
|
|
2266
|
+
}): Promise<SettlementVerdict>;
|
|
2267
|
+
|
|
1310
2268
|
/**
|
|
1311
2269
|
* Structured Logger for Cloak SDK
|
|
1312
2270
|
*
|
|
@@ -1604,6 +2562,36 @@ declare function fetchRiskQuoteIx(connection: Connection, wallet: PublicKey, ran
|
|
|
1604
2562
|
queue: PublicKey;
|
|
1605
2563
|
}>;
|
|
1606
2564
|
|
|
2565
|
+
/**
|
|
2566
|
+
* Encrypted Output Helpers
|
|
2567
|
+
*
|
|
2568
|
+
* Functions for creating encrypted outputs that enable note scanning
|
|
2569
|
+
*/
|
|
2570
|
+
|
|
2571
|
+
/**
|
|
2572
|
+
* Prepare encrypted output for scanning by wallet owner
|
|
2573
|
+
*
|
|
2574
|
+
* @param note - Note to encrypt
|
|
2575
|
+
* @param cloakKeys - Wallet's Cloak keys (for self-encryption)
|
|
2576
|
+
* @returns Base64-encoded encrypted output
|
|
2577
|
+
*/
|
|
2578
|
+
declare function prepareEncryptedOutput(note: CloakNote, cloakKeys: CloakKeyPair): string;
|
|
2579
|
+
/**
|
|
2580
|
+
* Prepare encrypted output for a specific recipient
|
|
2581
|
+
*
|
|
2582
|
+
* @param note - Note to encrypt
|
|
2583
|
+
* @param recipientPvkHex - Recipient's public view key (hex)
|
|
2584
|
+
* @returns Base64-encoded encrypted output
|
|
2585
|
+
*/
|
|
2586
|
+
declare function prepareEncryptedOutputForRecipient(note: CloakNote, recipientPvkHex: string): string;
|
|
2587
|
+
/**
|
|
2588
|
+
* Simple base64 encoding for v1.0 notes (no encryption)
|
|
2589
|
+
*
|
|
2590
|
+
* @param note - Note to encode
|
|
2591
|
+
* @returns Base64-encoded note data
|
|
2592
|
+
*/
|
|
2593
|
+
declare function encodeNoteSimple(note: CloakNote): string;
|
|
2594
|
+
|
|
1607
2595
|
/**
|
|
1608
2596
|
* Wallet Integration Helpers
|
|
1609
2597
|
*
|
|
@@ -1631,6 +2619,58 @@ declare function signTransaction<T extends Transaction>(transaction: T, wallet:
|
|
|
1631
2619
|
*/
|
|
1632
2620
|
declare function keypairToAdapter(keypair: Keypair): WalletAdapter;
|
|
1633
2621
|
|
|
2622
|
+
/**
|
|
2623
|
+
* Create a deposit instruction
|
|
2624
|
+
*
|
|
2625
|
+
* Deposits SOL into the Cloak protocol by creating a commitment.
|
|
2626
|
+
*
|
|
2627
|
+
* Instruction format:
|
|
2628
|
+
* - Byte 0: Discriminant (0x01 for Deposit, per ShieldPoolInstruction enum)
|
|
2629
|
+
* - Bytes 1-8: Amount (u64, little-endian)
|
|
2630
|
+
* - Bytes 9-40: Commitment (32 bytes)
|
|
2631
|
+
*
|
|
2632
|
+
* @param params - Deposit parameters
|
|
2633
|
+
* @returns Transaction instruction
|
|
2634
|
+
*
|
|
2635
|
+
* @example
|
|
2636
|
+
* ```typescript
|
|
2637
|
+
* const instruction = createDepositInstruction({
|
|
2638
|
+
* programId: CLOAK_PROGRAM_ID,
|
|
2639
|
+
* payer: wallet.publicKey,
|
|
2640
|
+
* pool: POOL_ADDRESS,
|
|
2641
|
+
* commitments: COMMITMENTS_ADDRESS,
|
|
2642
|
+
* amount: 1_000_000_000, // 1 SOL
|
|
2643
|
+
* commitment: commitmentBytes
|
|
2644
|
+
* });
|
|
2645
|
+
* ```
|
|
2646
|
+
*/
|
|
2647
|
+
declare function createDepositInstruction(params: {
|
|
2648
|
+
programId: PublicKey;
|
|
2649
|
+
payer: PublicKey;
|
|
2650
|
+
pool: PublicKey;
|
|
2651
|
+
merkleTree: PublicKey;
|
|
2652
|
+
amount: number;
|
|
2653
|
+
commitment: Uint8Array;
|
|
2654
|
+
}): TransactionInstruction;
|
|
2655
|
+
/**
|
|
2656
|
+
* Deposit instruction parameters for type safety
|
|
2657
|
+
*/
|
|
2658
|
+
interface DepositInstructionParams {
|
|
2659
|
+
programId: PublicKey;
|
|
2660
|
+
payer: PublicKey;
|
|
2661
|
+
pool: PublicKey;
|
|
2662
|
+
merkleTree: PublicKey;
|
|
2663
|
+
amount: number;
|
|
2664
|
+
commitment: Uint8Array;
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* Validate deposit instruction parameters
|
|
2668
|
+
*
|
|
2669
|
+
* @param params - Parameters to validate
|
|
2670
|
+
* @throws Error if invalid
|
|
2671
|
+
*/
|
|
2672
|
+
declare function validateDepositParams(params: DepositInstructionParams): void;
|
|
2673
|
+
|
|
1634
2674
|
/**
|
|
1635
2675
|
* Program Derived Address (PDA) utilities for Shield Pool
|
|
1636
2676
|
*
|
|
@@ -1683,6 +2723,111 @@ declare function getNullifierPDA(poolPubkey: PublicKey, nullifier: Uint8Array |
|
|
|
1683
2723
|
* @returns [PublicKey, bump] - The swap state PDA and its bump seed
|
|
1684
2724
|
*/
|
|
1685
2725
|
declare function getSwapStatePDA(poolPubkey: PublicKey, nullifier: Uint8Array | Buffer, programId?: PublicKey): [PublicKey, number];
|
|
2726
|
+
/**
|
|
2727
|
+
* H-04: derive the per-pool RefundLedger PDA (conservation counter).
|
|
2728
|
+
*
|
|
2729
|
+
* Seeds: ["refund_ledger", pool_mint]
|
|
2730
|
+
*/
|
|
2731
|
+
declare function getRefundLedgerPDA(mint?: PublicKey, programId?: PublicKey): [PublicKey, number];
|
|
2732
|
+
/**
|
|
2733
|
+
* H-04: derive the one-time RefundClaim PDA (replay guard for a voucher).
|
|
2734
|
+
*
|
|
2735
|
+
* Seeds: ["refund_claim", claim_id]
|
|
2736
|
+
*/
|
|
2737
|
+
declare function getRefundClaimPDA(claimId: Uint8Array | Buffer, programId?: PublicKey): [PublicKey, number];
|
|
2738
|
+
/**
|
|
2739
|
+
* M-07/H-04: derive the per-pool PoolAuthorityConfig PDA (holds the
|
|
2740
|
+
* withdraw-authorizer the refund voucher must be signed by).
|
|
2741
|
+
*
|
|
2742
|
+
* Seeds: ["pool_authority", pool_mint]
|
|
2743
|
+
*/
|
|
2744
|
+
declare function getPoolAuthorityConfigPDA(mint?: PublicKey, programId?: PublicKey): [PublicKey, number];
|
|
2745
|
+
/**
|
|
2746
|
+
* Registry PDA the relay's recipient-delivery carrier (CLKD1) touches so the carrier transaction is
|
|
2747
|
+
* enumerable via `getSignaturesForAddress`.
|
|
2748
|
+
*
|
|
2749
|
+
* Seed: ["cloak_delivery_registry"] — `DELIVERY_REGISTRY_SEED` in
|
|
2750
|
+
* `services/relay/src/solana/mod.rs::emit_recipient_delivery_carrier`. Mint-independent, exactly
|
|
2751
|
+
* like the chain-note registry: one registry per program, not per pool.
|
|
2752
|
+
*/
|
|
2753
|
+
declare function getDeliveryRegistryPDA(programId?: PublicKey): PublicKey;
|
|
2754
|
+
/**
|
|
2755
|
+
* Registry PDA the relay's compliance chain-note carrier (CLK1) touches.
|
|
2756
|
+
*
|
|
2757
|
+
* Seed: ["cloak_chain_note_registry"] — `CHAIN_NOTE_REGISTRY_SEED` in
|
|
2758
|
+
* `services/relay/src/solana/mod.rs::emit_chain_note_carrier`.
|
|
2759
|
+
*/
|
|
2760
|
+
declare function getChainNoteRegistryPDA(programId?: PublicKey): PublicKey;
|
|
2761
|
+
|
|
2762
|
+
/**
|
|
2763
|
+
* H-04 — claim-based timeout refund (SDK).
|
|
2764
|
+
*
|
|
2765
|
+
* When a private swap times out, `CloseSwapState` returns the parked principal
|
|
2766
|
+
* to the pool and credits the on-chain `RefundLedger`. The user later reclaims
|
|
2767
|
+
* that principal as a FRESH shielded note via `ClaimRefund`, gated by a
|
|
2768
|
+
* withdraw-authorizer voucher (signed by the relay) and a deposit-shape Groth16
|
|
2769
|
+
* proof — with no on-chain link back to the original swap.
|
|
2770
|
+
*
|
|
2771
|
+
* This module:
|
|
2772
|
+
* 1. asks the relay to sign the 81-byte refund voucher for `claim_id`,
|
|
2773
|
+
* 2. generates a deposit-shape proof (publicAmount = +refund_amount, zero
|
|
2774
|
+
* inputs, one fresh output note `C_r`) bound to the live merkle root,
|
|
2775
|
+
* 3. builds and submits the `[ed25519 voucher][CU][ClaimRefund]` transaction,
|
|
2776
|
+
* 4. returns the fresh refund UTXO for the caller to store/spend later.
|
|
2777
|
+
*
|
|
2778
|
+
* The proof construction is byte-for-byte the same as the standalone
|
|
2779
|
+
* `audit-tests/h-04/gen-claim-proof.cjs` generator that the program-side soak
|
|
2780
|
+
* verified, so the proof verifies against the deployed transaction vkey.
|
|
2781
|
+
*/
|
|
2782
|
+
|
|
2783
|
+
/** A relay-signed refund voucher, as returned by `POST /refund-voucher`. */
|
|
2784
|
+
interface RefundVoucher {
|
|
2785
|
+
/** base64 Ed25519 signature over the 81-byte message. */
|
|
2786
|
+
signature: string;
|
|
2787
|
+
/** hex 81-byte voucher message. */
|
|
2788
|
+
message: string;
|
|
2789
|
+
/** base58 signer (pool withdraw-authorizer). */
|
|
2790
|
+
signer_pubkey: string;
|
|
2791
|
+
/** expiry slot bound into the voucher. */
|
|
2792
|
+
expiry_slot?: number;
|
|
2793
|
+
}
|
|
2794
|
+
interface ClaimTimeoutRefundParams {
|
|
2795
|
+
/** Refund principal in lamports (must equal the closed swap's parked amount). */
|
|
2796
|
+
refundAmount: bigint;
|
|
2797
|
+
/** Pool mint; ClaimRefund is SOL-pool only (defaults to WSOL sentinel). */
|
|
2798
|
+
poolMint?: PublicKey;
|
|
2799
|
+
/** 32-byte claim id = H(user secret). Random if omitted. */
|
|
2800
|
+
claimId?: Uint8Array;
|
|
2801
|
+
/** Supply a pre-fetched voucher to skip the relay round-trip. */
|
|
2802
|
+
voucher?: RefundVoucher;
|
|
2803
|
+
}
|
|
2804
|
+
interface ClaimTimeoutRefundOptions {
|
|
2805
|
+
connection: Connection;
|
|
2806
|
+
programId: PublicKey;
|
|
2807
|
+
/** Relay base URL (used to fetch the voucher when not supplied). */
|
|
2808
|
+
relayUrl?: string;
|
|
2809
|
+
/** Pays fees + signs the claim tx (relay-submitted is a documented follow-up). */
|
|
2810
|
+
payerKeypair: Keypair;
|
|
2811
|
+
onProgress?: (message: string) => void;
|
|
2812
|
+
}
|
|
2813
|
+
interface ClaimTimeoutRefundResult {
|
|
2814
|
+
/** Submitted ClaimRefund transaction signature. */
|
|
2815
|
+
signature: string;
|
|
2816
|
+
/** The fresh refund note created by the claim — store this to spend later. */
|
|
2817
|
+
refundUtxo: Utxo;
|
|
2818
|
+
/** The claim id consumed (hex). */
|
|
2819
|
+
claimId: string;
|
|
2820
|
+
/** The merkle leaf index where the refund note `C_r` landed. */
|
|
2821
|
+
leafIndex: number;
|
|
2822
|
+
}
|
|
2823
|
+
/**
|
|
2824
|
+
* Reclaim a timed-out swap's principal as a fresh shielded note via ClaimRefund.
|
|
2825
|
+
*
|
|
2826
|
+
* Front-running note: this self-submits the claim. The production default is
|
|
2827
|
+
* relay submission (decision #3) to avoid mempool exposure of the voucher; a
|
|
2828
|
+
* relay `/claim-refund` endpoint is the documented follow-up.
|
|
2829
|
+
*/
|
|
2830
|
+
declare function claimTimeoutRefund(params: ClaimTimeoutRefundParams, options: ClaimTimeoutRefundOptions): Promise<ClaimTimeoutRefundResult>;
|
|
1686
2831
|
|
|
1687
2832
|
/**
|
|
1688
2833
|
* On-chain Merkle proof computation
|
|
@@ -1798,7 +2943,12 @@ declare function buildMerkleTreeFromRelay(relayUrl: string, options?: {
|
|
|
1798
2943
|
maxRetries?: number;
|
|
1799
2944
|
waitForIndex?: number;
|
|
1800
2945
|
}): Promise<MerkleTree>;
|
|
1801
|
-
declare function buildMerkleTreeFromChain(connection: Connection, programId: PublicKey, merkleTree: PublicKey, onProgress?: (message: string) => void
|
|
2946
|
+
declare function buildMerkleTreeFromChain(connection: Connection, programId: PublicKey, merkleTree: PublicKey, onProgress?: (message: string) => void,
|
|
2947
|
+
/**
|
|
2948
|
+
* The pool mint this tree belongs to. Required in practice: without it a `CloseSwapState` refund
|
|
2949
|
+
* leaf cannot be attributed to a tree, and reconstruction fails closed rather than guess.
|
|
2950
|
+
*/
|
|
2951
|
+
mint?: PublicKey): Promise<MerkleTree>;
|
|
1802
2952
|
/**
|
|
1803
2953
|
* Pre-flight root validation
|
|
1804
2954
|
*
|
|
@@ -1851,6 +3001,111 @@ declare function preflightCheck(relayUrl: string, rootHex: string): Promise<{
|
|
|
1851
3001
|
estimatedWaitMs?: number;
|
|
1852
3002
|
}>;
|
|
1853
3003
|
|
|
3004
|
+
/**
|
|
3005
|
+
* The SDK's host-environment predicates, in one place (X-S-02C).
|
|
3006
|
+
*
|
|
3007
|
+
* ── The defect this closes ────────────────────────────────────────────────────────────────────
|
|
3008
|
+
* The merkle-from-chain rebuild was guarded by TWO independently written predicates that disagreed
|
|
3009
|
+
* about React Native:
|
|
3010
|
+
*
|
|
3011
|
+
* - the GATE in `core/transact.ts` — `!IS_BROWSER && isMerkleClass && relayUrl`, where
|
|
3012
|
+
* `IS_BROWSER` was `typeof window !== "undefined" || typeof globalThis.document !== "undefined"`,
|
|
3013
|
+
* with NO React-Native carve-out. React Native defines `window`, so RN read as a browser and the
|
|
3014
|
+
* last-resort chain replay was silently skipped there.
|
|
3015
|
+
* - the BUILDER it guards — `utils/relay-client.ts::buildMerkleTreeFromChain`, whose own
|
|
3016
|
+
* `isBrowser()` short-circuits on `navigator.product === "ReactNative"` and therefore explicitly
|
|
3017
|
+
* PERMITS React Native to rebuild from chain.
|
|
3018
|
+
*
|
|
3019
|
+
* So one half of the same mechanism classified RN as a browser and the other half classified it as
|
|
3020
|
+
* not-a-browser.
|
|
3021
|
+
*
|
|
3022
|
+
* ── The decision: the BUILDER is right, and the gate was wrong ────────────────────────────────
|
|
3023
|
+
* The browser fails closed for a stated reason — a full signature-history scan is too slow and too
|
|
3024
|
+
* unreliable on a browser main thread, and a browser always has a reachable relay whose tree it can
|
|
3025
|
+
* use instead. Neither half of that reasoning holds for React Native: it is not a browser, it has no
|
|
3026
|
+
* DOM, it is not running on a page's main thread, and on a local node it has no relay-tree fallback
|
|
3027
|
+
* at all. Closing the gate against RN removed its ONLY recovery path from a drifted relay tree —
|
|
3028
|
+
* precisely the class of failure the fallback exists for — while leaving the builder it calls happy
|
|
3029
|
+
* to serve it.
|
|
3030
|
+
*
|
|
3031
|
+
* The alternative reading (make the builder refuse RN too, so the two agree by failing closed
|
|
3032
|
+
* everywhere) was rejected: it agrees by breaking the Cloak mobile wallet, whose merkle path depends
|
|
3033
|
+
* on the carve-out, and it discards a documented, deliberate decision in favour of an undocumented
|
|
3034
|
+
* accident. The gate's own comment never mentions React Native — it argues about SSR — which is what
|
|
3035
|
+
* an accident looks like.
|
|
3036
|
+
*
|
|
3037
|
+
* ── Two predicates, deliberately, and the difference is not RN ────────────────────────────────
|
|
3038
|
+
* `isBrowser()` is a HARD REFUSAL — "this host cannot do it at all" — so it demands a real DOM
|
|
3039
|
+
* (`window` AND `document`).
|
|
3040
|
+
* `isBrowserLike()` is a CONSERVATIVE OPT-OUT — "don't start something slow here" — so `window` OR
|
|
3041
|
+
* `document` is enough, which keeps an SSR host that polyfills only `document` from being mistaken
|
|
3042
|
+
* for Node and made to attempt a chain replay.
|
|
3043
|
+
*
|
|
3044
|
+
* They differ on SSR on purpose. They must NEVER differ on React Native again, which is why both are
|
|
3045
|
+
* built from the single `isReactNative()` below.
|
|
3046
|
+
*
|
|
3047
|
+
* All three are functions, not module-scope constants: a constant is frozen at import time, so any
|
|
3048
|
+
* host that installs its globals after the bundle loads — and any test that wants to pin the three
|
|
3049
|
+
* environments — reads a stale answer.
|
|
3050
|
+
*/
|
|
3051
|
+
/**
|
|
3052
|
+
* True on React Native. `navigator.product === "ReactNative"` is the canonical flag and is what
|
|
3053
|
+
* `utils/proof-generation.ts` has always used.
|
|
3054
|
+
*/
|
|
3055
|
+
declare function isReactNative(): boolean;
|
|
3056
|
+
/**
|
|
3057
|
+
* True only on a real browser: a DOM host with both `window` and `document`, and not React Native.
|
|
3058
|
+
*
|
|
3059
|
+
* Use for HARD refusals — work this host genuinely cannot perform.
|
|
3060
|
+
*/
|
|
3061
|
+
declare function isBrowser(): boolean;
|
|
3062
|
+
/**
|
|
3063
|
+
* True on a browser OR on a DOM-ish host such as SSR that defines only one of `window`/`document`,
|
|
3064
|
+
* and false on React Native and on Node.
|
|
3065
|
+
*
|
|
3066
|
+
* Use for CONSERVATIVE opt-outs — expensive work that should not be started speculatively.
|
|
3067
|
+
*/
|
|
3068
|
+
declare function isBrowserLike(): boolean;
|
|
3069
|
+
|
|
3070
|
+
/**
|
|
3071
|
+
* Circuit artifact releases — the single source of truth for
|
|
3072
|
+
* "which bundle version" and "which bytes are that bundle".
|
|
3073
|
+
*
|
|
3074
|
+
* Why this file exists
|
|
3075
|
+
* -------------------
|
|
3076
|
+
* The version segment of the artifact base URL and the pinned SHA-256 digests
|
|
3077
|
+
* used to be written out by hand in two unrelated places
|
|
3078
|
+
* (`utils/proof-generation.ts` held `.../circuits/0.1.0` plus a digest table,
|
|
3079
|
+
* `core/transact.ts` held its own copy of the same URL literal). They drifted:
|
|
3080
|
+
* the default base pointed at the `0.1.0` bundle while the pinned `transaction`
|
|
3081
|
+
* digests were the `0.2.0` trusted-setup ceremony output, so every default-config
|
|
3082
|
+
* proof died on a digest mismatch several megabytes into proof generation.
|
|
3083
|
+
*
|
|
3084
|
+
* Here a bundle is declared once, as a version plus the digests of the artifacts
|
|
3085
|
+
* that version contains, and the base URL is *derived* from that version by
|
|
3086
|
+
* {@link defineCircuitBundle}. There is no way to write a URL whose version
|
|
3087
|
+
* segment disagrees with the digests next to it:
|
|
3088
|
+
*
|
|
3089
|
+
* - **compile time** — `baseUrl` is typed `` `${string}/circuits/${V}` ``, where
|
|
3090
|
+
* `V` is the bundle's own `version` literal, and {@link BUNDLE_BY_CIRCUIT} is
|
|
3091
|
+
* an exhaustive `Record<CircuitName, …>`, so a new circuit with no bundle, or
|
|
3092
|
+
* an assertion about a version the bundle does not carry, fails `tsc`.
|
|
3093
|
+
* - **startup** — {@link assertCircuitReleaseConsistency} runs at module load
|
|
3094
|
+
* and throws if a bundle's digests are malformed, if a bundle does not pin the
|
|
3095
|
+
* circuit that maps to it, or if a hand-edited base URL stops ending in its own
|
|
3096
|
+
* version. That is an import-time failure, not a failure deep inside
|
|
3097
|
+
* `groth16.fullProve`.
|
|
3098
|
+
*
|
|
3099
|
+
* Publication is deliberately *not* assumed. A bundle whose bytes this SDK has
|
|
3100
|
+
* not verified at a public location carries `baseUrl: null`; callers must point
|
|
3101
|
+
* the SDK at a location themselves (`setCircuitsPath`). Nothing here silently
|
|
3102
|
+
* defaults to a URL that has not been checked against the digests below.
|
|
3103
|
+
*/
|
|
3104
|
+
/** Circuits whose artifacts carry a pinned SHA-256 digest in this SDK. */
|
|
3105
|
+
type CircuitName = 'withdraw_regular' | 'withdraw_swap' | 'transaction';
|
|
3106
|
+
/** Version of the trusted-setup ceremony bundle that froze the `transaction` circuit. */
|
|
3107
|
+
declare const TRANSACTION_CIRCUITS_VERSION = "0.2.0";
|
|
3108
|
+
|
|
1854
3109
|
/**
|
|
1855
3110
|
* UTXO Transaction Methods
|
|
1856
3111
|
*
|
|
@@ -1860,23 +3115,73 @@ declare function preflightCheck(relayUrl: string, rootHex: string): Promise<{
|
|
|
1860
3115
|
* - partialWithdraw(): Withdraw with change
|
|
1861
3116
|
*/
|
|
1862
3117
|
|
|
1863
|
-
|
|
3118
|
+
/**
|
|
3119
|
+
* Can this host attempt the last-resort "rebuild the merkle tree from chain" recovery?
|
|
3120
|
+
*
|
|
3121
|
+
* X-S-02C: this gate and the builder it guards (`buildMerkleTreeFromChain`, which refuses only on a
|
|
3122
|
+
* real browser and EXPLICITLY permits React Native) used to be written separately and disagreed —
|
|
3123
|
+
* RN defines `window`, so the old inline `typeof window !== "undefined" || typeof document !==
|
|
3124
|
+
* "undefined"` classified RN as a browser and skipped the rebuild, removing RN's only recovery path
|
|
3125
|
+
* from a drifted relay tree while the builder was perfectly willing to serve it. Both now come from
|
|
3126
|
+
* `utils/environment`, so they cannot disagree about React Native again.
|
|
3127
|
+
*
|
|
3128
|
+
* The SSR conservatism is kept: `isBrowserLike()` is true when EITHER `window` or `document` exists,
|
|
3129
|
+
* because an SSR host that polyfills only `document` must not be mistaken for Node and made to run a
|
|
3130
|
+
* full signature-history scan.
|
|
3131
|
+
*
|
|
3132
|
+
* A function, not a module-scope constant: the constant was frozen at import time, so a host that
|
|
3133
|
+
* installs its globals after the bundle loads read a stale answer.
|
|
3134
|
+
*/
|
|
3135
|
+
declare function canRebuildMerkleTreeFromChain(): boolean;
|
|
3136
|
+
|
|
3137
|
+
/**
|
|
3138
|
+
* Base the ceremony-frozen `transaction` artifacts are fetched from by default.
|
|
3139
|
+
*
|
|
3140
|
+
* Derived from {@link TRANSACTION_CIRCUIT_BUNDLE} — the same record that pins the
|
|
3141
|
+
* digests — so the version in the URL and the digests checked against it cannot
|
|
3142
|
+
* drift apart. It is `null` while this SDK build pins no location whose bytes
|
|
3143
|
+
* were verified to hash to those digests; that makes an unconfigured SDK a
|
|
3144
|
+
* `tsc` error at the call site (`setCircuitsPath(DEFAULT_TRANSACTION_CIRCUITS_URL)`
|
|
3145
|
+
* does not type-check against `string`) and an immediate, explanatory throw at
|
|
3146
|
+
* runtime, instead of a digest mismatch ~22 MB into proof generation.
|
|
3147
|
+
*/
|
|
3148
|
+
declare const DEFAULT_TRANSACTION_CIRCUITS_URL: string | null;
|
|
3149
|
+
|
|
1864
3150
|
/**
|
|
1865
3151
|
* Set circuits base path: local directory containing `transaction_js/` and `transaction_final.zkey`,
|
|
1866
|
-
* or an `http(s)` base URL to those artifacts (
|
|
3152
|
+
* or an `http(s)` base URL to those artifacts (loaded into memory once per process).
|
|
3153
|
+
*
|
|
3154
|
+
* No cache invalidation is needed here: verified artifact buffers are memoised
|
|
3155
|
+
* per base inside `proof-generation.ts`, so a new base loads and re-verifies its
|
|
3156
|
+
* own bytes.
|
|
1867
3157
|
*/
|
|
1868
3158
|
declare function setCircuitsPath(next: string): void;
|
|
1869
3159
|
/**
|
|
1870
|
-
* Get the current circuits path
|
|
3160
|
+
* Get the current circuits path, or `null` when none is configured and this SDK
|
|
3161
|
+
* build pins no verified default (see `DEFAULT_TRANSACTION_CIRCUITS_URL`).
|
|
3162
|
+
*/
|
|
3163
|
+
declare function getCircuitsPath(): string | null;
|
|
3164
|
+
/**
|
|
3165
|
+
* Resolve a base to prove from: an explicit argument, else `CLOAK_CIRCUITS_PATH`
|
|
3166
|
+
* / `CLOAK_CIRCUITS` from the environment, else this build's pinned default.
|
|
3167
|
+
*
|
|
3168
|
+
* Use this instead of writing an artifact URL out by hand — a hand-written URL
|
|
3169
|
+
* is exactly how the base's version segment came to disagree with the digests
|
|
3170
|
+
* this SDK checks against. Throws an explanatory error (naming the expected
|
|
3171
|
+
* bundle version and both expected digests) when nothing resolves.
|
|
1871
3172
|
*/
|
|
1872
|
-
declare function
|
|
1873
|
-
declare function computeChainNoteHash(publicAmount: bigint, extDataHash: bigint, noteTimestamp: bigint, noteCommitment: bigint): Promise<bigint>;
|
|
3173
|
+
declare function resolveCircuitsBase(explicit?: string): string;
|
|
1874
3174
|
/**
|
|
1875
|
-
*
|
|
3175
|
+
* Chain note v4.
|
|
3176
|
+
*
|
|
3177
|
+
* v4 binds noteSemantics = Poseidon(outAmount[0], outPubkey[0], noteIsSendToSelfKey0) into the note
|
|
3178
|
+
* tail, matching the ceremony circuit. The tail is therefore a 4-input hash, not 3.
|
|
1876
3179
|
*
|
|
1877
|
-
*
|
|
3180
|
+
* noteIsSendToSelfKey0 is 1 only when publicAmount == 0 AND output 0 went to the spender's own key,
|
|
3181
|
+
* exactly as the circuit computes it.
|
|
1878
3182
|
*/
|
|
1879
|
-
declare function
|
|
3183
|
+
declare function computeChainNoteHash(publicAmount: bigint, extDataHash: bigint, noteTimestamp: bigint, noteCommitment: bigint, noteSalt: bigint, outAmount0: bigint, outPubkey0: bigint, noteIsSendToSelfKey0: bigint): Promise<bigint>;
|
|
3184
|
+
declare function computeExtDataHash(recipient: PublicKey | null, relayerFee: bigint, relayer: PublicKey | null, maxFee?: bigint): Promise<bigint>;
|
|
1880
3185
|
/**
|
|
1881
3186
|
* Options for transact operation
|
|
1882
3187
|
*/
|
|
@@ -1894,8 +3199,8 @@ interface TransactOptions {
|
|
|
1894
3199
|
/** Relayer address for fee payment */
|
|
1895
3200
|
relayer?: PublicKey;
|
|
1896
3201
|
/**
|
|
1897
|
-
* Relay URL
|
|
1898
|
-
*
|
|
3202
|
+
* Relay URL. Never defaulted: resolved from this option, then from `CLOAK_RELAY_URL`.
|
|
3203
|
+
* Name production explicitly (`https://api.cloak.ag`).
|
|
1899
3204
|
*/
|
|
1900
3205
|
relayUrl?: string;
|
|
1901
3206
|
/** Keypair of the depositor (signs the deposit transaction) - for programmatic use */
|
|
@@ -1981,6 +3286,13 @@ interface TransactOptions {
|
|
|
1981
3286
|
* Each entry must be base64-encoded note bytes.
|
|
1982
3287
|
*/
|
|
1983
3288
|
encryptedNotes?: string[];
|
|
3289
|
+
/**
|
|
3290
|
+
* Omit the on-chain encrypted chain-note envelope entirely. The chainNoteHash public input
|
|
3291
|
+
* is unaffected (proof still binds it); only the optional ciphertext blob the program emits
|
|
3292
|
+
* for wallet auto-scan is dropped. Use when the caller tracks UTXOs out-of-band and needs the
|
|
3293
|
+
* smaller packet — e.g. V3 + SPL deposits that would otherwise overflow the 1232-byte limit.
|
|
3294
|
+
*/
|
|
3295
|
+
disableChainNotes?: boolean;
|
|
1984
3296
|
/**
|
|
1985
3297
|
* Optional nk (32 bytes, hex or bytes) for diversified chain note encryption (Phase 3).
|
|
1986
3298
|
* When provided, 2 per-output encrypted notes are embedded on-chain.
|
|
@@ -1992,6 +3304,32 @@ interface TransactOptions {
|
|
|
1992
3304
|
* passing nk at every call site — e.g. pass a getter from your key manager.
|
|
1993
3305
|
*/
|
|
1994
3306
|
getChainNoteViewingKeyNk?: () => Promise<string | Uint8Array | null>;
|
|
3307
|
+
/**
|
|
3308
|
+
* Pin the chain note's 96-bit `noteSalt` instead of drawing a fresh one (VK-01).
|
|
3309
|
+
*
|
|
3310
|
+
* Pass the `noteSalt` returned by {@link createRecoverableDepositUtxo}. That helper derives the
|
|
3311
|
+
* deposit note's keypair and blinding as `PRF(nk, noteSalt)`, and the chain note is the ONLY place
|
|
3312
|
+
* the salt is published — so a cold `(rpc, programId, nk)` scan can only rebuild the note if the
|
|
3313
|
+
* salt that anchored it is the salt that reaches the note. Leaving this unset draws a random salt,
|
|
3314
|
+
* which is correct for every non-derived flow.
|
|
3315
|
+
*
|
|
3316
|
+
* Requires an explicit `chainNoteViewingKeyNk` / `getChainNoteViewingKeyNk`; `transact` refuses
|
|
3317
|
+
* otherwise rather than pairing the salt with an nk inferred from the note's own key.
|
|
3318
|
+
*/
|
|
3319
|
+
chainNoteSalt?: bigint;
|
|
3320
|
+
/**
|
|
3321
|
+
* The RECIPIENT's 32-byte X25519 public viewing key, for a shield-to-shield send.
|
|
3322
|
+
*
|
|
3323
|
+
* Supply `deriveViewingKeyFromNk(recipientNk).publicKey` (hex or bytes). When present, the SDK
|
|
3324
|
+
* seals `{amount, blinding}` of output 0 to this key and ships it as `recipient_delivery_notes`,
|
|
3325
|
+
* which the relay publishes as a CLKD1 carrier the recipient can find with their `nk` alone.
|
|
3326
|
+
*
|
|
3327
|
+
* When absent, the send still succeeds but the recipient CANNOT discover the note from chain —
|
|
3328
|
+
* it must be handed over out of band. That was the shipped behaviour and is what campaign rows
|
|
3329
|
+
* S2S-08 / VK-01 measured. Ignored on deposits and withdrawals: the relay rejects the field
|
|
3330
|
+
* outright on anything that is not a send.
|
|
3331
|
+
*/
|
|
3332
|
+
recipientViewingPublicKey?: Uint8Array | string;
|
|
1995
3333
|
/**
|
|
1996
3334
|
* Cached Merkle tree from a previous transaction.
|
|
1997
3335
|
* When provided, the SDK skips fetching commitments from the relay and uses this
|
|
@@ -2067,17 +3405,119 @@ interface RiskQuoteInstructionResponse {
|
|
|
2067
3405
|
data: string;
|
|
2068
3406
|
};
|
|
2069
3407
|
}
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
* The backend should call Switchboard's fetchQuoteIx for the given wallet
|
|
2073
|
-
* (Range Risk API) and return the serialized instruction.
|
|
2074
|
-
* See: https://www.range.org/blog/integrate-range-onchain-risk-verifier-into-your-solana-program
|
|
2075
|
-
*/
|
|
2076
|
-
declare function fetchRiskQuoteInstruction(riskQuoteUrl: string, wallet: PublicKey, options?: {
|
|
3408
|
+
declare function fetchRiskQuoteInstruction(riskQuoteUrl: string, wallet: PublicKey, options: {
|
|
3409
|
+
poolMint: PublicKey;
|
|
2077
3410
|
recipient?: PublicKey;
|
|
2078
3411
|
amount?: bigint;
|
|
2079
3412
|
token?: PublicKey;
|
|
3413
|
+
commitment?: string | Uint8Array;
|
|
3414
|
+
context?: "deposit" | "send" | "withdraw";
|
|
3415
|
+
nullifier0?: string;
|
|
3416
|
+
nullifier1?: string;
|
|
2080
3417
|
}): Promise<TransactionInstruction>;
|
|
3418
|
+
/** Everything the `/transact` relay body is built from. */
|
|
3419
|
+
interface TransactRequestBodyParams {
|
|
3420
|
+
proofB64: string;
|
|
3421
|
+
publicInputsB64: string;
|
|
3422
|
+
mint: PublicKey;
|
|
3423
|
+
recipient?: PublicKey | null;
|
|
3424
|
+
relayer?: PublicKey | null;
|
|
3425
|
+
relayerFee: bigint;
|
|
3426
|
+
maxFee: bigint;
|
|
3427
|
+
encryptedNotes?: string[];
|
|
3428
|
+
riskQuote?: {
|
|
3429
|
+
signature: string;
|
|
3430
|
+
message: string;
|
|
3431
|
+
signer_pubkey: string;
|
|
3432
|
+
};
|
|
3433
|
+
/** Present only on shield-to-shield sends, where the relay enforces sanctions on the sender. */
|
|
3434
|
+
sender?: PublicKey | null;
|
|
3435
|
+
/** Signed public amount; zero is a shield-to-shield send. */
|
|
3436
|
+
externalAmount: bigint;
|
|
3437
|
+
/** Base64 CLKD1 envelopes, from `buildRecipientDeliveryNotes`. Omitted when the rail is off. */
|
|
3438
|
+
recipientDeliveryNotes?: string[];
|
|
3439
|
+
}
|
|
3440
|
+
/**
|
|
3441
|
+
* Assemble the `/transact` body.
|
|
3442
|
+
*
|
|
3443
|
+
* Extracted so the wire shape is testable without a proof. The field set is a contract with
|
|
3444
|
+
* `services/relay/src/api/transact.rs`, and `recipient_delivery_notes` in particular is part of the
|
|
3445
|
+
* relay's signed field view (`TRANSACT_AUTH_FIELDS`) — so a body that carries it must be signed
|
|
3446
|
+
* with it present, and a body that omits it must be signed with an explicit null. `buildAuthRequest`
|
|
3447
|
+
* handles that, but only if the field is genuinely absent rather than set to `undefined`-ish values.
|
|
3448
|
+
*/
|
|
3449
|
+
declare function buildTransactRequestBody(params: TransactRequestBodyParams): Record<string, unknown>;
|
|
3450
|
+
/** Everything settlement verification needs to check THIS proof against chain state. */
|
|
3451
|
+
interface SettlementContext {
|
|
3452
|
+
connection: SettlementConnection;
|
|
3453
|
+
programId: PublicKey;
|
|
3454
|
+
mint: PublicKey;
|
|
3455
|
+
/** The proof's public input nullifiers — the ground truth for "did this land". */
|
|
3456
|
+
inputNullifiers: bigint[];
|
|
3457
|
+
}
|
|
3458
|
+
type RelaySubmissionResult = {
|
|
3459
|
+
kind: "submitted";
|
|
3460
|
+
signature: string;
|
|
3461
|
+
commitmentIndices?: [number, number];
|
|
3462
|
+
viewingKeyRegistered?: boolean;
|
|
3463
|
+
settlement: SettlementVerdict;
|
|
3464
|
+
}
|
|
3465
|
+
/** The relay rejected the proof's root; the caller must rebuild the tree and re-prove. */
|
|
3466
|
+
| {
|
|
3467
|
+
kind: "stale-root";
|
|
3468
|
+
error: Error;
|
|
3469
|
+
} | {
|
|
3470
|
+
kind: "failed";
|
|
3471
|
+
error: Error;
|
|
3472
|
+
};
|
|
3473
|
+
interface SubmitTransactToRelayArgs {
|
|
3474
|
+
relayUrl: string;
|
|
3475
|
+
/** The exact body to POST. Auth fields are added ONCE, in place, and then never changed. */
|
|
3476
|
+
requestBody: Record<string, unknown>;
|
|
3477
|
+
programId: PublicKey;
|
|
3478
|
+
depositorKeypair?: Keypair;
|
|
3479
|
+
settlement: SettlementContext;
|
|
3480
|
+
/** True while the caller still has a re-prove budget for a stale root. */
|
|
3481
|
+
canRetryStaleRoot: boolean;
|
|
3482
|
+
maxNetworkRetries?: number;
|
|
3483
|
+
requestTimeoutMs?: number;
|
|
3484
|
+
/** Injection seam for tests; defaults to global fetch. */
|
|
3485
|
+
fetchImpl?: typeof fetch;
|
|
3486
|
+
onProgress?: (status: string) => void;
|
|
3487
|
+
/** Total window allowed for the on-chain settlement check after a reported success. */
|
|
3488
|
+
settlementTimeoutMs?: number;
|
|
3489
|
+
/** Shorter window used to ask "did it land anyway?" after a terminal relay failure. */
|
|
3490
|
+
failureProbeTimeoutMs?: number;
|
|
3491
|
+
}
|
|
3492
|
+
/**
|
|
3493
|
+
* POST one logical `/transact` request and REPORT ONLY WHAT THE CHAIN CONFIRMS.
|
|
3494
|
+
*
|
|
3495
|
+
* Extracted from `transact()` so the two campaign defects it fixes are reproducible without a
|
|
3496
|
+
* validator: a stub `fetchImpl` plays the hostile relay, a stub `SettlementConnection` plays the
|
|
3497
|
+
* RPC. Its control flow is the caller's old inline network-retry loop, unchanged except where
|
|
3498
|
+
* noted below.
|
|
3499
|
+
*
|
|
3500
|
+
* THREE fixes live here, all of them the same root cause — trusting the counterparty:
|
|
3501
|
+
*
|
|
3502
|
+
* 1. REL-A-1 (root cause of REL-A-10): the request is signed ONCE, before the retry loop, and
|
|
3503
|
+
* every retry re-POSTs the byte-identical body. The relay's recovery contract is keyed on
|
|
3504
|
+
* `(auth_nonce, endpoint, sender, request_digest)` — `request_auth.rs::authenticate_relay_request`
|
|
3505
|
+
* — and `handle_transact` answers an exact replay from its durable row: `Completed` replays the
|
|
3506
|
+
* stored response, `Prepared` reconciles the stored signature against chain, `Processing`
|
|
3507
|
+
* returns a retryable 503. The shipped client re-signed with a FRESH `randomUUID()` nonce inside
|
|
3508
|
+
* the loop, so every retry was a NEW request that could only collide with its own predecessor's
|
|
3509
|
+
* nullifier reservation (503) — measured: 7 POSTs, 7 nonces, byte-identical business fields, and
|
|
3510
|
+
* the recovery path unreachable. Re-signing here is not a fallback: a nonce that has aged past
|
|
3511
|
+
* the relay's 300s freshness window is still accepted for an EXACT replay whose row exists, and
|
|
3512
|
+
* when no row exists nothing was mutated, so the 401 is both correct and safe.
|
|
3513
|
+
* 2. X-S-01B: a reported success is verified against chain before it is returned. The relay's word
|
|
3514
|
+
* plus its commitment indices are not evidence; the input nullifier PDAs are.
|
|
3515
|
+
* 3. REL-A-10 / (b): the signature the relay puts in a `submission_outcome_unknown` body is
|
|
3516
|
+
* captured across attempts, and every terminal failure that could plausibly have been submitted
|
|
3517
|
+
* is resolved against chain into a `SettlementVerificationError` that states the outcome and
|
|
3518
|
+
* carries the signature.
|
|
3519
|
+
*/
|
|
3520
|
+
declare function submitTransactToRelay(args: SubmitTransactToRelayArgs): Promise<RelaySubmissionResult>;
|
|
2081
3521
|
declare function transact(params: TransactParams, options: TransactOptions): Promise<TransactResult>;
|
|
2082
3522
|
/**
|
|
2083
3523
|
* Execute a shield-to-shield transfer
|
|
@@ -2142,6 +3582,25 @@ interface UtxoSwapResult extends TransactResult {
|
|
|
2142
3582
|
nullifier: string;
|
|
2143
3583
|
/** Relay request ID for swap execution status */
|
|
2144
3584
|
requestId?: string;
|
|
3585
|
+
/**
|
|
3586
|
+
* V3-04 refund-fallback secret. **Persist this** to recover the swap principal if the swap times
|
|
3587
|
+
* out before TX2. On a timeout close the program builds the amount-bound note
|
|
3588
|
+
* R_fb = Poseidon(amountAfterFee, publicKey, blinding, WSOL)
|
|
3589
|
+
* where `amountAfterFee` is the principal it locked (read authoritatively from the on-chain
|
|
3590
|
+
* `SwapState.sol_amount`, = gross swap amount minus the on-chain swap fee). `ClaimRefund` then spends
|
|
3591
|
+
* `R_fb` with `privateKey` (a plain ZK membership proof of the refund tree). All values are hex.
|
|
3592
|
+
*
|
|
3593
|
+
* VK-02: when `derivedFromNk` is true this secret is ALSO recoverable from the wallet's `nk` plus
|
|
3594
|
+
* the swap's public first input nullifier — see `matchSwapRefundLeaf`. Persisting it is still the
|
|
3595
|
+
* fast path; losing it is no longer terminal. When false (no `nk` was supplied to the swap) this
|
|
3596
|
+
* object is the only copy that will ever exist.
|
|
3597
|
+
*/
|
|
3598
|
+
refund: {
|
|
3599
|
+
privateKey: string;
|
|
3600
|
+
publicKey: string;
|
|
3601
|
+
blinding: string;
|
|
3602
|
+
derivedFromNk: boolean;
|
|
3603
|
+
};
|
|
2145
3604
|
}
|
|
2146
3605
|
/**
|
|
2147
3606
|
* Execute a UTXO swap withdrawal
|
|
@@ -2171,24 +3630,755 @@ declare function swapUtxo(params: UtxoSwapParams, options: TransactOptions): Pro
|
|
|
2171
3630
|
declare function swapWithChange(inputUtxos: Utxo[], swapAmount: bigint, outputMint: PublicKey, recipientAta: PublicKey, minOutputAmount: bigint, options: TransactOptions, recipientWallet?: PublicKey): Promise<UtxoSwapResult>;
|
|
2172
3631
|
|
|
2173
3632
|
/**
|
|
2174
|
-
*
|
|
3633
|
+
* Direct Circom WASM Proof Generation
|
|
3634
|
+
*
|
|
3635
|
+
* This module provides direct proof generation using snarkjs and Circom WASM,
|
|
3636
|
+
* matching the approach used in services-new/tests/src/proof.ts
|
|
3637
|
+
*
|
|
3638
|
+
* Artifacts come from the pinned per-bundle hosts in `config/circuit-release`,
|
|
3639
|
+
* verified by digest; no backend prover service is required.
|
|
3640
|
+
*/
|
|
3641
|
+
|
|
3642
|
+
/**
|
|
3643
|
+
* Default base URL for the legacy `withdraw_regular` / `withdraw_swap` artifacts.
|
|
3644
|
+
*
|
|
3645
|
+
* Derived from {@link LEGACY_WITHDRAW_CIRCUIT_BUNDLE}, so the version segment is
|
|
3646
|
+
* the same one the pinned digests were declared under. Do not write this URL out
|
|
3647
|
+
* by hand anywhere — change the bundle instead.
|
|
3648
|
+
*
|
|
3649
|
+
* This is NOT the base for the ceremony-frozen `transaction` circuit: that
|
|
3650
|
+
* circuit lives in a different bundle ({@link TRANSACTION_CIRCUIT_BUNDLE}) with
|
|
3651
|
+
* different digests, and is configured through `setCircuitsPath()`.
|
|
3652
|
+
*
|
|
3653
|
+
* `string | null` — null when the bundle has no published host, which is the
|
|
3654
|
+
* case for the legacy withdraw artifacts.
|
|
3655
|
+
*
|
|
3656
|
+
* RESOLVED LAZILY ON PURPOSE. This was previously
|
|
3657
|
+
* `requirePinnedBaseUrl('withdraw_regular')` evaluated at module scope, so an
|
|
3658
|
+
* unpinned bundle made merely IMPORTING the SDK throw — breaking every consumer,
|
|
3659
|
+
* including those that never touch a legacy withdraw path. The diagnostic is
|
|
3660
|
+
* still raised, by `resolveCircuitsUrl` and `getDefaultCircuitsPath` at the
|
|
3661
|
+
* point of use, where a caller can actually act on it.
|
|
3662
|
+
*/
|
|
3663
|
+
declare const DEFAULT_CIRCUITS_URL: string | null;
|
|
3664
|
+
interface WithdrawRegularInputs {
|
|
3665
|
+
root: bigint;
|
|
3666
|
+
nullifier: bigint;
|
|
3667
|
+
outputs_hash: bigint;
|
|
3668
|
+
public_amount: bigint;
|
|
3669
|
+
amount: bigint;
|
|
3670
|
+
leaf_index: bigint;
|
|
3671
|
+
sk: [bigint, bigint];
|
|
3672
|
+
r: [bigint, bigint];
|
|
3673
|
+
pathElements: bigint[];
|
|
3674
|
+
pathIndices: number[];
|
|
3675
|
+
num_outputs: number;
|
|
3676
|
+
out_addr: bigint[][];
|
|
3677
|
+
out_amount: bigint[];
|
|
3678
|
+
out_flags: number[];
|
|
3679
|
+
var_fee: bigint;
|
|
3680
|
+
rem: bigint;
|
|
3681
|
+
}
|
|
3682
|
+
interface WithdrawSwapInputs {
|
|
3683
|
+
sk_spend: bigint;
|
|
3684
|
+
r: bigint;
|
|
3685
|
+
amount: bigint;
|
|
3686
|
+
leaf_index: bigint;
|
|
3687
|
+
path_elements: bigint[];
|
|
3688
|
+
path_indices: number[];
|
|
3689
|
+
root: bigint;
|
|
3690
|
+
nullifier: bigint;
|
|
3691
|
+
outputs_hash: bigint;
|
|
3692
|
+
public_amount: bigint;
|
|
3693
|
+
input_mint: bigint[];
|
|
3694
|
+
output_mint: bigint[];
|
|
3695
|
+
recipient_ata: bigint[];
|
|
3696
|
+
min_output_amount: bigint;
|
|
3697
|
+
var_fee: bigint;
|
|
3698
|
+
rem: bigint;
|
|
3699
|
+
}
|
|
3700
|
+
interface ProofResult {
|
|
3701
|
+
proof: Groth16Proof;
|
|
3702
|
+
publicSignals: string[];
|
|
3703
|
+
proofBytes: Uint8Array;
|
|
3704
|
+
publicInputsBytes: Uint8Array;
|
|
3705
|
+
}
|
|
3706
|
+
/**
|
|
3707
|
+
* Generate Groth16 proof for regular withdrawal using Circom WASM
|
|
3708
|
+
*
|
|
3709
|
+
* This matches the approach in services-new/tests/src/proof.ts
|
|
3710
|
+
*
|
|
3711
|
+
* @param inputs - Circuit inputs
|
|
3712
|
+
* @param circuitsPath - Ignored. Proof generation always uses pinned S3 circuits.
|
|
3713
|
+
*/
|
|
3714
|
+
declare function generateWithdrawRegularProof(inputs: WithdrawRegularInputs, circuitsPath: string): Promise<ProofResult>;
|
|
3715
|
+
/**
|
|
3716
|
+
* Generate Groth16 proof for swap withdrawal using Circom WASM
|
|
3717
|
+
*
|
|
3718
|
+
* This matches the approach in services-new/tests/src/proof.ts
|
|
3719
|
+
*
|
|
3720
|
+
* @param inputs - Circuit inputs
|
|
3721
|
+
* @param circuitsPath - Ignored. Proof generation always uses pinned S3 circuits.
|
|
3722
|
+
*/
|
|
3723
|
+
declare function generateWithdrawSwapProof(inputs: WithdrawSwapInputs, circuitsPath: string): Promise<ProofResult>;
|
|
3724
|
+
/**
|
|
3725
|
+
* Check if circuits are available from the pinned S3 source.
|
|
3726
|
+
*/
|
|
3727
|
+
declare function areCircuitsAvailable(circuitsPath: string): Promise<boolean>;
|
|
3728
|
+
/**
|
|
3729
|
+
* Get default circuits URL.
|
|
3730
|
+
*/
|
|
3731
|
+
declare function getDefaultCircuitsPath(): Promise<string>;
|
|
3732
|
+
/**
|
|
3733
|
+
* Pinned circuit artifact hashes (SHA-256), flattened from the release table in
|
|
3734
|
+
* `config/circuit-release.ts`.
|
|
3735
|
+
*
|
|
3736
|
+
* This is a *view*, not the source of truth: edit the bundle, not this object.
|
|
3737
|
+
* It stays writable because the artifact TOCTOU tests re-pin it to synthetic
|
|
3738
|
+
* fixtures; production code must not mutate it.
|
|
3739
|
+
*/
|
|
3740
|
+
declare const EXPECTED_CIRCUIT_HASHES: {
|
|
3741
|
+
withdraw_regular_wasm: string;
|
|
3742
|
+
withdraw_regular_zkey: string;
|
|
3743
|
+
withdraw_swap_wasm: string;
|
|
3744
|
+
withdraw_swap_zkey: string;
|
|
3745
|
+
transaction_wasm: string;
|
|
3746
|
+
transaction_zkey: string;
|
|
3747
|
+
};
|
|
3748
|
+
/**
|
|
3749
|
+
* Circuit verification result
|
|
3750
|
+
*/
|
|
3751
|
+
interface CircuitVerificationResult {
|
|
3752
|
+
/** Whether verification passed */
|
|
3753
|
+
valid: boolean;
|
|
3754
|
+
/** Which circuit was checked */
|
|
3755
|
+
circuit: CircuitName;
|
|
3756
|
+
/** Error message if verification failed */
|
|
3757
|
+
error?: string;
|
|
3758
|
+
computed?: {
|
|
3759
|
+
wasm: string;
|
|
3760
|
+
zkey: string;
|
|
3761
|
+
};
|
|
3762
|
+
expected?: {
|
|
3763
|
+
wasm: string;
|
|
3764
|
+
zkey: string;
|
|
3765
|
+
};
|
|
3766
|
+
}
|
|
3767
|
+
/** Circuit artifact bytes together with the digests computed over those bytes. */
|
|
3768
|
+
interface VerifiedCircuitArtifacts {
|
|
3769
|
+
/** `<circuit>_js/<circuit>.wasm` bytes. */
|
|
3770
|
+
wasm: Uint8Array;
|
|
3771
|
+
/** `<circuit>_final.zkey` bytes. */
|
|
3772
|
+
zkey: Uint8Array;
|
|
3773
|
+
/** SHA-256 (lowercase hex) of the buffers in this object. */
|
|
3774
|
+
digests: {
|
|
3775
|
+
wasm: string;
|
|
3776
|
+
zkey: string;
|
|
3777
|
+
};
|
|
3778
|
+
}
|
|
3779
|
+
/**
|
|
3780
|
+
* Load circuit artifacts and return the very bytes whose digest was checked.
|
|
3781
|
+
*
|
|
3782
|
+
* This is the only supported way to obtain proving artifacts: the caller passes
|
|
3783
|
+
* the returned buffers straight to `snarkjs.groth16.fullProve`, which accepts a
|
|
3784
|
+
* `Uint8Array` for both the wasm and the zkey. Passing snarkjs a URL or a file
|
|
3785
|
+
* path instead would re-read the artifact independently of the digest check, so
|
|
3786
|
+
* a CDN (or a concurrent writer on disk) could serve good bytes to the check and
|
|
3787
|
+
* different bytes to the prover.
|
|
3788
|
+
*
|
|
3789
|
+
* Fails closed: any digest mismatch, unreachable artifact, or environment that
|
|
3790
|
+
* cannot produce bytes (a browser pointed at a local directory) throws rather
|
|
3791
|
+
* than falling back to an unverified source.
|
|
3792
|
+
*/
|
|
3793
|
+
declare function loadVerifiedCircuitArtifacts(circuitsPath: string | null, circuit: CircuitName): Promise<VerifiedCircuitArtifacts>;
|
|
3794
|
+
/**
|
|
3795
|
+
* Report whether a circuit's artifacts match the digests pinned in this SDK.
|
|
3796
|
+
*
|
|
3797
|
+
* This is a *reporting* helper (used by `verifyAllCircuits` for start-up checks
|
|
3798
|
+
* and diagnostics). It is NOT what gates proving: a check that only inspects the
|
|
3799
|
+
* source cannot say anything about the bytes a later, independent read returns.
|
|
3800
|
+
* Proof paths must call {@link loadVerifiedCircuitArtifacts} and hand the bytes
|
|
3801
|
+
* it returns to snarkjs.
|
|
3802
|
+
*
|
|
3803
|
+
* IMPORTANT: If the hashes don't match, the circuit may produce proofs
|
|
3804
|
+
* that will be rejected by the on-chain verifier!
|
|
3805
|
+
*
|
|
3806
|
+
* @param circuitsPath - Ignored for the legacy withdraw circuits (they always use
|
|
3807
|
+
* their own pinned bundle); honoured for `transaction`.
|
|
3808
|
+
* @param circuit - Which circuit to verify
|
|
3809
|
+
* @returns Verification result
|
|
3810
|
+
*
|
|
3811
|
+
* @example
|
|
3812
|
+
* ```typescript
|
|
3813
|
+
* const result = await verifyCircuitIntegrity(DEFAULT_CIRCUITS_URL, 'withdraw_regular');
|
|
3814
|
+
* if (!result.valid) {
|
|
3815
|
+
* console.error('Circuit verification failed:', result.error);
|
|
3816
|
+
* // Don't proceed with proof generation!
|
|
3817
|
+
* }
|
|
3818
|
+
* ```
|
|
3819
|
+
*/
|
|
3820
|
+
declare function verifyCircuitIntegrity(circuitsPath: string | null, circuit: CircuitName, prefetched?: {
|
|
3821
|
+
wasm: Uint8Array;
|
|
3822
|
+
zkey: Uint8Array;
|
|
3823
|
+
}): Promise<CircuitVerificationResult>;
|
|
3824
|
+
/**
|
|
3825
|
+
* Assert the ceremony-frozen `transaction` circuit artifacts are the pinned ones.
|
|
3826
|
+
*
|
|
3827
|
+
* Throws (fail-closed) when the digests do not match, mirroring how
|
|
3828
|
+
* `generateWithdrawRegularProof` / `generateWithdrawSwapProof` gate the legacy
|
|
3829
|
+
* circuits. Proving against an unpinned zkey silently produces proofs the
|
|
3830
|
+
* on-chain verifying key rejects, so failing here is strictly better than
|
|
3831
|
+
* failing on-chain.
|
|
3832
|
+
*
|
|
3833
|
+
* @param circuitsPath - Base directory or URL holding `transaction_js/transaction.wasm`
|
|
3834
|
+
* and `transaction_final.zkey`.
|
|
3835
|
+
* @param prefetched - Already-downloaded artifact bytes, to avoid a second fetch.
|
|
3836
|
+
*/
|
|
3837
|
+
declare function assertTransactionCircuitIntegrity(circuitsPath: string | null, prefetched?: {
|
|
3838
|
+
wasm: Uint8Array;
|
|
3839
|
+
zkey: Uint8Array;
|
|
3840
|
+
}): Promise<void>;
|
|
3841
|
+
/**
|
|
3842
|
+
* Verify all circuits before use
|
|
3843
|
+
*
|
|
3844
|
+
* Call this at SDK initialization to ensure circuits are valid.
|
|
3845
|
+
*
|
|
3846
|
+
* @param circuitsPath - For the legacy withdraw circuits this is ignored; verification
|
|
3847
|
+
* always uses their own pinned bundle.
|
|
3848
|
+
* @param transactionCircuitsPath - Base for the ceremony-frozen `transaction` circuit.
|
|
3849
|
+
* Pass `getCircuitsPath()` when the caller has reconfigured it;
|
|
3850
|
+
* `null` reports the "no base configured" state rather than throwing.
|
|
3851
|
+
* @returns Array of verification results (one per circuit)
|
|
3852
|
+
*/
|
|
3853
|
+
declare function verifyAllCircuits(circuitsPath: string, transactionCircuitsPath?: string | null): Promise<CircuitVerificationResult[]>;
|
|
3854
|
+
|
|
3855
|
+
/**
|
|
3856
|
+
* Pending Operations Manager
|
|
3857
|
+
*
|
|
3858
|
+
* Utility for persisting pending deposit/withdrawal operations in browser storage.
|
|
3859
|
+
* This enables recovery if the browser crashes or user navigates away mid-operation.
|
|
3860
|
+
*
|
|
3861
|
+
* IMPORTANT: This uses localStorage by default which has security implications.
|
|
3862
|
+
* Notes contain sensitive spending keys - consider using more secure storage
|
|
3863
|
+
* in production (e.g., encrypted IndexedDB, secure enclave).
|
|
3864
|
+
*/
|
|
3865
|
+
|
|
3866
|
+
/**
|
|
3867
|
+
* Pending deposit record
|
|
3868
|
+
*/
|
|
3869
|
+
interface PendingDeposit {
|
|
3870
|
+
/** The note (contains spending secrets!) */
|
|
3871
|
+
note: CloakNote;
|
|
3872
|
+
/** When the deposit was initiated */
|
|
3873
|
+
startedAt: number;
|
|
3874
|
+
/** Transaction signature if available */
|
|
3875
|
+
txSignature?: string;
|
|
3876
|
+
/** Status of the deposit */
|
|
3877
|
+
status: "pending" | "tx_sent" | "confirmed" | "failed";
|
|
3878
|
+
/** Error message if failed */
|
|
3879
|
+
error?: string;
|
|
3880
|
+
}
|
|
3881
|
+
/**
|
|
3882
|
+
* Pending withdrawal record
|
|
3883
|
+
*/
|
|
3884
|
+
interface PendingWithdrawal {
|
|
3885
|
+
/** The relay request ID (for resumption) */
|
|
3886
|
+
requestId: string;
|
|
3887
|
+
/** The note commitment being withdrawn */
|
|
3888
|
+
commitment: string;
|
|
3889
|
+
/** The nullifier being used */
|
|
3890
|
+
nullifier: string;
|
|
3891
|
+
/** When the withdrawal was initiated */
|
|
3892
|
+
startedAt: number;
|
|
3893
|
+
/** Status of the withdrawal */
|
|
3894
|
+
status: "pending" | "processing" | "completed" | "failed";
|
|
3895
|
+
/** Transaction signature if completed */
|
|
3896
|
+
txSignature?: string;
|
|
3897
|
+
/** Error message if failed */
|
|
3898
|
+
error?: string;
|
|
3899
|
+
}
|
|
3900
|
+
/**
|
|
3901
|
+
* Save a pending deposit
|
|
3902
|
+
* Call this BEFORE sending the on-chain transaction to ensure note is persisted
|
|
3903
|
+
*/
|
|
3904
|
+
declare function savePendingDeposit(deposit: PendingDeposit): void;
|
|
3905
|
+
/**
|
|
3906
|
+
* Load all pending deposits
|
|
3907
|
+
*/
|
|
3908
|
+
declare function loadPendingDeposits(): PendingDeposit[];
|
|
3909
|
+
/**
|
|
3910
|
+
* Update a pending deposit status
|
|
3911
|
+
*/
|
|
3912
|
+
declare function updatePendingDeposit(commitment: string, updates: Partial<PendingDeposit>): void;
|
|
3913
|
+
/**
|
|
3914
|
+
* Remove a pending deposit (e.g., after successful confirmation)
|
|
3915
|
+
*/
|
|
3916
|
+
declare function removePendingDeposit(commitment: string): void;
|
|
3917
|
+
/**
|
|
3918
|
+
* Clear all pending deposits
|
|
3919
|
+
*/
|
|
3920
|
+
declare function clearPendingDeposits(): void;
|
|
3921
|
+
/**
|
|
3922
|
+
* Save a pending withdrawal
|
|
3923
|
+
* Call this when you receive the request_id from the relay
|
|
3924
|
+
*/
|
|
3925
|
+
declare function savePendingWithdrawal(withdrawal: PendingWithdrawal): void;
|
|
3926
|
+
/**
|
|
3927
|
+
* Load all pending withdrawals
|
|
3928
|
+
*/
|
|
3929
|
+
declare function loadPendingWithdrawals(): PendingWithdrawal[];
|
|
3930
|
+
/**
|
|
3931
|
+
* Update a pending withdrawal status
|
|
3932
|
+
*/
|
|
3933
|
+
declare function updatePendingWithdrawal(requestId: string, updates: Partial<PendingWithdrawal>): void;
|
|
3934
|
+
/**
|
|
3935
|
+
* Remove a pending withdrawal (e.g., after successful completion)
|
|
3936
|
+
*/
|
|
3937
|
+
declare function removePendingWithdrawal(requestId: string): void;
|
|
3938
|
+
/**
|
|
3939
|
+
* Clear all pending withdrawals
|
|
3940
|
+
*/
|
|
3941
|
+
declare function clearPendingWithdrawals(): void;
|
|
3942
|
+
/**
|
|
3943
|
+
* Check if there are any pending operations that need recovery
|
|
3944
|
+
* Call this on page load to determine if recovery UI should be shown
|
|
3945
|
+
*/
|
|
3946
|
+
declare function hasPendingOperations(): boolean;
|
|
3947
|
+
/**
|
|
3948
|
+
* Get summary of pending operations for recovery UI
|
|
3949
|
+
*/
|
|
3950
|
+
declare function getPendingOperationsSummary(): {
|
|
3951
|
+
deposits: PendingDeposit[];
|
|
3952
|
+
withdrawals: PendingWithdrawal[];
|
|
3953
|
+
totalPending: number;
|
|
3954
|
+
};
|
|
3955
|
+
/**
|
|
3956
|
+
* Clean up stale pending operations
|
|
3957
|
+
* Call this periodically to remove old failed/completed operations
|
|
3958
|
+
*
|
|
3959
|
+
* @param maxAgeMs Maximum age in milliseconds before an operation is removed (default: 24 hours)
|
|
3960
|
+
*/
|
|
3961
|
+
declare function cleanupStalePendingOperations(maxAgeMs?: number): {
|
|
3962
|
+
removedDeposits: number;
|
|
3963
|
+
removedWithdrawals: number;
|
|
3964
|
+
};
|
|
3965
|
+
|
|
3966
|
+
/**
|
|
3967
|
+
* Recipient-addressed delivery envelope (CLKD1).
|
|
3968
|
+
*
|
|
3969
|
+
* ── The defect this closes ────────────────────────────────────────────────────────────────────
|
|
3970
|
+
* A shield-to-shield send creates an output note OWNED BY THE RECIPIENT. The only discovery
|
|
3971
|
+
* artefact the SDK used to publish for it was the CLK1 compliance chain note, which is encrypted
|
|
3972
|
+
* under the SENDER's `nk` and keyed (HKDF salt) by the output commitment. That note is openable by
|
|
3973
|
+
* the sender and by nobody else — so the recipient's money sat on chain, valid and spendable, with
|
|
3974
|
+
* its owner unable to see it (campaign rows S2S-08 / VK-01).
|
|
3975
|
+
*
|
|
3976
|
+
* This envelope is the recipient's half. It is encrypted to the RECIPIENT's X25519 public viewing
|
|
3977
|
+
* key — the one derived by `deriveViewingKeyFromNk(nk)` — so a cold scan holding nothing but
|
|
3978
|
+
* `(rpc, programId, nk)` can open it. That triple is exactly the cold-scan contract VK-01 tested.
|
|
3979
|
+
*
|
|
3980
|
+
* ── Wire format — dictated by the relay, not by us ────────────────────────────────────────────
|
|
3981
|
+
* `services/relay/src/api/transact.rs:176-205` accepts EXACTLY ONE base64 envelope of EXACTLY
|
|
3982
|
+
* 112 bytes and only on a shield-to-shield send (`public_amount == 0`); anything else is a 400 and
|
|
3983
|
+
* no carrier is written. `services/relay/src/solana/mod.rs:2338` then publishes
|
|
3984
|
+
*
|
|
3985
|
+
* "CLKD1" || hex(output_commitment[0]) || hex(envelope)
|
|
3986
|
+
*
|
|
3987
|
+
* as an SPL Memo in a transaction that touches the PDA at seed `b"cloak_delivery_registry"`, which
|
|
3988
|
+
* is what makes it enumerable via `getSignaturesForAddress`.
|
|
3989
|
+
*
|
|
3990
|
+
* The 112 bytes are:
|
|
3991
|
+
*
|
|
3992
|
+
* [0 ..32) ephemeral X25519 public key
|
|
3993
|
+
* [32 ..56) 24-byte XSalsa20-Poly1305 nonce
|
|
3994
|
+
* [56..112) 56-byte ciphertext = 40-byte plaintext + 16-byte Poly1305 tag
|
|
3995
|
+
*
|
|
3996
|
+
* and the 40-byte plaintext is `amount(u64 LE) || blinding(u256 BE)` — precisely what a recipient
|
|
3997
|
+
* needs, alongside their own keypair and the pool mint, to recompute the commitment and spend it.
|
|
3998
|
+
* Amount is LE to match every other u64 the SDK writes (`chain-note.ts`, public inputs); blinding
|
|
3999
|
+
* is BE to match `bigintToBytes32` and the on-chain field-element convention.
|
|
4000
|
+
*
|
|
4001
|
+
* ── Crypto ────────────────────────────────────────────────────────────────────────────────────
|
|
4002
|
+
* X25519 ECDH + XSalsa20-Poly1305, i.e. `nacl.box`, reusing the exact construction already in
|
|
4003
|
+
* `core/keys.ts` (`nacl.box.before` + `nacl.secretbox`). No new primitive is introduced here.
|
|
4004
|
+
*/
|
|
4005
|
+
|
|
4006
|
+
/** Ephemeral X25519 public key, at offset 0. */
|
|
4007
|
+
declare const RECIPIENT_DELIVERY_EPHEMERAL_PK_LEN = 32;
|
|
4008
|
+
/** XSalsa20-Poly1305 nonce, immediately after the ephemeral key. */
|
|
4009
|
+
declare const RECIPIENT_DELIVERY_NONCE_LEN = 24;
|
|
4010
|
+
/** amount(8) + blinding(32) — everything the owner needs to rebuild and spend the note. */
|
|
4011
|
+
declare const RECIPIENT_DELIVERY_PLAINTEXT_LEN = 40;
|
|
4012
|
+
/** Poly1305 authentication tag. */
|
|
4013
|
+
declare const RECIPIENT_DELIVERY_TAG_LEN = 16;
|
|
4014
|
+
/** Sealed payload = plaintext + tag. */
|
|
4015
|
+
declare const RECIPIENT_DELIVERY_CIPHERTEXT_LEN: number;
|
|
4016
|
+
/**
|
|
4017
|
+
* Total envelope size. The relay rejects any other length outright
|
|
4018
|
+
* (`RECIPIENT_DELIVERY_NOTE_BYTES` in `services/relay/src/api/transact.rs`), so this constant is a
|
|
4019
|
+
* contract with a shipped binary, not a preference.
|
|
4020
|
+
*/
|
|
4021
|
+
declare const RECIPIENT_DELIVERY_NOTE_BYTES: number;
|
|
4022
|
+
/** PDA seed the relay derives the carrier's registry account from. */
|
|
4023
|
+
declare const DELIVERY_REGISTRY_SEED = "cloak_delivery_registry";
|
|
4024
|
+
/** ASCII tag prefixing the carrier memo payload. */
|
|
4025
|
+
declare const DELIVERY_MEMO_TAG = "CLKD1";
|
|
4026
|
+
/** The spendable contents of a delivery envelope. */
|
|
4027
|
+
interface RecipientDeliveryNote {
|
|
4028
|
+
/** Note amount in the pool mint's smallest unit. */
|
|
4029
|
+
amount: bigint;
|
|
4030
|
+
/** Note blinding factor (BN254 field element). */
|
|
4031
|
+
blinding: bigint;
|
|
4032
|
+
}
|
|
4033
|
+
/**
|
|
4034
|
+
* Seal `{amount, blinding}` to a recipient's X25519 public viewing key.
|
|
4035
|
+
*
|
|
4036
|
+
* Every input is length-checked here rather than at the relay: a malformed envelope costs a
|
|
4037
|
+
* confirmed transaction's worth of latency before the 400 comes back, and the send has already
|
|
4038
|
+
* generated its proof by then.
|
|
4039
|
+
*/
|
|
4040
|
+
declare function encodeRecipientDeliveryNote(note: RecipientDeliveryNote, recipientViewingPublicKey: Uint8Array): Uint8Array;
|
|
4041
|
+
/**
|
|
4042
|
+
* Trial-open an envelope with a viewing secret. Returns `null` — never throws — when the envelope
|
|
4043
|
+
* is not ours, because a scanner runs this against every carrier in the registry.
|
|
4044
|
+
*/
|
|
4045
|
+
declare function openRecipientDeliveryNote(envelope: Uint8Array, viewingSecretKey: Uint8Array): RecipientDeliveryNote | null;
|
|
4046
|
+
declare function recipientDeliveryNoteToBase64(envelope: Uint8Array): string;
|
|
4047
|
+
/** Parameters the send path already has in hand when it assembles the relay body. */
|
|
4048
|
+
interface BuildRecipientDeliveryNotesParams {
|
|
4049
|
+
/** Signed public amount. Zero — and only zero — is a shield-to-shield send. */
|
|
4050
|
+
externalAmount: bigint;
|
|
4051
|
+
/** External withdrawal recipient, if any. A send has none. */
|
|
4052
|
+
recipient?: PublicKey | null;
|
|
4053
|
+
/** The recipient's 32-byte X25519 public viewing key (`deriveViewingKeyFromNk(nk).publicKey`). */
|
|
4054
|
+
recipientViewingPublicKey?: Uint8Array | string | null;
|
|
4055
|
+
/**
|
|
4056
|
+
* The output note being delivered. MUST be output 0: the relay binds the carrier to
|
|
4057
|
+
* `public_inputs.output_commitments[0]` (`services/relay/src/api/transact.rs:1015`).
|
|
4058
|
+
*/
|
|
4059
|
+
note?: Pick<Utxo, "amount" | "blinding"> | null;
|
|
4060
|
+
/** UTXO public key that owns `note` (i.e. `paddedOutputs[0].keypair.publicKey`). */
|
|
4061
|
+
noteOwnerPublicKey?: bigint;
|
|
4062
|
+
/** UTXO public key doing the spending (i.e. `paddedInputs[0].keypair.publicKey`). */
|
|
4063
|
+
spenderPublicKey?: bigint;
|
|
4064
|
+
}
|
|
4065
|
+
/**
|
|
4066
|
+
* Build the `recipient_delivery_notes` field for `/transact`, or `undefined` when the rail does not
|
|
4067
|
+
* apply. Returning `undefined` (rather than an empty array) matters: the relay's signed field view
|
|
4068
|
+
* treats omitted and null identically, and an empty array on a withdrawal would still be a 400.
|
|
4069
|
+
*/
|
|
4070
|
+
declare function buildRecipientDeliveryNotes(params: BuildRecipientDeliveryNotesParams): string[] | undefined;
|
|
4071
|
+
/** Render the carrier memo byte-for-byte as `emit_recipient_delivery_carrier` does. */
|
|
4072
|
+
declare function encodeDeliveryCarrierMemo(outputCommitment: bigint | Uint8Array, envelope: Uint8Array): Uint8Array;
|
|
4073
|
+
interface ParsedDeliveryCarrier {
|
|
4074
|
+
/** Lowercase 64-char hex of the output commitment the carrier declares. */
|
|
4075
|
+
commitment: string;
|
|
4076
|
+
/** The raw 112-byte envelope. */
|
|
4077
|
+
note: Uint8Array;
|
|
4078
|
+
}
|
|
4079
|
+
/**
|
|
4080
|
+
* Parse one SPL Memo payload. Returns `null` for anything that is not a well-formed CLKD1 carrier —
|
|
4081
|
+
* the memo program accepts arbitrary UTF-8 from anyone, so this must fail closed on exact lengths
|
|
4082
|
+
* (I-12: exact `!==` gates, never `<`).
|
|
4083
|
+
*/
|
|
4084
|
+
declare function parseDeliveryCarrierMemo(data: Uint8Array): ParsedDeliveryCarrier | null;
|
|
4085
|
+
|
|
4086
|
+
/**
|
|
4087
|
+
* Recoverable deposit notes (VK-01, deposit shape).
|
|
4088
|
+
*
|
|
4089
|
+
* ── The defect this closes ────────────────────────────────────────────────────────────────────
|
|
4090
|
+
* A cold scan holding exactly `(rpc, programId, nk)` — the contract the SDK advertises for viewing
|
|
4091
|
+
* keys — found the WITHDRAWAL change note and did NOT find the DEPOSIT. The control that makes the
|
|
4092
|
+
* miss attributable is the withdrawal: the same scanner, the same 300-signature window, the same
|
|
4093
|
+
* key, one shape found and one not.
|
|
4094
|
+
*
|
|
4095
|
+
* Two things were missing, and only one of them was visible from the failing arm:
|
|
4096
|
+
*
|
|
4097
|
+
* 1. `outPubkey0`. The deposit's chain note is v3 (timestamp + noteSalt), and `chainNoteHash` binds
|
|
4098
|
+
* `noteSemantics = Poseidon(outAmount0, outPubkey0, isSendToSelfKey0)`. `outPubkey0` is the
|
|
4099
|
+
* output note's OWNER key, which is not derivable from `nk` — the key hierarchy runs
|
|
4100
|
+
* `skSpend → nk` through BLAKE3 and does not run backwards. So the scanner could decrypt the
|
|
4101
|
+
* note and then had to drop it, because the hash it recomputed could never match. That is why
|
|
4102
|
+
* the widened arm needed an undocumented `ownUtxoPublicKey`: it was feeding the scanner the one
|
|
4103
|
+
* value the advertised contract does not carry.
|
|
4104
|
+
* 2. The BLINDING. Even with `ownUtxoPublicKey` the deposit is only VISIBLE, not RECOVERABLE — the
|
|
4105
|
+
* blinding came from `randomFieldElement()` inside `createUtxo` and is written nowhere. A note
|
|
4106
|
+
* you can see and cannot spend is not a recovered note.
|
|
4107
|
+
*
|
|
4108
|
+
* ── Why derivation, and not another envelope ──────────────────────────────────────────────────
|
|
4109
|
+
* The shielded-send shape was fixed with a CLKD1 delivery envelope because a send's output is owned
|
|
4110
|
+
* by SOMEONE ELSE: the only way to reach them is to encrypt to their key. A deposit's output is
|
|
4111
|
+
* SELF-owned. There is nobody to deliver to, and an envelope would cost 112 bytes on the one
|
|
4112
|
+
* transaction in the protocol that is wallet-signed and already tight against the 1232-byte packet
|
|
4113
|
+
* limit (a v4 chain note's extra 41 bytes is measured at 1282 and is exactly why deposits stay v3).
|
|
4114
|
+
*
|
|
4115
|
+
* So make the existing chain-note path recoverable instead. Both missing values become a PRF of the
|
|
4116
|
+
* wallet's own `nk` and the note salt the chain note ALREADY carries:
|
|
4117
|
+
*
|
|
4118
|
+
* seed = BLAKE3("cloak_deposit_note_v1" || nk || noteSalt(32B BE) || info)
|
|
4119
|
+
* privateKey = seed("sk") reduced into the field
|
|
4120
|
+
* blinding = seed("blinding") reduced into the field
|
|
4121
|
+
* publicKey = PoseidonEx(privateKey, KEYPAIR_) [L-02, matches keypair.circom]
|
|
4122
|
+
*
|
|
4123
|
+
* A cold scanner decrypts the chain note with `nk` (AES-GCM, HKDF-salted by the output commitment),
|
|
4124
|
+
* reads `noteSalt` out of the plaintext, replays those three lines, recomputes
|
|
4125
|
+
* `Poseidon(amount, publicKey, blinding, mint)` and requires it to equal a commitment the
|
|
4126
|
+
* transaction actually published. That equality is the authentication: it is not a heuristic, and a
|
|
4127
|
+
* wrong `nk` cannot produce it. Zero extra bytes on chain, no new envelope, no relay change.
|
|
4128
|
+
*
|
|
4129
|
+
* This is the same shape already blessed for swap timeout refunds in `core/swap-refund.ts`
|
|
4130
|
+
* (PRF(nk, nullifier0)), for the same reason: unrecoverable randomness becomes recoverable
|
|
4131
|
+
* randomness without changing anything an observer can see.
|
|
4132
|
+
*
|
|
4133
|
+
* ── The consequence to be explicit about ──────────────────────────────────────────────────────
|
|
4134
|
+
* The deposit note is owned by a PER-DEPOSIT key rather than by the wallet's single long-lived UTXO
|
|
4135
|
+
* keypair. It is still fully spendable — the wallet re-derives `privateKey` from `nk` whenever it
|
|
4136
|
+
* needs it — and it is strictly better for privacy, because `outPubkey0` no longer links a wallet's
|
|
4137
|
+
* deposits to each other. But it means the caller MUST persist (or be able to re-derive) `nk`, which
|
|
4138
|
+
* a Cloak wallet already does, and it means `transact` must be given an explicit `nk` rather than
|
|
4139
|
+
* inferring one from the output note's own key. `transact` enforces that rather than trusting it.
|
|
4140
|
+
*
|
|
4141
|
+
* ── Guardrails preserved ──────────────────────────────────────────────────────────────────────
|
|
4142
|
+
* [L-02] the public key comes from the capacity-tagged `derivePublicKey`. [H-04-shaped] a zero
|
|
4143
|
+
* public key or blinding would be an unspendable note, so the reduction forces non-zero and the
|
|
4144
|
+
* derivation refuses to return a degenerate pair. [M-04] `noteSalt` stays a private input to
|
|
4145
|
+
* `chainNoteHash`; it is only ever published inside the note's own authenticated ciphertext.
|
|
4146
|
+
*/
|
|
4147
|
+
|
|
4148
|
+
/**
|
|
4149
|
+
* The chain note salt is 96 bits — the circuit constrains it with `Num2Bits(96)` and `transact`
|
|
4150
|
+
* generates exactly 12 bytes. A salt outside that range would produce a proof the circuit rejects,
|
|
4151
|
+
* so it is refused here rather than at proof time.
|
|
4152
|
+
*/
|
|
4153
|
+
declare const CHAIN_NOTE_SALT_BITS = 96;
|
|
4154
|
+
/** The secrets a deposit note is built from — and the ones a cold scan re-derives. */
|
|
4155
|
+
interface DepositNoteSecrets {
|
|
4156
|
+
keypair: UtxoKeypair;
|
|
4157
|
+
blinding: bigint;
|
|
4158
|
+
}
|
|
4159
|
+
/** A deposit note recovered from `nk` alone, in spendable form. */
|
|
4160
|
+
interface RecoveredDepositNote extends DepositNoteSecrets {
|
|
4161
|
+
amount: bigint;
|
|
4162
|
+
mintAddress: PublicKey;
|
|
4163
|
+
/** The commitment the transaction published, reproduced from the derived secrets. */
|
|
4164
|
+
commitment: bigint;
|
|
4165
|
+
/** The salt the chain note carried, which anchored the derivation. */
|
|
4166
|
+
noteSalt: bigint;
|
|
4167
|
+
}
|
|
4168
|
+
/** A fresh 96-bit chain-note salt, from the same fail-closed source `transact` uses. */
|
|
4169
|
+
declare function randomDepositNoteSalt(): bigint;
|
|
4170
|
+
/**
|
|
4171
|
+
* Derive a deposit note's keypair and blinding from `(nk, noteSalt)`.
|
|
4172
|
+
*
|
|
4173
|
+
* Deterministic by design: this is the whole reason a cold scan can rebuild the note. Both the
|
|
4174
|
+
* builder and the scanner call it, so there is exactly one definition of what a deposit note is.
|
|
4175
|
+
*/
|
|
4176
|
+
declare function deriveDepositNoteSecrets(viewingKeyNk: Uint8Array, noteSalt: bigint): Promise<DepositNoteSecrets>;
|
|
4177
|
+
/**
|
|
4178
|
+
* Build a deposit output note that a cold `(rpc, programId, nk)` scan can recover.
|
|
4179
|
+
*
|
|
4180
|
+
* Returns the UTXO to pass as `outputUtxos[0]` AND the salt that anchored it. The SAME salt must be
|
|
4181
|
+
* handed to `transact` as `options.chainNoteSalt`, because the chain note is what publishes it — a
|
|
4182
|
+
* salt that does not reach the note leaves the deposit exactly as undiscoverable as before.
|
|
4183
|
+
*
|
|
4184
|
+
* ```ts
|
|
4185
|
+
* const { utxo, noteSalt } = await createRecoverableDepositUtxo(amount, nk, mint);
|
|
4186
|
+
* await transact({ ..., outputUtxos: [utxo], externalAmount: amount },
|
|
4187
|
+
* { chainNoteViewingKeyNk: nk, chainNoteSalt: noteSalt, ... });
|
|
4188
|
+
* ```
|
|
4189
|
+
*/
|
|
4190
|
+
declare function createRecoverableDepositUtxo(amount: bigint, viewingKeyNk: Uint8Array, mintAddress?: PublicKey, noteSalt?: bigint): Promise<{
|
|
4191
|
+
utxo: Utxo;
|
|
4192
|
+
noteSalt: bigint;
|
|
4193
|
+
}>;
|
|
4194
|
+
interface MatchDepositNoteParams {
|
|
4195
|
+
/** The scanning wallet's incoming viewing base. */
|
|
4196
|
+
viewingKeyNk: Uint8Array;
|
|
4197
|
+
/** `noteSalt`, read out of the decrypted chain note. */
|
|
4198
|
+
noteSalt: bigint;
|
|
4199
|
+
/** Candidate note amount — for a shield with no inputs this is the public deposit amount. */
|
|
4200
|
+
amount: bigint;
|
|
4201
|
+
/** Pool mint the commitment was computed under. */
|
|
4202
|
+
mintAddress: PublicKey;
|
|
4203
|
+
/** Output commitments the transaction actually published, hex or field elements. */
|
|
4204
|
+
outputCommitments: Array<string | bigint>;
|
|
4205
|
+
}
|
|
4206
|
+
/**
|
|
4207
|
+
* Decide whether a deposit's published commitment is one this `nk` can rebuild, and if so return
|
|
4208
|
+
* the note in spendable form. Returns `null` for everything that is not ours.
|
|
4209
|
+
*
|
|
4210
|
+
* The commitment equality is the authentication. Nothing here trusts the chain note's own claim
|
|
4211
|
+
* about what it describes; the note only supplies `noteSalt`, and the derived secrets have to
|
|
4212
|
+
* reproduce a value the transaction published or the candidate is discarded.
|
|
4213
|
+
*/
|
|
4214
|
+
declare function matchDepositNote(params: MatchDepositNoteParams): Promise<RecoveredDepositNote | null>;
|
|
4215
|
+
|
|
4216
|
+
/**
|
|
4217
|
+
* Swap timeout-refund discovery (VK-02).
|
|
4218
|
+
*
|
|
4219
|
+
* ── The defect this closes ────────────────────────────────────────────────────────────────────
|
|
4220
|
+
* When a private swap exhausts its retry budget, `CloseSwapState` appends
|
|
4221
|
+
*
|
|
4222
|
+
* R_fb = Poseidon(amount_after_fee, refund_pubkey, refund_blinding, field(WSOL))
|
|
4223
|
+
*
|
|
4224
|
+
* to the main pool tree. `swapUtxo` generated `refund_pubkey` / `refund_blinding` from raw
|
|
4225
|
+
* randomness and returned them only in the in-memory `UtxoSwapResult.refund`. Lose that object —
|
|
4226
|
+
* a crashed tab, a different device, a scan from cold key material — and the leaf is real, funded,
|
|
4227
|
+
* and permanently unrecoverable: campaign row VK-02 observed R_fb under BOTH the refund note's own
|
|
4228
|
+
* viewing key and the swap creator's, and found nothing under either.
|
|
4229
|
+
*
|
|
4230
|
+
* ── The fix, and why it is SDK-only ───────────────────────────────────────────────────────────
|
|
4231
|
+
* Derive the refund authorization as a PRF of the owner's `nk` and the swap's own first input
|
|
4232
|
+
* nullifier:
|
|
4233
|
+
*
|
|
4234
|
+
* seed = BLAKE3("cloak_swap_refund_v1" || nk || nullifier0)
|
|
4235
|
+
*
|
|
4236
|
+
* `nullifier0` is published in the TransactSwap public inputs, so a cold scanner enumerating
|
|
4237
|
+
* program transactions can replay this derivation for every swap it sees, recompute R_fb, and
|
|
4238
|
+
* match it against the commitment `CloseSwapState` declared. `nk` is secret, so no third party can
|
|
4239
|
+
* predict, front-run or link the refund key — the on-chain artefacts are unchanged in shape and
|
|
4240
|
+
* every existing binding still holds.
|
|
4241
|
+
*
|
|
4242
|
+
* This changes NOTHING on chain or in the relay. `refund_pubkey` / `refund_blinding` remain
|
|
4243
|
+
* caller-supplied values bound into `computeSwapExtDataHash`; only their provenance changes, from
|
|
4244
|
+
* "unrecoverable randomness" to "recoverable randomness".
|
|
4245
|
+
*
|
|
4246
|
+
* ── Guardrails preserved ──────────────────────────────────────────────────────────────────────
|
|
4247
|
+
* [H-04 / DD-01] the derivation is rejected unless publicKey and blinding are both non-zero, and
|
|
4248
|
+
* it is unique per swap because `nullifier0` is unique per spend. [L-02] the public key comes from
|
|
4249
|
+
* the capacity-tagged `derivePublicKey`, matching `keypair.circom`.
|
|
4250
|
+
*
|
|
4251
|
+
* ── FORWARD-LOOKING ONLY ──────────────────────────────────────────────────────────────────────
|
|
4252
|
+
* Everything here — including `discoverSwapRefunds`, the RPC walker at the bottom of this file —
|
|
4253
|
+
* recovers only refunds whose authorization was DERIVED. Swaps already on chain whose refund keypair
|
|
4254
|
+
* and blinding came from raw randomness stay unrecoverable, permanently, by any key-only scan. There
|
|
4255
|
+
* is nothing to replay: those secrets existed only in the caller's in-memory `UtxoSwapResult.refund`.
|
|
4256
|
+
* Do not let this note get softened — a wallet that reports "no stranded refunds" on the strength of
|
|
4257
|
+
* an empty scan would be making a claim this code cannot support.
|
|
4258
|
+
*/
|
|
4259
|
+
|
|
4260
|
+
/** A refund authorization: what `swapUtxo` binds into the swap ext-data hash. */
|
|
4261
|
+
interface SwapRefundAuthorization {
|
|
4262
|
+
privateKey: bigint;
|
|
4263
|
+
publicKey: bigint;
|
|
4264
|
+
blinding: bigint;
|
|
4265
|
+
}
|
|
4266
|
+
/** A refund leaf matched back to its owner, ready to spend. */
|
|
4267
|
+
interface RecoveredSwapRefund {
|
|
4268
|
+
keypair: UtxoKeypair;
|
|
4269
|
+
blinding: bigint;
|
|
4270
|
+
amount: bigint;
|
|
4271
|
+
/** R_fb as a field element, identical to the commitment `CloseSwapState` appended. */
|
|
4272
|
+
commitment: bigint;
|
|
4273
|
+
}
|
|
4274
|
+
/**
|
|
4275
|
+
* Derive a swap's refund authorization from the owner's `nk` and the swap's first input nullifier.
|
|
4276
|
+
*
|
|
4277
|
+
* Deterministic by design: this is what makes the refund leaf recoverable from key material alone.
|
|
4278
|
+
*/
|
|
4279
|
+
declare function deriveSwapRefundAuthorization(viewingKeyNk: Uint8Array, inputNullifier: Uint8Array | bigint): Promise<SwapRefundAuthorization>;
|
|
4280
|
+
/**
|
|
4281
|
+
* R_fb, exactly as `CloseSwapState` computes it. The swap principal is always WSOL-locked, so the
|
|
4282
|
+
* mint term is the native-SOL sentinel.
|
|
4283
|
+
*/
|
|
4284
|
+
declare function computeSwapRefundCommitment(amountAfterFee: bigint, refundPublicKey: bigint, refundBlinding: bigint): Promise<bigint>;
|
|
4285
|
+
interface MatchSwapRefundLeafParams {
|
|
4286
|
+
/** The scanning wallet's incoming viewing base. */
|
|
4287
|
+
viewingKeyNk: Uint8Array;
|
|
4288
|
+
/** First input nullifier of the candidate swap, read from its TransactSwap public inputs. */
|
|
4289
|
+
inputNullifier: Uint8Array | bigint;
|
|
4290
|
+
/** Principal returned to the pool, from the `cloak/refund_leaf/v1` event. */
|
|
4291
|
+
amountAfterFee: bigint;
|
|
4292
|
+
/** R_fb as appended on chain. */
|
|
4293
|
+
commitment: bigint | Uint8Array;
|
|
4294
|
+
}
|
|
4295
|
+
/**
|
|
4296
|
+
* Decide whether a public refund leaf belongs to the holder of `viewingKeyNk`, and if so return it
|
|
4297
|
+
* in spendable form. Returns `null` for every leaf that is not ours — a scanner runs this against
|
|
4298
|
+
* every close it can see.
|
|
4299
|
+
*/
|
|
4300
|
+
declare function matchSwapRefundLeaf(params: MatchSwapRefundLeafParams): Promise<RecoveredSwapRefund | null>;
|
|
4301
|
+
/** One recovered refund leaf, with the chain coordinates that produced it. */
|
|
4302
|
+
interface DiscoveredSwapRefund extends RecoveredSwapRefund {
|
|
4303
|
+
/** Signature of the `CloseSwapState` transaction that appended the leaf. */
|
|
4304
|
+
signature: string;
|
|
4305
|
+
/** Leaf index, straight from the `cloak/refund_leaf/v1` event. */
|
|
4306
|
+
leafIndex: bigint;
|
|
4307
|
+
/** First input nullifier of the swap this refund belongs to (the PRF's second input). */
|
|
4308
|
+
inputNullifier: Uint8Array;
|
|
4309
|
+
/** Signature of the `TransactSwap` that opened the swap, when it was inside the scanned window. */
|
|
4310
|
+
swapSignature?: string;
|
|
4311
|
+
}
|
|
4312
|
+
interface DiscoverSwapRefundsOptions {
|
|
4313
|
+
/** Maximum program signatures to walk. Omit or 0 to walk the whole history. */
|
|
4314
|
+
limit?: number;
|
|
4315
|
+
/** Stop when this signature is reached (exclusive) — the cursor from a previous run. */
|
|
4316
|
+
untilSignature?: string;
|
|
4317
|
+
/** `getTransaction` concurrency (default 50). */
|
|
4318
|
+
batchSize?: number;
|
|
4319
|
+
/** Progress/status callback. */
|
|
4320
|
+
onStatus?: (status: string) => void;
|
|
4321
|
+
/**
|
|
4322
|
+
* Pool mint whose `swap_state` PDAs to derive. Swap input is wSOL-locked, so the default is the
|
|
4323
|
+
* native-SOL sentinel and there is no reason to change it outside a test.
|
|
4324
|
+
*/
|
|
4325
|
+
poolMint?: PublicKey;
|
|
4326
|
+
}
|
|
4327
|
+
/**
|
|
4328
|
+
* Find every swap timeout-refund leaf on chain that belongs to the holder of `viewingKeyNk`, and
|
|
4329
|
+
* return each in spendable form (`keypair`, `blinding`, `amount`, `commitment`).
|
|
4330
|
+
*
|
|
4331
|
+
* Key material only — no relay, no local note store, no prior knowledge of the swap. This is the
|
|
4332
|
+
* recovery path for the exact situation VK-02 described: `CloseSwapState` appended a funded leaf and
|
|
4333
|
+
* its owner could not see it under any key they held.
|
|
4334
|
+
*
|
|
4335
|
+
* FORWARD-LOOKING ONLY. This finds refunds whose authorization was DERIVED as PRF(nk, nullifier0).
|
|
4336
|
+
* A swap whose refund keypair and blinding were drawn from raw randomness — every swap submitted
|
|
4337
|
+
* before that derivation shipped, and any swap built without an `nk` — has no derivation to replay,
|
|
4338
|
+
* and no key-only scan can ever recover it. Those secrets existed solely in the caller's in-memory
|
|
4339
|
+
* `UtxoSwapResult.refund`. An empty result is therefore NOT proof that a wallet has no stranded
|
|
4340
|
+
* refund; it means none of the leaves in the scanned window were derivable from this `nk`.
|
|
4341
|
+
*
|
|
4342
|
+
* @param connection Solana RPC connection.
|
|
4343
|
+
* @param programId Shield-pool program id.
|
|
4344
|
+
* @param viewingKeyNk 32-byte `nk` — the same value chain notes are decrypted with.
|
|
4345
|
+
*/
|
|
4346
|
+
declare function discoverSwapRefunds(connection: Connection, programId: PublicKey, viewingKeyNk: Uint8Array, options?: DiscoverSwapRefundsOptions): Promise<DiscoveredSwapRefund[]>;
|
|
4347
|
+
|
|
4348
|
+
/**
|
|
4349
|
+
* Compact deterministic chain note format.
|
|
4350
|
+
*
|
|
4351
|
+
* Envelope: [version 1][ciphertext (plaintext + AES-GCM tag)]
|
|
2175
4352
|
*
|
|
2176
|
-
*
|
|
4353
|
+
* Plaintext layout by version:
|
|
4354
|
+
* - v3: [timestamp: u64 LE (8)][noteSalt: u256 BE (32)]
|
|
4355
|
+
* - v2: [timestamp: u64 LE (8)]
|
|
2177
4356
|
*/
|
|
2178
4357
|
type ChainNoteTxType = "deposit" | "withdraw" | "transfer" | "swap" | "unknown";
|
|
2179
4358
|
interface CompactChainNote {
|
|
2180
4359
|
timestamp: bigint;
|
|
2181
4360
|
commitment: string;
|
|
4361
|
+
noteSalt?: bigint;
|
|
4362
|
+
/** v4 only: the three terms that make up `noteSemantics`. */
|
|
4363
|
+
outAmount0?: bigint;
|
|
4364
|
+
outPubkey0?: bigint;
|
|
4365
|
+
isSendToSelfKey0?: bigint;
|
|
2182
4366
|
}
|
|
2183
4367
|
/**
|
|
2184
4368
|
* Encrypt a compact deterministic chain note.
|
|
2185
|
-
*
|
|
2186
|
-
*
|
|
4369
|
+
*
|
|
4370
|
+
* v3 carries noteSalt so the recipient can recompute and verify the public
|
|
4371
|
+
* chainNoteHash without making the output commitment linkable by observers.
|
|
2187
4372
|
*/
|
|
2188
|
-
declare function encryptCompactChainNote(timestamp: bigint, nk: Uint8Array, commitmentHex: string
|
|
4373
|
+
declare function encryptCompactChainNote(timestamp: bigint, nk: Uint8Array, commitmentHex: string, noteSalt: bigint, semantics?: {
|
|
4374
|
+
outAmount0: bigint;
|
|
4375
|
+
outPubkey0: bigint;
|
|
4376
|
+
isSendToSelfKey0: bigint;
|
|
4377
|
+
}): Promise<Uint8Array>;
|
|
2189
4378
|
/**
|
|
2190
4379
|
* Decrypt a compact deterministic chain note with candidate output commitments.
|
|
2191
4380
|
* Tries each commitment-derived key until AES-GCM authentication succeeds.
|
|
4381
|
+
* Accepts current v3 notes and legacy v2 notes.
|
|
2192
4382
|
*/
|
|
2193
4383
|
declare function decryptCompactChainNote(noteBytes: Uint8Array, nk: Uint8Array, candidateCommitments: string[]): Promise<CompactChainNote>;
|
|
2194
4384
|
declare function chainNoteToBase64(noteBytes: Uint8Array): string;
|
|
@@ -2257,6 +4447,30 @@ interface ScanResult {
|
|
|
2257
4447
|
lastSignature?: string;
|
|
2258
4448
|
/** Number of RPC getTransaction calls actually made (for diagnostics). */
|
|
2259
4449
|
rpcCallsMade: number;
|
|
4450
|
+
/**
|
|
4451
|
+
* Notes delivered TO this wallet by other people's shield-to-shield sends, recovered from the
|
|
4452
|
+
* CLKD1 registry. These are SPENDABLE note secrets, not history records — they are intentionally
|
|
4453
|
+
* kept out of `transactions`/`summary` so the compliance report shape is unchanged.
|
|
4454
|
+
*/
|
|
4455
|
+
deliveredNotes: DeliveredNote[];
|
|
4456
|
+
/**
|
|
4457
|
+
* This wallet's OWN deposits, rebuilt in SPENDABLE form from `nk` alone (VK-01).
|
|
4458
|
+
*
|
|
4459
|
+
* `transactions` records that a deposit happened and for how much. These carry the keypair and
|
|
4460
|
+
* blinding as well, which is the difference between seeing a note and being able to spend it.
|
|
4461
|
+
* Only deposits built with {@link createRecoverableDepositUtxo} appear here; a deposit whose
|
|
4462
|
+
* blinding came from raw randomness has nothing to rebuild and shows up as a history row only.
|
|
4463
|
+
*
|
|
4464
|
+
* Additive, for the same reason as `deliveredNotes`: note secrets are not compliance rows.
|
|
4465
|
+
*/
|
|
4466
|
+
recoveredDepositNotes: RecoveredDepositNoteRecord[];
|
|
4467
|
+
}
|
|
4468
|
+
/** A recovered deposit note plus the chain coordinates it was recovered from. */
|
|
4469
|
+
interface RecoveredDepositNoteRecord extends RecoveredDepositNote {
|
|
4470
|
+
/** Signature of the deposit transaction. */
|
|
4471
|
+
signature: string;
|
|
4472
|
+
/** Millisecond timestamp from the chain note. */
|
|
4473
|
+
timestamp: bigint;
|
|
2260
4474
|
}
|
|
2261
4475
|
/** Options for `scanTransactions`. */
|
|
2262
4476
|
interface ScanOptions {
|
|
@@ -2292,15 +4506,74 @@ interface ScanOptions {
|
|
|
2292
4506
|
* matching the wallet's associated token account against the on-chain recipient ATA.
|
|
2293
4507
|
*/
|
|
2294
4508
|
walletPublicKey?: string;
|
|
4509
|
+
/**
|
|
4510
|
+
* Sweep the CLKD1 recipient-delivery registry for notes sent TO this wallet (default: true).
|
|
4511
|
+
* Costs one extra `getSignaturesForAddress` page plus one `getTransaction` per carrier.
|
|
4512
|
+
*/
|
|
4513
|
+
includeRecipientDeliveries?: boolean;
|
|
4514
|
+
/**
|
|
4515
|
+
* This wallet's UTXO public key. Supply it to authenticate each delivery carrier's declared
|
|
4516
|
+
* commitment (see `ScanRecipientDeliveryOptions.ownerUtxoPublicKey`). Without it the commitment
|
|
4517
|
+
* is reported unverified.
|
|
4518
|
+
*/
|
|
4519
|
+
ownerUtxoPublicKey?: bigint;
|
|
4520
|
+
/** Candidate pool mints for delivery-carrier commitment verification. Defaults to native SOL. */
|
|
4521
|
+
deliveryMints?: PublicKey[];
|
|
4522
|
+
}
|
|
4523
|
+
/** A note delivered TO the scanning wallet by someone else's shield-to-shield send. */
|
|
4524
|
+
interface DeliveredNote {
|
|
4525
|
+
/** Output commitment (lowercase hex) the carrier declares. */
|
|
4526
|
+
commitment: string;
|
|
4527
|
+
/** Note amount, decrypted from the envelope. */
|
|
4528
|
+
amount: bigint;
|
|
4529
|
+
/** Note blinding, decrypted from the envelope. */
|
|
4530
|
+
blinding: bigint;
|
|
4531
|
+
/** Carrier transaction signature (the discovery record, NOT the source transaction). */
|
|
4532
|
+
carrierSignature: string;
|
|
4533
|
+
/** Carrier block time in seconds, when the RPC supplied one. */
|
|
4534
|
+
blockTime?: number;
|
|
4535
|
+
/**
|
|
4536
|
+
* Pool mint the note lives in, resolved by recomputing the commitment. Present only when
|
|
4537
|
+
* `ownerUtxoPublicKey` was supplied — without it the declared commitment cannot be checked.
|
|
4538
|
+
*/
|
|
4539
|
+
mint?: string;
|
|
4540
|
+
/**
|
|
4541
|
+
* True when the declared commitment was recomputed from the decrypted note and matched. The
|
|
4542
|
+
* memo's commitment field is unauthenticated; only this recomputation binds it.
|
|
4543
|
+
*/
|
|
4544
|
+
commitmentVerified: boolean;
|
|
4545
|
+
}
|
|
4546
|
+
interface ScanRecipientDeliveryOptions {
|
|
4547
|
+
connection: Connection;
|
|
4548
|
+
programId: PublicKey;
|
|
4549
|
+
/** nk (32 bytes). The X25519 opening key is `deriveViewingKeyFromNk(nk).privateKey`. */
|
|
4550
|
+
viewingKeyNk: Uint8Array;
|
|
4551
|
+
/**
|
|
4552
|
+
* The scanning wallet's UTXO public key. Supply it to authenticate the carrier's declared
|
|
4553
|
+
* commitment: the memo's commitment field is written by the relay and is not covered by the
|
|
4554
|
+
* envelope's Poly1305 tag, so a carrier can claim any commitment it likes. With this set, a
|
|
4555
|
+
* carrier survives only if `Poseidon(amount, ownerPubkey, blinding, mint)` reproduces it.
|
|
4556
|
+
*/
|
|
4557
|
+
ownerUtxoPublicKey?: bigint;
|
|
4558
|
+
/** Candidate pool mints to try when verifying. Defaults to the native-SOL sentinel. */
|
|
4559
|
+
mints?: PublicKey[];
|
|
4560
|
+
onStatus?: (status: string) => void;
|
|
4561
|
+
debug?: boolean;
|
|
2295
4562
|
}
|
|
2296
4563
|
/**
|
|
2297
|
-
*
|
|
2298
|
-
*
|
|
2299
|
-
* and return a sorted list of the caller's transactions.
|
|
4564
|
+
* Sweep the recipient-delivery registry (CLKD1) and trial-open every carrier with the caller's
|
|
4565
|
+
* viewing key.
|
|
2300
4566
|
*
|
|
2301
|
-
* This is
|
|
2302
|
-
*
|
|
4567
|
+
* This is the READ half of the fix for S2S-08 / VK-01. The pre-existing sweep
|
|
4568
|
+
* (`scanSwapNoteCarriers`) reads the CLK1 registry, which carries the SENDER-encrypted compliance
|
|
4569
|
+
* note — which is why a cold scan found the withdrawal change note and nothing that was sent TO the
|
|
4570
|
+
* scanning wallet. This registry carries the recipient-encrypted envelope, and is the only place a
|
|
4571
|
+
* recipient's own note is discoverable from `(rpc, programId, nk)` alone.
|
|
2303
4572
|
*/
|
|
4573
|
+
declare function scanRecipientDeliveryNotes(opts: ScanRecipientDeliveryOptions): Promise<{
|
|
4574
|
+
notes: DeliveredNote[];
|
|
4575
|
+
rpcCalls: number;
|
|
4576
|
+
}>;
|
|
2304
4577
|
declare function scanTransactions(opts: ScanOptions): Promise<ScanResult>;
|
|
2305
4578
|
/** JSON-serializable compliance report (numbers instead of bigint). Used for cache and display. */
|
|
2306
4579
|
interface ComplianceReport {
|
|
@@ -2474,25 +4747,10 @@ declare class SimpleWallet {
|
|
|
2474
4747
|
* Cloak SDK - TypeScript SDK for Private Transactions on Solana
|
|
2475
4748
|
*
|
|
2476
4749
|
* @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.
|
|
2492
4750
|
*/
|
|
2493
4751
|
|
|
2494
|
-
declare const VERSION = "0.
|
|
2495
|
-
/** True when scanner supports TransactSwap (tag 1). */
|
|
4752
|
+
declare const VERSION = "0.2.0";
|
|
4753
|
+
/** True when scanner supports TransactSwap (tag 1). Check this to verify the correct SDK bundle is loaded. */
|
|
2496
4754
|
declare const SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
2497
4755
|
|
|
2498
|
-
export { CLOAK_PROGRAM_ID, type ChainNoteTxType, type CloakConfig, CloakError, type CloakKeyPair,
|
|
4756
|
+
export { type BuildRecipientDeliveryNotesParams, CHAIN_NOTE_SALT_BITS, CLOAK_PROGRAM_ID, type ChainNoteTxType, type CircuitName, type CircuitVerificationResult, type ClaimTimeoutRefundOptions, type ClaimTimeoutRefundParams, type ClaimTimeoutRefundResult, type CloakConfig, CloakError, type CloakKeyPair, type CloakNote, CloakSDK, type CommitmentEntry, type CommitmentsResponse, type CompactChainNote, type ComplianceReport, type ComplianceTxType, type ConfirmSettlementParams, DEFAULT_CIRCUITS_URL, DEFAULT_TRANSACTION_CIRCUITS_URL, DELIVERY_MEMO_TAG, DELIVERY_REGISTRY_SEED, type DeliveredNote, type DepositInstructionParams, type DepositNoteSecrets, type DepositOptions, type DepositResult, type DepositStatus, type DiscoverSwapRefundsOptions, type DiscoveredSwapRefund, EXPECTED_CIRCUIT_HASHES, type EncryptedMetadataBundle, type EncryptedNote$1 as EncryptedNote, type ErrorCategory, type ExpandedSpendKey, FIXED_FEE_LAMPORTS, type Groth16Proof, InsecureRandomnessError, LAMPORTS_PER_SOL, LocalStorageAdapter, type LogLevel, type Logger, MERKLE_TREE_HEIGHT, MIN_DEPOSIT_LAMPORTS, type MasterKey, type MatchDepositNoteParams, type MatchSwapRefundLeafParams, type MaxLengthArray, MemoryStorageAdapter, type MerkleProof, type MerkleRootResponse, MerkleTree, NATIVE_SOL_MINT, type Network, type NoteData, type OnchainMerkleProof, type ParsedDeliveryCarrier, type PendingDeposit, type PendingWithdrawal, type ProofResult, RECIPIENT_DELIVERY_CIPHERTEXT_LEN, RECIPIENT_DELIVERY_EPHEMERAL_PK_LEN, RECIPIENT_DELIVERY_NONCE_LEN, RECIPIENT_DELIVERY_NOTE_BYTES, RECIPIENT_DELIVERY_PLAINTEXT_LEN, RECIPIENT_DELIVERY_TAG_LEN, type RecipientDeliveryNote, type RecoveredDepositNote, type RecoveredDepositNoteRecord, type RecoveredSwapRefund, type RefundVoucher, RelayInternalError, RelayService, type RelaySubmissionResult, type RiskQuoteInstructionResponse, RootNotFoundError, SCANNER_SUPPORTS_TRANSACT_SWAP, SIGN_IN_MESSAGE, SanctionsQuoteError, type ScanOptions, type ScanRecipientDeliveryOptions, type ScanResult, type ScanSummary, type ScannedTransaction, type SettlementConnection, type SettlementContext, type SettlementStatus, type SettlementVerdict, SettlementVerificationError, ShieldPoolErrors, type ShieldPoolPDAs, SimpleWallet, type SpendKey, type StorageAdapter, type SubmitTransactToRelayArgs, type SwapOptions, type SwapParams, type SwapRefundAuthorization, type SwapResult, TRANSACTION_CIRCUITS_VERSION, type TransactOptions, type TransactParams, type TransactRequestBodyParams, type TransactResult, type TransactionMetadata, type Transfer, type TransferOptions, type TransferResult, 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 WithdrawOptions, type WithdrawRegularInputs, type WithdrawSubmissionResult, type WithdrawSwapInputs, areCircuitsAvailable, assertDirectSubmissionLanded, assertTransactionCircuitIntegrity, bigintToBytes32$1 as bigintToBytes32, bigintToHex, buildMerkleTree, buildMerkleTreeFromChain, buildMerkleTreeFromRelay, buildPublicInputsBytes, buildRecipientDeliveryNotes, buildTransactRequestBody, bytesToHex, calculateFee, calculateFeeBigint, calculateRelayFee, canRebuildMerkleTreeFromChain, chainNoteFromBase64, chainNoteToBase64, claimTimeoutRefund, classifyRelayError, 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, computeSwapRefundCommitment, computeCommitment as computeUtxoCommitment, computeNullifier as computeUtxoNullifier, confirmTransactSettlement, copyNoteToClipboard, createCloakError, createDepositInstruction, createLogger, createRecoverableDepositUtxo, createUtxo, createZeroUtxo, decryptCompactChainNote, decryptComplianceMetadataWithMasterKey, decryptTransactionMetadata, deriveDepositNoteSecrets, deriveDiversifiedViewingKey, deriveDiversifier, deriveInputNullifierPdas, derivePublicKey, deriveSpendKey, deriveSwapRefundAuthorization, deriveUserCompliancePublicKey, deriveUserComplianceScalar, deriveUtxoKeypairFromSpendKey, deriveViewKey, deriveViewingKeyFromNk, deriveViewingKeyFromSpendKey, deriveViewingKeyFromUtxoPrivateKey, deserializeUtxo, detectNetworkFromRpcUrl, discoverSwapRefunds, downloadNote, encodeDeliveryCarrierMemo, encodeNoteSimple, encodeRecipientDeliveryNote, 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, getChainNoteRegistryPDA, getCircuitsPath, getDefaultCircuitsPath, getDeliveryRegistryPDA, getDistributableAmount, getExplorerUrl, getNkFromUtxoPrivateKey, getNullifierPDA, getPendingOperationsSummary, getPoolAuthorityConfigPDA, getPublicKey, getPublicViewKey, getRecipientAmount, getRefundClaimPDA, getRefundLedgerPDA, getRpcUrlForNetwork, getShieldPoolPDAs, getSwapStatePDA, getViewKey, hasPendingOperations, hexToBigint$1 as hexToBigint, hexToBytes, importKeys, importWalletKeys, isBrowser, isBrowserLike, isDebugEnabled, isPlausibleSignature, isReactNative, isRootNotFoundError, isSubmissionOutcomeUnknownResponse, isValidHex, isValidRpcUrl, isValidSolanaAddress, isWithdrawAmountSufficient, isWithdrawable, keypairToAdapter, loadPendingDeposits, loadPendingWithdrawals, loadVerifiedCircuitArtifacts, matchDepositNote, matchSwapRefundLeaf, openRecipientDeliveryNote, parseAmount, parseDeliveryCarrierMemo, parseError, parseNote, parseRelayErrorResponse, parseRelayErrorSignature, parseTransactionError, partialWithdraw, poseidonHash, preflightCheck, preflightNullifiers, prepareEncryptedOutput, prepareEncryptedOutputForRecipient, proofToBytes, pubkeyToFieldElement, pubkeyToLimbs, randomBytes, randomDepositNoteSalt, randomFieldElement, readMerkleTreeState, recipientDeliveryNoteToBase64, registerViewingKey, removePendingDeposit, removePendingWithdrawal, resolveCircuitsBase, savePendingDeposit, savePendingWithdrawal, scanNotesForWallet, scanRecipientDeliveryNotes, scanTransactions, sdkLogger, selectUtxos, sendTransaction, serializeNote, serializeUtxo, setCircuitsPath, setDebugMode, signTransaction, splitTo2Limbs, submitTransactToRelay, 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, verifyUtxos, waitForRoot, withTiming };
|