@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.
package/dist/index.js CHANGED
@@ -454,27 +454,9 @@ function buildPrivateRecordFromDeposit(input) {
454
454
 
455
455
  // src/transact/pool/service.ts
456
456
  import {
457
- PrivacyPoolSDK as PrivacyPoolSDK2,
458
- decodeDecodedEphemeralKey,
459
- randomFrDecimal253,
460
- scalarHexToFrDecimal as scalarHexToFrDecimal2
457
+ decodeDecodedEphemeralKey
461
458
  } from "@arcanetech/stellar-privacy-pool-zk-sdk";
462
459
 
463
- // src/transact/merkle/field-decimal.ts
464
- import { Buffer as Buffer4 } from "buffer";
465
- var BN254 = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
466
- var MERKLE_ROOT_BYTE_LENGTH = 32;
467
- var DECIMAL_STRING_RADIX = 10;
468
- function merkleRootBufferToFrDecimal(root) {
469
- if (root.length !== MERKLE_ROOT_BYTE_LENGTH) {
470
- throw new Error(
471
- `expected ${MERKLE_ROOT_BYTE_LENGTH}-byte merkle root, got ${root.length}`
472
- );
473
- }
474
- const hex = Buffer4.from(root).toString("hex");
475
- return (BigInt(`0x${hex}`) % BN254).toString(DECIMAL_STRING_RADIX);
476
- }
477
-
478
460
  // src/transact/private-address/codec.ts
479
461
  import {
480
462
  buildStealthAddressSignMessage,
@@ -530,18 +512,18 @@ function privateAddressSdk(sdk) {
530
512
  import { withdrawObjectFromMerkleWitness as withdrawObjectFromMerkleWitness2 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
531
513
 
532
514
  // src/transact/proofs/transaction-input.ts
533
- import { Buffer as Buffer6 } from "buffer";
515
+ import { Buffer as Buffer5 } from "buffer";
534
516
  import { StrKey as StrKey2 } from "@stellar/stellar-sdk";
535
517
 
536
518
  // src/transact/stellar/token-contract.ts
537
- import { Buffer as Buffer5 } from "buffer";
519
+ import { Buffer as Buffer4 } from "buffer";
538
520
  import { StrKey } from "@stellar/stellar-sdk";
539
521
  var CONTRACT_ID_BYTE_LENGTH = 32;
540
522
  var HEX_CHARS_PER_BYTE = 2;
541
523
  var CONTRACT_ID_HEX_CHAR_LENGTH = CONTRACT_ID_BYTE_LENGTH * HEX_CHARS_PER_BYTE;
542
524
  var CONTRACT_ID_HEX = /^(?:0x)?([0-9a-f]{64})$/i;
543
525
  function encodeContractFromHex(hexPayload) {
544
- return StrKey.encodeContract(Buffer5.from(hexPayload, "hex"));
526
+ return StrKey.encodeContract(Buffer4.from(hexPayload, "hex"));
545
527
  }
546
528
  function normalizeSorobanContractId(raw) {
547
529
  const trimmed = raw.trim();
@@ -574,14 +556,14 @@ var ZERO_PUBLIC_WITHDRAW_ADDRESS = {
574
556
  var CONTRACT_ADDRESS_HALF_BYTES = 16;
575
557
  var CONTRACT_ADDRESS_BYTES = 32;
576
558
  function bytes16ToDecimal(bytes) {
577
- return BigInt(`0x${Buffer6.from(bytes).toString("hex")}`).toString();
559
+ return BigInt(`0x${Buffer5.from(bytes).toString("hex")}`).toString();
578
560
  }
579
561
  function decimalToBytes16(value) {
580
562
  const hex = BigInt(value).toString(16).padStart(CONTRACT_ADDRESS_HALF_BYTES * 2, "0");
581
563
  if (hex.length > CONTRACT_ADDRESS_HALF_BYTES * 2) {
582
564
  throw new Error("Asset leg does not fit into 16 bytes.");
583
565
  }
584
- return Buffer6.from(hex, "hex");
566
+ return Buffer5.from(hex, "hex");
585
567
  }
586
568
  function tokenAddressFrParts(tokenAddress) {
587
569
  const normalized = resolveTokenContractId(tokenAddress);
@@ -623,7 +605,7 @@ function tokenAddressToAssetLeg(tokenAddress) {
623
605
  return [hi, lo];
624
606
  }
625
607
  function assetLegToTokenAddress(assetHi, assetLo) {
626
- const payload = Buffer6.concat([decimalToBytes16(assetHi), decimalToBytes16(assetLo)]);
608
+ const payload = Buffer5.concat([decimalToBytes16(assetHi), decimalToBytes16(assetLo)]);
627
609
  return StrKey2.encodeContract(payload);
628
610
  }
629
611
  function buildZeroPublicLegs(publicNInputs = 1, publicNOutputs = 1) {
@@ -746,14 +728,144 @@ function requireChangeRecipientWhenPartial(changeStroops, selfPrivateAddressStpl
746
728
  }
747
729
  }
748
730
  }
749
- function changeStroopsAfterDualTransfer(transferStroops, totalNotes) {
750
- if (transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
751
- throw new Error("Transfer amount must be positive");
731
+
732
+ // src/transact/fees/quote-request-body.ts
733
+ function presentInstructedAmount(amount) {
734
+ const trimmed = amount?.trim() ?? "";
735
+ if (!trimmed || trimmed === "0") {
736
+ return void 0;
737
+ }
738
+ return trimmed;
739
+ }
740
+ function feeQuoteRequestBody(request) {
741
+ const publicDepositAmount = presentInstructedAmount(request.publicDepositAmount);
742
+ const transferInstructedAmount = presentInstructedAmount(
743
+ request.transferInstructedAmount
744
+ );
745
+ const publicWithdrawalAmount = presentInstructedAmount(
746
+ request.publicWithdrawalAmount
747
+ );
748
+ return {
749
+ feeAsset: request.feeAsset,
750
+ spendsNotes: request.spendsNotes,
751
+ ...publicDepositAmount === void 0 ? {} : { publicDepositAmount },
752
+ ...transferInstructedAmount === void 0 ? {} : { transferInstructedAmount },
753
+ ...publicWithdrawalAmount === void 0 ? {} : { publicWithdrawalAmount }
754
+ };
755
+ }
756
+
757
+ // src/transact/fees/quote-required-fee.ts
758
+ function quoteHeaders(inspectAuthorization) {
759
+ const token = inspectAuthorization?.trim();
760
+ return {
761
+ "content-type": "application/json",
762
+ ...token ? { authorization: `Bearer ${token}` } : {}
763
+ };
764
+ }
765
+ function parseRequiredFee(raw) {
766
+ const text = `${raw ?? ""}`.trim();
767
+ if (!/^\d+$/u.test(text)) {
768
+ throw new Error("Fee Quote Required Fee must be an integer minor-unit string.");
769
+ }
770
+ return BigInt(text);
771
+ }
772
+ function requireQuoteString(raw, field) {
773
+ const text = `${raw ?? ""}`.trim();
774
+ if (!text) {
775
+ throw new Error(`Fee Quote is missing ${field}.`);
776
+ }
777
+ return text;
778
+ }
779
+ function parseQuotePayload(payload) {
780
+ return {
781
+ requiredFee: parseRequiredFee(payload.requiredFee),
782
+ feeAsset: requireQuoteString(payload.feeAsset, "Fee Asset"),
783
+ feeCollectorPrivateAddress: requireQuoteString(
784
+ payload.feeCollectorPrivateAddress,
785
+ "Fee Collector Private Address"
786
+ ),
787
+ feeRate: requireQuoteString(payload.feeRate, "Fee Rate")
788
+ };
789
+ }
790
+ async function quoteRequiredFee(input) {
791
+ const response = await fetch(
792
+ `${input.transactEnvironment.kyt.apiBaseUrl}/kyt/fees/quote`,
793
+ {
794
+ method: "POST",
795
+ headers: quoteHeaders(input.transactEnvironment.kyt.inspectAuthorization),
796
+ body: JSON.stringify(feeQuoteRequestBody(input.request))
797
+ }
798
+ );
799
+ const payload = await response.json();
800
+ if (!response.ok) {
801
+ throw new Error(`Fee Quote failed: ${JSON.stringify(payload)}`);
802
+ }
803
+ return parseQuotePayload(payload);
804
+ }
805
+
806
+ // src/transact/fees/should-attach-fee-output.ts
807
+ function shouldAttachFeeOutput(input) {
808
+ if (input.spendSource === "escrow" && input.escrowSend !== true) {
809
+ return false;
810
+ }
811
+ return input.kind === "deposit" || input.kind === "transfer" || input.kind === "withdraw";
812
+ }
813
+ function feeOutputKindFromPrepared(prepared) {
814
+ return {
815
+ kind: prepared.kind,
816
+ ...prepared.transactArtifacts?.spendSource ? { spendSource: prepared.transactArtifacts.spendSource } : {},
817
+ ...prepared.transactArtifacts?.escrowSend ? { escrowSend: true } : {}
818
+ };
819
+ }
820
+
821
+ // src/transact/fees/quote-fee-output-for-prepared.ts
822
+ function instructedMinorUnits(prepared) {
823
+ return prepared.intent.amount.toString();
824
+ }
825
+ function quoteRequestForPrepared(input) {
826
+ const amount = instructedMinorUnits(input.prepared);
827
+ if (input.prepared.kind === "deposit") {
828
+ return {
829
+ feeAsset: input.tokenAddress,
830
+ publicDepositAmount: amount,
831
+ spendsNotes: false
832
+ };
752
833
  }
753
- if (transferStroops > totalNotes) {
754
- throw new Error("Transfer amount exceeds combined note value");
834
+ if (input.prepared.kind === "withdraw") {
835
+ return {
836
+ feeAsset: input.tokenAddress,
837
+ publicWithdrawalAmount: amount,
838
+ spendsNotes: true
839
+ };
755
840
  }
756
- return totalNotes - transferStroops;
841
+ return {
842
+ feeAsset: input.tokenAddress,
843
+ transferInstructedAmount: amount,
844
+ spendsNotes: true
845
+ };
846
+ }
847
+ async function quoteFeeOutputForPrepared(input) {
848
+ if (!shouldAttachFeeOutput(feeOutputKindFromPrepared(input.prepared))) {
849
+ return void 0;
850
+ }
851
+ const quote = await quoteRequiredFee({
852
+ transactEnvironment: input.environment,
853
+ request: quoteRequestForPrepared(input)
854
+ });
855
+ if (quote.requiredFee <= 0n) {
856
+ return void 0;
857
+ }
858
+ return {
859
+ requiredFee: quote.requiredFee,
860
+ feeCollectorPrivateAddress: quote.feeCollectorPrivateAddress
861
+ };
862
+ }
863
+ function remainingAfterRequiredFee(input) {
864
+ const remaining = input.available - input.instructed - input.requiredFee;
865
+ if (remaining < 0n) {
866
+ throw new Error("Instructed amount and Required Fee exceed available note value");
867
+ }
868
+ return remaining;
757
869
  }
758
870
 
759
871
  // src/transact/audit/parameters.ts
@@ -761,7 +873,7 @@ import {
761
873
  DEMO_AUDIT_PUBLIC_KEY,
762
874
  resolveTransactionAuditParams
763
875
  } from "@arcanetech/stellar-privacy-pool-zk-sdk";
764
- import { Buffer as Buffer7 } from "buffer";
876
+ import { Buffer as Buffer6 } from "buffer";
765
877
  var UNCOMPRESSED_PREFIX = "04";
766
878
  var UNCOMPRESSED_HEX_LENGTH = 130;
767
879
  var XY_HEX_LENGTH = 128;
@@ -788,7 +900,7 @@ function parseAuditPublicKeyFromEnvHex(raw) {
788
900
  "Audit public key must be uncompressed 65-byte hex (04 + X + Y) or 64-byte X+Y hex"
789
901
  );
790
902
  }
791
- const bytes = Buffer7.from(payload, "hex");
903
+ const bytes = Buffer6.from(payload, "hex");
792
904
  const auditPublicKeyXDecimal = BigInt(
793
905
  `0x${bytes.subarray(0, X_OFFSET_END).toString("hex")}`
794
906
  ).toString(DECIMAL_RADIX);
@@ -810,6 +922,7 @@ function buildPoolTransactionAuditParameters(input) {
810
922
  input.nAuditSlots
811
923
  );
812
924
  }
925
+ var DECIMAL_APPLICATION_ID = /^\d+$/u;
813
926
  function resolvePoolApplicationId(applicationId) {
814
927
  const trimmed = applicationId?.trim();
815
928
  if (!trimmed) {
@@ -817,6 +930,11 @@ function resolvePoolApplicationId(applicationId) {
817
930
  "Privacy pool applicationId is required. Pass it in client factory config."
818
931
  );
819
932
  }
933
+ if (!DECIMAL_APPLICATION_ID.test(trimmed)) {
934
+ throw new Error(
935
+ "Privacy pool applicationId must be association.audit_id (decimal Fr), not a UUID or foreignId."
936
+ );
937
+ }
820
938
  return trimmed;
821
939
  }
822
940
 
@@ -825,13 +943,13 @@ import { ed25519PubkeyPayloadHexToWithdrawFrDecimals } from "@arcanetech/stellar
825
943
 
826
944
  // src/transact/stellar/account.ts
827
945
  import { StrKey as StrKey3 } from "@stellar/stellar-sdk";
828
- import { Buffer as Buffer8 } from "buffer";
946
+ import { Buffer as Buffer7 } from "buffer";
829
947
  var MUXED_ACCOUNT_ID_BYTE_LENGTH = 8;
830
948
  var ED25519_PUBLIC_KEY_BYTE_LENGTH = 32;
831
949
  function ed25519HexFromGAddress(trimmed) {
832
950
  try {
833
951
  const raw = StrKey3.decodeEd25519PublicKey(trimmed);
834
- return Buffer8.from(raw).toString("hex");
952
+ return Buffer7.from(raw).toString("hex");
835
953
  } catch {
836
954
  return void 0;
837
955
  }
@@ -888,6 +1006,32 @@ function privKeyScalarDecimalFromRecipientScalarHex(hexInput) {
888
1006
  return canonicalBabyJubScalarFromInteger(value).toString(DECIMAL_RADIX2);
889
1007
  }
890
1008
 
1009
+ // src/transact/fees/append-fee-output.ts
1010
+ function feeCoinFromSlot(slot) {
1011
+ return {
1012
+ commitment_hex: slot.commitment_hex,
1013
+ coin: slot.coin,
1014
+ depositScalarHex: slot.depositScalarHex,
1015
+ precommitementHex: slot.precommitementHex
1016
+ };
1017
+ }
1018
+ async function appendFeeOutputSlot(input) {
1019
+ const requiredFee = input.feeOutput?.requiredFee ?? ZERO_STROOPS;
1020
+ const collector = input.feeOutput?.feeCollectorPrivateAddress.trim() ?? "";
1021
+ if (requiredFee <= ZERO_STROOPS || !collector) {
1022
+ return { deposits: input.deposits };
1023
+ }
1024
+ const slot = await input.buildAlignedDepositSlot({
1025
+ privateAddressStpl1: collector,
1026
+ amountStroops: requiredFee,
1027
+ tokenAddress: input.tokenAddress
1028
+ });
1029
+ return {
1030
+ deposits: [...input.deposits, slot.deposit],
1031
+ feeCoin: feeCoinFromSlot(slot)
1032
+ };
1033
+ }
1034
+
891
1035
  // src/transact/proofs/withdraw/helpers.ts
892
1036
  function destinationWithdrawFrAndScalar(parameters) {
893
1037
  const pkHex = ed25519PublicKeyHexFromStellarAccount(
@@ -942,14 +1086,15 @@ async function resolveWithdrawChangeDeposits(parameters) {
942
1086
  }
943
1087
  };
944
1088
  }
945
- function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1) {
1089
+ function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1, requiredFee) {
946
1090
  if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
947
1091
  throw new Error("Withdraw amount must be positive");
948
1092
  }
949
- if (withdrawAmountStroops > totalNotes) {
950
- throw new Error("Withdraw amount exceeds combined note value");
951
- }
952
- const changeStroops = totalNotes - withdrawAmountStroops;
1093
+ const changeStroops = remainingAfterRequiredFee({
1094
+ available: totalNotes,
1095
+ instructed: withdrawAmountStroops,
1096
+ requiredFee
1097
+ });
953
1098
  requireChangeRecipientWhenPartial(changeStroops, changePrivateAddressStpl1);
954
1099
  return changeStroops;
955
1100
  }
@@ -962,11 +1107,31 @@ function buildDualWithdrawPublicInput(parameters) {
962
1107
  publicWithdrawalAmount: parameters.publicWithdrawalAmount
963
1108
  });
964
1109
  }
1110
+ async function withdrawOutputsWithOptionalFee(parameters) {
1111
+ const { deposits: changeDeposits, changeCoin } = await resolveWithdrawChangeDeposits({
1112
+ changeStroops: parameters.changeStroops,
1113
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1114
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1115
+ tokenAddress: parameters.tokenAddress
1116
+ });
1117
+ const withFee = await appendFeeOutputSlot({
1118
+ deposits: changeDeposits,
1119
+ tokenAddress: parameters.tokenAddress,
1120
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1121
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1122
+ });
1123
+ return {
1124
+ deposits: withFee.deposits,
1125
+ ...changeCoin ? { changeCoin } : {},
1126
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
1127
+ };
1128
+ }
965
1129
  async function prepareDualWithdrawProofInputs(parameters) {
966
1130
  const changeStroops = validateDualWithdrawAmounts(
967
1131
  parameters.withdrawAmountStroops,
968
1132
  BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value),
969
- parameters.changePrivateAddressStpl1
1133
+ parameters.changePrivateAddressStpl1,
1134
+ parameters.feeOutput?.requiredFee ?? 0n
970
1135
  );
971
1136
  const { legA, legB } = dualWithdrawLegsWithSharedRoot({
972
1137
  sdk: parameters.sdk,
@@ -981,11 +1146,12 @@ async function prepareDualWithdrawProofInputs(parameters) {
981
1146
  ),
982
1147
  applicationId: parameters.applicationId
983
1148
  });
984
- const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
1149
+ const { deposits, changeCoin, feeCoin } = await withdrawOutputsWithOptionalFee({
985
1150
  changeStroops,
986
1151
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
987
1152
  buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
988
- tokenAddress: parameters.tokenAddress
1153
+ tokenAddress: parameters.tokenAddress,
1154
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
989
1155
  });
990
1156
  const publicInput = buildDualWithdrawPublicInput({
991
1157
  destinationStellarAddress: parameters.destinationStellarAddress,
@@ -1004,19 +1170,55 @@ async function prepareDualWithdrawProofInputs(parameters) {
1004
1170
  audit,
1005
1171
  deposits,
1006
1172
  withdrawLegs: [legA.withdrawObject, legB.withdrawObject],
1007
- ...changeCoin ? { changeCoin } : {}
1173
+ ...changeCoin ? { changeCoin } : {},
1174
+ ...feeCoin ? { feeCoin } : {}
1175
+ };
1176
+ }
1177
+
1178
+ // src/transact/proofs/withdraw/fee-deposits.ts
1179
+ async function withdrawDepositsWithFee(parameters) {
1180
+ const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
1181
+ changeStroops: parameters.changeStroops,
1182
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1183
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1184
+ tokenAddress: parameters.tokenAddress
1185
+ });
1186
+ const withFee = await appendFeeOutputSlot({
1187
+ deposits,
1188
+ tokenAddress: parameters.tokenAddress,
1189
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1190
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1191
+ });
1192
+ return {
1193
+ deposits: withFee.deposits,
1194
+ ...changeCoin ? { changeCoin } : {},
1195
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
1008
1196
  };
1009
1197
  }
1198
+ function withdrawOutputApplicationIds(input) {
1199
+ const outputIds = [];
1200
+ if (input.hasChange) {
1201
+ outputIds.push(input.applicationId);
1202
+ }
1203
+ if (input.hasFeeOutput) {
1204
+ outputIds.push(input.applicationId);
1205
+ }
1206
+ if (outputIds.length === 0) {
1207
+ outputIds.push("0");
1208
+ }
1209
+ return outputIds;
1210
+ }
1010
1211
 
1011
1212
  // src/transact/proofs/withdraw/transact-proof.ts
1012
- function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops) {
1213
+ function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops, requiredFee) {
1013
1214
  if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
1014
1215
  throw new Error("Withdraw amount must be positive");
1015
1216
  }
1016
- if (withdrawAmountStroops > noteStroops) {
1017
- throw new Error("Withdraw amount exceeds note value");
1018
- }
1019
- return noteStroops - withdrawAmountStroops;
1217
+ return remainingAfterRequiredFee({
1218
+ available: noteStroops,
1219
+ instructed: withdrawAmountStroops,
1220
+ requiredFee
1221
+ });
1020
1222
  }
1021
1223
  async function proveSingleWithdrawTransaction(parameters) {
1022
1224
  return parameters.sdk.proveTransaction(
@@ -1055,7 +1257,8 @@ function buildPrimaryWithdrawForCoin(parameters) {
1055
1257
  async function prepareSingleWithdrawProofInputs(parameters) {
1056
1258
  const changeStroops = validateSingleWithdrawAmounts(
1057
1259
  parameters.withdrawAmountStroops,
1058
- BigInt(parameters.coin.value)
1260
+ BigInt(parameters.coin.value),
1261
+ parameters.feeOutput?.requiredFee ?? 0n
1059
1262
  );
1060
1263
  const { witness, primaryWithdraw } = buildPrimaryWithdrawForCoin({
1061
1264
  sdk: parameters.sdk,
@@ -1064,11 +1267,12 @@ async function prepareSingleWithdrawProofInputs(parameters) {
1064
1267
  state: parameters.state,
1065
1268
  privKeyScalarHex: parameters.privKeyScalarHex
1066
1269
  });
1067
- const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
1270
+ const { deposits, changeCoin, feeCoin } = await withdrawDepositsWithFee({
1068
1271
  changeStroops,
1069
1272
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1070
1273
  buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1071
- tokenAddress: parameters.tokenAddress
1274
+ tokenAddress: parameters.tokenAddress,
1275
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1072
1276
  });
1073
1277
  const publicInput = buildWithdrawPublicInput({
1074
1278
  stateRoot: witness.stateRoot,
@@ -1081,6 +1285,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
1081
1285
  primaryWithdraw,
1082
1286
  deposits,
1083
1287
  changeCoin,
1288
+ feeCoin,
1084
1289
  publicInput,
1085
1290
  audit: buildPoolTransactionAuditParameters({
1086
1291
  applicationId: parameters.applicationId,
@@ -1090,7 +1295,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
1090
1295
  };
1091
1296
  }
1092
1297
  async function proveWithdrawTransact(parameters) {
1093
- const { primaryWithdraw, deposits, changeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
1298
+ const { primaryWithdraw, deposits, changeCoin, feeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
1094
1299
  const proof = await proveSingleWithdrawTransaction({
1095
1300
  sdk: parameters.sdk,
1096
1301
  publicInput,
@@ -1105,13 +1310,17 @@ async function proveWithdrawTransact(parameters) {
1105
1310
  applicationIdsPlaintext: buildApplicationIdHints({
1106
1311
  sdk: parameters.sdk,
1107
1312
  inputIds: [parameters.applicationId],
1108
- outputIds: [changeCoin ? parameters.applicationId : "0"]
1313
+ outputIds: withdrawOutputApplicationIds({
1314
+ applicationId: parameters.applicationId,
1315
+ hasChange: Boolean(changeCoin),
1316
+ hasFeeOutput: Boolean(feeCoin)
1317
+ })
1109
1318
  }),
1110
1319
  ...changeCoin ? { changeCoin } : {}
1111
1320
  };
1112
1321
  }
1113
1322
  async function proveWithdrawTransactDual(parameters) {
1114
- const { publicInput, audit, withdrawLegs, deposits, changeCoin } = await prepareDualWithdrawProofInputs(parameters);
1323
+ const { publicInput, audit, withdrawLegs, deposits, changeCoin, feeCoin } = await prepareDualWithdrawProofInputs(parameters);
1115
1324
  const proof = await parameters.sdk.proveTransaction(
1116
1325
  publicInput,
1117
1326
  padPublicLegsToLayout(
@@ -1130,7 +1339,11 @@ async function proveWithdrawTransactDual(parameters) {
1130
1339
  applicationIdsPlaintext: buildApplicationIdHints({
1131
1340
  sdk: parameters.sdk,
1132
1341
  inputIds: [audit.applicationId, audit.applicationId],
1133
- outputIds: [changeCoin ? audit.applicationId : "0"]
1342
+ outputIds: withdrawOutputApplicationIds({
1343
+ applicationId: audit.applicationId,
1344
+ hasChange: Boolean(changeCoin),
1345
+ hasFeeOutput: Boolean(feeCoin)
1346
+ })
1134
1347
  }),
1135
1348
  ...changeCoin ? { changeCoin } : {}
1136
1349
  };
@@ -1154,6 +1367,9 @@ var DEFAULT_ZK_CONFIG_NONCE = BINDING_ZK_NONCE;
1154
1367
  function resolveZkConfigNonce(transactEnvironment) {
1155
1368
  return transactEnvironment.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1156
1369
  }
1370
+ function transactNonceFromArtifacts(artifacts, transactEnvironment) {
1371
+ return artifacts?.zkConfigNonce ?? resolveZkConfigNonce(transactEnvironment);
1372
+ }
1157
1373
 
1158
1374
  // src/transact/zk/browser-artifact-cache.ts
1159
1375
  import { ZK_ARTIFACT_VERSION } from "@arcanetech/stellar-privacy-pool-zk-sdk";
@@ -1406,6 +1622,122 @@ async function buildAlignedDepositSlotForSdk(sdk, applicationId, parameters) {
1406
1622
  };
1407
1623
  }
1408
1624
 
1625
+ // src/transact/pool/initialize-sdk.ts
1626
+ import { PrivacyPoolSDK as PrivacyPoolSDK2 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
1627
+ async function initializePrivacyPoolSdk(input, nonce) {
1628
+ const zkConfigNonce = nonce ?? input.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1629
+ const zkCircuits = await materializeSelectedZkCircuit(
1630
+ input.zkCircuits,
1631
+ zkConfigNonce,
1632
+ input.zkArtifactBaseUrl
1633
+ );
1634
+ return PrivacyPoolSDK2.init({
1635
+ wasmBinary: input.assets.sdkWasm,
1636
+ zkCircuits,
1637
+ zkConfigNonce,
1638
+ ...optionalZkArtifactBaseUrl(input.zkArtifactBaseUrl)
1639
+ });
1640
+ }
1641
+
1642
+ // src/transact/fees/zk-config-nonce-for-kind.ts
1643
+ function configuredNonceSpread(zkConfigNonce) {
1644
+ if (zkConfigNonce === void 0) {
1645
+ return {};
1646
+ }
1647
+ return { configuredNonce: zkConfigNonce };
1648
+ }
1649
+ function zkConfigNonceForFeeBearingKind(input) {
1650
+ if (input.configuredNonce === SIX_BY_SIX_BINDING_ZK_NONCE) {
1651
+ return SIX_BY_SIX_BINDING_ZK_NONCE;
1652
+ }
1653
+ if (input.kind === "transfer" && shouldAttachFeeOutput(input)) {
1654
+ return SIX_BY_SIX_BINDING_ZK_NONCE;
1655
+ }
1656
+ return input.configuredNonce ?? BINDING_ZK_NONCE;
1657
+ }
1658
+
1659
+ // src/transact/pool/prepare-deposit-proof.ts
1660
+ import {
1661
+ randomFrDecimal253,
1662
+ scalarHexToFrDecimal as scalarHexToFrDecimal2
1663
+ } from "@arcanetech/stellar-privacy-pool-zk-sdk";
1664
+
1665
+ // src/transact/merkle/field-decimal.ts
1666
+ import { Buffer as Buffer8 } from "buffer";
1667
+ var BN254 = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
1668
+ var MERKLE_ROOT_BYTE_LENGTH = 32;
1669
+ var DECIMAL_STRING_RADIX = 10;
1670
+ function merkleRootBufferToFrDecimal(root) {
1671
+ if (root.length !== MERKLE_ROOT_BYTE_LENGTH) {
1672
+ throw new Error(
1673
+ `expected ${MERKLE_ROOT_BYTE_LENGTH}-byte merkle root, got ${root.length}`
1674
+ );
1675
+ }
1676
+ const hex = Buffer8.from(root).toString("hex");
1677
+ return (BigInt(`0x${hex}`) % BN254).toString(DECIMAL_STRING_RADIX);
1678
+ }
1679
+
1680
+ // src/transact/pool/prepare-deposit-proof.ts
1681
+ function userDepositSlot(input) {
1682
+ return {
1683
+ value: input.coin.value,
1684
+ nullifier: input.coin.nullifier,
1685
+ ephemeralKeyScalar: scalarHexToFrDecimal2(input.depositScalarHex),
1686
+ asset: [input.coin.asset_hi, input.coin.asset_lo],
1687
+ applicationId: input.applicationId,
1688
+ recipientPublicKeys: recipientPublicKeysDecimalFromPrivateAddress(
1689
+ input.privateAddressStpl1
1690
+ )
1691
+ };
1692
+ }
1693
+ function outputApplicationIds(input) {
1694
+ if (input.hasFeeOutput) {
1695
+ return [input.applicationId, input.applicationId];
1696
+ }
1697
+ return [input.applicationId];
1698
+ }
1699
+ async function proveDepositTransact(input) {
1700
+ const publicDeposit = (input.publicDepositStroops ?? BigInt(input.coin.value)).toString();
1701
+ const withFee = await appendFeeOutputSlot({
1702
+ deposits: [userDepositSlot(input)],
1703
+ tokenAddress: input.tokenAddress,
1704
+ buildAlignedDepositSlot: input.buildAlignedDepositSlot,
1705
+ ...input.feeOutput ? { feeOutput: input.feeOutput } : {}
1706
+ });
1707
+ const audit = buildPoolTransactionAuditParameters({
1708
+ applicationId: input.applicationId,
1709
+ nAuditSlots: input.sdk.getLayout().nAuditSlots,
1710
+ ...input.auditPublicKey ? { auditPublicKey: input.auditPublicKey } : {}
1711
+ });
1712
+ const proof = await input.sdk.proveTransaction(
1713
+ withTokenAddressPublicInputs(
1714
+ {
1715
+ stateRoot: merkleRootBufferToFrDecimal(input.merkleRootBytes),
1716
+ privKeyScalar: randomFrDecimal253()
1717
+ },
1718
+ input.tokenAddress
1719
+ ),
1720
+ padPublicLegsToLayout(
1721
+ input.sdk,
1722
+ buildPublicDepositLegs(input.tokenAddress, publicDeposit)
1723
+ ),
1724
+ padWithdrawSlotsToLayout(input.sdk, []),
1725
+ padDepositSlotsToLayout(input.sdk, withFee.deposits),
1726
+ audit
1727
+ );
1728
+ return {
1729
+ ...proof,
1730
+ applicationIdsPlaintext: buildApplicationIdHints({
1731
+ sdk: input.sdk,
1732
+ inputIds: [],
1733
+ outputIds: outputApplicationIds({
1734
+ applicationId: input.applicationId,
1735
+ hasFeeOutput: Boolean(withFee.feeCoin)
1736
+ })
1737
+ })
1738
+ };
1739
+ }
1740
+
1409
1741
  // src/transact/pool/service.ts
1410
1742
  var PrivacyPoolService = class {
1411
1743
  constructor(assets, applicationId, auditPublicKey, zkCircuits, zkConfigNonce, zkArtifactBaseUrl) {
@@ -1422,42 +1754,50 @@ var PrivacyPoolService = class {
1422
1754
  zkCircuits;
1423
1755
  zkConfigNonce;
1424
1756
  zkArtifactBaseUrl;
1425
- sdk = void 0;
1426
- initPromise = void 0;
1427
- async ensureInit() {
1428
- if (this.sdk) {
1429
- return;
1757
+ sdks = /* @__PURE__ */ new Map();
1758
+ initPromises = /* @__PURE__ */ new Map();
1759
+ async getInitializedSdk(nonce) {
1760
+ const zkConfigNonce = nonce ?? this.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1761
+ const key = zkConfigNonce.toString();
1762
+ const existing = this.sdks.get(key);
1763
+ if (existing) {
1764
+ return existing;
1430
1765
  }
1431
- if (this.initPromise) {
1432
- return this.initPromise;
1766
+ const pending = this.initPromises.get(key);
1767
+ if (pending) {
1768
+ return pending;
1433
1769
  }
1434
- this.initPromise = (async () => {
1435
- if (!this.assets) {
1436
- throw new Error(
1437
- "PrivacyPoolService requires browser ZK assets before initialization."
1438
- );
1439
- }
1440
- const zkConfigNonce = this.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1441
- const zkCircuits = await materializeSelectedZkCircuit(
1442
- this.zkCircuits,
1443
- zkConfigNonce,
1444
- this.zkArtifactBaseUrl
1445
- );
1446
- this.sdk = await PrivacyPoolSDK2.init({
1447
- wasmBinary: this.assets.sdkWasm,
1448
- zkCircuits,
1449
- zkConfigNonce,
1450
- ...optionalZkArtifactBaseUrl(this.zkArtifactBaseUrl)
1451
- });
1452
- })();
1453
- return this.initPromise;
1770
+ const started = this.initializeSdk(zkConfigNonce);
1771
+ this.initPromises.set(key, started);
1772
+ try {
1773
+ const sdk = await started;
1774
+ this.sdks.set(key, sdk);
1775
+ return sdk;
1776
+ } finally {
1777
+ this.initPromises.delete(key);
1778
+ }
1779
+ }
1780
+ feeBearingZkConfigNonce(input) {
1781
+ return zkConfigNonceForFeeBearingKind({
1782
+ ...input,
1783
+ ...configuredNonceSpread(this.zkConfigNonce)
1784
+ });
1454
1785
  }
1455
- async getInitializedSdk() {
1456
- await this.ensureInit();
1457
- if (!this.sdk) {
1458
- throw new Error("SDK not initialized");
1786
+ async initializeSdk(nonce) {
1787
+ if (!this.assets) {
1788
+ throw new Error(
1789
+ "PrivacyPoolService requires browser ZK assets before initialization."
1790
+ );
1459
1791
  }
1460
- return this.sdk;
1792
+ return initializePrivacyPoolSdk(
1793
+ {
1794
+ assets: this.assets,
1795
+ ...this.zkCircuits ? { zkCircuits: this.zkCircuits } : {},
1796
+ ...this.zkConfigNonce === void 0 ? {} : { zkConfigNonce: this.zkConfigNonce },
1797
+ ...optionalZkArtifactBaseUrl(this.zkArtifactBaseUrl)
1798
+ },
1799
+ nonce
1800
+ );
1461
1801
  }
1462
1802
  getAuditPublicKey() {
1463
1803
  return this.auditPublicKey;
@@ -1490,65 +1830,51 @@ var PrivacyPoolService = class {
1490
1830
  return { commitment_hex, coin, depositScalarHex, precommitementHex };
1491
1831
  }
1492
1832
  async prepareDepositTransactProof(parameters) {
1493
- const sdk = await this.getInitializedSdk();
1494
- const stateRoot = merkleRootBufferToFrDecimal(parameters.merkleRootBytes);
1495
- const recipientPublicKeys = recipientPublicKeysDecimalFromPrivateAddress(
1496
- parameters.privateAddressStpl1
1833
+ const sdk = await this.getInitializedSdk(
1834
+ this.feeBearingZkConfigNonce({ kind: "deposit" })
1497
1835
  );
1498
- const applicationId = this.getApplicationId();
1499
- const audit = buildPoolTransactionAuditParameters({
1500
- applicationId,
1501
- nAuditSlots: sdk.getLayout().nAuditSlots,
1502
- ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {}
1503
- });
1504
- const deposit = {
1505
- value: parameters.coin.value,
1506
- nullifier: parameters.coin.nullifier,
1507
- ephemeralKeyScalar: scalarHexToFrDecimal2(parameters.depositScalarHex),
1508
- asset: [parameters.coin.asset_hi, parameters.coin.asset_lo],
1509
- applicationId,
1510
- recipientPublicKeys
1511
- };
1512
- const publicInput = withTokenAddressPublicInputs(
1513
- {
1514
- stateRoot,
1515
- privKeyScalar: randomFrDecimal253()
1516
- },
1517
- parameters.tokenAddress
1518
- );
1519
- const proof = await sdk.proveTransaction(
1520
- publicInput,
1521
- padPublicLegsToLayout(
1522
- sdk,
1523
- buildPublicDepositLegs(parameters.tokenAddress, parameters.coin.value)
1524
- ),
1525
- padWithdrawSlotsToLayout(sdk, []),
1526
- padDepositSlotsToLayout(sdk, [deposit]),
1527
- audit
1528
- );
1529
- const applicationIdsPlaintext = buildApplicationIdHints({
1836
+ return proveDepositTransact({
1530
1837
  sdk,
1531
- inputIds: [],
1532
- outputIds: [applicationId]
1838
+ applicationId: this.getApplicationId(),
1839
+ privateAddressStpl1: parameters.privateAddressStpl1,
1840
+ coin: parameters.coin,
1841
+ depositScalarHex: parameters.depositScalarHex,
1842
+ merkleRootBytes: parameters.merkleRootBytes,
1843
+ tokenAddress: parameters.tokenAddress,
1844
+ buildAlignedDepositSlot: (input) => this.buildAlignedDepositSlot(input),
1845
+ ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {},
1846
+ ...parameters.publicDepositStroops === void 0 ? {} : { publicDepositStroops: parameters.publicDepositStroops },
1847
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1533
1848
  });
1534
- return { ...proof, applicationIdsPlaintext };
1535
1849
  }
1536
1850
  async calculateNullifierHash(nullifier, privKeyScalar) {
1537
1851
  const sdk = await this.getInitializedSdk();
1538
1852
  return sdk.calculateNullifierHash(nullifier, privKeyScalar);
1539
1853
  }
1540
1854
  async prepareWithdrawTransactProof(parameters) {
1541
- const sdk = await this.getInitializedSdk();
1855
+ const sdk = await this.getInitializedSdk(
1856
+ this.feeBearingZkConfigNonce({ kind: "withdraw" })
1857
+ );
1542
1858
  return proveWithdrawTransact({
1543
1859
  sdk,
1544
1860
  applicationId: this.getApplicationId(),
1545
1861
  buildAlignedDepositSlot: (input) => this.buildAlignedDepositSlot(input),
1546
1862
  ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {},
1547
- ...parameters
1863
+ coin: parameters.coin,
1864
+ state: parameters.state,
1865
+ destinationStellarAddress: parameters.destinationStellarAddress,
1866
+ privKeyScalarHex: parameters.privKeyScalarHex,
1867
+ depositorEphemeralKey: parameters.depositorEphemeralKey,
1868
+ withdrawAmountStroops: parameters.withdrawAmountStroops,
1869
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1870
+ tokenAddress: parameters.tokenAddress,
1871
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1548
1872
  });
1549
1873
  }
1550
1874
  async prepareWithdrawTransactProofDual(parameters) {
1551
- const sdk = await this.getInitializedSdk();
1875
+ const sdk = await this.getInitializedSdk(
1876
+ this.feeBearingZkConfigNonce({ kind: "withdraw" })
1877
+ );
1552
1878
  return proveWithdrawTransactDual({
1553
1879
  sdk,
1554
1880
  applicationId: this.getApplicationId(),
@@ -1563,7 +1889,8 @@ var PrivacyPoolService = class {
1563
1889
  ephemeralBKey: parameters.depositorEphemeralBKey,
1564
1890
  withdrawAmountStroops: parameters.withdrawAmountStroops,
1565
1891
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1566
- tokenAddress: parameters.tokenAddress
1892
+ tokenAddress: parameters.tokenAddress,
1893
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1567
1894
  });
1568
1895
  }
1569
1896
  };
@@ -1593,92 +1920,167 @@ function getPrivacyPoolService() {
1593
1920
  }
1594
1921
 
1595
1922
  // src/transact/merkle/fetch-contract.ts
1923
+ import { Buffer as Buffer12 } from "buffer";
1924
+
1925
+ // src/transact/merkle/state-port.ts
1926
+ function poolMerkleStateToCachedView(state) {
1927
+ return {
1928
+ commitments: state.commitments,
1929
+ updatedAt: state.updatedAt,
1930
+ merkleRootHex: state.merkleRootHex
1931
+ };
1932
+ }
1933
+
1934
+ // src/transact/merkle/read-tree-leaves.ts
1935
+ import { Address, xdr } from "@stellar/stellar-sdk";
1596
1936
  import { Buffer as Buffer11 } from "buffer";
1597
1937
 
1598
1938
  // src/transact/merkle/encoding.ts
1599
1939
  import "buffer";
1600
1940
  var FR_ORDER = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n;
1601
- function bufferToDecimal(buf) {
1941
+ function commitmentBufferToDecimal(buf) {
1602
1942
  const hex = buf.toString("hex");
1603
1943
  const bigintFromHex = BigInt(`0x${hex}`);
1604
1944
  const reduced = bigintFromHex % FR_ORDER;
1605
1945
  return reduced.toString();
1606
1946
  }
1607
- function commitmentsBuffersToDecimal(buffers) {
1608
- return buffers.map((bufferRow) => bufferToDecimal(bufferRow));
1609
- }
1610
1947
 
1611
- // src/transact/merkle/state-port.ts
1612
- function poolMerkleStateToCachedView(state) {
1613
- return {
1614
- commitments: state.commitments,
1615
- updatedAt: state.updatedAt,
1616
- merkleRootHex: state.merkleRootHex
1617
- };
1618
- }
1619
-
1620
- // src/transact/merkle/fetch-contract.ts
1621
- function normalizeCommitmentBuffers(rawBuffers) {
1622
- return rawBuffers.map(
1623
- (bufferRow) => Buffer11.isBuffer(bufferRow) ? bufferRow : Buffer11.from(bufferRow)
1948
+ // src/transact/merkle/read-tree-leaves.ts
1949
+ var TREE_LEAF_LEDGER_BATCH_SIZE = 100;
1950
+ function treeLeafLedgerKey(contractId, leafIndex) {
1951
+ return xdr.LedgerKey.contractData(
1952
+ new xdr.LedgerKeyContractData({
1953
+ contract: new Address(contractId).toScAddress(),
1954
+ key: treeLeafKeyScValue(leafIndex),
1955
+ durability: xdr.ContractDataDurability.persistent()
1956
+ })
1624
1957
  );
1625
1958
  }
1626
- function mergeContractMerkleWithCache(parameters) {
1627
- const cachedCount = parameters.cachedState?.commitments.length ?? 0;
1628
- const shouldPreferContract = parameters.contractCount > cachedCount || !parameters.cachedState || cachedCount === 0;
1629
- if (shouldPreferContract) {
1630
- const updatedAt = Date.now();
1631
- void parameters.poolMerkleState?.set({
1632
- poolContract: parameters.poolContractId,
1633
- commitments: parameters.contractCommitments,
1634
- commitmentCount: parameters.contractCount,
1635
- merkleRootHex: parameters.merkleRootHex,
1636
- updatedAt
1637
- });
1638
- return {
1639
- commitments: parameters.contractCommitments,
1640
- updatedAt,
1641
- merkleRootHex: parameters.merkleRootHex,
1642
- commitmentCount: parameters.contractCount
1643
- };
1959
+ function treeLeafKeyScValue(leafIndex) {
1960
+ return xdr.ScVal.scvVec([xdr.ScVal.scvSymbol("Leaf"), xdr.ScVal.scvU32(leafIndex)]);
1961
+ }
1962
+ function leafIndexFromContractDataKey(key) {
1963
+ if (key.switch() !== xdr.ScValType.scvVec()) {
1964
+ return void 0;
1644
1965
  }
1645
- return {
1646
- commitments: parameters.cachedState.commitments,
1647
- updatedAt: parameters.cachedState.updatedAt,
1648
- merkleRootHex: parameters.cachedState.merkleRootHex ?? parameters.merkleRootHex,
1649
- commitmentCount: parameters.cachedState.commitments.length
1650
- };
1966
+ const fields = key.vec();
1967
+ if (!fields || fields.length !== 2) {
1968
+ return void 0;
1969
+ }
1970
+ const [tag, indexValue] = fields;
1971
+ if (!tag || tag.switch() !== xdr.ScValType.scvSymbol()) {
1972
+ return void 0;
1973
+ }
1974
+ if (tag.sym().toString() !== "Leaf") {
1975
+ return void 0;
1976
+ }
1977
+ if (!indexValue || indexValue.switch() !== xdr.ScValType.scvU32()) {
1978
+ return void 0;
1979
+ }
1980
+ return indexValue.u32();
1651
1981
  }
1652
- async function readCachedPoolMerkleView(poolMerkleState, poolContractId) {
1653
- if (!poolMerkleState) {
1982
+ function commitmentBytesFromContractDataValue(value) {
1983
+ if (value.switch() !== xdr.ScValType.scvBytes()) {
1654
1984
  return void 0;
1655
1985
  }
1656
- const cached = await poolMerkleState.get(poolContractId);
1657
- return cached ? poolMerkleStateToCachedView(cached) : void 0;
1986
+ return Buffer11.from(value.bytes());
1987
+ }
1988
+ async function readTreeLeafCommitments(input) {
1989
+ if (input.startIndex >= input.endIndex) {
1990
+ return [];
1991
+ }
1992
+ const byIndex = await readLeafBuffersByIndex(input);
1993
+ return decimalCommitmentsInRange(byIndex, input.startIndex, input.endIndex);
1994
+ }
1995
+ async function readLeafBuffersByIndex(input) {
1996
+ const keys = ledgerKeysForRange(input.contractId, input.startIndex, input.endIndex);
1997
+ const byIndex = /* @__PURE__ */ new Map();
1998
+ for (const batch of chunkLedgerKeys(keys, TREE_LEAF_LEDGER_BATCH_SIZE)) {
1999
+ const response = await input.reader.getLedgerEntries(...batch);
2000
+ collectLeafBuffers(response.entries, byIndex);
2001
+ }
2002
+ return byIndex;
2003
+ }
2004
+ function ledgerKeysForRange(contractId, startIndex, endIndex) {
2005
+ const keys = [];
2006
+ for (let leafIndex = startIndex; leafIndex < endIndex; leafIndex += 1) {
2007
+ keys.push(treeLeafLedgerKey(contractId, leafIndex));
2008
+ }
2009
+ return keys;
2010
+ }
2011
+ function chunkLedgerKeys(keys, batchSize) {
2012
+ const batches = [];
2013
+ for (let offset = 0; offset < keys.length; offset += batchSize) {
2014
+ batches.push(keys.slice(offset, offset + batchSize));
2015
+ }
2016
+ return batches;
2017
+ }
2018
+ function collectLeafBuffers(entries, byIndex) {
2019
+ for (const entry of entries) {
2020
+ const contractData = entry.val.contractData();
2021
+ const leafIndex = leafIndexFromContractDataKey(contractData.key());
2022
+ const bytes = commitmentBytesFromContractDataValue(contractData.val());
2023
+ if (leafIndex === void 0 || !bytes) {
2024
+ continue;
2025
+ }
2026
+ byIndex.set(leafIndex, bytes);
2027
+ }
2028
+ }
2029
+ function decimalCommitmentsInRange(byIndex, startIndex, endIndex) {
2030
+ const commitments = [];
2031
+ for (let leafIndex = startIndex; leafIndex < endIndex; leafIndex += 1) {
2032
+ const bytes = byIndex.get(leafIndex);
2033
+ if (!bytes) {
2034
+ throw new Error(`Missing Merkle leaf ${String(leafIndex)} in pool storage.`);
2035
+ }
2036
+ commitments.push(commitmentBufferToDecimal(bytes));
2037
+ }
2038
+ return commitments;
2039
+ }
2040
+
2041
+ // src/transact/merkle/reuse-cached-commitments.ts
2042
+ function completeCacheRootMatches(cachedState, merkleRootHex) {
2043
+ const cachedRoot = cachedState?.merkleRootHex;
2044
+ if (cachedRoot === void 0 || cachedRoot === "") {
2045
+ return true;
2046
+ }
2047
+ return cachedRoot === merkleRootHex;
2048
+ }
2049
+ function reuseCachedCommitments(input) {
2050
+ const cachedCommitments = input.cachedState?.commitments ?? [];
2051
+ if (cachedCommitments.length === 0) {
2052
+ return [];
2053
+ }
2054
+ if (cachedCommitments.length > input.onChainCount) {
2055
+ return [];
2056
+ }
2057
+ if (cachedCommitments.length === input.onChainCount) {
2058
+ return completeCacheRootMatches(input.cachedState, input.merkleRootHex) ? cachedCommitments : [];
2059
+ }
2060
+ return cachedCommitments;
1658
2061
  }
2062
+
2063
+ // src/transact/merkle/fetch-contract.ts
1659
2064
  async function fetchAndMergeMerkleState(parameters) {
1660
- const client = readPoolClientFactory(parameters.transactEnvironment)({
1661
- contractId: parameters.poolContractId,
1662
- walletPublicKey: parameters.walletPublicKey,
1663
- networkPassphrase: parameters.transactEnvironment.network.networkPassphrase,
1664
- sorobanRpcUrl: parameters.transactEnvironment.network.rpcUrl
1665
- });
1666
- const commitmentsRead = await client.get_commitments();
1667
- const rawBuffers = commitmentsRead.result;
1668
- const buffers = normalizeCommitmentBuffers(rawBuffers);
1669
- const contractCommitments = commitmentsBuffersToDecimal(buffers);
1670
- const contractCount = contractCommitments.length;
2065
+ const client = createPoolClient(parameters);
2066
+ const countRead = await client.get_commitment_count();
2067
+ const onChainCount = requireCommitmentCount(countRead.result);
1671
2068
  const rootRead = await client.get_merkle_root();
1672
- const merkleRootBuffer = rootRead.result;
1673
- const merkleRootHex = Buffer11.from(merkleRootBuffer).toString("hex");
2069
+ const merkleRootHex = merkleRootToHex(rootRead.result);
1674
2070
  const cachedState = await readCachedPoolMerkleView(
1675
2071
  parameters.poolMerkleState,
1676
2072
  parameters.poolContractId
1677
2073
  );
1678
- return mergeContractMerkleWithCache({
2074
+ const commitments = await loadCommitments({
2075
+ cachedState,
2076
+ onChainCount,
2077
+ merkleRootHex,
2078
+ rpcUrl: parameters.transactEnvironment.network.rpcUrl,
2079
+ contractId: parameters.poolContractId
2080
+ });
2081
+ return persistFetchedMerkleState({
1679
2082
  poolContractId: parameters.poolContractId,
1680
- contractCommitments,
1681
- contractCount,
2083
+ commitments,
1682
2084
  merkleRootHex,
1683
2085
  cachedState,
1684
2086
  ...parameters.poolMerkleState ? { poolMerkleState: parameters.poolMerkleState } : {}
@@ -1692,19 +2094,14 @@ async function readLeafEphemeralHex(parameters) {
1692
2094
  if (cached?.xHex && cached.yHex) {
1693
2095
  return { xHex: cached.xHex, yHex: cached.yHex };
1694
2096
  }
1695
- const client = readPoolClientFactory(parameters.transactEnvironment)({
1696
- contractId: parameters.poolContractId,
1697
- walletPublicKey: parameters.walletPublicKey,
1698
- networkPassphrase: parameters.transactEnvironment.network.networkPassphrase,
1699
- sorobanRpcUrl: parameters.transactEnvironment.network.rpcUrl
1700
- });
2097
+ const client = createPoolClient(parameters);
1701
2098
  const read = await client.get_leaf_ephemeral({ leaf_index: parameters.leafIndex });
1702
2099
  const coords = read.result;
1703
2100
  if (!coords?.x || !coords?.y) {
1704
2101
  throw new Error("Missing leaf ephemeral key on contract");
1705
2102
  }
1706
- const xHex = Buffer11.from(coords.x).toString("hex");
1707
- const yHex = Buffer11.from(coords.y).toString("hex");
2103
+ const xHex = Buffer12.from(coords.x).toString("hex");
2104
+ const yHex = Buffer12.from(coords.y).toString("hex");
1708
2105
  await parameters.leafEphemeral?.set({
1709
2106
  poolContract: parameters.poolContractId,
1710
2107
  leafIndex: parameters.leafIndex,
@@ -1714,6 +2111,84 @@ async function readLeafEphemeralHex(parameters) {
1714
2111
  });
1715
2112
  return { xHex, yHex };
1716
2113
  }
2114
+ function createPoolClient(parameters) {
2115
+ return readPoolClientFactory(parameters.transactEnvironment)({
2116
+ contractId: parameters.poolContractId,
2117
+ walletPublicKey: parameters.walletPublicKey,
2118
+ networkPassphrase: parameters.transactEnvironment.network.networkPassphrase,
2119
+ sorobanRpcUrl: parameters.transactEnvironment.network.rpcUrl
2120
+ });
2121
+ }
2122
+ function requireCommitmentCount(result) {
2123
+ if (typeof result === "number" && Number.isInteger(result) && result >= 0) {
2124
+ return result;
2125
+ }
2126
+ throw new Error("Pool commitment count is not a non-negative integer.");
2127
+ }
2128
+ function merkleRootToHex(root) {
2129
+ return Buffer12.from(root).toString("hex");
2130
+ }
2131
+ async function readCachedPoolMerkleView(poolMerkleState, poolContractId) {
2132
+ if (!poolMerkleState) {
2133
+ return void 0;
2134
+ }
2135
+ const cached = await poolMerkleState.get(poolContractId);
2136
+ return cached ? poolMerkleStateToCachedView(cached) : void 0;
2137
+ }
2138
+ async function loadCommitments(input) {
2139
+ const reused = reuseCachedCommitments({
2140
+ cachedState: input.cachedState,
2141
+ onChainCount: input.onChainCount,
2142
+ merkleRootHex: input.merkleRootHex
2143
+ });
2144
+ if (reused.length === input.onChainCount) {
2145
+ return reused;
2146
+ }
2147
+ const fetched = await readTreeLeafCommitments({
2148
+ reader: createStellarRpcServer(input.rpcUrl),
2149
+ contractId: input.contractId,
2150
+ startIndex: reused.length,
2151
+ endIndex: input.onChainCount
2152
+ });
2153
+ return [...reused, ...fetched];
2154
+ }
2155
+ async function persistFetchedMerkleState(input) {
2156
+ const cachedState = input.cachedState;
2157
+ if (cachedState && cachedMerkleUnchanged(cachedState, input.commitments, input.merkleRootHex)) {
2158
+ return viewFromCachedMerkle(cachedState, input.merkleRootHex);
2159
+ }
2160
+ const updatedAt = Date.now();
2161
+ await input.poolMerkleState?.set({
2162
+ poolContract: input.poolContractId,
2163
+ commitments: input.commitments,
2164
+ commitmentCount: input.commitments.length,
2165
+ merkleRootHex: input.merkleRootHex,
2166
+ updatedAt
2167
+ });
2168
+ return {
2169
+ commitments: input.commitments,
2170
+ updatedAt,
2171
+ merkleRootHex: input.merkleRootHex,
2172
+ commitmentCount: input.commitments.length
2173
+ };
2174
+ }
2175
+ function viewFromCachedMerkle(cachedState, merkleRootHex) {
2176
+ return {
2177
+ commitments: cachedState.commitments,
2178
+ updatedAt: cachedState.updatedAt,
2179
+ merkleRootHex: cachedState.merkleRootHex ?? merkleRootHex,
2180
+ commitmentCount: cachedState.commitments.length
2181
+ };
2182
+ }
2183
+ function cachedMerkleUnchanged(cachedState, commitments, merkleRootHex) {
2184
+ if (!cachedState) {
2185
+ return false;
2186
+ }
2187
+ if ((cachedState.merkleRootHex ?? merkleRootHex) !== merkleRootHex) {
2188
+ return false;
2189
+ }
2190
+ return cachedState.commitments.join(",") === commitments.join(",");
2191
+ }
1717
2192
 
1718
2193
  // src/transact/engine/prepare/shared.ts
1719
2194
  async function resolveTokenContractId2(environment, assetId) {
@@ -2003,6 +2478,13 @@ async function filterUnspentPrivateRecords(input) {
2003
2478
  }
2004
2479
  return unspent;
2005
2480
  }
2481
+ function spendAmountCoveringProtocolFee(amount) {
2482
+ if (amount <= 0n) {
2483
+ return amount;
2484
+ }
2485
+ const hundredths = (amount + 99n) / 100n;
2486
+ return amount + (hundredths === 0n ? 1n : hundredths);
2487
+ }
2006
2488
  function pickRecordsForSpendAmount(input) {
2007
2489
  let selectedTotal = 0n;
2008
2490
  const selected = [];
@@ -2056,7 +2538,7 @@ async function selectPrivateRecords(input) {
2056
2538
  return pickRecordsForSpendAmount({
2057
2539
  available,
2058
2540
  kind: input.kind,
2059
- amount: input.intent.amount
2541
+ amount: spendAmountCoveringProtocolFee(input.intent.amount)
2060
2542
  });
2061
2543
  }
2062
2544
  function sumRecordAmounts(records) {
@@ -4257,7 +4739,7 @@ function createStellarStorageAdapterFromState(state) {
4257
4739
  }
4258
4740
 
4259
4741
  // src/contracts/registry/registry-domain-service.ts
4260
- import { Buffer as Buffer12 } from "buffer";
4742
+ import { Buffer as Buffer13 } from "buffer";
4261
4743
 
4262
4744
  // src/transact/submit/ttl-preflight.ts
4263
4745
  function getNested(source, path) {
@@ -4306,11 +4788,11 @@ async function runTtlPreflight(assembledTransaction) {
4306
4788
  // src/contracts/registry/registry-domain-service.ts
4307
4789
  var COORDINATE_HEX_LENGTH = 64;
4308
4790
  function readCoordinateBytes(value) {
4309
- if (value instanceof Buffer12) {
4791
+ if (value instanceof Buffer13) {
4310
4792
  return value;
4311
4793
  }
4312
4794
  if (value instanceof Uint8Array) {
4313
- return Buffer12.from(value);
4795
+ return Buffer13.from(value);
4314
4796
  }
4315
4797
  return void 0;
4316
4798
  }
@@ -4349,8 +4831,8 @@ function parseRegistryLookupRecord(result) {
4349
4831
  return parseOptionSomeRecord(result);
4350
4832
  }
4351
4833
  function recordToLookupResult(record) {
4352
- const publicKeyXHex = Buffer12.from(record.public_key_x).toString("hex");
4353
- const publicKeyYHex = Buffer12.from(record.public_key_y).toString("hex");
4834
+ const publicKeyXHex = Buffer13.from(record.public_key_x).toString("hex");
4835
+ const publicKeyYHex = Buffer13.from(record.public_key_y).toString("hex");
4354
4836
  return {
4355
4837
  owner: record.owner,
4356
4838
  status: "registered",
@@ -4393,8 +4875,8 @@ async function registerPrivateAddressOnChain(input) {
4393
4875
  });
4394
4876
  const assembled = await client.register_private_address({
4395
4877
  owner: input.owner.trim(),
4396
- public_key_x: Buffer12.from(decoded.x.padStart(COORDINATE_HEX_LENGTH, "0"), "hex"),
4397
- public_key_y: Buffer12.from(decoded.y.padStart(COORDINATE_HEX_LENGTH, "0"), "hex")
4878
+ public_key_x: Buffer13.from(decoded.x.padStart(COORDINATE_HEX_LENGTH, "0"), "hex"),
4879
+ public_key_y: Buffer13.from(decoded.y.padStart(COORDINATE_HEX_LENGTH, "0"), "hex")
4398
4880
  });
4399
4881
  await runTtlPreflight(assembled);
4400
4882
  const sent = await assembled.signAndSend();
@@ -4409,7 +4891,7 @@ async function registerPrivateAddressOnChain(input) {
4409
4891
  import { StrKey as StrKey5 } from "@stellar/stellar-sdk";
4410
4892
 
4411
4893
  // src/transact/escrow/derived-escrow-recipient.ts
4412
- import { Buffer as Buffer13 } from "buffer";
4894
+ import { Buffer as Buffer14 } from "buffer";
4413
4895
  import { StrKey as StrKey4 } from "@stellar/stellar-sdk";
4414
4896
  import {
4415
4897
  derivedEscrowKey,
@@ -4419,7 +4901,7 @@ import {
4419
4901
  var COORDINATE_HEX_LENGTH2 = 64;
4420
4902
  function stellarAccountToFieldLimbs(stellarAddress) {
4421
4903
  const pkRaw = StrKey4.decodeEd25519PublicKey(stellarAddress.trim());
4422
- const pkHex = Buffer13.from(pkRaw).toString("hex");
4904
+ const pkHex = Buffer14.from(pkRaw).toString("hex");
4423
4905
  const { hi, lo } = ed25519PubkeyPayloadHexToWithdrawFrDecimals2(pkHex);
4424
4906
  return { recipientHi: hi, recipientLo: lo };
4425
4907
  }
@@ -4504,7 +4986,7 @@ async function resolveTransferRecipientFromChain(input) {
4504
4986
  import { Api } from "@stellar/stellar-sdk/rpc";
4505
4987
 
4506
4988
  // src/rpc/soroban-transaction-meta.ts
4507
- import { Address, StrKey as StrKey6 } from "@stellar/stellar-sdk";
4989
+ import { Address as Address2, StrKey as StrKey6 } from "@stellar/stellar-sdk";
4508
4990
  var BIGINT_SHIFT_64 = 64n;
4509
4991
  var TRANSACTION_META_SWITCH_SOROBAN_V3 = 3;
4510
4992
  var TRANSACTION_META_SWITCH_SOROBAN_V4 = 4;
@@ -4604,8 +5086,8 @@ function transferPartyAddressesFromTopics(topics) {
4604
5086
  return void 0;
4605
5087
  }
4606
5088
  return {
4607
- from: Address.fromScVal(fromTopic).toString(),
4608
- to: Address.fromScVal(toTopic).toString()
5089
+ from: Address2.fromScVal(fromTopic).toString(),
5090
+ to: Address2.fromScVal(toTopic).toString()
4609
5091
  };
4610
5092
  } catch {
4611
5093
  return void 0;
@@ -5311,7 +5793,7 @@ function createStellarPrivacyClientFromResolvedConfig(config) {
5311
5793
  }
5312
5794
 
5313
5795
  // src/transact/engine/default-factory.ts
5314
- import { PrivacyPoolSDK as PrivacyPoolSDK3 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
5796
+ import { PrivacyPoolSDK as PrivacyPoolSDK4 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
5315
5797
 
5316
5798
  // src/transact/pool/proof-types.ts
5317
5799
  function ciphertextArtifactsFromProof(proof) {
@@ -5326,9 +5808,15 @@ function ciphertextArtifactsFromProof(proof) {
5326
5808
  // src/transact/engine/prepare/deposit.ts
5327
5809
  var STROOPS_PER_UNIT = 10000000n;
5328
5810
  async function createAlignedDepositWithProof(parameters) {
5811
+ const feeOutput = await quoteFeeOutputForPrepared(parameters);
5812
+ const instructed = parameters.prepared.intent.amount;
5813
+ const userAmount = instructed - (feeOutput?.requiredFee ?? 0n);
5814
+ if (userAmount <= 0n) {
5815
+ throw new Error("Required Fee leaves no spendable deposit note");
5816
+ }
5329
5817
  const aligned = await parameters.poolService.createAlignedShieldCoinData({
5330
5818
  privateAddressStpl1: parameters.prepared.intent.to,
5331
- amountStroops: parameters.prepared.intent.amount,
5819
+ amountStroops: userAmount,
5332
5820
  tokenAddress: parameters.tokenAddress
5333
5821
  });
5334
5822
  const poolClient = readPoolClientFactory(parameters.environment)({
@@ -5343,7 +5831,9 @@ async function createAlignedDepositWithProof(parameters) {
5343
5831
  coin: aligned.coin,
5344
5832
  depositScalarHex: aligned.depositScalarHex,
5345
5833
  merkleRootBytes: merkleTx.result,
5346
- tokenAddress: parameters.tokenAddress
5834
+ tokenAddress: parameters.tokenAddress,
5835
+ publicDepositStroops: instructed,
5836
+ ...feeOutput ? { feeOutput } : {}
5347
5837
  });
5348
5838
  return { ...aligned, proof };
5349
5839
  }
@@ -5362,8 +5852,8 @@ async function prepareDepositOperation(prepared, environment, poolService) {
5362
5852
  privateAddress: prepared.intent.to,
5363
5853
  assetId: prepared.intent.asset,
5364
5854
  poolContract: environment.network.poolContract,
5365
- amount: prepared.intent.amount,
5366
- amountDisplay: Number(prepared.intent.amount) / Number(STROOPS_PER_UNIT),
5855
+ amount: BigInt(depositProof.coin.value),
5856
+ amountDisplay: Number(depositProof.coin.value) / Number(STROOPS_PER_UNIT),
5367
5857
  commitmentHex: depositProof.commitment_hex,
5368
5858
  coin: depositProof.coin,
5369
5859
  depositScalarHex: depositProof.depositScalarHex,
@@ -5379,7 +5869,11 @@ async function prepareDepositOperation(prepared, environment, poolService) {
5379
5869
  depositScalarHex: depositProof.depositScalarHex,
5380
5870
  precommitementHex: depositProof.precommitementHex,
5381
5871
  commitmentHex: depositProof.commitment_hex,
5382
- walletPublicKey
5872
+ walletPublicKey,
5873
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
5874
+ kind: "deposit",
5875
+ ...configuredNonceSpread(environment.zkConfigNonce)
5876
+ })
5383
5877
  });
5384
5878
  }
5385
5879
 
@@ -5553,9 +6047,6 @@ function escrowOutputFields(input) {
5553
6047
  recipientLo: input.escrowSend.recipientLo
5554
6048
  };
5555
6049
  }
5556
- function paddingDepositPair(recipientDeposit) {
5557
- return [recipientDeposit, "dummy"];
5558
- }
5559
6050
  async function buildChangeDepositPair(parameters) {
5560
6051
  const changeSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
5561
6052
  privateAddressStpl1: parameters.selfPrivateAddressStpl1ForChange.trim(),
@@ -5572,34 +6063,54 @@ async function buildChangeDepositPair(parameters) {
5572
6063
  }
5573
6064
  };
5574
6065
  }
6066
+ function recipientOnlyDeposits(recipientDeposit, hasFeeOutput) {
6067
+ if (hasFeeOutput) {
6068
+ return [recipientDeposit];
6069
+ }
6070
+ return [recipientDeposit, "dummy"];
6071
+ }
6072
+ async function depositsWithoutFee(input) {
6073
+ if (input.changeStroops <= ZERO_STROOPS) {
6074
+ return {
6075
+ deposits: recipientOnlyDeposits(input.recipientDeposit, input.hasFeeOutput)
6076
+ };
6077
+ }
6078
+ return buildChangeDepositPair({
6079
+ selfPrivateAddressStpl1ForChange: input.selfPrivateAddressStpl1ForChange,
6080
+ changeStroops: input.changeStroops,
6081
+ tokenAddress: input.tokenAddress,
6082
+ recipientDeposit: input.recipientDeposit
6083
+ });
6084
+ }
5575
6085
  async function buildRecipientAndOptionalChangeDeposits(parameters) {
5576
6086
  const escrow = {
5577
6087
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
5578
6088
  ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
5579
6089
  };
5580
- const recipientPrivateAddress = parameters.recipientPrivateAddressStpl1.trim();
5581
6090
  const recipientSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
5582
- privateAddressStpl1: recipientPrivateAddress,
6091
+ privateAddressStpl1: parameters.recipientPrivateAddressStpl1.trim(),
5583
6092
  amountStroops: parameters.transferStroops,
5584
6093
  tokenAddress: parameters.tokenAddress,
5585
6094
  ...escrowOutputFields(escrow)
5586
6095
  });
5587
- if (parameters.changeStroops <= ZERO_STROOPS) {
5588
- return {
5589
- recipientSlot,
5590
- deposits: paddingDepositPair(recipientSlot.deposit)
5591
- };
5592
- }
5593
- const changeResult = await buildChangeDepositPair({
5594
- selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
6096
+ const withoutFee = await depositsWithoutFee({
6097
+ recipientDeposit: recipientSlot.deposit,
5595
6098
  changeStroops: parameters.changeStroops,
6099
+ selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5596
6100
  tokenAddress: parameters.tokenAddress,
5597
- recipientDeposit: recipientSlot.deposit
6101
+ hasFeeOutput: (parameters.feeOutput?.requiredFee ?? ZERO_STROOPS) > ZERO_STROOPS
6102
+ });
6103
+ const withFee = await appendFeeOutputSlot({
6104
+ deposits: withoutFee.deposits,
6105
+ tokenAddress: parameters.tokenAddress,
6106
+ buildAlignedDepositSlot: (aligned) => getPrivacyPoolService().buildAlignedDepositSlot(aligned),
6107
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5598
6108
  });
5599
6109
  return {
5600
6110
  recipientSlot,
5601
- deposits: changeResult.deposits,
5602
- changeCoin: changeResult.changeCoin
6111
+ deposits: withFee.deposits,
6112
+ ..."changeCoin" in withoutFee ? { changeCoin: withoutFee.changeCoin } : {},
6113
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
5603
6114
  };
5604
6115
  }
5605
6116
 
@@ -5652,14 +6163,15 @@ function sweepOutputOwnerFields(parameters) {
5652
6163
  return { sweepOutputOwnerPubX, sweepOutputOwnerPubY };
5653
6164
  }
5654
6165
  async function buildTransferDepositsAndPublicInput(parameters) {
5655
- const { recipientSlot, deposits, changeCoin } = await buildRecipientAndOptionalChangeDeposits({
6166
+ const { recipientSlot, deposits, changeCoin, feeCoin } = await buildRecipientAndOptionalChangeDeposits({
5656
6167
  recipientPrivateAddressStpl1: parameters.recipientPrivateAddressStpl1,
5657
6168
  transferStroops: parameters.transferStroops,
5658
6169
  changeStroops: parameters.changeStroops,
5659
6170
  selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5660
6171
  tokenAddress: parameters.tokenAddress,
5661
6172
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
5662
- ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
6173
+ ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
6174
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5663
6175
  });
5664
6176
  const publicInput = withTokenAddressPublicInputs(
5665
6177
  {
@@ -5674,6 +6186,7 @@ async function buildTransferDepositsAndPublicInput(parameters) {
5674
6186
  recipientSlot,
5675
6187
  deposits,
5676
6188
  ...changeCoin ? { changeCoin } : {},
6189
+ ...feeCoin ? { feeCoin } : {},
5677
6190
  publicInput
5678
6191
  };
5679
6192
  }
@@ -5689,7 +6202,8 @@ async function buildSenderTransferDepositsAndPublicInput(parameters) {
5689
6202
  parameters.senderPrivKeyScalarHex
5690
6203
  ),
5691
6204
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
5692
- ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
6205
+ ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
6206
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5693
6207
  });
5694
6208
  }
5695
6209
 
@@ -5713,11 +6227,20 @@ function stampDualWithdrawLegs(legs, parameters) {
5713
6227
  }
5714
6228
  };
5715
6229
  }
5716
- function buildDualTransferApplicationIds(sdk, applicationId, changeCoin) {
6230
+ function buildDualTransferApplicationIds(sdk, applicationId, changeCoin, feeCoin) {
6231
+ const outputIds = [applicationId];
6232
+ if (changeCoin) {
6233
+ outputIds.push(applicationId);
6234
+ } else if (!feeCoin) {
6235
+ outputIds.push("0");
6236
+ }
6237
+ if (feeCoin) {
6238
+ outputIds.push(applicationId);
6239
+ }
5717
6240
  return buildApplicationIdHints({
5718
6241
  sdk,
5719
6242
  inputIds: [applicationId, applicationId],
5720
- outputIds: [applicationId, changeCoin ? applicationId : "0"]
6243
+ outputIds
5721
6244
  });
5722
6245
  }
5723
6246
  async function proveDualConfidentialTransfer(parameters) {
@@ -5737,16 +6260,24 @@ async function proveDualConfidentialTransfer(parameters) {
5737
6260
  audit
5738
6261
  );
5739
6262
  }
5740
- async function buildDualTransferProofContext(parameters, applicationId, sdk) {
6263
+ function dualTransferChangeStroops(parameters) {
5741
6264
  const totalNotes = BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value);
5742
- const changeStroops = changeStroopsAfterDualTransfer(
5743
- parameters.transferStroops,
5744
- totalNotes
5745
- );
6265
+ if (parameters.transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
6266
+ throw new Error("Transfer amount must be positive");
6267
+ }
6268
+ const changeStroops = remainingAfterRequiredFee({
6269
+ available: totalNotes,
6270
+ instructed: parameters.transferStroops,
6271
+ requiredFee: parameters.feeOutput?.requiredFee ?? 0n
6272
+ });
5746
6273
  requireChangeRecipientWhenPartial(
5747
6274
  changeStroops,
5748
6275
  parameters.selfPrivateAddressStpl1ForChange
5749
6276
  );
6277
+ return changeStroops;
6278
+ }
6279
+ async function buildDualTransferProofContext(parameters, applicationId, sdk) {
6280
+ const changeStroops = dualTransferChangeStroops(parameters);
5750
6281
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
5751
6282
  parameters.senderPrivKeyScalarHex
5752
6283
  );
@@ -5773,15 +6304,21 @@ async function buildDualTransferProofContext(parameters, applicationId, sdk) {
5773
6304
  selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5774
6305
  tokenAddress: parameters.tokenAddress,
5775
6306
  stateRoot: legA.witness.stateRoot,
5776
- ...confidentialEscrowFields(parameters)
6307
+ ...confidentialEscrowFields(parameters),
6308
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5777
6309
  });
5778
6310
  return { legA, legB, applicationId, ...transferInputs };
5779
6311
  }
5780
6312
  async function prepareConfidentialTransferProofDual(parameters) {
5781
6313
  const poolService = getPrivacyPoolService();
5782
- const sdk = await poolService.getInitializedSdk();
6314
+ const sdk = await poolService.getInitializedSdk(
6315
+ poolService.feeBearingZkConfigNonce({
6316
+ kind: "transfer",
6317
+ ...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
6318
+ })
6319
+ );
5783
6320
  const applicationId = poolService.getApplicationId();
5784
- const { legA, legB, recipientSlot, deposits, changeCoin, publicInput } = await buildDualTransferProofContext(parameters, applicationId, sdk);
6321
+ const { legA, legB, recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildDualTransferProofContext(parameters, applicationId, sdk);
5785
6322
  const auditPublicKey = poolService.getAuditPublicKey();
5786
6323
  const proof = await (auditPublicKey ? proveDualConfidentialTransfer({
5787
6324
  sdk,
@@ -5804,7 +6341,8 @@ async function prepareConfidentialTransferProofDual(parameters) {
5804
6341
  applicationIdsPlaintext: buildDualTransferApplicationIds(
5805
6342
  sdk,
5806
6343
  applicationId,
5807
- changeCoin
6344
+ changeCoin,
6345
+ feeCoin
5808
6346
  ),
5809
6347
  recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
5810
6348
  ...changeCoin ? { changeCoin } : {}
@@ -5812,22 +6350,32 @@ async function prepareConfidentialTransferProofDual(parameters) {
5812
6350
  }
5813
6351
 
5814
6352
  // src/transact/proofs/confidential/single.ts
5815
- function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange) {
6353
+ function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange, requiredFee) {
5816
6354
  if (transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
5817
6355
  throw new Error("Transfer amount must be positive");
5818
6356
  }
5819
- if (transferStroops > noteStroops) {
5820
- throw new Error("Transfer amount exceeds note value");
5821
- }
5822
- const changeStroops = noteStroops - transferStroops;
6357
+ const changeStroops = remainingAfterRequiredFee({
6358
+ available: noteStroops,
6359
+ instructed: transferStroops,
6360
+ requiredFee
6361
+ });
5823
6362
  requireChangeRecipientWhenPartial(changeStroops, selfPrivateAddressStpl1ForChange);
5824
6363
  return changeStroops;
5825
6364
  }
5826
- function buildSingleTransferApplicationIds(sdk, applicationId, changeCoin) {
6365
+ function buildSingleTransferApplicationIds(sdk, applicationId, changeCoin, feeCoin) {
6366
+ const outputIds = [applicationId];
6367
+ if (changeCoin) {
6368
+ outputIds.push(applicationId);
6369
+ } else if (!feeCoin) {
6370
+ outputIds.push("0");
6371
+ }
6372
+ if (feeCoin) {
6373
+ outputIds.push(applicationId);
6374
+ }
5827
6375
  return buildApplicationIdHints({
5828
6376
  sdk,
5829
6377
  inputIds: [applicationId],
5830
- outputIds: [applicationId, changeCoin ? applicationId : "0"]
6378
+ outputIds
5831
6379
  });
5832
6380
  }
5833
6381
  function buildTransferAuditParameters(parameters) {
@@ -5853,11 +6401,25 @@ function confidentialEscrowFields2(input) {
5853
6401
  ...input.escrowClaimantLimbs ? { escrowClaimantLimbs: input.escrowClaimantLimbs } : {}
5854
6402
  };
5855
6403
  }
6404
+ function transferDepositInputFromProof(input, changeStroops, stateRoot) {
6405
+ return {
6406
+ senderPrivKeyScalarHex: input.senderPrivKeyScalarHex,
6407
+ recipientPrivateAddressStpl1: input.recipientPrivateAddressStpl1,
6408
+ transferStroops: input.transferStroops,
6409
+ changeStroops,
6410
+ selfPrivateAddressStpl1ForChange: input.selfPrivateAddressStpl1ForChange,
6411
+ tokenAddress: input.tokenAddress,
6412
+ stateRoot,
6413
+ ...confidentialEscrowFields2(input),
6414
+ ...input.feeOutput ? { feeOutput: input.feeOutput } : {}
6415
+ };
6416
+ }
5856
6417
  async function buildTransferProofInputs(parameters) {
5857
6418
  const changeStroops = validateSingleCoinTransferAmounts(
5858
6419
  parameters.input.transferStroops,
5859
6420
  parameters.noteStroops,
5860
- parameters.input.selfPrivateAddressStpl1ForChange
6421
+ parameters.input.selfPrivateAddressStpl1ForChange,
6422
+ parameters.input.feeOutput?.requiredFee ?? 0n
5861
6423
  );
5862
6424
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
5863
6425
  parameters.input.senderPrivKeyScalarHex
@@ -5876,31 +6438,30 @@ async function buildTransferProofInputs(parameters) {
5876
6438
  withdrawObject,
5877
6439
  sweepWithdrawStamp(confidentialEscrowFields2(parameters.input))
5878
6440
  );
5879
- const { recipientSlot, deposits, changeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput({
5880
- senderPrivKeyScalarHex: parameters.input.senderPrivKeyScalarHex,
5881
- recipientPrivateAddressStpl1: parameters.input.recipientPrivateAddressStpl1,
5882
- transferStroops: parameters.input.transferStroops,
5883
- changeStroops,
5884
- selfPrivateAddressStpl1ForChange: parameters.input.selfPrivateAddressStpl1ForChange,
5885
- tokenAddress: parameters.input.tokenAddress,
5886
- stateRoot: witness.stateRoot,
5887
- ...confidentialEscrowFields2(parameters.input)
5888
- });
6441
+ const { recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput(
6442
+ transferDepositInputFromProof(parameters.input, changeStroops, witness.stateRoot)
6443
+ );
5889
6444
  return {
5890
6445
  witness,
5891
6446
  withdrawObject: stampedWithdraw,
5892
6447
  recipientSlot,
5893
6448
  deposits,
5894
6449
  changeCoin,
6450
+ feeCoin,
5895
6451
  publicInput
5896
6452
  };
5897
6453
  }
5898
6454
  async function prepareConfidentialTransferProof(parameters) {
5899
6455
  const poolService = getPrivacyPoolService();
5900
- const sdk = await poolService.getInitializedSdk();
6456
+ const sdk = await poolService.getInitializedSdk(
6457
+ poolService.feeBearingZkConfigNonce({
6458
+ kind: "transfer",
6459
+ ...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
6460
+ })
6461
+ );
5901
6462
  const applicationId = poolService.getApplicationId();
5902
6463
  const noteStroops = BigInt(parameters.coin.value);
5903
- const { withdrawObject, recipientSlot, deposits, changeCoin, publicInput } = await buildTransferProofInputs({
6464
+ const { withdrawObject, recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildTransferProofInputs({
5904
6465
  sdk,
5905
6466
  applicationId,
5906
6467
  input: parameters,
@@ -5923,7 +6484,8 @@ async function prepareConfidentialTransferProof(parameters) {
5923
6484
  applicationIdsPlaintext: buildSingleTransferApplicationIds(
5924
6485
  sdk,
5925
6486
  applicationId,
5926
- changeCoin
6487
+ changeCoin,
6488
+ feeCoin
5927
6489
  ),
5928
6490
  recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
5929
6491
  ...changeCoin ? { changeCoin } : {}
@@ -5946,7 +6508,12 @@ function proofEscrowFields(input) {
5946
6508
  }
5947
6509
  async function buildSingleTransferProofAtExecute(input) {
5948
6510
  const context = await buildSpendProofContextAtExecute(input);
5949
- const changeStroops = BigInt(context.coin.value) - input.prepared.intent.amount;
6511
+ const feeOutput = await quoteFeeOutputForPrepared({
6512
+ prepared: input.prepared,
6513
+ environment: input.environment,
6514
+ tokenAddress: context.tokenAddress
6515
+ });
6516
+ const changeStroops = BigInt(context.coin.value) - input.prepared.intent.amount - (feeOutput?.requiredFee ?? 0n);
5950
6517
  const proof = await prepareConfidentialTransferProof({
5951
6518
  coin: context.coin,
5952
6519
  state: { commitments: context.commitments },
@@ -5959,7 +6526,8 @@ async function buildSingleTransferProofAtExecute(input) {
5959
6526
  recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
5960
6527
  selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
5961
6528
  tokenAddress: context.tokenAddress,
5962
- ...proofEscrowFields(input)
6529
+ ...proofEscrowFields(input),
6530
+ ...feeOutput ? { feeOutput } : {}
5963
6531
  });
5964
6532
  return { context, proof };
5965
6533
  }
@@ -5976,7 +6544,12 @@ async function buildDualTransferProofAtExecute(input) {
5976
6544
  commitments: context.commitments
5977
6545
  });
5978
6546
  const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
5979
- const changeStroops = totalNotes - input.prepared.intent.amount;
6547
+ const feeOutput = await quoteFeeOutputForPrepared({
6548
+ prepared: input.prepared,
6549
+ environment: input.environment,
6550
+ tokenAddress: context.tokenAddress
6551
+ });
6552
+ const changeStroops = totalNotes - input.prepared.intent.amount - (feeOutput?.requiredFee ?? 0n);
5980
6553
  const proof = await prepareConfidentialTransferProofDual({
5981
6554
  coinA: context.coin,
5982
6555
  coinB: secondary.coin,
@@ -5994,7 +6567,8 @@ async function buildDualTransferProofAtExecute(input) {
5994
6567
  recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
5995
6568
  selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
5996
6569
  tokenAddress: context.tokenAddress,
5997
- ...proofEscrowFields(input)
6570
+ ...proofEscrowFields(input),
6571
+ ...feeOutput ? { feeOutput } : {}
5998
6572
  });
5999
6573
  return { context, proof };
6000
6574
  }
@@ -6126,11 +6700,17 @@ async function runTransferFinalizeSteps(input) {
6126
6700
  walletPublicKey: input.walletPublicKey,
6127
6701
  ...recipient.escrowSend ? { escrowSendPrivateAddress: recipient.recipientPrivateAddressStpl1 } : {}
6128
6702
  });
6129
- return buildTransferFinalizeArtifacts({
6130
- proof,
6131
- context,
6132
- ...sweepFinalizeStamp(input.prepared, recipient)
6133
- });
6703
+ return {
6704
+ ...buildTransferFinalizeArtifacts({
6705
+ proof,
6706
+ context,
6707
+ ...sweepFinalizeStamp(input.prepared, recipient)
6708
+ }),
6709
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
6710
+ ...feeOutputKindFromPrepared(input.prepared),
6711
+ ...configuredNonceSpread(input.environment.zkConfigNonce)
6712
+ })
6713
+ };
6134
6714
  }
6135
6715
  function sweepArtifactStamp(artifacts) {
6136
6716
  return {
@@ -6158,6 +6738,24 @@ async function finalizeTransferAtExecute(prepared, environment) {
6158
6738
  return runTransferFinalizeSteps({ prepared, environment, walletPublicKey });
6159
6739
  }
6160
6740
 
6741
+ // src/transact/engine/withdraw-fee.ts
6742
+ async function withdrawFeeProofFields(input) {
6743
+ const feeOutput = await quoteFeeOutputForPrepared({
6744
+ prepared: input.prepared,
6745
+ environment: input.environment,
6746
+ tokenAddress: input.tokenAddress
6747
+ });
6748
+ const changeStroops = remainingAfterRequiredFee({
6749
+ available: input.available,
6750
+ instructed: input.prepared.intent.amount,
6751
+ requiredFee: feeOutput?.requiredFee ?? 0n
6752
+ });
6753
+ return {
6754
+ ...feeOutput ? { feeOutput } : {},
6755
+ ...changeStroops > 0n ? { changePrivateAddressStpl1: input.withdrawFrom } : {}
6756
+ };
6757
+ }
6758
+
6161
6759
  // src/transact/engine/execute.ts
6162
6760
  function enrichWithdrawOutputRecords(prepared, proof) {
6163
6761
  if (!proof.changeCoin || prepared.outputRecords.length === 0) {
@@ -6173,7 +6771,7 @@ function enrichWithdrawOutputRecords(prepared, proof) {
6173
6771
  precommitementHex: changeCoin.precommitementHex
6174
6772
  }));
6175
6773
  }
6176
- function buildWithdrawFinalizeArtifacts(proof, context) {
6774
+ function buildWithdrawFinalizeArtifacts(proof, context, environment) {
6177
6775
  return {
6178
6776
  proofHex: proof.proof_hex,
6179
6777
  publicHex: proof.public_hex,
@@ -6181,7 +6779,11 @@ function buildWithdrawFinalizeArtifacts(proof, context) {
6181
6779
  applicationIdsPlaintext: proof.applicationIdsPlaintext,
6182
6780
  tokenAddress: context.tokenAddress,
6183
6781
  walletPublicKey: context.walletPublicKey,
6184
- executeFinalizeRequired: false
6782
+ executeFinalizeRequired: false,
6783
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
6784
+ kind: "withdraw",
6785
+ ...configuredNonceSpread(environment.zkConfigNonce)
6786
+ })
6185
6787
  };
6186
6788
  }
6187
6789
  async function finalizeSingleWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
@@ -6190,7 +6792,13 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
6190
6792
  environment,
6191
6793
  recipientPrivateAddressStpl1: withdrawFrom
6192
6794
  });
6193
- const changeStroops = BigInt(context.coin.value) - prepared.intent.amount;
6795
+ const feeFields = await withdrawFeeProofFields({
6796
+ prepared,
6797
+ environment,
6798
+ tokenAddress: context.tokenAddress,
6799
+ available: BigInt(context.coin.value),
6800
+ withdrawFrom
6801
+ });
6194
6802
  const proof = await poolService.prepareWithdrawTransactProof({
6195
6803
  coin: context.coin,
6196
6804
  state: { commitments: context.commitments },
@@ -6201,11 +6809,24 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
6201
6809
  yHex: context.ephemeral.yHex
6202
6810
  }),
6203
6811
  withdrawAmountStroops: prepared.intent.amount,
6204
- changePrivateAddressStpl1: changeStroops > 0n ? withdrawFrom : void 0,
6205
- tokenAddress: context.tokenAddress
6812
+ changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
6813
+ tokenAddress: context.tokenAddress,
6814
+ ...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
6206
6815
  });
6207
6816
  enrichWithdrawOutputRecords(prepared, proof);
6208
- return buildWithdrawFinalizeArtifacts(proof, context);
6817
+ return buildWithdrawFinalizeArtifacts(proof, context, environment);
6818
+ }
6819
+ function dualWithdrawEphemeralKeys(input) {
6820
+ return {
6821
+ depositorEphemeralAKey: serializeEphemeralKeyString({
6822
+ xHex: input.context.ephemeral.xHex,
6823
+ yHex: input.context.ephemeral.yHex
6824
+ }),
6825
+ depositorEphemeralBKey: serializeEphemeralKeyString({
6826
+ xHex: input.secondary.ephemeral.xHex,
6827
+ yHex: input.secondary.ephemeral.yHex
6828
+ })
6829
+ };
6209
6830
  }
6210
6831
  async function finalizeDualWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
6211
6832
  const [, secondaryRecord] = prepared.consumedRecords;
@@ -6223,28 +6844,27 @@ async function finalizeDualWithdrawAtExecute(prepared, environment, poolService,
6223
6844
  walletPublicKey: context.walletPublicKey,
6224
6845
  commitments: context.commitments
6225
6846
  });
6226
- const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
6227
- const changeStroops = totalNotes - prepared.intent.amount;
6847
+ const feeFields = await withdrawFeeProofFields({
6848
+ prepared,
6849
+ environment,
6850
+ tokenAddress: context.tokenAddress,
6851
+ available: BigInt(context.coin.value) + BigInt(secondary.coin.value),
6852
+ withdrawFrom
6853
+ });
6228
6854
  const proof = await poolService.prepareWithdrawTransactProofDual({
6229
6855
  coinA: context.coin,
6230
6856
  coinB: secondary.coin,
6231
6857
  state: { commitments: context.commitments },
6232
6858
  destinationStellarAddress: prepared.intent.to,
6233
6859
  privKeyScalarHex: context.senderPrivKeyScalarHex,
6234
- depositorEphemeralAKey: serializeEphemeralKeyString({
6235
- xHex: context.ephemeral.xHex,
6236
- yHex: context.ephemeral.yHex
6237
- }),
6238
- depositorEphemeralBKey: serializeEphemeralKeyString({
6239
- xHex: secondary.ephemeral.xHex,
6240
- yHex: secondary.ephemeral.yHex
6241
- }),
6860
+ ...dualWithdrawEphemeralKeys({ context, secondary }),
6242
6861
  withdrawAmountStroops: prepared.intent.amount,
6243
- changePrivateAddressStpl1: changeStroops > 0n ? withdrawFrom : void 0,
6244
- tokenAddress: context.tokenAddress
6862
+ changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
6863
+ tokenAddress: context.tokenAddress,
6864
+ ...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
6245
6865
  });
6246
6866
  enrichWithdrawOutputRecords(prepared, proof);
6247
- return buildWithdrawFinalizeArtifacts(proof, context);
6867
+ return buildWithdrawFinalizeArtifacts(proof, context, environment);
6248
6868
  }
6249
6869
  async function finalizeWithdrawAtExecute(prepared, environment, poolService) {
6250
6870
  if (prepared.kind !== "withdraw") {
@@ -6376,20 +6996,20 @@ async function waitForKytRegistration(parameters) {
6376
6996
  }
6377
6997
 
6378
6998
  // src/transact/kyt/passage/submit.ts
6379
- import { Buffer as Buffer14 } from "buffer";
6380
- import { Operation, TransactionBuilder, rpc as rpc3, xdr as xdr2 } from "@stellar/stellar-sdk";
6999
+ import { Buffer as Buffer15 } from "buffer";
7000
+ import { Operation, TransactionBuilder, rpc as rpc3, xdr as xdr3 } from "@stellar/stellar-sdk";
6381
7001
  var KYT_REGISTER_FEE_STROOPS = "1000000";
6382
7002
  var KYT_REGISTER_TIMEOUT_SECONDS = 300;
6383
7003
  function passageIdToScValue(passageId) {
6384
- return xdr2.ScVal.scvBytes(Buffer14.from(passageId.replace(/^0x/, ""), "hex"));
7004
+ return xdr3.ScVal.scvBytes(Buffer15.from(passageId.replace(/^0x/, ""), "hex"));
6385
7005
  }
6386
7006
  function approvalSignatureToBytes(signature) {
6387
7007
  const clean = signature.trim();
6388
- return /^(0x)?[0-9a-fA-F]{128}$/.test(clean) ? Buffer14.from(clean.replace(/^0x/i, ""), "hex") : Buffer14.from(clean, "base64");
7008
+ return /^(0x)?[0-9a-fA-F]{128}$/.test(clean) ? Buffer15.from(clean.replace(/^0x/i, ""), "hex") : Buffer15.from(clean, "base64");
6389
7009
  }
6390
7010
  function signatureToScValue(signature) {
6391
7011
  const bytes = approvalSignatureToBytes(signature);
6392
- return xdr2.ScVal.scvBytes(bytes);
7012
+ return xdr3.ScVal.scvBytes(bytes);
6393
7013
  }
6394
7014
  async function registerKytPassage(parameters) {
6395
7015
  const signTransaction = parameters.transactEnvironment.signTransaction;
@@ -6410,7 +7030,7 @@ async function registerKytPassage(parameters) {
6410
7030
  function: "register_passage",
6411
7031
  args: [
6412
7032
  passageIdToScValue(parameters.approval.passageId),
6413
- xdr2.ScVal.scvU32(parameters.approval.expiresAtLedger),
7033
+ xdr3.ScVal.scvU32(parameters.approval.expiresAtLedger),
6414
7034
  signatureToScValue(parameters.approval.signature)
6415
7035
  ]
6416
7036
  })
@@ -6506,7 +7126,7 @@ async function requestKytPassageForPoolInteraction(input) {
6506
7126
  const applicationId = resolvePoolApplicationId(
6507
7127
  input.transactEnvironment.network.applicationId
6508
7128
  );
6509
- const zkConfigNonce = resolveZkConfigNonce(input.transactEnvironment);
7129
+ const zkConfigNonce = input.zkConfigNonce ?? resolveZkConfigNonce(input.transactEnvironment);
6510
7130
  const applicationIdsPlaintext = input.applicationIdsPlaintext ?? buildApplicationIdHints2(
6511
7131
  applicationId,
6512
7132
  layoutForKnownNonce(zkConfigNonce).nAuditSlots
@@ -6535,12 +7155,12 @@ async function requestKytPassageForPoolInteraction(input) {
6535
7155
 
6536
7156
  // src/transact/submit/zk-config-ttl.ts
6537
7157
  import {
6538
- Address as Address2,
7158
+ Address as Address3,
6539
7159
  Operation as Operation2,
6540
7160
  SorobanDataBuilder,
6541
7161
  TransactionBuilder as TransactionBuilder2,
6542
7162
  rpc as rpc4,
6543
- xdr as xdr3
7163
+ xdr as xdr4
6544
7164
  } from "@stellar/stellar-sdk";
6545
7165
  var ZK_CONFIG_TTL_THRESHOLD_LEDGERS = 1e5;
6546
7166
  var ZK_CONFIG_TTL_EXTEND_TO_LEDGERS = 5e5;
@@ -6564,15 +7184,15 @@ async function waitForExtendFootprintTtlConfirmation(parameters) {
6564
7184
  throw new Error("ZK config TTL extension was not confirmed in time");
6565
7185
  }
6566
7186
  function zkConfigLedgerKey(contractId, nonce) {
6567
- const configKeyScValue = xdr3.ScVal.scvVec([
6568
- xdr3.ScVal.scvSymbol("Config"),
6569
- xdr3.ScVal.scvU64(xdr3.Uint64.fromString(nonce.toString()))
7187
+ const configKeyScValue = xdr4.ScVal.scvVec([
7188
+ xdr4.ScVal.scvSymbol("Config"),
7189
+ xdr4.ScVal.scvU64(xdr4.Uint64.fromString(nonce.toString()))
6570
7190
  ]);
6571
- return xdr3.LedgerKey.contractData(
6572
- new xdr3.LedgerKeyContractData({
6573
- contract: new Address2(contractId).toScAddress(),
7191
+ return xdr4.LedgerKey.contractData(
7192
+ new xdr4.LedgerKeyContractData({
7193
+ contract: new Address3(contractId).toScAddress(),
6574
7194
  key: configKeyScValue,
6575
- durability: xdr3.ContractDataDurability.persistent()
7195
+ durability: xdr4.ContractDataDurability.persistent()
6576
7196
  })
6577
7197
  );
6578
7198
  }
@@ -6637,7 +7257,7 @@ async function extendZkConfigTtlIfNeeded(parameters) {
6637
7257
  }
6638
7258
 
6639
7259
  // src/transact/submit/pool-transact.ts
6640
- import { Buffer as Buffer15 } from "buffer";
7260
+ import { Buffer as Buffer16 } from "buffer";
6641
7261
  async function submitPoolTransact(parameters) {
6642
7262
  await extendZkConfigTtlIfNeeded({
6643
7263
  sorobanRpcUrl: parameters.sorobanRpcUrl,
@@ -6650,9 +7270,9 @@ async function submitPoolTransact(parameters) {
6650
7270
  const assembledTransaction = await parameters.contractClient.transact({
6651
7271
  from: parameters.from,
6652
7272
  nonce: parameters.nonce,
6653
- proof_bytes: Buffer15.from(parameters.proofHex.replace(/^0x/iu, ""), "hex"),
6654
- pub_signals_bytes: Buffer15.from(parameters.publicHex.replace(/^0x/iu, ""), "hex"),
6655
- ciphertext_bytes: Buffer15.from(
7273
+ proof_bytes: Buffer16.from(parameters.proofHex.replace(/^0x/iu, ""), "hex"),
7274
+ pub_signals_bytes: Buffer16.from(parameters.publicHex.replace(/^0x/iu, ""), "hex"),
7275
+ ciphertext_bytes: Buffer16.from(
6656
7276
  (parameters.ciphertextHex ?? "").replace(/^0x/iu, ""),
6657
7277
  "hex"
6658
7278
  ),
@@ -6678,6 +7298,10 @@ async function submitSorobanConfidentialTransfer(input) {
6678
7298
  networkPassphrase: input.networkPassphrase,
6679
7299
  sorobanRpcUrl: input.sorobanRpcUrl
6680
7300
  });
7301
+ const nonce = transactNonceFromArtifacts(
7302
+ input.zkConfigNonce === void 0 ? void 0 : { zkConfigNonce: input.zkConfigNonce },
7303
+ input.transactEnvironment
7304
+ );
6681
7305
  const approval = await requestKytPassageForPoolInteraction({
6682
7306
  owner: input.walletPublicKey,
6683
7307
  poolContract: input.contractId,
@@ -6686,6 +7310,7 @@ async function submitSorobanConfidentialTransfer(input) {
6686
7310
  ...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
6687
7311
  ...input.outputNoteEphemeralScalars ? { outputNoteEphemeralScalars: input.outputNoteEphemeralScalars } : {},
6688
7312
  ...input.applicationIdsPlaintext ? { applicationIdsPlaintext: input.applicationIdsPlaintext } : {},
7313
+ ...input.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.zkConfigNonce },
6689
7314
  networkPassphrase: input.networkPassphrase,
6690
7315
  sorobanRpcUrl: input.sorobanRpcUrl,
6691
7316
  transactEnvironment: input.transactEnvironment
@@ -6694,7 +7319,7 @@ async function submitSorobanConfidentialTransfer(input) {
6694
7319
  contractClient: poolClient,
6695
7320
  contractId: input.contractId,
6696
7321
  from: input.walletPublicKey,
6697
- nonce: resolveZkConfigNonce(input.transactEnvironment),
7322
+ nonce,
6698
7323
  proofHex: input.proofHex,
6699
7324
  publicHex: input.publicHex,
6700
7325
  ...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
@@ -6725,12 +7350,14 @@ async function submitPreparedDeposit(prepared, environment) {
6725
7350
  networkPassphrase: environment.network.networkPassphrase,
6726
7351
  sorobanRpcUrl: environment.network.rpcUrl
6727
7352
  });
7353
+ const nonce = transactNonceFromArtifacts(artifacts, environment);
6728
7354
  const approval = await requestKytPassageForPoolInteraction({
6729
7355
  owner: artifacts.walletPublicKey,
6730
7356
  poolContract: environment.network.poolContract,
6731
7357
  proofHex: artifacts.proofHex,
6732
7358
  publicHex: artifacts.publicHex,
6733
7359
  ...optionalInspectCiphertext(artifacts),
7360
+ ...artifacts.zkConfigNonce === void 0 ? {} : { zkConfigNonce: artifacts.zkConfigNonce },
6734
7361
  networkPassphrase: environment.network.networkPassphrase,
6735
7362
  sorobanRpcUrl: environment.network.rpcUrl,
6736
7363
  transactEnvironment: environment
@@ -6739,7 +7366,7 @@ async function submitPreparedDeposit(prepared, environment) {
6739
7366
  contractClient: poolClient,
6740
7367
  contractId: environment.network.poolContract,
6741
7368
  from: artifacts.walletPublicKey,
6742
- nonce: resolveZkConfigNonce(environment),
7369
+ nonce,
6743
7370
  proofHex: artifacts.proofHex,
6744
7371
  publicHex: artifacts.publicHex,
6745
7372
  ...artifacts.ciphertextHex ? { ciphertextHex: artifacts.ciphertextHex } : {},
@@ -6760,6 +7387,7 @@ async function submitPreparedPoolTransact(prepared, environment) {
6760
7387
  proofHex: artifacts.proofHex,
6761
7388
  publicHex: artifacts.publicHex,
6762
7389
  ...optionalInspectCiphertext(artifacts),
7390
+ ...artifacts.zkConfigNonce === void 0 ? {} : { zkConfigNonce: artifacts.zkConfigNonce },
6763
7391
  networkPassphrase: environment.network.networkPassphrase,
6764
7392
  sorobanRpcUrl: environment.network.rpcUrl,
6765
7393
  transactEnvironment: environment
@@ -6815,7 +7443,7 @@ async function privacyPoolInitOptions(assets, transactEnvironment) {
6815
7443
  };
6816
7444
  }
6817
7445
  async function createDefaultTransactEngine(assets, transactEnvironment) {
6818
- await PrivacyPoolSDK3.init(await privacyPoolInitOptions(assets, transactEnvironment));
7446
+ await PrivacyPoolSDK4.init(await privacyPoolInitOptions(assets, transactEnvironment));
6819
7447
  if (transactEnvironment) {
6820
7448
  configurePrivacyPoolService(
6821
7449
  createPrivacyPoolService({
@@ -7217,13 +7845,13 @@ async function fetchEscrowOutputNoteEvents(input) {
7217
7845
  // src/transact/escrow/simulate-escrow-authorization.ts
7218
7846
  import {
7219
7847
  Account,
7220
- Address as Address4,
7848
+ Address as Address5,
7221
7849
  Contract,
7222
7850
  nativeToScVal as nativeToScVal2,
7223
7851
  TransactionBuilder as TransactionBuilder3,
7224
7852
  rpc as rpc5
7225
7853
  } from "@stellar/stellar-sdk";
7226
- import { Buffer as Buffer16 } from "buffer";
7854
+ import { Buffer as Buffer17 } from "buffer";
7227
7855
 
7228
7856
  // src/transact/escrow/kyt-authorization-sc-value.ts
7229
7857
  import { nativeToScVal } from "@stellar/stellar-sdk";
@@ -7243,14 +7871,14 @@ function kytAuthorizationScValue(parameters) {
7243
7871
  }
7244
7872
 
7245
7873
  // src/transact/escrow/pick-escrow-authorization.ts
7246
- import { Address as Address3 } from "@stellar/stellar-sdk";
7874
+ import { Address as Address4 } from "@stellar/stellar-sdk";
7247
7875
  function credentialAddress(entry) {
7248
7876
  try {
7249
7877
  const credentials = entry.credentials();
7250
7878
  if (credentials.switch().name !== "sorobanCredentialsAddress") {
7251
7879
  return void 0;
7252
7880
  }
7253
- return Address3.fromScAddress(credentials.address().address()).toString();
7881
+ return Address4.fromScAddress(credentials.address().address()).toString();
7254
7882
  } catch {
7255
7883
  return void 0;
7256
7884
  }
@@ -7269,7 +7897,7 @@ var SIMULATE_FEE = "100";
7269
7897
  var SIMULATE_TIMEOUT_SECONDS = 30;
7270
7898
  var RECORD_NONROOT_AUTH = "record_allow_nonroot";
7271
7899
  function hexToBytes(hex) {
7272
- return Buffer16.from(hex.replace(/^0x/iu, ""), "hex");
7900
+ return Buffer17.from(hex.replace(/^0x/iu, ""), "hex");
7273
7901
  }
7274
7902
  function optionalSweepCiphertext(artifacts) {
7275
7903
  return {
@@ -7311,6 +7939,7 @@ async function inspectRelayerSweepKyt(input) {
7311
7939
  ...input.prepared.transactArtifacts?.applicationIdsPlaintext ? {
7312
7940
  applicationIdsPlaintext: input.prepared.transactArtifacts.applicationIdsPlaintext
7313
7941
  } : {},
7942
+ ...input.prepared.transactArtifacts?.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.prepared.transactArtifacts.zkConfigNonce },
7314
7943
  networkPassphrase: environment.network.networkPassphrase,
7315
7944
  sorobanRpcUrl: environment.network.rpcUrl,
7316
7945
  transactEnvironment: environment
@@ -7322,8 +7951,11 @@ async function simulateSweepAuthEntries(input) {
7322
7951
  const account = await server.getAccount(input.relayerPublicKey);
7323
7952
  const operation = new Contract(environment.network.poolContract).call(
7324
7953
  "transact",
7325
- new Address4(input.relayerPublicKey).toScVal(),
7326
- nativeToScVal2(resolveZkConfigNonce(environment), { type: "u64" }),
7954
+ new Address5(input.relayerPublicKey).toScVal(),
7955
+ nativeToScVal2(
7956
+ transactNonceFromArtifacts(input.prepared.transactArtifacts, environment),
7957
+ { type: "u64" }
7958
+ ),
7327
7959
  nativeToScVal2(hexToBytes(input.artifacts.proofHex), { type: "bytes" }),
7328
7960
  nativeToScVal2(hexToBytes(input.artifacts.publicHex), { type: "bytes" }),
7329
7961
  nativeToScVal2(hexToBytes(input.artifacts.ciphertextHex ?? ""), { type: "bytes" }),
@@ -7360,6 +7992,7 @@ async function unsignedEscrowAuthorizationForSweep(input) {
7360
7992
  });
7361
7993
  const auth = await simulateSweepAuthEntries({
7362
7994
  artifacts,
7995
+ prepared: input.prepared,
7363
7996
  transactEnvironment: input.transactEnvironment,
7364
7997
  relayerPublicKey,
7365
7998
  approval
@@ -7391,15 +8024,15 @@ async function loadDefaultStellarBrowserAssets(options) {
7391
8024
  }
7392
8025
 
7393
8026
  // src/transact/private-address/temporary-recipient.ts
7394
- import { PrivacyPoolSDK as PrivacyPoolSDK4 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
8027
+ import { PrivacyPoolSDK as PrivacyPoolSDK5 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
7395
8028
  var COORDINATE_HEX_LENGTH3 = 64;
7396
8029
  async function generateRandomDepositScalarHex() {
7397
8030
  await getPrivacyPoolService().getInitializedSdk();
7398
- return PrivacyPoolSDK4.generateRandomScalarHex32();
8031
+ return PrivacyPoolSDK5.generateRandomScalarHex32();
7399
8032
  }
7400
8033
  async function generateTemporaryRecipientPrivateAddress() {
7401
8034
  await getPrivacyPoolService().getInitializedSdk();
7402
- const temporaryScalarHex = PrivacyPoolSDK4.generateRandomScalarHex32();
8035
+ const temporaryScalarHex = PrivacyPoolSDK5.generateRandomScalarHex32();
7403
8036
  const sdk = await getPrivacyPoolService().getInitializedSdk();
7404
8037
  const point = sdk.ecdhEphemeralPublicKeyFromScalarHex(temporaryScalarHex);
7405
8038
  const publicKeyXHex = point.x.padStart(COORDINATE_HEX_LENGTH3, "0");
@@ -7493,7 +8126,7 @@ var RELAY_SIGNAL_INDEX_PUBLIC_DEPOSIT = 91;
7493
8126
  var RELAY_SIGNAL_INDEX_PUBLIC_WITHDRAWAL = 92;
7494
8127
 
7495
8128
  // src/transact/relay/signals.ts
7496
- import { Buffer as Buffer17 } from "buffer";
8129
+ import { Buffer as Buffer18 } from "buffer";
7497
8130
 
7498
8131
  // src/transact/relay/layout-profile.ts
7499
8132
  var FIELD_BYTES = 32;
@@ -7573,7 +8206,7 @@ function relayLayoutProfileForNonce(nonce) {
7573
8206
 
7574
8207
  // src/transact/relay/signals.ts
7575
8208
  function decodePublicSignalBytes(publicSignals) {
7576
- return Buffer17.from(publicSignals.replace(/^0x/iu, ""), "hex");
8209
+ return Buffer18.from(publicSignals.replace(/^0x/iu, ""), "hex");
7577
8210
  }
7578
8211
  function hasSupportedLengthPrefix(bytes, signalCount) {
7579
8212
  const expected = RELAY_TRANSACT_SIGNAL_PREFIX_BYTES + signalCount * RELAY_TRANSACT_FIELD_BYTES;
@@ -7600,7 +8233,7 @@ function sliceSupportedPublicSignalFields(publicSignals, zkConfigNonce = DEFAULT
7600
8233
  const body = hasSupportedLengthPrefix(bytes, profile.signalCount) ? bytes.subarray(RELAY_TRANSACT_SIGNAL_PREFIX_BYTES) : bytes;
7601
8234
  return Array.from(
7602
8235
  { length: profile.signalCount },
7603
- (_unused, index) => Buffer17.from(
8236
+ (_unused, index) => Buffer18.from(
7604
8237
  body.subarray(
7605
8238
  index * RELAY_TRANSACT_FIELD_BYTES,
7606
8239
  (index + 1) * RELAY_TRANSACT_FIELD_BYTES
@@ -7655,10 +8288,11 @@ function requirePreparedArtifacts(prepared) {
7655
8288
  };
7656
8289
  }
7657
8290
  function prepareRelayTransactPackageFromPrepared(input) {
8291
+ const zkConfigNonce = input.zkConfigNonce ?? input.prepared.transactArtifacts?.zkConfigNonce;
7658
8292
  return prepareRelayTransactPackage({
7659
8293
  poolSelector: input.poolSelector,
7660
8294
  ...requirePreparedArtifacts(input.prepared),
7661
- ...input.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.zkConfigNonce },
8295
+ ...zkConfigNonce === void 0 ? {} : { zkConfigNonce },
7662
8296
  ...input.keyVersionHints ? { keyVersionHints: input.keyVersionHints } : {}
7663
8297
  });
7664
8298
  }
@@ -7768,6 +8402,7 @@ function requiredSubmissionMethod(prepared, zkConfigNonce = DEFAULT_ZK_CONFIG_NO
7768
8402
  return result.method;
7769
8403
  }
7770
8404
  export {
8405
+ BINDING_ZK_NONCE,
7771
8406
  BundledZkCircuit2 as BundledZkCircuit,
7772
8407
  COMMITMENT_V2_ZK_NONCE,
7773
8408
  DEFAULT_STEALTH_SIGN_NONCE as DEFAULT_PRIVATE_ADDRESS_SIGN_NONCE,
@@ -7796,6 +8431,7 @@ export {
7796
8431
  RELAY_TRANSACT_PACKAGE_VERSION_V1,
7797
8432
  RELAY_TRANSACT_SIGNAL_PREFIX_BYTES,
7798
8433
  RELAY_TRANSACT_SUPPORTED_SIGNAL_COUNT,
8434
+ SIX_BY_SIX_BINDING_ZK_NONCE,
7799
8435
  SIX_BY_SIX_ZK_NONCE,
7800
8436
  STANDARD_ZK_CONFIG_NONCE,
7801
8437
  SUBMISSION_METHOD,