@arcanetech/privacy-sdk-stellar 0.5.4 → 0.6.2

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.
@@ -29,6 +29,11 @@ interface StellarPrivateCoinNote {
29
29
  asset_lo: string;
30
30
  application_id?: string;
31
31
  }
32
+ interface StellarLeanImtNode {
33
+ level: number;
34
+ index: number;
35
+ value: string;
36
+ }
32
37
  interface StellarPoolMerkleState {
33
38
  poolContract: string;
34
39
  commitments: string[];
@@ -36,6 +41,7 @@ interface StellarPoolMerkleState {
36
41
  merkleRootHex: string;
37
42
  updatedAt: number;
38
43
  syncedLedger?: number;
44
+ nodes?: StellarLeanImtNode[];
39
45
  }
40
46
  interface StellarLeafEphemeral {
41
47
  poolContract: string;
@@ -100,6 +106,12 @@ interface LeafEphemeralStatePort {
100
106
  }): Promise<StellarLeafEphemeral | undefined>;
101
107
  set(ephemeral: StellarLeafEphemeral): Promise<void>;
102
108
  }
109
+ interface CachedPoolMerkleView {
110
+ commitments: string[];
111
+ updatedAt: number;
112
+ merkleRootHex?: string;
113
+ nodes?: StellarLeanImtNode[];
114
+ }
103
115
 
104
116
  type StellarSignTransaction = (input: {
105
117
  xdr: string;
@@ -574,7 +586,16 @@ interface FetchContractMerkleResult {
574
586
  commitments: string[];
575
587
  merkleRootHex: string;
576
588
  updatedAt: number;
589
+ nodes?: StellarLeanImtNode[];
577
590
  }
591
+ declare function persistFetchedMerkleState(input: {
592
+ poolContractId: string;
593
+ commitments: string[];
594
+ merkleRootHex: string;
595
+ cachedState: CachedPoolMerkleView | undefined;
596
+ poolMerkleState?: PoolMerkleStatePort;
597
+ }): Promise<FetchContractMerkleResult>;
598
+
578
599
  /**
579
600
  * Loads pool Merkle leaves without `get_commitments()`. That helper walks the
580
601
  * whole tree in one simulate and exceeds the Soroban host budget on a live
@@ -586,7 +607,7 @@ declare function fetchAndMergeMerkleState(parameters: {
586
607
  walletPublicKey: string;
587
608
  transactEnvironment: StellarTransactEnvironment;
588
609
  poolMerkleState?: PoolMerkleStatePort;
589
- }): Promise<FetchContractMerkleResult>;
610
+ }): Promise<Awaited<ReturnType<typeof persistFetchedMerkleState>>>;
590
611
  declare function readLeafEphemeralHex(parameters: {
591
612
  poolContractId: string;
592
613
  walletPublicKey: string;
@@ -228,11 +228,240 @@ function uniformApplicationIdHints(applicationId, nAuditSlots) {
228
228
 
229
229
  // src/transact/proofs/confidential/helpers.ts
230
230
  import { withdrawObjectFromMerkleWitness } from "@arcanetech/stellar-privacy-pool-zk-sdk";
231
+
232
+ // src/transact/merkle/lean-imt-api.ts
233
+ function asLeanImtSessionApi(sdk) {
234
+ const candidate = sdk;
235
+ if (typeof candidate.importLeanImtFromState !== "function") {
236
+ return void 0;
237
+ }
238
+ if (typeof candidate.insertTwoLeanImt !== "function") {
239
+ return void 0;
240
+ }
241
+ if (typeof candidate.exportLeanImt !== "function") {
242
+ return void 0;
243
+ }
244
+ if (typeof candidate.dropLeanImt !== "function") {
245
+ return void 0;
246
+ }
247
+ if (typeof candidate.buildWithdrawMerkleWitnessFromHandle !== "function") {
248
+ return void 0;
249
+ }
250
+ return candidate;
251
+ }
252
+
253
+ // src/transact/merkle/field-decimal.ts
254
+ import { Buffer as Buffer4 } from "buffer";
255
+ var BN254 = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
256
+ var MERKLE_ROOT_BYTE_LENGTH = 32;
257
+ var DECIMAL_STRING_RADIX = 10;
258
+ function merkleRootBufferToFrDecimal(root) {
259
+ if (root.length !== MERKLE_ROOT_BYTE_LENGTH) {
260
+ throw new Error(
261
+ `expected ${MERKLE_ROOT_BYTE_LENGTH}-byte merkle root, got ${root.length}`
262
+ );
263
+ }
264
+ const hex = Buffer4.from(root).toString("hex");
265
+ return (BigInt(`0x${hex}`) % BN254).toString(DECIMAL_STRING_RADIX);
266
+ }
267
+ var MERKLE_ROOT_HEX_PATTERN = /^[0-9a-fA-F]{64}$/;
268
+ function merkleRootHexToFrDecimal(merkleRootHex) {
269
+ const trimmed = merkleRootHex.trim();
270
+ if (!MERKLE_ROOT_HEX_PATTERN.test(trimmed)) {
271
+ return void 0;
272
+ }
273
+ return merkleRootBufferToFrDecimal(Buffer4.from(trimmed, "hex"));
274
+ }
275
+
276
+ // src/transact/merkle/state-file.ts
277
+ function merkleSnapshotToStateFile(input) {
278
+ const root = merkleRootHexToFrDecimal(input.merkleRootHex);
279
+ const nodes = input.nodes;
280
+ if (!nodes || nodes.length === 0 || root === void 0) {
281
+ return { commitments: input.commitments };
282
+ }
283
+ return {
284
+ commitments: input.commitments,
285
+ nodes,
286
+ root
287
+ };
288
+ }
289
+
290
+ // src/transact/merkle/extend-snapshot.ts
291
+ function isEvenLeafCount(count) {
292
+ return count % 2 === 0;
293
+ }
294
+ function importHandle(sdk, state) {
295
+ try {
296
+ return sdk.importLeanImtFromState(state);
297
+ } catch {
298
+ return void 0;
299
+ }
300
+ }
301
+ function insertNewPairs(input) {
302
+ for (let index = input.prefixLength; index < input.commitments.length; index += 2) {
303
+ const leafA = input.commitments.at(index);
304
+ const leafB = input.commitments.at(index + 1);
305
+ if (leafA === void 0 || leafB === void 0) {
306
+ return false;
307
+ }
308
+ try {
309
+ input.sdk.insertTwoLeanImt(input.handle, leafA, leafB);
310
+ } catch {
311
+ return false;
312
+ }
313
+ }
314
+ return true;
315
+ }
316
+ function snapshotNodes(sdk, handle) {
317
+ try {
318
+ return sdk.exportLeanImt(handle).nodes;
319
+ } catch {
320
+ return void 0;
321
+ }
322
+ }
323
+ function prefixSnapshotState(input) {
324
+ const prefixLength = input.cachedState?.nodes ? input.cachedState.commitments.length : 0;
325
+ return merkleSnapshotToStateFile({
326
+ commitments: input.commitments.slice(0, prefixLength),
327
+ merkleRootHex: input.cachedState?.merkleRootHex ?? input.merkleRootHex,
328
+ ...input.cachedState?.nodes ? { nodes: input.cachedState.nodes } : {}
329
+ });
330
+ }
331
+ function importSnapshotState(input) {
332
+ if (input.prefixState.nodes && input.prefixState.commitments.length > 0) {
333
+ return input.prefixState;
334
+ }
335
+ return merkleSnapshotToStateFile({
336
+ commitments: input.commitments,
337
+ merkleRootHex: input.merkleRootHex
338
+ });
339
+ }
340
+ function exportExtendedNodes(input) {
341
+ const inserted = input.prefixState.nodes ? insertNewPairs({
342
+ sdk: input.sdk,
343
+ handle: input.handle,
344
+ prefixLength: input.prefixState.commitments.length,
345
+ commitments: input.commitments
346
+ }) : true;
347
+ if (!inserted) {
348
+ input.sdk.dropLeanImt(input.handle);
349
+ return void 0;
350
+ }
351
+ const nodes = snapshotNodes(input.sdk, input.handle);
352
+ if (!nodes) {
353
+ input.sdk.dropLeanImt(input.handle);
354
+ }
355
+ return nodes;
356
+ }
357
+ function extendMerkleSnapshotNodes(input) {
358
+ if (!isEvenLeafCount(input.commitments.length)) {
359
+ return {};
360
+ }
361
+ const prefixState = prefixSnapshotState(input);
362
+ const handle = importHandle(
363
+ input.sdk,
364
+ importSnapshotState({ ...input, prefixState })
365
+ );
366
+ if (handle === void 0) {
367
+ return {};
368
+ }
369
+ const nodes = exportExtendedNodes({
370
+ sdk: input.sdk,
371
+ handle,
372
+ prefixState,
373
+ commitments: input.commitments
374
+ });
375
+ if (!nodes) {
376
+ return {};
377
+ }
378
+ return { nodes, handle };
379
+ }
380
+
381
+ // src/transact/merkle/tree-session.ts
382
+ var merkleSdkHost;
383
+ function registerMerkleSessionSdkHost(host) {
384
+ merkleSdkHost = host;
385
+ }
386
+ async function loadSessionSdk() {
387
+ const host = merkleSdkHost;
388
+ if (typeof host?.getInitializedSdk !== "function") {
389
+ return void 0;
390
+ }
391
+ try {
392
+ return asLeanImtSessionApi(await host.getInitializedSdk());
393
+ } catch {
394
+ return void 0;
395
+ }
396
+ }
397
+ var sessionsByPool = /* @__PURE__ */ new Map();
398
+ var handlesByKey = /* @__PURE__ */ new Map();
399
+ function merkleSessionKey(state) {
400
+ const root = state.root?.trim();
401
+ if (root) {
402
+ return `${state.commitments.length}:${root}`;
403
+ }
404
+ const first = state.commitments[0] ?? "";
405
+ const last = state.commitments.at(-1) ?? "";
406
+ return `${state.commitments.length}:${first}:${last}`;
407
+ }
408
+ function rememberHandle(poolContract, key, handle) {
409
+ const existing = sessionsByPool.get(poolContract);
410
+ if (existing && existing.handle !== handle) {
411
+ handlesByKey.delete(existing.key);
412
+ }
413
+ sessionsByPool.set(poolContract, { handle, key });
414
+ handlesByKey.set(key, handle);
415
+ }
416
+ function merkleTreeHandleForState(state) {
417
+ return handlesByKey.get(merkleSessionKey(state));
418
+ }
419
+ function bindImportedMerkleTree(input) {
420
+ rememberHandle(input.poolContract, merkleSessionKey(input.state), input.handle);
421
+ }
422
+ async function resolveExtendedMerkleNodes(input) {
423
+ const cached = input.cachedState;
424
+ if (cached && cached.commitments.length === input.commitments.length && cached.merkleRootHex === input.merkleRootHex) {
425
+ return cached.nodes;
426
+ }
427
+ const sdk = await loadSessionSdk();
428
+ if (!sdk) {
429
+ return void 0;
430
+ }
431
+ const extended = extendMerkleSnapshotNodes({
432
+ sdk,
433
+ commitments: input.commitments,
434
+ merkleRootHex: input.merkleRootHex,
435
+ ...cached ? { cachedState: cached } : {}
436
+ });
437
+ if (extended.handle !== void 0) {
438
+ bindImportedMerkleTree({
439
+ poolContract: input.poolContract,
440
+ handle: extended.handle,
441
+ state: merkleSnapshotToStateFile({
442
+ commitments: input.commitments,
443
+ merkleRootHex: input.merkleRootHex,
444
+ ...extended.nodes ? { nodes: extended.nodes } : {}
445
+ })
446
+ });
447
+ }
448
+ return extended.nodes;
449
+ }
450
+ function withdrawMerkleWitnessFromTree(input) {
451
+ const session = asLeanImtSessionApi(input.sdk);
452
+ const handle = merkleTreeHandleForState(input.state);
453
+ if (session && handle !== void 0) {
454
+ return session.buildWithdrawMerkleWitnessFromHandle(input.coin, handle);
455
+ }
456
+ return input.sdk.buildWithdrawMerkleWitness(input.coin, input.state);
457
+ }
458
+
459
+ // src/transact/proofs/confidential/helpers.ts
231
460
  var MIN_CONFIDENTIAL_TRANSFER_STROOPS = 1n;
232
461
  var ZERO_STROOPS = 0n;
233
462
  function withdrawWitnessForCoin(parameters) {
234
463
  const { sdk, coin, state } = parameters;
235
- const witness = sdk.buildWithdrawMerkleWitness(coin, state);
464
+ const witness = withdrawMerkleWitnessFromTree({ sdk, coin, state });
236
465
  const withdrawApplicationId = coin.application_id ?? parameters.applicationId;
237
466
  const withdrawObject = withdrawObjectFromMerkleWitness(
238
467
  witness,
@@ -419,7 +648,7 @@ import {
419
648
  DEMO_AUDIT_PUBLIC_KEY,
420
649
  resolveTransactionAuditParams
421
650
  } from "@arcanetech/stellar-privacy-pool-zk-sdk";
422
- import { Buffer as Buffer4 } from "buffer";
651
+ import { Buffer as Buffer5 } from "buffer";
423
652
  var UNCOMPRESSED_PREFIX = "04";
424
653
  var UNCOMPRESSED_HEX_LENGTH = 130;
425
654
  var XY_HEX_LENGTH = 128;
@@ -446,7 +675,7 @@ function parseAuditPublicKeyFromEnvHex(raw) {
446
675
  "Audit public key must be uncompressed 65-byte hex (04 + X + Y) or 64-byte X+Y hex"
447
676
  );
448
677
  }
449
- const bytes = Buffer4.from(payload, "hex");
678
+ const bytes = Buffer5.from(payload, "hex");
450
679
  const auditPublicKeyXDecimal = BigInt(
451
680
  `0x${bytes.subarray(0, X_OFFSET_END).toString("hex")}`
452
681
  ).toString(DECIMAL_RADIX);
@@ -489,13 +718,13 @@ import { ed25519PubkeyPayloadHexToWithdrawFrDecimals } from "@arcanetech/stellar
489
718
 
490
719
  // src/transact/stellar/account.ts
491
720
  import { StrKey as StrKey3 } from "@stellar/stellar-sdk";
492
- import { Buffer as Buffer5 } from "buffer";
721
+ import { Buffer as Buffer6 } from "buffer";
493
722
  var MUXED_ACCOUNT_ID_BYTE_LENGTH = 8;
494
723
  var ED25519_PUBLIC_KEY_BYTE_LENGTH = 32;
495
724
  function ed25519HexFromGAddress(trimmed) {
496
725
  try {
497
726
  const raw = StrKey3.decodeEd25519PublicKey(trimmed);
498
- return Buffer5.from(raw).toString("hex");
727
+ return Buffer6.from(raw).toString("hex");
499
728
  } catch {
500
729
  return void 0;
501
730
  }
@@ -782,10 +1011,11 @@ async function proveSingleWithdrawTransaction(parameters) {
782
1011
  );
783
1012
  }
784
1013
  function buildPrimaryWithdrawForCoin(parameters) {
785
- const witness = parameters.sdk.buildWithdrawMerkleWitness(
786
- parameters.coin,
787
- parameters.state
788
- );
1014
+ const witness = withdrawMerkleWitnessFromTree({
1015
+ sdk: parameters.sdk,
1016
+ coin: parameters.coin,
1017
+ state: parameters.state
1018
+ });
789
1019
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
790
1020
  parameters.privKeyScalarHex
791
1021
  );
@@ -1207,23 +1437,6 @@ import {
1207
1437
  randomFrDecimal253,
1208
1438
  scalarHexToFrDecimal as scalarHexToFrDecimal2
1209
1439
  } from "@arcanetech/stellar-privacy-pool-zk-sdk";
1210
-
1211
- // src/transact/merkle/field-decimal.ts
1212
- import { Buffer as Buffer6 } from "buffer";
1213
- var BN254 = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
1214
- var MERKLE_ROOT_BYTE_LENGTH = 32;
1215
- var DECIMAL_STRING_RADIX = 10;
1216
- function merkleRootBufferToFrDecimal(root) {
1217
- if (root.length !== MERKLE_ROOT_BYTE_LENGTH) {
1218
- throw new Error(
1219
- `expected ${MERKLE_ROOT_BYTE_LENGTH}-byte merkle root, got ${root.length}`
1220
- );
1221
- }
1222
- const hex = Buffer6.from(root).toString("hex");
1223
- return (BigInt(`0x${hex}`) % BN254).toString(DECIMAL_STRING_RADIX);
1224
- }
1225
-
1226
- // src/transact/pool/prepare-deposit-proof.ts
1227
1440
  function userDepositSlot(input) {
1228
1441
  return {
1229
1442
  value: input.coin.value,
@@ -1455,6 +1668,7 @@ function createPrivacyPoolService(input) {
1455
1668
  var configuredPrivacyPoolService;
1456
1669
  function configurePrivacyPoolService(service) {
1457
1670
  configuredPrivacyPoolService = service;
1671
+ registerMerkleSessionSdkHost(service);
1458
1672
  }
1459
1673
  function getPrivacyPoolService() {
1460
1674
  if (!configuredPrivacyPoolService) {
@@ -3112,7 +3326,8 @@ function poolMerkleStateToCachedView(state) {
3112
3326
  return {
3113
3327
  commitments: state.commitments,
3114
3328
  updatedAt: state.updatedAt,
3115
- merkleRootHex: state.merkleRootHex
3329
+ merkleRootHex: state.merkleRootHex,
3330
+ ...state.nodes ? { nodes: state.nodes } : {}
3116
3331
  };
3117
3332
  }
3118
3333
 
@@ -3245,6 +3460,65 @@ function reuseCachedCommitments(input) {
3245
3460
  return cachedCommitments;
3246
3461
  }
3247
3462
 
3463
+ // src/transact/merkle/persist-fetched.ts
3464
+ function cachedMerkleUnchanged(cachedState, commitments, merkleRootHex) {
3465
+ if (!cachedState) {
3466
+ return false;
3467
+ }
3468
+ if (cachedState.commitments.length !== commitments.length) {
3469
+ return false;
3470
+ }
3471
+ return cachedState.merkleRootHex === merkleRootHex;
3472
+ }
3473
+ function viewFromCachedMerkle(cachedState, merkleRootHex) {
3474
+ return {
3475
+ commitments: cachedState.commitments,
3476
+ updatedAt: cachedState.updatedAt,
3477
+ merkleRootHex: cachedState.merkleRootHex ?? merkleRootHex,
3478
+ commitmentCount: cachedState.commitments.length,
3479
+ ...cachedState.nodes ? { nodes: cachedState.nodes } : {}
3480
+ };
3481
+ }
3482
+ function merkleStateForPersist(input) {
3483
+ return {
3484
+ poolContract: input.poolContractId,
3485
+ commitments: input.commitments,
3486
+ commitmentCount: input.commitments.length,
3487
+ merkleRootHex: input.merkleRootHex,
3488
+ updatedAt: input.updatedAt,
3489
+ ...input.nodes ? { nodes: input.nodes } : {}
3490
+ };
3491
+ }
3492
+ async function persistFetchedMerkleState(input) {
3493
+ const cachedState = input.cachedState;
3494
+ if (cachedMerkleUnchanged(cachedState, input.commitments, input.merkleRootHex) && cachedState) {
3495
+ return viewFromCachedMerkle(cachedState, input.merkleRootHex);
3496
+ }
3497
+ const nodes = await resolveExtendedMerkleNodes({
3498
+ poolContract: input.poolContractId,
3499
+ commitments: input.commitments,
3500
+ merkleRootHex: input.merkleRootHex,
3501
+ ...cachedState ? { cachedState } : {}
3502
+ });
3503
+ const updatedAt = Date.now();
3504
+ await input.poolMerkleState?.set(
3505
+ merkleStateForPersist({
3506
+ poolContractId: input.poolContractId,
3507
+ commitments: input.commitments,
3508
+ merkleRootHex: input.merkleRootHex,
3509
+ updatedAt,
3510
+ ...nodes ? { nodes } : {}
3511
+ })
3512
+ );
3513
+ return {
3514
+ commitments: input.commitments,
3515
+ updatedAt,
3516
+ merkleRootHex: input.merkleRootHex,
3517
+ commitmentCount: input.commitments.length,
3518
+ ...nodes ? { nodes } : {}
3519
+ };
3520
+ }
3521
+
3248
3522
  // src/transact/merkle/fetch-contract.ts
3249
3523
  async function fetchAndMergeMerkleState(parameters) {
3250
3524
  const client = createPoolClient(parameters);
@@ -3337,43 +3611,6 @@ async function loadCommitments(input) {
3337
3611
  });
3338
3612
  return [...reused, ...fetched];
3339
3613
  }
3340
- async function persistFetchedMerkleState(input) {
3341
- const cachedState = input.cachedState;
3342
- if (cachedState && cachedMerkleUnchanged(cachedState, input.commitments, input.merkleRootHex)) {
3343
- return viewFromCachedMerkle(cachedState, input.merkleRootHex);
3344
- }
3345
- const updatedAt = Date.now();
3346
- await input.poolMerkleState?.set({
3347
- poolContract: input.poolContractId,
3348
- commitments: input.commitments,
3349
- commitmentCount: input.commitments.length,
3350
- merkleRootHex: input.merkleRootHex,
3351
- updatedAt
3352
- });
3353
- return {
3354
- commitments: input.commitments,
3355
- updatedAt,
3356
- merkleRootHex: input.merkleRootHex,
3357
- commitmentCount: input.commitments.length
3358
- };
3359
- }
3360
- function viewFromCachedMerkle(cachedState, merkleRootHex) {
3361
- return {
3362
- commitments: cachedState.commitments,
3363
- updatedAt: cachedState.updatedAt,
3364
- merkleRootHex: cachedState.merkleRootHex ?? merkleRootHex,
3365
- commitmentCount: cachedState.commitments.length
3366
- };
3367
- }
3368
- function cachedMerkleUnchanged(cachedState, commitments, merkleRootHex) {
3369
- if (!cachedState) {
3370
- return false;
3371
- }
3372
- if ((cachedState.merkleRootHex ?? merkleRootHex) !== merkleRootHex) {
3373
- return false;
3374
- }
3375
- return cachedState.commitments.join(",") === commitments.join(",");
3376
- }
3377
3614
 
3378
3615
  // src/transact/encoding/fr-decimal-to-bytes.ts
3379
3616
  var BN254_SCALAR_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
@@ -3486,13 +3723,19 @@ var walletPrivateAddressRecordSchema = z.object({
3486
3723
  privateAddress: z.string().min(1),
3487
3724
  createdAt: z.number()
3488
3725
  });
3726
+ var leanImtNodeSchema = z.object({
3727
+ level: z.number().int().nonnegative(),
3728
+ index: z.number().int().nonnegative(),
3729
+ value: z.string().min(1)
3730
+ });
3489
3731
  var poolMerkleStateSchema = z.object({
3490
3732
  poolContract: z.string().min(1),
3491
3733
  commitments: z.array(z.string()),
3492
3734
  commitmentCount: z.number().int().nonnegative(),
3493
3735
  merkleRootHex: z.string().min(1),
3494
3736
  updatedAt: z.number(),
3495
- syncedLedger: z.number().int().optional()
3737
+ syncedLedger: z.number().int().optional(),
3738
+ nodes: z.array(leanImtNodeSchema).optional()
3496
3739
  });
3497
3740
  var leafEphemeralSchema = z.object({
3498
3741
  poolContract: z.string().min(1),
@@ -4905,6 +5148,7 @@ function createPoolMerkleService(context) {
4905
5148
  async appendPoolCommitments(input) {
4906
5149
  const current = await this.getPoolMerkleState(input.poolContract);
4907
5150
  const mergedCommitments = [...current?.commitments ?? [], ...input.commitments];
5151
+ const nodes = input.nodes ?? (input.commitments.length === 0 ? current?.nodes : void 0);
4908
5152
  await this.setPoolMerkleState(
4909
5153
  withoutUndefinedFields({
4910
5154
  poolContract: input.poolContract,
@@ -4912,7 +5156,8 @@ function createPoolMerkleService(context) {
4912
5156
  commitmentCount: mergedCommitments.length,
4913
5157
  merkleRootHex: input.merkleRootHex,
4914
5158
  updatedAt: Date.now(),
4915
- syncedLedger: input.syncedLedger ?? current?.syncedLedger
5159
+ syncedLedger: input.syncedLedger ?? current?.syncedLedger,
5160
+ nodes
4916
5161
  })
4917
5162
  );
4918
5163
  }
@@ -5441,7 +5686,11 @@ async function loadMerkleState(environment, walletPublicKey) {
5441
5686
  transactEnvironment: environment,
5442
5687
  ...environment.poolMerkleState ? { poolMerkleState: environment.poolMerkleState } : {}
5443
5688
  });
5444
- return { commitments: merged.commitments };
5689
+ return merkleSnapshotToStateFile({
5690
+ commitments: merged.commitments,
5691
+ merkleRootHex: merged.merkleRootHex,
5692
+ ...merged.nodes ? { nodes: merged.nodes } : {}
5693
+ });
5445
5694
  }
5446
5695
 
5447
5696
  // src/transact/escrow/require-escrow-spend-scalar.ts
@@ -5588,7 +5837,8 @@ async function buildSpendProofContextAtExecute(input) {
5588
5837
  input.prepared.intent.asset
5589
5838
  );
5590
5839
  const coin = requireCoinNoteFromRecord(primaryRecord);
5591
- const { commitments } = await loadMerkleState(input.environment, walletPublicKey);
5840
+ const merkleState = await loadMerkleState(input.environment, walletPublicKey);
5841
+ const commitments = merkleState.commitments;
5592
5842
  const leafIndex = findCommitmentLeafIndex(commitments, coin.commitment);
5593
5843
  if (leafIndex < 0) {
5594
5844
  throw new Error("Coin commitment not found in on-chain Merkle state.");
@@ -5605,6 +5855,7 @@ async function buildSpendProofContextAtExecute(input) {
5605
5855
  tokenAddress,
5606
5856
  coin,
5607
5857
  commitments,
5858
+ merkleState,
5608
5859
  ephemeral,
5609
5860
  senderPrivKeyScalarHex,
5610
5861
  recipientPrivateAddressStpl1: input.recipientPrivateAddressStpl1
@@ -5670,7 +5921,7 @@ async function buildSingleTransferProofAtExecute(input) {
5670
5921
  const changeStroops = BigInt(context.coin.value) - input.prepared.intent.amount - (feeOutput?.requiredFee ?? 0n);
5671
5922
  const proof = await prepareConfidentialTransferProof({
5672
5923
  coin: context.coin,
5673
- state: { commitments: context.commitments },
5924
+ state: context.merkleState,
5674
5925
  senderPrivKeyScalarHex: context.senderPrivKeyScalarHex,
5675
5926
  depositorEphemeralKey: serializeEphemeralKeyString({
5676
5927
  xHex: context.ephemeral.xHex,
@@ -5707,7 +5958,7 @@ async function buildDualTransferProofAtExecute(input) {
5707
5958
  const proof = await prepareConfidentialTransferProofDual({
5708
5959
  coinA: context.coin,
5709
5960
  coinB: secondary.coin,
5710
- state: { commitments: context.commitments },
5961
+ state: context.merkleState,
5711
5962
  senderPrivKeyScalarHex: context.senderPrivKeyScalarHex,
5712
5963
  ephemeralAKey: serializeEphemeralKeyString({
5713
5964
  xHex: context.ephemeral.xHex,
@@ -5955,7 +6206,7 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
5955
6206
  });
5956
6207
  const proof = await poolService.prepareWithdrawTransactProof({
5957
6208
  coin: context.coin,
5958
- state: { commitments: context.commitments },
6209
+ state: context.merkleState,
5959
6210
  destinationStellarAddress: prepared.intent.to,
5960
6211
  privKeyScalarHex: context.senderPrivKeyScalarHex,
5961
6212
  depositorEphemeralKey: serializeEphemeralKeyString({
@@ -6008,7 +6259,7 @@ async function finalizeDualWithdrawAtExecute(prepared, environment, poolService,
6008
6259
  const proof = await poolService.prepareWithdrawTransactProofDual({
6009
6260
  coinA: context.coin,
6010
6261
  coinB: secondary.coin,
6011
- state: { commitments: context.commitments },
6262
+ state: context.merkleState,
6012
6263
  destinationStellarAddress: prepared.intent.to,
6013
6264
  privKeyScalarHex: context.senderPrivKeyScalarHex,
6014
6265
  ...dualWithdrawEphemeralKeys({ context, secondary }),