@cloak.dev/sdk 0.1.5 → 0.1.7
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/CHANGELOG.md +173 -0
- package/README.md +183 -96
- package/dist/index.cjs +296 -2256
- package/dist/index.d.cts +443 -1512
- package/dist/index.d.ts +443 -1512
- package/dist/index.js +252 -2161
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@cloak.dev/sdk` are documented here. The format
|
|
4
|
+
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
|
|
5
|
+
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.1.6] — 2026-04-27
|
|
8
|
+
|
|
9
|
+
### Removed (BREAKING)
|
|
10
|
+
|
|
11
|
+
- **`CloakSDK.deposit / privateTransfer / withdraw / send / swap`** —
|
|
12
|
+
these methods built a legacy `[discriminator: 1, amount: u64,
|
|
13
|
+
commitment: 32]` deposit instruction. The deployed shield-pool program
|
|
14
|
+
has repurposed discriminator `1` to `TransactSwap` (which requires
|
|
15
|
+
`>= 8` accounts), so every external invocation hit `0x1063
|
|
16
|
+
MissingAccounts` at ~112 CU. Reported by the cloak-squads hackathon
|
|
17
|
+
team on 2026-04-26.
|
|
18
|
+
- **`createDepositInstruction` / `validateDepositParams` /
|
|
19
|
+
`DepositInstructionParams`** — the disc-1 instruction builder and its
|
|
20
|
+
validators.
|
|
21
|
+
- **`CloakNote` model and helpers** — `generateNote`, `parseNote`,
|
|
22
|
+
`exportNote`, `findNoteByCommitment`, `filterNotesByNetwork`,
|
|
23
|
+
`filterWithdrawableNotes`, `isWithdrawable`, `updateNoteWithDeposit`,
|
|
24
|
+
`getRecipientAmount`, `generateNoteFromWallet`. The OLD
|
|
25
|
+
`withdraw_regular.circom` model used a 3-input
|
|
26
|
+
`Poseidon(amount, r0, r1, pk_spend)` commitment that is incompatible
|
|
27
|
+
with the deployed `transaction.circom` UTXO circuit (4-input
|
|
28
|
+
`Poseidon(amount, pubkey, blinding, mintAddress)`).
|
|
29
|
+
- **`CloakSDK.loadNotes / saveNote / findNote / getTransactionMetadata`**
|
|
30
|
+
and the `StorageAdapter.notes*` methods. The adapter is now keys-only.
|
|
31
|
+
- **Encrypted-note helpers** — `prepareEncryptedOutput`,
|
|
32
|
+
`prepareEncryptedOutputForRecipient`, `encodeNoteSimple` (legacy
|
|
33
|
+
`CloakNote`-based variants).
|
|
34
|
+
- **Pending-operations helpers** — `savePendingDeposit`/`Withdrawal`
|
|
35
|
+
and friends, plus `PendingDeposit` / `PendingWithdrawal` types.
|
|
36
|
+
- **Legacy proof generation** — `src/utils/proof-generation.ts`
|
|
37
|
+
(`generateWithdrawRegularProof`, `generateWithdrawSwapProof`,
|
|
38
|
+
`verifyCircuitIntegrity`, `verifyAllCircuits`,
|
|
39
|
+
`areCircuitsAvailable`, `getDefaultCircuitsPath`,
|
|
40
|
+
`EXPECTED_CIRCUIT_HASHES`, `DEFAULT_CIRCUITS_URL`).
|
|
41
|
+
- **Legacy bigint hash helpers** in `src/utils/crypto.ts` —
|
|
42
|
+
`computeCommitment` (3-arg), `generateCommitmentAsync`,
|
|
43
|
+
`computeNullifier{,Async,Sync}`, `computeOutputsHash{,Async,Sync,FromLimbs}`,
|
|
44
|
+
`computeSwapOutputsHash{,Async,Sync}`. The UTXO-model
|
|
45
|
+
`computeCommitment(utxo)` / `computeNullifier(utxo)` live in
|
|
46
|
+
`core/utxo` and are re-exported as `computeUtxoCommitment` /
|
|
47
|
+
`computeUtxoNullifier`.
|
|
48
|
+
- **OLD types** in `src/core/types.ts` — `CloakNote`, `Transfer`,
|
|
49
|
+
`MaxLengthArray`, `TransferResult`, `DepositResult`, `DepositOptions`,
|
|
50
|
+
`TransferOptions`, `WithdrawOptions`, `DepositStatus`, `SwapOptions`,
|
|
51
|
+
`SwapParams`, `SwapResult`. Plus `proofTimeout` (was unused on the
|
|
52
|
+
snapshot).
|
|
53
|
+
- **`withdraw_regular_final.zkey`** — the ~10 MB legacy circuit asset.
|
|
54
|
+
|
|
55
|
+
### Changed (BREAKING)
|
|
56
|
+
|
|
57
|
+
- **`CloakConfig` is now a snapshot-only type.** It is the structurally
|
|
58
|
+
narrow value returned by `CloakSDK.getConfig()`: exactly seven
|
|
59
|
+
fields — `network`, `cloakKeys`, `programId`, `poolAddress`,
|
|
60
|
+
`merkleTreeAddress`, `treasuryAddress`, `debug`. The constructor
|
|
61
|
+
input is the new exported `CloakSDKOptions` interface (which still
|
|
62
|
+
accepts `keypairBytes`, `wallet`, and `storage` — they are consumed
|
|
63
|
+
into private SDK state and are *never* persisted on the snapshot).
|
|
64
|
+
`sdk.getConfig().keypairBytes` is now a `TS2339` compile-time error.
|
|
65
|
+
- `CloakConfig.cloakKeys` is now typed as `CloakKeyPair | undefined`
|
|
66
|
+
(was `any`).
|
|
67
|
+
- `LocalStorageAdapter` constructor signature is `(keysKey?: string)`;
|
|
68
|
+
the second `notesKey` parameter is gone.
|
|
69
|
+
- `CloakSDK` constructor now throws `CloakError(_, "validation", false)`
|
|
70
|
+
instead of plain `Error` when no signer is provided; `getPublicKey()`
|
|
71
|
+
throws `CloakError(_, "wallet", false)` when neither keypair nor
|
|
72
|
+
wallet adapter is configured. Consumers' `parseError()` /
|
|
73
|
+
`classifyRelayError()` flows now handle these uniformly.
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
77
|
+
- **`LocalStorageAdapter` auto-purges legacy plaintext secrets.** On
|
|
78
|
+
the first construction in any browser origin, the adapter removes the
|
|
79
|
+
pre-0.1.6 `cloak_notes` localStorage entry (which contained `r`
|
|
80
|
+
randomness and `sk_spend` hex in plaintext) and writes a sentinel at
|
|
81
|
+
`cloak_purged_v0_1_5` so subsequent constructions skip the work.
|
|
82
|
+
Closes the upgrade-time security gap without requiring consumers to
|
|
83
|
+
call any helper. Consumers using a non-default legacy key can still
|
|
84
|
+
call `LocalStorageAdapter.purgeLegacyNoteStorage(customKey)`
|
|
85
|
+
explicitly.
|
|
86
|
+
- `LocalStorageAdapter.LEGACY_PURGE_SENTINEL_KEY` and
|
|
87
|
+
`LocalStorageAdapter.LEGACY_NOTES_KEY` static constants for callers
|
|
88
|
+
that want to inspect / reset the purge state.
|
|
89
|
+
- `LocalStorageAdapter.runLegacyPurgeOnce()` static helper for callers
|
|
90
|
+
that want to force the auto-purge before the first adapter
|
|
91
|
+
instantiation (e.g. during an upgrade boot sequence).
|
|
92
|
+
|
|
93
|
+
### Security
|
|
94
|
+
|
|
95
|
+
- `getConfig()` no longer leaks `keypairBytes`. The constructor
|
|
96
|
+
consumed the secret bytes into a `Keypair` and used to spread the
|
|
97
|
+
whole input onto `this.config`; `getConfig()`'s shallow-copy return
|
|
98
|
+
exposed the original `Uint8Array` by reference. `this.config` is now
|
|
99
|
+
built explicitly without `keypairBytes`/`wallet`/`storage`, and the
|
|
100
|
+
type system enforces the asymmetry (see "Changed" above).
|
|
101
|
+
- `LocalStorageAdapter` upgrade-time auto-purge (see "Added") removes
|
|
102
|
+
the now-unredeemable plaintext secrets that pre-0.1.6 versions left
|
|
103
|
+
in browser localStorage.
|
|
104
|
+
|
|
105
|
+
### Migration guide
|
|
106
|
+
|
|
107
|
+
The supported flow for transaction emission is the functional UTXO API.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import {
|
|
111
|
+
transact,
|
|
112
|
+
fullWithdraw,
|
|
113
|
+
partialWithdraw,
|
|
114
|
+
transfer,
|
|
115
|
+
swapUtxo,
|
|
116
|
+
createUtxo,
|
|
117
|
+
createZeroUtxo,
|
|
118
|
+
generateUtxoKeypair,
|
|
119
|
+
CLOAK_PROGRAM_ID,
|
|
120
|
+
NATIVE_SOL_MINT,
|
|
121
|
+
} from "@cloak.dev/sdk";
|
|
122
|
+
import { Connection, Keypair } from "@solana/web3.js";
|
|
123
|
+
|
|
124
|
+
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
125
|
+
const signer = Keypair.fromSecretKey(/* ... */);
|
|
126
|
+
|
|
127
|
+
// Deposit (positive externalAmount).
|
|
128
|
+
const owner = await generateUtxoKeypair();
|
|
129
|
+
const output = await createUtxo(10_000_000n, owner, NATIVE_SOL_MINT);
|
|
130
|
+
const deposited = await transact(
|
|
131
|
+
{
|
|
132
|
+
inputUtxos: [
|
|
133
|
+
await createZeroUtxo(NATIVE_SOL_MINT),
|
|
134
|
+
await createZeroUtxo(NATIVE_SOL_MINT),
|
|
135
|
+
],
|
|
136
|
+
outputUtxos: [output, await createZeroUtxo(NATIVE_SOL_MINT)],
|
|
137
|
+
externalAmount: 10_000_000n,
|
|
138
|
+
depositor: signer.publicKey,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
connection,
|
|
142
|
+
programId: CLOAK_PROGRAM_ID,
|
|
143
|
+
relayUrl: "https://api.cloak.ag",
|
|
144
|
+
depositorKeypair: signer,
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Reclaim to a public address.
|
|
149
|
+
await fullWithdraw(deposited.outputUtxos, signer.publicKey, {
|
|
150
|
+
connection,
|
|
151
|
+
programId: CLOAK_PROGRAM_ID,
|
|
152
|
+
relayUrl: "https://api.cloak.ag",
|
|
153
|
+
depositorKeypair: signer,
|
|
154
|
+
cachedMerkleTree: deposited.merkleTree,
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The `CloakSDK` class is retained as a thin config + read-only helper
|
|
159
|
+
(`getPublicKey`, `getCurrentRoot`, `getMerkleProof`,
|
|
160
|
+
`getTransactionStatus`, `importWalletKeys`, `exportWalletKeys`,
|
|
161
|
+
`getConfig`). Construction is unchanged for new fields.
|
|
162
|
+
|
|
163
|
+
### Verification
|
|
164
|
+
|
|
165
|
+
- Mainnet end-to-end smoke (deposit + withdraw cycle) on the patched
|
|
166
|
+
SDK: deposit signature
|
|
167
|
+
`5iGmrMYoSucYJ5PzEo5BCWioqTof4KNfjowq14ZYrbQACicaP2tJdJfRUy2CctsQLyNu6xpYXn8gYBg3EefsmXrv`
|
|
168
|
+
(slot 416038846, leaf 318); withdraw signature
|
|
169
|
+
`37A4T1KNiZLkmRvLAtui5ZN2VCmzjsZMzzGNwMosFA2UM1NGn7kP28p5APTvKRb51rNaLmWF42TJqK9GSZM5xCxn`
|
|
170
|
+
(slot 416055377). Net delta on the test wallet matches the expected
|
|
171
|
+
fee schedule (0.005 fixed + 0.3% var).
|
|
172
|
+
|
|
173
|
+
[0.1.6]: https://github.com/cloak-ag/sdk/pull/11
|
package/README.md
CHANGED
|
@@ -4,12 +4,12 @@ TypeScript SDK for the Cloak Protocol - Private transactions on Solana using zer
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- 🔒 **Private Transfers**:
|
|
8
|
-
-
|
|
9
|
-
- 💱 **Token Swaps**: Swap SOL for SPL tokens
|
|
10
|
-
- 🔐 **Type-Safe**: Full TypeScript
|
|
11
|
-
- 🌐 **Cross-Platform**:
|
|
12
|
-
- ⚡ **
|
|
7
|
+
- 🔒 **Private Transfers**: Shield, send, and reclaim SOL or SPL tokens via the UTXO model and zero-knowledge proofs
|
|
8
|
+
- 🔁 **Shield-to-shield**: Move funds privately between UTXOs (`transfer`) without ever de-shielding
|
|
9
|
+
- 💱 **Token Swaps**: Swap shielded SOL for SPL tokens via Jupiter (`swapUtxo`) — SOL → USDC, BRZ, etc.
|
|
10
|
+
- 🔐 **Type-Safe**: Full TypeScript types; the `getConfig()` snapshot is structurally narrower than the constructor input so secrets cannot be re-exposed by accident
|
|
11
|
+
- 🌐 **Cross-Platform**: Browser (wallet adapter) and Node.js (`Keypair`) modes
|
|
12
|
+
- ⚡ **Functional API**: `transact` / `transfer` / `partialWithdraw` / `fullWithdraw` / `swapUtxo` — pass connection + program + relay + signer per call
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
@@ -155,146 +155,215 @@ npm run test:examples
|
|
|
155
155
|
### Node.js (with Keypair)
|
|
156
156
|
|
|
157
157
|
```typescript
|
|
158
|
-
import {
|
|
158
|
+
import {
|
|
159
|
+
CLOAK_PROGRAM_ID,
|
|
160
|
+
NATIVE_SOL_MINT,
|
|
161
|
+
createUtxo,
|
|
162
|
+
createZeroUtxo,
|
|
163
|
+
fullWithdraw,
|
|
164
|
+
generateUtxoKeypair,
|
|
165
|
+
transact,
|
|
166
|
+
} from "@cloak.dev/sdk";
|
|
159
167
|
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
|
|
160
168
|
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
const keypair = Keypair.fromSecretKey(/* your secret key */);
|
|
169
|
+
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
170
|
+
const signer = Keypair.fromSecretKey(/* your secret key */);
|
|
164
171
|
|
|
165
|
-
//
|
|
166
|
-
const
|
|
167
|
-
keypairBytes: keypair.secretKey,
|
|
168
|
-
network: "devnet",
|
|
169
|
-
});
|
|
172
|
+
// Generate a UTXO keypair (owner of the shielded balance)
|
|
173
|
+
const owner = await generateUtxoKeypair();
|
|
170
174
|
|
|
171
|
-
//
|
|
172
|
-
const
|
|
173
|
-
|
|
175
|
+
// Build a 0.01 SOL output UTXO and shield it
|
|
176
|
+
const amountLamports = 10_000_000n;
|
|
177
|
+
const output = await createUtxo(amountLamports, owner, NATIVE_SOL_MINT);
|
|
174
178
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
const deposited = await transact(
|
|
180
|
+
{
|
|
181
|
+
inputUtxos: [await createZeroUtxo(NATIVE_SOL_MINT), await createZeroUtxo(NATIVE_SOL_MINT)],
|
|
182
|
+
outputUtxos: [output, await createZeroUtxo(NATIVE_SOL_MINT)],
|
|
183
|
+
externalAmount: amountLamports, // positive = deposit
|
|
184
|
+
depositor: signer.publicKey,
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
connection,
|
|
188
|
+
programId: CLOAK_PROGRAM_ID,
|
|
189
|
+
relayUrl: "https://api.cloak.ag",
|
|
190
|
+
depositorKeypair: signer,
|
|
191
|
+
},
|
|
181
192
|
);
|
|
182
|
-
console.log("
|
|
193
|
+
console.log("Deposit signature:", deposited.signature);
|
|
194
|
+
|
|
195
|
+
// Reclaim the full balance to a public address
|
|
196
|
+
const recipient = new PublicKey("RECIPIENT_ADDRESS");
|
|
197
|
+
const withdrawn = await fullWithdraw(deposited.outputUtxos, recipient, {
|
|
198
|
+
connection,
|
|
199
|
+
programId: CLOAK_PROGRAM_ID,
|
|
200
|
+
relayUrl: "https://api.cloak.ag",
|
|
201
|
+
depositorKeypair: signer,
|
|
202
|
+
cachedMerkleTree: deposited.merkleTree,
|
|
203
|
+
});
|
|
204
|
+
console.log("Withdraw signature:", withdrawn.signature);
|
|
183
205
|
```
|
|
184
206
|
|
|
185
207
|
### React/Next.js (with Wallet Adapter)
|
|
186
208
|
|
|
187
209
|
```typescript
|
|
188
|
-
import {
|
|
189
|
-
|
|
190
|
-
|
|
210
|
+
import {
|
|
211
|
+
CLOAK_PROGRAM_ID,
|
|
212
|
+
NATIVE_SOL_MINT,
|
|
213
|
+
createUtxo,
|
|
214
|
+
createZeroUtxo,
|
|
215
|
+
generateUtxoKeypair,
|
|
216
|
+
transact,
|
|
217
|
+
} from "@cloak.dev/sdk";
|
|
218
|
+
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
|
|
219
|
+
import { useCallback } from "react";
|
|
191
220
|
|
|
192
|
-
function
|
|
193
|
-
const { publicKey, signTransaction, sendTransaction } = useWallet();
|
|
221
|
+
export function DepositButton() {
|
|
194
222
|
const { connection } = useConnection();
|
|
223
|
+
const wallet = useWallet();
|
|
195
224
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (!publicKey) return null;
|
|
199
|
-
return new CloakSDK({
|
|
200
|
-
network: "devnet",
|
|
201
|
-
wallet: {
|
|
202
|
-
publicKey,
|
|
203
|
-
signTransaction: (tx) => signTransaction!(tx),
|
|
204
|
-
sendTransaction: (tx, conn, opts) => sendTransaction(tx, conn, opts),
|
|
205
|
-
},
|
|
206
|
-
});
|
|
207
|
-
}, [publicKey, signTransaction, sendTransaction]);
|
|
225
|
+
const onClick = useCallback(async () => {
|
|
226
|
+
if (!wallet.publicKey || !wallet.signTransaction) return;
|
|
208
227
|
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
};
|
|
228
|
+
const owner = await generateUtxoKeypair();
|
|
229
|
+
const amount = 10_000_000n; // 0.01 SOL — SDK-enforced minimum
|
|
230
|
+
const output = await createUtxo(amount, owner, NATIVE_SOL_MINT);
|
|
213
231
|
|
|
214
|
-
|
|
232
|
+
const result = await transact(
|
|
233
|
+
{
|
|
234
|
+
inputUtxos: [await createZeroUtxo(NATIVE_SOL_MINT), await createZeroUtxo(NATIVE_SOL_MINT)],
|
|
235
|
+
outputUtxos: [output, await createZeroUtxo(NATIVE_SOL_MINT)],
|
|
236
|
+
externalAmount: amount,
|
|
237
|
+
depositor: wallet.publicKey,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
connection,
|
|
241
|
+
programId: CLOAK_PROGRAM_ID,
|
|
242
|
+
relayUrl: "https://api.cloak.ag",
|
|
243
|
+
wallet: {
|
|
244
|
+
publicKey: wallet.publicKey,
|
|
245
|
+
signTransaction: wallet.signTransaction,
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
console.log("Deposited:", result.signature);
|
|
250
|
+
}, [connection, wallet]);
|
|
251
|
+
|
|
252
|
+
return <button onClick={onClick}>Deposit 0.01 SOL</button>;
|
|
215
253
|
}
|
|
216
254
|
```
|
|
217
255
|
|
|
218
|
-
|
|
256
|
+
The `CloakSDK` class is kept as a thin config + read-only chain helper
|
|
257
|
+
(`getPublicKey`, `getCurrentRoot`, `getMerkleProof`, `getTransactionStatus`,
|
|
258
|
+
`importWalletKeys`, `exportWalletKeys`, `getConfig`). All transaction-emitting
|
|
259
|
+
calls use the standalone helpers above.
|
|
219
260
|
|
|
220
|
-
|
|
261
|
+
## Core Functions
|
|
221
262
|
|
|
222
|
-
Deposit
|
|
263
|
+
### Deposit (`transact` with positive `externalAmount`)
|
|
223
264
|
|
|
224
265
|
```typescript
|
|
225
|
-
const result = await
|
|
226
|
-
|
|
227
|
-
|
|
266
|
+
const result = await transact(
|
|
267
|
+
{
|
|
268
|
+
inputUtxos: [await createZeroUtxo(), await createZeroUtxo()],
|
|
269
|
+
outputUtxos: [outputUtxo, await createZeroUtxo()],
|
|
270
|
+
externalAmount: 10_000_000n, // positive = deposit
|
|
271
|
+
depositor: signer.publicKey,
|
|
272
|
+
},
|
|
273
|
+
{ connection, programId: CLOAK_PROGRAM_ID, relayUrl: "https://api.cloak.ag", depositorKeypair: signer },
|
|
274
|
+
);
|
|
275
|
+
console.log("Leaf index:", result.commitmentIndices[0]);
|
|
276
|
+
console.log("Output UTXO:", result.outputUtxos[0]);
|
|
228
277
|
```
|
|
229
278
|
|
|
230
279
|
### Withdraw
|
|
231
280
|
|
|
232
|
-
Withdraw to a single recipient:
|
|
233
|
-
|
|
234
281
|
```typescript
|
|
235
|
-
|
|
282
|
+
import { fullWithdraw, partialWithdraw } from "@cloak.dev/sdk";
|
|
283
|
+
|
|
284
|
+
// Reclaim everything in one or more input UTXOs to a public address.
|
|
285
|
+
const result = await fullWithdraw(myUtxos, recipientPublicKey, {
|
|
236
286
|
connection,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
);
|
|
241
|
-
```
|
|
287
|
+
programId: CLOAK_PROGRAM_ID,
|
|
288
|
+
relayUrl: "https://api.cloak.ag",
|
|
289
|
+
depositorKeypair: signer,
|
|
290
|
+
});
|
|
242
291
|
|
|
243
|
-
|
|
292
|
+
// Or withdraw a portion and keep the change shielded.
|
|
293
|
+
const partial = await partialWithdraw(myUtxos, recipientPublicKey, 5_000_000n, {
|
|
294
|
+
connection,
|
|
295
|
+
programId: CLOAK_PROGRAM_ID,
|
|
296
|
+
relayUrl: "https://api.cloak.ag",
|
|
297
|
+
depositorKeypair: signer,
|
|
298
|
+
});
|
|
299
|
+
```
|
|
244
300
|
|
|
245
|
-
|
|
301
|
+
### Shield-to-shield Transfer
|
|
246
302
|
|
|
247
303
|
```typescript
|
|
248
|
-
|
|
249
|
-
{ recipient: addr1, amount: 50_000_000 },
|
|
250
|
-
{ recipient: addr2, amount: 47_000_000 },
|
|
251
|
-
]);
|
|
252
|
-
```
|
|
304
|
+
import { transfer } from "@cloak.dev/sdk";
|
|
253
305
|
|
|
254
|
-
|
|
306
|
+
const result = await transfer(myUtxos, recipientUtxoPublicKey, amountLamports, {
|
|
307
|
+
connection,
|
|
308
|
+
programId: CLOAK_PROGRAM_ID,
|
|
309
|
+
relayUrl: "https://api.cloak.ag",
|
|
310
|
+
depositorKeypair: signer,
|
|
311
|
+
});
|
|
312
|
+
```
|
|
255
313
|
|
|
256
|
-
Swap SOL
|
|
314
|
+
### Swap (SOL → SPL token)
|
|
257
315
|
|
|
258
316
|
```typescript
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
317
|
+
import { swapUtxo } from "@cloak.dev/sdk";
|
|
318
|
+
|
|
319
|
+
const result = await swapUtxo(
|
|
320
|
+
{
|
|
321
|
+
inputUtxos: myUtxos,
|
|
322
|
+
swapAmount: 10_000_000n,
|
|
323
|
+
outputMint: new PublicKey("TOKEN_MINT_ADDRESS"),
|
|
324
|
+
recipientAta: recipientTokenAccount,
|
|
325
|
+
minOutputAmount: 1_000_000n,
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
connection,
|
|
329
|
+
programId: CLOAK_PROGRAM_ID,
|
|
330
|
+
relayUrl: "https://api.cloak.ag",
|
|
331
|
+
depositorKeypair: signer,
|
|
332
|
+
},
|
|
333
|
+
);
|
|
263
334
|
```
|
|
264
335
|
|
|
265
336
|
## Fee Structure
|
|
266
337
|
|
|
267
|
-
- **Fixed Fee**: 0.005 SOL (5,000,000 lamports)
|
|
268
|
-
- **Variable Fee**: 0.3% of
|
|
338
|
+
- **Fixed Fee**: 0.005 SOL (5,000,000 lamports) on withdraw
|
|
339
|
+
- **Variable Fee**: 0.3% of withdrawn amount
|
|
269
340
|
|
|
270
|
-
Use `getDistributableAmount()` to calculate the amount after fees:
|
|
341
|
+
Use `getDistributableAmount()` to calculate the amount a recipient receives after fees:
|
|
271
342
|
|
|
272
343
|
```typescript
|
|
273
344
|
import { getDistributableAmount } from "@cloak.dev/sdk";
|
|
274
345
|
|
|
275
|
-
const
|
|
276
|
-
const
|
|
346
|
+
const withdrawing = 100_000_000; // 0.1 SOL
|
|
347
|
+
const recipientReceives = getDistributableAmount(withdrawing); // ~94,700,000 lamports
|
|
277
348
|
```
|
|
278
349
|
|
|
279
|
-
##
|
|
350
|
+
## UTXOs
|
|
280
351
|
|
|
281
|
-
A **
|
|
352
|
+
A **UTXO** is a cryptographic commitment over `(amount, ownerPubkey, blinding, mintAddress)`. Each `transact` call consumes up to 2 input UTXOs and produces up to 2 outputs:
|
|
282
353
|
|
|
283
354
|
```typescript
|
|
284
|
-
interface
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
leafIndex?: number; // Set after deposit
|
|
293
|
-
depositSignature?: string;
|
|
355
|
+
interface Utxo {
|
|
356
|
+
amount: bigint; // lamports for SOL, token units for SPL
|
|
357
|
+
keypair: UtxoKeypair; // { privateKey, publicKey } — bigints
|
|
358
|
+
blinding: bigint; // randomness
|
|
359
|
+
mintAddress: PublicKey; // NATIVE_SOL_MINT for SOL
|
|
360
|
+
index?: number; // leaf index in the Merkle tree (set after the UTXO is in-tree)
|
|
361
|
+
commitment?: bigint;
|
|
362
|
+
nullifier?: bigint;
|
|
294
363
|
}
|
|
295
364
|
```
|
|
296
365
|
|
|
297
|
-
**⚠️ Important**:
|
|
366
|
+
**⚠️ Important**: persist the spend `privateKey`, `blinding`, `amount`, `index`, and `mintAddress` for every output UTXO — those are the only inputs you need to spend it later. The SDK ships `LocalStorageAdapter` for browser wallet keys; UTXO persistence is the consumer's responsibility (see `lib/utxo-manager.ts` patterns in cloak-ag/web for a reference implementation).
|
|
298
367
|
|
|
299
368
|
## Compliance Chain Scanning
|
|
300
369
|
|
|
@@ -334,13 +403,31 @@ sequenceDiagram
|
|
|
334
403
|
## Error Handling
|
|
335
404
|
|
|
336
405
|
```typescript
|
|
337
|
-
import {
|
|
406
|
+
import {
|
|
407
|
+
CloakError,
|
|
408
|
+
UtxoAlreadySpentError,
|
|
409
|
+
RootNotFoundError,
|
|
410
|
+
classifyRelayError,
|
|
411
|
+
fullWithdraw,
|
|
412
|
+
CLOAK_PROGRAM_ID,
|
|
413
|
+
} from "@cloak.dev/sdk";
|
|
338
414
|
|
|
339
415
|
try {
|
|
340
|
-
await
|
|
416
|
+
await fullWithdraw(myUtxos, recipient, {
|
|
417
|
+
connection,
|
|
418
|
+
programId: CLOAK_PROGRAM_ID,
|
|
419
|
+
relayUrl: "https://api.cloak.ag",
|
|
420
|
+
depositorKeypair: signer,
|
|
421
|
+
});
|
|
341
422
|
} catch (error) {
|
|
342
|
-
if (error instanceof
|
|
343
|
-
|
|
423
|
+
if (error instanceof UtxoAlreadySpentError) {
|
|
424
|
+
// Drop the spent UTXO from local storage; the structured error
|
|
425
|
+
// carries which nullifiers collided.
|
|
426
|
+
console.log("Spent nullifiers:", error.nullifiers);
|
|
427
|
+
} else if (error instanceof RootNotFoundError) {
|
|
428
|
+
// Relay/chain root mismatch; retry against a fresh tree.
|
|
429
|
+
} else if (error instanceof CloakError) {
|
|
430
|
+
console.log("Category:", error.category); // 'wallet' | 'network' | 'prover' | 'relay' | 'validation' | 'environment' | 'service' | 'indexer'
|
|
344
431
|
console.log("Retryable:", error.retryable);
|
|
345
432
|
}
|
|
346
433
|
}
|