@0xbow/privacy-pools-core-sdk 1.1.0 → 1.2.0

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.
Files changed (34) hide show
  1. package/README.md +102 -23
  2. package/dist/esm/{fetchArtifacts.esm-B6qveiM8.js → fetchArtifacts.esm-B0qaot8v.js} +2 -2
  3. package/dist/esm/{fetchArtifacts.esm-B6qveiM8.js.map → fetchArtifacts.esm-B0qaot8v.js.map} +1 -1
  4. package/dist/esm/{fetchArtifacts.node-BPQQPsnb.js → fetchArtifacts.node-PzijuwVc.js} +2 -2
  5. package/dist/esm/{fetchArtifacts.node-BPQQPsnb.js.map → fetchArtifacts.node-PzijuwVc.js.map} +1 -1
  6. package/dist/esm/{index-CRtEyHEf.js → index-BjOXETm6.js} +316 -316
  7. package/dist/esm/{index-CRtEyHEf.js.map → index-BjOXETm6.js.map} +1 -1
  8. package/dist/esm/index.mjs +1 -1
  9. package/dist/index.d.mts +81 -0
  10. package/dist/node/{fetchArtifacts.esm-z-KXbilc.js → fetchArtifacts.esm-B6uU6QdA.js} +2 -2
  11. package/dist/node/{fetchArtifacts.esm-z-KXbilc.js.map → fetchArtifacts.esm-B6uU6QdA.js.map} +1 -1
  12. package/dist/node/{fetchArtifacts.node-DvqhqpW9.js → fetchArtifacts.node-CZRy6KmV.js} +2 -2
  13. package/dist/node/{fetchArtifacts.node-DvqhqpW9.js.map → fetchArtifacts.node-CZRy6KmV.js.map} +1 -1
  14. package/dist/node/{index-BsmEKESv.js → index-b-U_m4Mi.js} +337 -337
  15. package/dist/node/{index-BsmEKESv.js.map → index-b-U_m4Mi.js.map} +1 -1
  16. package/dist/node/index.mjs +1 -1
  17. package/dist/types/circuits/artifactHashes.d.ts +19 -0
  18. package/dist/types/core/account.service.d.ts +79 -0
  19. package/dist/types/core/tmp.d.ts +1 -0
  20. package/dist/types/{fetchArtifacts.esm-DF01Zpo3.js → fetchArtifacts.esm-BKxGrC6w.js} +1 -1
  21. package/dist/types/{fetchArtifacts.node-BO6FBCAw.js → fetchArtifacts.node-kXMUDgNn.js} +1 -1
  22. package/dist/types/{index-CH7gk4sK.js → index-BwyNuaY0.js} +336 -336
  23. package/dist/types/index.js +1 -1
  24. package/dist/types/types/account.d.ts +2 -0
  25. package/package.json +1 -1
  26. package/src/circuits/artifactHashes.ts +74 -0
  27. package/src/circuits/circuits.impl.ts +8 -0
  28. package/src/core/account.service.ts +329 -35
  29. package/src/core/data.service.ts +3 -9
  30. package/src/core/tmp.ts +4 -0
  31. package/src/crypto.ts +5 -6
  32. package/src/types/account.ts +3 -1
  33. package/dist/types/keys.d.ts +0 -18
  34. package/src/keys.ts +0 -42
@@ -1,25 +1,25 @@
1
1
  import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
2
2
  import require$$0 from 'buffer';
3
3
  import require$$1 from 'http';
4
- import require$$1$1 from 'https';
5
- import require$$0$1 from 'zlib';
4
+ import require$$2 from 'https';
5
+ import require$$3$1 from 'zlib';
6
6
  import crypto$2 from 'crypto';
7
- import nc from 'node:crypto';
7
+ import require$$0$1 from 'node:crypto';
8
8
  import require$$0$3 from 'events';
9
- import require$$3$1 from 'net';
9
+ import require$$3$2 from 'net';
10
10
  import require$$4 from 'tls';
11
11
  import require$$0$2 from 'stream';
12
12
  import require$$7 from 'url';
13
- import require$$2 from 'assert';
14
- import { keccak256, encodeAbiParameters, numberToHex, createPublicClient, http, createWalletClient, getAddress, bytesToNumber as bytesToNumber$1, parseAbiItem } from 'viem';
13
+ import require$$2$1 from 'assert';
14
+ import { bytesToBigInt, keccak256, encodeAbiParameters, numberToHex, createPublicClient, http, createWalletClient, getAddress, bytesToNumber, parseAbiItem } from 'viem';
15
15
  import { mainnet } from 'viem/chains';
16
16
  import os from 'os';
17
- import require$$1$2 from 'vm';
18
- import require$$2$1 from 'worker_threads';
17
+ import require$$1$1 from 'vm';
18
+ import require$$2$2 from 'worker_threads';
19
19
  import fs from 'fs';
20
20
  import { O_TRUNC, O_CREAT, O_RDWR, O_RDONLY } from 'constants';
21
21
  import 'readline';
22
- import require$$1$3 from 'path';
22
+ import require$$1$2 from 'path';
23
23
 
24
24
  /**
25
25
  * Default log fetch configuration
@@ -33,281 +33,6 @@ const DEFAULT_LOG_FETCH_CONFIG = {
33
33
  retryBaseDelayMs: 1000,
34
34
  };
35
35
 
36
- const version$1 = '2.22.14';
37
-
38
- let errorConfig = {
39
- getDocsUrl: ({ docsBaseUrl, docsPath = '', docsSlug, }) => docsPath
40
- ? `${docsBaseUrl ?? 'https://viem.sh'}${docsPath}${docsSlug ? `#${docsSlug}` : ''}`
41
- : undefined,
42
- version: `viem@${version$1}`,
43
- };
44
- class BaseError extends Error {
45
- constructor(shortMessage, args = {}) {
46
- const details = (() => {
47
- if (args.cause instanceof BaseError)
48
- return args.cause.details;
49
- if (args.cause?.message)
50
- return args.cause.message;
51
- return args.details;
52
- })();
53
- const docsPath = (() => {
54
- if (args.cause instanceof BaseError)
55
- return args.cause.docsPath || args.docsPath;
56
- return args.docsPath;
57
- })();
58
- const docsUrl = errorConfig.getDocsUrl?.({ ...args, docsPath });
59
- const message = [
60
- shortMessage || 'An error occurred.',
61
- '',
62
- ...(args.metaMessages ? [...args.metaMessages, ''] : []),
63
- ...(docsUrl ? [`Docs: ${docsUrl}`] : []),
64
- ...(details ? [`Details: ${details}`] : []),
65
- ...(errorConfig.version ? [`Version: ${errorConfig.version}`] : []),
66
- ].join('\n');
67
- super(message, args.cause ? { cause: args.cause } : undefined);
68
- Object.defineProperty(this, "details", {
69
- enumerable: true,
70
- configurable: true,
71
- writable: true,
72
- value: undefined
73
- });
74
- Object.defineProperty(this, "docsPath", {
75
- enumerable: true,
76
- configurable: true,
77
- writable: true,
78
- value: undefined
79
- });
80
- Object.defineProperty(this, "metaMessages", {
81
- enumerable: true,
82
- configurable: true,
83
- writable: true,
84
- value: undefined
85
- });
86
- Object.defineProperty(this, "shortMessage", {
87
- enumerable: true,
88
- configurable: true,
89
- writable: true,
90
- value: undefined
91
- });
92
- Object.defineProperty(this, "version", {
93
- enumerable: true,
94
- configurable: true,
95
- writable: true,
96
- value: undefined
97
- });
98
- Object.defineProperty(this, "name", {
99
- enumerable: true,
100
- configurable: true,
101
- writable: true,
102
- value: 'BaseError'
103
- });
104
- this.details = details;
105
- this.docsPath = docsPath;
106
- this.metaMessages = args.metaMessages;
107
- this.name = args.name ?? this.name;
108
- this.shortMessage = shortMessage;
109
- this.version = version$1;
110
- }
111
- walk(fn) {
112
- return walk(this, fn);
113
- }
114
- }
115
- function walk(err, fn) {
116
- if (fn?.(err))
117
- return err;
118
- if (err &&
119
- typeof err === 'object' &&
120
- 'cause' in err &&
121
- err.cause !== undefined)
122
- return walk(err.cause, fn);
123
- return fn ? null : err;
124
- }
125
-
126
- class SizeOverflowError extends BaseError {
127
- constructor({ givenSize, maxSize }) {
128
- super(`Size cannot exceed ${maxSize} bytes. Given size: ${givenSize} bytes.`, { name: 'SizeOverflowError' });
129
- }
130
- }
131
-
132
- class SizeExceedsPaddingSizeError extends BaseError {
133
- constructor({ size, targetSize, type, }) {
134
- super(`${type.charAt(0).toUpperCase()}${type
135
- .slice(1)
136
- .toLowerCase()} size (${size}) exceeds padding size (${targetSize}).`, { name: 'SizeExceedsPaddingSizeError' });
137
- }
138
- }
139
-
140
- function pad(hexOrBytes, { dir, size = 32 } = {}) {
141
- if (typeof hexOrBytes === 'string')
142
- return padHex(hexOrBytes, { dir, size });
143
- return padBytes(hexOrBytes, { dir, size });
144
- }
145
- function padHex(hex_, { dir, size = 32 } = {}) {
146
- if (size === null)
147
- return hex_;
148
- const hex = hex_.replace('0x', '');
149
- if (hex.length > size * 2)
150
- throw new SizeExceedsPaddingSizeError({
151
- size: Math.ceil(hex.length / 2),
152
- targetSize: size,
153
- type: 'hex',
154
- });
155
- return `0x${hex[dir === 'right' ? 'padEnd' : 'padStart'](size * 2, '0')}`;
156
- }
157
- function padBytes(bytes, { dir, size = 32 } = {}) {
158
- if (size === null)
159
- return bytes;
160
- if (bytes.length > size)
161
- throw new SizeExceedsPaddingSizeError({
162
- size: bytes.length,
163
- targetSize: size,
164
- type: 'bytes',
165
- });
166
- const paddedBytes = new Uint8Array(size);
167
- for (let i = 0; i < size; i++) {
168
- const padEnd = dir === 'right';
169
- paddedBytes[padEnd ? i : size - i - 1] =
170
- bytes[padEnd ? i : bytes.length - i - 1];
171
- }
172
- return paddedBytes;
173
- }
174
-
175
- function isHex(value, { strict = true } = {}) {
176
- if (!value)
177
- return false;
178
- if (typeof value !== 'string')
179
- return false;
180
- return strict ? /^0x[0-9a-fA-F]*$/.test(value) : value.startsWith('0x');
181
- }
182
-
183
- /**
184
- * @description Retrieves the size of the value (in bytes).
185
- *
186
- * @param value The value (hex or byte array) to retrieve the size of.
187
- * @returns The size of the value (in bytes).
188
- */
189
- function size(value) {
190
- if (isHex(value, { strict: false }))
191
- return Math.ceil((value.length - 2) / 2);
192
- return value.length;
193
- }
194
-
195
- function assertSize(hexOrBytes, { size: size$1 }) {
196
- if (size(hexOrBytes) > size$1)
197
- throw new SizeOverflowError({
198
- givenSize: size(hexOrBytes),
199
- maxSize: size$1,
200
- });
201
- }
202
- /**
203
- * Decodes a hex value into a bigint.
204
- *
205
- * - Docs: https://viem.sh/docs/utilities/fromHex#hextobigint
206
- *
207
- * @param hex Hex value to decode.
208
- * @param opts Options.
209
- * @returns BigInt value.
210
- *
211
- * @example
212
- * import { hexToBigInt } from 'viem'
213
- * const data = hexToBigInt('0x1a4', { signed: true })
214
- * // 420n
215
- *
216
- * @example
217
- * import { hexToBigInt } from 'viem'
218
- * const data = hexToBigInt('0x00000000000000000000000000000000000000000000000000000000000001a4', { size: 32 })
219
- * // 420n
220
- */
221
- function hexToBigInt(hex, opts = {}) {
222
- const { signed } = opts;
223
- if (opts.size)
224
- assertSize(hex, { size: opts.size });
225
- const value = BigInt(hex);
226
- if (!signed)
227
- return value;
228
- const size = (hex.length - 2) / 2;
229
- const max = (1n << (BigInt(size) * 8n - 1n)) - 1n;
230
- if (value <= max)
231
- return value;
232
- return value - BigInt(`0x${'f'.padStart(size * 2, 'f')}`) - 1n;
233
- }
234
- /**
235
- * Decodes a hex string into a number.
236
- *
237
- * - Docs: https://viem.sh/docs/utilities/fromHex#hextonumber
238
- *
239
- * @param hex Hex value to decode.
240
- * @param opts Options.
241
- * @returns Number value.
242
- *
243
- * @example
244
- * import { hexToNumber } from 'viem'
245
- * const data = hexToNumber('0x1a4')
246
- * // 420
247
- *
248
- * @example
249
- * import { hexToNumber } from 'viem'
250
- * const data = hexToBigInt('0x00000000000000000000000000000000000000000000000000000000000001a4', { size: 32 })
251
- * // 420
252
- */
253
- function hexToNumber(hex, opts = {}) {
254
- return Number(hexToBigInt(hex, opts));
255
- }
256
-
257
- const hexes = /*#__PURE__*/ Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, '0'));
258
- /**
259
- * Encodes a bytes array into a hex string
260
- *
261
- * - Docs: https://viem.sh/docs/utilities/toHex#bytestohex
262
- *
263
- * @param value Value to encode.
264
- * @param opts Options.
265
- * @returns Hex value.
266
- *
267
- * @example
268
- * import { bytesToHex } from 'viem'
269
- * const data = bytesToHex(Uint8Array.from([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])
270
- * // '0x48656c6c6f20576f726c6421'
271
- *
272
- * @example
273
- * import { bytesToHex } from 'viem'
274
- * const data = bytesToHex(Uint8Array.from([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]), { size: 32 })
275
- * // '0x48656c6c6f20576f726c64210000000000000000000000000000000000000000'
276
- */
277
- function bytesToHex(value, opts = {}) {
278
- let string = '';
279
- for (let i = 0; i < value.length; i++) {
280
- string += hexes[value[i]];
281
- }
282
- const hex = `0x${string}`;
283
- if (typeof opts.size === 'number') {
284
- assertSize(hex, { size: opts.size });
285
- return pad(hex, { dir: 'right', size: opts.size });
286
- }
287
- return hex;
288
- }
289
-
290
- /**
291
- * Decodes a byte array into a number.
292
- *
293
- * - Docs: https://viem.sh/docs/utilities/fromBytes#bytestonumber
294
- *
295
- * @param bytes Byte array to decode.
296
- * @param opts Options.
297
- * @returns Number value.
298
- *
299
- * @example
300
- * import { bytesToNumber } from 'viem'
301
- * const data = bytesToNumber(new Uint8Array([1, 164]))
302
- * // 420
303
- */
304
- function bytesToNumber(bytes, opts = {}) {
305
- if (typeof opts.size !== 'undefined')
306
- assertSize(bytes, { size: opts.size });
307
- const hex = bytesToHex(bytes, opts);
308
- return hexToNumber(hex, opts);
309
- }
310
-
311
36
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
312
37
 
313
38
  function getDefaultExportFromCjs (x) {
@@ -16461,8 +16186,8 @@ function requireGeturl () {
16461
16186
  geturl.getUrl = geturl.createGetUrl = undefined;
16462
16187
  const tslib_1 = tslib_es6$1;
16463
16188
  const http_1 = tslib_1.__importDefault(require$$1);
16464
- const https_1 = tslib_1.__importDefault(require$$1$1);
16465
- const zlib_1 = require$$0$1;
16189
+ const https_1 = tslib_1.__importDefault(require$$2);
16190
+ const zlib_1 = require$$3$1;
16466
16191
  const errors_js_1 = /*@__PURE__*/ requireErrors();
16467
16192
  const data_js_1 = /*@__PURE__*/ requireData();
16468
16193
  /**
@@ -19099,8 +18824,8 @@ function requireCryptoNode () {
19099
18824
  // See utils.ts for details.
19100
18825
  // The file will throw on node.js 14 and earlier.
19101
18826
  // @ts-ignore
19102
- const nc$1 = nc;
19103
- cryptoNode.crypto = nc$1 && typeof nc$1 === 'object' && 'webcrypto' in nc$1 ? nc$1.webcrypto : undefined;
18827
+ const nc = require$$0$1;
18828
+ cryptoNode.crypto = nc && typeof nc === 'object' && 'webcrypto' in nc ? nc.webcrypto : undefined;
19104
18829
 
19105
18830
  return cryptoNode;
19106
18831
  }
@@ -39974,7 +39699,7 @@ function requirePermessageDeflate () {
39974
39699
  if (hasRequiredPermessageDeflate) return permessageDeflate;
39975
39700
  hasRequiredPermessageDeflate = 1;
39976
39701
 
39977
- const zlib = require$$0$1;
39702
+ const zlib = require$$3$1;
39978
39703
 
39979
39704
  const bufferUtil = requireBufferUtil();
39980
39705
  const Limiter = requireLimiter();
@@ -42364,9 +42089,9 @@ function requireWebsocket () {
42364
42089
  hasRequiredWebsocket = 1;
42365
42090
 
42366
42091
  const EventEmitter = require$$0$3;
42367
- const https = require$$1$1;
42092
+ const https = require$$2;
42368
42093
  const http = require$$1;
42369
- const net = require$$3$1;
42094
+ const net = require$$3$2;
42370
42095
  const tls = require$$4;
42371
42096
  const { randomBytes, createHash } = crypto$2;
42372
42097
  const { URL } = require$$7;
@@ -46456,7 +46181,7 @@ function requireProviderIpcsocket () {
46456
46181
  hasRequiredProviderIpcsocket = 1;
46457
46182
  Object.defineProperty(providerIpcsocket, "__esModule", { value: true });
46458
46183
  providerIpcsocket.IpcSocketProvider = undefined;
46459
- const net_1 = require$$3$1;
46184
+ const net_1 = require$$3$2;
46460
46185
  const provider_socket_js_1 = /*@__PURE__*/ requireProviderSocket();
46461
46186
  // @TODO: Is this sufficient? Is this robust? Will newlines occur between
46462
46187
  // all payloads and only between payloads?
@@ -50179,7 +49904,7 @@ function requireConstants () {
50179
49904
  exports.NOTHING_UP_MY_SLEEVE = exports.SNARK_FIELD_SIZE = undefined;
50180
49905
  const baby_jubjub_1 = requireDist$2();
50181
49906
  const ethers_1 = /*@__PURE__*/ requireLib_commonjs();
50182
- const assert_1 = __importDefault(require$$2);
49907
+ const assert_1 = __importDefault(require$$2$1);
50183
49908
  exports.SNARK_FIELD_SIZE = baby_jubjub_1.r;
50184
49909
  // A nothing-up-my-sleeve zero value
50185
49910
  // Should be equal to 8370432830353022751713833565135785980866757267633941821328460903436894336785
@@ -50203,7 +49928,7 @@ function requireHashing () {
50203
49928
  exports.hashOne = exports.hash12 = exports.hash5 = exports.hash4 = exports.hash3 = exports.hash2 = exports.hashN = exports.hashLeftRight = exports.poseidonT6 = exports.poseidonT5 = exports.poseidonT4 = exports.poseidonT3 = exports.poseidon = exports.sha256Hash = undefined;
50204
49929
  const poseidon_cipher_1 = requireDist$1();
50205
49930
  const ethers_1 = /*@__PURE__*/ requireLib_commonjs();
50206
- const assert_1 = __importDefault(require$$2);
49931
+ const assert_1 = __importDefault(require$$2$1);
50207
49932
  const constants_1 = requireConstants();
50208
49933
  /**
50209
49934
  * Hash an array of uint256 values the same way that the EVM does.
@@ -50862,10 +50587,10 @@ function generateMasterKeys(mnemonic) {
50862
50587
  if (!mnemonic) {
50863
50588
  throw new PrivacyPoolError(ErrorCode$1.INVALID_VALUE, "Invalid input: mnemonic phrase is required.");
50864
50589
  }
50865
- const key1 = bytesToNumber(mnemonicToAccount(mnemonic, { accountIndex: 0 }).getHdKey().privateKey);
50866
- const key2 = bytesToNumber(mnemonicToAccount(mnemonic, { accountIndex: 1 }).getHdKey().privateKey);
50867
- const masterNullifier = hashingExports.poseidon([BigInt(key1)]);
50868
- const masterSecret = hashingExports.poseidon([BigInt(key2)]);
50590
+ const key1 = bytesToBigInt(mnemonicToAccount(mnemonic, { accountIndex: 0 }).getHdKey().privateKey);
50591
+ const key2 = bytesToBigInt(mnemonicToAccount(mnemonic, { accountIndex: 1 }).getHdKey().privateKey);
50592
+ const masterNullifier = hashingExports.poseidon([key1]);
50593
+ const masterSecret = hashingExports.poseidon([key2]);
50869
50594
  return { masterNullifier, masterSecret };
50870
50595
  }
50871
50596
  /**
@@ -51082,10 +50807,62 @@ const circuitToAsset = {
51082
50807
 
51083
50808
  async function importFetchVersionedArtifact(isBrowser) {
51084
50809
  if (isBrowser) {
51085
- return import('./fetchArtifacts.esm-DF01Zpo3.js');
50810
+ return import('./fetchArtifacts.esm-BKxGrC6w.js');
51086
50811
  }
51087
50812
  else {
51088
- return import('./fetchArtifacts.node-BO6FBCAw.js');
50813
+ return import('./fetchArtifacts.node-kXMUDgNn.js');
50814
+ }
50815
+ }
50816
+
50817
+ /**
50818
+ * Expected SHA-256 hex digests for every downloaded circuit artifact.
50819
+ *
50820
+ * vkey and zkey hashes are derived from the trusted-setup ceremony outputs
50821
+ * committed in packages/circuits/trusted-setup/final-keys/.
50822
+ *
50823
+ * wasm hashes are derived from the compiled circuit outputs
50824
+ * in packages/circuits/build/.
50825
+ *
50826
+ * Every artifact downloaded by the SDK MUST have a hash entry here.
50827
+ * verifyArtifactIntegrity throws if a hash is missing — refusing to
50828
+ * load unverified artifacts is the correct security posture.
50829
+ */
50830
+ const ARTIFACT_HASHES = {
50831
+ [CircuitName$1.Commitment]: {
50832
+ wasm: "254d2130607182fd6fd1aee67971526b13cfe178c88e360da96dce92663828d8",
50833
+ vkey: "7d48b4eb3dedc12fb774348287b587f0c18c3c7254cd60e9cf0f8b3636a570d8",
50834
+ zkey: "494ae92d64098fda2a5649690ddc5821fcd7449ca5fe8ef99ee7447544d7e1f3",
50835
+ },
50836
+ [CircuitName$1.Withdraw]: {
50837
+ wasm: "36cda22791def3d520a55c0fc808369cd5849532a75fab65686e666ed3d55c10",
50838
+ vkey: "666bd0983b20c1611543b04f7712e067fbe8cad69f07ada8a310837ff398d21e",
50839
+ zkey: "2a893b42174c813566e5c40c715a8b90cd49fc4ecf384e3a6024158c3d6de677",
50840
+ },
50841
+ [CircuitName$1.MerkleTree]: {},
50842
+ };
50843
+ // Freeze the manifest so runtime code cannot swap out trusted hashes.
50844
+ for (const circuitHashes of Object.values(ARTIFACT_HASHES)) {
50845
+ if (circuitHashes != null) {
50846
+ Object.freeze(circuitHashes);
50847
+ }
50848
+ }
50849
+ Object.freeze(ARTIFACT_HASHES);
50850
+ async function sha256Hex(data) {
50851
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
50852
+ return Array.from(new Uint8Array(hashBuffer))
50853
+ .map((b) => b.toString(16).padStart(2, "0"))
50854
+ .join("");
50855
+ }
50856
+ async function verifyArtifactIntegrity(circuitName, artifactType, data) {
50857
+ const expectedHash = ARTIFACT_HASHES[circuitName]?.[artifactType];
50858
+ if (expectedHash === undefined) {
50859
+ throw new Error(`No integrity hash registered for ${circuitName}.${artifactType}. ` +
50860
+ `Refusing to load unverified artifact.`);
50861
+ }
50862
+ const actualHash = await sha256Hex(data);
50863
+ if (actualHash !== expectedHash) {
50864
+ throw new Error(`Integrity check failed for ${circuitName}.${artifactType}: ` +
50865
+ `expected ${expectedHash}, got ${actualHash}`);
51089
50866
  }
51090
50867
  }
51091
50868
 
@@ -51202,6 +50979,11 @@ class Circuits {
51202
50979
  this._fetchVersionedArtifact(["artifacts", assetName.vkey].join("/")),
51203
50980
  this._fetchVersionedArtifact(["artifacts", assetName.zkey].join("/")),
51204
50981
  ]);
50982
+ await Promise.all([
50983
+ verifyArtifactIntegrity(circuitName, "wasm", wasm),
50984
+ verifyArtifactIntegrity(circuitName, "vkey", vkey),
50985
+ verifyArtifactIntegrity(circuitName, "zkey", zkey),
50986
+ ]);
51205
50987
  return { wasm, vkey, zkey };
51206
50988
  }
51207
50989
  /**
@@ -67075,9 +66857,9 @@ function requireNode () {
67075
66857
  hasRequiredNode = 1;
67076
66858
  const URL = require$$7;
67077
66859
 
67078
- const VM = require$$1$2;
66860
+ const VM = require$$1$1;
67079
66861
 
67080
- const threads = require$$2$1;
66862
+ const threads = require$$2$2;
67081
66863
 
67082
66864
  const WORKER = Symbol.for('worker');
67083
66865
  const EVENTS = Symbol.for('events');
@@ -74540,7 +74322,7 @@ function requireEjs () {
74540
74322
 
74541
74323
 
74542
74324
  var fs$1 = fs;
74543
- var path = require$$1$3;
74325
+ var path = require$$1$2;
74544
74326
  var utils = requireUtils();
74545
74327
 
74546
74328
  var scopeOptionWarned = false;
@@ -79133,6 +78915,38 @@ class AccountService {
79133
78915
  this.account = config.account;
79134
78916
  }
79135
78917
  }
78918
+ /**
78919
+ * Initializes a new account from a mnemonic phrase for the legacy account.
78920
+ *
78921
+ * @param mnemonic - The mnemonic phrase to derive keys from
78922
+ * @returns A new PrivacyPoolAccount with derived master keys
78923
+ *
78924
+ * @remarks
78925
+ * This method derives two master keys from the mnemonic:
78926
+ * 1. A master nullifier key from account index 0
78927
+ * 2. A master secret key from account index 1
78928
+ * These keys are used to deterministically generate nullifiers and secrets for deposits and withdrawals.
78929
+ *
78930
+ * @throws {AccountError} If account initialization fails
78931
+ * @private
78932
+ */
78933
+ static _initializeLegacyAccount(mnemonic) {
78934
+ try {
78935
+ const masterNullifierSeed = bytesToNumber(mnemonicToAccount(mnemonic, { accountIndex: 0 }).getHdKey().privateKey);
78936
+ const masterSecretSeed = bytesToNumber(mnemonicToAccount(mnemonic, { accountIndex: 1 }).getHdKey().privateKey);
78937
+ const masterNullifier = hashingExports.poseidon([BigInt(masterNullifierSeed)]);
78938
+ const masterSecret = hashingExports.poseidon([BigInt(masterSecretSeed)]);
78939
+ return {
78940
+ masterKeys: [masterNullifier, masterSecret],
78941
+ poolAccounts: new Map(),
78942
+ creationTimestamp: 0n,
78943
+ lastUpdateTimestamp: 0n,
78944
+ };
78945
+ }
78946
+ catch (error) {
78947
+ throw AccountError.accountInitializationFailed(error instanceof Error ? error.message : "Unknown error");
78948
+ }
78949
+ }
79136
78950
  /**
79137
78951
  * Initializes a new account from a mnemonic phrase.
79138
78952
  *
@@ -79151,10 +78965,7 @@ class AccountService {
79151
78965
  _initializeAccount(mnemonic) {
79152
78966
  try {
79153
78967
  this.logger.debug("Initializing account with mnemonic");
79154
- const masterNullifierSeed = bytesToNumber$1(mnemonicToAccount(mnemonic, { accountIndex: 0 }).getHdKey().privateKey);
79155
- const masterSecretSeed = bytesToNumber$1(mnemonicToAccount(mnemonic, { accountIndex: 1 }).getHdKey().privateKey);
79156
- const masterNullifier = hashingExports.poseidon([BigInt(masterNullifierSeed)]);
79157
- const masterSecret = hashingExports.poseidon([BigInt(masterSecretSeed)]);
78968
+ const { masterNullifier, masterSecret } = generateMasterKeys(mnemonic);
79158
78969
  return {
79159
78970
  masterKeys: [masterNullifier, masterSecret],
79160
78971
  poolAccounts: new Map(),
@@ -79253,7 +79064,7 @@ class AccountService {
79253
79064
  const nonZeroCommitments = [];
79254
79065
  for (const account of accounts) {
79255
79066
  // Skip accounts that have been ragequit
79256
- if (account.ragequit) {
79067
+ if (account.ragequit || account.isMigrated) {
79257
79068
  continue;
79258
79069
  }
79259
79070
  const lastCommitment = account.children.length > 0
@@ -79409,6 +79220,57 @@ class AccountService {
79409
79220
  this.logger.info(`Added new commitment with value ${value} to account with label ${parentCommitment.label}`);
79410
79221
  return newCommitment;
79411
79222
  }
79223
+ /**
79224
+ * Adds a new commitment to the account after migrate
79225
+ *
79226
+ * @param parentCommitment - The commitment that was spent
79227
+ * @param value - The remaining value after spending
79228
+ * @param nullifier - The nullifier used for migrate
79229
+ * @param secret - The secret used for migrate
79230
+ * @param blockNumber - The block number of the withdrawal
79231
+ * @param txHash - The transaction hash of the withdrawal
79232
+ * @returns The new commitment
79233
+ *
79234
+ * @remarks
79235
+ * This method finds the account containing the parent commitment, creates a new
79236
+ * commitment with the provided parameters, and adds it to the account's children.
79237
+ * The new commitment inherits the label from the parent commitment.
79238
+ *
79239
+ * @throws {AccountError} If no account is found for the commitment
79240
+ */
79241
+ addMigrationCommitment(parentCommitment, value, nullifier, secret, blockNumber, txHash) {
79242
+ let foundAccount;
79243
+ let foundScope;
79244
+ for (const [scope, accounts] of this.account.poolAccounts.entries()) {
79245
+ foundAccount = accounts.find((account) => {
79246
+ if (account.deposit.hash === parentCommitment.hash)
79247
+ return true;
79248
+ return account.children.some((child) => child.hash === parentCommitment.hash);
79249
+ });
79250
+ if (foundAccount) {
79251
+ foundScope = scope;
79252
+ break;
79253
+ }
79254
+ }
79255
+ if (!foundAccount || !foundScope) {
79256
+ throw AccountError.commitmentNotFound(parentCommitment.hash);
79257
+ }
79258
+ const precommitment = this._hashPrecommitment(nullifier, secret);
79259
+ const newCommitment = {
79260
+ hash: this._hashCommitment(value, parentCommitment.label, precommitment),
79261
+ value,
79262
+ label: parentCommitment.label,
79263
+ nullifier,
79264
+ secret,
79265
+ blockNumber,
79266
+ txHash,
79267
+ isMigration: true
79268
+ };
79269
+ foundAccount.children.push(newCommitment);
79270
+ foundAccount.isMigrated = true;
79271
+ this.logger.info(`Added new commitment with value ${value} to account with label ${parentCommitment.label}`);
79272
+ return newCommitment;
79273
+ }
79412
79274
  /**
79413
79275
  * Adds a ragequit event to an existing pool account
79414
79276
  *
@@ -79560,9 +79422,11 @@ class AccountService {
79560
79422
  });
79561
79423
  }
79562
79424
  else {
79563
- events.set(result.reason.details?.scope, {
79425
+ const errorWithDetails = result.reason;
79426
+ const scope = errorWithDetails.details?.scope;
79427
+ events.set(scope, {
79564
79428
  reason: result.reason.message,
79565
- scope: result.reason.details?.scope,
79429
+ scope: scope,
79566
79430
  });
79567
79431
  }
79568
79432
  }
@@ -79576,11 +79440,11 @@ class AccountService {
79576
79440
  * @param depositEvents - The map of deposit events
79577
79441
  *
79578
79442
  */
79579
- _processDepositEvents(scope, depositEvents) {
79443
+ _processDepositEvents(scope, depositEvents, startIndex = 0n) {
79580
79444
  const MAX_CONSECUTIVE_MISSES = 10; // Large enough to avoid tx failures
79581
79445
  const foundIndices = new Set();
79582
79446
  let consecutiveMisses = 0;
79583
- for (let index = BigInt(0);; index++) {
79447
+ for (let index = startIndex;; index++) {
79584
79448
  // Generate nullifier, secret, and precommitment for this index
79585
79449
  const { nullifier, secret, precommitment } = this.createDepositSecrets(scope, index);
79586
79450
  // Look for a deposit with this precommitment
@@ -79631,8 +79495,8 @@ class AccountService {
79631
79495
  // Process each account in parallel for better performance
79632
79496
  for (const account of accounts) {
79633
79497
  let currentCommitment = account.deposit;
79634
- let index = BigInt(0);
79635
- // Continue processing withdrawals until no more are found secuentially
79498
+ let index = BigInt(account.children.length);
79499
+ // Continue processing withdrawals until no more are found sequentially
79636
79500
  while (true) {
79637
79501
  // Generate nullifier for this withdrawal
79638
79502
  const nullifierHash = hashingExports.poseidon([currentCommitment.nullifier]);
@@ -79641,13 +79505,26 @@ class AccountService {
79641
79505
  if (!withdrawal) {
79642
79506
  break;
79643
79507
  }
79508
+ const remainingValue = currentCommitment.value - withdrawal.withdrawn;
79644
79509
  // Generate secret for this withdrawal
79645
79510
  const nullifier = this._genWithdrawalNullifier(account.label, index);
79646
79511
  const secret = this._genWithdrawalSecret(account.label, index);
79647
- // Add the withdrawal commitment to the account
79648
- const newCommitment = this.addWithdrawalCommitment(currentCommitment, currentCommitment.value - withdrawal.withdrawn, nullifier, secret, withdrawal.blockNumber, withdrawal.transactionHash);
79649
- // Update current commitment to the newly created one
79650
- currentCommitment = newCommitment;
79512
+ const precommitment = this._hashPrecommitment(nullifier, secret);
79513
+ const accountCommitment = this._hashCommitment(remainingValue, currentCommitment.label, precommitment);
79514
+ // If the locally-computed hash doesn't match the on-chain commitment,
79515
+ // the withdrawal was performed with different keys (e.g. migration from
79516
+ // legacy to safe keys). Mark the child as unspendable from this account.
79517
+ if (accountCommitment !== withdrawal.newCommitment) {
79518
+ this.logger.info(`Withdrawal commitment hash mismatch — marking as unspendable (migrated with different keys)`, { label: currentCommitment.label, expected: withdrawal.newCommitment, computed: accountCommitment });
79519
+ // Add the withdrawal commitment to the account
79520
+ const migrationCommitment = this.addMigrationCommitment(currentCommitment, remainingValue, nullifier, secret, withdrawal.blockNumber, withdrawal.transactionHash);
79521
+ currentCommitment = migrationCommitment;
79522
+ }
79523
+ else {
79524
+ // Add the withdrawal commitment to the account
79525
+ const withdrawalCommitment = this.addWithdrawalCommitment(currentCommitment, remainingValue, nullifier, secret, withdrawal.blockNumber, withdrawal.transactionHash);
79526
+ currentCommitment = withdrawalCommitment;
79527
+ }
79651
79528
  // Increment index for next potential withdrawal
79652
79529
  index++;
79653
79530
  }
@@ -79682,6 +79559,59 @@ class AccountService {
79682
79559
  }
79683
79560
  }
79684
79561
  }
79562
+ /**
79563
+ * Discovers commitments that were migrated from legacy accounts via 0-value withdrawal.
79564
+ *
79565
+ * @param scope - The scope of the pool
79566
+ * @param legacyAccounts - The legacy pool accounts for this scope
79567
+ * @param withdrawalEvents - The map of withdrawal events (keyed by spentNullifier)
79568
+ *
79569
+ * @remarks
79570
+ * When a legacy account performs a 0-value withdrawal to rotate keys (migration),
79571
+ * the resulting on-chain commitment is created with safe keys. This method finds
79572
+ * those commitments by:
79573
+ * 1. Identifying legacy accounts with the `isMigrated` flag (set by `addMigrationCommitment`)
79574
+ * 2. Computing the expected commitment hash using safe keys at withdrawal index 0
79575
+ * 3. Verifying the hash exists in on-chain withdrawal events
79576
+ * 4. Adding verified commitments as new safe pool accounts
79577
+ *
79578
+ * @private
79579
+ */
79580
+ _discoverMigratedCommitments(scope, legacyAccounts, withdrawalEvents) {
79581
+ // Build reverse lookup: newCommitment hash → WithdrawalEvent
79582
+ const newCommitmentMap = new Map();
79583
+ for (const event of withdrawalEvents.values()) {
79584
+ newCommitmentMap.set(event.newCommitment, event);
79585
+ }
79586
+ for (const legacyAccount of legacyAccounts) {
79587
+ // Skip if not flagged as migrated (set by addMigrationCommitment)
79588
+ if (!legacyAccount.isMigrated)
79589
+ continue;
79590
+ const migrationChild = legacyAccount.children.find(c => c.isMigration);
79591
+ if (!migrationChild)
79592
+ continue;
79593
+ const label = legacyAccount.label;
79594
+ // The migration child's value is the remaining value carried forward.
79595
+ // Zero-value migrations (full withdrawal + key rotation) are valid and
79596
+ // must still be registered so that poolAccounts.length reflects the
79597
+ // correct slot count for deposit index alignment in step C.
79598
+ const remainingValue = migrationChild.value;
79599
+ // Generate safe nullifier/secret at withdrawal index 0
79600
+ const nullifier = this._genWithdrawalNullifier(label, 0n);
79601
+ const secret = this._genWithdrawalSecret(label, 0n);
79602
+ // Compute expected commitment hash
79603
+ const precommitment = this._hashPrecommitment(nullifier, secret);
79604
+ const expectedHash = this._hashCommitment(remainingValue, label, precommitment);
79605
+ // Verify hash exists in withdrawal events' newCommitment
79606
+ const withdrawalEvent = newCommitmentMap.get(expectedHash);
79607
+ if (!withdrawalEvent)
79608
+ continue;
79609
+ // Verified — add as a new safe pool account
79610
+ const newAccount = this.addPoolAccount(scope, remainingValue, nullifier, secret, label, withdrawalEvent.blockNumber, withdrawalEvent.transactionHash);
79611
+ this.addWithdrawalCommitment(newAccount.deposit, remainingValue, nullifier, secret, withdrawalEvent.blockNumber, withdrawalEvent.transactionHash);
79612
+ this.logger.info(`Discovered migrated commitment for label ${label} with value ${remainingValue}`);
79613
+ }
79614
+ }
79685
79615
  /**
79686
79616
  * Initializes an AccountService instance with events for a given set of pools
79687
79617
  *
@@ -79715,25 +79645,99 @@ class AccountService {
79715
79645
  }
79716
79646
  uniqueScopes.add(pool.scope);
79717
79647
  }
79648
+ // Retry path (non-migration): reuse the existing service's account and
79649
+ // only process pools whose scopes haven't been fully processed yet.
79650
+ // Already-processed scopes are skipped to avoid duplicate deposits and
79651
+ // withdrawal misclassification.
79652
+ //
79653
+ // This path performs simple deposit/withdrawal/ragequit processing only
79654
+ // — no migration discovery. For migration-aware retries, the caller
79655
+ // should re-invoke with { mnemonic } scoped to only the failed pools;
79656
+ // the mnemonic path builds both safe and legacy accounts from scratch
79657
+ // with no shared references.
79658
+ if (!('mnemonic' in source)) {
79659
+ const account = new AccountService(dataService, { account: source.service.account });
79660
+ const processedScopes = source.service.account.poolAccounts;
79661
+ const newPools = pools.filter((p) => !processedScopes.has(p.scope));
79662
+ const errors = await account._processEvents(newPools);
79663
+ return { account, errors };
79664
+ }
79665
+ // Mnemonic path: phased processing with migration discovery
79666
+ const account = new AccountService(dataService, { mnemonic: source.mnemonic });
79667
+ const legacyPrivacyPoolAccount = AccountService._initializeLegacyAccount(source.mnemonic);
79668
+ const legacyAccount = new AccountService(dataService, { account: legacyPrivacyPoolAccount });
79669
+ const errors = await account._processEvents(pools, legacyAccount);
79670
+ return { account, legacyAccount, errors };
79671
+ }
79672
+ /**
79673
+ * Fetches and processes events for a set of pools.
79674
+ *
79675
+ * When a legacyAccount is provided, the full migration-aware pipeline runs
79676
+ * for each scope:
79677
+ * 1. Legacy account: process deposits and withdrawals (to detect migrations)
79678
+ * 2. Safe account: discover migrated commitments from the legacy accounts
79679
+ * 3. Safe account (this): process deposits (starting after migrated accounts)
79680
+ * 4. Safe account: process withdrawals (now includes migrated accounts)
79681
+ * 5. Both accounts: process ragequits
79682
+ *
79683
+ * Migration discovery (step 2) must run before safe deposit scanning (step 3)
79684
+ * so that the migrated account count can be used as the starting index.
79685
+ * Post-migration deposits use poolAccounts.length as their index, which
79686
+ * sits right after the migrated slots; scanning from 0 would hit
79687
+ * MAX_CONSECUTIVE_MISSES on the legacy-key indices and never reach them.
79688
+ *
79689
+ * Without a legacyAccount, only steps 3, 4, and 5 run (simple processing).
79690
+ *
79691
+ * Per-scope errors are caught and returned rather than thrown, and any
79692
+ * partial state left by a mid-scope failure is cleaned from both accounts
79693
+ * so that a subsequent retry starts fresh for that scope.
79694
+ */
79695
+ async _processEvents(pools, legacyAccount) {
79718
79696
  const errors = [];
79719
- const account = new AccountService(dataService, "mnemonic" in source
79720
- ? { mnemonic: source.mnemonic }
79721
- : { account: source.service.account });
79722
- const events = await account.getEvents(pools);
79697
+ const events = await this.getEvents(pools);
79723
79698
  for (const [scope, result] of events.entries()) {
79724
79699
  if ("reason" in result) {
79725
79700
  errors.push(result);
79726
79701
  }
79727
79702
  else {
79728
- // Process deposit events an create pool accounts
79729
- account._processDepositEvents(scope, result.depositEvents);
79730
- // Process withdrawal events and add commitments to pool accounts
79731
- account._processWithdrawalEvents(scope, result.withdrawalEvents);
79732
- // Process ragequit events and add ragequit to pool accounts
79733
- account._processRagequitEvents(scope, result.ragequitEvents);
79703
+ try {
79704
+ // a. Legacy: process deposits + withdrawals
79705
+ if (legacyAccount) {
79706
+ legacyAccount._processDepositEvents(scope, result.depositEvents);
79707
+ legacyAccount._processWithdrawalEvents(scope, result.withdrawalEvents);
79708
+ }
79709
+ // b. Safe: discover migrated commitments from legacy accounts.
79710
+ // Must run before safe deposit scanning so that the migrated
79711
+ // account count can serve as the starting index for step (c),
79712
+ // avoiding a gap of consecutive misses over legacy-key indices.
79713
+ if (legacyAccount) {
79714
+ const legacyAccounts = legacyAccount.account.poolAccounts.get(scope) ?? [];
79715
+ this._discoverMigratedCommitments(scope, legacyAccounts, result.withdrawalEvents);
79716
+ }
79717
+ // c. Safe: process deposits, starting after any migrated accounts.
79718
+ // New deposits created after migration use poolAccounts.length as
79719
+ // their index, so they sit right after the migrated slots.
79720
+ const depositStartIndex = BigInt(this.account.poolAccounts.get(scope)?.length ?? 0);
79721
+ this._processDepositEvents(scope, result.depositEvents, depositStartIndex);
79722
+ // d. Safe: process withdrawals (now includes migrated accounts)
79723
+ this._processWithdrawalEvents(scope, result.withdrawalEvents);
79724
+ // e. Both: process ragequits
79725
+ if (legacyAccount) {
79726
+ legacyAccount._processRagequitEvents(scope, result.ragequitEvents);
79727
+ }
79728
+ this._processRagequitEvents(scope, result.ragequitEvents);
79729
+ }
79730
+ catch (e) {
79731
+ this.account.poolAccounts.delete(scope);
79732
+ legacyAccount?.account.poolAccounts.delete(scope);
79733
+ errors.push({
79734
+ reason: e instanceof Error ? e.message : String(e),
79735
+ scope,
79736
+ });
79737
+ }
79734
79738
  }
79735
79739
  }
79736
- return { account, errors };
79740
+ return errors;
79737
79741
  }
79738
79742
  /**
79739
79743
  * @deprecated Use `initializeWithEvents` for instantiating an account with history reconstruction
@@ -79992,7 +79996,7 @@ class DataService {
79992
79996
  depositor: depositor.toLowerCase(),
79993
79997
  commitment: commitment,
79994
79998
  label: label,
79995
- value: value || BigInt(0),
79999
+ value: value ?? BigInt(0),
79996
80000
  precommitment: precommitment,
79997
80001
  blockNumber: BigInt(typedLog.blockNumber),
79998
80002
  transactionHash: typedLog.transactionHash,
@@ -80044,11 +80048,7 @@ class DataService {
80044
80048
  throw DataError.invalidLog("withdrawal", "missing args");
80045
80049
  }
80046
80050
  const { _value: value, _spentNullifier: spentNullifier, _newCommitment: newCommitment, } = typedLog.args;
80047
- if (!value ||
80048
- !spentNullifier ||
80049
- !newCommitment ||
80050
- !typedLog.blockNumber ||
80051
- !typedLog.transactionHash) {
80051
+ if (value == null || !spentNullifier || !newCommitment || !typedLog.blockNumber || !typedLog.transactionHash) {
80052
80052
  throw DataError.invalidLog("withdrawal", "missing required fields");
80053
80053
  }
80054
80054
  return {
@@ -80116,7 +80116,7 @@ class DataService {
80116
80116
  ragequitter: ragequitter.toLowerCase(),
80117
80117
  commitment: commitment,
80118
80118
  label: label,
80119
- value: value || BigInt(0),
80119
+ value: value ?? BigInt(0),
80120
80120
  blockNumber: BigInt(typedLog.blockNumber),
80121
80121
  transactionHash: typedLog.transactionHash,
80122
80122
  };