@permissionless-technologies/upp-sdk 0.5.4 → 0.5.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.
Files changed (90) hide show
  1. package/dist/{chunk-Q6BLTPWV.js → chunk-2NKFTLPD.js} +3 -3
  2. package/dist/{chunk-Q6BLTPWV.js.map → chunk-2NKFTLPD.js.map} +1 -1
  3. package/dist/{chunk-EHGH6TAW.js → chunk-37RFFZU2.js} +3 -3
  4. package/dist/{chunk-EHGH6TAW.js.map → chunk-37RFFZU2.js.map} +1 -1
  5. package/dist/{chunk-UQIM2KT3.js → chunk-4E23V3AT.js} +29 -4
  6. package/dist/chunk-4E23V3AT.js.map +1 -0
  7. package/dist/{chunk-53JACDGZ.js → chunk-A6IYQ7UF.js} +3 -3
  8. package/dist/chunk-A6IYQ7UF.js.map +1 -0
  9. package/dist/{chunk-7T4CUE6E.js → chunk-AVSR443A.js} +3 -3
  10. package/dist/{chunk-7T4CUE6E.js.map → chunk-AVSR443A.js.map} +1 -1
  11. package/dist/{chunk-UFEDJJSH.cjs → chunk-BCSMUH4L.cjs} +30 -3
  12. package/dist/chunk-BCSMUH4L.cjs.map +1 -0
  13. package/dist/{chunk-IYRCJAME.cjs → chunk-C3HXJ5A6.cjs} +9 -9
  14. package/dist/{chunk-IYRCJAME.cjs.map → chunk-C3HXJ5A6.cjs.map} +1 -1
  15. package/dist/{chunk-DD4NT4D7.js → chunk-CRUJLZV7.js} +4 -3
  16. package/dist/chunk-CRUJLZV7.js.map +1 -0
  17. package/dist/{chunk-DTEAFJG7.js → chunk-FTEXUSHR.js} +4 -4
  18. package/dist/{chunk-DTEAFJG7.js.map → chunk-FTEXUSHR.js.map} +1 -1
  19. package/dist/chunk-FW2U6TKQ.js +498 -0
  20. package/dist/chunk-FW2U6TKQ.js.map +1 -0
  21. package/dist/{chunk-6TFDBBAQ.js → chunk-H4NDMIPF.js} +3 -3
  22. package/dist/{chunk-6TFDBBAQ.js.map → chunk-H4NDMIPF.js.map} +1 -1
  23. package/dist/{chunk-4T5DWZBN.cjs → chunk-HVSP62AH.cjs} +22 -36
  24. package/dist/chunk-HVSP62AH.cjs.map +1 -0
  25. package/dist/{chunk-7BNJV2ZS.cjs → chunk-LKXC3OQT.cjs} +14 -14
  26. package/dist/{chunk-7BNJV2ZS.cjs.map → chunk-LKXC3OQT.cjs.map} +1 -1
  27. package/dist/{chunk-HB43C26P.cjs → chunk-NGXEIUQ6.cjs} +4 -4
  28. package/dist/chunk-NGXEIUQ6.cjs.map +1 -0
  29. package/dist/{chunk-O2SKZZAP.cjs → chunk-P6E3LE7T.cjs} +4 -3
  30. package/dist/chunk-P6E3LE7T.cjs.map +1 -0
  31. package/dist/chunk-PGIV2GDM.cjs +511 -0
  32. package/dist/chunk-PGIV2GDM.cjs.map +1 -0
  33. package/dist/{chunk-4W7BBQ4H.js → chunk-UHMHZQZV.js} +6 -20
  34. package/dist/chunk-UHMHZQZV.js.map +1 -0
  35. package/dist/{chunk-I5EKGD4P.cjs → chunk-XNSMPNY6.cjs} +4 -4
  36. package/dist/{chunk-I5EKGD4P.cjs.map → chunk-XNSMPNY6.cjs.map} +1 -1
  37. package/dist/{chunk-U3YFYMWF.cjs → chunk-XSJ5VVH4.cjs} +5 -5
  38. package/dist/{chunk-U3YFYMWF.cjs.map → chunk-XSJ5VVH4.cjs.map} +1 -1
  39. package/dist/{chunk-SWTNJPK5.cjs → chunk-Y6WCXYOC.cjs} +11 -11
  40. package/dist/{chunk-SWTNJPK5.cjs.map → chunk-Y6WCXYOC.cjs.map} +1 -1
  41. package/dist/core/index.cjs +62 -66
  42. package/dist/core/index.d.cts +2 -5
  43. package/dist/core/index.d.ts +2 -5
  44. package/dist/core/index.js +6 -6
  45. package/dist/crypto-FWREDAVI.js +8 -0
  46. package/dist/crypto-FWREDAVI.js.map +1 -0
  47. package/dist/crypto-IZKHHFDU.cjs +42 -0
  48. package/dist/crypto-IZKHHFDU.cjs.map +1 -0
  49. package/dist/{index-BRgBwiBM.d.ts → index-DLvLv3mg.d.ts} +11 -23
  50. package/dist/{index-D6YhhbRP.d.cts → index-DOiHUft6.d.cts} +3 -4
  51. package/dist/{index-DHW5lKcd.d.ts → index-DY0XAmFw.d.ts} +3 -4
  52. package/dist/{index-brLSTa0y.d.cts → index-KTJgQUxb.d.cts} +11 -23
  53. package/dist/index.cjs +126 -126
  54. package/dist/index.d.cts +4 -7
  55. package/dist/index.d.ts +4 -7
  56. package/dist/index.js +9 -9
  57. package/dist/indexer/index.cjs +62 -11
  58. package/dist/indexer/index.d.cts +196 -12
  59. package/dist/indexer/index.d.ts +196 -12
  60. package/dist/indexer/index.js +4 -1
  61. package/dist/keys/index.cjs +17 -17
  62. package/dist/keys/index.js +3 -3
  63. package/dist/react/index.cjs +214 -690
  64. package/dist/react/index.cjs.map +1 -1
  65. package/dist/react/index.d.cts +138 -6
  66. package/dist/react/index.d.ts +138 -6
  67. package/dist/react/index.js +193 -669
  68. package/dist/react/index.js.map +1 -1
  69. package/dist/{transfer-CKA4PU2C.js → transfer-2UYFZMIK.js} +3 -3
  70. package/dist/{transfer-CKA4PU2C.js.map → transfer-2UYFZMIK.js.map} +1 -1
  71. package/dist/{transfer-QFTVCREE.cjs → transfer-6OW3XKVC.cjs} +9 -9
  72. package/dist/{transfer-QFTVCREE.cjs.map → transfer-6OW3XKVC.cjs.map} +1 -1
  73. package/dist/{transfer-p-NXHdGY.d.cts → transfer-C1XU_z-6.d.cts} +46 -156
  74. package/dist/{transfer-BxiDgCvx.d.ts → transfer-DgjxZlR7.d.ts} +46 -156
  75. package/dist/utils/index.cjs +44 -44
  76. package/dist/utils/index.d.cts +186 -5
  77. package/dist/utils/index.d.ts +186 -5
  78. package/dist/utils/index.js +4 -4
  79. package/package.json +1 -1
  80. package/src/deployments/31337.json +2 -1
  81. package/dist/chunk-4T5DWZBN.cjs.map +0 -1
  82. package/dist/chunk-4W7BBQ4H.js.map +0 -1
  83. package/dist/chunk-53JACDGZ.js.map +0 -1
  84. package/dist/chunk-DD4NT4D7.js.map +0 -1
  85. package/dist/chunk-HB43C26P.cjs.map +0 -1
  86. package/dist/chunk-O2SKZZAP.cjs.map +0 -1
  87. package/dist/chunk-UFEDJJSH.cjs.map +0 -1
  88. package/dist/chunk-UQIM2KT3.js.map +0 -1
  89. package/dist/stark-BcTD1OaJ.d.cts +0 -185
  90. package/dist/stark-BcTD1OaJ.d.ts +0 -185
@@ -0,0 +1,498 @@
1
+ import { init_crypto, hexToBytes, bytesToBigint } from './chunk-4E23V3AT.js';
2
+ import { keccak256, toHex, getAddress } from 'viem';
3
+
4
+ // src/indexer/events.ts
5
+ var SHIELDED_EVENT = {
6
+ type: "event",
7
+ name: "Shielded",
8
+ inputs: [
9
+ { name: "token", type: "address", indexed: true },
10
+ { name: "depositor", type: "address", indexed: true },
11
+ { name: "commitment", type: "bytes32", indexed: true },
12
+ { name: "leafIndex", type: "uint256", indexed: false },
13
+ { name: "encryptedNote", type: "bytes", indexed: false }
14
+ ]
15
+ };
16
+ var COMMITMENT_INSERTED_EVENT = {
17
+ type: "event",
18
+ name: "CommitmentInserted",
19
+ inputs: [
20
+ { name: "commitment", type: "bytes32", indexed: true },
21
+ { name: "leafIndex", type: "uint256", indexed: false },
22
+ { name: "timestamp", type: "uint256", indexed: false }
23
+ ]
24
+ };
25
+ var TRANSFERRED_EVENT = {
26
+ type: "event",
27
+ name: "Transferred",
28
+ inputs: [
29
+ { name: "nullifier", type: "bytes32", indexed: true },
30
+ { name: "outputCommitment1", type: "bytes32", indexed: true },
31
+ { name: "outputCommitment2", type: "bytes32", indexed: true },
32
+ { name: "encryptedNote1", type: "bytes", indexed: false },
33
+ { name: "encryptedNote2", type: "bytes", indexed: false }
34
+ ]
35
+ };
36
+ var SWAP_ORDER_FILLED_EVENT = {
37
+ type: "event",
38
+ name: "SwapOrderFilled",
39
+ inputs: [
40
+ { name: "orderId", type: "bytes32", indexed: true },
41
+ { name: "fillerNullifier", type: "bytes32", indexed: true },
42
+ { name: "fillerOutputCommitment", type: "bytes32", indexed: true },
43
+ { name: "takeAmount", type: "uint256", indexed: false },
44
+ { name: "giveAmount", type: "uint256", indexed: false },
45
+ { name: "fillerAspId", type: "uint256", indexed: false },
46
+ { name: "remainingSellAmount", type: "uint256", indexed: false },
47
+ { name: "encryptedFillerNote", type: "bytes", indexed: false }
48
+ ]
49
+ };
50
+ var SWAP_ORDER_CLAIMED_EVENT = {
51
+ type: "event",
52
+ name: "SwapOrderClaimed",
53
+ inputs: [
54
+ { name: "orderId", type: "bytes32", indexed: true },
55
+ { name: "accumulatedBuyAmount", type: "uint256", indexed: false },
56
+ { name: "refundedSellAmount", type: "uint256", indexed: false },
57
+ { name: "buyOutputCommitment", type: "bytes32", indexed: false },
58
+ { name: "encryptedBuyNote", type: "bytes", indexed: false },
59
+ { name: "refundCommitment", type: "bytes32", indexed: false },
60
+ { name: "encryptedRefundNote", type: "bytes", indexed: false }
61
+ ]
62
+ };
63
+ var SWAP_ORDER_CANCELLED_EVENT = {
64
+ type: "event",
65
+ name: "SwapOrderCancelled",
66
+ inputs: [
67
+ { name: "orderId", type: "bytes32", indexed: true },
68
+ { name: "refundedSellAmount", type: "uint256", indexed: false },
69
+ { name: "refundCommitment", type: "bytes32", indexed: false },
70
+ { name: "encryptedRefundNote", type: "bytes", indexed: false }
71
+ ]
72
+ };
73
+ var NULLIFIER_USED_ABI = [{
74
+ type: "function",
75
+ name: "nullifierUsed",
76
+ inputs: [{ name: "", type: "bytes32" }],
77
+ outputs: [{ name: "", type: "bool" }],
78
+ stateMutability: "view"
79
+ }];
80
+
81
+ // src/indexer/hash-decryption.ts
82
+ init_crypto();
83
+ async function deriveDecryptionKey(viewingSecret) {
84
+ const aesKeyHex = keccak256(toHex(viewingSecret, { size: 32 }));
85
+ const aesKeyBytes = hexToBytes(aesKeyHex);
86
+ return crypto.subtle.importKey(
87
+ "raw",
88
+ aesKeyBytes.buffer.slice(aesKeyBytes.byteOffset, aesKeyBytes.byteOffset + aesKeyBytes.byteLength),
89
+ { name: "AES-GCM", length: 256 },
90
+ false,
91
+ ["decrypt"]
92
+ );
93
+ }
94
+ async function tryDecryptHashBased(candidate, config, cryptoKey, poseidonFn) {
95
+ const { packed, raw } = unpackRaw(candidate.packedData);
96
+ if (!packed) return null;
97
+ const noteOwnerHash = BigInt("0x" + raw.slice(16, 80));
98
+ if (noteOwnerHash !== config.ownerHash) return null;
99
+ const encryptedPayload = raw.slice(80);
100
+ const encBytes = hexToBytes("0x" + encryptedPayload);
101
+ if (encBytes.length < 12) return null;
102
+ const nonce = encBytes.slice(0, 12);
103
+ const ciphertext = encBytes.slice(12);
104
+ let plaintext;
105
+ try {
106
+ plaintext = await crypto.subtle.decrypt(
107
+ { name: "AES-GCM", iv: nonce },
108
+ cryptoKey,
109
+ ciphertext
110
+ );
111
+ } catch {
112
+ return null;
113
+ }
114
+ const ptBytes = new Uint8Array(plaintext);
115
+ if (ptBytes.length !== 104) return null;
116
+ const amount = bytesToBigint(ptBytes.slice(0, 32));
117
+ const blinding = bytesToBigint(ptBytes.slice(32, 64));
118
+ const originRaw = "0x" + Array.from(ptBytes.slice(64, 84)).map((b) => b.toString(16).padStart(2, "0")).join("");
119
+ const tokenRaw = "0x" + Array.from(ptBytes.slice(84, 104)).map((b) => b.toString(16).padStart(2, "0")).join("");
120
+ const origin = getAddress(originRaw);
121
+ const token = getAddress(tokenRaw);
122
+ const ownerHash = await poseidonFn([config.spendingSecret]);
123
+ const expectedCommitment = await poseidonFn([
124
+ amount,
125
+ ownerHash,
126
+ blinding,
127
+ BigInt(origin),
128
+ BigInt(token)
129
+ ]);
130
+ const expectedHex = toHex(expectedCommitment, { size: 32 });
131
+ if (expectedHex.toLowerCase() !== candidate.commitment.toLowerCase()) {
132
+ return null;
133
+ }
134
+ return {
135
+ commitment: candidate.commitment,
136
+ amount,
137
+ blinding,
138
+ ownerSecret: toHex(config.spendingSecret, { size: 32 }),
139
+ ownerHash: toHex(config.ownerHash, { size: 32 }),
140
+ origin,
141
+ token,
142
+ leafIndex: candidate.leafIndex,
143
+ status: "confirmed",
144
+ timestamp: Math.floor(Date.now() / 1e3),
145
+ txHash: candidate.txHash,
146
+ proofSystem: "snark"
147
+ };
148
+ }
149
+ async function decryptCandidates(candidates, config, poseidonFn) {
150
+ if (candidates.length === 0) return [];
151
+ const cryptoKey = await deriveDecryptionKey(config.viewingSecret);
152
+ const discovered = [];
153
+ for (const candidate of candidates) {
154
+ try {
155
+ const note = await tryDecryptHashBased(candidate, config, cryptoKey, poseidonFn);
156
+ if (note) discovered.push(note);
157
+ } catch {
158
+ }
159
+ }
160
+ return discovered;
161
+ }
162
+ function unpackRaw(packedData) {
163
+ if (!packedData || packedData === "0x" || packedData.length < 82) {
164
+ return { packed: false, raw: null };
165
+ }
166
+ const raw = packedData.startsWith("0x") ? packedData.slice(2) : packedData;
167
+ return { packed: true, raw };
168
+ }
169
+ async function verifyNullifiers(noteStore, commitmentLeafMap, contractAddress, client, poseidonFn) {
170
+ let repaired = 0;
171
+ const currentNotes = noteStore.getNotes();
172
+ const snarkNotes = currentNotes.filter((n) => n.proofSystem !== "stark");
173
+ const unspentSnarkNotes = snarkNotes.filter((n) => n.status !== "spent");
174
+ for (const note of unspentSnarkNotes) {
175
+ const correctLeaf = commitmentLeafMap.get(note.commitment.toLowerCase());
176
+ if (correctLeaf === void 0) continue;
177
+ const isSpent = await checkNullifierOnChain(
178
+ note,
179
+ correctLeaf,
180
+ contractAddress,
181
+ client,
182
+ poseidonFn
183
+ );
184
+ if (isSpent) {
185
+ console.log(`[nullifier] Marking note ${note.commitment.slice(0, 12)} as spent \u2014 nullifier found on-chain`);
186
+ noteStore.markSpent(note.commitment);
187
+ repaired++;
188
+ }
189
+ }
190
+ const spentSnarkNotes = snarkNotes.filter((n) => n.status === "spent");
191
+ for (const note of spentSnarkNotes) {
192
+ const correctLeaf = commitmentLeafMap.get(note.commitment.toLowerCase());
193
+ if (correctLeaf === void 0) continue;
194
+ const isSpent = await checkNullifierOnChain(
195
+ note,
196
+ correctLeaf,
197
+ contractAddress,
198
+ client,
199
+ poseidonFn
200
+ );
201
+ if (!isSpent) {
202
+ console.log(`[nullifier] Un-spending note ${note.commitment.slice(0, 12)} \u2014 nullifier not used on-chain`);
203
+ noteStore.unmarkSpent(note.commitment);
204
+ noteStore.updateLeafIndex(note.commitment, correctLeaf);
205
+ repaired++;
206
+ }
207
+ }
208
+ return { repaired };
209
+ }
210
+ async function checkNullifierOnChain(note, leafIndex, contractAddress, client, poseidonFn) {
211
+ const ownerSecret = BigInt(note.ownerSecret);
212
+ const commitment = BigInt(note.commitment);
213
+ const nullifier = await poseidonFn([
214
+ ownerSecret,
215
+ BigInt(leafIndex),
216
+ commitment
217
+ ]);
218
+ const nullifierHex = toHex(nullifier, { size: 32 });
219
+ return client.readContract({
220
+ address: contractAddress,
221
+ abi: NULLIFIER_USED_ABI,
222
+ functionName: "nullifierUsed",
223
+ args: [nullifierHex]
224
+ });
225
+ }
226
+
227
+ // src/indexer/sync-engine.ts
228
+ function createSyncEngine(config) {
229
+ const {
230
+ client,
231
+ contractAddress,
232
+ swapModuleAddress,
233
+ noteStore,
234
+ poseidonFn
235
+ } = config;
236
+ const scanFromBlock = BigInt(config.fromBlock ?? 0);
237
+ const decryptionConfig = {
238
+ ownerHash: config.ownerHash,
239
+ spendingSecret: config.spendingSecret,
240
+ viewingSecret: config.viewingSecret
241
+ };
242
+ let liveSyncInterval = null;
243
+ let unwatchFn = null;
244
+ let syncInProgress = null;
245
+ async function sync() {
246
+ if (syncInProgress) return syncInProgress;
247
+ syncInProgress = doSync();
248
+ try {
249
+ return await syncInProgress;
250
+ } finally {
251
+ syncInProgress = null;
252
+ }
253
+ }
254
+ async function doSync() {
255
+ const existingCommitments = new Set(
256
+ noteStore.getNotes().map((n) => n.commitment.toLowerCase())
257
+ );
258
+ const candidates = [];
259
+ const commitmentLeafMap = /* @__PURE__ */ new Map();
260
+ await scanCommitmentInserted(commitmentLeafMap);
261
+ await scanShielded(candidates, existingCommitments);
262
+ await scanTransferred(candidates, existingCommitments, commitmentLeafMap);
263
+ if (swapModuleAddress) {
264
+ await scanSwapFilled(candidates, existingCommitments, commitmentLeafMap);
265
+ await scanSwapClaimed(candidates, existingCommitments, commitmentLeafMap);
266
+ await scanSwapCancelled(candidates, existingCommitments, commitmentLeafMap);
267
+ }
268
+ const discovered = await decryptCandidates(candidates, decryptionConfig, poseidonFn);
269
+ let addedCount = 0;
270
+ for (const note of discovered) {
271
+ if (noteStore.addNote(note)) addedCount++;
272
+ }
273
+ let repaired = 0;
274
+ for (const note of noteStore.getNotes()) {
275
+ const correctLeafIndex = commitmentLeafMap.get(note.commitment.toLowerCase());
276
+ if (correctLeafIndex !== void 0 && note.leafIndex !== correctLeafIndex) {
277
+ noteStore.updateLeafIndex(note.commitment, correctLeafIndex);
278
+ repaired++;
279
+ }
280
+ }
281
+ try {
282
+ const nullResult = await verifyNullifiers(
283
+ noteStore,
284
+ commitmentLeafMap,
285
+ contractAddress,
286
+ client,
287
+ poseidonFn
288
+ );
289
+ repaired += nullResult.repaired;
290
+ } catch (e) {
291
+ console.warn("[syncEngine] Nullifier verification failed:", e);
292
+ }
293
+ if (addedCount > 0 || repaired > 0) {
294
+ await noteStore.persist();
295
+ }
296
+ if (addedCount > 0 || candidates.length > 0) {
297
+ console.log(`[syncEngine] Discovered ${addedCount} new notes from ${candidates.length} candidates`);
298
+ }
299
+ if (repaired > 0) {
300
+ console.log(`[syncEngine] Repaired ${repaired} existing notes`);
301
+ }
302
+ return {
303
+ discovered: addedCount,
304
+ repaired,
305
+ candidatesScanned: candidates.length
306
+ };
307
+ }
308
+ async function scanCommitmentInserted(commitmentLeafMap) {
309
+ try {
310
+ const logs = await client.getLogs({
311
+ address: contractAddress,
312
+ event: COMMITMENT_INSERTED_EVENT,
313
+ fromBlock: scanFromBlock,
314
+ toBlock: "latest"
315
+ });
316
+ for (const log of logs) {
317
+ commitmentLeafMap.set(
318
+ log.args.commitment.toLowerCase(),
319
+ Number(log.args.leafIndex)
320
+ );
321
+ }
322
+ } catch (e) {
323
+ console.warn("[syncEngine] Failed to scan CommitmentInserted events:", e);
324
+ }
325
+ }
326
+ async function scanShielded(candidates, existingCommitments) {
327
+ try {
328
+ const logs = await client.getLogs({
329
+ address: contractAddress,
330
+ event: SHIELDED_EVENT,
331
+ fromBlock: scanFromBlock,
332
+ toBlock: "latest"
333
+ });
334
+ for (const log of logs) {
335
+ const commitment = log.args.commitment;
336
+ if (existingCommitments.has(commitment.toLowerCase())) continue;
337
+ candidates.push({
338
+ commitment,
339
+ leafIndex: Number(log.args.leafIndex),
340
+ packedData: log.args.encryptedNote,
341
+ txHash: log.transactionHash
342
+ });
343
+ }
344
+ } catch (e) {
345
+ console.warn("[syncEngine] Failed to scan Shielded events:", e?.message ?? e);
346
+ }
347
+ }
348
+ async function scanTransferred(candidates, existingCommitments, commitmentLeafMap) {
349
+ try {
350
+ const logs = await client.getLogs({
351
+ address: contractAddress,
352
+ event: TRANSFERRED_EVENT,
353
+ fromBlock: scanFromBlock,
354
+ toBlock: "latest"
355
+ });
356
+ for (const log of logs) {
357
+ const c1 = log.args.outputCommitment1;
358
+ const c2 = log.args.outputCommitment2;
359
+ const txHash = log.transactionHash;
360
+ if (!existingCommitments.has(c1.toLowerCase())) {
361
+ candidates.push({
362
+ commitment: c1,
363
+ leafIndex: commitmentLeafMap.get(c1.toLowerCase()) ?? -1,
364
+ packedData: log.args.encryptedNote1,
365
+ txHash
366
+ });
367
+ }
368
+ if (!existingCommitments.has(c2.toLowerCase())) {
369
+ candidates.push({
370
+ commitment: c2,
371
+ leafIndex: commitmentLeafMap.get(c2.toLowerCase()) ?? -1,
372
+ packedData: log.args.encryptedNote2,
373
+ txHash
374
+ });
375
+ }
376
+ }
377
+ } catch (e) {
378
+ console.warn("[syncEngine] Failed to scan Transferred events:", e);
379
+ }
380
+ }
381
+ async function scanSwapFilled(candidates, existingCommitments, commitmentLeafMap) {
382
+ try {
383
+ const logs = await client.getLogs({
384
+ address: swapModuleAddress,
385
+ event: SWAP_ORDER_FILLED_EVENT,
386
+ fromBlock: scanFromBlock,
387
+ toBlock: "latest"
388
+ });
389
+ for (const log of logs) {
390
+ const c = log.args.fillerOutputCommitment;
391
+ if (!existingCommitments.has(c.toLowerCase())) {
392
+ candidates.push({
393
+ commitment: c,
394
+ leafIndex: commitmentLeafMap.get(c.toLowerCase()) ?? -1,
395
+ packedData: log.args.encryptedFillerNote,
396
+ txHash: log.transactionHash
397
+ });
398
+ }
399
+ }
400
+ } catch (e) {
401
+ console.warn("[syncEngine] Failed to scan SwapOrderFilled events:", e);
402
+ }
403
+ }
404
+ async function scanSwapClaimed(candidates, existingCommitments, commitmentLeafMap) {
405
+ try {
406
+ const logs = await client.getLogs({
407
+ address: swapModuleAddress,
408
+ event: SWAP_ORDER_CLAIMED_EVENT,
409
+ fromBlock: scanFromBlock,
410
+ toBlock: "latest"
411
+ });
412
+ for (const log of logs) {
413
+ const buyC = log.args.buyOutputCommitment;
414
+ const refundC = log.args.refundCommitment;
415
+ const txHash = log.transactionHash;
416
+ if (buyC && !existingCommitments.has(buyC.toLowerCase())) {
417
+ candidates.push({
418
+ commitment: buyC,
419
+ leafIndex: commitmentLeafMap.get(buyC.toLowerCase()) ?? -1,
420
+ packedData: log.args.encryptedBuyNote,
421
+ txHash
422
+ });
423
+ }
424
+ if (refundC && !existingCommitments.has(refundC.toLowerCase())) {
425
+ candidates.push({
426
+ commitment: refundC,
427
+ leafIndex: commitmentLeafMap.get(refundC.toLowerCase()) ?? -1,
428
+ packedData: log.args.encryptedRefundNote,
429
+ txHash
430
+ });
431
+ }
432
+ }
433
+ } catch (e) {
434
+ console.warn("[syncEngine] Failed to scan SwapOrderClaimed events:", e);
435
+ }
436
+ }
437
+ async function scanSwapCancelled(candidates, existingCommitments, commitmentLeafMap) {
438
+ try {
439
+ const logs = await client.getLogs({
440
+ address: swapModuleAddress,
441
+ event: SWAP_ORDER_CANCELLED_EVENT,
442
+ fromBlock: scanFromBlock,
443
+ toBlock: "latest"
444
+ });
445
+ for (const log of logs) {
446
+ const c = log.args.refundCommitment;
447
+ if (c && !existingCommitments.has(c.toLowerCase())) {
448
+ candidates.push({
449
+ commitment: c,
450
+ leafIndex: commitmentLeafMap.get(c.toLowerCase()) ?? -1,
451
+ packedData: log.args.encryptedRefundNote,
452
+ txHash: log.transactionHash
453
+ });
454
+ }
455
+ }
456
+ } catch (e) {
457
+ console.warn("[syncEngine] Failed to scan SwapOrderCancelled events:", e);
458
+ }
459
+ }
460
+ function startLiveSync(liveConfig) {
461
+ stopLiveSync();
462
+ sync().catch(console.error);
463
+ try {
464
+ const unwatch = client.watchContractEvent({
465
+ address: contractAddress,
466
+ eventName: "CommitmentInserted",
467
+ onLogs: () => {
468
+ sync().catch(console.error);
469
+ }
470
+ });
471
+ unwatchFn = unwatch;
472
+ } catch (e) {
473
+ console.warn("[syncEngine] watchContractEvent not available, using polling only:", e);
474
+ }
475
+ const intervalMs = liveConfig?.intervalMs ?? 6e4;
476
+ liveSyncInterval = setInterval(() => {
477
+ sync().catch(console.error);
478
+ }, intervalMs);
479
+ }
480
+ function stopLiveSync() {
481
+ if (liveSyncInterval) {
482
+ clearInterval(liveSyncInterval);
483
+ liveSyncInterval = null;
484
+ }
485
+ if (unwatchFn) {
486
+ unwatchFn();
487
+ unwatchFn = null;
488
+ }
489
+ }
490
+ function isLiveSyncing() {
491
+ return liveSyncInterval !== null;
492
+ }
493
+ return { sync, startLiveSync, stopLiveSync, isLiveSyncing };
494
+ }
495
+
496
+ export { COMMITMENT_INSERTED_EVENT, NULLIFIER_USED_ABI, SHIELDED_EVENT, SWAP_ORDER_CANCELLED_EVENT, SWAP_ORDER_CLAIMED_EVENT, SWAP_ORDER_FILLED_EVENT, TRANSFERRED_EVENT, createSyncEngine, decryptCandidates, deriveDecryptionKey, tryDecryptHashBased, verifyNullifiers };
497
+ //# sourceMappingURL=chunk-FW2U6TKQ.js.map
498
+ //# sourceMappingURL=chunk-FW2U6TKQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/indexer/events.ts","../src/indexer/hash-decryption.ts","../src/indexer/nullifier-verifier.ts","../src/indexer/sync-engine.ts"],"names":["toHex"],"mappings":"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","file":"chunk-FW2U6TKQ.js","sourcesContent":["/**\n * Centralized event ABI definitions for UPP contracts\n *\n * Single source of truth for all on-chain event schemas used by the sync engine.\n */\n\nexport const SHIELDED_EVENT = {\n type: 'event' as const,\n name: 'Shielded',\n inputs: [\n { name: 'token', type: 'address', indexed: true },\n { name: 'depositor', type: 'address', indexed: true },\n { name: 'commitment', type: 'bytes32', indexed: true },\n { name: 'leafIndex', type: 'uint256', indexed: false },\n { name: 'encryptedNote', type: 'bytes', indexed: false },\n ],\n}\n\nexport const COMMITMENT_INSERTED_EVENT = {\n type: 'event' as const,\n name: 'CommitmentInserted',\n inputs: [\n { name: 'commitment', type: 'bytes32', indexed: true },\n { name: 'leafIndex', type: 'uint256', indexed: false },\n { name: 'timestamp', type: 'uint256', indexed: false },\n ],\n}\n\nexport const TRANSFERRED_EVENT = {\n type: 'event' as const,\n name: 'Transferred',\n inputs: [\n { name: 'nullifier', type: 'bytes32', indexed: true },\n { name: 'outputCommitment1', type: 'bytes32', indexed: true },\n { name: 'outputCommitment2', type: 'bytes32', indexed: true },\n { name: 'encryptedNote1', type: 'bytes', indexed: false },\n { name: 'encryptedNote2', type: 'bytes', indexed: false },\n ],\n}\n\nexport const SWAP_ORDER_FILLED_EVENT = {\n type: 'event' as const,\n name: 'SwapOrderFilled',\n inputs: [\n { name: 'orderId', type: 'bytes32', indexed: true },\n { name: 'fillerNullifier', type: 'bytes32', indexed: true },\n { name: 'fillerOutputCommitment', type: 'bytes32', indexed: true },\n { name: 'takeAmount', type: 'uint256', indexed: false },\n { name: 'giveAmount', type: 'uint256', indexed: false },\n { name: 'fillerAspId', type: 'uint256', indexed: false },\n { name: 'remainingSellAmount', type: 'uint256', indexed: false },\n { name: 'encryptedFillerNote', type: 'bytes', indexed: false },\n ],\n}\n\nexport const SWAP_ORDER_CLAIMED_EVENT = {\n type: 'event' as const,\n name: 'SwapOrderClaimed',\n inputs: [\n { name: 'orderId', type: 'bytes32', indexed: true },\n { name: 'accumulatedBuyAmount', type: 'uint256', indexed: false },\n { name: 'refundedSellAmount', type: 'uint256', indexed: false },\n { name: 'buyOutputCommitment', type: 'bytes32', indexed: false },\n { name: 'encryptedBuyNote', type: 'bytes', indexed: false },\n { name: 'refundCommitment', type: 'bytes32', indexed: false },\n { name: 'encryptedRefundNote', type: 'bytes', indexed: false },\n ],\n}\n\nexport const SWAP_ORDER_CANCELLED_EVENT = {\n type: 'event' as const,\n name: 'SwapOrderCancelled',\n inputs: [\n { name: 'orderId', type: 'bytes32', indexed: true },\n { name: 'refundedSellAmount', type: 'uint256', indexed: false },\n { name: 'refundCommitment', type: 'bytes32', indexed: false },\n { name: 'encryptedRefundNote', type: 'bytes', indexed: false },\n ],\n}\n\nexport const NULLIFIER_USED_ABI = [{\n type: 'function' as const,\n name: 'nullifierUsed',\n inputs: [{ name: '', type: 'bytes32' }],\n outputs: [{ name: '', type: 'bool' }],\n stateMutability: 'view' as const,\n}]\n","/**\n * Hash-based note decryption for UPP\n *\n * Decrypts notes encrypted with the hash-based ownership scheme:\n * 1. Fast ownerHash match (skip notes not belonging to us)\n * 2. AES-GCM decryption with keccak256(viewingSecret) as key\n * 3. Commitment verification: Poseidon(amount, ownerHash, blinding, origin, token)\n *\n * This is the correct decryption path for the current protocol (no ECDH).\n */\n\nimport { keccak256, toHex, getAddress } from 'viem'\nimport { hexToBytes, bytesToBigint } from '../utils/crypto.js'\nimport type { ShieldedNote } from '../core/types.js'\n\n/** A candidate note to attempt decryption on */\nexport interface NoteCandidate {\n commitment: string\n leafIndex: number\n packedData: string\n txHash?: string\n}\n\n/** Configuration for the decryption engine */\nexport interface HashDecryptionConfig {\n ownerHash: bigint\n spendingSecret: bigint\n viewingSecret: bigint\n}\n\n/**\n * Derive an AES-GCM CryptoKey from the viewing secret.\n * Reuse this key across all decryption attempts in a sync cycle.\n */\nexport async function deriveDecryptionKey(viewingSecret: bigint): Promise<CryptoKey> {\n const aesKeyHex = keccak256(toHex(viewingSecret, { size: 32 }))\n const aesKeyBytes = hexToBytes(aesKeyHex)\n return crypto.subtle.importKey(\n 'raw',\n aesKeyBytes.buffer.slice(aesKeyBytes.byteOffset, aesKeyBytes.byteOffset + aesKeyBytes.byteLength) as ArrayBuffer,\n { name: 'AES-GCM', length: 256 },\n false,\n ['decrypt']\n )\n}\n\n/**\n * Try to decrypt a single packed note using hash-based ownership.\n *\n * Packed format: searchTag (8 bytes) + ownerHash (32 bytes) + AES-GCM encrypted payload\n * Returns a ShieldedNote if decryption and verification succeed, null otherwise.\n */\nexport async function tryDecryptHashBased(\n candidate: NoteCandidate,\n config: HashDecryptionConfig,\n cryptoKey: CryptoKey,\n poseidonFn: (inputs: bigint[]) => Promise<bigint>,\n): Promise<ShieldedNote | null> {\n const { packed, raw } = unpackRaw(candidate.packedData)\n if (!packed) return null\n\n // Fast path: ownerHash match\n const noteOwnerHash = BigInt('0x' + raw!.slice(16, 80))\n if (noteOwnerHash !== config.ownerHash) return null\n\n // Decrypt AES-GCM payload\n const encryptedPayload = raw!.slice(80)\n const encBytes = hexToBytes('0x' + encryptedPayload)\n if (encBytes.length < 12) return null\n\n const nonce = encBytes.slice(0, 12)\n const ciphertext = encBytes.slice(12)\n\n let plaintext: ArrayBuffer\n try {\n plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: nonce },\n cryptoKey,\n ciphertext\n )\n } catch {\n return null // decryption failed — not our note\n }\n\n // Parse: amount (32) + blinding (32) + origin (20) + token (20) = 104 bytes\n const ptBytes = new Uint8Array(plaintext)\n if (ptBytes.length !== 104) return null\n\n const amount = bytesToBigint(ptBytes.slice(0, 32))\n const blinding = bytesToBigint(ptBytes.slice(32, 64))\n const originRaw = '0x' + Array.from(ptBytes.slice(64, 84)).map(b => b.toString(16).padStart(2, '0')).join('')\n const tokenRaw = '0x' + Array.from(ptBytes.slice(84, 104)).map(b => b.toString(16).padStart(2, '0')).join('')\n const origin = getAddress(originRaw)\n const token = getAddress(tokenRaw)\n\n // Verify commitment: Poseidon(amount, ownerHash, blinding, origin, token)\n const ownerHash = await poseidonFn([config.spendingSecret])\n const expectedCommitment = await poseidonFn([\n amount,\n ownerHash,\n blinding,\n BigInt(origin),\n BigInt(token),\n ])\n const expectedHex = toHex(expectedCommitment, { size: 32 })\n\n if (expectedHex.toLowerCase() !== candidate.commitment.toLowerCase()) {\n return null\n }\n\n return {\n commitment: candidate.commitment,\n amount,\n blinding,\n ownerSecret: toHex(config.spendingSecret, { size: 32 }),\n ownerHash: toHex(config.ownerHash, { size: 32 }),\n origin,\n token,\n leafIndex: candidate.leafIndex,\n status: 'confirmed',\n timestamp: Math.floor(Date.now() / 1000),\n txHash: candidate.txHash,\n proofSystem: 'snark',\n }\n}\n\n/**\n * Batch decrypt candidates. Returns discovered notes.\n */\nexport async function decryptCandidates(\n candidates: NoteCandidate[],\n config: HashDecryptionConfig,\n poseidonFn: (inputs: bigint[]) => Promise<bigint>,\n): Promise<ShieldedNote[]> {\n if (candidates.length === 0) return []\n\n const cryptoKey = await deriveDecryptionKey(config.viewingSecret)\n const discovered: ShieldedNote[] = []\n\n for (const candidate of candidates) {\n try {\n const note = await tryDecryptHashBased(candidate, config, cryptoKey, poseidonFn)\n if (note) discovered.push(note)\n } catch {\n // Skip failed candidates\n }\n }\n\n return discovered\n}\n\n// Internal helper\nfunction unpackRaw(packedData: string): { packed: boolean; raw: string | null } {\n if (!packedData || packedData === '0x' || packedData.length < 82) {\n return { packed: false, raw: null }\n }\n const raw = packedData.startsWith('0x') ? packedData.slice(2) : packedData\n return { packed: true, raw }\n}\n","/**\n * On-chain nullifier verification\n *\n * Reconciles local note state against on-chain nullifier status:\n * - Forward check: unspent notes whose nullifier IS on-chain → mark spent\n * - Reverse check: spent notes whose nullifier NOT on-chain → unmark spent\n */\n\nimport { toHex, type Address } from 'viem'\nimport { NULLIFIER_USED_ABI } from './events.js'\nimport type { ShieldedNote } from '../core/types.js'\nimport type { INoteStore } from '../core/note-store.js'\n\nexport interface NullifierVerificationResult {\n /** Number of notes whose status was corrected */\n repaired: number\n}\n\n/**\n * Verify note spent/unspent status against on-chain nullifiers.\n *\n * Only checks SNARK notes (STARK nullifiers use a different scheme).\n */\nexport async function verifyNullifiers(\n noteStore: INoteStore,\n commitmentLeafMap: Map<string, number>,\n contractAddress: Address,\n client: any,\n poseidonFn: (inputs: bigint[]) => Promise<bigint>,\n): Promise<NullifierVerificationResult> {\n let repaired = 0\n const currentNotes = noteStore.getNotes()\n const snarkNotes = currentNotes.filter(n => n.proofSystem !== 'stark')\n\n // Forward check: detect notes spent on-chain but not locally marked\n const unspentSnarkNotes = snarkNotes.filter(n => n.status !== 'spent')\n for (const note of unspentSnarkNotes) {\n const correctLeaf = commitmentLeafMap.get(note.commitment.toLowerCase())\n if (correctLeaf === undefined) continue\n\n const isSpent = await checkNullifierOnChain(\n note, correctLeaf, contractAddress, client, poseidonFn\n )\n if (isSpent) {\n console.log(`[nullifier] Marking note ${note.commitment.slice(0, 12)} as spent — nullifier found on-chain`)\n noteStore.markSpent(note.commitment)\n repaired++\n }\n }\n\n // Reverse check: un-spend notes whose nullifier is NOT on-chain\n const spentSnarkNotes = snarkNotes.filter(n => n.status === 'spent')\n for (const note of spentSnarkNotes) {\n const correctLeaf = commitmentLeafMap.get(note.commitment.toLowerCase())\n if (correctLeaf === undefined) continue\n\n const isSpent = await checkNullifierOnChain(\n note, correctLeaf, contractAddress, client, poseidonFn\n )\n if (!isSpent) {\n console.log(`[nullifier] Un-spending note ${note.commitment.slice(0, 12)} — nullifier not used on-chain`)\n noteStore.unmarkSpent(note.commitment)\n noteStore.updateLeafIndex(note.commitment, correctLeaf)\n repaired++\n }\n }\n\n return { repaired }\n}\n\n/**\n * Check whether a note's nullifier has been used on-chain.\n */\nasync function checkNullifierOnChain(\n note: ShieldedNote,\n leafIndex: number,\n contractAddress: Address,\n client: any,\n poseidonFn: (inputs: bigint[]) => Promise<bigint>,\n): Promise<boolean> {\n const ownerSecret = BigInt(note.ownerSecret)\n const commitment = BigInt(note.commitment)\n const nullifier = await poseidonFn([\n ownerSecret,\n BigInt(leafIndex),\n commitment,\n ])\n const nullifierHex = toHex(nullifier, { size: 32 })\n\n return client.readContract({\n address: contractAddress,\n abi: NULLIFIER_USED_ABI,\n functionName: 'nullifierUsed',\n args: [nullifierHex],\n })\n}\n","/**\n * Unified Sync Engine — framework-agnostic note discovery\n *\n * Scans blockchain events, decrypts notes, reconciles nullifiers,\n * and writes directly to NoteStore. No React dependency.\n *\n * Replaces the ~530-line inline syncNotes callback from use-upp-account.tsx\n * with a testable, reusable engine.\n */\n\nimport type { Address } from 'viem'\nimport type { INoteStore } from '../core/note-store.js'\nimport {\n SHIELDED_EVENT,\n COMMITMENT_INSERTED_EVENT,\n TRANSFERRED_EVENT,\n SWAP_ORDER_FILLED_EVENT,\n SWAP_ORDER_CLAIMED_EVENT,\n SWAP_ORDER_CANCELLED_EVENT,\n} from './events.js'\nimport {\n type NoteCandidate,\n type HashDecryptionConfig,\n decryptCandidates,\n} from './hash-decryption.js'\nimport { verifyNullifiers } from './nullifier-verifier.js'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SyncEngineConfig {\n /** viem PublicClient (or compatible) */\n client: any\n /** Pool contract address */\n contractAddress: Address\n /** Optional swap module address (swap events come from here) */\n swapModuleAddress?: Address\n /** Owner hash for note ownership check */\n ownerHash: bigint\n /** Spending secret for commitment verification */\n spendingSecret: bigint\n /** Viewing secret for AES-GCM decryption */\n viewingSecret: bigint\n /** NoteStore to write discovered notes into */\n noteStore: INoteStore\n /** Block to start scanning from (default: 0) */\n fromBlock?: number\n /** Poseidon hash function */\n poseidonFn: (inputs: bigint[]) => Promise<bigint>\n}\n\nexport interface SyncResult {\n /** Number of newly discovered notes */\n discovered: number\n /** Number of notes with corrected leafIndex or status */\n repaired: number\n /** Total candidates scanned */\n candidatesScanned: number\n}\n\nexport interface LiveSyncConfig {\n /** Polling interval in ms (default: 60000) */\n intervalMs?: number\n}\n\nexport interface SyncEngine {\n /** Run a full sync cycle */\n sync(): Promise<SyncResult>\n /** Start live sync (poll + optional WebSocket watch) */\n startLiveSync(config?: LiveSyncConfig): void\n /** Stop live sync */\n stopLiveSync(): void\n /** Whether live sync is currently active */\n isLiveSyncing(): boolean\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nexport function createSyncEngine(config: SyncEngineConfig): SyncEngine {\n const {\n client,\n contractAddress,\n swapModuleAddress,\n noteStore,\n poseidonFn,\n } = config\n const scanFromBlock = BigInt(config.fromBlock ?? 0)\n const decryptionConfig: HashDecryptionConfig = {\n ownerHash: config.ownerHash,\n spendingSecret: config.spendingSecret,\n viewingSecret: config.viewingSecret,\n }\n\n let liveSyncInterval: ReturnType<typeof setInterval> | null = null\n let unwatchFn: (() => void) | null = null\n let syncInProgress: Promise<SyncResult> | null = null\n\n async function sync(): Promise<SyncResult> {\n // Prevent concurrent syncs\n if (syncInProgress) return syncInProgress\n\n syncInProgress = doSync()\n try {\n return await syncInProgress\n } finally {\n syncInProgress = null\n }\n }\n\n async function doSync(): Promise<SyncResult> {\n const existingCommitments = new Set(\n noteStore.getNotes().map(n => n.commitment.toLowerCase())\n )\n\n const candidates: NoteCandidate[] = []\n const commitmentLeafMap = new Map<string, number>()\n\n // 1. Scan CommitmentInserted events (needed for leafIndex mapping)\n await scanCommitmentInserted(commitmentLeafMap)\n\n // 2. Scan Shielded events\n await scanShielded(candidates, existingCommitments)\n\n // 3. Scan Transferred events\n await scanTransferred(candidates, existingCommitments, commitmentLeafMap)\n\n // 4. Scan swap events (if swap module configured)\n if (swapModuleAddress) {\n await scanSwapFilled(candidates, existingCommitments, commitmentLeafMap)\n await scanSwapClaimed(candidates, existingCommitments, commitmentLeafMap)\n await scanSwapCancelled(candidates, existingCommitments, commitmentLeafMap)\n }\n\n // 5. Decrypt candidates\n const discovered = await decryptCandidates(candidates, decryptionConfig, poseidonFn)\n let addedCount = 0\n for (const note of discovered) {\n if (noteStore.addNote(note)) addedCount++\n }\n\n // 6. Repair leafIndex from on-chain data\n let repaired = 0\n for (const note of noteStore.getNotes()) {\n const correctLeafIndex = commitmentLeafMap.get(note.commitment.toLowerCase())\n if (correctLeafIndex !== undefined && note.leafIndex !== correctLeafIndex) {\n noteStore.updateLeafIndex(note.commitment, correctLeafIndex)\n repaired++\n }\n }\n\n // 7. Verify nullifiers\n try {\n const nullResult = await verifyNullifiers(\n noteStore, commitmentLeafMap, contractAddress, client, poseidonFn\n )\n repaired += nullResult.repaired\n } catch (e) {\n console.warn('[syncEngine] Nullifier verification failed:', e)\n }\n\n // 8. Persist\n if (addedCount > 0 || repaired > 0) {\n await noteStore.persist()\n }\n\n if (addedCount > 0 || candidates.length > 0) {\n console.log(`[syncEngine] Discovered ${addedCount} new notes from ${candidates.length} candidates`)\n }\n if (repaired > 0) {\n console.log(`[syncEngine] Repaired ${repaired} existing notes`)\n }\n\n return {\n discovered: addedCount,\n repaired,\n candidatesScanned: candidates.length,\n }\n }\n\n // ============================================================================\n // Event scanning helpers\n // ============================================================================\n\n async function scanCommitmentInserted(commitmentLeafMap: Map<string, number>) {\n try {\n const logs = await client.getLogs({\n address: contractAddress,\n event: COMMITMENT_INSERTED_EVENT,\n fromBlock: scanFromBlock,\n toBlock: 'latest',\n })\n for (const log of logs) {\n commitmentLeafMap.set(\n (log.args.commitment as string).toLowerCase(),\n Number(log.args.leafIndex)\n )\n }\n } catch (e) {\n console.warn('[syncEngine] Failed to scan CommitmentInserted events:', e)\n }\n }\n\n async function scanShielded(\n candidates: NoteCandidate[],\n existingCommitments: Set<string>,\n ) {\n try {\n const logs = await client.getLogs({\n address: contractAddress,\n event: SHIELDED_EVENT,\n fromBlock: scanFromBlock,\n toBlock: 'latest',\n })\n for (const log of logs) {\n const commitment = log.args.commitment as string\n if (existingCommitments.has(commitment.toLowerCase())) continue\n candidates.push({\n commitment,\n leafIndex: Number(log.args.leafIndex),\n packedData: log.args.encryptedNote as string,\n txHash: log.transactionHash,\n })\n }\n } catch (e: any) {\n console.warn('[syncEngine] Failed to scan Shielded events:', e?.message ?? e)\n }\n }\n\n async function scanTransferred(\n candidates: NoteCandidate[],\n existingCommitments: Set<string>,\n commitmentLeafMap: Map<string, number>,\n ) {\n try {\n const logs = await client.getLogs({\n address: contractAddress,\n event: TRANSFERRED_EVENT,\n fromBlock: scanFromBlock,\n toBlock: 'latest',\n })\n for (const log of logs) {\n const c1 = log.args.outputCommitment1 as string\n const c2 = log.args.outputCommitment2 as string\n const txHash = log.transactionHash\n\n if (!existingCommitments.has(c1.toLowerCase())) {\n candidates.push({\n commitment: c1,\n leafIndex: commitmentLeafMap.get(c1.toLowerCase()) ?? -1,\n packedData: log.args.encryptedNote1 as string,\n txHash,\n })\n }\n if (!existingCommitments.has(c2.toLowerCase())) {\n candidates.push({\n commitment: c2,\n leafIndex: commitmentLeafMap.get(c2.toLowerCase()) ?? -1,\n packedData: log.args.encryptedNote2 as string,\n txHash,\n })\n }\n }\n } catch (e) {\n console.warn('[syncEngine] Failed to scan Transferred events:', e)\n }\n }\n\n async function scanSwapFilled(\n candidates: NoteCandidate[],\n existingCommitments: Set<string>,\n commitmentLeafMap: Map<string, number>,\n ) {\n try {\n const logs = await client.getLogs({\n address: swapModuleAddress,\n event: SWAP_ORDER_FILLED_EVENT,\n fromBlock: scanFromBlock,\n toBlock: 'latest',\n })\n for (const log of logs) {\n const c = log.args.fillerOutputCommitment as string\n if (!existingCommitments.has(c.toLowerCase())) {\n candidates.push({\n commitment: c,\n leafIndex: commitmentLeafMap.get(c.toLowerCase()) ?? -1,\n packedData: log.args.encryptedFillerNote as string,\n txHash: log.transactionHash,\n })\n }\n }\n } catch (e) {\n console.warn('[syncEngine] Failed to scan SwapOrderFilled events:', e)\n }\n }\n\n async function scanSwapClaimed(\n candidates: NoteCandidate[],\n existingCommitments: Set<string>,\n commitmentLeafMap: Map<string, number>,\n ) {\n try {\n const logs = await client.getLogs({\n address: swapModuleAddress,\n event: SWAP_ORDER_CLAIMED_EVENT,\n fromBlock: scanFromBlock,\n toBlock: 'latest',\n })\n for (const log of logs) {\n const buyC = log.args.buyOutputCommitment as string\n const refundC = log.args.refundCommitment as string\n const txHash = log.transactionHash\n\n if (buyC && !existingCommitments.has(buyC.toLowerCase())) {\n candidates.push({\n commitment: buyC,\n leafIndex: commitmentLeafMap.get(buyC.toLowerCase()) ?? -1,\n packedData: log.args.encryptedBuyNote as string,\n txHash,\n })\n }\n if (refundC && !existingCommitments.has(refundC.toLowerCase())) {\n candidates.push({\n commitment: refundC,\n leafIndex: commitmentLeafMap.get(refundC.toLowerCase()) ?? -1,\n packedData: log.args.encryptedRefundNote as string,\n txHash,\n })\n }\n }\n } catch (e) {\n console.warn('[syncEngine] Failed to scan SwapOrderClaimed events:', e)\n }\n }\n\n async function scanSwapCancelled(\n candidates: NoteCandidate[],\n existingCommitments: Set<string>,\n commitmentLeafMap: Map<string, number>,\n ) {\n try {\n const logs = await client.getLogs({\n address: swapModuleAddress,\n event: SWAP_ORDER_CANCELLED_EVENT,\n fromBlock: scanFromBlock,\n toBlock: 'latest',\n })\n for (const log of logs) {\n const c = log.args.refundCommitment as string\n if (c && !existingCommitments.has(c.toLowerCase())) {\n candidates.push({\n commitment: c,\n leafIndex: commitmentLeafMap.get(c.toLowerCase()) ?? -1,\n packedData: log.args.encryptedRefundNote as string,\n txHash: log.transactionHash,\n })\n }\n }\n } catch (e) {\n console.warn('[syncEngine] Failed to scan SwapOrderCancelled events:', e)\n }\n }\n\n // ============================================================================\n // Live sync\n // ============================================================================\n\n function startLiveSync(liveConfig?: LiveSyncConfig) {\n stopLiveSync()\n\n // Initial full scan\n sync().catch(console.error)\n\n // Watch for new commitments via WebSocket/polling\n try {\n const unwatch = client.watchContractEvent({\n address: contractAddress,\n eventName: 'CommitmentInserted',\n onLogs: () => {\n sync().catch(console.error)\n },\n })\n unwatchFn = unwatch\n } catch (e) {\n console.warn('[syncEngine] watchContractEvent not available, using polling only:', e)\n }\n\n // Fallback poll\n const intervalMs = liveConfig?.intervalMs ?? 60000\n liveSyncInterval = setInterval(() => {\n sync().catch(console.error)\n }, intervalMs)\n }\n\n function stopLiveSync() {\n if (liveSyncInterval) {\n clearInterval(liveSyncInterval)\n liveSyncInterval = null\n }\n if (unwatchFn) {\n unwatchFn()\n unwatchFn = null\n }\n }\n\n function isLiveSyncing() {\n return liveSyncInterval !== null\n }\n\n return { sync, startLiveSync, stopLiveSync, isLiveSyncing }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { init_crypto, bytesToHex } from './chunk-UQIM2KT3.js';
1
+ import { init_crypto, bytesToHex } from './chunk-4E23V3AT.js';
2
2
  import { keccak256 } from 'viem';
3
3
 
4
4
  // src/utils/keccak-m31.ts
@@ -77,5 +77,5 @@ function hexToUint8Array(hex) {
77
77
  }
78
78
 
79
79
  export { DIGEST_SIZE, M31_P, SECRET_LIMBS, computeStarkCommitment, computeStarkNullifier, computeStarkOwnerHash, keccakHashTwo, keccakM31, splitToM31Limbs };
80
- //# sourceMappingURL=chunk-6TFDBBAQ.js.map
81
- //# sourceMappingURL=chunk-6TFDBBAQ.js.map
80
+ //# sourceMappingURL=chunk-H4NDMIPF.js.map
81
+ //# sourceMappingURL=chunk-H4NDMIPF.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/keccak-m31.ts"],"names":[],"mappings":"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","file":"chunk-6TFDBBAQ.js","sourcesContent":["/**\n * Keccak-256 hashing over M31 field elements.\n *\n * This module implements the same hashing algorithm as the Rust Stwo prover\n * (stwo-prover/src/hash.rs), producing identical outputs for identical inputs.\n *\n * Algorithm:\n * 1. Each M31 element is encoded as 4 little-endian bytes\n * 2. All encoded bytes are concatenated and fed to Keccak-256\n * 3. The 32-byte output is split into 4 chunks of 8 bytes\n * 4. Each chunk is read as a little-endian uint64 and reduced mod M31_P\n *\n * M31 field: p = 2^31 - 1 = 2,147,483,647\n */\n\nimport { keccak256 } from 'viem'\nimport { bytesToHex } from './crypto.js'\n\n/** The M31 prime: 2^31 - 1 */\nexport const M31_P = 2147483647n\n\n/** Number of M31 elements in a digest */\nexport const DIGEST_SIZE = 4\n\n/** Number of M31 limbs for owner secrets (248 bits of entropy) */\nexport const SECRET_LIMBS = 8\n\n/** M31 digest type: tuple of 4 M31 values */\nexport type M31Digest = readonly [bigint, bigint, bigint, bigint]\n\n/** M31 secret type: tuple of 8 M31 values */\nexport type M31Secret = readonly [bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint]\n\n/**\n * Hash arbitrary M31 elements using Keccak-256.\n *\n * Matches Rust: `hash_m31(inputs: &[M31]) -> [M31; 4]`\n *\n * Each M31 is encoded as 4 little-endian bytes. The keccak output\n * is split into 4 × 8-byte LE chunks, each reduced mod M31_P.\n */\nexport function keccakM31(inputs: readonly bigint[]): M31Digest {\n // Encode each M31 as 4 little-endian bytes\n const bytes = new Uint8Array(inputs.length * 4)\n for (let i = 0; i < inputs.length; i++) {\n const val = Number(inputs[i]! & 0xFFFFFFFFn)\n const offset = i * 4\n bytes[offset] = val & 0xFF\n bytes[offset + 1] = (val >> 8) & 0xFF\n bytes[offset + 2] = (val >> 16) & 0xFF\n bytes[offset + 3] = (val >> 24) & 0xFF\n }\n\n // Keccak-256\n const hashHex = keccak256(bytesToHex(bytes))\n\n // Parse 32-byte output into 4 × uint64 LE, reduce mod M31_P\n return bytesToM31Digest(hashHex)\n}\n\n/**\n * Hash two M31 elements (Merkle tree node hash).\n *\n * Matches Rust: `hash_two(left: M31, right: M31) -> [M31; 4]`\n */\nexport function keccakHashTwo(left: bigint, right: bigint): M31Digest {\n return keccakM31([left, right])\n}\n\n/**\n * Compute owner hash from an 8-limb secret.\n *\n * Matches Rust: `compute_owner_hash(owner_secret: &[M31; 8]) -> [M31; 4]`\n */\nexport function computeStarkOwnerHash(ownerSecret: M31Secret): M31Digest {\n return keccakM31(ownerSecret)\n}\n\n/**\n * Compute a STARK note commitment.\n *\n * Matches Rust: `compute_commitment(amount, owner_hash, blinding, origin, token)`\n *\n * Input order: [amount, ownerHash[0], ownerHash[1], ownerHash[2], ownerHash[3], blinding, origin, token]\n */\nexport function computeStarkCommitment(\n amount: bigint,\n ownerHash: M31Digest,\n blinding: bigint,\n origin: bigint,\n token: bigint,\n): M31Digest {\n return keccakM31([\n amount,\n ownerHash[0],\n ownerHash[1],\n ownerHash[2],\n ownerHash[3],\n blinding,\n origin,\n token,\n ])\n}\n\n/**\n * Compute a STARK nullifier.\n *\n * Matches Rust: `compute_nullifier(owner_secret, leaf_index, commitment)`\n *\n * Input order: [ownerSecret[0..8], leafIndex, commitment[0..4]]\n */\nexport function computeStarkNullifier(\n ownerSecret: M31Secret,\n leafIndex: bigint,\n commitment: M31Digest,\n): M31Digest {\n return keccakM31([\n ...ownerSecret,\n leafIndex,\n ...commitment,\n ])\n}\n\n/**\n * Split a 32-byte keccak hash into 8 M31 limbs.\n *\n * Used for deriving STARK spending/viewing secrets from a seed.\n * Each limb is 4 bytes of the hash, read as LE uint32, reduced mod M31_P.\n */\nexport function splitToM31Limbs(hashHex: `0x${string}`): M31Secret {\n const bytes = hexToUint8Array(hashHex)\n const limbs: bigint[] = []\n for (let i = 0; i < SECRET_LIMBS; i++) {\n const offset = i * 4\n const val = (bytes[offset]!)\n | ((bytes[offset + 1]!) << 8)\n | ((bytes[offset + 2]!) << 16)\n | ((bytes[offset + 3]!) << 24)\n limbs.push(BigInt(val >>> 0) % M31_P)\n }\n return limbs as unknown as M31Secret\n}\n\n/**\n * Convert a hex hash output to an M31 digest (4 elements).\n *\n * Splits 32 bytes into 4 × 8-byte LE chunks, reduces each mod M31_P.\n */\nfunction bytesToM31Digest(hashHex: `0x${string}`): M31Digest {\n const bytes = hexToUint8Array(hashHex)\n const digest: bigint[] = []\n\n for (let i = 0; i < DIGEST_SIZE; i++) {\n const offset = i * 8\n // Read 8 bytes as little-endian uint64\n let val = 0n\n for (let j = 0; j < 8; j++) {\n val |= BigInt(bytes[offset + j]!) << BigInt(j * 8)\n }\n digest.push(val % M31_P)\n }\n\n return digest as unknown as M31Digest\n}\n\n/**\n * Convert hex string to Uint8Array\n */\nfunction hexToUint8Array(hex: `0x${string}`): Uint8Array {\n const str = hex.slice(2)\n const bytes = new Uint8Array(str.length / 2)\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(str.slice(i * 2, i * 2 + 2), 16)\n }\n return bytes\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/keccak-m31.ts"],"names":[],"mappings":"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","file":"chunk-H4NDMIPF.js","sourcesContent":["/**\n * Keccak-256 hashing over M31 field elements.\n *\n * This module implements the same hashing algorithm as the Rust Stwo prover\n * (stwo-prover/src/hash.rs), producing identical outputs for identical inputs.\n *\n * Algorithm:\n * 1. Each M31 element is encoded as 4 little-endian bytes\n * 2. All encoded bytes are concatenated and fed to Keccak-256\n * 3. The 32-byte output is split into 4 chunks of 8 bytes\n * 4. Each chunk is read as a little-endian uint64 and reduced mod M31_P\n *\n * M31 field: p = 2^31 - 1 = 2,147,483,647\n */\n\nimport { keccak256 } from 'viem'\nimport { bytesToHex } from './crypto.js'\n\n/** The M31 prime: 2^31 - 1 */\nexport const M31_P = 2147483647n\n\n/** Number of M31 elements in a digest */\nexport const DIGEST_SIZE = 4\n\n/** Number of M31 limbs for owner secrets (248 bits of entropy) */\nexport const SECRET_LIMBS = 8\n\n/** M31 digest type: tuple of 4 M31 values */\nexport type M31Digest = readonly [bigint, bigint, bigint, bigint]\n\n/** M31 secret type: tuple of 8 M31 values */\nexport type M31Secret = readonly [bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint]\n\n/**\n * Hash arbitrary M31 elements using Keccak-256.\n *\n * Matches Rust: `hash_m31(inputs: &[M31]) -> [M31; 4]`\n *\n * Each M31 is encoded as 4 little-endian bytes. The keccak output\n * is split into 4 × 8-byte LE chunks, each reduced mod M31_P.\n */\nexport function keccakM31(inputs: readonly bigint[]): M31Digest {\n // Encode each M31 as 4 little-endian bytes\n const bytes = new Uint8Array(inputs.length * 4)\n for (let i = 0; i < inputs.length; i++) {\n const val = Number(inputs[i]! & 0xFFFFFFFFn)\n const offset = i * 4\n bytes[offset] = val & 0xFF\n bytes[offset + 1] = (val >> 8) & 0xFF\n bytes[offset + 2] = (val >> 16) & 0xFF\n bytes[offset + 3] = (val >> 24) & 0xFF\n }\n\n // Keccak-256\n const hashHex = keccak256(bytesToHex(bytes))\n\n // Parse 32-byte output into 4 × uint64 LE, reduce mod M31_P\n return bytesToM31Digest(hashHex)\n}\n\n/**\n * Hash two M31 elements (Merkle tree node hash).\n *\n * Matches Rust: `hash_two(left: M31, right: M31) -> [M31; 4]`\n */\nexport function keccakHashTwo(left: bigint, right: bigint): M31Digest {\n return keccakM31([left, right])\n}\n\n/**\n * Compute owner hash from an 8-limb secret.\n *\n * Matches Rust: `compute_owner_hash(owner_secret: &[M31; 8]) -> [M31; 4]`\n */\nexport function computeStarkOwnerHash(ownerSecret: M31Secret): M31Digest {\n return keccakM31(ownerSecret)\n}\n\n/**\n * Compute a STARK note commitment.\n *\n * Matches Rust: `compute_commitment(amount, owner_hash, blinding, origin, token)`\n *\n * Input order: [amount, ownerHash[0], ownerHash[1], ownerHash[2], ownerHash[3], blinding, origin, token]\n */\nexport function computeStarkCommitment(\n amount: bigint,\n ownerHash: M31Digest,\n blinding: bigint,\n origin: bigint,\n token: bigint,\n): M31Digest {\n return keccakM31([\n amount,\n ownerHash[0],\n ownerHash[1],\n ownerHash[2],\n ownerHash[3],\n blinding,\n origin,\n token,\n ])\n}\n\n/**\n * Compute a STARK nullifier.\n *\n * Matches Rust: `compute_nullifier(owner_secret, leaf_index, commitment)`\n *\n * Input order: [ownerSecret[0..8], leafIndex, commitment[0..4]]\n */\nexport function computeStarkNullifier(\n ownerSecret: M31Secret,\n leafIndex: bigint,\n commitment: M31Digest,\n): M31Digest {\n return keccakM31([\n ...ownerSecret,\n leafIndex,\n ...commitment,\n ])\n}\n\n/**\n * Split a 32-byte keccak hash into 8 M31 limbs.\n *\n * Used for deriving STARK spending/viewing secrets from a seed.\n * Each limb is 4 bytes of the hash, read as LE uint32, reduced mod M31_P.\n */\nexport function splitToM31Limbs(hashHex: `0x${string}`): M31Secret {\n const bytes = hexToUint8Array(hashHex)\n const limbs: bigint[] = []\n for (let i = 0; i < SECRET_LIMBS; i++) {\n const offset = i * 4\n const val = (bytes[offset]!)\n | ((bytes[offset + 1]!) << 8)\n | ((bytes[offset + 2]!) << 16)\n | ((bytes[offset + 3]!) << 24)\n limbs.push(BigInt(val >>> 0) % M31_P)\n }\n return limbs as unknown as M31Secret\n}\n\n/**\n * Convert a hex hash output to an M31 digest (4 elements).\n *\n * Splits 32 bytes into 4 × 8-byte LE chunks, reduces each mod M31_P.\n */\nfunction bytesToM31Digest(hashHex: `0x${string}`): M31Digest {\n const bytes = hexToUint8Array(hashHex)\n const digest: bigint[] = []\n\n for (let i = 0; i < DIGEST_SIZE; i++) {\n const offset = i * 8\n // Read 8 bytes as little-endian uint64\n let val = 0n\n for (let j = 0; j < 8; j++) {\n val |= BigInt(bytes[offset + j]!) << BigInt(j * 8)\n }\n digest.push(val % M31_P)\n }\n\n return digest as unknown as M31Digest\n}\n\n/**\n * Convert hex string to Uint8Array\n */\nfunction hexToUint8Array(hex: `0x${string}`): Uint8Array {\n const str = hex.slice(2)\n const bytes = new Uint8Array(str.length / 2)\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(str.slice(i * 2, i * 2 + 2), 16)\n }\n return bytes\n}\n"]}