@cloak.dev/sdk 0.2.0 → 0.2.2-staging.9ffa9f5
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 +202 -0
- package/README.md +201 -57
- package/dist/{chunk-YX5SCAMR.js → chunk-KNM6K5NG.js} +8 -95
- package/dist/index.cjs +3565 -4351
- package/dist/index.d.cts +732 -807
- package/dist/index.d.ts +732 -807
- package/dist/index.js +3452 -4182
- package/dist/{utxo-PFJT3ETR.js → utxo-XPEGGWZ6.js} +3 -3
- package/package.json +50 -20
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
|
@@ -5,13 +5,13 @@ per-mint pool, send privately inside the pool, withdraw to any address, or swap
|
|
|
5
5
|
USDC/USDT with the output landing in a public token account. Groth16 proofs are generated
|
|
6
6
|
client-side (snarkjs) and verified on-chain by the Cloak program.
|
|
7
7
|
|
|
8
|
-
Version 0.2.
|
|
8
|
+
Version 0.2.2 targets the mainnet program deployed on 2026-08-24 and the ceremony-frozen
|
|
9
9
|
`cloak-transaction-0.2.0` circuit bundle. See [Version and compatibility](#version-and-compatibility).
|
|
10
10
|
|
|
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.2 @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
|
-
|
|
|
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
|
|
35
|
-
`
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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=<
|
|
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
|
|
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;
|
|
@@ -260,8 +376,11 @@ await transact(
|
|
|
260
376
|
```
|
|
261
377
|
|
|
262
378
|
Minimum SPL deposit is 1.00 token. SOL deposits fit in a single v0 packet with the production
|
|
263
|
-
lookup table; SPL deposits
|
|
264
|
-
(about 0.0056 SOL rent, reclaimable by the depositor)
|
|
379
|
+
lookup table; SPL deposits by default create a small depositor-signed supplemental lookup table
|
|
380
|
+
(about 0.0056 SOL rent, reclaimable by the depositor), which costs a second wallet signature. Set
|
|
381
|
+
`relaySupplementalAlt: true` in `TransactOptions` to ask the relay to extend its own shared lookup
|
|
382
|
+
table instead, cutting an SPL deposit to one signature; off by default, and any relay-side failure
|
|
383
|
+
falls back automatically to the depositor-signed path above with no caller action needed.
|
|
265
384
|
|
|
266
385
|
### Shielded swap (SOL to USDC/USDT)
|
|
267
386
|
|
|
@@ -343,10 +462,10 @@ notes also derive their secrets from it, which is what makes key-only recovery p
|
|
|
343
462
|
it with the same care as the spend key.
|
|
344
463
|
|
|
345
464
|
Before a wallet's first transaction the SDK registers `nk` with the relay for compliance scanning:
|
|
346
|
-
it
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
`
|
|
465
|
+
it signs a one-time challenge with `signMessage` (wallet) or `depositorKeypair` and submits `nk`
|
|
466
|
+
with that signature. Nothing viewing-key related is written on-chain. The step is on by default;
|
|
467
|
+
`enforceViewingKeyRegistration: false` disables it. `registerViewingKey(relayUrl, userPubkey, nk,
|
|
468
|
+
signMessage)` performs the same registration on its own.
|
|
350
469
|
|
|
351
470
|
```ts
|
|
352
471
|
import { scanTransactions, fetchCommitments, type Utxo } from "@cloak.dev/sdk";
|
|
@@ -365,8 +484,9 @@ scan.deliveredNotes; // notes sent to this wallet (amount + blinding; the
|
|
|
365
484
|
scan.transactions; // history rows for compliance reporting
|
|
366
485
|
```
|
|
367
486
|
|
|
368
|
-
`scanRecipientDeliveryNotes`
|
|
369
|
-
|
|
487
|
+
`scanRecipientDeliveryNotes` does the delivery sweep alone, returning `{ notes, rpcCalls }`
|
|
488
|
+
instead of a full scan result. Deposit recovery applies to deposits built with
|
|
489
|
+
`createRecoverableDepositUtxo` (the default in the examples); a deposit built with
|
|
370
490
|
`createUtxo` has a random blinding that is written nowhere and appears as history only.
|
|
371
491
|
|
|
372
492
|
A recovered or delivered note needs its leaf index before it can be spent. Look it up by commitment.
|
|
@@ -417,14 +537,12 @@ const deliveredNote: Utxo = {
|
|
|
417
537
|
|
|
418
538
|
## Nullifiers and spent checks
|
|
419
539
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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.
|
|
540
|
+
The nullifier export is `computeUtxoNullifier(utxo)`, the circuit's scheme:
|
|
541
|
+
`Poseidon(commitment, index, signature)`. This is the value the program records when a note is
|
|
542
|
+
spent, so it is what a spent check compares against. It is async and requires `utxo.index` to be
|
|
543
|
+
set.
|
|
426
544
|
|
|
427
|
-
Prefer the helpers, which
|
|
545
|
+
Prefer the helpers, which derive the nullifier and check the on-chain PDA for you:
|
|
428
546
|
|
|
429
547
|
```ts
|
|
430
548
|
import { verifyUtxos, preflightNullifiers } from "@cloak.dev/sdk";
|
|
@@ -450,7 +568,7 @@ Deposits and private sends carry no protocol fee.
|
|
|
450
568
|
A withdrawal or swap must exceed its fee or the program rejects it; deposits below the minimum are
|
|
451
569
|
rejected with `DepositTooSmall`.
|
|
452
570
|
|
|
453
|
-
`
|
|
571
|
+
`src/shared/fees.ts` mirrors the SOL pool for estimates in the UI:
|
|
454
572
|
|
|
455
573
|
```ts
|
|
456
574
|
import { calculateFeeBigint, getDistributableAmount, isWithdrawAmountSufficient } from "@cloak.dev/sdk";
|
|
@@ -462,78 +580,105 @@ isWithdrawAmountSufficient(6_000_000n); // true: 0.006 SOL covers the 0.005018
|
|
|
462
580
|
|
|
463
581
|
For USDC/USDT compute `450_000n + amount * 3n / 1000n`. Treat these as estimates: the deployed
|
|
464
582
|
`PoolConfig` is the source of truth. Solana transaction fees and, for SPL deposits, the supplemental
|
|
465
|
-
lookup-table rent are separate
|
|
583
|
+
lookup-table rent are separate (see `relaySupplementalAlt` under [SPL pools](#spl-pools-usdc-usdt)
|
|
584
|
+
for cutting that rent's signature cost).
|
|
466
585
|
|
|
467
586
|
## Circuit artifacts
|
|
468
587
|
|
|
469
588
|
Proving uses the `transaction` circuit from bundle `cloak-transaction-0.2.0` (multi-party ceremony,
|
|
470
589
|
6 contributors plus a public final beacon; 42,672 constraints, 9 public inputs). The verifying key is
|
|
471
|
-
embedded in the program
|
|
590
|
+
embedded in the program, so the SDK never fetches it; for the record, the exported
|
|
591
|
+
`transaction.vkey.json` from the same ceremony is SHA-256
|
|
592
|
+
`deb40e7b94eae17db2975d23dcf26c26db2a36a4f02d14a25830dee3e88fb93c`.
|
|
472
593
|
|
|
473
594
|
| Artifact | SHA-256 |
|
|
474
595
|
| --- | --- |
|
|
475
596
|
| `transaction_js/transaction.wasm` | `02ec02e954ae3932827ad9de51afa597ca95569aa97fec8410879c937a58aa2b` |
|
|
476
597
|
| `transaction_final.zkey` | `9da7db8cb1370fc497d36a0365f1f107ab0b0c13ca66fa9f0287e5f96ee68d25` |
|
|
477
|
-
| `transaction.vkey.json` | `deb40e7b94eae17db2975d23dcf26c26db2a36a4f02d14a25830dee3e88fb93c` |
|
|
478
598
|
|
|
479
|
-
The SDK downloads
|
|
599
|
+
The SDK downloads both artifacts once per process from
|
|
480
600
|
`DEFAULT_TRANSACTION_CIRCUITS_URL` (`https://storage.googleapis.com/cloak-circuits/circuits/0.2.0`),
|
|
481
601
|
hashes them, and refuses to prove if any digest differs from the pinned values. No configuration is
|
|
482
602
|
needed for mainnet.
|
|
483
603
|
|
|
484
|
-
To
|
|
485
|
-
`transaction_js/transaction.wasm` and
|
|
604
|
+
To load the artifacts from a local directory instead (an air-gapped machine, a React Native bundle,
|
|
605
|
+
or the ceremony output — the directory must contain `transaction_js/transaction.wasm` and
|
|
606
|
+
`transaction_final.zkey` with the same digests):
|
|
486
607
|
|
|
487
608
|
```ts
|
|
488
609
|
import { resolveCircuitsBase, setCircuitsPath } from "@cloak.dev/sdk";
|
|
489
610
|
|
|
490
|
-
// explicit argument, else
|
|
611
|
+
// explicit argument, else the pinned default; no environment variable is consulted
|
|
491
612
|
setCircuitsPath(resolveCircuitsBase());
|
|
613
|
+
setCircuitsPath("/opt/cloak/ceremony-0.2.0"); // or a local directory you control
|
|
492
614
|
```
|
|
493
615
|
|
|
494
616
|
Never write the artifact URL out by hand: the version segment and the digests are declared together
|
|
495
|
-
in `src/
|
|
617
|
+
in `src/proving/circuits.ts` so they cannot drift.
|
|
618
|
+
|
|
619
|
+
Where the artifacts may be read from is fixed when the SDK is built. `transaction.wasm` is the
|
|
620
|
+
witness generator, so it is handed your spend key, your note secrets, the amounts and the recipient
|
|
621
|
+
— a mirror on another host is therefore not accepted, and the SHA-256 check over the bytes cannot be
|
|
622
|
+
switched off by any option or environment variable. A local directory is accepted (nothing leaves
|
|
623
|
+
the machine, and the digest check still decides whether the bytes are usable); an `http(s)` base
|
|
624
|
+
must be the bundle base this build pins. To point a build somewhere else, edit
|
|
625
|
+
`RELAY_ORIGIN_ALLOWLIST` in `src/config/relay.ts` and rebuild — the one edit that also unlocks the
|
|
626
|
+
relay endpoint and the localhost-RPC check.
|
|
496
627
|
|
|
497
628
|
## Version and compatibility
|
|
498
629
|
|
|
499
|
-
- SDK 0.2.
|
|
500
|
-
(deployed 2026-08-24) and the 0.2.0 circuit bundle
|
|
630
|
+
- SDK 0.2.2 pairs with the mainnet program at `zh1eLd6rSphLejbFfJEneUwzHRfMKxgzrgkfwA6qRkW`
|
|
631
|
+
(deployed 2026-08-24) and the 0.2.0 circuit bundle — the program and circuit bundle are unchanged
|
|
632
|
+
since 0.2.0; only the SDK package version has moved. All flows in this README were exercised on
|
|
501
633
|
mainnet on 2026-08-25: deposit, private send and withdraw on SOL, USDC and USDT; swaps SOL to
|
|
502
634
|
USDC and USDT; viewing-key discovery followed by a spend; keypair-only deposit recovery.
|
|
635
|
+
- 0.2.2 adds the opt-in `TransactOptions.relaySupplementalAlt` (see [SPL pools](#spl-pools-usdc-usdt)):
|
|
636
|
+
off by default, and existing behavior is unchanged when it is left unset.
|
|
503
637
|
- Proofs generated with the 0.1.x circuits are rejected by the mainnet program. SDK 0.1.x builds
|
|
504
|
-
(which ship the 0.1.0 bundle) are not usable against it; upgrade to 0.2.
|
|
505
|
-
- The note-based `CloakSDK` class
|
|
506
|
-
|
|
507
|
-
|
|
638
|
+
(which ship the 0.1.0 bundle) are not usable against it; upgrade to 0.2.x.
|
|
639
|
+
- The note-based `CloakSDK` class is removed in 0.2.0. It targeted the pre-0.2.0 instruction layout
|
|
640
|
+
and the unpublished 0.1.0 `withdraw_*` circuits, so it could not work against the current program.
|
|
641
|
+
Use the UTXO API above: `transact`, `transfer`, `partialWithdraw`, `fullWithdraw`, `swapUtxo`.
|
|
508
642
|
|
|
509
643
|
## Examples
|
|
510
644
|
|
|
511
645
|
```bash
|
|
512
|
-
|
|
513
|
-
npm run example:
|
|
514
|
-
npm run example:
|
|
515
|
-
npm run example:
|
|
516
|
-
npm run example:swap # SOL
|
|
517
|
-
|
|
646
|
+
# SOL pool
|
|
647
|
+
npm run example:deposit # move lamports into the shielded SOL pool
|
|
648
|
+
npm run example:private-send # shield-to-shield send the recipient can discover
|
|
649
|
+
npm run example:withdraw # partial withdraw, then full withdraw of the change
|
|
650
|
+
npm run example:swap # SOL to USDC through Jupiter
|
|
651
|
+
|
|
652
|
+
# SPL pools (USDC, USDT). There is no SPL swap: swap input is wSOL-locked.
|
|
653
|
+
npm run example:spl-deposit
|
|
654
|
+
npm run example:spl-private-send
|
|
655
|
+
npm run example:spl-withdraw
|
|
656
|
+
|
|
657
|
+
# Not pool specific
|
|
658
|
+
npm run example:viewing-keys # derive a viewing key, register it, read your history
|
|
659
|
+
|
|
660
|
+
# Swap variants
|
|
661
|
+
npm run example:swap-usdc # same swap, with the canonical swap markers
|
|
518
662
|
npm run example:swap-brz # BRZ with automatic USDC fallback
|
|
519
|
-
|
|
520
|
-
npm run
|
|
521
|
-
npm run test:examples # dry-run all of the above (no wallet, no RPC)
|
|
663
|
+
|
|
664
|
+
npm run test:examples # dry-run all eight (no wallet, no RPC)
|
|
522
665
|
```
|
|
523
666
|
|
|
667
|
+
Examples log by default. Set `CLOAK_DEBUG=0` to silence them.
|
|
668
|
+
|
|
524
669
|
Environment read by the examples:
|
|
525
670
|
|
|
526
671
|
| Variable | Default |
|
|
527
672
|
| --- | --- |
|
|
528
673
|
| `SOLANA_RPC_URL` | `http://127.0.0.1:8899` (local fork) |
|
|
529
|
-
| `CLOAK_RELAY_URL` | `http://127.0.0.1:5500` (local
|
|
674
|
+
| `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
675
|
| `CLOAK_PROGRAM_ID` | `CLOAK_PROGRAM_ID` |
|
|
531
|
-
| `CLOAK_CIRCUITS_PATH` | the pinned 0.2.0 bundle |
|
|
532
676
|
| `CLOAK_ALT_ADDRESSES` | unset; the production lookup tables are resolved automatically |
|
|
533
677
|
|
|
534
678
|
The examples fund throwaway keypairs from `~/.config/solana/id.json` and print stable
|
|
535
|
-
`FULL_SIG|<example>|<step>|<signature>` markers. They import `@cloak.
|
|
536
|
-
`tsconfig.json` maps to `src/index.ts
|
|
679
|
+
`FULL_SIG|<example>|<step>|<signature>` markers. They import `@cloak.dev/sdk`, the published
|
|
680
|
+
package name; inside this repo `tsconfig.json` maps that specifier to `src/index.ts` so the
|
|
681
|
+
examples run against source.
|
|
537
682
|
|
|
538
683
|
## Error handling
|
|
539
684
|
|
|
@@ -573,14 +718,13 @@ npm run verify:dist # dist is byte-identical to a fresh build
|
|
|
573
718
|
npm run test:examples # dry-run the examples
|
|
574
719
|
```
|
|
575
720
|
|
|
576
|
-
Notes:
|
|
577
|
-
|
|
721
|
+
Notes: `docs/DEPOSIT-SIZE-NOTES.md` (deposit transaction size, confirmed-tree reads,
|
|
722
|
+
supplemental lookup tables), `docs/transact-split.md` (how `flows/transact.ts` is split).
|
|
578
723
|
|
|
579
724
|
## Links
|
|
580
725
|
|
|
581
726
|
- Website: [https://cloak.ag](https://cloak.ag)
|
|
582
727
|
- Documentation: [https://docs.cloak.ag](https://docs.cloak.ag)
|
|
583
|
-
- GitHub: [https://github.com/cloak-ag/sdk](https://github.com/cloak-ag/sdk)
|
|
584
728
|
|
|
585
729
|
## License
|
|
586
730
|
|