@arcanetech/privacy-sdk-stellar 0.5.1 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,26 +1,8 @@
1
1
  // src/transact/pool/service.ts
2
2
  import {
3
- PrivacyPoolSDK as PrivacyPoolSDK2,
4
- decodeDecodedEphemeralKey,
5
- randomFrDecimal253,
6
- scalarHexToFrDecimal as scalarHexToFrDecimal2
3
+ decodeDecodedEphemeralKey
7
4
  } from "@arcanetech/stellar-privacy-pool-zk-sdk";
8
5
 
9
- // src/transact/merkle/field-decimal.ts
10
- import { Buffer as Buffer2 } from "buffer";
11
- var BN254 = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
12
- var MERKLE_ROOT_BYTE_LENGTH = 32;
13
- var DECIMAL_STRING_RADIX = 10;
14
- function merkleRootBufferToFrDecimal(root) {
15
- if (root.length !== MERKLE_ROOT_BYTE_LENGTH) {
16
- throw new Error(
17
- `expected ${MERKLE_ROOT_BYTE_LENGTH}-byte merkle root, got ${root.length}`
18
- );
19
- }
20
- const hex = Buffer2.from(root).toString("hex");
21
- return (BigInt(`0x${hex}`) % BN254).toString(DECIMAL_STRING_RADIX);
22
- }
23
-
24
6
  // src/transact/private-address/codec.ts
25
7
  import {
26
8
  buildStealthAddressSignMessage,
@@ -76,18 +58,18 @@ function privateAddressSdk(sdk) {
76
58
  import { withdrawObjectFromMerkleWitness as withdrawObjectFromMerkleWitness2 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
77
59
 
78
60
  // src/transact/proofs/transaction-input.ts
79
- import { Buffer as Buffer4 } from "buffer";
61
+ import { Buffer as Buffer3 } from "buffer";
80
62
  import { StrKey as StrKey2 } from "@stellar/stellar-sdk";
81
63
 
82
64
  // src/transact/stellar/token-contract.ts
83
- import { Buffer as Buffer3 } from "buffer";
65
+ import { Buffer as Buffer2 } from "buffer";
84
66
  import { StrKey } from "@stellar/stellar-sdk";
85
67
  var CONTRACT_ID_BYTE_LENGTH = 32;
86
68
  var HEX_CHARS_PER_BYTE = 2;
87
69
  var CONTRACT_ID_HEX_CHAR_LENGTH = CONTRACT_ID_BYTE_LENGTH * HEX_CHARS_PER_BYTE;
88
70
  var CONTRACT_ID_HEX = /^(?:0x)?([0-9a-f]{64})$/i;
89
71
  function encodeContractFromHex(hexPayload) {
90
- return StrKey.encodeContract(Buffer3.from(hexPayload, "hex"));
72
+ return StrKey.encodeContract(Buffer2.from(hexPayload, "hex"));
91
73
  }
92
74
  function normalizeSorobanContractId(raw) {
93
75
  const trimmed = raw.trim();
@@ -120,14 +102,14 @@ var ZERO_PUBLIC_WITHDRAW_ADDRESS = {
120
102
  var CONTRACT_ADDRESS_HALF_BYTES = 16;
121
103
  var CONTRACT_ADDRESS_BYTES = 32;
122
104
  function bytes16ToDecimal(bytes) {
123
- return BigInt(`0x${Buffer4.from(bytes).toString("hex")}`).toString();
105
+ return BigInt(`0x${Buffer3.from(bytes).toString("hex")}`).toString();
124
106
  }
125
107
  function decimalToBytes16(value) {
126
108
  const hex = BigInt(value).toString(16).padStart(CONTRACT_ADDRESS_HALF_BYTES * 2, "0");
127
109
  if (hex.length > CONTRACT_ADDRESS_HALF_BYTES * 2) {
128
110
  throw new Error("Asset leg does not fit into 16 bytes.");
129
111
  }
130
- return Buffer4.from(hex, "hex");
112
+ return Buffer3.from(hex, "hex");
131
113
  }
132
114
  function tokenAddressFrParts(tokenAddress) {
133
115
  const normalized = resolveTokenContractId(tokenAddress);
@@ -169,7 +151,7 @@ function tokenAddressToAssetLeg(tokenAddress) {
169
151
  return [hi, lo];
170
152
  }
171
153
  function assetLegToTokenAddress(assetHi, assetLo) {
172
- const payload = Buffer4.concat([decimalToBytes16(assetHi), decimalToBytes16(assetLo)]);
154
+ const payload = Buffer3.concat([decimalToBytes16(assetHi), decimalToBytes16(assetLo)]);
173
155
  return StrKey2.encodeContract(payload);
174
156
  }
175
157
  function buildZeroPublicLegs(publicNInputs = 1, publicNOutputs = 1) {
@@ -292,14 +274,144 @@ function requireChangeRecipientWhenPartial(changeStroops, selfPrivateAddressStpl
292
274
  }
293
275
  }
294
276
  }
295
- function changeStroopsAfterDualTransfer(transferStroops, totalNotes) {
296
- if (transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
297
- throw new Error("Transfer amount must be positive");
277
+
278
+ // src/transact/fees/quote-request-body.ts
279
+ function presentInstructedAmount(amount) {
280
+ const trimmed = amount?.trim() ?? "";
281
+ if (!trimmed || trimmed === "0") {
282
+ return void 0;
283
+ }
284
+ return trimmed;
285
+ }
286
+ function feeQuoteRequestBody(request) {
287
+ const publicDepositAmount = presentInstructedAmount(request.publicDepositAmount);
288
+ const transferInstructedAmount = presentInstructedAmount(
289
+ request.transferInstructedAmount
290
+ );
291
+ const publicWithdrawalAmount = presentInstructedAmount(
292
+ request.publicWithdrawalAmount
293
+ );
294
+ return {
295
+ feeAsset: request.feeAsset,
296
+ spendsNotes: request.spendsNotes,
297
+ ...publicDepositAmount === void 0 ? {} : { publicDepositAmount },
298
+ ...transferInstructedAmount === void 0 ? {} : { transferInstructedAmount },
299
+ ...publicWithdrawalAmount === void 0 ? {} : { publicWithdrawalAmount }
300
+ };
301
+ }
302
+
303
+ // src/transact/fees/quote-required-fee.ts
304
+ function quoteHeaders(inspectAuthorization) {
305
+ const token = inspectAuthorization?.trim();
306
+ return {
307
+ "content-type": "application/json",
308
+ ...token ? { authorization: `Bearer ${token}` } : {}
309
+ };
310
+ }
311
+ function parseRequiredFee(raw) {
312
+ const text = `${raw ?? ""}`.trim();
313
+ if (!/^\d+$/u.test(text)) {
314
+ throw new Error("Fee Quote Required Fee must be an integer minor-unit string.");
315
+ }
316
+ return BigInt(text);
317
+ }
318
+ function requireQuoteString(raw, field) {
319
+ const text = `${raw ?? ""}`.trim();
320
+ if (!text) {
321
+ throw new Error(`Fee Quote is missing ${field}.`);
322
+ }
323
+ return text;
324
+ }
325
+ function parseQuotePayload(payload) {
326
+ return {
327
+ requiredFee: parseRequiredFee(payload.requiredFee),
328
+ feeAsset: requireQuoteString(payload.feeAsset, "Fee Asset"),
329
+ feeCollectorPrivateAddress: requireQuoteString(
330
+ payload.feeCollectorPrivateAddress,
331
+ "Fee Collector Private Address"
332
+ ),
333
+ feeRate: requireQuoteString(payload.feeRate, "Fee Rate")
334
+ };
335
+ }
336
+ async function quoteRequiredFee(input) {
337
+ const response = await fetch(
338
+ `${input.transactEnvironment.kyt.apiBaseUrl}/kyt/fees/quote`,
339
+ {
340
+ method: "POST",
341
+ headers: quoteHeaders(input.transactEnvironment.kyt.inspectAuthorization),
342
+ body: JSON.stringify(feeQuoteRequestBody(input.request))
343
+ }
344
+ );
345
+ const payload = await response.json();
346
+ if (!response.ok) {
347
+ throw new Error(`Fee Quote failed: ${JSON.stringify(payload)}`);
348
+ }
349
+ return parseQuotePayload(payload);
350
+ }
351
+
352
+ // src/transact/fees/should-attach-fee-output.ts
353
+ function shouldAttachFeeOutput(input) {
354
+ if (input.spendSource === "escrow" && input.escrowSend !== true) {
355
+ return false;
356
+ }
357
+ return input.kind === "deposit" || input.kind === "transfer" || input.kind === "withdraw";
358
+ }
359
+ function feeOutputKindFromPrepared(prepared) {
360
+ return {
361
+ kind: prepared.kind,
362
+ ...prepared.transactArtifacts?.spendSource ? { spendSource: prepared.transactArtifacts.spendSource } : {},
363
+ ...prepared.transactArtifacts?.escrowSend ? { escrowSend: true } : {}
364
+ };
365
+ }
366
+
367
+ // src/transact/fees/quote-fee-output-for-prepared.ts
368
+ function instructedMinorUnits(prepared) {
369
+ return prepared.intent.amount.toString();
370
+ }
371
+ function quoteRequestForPrepared(input) {
372
+ const amount = instructedMinorUnits(input.prepared);
373
+ if (input.prepared.kind === "deposit") {
374
+ return {
375
+ feeAsset: input.tokenAddress,
376
+ publicDepositAmount: amount,
377
+ spendsNotes: false
378
+ };
379
+ }
380
+ if (input.prepared.kind === "withdraw") {
381
+ return {
382
+ feeAsset: input.tokenAddress,
383
+ publicWithdrawalAmount: amount,
384
+ spendsNotes: true
385
+ };
386
+ }
387
+ return {
388
+ feeAsset: input.tokenAddress,
389
+ transferInstructedAmount: amount,
390
+ spendsNotes: true
391
+ };
392
+ }
393
+ async function quoteFeeOutputForPrepared(input) {
394
+ if (!shouldAttachFeeOutput(feeOutputKindFromPrepared(input.prepared))) {
395
+ return void 0;
298
396
  }
299
- if (transferStroops > totalNotes) {
300
- throw new Error("Transfer amount exceeds combined note value");
397
+ const quote = await quoteRequiredFee({
398
+ transactEnvironment: input.environment,
399
+ request: quoteRequestForPrepared(input)
400
+ });
401
+ if (quote.requiredFee <= 0n) {
402
+ return void 0;
403
+ }
404
+ return {
405
+ requiredFee: quote.requiredFee,
406
+ feeCollectorPrivateAddress: quote.feeCollectorPrivateAddress
407
+ };
408
+ }
409
+ function remainingAfterRequiredFee(input) {
410
+ const remaining = input.available - input.instructed - input.requiredFee;
411
+ if (remaining < 0n) {
412
+ throw new Error("Instructed amount and Required Fee exceed available note value");
301
413
  }
302
- return totalNotes - transferStroops;
414
+ return remaining;
303
415
  }
304
416
 
305
417
  // src/transact/audit/parameters.ts
@@ -307,7 +419,7 @@ import {
307
419
  DEMO_AUDIT_PUBLIC_KEY,
308
420
  resolveTransactionAuditParams
309
421
  } from "@arcanetech/stellar-privacy-pool-zk-sdk";
310
- import { Buffer as Buffer5 } from "buffer";
422
+ import { Buffer as Buffer4 } from "buffer";
311
423
  var UNCOMPRESSED_PREFIX = "04";
312
424
  var UNCOMPRESSED_HEX_LENGTH = 130;
313
425
  var XY_HEX_LENGTH = 128;
@@ -334,7 +446,7 @@ function parseAuditPublicKeyFromEnvHex(raw) {
334
446
  "Audit public key must be uncompressed 65-byte hex (04 + X + Y) or 64-byte X+Y hex"
335
447
  );
336
448
  }
337
- const bytes = Buffer5.from(payload, "hex");
449
+ const bytes = Buffer4.from(payload, "hex");
338
450
  const auditPublicKeyXDecimal = BigInt(
339
451
  `0x${bytes.subarray(0, X_OFFSET_END).toString("hex")}`
340
452
  ).toString(DECIMAL_RADIX);
@@ -356,6 +468,7 @@ function buildPoolTransactionAuditParameters(input) {
356
468
  input.nAuditSlots
357
469
  );
358
470
  }
471
+ var DECIMAL_APPLICATION_ID = /^\d+$/u;
359
472
  function resolvePoolApplicationId(applicationId) {
360
473
  const trimmed = applicationId?.trim();
361
474
  if (!trimmed) {
@@ -363,6 +476,11 @@ function resolvePoolApplicationId(applicationId) {
363
476
  "Privacy pool applicationId is required. Pass it in client factory config."
364
477
  );
365
478
  }
479
+ if (!DECIMAL_APPLICATION_ID.test(trimmed)) {
480
+ throw new Error(
481
+ "Privacy pool applicationId must be association.audit_id (decimal Fr), not a UUID or foreignId."
482
+ );
483
+ }
366
484
  return trimmed;
367
485
  }
368
486
 
@@ -371,13 +489,13 @@ import { ed25519PubkeyPayloadHexToWithdrawFrDecimals } from "@arcanetech/stellar
371
489
 
372
490
  // src/transact/stellar/account.ts
373
491
  import { StrKey as StrKey3 } from "@stellar/stellar-sdk";
374
- import { Buffer as Buffer6 } from "buffer";
492
+ import { Buffer as Buffer5 } from "buffer";
375
493
  var MUXED_ACCOUNT_ID_BYTE_LENGTH = 8;
376
494
  var ED25519_PUBLIC_KEY_BYTE_LENGTH = 32;
377
495
  function ed25519HexFromGAddress(trimmed) {
378
496
  try {
379
497
  const raw = StrKey3.decodeEd25519PublicKey(trimmed);
380
- return Buffer6.from(raw).toString("hex");
498
+ return Buffer5.from(raw).toString("hex");
381
499
  } catch {
382
500
  return void 0;
383
501
  }
@@ -434,6 +552,32 @@ function privKeyScalarDecimalFromRecipientScalarHex(hexInput) {
434
552
  return canonicalBabyJubScalarFromInteger(value).toString(DECIMAL_RADIX2);
435
553
  }
436
554
 
555
+ // src/transact/fees/append-fee-output.ts
556
+ function feeCoinFromSlot(slot) {
557
+ return {
558
+ commitment_hex: slot.commitment_hex,
559
+ coin: slot.coin,
560
+ depositScalarHex: slot.depositScalarHex,
561
+ precommitementHex: slot.precommitementHex
562
+ };
563
+ }
564
+ async function appendFeeOutputSlot(input) {
565
+ const requiredFee = input.feeOutput?.requiredFee ?? ZERO_STROOPS;
566
+ const collector = input.feeOutput?.feeCollectorPrivateAddress.trim() ?? "";
567
+ if (requiredFee <= ZERO_STROOPS || !collector) {
568
+ return { deposits: input.deposits };
569
+ }
570
+ const slot = await input.buildAlignedDepositSlot({
571
+ privateAddressStpl1: collector,
572
+ amountStroops: requiredFee,
573
+ tokenAddress: input.tokenAddress
574
+ });
575
+ return {
576
+ deposits: [...input.deposits, slot.deposit],
577
+ feeCoin: feeCoinFromSlot(slot)
578
+ };
579
+ }
580
+
437
581
  // src/transact/proofs/withdraw/helpers.ts
438
582
  function destinationWithdrawFrAndScalar(parameters) {
439
583
  const pkHex = ed25519PublicKeyHexFromStellarAccount(
@@ -488,14 +632,15 @@ async function resolveWithdrawChangeDeposits(parameters) {
488
632
  }
489
633
  };
490
634
  }
491
- function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1) {
635
+ function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1, requiredFee) {
492
636
  if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
493
637
  throw new Error("Withdraw amount must be positive");
494
638
  }
495
- if (withdrawAmountStroops > totalNotes) {
496
- throw new Error("Withdraw amount exceeds combined note value");
497
- }
498
- const changeStroops = totalNotes - withdrawAmountStroops;
639
+ const changeStroops = remainingAfterRequiredFee({
640
+ available: totalNotes,
641
+ instructed: withdrawAmountStroops,
642
+ requiredFee
643
+ });
499
644
  requireChangeRecipientWhenPartial(changeStroops, changePrivateAddressStpl1);
500
645
  return changeStroops;
501
646
  }
@@ -508,11 +653,31 @@ function buildDualWithdrawPublicInput(parameters) {
508
653
  publicWithdrawalAmount: parameters.publicWithdrawalAmount
509
654
  });
510
655
  }
656
+ async function withdrawOutputsWithOptionalFee(parameters) {
657
+ const { deposits: changeDeposits, changeCoin } = await resolveWithdrawChangeDeposits({
658
+ changeStroops: parameters.changeStroops,
659
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
660
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
661
+ tokenAddress: parameters.tokenAddress
662
+ });
663
+ const withFee = await appendFeeOutputSlot({
664
+ deposits: changeDeposits,
665
+ tokenAddress: parameters.tokenAddress,
666
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
667
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
668
+ });
669
+ return {
670
+ deposits: withFee.deposits,
671
+ ...changeCoin ? { changeCoin } : {},
672
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
673
+ };
674
+ }
511
675
  async function prepareDualWithdrawProofInputs(parameters) {
512
676
  const changeStroops = validateDualWithdrawAmounts(
513
677
  parameters.withdrawAmountStroops,
514
678
  BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value),
515
- parameters.changePrivateAddressStpl1
679
+ parameters.changePrivateAddressStpl1,
680
+ parameters.feeOutput?.requiredFee ?? 0n
516
681
  );
517
682
  const { legA, legB } = dualWithdrawLegsWithSharedRoot({
518
683
  sdk: parameters.sdk,
@@ -527,11 +692,12 @@ async function prepareDualWithdrawProofInputs(parameters) {
527
692
  ),
528
693
  applicationId: parameters.applicationId
529
694
  });
530
- const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
695
+ const { deposits, changeCoin, feeCoin } = await withdrawOutputsWithOptionalFee({
531
696
  changeStroops,
532
697
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
533
698
  buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
534
- tokenAddress: parameters.tokenAddress
699
+ tokenAddress: parameters.tokenAddress,
700
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
535
701
  });
536
702
  const publicInput = buildDualWithdrawPublicInput({
537
703
  destinationStellarAddress: parameters.destinationStellarAddress,
@@ -550,19 +716,55 @@ async function prepareDualWithdrawProofInputs(parameters) {
550
716
  audit,
551
717
  deposits,
552
718
  withdrawLegs: [legA.withdrawObject, legB.withdrawObject],
553
- ...changeCoin ? { changeCoin } : {}
719
+ ...changeCoin ? { changeCoin } : {},
720
+ ...feeCoin ? { feeCoin } : {}
721
+ };
722
+ }
723
+
724
+ // src/transact/proofs/withdraw/fee-deposits.ts
725
+ async function withdrawDepositsWithFee(parameters) {
726
+ const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
727
+ changeStroops: parameters.changeStroops,
728
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
729
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
730
+ tokenAddress: parameters.tokenAddress
731
+ });
732
+ const withFee = await appendFeeOutputSlot({
733
+ deposits,
734
+ tokenAddress: parameters.tokenAddress,
735
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
736
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
737
+ });
738
+ return {
739
+ deposits: withFee.deposits,
740
+ ...changeCoin ? { changeCoin } : {},
741
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
554
742
  };
555
743
  }
744
+ function withdrawOutputApplicationIds(input) {
745
+ const outputIds = [];
746
+ if (input.hasChange) {
747
+ outputIds.push(input.applicationId);
748
+ }
749
+ if (input.hasFeeOutput) {
750
+ outputIds.push(input.applicationId);
751
+ }
752
+ if (outputIds.length === 0) {
753
+ outputIds.push("0");
754
+ }
755
+ return outputIds;
756
+ }
556
757
 
557
758
  // src/transact/proofs/withdraw/transact-proof.ts
558
- function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops) {
759
+ function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops, requiredFee) {
559
760
  if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
560
761
  throw new Error("Withdraw amount must be positive");
561
762
  }
562
- if (withdrawAmountStroops > noteStroops) {
563
- throw new Error("Withdraw amount exceeds note value");
564
- }
565
- return noteStroops - withdrawAmountStroops;
763
+ return remainingAfterRequiredFee({
764
+ available: noteStroops,
765
+ instructed: withdrawAmountStroops,
766
+ requiredFee
767
+ });
566
768
  }
567
769
  async function proveSingleWithdrawTransaction(parameters) {
568
770
  return parameters.sdk.proveTransaction(
@@ -601,7 +803,8 @@ function buildPrimaryWithdrawForCoin(parameters) {
601
803
  async function prepareSingleWithdrawProofInputs(parameters) {
602
804
  const changeStroops = validateSingleWithdrawAmounts(
603
805
  parameters.withdrawAmountStroops,
604
- BigInt(parameters.coin.value)
806
+ BigInt(parameters.coin.value),
807
+ parameters.feeOutput?.requiredFee ?? 0n
605
808
  );
606
809
  const { witness, primaryWithdraw } = buildPrimaryWithdrawForCoin({
607
810
  sdk: parameters.sdk,
@@ -610,11 +813,12 @@ async function prepareSingleWithdrawProofInputs(parameters) {
610
813
  state: parameters.state,
611
814
  privKeyScalarHex: parameters.privKeyScalarHex
612
815
  });
613
- const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
816
+ const { deposits, changeCoin, feeCoin } = await withdrawDepositsWithFee({
614
817
  changeStroops,
615
818
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
616
819
  buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
617
- tokenAddress: parameters.tokenAddress
820
+ tokenAddress: parameters.tokenAddress,
821
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
618
822
  });
619
823
  const publicInput = buildWithdrawPublicInput({
620
824
  stateRoot: witness.stateRoot,
@@ -627,6 +831,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
627
831
  primaryWithdraw,
628
832
  deposits,
629
833
  changeCoin,
834
+ feeCoin,
630
835
  publicInput,
631
836
  audit: buildPoolTransactionAuditParameters({
632
837
  applicationId: parameters.applicationId,
@@ -636,7 +841,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
636
841
  };
637
842
  }
638
843
  async function proveWithdrawTransact(parameters) {
639
- const { primaryWithdraw, deposits, changeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
844
+ const { primaryWithdraw, deposits, changeCoin, feeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
640
845
  const proof = await proveSingleWithdrawTransaction({
641
846
  sdk: parameters.sdk,
642
847
  publicInput,
@@ -651,13 +856,17 @@ async function proveWithdrawTransact(parameters) {
651
856
  applicationIdsPlaintext: buildApplicationIdHints({
652
857
  sdk: parameters.sdk,
653
858
  inputIds: [parameters.applicationId],
654
- outputIds: [changeCoin ? parameters.applicationId : "0"]
859
+ outputIds: withdrawOutputApplicationIds({
860
+ applicationId: parameters.applicationId,
861
+ hasChange: Boolean(changeCoin),
862
+ hasFeeOutput: Boolean(feeCoin)
863
+ })
655
864
  }),
656
865
  ...changeCoin ? { changeCoin } : {}
657
866
  };
658
867
  }
659
868
  async function proveWithdrawTransactDual(parameters) {
660
- const { publicInput, audit, withdrawLegs, deposits, changeCoin } = await prepareDualWithdrawProofInputs(parameters);
869
+ const { publicInput, audit, withdrawLegs, deposits, changeCoin, feeCoin } = await prepareDualWithdrawProofInputs(parameters);
661
870
  const proof = await parameters.sdk.proveTransaction(
662
871
  publicInput,
663
872
  padPublicLegsToLayout(
@@ -676,7 +885,11 @@ async function proveWithdrawTransactDual(parameters) {
676
885
  applicationIdsPlaintext: buildApplicationIdHints({
677
886
  sdk: parameters.sdk,
678
887
  inputIds: [audit.applicationId, audit.applicationId],
679
- outputIds: [changeCoin ? audit.applicationId : "0"]
888
+ outputIds: withdrawOutputApplicationIds({
889
+ applicationId: audit.applicationId,
890
+ hasChange: Boolean(changeCoin),
891
+ hasFeeOutput: Boolean(feeCoin)
892
+ })
680
893
  }),
681
894
  ...changeCoin ? { changeCoin } : {}
682
895
  };
@@ -700,6 +913,9 @@ var DEFAULT_ZK_CONFIG_NONCE = BINDING_ZK_NONCE;
700
913
  function resolveZkConfigNonce(transactEnvironment) {
701
914
  return transactEnvironment.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
702
915
  }
916
+ function transactNonceFromArtifacts(artifacts, transactEnvironment) {
917
+ return artifacts?.zkConfigNonce ?? resolveZkConfigNonce(transactEnvironment);
918
+ }
703
919
 
704
920
  // src/transact/zk/browser-artifact-cache.ts
705
921
  import { ZK_ARTIFACT_VERSION } from "@arcanetech/stellar-privacy-pool-zk-sdk";
@@ -952,6 +1168,122 @@ async function buildAlignedDepositSlotForSdk(sdk, applicationId, parameters) {
952
1168
  };
953
1169
  }
954
1170
 
1171
+ // src/transact/pool/initialize-sdk.ts
1172
+ import { PrivacyPoolSDK as PrivacyPoolSDK2 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
1173
+ async function initializePrivacyPoolSdk(input, nonce) {
1174
+ const zkConfigNonce = nonce ?? input.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1175
+ const zkCircuits = await materializeSelectedZkCircuit(
1176
+ input.zkCircuits,
1177
+ zkConfigNonce,
1178
+ input.zkArtifactBaseUrl
1179
+ );
1180
+ return PrivacyPoolSDK2.init({
1181
+ wasmBinary: input.assets.sdkWasm,
1182
+ zkCircuits,
1183
+ zkConfigNonce,
1184
+ ...optionalZkArtifactBaseUrl(input.zkArtifactBaseUrl)
1185
+ });
1186
+ }
1187
+
1188
+ // src/transact/fees/zk-config-nonce-for-kind.ts
1189
+ function configuredNonceSpread(zkConfigNonce) {
1190
+ if (zkConfigNonce === void 0) {
1191
+ return {};
1192
+ }
1193
+ return { configuredNonce: zkConfigNonce };
1194
+ }
1195
+ function zkConfigNonceForFeeBearingKind(input) {
1196
+ if (input.configuredNonce === SIX_BY_SIX_BINDING_ZK_NONCE) {
1197
+ return SIX_BY_SIX_BINDING_ZK_NONCE;
1198
+ }
1199
+ if (input.kind === "transfer" && shouldAttachFeeOutput(input)) {
1200
+ return SIX_BY_SIX_BINDING_ZK_NONCE;
1201
+ }
1202
+ return input.configuredNonce ?? BINDING_ZK_NONCE;
1203
+ }
1204
+
1205
+ // src/transact/pool/prepare-deposit-proof.ts
1206
+ import {
1207
+ randomFrDecimal253,
1208
+ scalarHexToFrDecimal as scalarHexToFrDecimal2
1209
+ } 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
+ function userDepositSlot(input) {
1228
+ return {
1229
+ value: input.coin.value,
1230
+ nullifier: input.coin.nullifier,
1231
+ ephemeralKeyScalar: scalarHexToFrDecimal2(input.depositScalarHex),
1232
+ asset: [input.coin.asset_hi, input.coin.asset_lo],
1233
+ applicationId: input.applicationId,
1234
+ recipientPublicKeys: recipientPublicKeysDecimalFromPrivateAddress(
1235
+ input.privateAddressStpl1
1236
+ )
1237
+ };
1238
+ }
1239
+ function outputApplicationIds(input) {
1240
+ if (input.hasFeeOutput) {
1241
+ return [input.applicationId, input.applicationId];
1242
+ }
1243
+ return [input.applicationId];
1244
+ }
1245
+ async function proveDepositTransact(input) {
1246
+ const publicDeposit = (input.publicDepositStroops ?? BigInt(input.coin.value)).toString();
1247
+ const withFee = await appendFeeOutputSlot({
1248
+ deposits: [userDepositSlot(input)],
1249
+ tokenAddress: input.tokenAddress,
1250
+ buildAlignedDepositSlot: input.buildAlignedDepositSlot,
1251
+ ...input.feeOutput ? { feeOutput: input.feeOutput } : {}
1252
+ });
1253
+ const audit = buildPoolTransactionAuditParameters({
1254
+ applicationId: input.applicationId,
1255
+ nAuditSlots: input.sdk.getLayout().nAuditSlots,
1256
+ ...input.auditPublicKey ? { auditPublicKey: input.auditPublicKey } : {}
1257
+ });
1258
+ const proof = await input.sdk.proveTransaction(
1259
+ withTokenAddressPublicInputs(
1260
+ {
1261
+ stateRoot: merkleRootBufferToFrDecimal(input.merkleRootBytes),
1262
+ privKeyScalar: randomFrDecimal253()
1263
+ },
1264
+ input.tokenAddress
1265
+ ),
1266
+ padPublicLegsToLayout(
1267
+ input.sdk,
1268
+ buildPublicDepositLegs(input.tokenAddress, publicDeposit)
1269
+ ),
1270
+ padWithdrawSlotsToLayout(input.sdk, []),
1271
+ padDepositSlotsToLayout(input.sdk, withFee.deposits),
1272
+ audit
1273
+ );
1274
+ return {
1275
+ ...proof,
1276
+ applicationIdsPlaintext: buildApplicationIdHints({
1277
+ sdk: input.sdk,
1278
+ inputIds: [],
1279
+ outputIds: outputApplicationIds({
1280
+ applicationId: input.applicationId,
1281
+ hasFeeOutput: Boolean(withFee.feeCoin)
1282
+ })
1283
+ })
1284
+ };
1285
+ }
1286
+
955
1287
  // src/transact/pool/service.ts
956
1288
  var PrivacyPoolService = class {
957
1289
  constructor(assets, applicationId, auditPublicKey, zkCircuits, zkConfigNonce, zkArtifactBaseUrl) {
@@ -968,42 +1300,50 @@ var PrivacyPoolService = class {
968
1300
  zkCircuits;
969
1301
  zkConfigNonce;
970
1302
  zkArtifactBaseUrl;
971
- sdk = void 0;
972
- initPromise = void 0;
973
- async ensureInit() {
974
- if (this.sdk) {
975
- return;
1303
+ sdks = /* @__PURE__ */ new Map();
1304
+ initPromises = /* @__PURE__ */ new Map();
1305
+ async getInitializedSdk(nonce) {
1306
+ const zkConfigNonce = nonce ?? this.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1307
+ const key = zkConfigNonce.toString();
1308
+ const existing = this.sdks.get(key);
1309
+ if (existing) {
1310
+ return existing;
976
1311
  }
977
- if (this.initPromise) {
978
- return this.initPromise;
1312
+ const pending = this.initPromises.get(key);
1313
+ if (pending) {
1314
+ return pending;
1315
+ }
1316
+ const started = this.initializeSdk(zkConfigNonce);
1317
+ this.initPromises.set(key, started);
1318
+ try {
1319
+ const sdk = await started;
1320
+ this.sdks.set(key, sdk);
1321
+ return sdk;
1322
+ } finally {
1323
+ this.initPromises.delete(key);
979
1324
  }
980
- this.initPromise = (async () => {
981
- if (!this.assets) {
982
- throw new Error(
983
- "PrivacyPoolService requires browser ZK assets before initialization."
984
- );
985
- }
986
- const zkConfigNonce = this.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
987
- const zkCircuits = await materializeSelectedZkCircuit(
988
- this.zkCircuits,
989
- zkConfigNonce,
990
- this.zkArtifactBaseUrl
991
- );
992
- this.sdk = await PrivacyPoolSDK2.init({
993
- wasmBinary: this.assets.sdkWasm,
994
- zkCircuits,
995
- zkConfigNonce,
996
- ...optionalZkArtifactBaseUrl(this.zkArtifactBaseUrl)
997
- });
998
- })();
999
- return this.initPromise;
1000
1325
  }
1001
- async getInitializedSdk() {
1002
- await this.ensureInit();
1003
- if (!this.sdk) {
1004
- throw new Error("SDK not initialized");
1326
+ feeBearingZkConfigNonce(input) {
1327
+ return zkConfigNonceForFeeBearingKind({
1328
+ ...input,
1329
+ ...configuredNonceSpread(this.zkConfigNonce)
1330
+ });
1331
+ }
1332
+ async initializeSdk(nonce) {
1333
+ if (!this.assets) {
1334
+ throw new Error(
1335
+ "PrivacyPoolService requires browser ZK assets before initialization."
1336
+ );
1005
1337
  }
1006
- return this.sdk;
1338
+ return initializePrivacyPoolSdk(
1339
+ {
1340
+ assets: this.assets,
1341
+ ...this.zkCircuits ? { zkCircuits: this.zkCircuits } : {},
1342
+ ...this.zkConfigNonce === void 0 ? {} : { zkConfigNonce: this.zkConfigNonce },
1343
+ ...optionalZkArtifactBaseUrl(this.zkArtifactBaseUrl)
1344
+ },
1345
+ nonce
1346
+ );
1007
1347
  }
1008
1348
  getAuditPublicKey() {
1009
1349
  return this.auditPublicKey;
@@ -1036,65 +1376,51 @@ var PrivacyPoolService = class {
1036
1376
  return { commitment_hex, coin, depositScalarHex, precommitementHex };
1037
1377
  }
1038
1378
  async prepareDepositTransactProof(parameters) {
1039
- const sdk = await this.getInitializedSdk();
1040
- const stateRoot = merkleRootBufferToFrDecimal(parameters.merkleRootBytes);
1041
- const recipientPublicKeys = recipientPublicKeysDecimalFromPrivateAddress(
1042
- parameters.privateAddressStpl1
1043
- );
1044
- const applicationId = this.getApplicationId();
1045
- const audit = buildPoolTransactionAuditParameters({
1046
- applicationId,
1047
- nAuditSlots: sdk.getLayout().nAuditSlots,
1048
- ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {}
1049
- });
1050
- const deposit = {
1051
- value: parameters.coin.value,
1052
- nullifier: parameters.coin.nullifier,
1053
- ephemeralKeyScalar: scalarHexToFrDecimal2(parameters.depositScalarHex),
1054
- asset: [parameters.coin.asset_hi, parameters.coin.asset_lo],
1055
- applicationId,
1056
- recipientPublicKeys
1057
- };
1058
- const publicInput = withTokenAddressPublicInputs(
1059
- {
1060
- stateRoot,
1061
- privKeyScalar: randomFrDecimal253()
1062
- },
1063
- parameters.tokenAddress
1064
- );
1065
- const proof = await sdk.proveTransaction(
1066
- publicInput,
1067
- padPublicLegsToLayout(
1068
- sdk,
1069
- buildPublicDepositLegs(parameters.tokenAddress, parameters.coin.value)
1070
- ),
1071
- padWithdrawSlotsToLayout(sdk, []),
1072
- padDepositSlotsToLayout(sdk, [deposit]),
1073
- audit
1379
+ const sdk = await this.getInitializedSdk(
1380
+ this.feeBearingZkConfigNonce({ kind: "deposit" })
1074
1381
  );
1075
- const applicationIdsPlaintext = buildApplicationIdHints({
1382
+ return proveDepositTransact({
1076
1383
  sdk,
1077
- inputIds: [],
1078
- outputIds: [applicationId]
1384
+ applicationId: this.getApplicationId(),
1385
+ privateAddressStpl1: parameters.privateAddressStpl1,
1386
+ coin: parameters.coin,
1387
+ depositScalarHex: parameters.depositScalarHex,
1388
+ merkleRootBytes: parameters.merkleRootBytes,
1389
+ tokenAddress: parameters.tokenAddress,
1390
+ buildAlignedDepositSlot: (input) => this.buildAlignedDepositSlot(input),
1391
+ ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {},
1392
+ ...parameters.publicDepositStroops === void 0 ? {} : { publicDepositStroops: parameters.publicDepositStroops },
1393
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1079
1394
  });
1080
- return { ...proof, applicationIdsPlaintext };
1081
1395
  }
1082
1396
  async calculateNullifierHash(nullifier, privKeyScalar) {
1083
1397
  const sdk = await this.getInitializedSdk();
1084
1398
  return sdk.calculateNullifierHash(nullifier, privKeyScalar);
1085
1399
  }
1086
1400
  async prepareWithdrawTransactProof(parameters) {
1087
- const sdk = await this.getInitializedSdk();
1401
+ const sdk = await this.getInitializedSdk(
1402
+ this.feeBearingZkConfigNonce({ kind: "withdraw" })
1403
+ );
1088
1404
  return proveWithdrawTransact({
1089
1405
  sdk,
1090
1406
  applicationId: this.getApplicationId(),
1091
1407
  buildAlignedDepositSlot: (input) => this.buildAlignedDepositSlot(input),
1092
1408
  ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {},
1093
- ...parameters
1409
+ coin: parameters.coin,
1410
+ state: parameters.state,
1411
+ destinationStellarAddress: parameters.destinationStellarAddress,
1412
+ privKeyScalarHex: parameters.privKeyScalarHex,
1413
+ depositorEphemeralKey: parameters.depositorEphemeralKey,
1414
+ withdrawAmountStroops: parameters.withdrawAmountStroops,
1415
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1416
+ tokenAddress: parameters.tokenAddress,
1417
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1094
1418
  });
1095
1419
  }
1096
1420
  async prepareWithdrawTransactProofDual(parameters) {
1097
- const sdk = await this.getInitializedSdk();
1421
+ const sdk = await this.getInitializedSdk(
1422
+ this.feeBearingZkConfigNonce({ kind: "withdraw" })
1423
+ );
1098
1424
  return proveWithdrawTransactDual({
1099
1425
  sdk,
1100
1426
  applicationId: this.getApplicationId(),
@@ -1109,7 +1435,8 @@ var PrivacyPoolService = class {
1109
1435
  ephemeralBKey: parameters.depositorEphemeralBKey,
1110
1436
  withdrawAmountStroops: parameters.withdrawAmountStroops,
1111
1437
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1112
- tokenAddress: parameters.tokenAddress
1438
+ tokenAddress: parameters.tokenAddress,
1439
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1113
1440
  });
1114
1441
  }
1115
1442
  };
@@ -1814,7 +2141,7 @@ async function requestKytPassageForPoolInteraction(input) {
1814
2141
  const applicationId = resolvePoolApplicationId(
1815
2142
  input.transactEnvironment.network.applicationId
1816
2143
  );
1817
- const zkConfigNonce = resolveZkConfigNonce(input.transactEnvironment);
2144
+ const zkConfigNonce = input.zkConfigNonce ?? resolveZkConfigNonce(input.transactEnvironment);
1818
2145
  const applicationIdsPlaintext = input.applicationIdsPlaintext ?? buildApplicationIdHints2(
1819
2146
  applicationId,
1820
2147
  layoutForKnownNonce(zkConfigNonce).nAuditSlots
@@ -1927,6 +2254,7 @@ async function inspectRelayerSweepKyt(input) {
1927
2254
  ...input.prepared.transactArtifacts?.applicationIdsPlaintext ? {
1928
2255
  applicationIdsPlaintext: input.prepared.transactArtifacts.applicationIdsPlaintext
1929
2256
  } : {},
2257
+ ...input.prepared.transactArtifacts?.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.prepared.transactArtifacts.zkConfigNonce },
1930
2258
  networkPassphrase: environment.network.networkPassphrase,
1931
2259
  sorobanRpcUrl: environment.network.rpcUrl,
1932
2260
  transactEnvironment: environment
@@ -1939,7 +2267,10 @@ async function simulateSweepAuthEntries(input) {
1939
2267
  const operation = new Contract(environment.network.poolContract).call(
1940
2268
  "transact",
1941
2269
  new Address3(input.relayerPublicKey).toScVal(),
1942
- nativeToScVal2(resolveZkConfigNonce(environment), { type: "u64" }),
2270
+ nativeToScVal2(
2271
+ transactNonceFromArtifacts(input.prepared.transactArtifacts, environment),
2272
+ { type: "u64" }
2273
+ ),
1943
2274
  nativeToScVal2(hexToBytes(input.artifacts.proofHex), { type: "bytes" }),
1944
2275
  nativeToScVal2(hexToBytes(input.artifacts.publicHex), { type: "bytes" }),
1945
2276
  nativeToScVal2(hexToBytes(input.artifacts.ciphertextHex ?? ""), { type: "bytes" }),
@@ -1976,6 +2307,7 @@ async function unsignedEscrowAuthorizationForSweep(input) {
1976
2307
  });
1977
2308
  const auth = await simulateSweepAuthEntries({
1978
2309
  artifacts,
2310
+ prepared: input.prepared,
1979
2311
  transactEnvironment: input.transactEnvironment,
1980
2312
  relayerPublicKey,
1981
2313
  approval
@@ -2007,15 +2339,15 @@ async function loadDefaultStellarBrowserAssets(options) {
2007
2339
  }
2008
2340
 
2009
2341
  // src/transact/private-address/temporary-recipient.ts
2010
- import { PrivacyPoolSDK as PrivacyPoolSDK3 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
2342
+ import { PrivacyPoolSDK as PrivacyPoolSDK4 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
2011
2343
  var COORDINATE_HEX_LENGTH2 = 64;
2012
2344
  async function generateRandomDepositScalarHex() {
2013
2345
  await getPrivacyPoolService().getInitializedSdk();
2014
- return PrivacyPoolSDK3.generateRandomScalarHex32();
2346
+ return PrivacyPoolSDK4.generateRandomScalarHex32();
2015
2347
  }
2016
2348
  async function generateTemporaryRecipientPrivateAddress() {
2017
2349
  await getPrivacyPoolService().getInitializedSdk();
2018
- const temporaryScalarHex = PrivacyPoolSDK3.generateRandomScalarHex32();
2350
+ const temporaryScalarHex = PrivacyPoolSDK4.generateRandomScalarHex32();
2019
2351
  const sdk = await getPrivacyPoolService().getInitializedSdk();
2020
2352
  const point = sdk.ecdhEphemeralPublicKeyFromScalarHex(temporaryScalarHex);
2021
2353
  const publicKeyXHex = point.x.padStart(COORDINATE_HEX_LENGTH2, "0");
@@ -2284,6 +2616,10 @@ async function submitSorobanConfidentialTransfer(input) {
2284
2616
  networkPassphrase: input.networkPassphrase,
2285
2617
  sorobanRpcUrl: input.sorobanRpcUrl
2286
2618
  });
2619
+ const nonce = transactNonceFromArtifacts(
2620
+ input.zkConfigNonce === void 0 ? void 0 : { zkConfigNonce: input.zkConfigNonce },
2621
+ input.transactEnvironment
2622
+ );
2287
2623
  const approval = await requestKytPassageForPoolInteraction({
2288
2624
  owner: input.walletPublicKey,
2289
2625
  poolContract: input.contractId,
@@ -2292,6 +2628,7 @@ async function submitSorobanConfidentialTransfer(input) {
2292
2628
  ...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
2293
2629
  ...input.outputNoteEphemeralScalars ? { outputNoteEphemeralScalars: input.outputNoteEphemeralScalars } : {},
2294
2630
  ...input.applicationIdsPlaintext ? { applicationIdsPlaintext: input.applicationIdsPlaintext } : {},
2631
+ ...input.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.zkConfigNonce },
2295
2632
  networkPassphrase: input.networkPassphrase,
2296
2633
  sorobanRpcUrl: input.sorobanRpcUrl,
2297
2634
  transactEnvironment: input.transactEnvironment
@@ -2300,7 +2637,7 @@ async function submitSorobanConfidentialTransfer(input) {
2300
2637
  contractClient: poolClient,
2301
2638
  contractId: input.contractId,
2302
2639
  from: input.walletPublicKey,
2303
- nonce: resolveZkConfigNonce(input.transactEnvironment),
2640
+ nonce,
2304
2641
  proofHex: input.proofHex,
2305
2642
  publicHex: input.publicHex,
2306
2643
  ...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
@@ -2322,9 +2659,6 @@ function escrowOutputFields(input) {
2322
2659
  recipientLo: input.escrowSend.recipientLo
2323
2660
  };
2324
2661
  }
2325
- function paddingDepositPair(recipientDeposit) {
2326
- return [recipientDeposit, "dummy"];
2327
- }
2328
2662
  async function buildChangeDepositPair(parameters) {
2329
2663
  const changeSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
2330
2664
  privateAddressStpl1: parameters.selfPrivateAddressStpl1ForChange.trim(),
@@ -2341,34 +2675,54 @@ async function buildChangeDepositPair(parameters) {
2341
2675
  }
2342
2676
  };
2343
2677
  }
2678
+ function recipientOnlyDeposits(recipientDeposit, hasFeeOutput) {
2679
+ if (hasFeeOutput) {
2680
+ return [recipientDeposit];
2681
+ }
2682
+ return [recipientDeposit, "dummy"];
2683
+ }
2684
+ async function depositsWithoutFee(input) {
2685
+ if (input.changeStroops <= ZERO_STROOPS) {
2686
+ return {
2687
+ deposits: recipientOnlyDeposits(input.recipientDeposit, input.hasFeeOutput)
2688
+ };
2689
+ }
2690
+ return buildChangeDepositPair({
2691
+ selfPrivateAddressStpl1ForChange: input.selfPrivateAddressStpl1ForChange,
2692
+ changeStroops: input.changeStroops,
2693
+ tokenAddress: input.tokenAddress,
2694
+ recipientDeposit: input.recipientDeposit
2695
+ });
2696
+ }
2344
2697
  async function buildRecipientAndOptionalChangeDeposits(parameters) {
2345
2698
  const escrow = {
2346
2699
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
2347
2700
  ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
2348
2701
  };
2349
- const recipientPrivateAddress = parameters.recipientPrivateAddressStpl1.trim();
2350
2702
  const recipientSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
2351
- privateAddressStpl1: recipientPrivateAddress,
2703
+ privateAddressStpl1: parameters.recipientPrivateAddressStpl1.trim(),
2352
2704
  amountStroops: parameters.transferStroops,
2353
2705
  tokenAddress: parameters.tokenAddress,
2354
2706
  ...escrowOutputFields(escrow)
2355
2707
  });
2356
- if (parameters.changeStroops <= ZERO_STROOPS) {
2357
- return {
2358
- recipientSlot,
2359
- deposits: paddingDepositPair(recipientSlot.deposit)
2360
- };
2361
- }
2362
- const changeResult = await buildChangeDepositPair({
2363
- selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
2708
+ const withoutFee = await depositsWithoutFee({
2709
+ recipientDeposit: recipientSlot.deposit,
2364
2710
  changeStroops: parameters.changeStroops,
2711
+ selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
2712
+ tokenAddress: parameters.tokenAddress,
2713
+ hasFeeOutput: (parameters.feeOutput?.requiredFee ?? ZERO_STROOPS) > ZERO_STROOPS
2714
+ });
2715
+ const withFee = await appendFeeOutputSlot({
2716
+ deposits: withoutFee.deposits,
2365
2717
  tokenAddress: parameters.tokenAddress,
2366
- recipientDeposit: recipientSlot.deposit
2718
+ buildAlignedDepositSlot: (aligned) => getPrivacyPoolService().buildAlignedDepositSlot(aligned),
2719
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
2367
2720
  });
2368
2721
  return {
2369
2722
  recipientSlot,
2370
- deposits: changeResult.deposits,
2371
- changeCoin: changeResult.changeCoin
2723
+ deposits: withFee.deposits,
2724
+ ..."changeCoin" in withoutFee ? { changeCoin: withoutFee.changeCoin } : {},
2725
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
2372
2726
  };
2373
2727
  }
2374
2728
 
@@ -2421,14 +2775,15 @@ function sweepOutputOwnerFields(parameters) {
2421
2775
  return { sweepOutputOwnerPubX, sweepOutputOwnerPubY };
2422
2776
  }
2423
2777
  async function buildTransferDepositsAndPublicInput(parameters) {
2424
- const { recipientSlot, deposits, changeCoin } = await buildRecipientAndOptionalChangeDeposits({
2778
+ const { recipientSlot, deposits, changeCoin, feeCoin } = await buildRecipientAndOptionalChangeDeposits({
2425
2779
  recipientPrivateAddressStpl1: parameters.recipientPrivateAddressStpl1,
2426
2780
  transferStroops: parameters.transferStroops,
2427
2781
  changeStroops: parameters.changeStroops,
2428
2782
  selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
2429
2783
  tokenAddress: parameters.tokenAddress,
2430
2784
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
2431
- ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
2785
+ ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
2786
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
2432
2787
  });
2433
2788
  const publicInput = withTokenAddressPublicInputs(
2434
2789
  {
@@ -2443,6 +2798,7 @@ async function buildTransferDepositsAndPublicInput(parameters) {
2443
2798
  recipientSlot,
2444
2799
  deposits,
2445
2800
  ...changeCoin ? { changeCoin } : {},
2801
+ ...feeCoin ? { feeCoin } : {},
2446
2802
  publicInput
2447
2803
  };
2448
2804
  }
@@ -2458,7 +2814,8 @@ async function buildSenderTransferDepositsAndPublicInput(parameters) {
2458
2814
  parameters.senderPrivKeyScalarHex
2459
2815
  ),
2460
2816
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
2461
- ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
2817
+ ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
2818
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
2462
2819
  });
2463
2820
  }
2464
2821
 
@@ -2482,11 +2839,20 @@ function stampDualWithdrawLegs(legs, parameters) {
2482
2839
  }
2483
2840
  };
2484
2841
  }
2485
- function buildDualTransferApplicationIds(sdk, applicationId, changeCoin) {
2842
+ function buildDualTransferApplicationIds(sdk, applicationId, changeCoin, feeCoin) {
2843
+ const outputIds = [applicationId];
2844
+ if (changeCoin) {
2845
+ outputIds.push(applicationId);
2846
+ } else if (!feeCoin) {
2847
+ outputIds.push("0");
2848
+ }
2849
+ if (feeCoin) {
2850
+ outputIds.push(applicationId);
2851
+ }
2486
2852
  return buildApplicationIdHints({
2487
2853
  sdk,
2488
2854
  inputIds: [applicationId, applicationId],
2489
- outputIds: [applicationId, changeCoin ? applicationId : "0"]
2855
+ outputIds
2490
2856
  });
2491
2857
  }
2492
2858
  async function proveDualConfidentialTransfer(parameters) {
@@ -2506,16 +2872,24 @@ async function proveDualConfidentialTransfer(parameters) {
2506
2872
  audit
2507
2873
  );
2508
2874
  }
2509
- async function buildDualTransferProofContext(parameters, applicationId, sdk) {
2875
+ function dualTransferChangeStroops(parameters) {
2510
2876
  const totalNotes = BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value);
2511
- const changeStroops = changeStroopsAfterDualTransfer(
2512
- parameters.transferStroops,
2513
- totalNotes
2514
- );
2877
+ if (parameters.transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
2878
+ throw new Error("Transfer amount must be positive");
2879
+ }
2880
+ const changeStroops = remainingAfterRequiredFee({
2881
+ available: totalNotes,
2882
+ instructed: parameters.transferStroops,
2883
+ requiredFee: parameters.feeOutput?.requiredFee ?? 0n
2884
+ });
2515
2885
  requireChangeRecipientWhenPartial(
2516
2886
  changeStroops,
2517
2887
  parameters.selfPrivateAddressStpl1ForChange
2518
2888
  );
2889
+ return changeStroops;
2890
+ }
2891
+ async function buildDualTransferProofContext(parameters, applicationId, sdk) {
2892
+ const changeStroops = dualTransferChangeStroops(parameters);
2519
2893
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
2520
2894
  parameters.senderPrivKeyScalarHex
2521
2895
  );
@@ -2542,15 +2916,21 @@ async function buildDualTransferProofContext(parameters, applicationId, sdk) {
2542
2916
  selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
2543
2917
  tokenAddress: parameters.tokenAddress,
2544
2918
  stateRoot: legA.witness.stateRoot,
2545
- ...confidentialEscrowFields(parameters)
2919
+ ...confidentialEscrowFields(parameters),
2920
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
2546
2921
  });
2547
2922
  return { legA, legB, applicationId, ...transferInputs };
2548
2923
  }
2549
2924
  async function prepareConfidentialTransferProofDual(parameters) {
2550
2925
  const poolService = getPrivacyPoolService();
2551
- const sdk = await poolService.getInitializedSdk();
2926
+ const sdk = await poolService.getInitializedSdk(
2927
+ poolService.feeBearingZkConfigNonce({
2928
+ kind: "transfer",
2929
+ ...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
2930
+ })
2931
+ );
2552
2932
  const applicationId = poolService.getApplicationId();
2553
- const { legA, legB, recipientSlot, deposits, changeCoin, publicInput } = await buildDualTransferProofContext(parameters, applicationId, sdk);
2933
+ const { legA, legB, recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildDualTransferProofContext(parameters, applicationId, sdk);
2554
2934
  const auditPublicKey = poolService.getAuditPublicKey();
2555
2935
  const proof = await (auditPublicKey ? proveDualConfidentialTransfer({
2556
2936
  sdk,
@@ -2573,7 +2953,8 @@ async function prepareConfidentialTransferProofDual(parameters) {
2573
2953
  applicationIdsPlaintext: buildDualTransferApplicationIds(
2574
2954
  sdk,
2575
2955
  applicationId,
2576
- changeCoin
2956
+ changeCoin,
2957
+ feeCoin
2577
2958
  ),
2578
2959
  recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
2579
2960
  ...changeCoin ? { changeCoin } : {}
@@ -2581,22 +2962,32 @@ async function prepareConfidentialTransferProofDual(parameters) {
2581
2962
  }
2582
2963
 
2583
2964
  // src/transact/proofs/confidential/single.ts
2584
- function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange) {
2965
+ function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange, requiredFee) {
2585
2966
  if (transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
2586
2967
  throw new Error("Transfer amount must be positive");
2587
2968
  }
2588
- if (transferStroops > noteStroops) {
2589
- throw new Error("Transfer amount exceeds note value");
2590
- }
2591
- const changeStroops = noteStroops - transferStroops;
2969
+ const changeStroops = remainingAfterRequiredFee({
2970
+ available: noteStroops,
2971
+ instructed: transferStroops,
2972
+ requiredFee
2973
+ });
2592
2974
  requireChangeRecipientWhenPartial(changeStroops, selfPrivateAddressStpl1ForChange);
2593
2975
  return changeStroops;
2594
2976
  }
2595
- function buildSingleTransferApplicationIds(sdk, applicationId, changeCoin) {
2977
+ function buildSingleTransferApplicationIds(sdk, applicationId, changeCoin, feeCoin) {
2978
+ const outputIds = [applicationId];
2979
+ if (changeCoin) {
2980
+ outputIds.push(applicationId);
2981
+ } else if (!feeCoin) {
2982
+ outputIds.push("0");
2983
+ }
2984
+ if (feeCoin) {
2985
+ outputIds.push(applicationId);
2986
+ }
2596
2987
  return buildApplicationIdHints({
2597
2988
  sdk,
2598
2989
  inputIds: [applicationId],
2599
- outputIds: [applicationId, changeCoin ? applicationId : "0"]
2990
+ outputIds
2600
2991
  });
2601
2992
  }
2602
2993
  function buildTransferAuditParameters(parameters) {
@@ -2622,11 +3013,25 @@ function confidentialEscrowFields2(input) {
2622
3013
  ...input.escrowClaimantLimbs ? { escrowClaimantLimbs: input.escrowClaimantLimbs } : {}
2623
3014
  };
2624
3015
  }
3016
+ function transferDepositInputFromProof(input, changeStroops, stateRoot) {
3017
+ return {
3018
+ senderPrivKeyScalarHex: input.senderPrivKeyScalarHex,
3019
+ recipientPrivateAddressStpl1: input.recipientPrivateAddressStpl1,
3020
+ transferStroops: input.transferStroops,
3021
+ changeStroops,
3022
+ selfPrivateAddressStpl1ForChange: input.selfPrivateAddressStpl1ForChange,
3023
+ tokenAddress: input.tokenAddress,
3024
+ stateRoot,
3025
+ ...confidentialEscrowFields2(input),
3026
+ ...input.feeOutput ? { feeOutput: input.feeOutput } : {}
3027
+ };
3028
+ }
2625
3029
  async function buildTransferProofInputs(parameters) {
2626
3030
  const changeStroops = validateSingleCoinTransferAmounts(
2627
3031
  parameters.input.transferStroops,
2628
3032
  parameters.noteStroops,
2629
- parameters.input.selfPrivateAddressStpl1ForChange
3033
+ parameters.input.selfPrivateAddressStpl1ForChange,
3034
+ parameters.input.feeOutput?.requiredFee ?? 0n
2630
3035
  );
2631
3036
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
2632
3037
  parameters.input.senderPrivKeyScalarHex
@@ -2645,31 +3050,30 @@ async function buildTransferProofInputs(parameters) {
2645
3050
  withdrawObject,
2646
3051
  sweepWithdrawStamp(confidentialEscrowFields2(parameters.input))
2647
3052
  );
2648
- const { recipientSlot, deposits, changeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput({
2649
- senderPrivKeyScalarHex: parameters.input.senderPrivKeyScalarHex,
2650
- recipientPrivateAddressStpl1: parameters.input.recipientPrivateAddressStpl1,
2651
- transferStroops: parameters.input.transferStroops,
2652
- changeStroops,
2653
- selfPrivateAddressStpl1ForChange: parameters.input.selfPrivateAddressStpl1ForChange,
2654
- tokenAddress: parameters.input.tokenAddress,
2655
- stateRoot: witness.stateRoot,
2656
- ...confidentialEscrowFields2(parameters.input)
2657
- });
3053
+ const { recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput(
3054
+ transferDepositInputFromProof(parameters.input, changeStroops, witness.stateRoot)
3055
+ );
2658
3056
  return {
2659
3057
  witness,
2660
3058
  withdrawObject: stampedWithdraw,
2661
3059
  recipientSlot,
2662
3060
  deposits,
2663
3061
  changeCoin,
3062
+ feeCoin,
2664
3063
  publicInput
2665
3064
  };
2666
3065
  }
2667
3066
  async function prepareConfidentialTransferProof(parameters) {
2668
3067
  const poolService = getPrivacyPoolService();
2669
- const sdk = await poolService.getInitializedSdk();
3068
+ const sdk = await poolService.getInitializedSdk(
3069
+ poolService.feeBearingZkConfigNonce({
3070
+ kind: "transfer",
3071
+ ...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
3072
+ })
3073
+ );
2670
3074
  const applicationId = poolService.getApplicationId();
2671
3075
  const noteStroops = BigInt(parameters.coin.value);
2672
- const { withdrawObject, recipientSlot, deposits, changeCoin, publicInput } = await buildTransferProofInputs({
3076
+ const { withdrawObject, recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildTransferProofInputs({
2673
3077
  sdk,
2674
3078
  applicationId,
2675
3079
  input: parameters,
@@ -2692,7 +3096,8 @@ async function prepareConfidentialTransferProof(parameters) {
2692
3096
  applicationIdsPlaintext: buildSingleTransferApplicationIds(
2693
3097
  sdk,
2694
3098
  applicationId,
2695
- changeCoin
3099
+ changeCoin,
3100
+ feeCoin
2696
3101
  ),
2697
3102
  recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
2698
3103
  ...changeCoin ? { changeCoin } : {}
@@ -2700,92 +3105,167 @@ async function prepareConfidentialTransferProof(parameters) {
2700
3105
  }
2701
3106
 
2702
3107
  // src/transact/merkle/fetch-contract.ts
3108
+ import { Buffer as Buffer14 } from "buffer";
3109
+
3110
+ // src/transact/merkle/state-port.ts
3111
+ function poolMerkleStateToCachedView(state) {
3112
+ return {
3113
+ commitments: state.commitments,
3114
+ updatedAt: state.updatedAt,
3115
+ merkleRootHex: state.merkleRootHex
3116
+ };
3117
+ }
3118
+
3119
+ // src/transact/merkle/read-tree-leaves.ts
3120
+ import { Address as Address5, xdr as xdr6 } from "@stellar/stellar-sdk";
2703
3121
  import { Buffer as Buffer13 } from "buffer";
2704
3122
 
2705
3123
  // src/transact/merkle/encoding.ts
2706
3124
  import "buffer";
2707
3125
  var FR_ORDER = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n;
2708
- function bufferToDecimal(buf) {
3126
+ function commitmentBufferToDecimal(buf) {
2709
3127
  const hex = buf.toString("hex");
2710
3128
  const bigintFromHex = BigInt(`0x${hex}`);
2711
3129
  const reduced = bigintFromHex % FR_ORDER;
2712
3130
  return reduced.toString();
2713
3131
  }
2714
- function commitmentsBuffersToDecimal(buffers) {
2715
- return buffers.map((bufferRow) => bufferToDecimal(bufferRow));
2716
- }
2717
3132
 
2718
- // src/transact/merkle/state-port.ts
2719
- function poolMerkleStateToCachedView(state) {
2720
- return {
2721
- commitments: state.commitments,
2722
- updatedAt: state.updatedAt,
2723
- merkleRootHex: state.merkleRootHex
2724
- };
2725
- }
2726
-
2727
- // src/transact/merkle/fetch-contract.ts
2728
- function normalizeCommitmentBuffers(rawBuffers) {
2729
- return rawBuffers.map(
2730
- (bufferRow) => Buffer13.isBuffer(bufferRow) ? bufferRow : Buffer13.from(bufferRow)
3133
+ // src/transact/merkle/read-tree-leaves.ts
3134
+ var TREE_LEAF_LEDGER_BATCH_SIZE = 100;
3135
+ function treeLeafLedgerKey(contractId, leafIndex) {
3136
+ return xdr6.LedgerKey.contractData(
3137
+ new xdr6.LedgerKeyContractData({
3138
+ contract: new Address5(contractId).toScAddress(),
3139
+ key: treeLeafKeyScValue(leafIndex),
3140
+ durability: xdr6.ContractDataDurability.persistent()
3141
+ })
2731
3142
  );
2732
3143
  }
2733
- function mergeContractMerkleWithCache(parameters) {
2734
- const cachedCount = parameters.cachedState?.commitments.length ?? 0;
2735
- const shouldPreferContract = parameters.contractCount > cachedCount || !parameters.cachedState || cachedCount === 0;
2736
- if (shouldPreferContract) {
2737
- const updatedAt = Date.now();
2738
- void parameters.poolMerkleState?.set({
2739
- poolContract: parameters.poolContractId,
2740
- commitments: parameters.contractCommitments,
2741
- commitmentCount: parameters.contractCount,
2742
- merkleRootHex: parameters.merkleRootHex,
2743
- updatedAt
2744
- });
2745
- return {
2746
- commitments: parameters.contractCommitments,
2747
- updatedAt,
2748
- merkleRootHex: parameters.merkleRootHex,
2749
- commitmentCount: parameters.contractCount
2750
- };
3144
+ function treeLeafKeyScValue(leafIndex) {
3145
+ return xdr6.ScVal.scvVec([xdr6.ScVal.scvSymbol("Leaf"), xdr6.ScVal.scvU32(leafIndex)]);
3146
+ }
3147
+ function leafIndexFromContractDataKey(key) {
3148
+ if (key.switch() !== xdr6.ScValType.scvVec()) {
3149
+ return void 0;
2751
3150
  }
2752
- return {
2753
- commitments: parameters.cachedState.commitments,
2754
- updatedAt: parameters.cachedState.updatedAt,
2755
- merkleRootHex: parameters.cachedState.merkleRootHex ?? parameters.merkleRootHex,
2756
- commitmentCount: parameters.cachedState.commitments.length
2757
- };
3151
+ const fields = key.vec();
3152
+ if (!fields || fields.length !== 2) {
3153
+ return void 0;
3154
+ }
3155
+ const [tag, indexValue] = fields;
3156
+ if (!tag || tag.switch() !== xdr6.ScValType.scvSymbol()) {
3157
+ return void 0;
3158
+ }
3159
+ if (tag.sym().toString() !== "Leaf") {
3160
+ return void 0;
3161
+ }
3162
+ if (!indexValue || indexValue.switch() !== xdr6.ScValType.scvU32()) {
3163
+ return void 0;
3164
+ }
3165
+ return indexValue.u32();
2758
3166
  }
2759
- async function readCachedPoolMerkleView(poolMerkleState, poolContractId) {
2760
- if (!poolMerkleState) {
3167
+ function commitmentBytesFromContractDataValue(value) {
3168
+ if (value.switch() !== xdr6.ScValType.scvBytes()) {
2761
3169
  return void 0;
2762
3170
  }
2763
- const cached = await poolMerkleState.get(poolContractId);
2764
- return cached ? poolMerkleStateToCachedView(cached) : void 0;
3171
+ return Buffer13.from(value.bytes());
2765
3172
  }
3173
+ async function readTreeLeafCommitments(input) {
3174
+ if (input.startIndex >= input.endIndex) {
3175
+ return [];
3176
+ }
3177
+ const byIndex = await readLeafBuffersByIndex(input);
3178
+ return decimalCommitmentsInRange(byIndex, input.startIndex, input.endIndex);
3179
+ }
3180
+ async function readLeafBuffersByIndex(input) {
3181
+ const keys = ledgerKeysForRange(input.contractId, input.startIndex, input.endIndex);
3182
+ const byIndex = /* @__PURE__ */ new Map();
3183
+ for (const batch of chunkLedgerKeys(keys, TREE_LEAF_LEDGER_BATCH_SIZE)) {
3184
+ const response = await input.reader.getLedgerEntries(...batch);
3185
+ collectLeafBuffers(response.entries, byIndex);
3186
+ }
3187
+ return byIndex;
3188
+ }
3189
+ function ledgerKeysForRange(contractId, startIndex, endIndex) {
3190
+ const keys = [];
3191
+ for (let leafIndex = startIndex; leafIndex < endIndex; leafIndex += 1) {
3192
+ keys.push(treeLeafLedgerKey(contractId, leafIndex));
3193
+ }
3194
+ return keys;
3195
+ }
3196
+ function chunkLedgerKeys(keys, batchSize) {
3197
+ const batches = [];
3198
+ for (let offset = 0; offset < keys.length; offset += batchSize) {
3199
+ batches.push(keys.slice(offset, offset + batchSize));
3200
+ }
3201
+ return batches;
3202
+ }
3203
+ function collectLeafBuffers(entries, byIndex) {
3204
+ for (const entry of entries) {
3205
+ const contractData = entry.val.contractData();
3206
+ const leafIndex = leafIndexFromContractDataKey(contractData.key());
3207
+ const bytes = commitmentBytesFromContractDataValue(contractData.val());
3208
+ if (leafIndex === void 0 || !bytes) {
3209
+ continue;
3210
+ }
3211
+ byIndex.set(leafIndex, bytes);
3212
+ }
3213
+ }
3214
+ function decimalCommitmentsInRange(byIndex, startIndex, endIndex) {
3215
+ const commitments = [];
3216
+ for (let leafIndex = startIndex; leafIndex < endIndex; leafIndex += 1) {
3217
+ const bytes = byIndex.get(leafIndex);
3218
+ if (!bytes) {
3219
+ throw new Error(`Missing Merkle leaf ${String(leafIndex)} in pool storage.`);
3220
+ }
3221
+ commitments.push(commitmentBufferToDecimal(bytes));
3222
+ }
3223
+ return commitments;
3224
+ }
3225
+
3226
+ // src/transact/merkle/reuse-cached-commitments.ts
3227
+ function completeCacheRootMatches(cachedState, merkleRootHex) {
3228
+ const cachedRoot = cachedState?.merkleRootHex;
3229
+ if (cachedRoot === void 0 || cachedRoot === "") {
3230
+ return true;
3231
+ }
3232
+ return cachedRoot === merkleRootHex;
3233
+ }
3234
+ function reuseCachedCommitments(input) {
3235
+ const cachedCommitments = input.cachedState?.commitments ?? [];
3236
+ if (cachedCommitments.length === 0) {
3237
+ return [];
3238
+ }
3239
+ if (cachedCommitments.length > input.onChainCount) {
3240
+ return [];
3241
+ }
3242
+ if (cachedCommitments.length === input.onChainCount) {
3243
+ return completeCacheRootMatches(input.cachedState, input.merkleRootHex) ? cachedCommitments : [];
3244
+ }
3245
+ return cachedCommitments;
3246
+ }
3247
+
3248
+ // src/transact/merkle/fetch-contract.ts
2766
3249
  async function fetchAndMergeMerkleState(parameters) {
2767
- const client = readPoolClientFactory(parameters.transactEnvironment)({
2768
- contractId: parameters.poolContractId,
2769
- walletPublicKey: parameters.walletPublicKey,
2770
- networkPassphrase: parameters.transactEnvironment.network.networkPassphrase,
2771
- sorobanRpcUrl: parameters.transactEnvironment.network.rpcUrl
2772
- });
2773
- const commitmentsRead = await client.get_commitments();
2774
- const rawBuffers = commitmentsRead.result;
2775
- const buffers = normalizeCommitmentBuffers(rawBuffers);
2776
- const contractCommitments = commitmentsBuffersToDecimal(buffers);
2777
- const contractCount = contractCommitments.length;
3250
+ const client = createPoolClient(parameters);
3251
+ const countRead = await client.get_commitment_count();
3252
+ const onChainCount = requireCommitmentCount(countRead.result);
2778
3253
  const rootRead = await client.get_merkle_root();
2779
- const merkleRootBuffer = rootRead.result;
2780
- const merkleRootHex = Buffer13.from(merkleRootBuffer).toString("hex");
3254
+ const merkleRootHex = merkleRootToHex(rootRead.result);
2781
3255
  const cachedState = await readCachedPoolMerkleView(
2782
3256
  parameters.poolMerkleState,
2783
3257
  parameters.poolContractId
2784
3258
  );
2785
- return mergeContractMerkleWithCache({
3259
+ const commitments = await loadCommitments({
3260
+ cachedState,
3261
+ onChainCount,
3262
+ merkleRootHex,
3263
+ rpcUrl: parameters.transactEnvironment.network.rpcUrl,
3264
+ contractId: parameters.poolContractId
3265
+ });
3266
+ return persistFetchedMerkleState({
2786
3267
  poolContractId: parameters.poolContractId,
2787
- contractCommitments,
2788
- contractCount,
3268
+ commitments,
2789
3269
  merkleRootHex,
2790
3270
  cachedState,
2791
3271
  ...parameters.poolMerkleState ? { poolMerkleState: parameters.poolMerkleState } : {}
@@ -2799,19 +3279,14 @@ async function readLeafEphemeralHex(parameters) {
2799
3279
  if (cached?.xHex && cached.yHex) {
2800
3280
  return { xHex: cached.xHex, yHex: cached.yHex };
2801
3281
  }
2802
- const client = readPoolClientFactory(parameters.transactEnvironment)({
2803
- contractId: parameters.poolContractId,
2804
- walletPublicKey: parameters.walletPublicKey,
2805
- networkPassphrase: parameters.transactEnvironment.network.networkPassphrase,
2806
- sorobanRpcUrl: parameters.transactEnvironment.network.rpcUrl
2807
- });
3282
+ const client = createPoolClient(parameters);
2808
3283
  const read = await client.get_leaf_ephemeral({ leaf_index: parameters.leafIndex });
2809
3284
  const coords = read.result;
2810
3285
  if (!coords?.x || !coords?.y) {
2811
3286
  throw new Error("Missing leaf ephemeral key on contract");
2812
3287
  }
2813
- const xHex = Buffer13.from(coords.x).toString("hex");
2814
- const yHex = Buffer13.from(coords.y).toString("hex");
3288
+ const xHex = Buffer14.from(coords.x).toString("hex");
3289
+ const yHex = Buffer14.from(coords.y).toString("hex");
2815
3290
  await parameters.leafEphemeral?.set({
2816
3291
  poolContract: parameters.poolContractId,
2817
3292
  leafIndex: parameters.leafIndex,
@@ -2821,6 +3296,84 @@ async function readLeafEphemeralHex(parameters) {
2821
3296
  });
2822
3297
  return { xHex, yHex };
2823
3298
  }
3299
+ function createPoolClient(parameters) {
3300
+ return readPoolClientFactory(parameters.transactEnvironment)({
3301
+ contractId: parameters.poolContractId,
3302
+ walletPublicKey: parameters.walletPublicKey,
3303
+ networkPassphrase: parameters.transactEnvironment.network.networkPassphrase,
3304
+ sorobanRpcUrl: parameters.transactEnvironment.network.rpcUrl
3305
+ });
3306
+ }
3307
+ function requireCommitmentCount(result) {
3308
+ if (typeof result === "number" && Number.isInteger(result) && result >= 0) {
3309
+ return result;
3310
+ }
3311
+ throw new Error("Pool commitment count is not a non-negative integer.");
3312
+ }
3313
+ function merkleRootToHex(root) {
3314
+ return Buffer14.from(root).toString("hex");
3315
+ }
3316
+ async function readCachedPoolMerkleView(poolMerkleState, poolContractId) {
3317
+ if (!poolMerkleState) {
3318
+ return void 0;
3319
+ }
3320
+ const cached = await poolMerkleState.get(poolContractId);
3321
+ return cached ? poolMerkleStateToCachedView(cached) : void 0;
3322
+ }
3323
+ async function loadCommitments(input) {
3324
+ const reused = reuseCachedCommitments({
3325
+ cachedState: input.cachedState,
3326
+ onChainCount: input.onChainCount,
3327
+ merkleRootHex: input.merkleRootHex
3328
+ });
3329
+ if (reused.length === input.onChainCount) {
3330
+ return reused;
3331
+ }
3332
+ const fetched = await readTreeLeafCommitments({
3333
+ reader: createStellarRpcServer(input.rpcUrl),
3334
+ contractId: input.contractId,
3335
+ startIndex: reused.length,
3336
+ endIndex: input.onChainCount
3337
+ });
3338
+ return [...reused, ...fetched];
3339
+ }
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
+ }
2824
3377
 
2825
3378
  // src/transact/encoding/fr-decimal-to-bytes.ts
2826
3379
  var BN254_SCALAR_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
@@ -4921,10 +5474,10 @@ function readTransferFromPrivateAddress(from) {
4921
5474
  import { executionError, insufficientStateError as insufficientStateError2 } from "@arcanetech/privacy-sdk-core";
4922
5475
 
4923
5476
  // src/contracts/pool/pool-domain-service.ts
4924
- import { Buffer as Buffer14 } from "buffer";
5477
+ import { Buffer as Buffer15 } from "buffer";
4925
5478
  function hexToBuffer(hex) {
4926
5479
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
4927
- return Buffer14.from(clean, "hex");
5480
+ return Buffer15.from(clean, "hex");
4928
5481
  }
4929
5482
  async function readNullifierConsumedOnChain(input) {
4930
5483
  const client = input.contractContext.createPoolClient({
@@ -5109,7 +5662,12 @@ function proofEscrowFields(input) {
5109
5662
  }
5110
5663
  async function buildSingleTransferProofAtExecute(input) {
5111
5664
  const context = await buildSpendProofContextAtExecute(input);
5112
- const changeStroops = BigInt(context.coin.value) - input.prepared.intent.amount;
5665
+ const feeOutput = await quoteFeeOutputForPrepared({
5666
+ prepared: input.prepared,
5667
+ environment: input.environment,
5668
+ tokenAddress: context.tokenAddress
5669
+ });
5670
+ const changeStroops = BigInt(context.coin.value) - input.prepared.intent.amount - (feeOutput?.requiredFee ?? 0n);
5113
5671
  const proof = await prepareConfidentialTransferProof({
5114
5672
  coin: context.coin,
5115
5673
  state: { commitments: context.commitments },
@@ -5122,7 +5680,8 @@ async function buildSingleTransferProofAtExecute(input) {
5122
5680
  recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
5123
5681
  selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
5124
5682
  tokenAddress: context.tokenAddress,
5125
- ...proofEscrowFields(input)
5683
+ ...proofEscrowFields(input),
5684
+ ...feeOutput ? { feeOutput } : {}
5126
5685
  });
5127
5686
  return { context, proof };
5128
5687
  }
@@ -5139,7 +5698,12 @@ async function buildDualTransferProofAtExecute(input) {
5139
5698
  commitments: context.commitments
5140
5699
  });
5141
5700
  const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
5142
- const changeStroops = totalNotes - input.prepared.intent.amount;
5701
+ const feeOutput = await quoteFeeOutputForPrepared({
5702
+ prepared: input.prepared,
5703
+ environment: input.environment,
5704
+ tokenAddress: context.tokenAddress
5705
+ });
5706
+ const changeStroops = totalNotes - input.prepared.intent.amount - (feeOutput?.requiredFee ?? 0n);
5143
5707
  const proof = await prepareConfidentialTransferProofDual({
5144
5708
  coinA: context.coin,
5145
5709
  coinB: secondary.coin,
@@ -5157,7 +5721,8 @@ async function buildDualTransferProofAtExecute(input) {
5157
5721
  recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
5158
5722
  selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
5159
5723
  tokenAddress: context.tokenAddress,
5160
- ...proofEscrowFields(input)
5724
+ ...proofEscrowFields(input),
5725
+ ...feeOutput ? { feeOutput } : {}
5161
5726
  });
5162
5727
  return { context, proof };
5163
5728
  }
@@ -5289,11 +5854,17 @@ async function runTransferFinalizeSteps(input) {
5289
5854
  walletPublicKey: input.walletPublicKey,
5290
5855
  ...recipient.escrowSend ? { escrowSendPrivateAddress: recipient.recipientPrivateAddressStpl1 } : {}
5291
5856
  });
5292
- return buildTransferFinalizeArtifacts({
5293
- proof,
5294
- context,
5295
- ...sweepFinalizeStamp(input.prepared, recipient)
5296
- });
5857
+ return {
5858
+ ...buildTransferFinalizeArtifacts({
5859
+ proof,
5860
+ context,
5861
+ ...sweepFinalizeStamp(input.prepared, recipient)
5862
+ }),
5863
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
5864
+ ...feeOutputKindFromPrepared(input.prepared),
5865
+ ...configuredNonceSpread(input.environment.zkConfigNonce)
5866
+ })
5867
+ };
5297
5868
  }
5298
5869
  function sweepArtifactStamp(artifacts) {
5299
5870
  return {
@@ -5321,6 +5892,24 @@ async function finalizeTransferAtExecute(prepared, environment) {
5321
5892
  return runTransferFinalizeSteps({ prepared, environment, walletPublicKey });
5322
5893
  }
5323
5894
 
5895
+ // src/transact/engine/withdraw-fee.ts
5896
+ async function withdrawFeeProofFields(input) {
5897
+ const feeOutput = await quoteFeeOutputForPrepared({
5898
+ prepared: input.prepared,
5899
+ environment: input.environment,
5900
+ tokenAddress: input.tokenAddress
5901
+ });
5902
+ const changeStroops = remainingAfterRequiredFee({
5903
+ available: input.available,
5904
+ instructed: input.prepared.intent.amount,
5905
+ requiredFee: feeOutput?.requiredFee ?? 0n
5906
+ });
5907
+ return {
5908
+ ...feeOutput ? { feeOutput } : {},
5909
+ ...changeStroops > 0n ? { changePrivateAddressStpl1: input.withdrawFrom } : {}
5910
+ };
5911
+ }
5912
+
5324
5913
  // src/transact/engine/execute.ts
5325
5914
  function enrichWithdrawOutputRecords(prepared, proof) {
5326
5915
  if (!proof.changeCoin || prepared.outputRecords.length === 0) {
@@ -5336,7 +5925,7 @@ function enrichWithdrawOutputRecords(prepared, proof) {
5336
5925
  precommitementHex: changeCoin.precommitementHex
5337
5926
  }));
5338
5927
  }
5339
- function buildWithdrawFinalizeArtifacts(proof, context) {
5928
+ function buildWithdrawFinalizeArtifacts(proof, context, environment) {
5340
5929
  return {
5341
5930
  proofHex: proof.proof_hex,
5342
5931
  publicHex: proof.public_hex,
@@ -5344,7 +5933,11 @@ function buildWithdrawFinalizeArtifacts(proof, context) {
5344
5933
  applicationIdsPlaintext: proof.applicationIdsPlaintext,
5345
5934
  tokenAddress: context.tokenAddress,
5346
5935
  walletPublicKey: context.walletPublicKey,
5347
- executeFinalizeRequired: false
5936
+ executeFinalizeRequired: false,
5937
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
5938
+ kind: "withdraw",
5939
+ ...configuredNonceSpread(environment.zkConfigNonce)
5940
+ })
5348
5941
  };
5349
5942
  }
5350
5943
  async function finalizeSingleWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
@@ -5353,7 +5946,13 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
5353
5946
  environment,
5354
5947
  recipientPrivateAddressStpl1: withdrawFrom
5355
5948
  });
5356
- const changeStroops = BigInt(context.coin.value) - prepared.intent.amount;
5949
+ const feeFields = await withdrawFeeProofFields({
5950
+ prepared,
5951
+ environment,
5952
+ tokenAddress: context.tokenAddress,
5953
+ available: BigInt(context.coin.value),
5954
+ withdrawFrom
5955
+ });
5357
5956
  const proof = await poolService.prepareWithdrawTransactProof({
5358
5957
  coin: context.coin,
5359
5958
  state: { commitments: context.commitments },
@@ -5364,11 +5963,24 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
5364
5963
  yHex: context.ephemeral.yHex
5365
5964
  }),
5366
5965
  withdrawAmountStroops: prepared.intent.amount,
5367
- changePrivateAddressStpl1: changeStroops > 0n ? withdrawFrom : void 0,
5368
- tokenAddress: context.tokenAddress
5966
+ changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
5967
+ tokenAddress: context.tokenAddress,
5968
+ ...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
5369
5969
  });
5370
5970
  enrichWithdrawOutputRecords(prepared, proof);
5371
- return buildWithdrawFinalizeArtifacts(proof, context);
5971
+ return buildWithdrawFinalizeArtifacts(proof, context, environment);
5972
+ }
5973
+ function dualWithdrawEphemeralKeys(input) {
5974
+ return {
5975
+ depositorEphemeralAKey: serializeEphemeralKeyString({
5976
+ xHex: input.context.ephemeral.xHex,
5977
+ yHex: input.context.ephemeral.yHex
5978
+ }),
5979
+ depositorEphemeralBKey: serializeEphemeralKeyString({
5980
+ xHex: input.secondary.ephemeral.xHex,
5981
+ yHex: input.secondary.ephemeral.yHex
5982
+ })
5983
+ };
5372
5984
  }
5373
5985
  async function finalizeDualWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
5374
5986
  const [, secondaryRecord] = prepared.consumedRecords;
@@ -5386,28 +5998,27 @@ async function finalizeDualWithdrawAtExecute(prepared, environment, poolService,
5386
5998
  walletPublicKey: context.walletPublicKey,
5387
5999
  commitments: context.commitments
5388
6000
  });
5389
- const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
5390
- const changeStroops = totalNotes - prepared.intent.amount;
6001
+ const feeFields = await withdrawFeeProofFields({
6002
+ prepared,
6003
+ environment,
6004
+ tokenAddress: context.tokenAddress,
6005
+ available: BigInt(context.coin.value) + BigInt(secondary.coin.value),
6006
+ withdrawFrom
6007
+ });
5391
6008
  const proof = await poolService.prepareWithdrawTransactProofDual({
5392
6009
  coinA: context.coin,
5393
6010
  coinB: secondary.coin,
5394
6011
  state: { commitments: context.commitments },
5395
6012
  destinationStellarAddress: prepared.intent.to,
5396
6013
  privKeyScalarHex: context.senderPrivKeyScalarHex,
5397
- depositorEphemeralAKey: serializeEphemeralKeyString({
5398
- xHex: context.ephemeral.xHex,
5399
- yHex: context.ephemeral.yHex
5400
- }),
5401
- depositorEphemeralBKey: serializeEphemeralKeyString({
5402
- xHex: secondary.ephemeral.xHex,
5403
- yHex: secondary.ephemeral.yHex
5404
- }),
6014
+ ...dualWithdrawEphemeralKeys({ context, secondary }),
5405
6015
  withdrawAmountStroops: prepared.intent.amount,
5406
- changePrivateAddressStpl1: changeStroops > 0n ? withdrawFrom : void 0,
5407
- tokenAddress: context.tokenAddress
6016
+ changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
6017
+ tokenAddress: context.tokenAddress,
6018
+ ...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
5408
6019
  });
5409
6020
  enrichWithdrawOutputRecords(prepared, proof);
5410
- return buildWithdrawFinalizeArtifacts(proof, context);
6021
+ return buildWithdrawFinalizeArtifacts(proof, context, environment);
5411
6022
  }
5412
6023
  async function finalizeWithdrawAtExecute(prepared, environment, poolService) {
5413
6024
  if (prepared.kind !== "withdraw") {
@@ -5471,7 +6082,7 @@ var RELAY_SIGNAL_INDEX_PUBLIC_DEPOSIT = 91;
5471
6082
  var RELAY_SIGNAL_INDEX_PUBLIC_WITHDRAWAL = 92;
5472
6083
 
5473
6084
  // src/transact/relay/signals.ts
5474
- import { Buffer as Buffer15 } from "buffer";
6085
+ import { Buffer as Buffer16 } from "buffer";
5475
6086
 
5476
6087
  // src/transact/relay/layout-profile.ts
5477
6088
  var FIELD_BYTES = 32;
@@ -5551,7 +6162,7 @@ function relayLayoutProfileForNonce(nonce) {
5551
6162
 
5552
6163
  // src/transact/relay/signals.ts
5553
6164
  function decodePublicSignalBytes(publicSignals) {
5554
- return Buffer15.from(publicSignals.replace(/^0x/iu, ""), "hex");
6165
+ return Buffer16.from(publicSignals.replace(/^0x/iu, ""), "hex");
5555
6166
  }
5556
6167
  function hasSupportedLengthPrefix(bytes, signalCount) {
5557
6168
  const expected = RELAY_TRANSACT_SIGNAL_PREFIX_BYTES + signalCount * RELAY_TRANSACT_FIELD_BYTES;
@@ -5578,7 +6189,7 @@ function sliceSupportedPublicSignalFields(publicSignals, zkConfigNonce = DEFAULT
5578
6189
  const body = hasSupportedLengthPrefix(bytes, profile.signalCount) ? bytes.subarray(RELAY_TRANSACT_SIGNAL_PREFIX_BYTES) : bytes;
5579
6190
  return Array.from(
5580
6191
  { length: profile.signalCount },
5581
- (_unused, index) => Buffer15.from(
6192
+ (_unused, index) => Buffer16.from(
5582
6193
  body.subarray(
5583
6194
  index * RELAY_TRANSACT_FIELD_BYTES,
5584
6195
  (index + 1) * RELAY_TRANSACT_FIELD_BYTES
@@ -5633,10 +6244,11 @@ function requirePreparedArtifacts(prepared) {
5633
6244
  };
5634
6245
  }
5635
6246
  function prepareRelayTransactPackageFromPrepared(input) {
6247
+ const zkConfigNonce = input.zkConfigNonce ?? input.prepared.transactArtifacts?.zkConfigNonce;
5636
6248
  return prepareRelayTransactPackage({
5637
6249
  poolSelector: input.poolSelector,
5638
6250
  ...requirePreparedArtifacts(input.prepared),
5639
- ...input.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.zkConfigNonce },
6251
+ ...zkConfigNonce === void 0 ? {} : { zkConfigNonce },
5640
6252
  ...input.keyVersionHints ? { keyVersionHints: input.keyVersionHints } : {}
5641
6253
  });
5642
6254
  }
@@ -5746,6 +6358,7 @@ function requiredSubmissionMethod(prepared, zkConfigNonce = DEFAULT_ZK_CONFIG_NO
5746
6358
  return result.method;
5747
6359
  }
5748
6360
  export {
6361
+ BINDING_ZK_NONCE,
5749
6362
  BundledZkCircuit2 as BundledZkCircuit,
5750
6363
  COMMITMENT_V2_ZK_NONCE,
5751
6364
  DEFAULT_STEALTH_SIGN_NONCE as DEFAULT_PRIVATE_ADDRESS_SIGN_NONCE,
@@ -5774,6 +6387,7 @@ export {
5774
6387
  RELAY_TRANSACT_PACKAGE_VERSION_V1,
5775
6388
  RELAY_TRANSACT_SIGNAL_PREFIX_BYTES,
5776
6389
  RELAY_TRANSACT_SUPPORTED_SIGNAL_COUNT,
6390
+ SIX_BY_SIX_BINDING_ZK_NONCE,
5777
6391
  SIX_BY_SIX_ZK_NONCE,
5778
6392
  STANDARD_ZK_CONFIG_NONCE,
5779
6393
  SUBMISSION_METHOD,