@cloak.ag/sdk 1.0.5 → 1.0.6
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/dist/index.cjs +109 -42
- package/dist/index.d.cts +38 -2
- package/dist/index.d.ts +38 -2
- package/dist/index.js +112 -43
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -307,14 +307,14 @@ function randomBytes(length) {
|
|
|
307
307
|
} catch {
|
|
308
308
|
}
|
|
309
309
|
try {
|
|
310
|
-
const
|
|
311
|
-
if (
|
|
312
|
-
const buffer =
|
|
310
|
+
const nodeCrypto = require("crypto");
|
|
311
|
+
if (nodeCrypto?.randomBytes) {
|
|
312
|
+
const buffer = nodeCrypto.randomBytes(length);
|
|
313
313
|
bytes.set(buffer);
|
|
314
314
|
return bytes;
|
|
315
315
|
}
|
|
316
|
-
if (
|
|
317
|
-
|
|
316
|
+
if (nodeCrypto?.webcrypto?.getRandomValues) {
|
|
317
|
+
nodeCrypto.webcrypto.getRandomValues(bytes);
|
|
318
318
|
return bytes;
|
|
319
319
|
}
|
|
320
320
|
} catch {
|
|
@@ -436,8 +436,8 @@ function generateMasterSeed() {
|
|
|
436
436
|
cryptoObj.getRandomValues(seed);
|
|
437
437
|
} else {
|
|
438
438
|
try {
|
|
439
|
-
const
|
|
440
|
-
const buffer =
|
|
439
|
+
const nodeCrypto = require("crypto");
|
|
440
|
+
const buffer = nodeCrypto.randomBytes(32);
|
|
441
441
|
seed.set(buffer);
|
|
442
442
|
} catch {
|
|
443
443
|
throw new Error("No secure random number generator available");
|
|
@@ -1881,10 +1881,25 @@ function getShieldPoolPDAs(programId, mint) {
|
|
|
1881
1881
|
// src/utils/proof-generation.ts
|
|
1882
1882
|
var snarkjs = __toESM(require("snarkjs"), 1);
|
|
1883
1883
|
var IS_BROWSER = typeof window !== "undefined" || typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
1884
|
+
var _nodePath = null;
|
|
1885
|
+
var _nodeFs = null;
|
|
1886
|
+
var _nodeCrypto = null;
|
|
1887
|
+
async function getNodePath() {
|
|
1888
|
+
if (_nodePath) return _nodePath;
|
|
1889
|
+
_nodePath = await import("path");
|
|
1890
|
+
return _nodePath;
|
|
1891
|
+
}
|
|
1892
|
+
async function getNodeFs() {
|
|
1893
|
+
if (_nodeFs) return _nodeFs;
|
|
1894
|
+
_nodeFs = await import("fs");
|
|
1895
|
+
return _nodeFs;
|
|
1896
|
+
}
|
|
1897
|
+
async function getNodeCrypto() {
|
|
1898
|
+
if (_nodeCrypto) return _nodeCrypto;
|
|
1899
|
+
_nodeCrypto = await import("crypto");
|
|
1900
|
+
return _nodeCrypto;
|
|
1901
|
+
}
|
|
1884
1902
|
function joinPath(...parts) {
|
|
1885
|
-
if (IS_BROWSER) {
|
|
1886
|
-
return parts.join("/").replace(/\/+/g, "/");
|
|
1887
|
-
}
|
|
1888
1903
|
return parts.join("/").replace(/\/+/g, "/");
|
|
1889
1904
|
}
|
|
1890
1905
|
async function fileExists(filePath) {
|
|
@@ -1897,9 +1912,9 @@ async function fileExists(filePath) {
|
|
|
1897
1912
|
}
|
|
1898
1913
|
}
|
|
1899
1914
|
try {
|
|
1900
|
-
const
|
|
1901
|
-
if (
|
|
1902
|
-
return
|
|
1915
|
+
const nodeFs = globalThis.require?.("fs") || (typeof require !== "undefined" ? require("fs") : null);
|
|
1916
|
+
if (nodeFs) {
|
|
1917
|
+
return nodeFs.existsSync(filePath);
|
|
1903
1918
|
}
|
|
1904
1919
|
const fsModule = await import("fs");
|
|
1905
1920
|
return fsModule.existsSync(filePath);
|
|
@@ -1907,15 +1922,15 @@ async function fileExists(filePath) {
|
|
|
1907
1922
|
return false;
|
|
1908
1923
|
}
|
|
1909
1924
|
}
|
|
1910
|
-
async function generateWithdrawRegularProof(inputs,
|
|
1925
|
+
async function generateWithdrawRegularProof(inputs, circuitsPath) {
|
|
1911
1926
|
let wasmPath;
|
|
1912
1927
|
let zkeyPath;
|
|
1913
1928
|
if (IS_BROWSER) {
|
|
1914
|
-
wasmPath = `${
|
|
1915
|
-
zkeyPath = `${
|
|
1929
|
+
wasmPath = `${circuitsPath}/withdraw_regular_js/withdraw_regular.wasm`;
|
|
1930
|
+
zkeyPath = `${circuitsPath}/withdraw_regular_final.zkey`;
|
|
1916
1931
|
} else {
|
|
1917
|
-
wasmPath = joinPath(
|
|
1918
|
-
zkeyPath = joinPath(
|
|
1932
|
+
wasmPath = joinPath(circuitsPath, "build", "withdraw_regular_js", "withdraw_regular.wasm");
|
|
1933
|
+
zkeyPath = joinPath(circuitsPath, "build", "withdraw_regular_final.zkey");
|
|
1919
1934
|
const wasmExists = await fileExists(wasmPath);
|
|
1920
1935
|
const zkeyExists = await fileExists(zkeyPath);
|
|
1921
1936
|
if (!wasmExists) {
|
|
@@ -1957,15 +1972,15 @@ async function generateWithdrawRegularProof(inputs, circuitsPath2) {
|
|
|
1957
1972
|
// Not used, kept for compatibility
|
|
1958
1973
|
};
|
|
1959
1974
|
}
|
|
1960
|
-
async function generateWithdrawSwapProof(inputs,
|
|
1975
|
+
async function generateWithdrawSwapProof(inputs, circuitsPath) {
|
|
1961
1976
|
let wasmPath;
|
|
1962
1977
|
let zkeyPath;
|
|
1963
1978
|
if (IS_BROWSER) {
|
|
1964
|
-
wasmPath = `${
|
|
1965
|
-
zkeyPath = `${
|
|
1979
|
+
wasmPath = `${circuitsPath}/withdraw_swap_js/withdraw_swap.wasm`;
|
|
1980
|
+
zkeyPath = `${circuitsPath}/withdraw_swap_final.zkey`;
|
|
1966
1981
|
} else {
|
|
1967
|
-
wasmPath = joinPath(
|
|
1968
|
-
zkeyPath = joinPath(
|
|
1982
|
+
wasmPath = joinPath(circuitsPath, "build", "withdraw_swap_js", "withdraw_swap.wasm");
|
|
1983
|
+
zkeyPath = joinPath(circuitsPath, "build", "withdraw_swap_final.zkey");
|
|
1969
1984
|
const wasmExists = await fileExists(wasmPath);
|
|
1970
1985
|
const zkeyExists = await fileExists(zkeyPath);
|
|
1971
1986
|
if (!wasmExists) {
|
|
@@ -2014,12 +2029,12 @@ async function generateWithdrawSwapProof(inputs, circuitsPath2) {
|
|
|
2014
2029
|
// Not used, kept for compatibility
|
|
2015
2030
|
};
|
|
2016
2031
|
}
|
|
2017
|
-
async function areCircuitsAvailable(
|
|
2032
|
+
async function areCircuitsAvailable(circuitsPath) {
|
|
2018
2033
|
if (IS_BROWSER) {
|
|
2019
|
-
return Boolean(
|
|
2034
|
+
return Boolean(circuitsPath && circuitsPath !== "");
|
|
2020
2035
|
}
|
|
2021
|
-
const wasmPath = joinPath(
|
|
2022
|
-
const zkeyPath = joinPath(
|
|
2036
|
+
const wasmPath = joinPath(circuitsPath, "build", "withdraw_regular_js", "withdraw_regular.wasm");
|
|
2037
|
+
const zkeyPath = joinPath(circuitsPath, "build", "withdraw_regular_final.zkey");
|
|
2023
2038
|
const wasmExists = await fileExists(wasmPath);
|
|
2024
2039
|
const zkeyExists = await fileExists(zkeyPath);
|
|
2025
2040
|
return wasmExists && zkeyExists;
|
|
@@ -2033,7 +2048,7 @@ async function getDefaultCircuitsPath() {
|
|
|
2033
2048
|
}
|
|
2034
2049
|
let nodePath;
|
|
2035
2050
|
try {
|
|
2036
|
-
nodePath =
|
|
2051
|
+
nodePath = await getNodePath();
|
|
2037
2052
|
} catch {
|
|
2038
2053
|
return "/circuits";
|
|
2039
2054
|
}
|
|
@@ -2083,7 +2098,7 @@ async function verifyCircuitIntegrity(circuitsPath, circuit) {
|
|
|
2083
2098
|
vkeyData = await response.text();
|
|
2084
2099
|
} else {
|
|
2085
2100
|
try {
|
|
2086
|
-
const nodeFs =
|
|
2101
|
+
const nodeFs = await getNodeFs();
|
|
2087
2102
|
vkeyData = nodeFs.readFileSync(vkeyPath, "utf8");
|
|
2088
2103
|
} catch (e) {
|
|
2089
2104
|
return {
|
|
@@ -2101,7 +2116,7 @@ async function verifyCircuitIntegrity(circuitsPath, circuit) {
|
|
|
2101
2116
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
2102
2117
|
computedHash = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2103
2118
|
} else {
|
|
2104
|
-
const nodeCrypto =
|
|
2119
|
+
const nodeCrypto = await getNodeCrypto();
|
|
2105
2120
|
computedHash = nodeCrypto.createHash("sha256").update(vkeyData).digest("hex");
|
|
2106
2121
|
}
|
|
2107
2122
|
if (computedHash === expectedHash) {
|
|
@@ -2128,15 +2143,26 @@ async function verifyCircuitIntegrity(circuitsPath, circuit) {
|
|
|
2128
2143
|
};
|
|
2129
2144
|
}
|
|
2130
2145
|
}
|
|
2131
|
-
async function verifyAllCircuits(
|
|
2146
|
+
async function verifyAllCircuits(circuitsPath) {
|
|
2132
2147
|
const results = await Promise.all([
|
|
2133
|
-
verifyCircuitIntegrity(
|
|
2134
|
-
verifyCircuitIntegrity(
|
|
2148
|
+
verifyCircuitIntegrity(circuitsPath, "withdraw_regular"),
|
|
2149
|
+
verifyCircuitIntegrity(circuitsPath, "withdraw_swap")
|
|
2135
2150
|
]);
|
|
2136
2151
|
return results;
|
|
2137
2152
|
}
|
|
2138
2153
|
|
|
2139
2154
|
// src/core/CloakSDK.ts
|
|
2155
|
+
var COMPUTE_BUDGET_PROGRAM_ID = new import_web35.PublicKey("ComputeBudget111111111111111111111111111111");
|
|
2156
|
+
function createSetLoadedAccountsDataSizeLimitInstruction(bytes) {
|
|
2157
|
+
const data = Buffer.alloc(5);
|
|
2158
|
+
data.writeUInt8(4, 0);
|
|
2159
|
+
data.writeUInt32LE(bytes, 1);
|
|
2160
|
+
return new import_web35.TransactionInstruction({
|
|
2161
|
+
keys: [],
|
|
2162
|
+
programId: COMPUTE_BUDGET_PROGRAM_ID,
|
|
2163
|
+
data
|
|
2164
|
+
});
|
|
2165
|
+
}
|
|
2140
2166
|
var CLOAK_PROGRAM_ID = new import_web35.PublicKey("c1oak6tetxYnNfvXKFkpn1d98FxtK7B68vBQLYQpWKp");
|
|
2141
2167
|
var CLOAK_API_URL = "https://api.cloak.ag";
|
|
2142
2168
|
var CloakSDK = class {
|
|
@@ -2297,10 +2323,51 @@ var CloakSDK = class {
|
|
|
2297
2323
|
commitment: commitmentBytes
|
|
2298
2324
|
});
|
|
2299
2325
|
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
|
|
2326
|
+
const priorityFee = options?.priorityFee ?? 1e4;
|
|
2327
|
+
const cuPriceIx = import_web35.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee });
|
|
2328
|
+
const dataSizeLimit = options?.loadedAccountsDataSizeLimit ?? 256 * 1024;
|
|
2329
|
+
const dataSizeLimitIx = dataSizeLimit > 0 ? createSetLoadedAccountsDataSizeLimitInstruction(dataSizeLimit) : null;
|
|
2330
|
+
let computeUnits;
|
|
2331
|
+
if (options?.optimizeCU && this.keypair) {
|
|
2332
|
+
options?.onProgress?.("simulating", { message: "Simulating transaction for optimal CU..." });
|
|
2333
|
+
const simCuLimitIx = import_web35.ComputeBudgetProgram.setComputeUnitLimit({ units: 2e5 });
|
|
2334
|
+
const simTransaction = new import_web35.Transaction({
|
|
2335
|
+
feePayer: payerPubkey,
|
|
2336
|
+
recentBlockhash: blockhash
|
|
2337
|
+
}).add(simCuLimitIx).add(cuPriceIx);
|
|
2338
|
+
if (dataSizeLimitIx) {
|
|
2339
|
+
simTransaction.add(dataSizeLimitIx);
|
|
2340
|
+
}
|
|
2341
|
+
simTransaction.add(depositIx);
|
|
2342
|
+
try {
|
|
2343
|
+
const simulation = await connection.simulateTransaction(simTransaction, [this.keypair]);
|
|
2344
|
+
if (simulation.value.err) {
|
|
2345
|
+
console.warn("Simulation failed, using default CU:", simulation.value.err);
|
|
2346
|
+
computeUnits = 4e4;
|
|
2347
|
+
} else {
|
|
2348
|
+
const simulatedCU = simulation.value.unitsConsumed ?? 3e4;
|
|
2349
|
+
computeUnits = Math.ceil(simulatedCU * 1.2);
|
|
2350
|
+
console.log(`CU optimization: ${simulatedCU} simulated \u2192 ${computeUnits} limit`);
|
|
2351
|
+
}
|
|
2352
|
+
} catch (simError) {
|
|
2353
|
+
console.warn("Simulation RPC error, using default CU:", simError);
|
|
2354
|
+
computeUnits = 4e4;
|
|
2355
|
+
}
|
|
2356
|
+
} else if (options?.optimizeCU && this.wallet) {
|
|
2357
|
+
console.warn("CU optimization via simulation not available in wallet mode (would require double signing). Using default.");
|
|
2358
|
+
computeUnits = options?.computeUnits ?? 4e4;
|
|
2359
|
+
} else {
|
|
2360
|
+
computeUnits = options?.computeUnits ?? 4e4;
|
|
2361
|
+
}
|
|
2362
|
+
const cuLimitIx = import_web35.ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits });
|
|
2300
2363
|
const transaction = new import_web35.Transaction({
|
|
2301
2364
|
feePayer: payerPubkey,
|
|
2302
2365
|
recentBlockhash: blockhash
|
|
2303
|
-
}).add(
|
|
2366
|
+
}).add(cuLimitIx).add(cuPriceIx);
|
|
2367
|
+
if (dataSizeLimitIx) {
|
|
2368
|
+
transaction.add(dataSizeLimitIx);
|
|
2369
|
+
}
|
|
2370
|
+
transaction.add(depositIx);
|
|
2304
2371
|
if (!transaction.feePayer) {
|
|
2305
2372
|
throw new Error("Transaction feePayer is not set");
|
|
2306
2373
|
}
|
|
@@ -2580,8 +2647,8 @@ var CloakSDK = class {
|
|
|
2580
2647
|
}
|
|
2581
2648
|
}
|
|
2582
2649
|
const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
|
|
2583
|
-
const
|
|
2584
|
-
const useDirectProof = await areCircuitsAvailable(
|
|
2650
|
+
const circuitsPath = isBrowser ? "/circuits" : typeof process !== "undefined" && process.env?.CIRCUITS_PATH || await getDefaultCircuitsPath();
|
|
2651
|
+
const useDirectProof = await areCircuitsAvailable(circuitsPath);
|
|
2585
2652
|
let proofHex;
|
|
2586
2653
|
let finalPublicInputs;
|
|
2587
2654
|
if (useDirectProof) {
|
|
@@ -2643,7 +2710,7 @@ var CloakSDK = class {
|
|
|
2643
2710
|
rem
|
|
2644
2711
|
};
|
|
2645
2712
|
options?.onProgress?.("proof_generating");
|
|
2646
|
-
const proofResult = await generateWithdrawRegularProof(proofInputs,
|
|
2713
|
+
const proofResult = await generateWithdrawRegularProof(proofInputs, circuitsPath);
|
|
2647
2714
|
options?.onProgress?.("proof_complete");
|
|
2648
2715
|
proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
|
|
2649
2716
|
finalPublicInputs = {
|
|
@@ -2654,7 +2721,7 @@ var CloakSDK = class {
|
|
|
2654
2721
|
};
|
|
2655
2722
|
} else {
|
|
2656
2723
|
throw new Error(
|
|
2657
|
-
`Circuits not available at ${
|
|
2724
|
+
`Circuits not available at ${circuitsPath}. Make sure circuits are built and accessible. In browser, circuits should be served from /circuits. In Node.js, set CIRCUITS_PATH environment variable or ensure circuits are in the expected location.`
|
|
2658
2725
|
);
|
|
2659
2726
|
}
|
|
2660
2727
|
const signature = await this.relay.submitWithdraw(
|
|
@@ -2851,8 +2918,8 @@ var CloakSDK = class {
|
|
|
2851
2918
|
throw new Error("Merkle proof is invalid: missing path elements");
|
|
2852
2919
|
}
|
|
2853
2920
|
const envCircuitsPath = typeof window !== "undefined" ? typeof process !== "undefined" && process.env?.NEXT_PUBLIC_CIRCUITS_PATH || void 0 : typeof process !== "undefined" && process.env?.CIRCUITS_PATH || void 0;
|
|
2854
|
-
const
|
|
2855
|
-
const useDirectProof = await areCircuitsAvailable(
|
|
2921
|
+
const circuitsPath = envCircuitsPath || await getDefaultCircuitsPath();
|
|
2922
|
+
const useDirectProof = await areCircuitsAvailable(circuitsPath);
|
|
2856
2923
|
let proofHex;
|
|
2857
2924
|
let finalPublicInputs;
|
|
2858
2925
|
if (useDirectProof) {
|
|
@@ -2895,7 +2962,7 @@ var CloakSDK = class {
|
|
|
2895
2962
|
rem
|
|
2896
2963
|
};
|
|
2897
2964
|
options?.onProgress?.("proof_generating");
|
|
2898
|
-
const proofResult = await generateWithdrawSwapProof(proofInputs,
|
|
2965
|
+
const proofResult = await generateWithdrawSwapProof(proofInputs, circuitsPath);
|
|
2899
2966
|
options?.onProgress?.("proof_complete");
|
|
2900
2967
|
proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
|
|
2901
2968
|
finalPublicInputs = {
|
|
@@ -2906,7 +2973,7 @@ var CloakSDK = class {
|
|
|
2906
2973
|
};
|
|
2907
2974
|
} else {
|
|
2908
2975
|
throw new Error(
|
|
2909
|
-
`Circuits not available at ${
|
|
2976
|
+
`Circuits not available at ${circuitsPath}. Make sure circuits are built and accessible. In browser, circuits should be served from /circuits. In Node.js, set CIRCUITS_PATH environment variable or ensure circuits are in the expected location.`
|
|
2910
2977
|
);
|
|
2911
2978
|
}
|
|
2912
2979
|
const signature = await this.relay.submitSwap(
|
package/dist/index.d.cts
CHANGED
|
@@ -199,10 +199,46 @@ interface DepositOptions {
|
|
|
199
199
|
requireNoteAcknowledgment?: boolean;
|
|
200
200
|
/** Skip simulation (default: false) */
|
|
201
201
|
skipPreflight?: boolean;
|
|
202
|
-
/**
|
|
202
|
+
/**
|
|
203
|
+
* Compute units to request.
|
|
204
|
+
* - If `optimizeCU` is true, this is ignored and CU is determined via simulation
|
|
205
|
+
* - Otherwise defaults to 40,000 (suitable for typical deposits)
|
|
206
|
+
*/
|
|
203
207
|
computeUnits?: number;
|
|
204
|
-
/**
|
|
208
|
+
/**
|
|
209
|
+
* Enable simulation-based CU optimization.
|
|
210
|
+
* When true, simulates the transaction first to determine actual CU usage,
|
|
211
|
+
* then sets an optimal limit (simulated + 20% buffer).
|
|
212
|
+
*
|
|
213
|
+
* **Note: Only works in keypair mode (Node.js/scripts).**
|
|
214
|
+
* In wallet/browser mode, this is ignored because simulation would require
|
|
215
|
+
* the user to sign twice (simulation + actual tx) which is bad UX.
|
|
216
|
+
*
|
|
217
|
+
* Trade-offs:
|
|
218
|
+
* - ✅ Optimal block scheduling priority
|
|
219
|
+
* - ❌ Adds ~200-500ms latency (extra RPC call)
|
|
220
|
+
* - ❌ Only available in keypair mode
|
|
221
|
+
*
|
|
222
|
+
* Default: false (uses fixed 40K CU which is ~75% efficient for typical deposits)
|
|
223
|
+
*/
|
|
224
|
+
optimizeCU?: boolean;
|
|
225
|
+
/** Priority fee in micro-lamports (default: 10,000) */
|
|
205
226
|
priorityFee?: number;
|
|
227
|
+
/**
|
|
228
|
+
* Loaded accounts data size limit in bytes.
|
|
229
|
+
*
|
|
230
|
+
* Without this, Solana defaults to 64MB which incurs CU overhead
|
|
231
|
+
* (charged at 8 CU per 32KB). Setting a lower limit improves priority.
|
|
232
|
+
*
|
|
233
|
+
* **Note for Cloak deposits:**
|
|
234
|
+
* The Shield Pool program is ~104KB, so the minimum practical limit is ~128KB.
|
|
235
|
+
*
|
|
236
|
+
* Default: 256 * 1024 (256KB) - provides safety margin above program size.
|
|
237
|
+
* Set to 0 to disable (use Solana 64MB default).
|
|
238
|
+
*
|
|
239
|
+
* @see https://www.anza.xyz/blog/cu-optimization-with-setloadedaccountsdatasizelimit
|
|
240
|
+
*/
|
|
241
|
+
loadedAccountsDataSizeLimit?: number;
|
|
206
242
|
/**
|
|
207
243
|
* Optional: Encrypt output for specific recipient's view key
|
|
208
244
|
* If not provided, encrypts for the wallet's own view key (for self-scanning)
|
package/dist/index.d.ts
CHANGED
|
@@ -199,10 +199,46 @@ interface DepositOptions {
|
|
|
199
199
|
requireNoteAcknowledgment?: boolean;
|
|
200
200
|
/** Skip simulation (default: false) */
|
|
201
201
|
skipPreflight?: boolean;
|
|
202
|
-
/**
|
|
202
|
+
/**
|
|
203
|
+
* Compute units to request.
|
|
204
|
+
* - If `optimizeCU` is true, this is ignored and CU is determined via simulation
|
|
205
|
+
* - Otherwise defaults to 40,000 (suitable for typical deposits)
|
|
206
|
+
*/
|
|
203
207
|
computeUnits?: number;
|
|
204
|
-
/**
|
|
208
|
+
/**
|
|
209
|
+
* Enable simulation-based CU optimization.
|
|
210
|
+
* When true, simulates the transaction first to determine actual CU usage,
|
|
211
|
+
* then sets an optimal limit (simulated + 20% buffer).
|
|
212
|
+
*
|
|
213
|
+
* **Note: Only works in keypair mode (Node.js/scripts).**
|
|
214
|
+
* In wallet/browser mode, this is ignored because simulation would require
|
|
215
|
+
* the user to sign twice (simulation + actual tx) which is bad UX.
|
|
216
|
+
*
|
|
217
|
+
* Trade-offs:
|
|
218
|
+
* - ✅ Optimal block scheduling priority
|
|
219
|
+
* - ❌ Adds ~200-500ms latency (extra RPC call)
|
|
220
|
+
* - ❌ Only available in keypair mode
|
|
221
|
+
*
|
|
222
|
+
* Default: false (uses fixed 40K CU which is ~75% efficient for typical deposits)
|
|
223
|
+
*/
|
|
224
|
+
optimizeCU?: boolean;
|
|
225
|
+
/** Priority fee in micro-lamports (default: 10,000) */
|
|
205
226
|
priorityFee?: number;
|
|
227
|
+
/**
|
|
228
|
+
* Loaded accounts data size limit in bytes.
|
|
229
|
+
*
|
|
230
|
+
* Without this, Solana defaults to 64MB which incurs CU overhead
|
|
231
|
+
* (charged at 8 CU per 32KB). Setting a lower limit improves priority.
|
|
232
|
+
*
|
|
233
|
+
* **Note for Cloak deposits:**
|
|
234
|
+
* The Shield Pool program is ~104KB, so the minimum practical limit is ~128KB.
|
|
235
|
+
*
|
|
236
|
+
* Default: 256 * 1024 (256KB) - provides safety margin above program size.
|
|
237
|
+
* Set to 0 to disable (use Solana 64MB default).
|
|
238
|
+
*
|
|
239
|
+
* @see https://www.anza.xyz/blog/cu-optimization-with-setloadedaccountsdatasizelimit
|
|
240
|
+
*/
|
|
241
|
+
loadedAccountsDataSizeLimit?: number;
|
|
206
242
|
/**
|
|
207
243
|
* Optional: Encrypt output for specific recipient's view key
|
|
208
244
|
* If not provided, encrypts for the wallet's own view key (for self-scanning)
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,9 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
import {
|
|
10
10
|
Keypair,
|
|
11
11
|
PublicKey as PublicKey4,
|
|
12
|
-
Transaction
|
|
12
|
+
Transaction,
|
|
13
|
+
TransactionInstruction as TransactionInstruction2,
|
|
14
|
+
ComputeBudgetProgram
|
|
13
15
|
} from "@solana/web3.js";
|
|
14
16
|
|
|
15
17
|
// src/core/types.ts
|
|
@@ -172,14 +174,14 @@ function randomBytes(length) {
|
|
|
172
174
|
} catch {
|
|
173
175
|
}
|
|
174
176
|
try {
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
177
|
-
const buffer =
|
|
177
|
+
const nodeCrypto = __require("crypto");
|
|
178
|
+
if (nodeCrypto?.randomBytes) {
|
|
179
|
+
const buffer = nodeCrypto.randomBytes(length);
|
|
178
180
|
bytes.set(buffer);
|
|
179
181
|
return bytes;
|
|
180
182
|
}
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
+
if (nodeCrypto?.webcrypto?.getRandomValues) {
|
|
184
|
+
nodeCrypto.webcrypto.getRandomValues(bytes);
|
|
183
185
|
return bytes;
|
|
184
186
|
}
|
|
185
187
|
} catch {
|
|
@@ -301,8 +303,8 @@ function generateMasterSeed() {
|
|
|
301
303
|
cryptoObj.getRandomValues(seed);
|
|
302
304
|
} else {
|
|
303
305
|
try {
|
|
304
|
-
const
|
|
305
|
-
const buffer =
|
|
306
|
+
const nodeCrypto = __require("crypto");
|
|
307
|
+
const buffer = nodeCrypto.randomBytes(32);
|
|
306
308
|
seed.set(buffer);
|
|
307
309
|
} catch {
|
|
308
310
|
throw new Error("No secure random number generator available");
|
|
@@ -1750,10 +1752,25 @@ function getShieldPoolPDAs(programId, mint) {
|
|
|
1750
1752
|
// src/utils/proof-generation.ts
|
|
1751
1753
|
import * as snarkjs from "snarkjs";
|
|
1752
1754
|
var IS_BROWSER = typeof window !== "undefined" || typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
1755
|
+
var _nodePath = null;
|
|
1756
|
+
var _nodeFs = null;
|
|
1757
|
+
var _nodeCrypto = null;
|
|
1758
|
+
async function getNodePath() {
|
|
1759
|
+
if (_nodePath) return _nodePath;
|
|
1760
|
+
_nodePath = await import("path");
|
|
1761
|
+
return _nodePath;
|
|
1762
|
+
}
|
|
1763
|
+
async function getNodeFs() {
|
|
1764
|
+
if (_nodeFs) return _nodeFs;
|
|
1765
|
+
_nodeFs = await import("fs");
|
|
1766
|
+
return _nodeFs;
|
|
1767
|
+
}
|
|
1768
|
+
async function getNodeCrypto() {
|
|
1769
|
+
if (_nodeCrypto) return _nodeCrypto;
|
|
1770
|
+
_nodeCrypto = await import("crypto");
|
|
1771
|
+
return _nodeCrypto;
|
|
1772
|
+
}
|
|
1753
1773
|
function joinPath(...parts) {
|
|
1754
|
-
if (IS_BROWSER) {
|
|
1755
|
-
return parts.join("/").replace(/\/+/g, "/");
|
|
1756
|
-
}
|
|
1757
1774
|
return parts.join("/").replace(/\/+/g, "/");
|
|
1758
1775
|
}
|
|
1759
1776
|
async function fileExists(filePath) {
|
|
@@ -1766,9 +1783,9 @@ async function fileExists(filePath) {
|
|
|
1766
1783
|
}
|
|
1767
1784
|
}
|
|
1768
1785
|
try {
|
|
1769
|
-
const
|
|
1770
|
-
if (
|
|
1771
|
-
return
|
|
1786
|
+
const nodeFs = globalThis.require?.("fs") || (typeof __require !== "undefined" ? __require("fs") : null);
|
|
1787
|
+
if (nodeFs) {
|
|
1788
|
+
return nodeFs.existsSync(filePath);
|
|
1772
1789
|
}
|
|
1773
1790
|
const fsModule = await import("fs");
|
|
1774
1791
|
return fsModule.existsSync(filePath);
|
|
@@ -1776,15 +1793,15 @@ async function fileExists(filePath) {
|
|
|
1776
1793
|
return false;
|
|
1777
1794
|
}
|
|
1778
1795
|
}
|
|
1779
|
-
async function generateWithdrawRegularProof(inputs,
|
|
1796
|
+
async function generateWithdrawRegularProof(inputs, circuitsPath) {
|
|
1780
1797
|
let wasmPath;
|
|
1781
1798
|
let zkeyPath;
|
|
1782
1799
|
if (IS_BROWSER) {
|
|
1783
|
-
wasmPath = `${
|
|
1784
|
-
zkeyPath = `${
|
|
1800
|
+
wasmPath = `${circuitsPath}/withdraw_regular_js/withdraw_regular.wasm`;
|
|
1801
|
+
zkeyPath = `${circuitsPath}/withdraw_regular_final.zkey`;
|
|
1785
1802
|
} else {
|
|
1786
|
-
wasmPath = joinPath(
|
|
1787
|
-
zkeyPath = joinPath(
|
|
1803
|
+
wasmPath = joinPath(circuitsPath, "build", "withdraw_regular_js", "withdraw_regular.wasm");
|
|
1804
|
+
zkeyPath = joinPath(circuitsPath, "build", "withdraw_regular_final.zkey");
|
|
1788
1805
|
const wasmExists = await fileExists(wasmPath);
|
|
1789
1806
|
const zkeyExists = await fileExists(zkeyPath);
|
|
1790
1807
|
if (!wasmExists) {
|
|
@@ -1826,15 +1843,15 @@ async function generateWithdrawRegularProof(inputs, circuitsPath2) {
|
|
|
1826
1843
|
// Not used, kept for compatibility
|
|
1827
1844
|
};
|
|
1828
1845
|
}
|
|
1829
|
-
async function generateWithdrawSwapProof(inputs,
|
|
1846
|
+
async function generateWithdrawSwapProof(inputs, circuitsPath) {
|
|
1830
1847
|
let wasmPath;
|
|
1831
1848
|
let zkeyPath;
|
|
1832
1849
|
if (IS_BROWSER) {
|
|
1833
|
-
wasmPath = `${
|
|
1834
|
-
zkeyPath = `${
|
|
1850
|
+
wasmPath = `${circuitsPath}/withdraw_swap_js/withdraw_swap.wasm`;
|
|
1851
|
+
zkeyPath = `${circuitsPath}/withdraw_swap_final.zkey`;
|
|
1835
1852
|
} else {
|
|
1836
|
-
wasmPath = joinPath(
|
|
1837
|
-
zkeyPath = joinPath(
|
|
1853
|
+
wasmPath = joinPath(circuitsPath, "build", "withdraw_swap_js", "withdraw_swap.wasm");
|
|
1854
|
+
zkeyPath = joinPath(circuitsPath, "build", "withdraw_swap_final.zkey");
|
|
1838
1855
|
const wasmExists = await fileExists(wasmPath);
|
|
1839
1856
|
const zkeyExists = await fileExists(zkeyPath);
|
|
1840
1857
|
if (!wasmExists) {
|
|
@@ -1883,12 +1900,12 @@ async function generateWithdrawSwapProof(inputs, circuitsPath2) {
|
|
|
1883
1900
|
// Not used, kept for compatibility
|
|
1884
1901
|
};
|
|
1885
1902
|
}
|
|
1886
|
-
async function areCircuitsAvailable(
|
|
1903
|
+
async function areCircuitsAvailable(circuitsPath) {
|
|
1887
1904
|
if (IS_BROWSER) {
|
|
1888
|
-
return Boolean(
|
|
1905
|
+
return Boolean(circuitsPath && circuitsPath !== "");
|
|
1889
1906
|
}
|
|
1890
|
-
const wasmPath = joinPath(
|
|
1891
|
-
const zkeyPath = joinPath(
|
|
1907
|
+
const wasmPath = joinPath(circuitsPath, "build", "withdraw_regular_js", "withdraw_regular.wasm");
|
|
1908
|
+
const zkeyPath = joinPath(circuitsPath, "build", "withdraw_regular_final.zkey");
|
|
1892
1909
|
const wasmExists = await fileExists(wasmPath);
|
|
1893
1910
|
const zkeyExists = await fileExists(zkeyPath);
|
|
1894
1911
|
return wasmExists && zkeyExists;
|
|
@@ -1902,7 +1919,7 @@ async function getDefaultCircuitsPath() {
|
|
|
1902
1919
|
}
|
|
1903
1920
|
let nodePath;
|
|
1904
1921
|
try {
|
|
1905
|
-
nodePath =
|
|
1922
|
+
nodePath = await getNodePath();
|
|
1906
1923
|
} catch {
|
|
1907
1924
|
return "/circuits";
|
|
1908
1925
|
}
|
|
@@ -1952,7 +1969,7 @@ async function verifyCircuitIntegrity(circuitsPath, circuit) {
|
|
|
1952
1969
|
vkeyData = await response.text();
|
|
1953
1970
|
} else {
|
|
1954
1971
|
try {
|
|
1955
|
-
const nodeFs =
|
|
1972
|
+
const nodeFs = await getNodeFs();
|
|
1956
1973
|
vkeyData = nodeFs.readFileSync(vkeyPath, "utf8");
|
|
1957
1974
|
} catch (e) {
|
|
1958
1975
|
return {
|
|
@@ -1970,7 +1987,7 @@ async function verifyCircuitIntegrity(circuitsPath, circuit) {
|
|
|
1970
1987
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
1971
1988
|
computedHash = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1972
1989
|
} else {
|
|
1973
|
-
const nodeCrypto =
|
|
1990
|
+
const nodeCrypto = await getNodeCrypto();
|
|
1974
1991
|
computedHash = nodeCrypto.createHash("sha256").update(vkeyData).digest("hex");
|
|
1975
1992
|
}
|
|
1976
1993
|
if (computedHash === expectedHash) {
|
|
@@ -1997,15 +2014,26 @@ async function verifyCircuitIntegrity(circuitsPath, circuit) {
|
|
|
1997
2014
|
};
|
|
1998
2015
|
}
|
|
1999
2016
|
}
|
|
2000
|
-
async function verifyAllCircuits(
|
|
2017
|
+
async function verifyAllCircuits(circuitsPath) {
|
|
2001
2018
|
const results = await Promise.all([
|
|
2002
|
-
verifyCircuitIntegrity(
|
|
2003
|
-
verifyCircuitIntegrity(
|
|
2019
|
+
verifyCircuitIntegrity(circuitsPath, "withdraw_regular"),
|
|
2020
|
+
verifyCircuitIntegrity(circuitsPath, "withdraw_swap")
|
|
2004
2021
|
]);
|
|
2005
2022
|
return results;
|
|
2006
2023
|
}
|
|
2007
2024
|
|
|
2008
2025
|
// src/core/CloakSDK.ts
|
|
2026
|
+
var COMPUTE_BUDGET_PROGRAM_ID = new PublicKey4("ComputeBudget111111111111111111111111111111");
|
|
2027
|
+
function createSetLoadedAccountsDataSizeLimitInstruction(bytes) {
|
|
2028
|
+
const data = Buffer.alloc(5);
|
|
2029
|
+
data.writeUInt8(4, 0);
|
|
2030
|
+
data.writeUInt32LE(bytes, 1);
|
|
2031
|
+
return new TransactionInstruction2({
|
|
2032
|
+
keys: [],
|
|
2033
|
+
programId: COMPUTE_BUDGET_PROGRAM_ID,
|
|
2034
|
+
data
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2009
2037
|
var CLOAK_PROGRAM_ID = new PublicKey4("c1oak6tetxYnNfvXKFkpn1d98FxtK7B68vBQLYQpWKp");
|
|
2010
2038
|
var CLOAK_API_URL = "https://api.cloak.ag";
|
|
2011
2039
|
var CloakSDK = class {
|
|
@@ -2166,10 +2194,51 @@ var CloakSDK = class {
|
|
|
2166
2194
|
commitment: commitmentBytes
|
|
2167
2195
|
});
|
|
2168
2196
|
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
|
|
2197
|
+
const priorityFee = options?.priorityFee ?? 1e4;
|
|
2198
|
+
const cuPriceIx = ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee });
|
|
2199
|
+
const dataSizeLimit = options?.loadedAccountsDataSizeLimit ?? 256 * 1024;
|
|
2200
|
+
const dataSizeLimitIx = dataSizeLimit > 0 ? createSetLoadedAccountsDataSizeLimitInstruction(dataSizeLimit) : null;
|
|
2201
|
+
let computeUnits;
|
|
2202
|
+
if (options?.optimizeCU && this.keypair) {
|
|
2203
|
+
options?.onProgress?.("simulating", { message: "Simulating transaction for optimal CU..." });
|
|
2204
|
+
const simCuLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 2e5 });
|
|
2205
|
+
const simTransaction = new Transaction({
|
|
2206
|
+
feePayer: payerPubkey,
|
|
2207
|
+
recentBlockhash: blockhash
|
|
2208
|
+
}).add(simCuLimitIx).add(cuPriceIx);
|
|
2209
|
+
if (dataSizeLimitIx) {
|
|
2210
|
+
simTransaction.add(dataSizeLimitIx);
|
|
2211
|
+
}
|
|
2212
|
+
simTransaction.add(depositIx);
|
|
2213
|
+
try {
|
|
2214
|
+
const simulation = await connection.simulateTransaction(simTransaction, [this.keypair]);
|
|
2215
|
+
if (simulation.value.err) {
|
|
2216
|
+
console.warn("Simulation failed, using default CU:", simulation.value.err);
|
|
2217
|
+
computeUnits = 4e4;
|
|
2218
|
+
} else {
|
|
2219
|
+
const simulatedCU = simulation.value.unitsConsumed ?? 3e4;
|
|
2220
|
+
computeUnits = Math.ceil(simulatedCU * 1.2);
|
|
2221
|
+
console.log(`CU optimization: ${simulatedCU} simulated \u2192 ${computeUnits} limit`);
|
|
2222
|
+
}
|
|
2223
|
+
} catch (simError) {
|
|
2224
|
+
console.warn("Simulation RPC error, using default CU:", simError);
|
|
2225
|
+
computeUnits = 4e4;
|
|
2226
|
+
}
|
|
2227
|
+
} else if (options?.optimizeCU && this.wallet) {
|
|
2228
|
+
console.warn("CU optimization via simulation not available in wallet mode (would require double signing). Using default.");
|
|
2229
|
+
computeUnits = options?.computeUnits ?? 4e4;
|
|
2230
|
+
} else {
|
|
2231
|
+
computeUnits = options?.computeUnits ?? 4e4;
|
|
2232
|
+
}
|
|
2233
|
+
const cuLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits });
|
|
2169
2234
|
const transaction = new Transaction({
|
|
2170
2235
|
feePayer: payerPubkey,
|
|
2171
2236
|
recentBlockhash: blockhash
|
|
2172
|
-
}).add(
|
|
2237
|
+
}).add(cuLimitIx).add(cuPriceIx);
|
|
2238
|
+
if (dataSizeLimitIx) {
|
|
2239
|
+
transaction.add(dataSizeLimitIx);
|
|
2240
|
+
}
|
|
2241
|
+
transaction.add(depositIx);
|
|
2173
2242
|
if (!transaction.feePayer) {
|
|
2174
2243
|
throw new Error("Transaction feePayer is not set");
|
|
2175
2244
|
}
|
|
@@ -2449,8 +2518,8 @@ var CloakSDK = class {
|
|
|
2449
2518
|
}
|
|
2450
2519
|
}
|
|
2451
2520
|
const isBrowser = typeof window !== "undefined" || typeof globalThis !== "undefined" && globalThis.window;
|
|
2452
|
-
const
|
|
2453
|
-
const useDirectProof = await areCircuitsAvailable(
|
|
2521
|
+
const circuitsPath = isBrowser ? "/circuits" : typeof process !== "undefined" && process.env?.CIRCUITS_PATH || await getDefaultCircuitsPath();
|
|
2522
|
+
const useDirectProof = await areCircuitsAvailable(circuitsPath);
|
|
2454
2523
|
let proofHex;
|
|
2455
2524
|
let finalPublicInputs;
|
|
2456
2525
|
if (useDirectProof) {
|
|
@@ -2512,7 +2581,7 @@ var CloakSDK = class {
|
|
|
2512
2581
|
rem
|
|
2513
2582
|
};
|
|
2514
2583
|
options?.onProgress?.("proof_generating");
|
|
2515
|
-
const proofResult = await generateWithdrawRegularProof(proofInputs,
|
|
2584
|
+
const proofResult = await generateWithdrawRegularProof(proofInputs, circuitsPath);
|
|
2516
2585
|
options?.onProgress?.("proof_complete");
|
|
2517
2586
|
proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
|
|
2518
2587
|
finalPublicInputs = {
|
|
@@ -2523,7 +2592,7 @@ var CloakSDK = class {
|
|
|
2523
2592
|
};
|
|
2524
2593
|
} else {
|
|
2525
2594
|
throw new Error(
|
|
2526
|
-
`Circuits not available at ${
|
|
2595
|
+
`Circuits not available at ${circuitsPath}. Make sure circuits are built and accessible. In browser, circuits should be served from /circuits. In Node.js, set CIRCUITS_PATH environment variable or ensure circuits are in the expected location.`
|
|
2527
2596
|
);
|
|
2528
2597
|
}
|
|
2529
2598
|
const signature = await this.relay.submitWithdraw(
|
|
@@ -2720,8 +2789,8 @@ var CloakSDK = class {
|
|
|
2720
2789
|
throw new Error("Merkle proof is invalid: missing path elements");
|
|
2721
2790
|
}
|
|
2722
2791
|
const envCircuitsPath = typeof window !== "undefined" ? typeof process !== "undefined" && process.env?.NEXT_PUBLIC_CIRCUITS_PATH || void 0 : typeof process !== "undefined" && process.env?.CIRCUITS_PATH || void 0;
|
|
2723
|
-
const
|
|
2724
|
-
const useDirectProof = await areCircuitsAvailable(
|
|
2792
|
+
const circuitsPath = envCircuitsPath || await getDefaultCircuitsPath();
|
|
2793
|
+
const useDirectProof = await areCircuitsAvailable(circuitsPath);
|
|
2725
2794
|
let proofHex;
|
|
2726
2795
|
let finalPublicInputs;
|
|
2727
2796
|
if (useDirectProof) {
|
|
@@ -2764,7 +2833,7 @@ var CloakSDK = class {
|
|
|
2764
2833
|
rem
|
|
2765
2834
|
};
|
|
2766
2835
|
options?.onProgress?.("proof_generating");
|
|
2767
|
-
const proofResult = await generateWithdrawSwapProof(proofInputs,
|
|
2836
|
+
const proofResult = await generateWithdrawSwapProof(proofInputs, circuitsPath);
|
|
2768
2837
|
options?.onProgress?.("proof_complete");
|
|
2769
2838
|
proofHex = Buffer.from(proofResult.proofBytes).toString("hex");
|
|
2770
2839
|
finalPublicInputs = {
|
|
@@ -2775,7 +2844,7 @@ var CloakSDK = class {
|
|
|
2775
2844
|
};
|
|
2776
2845
|
} else {
|
|
2777
2846
|
throw new Error(
|
|
2778
|
-
`Circuits not available at ${
|
|
2847
|
+
`Circuits not available at ${circuitsPath}. Make sure circuits are built and accessible. In browser, circuits should be served from /circuits. In Node.js, set CIRCUITS_PATH environment variable or ensure circuits are in the expected location.`
|
|
2779
2848
|
);
|
|
2780
2849
|
}
|
|
2781
2850
|
const signature = await this.relay.submitSwap(
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloak.ag/sdk",
|
|
3
3
|
"description": "TypeScript SDK for Cloak",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.6",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"build": "tsup src/index.ts --format cjs,esm --dts --external fs --external path",
|
|
17
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --external fs --external path --external crypto",
|
|
18
18
|
"lint": "tsc --noEmit",
|
|
19
19
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
20
20
|
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
|