@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/node.js CHANGED
@@ -468,27 +468,9 @@ function buildPrivateRecordFromDeposit(input) {
468
468
 
469
469
  // src/transact/pool/service.ts
470
470
  import {
471
- PrivacyPoolSDK as PrivacyPoolSDK2,
472
- decodeDecodedEphemeralKey,
473
- randomFrDecimal253,
474
- scalarHexToFrDecimal as scalarHexToFrDecimal2
471
+ decodeDecodedEphemeralKey
475
472
  } from "@arcanetech/stellar-privacy-pool-zk-sdk";
476
473
 
477
- // src/transact/merkle/field-decimal.ts
478
- import { Buffer as Buffer4 } from "buffer";
479
- var BN254 = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
480
- var MERKLE_ROOT_BYTE_LENGTH = 32;
481
- var DECIMAL_STRING_RADIX = 10;
482
- function merkleRootBufferToFrDecimal(root) {
483
- if (root.length !== MERKLE_ROOT_BYTE_LENGTH) {
484
- throw new Error(
485
- `expected ${MERKLE_ROOT_BYTE_LENGTH}-byte merkle root, got ${root.length}`
486
- );
487
- }
488
- const hex = Buffer4.from(root).toString("hex");
489
- return (BigInt(`0x${hex}`) % BN254).toString(DECIMAL_STRING_RADIX);
490
- }
491
-
492
474
  // src/transact/private-address/codec.ts
493
475
  import {
494
476
  buildStealthAddressSignMessage,
@@ -537,18 +519,18 @@ function privateAddressSdk(sdk) {
537
519
  import { withdrawObjectFromMerkleWitness as withdrawObjectFromMerkleWitness2 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
538
520
 
539
521
  // src/transact/proofs/transaction-input.ts
540
- import { Buffer as Buffer6 } from "buffer";
522
+ import { Buffer as Buffer5 } from "buffer";
541
523
  import { StrKey as StrKey2 } from "@stellar/stellar-sdk";
542
524
 
543
525
  // src/transact/stellar/token-contract.ts
544
- import { Buffer as Buffer5 } from "buffer";
526
+ import { Buffer as Buffer4 } from "buffer";
545
527
  import { StrKey } from "@stellar/stellar-sdk";
546
528
  var CONTRACT_ID_BYTE_LENGTH = 32;
547
529
  var HEX_CHARS_PER_BYTE = 2;
548
530
  var CONTRACT_ID_HEX_CHAR_LENGTH = CONTRACT_ID_BYTE_LENGTH * HEX_CHARS_PER_BYTE;
549
531
  var CONTRACT_ID_HEX = /^(?:0x)?([0-9a-f]{64})$/i;
550
532
  function encodeContractFromHex(hexPayload) {
551
- return StrKey.encodeContract(Buffer5.from(hexPayload, "hex"));
533
+ return StrKey.encodeContract(Buffer4.from(hexPayload, "hex"));
552
534
  }
553
535
  function normalizeSorobanContractId(raw) {
554
536
  const trimmed = raw.trim();
@@ -581,7 +563,7 @@ var ZERO_PUBLIC_WITHDRAW_ADDRESS = {
581
563
  var CONTRACT_ADDRESS_HALF_BYTES = 16;
582
564
  var CONTRACT_ADDRESS_BYTES = 32;
583
565
  function bytes16ToDecimal(bytes) {
584
- return BigInt(`0x${Buffer6.from(bytes).toString("hex")}`).toString();
566
+ return BigInt(`0x${Buffer5.from(bytes).toString("hex")}`).toString();
585
567
  }
586
568
  function tokenAddressFrParts(tokenAddress) {
587
569
  const normalized = resolveTokenContractId(tokenAddress);
@@ -742,14 +724,144 @@ function requireChangeRecipientWhenPartial(changeStroops, selfPrivateAddressStpl
742
724
  }
743
725
  }
744
726
  }
745
- function changeStroopsAfterDualTransfer(transferStroops, totalNotes) {
746
- if (transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
747
- throw new Error("Transfer amount must be positive");
727
+
728
+ // src/transact/fees/quote-request-body.ts
729
+ function presentInstructedAmount(amount) {
730
+ const trimmed = amount?.trim() ?? "";
731
+ if (!trimmed || trimmed === "0") {
732
+ return void 0;
733
+ }
734
+ return trimmed;
735
+ }
736
+ function feeQuoteRequestBody(request) {
737
+ const publicDepositAmount = presentInstructedAmount(request.publicDepositAmount);
738
+ const transferInstructedAmount = presentInstructedAmount(
739
+ request.transferInstructedAmount
740
+ );
741
+ const publicWithdrawalAmount = presentInstructedAmount(
742
+ request.publicWithdrawalAmount
743
+ );
744
+ return {
745
+ feeAsset: request.feeAsset,
746
+ spendsNotes: request.spendsNotes,
747
+ ...publicDepositAmount === void 0 ? {} : { publicDepositAmount },
748
+ ...transferInstructedAmount === void 0 ? {} : { transferInstructedAmount },
749
+ ...publicWithdrawalAmount === void 0 ? {} : { publicWithdrawalAmount }
750
+ };
751
+ }
752
+
753
+ // src/transact/fees/quote-required-fee.ts
754
+ function quoteHeaders(inspectAuthorization) {
755
+ const token = inspectAuthorization?.trim();
756
+ return {
757
+ "content-type": "application/json",
758
+ ...token ? { authorization: `Bearer ${token}` } : {}
759
+ };
760
+ }
761
+ function parseRequiredFee(raw) {
762
+ const text = `${raw ?? ""}`.trim();
763
+ if (!/^\d+$/u.test(text)) {
764
+ throw new Error("Fee Quote Required Fee must be an integer minor-unit string.");
765
+ }
766
+ return BigInt(text);
767
+ }
768
+ function requireQuoteString(raw, field) {
769
+ const text = `${raw ?? ""}`.trim();
770
+ if (!text) {
771
+ throw new Error(`Fee Quote is missing ${field}.`);
772
+ }
773
+ return text;
774
+ }
775
+ function parseQuotePayload(payload) {
776
+ return {
777
+ requiredFee: parseRequiredFee(payload.requiredFee),
778
+ feeAsset: requireQuoteString(payload.feeAsset, "Fee Asset"),
779
+ feeCollectorPrivateAddress: requireQuoteString(
780
+ payload.feeCollectorPrivateAddress,
781
+ "Fee Collector Private Address"
782
+ ),
783
+ feeRate: requireQuoteString(payload.feeRate, "Fee Rate")
784
+ };
785
+ }
786
+ async function quoteRequiredFee(input) {
787
+ const response = await fetch(
788
+ `${input.transactEnvironment.kyt.apiBaseUrl}/kyt/fees/quote`,
789
+ {
790
+ method: "POST",
791
+ headers: quoteHeaders(input.transactEnvironment.kyt.inspectAuthorization),
792
+ body: JSON.stringify(feeQuoteRequestBody(input.request))
793
+ }
794
+ );
795
+ const payload = await response.json();
796
+ if (!response.ok) {
797
+ throw new Error(`Fee Quote failed: ${JSON.stringify(payload)}`);
798
+ }
799
+ return parseQuotePayload(payload);
800
+ }
801
+
802
+ // src/transact/fees/should-attach-fee-output.ts
803
+ function shouldAttachFeeOutput(input) {
804
+ if (input.spendSource === "escrow" && input.escrowSend !== true) {
805
+ return false;
806
+ }
807
+ return input.kind === "deposit" || input.kind === "transfer" || input.kind === "withdraw";
808
+ }
809
+ function feeOutputKindFromPrepared(prepared) {
810
+ return {
811
+ kind: prepared.kind,
812
+ ...prepared.transactArtifacts?.spendSource ? { spendSource: prepared.transactArtifacts.spendSource } : {},
813
+ ...prepared.transactArtifacts?.escrowSend ? { escrowSend: true } : {}
814
+ };
815
+ }
816
+
817
+ // src/transact/fees/quote-fee-output-for-prepared.ts
818
+ function instructedMinorUnits(prepared) {
819
+ return prepared.intent.amount.toString();
820
+ }
821
+ function quoteRequestForPrepared(input) {
822
+ const amount = instructedMinorUnits(input.prepared);
823
+ if (input.prepared.kind === "deposit") {
824
+ return {
825
+ feeAsset: input.tokenAddress,
826
+ publicDepositAmount: amount,
827
+ spendsNotes: false
828
+ };
829
+ }
830
+ if (input.prepared.kind === "withdraw") {
831
+ return {
832
+ feeAsset: input.tokenAddress,
833
+ publicWithdrawalAmount: amount,
834
+ spendsNotes: true
835
+ };
836
+ }
837
+ return {
838
+ feeAsset: input.tokenAddress,
839
+ transferInstructedAmount: amount,
840
+ spendsNotes: true
841
+ };
842
+ }
843
+ async function quoteFeeOutputForPrepared(input) {
844
+ if (!shouldAttachFeeOutput(feeOutputKindFromPrepared(input.prepared))) {
845
+ return void 0;
846
+ }
847
+ const quote = await quoteRequiredFee({
848
+ transactEnvironment: input.environment,
849
+ request: quoteRequestForPrepared(input)
850
+ });
851
+ if (quote.requiredFee <= 0n) {
852
+ return void 0;
748
853
  }
749
- if (transferStroops > totalNotes) {
750
- throw new Error("Transfer amount exceeds combined note value");
854
+ return {
855
+ requiredFee: quote.requiredFee,
856
+ feeCollectorPrivateAddress: quote.feeCollectorPrivateAddress
857
+ };
858
+ }
859
+ function remainingAfterRequiredFee(input) {
860
+ const remaining = input.available - input.instructed - input.requiredFee;
861
+ if (remaining < 0n) {
862
+ throw new Error("Instructed amount and Required Fee exceed available note value");
751
863
  }
752
- return totalNotes - transferStroops;
864
+ return remaining;
753
865
  }
754
866
 
755
867
  // src/transact/audit/parameters.ts
@@ -757,7 +869,7 @@ import {
757
869
  DEMO_AUDIT_PUBLIC_KEY,
758
870
  resolveTransactionAuditParams
759
871
  } from "@arcanetech/stellar-privacy-pool-zk-sdk";
760
- import { Buffer as Buffer7 } from "buffer";
872
+ import { Buffer as Buffer6 } from "buffer";
761
873
  var UNCOMPRESSED_PREFIX = "04";
762
874
  var UNCOMPRESSED_HEX_LENGTH = 130;
763
875
  var XY_HEX_LENGTH = 128;
@@ -784,7 +896,7 @@ function parseAuditPublicKeyFromEnvHex(raw) {
784
896
  "Audit public key must be uncompressed 65-byte hex (04 + X + Y) or 64-byte X+Y hex"
785
897
  );
786
898
  }
787
- const bytes = Buffer7.from(payload, "hex");
899
+ const bytes = Buffer6.from(payload, "hex");
788
900
  const auditPublicKeyXDecimal = BigInt(
789
901
  `0x${bytes.subarray(0, X_OFFSET_END).toString("hex")}`
790
902
  ).toString(DECIMAL_RADIX);
@@ -806,6 +918,7 @@ function buildPoolTransactionAuditParameters(input) {
806
918
  input.nAuditSlots
807
919
  );
808
920
  }
921
+ var DECIMAL_APPLICATION_ID = /^\d+$/u;
809
922
  function resolvePoolApplicationId(applicationId) {
810
923
  const trimmed = applicationId?.trim();
811
924
  if (!trimmed) {
@@ -813,6 +926,11 @@ function resolvePoolApplicationId(applicationId) {
813
926
  "Privacy pool applicationId is required. Pass it in client factory config."
814
927
  );
815
928
  }
929
+ if (!DECIMAL_APPLICATION_ID.test(trimmed)) {
930
+ throw new Error(
931
+ "Privacy pool applicationId must be association.audit_id (decimal Fr), not a UUID or foreignId."
932
+ );
933
+ }
816
934
  return trimmed;
817
935
  }
818
936
 
@@ -821,13 +939,13 @@ import { ed25519PubkeyPayloadHexToWithdrawFrDecimals } from "@arcanetech/stellar
821
939
 
822
940
  // src/transact/stellar/account.ts
823
941
  import { StrKey as StrKey3 } from "@stellar/stellar-sdk";
824
- import { Buffer as Buffer8 } from "buffer";
942
+ import { Buffer as Buffer7 } from "buffer";
825
943
  var MUXED_ACCOUNT_ID_BYTE_LENGTH = 8;
826
944
  var ED25519_PUBLIC_KEY_BYTE_LENGTH = 32;
827
945
  function ed25519HexFromGAddress(trimmed) {
828
946
  try {
829
947
  const raw = StrKey3.decodeEd25519PublicKey(trimmed);
830
- return Buffer8.from(raw).toString("hex");
948
+ return Buffer7.from(raw).toString("hex");
831
949
  } catch {
832
950
  return void 0;
833
951
  }
@@ -884,6 +1002,32 @@ function privKeyScalarDecimalFromRecipientScalarHex(hexInput) {
884
1002
  return canonicalBabyJubScalarFromInteger(value).toString(DECIMAL_RADIX2);
885
1003
  }
886
1004
 
1005
+ // src/transact/fees/append-fee-output.ts
1006
+ function feeCoinFromSlot(slot) {
1007
+ return {
1008
+ commitment_hex: slot.commitment_hex,
1009
+ coin: slot.coin,
1010
+ depositScalarHex: slot.depositScalarHex,
1011
+ precommitementHex: slot.precommitementHex
1012
+ };
1013
+ }
1014
+ async function appendFeeOutputSlot(input) {
1015
+ const requiredFee = input.feeOutput?.requiredFee ?? ZERO_STROOPS;
1016
+ const collector = input.feeOutput?.feeCollectorPrivateAddress.trim() ?? "";
1017
+ if (requiredFee <= ZERO_STROOPS || !collector) {
1018
+ return { deposits: input.deposits };
1019
+ }
1020
+ const slot = await input.buildAlignedDepositSlot({
1021
+ privateAddressStpl1: collector,
1022
+ amountStroops: requiredFee,
1023
+ tokenAddress: input.tokenAddress
1024
+ });
1025
+ return {
1026
+ deposits: [...input.deposits, slot.deposit],
1027
+ feeCoin: feeCoinFromSlot(slot)
1028
+ };
1029
+ }
1030
+
887
1031
  // src/transact/proofs/withdraw/helpers.ts
888
1032
  function destinationWithdrawFrAndScalar(parameters) {
889
1033
  const pkHex = ed25519PublicKeyHexFromStellarAccount(
@@ -938,14 +1082,15 @@ async function resolveWithdrawChangeDeposits(parameters) {
938
1082
  }
939
1083
  };
940
1084
  }
941
- function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1) {
1085
+ function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1, requiredFee) {
942
1086
  if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
943
1087
  throw new Error("Withdraw amount must be positive");
944
1088
  }
945
- if (withdrawAmountStroops > totalNotes) {
946
- throw new Error("Withdraw amount exceeds combined note value");
947
- }
948
- const changeStroops = totalNotes - withdrawAmountStroops;
1089
+ const changeStroops = remainingAfterRequiredFee({
1090
+ available: totalNotes,
1091
+ instructed: withdrawAmountStroops,
1092
+ requiredFee
1093
+ });
949
1094
  requireChangeRecipientWhenPartial(changeStroops, changePrivateAddressStpl1);
950
1095
  return changeStroops;
951
1096
  }
@@ -958,11 +1103,31 @@ function buildDualWithdrawPublicInput(parameters) {
958
1103
  publicWithdrawalAmount: parameters.publicWithdrawalAmount
959
1104
  });
960
1105
  }
1106
+ async function withdrawOutputsWithOptionalFee(parameters) {
1107
+ const { deposits: changeDeposits, changeCoin } = await resolveWithdrawChangeDeposits({
1108
+ changeStroops: parameters.changeStroops,
1109
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1110
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1111
+ tokenAddress: parameters.tokenAddress
1112
+ });
1113
+ const withFee = await appendFeeOutputSlot({
1114
+ deposits: changeDeposits,
1115
+ tokenAddress: parameters.tokenAddress,
1116
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1117
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1118
+ });
1119
+ return {
1120
+ deposits: withFee.deposits,
1121
+ ...changeCoin ? { changeCoin } : {},
1122
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
1123
+ };
1124
+ }
961
1125
  async function prepareDualWithdrawProofInputs(parameters) {
962
1126
  const changeStroops = validateDualWithdrawAmounts(
963
1127
  parameters.withdrawAmountStroops,
964
1128
  BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value),
965
- parameters.changePrivateAddressStpl1
1129
+ parameters.changePrivateAddressStpl1,
1130
+ parameters.feeOutput?.requiredFee ?? 0n
966
1131
  );
967
1132
  const { legA, legB } = dualWithdrawLegsWithSharedRoot({
968
1133
  sdk: parameters.sdk,
@@ -977,11 +1142,12 @@ async function prepareDualWithdrawProofInputs(parameters) {
977
1142
  ),
978
1143
  applicationId: parameters.applicationId
979
1144
  });
980
- const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
1145
+ const { deposits, changeCoin, feeCoin } = await withdrawOutputsWithOptionalFee({
981
1146
  changeStroops,
982
1147
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
983
1148
  buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
984
- tokenAddress: parameters.tokenAddress
1149
+ tokenAddress: parameters.tokenAddress,
1150
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
985
1151
  });
986
1152
  const publicInput = buildDualWithdrawPublicInput({
987
1153
  destinationStellarAddress: parameters.destinationStellarAddress,
@@ -1000,19 +1166,55 @@ async function prepareDualWithdrawProofInputs(parameters) {
1000
1166
  audit,
1001
1167
  deposits,
1002
1168
  withdrawLegs: [legA.withdrawObject, legB.withdrawObject],
1003
- ...changeCoin ? { changeCoin } : {}
1169
+ ...changeCoin ? { changeCoin } : {},
1170
+ ...feeCoin ? { feeCoin } : {}
1004
1171
  };
1005
1172
  }
1006
1173
 
1174
+ // src/transact/proofs/withdraw/fee-deposits.ts
1175
+ async function withdrawDepositsWithFee(parameters) {
1176
+ const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
1177
+ changeStroops: parameters.changeStroops,
1178
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1179
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1180
+ tokenAddress: parameters.tokenAddress
1181
+ });
1182
+ const withFee = await appendFeeOutputSlot({
1183
+ deposits,
1184
+ tokenAddress: parameters.tokenAddress,
1185
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1186
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1187
+ });
1188
+ return {
1189
+ deposits: withFee.deposits,
1190
+ ...changeCoin ? { changeCoin } : {},
1191
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
1192
+ };
1193
+ }
1194
+ function withdrawOutputApplicationIds(input) {
1195
+ const outputIds = [];
1196
+ if (input.hasChange) {
1197
+ outputIds.push(input.applicationId);
1198
+ }
1199
+ if (input.hasFeeOutput) {
1200
+ outputIds.push(input.applicationId);
1201
+ }
1202
+ if (outputIds.length === 0) {
1203
+ outputIds.push("0");
1204
+ }
1205
+ return outputIds;
1206
+ }
1207
+
1007
1208
  // src/transact/proofs/withdraw/transact-proof.ts
1008
- function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops) {
1209
+ function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops, requiredFee) {
1009
1210
  if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
1010
1211
  throw new Error("Withdraw amount must be positive");
1011
1212
  }
1012
- if (withdrawAmountStroops > noteStroops) {
1013
- throw new Error("Withdraw amount exceeds note value");
1014
- }
1015
- return noteStroops - withdrawAmountStroops;
1213
+ return remainingAfterRequiredFee({
1214
+ available: noteStroops,
1215
+ instructed: withdrawAmountStroops,
1216
+ requiredFee
1217
+ });
1016
1218
  }
1017
1219
  async function proveSingleWithdrawTransaction(parameters) {
1018
1220
  return parameters.sdk.proveTransaction(
@@ -1051,7 +1253,8 @@ function buildPrimaryWithdrawForCoin(parameters) {
1051
1253
  async function prepareSingleWithdrawProofInputs(parameters) {
1052
1254
  const changeStroops = validateSingleWithdrawAmounts(
1053
1255
  parameters.withdrawAmountStroops,
1054
- BigInt(parameters.coin.value)
1256
+ BigInt(parameters.coin.value),
1257
+ parameters.feeOutput?.requiredFee ?? 0n
1055
1258
  );
1056
1259
  const { witness, primaryWithdraw } = buildPrimaryWithdrawForCoin({
1057
1260
  sdk: parameters.sdk,
@@ -1060,11 +1263,12 @@ async function prepareSingleWithdrawProofInputs(parameters) {
1060
1263
  state: parameters.state,
1061
1264
  privKeyScalarHex: parameters.privKeyScalarHex
1062
1265
  });
1063
- const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
1266
+ const { deposits, changeCoin, feeCoin } = await withdrawDepositsWithFee({
1064
1267
  changeStroops,
1065
1268
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1066
1269
  buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1067
- tokenAddress: parameters.tokenAddress
1270
+ tokenAddress: parameters.tokenAddress,
1271
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1068
1272
  });
1069
1273
  const publicInput = buildWithdrawPublicInput({
1070
1274
  stateRoot: witness.stateRoot,
@@ -1077,6 +1281,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
1077
1281
  primaryWithdraw,
1078
1282
  deposits,
1079
1283
  changeCoin,
1284
+ feeCoin,
1080
1285
  publicInput,
1081
1286
  audit: buildPoolTransactionAuditParameters({
1082
1287
  applicationId: parameters.applicationId,
@@ -1086,7 +1291,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
1086
1291
  };
1087
1292
  }
1088
1293
  async function proveWithdrawTransact(parameters) {
1089
- const { primaryWithdraw, deposits, changeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
1294
+ const { primaryWithdraw, deposits, changeCoin, feeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
1090
1295
  const proof = await proveSingleWithdrawTransaction({
1091
1296
  sdk: parameters.sdk,
1092
1297
  publicInput,
@@ -1101,13 +1306,17 @@ async function proveWithdrawTransact(parameters) {
1101
1306
  applicationIdsPlaintext: buildApplicationIdHints({
1102
1307
  sdk: parameters.sdk,
1103
1308
  inputIds: [parameters.applicationId],
1104
- outputIds: [changeCoin ? parameters.applicationId : "0"]
1309
+ outputIds: withdrawOutputApplicationIds({
1310
+ applicationId: parameters.applicationId,
1311
+ hasChange: Boolean(changeCoin),
1312
+ hasFeeOutput: Boolean(feeCoin)
1313
+ })
1105
1314
  }),
1106
1315
  ...changeCoin ? { changeCoin } : {}
1107
1316
  };
1108
1317
  }
1109
1318
  async function proveWithdrawTransactDual(parameters) {
1110
- const { publicInput, audit, withdrawLegs, deposits, changeCoin } = await prepareDualWithdrawProofInputs(parameters);
1319
+ const { publicInput, audit, withdrawLegs, deposits, changeCoin, feeCoin } = await prepareDualWithdrawProofInputs(parameters);
1111
1320
  const proof = await parameters.sdk.proveTransaction(
1112
1321
  publicInput,
1113
1322
  padPublicLegsToLayout(
@@ -1126,7 +1335,11 @@ async function proveWithdrawTransactDual(parameters) {
1126
1335
  applicationIdsPlaintext: buildApplicationIdHints({
1127
1336
  sdk: parameters.sdk,
1128
1337
  inputIds: [audit.applicationId, audit.applicationId],
1129
- outputIds: [changeCoin ? audit.applicationId : "0"]
1338
+ outputIds: withdrawOutputApplicationIds({
1339
+ applicationId: audit.applicationId,
1340
+ hasChange: Boolean(changeCoin),
1341
+ hasFeeOutput: Boolean(feeCoin)
1342
+ })
1130
1343
  }),
1131
1344
  ...changeCoin ? { changeCoin } : {}
1132
1345
  };
@@ -1147,6 +1360,9 @@ var DEFAULT_ZK_CONFIG_NONCE = BINDING_ZK_NONCE;
1147
1360
  function resolveZkConfigNonce(transactEnvironment) {
1148
1361
  return transactEnvironment.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1149
1362
  }
1363
+ function transactNonceFromArtifacts(artifacts, transactEnvironment) {
1364
+ return artifacts?.zkConfigNonce ?? resolveZkConfigNonce(transactEnvironment);
1365
+ }
1150
1366
 
1151
1367
  // src/transact/zk/browser-artifact-cache.ts
1152
1368
  import { ZK_ARTIFACT_VERSION } from "@arcanetech/stellar-privacy-pool-zk-sdk";
@@ -1399,6 +1615,122 @@ async function buildAlignedDepositSlotForSdk(sdk, applicationId, parameters) {
1399
1615
  };
1400
1616
  }
1401
1617
 
1618
+ // src/transact/pool/initialize-sdk.ts
1619
+ import { PrivacyPoolSDK as PrivacyPoolSDK2 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
1620
+ async function initializePrivacyPoolSdk(input, nonce) {
1621
+ const zkConfigNonce = nonce ?? input.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1622
+ const zkCircuits = await materializeSelectedZkCircuit(
1623
+ input.zkCircuits,
1624
+ zkConfigNonce,
1625
+ input.zkArtifactBaseUrl
1626
+ );
1627
+ return PrivacyPoolSDK2.init({
1628
+ wasmBinary: input.assets.sdkWasm,
1629
+ zkCircuits,
1630
+ zkConfigNonce,
1631
+ ...optionalZkArtifactBaseUrl(input.zkArtifactBaseUrl)
1632
+ });
1633
+ }
1634
+
1635
+ // src/transact/fees/zk-config-nonce-for-kind.ts
1636
+ function configuredNonceSpread(zkConfigNonce) {
1637
+ if (zkConfigNonce === void 0) {
1638
+ return {};
1639
+ }
1640
+ return { configuredNonce: zkConfigNonce };
1641
+ }
1642
+ function zkConfigNonceForFeeBearingKind(input) {
1643
+ if (input.configuredNonce === SIX_BY_SIX_BINDING_ZK_NONCE) {
1644
+ return SIX_BY_SIX_BINDING_ZK_NONCE;
1645
+ }
1646
+ if (input.kind === "transfer" && shouldAttachFeeOutput(input)) {
1647
+ return SIX_BY_SIX_BINDING_ZK_NONCE;
1648
+ }
1649
+ return input.configuredNonce ?? BINDING_ZK_NONCE;
1650
+ }
1651
+
1652
+ // src/transact/pool/prepare-deposit-proof.ts
1653
+ import {
1654
+ randomFrDecimal253,
1655
+ scalarHexToFrDecimal as scalarHexToFrDecimal2
1656
+ } from "@arcanetech/stellar-privacy-pool-zk-sdk";
1657
+
1658
+ // src/transact/merkle/field-decimal.ts
1659
+ import { Buffer as Buffer8 } from "buffer";
1660
+ var BN254 = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
1661
+ var MERKLE_ROOT_BYTE_LENGTH = 32;
1662
+ var DECIMAL_STRING_RADIX = 10;
1663
+ function merkleRootBufferToFrDecimal(root) {
1664
+ if (root.length !== MERKLE_ROOT_BYTE_LENGTH) {
1665
+ throw new Error(
1666
+ `expected ${MERKLE_ROOT_BYTE_LENGTH}-byte merkle root, got ${root.length}`
1667
+ );
1668
+ }
1669
+ const hex = Buffer8.from(root).toString("hex");
1670
+ return (BigInt(`0x${hex}`) % BN254).toString(DECIMAL_STRING_RADIX);
1671
+ }
1672
+
1673
+ // src/transact/pool/prepare-deposit-proof.ts
1674
+ function userDepositSlot(input) {
1675
+ return {
1676
+ value: input.coin.value,
1677
+ nullifier: input.coin.nullifier,
1678
+ ephemeralKeyScalar: scalarHexToFrDecimal2(input.depositScalarHex),
1679
+ asset: [input.coin.asset_hi, input.coin.asset_lo],
1680
+ applicationId: input.applicationId,
1681
+ recipientPublicKeys: recipientPublicKeysDecimalFromPrivateAddress(
1682
+ input.privateAddressStpl1
1683
+ )
1684
+ };
1685
+ }
1686
+ function outputApplicationIds(input) {
1687
+ if (input.hasFeeOutput) {
1688
+ return [input.applicationId, input.applicationId];
1689
+ }
1690
+ return [input.applicationId];
1691
+ }
1692
+ async function proveDepositTransact(input) {
1693
+ const publicDeposit = (input.publicDepositStroops ?? BigInt(input.coin.value)).toString();
1694
+ const withFee = await appendFeeOutputSlot({
1695
+ deposits: [userDepositSlot(input)],
1696
+ tokenAddress: input.tokenAddress,
1697
+ buildAlignedDepositSlot: input.buildAlignedDepositSlot,
1698
+ ...input.feeOutput ? { feeOutput: input.feeOutput } : {}
1699
+ });
1700
+ const audit = buildPoolTransactionAuditParameters({
1701
+ applicationId: input.applicationId,
1702
+ nAuditSlots: input.sdk.getLayout().nAuditSlots,
1703
+ ...input.auditPublicKey ? { auditPublicKey: input.auditPublicKey } : {}
1704
+ });
1705
+ const proof = await input.sdk.proveTransaction(
1706
+ withTokenAddressPublicInputs(
1707
+ {
1708
+ stateRoot: merkleRootBufferToFrDecimal(input.merkleRootBytes),
1709
+ privKeyScalar: randomFrDecimal253()
1710
+ },
1711
+ input.tokenAddress
1712
+ ),
1713
+ padPublicLegsToLayout(
1714
+ input.sdk,
1715
+ buildPublicDepositLegs(input.tokenAddress, publicDeposit)
1716
+ ),
1717
+ padWithdrawSlotsToLayout(input.sdk, []),
1718
+ padDepositSlotsToLayout(input.sdk, withFee.deposits),
1719
+ audit
1720
+ );
1721
+ return {
1722
+ ...proof,
1723
+ applicationIdsPlaintext: buildApplicationIdHints({
1724
+ sdk: input.sdk,
1725
+ inputIds: [],
1726
+ outputIds: outputApplicationIds({
1727
+ applicationId: input.applicationId,
1728
+ hasFeeOutput: Boolean(withFee.feeCoin)
1729
+ })
1730
+ })
1731
+ };
1732
+ }
1733
+
1402
1734
  // src/transact/pool/service.ts
1403
1735
  var PrivacyPoolService = class {
1404
1736
  constructor(assets, applicationId, auditPublicKey, zkCircuits, zkConfigNonce, zkArtifactBaseUrl) {
@@ -1415,42 +1747,50 @@ var PrivacyPoolService = class {
1415
1747
  zkCircuits;
1416
1748
  zkConfigNonce;
1417
1749
  zkArtifactBaseUrl;
1418
- sdk = void 0;
1419
- initPromise = void 0;
1420
- async ensureInit() {
1421
- if (this.sdk) {
1422
- return;
1750
+ sdks = /* @__PURE__ */ new Map();
1751
+ initPromises = /* @__PURE__ */ new Map();
1752
+ async getInitializedSdk(nonce) {
1753
+ const zkConfigNonce = nonce ?? this.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1754
+ const key = zkConfigNonce.toString();
1755
+ const existing = this.sdks.get(key);
1756
+ if (existing) {
1757
+ return existing;
1423
1758
  }
1424
- if (this.initPromise) {
1425
- return this.initPromise;
1759
+ const pending = this.initPromises.get(key);
1760
+ if (pending) {
1761
+ return pending;
1426
1762
  }
1427
- this.initPromise = (async () => {
1428
- if (!this.assets) {
1429
- throw new Error(
1430
- "PrivacyPoolService requires browser ZK assets before initialization."
1431
- );
1432
- }
1433
- const zkConfigNonce = this.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1434
- const zkCircuits = await materializeSelectedZkCircuit(
1435
- this.zkCircuits,
1436
- zkConfigNonce,
1437
- this.zkArtifactBaseUrl
1438
- );
1439
- this.sdk = await PrivacyPoolSDK2.init({
1440
- wasmBinary: this.assets.sdkWasm,
1441
- zkCircuits,
1442
- zkConfigNonce,
1443
- ...optionalZkArtifactBaseUrl(this.zkArtifactBaseUrl)
1444
- });
1445
- })();
1446
- return this.initPromise;
1763
+ const started = this.initializeSdk(zkConfigNonce);
1764
+ this.initPromises.set(key, started);
1765
+ try {
1766
+ const sdk = await started;
1767
+ this.sdks.set(key, sdk);
1768
+ return sdk;
1769
+ } finally {
1770
+ this.initPromises.delete(key);
1771
+ }
1772
+ }
1773
+ feeBearingZkConfigNonce(input) {
1774
+ return zkConfigNonceForFeeBearingKind({
1775
+ ...input,
1776
+ ...configuredNonceSpread(this.zkConfigNonce)
1777
+ });
1447
1778
  }
1448
- async getInitializedSdk() {
1449
- await this.ensureInit();
1450
- if (!this.sdk) {
1451
- throw new Error("SDK not initialized");
1779
+ async initializeSdk(nonce) {
1780
+ if (!this.assets) {
1781
+ throw new Error(
1782
+ "PrivacyPoolService requires browser ZK assets before initialization."
1783
+ );
1452
1784
  }
1453
- return this.sdk;
1785
+ return initializePrivacyPoolSdk(
1786
+ {
1787
+ assets: this.assets,
1788
+ ...this.zkCircuits ? { zkCircuits: this.zkCircuits } : {},
1789
+ ...this.zkConfigNonce === void 0 ? {} : { zkConfigNonce: this.zkConfigNonce },
1790
+ ...optionalZkArtifactBaseUrl(this.zkArtifactBaseUrl)
1791
+ },
1792
+ nonce
1793
+ );
1454
1794
  }
1455
1795
  getAuditPublicKey() {
1456
1796
  return this.auditPublicKey;
@@ -1483,65 +1823,51 @@ var PrivacyPoolService = class {
1483
1823
  return { commitment_hex, coin, depositScalarHex, precommitementHex };
1484
1824
  }
1485
1825
  async prepareDepositTransactProof(parameters) {
1486
- const sdk = await this.getInitializedSdk();
1487
- const stateRoot = merkleRootBufferToFrDecimal(parameters.merkleRootBytes);
1488
- const recipientPublicKeys = recipientPublicKeysDecimalFromPrivateAddress(
1489
- parameters.privateAddressStpl1
1490
- );
1491
- const applicationId = this.getApplicationId();
1492
- const audit = buildPoolTransactionAuditParameters({
1493
- applicationId,
1494
- nAuditSlots: sdk.getLayout().nAuditSlots,
1495
- ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {}
1496
- });
1497
- const deposit = {
1498
- value: parameters.coin.value,
1499
- nullifier: parameters.coin.nullifier,
1500
- ephemeralKeyScalar: scalarHexToFrDecimal2(parameters.depositScalarHex),
1501
- asset: [parameters.coin.asset_hi, parameters.coin.asset_lo],
1502
- applicationId,
1503
- recipientPublicKeys
1504
- };
1505
- const publicInput = withTokenAddressPublicInputs(
1506
- {
1507
- stateRoot,
1508
- privKeyScalar: randomFrDecimal253()
1509
- },
1510
- parameters.tokenAddress
1826
+ const sdk = await this.getInitializedSdk(
1827
+ this.feeBearingZkConfigNonce({ kind: "deposit" })
1511
1828
  );
1512
- const proof = await sdk.proveTransaction(
1513
- publicInput,
1514
- padPublicLegsToLayout(
1515
- sdk,
1516
- buildPublicDepositLegs(parameters.tokenAddress, parameters.coin.value)
1517
- ),
1518
- padWithdrawSlotsToLayout(sdk, []),
1519
- padDepositSlotsToLayout(sdk, [deposit]),
1520
- audit
1521
- );
1522
- const applicationIdsPlaintext = buildApplicationIdHints({
1829
+ return proveDepositTransact({
1523
1830
  sdk,
1524
- inputIds: [],
1525
- outputIds: [applicationId]
1831
+ applicationId: this.getApplicationId(),
1832
+ privateAddressStpl1: parameters.privateAddressStpl1,
1833
+ coin: parameters.coin,
1834
+ depositScalarHex: parameters.depositScalarHex,
1835
+ merkleRootBytes: parameters.merkleRootBytes,
1836
+ tokenAddress: parameters.tokenAddress,
1837
+ buildAlignedDepositSlot: (input) => this.buildAlignedDepositSlot(input),
1838
+ ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {},
1839
+ ...parameters.publicDepositStroops === void 0 ? {} : { publicDepositStroops: parameters.publicDepositStroops },
1840
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1526
1841
  });
1527
- return { ...proof, applicationIdsPlaintext };
1528
1842
  }
1529
1843
  async calculateNullifierHash(nullifier, privKeyScalar) {
1530
1844
  const sdk = await this.getInitializedSdk();
1531
1845
  return sdk.calculateNullifierHash(nullifier, privKeyScalar);
1532
1846
  }
1533
1847
  async prepareWithdrawTransactProof(parameters) {
1534
- const sdk = await this.getInitializedSdk();
1848
+ const sdk = await this.getInitializedSdk(
1849
+ this.feeBearingZkConfigNonce({ kind: "withdraw" })
1850
+ );
1535
1851
  return proveWithdrawTransact({
1536
1852
  sdk,
1537
1853
  applicationId: this.getApplicationId(),
1538
1854
  buildAlignedDepositSlot: (input) => this.buildAlignedDepositSlot(input),
1539
1855
  ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {},
1540
- ...parameters
1856
+ coin: parameters.coin,
1857
+ state: parameters.state,
1858
+ destinationStellarAddress: parameters.destinationStellarAddress,
1859
+ privKeyScalarHex: parameters.privKeyScalarHex,
1860
+ depositorEphemeralKey: parameters.depositorEphemeralKey,
1861
+ withdrawAmountStroops: parameters.withdrawAmountStroops,
1862
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1863
+ tokenAddress: parameters.tokenAddress,
1864
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1541
1865
  });
1542
1866
  }
1543
1867
  async prepareWithdrawTransactProofDual(parameters) {
1544
- const sdk = await this.getInitializedSdk();
1868
+ const sdk = await this.getInitializedSdk(
1869
+ this.feeBearingZkConfigNonce({ kind: "withdraw" })
1870
+ );
1545
1871
  return proveWithdrawTransactDual({
1546
1872
  sdk,
1547
1873
  applicationId: this.getApplicationId(),
@@ -1556,7 +1882,8 @@ var PrivacyPoolService = class {
1556
1882
  ephemeralBKey: parameters.depositorEphemeralBKey,
1557
1883
  withdrawAmountStroops: parameters.withdrawAmountStroops,
1558
1884
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1559
- tokenAddress: parameters.tokenAddress
1885
+ tokenAddress: parameters.tokenAddress,
1886
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1560
1887
  });
1561
1888
  }
1562
1889
  };
@@ -1586,92 +1913,167 @@ function getPrivacyPoolService() {
1586
1913
  }
1587
1914
 
1588
1915
  // src/transact/merkle/fetch-contract.ts
1916
+ import { Buffer as Buffer12 } from "buffer";
1917
+
1918
+ // src/transact/merkle/state-port.ts
1919
+ function poolMerkleStateToCachedView(state) {
1920
+ return {
1921
+ commitments: state.commitments,
1922
+ updatedAt: state.updatedAt,
1923
+ merkleRootHex: state.merkleRootHex
1924
+ };
1925
+ }
1926
+
1927
+ // src/transact/merkle/read-tree-leaves.ts
1928
+ import { Address, xdr } from "@stellar/stellar-sdk";
1589
1929
  import { Buffer as Buffer11 } from "buffer";
1590
1930
 
1591
1931
  // src/transact/merkle/encoding.ts
1592
1932
  import "buffer";
1593
1933
  var FR_ORDER = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n;
1594
- function bufferToDecimal(buf) {
1934
+ function commitmentBufferToDecimal(buf) {
1595
1935
  const hex = buf.toString("hex");
1596
1936
  const bigintFromHex = BigInt(`0x${hex}`);
1597
1937
  const reduced = bigintFromHex % FR_ORDER;
1598
1938
  return reduced.toString();
1599
1939
  }
1600
- function commitmentsBuffersToDecimal(buffers) {
1601
- return buffers.map((bufferRow) => bufferToDecimal(bufferRow));
1602
- }
1603
1940
 
1604
- // src/transact/merkle/state-port.ts
1605
- function poolMerkleStateToCachedView(state) {
1606
- return {
1607
- commitments: state.commitments,
1608
- updatedAt: state.updatedAt,
1609
- merkleRootHex: state.merkleRootHex
1610
- };
1611
- }
1612
-
1613
- // src/transact/merkle/fetch-contract.ts
1614
- function normalizeCommitmentBuffers(rawBuffers) {
1615
- return rawBuffers.map(
1616
- (bufferRow) => Buffer11.isBuffer(bufferRow) ? bufferRow : Buffer11.from(bufferRow)
1941
+ // src/transact/merkle/read-tree-leaves.ts
1942
+ var TREE_LEAF_LEDGER_BATCH_SIZE = 100;
1943
+ function treeLeafLedgerKey(contractId, leafIndex) {
1944
+ return xdr.LedgerKey.contractData(
1945
+ new xdr.LedgerKeyContractData({
1946
+ contract: new Address(contractId).toScAddress(),
1947
+ key: treeLeafKeyScValue(leafIndex),
1948
+ durability: xdr.ContractDataDurability.persistent()
1949
+ })
1617
1950
  );
1618
1951
  }
1619
- function mergeContractMerkleWithCache(parameters) {
1620
- const cachedCount = parameters.cachedState?.commitments.length ?? 0;
1621
- const shouldPreferContract = parameters.contractCount > cachedCount || !parameters.cachedState || cachedCount === 0;
1622
- if (shouldPreferContract) {
1623
- const updatedAt = Date.now();
1624
- void parameters.poolMerkleState?.set({
1625
- poolContract: parameters.poolContractId,
1626
- commitments: parameters.contractCommitments,
1627
- commitmentCount: parameters.contractCount,
1628
- merkleRootHex: parameters.merkleRootHex,
1629
- updatedAt
1630
- });
1631
- return {
1632
- commitments: parameters.contractCommitments,
1633
- updatedAt,
1634
- merkleRootHex: parameters.merkleRootHex,
1635
- commitmentCount: parameters.contractCount
1636
- };
1952
+ function treeLeafKeyScValue(leafIndex) {
1953
+ return xdr.ScVal.scvVec([xdr.ScVal.scvSymbol("Leaf"), xdr.ScVal.scvU32(leafIndex)]);
1954
+ }
1955
+ function leafIndexFromContractDataKey(key) {
1956
+ if (key.switch() !== xdr.ScValType.scvVec()) {
1957
+ return void 0;
1637
1958
  }
1638
- return {
1639
- commitments: parameters.cachedState.commitments,
1640
- updatedAt: parameters.cachedState.updatedAt,
1641
- merkleRootHex: parameters.cachedState.merkleRootHex ?? parameters.merkleRootHex,
1642
- commitmentCount: parameters.cachedState.commitments.length
1643
- };
1959
+ const fields = key.vec();
1960
+ if (!fields || fields.length !== 2) {
1961
+ return void 0;
1962
+ }
1963
+ const [tag, indexValue] = fields;
1964
+ if (!tag || tag.switch() !== xdr.ScValType.scvSymbol()) {
1965
+ return void 0;
1966
+ }
1967
+ if (tag.sym().toString() !== "Leaf") {
1968
+ return void 0;
1969
+ }
1970
+ if (!indexValue || indexValue.switch() !== xdr.ScValType.scvU32()) {
1971
+ return void 0;
1972
+ }
1973
+ return indexValue.u32();
1644
1974
  }
1645
- async function readCachedPoolMerkleView(poolMerkleState, poolContractId) {
1646
- if (!poolMerkleState) {
1975
+ function commitmentBytesFromContractDataValue(value) {
1976
+ if (value.switch() !== xdr.ScValType.scvBytes()) {
1647
1977
  return void 0;
1648
1978
  }
1649
- const cached = await poolMerkleState.get(poolContractId);
1650
- return cached ? poolMerkleStateToCachedView(cached) : void 0;
1979
+ return Buffer11.from(value.bytes());
1980
+ }
1981
+ async function readTreeLeafCommitments(input) {
1982
+ if (input.startIndex >= input.endIndex) {
1983
+ return [];
1984
+ }
1985
+ const byIndex = await readLeafBuffersByIndex(input);
1986
+ return decimalCommitmentsInRange(byIndex, input.startIndex, input.endIndex);
1987
+ }
1988
+ async function readLeafBuffersByIndex(input) {
1989
+ const keys = ledgerKeysForRange(input.contractId, input.startIndex, input.endIndex);
1990
+ const byIndex = /* @__PURE__ */ new Map();
1991
+ for (const batch of chunkLedgerKeys(keys, TREE_LEAF_LEDGER_BATCH_SIZE)) {
1992
+ const response = await input.reader.getLedgerEntries(...batch);
1993
+ collectLeafBuffers(response.entries, byIndex);
1994
+ }
1995
+ return byIndex;
1996
+ }
1997
+ function ledgerKeysForRange(contractId, startIndex, endIndex) {
1998
+ const keys = [];
1999
+ for (let leafIndex = startIndex; leafIndex < endIndex; leafIndex += 1) {
2000
+ keys.push(treeLeafLedgerKey(contractId, leafIndex));
2001
+ }
2002
+ return keys;
2003
+ }
2004
+ function chunkLedgerKeys(keys, batchSize) {
2005
+ const batches = [];
2006
+ for (let offset = 0; offset < keys.length; offset += batchSize) {
2007
+ batches.push(keys.slice(offset, offset + batchSize));
2008
+ }
2009
+ return batches;
2010
+ }
2011
+ function collectLeafBuffers(entries, byIndex) {
2012
+ for (const entry of entries) {
2013
+ const contractData = entry.val.contractData();
2014
+ const leafIndex = leafIndexFromContractDataKey(contractData.key());
2015
+ const bytes = commitmentBytesFromContractDataValue(contractData.val());
2016
+ if (leafIndex === void 0 || !bytes) {
2017
+ continue;
2018
+ }
2019
+ byIndex.set(leafIndex, bytes);
2020
+ }
2021
+ }
2022
+ function decimalCommitmentsInRange(byIndex, startIndex, endIndex) {
2023
+ const commitments = [];
2024
+ for (let leafIndex = startIndex; leafIndex < endIndex; leafIndex += 1) {
2025
+ const bytes = byIndex.get(leafIndex);
2026
+ if (!bytes) {
2027
+ throw new Error(`Missing Merkle leaf ${String(leafIndex)} in pool storage.`);
2028
+ }
2029
+ commitments.push(commitmentBufferToDecimal(bytes));
2030
+ }
2031
+ return commitments;
2032
+ }
2033
+
2034
+ // src/transact/merkle/reuse-cached-commitments.ts
2035
+ function completeCacheRootMatches(cachedState, merkleRootHex) {
2036
+ const cachedRoot = cachedState?.merkleRootHex;
2037
+ if (cachedRoot === void 0 || cachedRoot === "") {
2038
+ return true;
2039
+ }
2040
+ return cachedRoot === merkleRootHex;
2041
+ }
2042
+ function reuseCachedCommitments(input) {
2043
+ const cachedCommitments = input.cachedState?.commitments ?? [];
2044
+ if (cachedCommitments.length === 0) {
2045
+ return [];
2046
+ }
2047
+ if (cachedCommitments.length > input.onChainCount) {
2048
+ return [];
2049
+ }
2050
+ if (cachedCommitments.length === input.onChainCount) {
2051
+ return completeCacheRootMatches(input.cachedState, input.merkleRootHex) ? cachedCommitments : [];
2052
+ }
2053
+ return cachedCommitments;
1651
2054
  }
2055
+
2056
+ // src/transact/merkle/fetch-contract.ts
1652
2057
  async function fetchAndMergeMerkleState(parameters) {
1653
- const client = readPoolClientFactory(parameters.transactEnvironment)({
1654
- contractId: parameters.poolContractId,
1655
- walletPublicKey: parameters.walletPublicKey,
1656
- networkPassphrase: parameters.transactEnvironment.network.networkPassphrase,
1657
- sorobanRpcUrl: parameters.transactEnvironment.network.rpcUrl
1658
- });
1659
- const commitmentsRead = await client.get_commitments();
1660
- const rawBuffers = commitmentsRead.result;
1661
- const buffers = normalizeCommitmentBuffers(rawBuffers);
1662
- const contractCommitments = commitmentsBuffersToDecimal(buffers);
1663
- const contractCount = contractCommitments.length;
2058
+ const client = createPoolClient(parameters);
2059
+ const countRead = await client.get_commitment_count();
2060
+ const onChainCount = requireCommitmentCount(countRead.result);
1664
2061
  const rootRead = await client.get_merkle_root();
1665
- const merkleRootBuffer = rootRead.result;
1666
- const merkleRootHex = Buffer11.from(merkleRootBuffer).toString("hex");
2062
+ const merkleRootHex = merkleRootToHex(rootRead.result);
1667
2063
  const cachedState = await readCachedPoolMerkleView(
1668
2064
  parameters.poolMerkleState,
1669
2065
  parameters.poolContractId
1670
2066
  );
1671
- return mergeContractMerkleWithCache({
2067
+ const commitments = await loadCommitments({
2068
+ cachedState,
2069
+ onChainCount,
2070
+ merkleRootHex,
2071
+ rpcUrl: parameters.transactEnvironment.network.rpcUrl,
2072
+ contractId: parameters.poolContractId
2073
+ });
2074
+ return persistFetchedMerkleState({
1672
2075
  poolContractId: parameters.poolContractId,
1673
- contractCommitments,
1674
- contractCount,
2076
+ commitments,
1675
2077
  merkleRootHex,
1676
2078
  cachedState,
1677
2079
  ...parameters.poolMerkleState ? { poolMerkleState: parameters.poolMerkleState } : {}
@@ -1685,19 +2087,14 @@ async function readLeafEphemeralHex(parameters) {
1685
2087
  if (cached?.xHex && cached.yHex) {
1686
2088
  return { xHex: cached.xHex, yHex: cached.yHex };
1687
2089
  }
1688
- const client = readPoolClientFactory(parameters.transactEnvironment)({
1689
- contractId: parameters.poolContractId,
1690
- walletPublicKey: parameters.walletPublicKey,
1691
- networkPassphrase: parameters.transactEnvironment.network.networkPassphrase,
1692
- sorobanRpcUrl: parameters.transactEnvironment.network.rpcUrl
1693
- });
2090
+ const client = createPoolClient(parameters);
1694
2091
  const read = await client.get_leaf_ephemeral({ leaf_index: parameters.leafIndex });
1695
2092
  const coords = read.result;
1696
2093
  if (!coords?.x || !coords?.y) {
1697
2094
  throw new Error("Missing leaf ephemeral key on contract");
1698
2095
  }
1699
- const xHex = Buffer11.from(coords.x).toString("hex");
1700
- const yHex = Buffer11.from(coords.y).toString("hex");
2096
+ const xHex = Buffer12.from(coords.x).toString("hex");
2097
+ const yHex = Buffer12.from(coords.y).toString("hex");
1701
2098
  await parameters.leafEphemeral?.set({
1702
2099
  poolContract: parameters.poolContractId,
1703
2100
  leafIndex: parameters.leafIndex,
@@ -1707,6 +2104,84 @@ async function readLeafEphemeralHex(parameters) {
1707
2104
  });
1708
2105
  return { xHex, yHex };
1709
2106
  }
2107
+ function createPoolClient(parameters) {
2108
+ return readPoolClientFactory(parameters.transactEnvironment)({
2109
+ contractId: parameters.poolContractId,
2110
+ walletPublicKey: parameters.walletPublicKey,
2111
+ networkPassphrase: parameters.transactEnvironment.network.networkPassphrase,
2112
+ sorobanRpcUrl: parameters.transactEnvironment.network.rpcUrl
2113
+ });
2114
+ }
2115
+ function requireCommitmentCount(result) {
2116
+ if (typeof result === "number" && Number.isInteger(result) && result >= 0) {
2117
+ return result;
2118
+ }
2119
+ throw new Error("Pool commitment count is not a non-negative integer.");
2120
+ }
2121
+ function merkleRootToHex(root) {
2122
+ return Buffer12.from(root).toString("hex");
2123
+ }
2124
+ async function readCachedPoolMerkleView(poolMerkleState, poolContractId) {
2125
+ if (!poolMerkleState) {
2126
+ return void 0;
2127
+ }
2128
+ const cached = await poolMerkleState.get(poolContractId);
2129
+ return cached ? poolMerkleStateToCachedView(cached) : void 0;
2130
+ }
2131
+ async function loadCommitments(input) {
2132
+ const reused = reuseCachedCommitments({
2133
+ cachedState: input.cachedState,
2134
+ onChainCount: input.onChainCount,
2135
+ merkleRootHex: input.merkleRootHex
2136
+ });
2137
+ if (reused.length === input.onChainCount) {
2138
+ return reused;
2139
+ }
2140
+ const fetched = await readTreeLeafCommitments({
2141
+ reader: createStellarRpcServer(input.rpcUrl),
2142
+ contractId: input.contractId,
2143
+ startIndex: reused.length,
2144
+ endIndex: input.onChainCount
2145
+ });
2146
+ return [...reused, ...fetched];
2147
+ }
2148
+ async function persistFetchedMerkleState(input) {
2149
+ const cachedState = input.cachedState;
2150
+ if (cachedState && cachedMerkleUnchanged(cachedState, input.commitments, input.merkleRootHex)) {
2151
+ return viewFromCachedMerkle(cachedState, input.merkleRootHex);
2152
+ }
2153
+ const updatedAt = Date.now();
2154
+ await input.poolMerkleState?.set({
2155
+ poolContract: input.poolContractId,
2156
+ commitments: input.commitments,
2157
+ commitmentCount: input.commitments.length,
2158
+ merkleRootHex: input.merkleRootHex,
2159
+ updatedAt
2160
+ });
2161
+ return {
2162
+ commitments: input.commitments,
2163
+ updatedAt,
2164
+ merkleRootHex: input.merkleRootHex,
2165
+ commitmentCount: input.commitments.length
2166
+ };
2167
+ }
2168
+ function viewFromCachedMerkle(cachedState, merkleRootHex) {
2169
+ return {
2170
+ commitments: cachedState.commitments,
2171
+ updatedAt: cachedState.updatedAt,
2172
+ merkleRootHex: cachedState.merkleRootHex ?? merkleRootHex,
2173
+ commitmentCount: cachedState.commitments.length
2174
+ };
2175
+ }
2176
+ function cachedMerkleUnchanged(cachedState, commitments, merkleRootHex) {
2177
+ if (!cachedState) {
2178
+ return false;
2179
+ }
2180
+ if ((cachedState.merkleRootHex ?? merkleRootHex) !== merkleRootHex) {
2181
+ return false;
2182
+ }
2183
+ return cachedState.commitments.join(",") === commitments.join(",");
2184
+ }
1710
2185
 
1711
2186
  // src/transact/engine/prepare/shared.ts
1712
2187
  async function resolveTokenContractId2(environment, assetId) {
@@ -1996,6 +2471,13 @@ async function filterUnspentPrivateRecords(input) {
1996
2471
  }
1997
2472
  return unspent;
1998
2473
  }
2474
+ function spendAmountCoveringProtocolFee(amount) {
2475
+ if (amount <= 0n) {
2476
+ return amount;
2477
+ }
2478
+ const hundredths = (amount + 99n) / 100n;
2479
+ return amount + (hundredths === 0n ? 1n : hundredths);
2480
+ }
1999
2481
  function pickRecordsForSpendAmount(input) {
2000
2482
  let selectedTotal = 0n;
2001
2483
  const selected = [];
@@ -2049,7 +2531,7 @@ async function selectPrivateRecords(input) {
2049
2531
  return pickRecordsForSpendAmount({
2050
2532
  available,
2051
2533
  kind: input.kind,
2052
- amount: input.intent.amount
2534
+ amount: spendAmountCoveringProtocolFee(input.intent.amount)
2053
2535
  });
2054
2536
  }
2055
2537
  function sumRecordAmounts(records) {
@@ -4104,7 +4586,7 @@ function createStellarStorageAdapterFromState(state) {
4104
4586
  }
4105
4587
 
4106
4588
  // src/contracts/registry/registry-domain-service.ts
4107
- import { Buffer as Buffer12 } from "buffer";
4589
+ import { Buffer as Buffer13 } from "buffer";
4108
4590
 
4109
4591
  // src/transact/submit/ttl-preflight.ts
4110
4592
  function getNested(source, path) {
@@ -4153,11 +4635,11 @@ async function runTtlPreflight(assembledTransaction) {
4153
4635
  // src/contracts/registry/registry-domain-service.ts
4154
4636
  var COORDINATE_HEX_LENGTH = 64;
4155
4637
  function readCoordinateBytes(value) {
4156
- if (value instanceof Buffer12) {
4638
+ if (value instanceof Buffer13) {
4157
4639
  return value;
4158
4640
  }
4159
4641
  if (value instanceof Uint8Array) {
4160
- return Buffer12.from(value);
4642
+ return Buffer13.from(value);
4161
4643
  }
4162
4644
  return void 0;
4163
4645
  }
@@ -4196,8 +4678,8 @@ function parseRegistryLookupRecord(result) {
4196
4678
  return parseOptionSomeRecord(result);
4197
4679
  }
4198
4680
  function recordToLookupResult(record) {
4199
- const publicKeyXHex = Buffer12.from(record.public_key_x).toString("hex");
4200
- const publicKeyYHex = Buffer12.from(record.public_key_y).toString("hex");
4681
+ const publicKeyXHex = Buffer13.from(record.public_key_x).toString("hex");
4682
+ const publicKeyYHex = Buffer13.from(record.public_key_y).toString("hex");
4201
4683
  return {
4202
4684
  owner: record.owner,
4203
4685
  status: "registered",
@@ -4240,8 +4722,8 @@ async function registerPrivateAddressOnChain(input) {
4240
4722
  });
4241
4723
  const assembled = await client.register_private_address({
4242
4724
  owner: input.owner.trim(),
4243
- public_key_x: Buffer12.from(decoded.x.padStart(COORDINATE_HEX_LENGTH, "0"), "hex"),
4244
- public_key_y: Buffer12.from(decoded.y.padStart(COORDINATE_HEX_LENGTH, "0"), "hex")
4725
+ public_key_x: Buffer13.from(decoded.x.padStart(COORDINATE_HEX_LENGTH, "0"), "hex"),
4726
+ public_key_y: Buffer13.from(decoded.y.padStart(COORDINATE_HEX_LENGTH, "0"), "hex")
4245
4727
  });
4246
4728
  await runTtlPreflight(assembled);
4247
4729
  const sent = await assembled.signAndSend();
@@ -4256,7 +4738,7 @@ async function registerPrivateAddressOnChain(input) {
4256
4738
  import { StrKey as StrKey5 } from "@stellar/stellar-sdk";
4257
4739
 
4258
4740
  // src/transact/escrow/derived-escrow-recipient.ts
4259
- import { Buffer as Buffer13 } from "buffer";
4741
+ import { Buffer as Buffer14 } from "buffer";
4260
4742
  import { StrKey as StrKey4 } from "@stellar/stellar-sdk";
4261
4743
  import {
4262
4744
  derivedEscrowKey,
@@ -4266,7 +4748,7 @@ import {
4266
4748
  var COORDINATE_HEX_LENGTH2 = 64;
4267
4749
  function stellarAccountToFieldLimbs(stellarAddress) {
4268
4750
  const pkRaw = StrKey4.decodeEd25519PublicKey(stellarAddress.trim());
4269
- const pkHex = Buffer13.from(pkRaw).toString("hex");
4751
+ const pkHex = Buffer14.from(pkRaw).toString("hex");
4270
4752
  const { hi, lo } = ed25519PubkeyPayloadHexToWithdrawFrDecimals2(pkHex);
4271
4753
  return { recipientHi: hi, recipientLo: lo };
4272
4754
  }
@@ -4351,7 +4833,7 @@ async function resolveTransferRecipientFromChain(input) {
4351
4833
  import { Api } from "@stellar/stellar-sdk/rpc";
4352
4834
 
4353
4835
  // src/rpc/soroban-transaction-meta.ts
4354
- import { Address, StrKey as StrKey6 } from "@stellar/stellar-sdk";
4836
+ import { Address as Address2, StrKey as StrKey6 } from "@stellar/stellar-sdk";
4355
4837
  var BIGINT_SHIFT_64 = 64n;
4356
4838
  var TRANSACTION_META_SWITCH_SOROBAN_V3 = 3;
4357
4839
  var TRANSACTION_META_SWITCH_SOROBAN_V4 = 4;
@@ -4451,8 +4933,8 @@ function transferPartyAddressesFromTopics(topics) {
4451
4933
  return void 0;
4452
4934
  }
4453
4935
  return {
4454
- from: Address.fromScVal(fromTopic).toString(),
4455
- to: Address.fromScVal(toTopic).toString()
4936
+ from: Address2.fromScVal(fromTopic).toString(),
4937
+ to: Address2.fromScVal(toTopic).toString()
4456
4938
  };
4457
4939
  } catch {
4458
4940
  return void 0;
@@ -5158,7 +5640,7 @@ function createStellarPrivacyClientFromResolvedConfig(config) {
5158
5640
  }
5159
5641
 
5160
5642
  // src/transact/engine/default-factory.ts
5161
- import { PrivacyPoolSDK as PrivacyPoolSDK3 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
5643
+ import { PrivacyPoolSDK as PrivacyPoolSDK4 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
5162
5644
 
5163
5645
  // src/transact/pool/proof-types.ts
5164
5646
  function ciphertextArtifactsFromProof(proof) {
@@ -5173,9 +5655,15 @@ function ciphertextArtifactsFromProof(proof) {
5173
5655
  // src/transact/engine/prepare/deposit.ts
5174
5656
  var STROOPS_PER_UNIT = 10000000n;
5175
5657
  async function createAlignedDepositWithProof(parameters) {
5658
+ const feeOutput = await quoteFeeOutputForPrepared(parameters);
5659
+ const instructed = parameters.prepared.intent.amount;
5660
+ const userAmount = instructed - (feeOutput?.requiredFee ?? 0n);
5661
+ if (userAmount <= 0n) {
5662
+ throw new Error("Required Fee leaves no spendable deposit note");
5663
+ }
5176
5664
  const aligned = await parameters.poolService.createAlignedShieldCoinData({
5177
5665
  privateAddressStpl1: parameters.prepared.intent.to,
5178
- amountStroops: parameters.prepared.intent.amount,
5666
+ amountStroops: userAmount,
5179
5667
  tokenAddress: parameters.tokenAddress
5180
5668
  });
5181
5669
  const poolClient = readPoolClientFactory(parameters.environment)({
@@ -5190,7 +5678,9 @@ async function createAlignedDepositWithProof(parameters) {
5190
5678
  coin: aligned.coin,
5191
5679
  depositScalarHex: aligned.depositScalarHex,
5192
5680
  merkleRootBytes: merkleTx.result,
5193
- tokenAddress: parameters.tokenAddress
5681
+ tokenAddress: parameters.tokenAddress,
5682
+ publicDepositStroops: instructed,
5683
+ ...feeOutput ? { feeOutput } : {}
5194
5684
  });
5195
5685
  return { ...aligned, proof };
5196
5686
  }
@@ -5209,8 +5699,8 @@ async function prepareDepositOperation(prepared, environment, poolService) {
5209
5699
  privateAddress: prepared.intent.to,
5210
5700
  assetId: prepared.intent.asset,
5211
5701
  poolContract: environment.network.poolContract,
5212
- amount: prepared.intent.amount,
5213
- amountDisplay: Number(prepared.intent.amount) / Number(STROOPS_PER_UNIT),
5702
+ amount: BigInt(depositProof.coin.value),
5703
+ amountDisplay: Number(depositProof.coin.value) / Number(STROOPS_PER_UNIT),
5214
5704
  commitmentHex: depositProof.commitment_hex,
5215
5705
  coin: depositProof.coin,
5216
5706
  depositScalarHex: depositProof.depositScalarHex,
@@ -5226,7 +5716,11 @@ async function prepareDepositOperation(prepared, environment, poolService) {
5226
5716
  depositScalarHex: depositProof.depositScalarHex,
5227
5717
  precommitementHex: depositProof.precommitementHex,
5228
5718
  commitmentHex: depositProof.commitment_hex,
5229
- walletPublicKey
5719
+ walletPublicKey,
5720
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
5721
+ kind: "deposit",
5722
+ ...configuredNonceSpread(environment.zkConfigNonce)
5723
+ })
5230
5724
  });
5231
5725
  }
5232
5726
 
@@ -5400,9 +5894,6 @@ function escrowOutputFields(input) {
5400
5894
  recipientLo: input.escrowSend.recipientLo
5401
5895
  };
5402
5896
  }
5403
- function paddingDepositPair(recipientDeposit) {
5404
- return [recipientDeposit, "dummy"];
5405
- }
5406
5897
  async function buildChangeDepositPair(parameters) {
5407
5898
  const changeSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
5408
5899
  privateAddressStpl1: parameters.selfPrivateAddressStpl1ForChange.trim(),
@@ -5419,34 +5910,54 @@ async function buildChangeDepositPair(parameters) {
5419
5910
  }
5420
5911
  };
5421
5912
  }
5913
+ function recipientOnlyDeposits(recipientDeposit, hasFeeOutput) {
5914
+ if (hasFeeOutput) {
5915
+ return [recipientDeposit];
5916
+ }
5917
+ return [recipientDeposit, "dummy"];
5918
+ }
5919
+ async function depositsWithoutFee(input) {
5920
+ if (input.changeStroops <= ZERO_STROOPS) {
5921
+ return {
5922
+ deposits: recipientOnlyDeposits(input.recipientDeposit, input.hasFeeOutput)
5923
+ };
5924
+ }
5925
+ return buildChangeDepositPair({
5926
+ selfPrivateAddressStpl1ForChange: input.selfPrivateAddressStpl1ForChange,
5927
+ changeStroops: input.changeStroops,
5928
+ tokenAddress: input.tokenAddress,
5929
+ recipientDeposit: input.recipientDeposit
5930
+ });
5931
+ }
5422
5932
  async function buildRecipientAndOptionalChangeDeposits(parameters) {
5423
5933
  const escrow = {
5424
5934
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
5425
5935
  ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
5426
5936
  };
5427
- const recipientPrivateAddress = parameters.recipientPrivateAddressStpl1.trim();
5428
5937
  const recipientSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
5429
- privateAddressStpl1: recipientPrivateAddress,
5938
+ privateAddressStpl1: parameters.recipientPrivateAddressStpl1.trim(),
5430
5939
  amountStroops: parameters.transferStroops,
5431
5940
  tokenAddress: parameters.tokenAddress,
5432
5941
  ...escrowOutputFields(escrow)
5433
5942
  });
5434
- if (parameters.changeStroops <= ZERO_STROOPS) {
5435
- return {
5436
- recipientSlot,
5437
- deposits: paddingDepositPair(recipientSlot.deposit)
5438
- };
5439
- }
5440
- const changeResult = await buildChangeDepositPair({
5441
- selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5943
+ const withoutFee = await depositsWithoutFee({
5944
+ recipientDeposit: recipientSlot.deposit,
5442
5945
  changeStroops: parameters.changeStroops,
5946
+ selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5947
+ tokenAddress: parameters.tokenAddress,
5948
+ hasFeeOutput: (parameters.feeOutput?.requiredFee ?? ZERO_STROOPS) > ZERO_STROOPS
5949
+ });
5950
+ const withFee = await appendFeeOutputSlot({
5951
+ deposits: withoutFee.deposits,
5443
5952
  tokenAddress: parameters.tokenAddress,
5444
- recipientDeposit: recipientSlot.deposit
5953
+ buildAlignedDepositSlot: (aligned) => getPrivacyPoolService().buildAlignedDepositSlot(aligned),
5954
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5445
5955
  });
5446
5956
  return {
5447
5957
  recipientSlot,
5448
- deposits: changeResult.deposits,
5449
- changeCoin: changeResult.changeCoin
5958
+ deposits: withFee.deposits,
5959
+ ..."changeCoin" in withoutFee ? { changeCoin: withoutFee.changeCoin } : {},
5960
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
5450
5961
  };
5451
5962
  }
5452
5963
 
@@ -5499,14 +6010,15 @@ function sweepOutputOwnerFields(parameters) {
5499
6010
  return { sweepOutputOwnerPubX, sweepOutputOwnerPubY };
5500
6011
  }
5501
6012
  async function buildTransferDepositsAndPublicInput(parameters) {
5502
- const { recipientSlot, deposits, changeCoin } = await buildRecipientAndOptionalChangeDeposits({
6013
+ const { recipientSlot, deposits, changeCoin, feeCoin } = await buildRecipientAndOptionalChangeDeposits({
5503
6014
  recipientPrivateAddressStpl1: parameters.recipientPrivateAddressStpl1,
5504
6015
  transferStroops: parameters.transferStroops,
5505
6016
  changeStroops: parameters.changeStroops,
5506
6017
  selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5507
6018
  tokenAddress: parameters.tokenAddress,
5508
6019
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
5509
- ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
6020
+ ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
6021
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5510
6022
  });
5511
6023
  const publicInput = withTokenAddressPublicInputs(
5512
6024
  {
@@ -5521,6 +6033,7 @@ async function buildTransferDepositsAndPublicInput(parameters) {
5521
6033
  recipientSlot,
5522
6034
  deposits,
5523
6035
  ...changeCoin ? { changeCoin } : {},
6036
+ ...feeCoin ? { feeCoin } : {},
5524
6037
  publicInput
5525
6038
  };
5526
6039
  }
@@ -5536,7 +6049,8 @@ async function buildSenderTransferDepositsAndPublicInput(parameters) {
5536
6049
  parameters.senderPrivKeyScalarHex
5537
6050
  ),
5538
6051
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
5539
- ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
6052
+ ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
6053
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5540
6054
  });
5541
6055
  }
5542
6056
 
@@ -5560,11 +6074,20 @@ function stampDualWithdrawLegs(legs, parameters) {
5560
6074
  }
5561
6075
  };
5562
6076
  }
5563
- function buildDualTransferApplicationIds(sdk, applicationId, changeCoin) {
6077
+ function buildDualTransferApplicationIds(sdk, applicationId, changeCoin, feeCoin) {
6078
+ const outputIds = [applicationId];
6079
+ if (changeCoin) {
6080
+ outputIds.push(applicationId);
6081
+ } else if (!feeCoin) {
6082
+ outputIds.push("0");
6083
+ }
6084
+ if (feeCoin) {
6085
+ outputIds.push(applicationId);
6086
+ }
5564
6087
  return buildApplicationIdHints({
5565
6088
  sdk,
5566
6089
  inputIds: [applicationId, applicationId],
5567
- outputIds: [applicationId, changeCoin ? applicationId : "0"]
6090
+ outputIds
5568
6091
  });
5569
6092
  }
5570
6093
  async function proveDualConfidentialTransfer(parameters) {
@@ -5584,16 +6107,24 @@ async function proveDualConfidentialTransfer(parameters) {
5584
6107
  audit
5585
6108
  );
5586
6109
  }
5587
- async function buildDualTransferProofContext(parameters, applicationId, sdk) {
6110
+ function dualTransferChangeStroops(parameters) {
5588
6111
  const totalNotes = BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value);
5589
- const changeStroops = changeStroopsAfterDualTransfer(
5590
- parameters.transferStroops,
5591
- totalNotes
5592
- );
6112
+ if (parameters.transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
6113
+ throw new Error("Transfer amount must be positive");
6114
+ }
6115
+ const changeStroops = remainingAfterRequiredFee({
6116
+ available: totalNotes,
6117
+ instructed: parameters.transferStroops,
6118
+ requiredFee: parameters.feeOutput?.requiredFee ?? 0n
6119
+ });
5593
6120
  requireChangeRecipientWhenPartial(
5594
6121
  changeStroops,
5595
6122
  parameters.selfPrivateAddressStpl1ForChange
5596
6123
  );
6124
+ return changeStroops;
6125
+ }
6126
+ async function buildDualTransferProofContext(parameters, applicationId, sdk) {
6127
+ const changeStroops = dualTransferChangeStroops(parameters);
5597
6128
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
5598
6129
  parameters.senderPrivKeyScalarHex
5599
6130
  );
@@ -5620,15 +6151,21 @@ async function buildDualTransferProofContext(parameters, applicationId, sdk) {
5620
6151
  selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5621
6152
  tokenAddress: parameters.tokenAddress,
5622
6153
  stateRoot: legA.witness.stateRoot,
5623
- ...confidentialEscrowFields(parameters)
6154
+ ...confidentialEscrowFields(parameters),
6155
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5624
6156
  });
5625
6157
  return { legA, legB, applicationId, ...transferInputs };
5626
6158
  }
5627
6159
  async function prepareConfidentialTransferProofDual(parameters) {
5628
6160
  const poolService = getPrivacyPoolService();
5629
- const sdk = await poolService.getInitializedSdk();
6161
+ const sdk = await poolService.getInitializedSdk(
6162
+ poolService.feeBearingZkConfigNonce({
6163
+ kind: "transfer",
6164
+ ...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
6165
+ })
6166
+ );
5630
6167
  const applicationId = poolService.getApplicationId();
5631
- const { legA, legB, recipientSlot, deposits, changeCoin, publicInput } = await buildDualTransferProofContext(parameters, applicationId, sdk);
6168
+ const { legA, legB, recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildDualTransferProofContext(parameters, applicationId, sdk);
5632
6169
  const auditPublicKey = poolService.getAuditPublicKey();
5633
6170
  const proof = await (auditPublicKey ? proveDualConfidentialTransfer({
5634
6171
  sdk,
@@ -5651,7 +6188,8 @@ async function prepareConfidentialTransferProofDual(parameters) {
5651
6188
  applicationIdsPlaintext: buildDualTransferApplicationIds(
5652
6189
  sdk,
5653
6190
  applicationId,
5654
- changeCoin
6191
+ changeCoin,
6192
+ feeCoin
5655
6193
  ),
5656
6194
  recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
5657
6195
  ...changeCoin ? { changeCoin } : {}
@@ -5659,22 +6197,32 @@ async function prepareConfidentialTransferProofDual(parameters) {
5659
6197
  }
5660
6198
 
5661
6199
  // src/transact/proofs/confidential/single.ts
5662
- function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange) {
6200
+ function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange, requiredFee) {
5663
6201
  if (transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
5664
6202
  throw new Error("Transfer amount must be positive");
5665
6203
  }
5666
- if (transferStroops > noteStroops) {
5667
- throw new Error("Transfer amount exceeds note value");
5668
- }
5669
- const changeStroops = noteStroops - transferStroops;
6204
+ const changeStroops = remainingAfterRequiredFee({
6205
+ available: noteStroops,
6206
+ instructed: transferStroops,
6207
+ requiredFee
6208
+ });
5670
6209
  requireChangeRecipientWhenPartial(changeStroops, selfPrivateAddressStpl1ForChange);
5671
6210
  return changeStroops;
5672
6211
  }
5673
- function buildSingleTransferApplicationIds(sdk, applicationId, changeCoin) {
6212
+ function buildSingleTransferApplicationIds(sdk, applicationId, changeCoin, feeCoin) {
6213
+ const outputIds = [applicationId];
6214
+ if (changeCoin) {
6215
+ outputIds.push(applicationId);
6216
+ } else if (!feeCoin) {
6217
+ outputIds.push("0");
6218
+ }
6219
+ if (feeCoin) {
6220
+ outputIds.push(applicationId);
6221
+ }
5674
6222
  return buildApplicationIdHints({
5675
6223
  sdk,
5676
6224
  inputIds: [applicationId],
5677
- outputIds: [applicationId, changeCoin ? applicationId : "0"]
6225
+ outputIds
5678
6226
  });
5679
6227
  }
5680
6228
  function buildTransferAuditParameters(parameters) {
@@ -5700,11 +6248,25 @@ function confidentialEscrowFields2(input) {
5700
6248
  ...input.escrowClaimantLimbs ? { escrowClaimantLimbs: input.escrowClaimantLimbs } : {}
5701
6249
  };
5702
6250
  }
6251
+ function transferDepositInputFromProof(input, changeStroops, stateRoot) {
6252
+ return {
6253
+ senderPrivKeyScalarHex: input.senderPrivKeyScalarHex,
6254
+ recipientPrivateAddressStpl1: input.recipientPrivateAddressStpl1,
6255
+ transferStroops: input.transferStroops,
6256
+ changeStroops,
6257
+ selfPrivateAddressStpl1ForChange: input.selfPrivateAddressStpl1ForChange,
6258
+ tokenAddress: input.tokenAddress,
6259
+ stateRoot,
6260
+ ...confidentialEscrowFields2(input),
6261
+ ...input.feeOutput ? { feeOutput: input.feeOutput } : {}
6262
+ };
6263
+ }
5703
6264
  async function buildTransferProofInputs(parameters) {
5704
6265
  const changeStroops = validateSingleCoinTransferAmounts(
5705
6266
  parameters.input.transferStroops,
5706
6267
  parameters.noteStroops,
5707
- parameters.input.selfPrivateAddressStpl1ForChange
6268
+ parameters.input.selfPrivateAddressStpl1ForChange,
6269
+ parameters.input.feeOutput?.requiredFee ?? 0n
5708
6270
  );
5709
6271
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
5710
6272
  parameters.input.senderPrivKeyScalarHex
@@ -5723,31 +6285,30 @@ async function buildTransferProofInputs(parameters) {
5723
6285
  withdrawObject,
5724
6286
  sweepWithdrawStamp(confidentialEscrowFields2(parameters.input))
5725
6287
  );
5726
- const { recipientSlot, deposits, changeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput({
5727
- senderPrivKeyScalarHex: parameters.input.senderPrivKeyScalarHex,
5728
- recipientPrivateAddressStpl1: parameters.input.recipientPrivateAddressStpl1,
5729
- transferStroops: parameters.input.transferStroops,
5730
- changeStroops,
5731
- selfPrivateAddressStpl1ForChange: parameters.input.selfPrivateAddressStpl1ForChange,
5732
- tokenAddress: parameters.input.tokenAddress,
5733
- stateRoot: witness.stateRoot,
5734
- ...confidentialEscrowFields2(parameters.input)
5735
- });
6288
+ const { recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput(
6289
+ transferDepositInputFromProof(parameters.input, changeStroops, witness.stateRoot)
6290
+ );
5736
6291
  return {
5737
6292
  witness,
5738
6293
  withdrawObject: stampedWithdraw,
5739
6294
  recipientSlot,
5740
6295
  deposits,
5741
6296
  changeCoin,
6297
+ feeCoin,
5742
6298
  publicInput
5743
6299
  };
5744
6300
  }
5745
6301
  async function prepareConfidentialTransferProof(parameters) {
5746
6302
  const poolService = getPrivacyPoolService();
5747
- const sdk = await poolService.getInitializedSdk();
6303
+ const sdk = await poolService.getInitializedSdk(
6304
+ poolService.feeBearingZkConfigNonce({
6305
+ kind: "transfer",
6306
+ ...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
6307
+ })
6308
+ );
5748
6309
  const applicationId = poolService.getApplicationId();
5749
6310
  const noteStroops = BigInt(parameters.coin.value);
5750
- const { withdrawObject, recipientSlot, deposits, changeCoin, publicInput } = await buildTransferProofInputs({
6311
+ const { withdrawObject, recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildTransferProofInputs({
5751
6312
  sdk,
5752
6313
  applicationId,
5753
6314
  input: parameters,
@@ -5770,7 +6331,8 @@ async function prepareConfidentialTransferProof(parameters) {
5770
6331
  applicationIdsPlaintext: buildSingleTransferApplicationIds(
5771
6332
  sdk,
5772
6333
  applicationId,
5773
- changeCoin
6334
+ changeCoin,
6335
+ feeCoin
5774
6336
  ),
5775
6337
  recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
5776
6338
  ...changeCoin ? { changeCoin } : {}
@@ -5793,7 +6355,12 @@ function proofEscrowFields(input) {
5793
6355
  }
5794
6356
  async function buildSingleTransferProofAtExecute(input) {
5795
6357
  const context = await buildSpendProofContextAtExecute(input);
5796
- const changeStroops = BigInt(context.coin.value) - input.prepared.intent.amount;
6358
+ const feeOutput = await quoteFeeOutputForPrepared({
6359
+ prepared: input.prepared,
6360
+ environment: input.environment,
6361
+ tokenAddress: context.tokenAddress
6362
+ });
6363
+ const changeStroops = BigInt(context.coin.value) - input.prepared.intent.amount - (feeOutput?.requiredFee ?? 0n);
5797
6364
  const proof = await prepareConfidentialTransferProof({
5798
6365
  coin: context.coin,
5799
6366
  state: { commitments: context.commitments },
@@ -5806,7 +6373,8 @@ async function buildSingleTransferProofAtExecute(input) {
5806
6373
  recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
5807
6374
  selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
5808
6375
  tokenAddress: context.tokenAddress,
5809
- ...proofEscrowFields(input)
6376
+ ...proofEscrowFields(input),
6377
+ ...feeOutput ? { feeOutput } : {}
5810
6378
  });
5811
6379
  return { context, proof };
5812
6380
  }
@@ -5823,7 +6391,12 @@ async function buildDualTransferProofAtExecute(input) {
5823
6391
  commitments: context.commitments
5824
6392
  });
5825
6393
  const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
5826
- const changeStroops = totalNotes - input.prepared.intent.amount;
6394
+ const feeOutput = await quoteFeeOutputForPrepared({
6395
+ prepared: input.prepared,
6396
+ environment: input.environment,
6397
+ tokenAddress: context.tokenAddress
6398
+ });
6399
+ const changeStroops = totalNotes - input.prepared.intent.amount - (feeOutput?.requiredFee ?? 0n);
5827
6400
  const proof = await prepareConfidentialTransferProofDual({
5828
6401
  coinA: context.coin,
5829
6402
  coinB: secondary.coin,
@@ -5841,7 +6414,8 @@ async function buildDualTransferProofAtExecute(input) {
5841
6414
  recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
5842
6415
  selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
5843
6416
  tokenAddress: context.tokenAddress,
5844
- ...proofEscrowFields(input)
6417
+ ...proofEscrowFields(input),
6418
+ ...feeOutput ? { feeOutput } : {}
5845
6419
  });
5846
6420
  return { context, proof };
5847
6421
  }
@@ -5973,11 +6547,17 @@ async function runTransferFinalizeSteps(input) {
5973
6547
  walletPublicKey: input.walletPublicKey,
5974
6548
  ...recipient.escrowSend ? { escrowSendPrivateAddress: recipient.recipientPrivateAddressStpl1 } : {}
5975
6549
  });
5976
- return buildTransferFinalizeArtifacts({
5977
- proof,
5978
- context,
5979
- ...sweepFinalizeStamp(input.prepared, recipient)
5980
- });
6550
+ return {
6551
+ ...buildTransferFinalizeArtifacts({
6552
+ proof,
6553
+ context,
6554
+ ...sweepFinalizeStamp(input.prepared, recipient)
6555
+ }),
6556
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
6557
+ ...feeOutputKindFromPrepared(input.prepared),
6558
+ ...configuredNonceSpread(input.environment.zkConfigNonce)
6559
+ })
6560
+ };
5981
6561
  }
5982
6562
  function sweepArtifactStamp(artifacts) {
5983
6563
  return {
@@ -6005,6 +6585,24 @@ async function finalizeTransferAtExecute(prepared, environment) {
6005
6585
  return runTransferFinalizeSteps({ prepared, environment, walletPublicKey });
6006
6586
  }
6007
6587
 
6588
+ // src/transact/engine/withdraw-fee.ts
6589
+ async function withdrawFeeProofFields(input) {
6590
+ const feeOutput = await quoteFeeOutputForPrepared({
6591
+ prepared: input.prepared,
6592
+ environment: input.environment,
6593
+ tokenAddress: input.tokenAddress
6594
+ });
6595
+ const changeStroops = remainingAfterRequiredFee({
6596
+ available: input.available,
6597
+ instructed: input.prepared.intent.amount,
6598
+ requiredFee: feeOutput?.requiredFee ?? 0n
6599
+ });
6600
+ return {
6601
+ ...feeOutput ? { feeOutput } : {},
6602
+ ...changeStroops > 0n ? { changePrivateAddressStpl1: input.withdrawFrom } : {}
6603
+ };
6604
+ }
6605
+
6008
6606
  // src/transact/engine/execute.ts
6009
6607
  function enrichWithdrawOutputRecords(prepared, proof) {
6010
6608
  if (!proof.changeCoin || prepared.outputRecords.length === 0) {
@@ -6020,7 +6618,7 @@ function enrichWithdrawOutputRecords(prepared, proof) {
6020
6618
  precommitementHex: changeCoin.precommitementHex
6021
6619
  }));
6022
6620
  }
6023
- function buildWithdrawFinalizeArtifacts(proof, context) {
6621
+ function buildWithdrawFinalizeArtifacts(proof, context, environment) {
6024
6622
  return {
6025
6623
  proofHex: proof.proof_hex,
6026
6624
  publicHex: proof.public_hex,
@@ -6028,7 +6626,11 @@ function buildWithdrawFinalizeArtifacts(proof, context) {
6028
6626
  applicationIdsPlaintext: proof.applicationIdsPlaintext,
6029
6627
  tokenAddress: context.tokenAddress,
6030
6628
  walletPublicKey: context.walletPublicKey,
6031
- executeFinalizeRequired: false
6629
+ executeFinalizeRequired: false,
6630
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
6631
+ kind: "withdraw",
6632
+ ...configuredNonceSpread(environment.zkConfigNonce)
6633
+ })
6032
6634
  };
6033
6635
  }
6034
6636
  async function finalizeSingleWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
@@ -6037,7 +6639,13 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
6037
6639
  environment,
6038
6640
  recipientPrivateAddressStpl1: withdrawFrom
6039
6641
  });
6040
- const changeStroops = BigInt(context.coin.value) - prepared.intent.amount;
6642
+ const feeFields = await withdrawFeeProofFields({
6643
+ prepared,
6644
+ environment,
6645
+ tokenAddress: context.tokenAddress,
6646
+ available: BigInt(context.coin.value),
6647
+ withdrawFrom
6648
+ });
6041
6649
  const proof = await poolService.prepareWithdrawTransactProof({
6042
6650
  coin: context.coin,
6043
6651
  state: { commitments: context.commitments },
@@ -6048,11 +6656,24 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
6048
6656
  yHex: context.ephemeral.yHex
6049
6657
  }),
6050
6658
  withdrawAmountStroops: prepared.intent.amount,
6051
- changePrivateAddressStpl1: changeStroops > 0n ? withdrawFrom : void 0,
6052
- tokenAddress: context.tokenAddress
6659
+ changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
6660
+ tokenAddress: context.tokenAddress,
6661
+ ...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
6053
6662
  });
6054
6663
  enrichWithdrawOutputRecords(prepared, proof);
6055
- return buildWithdrawFinalizeArtifacts(proof, context);
6664
+ return buildWithdrawFinalizeArtifacts(proof, context, environment);
6665
+ }
6666
+ function dualWithdrawEphemeralKeys(input) {
6667
+ return {
6668
+ depositorEphemeralAKey: serializeEphemeralKeyString({
6669
+ xHex: input.context.ephemeral.xHex,
6670
+ yHex: input.context.ephemeral.yHex
6671
+ }),
6672
+ depositorEphemeralBKey: serializeEphemeralKeyString({
6673
+ xHex: input.secondary.ephemeral.xHex,
6674
+ yHex: input.secondary.ephemeral.yHex
6675
+ })
6676
+ };
6056
6677
  }
6057
6678
  async function finalizeDualWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
6058
6679
  const [, secondaryRecord] = prepared.consumedRecords;
@@ -6070,28 +6691,27 @@ async function finalizeDualWithdrawAtExecute(prepared, environment, poolService,
6070
6691
  walletPublicKey: context.walletPublicKey,
6071
6692
  commitments: context.commitments
6072
6693
  });
6073
- const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
6074
- const changeStroops = totalNotes - prepared.intent.amount;
6694
+ const feeFields = await withdrawFeeProofFields({
6695
+ prepared,
6696
+ environment,
6697
+ tokenAddress: context.tokenAddress,
6698
+ available: BigInt(context.coin.value) + BigInt(secondary.coin.value),
6699
+ withdrawFrom
6700
+ });
6075
6701
  const proof = await poolService.prepareWithdrawTransactProofDual({
6076
6702
  coinA: context.coin,
6077
6703
  coinB: secondary.coin,
6078
6704
  state: { commitments: context.commitments },
6079
6705
  destinationStellarAddress: prepared.intent.to,
6080
6706
  privKeyScalarHex: context.senderPrivKeyScalarHex,
6081
- depositorEphemeralAKey: serializeEphemeralKeyString({
6082
- xHex: context.ephemeral.xHex,
6083
- yHex: context.ephemeral.yHex
6084
- }),
6085
- depositorEphemeralBKey: serializeEphemeralKeyString({
6086
- xHex: secondary.ephemeral.xHex,
6087
- yHex: secondary.ephemeral.yHex
6088
- }),
6707
+ ...dualWithdrawEphemeralKeys({ context, secondary }),
6089
6708
  withdrawAmountStroops: prepared.intent.amount,
6090
- changePrivateAddressStpl1: changeStroops > 0n ? withdrawFrom : void 0,
6091
- tokenAddress: context.tokenAddress
6709
+ changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
6710
+ tokenAddress: context.tokenAddress,
6711
+ ...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
6092
6712
  });
6093
6713
  enrichWithdrawOutputRecords(prepared, proof);
6094
- return buildWithdrawFinalizeArtifacts(proof, context);
6714
+ return buildWithdrawFinalizeArtifacts(proof, context, environment);
6095
6715
  }
6096
6716
  async function finalizeWithdrawAtExecute(prepared, environment, poolService) {
6097
6717
  if (prepared.kind !== "withdraw") {
@@ -6223,20 +6843,20 @@ async function waitForKytRegistration(parameters) {
6223
6843
  }
6224
6844
 
6225
6845
  // src/transact/kyt/passage/submit.ts
6226
- import { Buffer as Buffer14 } from "buffer";
6227
- import { Operation, TransactionBuilder, rpc as rpc3, xdr as xdr2 } from "@stellar/stellar-sdk";
6846
+ import { Buffer as Buffer15 } from "buffer";
6847
+ import { Operation, TransactionBuilder, rpc as rpc3, xdr as xdr3 } from "@stellar/stellar-sdk";
6228
6848
  var KYT_REGISTER_FEE_STROOPS = "1000000";
6229
6849
  var KYT_REGISTER_TIMEOUT_SECONDS = 300;
6230
6850
  function passageIdToScValue(passageId) {
6231
- return xdr2.ScVal.scvBytes(Buffer14.from(passageId.replace(/^0x/, ""), "hex"));
6851
+ return xdr3.ScVal.scvBytes(Buffer15.from(passageId.replace(/^0x/, ""), "hex"));
6232
6852
  }
6233
6853
  function approvalSignatureToBytes(signature) {
6234
6854
  const clean = signature.trim();
6235
- return /^(0x)?[0-9a-fA-F]{128}$/.test(clean) ? Buffer14.from(clean.replace(/^0x/i, ""), "hex") : Buffer14.from(clean, "base64");
6855
+ return /^(0x)?[0-9a-fA-F]{128}$/.test(clean) ? Buffer15.from(clean.replace(/^0x/i, ""), "hex") : Buffer15.from(clean, "base64");
6236
6856
  }
6237
6857
  function signatureToScValue(signature) {
6238
6858
  const bytes = approvalSignatureToBytes(signature);
6239
- return xdr2.ScVal.scvBytes(bytes);
6859
+ return xdr3.ScVal.scvBytes(bytes);
6240
6860
  }
6241
6861
  async function registerKytPassage(parameters) {
6242
6862
  const signTransaction = parameters.transactEnvironment.signTransaction;
@@ -6257,7 +6877,7 @@ async function registerKytPassage(parameters) {
6257
6877
  function: "register_passage",
6258
6878
  args: [
6259
6879
  passageIdToScValue(parameters.approval.passageId),
6260
- xdr2.ScVal.scvU32(parameters.approval.expiresAtLedger),
6880
+ xdr3.ScVal.scvU32(parameters.approval.expiresAtLedger),
6261
6881
  signatureToScValue(parameters.approval.signature)
6262
6882
  ]
6263
6883
  })
@@ -6353,7 +6973,7 @@ async function requestKytPassageForPoolInteraction(input) {
6353
6973
  const applicationId = resolvePoolApplicationId(
6354
6974
  input.transactEnvironment.network.applicationId
6355
6975
  );
6356
- const zkConfigNonce = resolveZkConfigNonce(input.transactEnvironment);
6976
+ const zkConfigNonce = input.zkConfigNonce ?? resolveZkConfigNonce(input.transactEnvironment);
6357
6977
  const applicationIdsPlaintext = input.applicationIdsPlaintext ?? buildApplicationIdHints2(
6358
6978
  applicationId,
6359
6979
  layoutForKnownNonce(zkConfigNonce).nAuditSlots
@@ -6382,12 +7002,12 @@ async function requestKytPassageForPoolInteraction(input) {
6382
7002
 
6383
7003
  // src/transact/submit/zk-config-ttl.ts
6384
7004
  import {
6385
- Address as Address2,
7005
+ Address as Address3,
6386
7006
  Operation as Operation2,
6387
7007
  SorobanDataBuilder,
6388
7008
  TransactionBuilder as TransactionBuilder2,
6389
7009
  rpc as rpc4,
6390
- xdr as xdr3
7010
+ xdr as xdr4
6391
7011
  } from "@stellar/stellar-sdk";
6392
7012
  var ZK_CONFIG_TTL_THRESHOLD_LEDGERS = 1e5;
6393
7013
  var ZK_CONFIG_TTL_EXTEND_TO_LEDGERS = 5e5;
@@ -6411,15 +7031,15 @@ async function waitForExtendFootprintTtlConfirmation(parameters) {
6411
7031
  throw new Error("ZK config TTL extension was not confirmed in time");
6412
7032
  }
6413
7033
  function zkConfigLedgerKey(contractId, nonce) {
6414
- const configKeyScValue = xdr3.ScVal.scvVec([
6415
- xdr3.ScVal.scvSymbol("Config"),
6416
- xdr3.ScVal.scvU64(xdr3.Uint64.fromString(nonce.toString()))
7034
+ const configKeyScValue = xdr4.ScVal.scvVec([
7035
+ xdr4.ScVal.scvSymbol("Config"),
7036
+ xdr4.ScVal.scvU64(xdr4.Uint64.fromString(nonce.toString()))
6417
7037
  ]);
6418
- return xdr3.LedgerKey.contractData(
6419
- new xdr3.LedgerKeyContractData({
6420
- contract: new Address2(contractId).toScAddress(),
7038
+ return xdr4.LedgerKey.contractData(
7039
+ new xdr4.LedgerKeyContractData({
7040
+ contract: new Address3(contractId).toScAddress(),
6421
7041
  key: configKeyScValue,
6422
- durability: xdr3.ContractDataDurability.persistent()
7042
+ durability: xdr4.ContractDataDurability.persistent()
6423
7043
  })
6424
7044
  );
6425
7045
  }
@@ -6484,7 +7104,7 @@ async function extendZkConfigTtlIfNeeded(parameters) {
6484
7104
  }
6485
7105
 
6486
7106
  // src/transact/submit/pool-transact.ts
6487
- import { Buffer as Buffer15 } from "buffer";
7107
+ import { Buffer as Buffer16 } from "buffer";
6488
7108
  async function submitPoolTransact(parameters) {
6489
7109
  await extendZkConfigTtlIfNeeded({
6490
7110
  sorobanRpcUrl: parameters.sorobanRpcUrl,
@@ -6497,9 +7117,9 @@ async function submitPoolTransact(parameters) {
6497
7117
  const assembledTransaction = await parameters.contractClient.transact({
6498
7118
  from: parameters.from,
6499
7119
  nonce: parameters.nonce,
6500
- proof_bytes: Buffer15.from(parameters.proofHex.replace(/^0x/iu, ""), "hex"),
6501
- pub_signals_bytes: Buffer15.from(parameters.publicHex.replace(/^0x/iu, ""), "hex"),
6502
- ciphertext_bytes: Buffer15.from(
7120
+ proof_bytes: Buffer16.from(parameters.proofHex.replace(/^0x/iu, ""), "hex"),
7121
+ pub_signals_bytes: Buffer16.from(parameters.publicHex.replace(/^0x/iu, ""), "hex"),
7122
+ ciphertext_bytes: Buffer16.from(
6503
7123
  (parameters.ciphertextHex ?? "").replace(/^0x/iu, ""),
6504
7124
  "hex"
6505
7125
  ),
@@ -6525,6 +7145,10 @@ async function submitSorobanConfidentialTransfer(input) {
6525
7145
  networkPassphrase: input.networkPassphrase,
6526
7146
  sorobanRpcUrl: input.sorobanRpcUrl
6527
7147
  });
7148
+ const nonce = transactNonceFromArtifacts(
7149
+ input.zkConfigNonce === void 0 ? void 0 : { zkConfigNonce: input.zkConfigNonce },
7150
+ input.transactEnvironment
7151
+ );
6528
7152
  const approval = await requestKytPassageForPoolInteraction({
6529
7153
  owner: input.walletPublicKey,
6530
7154
  poolContract: input.contractId,
@@ -6533,6 +7157,7 @@ async function submitSorobanConfidentialTransfer(input) {
6533
7157
  ...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
6534
7158
  ...input.outputNoteEphemeralScalars ? { outputNoteEphemeralScalars: input.outputNoteEphemeralScalars } : {},
6535
7159
  ...input.applicationIdsPlaintext ? { applicationIdsPlaintext: input.applicationIdsPlaintext } : {},
7160
+ ...input.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.zkConfigNonce },
6536
7161
  networkPassphrase: input.networkPassphrase,
6537
7162
  sorobanRpcUrl: input.sorobanRpcUrl,
6538
7163
  transactEnvironment: input.transactEnvironment
@@ -6541,7 +7166,7 @@ async function submitSorobanConfidentialTransfer(input) {
6541
7166
  contractClient: poolClient,
6542
7167
  contractId: input.contractId,
6543
7168
  from: input.walletPublicKey,
6544
- nonce: resolveZkConfigNonce(input.transactEnvironment),
7169
+ nonce,
6545
7170
  proofHex: input.proofHex,
6546
7171
  publicHex: input.publicHex,
6547
7172
  ...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
@@ -6572,12 +7197,14 @@ async function submitPreparedDeposit(prepared, environment) {
6572
7197
  networkPassphrase: environment.network.networkPassphrase,
6573
7198
  sorobanRpcUrl: environment.network.rpcUrl
6574
7199
  });
7200
+ const nonce = transactNonceFromArtifacts(artifacts, environment);
6575
7201
  const approval = await requestKytPassageForPoolInteraction({
6576
7202
  owner: artifacts.walletPublicKey,
6577
7203
  poolContract: environment.network.poolContract,
6578
7204
  proofHex: artifacts.proofHex,
6579
7205
  publicHex: artifacts.publicHex,
6580
7206
  ...optionalInspectCiphertext(artifacts),
7207
+ ...artifacts.zkConfigNonce === void 0 ? {} : { zkConfigNonce: artifacts.zkConfigNonce },
6581
7208
  networkPassphrase: environment.network.networkPassphrase,
6582
7209
  sorobanRpcUrl: environment.network.rpcUrl,
6583
7210
  transactEnvironment: environment
@@ -6586,7 +7213,7 @@ async function submitPreparedDeposit(prepared, environment) {
6586
7213
  contractClient: poolClient,
6587
7214
  contractId: environment.network.poolContract,
6588
7215
  from: artifacts.walletPublicKey,
6589
- nonce: resolveZkConfigNonce(environment),
7216
+ nonce,
6590
7217
  proofHex: artifacts.proofHex,
6591
7218
  publicHex: artifacts.publicHex,
6592
7219
  ...artifacts.ciphertextHex ? { ciphertextHex: artifacts.ciphertextHex } : {},
@@ -6607,6 +7234,7 @@ async function submitPreparedPoolTransact(prepared, environment) {
6607
7234
  proofHex: artifacts.proofHex,
6608
7235
  publicHex: artifacts.publicHex,
6609
7236
  ...optionalInspectCiphertext(artifacts),
7237
+ ...artifacts.zkConfigNonce === void 0 ? {} : { zkConfigNonce: artifacts.zkConfigNonce },
6610
7238
  networkPassphrase: environment.network.networkPassphrase,
6611
7239
  sorobanRpcUrl: environment.network.rpcUrl,
6612
7240
  transactEnvironment: environment
@@ -6662,7 +7290,7 @@ async function privacyPoolInitOptions(assets, transactEnvironment) {
6662
7290
  };
6663
7291
  }
6664
7292
  async function createDefaultTransactEngine(assets, transactEnvironment) {
6665
- await PrivacyPoolSDK3.init(await privacyPoolInitOptions(assets, transactEnvironment));
7293
+ await PrivacyPoolSDK4.init(await privacyPoolInitOptions(assets, transactEnvironment));
6666
7294
  if (transactEnvironment) {
6667
7295
  configurePrivacyPoolService(
6668
7296
  createPrivacyPoolService({