@cloak.dev/sdk 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @cloak.ag/sdk
1
+ # @cloak.dev/sdk
2
2
 
3
3
  TypeScript SDK for the Cloak Protocol - Private transactions on Solana using zero-knowledge proofs.
4
4
 
@@ -14,11 +14,11 @@ TypeScript SDK for the Cloak Protocol - Private transactions on Solana using zer
14
14
  ## Installation
15
15
 
16
16
  ```bash
17
- npm install @cloak.ag/sdk @solana/web3.js
17
+ npm install @cloak.dev/sdk @solana/web3.js
18
18
  # or
19
- yarn add @cloak.ag/sdk @solana/web3.js
19
+ yarn add @cloak.dev/sdk @solana/web3.js
20
20
  # or
21
- pnpm add @cloak.ag/sdk @solana/web3.js
21
+ pnpm add @cloak.dev/sdk @solana/web3.js
22
22
  ```
23
23
 
24
24
  **Note**: For swap functionality, you'll also need `@solana/spl-token`:
@@ -28,12 +28,101 @@ npm install @solana/spl-token
28
28
 
29
29
  ## Quick Start
30
30
 
31
- ### Risk Quote Configuration (Recommended)
31
+ ### SDK Defaults (Recommended)
32
32
 
33
- - Default production flow: set `CLOAK_RELAY_URL` and let relay handle Range quoting.
34
- - Keep `RANGE_API_KEY` only in relay/service backend, not in SDK clients.
35
- - SDK auto-uses `${CLOAK_RELAY_URL}/range-quote` when `relayUrl` is provided.
36
- - SDK-side `rangeApiKey` exists only as a server-side fallback (no-relay scenarios).
33
+ - Standard integrations should use SDK defaults for program, relay, and circuits.
34
+ - Do not expose protocol-level config (`programId`, relay URL, circuits URL) as end-user input.
35
+ - `transact`, `partialWithdraw`, and `fullWithdraw` already include stale-root retry handling.
36
+ - For simple CLI sends, require only `SOLANA_RPC_URL` and `KEYPAIR_PATH`.
37
+
38
+ ### Minimal Private SOL Send (Single File, Keypair)
39
+
40
+ Use this contract for one-shot scripts and AI-generated snippets.
41
+
42
+ ```ts
43
+ import { readFileSync } from "fs";
44
+ import {
45
+ CLOAK_PROGRAM_ID,
46
+ NATIVE_SOL_MINT,
47
+ createUtxo,
48
+ createZeroUtxo,
49
+ fullWithdraw,
50
+ generateUtxoKeypair,
51
+ transact,
52
+ } from "@cloak.dev/sdk";
53
+ import { Connection, Keypair, PublicKey } from "@solana/web3.js";
54
+
55
+ async function main() {
56
+ const [recipientArg, lamportsArg] = process.argv.slice(2);
57
+ if (!recipientArg || !lamportsArg) {
58
+ throw new Error("Usage: npx tsx send-sol-private.ts <recipientPubkey> <lamports>");
59
+ }
60
+
61
+ const rpcUrl = process.env.SOLANA_RPC_URL;
62
+ const keypairPath = process.env.KEYPAIR_PATH;
63
+ if (!rpcUrl || !keypairPath) {
64
+ throw new Error("Set SOLANA_RPC_URL and KEYPAIR_PATH");
65
+ }
66
+
67
+ const connection = new Connection(rpcUrl, "confirmed");
68
+ const signer = Keypair.fromSecretKey(
69
+ Uint8Array.from(JSON.parse(readFileSync(keypairPath, "utf8"))),
70
+ );
71
+
72
+ const recipient = new PublicKey(recipientArg);
73
+ const amountLamports = BigInt(lamportsArg);
74
+
75
+ const owner = await generateUtxoKeypair();
76
+ const output = await createUtxo(amountLamports, owner, NATIVE_SOL_MINT);
77
+
78
+ const deposited = await transact(
79
+ {
80
+ inputUtxos: [await createZeroUtxo(NATIVE_SOL_MINT)],
81
+ outputUtxos: [output],
82
+ externalAmount: amountLamports,
83
+ depositor: signer.publicKey,
84
+ },
85
+ {
86
+ connection,
87
+ programId: CLOAK_PROGRAM_ID,
88
+ depositorKeypair: signer,
89
+ walletPublicKey: signer.publicKey,
90
+ enforceViewingKeyRegistration: false,
91
+ },
92
+ );
93
+
94
+ const withdrawn = await fullWithdraw(deposited.outputUtxos, recipient, {
95
+ connection,
96
+ programId: CLOAK_PROGRAM_ID,
97
+ depositorKeypair: signer,
98
+ walletPublicKey: signer.publicKey,
99
+ cachedMerkleTree: deposited.merkleTree,
100
+ enforceViewingKeyRegistration: false,
101
+ });
102
+
103
+ console.log(withdrawn.signature);
104
+ }
105
+
106
+ main().catch((e) => {
107
+ console.error(e instanceof Error ? e.message : String(e));
108
+ process.exit(1);
109
+ });
110
+ ```
111
+
112
+ Run:
113
+
114
+ ```bash
115
+ SOLANA_RPC_URL="https://api.mainnet-beta.solana.com" \
116
+ KEYPAIR_PATH="/absolute/path/to/id.json" \
117
+ npx tsx send-sol-private.ts <recipientPubkey> <lamports>
118
+ ```
119
+
120
+ Hard rules for minimal scripts:
121
+
122
+ - Use lamports from CLI (`<lamports>`) and keep transaction math in `bigint`.
123
+ - Use `KEYPAIR_PATH`; do not ask for raw private key env vars.
124
+ - Do not parse SOL decimals with float math (`parseFloat`, `AMOUNT_SOL`).
125
+ - Keep `programId` fixed to `CLOAK_PROGRAM_ID` (no end-user override).
37
126
 
38
127
  ### Maintained Examples
39
128
 
@@ -66,7 +155,7 @@ npm run test:examples
66
155
  ### Node.js (with Keypair)
67
156
 
68
157
  ```typescript
69
- import { CloakSDK } from "@cloak.ag/sdk";
158
+ import { CloakSDK } from "@cloak.dev/sdk";
70
159
  import { Connection, Keypair, PublicKey } from "@solana/web3.js";
71
160
 
72
161
  // Initialize connection and keypair
@@ -96,7 +185,7 @@ console.log("Withdrawn! TX:", withdrawResult.signature);
96
185
  ### React/Next.js (with Wallet Adapter)
97
186
 
98
187
  ```typescript
99
- import { CloakSDK } from "@cloak.ag/sdk";
188
+ import { CloakSDK } from "@cloak.dev/sdk";
100
189
  import { useWallet, useConnection } from "@solana/wallet-adapter-react";
101
190
  import { PublicKey } from "@solana/web3.js";
102
191
 
@@ -181,7 +270,7 @@ const result = await sdk.swap(connection, note, recipientPublicKey, {
181
270
  Use `getDistributableAmount()` to calculate the amount after fees:
182
271
 
183
272
  ```typescript
184
- import { getDistributableAmount } from "@cloak.ag/sdk";
273
+ import { getDistributableAmount } from "@cloak.dev/sdk";
185
274
 
186
275
  const deposited = 100_000_000; // 0.1 SOL
187
276
  const afterFees = getDistributableAmount(deposited); // ~94,700,000 lamports
@@ -245,7 +334,7 @@ sequenceDiagram
245
334
  ## Error Handling
246
335
 
247
336
  ```typescript
248
- import { CloakError } from "@cloak.ag/sdk";
337
+ import { CloakError } from "@cloak.dev/sdk";
249
338
 
250
339
  try {
251
340
  await sdk.withdraw(connection, note, recipient);
package/dist/index.cjs CHANGED
@@ -6083,6 +6083,7 @@ function collectLookupCandidatesFromInstructions(instructions, payer, existingAl
6083
6083
  }
6084
6084
  async function createEphemeralALT(connection, depositor, onProgress, additionalAddresses = []) {
6085
6085
  const MAX_ALT_ADDRESSES = 20;
6086
+ const MAX_ALT_CREATE_RETRIES = 3;
6086
6087
  const altAddresses = dedupePubkeys([
6087
6088
  ...getCommonALTAddresses(),
6088
6089
  ...additionalAddresses
@@ -6093,46 +6094,59 @@ async function createEphemeralALT(connection, depositor, onProgress, additionalA
6093
6094
  `ALT address set truncated (${addressesForCreate.length}/${altAddresses.length}); packet savings may be limited.`
6094
6095
  );
6095
6096
  }
6096
- const slot = await connection.getSlot();
6097
- const [createIx, altAddress] = import_web37.AddressLookupTableProgram.createLookupTable({
6098
- authority: depositor.publicKey,
6099
- payer: depositor.publicKey,
6100
- recentSlot: slot
6101
- });
6102
- const extendIx = import_web37.AddressLookupTableProgram.extendLookupTable({
6103
- payer: depositor.publicKey,
6104
- authority: depositor.publicKey,
6105
- lookupTable: altAddress,
6106
- addresses: addressesForCreate
6107
- });
6108
- const tx = new import_web37.Transaction().add(createIx).add(extendIx);
6109
- const { blockhash } = await connection.getLatestBlockhash();
6110
- tx.recentBlockhash = blockhash;
6111
- tx.feePayer = depositor.publicKey;
6112
- if (depositor.keypair) {
6113
- await (0, import_web37.sendAndConfirmTransaction)(connection, tx, [depositor.keypair], { commitment: "confirmed" });
6114
- } else if (depositor.signTransaction) {
6115
- const signedTx = await depositor.signTransaction(tx);
6116
- const sig = await connection.sendRawTransaction(signedTx.serialize());
6117
- const latestBH = await connection.getLatestBlockhash();
6118
- await connection.confirmTransaction({ signature: sig, blockhash: latestBH.blockhash, lastValidBlockHeight: latestBH.lastValidBlockHeight }, "confirmed");
6119
- } else {
6120
- throw new Error("Cannot create ALT: no signing method provided");
6121
- }
6122
- for (let i = 0; i < 30; i++) {
6123
- await new Promise((r) => setTimeout(r, 500));
6124
- const result = await connection.getAddressLookupTable(altAddress);
6125
- if (result.value && result.value.isActive()) {
6126
- onProgress?.(`ALT created and active: ${altAddress.toBase58()}`);
6127
- return [result.value];
6097
+ for (let altAttempt = 0; altAttempt < MAX_ALT_CREATE_RETRIES; altAttempt++) {
6098
+ try {
6099
+ const slot = await connection.getSlot("finalized");
6100
+ const [createIx, altAddress] = import_web37.AddressLookupTableProgram.createLookupTable({
6101
+ authority: depositor.publicKey,
6102
+ payer: depositor.publicKey,
6103
+ recentSlot: slot
6104
+ });
6105
+ const extendIx = import_web37.AddressLookupTableProgram.extendLookupTable({
6106
+ payer: depositor.publicKey,
6107
+ authority: depositor.publicKey,
6108
+ lookupTable: altAddress,
6109
+ addresses: addressesForCreate
6110
+ });
6111
+ const tx = new import_web37.Transaction().add(createIx).add(extendIx);
6112
+ const { blockhash } = await connection.getLatestBlockhash();
6113
+ tx.recentBlockhash = blockhash;
6114
+ tx.feePayer = depositor.publicKey;
6115
+ if (depositor.keypair) {
6116
+ await (0, import_web37.sendAndConfirmTransaction)(connection, tx, [depositor.keypair], { commitment: "confirmed" });
6117
+ } else if (depositor.signTransaction) {
6118
+ const signedTx = await depositor.signTransaction(tx);
6119
+ const sig = await connection.sendRawTransaction(signedTx.serialize());
6120
+ const latestBH = await connection.getLatestBlockhash();
6121
+ await connection.confirmTransaction({ signature: sig, blockhash: latestBH.blockhash, lastValidBlockHeight: latestBH.lastValidBlockHeight }, "confirmed");
6122
+ } else {
6123
+ throw new Error("Cannot create ALT: no signing method provided");
6124
+ }
6125
+ for (let i = 0; i < 30; i++) {
6126
+ await new Promise((r) => setTimeout(r, 500));
6127
+ const result = await connection.getAddressLookupTable(altAddress);
6128
+ if (result.value && result.value.isActive()) {
6129
+ onProgress?.(`ALT created and active: ${altAddress.toBase58()}`);
6130
+ return [result.value];
6131
+ }
6132
+ }
6133
+ const finalResult = await connection.getAddressLookupTable(altAddress);
6134
+ if (finalResult.value) {
6135
+ onProgress?.("ALT created (activation pending)");
6136
+ return [finalResult.value];
6137
+ }
6138
+ throw new Error("Failed to create ALT: not active after 15 seconds");
6139
+ } catch (err) {
6140
+ const msg = err?.message ?? String(err);
6141
+ if (msg.includes("not a recent slot") && altAttempt < MAX_ALT_CREATE_RETRIES - 1) {
6142
+ onProgress?.(`ALT creation failed (stale slot), retrying (${altAttempt + 2}/${MAX_ALT_CREATE_RETRIES})...`);
6143
+ await new Promise((r) => setTimeout(r, 2e3));
6144
+ continue;
6145
+ }
6146
+ throw err;
6128
6147
  }
6129
6148
  }
6130
- const finalResult = await connection.getAddressLookupTable(altAddress);
6131
- if (finalResult.value) {
6132
- onProgress?.("ALT created (activation pending)");
6133
- return [finalResult.value];
6134
- }
6135
- throw new Error("Failed to create ALT: not active after 15 seconds");
6149
+ throw new Error(`Failed to create ALT after ${MAX_ALT_CREATE_RETRIES} retries`);
6136
6150
  }
6137
6151
  async function submitTransactionDirect(connection, programId, depositor, proofBytes, publicInputsBytes, nullifiers, mint, recipient, riskOracleQueue, riskQuoteUrl, getRiskQuoteInstruction, onProgress, addressLookupTableAccounts, rangeApiKey, encryptedNoteBytes, relayUrl, altAddresses) {
6138
6152
  onProgress?.("Building transaction...");
@@ -6163,6 +6177,10 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
6163
6177
  onProgress?.("Creating address lookup table (chain notes require v0 tx to fit)...");
6164
6178
  addressLookupTableAccounts = await createEphemeralALT(connection, depositor, onProgress);
6165
6179
  }
6180
+ if (isDeposit && isSplPool && (!addressLookupTableAccounts || addressLookupTableAccounts.length === 0)) {
6181
+ onProgress?.("Creating address lookup table (SPL deposit requires v0 tx)...");
6182
+ addressLookupTableAccounts = await createEphemeralALT(connection, depositor, onProgress);
6183
+ }
6166
6184
  const publicAmountBytes = publicInputsBytes.slice(32, 40);
6167
6185
  const publicAmountBuffer = Buffer.from(publicAmountBytes);
6168
6186
  const publicAmount = publicAmountBuffer.readBigInt64LE(0);
package/dist/index.js CHANGED
@@ -5636,6 +5636,7 @@ function collectLookupCandidatesFromInstructions(instructions, payer, existingAl
5636
5636
  }
5637
5637
  async function createEphemeralALT(connection, depositor, onProgress, additionalAddresses = []) {
5638
5638
  const MAX_ALT_ADDRESSES = 20;
5639
+ const MAX_ALT_CREATE_RETRIES = 3;
5639
5640
  const altAddresses = dedupePubkeys([
5640
5641
  ...getCommonALTAddresses(),
5641
5642
  ...additionalAddresses
@@ -5646,46 +5647,59 @@ async function createEphemeralALT(connection, depositor, onProgress, additionalA
5646
5647
  `ALT address set truncated (${addressesForCreate.length}/${altAddresses.length}); packet savings may be limited.`
5647
5648
  );
5648
5649
  }
5649
- const slot = await connection.getSlot();
5650
- const [createIx, altAddress] = AddressLookupTableProgram.createLookupTable({
5651
- authority: depositor.publicKey,
5652
- payer: depositor.publicKey,
5653
- recentSlot: slot
5654
- });
5655
- const extendIx = AddressLookupTableProgram.extendLookupTable({
5656
- payer: depositor.publicKey,
5657
- authority: depositor.publicKey,
5658
- lookupTable: altAddress,
5659
- addresses: addressesForCreate
5660
- });
5661
- const tx = new Transaction3().add(createIx).add(extendIx);
5662
- const { blockhash } = await connection.getLatestBlockhash();
5663
- tx.recentBlockhash = blockhash;
5664
- tx.feePayer = depositor.publicKey;
5665
- if (depositor.keypair) {
5666
- await sendAndConfirmTransaction(connection, tx, [depositor.keypair], { commitment: "confirmed" });
5667
- } else if (depositor.signTransaction) {
5668
- const signedTx = await depositor.signTransaction(tx);
5669
- const sig = await connection.sendRawTransaction(signedTx.serialize());
5670
- const latestBH = await connection.getLatestBlockhash();
5671
- await connection.confirmTransaction({ signature: sig, blockhash: latestBH.blockhash, lastValidBlockHeight: latestBH.lastValidBlockHeight }, "confirmed");
5672
- } else {
5673
- throw new Error("Cannot create ALT: no signing method provided");
5674
- }
5675
- for (let i = 0; i < 30; i++) {
5676
- await new Promise((r) => setTimeout(r, 500));
5677
- const result = await connection.getAddressLookupTable(altAddress);
5678
- if (result.value && result.value.isActive()) {
5679
- onProgress?.(`ALT created and active: ${altAddress.toBase58()}`);
5680
- return [result.value];
5650
+ for (let altAttempt = 0; altAttempt < MAX_ALT_CREATE_RETRIES; altAttempt++) {
5651
+ try {
5652
+ const slot = await connection.getSlot("finalized");
5653
+ const [createIx, altAddress] = AddressLookupTableProgram.createLookupTable({
5654
+ authority: depositor.publicKey,
5655
+ payer: depositor.publicKey,
5656
+ recentSlot: slot
5657
+ });
5658
+ const extendIx = AddressLookupTableProgram.extendLookupTable({
5659
+ payer: depositor.publicKey,
5660
+ authority: depositor.publicKey,
5661
+ lookupTable: altAddress,
5662
+ addresses: addressesForCreate
5663
+ });
5664
+ const tx = new Transaction3().add(createIx).add(extendIx);
5665
+ const { blockhash } = await connection.getLatestBlockhash();
5666
+ tx.recentBlockhash = blockhash;
5667
+ tx.feePayer = depositor.publicKey;
5668
+ if (depositor.keypair) {
5669
+ await sendAndConfirmTransaction(connection, tx, [depositor.keypair], { commitment: "confirmed" });
5670
+ } else if (depositor.signTransaction) {
5671
+ const signedTx = await depositor.signTransaction(tx);
5672
+ const sig = await connection.sendRawTransaction(signedTx.serialize());
5673
+ const latestBH = await connection.getLatestBlockhash();
5674
+ await connection.confirmTransaction({ signature: sig, blockhash: latestBH.blockhash, lastValidBlockHeight: latestBH.lastValidBlockHeight }, "confirmed");
5675
+ } else {
5676
+ throw new Error("Cannot create ALT: no signing method provided");
5677
+ }
5678
+ for (let i = 0; i < 30; i++) {
5679
+ await new Promise((r) => setTimeout(r, 500));
5680
+ const result = await connection.getAddressLookupTable(altAddress);
5681
+ if (result.value && result.value.isActive()) {
5682
+ onProgress?.(`ALT created and active: ${altAddress.toBase58()}`);
5683
+ return [result.value];
5684
+ }
5685
+ }
5686
+ const finalResult = await connection.getAddressLookupTable(altAddress);
5687
+ if (finalResult.value) {
5688
+ onProgress?.("ALT created (activation pending)");
5689
+ return [finalResult.value];
5690
+ }
5691
+ throw new Error("Failed to create ALT: not active after 15 seconds");
5692
+ } catch (err) {
5693
+ const msg = err?.message ?? String(err);
5694
+ if (msg.includes("not a recent slot") && altAttempt < MAX_ALT_CREATE_RETRIES - 1) {
5695
+ onProgress?.(`ALT creation failed (stale slot), retrying (${altAttempt + 2}/${MAX_ALT_CREATE_RETRIES})...`);
5696
+ await new Promise((r) => setTimeout(r, 2e3));
5697
+ continue;
5698
+ }
5699
+ throw err;
5681
5700
  }
5682
5701
  }
5683
- const finalResult = await connection.getAddressLookupTable(altAddress);
5684
- if (finalResult.value) {
5685
- onProgress?.("ALT created (activation pending)");
5686
- return [finalResult.value];
5687
- }
5688
- throw new Error("Failed to create ALT: not active after 15 seconds");
5702
+ throw new Error(`Failed to create ALT after ${MAX_ALT_CREATE_RETRIES} retries`);
5689
5703
  }
5690
5704
  async function submitTransactionDirect(connection, programId, depositor, proofBytes, publicInputsBytes, nullifiers, mint, recipient, riskOracleQueue, riskQuoteUrl, getRiskQuoteInstruction, onProgress, addressLookupTableAccounts, rangeApiKey, encryptedNoteBytes, relayUrl, altAddresses) {
5691
5705
  onProgress?.("Building transaction...");
@@ -5716,6 +5730,10 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
5716
5730
  onProgress?.("Creating address lookup table (chain notes require v0 tx to fit)...");
5717
5731
  addressLookupTableAccounts = await createEphemeralALT(connection, depositor, onProgress);
5718
5732
  }
5733
+ if (isDeposit && isSplPool && (!addressLookupTableAccounts || addressLookupTableAccounts.length === 0)) {
5734
+ onProgress?.("Creating address lookup table (SPL deposit requires v0 tx)...");
5735
+ addressLookupTableAccounts = await createEphemeralALT(connection, depositor, onProgress);
5736
+ }
5719
5737
  const publicAmountBytes = publicInputsBytes.slice(32, 40);
5720
5738
  const publicAmountBuffer = Buffer.from(publicAmountBytes);
5721
5739
  const publicAmount = publicAmountBuffer.readBigInt64LE(0);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloak.dev/sdk",
3
- "description": "TypeScript SDK for Cloak",
4
- "version": "0.1.1",
3
+ "description": "Shield, send, and swap on Solana privately — TypeScript SDK with UTXO-based zero-knowledge transactions",
4
+ "version": "0.1.3",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.js",