@cloak.dev/sdk 0.2.0 → 0.2.1

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/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/README.md CHANGED
@@ -11,7 +11,7 @@ Version 0.2.0 targets the mainnet program deployed on 2026-08-24 and the ceremon
11
11
  ## Installation
12
12
 
13
13
  ```bash
14
- npm install @cloak.dev/sdk @solana/web3.js
14
+ npm install @cloak.dev/sdk@^0.2.0 @solana/web3.js
15
15
  # swaps and SPL pools also need:
16
16
  npm install @solana/spl-token
17
17
  ```
@@ -26,25 +26,44 @@ Node >= 18. Ships ESM and CJS builds with type declarations. Runs in Node and in
26
26
  | Setting | Value |
27
27
  | --- | --- |
28
28
  | Program | `zh1eLd6rSphLejbFfJEneUwzHRfMKxgzrgkfwA6qRkW` (`CLOAK_PROGRAM_ID`) |
29
- | Relay | `https://api.cloak.ag`, passed as `relayUrl` or via `CLOAK_RELAY_URL` (see below) |
29
+ | Cloak endpoint | `https://api.cloak.ag` pinned into the build, not configurable (see below) |
30
30
  | Circuits | `https://storage.googleapis.com/cloak-circuits/circuits/0.2.0` (the SDK default) |
31
31
  | Pools | WSOL (`NATIVE_SOL_MINT`), USDC `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`, USDT `Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB` |
32
32
  | RPC | any mainnet RPC; use `new Connection(url, "confirmed")` |
33
33
 
34
- The relay URL is never defaulted. `transact` resolves it from the `relayUrl` option, then from
35
- `CLOAK_RELAY_URL`, and with neither set it throws before anything is signed or sent. This is
36
- deliberate: a script that forgets the option must not ship its proof to production. Name production
37
- explicitly:
34
+ The endpoint is fixed when the SDK is built and cannot be changed by a consumer. A published build
35
+ carries `RELAY_ORIGIN_ALLOWLIST` as a compiled-in constant, every request is checked against it
36
+ before it leaves the process, and there is no option, environment variable, or bundler define that
37
+ moves it. Name it by importing the constant rather than typing a host:
38
38
 
39
39
  ```ts
40
- relayUrl: "https://api.cloak.ag"
40
+ import { CLOAK_PRODUCTION_RELAY_URL } from "@cloak.dev/sdk";
41
+
42
+ relayUrl: CLOAK_PRODUCTION_RELAY_URL
43
+ ```
44
+
45
+ It is still never defaulted: omitting the option throws before anything is signed or sent, so a
46
+ script that forgets it cannot ship its proof anywhere. `relayUrl: ""` means the opposite — no
47
+ endpoint at all, so a deposit is signed and submitted by the caller.
48
+
49
+ A build pinned somewhere else is made by editing source and rebuilding, which is also what makes a
50
+ published build unrepointable:
51
+
52
+ ```bash
53
+ # in a checkout of this repo
54
+ $ $EDITOR src/config/relay.ts # replace the RELAY_ORIGIN_ALLOWLIST entry, do not append
55
+ $ npm run build # only needed by consumers that import dist/
41
56
  ```
42
57
 
58
+ The same edit unlocks the second half of the lock: a build pinned to a non-local origin refuses to
59
+ run against an RPC served by the local machine, and a build pinned to a local origin does not. One
60
+ constant, both behaviours.
61
+
43
62
  Every flow needs it, deposits included: the SDK registers the wallet's viewing key through the relay
44
63
  before its first transaction (see [Recovery and discovery](#recovery-and-discovery-viewing-key)),
45
64
  deposits fetch the signed risk quote the program requires from `<relayUrl>/range-quote`, and sends,
46
65
  withdrawals and swaps are submitted through it. `enforceViewingKeyRegistration: false` skips only the
47
- registration step (the shipped examples set it on a local fork); it is not a no-relay mode.
66
+ registration step (some of the shipped examples set it on a local fork); it is not a no-relay mode.
48
67
 
49
68
  Do not expose `programId`, the relay URL or the circuits base as end-user input. Wallet adapters are
50
69
  the in-app signer; keypair files belong to scripts only.
@@ -55,10 +74,14 @@ The same SDK runs against a devnet or local deployment by pointing the three coo
55
74
 
56
75
  ```bash
57
76
  SOLANA_RPC_URL=https://api.devnet.solana.com # or http://127.0.0.1:8899 for a local fork
58
- CLOAK_RELAY_URL=<relay serving that deployment>
77
+ CLOAK_RELAY_URL=<endpoint serving that deployment> # read by examples/ and scripts/, NOT by the SDK
59
78
  CLOAK_PROGRAM_ID=<program id of that deployment> # examples read this; pass it as programId in your code
60
79
  ```
61
80
 
81
+ `CLOAK_RELAY_URL` configures the shipped examples and scripts, which pass its value as `relayUrl`.
82
+ The SDK itself does not read it: the endpoint a build may talk to comes from `src/config/relay.ts`,
83
+ so pointing at a devnet or local deployment means editing that file in a checkout and building it.
84
+
62
85
  The shipped examples default to a local fork (`http://127.0.0.1:8899`) with a local relay
63
86
  (`http://127.0.0.1:5500`); see [Examples](#examples).
64
87
 
@@ -188,11 +211,104 @@ address lookup tables); deposits use either depending on the path taken. `signMe
188
211
  unless `enforceViewingKeyRegistration: false` is set; without it the call fails with
189
212
  "Viewing key registration is mandatory: signMessage (wallet) or depositorKeypair is required."
190
213
 
214
+ A deposit is signed by the wallet and goes straight to chain. A private send, a withdrawal and a
215
+ swap are submitted for you, and each one carries an authenticated sender — so those three need
216
+ `signMessage` and `walletPublicKey` even when `enforceViewingKeyRegistration` is off:
217
+
218
+ ```ts
219
+ // Private send (externalAmount 0) from a wallet adapter. Same for fullWithdraw / partialWithdraw
220
+ // (externalAmount < 0) and swapUtxo / swapWithChange.
221
+ await transact(
222
+ { inputUtxos, outputUtxos, externalAmount: BigInt(0) },
223
+ {
224
+ connection,
225
+ programId: CLOAK_PROGRAM_ID,
226
+ relayUrl,
227
+ signMessage: (message) => wallet.signMessage(message),
228
+ walletPublicKey: wallet.publicKey, // becomes the authenticated sender
229
+ chainNoteViewingKeyNk: nk,
230
+ },
231
+ );
232
+ ```
233
+
234
+ `signMessage` must return the raw 64-byte ed25519 detached signature over the bytes it is given;
235
+ adapters that re-encode or wrap the result are rejected by name. `walletPublicKey` must be the end
236
+ user's own wallet — it is the identity the transaction is authenticated as. Passing neither a
237
+ `depositorKeypair` nor both of these fails immediately, before any proof is generated.
238
+
239
+ `depositorKeypair`, `walletPublicKey` and `depositorPublicKey` are three names for the SAME end
240
+ user. That one key is the request's authenticated sender, the key screened for sanctions, and the
241
+ wallet a viewing key is registered against, so setting two of the names to two different keys would
242
+ screen one person and authenticate another. Passing two different wallets is refused at the call.
243
+
244
+ ### The approval window
245
+
246
+ The request's timestamp is stamped when the bytes are built, because it is part of what the wallet
247
+ signs, and the relay accepts a first-use request for 300 seconds from that moment. An approval left
248
+ sitting in a hardware wallet past that window cannot be rescued by re-stamping it, so the SDK stops
249
+ there and says so instead of shipping a request that can only come back as a 401. Retry the
250
+ operation and approve the prompt when it appears.
251
+
252
+ `auth_issued_at` comes from the machine's own clock, and the relay rejects anything more than 30
253
+ seconds ahead of its own. A user whose clock is fast cannot authenticate at all until it is
254
+ corrected; `explainRelayAuthRejection` turns that rejection, and the rest of the relay's 401s, into
255
+ a sentence that names the cause. The SDK applies it to its own relay errors already.
256
+
257
+ A private send or withdrawal signs exactly ONCE: every network retry re-POSTs the same signed
258
+ bytes, so the user sees one prompt. A swap re-proves on every retry, so each retry needs its own
259
+ approval; `maxWalletApprovals` (default 5) bounds how many times one swap may interrupt the user,
260
+ independently of `maxRootRetries`. A `depositorKeypair` signs silently and is unaffected.
261
+
262
+ ### Authenticating a call you build yourself
263
+
264
+ To authenticate a call you build yourself (a swap retry that posts only `retry_request_id`, for
265
+ example), use the exported primitives rather than re-deriving the scheme:
266
+
267
+ ```ts
268
+ import { buildRelayAuthPreimage, TRANSACT_SWAP_AUTH_FIELDS } from "@cloak.dev/sdk";
269
+
270
+ const body = { retry_request_id: requestId, slippage_bps: 500 };
271
+ const preimage = buildRelayAuthPreimage(
272
+ "/transact_swap",
273
+ CLOAK_PROGRAM_ID,
274
+ body,
275
+ wallet.publicKey,
276
+ undefined,
277
+ TRANSACT_SWAP_AUTH_FIELDS,
278
+ );
279
+ const signature = await wallet.signMessage(preimage.message);
280
+
281
+ const payload = {
282
+ ...body,
283
+ sender: preimage.sender,
284
+ auth_issued_at: preimage.auth_issued_at,
285
+ auth_nonce: preimage.auth_nonce,
286
+ auth_signature: Buffer.from(signature).toString("base64"),
287
+ };
288
+ ```
289
+
290
+ Reuse one `preimage` for every retry of the same logical request; rebuilding it produces a new
291
+ nonce and a new request.
292
+
293
+ `slippage_bps` in that snippet is not decoration. Every other field in both lists is optional on
294
+ the relay side, so omitting one and signing it as `null` matches what the relay signs. `slippage_bps`
295
+ is the exception: it is not optional, and a request that omits it is read by the relay as **500**.
296
+ Omit it and you sign `null` while the relay signs `500`, the digests differ, and the answer is a 401
297
+ that points at nothing. Set it explicitly, to the same value the body carries. The SDK's own swap
298
+ path always does; a hand-built body that leaves it out is refused before it is signed.
299
+
300
+ `canonicalJson` is exported for the same reason, and it is the SDK's half of a byte-for-byte
301
+ agreement with one Rust function over one fixed schema: ASCII keys over strings, small unsigned
302
+ integers, booleans, nulls, arrays and plain objects. Amounts travel as decimal strings, not numbers,
303
+ because JavaScript and `serde_json` do not spell every number the same way. Anything outside that
304
+ schema that could serialize differently on the two sides is refused rather than silently signed.
305
+
191
306
  ## The UTXO model
192
307
 
193
308
  - A shielded balance is a set of notes (`Utxo`): `{ amount, keypair, blinding, mintAddress, index?, commitment? }`.
194
309
  Amounts are `bigint` in base units (lamports, or 10^-6 for USDC/USDT).
195
- - Every transaction is 2-in / 2-out. Pad unused slots with `createZeroUtxo(mint)`.
310
+ - Every transaction is 2-in / 2-out; `transact` pads unused slots with zero notes for you.
311
+ `createZeroUtxo(mint)` is exported if you want to pass them explicitly.
196
312
  - `externalAmount > 0` is a deposit (funds enter from `depositor`), `< 0` is a withdrawal to
197
313
  `recipient`, `0` is a private send inside the pool.
198
314
  - Pools are per mint. All inputs and outputs of one transaction live in the same pool;
@@ -261,7 +377,7 @@ await transact(
261
377
 
262
378
  Minimum SPL deposit is 1.00 token. SOL deposits fit in a single v0 packet with the production
263
379
  lookup table; SPL deposits currently still create a small supplemental lookup table
264
- (about 0.0056 SOL rent, reclaimable by the depositor). Details: [docs/DEPOSIT-SIZE-NOTES.md](docs/DEPOSIT-SIZE-NOTES.md).
380
+ (about 0.0056 SOL rent, reclaimable by the depositor).
265
381
 
266
382
  ### Shielded swap (SOL to USDC/USDT)
267
383
 
@@ -343,10 +459,10 @@ notes also derive their secrets from it, which is what makes key-only recovery p
343
459
  it with the same care as the spend key.
344
460
 
345
461
  Before a wallet's first transaction the SDK registers `nk` with the relay for compliance scanning:
346
- it requests a challenge from `<relayUrl>/viewing-key/challenge`, signs it with `signMessage` (wallet)
347
- or `depositorKeypair`, and posts `nk` with the signature to `<relayUrl>/viewing-key/register`.
348
- Nothing viewing-key related is written on-chain. The step is on by default;
349
- `enforceViewingKeyRegistration: false` disables it.
462
+ it signs a one-time challenge with `signMessage` (wallet) or `depositorKeypair` and submits `nk`
463
+ with that signature. Nothing viewing-key related is written on-chain. The step is on by default;
464
+ `enforceViewingKeyRegistration: false` disables it. `registerViewingKey(relayUrl, userPubkey, nk,
465
+ signMessage)` performs the same registration on its own.
350
466
 
351
467
  ```ts
352
468
  import { scanTransactions, fetchCommitments, type Utxo } from "@cloak.dev/sdk";
@@ -365,8 +481,9 @@ scan.deliveredNotes; // notes sent to this wallet (amount + blinding; the
365
481
  scan.transactions; // history rows for compliance reporting
366
482
  ```
367
483
 
368
- `scanRecipientDeliveryNotes` returns only the delivered notes. Deposit recovery applies to deposits
369
- built with `createRecoverableDepositUtxo` (the default in the examples); a deposit built with
484
+ `scanRecipientDeliveryNotes` does the delivery sweep alone, returning `{ notes, rpcCalls }`
485
+ instead of a full scan result. Deposit recovery applies to deposits built with
486
+ `createRecoverableDepositUtxo` (the default in the examples); a deposit built with
370
487
  `createUtxo` has a random blinding that is written nowhere and appears as history only.
371
488
 
372
489
  A recovered or delivered note needs its leaf index before it can be spent. Look it up by commitment.
@@ -417,14 +534,12 @@ const deliveredNote: Utxo = {
417
534
 
418
535
  ## Nullifiers and spent checks
419
536
 
420
- Two exports share a name and are not interchangeable:
421
-
422
- - `computeUtxoNullifier(utxo)` is the circuit's scheme: `Poseidon(commitment, index, signature)`.
423
- This is the value the program records when a note is spent. Use it for spent checks.
424
- - `computeNullifier(skSpend, leafIndex)` is `Poseidon(sk0, sk1, leafIndex)` from the legacy
425
- `withdraw_regular` circuit. It does not match anything the 0.2.0 program stores.
537
+ The nullifier export is `computeUtxoNullifier(utxo)`, the circuit's scheme:
538
+ `Poseidon(commitment, index, signature)`. This is the value the program records when a note is
539
+ spent, so it is what a spent check compares against. It is async and requires `utxo.index` to be
540
+ set.
426
541
 
427
- Prefer the helpers, which use the UTXO scheme:
542
+ Prefer the helpers, which derive the nullifier and check the on-chain PDA for you:
428
543
 
429
544
  ```ts
430
545
  import { verifyUtxos, preflightNullifiers } from "@cloak.dev/sdk";
@@ -450,7 +565,7 @@ Deposits and private sends carry no protocol fee.
450
565
  A withdrawal or swap must exceed its fee or the program rejects it; deposits below the minimum are
451
566
  rejected with `DepositTooSmall`.
452
567
 
453
- `utils/fees.ts` mirrors the SOL pool for estimates in the UI:
568
+ `shared/fees.ts` mirrors the SOL pool for estimates in the UI:
454
569
 
455
570
  ```ts
456
571
  import { calculateFeeBigint, getDistributableAmount, isWithdrawAmountSufficient } from "@cloak.dev/sdk";
@@ -468,31 +583,43 @@ lookup-table rent are separate.
468
583
 
469
584
  Proving uses the `transaction` circuit from bundle `cloak-transaction-0.2.0` (multi-party ceremony,
470
585
  6 contributors plus a public final beacon; 42,672 constraints, 9 public inputs). The verifying key is
471
- embedded in the program.
586
+ embedded in the program, so the SDK never fetches it; for the record, the exported
587
+ `transaction.vkey.json` from the same ceremony is SHA-256
588
+ `deb40e7b94eae17db2975d23dcf26c26db2a36a4f02d14a25830dee3e88fb93c`.
472
589
 
473
590
  | Artifact | SHA-256 |
474
591
  | --- | --- |
475
592
  | `transaction_js/transaction.wasm` | `02ec02e954ae3932827ad9de51afa597ca95569aa97fec8410879c937a58aa2b` |
476
593
  | `transaction_final.zkey` | `9da7db8cb1370fc497d36a0365f1f107ab0b0c13ca66fa9f0287e5f96ee68d25` |
477
- | `transaction.vkey.json` | `deb40e7b94eae17db2975d23dcf26c26db2a36a4f02d14a25830dee3e88fb93c` |
478
594
 
479
- The SDK downloads the artifacts once per process from
595
+ The SDK downloads both artifacts once per process from
480
596
  `DEFAULT_TRANSACTION_CIRCUITS_URL` (`https://storage.googleapis.com/cloak-circuits/circuits/0.2.0`),
481
597
  hashes them, and refuses to prove if any digest differs from the pinned values. No configuration is
482
598
  needed for mainnet.
483
599
 
484
- To serve the artifacts yourself (a mirror or a local directory containing
485
- `transaction_js/transaction.wasm` and `transaction_final.zkey` with the same digests):
600
+ To load the artifacts from a local directory instead (an air-gapped machine, a React Native bundle,
601
+ or the ceremony output — the directory must contain `transaction_js/transaction.wasm` and
602
+ `transaction_final.zkey` with the same digests):
486
603
 
487
604
  ```ts
488
605
  import { resolveCircuitsBase, setCircuitsPath } from "@cloak.dev/sdk";
489
606
 
490
- // explicit argument, else CLOAK_CIRCUITS_PATH / CLOAK_CIRCUITS, else the pinned default
607
+ // explicit argument, else the pinned default; no environment variable is consulted
491
608
  setCircuitsPath(resolveCircuitsBase());
609
+ setCircuitsPath("/opt/cloak/ceremony-0.2.0"); // or a local directory you control
492
610
  ```
493
611
 
494
612
  Never write the artifact URL out by hand: the version segment and the digests are declared together
495
- in `src/config/circuit-release.ts` so they cannot drift.
613
+ in `src/proving/circuits.ts` so they cannot drift.
614
+
615
+ Where the artifacts may be read from is fixed when the SDK is built. `transaction.wasm` is the
616
+ witness generator, so it is handed your spend key, your note secrets, the amounts and the recipient
617
+ — a mirror on another host is therefore not accepted, and the SHA-256 check over the bytes cannot be
618
+ switched off by any option or environment variable. A local directory is accepted (nothing leaves
619
+ the machine, and the digest check still decides whether the bytes are usable); an `http(s)` base
620
+ must be the bundle base this build pins. To point a build somewhere else, edit
621
+ `RELAY_ORIGIN_ALLOWLIST` in `src/config/relay.ts` and rebuild — the one edit that also unlocks the
622
+ relay endpoint and the localhost-RPC check.
496
623
 
497
624
  ## Version and compatibility
498
625
 
@@ -502,38 +629,49 @@ in `src/config/circuit-release.ts` so they cannot drift.
502
629
  USDC and USDT; viewing-key discovery followed by a spend; keypair-only deposit recovery.
503
630
  - Proofs generated with the 0.1.x circuits are rejected by the mainnet program. SDK 0.1.x builds
504
631
  (which ship the 0.1.0 bundle) are not usable against it; upgrade to 0.2.0.
505
- - The note-based `CloakSDK` class (`deposit` / `withdraw` / `send` / `swap` on `CloakNote`) targets
506
- the pre-0.2.0 instruction layout and the unpublished 0.1.0 `withdraw_*` circuits. It is still
507
- exported but does not work against the current program. Use the UTXO API above.
632
+ - The note-based `CloakSDK` class is removed in 0.2.0. It targeted the pre-0.2.0 instruction layout
633
+ and the unpublished 0.1.0 `withdraw_*` circuits, so it could not work against the current program.
634
+ Use the UTXO API above: `transact`, `transfer`, `partialWithdraw`, `fullWithdraw`, `swapUtxo`.
508
635
 
509
636
  ## Examples
510
637
 
511
638
  ```bash
512
- npm run example:fast-send # deposit SOL, withdraw to a recipient
513
- npm run example:transfer # deposit, private send, recipient withdraw
514
- npm run example:fast-usdc-send # deposit SOL, swap to USDC, deposit USDC, withdraw USDC
515
- npm run example:usdc-pool-transfer # USDC pool: A deposits, sends to B, B spends
516
- npm run example:swap # SOL -> USDC swap with interactive route retry
517
- npm run example:swap-usdc # same, with the canonical swap markers
639
+ # SOL pool
640
+ npm run example:deposit # move lamports into the shielded SOL pool
641
+ npm run example:private-send # shield-to-shield send the recipient can discover
642
+ npm run example:withdraw # partial withdraw, then full withdraw of the change
643
+ npm run example:swap # SOL to USDC through Jupiter
644
+
645
+ # SPL pools (USDC, USDT). There is no SPL swap: swap input is wSOL-locked.
646
+ npm run example:spl-deposit
647
+ npm run example:spl-private-send
648
+ npm run example:spl-withdraw
649
+
650
+ # Not pool specific
651
+ npm run example:viewing-keys # derive a viewing key, register it, read your history
652
+
653
+ # Swap variants
654
+ npm run example:swap-usdc # same swap, with the canonical swap markers
518
655
  npm run example:swap-brz # BRZ with automatic USDC fallback
519
- npm run example:swap-recovery # pending / timed-out swap handling
520
- npm run example:history-scan # every flow, then full and incremental viewing-key scans
521
- npm run test:examples # dry-run all of the above (no wallet, no RPC)
656
+
657
+ npm run test:examples # dry-run all eight (no wallet, no RPC)
522
658
  ```
523
659
 
660
+ Examples log by default. Set `CLOAK_DEBUG=0` to silence them.
661
+
524
662
  Environment read by the examples:
525
663
 
526
664
  | Variable | Default |
527
665
  | --- | --- |
528
666
  | `SOLANA_RPC_URL` | `http://127.0.0.1:8899` (local fork) |
529
- | `CLOAK_RELAY_URL` | `http://127.0.0.1:5500` (local relay) |
667
+ | `CLOAK_RELAY_URL` | `http://127.0.0.1:5500` (local endpoint) — read by the examples, not by the SDK; it must name an origin the SDK build is pinned to |
530
668
  | `CLOAK_PROGRAM_ID` | `CLOAK_PROGRAM_ID` |
531
- | `CLOAK_CIRCUITS_PATH` | the pinned 0.2.0 bundle |
532
669
  | `CLOAK_ALT_ADDRESSES` | unset; the production lookup tables are resolved automatically |
533
670
 
534
671
  The examples fund throwaway keypairs from `~/.config/solana/id.json` and print stable
535
- `FULL_SIG|<example>|<step>|<signature>` markers. They import `@cloak.ag/sdk`, which
536
- `tsconfig.json` maps to `src/index.ts`; outside this repo import `@cloak.dev/sdk`.
672
+ `FULL_SIG|<example>|<step>|<signature>` markers. They import `@cloak.dev/sdk`, the published
673
+ package name; inside this repo `tsconfig.json` maps that specifier to `src/index.ts` so the
674
+ examples run against source.
537
675
 
538
676
  ## Error handling
539
677
 
@@ -573,14 +711,14 @@ npm run verify:dist # dist is byte-identical to a fresh build
573
711
  npm run test:examples # dry-run the examples
574
712
  ```
575
713
 
576
- Notes: [docs/DEPOSIT-SIZE-NOTES.md](docs/DEPOSIT-SIZE-NOTES.md) (deposit transaction size,
577
- confirmed-tree reads, supplemental lookup tables).
714
+ Notes: `docs/DEPOSIT-SIZE-NOTES.md` (deposit transaction size, confirmed-tree reads,
715
+ supplemental lookup tables), `docs/transact-split.md` (how `flows/transact.ts` is split),
716
+ `docs/cleanup-plan.md` (the root/scripts/examples cleanup this repo is executing).
578
717
 
579
718
  ## Links
580
719
 
581
720
  - Website: [https://cloak.ag](https://cloak.ag)
582
721
  - Documentation: [https://docs.cloak.ag](https://docs.cloak.ag)
583
- - GitHub: [https://github.com/cloak-ag/sdk](https://github.com/cloak-ag/sdk)
584
722
 
585
723
  ## License
586
724