@veil-cash/sdk 0.6.3 → 0.6.4

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
@@ -288,6 +288,7 @@ Commands print human-readable success output by default. Errors are standardized
288
288
  The CLI is the main entrypoint for most users. If you are integrating Veil programmatically, use the dedicated SDK guide:
289
289
 
290
290
  - [SDK Quick Start and API Reference](./SDK.md)
291
+ - [Browser proof generation](./SDK.md#browser-proof-generation)
291
292
  - [AI agent and signer integration notes](./SDK.md#for-ai-agents)
292
293
 
293
294
  ## Deposit Flow
package/SDK.md CHANGED
@@ -182,6 +182,51 @@ const mergeResult = await mergeUtxos({
182
182
  });
183
183
  ```
184
184
 
185
+ ### Browser Proof Generation
186
+
187
+ `withdraw()`, `transfer()`, `mergeUtxos()`, `mergeSubaccount()`, `buildWithdrawProof()`,
188
+ `buildTransferProof()`, and `prepareTransaction()` can generate proofs in browser
189
+ runtimes.
190
+
191
+ In Node, the SDK uses the packaged `keys/` directory by default. In browsers,
192
+ the SDK defaults to same-origin hosted proving keys:
193
+
194
+ ```text
195
+ /keys/transaction2.wasm
196
+ /keys/transaction2.zkey
197
+ /keys/transaction16.wasm
198
+ /keys/transaction16.zkey
199
+ ```
200
+
201
+ For most web apps, copy the SDK `keys/` files into your app's public assets
202
+ directory. For example, in Next.js/Vite, serving them from `public/keys` makes
203
+ the default path work.
204
+
205
+ If you host keys elsewhere, pass `provingKeyPath`:
206
+
207
+ ```typescript
208
+ await withdraw({
209
+ amount: '0.05',
210
+ recipient: '0xRecipientAddress',
211
+ keypair,
212
+ provingKeyPath: 'https://cdn.example.com/veil-keys',
213
+ });
214
+
215
+ await transfer({
216
+ amount: '0.02',
217
+ recipientAddress: '0xRecipientAddress',
218
+ senderKeypair: keypair,
219
+ provingKeyPath: (circuitName) => `/static/zk/${circuitName}`,
220
+ });
221
+ ```
222
+
223
+ `provingKeyPath` may be a directory/base URL (`/keys`) or a resolver returning
224
+ the circuit base path without the `.wasm` / `.zkey` extension.
225
+
226
+ This removes Node `fs` key lookup from the browser proof path. Some bundlers
227
+ may still require standard Node-core polyfills for existing legacy dependencies
228
+ such as `circomlib`, `eth-sig-util`, and `fixed-merkle-tree`.
229
+
185
230
  ### Balance Queries
186
231
 
187
232
  Balance functions accept an optional `pool` parameter (`'eth'` | `'usdc'`), defaulting to `'eth'`.
@@ -8,35 +8,18 @@ var readline = require('readline');
8
8
  var fs = require('fs');
9
9
  var path = require('path');
10
10
  var ethers = require('ethers');
11
- var crypto = require('crypto');
11
+ var buffer = require('buffer');
12
+ var ethSigUtil = require('eth-sig-util');
13
+ var circomlib = require('circomlib');
12
14
  var MerkleTree = require('fixed-merkle-tree-legacy');
13
15
  var snarkjs = require('snarkjs');
14
- var url = require('url');
16
+ var ffjavascript = require('ffjavascript');
15
17
 
16
18
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
17
19
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
20
 
19
- function _interopNamespace(e) {
20
- if (e && e.__esModule) return e;
21
- var n = Object.create(null);
22
- if (e) {
23
- Object.keys(e).forEach(function (k) {
24
- if (k !== 'default') {
25
- var d = Object.getOwnPropertyDescriptor(e, k);
26
- Object.defineProperty(n, k, d.get ? d : {
27
- enumerable: true,
28
- get: function () { return e[k]; }
29
- });
30
- }
31
- });
32
- }
33
- n.default = e;
34
- return Object.freeze(n);
35
- }
36
-
37
- var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
38
- var path__namespace = /*#__PURE__*/_interopNamespace(path);
39
- var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
21
+ var ethSigUtil__default = /*#__PURE__*/_interopDefault(ethSigUtil);
22
+ var circomlib__default = /*#__PURE__*/_interopDefault(circomlib);
40
23
  var MerkleTree__default = /*#__PURE__*/_interopDefault(MerkleTree);
41
24
 
42
25
  var __create = Object.create;
@@ -1234,8 +1217,8 @@ var require_command = __commonJS({
1234
1217
  "node_modules/commander/lib/command.js"(exports$1) {
1235
1218
  var EventEmitter = __require("events").EventEmitter;
1236
1219
  var childProcess = __require("child_process");
1237
- var path2 = __require("path");
1238
- var fs2 = __require("fs");
1220
+ var path = __require("path");
1221
+ var fs = __require("fs");
1239
1222
  var process2 = __require("process");
1240
1223
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1241
1224
  var { CommanderError: CommanderError2 } = require_error();
@@ -2228,7 +2211,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2228
2211
  * @param {string} subcommandName
2229
2212
  */
2230
2213
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2231
- if (fs2.existsSync(executableFile)) return;
2214
+ if (fs.existsSync(executableFile)) return;
2232
2215
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
2233
2216
  const executableMissing = `'${executableFile}' does not exist
2234
2217
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2246,11 +2229,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2246
2229
  let launchWithNode = false;
2247
2230
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2248
2231
  function findFile(baseDir, baseName) {
2249
- const localBin = path2.resolve(baseDir, baseName);
2250
- if (fs2.existsSync(localBin)) return localBin;
2251
- if (sourceExt.includes(path2.extname(baseName))) return void 0;
2232
+ const localBin = path.resolve(baseDir, baseName);
2233
+ if (fs.existsSync(localBin)) return localBin;
2234
+ if (sourceExt.includes(path.extname(baseName))) return void 0;
2252
2235
  const foundExt = sourceExt.find(
2253
- (ext) => fs2.existsSync(`${localBin}${ext}`)
2236
+ (ext) => fs.existsSync(`${localBin}${ext}`)
2254
2237
  );
2255
2238
  if (foundExt) return `${localBin}${foundExt}`;
2256
2239
  return void 0;
@@ -2262,21 +2245,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2262
2245
  if (this._scriptPath) {
2263
2246
  let resolvedScriptPath;
2264
2247
  try {
2265
- resolvedScriptPath = fs2.realpathSync(this._scriptPath);
2248
+ resolvedScriptPath = fs.realpathSync(this._scriptPath);
2266
2249
  } catch {
2267
2250
  resolvedScriptPath = this._scriptPath;
2268
2251
  }
2269
- executableDir = path2.resolve(
2270
- path2.dirname(resolvedScriptPath),
2252
+ executableDir = path.resolve(
2253
+ path.dirname(resolvedScriptPath),
2271
2254
  executableDir
2272
2255
  );
2273
2256
  }
2274
2257
  if (executableDir) {
2275
2258
  let localFile = findFile(executableDir, executableFile);
2276
2259
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2277
- const legacyName = path2.basename(
2260
+ const legacyName = path.basename(
2278
2261
  this._scriptPath,
2279
- path2.extname(this._scriptPath)
2262
+ path.extname(this._scriptPath)
2280
2263
  );
2281
2264
  if (legacyName !== this._name) {
2282
2265
  localFile = findFile(
@@ -2287,7 +2270,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2287
2270
  }
2288
2271
  executableFile = localFile || executableFile;
2289
2272
  }
2290
- launchWithNode = sourceExt.includes(path2.extname(executableFile));
2273
+ launchWithNode = sourceExt.includes(path.extname(executableFile));
2291
2274
  let proc;
2292
2275
  if (process2.platform !== "win32") {
2293
2276
  if (launchWithNode) {
@@ -3202,7 +3185,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3202
3185
  * @return {Command}
3203
3186
  */
3204
3187
  nameFromFilename(filename) {
3205
- this._name = path2.basename(filename, path2.extname(filename));
3188
+ this._name = path.basename(filename, path.extname(filename));
3206
3189
  return this;
3207
3190
  }
3208
3191
  /**
@@ -3216,9 +3199,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3216
3199
  * @param {string} [path]
3217
3200
  * @return {(string|null|Command)}
3218
3201
  */
3219
- executableDir(path3) {
3220
- if (path3 === void 0) return this._executableDir;
3221
- this._executableDir = path3;
3202
+ executableDir(path2) {
3203
+ if (path2 === void 0) return this._executableDir;
3204
+ this._executableDir = path2;
3222
3205
  return this;
3223
3206
  }
3224
3207
  /**
@@ -3567,10 +3550,10 @@ var require_package = __commonJS({
3567
3550
  // node_modules/dotenv/lib/main.js
3568
3551
  var require_main = __commonJS({
3569
3552
  "node_modules/dotenv/lib/main.js"(exports$1, module) {
3570
- var fs2 = __require("fs");
3571
- var path2 = __require("path");
3553
+ var fs = __require("fs");
3554
+ var path = __require("path");
3572
3555
  var os = __require("os");
3573
- var crypto2 = __require("crypto");
3556
+ var crypto = __require("crypto");
3574
3557
  var packageJson = require_package();
3575
3558
  var version = packageJson.version;
3576
3559
  var TIPS = [
@@ -3709,7 +3692,7 @@ var require_main = __commonJS({
3709
3692
  if (options && options.path && options.path.length > 0) {
3710
3693
  if (Array.isArray(options.path)) {
3711
3694
  for (const filepath of options.path) {
3712
- if (fs2.existsSync(filepath)) {
3695
+ if (fs.existsSync(filepath)) {
3713
3696
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
3714
3697
  }
3715
3698
  }
@@ -3717,15 +3700,15 @@ var require_main = __commonJS({
3717
3700
  possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
3718
3701
  }
3719
3702
  } else {
3720
- possibleVaultPath = path2.resolve(process.cwd(), ".env.vault");
3703
+ possibleVaultPath = path.resolve(process.cwd(), ".env.vault");
3721
3704
  }
3722
- if (fs2.existsSync(possibleVaultPath)) {
3705
+ if (fs.existsSync(possibleVaultPath)) {
3723
3706
  return possibleVaultPath;
3724
3707
  }
3725
3708
  return null;
3726
3709
  }
3727
3710
  function _resolveHome(envPath) {
3728
- return envPath[0] === "~" ? path2.join(os.homedir(), envPath.slice(1)) : envPath;
3711
+ return envPath[0] === "~" ? path.join(os.homedir(), envPath.slice(1)) : envPath;
3729
3712
  }
3730
3713
  function _configVault(options) {
3731
3714
  const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
@@ -3742,7 +3725,7 @@ var require_main = __commonJS({
3742
3725
  return { parsed };
3743
3726
  }
3744
3727
  function configDotenv(options) {
3745
- const dotenvPath = path2.resolve(process.cwd(), ".env");
3728
+ const dotenvPath = path.resolve(process.cwd(), ".env");
3746
3729
  let encoding = "utf8";
3747
3730
  let processEnv = process.env;
3748
3731
  if (options && options.processEnv != null) {
@@ -3770,13 +3753,13 @@ var require_main = __commonJS({
3770
3753
  }
3771
3754
  let lastError;
3772
3755
  const parsedAll = {};
3773
- for (const path3 of optionPaths) {
3756
+ for (const path2 of optionPaths) {
3774
3757
  try {
3775
- const parsed = DotenvModule.parse(fs2.readFileSync(path3, { encoding }));
3758
+ const parsed = DotenvModule.parse(fs.readFileSync(path2, { encoding }));
3776
3759
  DotenvModule.populate(parsedAll, parsed, options);
3777
3760
  } catch (e) {
3778
3761
  if (debug) {
3779
- _debug(`Failed to load ${path3} ${e.message}`);
3762
+ _debug(`Failed to load ${path2} ${e.message}`);
3780
3763
  }
3781
3764
  lastError = e;
3782
3765
  }
@@ -3789,7 +3772,7 @@ var require_main = __commonJS({
3789
3772
  const shortPaths = [];
3790
3773
  for (const filePath of optionPaths) {
3791
3774
  try {
3792
- const relative = path2.relative(process.cwd(), filePath);
3775
+ const relative = path.relative(process.cwd(), filePath);
3793
3776
  shortPaths.push(relative);
3794
3777
  } catch (e) {
3795
3778
  if (debug) {
@@ -3824,7 +3807,7 @@ var require_main = __commonJS({
3824
3807
  const authTag = ciphertext.subarray(-16);
3825
3808
  ciphertext = ciphertext.subarray(12, -16);
3826
3809
  try {
3827
- const aesgcm = crypto2.createDecipheriv("aes-256-gcm", key, nonce);
3810
+ const aesgcm = crypto.createDecipheriv("aes-256-gcm", key, nonce);
3828
3811
  aesgcm.setAuthTag(authTag);
3829
3812
  return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
3830
3813
  } catch (error) {
@@ -5107,15 +5090,18 @@ function loadEnv() {
5107
5090
  } catch {
5108
5091
  }
5109
5092
  }
5110
- var circomlib = __require("circomlib");
5111
- var poseidon = circomlib.poseidon;
5093
+ var poseidon = circomlib__default.default.poseidon;
5112
5094
  var FIELD_SIZE = BigInt(
5113
5095
  "21888242871839275222246405745257275088548364400416034343698204186575808495617"
5114
5096
  );
5115
5097
  var poseidonHash = (items) => BigInt(poseidon(items).toString());
5116
5098
  var poseidonHash2 = (a, b) => poseidonHash([a, b]);
5117
5099
  var randomBN = (nbytes = 31) => {
5118
- const bytes = crypto__namespace.randomBytes(nbytes);
5100
+ const cryptoApi = globalThis.crypto;
5101
+ if (!cryptoApi?.getRandomValues) {
5102
+ throw new Error("Secure random number generation is unavailable in this runtime");
5103
+ }
5104
+ const bytes = cryptoApi.getRandomValues(new Uint8Array(nbytes));
5119
5105
  let hex = "0x";
5120
5106
  for (let i = 0; i < bytes.length; i++) {
5121
5107
  hex += bytes[i].toString(16).padStart(2, "0");
@@ -5124,7 +5110,7 @@ var randomBN = (nbytes = 31) => {
5124
5110
  };
5125
5111
  function toFixedHex(number, length = 32) {
5126
5112
  let hexValue;
5127
- if (number instanceof Buffer) {
5113
+ if (number instanceof buffer.Buffer) {
5128
5114
  hexValue = number.toString("hex");
5129
5115
  } else {
5130
5116
  let bigIntValue = BigInt(number);
@@ -5139,11 +5125,10 @@ function toFixedHex(number, length = 32) {
5139
5125
  var toBuffer = (value, length) => {
5140
5126
  const bigIntValue = BigInt(value);
5141
5127
  const hex = bigIntValue.toString(16).padStart(length * 2, "0");
5142
- return Buffer.from(hex, "hex");
5128
+ return buffer.Buffer.from(hex, "hex");
5143
5129
  };
5144
5130
  function getExtDataHash(extData) {
5145
- const { ethers: ethers2 } = __require("ethers");
5146
- const abi = ethers2.AbiCoder.defaultAbiCoder();
5131
+ const abi = ethers.ethers.AbiCoder.defaultAbiCoder();
5147
5132
  const encodedData = abi.encode(
5148
5133
  ["tuple(address,int256,address,uint256,bytes,bytes)"],
5149
5134
  [[
@@ -5155,7 +5140,7 @@ function getExtDataHash(extData) {
5155
5140
  extData.encryptedOutput2
5156
5141
  ]]
5157
5142
  );
5158
- const hash = ethers2.keccak256(encodedData);
5143
+ const hash = ethers.ethers.keccak256(encodedData);
5159
5144
  return BigInt(hash) % FIELD_SIZE;
5160
5145
  }
5161
5146
  function shuffle(array) {
@@ -5171,15 +5156,14 @@ function shuffle(array) {
5171
5156
 
5172
5157
  // src/keypair.ts
5173
5158
  var VEIL_SIGNED_MESSAGE = "Sign this message to create your Veil Wallet private key. This will be used to decrypt your balances. Ensure you are signing this message on the Veil Cash website.";
5174
- var ethSigUtil = __require("eth-sig-util");
5175
5159
  function packEncryptedMessage(encryptedMessage) {
5176
- const nonceBuf = Buffer.from(encryptedMessage.nonce, "base64");
5177
- const ephemPublicKeyBuf = Buffer.from(encryptedMessage.ephemPublicKey, "base64");
5178
- const ciphertextBuf = Buffer.from(encryptedMessage.ciphertext, "base64");
5179
- const messageBuff = Buffer.concat([
5180
- Buffer.alloc(24 - nonceBuf.length),
5160
+ const nonceBuf = buffer.Buffer.from(encryptedMessage.nonce, "base64");
5161
+ const ephemPublicKeyBuf = buffer.Buffer.from(encryptedMessage.ephemPublicKey, "base64");
5162
+ const ciphertextBuf = buffer.Buffer.from(encryptedMessage.ciphertext, "base64");
5163
+ const messageBuff = buffer.Buffer.concat([
5164
+ buffer.Buffer.alloc(24 - nonceBuf.length),
5181
5165
  nonceBuf,
5182
- Buffer.alloc(32 - ephemPublicKeyBuf.length),
5166
+ buffer.Buffer.alloc(32 - ephemPublicKeyBuf.length),
5183
5167
  ephemPublicKeyBuf,
5184
5168
  ciphertextBuf
5185
5169
  ]);
@@ -5189,7 +5173,7 @@ function unpackEncryptedMessage(encryptedMessage) {
5189
5173
  if (encryptedMessage.slice(0, 2) === "0x") {
5190
5174
  encryptedMessage = encryptedMessage.slice(2);
5191
5175
  }
5192
- const messageBuff = Buffer.from(encryptedMessage, "hex");
5176
+ const messageBuff = buffer.Buffer.from(encryptedMessage, "hex");
5193
5177
  const nonceBuf = messageBuff.slice(0, 24);
5194
5178
  const ephemPublicKeyBuf = messageBuff.slice(24, 56);
5195
5179
  const ciphertextBuf = messageBuff.slice(56);
@@ -5214,7 +5198,7 @@ var Keypair = class _Keypair {
5214
5198
  constructor(privkey = ethers.ethers.Wallet.createRandom().privateKey) {
5215
5199
  this.privkey = privkey;
5216
5200
  this.pubkey = poseidonHash([this.privkey]);
5217
- this.encryptionKey = ethSigUtil.getEncryptionPublicKey(privkey.slice(2));
5201
+ this.encryptionKey = ethSigUtil__default.default.getEncryptionPublicKey(privkey.slice(2));
5218
5202
  }
5219
5203
  /**
5220
5204
  * Get the deposit key for this keypair
@@ -5222,7 +5206,7 @@ var Keypair = class _Keypair {
5222
5206
  * @returns Deposit key as hex string (130 chars with 0x prefix)
5223
5207
  */
5224
5208
  toString() {
5225
- return toFixedHex(this.pubkey) + Buffer.from(this.encryptionKey, "base64").toString("hex");
5209
+ return toFixedHex(this.pubkey) + buffer.Buffer.from(this.encryptionKey, "base64").toString("hex");
5226
5210
  }
5227
5211
  /**
5228
5212
  * Alias for toString() - returns the deposit key
@@ -5247,7 +5231,7 @@ var Keypair = class _Keypair {
5247
5231
  return Object.assign(new _Keypair(), {
5248
5232
  privkey: null,
5249
5233
  pubkey: BigInt("0x" + str.slice(0, 64)),
5250
- encryptionKey: Buffer.from(str.slice(64, 128), "hex").toString("base64")
5234
+ encryptionKey: buffer.Buffer.from(str.slice(64, 128), "hex").toString("base64")
5251
5235
  });
5252
5236
  }
5253
5237
  /**
@@ -5332,7 +5316,7 @@ var Keypair = class _Keypair {
5332
5316
  */
5333
5317
  encrypt(bytes) {
5334
5318
  return packEncryptedMessage(
5335
- ethSigUtil.encrypt(
5319
+ ethSigUtil__default.default.encrypt(
5336
5320
  this.encryptionKey,
5337
5321
  { data: bytes.toString("base64") },
5338
5322
  "x25519-xsalsa20-poly1305"
@@ -5348,8 +5332,8 @@ var Keypair = class _Keypair {
5348
5332
  if (!this.privkey) {
5349
5333
  throw new Error("Cannot decrypt without private key");
5350
5334
  }
5351
- return Buffer.from(
5352
- ethSigUtil.decrypt(unpackEncryptedMessage(data), this.privkey.slice(2)),
5335
+ return buffer.Buffer.from(
5336
+ ethSigUtil__default.default.decrypt(unpackEncryptedMessage(data), this.privkey.slice(2)),
5353
5337
  "base64"
5354
5338
  );
5355
5339
  }
@@ -5423,10 +5407,10 @@ async function confirm(question) {
5423
5407
  input: process.stdin,
5424
5408
  output: process.stdout
5425
5409
  });
5426
- return new Promise((resolve2) => {
5410
+ return new Promise((resolve) => {
5427
5411
  rl.question(`${question} (y/n): `, (answer) => {
5428
5412
  rl.close();
5429
- resolve2(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
5413
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
5430
5414
  });
5431
5415
  });
5432
5416
  }
@@ -5792,8 +5776,6 @@ function createRegisterCommand() {
5792
5776
  });
5793
5777
  return register;
5794
5778
  }
5795
-
5796
- // src/utxo.ts
5797
5779
  var Utxo = class _Utxo {
5798
5780
  amount;
5799
5781
  blinding;
@@ -5844,7 +5826,7 @@ var Utxo = class _Utxo {
5844
5826
  * @returns Encrypted data as 0x-prefixed hex string
5845
5827
  */
5846
5828
  encrypt() {
5847
- const bytes = Buffer.concat([
5829
+ const bytes = buffer.Buffer.concat([
5848
5830
  toBuffer(this.amount, 31),
5849
5831
  toBuffer(this.blinding, 31)
5850
5832
  ]);
@@ -6533,30 +6515,46 @@ async function buildMerkleTree(commitments) {
6533
6515
  const tree = new MerkleTree__default.default(MERKLE_TREE_HEIGHT, leaves, { hashFunction });
6534
6516
  return tree;
6535
6517
  }
6536
- var utils = null;
6537
- try {
6538
- const ffjavascript = __require("ffjavascript");
6539
- utils = ffjavascript.utils;
6540
- } catch {
6541
- console.warn("ffjavascript not found. Proof generation may not work.");
6518
+ var ffUtils = ffjavascript.utils;
6519
+ function isBrowserRuntime() {
6520
+ return !(typeof process !== "undefined" && !!process.versions?.node);
6542
6521
  }
6543
- function findKeysDirectory() {
6522
+ function stripTrailingSlash(value) {
6523
+ return value.endsWith("/") ? value.slice(0, -1) : value;
6524
+ }
6525
+ function normalizeCircuitBasePath(provingKeyPath, circuitName) {
6526
+ const resolvedPath = typeof provingKeyPath === "function" ? provingKeyPath(circuitName) : provingKeyPath;
6527
+ const withoutExtension = resolvedPath.replace(/\.(wasm|zkey)$/i, "");
6528
+ if (withoutExtension.endsWith(`/${circuitName}`) || withoutExtension.endsWith(`\\${circuitName}`)) {
6529
+ return withoutExtension;
6530
+ }
6531
+ return `${stripTrailingSlash(withoutExtension)}/${circuitName}`;
6532
+ }
6533
+ function importNodeModule(specifier) {
6534
+ const dynamicImport = new Function("specifier", "return import(specifier)");
6535
+ return dynamicImport(specifier);
6536
+ }
6537
+ async function findNodeKeysDirectory() {
6538
+ const [{ existsSync: existsSync2 }, pathModule, { fileURLToPath }] = await Promise.all([
6539
+ importNodeModule("node:fs"),
6540
+ importNodeModule("node:path"),
6541
+ importNodeModule("node:url")
6542
+ ]);
6543
+ const path = pathModule;
6544
6544
  const possiblePaths = [
6545
6545
  // When running from package (installed via npm)
6546
- path__namespace.resolve(__dirname, "..", "keys"),
6547
- path__namespace.resolve(__dirname, "..", "..", "keys"),
6546
+ path.resolve(process.cwd(), "node_modules", "@veil-cash", "sdk", "keys"),
6548
6547
  // When running from source
6549
- path__namespace.resolve(process.cwd(), "keys")
6550
- // ESM module path
6548
+ path.resolve(process.cwd(), "keys")
6551
6549
  ];
6552
6550
  try {
6553
- const currentFilePath = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
6554
- const currentDir = path__namespace.dirname(currentFilePath);
6555
- possiblePaths.unshift(path__namespace.resolve(currentDir, "..", "keys"));
6551
+ const currentFilePath = fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
6552
+ const currentDir = path.dirname(currentFilePath);
6553
+ possiblePaths.unshift(path.resolve(currentDir, "..", "keys"));
6556
6554
  } catch {
6557
6555
  }
6558
6556
  for (const p of possiblePaths) {
6559
- if (fs__namespace.existsSync(p) && fs__namespace.existsSync(path__namespace.join(p, "transaction2.wasm"))) {
6557
+ if (existsSync2(p) && existsSync2(path.join(p, "transaction2.wasm"))) {
6560
6558
  return p;
6561
6559
  }
6562
6560
  }
@@ -6564,26 +6562,48 @@ function findKeysDirectory() {
6564
6562
  "Circuit keys not found. Expected to find keys/ directory with transaction2.wasm and transaction2.zkey files."
6565
6563
  );
6566
6564
  }
6567
- async function prove(input, circuitName) {
6568
- if (!utils) {
6569
- throw new Error("ffjavascript is required for proof generation. Please install it: npm install ffjavascript");
6570
- }
6571
- const keysDir = findKeysDirectory();
6572
- const wasmPath = path__namespace.join(keysDir, `${circuitName}.wasm`);
6573
- const zkeyPath = path__namespace.join(keysDir, `${circuitName}.zkey`);
6574
- if (!fs__namespace.existsSync(wasmPath)) {
6565
+ async function resolveProvingKeyPaths(circuitName, provingKeyPath) {
6566
+ if (provingKeyPath) {
6567
+ const circuitBasePath = normalizeCircuitBasePath(provingKeyPath, circuitName);
6568
+ return {
6569
+ wasmPath: `${circuitBasePath}.wasm`,
6570
+ zkeyPath: `${circuitBasePath}.zkey`
6571
+ };
6572
+ }
6573
+ if (isBrowserRuntime()) {
6574
+ return {
6575
+ wasmPath: `/keys/${circuitName}.wasm`,
6576
+ zkeyPath: `/keys/${circuitName}.zkey`
6577
+ };
6578
+ }
6579
+ const keysDir = await findNodeKeysDirectory();
6580
+ return {
6581
+ wasmPath: `${keysDir}/${circuitName}.wasm`,
6582
+ zkeyPath: `${keysDir}/${circuitName}.zkey`
6583
+ };
6584
+ }
6585
+ async function assertNodeKeyFilesExist(wasmPath, zkeyPath) {
6586
+ if (isBrowserRuntime() || wasmPath.startsWith("http://") || wasmPath.startsWith("https://")) {
6587
+ return;
6588
+ }
6589
+ const { existsSync: existsSync2 } = await importNodeModule("node:fs");
6590
+ if (!existsSync2(wasmPath)) {
6575
6591
  throw new Error(`Circuit WASM file not found: ${wasmPath}`);
6576
6592
  }
6577
- if (!fs__namespace.existsSync(zkeyPath)) {
6593
+ if (!existsSync2(zkeyPath)) {
6578
6594
  throw new Error(`Circuit zkey file not found: ${zkeyPath}`);
6579
6595
  }
6596
+ }
6597
+ async function prove(input, circuitName, options = {}) {
6598
+ const { wasmPath, zkeyPath } = await resolveProvingKeyPaths(circuitName, options.provingKeyPath);
6599
+ await assertNodeKeyFilesExist(wasmPath, zkeyPath);
6580
6600
  const result = await snarkjs.groth16.fullProve(
6581
- utils.stringifyBigInts(input),
6601
+ ffUtils.stringifyBigInts(input),
6582
6602
  wasmPath,
6583
6603
  zkeyPath,
6584
6604
  void 0,
6585
6605
  void 0,
6586
- { singleThread: true }
6606
+ { singleThread: options.singleThread ?? true }
6587
6607
  );
6588
6608
  const proof = result.proof;
6589
6609
  return "0x" + toFixedHex(proof.pi_a[0]).slice(2) + toFixedHex(proof.pi_a[1]).slice(2) + toFixedHex(proof.pi_b[0][1]).slice(2) + toFixedHex(proof.pi_b[0][0]).slice(2) + toFixedHex(proof.pi_b[1][1]).slice(2) + toFixedHex(proof.pi_b[1][0]).slice(2) + toFixedHex(proof.pi_c[0]).slice(2) + toFixedHex(proof.pi_c[1]).slice(2);
@@ -6607,7 +6627,8 @@ async function getProof({
6607
6627
  fee,
6608
6628
  recipient,
6609
6629
  relayer,
6610
- onProgress
6630
+ onProgress,
6631
+ provingKeyPath
6611
6632
  }) {
6612
6633
  inputs = shuffle([...inputs]);
6613
6634
  outputs = shuffle([...outputs]);
@@ -6667,7 +6688,7 @@ async function getProof({
6667
6688
  };
6668
6689
  onProgress?.("Generating ZK proof...", `${inputs.length} inputs`);
6669
6690
  const circuitName = selectCircuit(inputs.length);
6670
- const proof = await prove(proofInput, circuitName);
6691
+ const proof = await prove(proofInput, circuitName, { provingKeyPath });
6671
6692
  const args = {
6672
6693
  proof,
6673
6694
  root: toFixedHex(proofInput.root),
@@ -6689,7 +6710,8 @@ async function prepareTransaction({
6689
6710
  fee = 0,
6690
6711
  recipient = 0,
6691
6712
  relayer = 0,
6692
- onProgress
6713
+ onProgress,
6714
+ provingKeyPath
6693
6715
  }) {
6694
6716
  if (inputs.length > 16 || outputs.length > 2) {
6695
6717
  throw new Error("Incorrect inputs/outputs count. Maximum: 16 inputs, 2 outputs.");
@@ -6711,7 +6733,8 @@ async function prepareTransaction({
6711
6733
  fee: BigInt(fee),
6712
6734
  recipient: String(recipient),
6713
6735
  relayer: String(relayer),
6714
- onProgress
6736
+ onProgress,
6737
+ provingKeyPath
6715
6738
  });
6716
6739
  return result;
6717
6740
  }
@@ -6861,6 +6884,7 @@ async function buildWithdrawProof(options) {
6861
6884
  keypair,
6862
6885
  pool = "eth",
6863
6886
  rpcUrl,
6887
+ provingKeyPath,
6864
6888
  onProgress
6865
6889
  } = options;
6866
6890
  const poolConfig = POOL_CONFIG[pool];
@@ -6924,7 +6948,8 @@ async function buildWithdrawProof(options) {
6924
6948
  fee: 0,
6925
6949
  recipient,
6926
6950
  relayer: "0x0000000000000000000000000000000000000000",
6927
- onProgress
6951
+ onProgress,
6952
+ provingKeyPath
6928
6953
  });
6929
6954
  return {
6930
6955
  proofArgs: {
@@ -7085,6 +7110,7 @@ async function buildTransferProof(options) {
7085
7110
  senderKeypair,
7086
7111
  pool = "eth",
7087
7112
  rpcUrl,
7113
+ provingKeyPath,
7088
7114
  onProgress
7089
7115
  } = options;
7090
7116
  const poolConfig = POOL_CONFIG[pool];
@@ -7163,7 +7189,8 @@ async function buildTransferProof(options) {
7163
7189
  fee: 0,
7164
7190
  recipient: "0x0000000000000000000000000000000000000000",
7165
7191
  relayer: "0x0000000000000000000000000000000000000000",
7166
- onProgress
7192
+ onProgress,
7193
+ provingKeyPath
7167
7194
  });
7168
7195
  return {
7169
7196
  proofArgs: {
@@ -7205,7 +7232,7 @@ async function transfer(options) {
7205
7232
  };
7206
7233
  }
7207
7234
  async function mergeUtxos(options) {
7208
- const { amount, keypair, pool = "eth", rpcUrl, onProgress } = options;
7235
+ const { amount, keypair, pool = "eth", rpcUrl, provingKeyPath, onProgress } = options;
7209
7236
  const poolConfig = POOL_CONFIG[pool];
7210
7237
  const poolAddress = getPoolAddress(pool);
7211
7238
  onProgress?.("Fetching your UTXOs...");
@@ -7273,7 +7300,8 @@ async function mergeUtxos(options) {
7273
7300
  fee: 0,
7274
7301
  recipient: "0x0000000000000000000000000000000000000000",
7275
7302
  relayer: "0x0000000000000000000000000000000000000000",
7276
- onProgress
7303
+ onProgress,
7304
+ provingKeyPath
7277
7305
  });
7278
7306
  onProgress?.("Submitting to relay...");
7279
7307
  const relayResult = await submitRelay({
@@ -7964,6 +7992,7 @@ async function mergeSubaccount(options) {
7964
7992
  pool = "eth",
7965
7993
  rpcUrl,
7966
7994
  relayUrl,
7995
+ provingKeyPath,
7967
7996
  onProgress
7968
7997
  } = options;
7969
7998
  const normalizedSlot = normalizeSlot(slot);
@@ -8056,7 +8085,8 @@ async function mergeSubaccount(options) {
8056
8085
  fee: 0,
8057
8086
  recipient: "0x0000000000000000000000000000000000000000",
8058
8087
  relayer: "0x0000000000000000000000000000000000000000",
8059
- onProgress
8088
+ onProgress,
8089
+ provingKeyPath
8060
8090
  });
8061
8091
  onProgress?.("Submitting to relay...");
8062
8092
  const relayResult = await submitRelay({