@arcanetech/privacy-sdk-stellar 0.5.2 → 0.6.1

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
@@ -452,13 +452,26 @@ function buildPrivateRecordFromDeposit(input) {
452
452
  };
453
453
  }
454
454
 
455
- // src/transact/pool/service.ts
456
- import {
457
- PrivacyPoolSDK as PrivacyPoolSDK2,
458
- decodeDecodedEphemeralKey,
459
- randomFrDecimal253,
460
- scalarHexToFrDecimal as scalarHexToFrDecimal2
461
- } from "@arcanetech/stellar-privacy-pool-zk-sdk";
455
+ // src/transact/merkle/lean-imt-api.ts
456
+ function asLeanImtSessionApi(sdk) {
457
+ const candidate = sdk;
458
+ if (typeof candidate.importLeanImtFromState !== "function") {
459
+ return void 0;
460
+ }
461
+ if (typeof candidate.insertTwoLeanImt !== "function") {
462
+ return void 0;
463
+ }
464
+ if (typeof candidate.exportLeanImt !== "function") {
465
+ return void 0;
466
+ }
467
+ if (typeof candidate.dropLeanImt !== "function") {
468
+ return void 0;
469
+ }
470
+ if (typeof candidate.buildWithdrawMerkleWitnessFromHandle !== "function") {
471
+ return void 0;
472
+ }
473
+ return candidate;
474
+ }
462
475
 
463
476
  // src/transact/merkle/field-decimal.ts
464
477
  import { Buffer as Buffer4 } from "buffer";
@@ -474,6 +487,230 @@ function merkleRootBufferToFrDecimal(root) {
474
487
  const hex = Buffer4.from(root).toString("hex");
475
488
  return (BigInt(`0x${hex}`) % BN254).toString(DECIMAL_STRING_RADIX);
476
489
  }
490
+ var MERKLE_ROOT_HEX_PATTERN = /^[0-9a-fA-F]{64}$/;
491
+ function merkleRootHexToFrDecimal(merkleRootHex) {
492
+ const trimmed = merkleRootHex.trim();
493
+ if (!MERKLE_ROOT_HEX_PATTERN.test(trimmed)) {
494
+ return void 0;
495
+ }
496
+ return merkleRootBufferToFrDecimal(Buffer4.from(trimmed, "hex"));
497
+ }
498
+
499
+ // src/transact/merkle/state-file.ts
500
+ function merkleSnapshotToStateFile(input) {
501
+ const root = merkleRootHexToFrDecimal(input.merkleRootHex);
502
+ const nodes = input.nodes;
503
+ if (!nodes || nodes.length === 0 || root === void 0) {
504
+ return { commitments: input.commitments };
505
+ }
506
+ return {
507
+ commitments: input.commitments,
508
+ nodes,
509
+ root
510
+ };
511
+ }
512
+
513
+ // src/transact/merkle/extend-snapshot.ts
514
+ function isEvenLeafCount(count) {
515
+ return count % 2 === 0;
516
+ }
517
+ function importHandle(sdk, state) {
518
+ try {
519
+ return sdk.importLeanImtFromState(state);
520
+ } catch {
521
+ return void 0;
522
+ }
523
+ }
524
+ function insertNewPairs(input) {
525
+ for (let index = input.prefixLength; index < input.commitments.length; index += 2) {
526
+ const leafA = input.commitments.at(index);
527
+ const leafB = input.commitments.at(index + 1);
528
+ if (leafA === void 0 || leafB === void 0) {
529
+ return false;
530
+ }
531
+ try {
532
+ input.sdk.insertTwoLeanImt(input.handle, leafA, leafB);
533
+ } catch {
534
+ return false;
535
+ }
536
+ }
537
+ return true;
538
+ }
539
+ function snapshotNodes(sdk, handle) {
540
+ try {
541
+ return sdk.exportLeanImt(handle).nodes;
542
+ } catch {
543
+ return void 0;
544
+ }
545
+ }
546
+ function prefixSnapshotState(input) {
547
+ const prefixLength = input.cachedState?.nodes ? input.cachedState.commitments.length : 0;
548
+ return merkleSnapshotToStateFile({
549
+ commitments: input.commitments.slice(0, prefixLength),
550
+ merkleRootHex: input.cachedState?.merkleRootHex ?? input.merkleRootHex,
551
+ ...input.cachedState?.nodes ? { nodes: input.cachedState.nodes } : {}
552
+ });
553
+ }
554
+ function importSnapshotState(input) {
555
+ if (input.prefixState.nodes && input.prefixState.commitments.length > 0) {
556
+ return input.prefixState;
557
+ }
558
+ return merkleSnapshotToStateFile({
559
+ commitments: input.commitments,
560
+ merkleRootHex: input.merkleRootHex
561
+ });
562
+ }
563
+ function exportExtendedNodes(input) {
564
+ const inserted = input.prefixState.nodes ? insertNewPairs({
565
+ sdk: input.sdk,
566
+ handle: input.handle,
567
+ prefixLength: input.prefixState.commitments.length,
568
+ commitments: input.commitments
569
+ }) : true;
570
+ if (!inserted) {
571
+ input.sdk.dropLeanImt(input.handle);
572
+ return void 0;
573
+ }
574
+ const nodes = snapshotNodes(input.sdk, input.handle);
575
+ if (!nodes) {
576
+ input.sdk.dropLeanImt(input.handle);
577
+ }
578
+ return nodes;
579
+ }
580
+ function extendMerkleSnapshotNodes(input) {
581
+ if (!isEvenLeafCount(input.commitments.length)) {
582
+ return {};
583
+ }
584
+ const prefixState = prefixSnapshotState(input);
585
+ const handle = importHandle(
586
+ input.sdk,
587
+ importSnapshotState({ ...input, prefixState })
588
+ );
589
+ if (handle === void 0) {
590
+ return {};
591
+ }
592
+ const nodes = exportExtendedNodes({
593
+ sdk: input.sdk,
594
+ handle,
595
+ prefixState,
596
+ commitments: input.commitments
597
+ });
598
+ if (!nodes) {
599
+ return {};
600
+ }
601
+ return { nodes, handle };
602
+ }
603
+
604
+ // src/transact/merkle/tree-session.ts
605
+ var merkleSdkHost;
606
+ function registerMerkleSessionSdkHost(host) {
607
+ merkleSdkHost = host;
608
+ }
609
+ async function loadSessionSdk() {
610
+ const host = merkleSdkHost;
611
+ if (typeof host?.getInitializedSdk !== "function") {
612
+ return void 0;
613
+ }
614
+ try {
615
+ return asLeanImtSessionApi(await host.getInitializedSdk());
616
+ } catch {
617
+ return void 0;
618
+ }
619
+ }
620
+ var sessionsByPool = /* @__PURE__ */ new Map();
621
+ var handlesByKey = /* @__PURE__ */ new Map();
622
+ function merkleSessionKey(state) {
623
+ const root = state.root?.trim();
624
+ if (root) {
625
+ return `${state.commitments.length}:${root}`;
626
+ }
627
+ const first = state.commitments[0] ?? "";
628
+ const last = state.commitments.at(-1) ?? "";
629
+ return `${state.commitments.length}:${first}:${last}`;
630
+ }
631
+ function rememberHandle(poolContract, key, handle) {
632
+ const existing = sessionsByPool.get(poolContract);
633
+ if (existing && existing.handle !== handle) {
634
+ handlesByKey.delete(existing.key);
635
+ }
636
+ sessionsByPool.set(poolContract, { handle, key });
637
+ handlesByKey.set(key, handle);
638
+ }
639
+ function importHandle2(sdk, state) {
640
+ try {
641
+ return sdk.importLeanImtFromState(state);
642
+ } catch {
643
+ return void 0;
644
+ }
645
+ }
646
+ function merkleTreeHandleForState(state) {
647
+ return handlesByKey.get(merkleSessionKey(state));
648
+ }
649
+ function bindImportedMerkleTree(input) {
650
+ rememberHandle(input.poolContract, merkleSessionKey(input.state), input.handle);
651
+ }
652
+ async function bindPoolMerkleTree(state) {
653
+ const sdk = await loadSessionSdk();
654
+ if (!sdk) {
655
+ return void 0;
656
+ }
657
+ const snapshot = merkleSnapshotToStateFile(state);
658
+ const key = merkleSessionKey(snapshot);
659
+ const existing = sessionsByPool.get(state.poolContract);
660
+ if (existing?.key === key) {
661
+ return existing.handle;
662
+ }
663
+ const handle = importHandle2(sdk, snapshot);
664
+ if (handle === void 0) {
665
+ return void 0;
666
+ }
667
+ if (existing) {
668
+ sdk.dropLeanImt(existing.handle);
669
+ }
670
+ rememberHandle(state.poolContract, key, handle);
671
+ return handle;
672
+ }
673
+ async function resolveExtendedMerkleNodes(input) {
674
+ const cached = input.cachedState;
675
+ if (cached && cached.commitments.length === input.commitments.length && cached.merkleRootHex === input.merkleRootHex) {
676
+ return cached.nodes;
677
+ }
678
+ const sdk = await loadSessionSdk();
679
+ if (!sdk) {
680
+ return void 0;
681
+ }
682
+ const extended = extendMerkleSnapshotNodes({
683
+ sdk,
684
+ commitments: input.commitments,
685
+ merkleRootHex: input.merkleRootHex,
686
+ ...cached ? { cachedState: cached } : {}
687
+ });
688
+ if (extended.handle !== void 0) {
689
+ bindImportedMerkleTree({
690
+ poolContract: input.poolContract,
691
+ handle: extended.handle,
692
+ state: merkleSnapshotToStateFile({
693
+ commitments: input.commitments,
694
+ merkleRootHex: input.merkleRootHex,
695
+ ...extended.nodes ? { nodes: extended.nodes } : {}
696
+ })
697
+ });
698
+ }
699
+ return extended.nodes;
700
+ }
701
+ function withdrawMerkleWitnessFromTree(input) {
702
+ const session = asLeanImtSessionApi(input.sdk);
703
+ const handle = merkleTreeHandleForState(input.state);
704
+ if (session && handle !== void 0) {
705
+ return session.buildWithdrawMerkleWitnessFromHandle(input.coin, handle);
706
+ }
707
+ return input.sdk.buildWithdrawMerkleWitness(input.coin, input.state);
708
+ }
709
+
710
+ // src/transact/pool/service.ts
711
+ import {
712
+ decodeDecodedEphemeralKey
713
+ } from "@arcanetech/stellar-privacy-pool-zk-sdk";
477
714
 
478
715
  // src/transact/private-address/codec.ts
479
716
  import {
@@ -704,7 +941,7 @@ var MIN_CONFIDENTIAL_TRANSFER_STROOPS = 1n;
704
941
  var ZERO_STROOPS = 0n;
705
942
  function withdrawWitnessForCoin(parameters) {
706
943
  const { sdk, coin, state } = parameters;
707
- const witness = sdk.buildWithdrawMerkleWitness(coin, state);
944
+ const witness = withdrawMerkleWitnessFromTree({ sdk, coin, state });
708
945
  const withdrawApplicationId = coin.application_id ?? parameters.applicationId;
709
946
  const withdrawObject = withdrawObjectFromMerkleWitness(
710
947
  witness,
@@ -746,14 +983,144 @@ function requireChangeRecipientWhenPartial(changeStroops, selfPrivateAddressStpl
746
983
  }
747
984
  }
748
985
  }
749
- function changeStroopsAfterDualTransfer(transferStroops, totalNotes) {
750
- if (transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
751
- throw new Error("Transfer amount must be positive");
986
+
987
+ // src/transact/fees/quote-request-body.ts
988
+ function presentInstructedAmount(amount) {
989
+ const trimmed = amount?.trim() ?? "";
990
+ if (!trimmed || trimmed === "0") {
991
+ return void 0;
992
+ }
993
+ return trimmed;
994
+ }
995
+ function feeQuoteRequestBody(request) {
996
+ const publicDepositAmount = presentInstructedAmount(request.publicDepositAmount);
997
+ const transferInstructedAmount = presentInstructedAmount(
998
+ request.transferInstructedAmount
999
+ );
1000
+ const publicWithdrawalAmount = presentInstructedAmount(
1001
+ request.publicWithdrawalAmount
1002
+ );
1003
+ return {
1004
+ feeAsset: request.feeAsset,
1005
+ spendsNotes: request.spendsNotes,
1006
+ ...publicDepositAmount === void 0 ? {} : { publicDepositAmount },
1007
+ ...transferInstructedAmount === void 0 ? {} : { transferInstructedAmount },
1008
+ ...publicWithdrawalAmount === void 0 ? {} : { publicWithdrawalAmount }
1009
+ };
1010
+ }
1011
+
1012
+ // src/transact/fees/quote-required-fee.ts
1013
+ function quoteHeaders(inspectAuthorization) {
1014
+ const token = inspectAuthorization?.trim();
1015
+ return {
1016
+ "content-type": "application/json",
1017
+ ...token ? { authorization: `Bearer ${token}` } : {}
1018
+ };
1019
+ }
1020
+ function parseRequiredFee(raw) {
1021
+ const text = `${raw ?? ""}`.trim();
1022
+ if (!/^\d+$/u.test(text)) {
1023
+ throw new Error("Fee Quote Required Fee must be an integer minor-unit string.");
752
1024
  }
753
- if (transferStroops > totalNotes) {
754
- throw new Error("Transfer amount exceeds combined note value");
1025
+ return BigInt(text);
1026
+ }
1027
+ function requireQuoteString(raw, field) {
1028
+ const text = `${raw ?? ""}`.trim();
1029
+ if (!text) {
1030
+ throw new Error(`Fee Quote is missing ${field}.`);
755
1031
  }
756
- return totalNotes - transferStroops;
1032
+ return text;
1033
+ }
1034
+ function parseQuotePayload(payload) {
1035
+ return {
1036
+ requiredFee: parseRequiredFee(payload.requiredFee),
1037
+ feeAsset: requireQuoteString(payload.feeAsset, "Fee Asset"),
1038
+ feeCollectorPrivateAddress: requireQuoteString(
1039
+ payload.feeCollectorPrivateAddress,
1040
+ "Fee Collector Private Address"
1041
+ ),
1042
+ feeRate: requireQuoteString(payload.feeRate, "Fee Rate")
1043
+ };
1044
+ }
1045
+ async function quoteRequiredFee(input) {
1046
+ const response = await fetch(
1047
+ `${input.transactEnvironment.kyt.apiBaseUrl}/kyt/fees/quote`,
1048
+ {
1049
+ method: "POST",
1050
+ headers: quoteHeaders(input.transactEnvironment.kyt.inspectAuthorization),
1051
+ body: JSON.stringify(feeQuoteRequestBody(input.request))
1052
+ }
1053
+ );
1054
+ const payload = await response.json();
1055
+ if (!response.ok) {
1056
+ throw new Error(`Fee Quote failed: ${JSON.stringify(payload)}`);
1057
+ }
1058
+ return parseQuotePayload(payload);
1059
+ }
1060
+
1061
+ // src/transact/fees/should-attach-fee-output.ts
1062
+ function shouldAttachFeeOutput(input) {
1063
+ if (input.spendSource === "escrow" && input.escrowSend !== true) {
1064
+ return false;
1065
+ }
1066
+ return input.kind === "deposit" || input.kind === "transfer" || input.kind === "withdraw";
1067
+ }
1068
+ function feeOutputKindFromPrepared(prepared) {
1069
+ return {
1070
+ kind: prepared.kind,
1071
+ ...prepared.transactArtifacts?.spendSource ? { spendSource: prepared.transactArtifacts.spendSource } : {},
1072
+ ...prepared.transactArtifacts?.escrowSend ? { escrowSend: true } : {}
1073
+ };
1074
+ }
1075
+
1076
+ // src/transact/fees/quote-fee-output-for-prepared.ts
1077
+ function instructedMinorUnits(prepared) {
1078
+ return prepared.intent.amount.toString();
1079
+ }
1080
+ function quoteRequestForPrepared(input) {
1081
+ const amount = instructedMinorUnits(input.prepared);
1082
+ if (input.prepared.kind === "deposit") {
1083
+ return {
1084
+ feeAsset: input.tokenAddress,
1085
+ publicDepositAmount: amount,
1086
+ spendsNotes: false
1087
+ };
1088
+ }
1089
+ if (input.prepared.kind === "withdraw") {
1090
+ return {
1091
+ feeAsset: input.tokenAddress,
1092
+ publicWithdrawalAmount: amount,
1093
+ spendsNotes: true
1094
+ };
1095
+ }
1096
+ return {
1097
+ feeAsset: input.tokenAddress,
1098
+ transferInstructedAmount: amount,
1099
+ spendsNotes: true
1100
+ };
1101
+ }
1102
+ async function quoteFeeOutputForPrepared(input) {
1103
+ if (!shouldAttachFeeOutput(feeOutputKindFromPrepared(input.prepared))) {
1104
+ return void 0;
1105
+ }
1106
+ const quote = await quoteRequiredFee({
1107
+ transactEnvironment: input.environment,
1108
+ request: quoteRequestForPrepared(input)
1109
+ });
1110
+ if (quote.requiredFee <= 0n) {
1111
+ return void 0;
1112
+ }
1113
+ return {
1114
+ requiredFee: quote.requiredFee,
1115
+ feeCollectorPrivateAddress: quote.feeCollectorPrivateAddress
1116
+ };
1117
+ }
1118
+ function remainingAfterRequiredFee(input) {
1119
+ const remaining = input.available - input.instructed - input.requiredFee;
1120
+ if (remaining < 0n) {
1121
+ throw new Error("Instructed amount and Required Fee exceed available note value");
1122
+ }
1123
+ return remaining;
757
1124
  }
758
1125
 
759
1126
  // src/transact/audit/parameters.ts
@@ -894,6 +1261,32 @@ function privKeyScalarDecimalFromRecipientScalarHex(hexInput) {
894
1261
  return canonicalBabyJubScalarFromInteger(value).toString(DECIMAL_RADIX2);
895
1262
  }
896
1263
 
1264
+ // src/transact/fees/append-fee-output.ts
1265
+ function feeCoinFromSlot(slot) {
1266
+ return {
1267
+ commitment_hex: slot.commitment_hex,
1268
+ coin: slot.coin,
1269
+ depositScalarHex: slot.depositScalarHex,
1270
+ precommitementHex: slot.precommitementHex
1271
+ };
1272
+ }
1273
+ async function appendFeeOutputSlot(input) {
1274
+ const requiredFee = input.feeOutput?.requiredFee ?? ZERO_STROOPS;
1275
+ const collector = input.feeOutput?.feeCollectorPrivateAddress.trim() ?? "";
1276
+ if (requiredFee <= ZERO_STROOPS || !collector) {
1277
+ return { deposits: input.deposits };
1278
+ }
1279
+ const slot = await input.buildAlignedDepositSlot({
1280
+ privateAddressStpl1: collector,
1281
+ amountStroops: requiredFee,
1282
+ tokenAddress: input.tokenAddress
1283
+ });
1284
+ return {
1285
+ deposits: [...input.deposits, slot.deposit],
1286
+ feeCoin: feeCoinFromSlot(slot)
1287
+ };
1288
+ }
1289
+
897
1290
  // src/transact/proofs/withdraw/helpers.ts
898
1291
  function destinationWithdrawFrAndScalar(parameters) {
899
1292
  const pkHex = ed25519PublicKeyHexFromStellarAccount(
@@ -948,14 +1341,15 @@ async function resolveWithdrawChangeDeposits(parameters) {
948
1341
  }
949
1342
  };
950
1343
  }
951
- function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1) {
1344
+ function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1, requiredFee) {
952
1345
  if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
953
1346
  throw new Error("Withdraw amount must be positive");
954
1347
  }
955
- if (withdrawAmountStroops > totalNotes) {
956
- throw new Error("Withdraw amount exceeds combined note value");
957
- }
958
- const changeStroops = totalNotes - withdrawAmountStroops;
1348
+ const changeStroops = remainingAfterRequiredFee({
1349
+ available: totalNotes,
1350
+ instructed: withdrawAmountStroops,
1351
+ requiredFee
1352
+ });
959
1353
  requireChangeRecipientWhenPartial(changeStroops, changePrivateAddressStpl1);
960
1354
  return changeStroops;
961
1355
  }
@@ -968,11 +1362,31 @@ function buildDualWithdrawPublicInput(parameters) {
968
1362
  publicWithdrawalAmount: parameters.publicWithdrawalAmount
969
1363
  });
970
1364
  }
1365
+ async function withdrawOutputsWithOptionalFee(parameters) {
1366
+ const { deposits: changeDeposits, changeCoin } = await resolveWithdrawChangeDeposits({
1367
+ changeStroops: parameters.changeStroops,
1368
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1369
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1370
+ tokenAddress: parameters.tokenAddress
1371
+ });
1372
+ const withFee = await appendFeeOutputSlot({
1373
+ deposits: changeDeposits,
1374
+ tokenAddress: parameters.tokenAddress,
1375
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1376
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1377
+ });
1378
+ return {
1379
+ deposits: withFee.deposits,
1380
+ ...changeCoin ? { changeCoin } : {},
1381
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
1382
+ };
1383
+ }
971
1384
  async function prepareDualWithdrawProofInputs(parameters) {
972
1385
  const changeStroops = validateDualWithdrawAmounts(
973
1386
  parameters.withdrawAmountStroops,
974
1387
  BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value),
975
- parameters.changePrivateAddressStpl1
1388
+ parameters.changePrivateAddressStpl1,
1389
+ parameters.feeOutput?.requiredFee ?? 0n
976
1390
  );
977
1391
  const { legA, legB } = dualWithdrawLegsWithSharedRoot({
978
1392
  sdk: parameters.sdk,
@@ -987,11 +1401,12 @@ async function prepareDualWithdrawProofInputs(parameters) {
987
1401
  ),
988
1402
  applicationId: parameters.applicationId
989
1403
  });
990
- const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
1404
+ const { deposits, changeCoin, feeCoin } = await withdrawOutputsWithOptionalFee({
991
1405
  changeStroops,
992
1406
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
993
1407
  buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
994
- tokenAddress: parameters.tokenAddress
1408
+ tokenAddress: parameters.tokenAddress,
1409
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
995
1410
  });
996
1411
  const publicInput = buildDualWithdrawPublicInput({
997
1412
  destinationStellarAddress: parameters.destinationStellarAddress,
@@ -1010,19 +1425,55 @@ async function prepareDualWithdrawProofInputs(parameters) {
1010
1425
  audit,
1011
1426
  deposits,
1012
1427
  withdrawLegs: [legA.withdrawObject, legB.withdrawObject],
1013
- ...changeCoin ? { changeCoin } : {}
1428
+ ...changeCoin ? { changeCoin } : {},
1429
+ ...feeCoin ? { feeCoin } : {}
1014
1430
  };
1015
1431
  }
1016
1432
 
1433
+ // src/transact/proofs/withdraw/fee-deposits.ts
1434
+ async function withdrawDepositsWithFee(parameters) {
1435
+ const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
1436
+ changeStroops: parameters.changeStroops,
1437
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1438
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1439
+ tokenAddress: parameters.tokenAddress
1440
+ });
1441
+ const withFee = await appendFeeOutputSlot({
1442
+ deposits,
1443
+ tokenAddress: parameters.tokenAddress,
1444
+ buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1445
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1446
+ });
1447
+ return {
1448
+ deposits: withFee.deposits,
1449
+ ...changeCoin ? { changeCoin } : {},
1450
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
1451
+ };
1452
+ }
1453
+ function withdrawOutputApplicationIds(input) {
1454
+ const outputIds = [];
1455
+ if (input.hasChange) {
1456
+ outputIds.push(input.applicationId);
1457
+ }
1458
+ if (input.hasFeeOutput) {
1459
+ outputIds.push(input.applicationId);
1460
+ }
1461
+ if (outputIds.length === 0) {
1462
+ outputIds.push("0");
1463
+ }
1464
+ return outputIds;
1465
+ }
1466
+
1017
1467
  // src/transact/proofs/withdraw/transact-proof.ts
1018
- function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops) {
1468
+ function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops, requiredFee) {
1019
1469
  if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
1020
1470
  throw new Error("Withdraw amount must be positive");
1021
1471
  }
1022
- if (withdrawAmountStroops > noteStroops) {
1023
- throw new Error("Withdraw amount exceeds note value");
1024
- }
1025
- return noteStroops - withdrawAmountStroops;
1472
+ return remainingAfterRequiredFee({
1473
+ available: noteStroops,
1474
+ instructed: withdrawAmountStroops,
1475
+ requiredFee
1476
+ });
1026
1477
  }
1027
1478
  async function proveSingleWithdrawTransaction(parameters) {
1028
1479
  return parameters.sdk.proveTransaction(
@@ -1040,10 +1491,11 @@ async function proveSingleWithdrawTransaction(parameters) {
1040
1491
  );
1041
1492
  }
1042
1493
  function buildPrimaryWithdrawForCoin(parameters) {
1043
- const witness = parameters.sdk.buildWithdrawMerkleWitness(
1044
- parameters.coin,
1045
- parameters.state
1046
- );
1494
+ const witness = withdrawMerkleWitnessFromTree({
1495
+ sdk: parameters.sdk,
1496
+ coin: parameters.coin,
1497
+ state: parameters.state
1498
+ });
1047
1499
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
1048
1500
  parameters.privKeyScalarHex
1049
1501
  );
@@ -1061,7 +1513,8 @@ function buildPrimaryWithdrawForCoin(parameters) {
1061
1513
  async function prepareSingleWithdrawProofInputs(parameters) {
1062
1514
  const changeStroops = validateSingleWithdrawAmounts(
1063
1515
  parameters.withdrawAmountStroops,
1064
- BigInt(parameters.coin.value)
1516
+ BigInt(parameters.coin.value),
1517
+ parameters.feeOutput?.requiredFee ?? 0n
1065
1518
  );
1066
1519
  const { witness, primaryWithdraw } = buildPrimaryWithdrawForCoin({
1067
1520
  sdk: parameters.sdk,
@@ -1070,11 +1523,12 @@ async function prepareSingleWithdrawProofInputs(parameters) {
1070
1523
  state: parameters.state,
1071
1524
  privKeyScalarHex: parameters.privKeyScalarHex
1072
1525
  });
1073
- const { deposits, changeCoin } = await resolveWithdrawChangeDeposits({
1526
+ const { deposits, changeCoin, feeCoin } = await withdrawDepositsWithFee({
1074
1527
  changeStroops,
1075
1528
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1076
1529
  buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
1077
- tokenAddress: parameters.tokenAddress
1530
+ tokenAddress: parameters.tokenAddress,
1531
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1078
1532
  });
1079
1533
  const publicInput = buildWithdrawPublicInput({
1080
1534
  stateRoot: witness.stateRoot,
@@ -1087,6 +1541,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
1087
1541
  primaryWithdraw,
1088
1542
  deposits,
1089
1543
  changeCoin,
1544
+ feeCoin,
1090
1545
  publicInput,
1091
1546
  audit: buildPoolTransactionAuditParameters({
1092
1547
  applicationId: parameters.applicationId,
@@ -1096,7 +1551,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
1096
1551
  };
1097
1552
  }
1098
1553
  async function proveWithdrawTransact(parameters) {
1099
- const { primaryWithdraw, deposits, changeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
1554
+ const { primaryWithdraw, deposits, changeCoin, feeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
1100
1555
  const proof = await proveSingleWithdrawTransaction({
1101
1556
  sdk: parameters.sdk,
1102
1557
  publicInput,
@@ -1111,13 +1566,17 @@ async function proveWithdrawTransact(parameters) {
1111
1566
  applicationIdsPlaintext: buildApplicationIdHints({
1112
1567
  sdk: parameters.sdk,
1113
1568
  inputIds: [parameters.applicationId],
1114
- outputIds: [changeCoin ? parameters.applicationId : "0"]
1569
+ outputIds: withdrawOutputApplicationIds({
1570
+ applicationId: parameters.applicationId,
1571
+ hasChange: Boolean(changeCoin),
1572
+ hasFeeOutput: Boolean(feeCoin)
1573
+ })
1115
1574
  }),
1116
1575
  ...changeCoin ? { changeCoin } : {}
1117
1576
  };
1118
1577
  }
1119
1578
  async function proveWithdrawTransactDual(parameters) {
1120
- const { publicInput, audit, withdrawLegs, deposits, changeCoin } = await prepareDualWithdrawProofInputs(parameters);
1579
+ const { publicInput, audit, withdrawLegs, deposits, changeCoin, feeCoin } = await prepareDualWithdrawProofInputs(parameters);
1121
1580
  const proof = await parameters.sdk.proveTransaction(
1122
1581
  publicInput,
1123
1582
  padPublicLegsToLayout(
@@ -1136,7 +1595,11 @@ async function proveWithdrawTransactDual(parameters) {
1136
1595
  applicationIdsPlaintext: buildApplicationIdHints({
1137
1596
  sdk: parameters.sdk,
1138
1597
  inputIds: [audit.applicationId, audit.applicationId],
1139
- outputIds: [changeCoin ? audit.applicationId : "0"]
1598
+ outputIds: withdrawOutputApplicationIds({
1599
+ applicationId: audit.applicationId,
1600
+ hasChange: Boolean(changeCoin),
1601
+ hasFeeOutput: Boolean(feeCoin)
1602
+ })
1140
1603
  }),
1141
1604
  ...changeCoin ? { changeCoin } : {}
1142
1605
  };
@@ -1160,6 +1623,9 @@ var DEFAULT_ZK_CONFIG_NONCE = BINDING_ZK_NONCE;
1160
1623
  function resolveZkConfigNonce(transactEnvironment) {
1161
1624
  return transactEnvironment.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1162
1625
  }
1626
+ function transactNonceFromArtifacts(artifacts, transactEnvironment) {
1627
+ return artifacts?.zkConfigNonce ?? resolveZkConfigNonce(transactEnvironment);
1628
+ }
1163
1629
 
1164
1630
  // src/transact/zk/browser-artifact-cache.ts
1165
1631
  import { ZK_ARTIFACT_VERSION } from "@arcanetech/stellar-privacy-pool-zk-sdk";
@@ -1412,6 +1878,105 @@ async function buildAlignedDepositSlotForSdk(sdk, applicationId, parameters) {
1412
1878
  };
1413
1879
  }
1414
1880
 
1881
+ // src/transact/pool/initialize-sdk.ts
1882
+ import { PrivacyPoolSDK as PrivacyPoolSDK2 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
1883
+ async function initializePrivacyPoolSdk(input, nonce) {
1884
+ const zkConfigNonce = nonce ?? input.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1885
+ const zkCircuits = await materializeSelectedZkCircuit(
1886
+ input.zkCircuits,
1887
+ zkConfigNonce,
1888
+ input.zkArtifactBaseUrl
1889
+ );
1890
+ return PrivacyPoolSDK2.init({
1891
+ wasmBinary: input.assets.sdkWasm,
1892
+ zkCircuits,
1893
+ zkConfigNonce,
1894
+ ...optionalZkArtifactBaseUrl(input.zkArtifactBaseUrl)
1895
+ });
1896
+ }
1897
+
1898
+ // src/transact/fees/zk-config-nonce-for-kind.ts
1899
+ function configuredNonceSpread(zkConfigNonce) {
1900
+ if (zkConfigNonce === void 0) {
1901
+ return {};
1902
+ }
1903
+ return { configuredNonce: zkConfigNonce };
1904
+ }
1905
+ function zkConfigNonceForFeeBearingKind(input) {
1906
+ if (input.configuredNonce === SIX_BY_SIX_BINDING_ZK_NONCE) {
1907
+ return SIX_BY_SIX_BINDING_ZK_NONCE;
1908
+ }
1909
+ if (input.kind === "transfer" && shouldAttachFeeOutput(input)) {
1910
+ return SIX_BY_SIX_BINDING_ZK_NONCE;
1911
+ }
1912
+ return input.configuredNonce ?? BINDING_ZK_NONCE;
1913
+ }
1914
+
1915
+ // src/transact/pool/prepare-deposit-proof.ts
1916
+ import {
1917
+ randomFrDecimal253,
1918
+ scalarHexToFrDecimal as scalarHexToFrDecimal2
1919
+ } from "@arcanetech/stellar-privacy-pool-zk-sdk";
1920
+ function userDepositSlot(input) {
1921
+ return {
1922
+ value: input.coin.value,
1923
+ nullifier: input.coin.nullifier,
1924
+ ephemeralKeyScalar: scalarHexToFrDecimal2(input.depositScalarHex),
1925
+ asset: [input.coin.asset_hi, input.coin.asset_lo],
1926
+ applicationId: input.applicationId,
1927
+ recipientPublicKeys: recipientPublicKeysDecimalFromPrivateAddress(
1928
+ input.privateAddressStpl1
1929
+ )
1930
+ };
1931
+ }
1932
+ function outputApplicationIds(input) {
1933
+ if (input.hasFeeOutput) {
1934
+ return [input.applicationId, input.applicationId];
1935
+ }
1936
+ return [input.applicationId];
1937
+ }
1938
+ async function proveDepositTransact(input) {
1939
+ const publicDeposit = (input.publicDepositStroops ?? BigInt(input.coin.value)).toString();
1940
+ const withFee = await appendFeeOutputSlot({
1941
+ deposits: [userDepositSlot(input)],
1942
+ tokenAddress: input.tokenAddress,
1943
+ buildAlignedDepositSlot: input.buildAlignedDepositSlot,
1944
+ ...input.feeOutput ? { feeOutput: input.feeOutput } : {}
1945
+ });
1946
+ const audit = buildPoolTransactionAuditParameters({
1947
+ applicationId: input.applicationId,
1948
+ nAuditSlots: input.sdk.getLayout().nAuditSlots,
1949
+ ...input.auditPublicKey ? { auditPublicKey: input.auditPublicKey } : {}
1950
+ });
1951
+ const proof = await input.sdk.proveTransaction(
1952
+ withTokenAddressPublicInputs(
1953
+ {
1954
+ stateRoot: merkleRootBufferToFrDecimal(input.merkleRootBytes),
1955
+ privKeyScalar: randomFrDecimal253()
1956
+ },
1957
+ input.tokenAddress
1958
+ ),
1959
+ padPublicLegsToLayout(
1960
+ input.sdk,
1961
+ buildPublicDepositLegs(input.tokenAddress, publicDeposit)
1962
+ ),
1963
+ padWithdrawSlotsToLayout(input.sdk, []),
1964
+ padDepositSlotsToLayout(input.sdk, withFee.deposits),
1965
+ audit
1966
+ );
1967
+ return {
1968
+ ...proof,
1969
+ applicationIdsPlaintext: buildApplicationIdHints({
1970
+ sdk: input.sdk,
1971
+ inputIds: [],
1972
+ outputIds: outputApplicationIds({
1973
+ applicationId: input.applicationId,
1974
+ hasFeeOutput: Boolean(withFee.feeCoin)
1975
+ })
1976
+ })
1977
+ };
1978
+ }
1979
+
1415
1980
  // src/transact/pool/service.ts
1416
1981
  var PrivacyPoolService = class {
1417
1982
  constructor(assets, applicationId, auditPublicKey, zkCircuits, zkConfigNonce, zkArtifactBaseUrl) {
@@ -1428,42 +1993,50 @@ var PrivacyPoolService = class {
1428
1993
  zkCircuits;
1429
1994
  zkConfigNonce;
1430
1995
  zkArtifactBaseUrl;
1431
- sdk = void 0;
1432
- initPromise = void 0;
1433
- async ensureInit() {
1434
- if (this.sdk) {
1435
- return;
1996
+ sdks = /* @__PURE__ */ new Map();
1997
+ initPromises = /* @__PURE__ */ new Map();
1998
+ async getInitializedSdk(nonce) {
1999
+ const zkConfigNonce = nonce ?? this.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
2000
+ const key = zkConfigNonce.toString();
2001
+ const existing = this.sdks.get(key);
2002
+ if (existing) {
2003
+ return existing;
1436
2004
  }
1437
- if (this.initPromise) {
1438
- return this.initPromise;
2005
+ const pending = this.initPromises.get(key);
2006
+ if (pending) {
2007
+ return pending;
2008
+ }
2009
+ const started = this.initializeSdk(zkConfigNonce);
2010
+ this.initPromises.set(key, started);
2011
+ try {
2012
+ const sdk = await started;
2013
+ this.sdks.set(key, sdk);
2014
+ return sdk;
2015
+ } finally {
2016
+ this.initPromises.delete(key);
1439
2017
  }
1440
- this.initPromise = (async () => {
1441
- if (!this.assets) {
1442
- throw new Error(
1443
- "PrivacyPoolService requires browser ZK assets before initialization."
1444
- );
1445
- }
1446
- const zkConfigNonce = this.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
1447
- const zkCircuits = await materializeSelectedZkCircuit(
1448
- this.zkCircuits,
1449
- zkConfigNonce,
1450
- this.zkArtifactBaseUrl
1451
- );
1452
- this.sdk = await PrivacyPoolSDK2.init({
1453
- wasmBinary: this.assets.sdkWasm,
1454
- zkCircuits,
1455
- zkConfigNonce,
1456
- ...optionalZkArtifactBaseUrl(this.zkArtifactBaseUrl)
1457
- });
1458
- })();
1459
- return this.initPromise;
1460
2018
  }
1461
- async getInitializedSdk() {
1462
- await this.ensureInit();
1463
- if (!this.sdk) {
1464
- throw new Error("SDK not initialized");
2019
+ feeBearingZkConfigNonce(input) {
2020
+ return zkConfigNonceForFeeBearingKind({
2021
+ ...input,
2022
+ ...configuredNonceSpread(this.zkConfigNonce)
2023
+ });
2024
+ }
2025
+ async initializeSdk(nonce) {
2026
+ if (!this.assets) {
2027
+ throw new Error(
2028
+ "PrivacyPoolService requires browser ZK assets before initialization."
2029
+ );
1465
2030
  }
1466
- return this.sdk;
2031
+ return initializePrivacyPoolSdk(
2032
+ {
2033
+ assets: this.assets,
2034
+ ...this.zkCircuits ? { zkCircuits: this.zkCircuits } : {},
2035
+ ...this.zkConfigNonce === void 0 ? {} : { zkConfigNonce: this.zkConfigNonce },
2036
+ ...optionalZkArtifactBaseUrl(this.zkArtifactBaseUrl)
2037
+ },
2038
+ nonce
2039
+ );
1467
2040
  }
1468
2041
  getAuditPublicKey() {
1469
2042
  return this.auditPublicKey;
@@ -1496,65 +2069,51 @@ var PrivacyPoolService = class {
1496
2069
  return { commitment_hex, coin, depositScalarHex, precommitementHex };
1497
2070
  }
1498
2071
  async prepareDepositTransactProof(parameters) {
1499
- const sdk = await this.getInitializedSdk();
1500
- const stateRoot = merkleRootBufferToFrDecimal(parameters.merkleRootBytes);
1501
- const recipientPublicKeys = recipientPublicKeysDecimalFromPrivateAddress(
1502
- parameters.privateAddressStpl1
1503
- );
1504
- const applicationId = this.getApplicationId();
1505
- const audit = buildPoolTransactionAuditParameters({
1506
- applicationId,
1507
- nAuditSlots: sdk.getLayout().nAuditSlots,
1508
- ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {}
1509
- });
1510
- const deposit = {
1511
- value: parameters.coin.value,
1512
- nullifier: parameters.coin.nullifier,
1513
- ephemeralKeyScalar: scalarHexToFrDecimal2(parameters.depositScalarHex),
1514
- asset: [parameters.coin.asset_hi, parameters.coin.asset_lo],
1515
- applicationId,
1516
- recipientPublicKeys
1517
- };
1518
- const publicInput = withTokenAddressPublicInputs(
1519
- {
1520
- stateRoot,
1521
- privKeyScalar: randomFrDecimal253()
1522
- },
1523
- parameters.tokenAddress
1524
- );
1525
- const proof = await sdk.proveTransaction(
1526
- publicInput,
1527
- padPublicLegsToLayout(
1528
- sdk,
1529
- buildPublicDepositLegs(parameters.tokenAddress, parameters.coin.value)
1530
- ),
1531
- padWithdrawSlotsToLayout(sdk, []),
1532
- padDepositSlotsToLayout(sdk, [deposit]),
1533
- audit
2072
+ const sdk = await this.getInitializedSdk(
2073
+ this.feeBearingZkConfigNonce({ kind: "deposit" })
1534
2074
  );
1535
- const applicationIdsPlaintext = buildApplicationIdHints({
2075
+ return proveDepositTransact({
1536
2076
  sdk,
1537
- inputIds: [],
1538
- outputIds: [applicationId]
2077
+ applicationId: this.getApplicationId(),
2078
+ privateAddressStpl1: parameters.privateAddressStpl1,
2079
+ coin: parameters.coin,
2080
+ depositScalarHex: parameters.depositScalarHex,
2081
+ merkleRootBytes: parameters.merkleRootBytes,
2082
+ tokenAddress: parameters.tokenAddress,
2083
+ buildAlignedDepositSlot: (input) => this.buildAlignedDepositSlot(input),
2084
+ ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {},
2085
+ ...parameters.publicDepositStroops === void 0 ? {} : { publicDepositStroops: parameters.publicDepositStroops },
2086
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1539
2087
  });
1540
- return { ...proof, applicationIdsPlaintext };
1541
2088
  }
1542
2089
  async calculateNullifierHash(nullifier, privKeyScalar) {
1543
2090
  const sdk = await this.getInitializedSdk();
1544
2091
  return sdk.calculateNullifierHash(nullifier, privKeyScalar);
1545
2092
  }
1546
2093
  async prepareWithdrawTransactProof(parameters) {
1547
- const sdk = await this.getInitializedSdk();
2094
+ const sdk = await this.getInitializedSdk(
2095
+ this.feeBearingZkConfigNonce({ kind: "withdraw" })
2096
+ );
1548
2097
  return proveWithdrawTransact({
1549
2098
  sdk,
1550
2099
  applicationId: this.getApplicationId(),
1551
2100
  buildAlignedDepositSlot: (input) => this.buildAlignedDepositSlot(input),
1552
2101
  ...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {},
1553
- ...parameters
2102
+ coin: parameters.coin,
2103
+ state: parameters.state,
2104
+ destinationStellarAddress: parameters.destinationStellarAddress,
2105
+ privKeyScalarHex: parameters.privKeyScalarHex,
2106
+ depositorEphemeralKey: parameters.depositorEphemeralKey,
2107
+ withdrawAmountStroops: parameters.withdrawAmountStroops,
2108
+ changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
2109
+ tokenAddress: parameters.tokenAddress,
2110
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1554
2111
  });
1555
2112
  }
1556
2113
  async prepareWithdrawTransactProofDual(parameters) {
1557
- const sdk = await this.getInitializedSdk();
2114
+ const sdk = await this.getInitializedSdk(
2115
+ this.feeBearingZkConfigNonce({ kind: "withdraw" })
2116
+ );
1558
2117
  return proveWithdrawTransactDual({
1559
2118
  sdk,
1560
2119
  applicationId: this.getApplicationId(),
@@ -1569,7 +2128,8 @@ var PrivacyPoolService = class {
1569
2128
  ephemeralBKey: parameters.depositorEphemeralBKey,
1570
2129
  withdrawAmountStroops: parameters.withdrawAmountStroops,
1571
2130
  changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
1572
- tokenAddress: parameters.tokenAddress
2131
+ tokenAddress: parameters.tokenAddress,
2132
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
1573
2133
  });
1574
2134
  }
1575
2135
  };
@@ -1588,6 +2148,7 @@ function createPrivacyPoolService(input) {
1588
2148
  var configuredPrivacyPoolService;
1589
2149
  function configurePrivacyPoolService(service) {
1590
2150
  configuredPrivacyPoolService = service;
2151
+ registerMerkleSessionSdkHost(service);
1591
2152
  }
1592
2153
  function getPrivacyPoolService() {
1593
2154
  if (!configuredPrivacyPoolService) {
@@ -1606,7 +2167,8 @@ function poolMerkleStateToCachedView(state) {
1606
2167
  return {
1607
2168
  commitments: state.commitments,
1608
2169
  updatedAt: state.updatedAt,
1609
- merkleRootHex: state.merkleRootHex
2170
+ merkleRootHex: state.merkleRootHex,
2171
+ ...state.nodes ? { nodes: state.nodes } : {}
1610
2172
  };
1611
2173
  }
1612
2174
 
@@ -1739,6 +2301,65 @@ function reuseCachedCommitments(input) {
1739
2301
  return cachedCommitments;
1740
2302
  }
1741
2303
 
2304
+ // src/transact/merkle/persist-fetched.ts
2305
+ function cachedMerkleUnchanged(cachedState, commitments, merkleRootHex) {
2306
+ if (!cachedState) {
2307
+ return false;
2308
+ }
2309
+ if (cachedState.commitments.length !== commitments.length) {
2310
+ return false;
2311
+ }
2312
+ return cachedState.merkleRootHex === merkleRootHex;
2313
+ }
2314
+ function viewFromCachedMerkle(cachedState, merkleRootHex) {
2315
+ return {
2316
+ commitments: cachedState.commitments,
2317
+ updatedAt: cachedState.updatedAt,
2318
+ merkleRootHex: cachedState.merkleRootHex ?? merkleRootHex,
2319
+ commitmentCount: cachedState.commitments.length,
2320
+ ...cachedState.nodes ? { nodes: cachedState.nodes } : {}
2321
+ };
2322
+ }
2323
+ function merkleStateForPersist(input) {
2324
+ return {
2325
+ poolContract: input.poolContractId,
2326
+ commitments: input.commitments,
2327
+ commitmentCount: input.commitments.length,
2328
+ merkleRootHex: input.merkleRootHex,
2329
+ updatedAt: input.updatedAt,
2330
+ ...input.nodes ? { nodes: input.nodes } : {}
2331
+ };
2332
+ }
2333
+ async function persistFetchedMerkleState(input) {
2334
+ const cachedState = input.cachedState;
2335
+ if (cachedMerkleUnchanged(cachedState, input.commitments, input.merkleRootHex) && cachedState) {
2336
+ return viewFromCachedMerkle(cachedState, input.merkleRootHex);
2337
+ }
2338
+ const nodes = await resolveExtendedMerkleNodes({
2339
+ poolContract: input.poolContractId,
2340
+ commitments: input.commitments,
2341
+ merkleRootHex: input.merkleRootHex,
2342
+ ...cachedState ? { cachedState } : {}
2343
+ });
2344
+ const updatedAt = Date.now();
2345
+ await input.poolMerkleState?.set(
2346
+ merkleStateForPersist({
2347
+ poolContractId: input.poolContractId,
2348
+ commitments: input.commitments,
2349
+ merkleRootHex: input.merkleRootHex,
2350
+ updatedAt,
2351
+ ...nodes ? { nodes } : {}
2352
+ })
2353
+ );
2354
+ return {
2355
+ commitments: input.commitments,
2356
+ updatedAt,
2357
+ merkleRootHex: input.merkleRootHex,
2358
+ commitmentCount: input.commitments.length,
2359
+ ...nodes ? { nodes } : {}
2360
+ };
2361
+ }
2362
+
1742
2363
  // src/transact/merkle/fetch-contract.ts
1743
2364
  async function fetchAndMergeMerkleState(parameters) {
1744
2365
  const client = createPoolClient(parameters);
@@ -1831,43 +2452,6 @@ async function loadCommitments(input) {
1831
2452
  });
1832
2453
  return [...reused, ...fetched];
1833
2454
  }
1834
- async function persistFetchedMerkleState(input) {
1835
- const cachedState = input.cachedState;
1836
- if (cachedState && cachedMerkleUnchanged(cachedState, input.commitments, input.merkleRootHex)) {
1837
- return viewFromCachedMerkle(cachedState, input.merkleRootHex);
1838
- }
1839
- const updatedAt = Date.now();
1840
- await input.poolMerkleState?.set({
1841
- poolContract: input.poolContractId,
1842
- commitments: input.commitments,
1843
- commitmentCount: input.commitments.length,
1844
- merkleRootHex: input.merkleRootHex,
1845
- updatedAt
1846
- });
1847
- return {
1848
- commitments: input.commitments,
1849
- updatedAt,
1850
- merkleRootHex: input.merkleRootHex,
1851
- commitmentCount: input.commitments.length
1852
- };
1853
- }
1854
- function viewFromCachedMerkle(cachedState, merkleRootHex) {
1855
- return {
1856
- commitments: cachedState.commitments,
1857
- updatedAt: cachedState.updatedAt,
1858
- merkleRootHex: cachedState.merkleRootHex ?? merkleRootHex,
1859
- commitmentCount: cachedState.commitments.length
1860
- };
1861
- }
1862
- function cachedMerkleUnchanged(cachedState, commitments, merkleRootHex) {
1863
- if (!cachedState) {
1864
- return false;
1865
- }
1866
- if ((cachedState.merkleRootHex ?? merkleRootHex) !== merkleRootHex) {
1867
- return false;
1868
- }
1869
- return cachedState.commitments.join(",") === commitments.join(",");
1870
- }
1871
2455
 
1872
2456
  // src/transact/engine/prepare/shared.ts
1873
2457
  async function resolveTokenContractId2(environment, assetId) {
@@ -1942,7 +2526,11 @@ async function loadMerkleState(environment, walletPublicKey) {
1942
2526
  transactEnvironment: environment,
1943
2527
  ...environment.poolMerkleState ? { poolMerkleState: environment.poolMerkleState } : {}
1944
2528
  });
1945
- return { commitments: merged.commitments };
2529
+ return merkleSnapshotToStateFile({
2530
+ commitments: merged.commitments,
2531
+ merkleRootHex: merged.merkleRootHex,
2532
+ ...merged.nodes ? { nodes: merged.nodes } : {}
2533
+ });
1946
2534
  }
1947
2535
  function enrichPreparedOperation(prepared, outputRecords, artifacts) {
1948
2536
  return {
@@ -2157,6 +2745,13 @@ async function filterUnspentPrivateRecords(input) {
2157
2745
  }
2158
2746
  return unspent;
2159
2747
  }
2748
+ function spendAmountCoveringProtocolFee(amount) {
2749
+ if (amount <= 0n) {
2750
+ return amount;
2751
+ }
2752
+ const hundredths = (amount + 99n) / 100n;
2753
+ return amount + (hundredths === 0n ? 1n : hundredths);
2754
+ }
2160
2755
  function pickRecordsForSpendAmount(input) {
2161
2756
  let selectedTotal = 0n;
2162
2757
  const selected = [];
@@ -2210,7 +2805,7 @@ async function selectPrivateRecords(input) {
2210
2805
  return pickRecordsForSpendAmount({
2211
2806
  available,
2212
2807
  kind: input.kind,
2213
- amount: input.intent.amount
2808
+ amount: spendAmountCoveringProtocolFee(input.intent.amount)
2214
2809
  });
2215
2810
  }
2216
2811
  function sumRecordAmounts(records) {
@@ -2444,13 +3039,19 @@ var walletPrivateAddressRecordSchema = z.object({
2444
3039
  privateAddress: z.string().min(1),
2445
3040
  createdAt: z.number()
2446
3041
  });
3042
+ var leanImtNodeSchema = z.object({
3043
+ level: z.number().int().nonnegative(),
3044
+ index: z.number().int().nonnegative(),
3045
+ value: z.string().min(1)
3046
+ });
2447
3047
  var poolMerkleStateSchema = z.object({
2448
3048
  poolContract: z.string().min(1),
2449
3049
  commitments: z.array(z.string()),
2450
3050
  commitmentCount: z.number().int().nonnegative(),
2451
3051
  merkleRootHex: z.string().min(1),
2452
3052
  updatedAt: z.number(),
2453
- syncedLedger: z.number().int().optional()
3053
+ syncedLedger: z.number().int().optional(),
3054
+ nodes: z.array(leanImtNodeSchema).optional()
2454
3055
  });
2455
3056
  var leafEphemeralSchema = z.object({
2456
3057
  poolContract: z.string().min(1),
@@ -4009,6 +4610,7 @@ function createPoolMerkleService(context) {
4009
4610
  async appendPoolCommitments(input) {
4010
4611
  const current = await this.getPoolMerkleState(input.poolContract);
4011
4612
  const mergedCommitments = [...current?.commitments ?? [], ...input.commitments];
4613
+ const nodes = input.nodes ?? (input.commitments.length === 0 ? current?.nodes : void 0);
4012
4614
  await this.setPoolMerkleState(
4013
4615
  withoutUndefinedFields({
4014
4616
  poolContract: input.poolContract,
@@ -4016,7 +4618,8 @@ function createPoolMerkleService(context) {
4016
4618
  commitmentCount: mergedCommitments.length,
4017
4619
  merkleRootHex: input.merkleRootHex,
4018
4620
  updatedAt: Date.now(),
4019
- syncedLedger: input.syncedLedger ?? current?.syncedLedger
4621
+ syncedLedger: input.syncedLedger ?? current?.syncedLedger,
4622
+ nodes
4020
4623
  })
4021
4624
  );
4022
4625
  }
@@ -5286,12 +5889,46 @@ function createDeliveriesStateDelegates(state) {
5286
5889
  };
5287
5890
  }
5288
5891
 
5892
+ // src/transact/merkle/write-snapshot.ts
5893
+ async function writePoolMerkleState(input) {
5894
+ await input.setPoolMerkleState(input.state);
5895
+ await bindPoolMerkleTree(input.state);
5896
+ }
5897
+ async function appendPoolMerkleCommitments(input) {
5898
+ const current = await input.getPoolMerkleState(input.payload.poolContract);
5899
+ const merged = [...current?.commitments ?? [], ...input.payload.commitments];
5900
+ const nodes = input.payload.nodes ?? await resolveExtendedMerkleNodes({
5901
+ poolContract: input.payload.poolContract,
5902
+ commitments: merged,
5903
+ merkleRootHex: input.payload.merkleRootHex,
5904
+ ...current ? { cachedState: poolMerkleStateToCachedView(current) } : {}
5905
+ });
5906
+ await input.appendPoolCommitments({
5907
+ poolContract: input.payload.poolContract,
5908
+ commitments: input.payload.commitments,
5909
+ merkleRootHex: input.payload.merkleRootHex,
5910
+ ...input.payload.syncedLedger === void 0 ? {} : { syncedLedger: input.payload.syncedLedger },
5911
+ ...nodes ? { nodes } : {}
5912
+ });
5913
+ const written = await input.getPoolMerkleState(input.payload.poolContract);
5914
+ if (written) {
5915
+ await bindPoolMerkleTree(written);
5916
+ }
5917
+ }
5918
+
5289
5919
  // src/client/state-delegates/pool.ts
5290
5920
  function createPoolStateDelegates(state) {
5291
5921
  return {
5292
5922
  getPoolMerkleState: (poolContract) => state.getPoolMerkleState(poolContract),
5293
- setPoolMerkleState: (poolState) => state.setPoolMerkleState(poolState),
5294
- appendPoolCommitments: (input) => state.appendPoolCommitments(input),
5923
+ setPoolMerkleState: (poolState) => writePoolMerkleState({
5924
+ setPoolMerkleState: (next) => state.setPoolMerkleState(next),
5925
+ state: poolState
5926
+ }),
5927
+ appendPoolCommitments: (input) => appendPoolMerkleCommitments({
5928
+ getPoolMerkleState: (poolContract) => state.getPoolMerkleState(poolContract),
5929
+ appendPoolCommitments: (payload) => state.appendPoolCommitments(payload),
5930
+ payload: input
5931
+ }),
5295
5932
  getLeafEphemeral: (input) => state.getLeafEphemeral(input),
5296
5933
  setLeafEphemeral: (ephemeral) => state.setLeafEphemeral(ephemeral)
5297
5934
  };
@@ -5344,12 +5981,42 @@ function createStateDelegates(state) {
5344
5981
  };
5345
5982
  }
5346
5983
 
5984
+ // src/client/hydrate-state.ts
5985
+ async function hydratePoolMerkleStates(client, states) {
5986
+ for (const poolState of states ?? []) {
5987
+ await client.setPoolMerkleState(poolState);
5988
+ }
5989
+ }
5990
+ async function hydrateWalletAddresses(client, snapshot) {
5991
+ for (const record of snapshot.walletPrivateAddressRecords ?? []) {
5992
+ await client.saveWalletPrivateAddressRecord(record);
5993
+ }
5994
+ for (const scalar of snapshot.walletPrivateAddressScalars ?? []) {
5995
+ await client.saveWalletPrivateAddressScalar(scalar);
5996
+ }
5997
+ for (const entry of snapshot.walletDefaultPrivateAddressNonces ?? []) {
5998
+ await client.setWalletDefaultPrivateAddressNonce(entry);
5999
+ }
6000
+ }
6001
+ async function hydrateStellarPrivacyState(client, snapshot) {
6002
+ if (snapshot.assets && snapshot.assets.length > 0) {
6003
+ await client.replaceAssets(snapshot.assets);
6004
+ }
6005
+ await hydratePoolMerkleStates(client, snapshot.poolMerkleStates);
6006
+ if (snapshot.privateRecords && snapshot.privateRecords.length > 0) {
6007
+ await client.upsertPrivateRecords(snapshot.privateRecords);
6008
+ }
6009
+ await hydrateWalletAddresses(client, snapshot);
6010
+ }
6011
+
5347
6012
  // src/client/client.ts
5348
6013
  function createStellarPrivacyClientInstance(config) {
5349
6014
  const composed = composeStellarPrivacyClient(config);
6015
+ const stateDelegates = createStateDelegates(composed.state);
5350
6016
  return {
5351
6017
  state: composed.state,
5352
- ...createStateDelegates(composed.state),
6018
+ ...stateDelegates,
6019
+ hydrateState: (snapshot) => hydrateStellarPrivacyState(stateDelegates, snapshot),
5353
6020
  deposit: (intent) => composed.operations.deposit(intent),
5354
6021
  transfer: (intent) => composed.operations.transfer(intent),
5355
6022
  withdraw: (intent) => composed.operations.withdraw(intent),
@@ -5465,7 +6132,7 @@ function createStellarPrivacyClientFromResolvedConfig(config) {
5465
6132
  }
5466
6133
 
5467
6134
  // src/transact/engine/default-factory.ts
5468
- import { PrivacyPoolSDK as PrivacyPoolSDK3 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
6135
+ import { PrivacyPoolSDK as PrivacyPoolSDK4 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
5469
6136
 
5470
6137
  // src/transact/pool/proof-types.ts
5471
6138
  function ciphertextArtifactsFromProof(proof) {
@@ -5480,9 +6147,15 @@ function ciphertextArtifactsFromProof(proof) {
5480
6147
  // src/transact/engine/prepare/deposit.ts
5481
6148
  var STROOPS_PER_UNIT = 10000000n;
5482
6149
  async function createAlignedDepositWithProof(parameters) {
6150
+ const feeOutput = await quoteFeeOutputForPrepared(parameters);
6151
+ const instructed = parameters.prepared.intent.amount;
6152
+ const userAmount = instructed - (feeOutput?.requiredFee ?? 0n);
6153
+ if (userAmount <= 0n) {
6154
+ throw new Error("Required Fee leaves no spendable deposit note");
6155
+ }
5483
6156
  const aligned = await parameters.poolService.createAlignedShieldCoinData({
5484
6157
  privateAddressStpl1: parameters.prepared.intent.to,
5485
- amountStroops: parameters.prepared.intent.amount,
6158
+ amountStroops: userAmount,
5486
6159
  tokenAddress: parameters.tokenAddress
5487
6160
  });
5488
6161
  const poolClient = readPoolClientFactory(parameters.environment)({
@@ -5497,7 +6170,9 @@ async function createAlignedDepositWithProof(parameters) {
5497
6170
  coin: aligned.coin,
5498
6171
  depositScalarHex: aligned.depositScalarHex,
5499
6172
  merkleRootBytes: merkleTx.result,
5500
- tokenAddress: parameters.tokenAddress
6173
+ tokenAddress: parameters.tokenAddress,
6174
+ publicDepositStroops: instructed,
6175
+ ...feeOutput ? { feeOutput } : {}
5501
6176
  });
5502
6177
  return { ...aligned, proof };
5503
6178
  }
@@ -5516,8 +6191,8 @@ async function prepareDepositOperation(prepared, environment, poolService) {
5516
6191
  privateAddress: prepared.intent.to,
5517
6192
  assetId: prepared.intent.asset,
5518
6193
  poolContract: environment.network.poolContract,
5519
- amount: prepared.intent.amount,
5520
- amountDisplay: Number(prepared.intent.amount) / Number(STROOPS_PER_UNIT),
6194
+ amount: BigInt(depositProof.coin.value),
6195
+ amountDisplay: Number(depositProof.coin.value) / Number(STROOPS_PER_UNIT),
5521
6196
  commitmentHex: depositProof.commitment_hex,
5522
6197
  coin: depositProof.coin,
5523
6198
  depositScalarHex: depositProof.depositScalarHex,
@@ -5533,7 +6208,11 @@ async function prepareDepositOperation(prepared, environment, poolService) {
5533
6208
  depositScalarHex: depositProof.depositScalarHex,
5534
6209
  precommitementHex: depositProof.precommitementHex,
5535
6210
  commitmentHex: depositProof.commitment_hex,
5536
- walletPublicKey
6211
+ walletPublicKey,
6212
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
6213
+ kind: "deposit",
6214
+ ...configuredNonceSpread(environment.zkConfigNonce)
6215
+ })
5537
6216
  });
5538
6217
  }
5539
6218
 
@@ -5658,7 +6337,8 @@ async function buildSpendProofContextAtExecute(input) {
5658
6337
  input.prepared.intent.asset
5659
6338
  );
5660
6339
  const coin = requireCoinNoteFromRecord(primaryRecord);
5661
- const { commitments } = await loadMerkleState(input.environment, walletPublicKey);
6340
+ const merkleState = await loadMerkleState(input.environment, walletPublicKey);
6341
+ const commitments = merkleState.commitments;
5662
6342
  const leafIndex = findCommitmentLeafIndex(commitments, coin.commitment);
5663
6343
  if (leafIndex < 0) {
5664
6344
  throw new Error("Coin commitment not found in on-chain Merkle state.");
@@ -5675,6 +6355,7 @@ async function buildSpendProofContextAtExecute(input) {
5675
6355
  tokenAddress,
5676
6356
  coin,
5677
6357
  commitments,
6358
+ merkleState,
5678
6359
  ephemeral,
5679
6360
  senderPrivKeyScalarHex,
5680
6361
  recipientPrivateAddressStpl1: input.recipientPrivateAddressStpl1
@@ -5707,9 +6388,6 @@ function escrowOutputFields(input) {
5707
6388
  recipientLo: input.escrowSend.recipientLo
5708
6389
  };
5709
6390
  }
5710
- function paddingDepositPair(recipientDeposit) {
5711
- return [recipientDeposit, "dummy"];
5712
- }
5713
6391
  async function buildChangeDepositPair(parameters) {
5714
6392
  const changeSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
5715
6393
  privateAddressStpl1: parameters.selfPrivateAddressStpl1ForChange.trim(),
@@ -5726,34 +6404,54 @@ async function buildChangeDepositPair(parameters) {
5726
6404
  }
5727
6405
  };
5728
6406
  }
6407
+ function recipientOnlyDeposits(recipientDeposit, hasFeeOutput) {
6408
+ if (hasFeeOutput) {
6409
+ return [recipientDeposit];
6410
+ }
6411
+ return [recipientDeposit, "dummy"];
6412
+ }
6413
+ async function depositsWithoutFee(input) {
6414
+ if (input.changeStroops <= ZERO_STROOPS) {
6415
+ return {
6416
+ deposits: recipientOnlyDeposits(input.recipientDeposit, input.hasFeeOutput)
6417
+ };
6418
+ }
6419
+ return buildChangeDepositPair({
6420
+ selfPrivateAddressStpl1ForChange: input.selfPrivateAddressStpl1ForChange,
6421
+ changeStroops: input.changeStroops,
6422
+ tokenAddress: input.tokenAddress,
6423
+ recipientDeposit: input.recipientDeposit
6424
+ });
6425
+ }
5729
6426
  async function buildRecipientAndOptionalChangeDeposits(parameters) {
5730
6427
  const escrow = {
5731
6428
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
5732
6429
  ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
5733
6430
  };
5734
- const recipientPrivateAddress = parameters.recipientPrivateAddressStpl1.trim();
5735
6431
  const recipientSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
5736
- privateAddressStpl1: recipientPrivateAddress,
6432
+ privateAddressStpl1: parameters.recipientPrivateAddressStpl1.trim(),
5737
6433
  amountStroops: parameters.transferStroops,
5738
6434
  tokenAddress: parameters.tokenAddress,
5739
6435
  ...escrowOutputFields(escrow)
5740
6436
  });
5741
- if (parameters.changeStroops <= ZERO_STROOPS) {
5742
- return {
5743
- recipientSlot,
5744
- deposits: paddingDepositPair(recipientSlot.deposit)
5745
- };
5746
- }
5747
- const changeResult = await buildChangeDepositPair({
5748
- selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
6437
+ const withoutFee = await depositsWithoutFee({
6438
+ recipientDeposit: recipientSlot.deposit,
5749
6439
  changeStroops: parameters.changeStroops,
6440
+ selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5750
6441
  tokenAddress: parameters.tokenAddress,
5751
- recipientDeposit: recipientSlot.deposit
6442
+ hasFeeOutput: (parameters.feeOutput?.requiredFee ?? ZERO_STROOPS) > ZERO_STROOPS
6443
+ });
6444
+ const withFee = await appendFeeOutputSlot({
6445
+ deposits: withoutFee.deposits,
6446
+ tokenAddress: parameters.tokenAddress,
6447
+ buildAlignedDepositSlot: (aligned) => getPrivacyPoolService().buildAlignedDepositSlot(aligned),
6448
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5752
6449
  });
5753
6450
  return {
5754
6451
  recipientSlot,
5755
- deposits: changeResult.deposits,
5756
- changeCoin: changeResult.changeCoin
6452
+ deposits: withFee.deposits,
6453
+ ..."changeCoin" in withoutFee ? { changeCoin: withoutFee.changeCoin } : {},
6454
+ ...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
5757
6455
  };
5758
6456
  }
5759
6457
 
@@ -5806,14 +6504,15 @@ function sweepOutputOwnerFields(parameters) {
5806
6504
  return { sweepOutputOwnerPubX, sweepOutputOwnerPubY };
5807
6505
  }
5808
6506
  async function buildTransferDepositsAndPublicInput(parameters) {
5809
- const { recipientSlot, deposits, changeCoin } = await buildRecipientAndOptionalChangeDeposits({
6507
+ const { recipientSlot, deposits, changeCoin, feeCoin } = await buildRecipientAndOptionalChangeDeposits({
5810
6508
  recipientPrivateAddressStpl1: parameters.recipientPrivateAddressStpl1,
5811
6509
  transferStroops: parameters.transferStroops,
5812
6510
  changeStroops: parameters.changeStroops,
5813
6511
  selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5814
6512
  tokenAddress: parameters.tokenAddress,
5815
6513
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
5816
- ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
6514
+ ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
6515
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5817
6516
  });
5818
6517
  const publicInput = withTokenAddressPublicInputs(
5819
6518
  {
@@ -5828,6 +6527,7 @@ async function buildTransferDepositsAndPublicInput(parameters) {
5828
6527
  recipientSlot,
5829
6528
  deposits,
5830
6529
  ...changeCoin ? { changeCoin } : {},
6530
+ ...feeCoin ? { feeCoin } : {},
5831
6531
  publicInput
5832
6532
  };
5833
6533
  }
@@ -5843,7 +6543,8 @@ async function buildSenderTransferDepositsAndPublicInput(parameters) {
5843
6543
  parameters.senderPrivKeyScalarHex
5844
6544
  ),
5845
6545
  ...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
5846
- ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
6546
+ ...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
6547
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5847
6548
  });
5848
6549
  }
5849
6550
 
@@ -5867,11 +6568,20 @@ function stampDualWithdrawLegs(legs, parameters) {
5867
6568
  }
5868
6569
  };
5869
6570
  }
5870
- function buildDualTransferApplicationIds(sdk, applicationId, changeCoin) {
6571
+ function buildDualTransferApplicationIds(sdk, applicationId, changeCoin, feeCoin) {
6572
+ const outputIds = [applicationId];
6573
+ if (changeCoin) {
6574
+ outputIds.push(applicationId);
6575
+ } else if (!feeCoin) {
6576
+ outputIds.push("0");
6577
+ }
6578
+ if (feeCoin) {
6579
+ outputIds.push(applicationId);
6580
+ }
5871
6581
  return buildApplicationIdHints({
5872
6582
  sdk,
5873
6583
  inputIds: [applicationId, applicationId],
5874
- outputIds: [applicationId, changeCoin ? applicationId : "0"]
6584
+ outputIds
5875
6585
  });
5876
6586
  }
5877
6587
  async function proveDualConfidentialTransfer(parameters) {
@@ -5891,16 +6601,24 @@ async function proveDualConfidentialTransfer(parameters) {
5891
6601
  audit
5892
6602
  );
5893
6603
  }
5894
- async function buildDualTransferProofContext(parameters, applicationId, sdk) {
6604
+ function dualTransferChangeStroops(parameters) {
5895
6605
  const totalNotes = BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value);
5896
- const changeStroops = changeStroopsAfterDualTransfer(
5897
- parameters.transferStroops,
5898
- totalNotes
5899
- );
6606
+ if (parameters.transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
6607
+ throw new Error("Transfer amount must be positive");
6608
+ }
6609
+ const changeStroops = remainingAfterRequiredFee({
6610
+ available: totalNotes,
6611
+ instructed: parameters.transferStroops,
6612
+ requiredFee: parameters.feeOutput?.requiredFee ?? 0n
6613
+ });
5900
6614
  requireChangeRecipientWhenPartial(
5901
6615
  changeStroops,
5902
6616
  parameters.selfPrivateAddressStpl1ForChange
5903
6617
  );
6618
+ return changeStroops;
6619
+ }
6620
+ async function buildDualTransferProofContext(parameters, applicationId, sdk) {
6621
+ const changeStroops = dualTransferChangeStroops(parameters);
5904
6622
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
5905
6623
  parameters.senderPrivKeyScalarHex
5906
6624
  );
@@ -5927,15 +6645,21 @@ async function buildDualTransferProofContext(parameters, applicationId, sdk) {
5927
6645
  selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
5928
6646
  tokenAddress: parameters.tokenAddress,
5929
6647
  stateRoot: legA.witness.stateRoot,
5930
- ...confidentialEscrowFields(parameters)
6648
+ ...confidentialEscrowFields(parameters),
6649
+ ...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
5931
6650
  });
5932
6651
  return { legA, legB, applicationId, ...transferInputs };
5933
6652
  }
5934
6653
  async function prepareConfidentialTransferProofDual(parameters) {
5935
6654
  const poolService = getPrivacyPoolService();
5936
- const sdk = await poolService.getInitializedSdk();
6655
+ const sdk = await poolService.getInitializedSdk(
6656
+ poolService.feeBearingZkConfigNonce({
6657
+ kind: "transfer",
6658
+ ...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
6659
+ })
6660
+ );
5937
6661
  const applicationId = poolService.getApplicationId();
5938
- const { legA, legB, recipientSlot, deposits, changeCoin, publicInput } = await buildDualTransferProofContext(parameters, applicationId, sdk);
6662
+ const { legA, legB, recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildDualTransferProofContext(parameters, applicationId, sdk);
5939
6663
  const auditPublicKey = poolService.getAuditPublicKey();
5940
6664
  const proof = await (auditPublicKey ? proveDualConfidentialTransfer({
5941
6665
  sdk,
@@ -5958,7 +6682,8 @@ async function prepareConfidentialTransferProofDual(parameters) {
5958
6682
  applicationIdsPlaintext: buildDualTransferApplicationIds(
5959
6683
  sdk,
5960
6684
  applicationId,
5961
- changeCoin
6685
+ changeCoin,
6686
+ feeCoin
5962
6687
  ),
5963
6688
  recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
5964
6689
  ...changeCoin ? { changeCoin } : {}
@@ -5966,22 +6691,32 @@ async function prepareConfidentialTransferProofDual(parameters) {
5966
6691
  }
5967
6692
 
5968
6693
  // src/transact/proofs/confidential/single.ts
5969
- function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange) {
6694
+ function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange, requiredFee) {
5970
6695
  if (transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
5971
6696
  throw new Error("Transfer amount must be positive");
5972
6697
  }
5973
- if (transferStroops > noteStroops) {
5974
- throw new Error("Transfer amount exceeds note value");
5975
- }
5976
- const changeStroops = noteStroops - transferStroops;
6698
+ const changeStroops = remainingAfterRequiredFee({
6699
+ available: noteStroops,
6700
+ instructed: transferStroops,
6701
+ requiredFee
6702
+ });
5977
6703
  requireChangeRecipientWhenPartial(changeStroops, selfPrivateAddressStpl1ForChange);
5978
6704
  return changeStroops;
5979
6705
  }
5980
- function buildSingleTransferApplicationIds(sdk, applicationId, changeCoin) {
6706
+ function buildSingleTransferApplicationIds(sdk, applicationId, changeCoin, feeCoin) {
6707
+ const outputIds = [applicationId];
6708
+ if (changeCoin) {
6709
+ outputIds.push(applicationId);
6710
+ } else if (!feeCoin) {
6711
+ outputIds.push("0");
6712
+ }
6713
+ if (feeCoin) {
6714
+ outputIds.push(applicationId);
6715
+ }
5981
6716
  return buildApplicationIdHints({
5982
6717
  sdk,
5983
6718
  inputIds: [applicationId],
5984
- outputIds: [applicationId, changeCoin ? applicationId : "0"]
6719
+ outputIds
5985
6720
  });
5986
6721
  }
5987
6722
  function buildTransferAuditParameters(parameters) {
@@ -6007,11 +6742,25 @@ function confidentialEscrowFields2(input) {
6007
6742
  ...input.escrowClaimantLimbs ? { escrowClaimantLimbs: input.escrowClaimantLimbs } : {}
6008
6743
  };
6009
6744
  }
6745
+ function transferDepositInputFromProof(input, changeStroops, stateRoot) {
6746
+ return {
6747
+ senderPrivKeyScalarHex: input.senderPrivKeyScalarHex,
6748
+ recipientPrivateAddressStpl1: input.recipientPrivateAddressStpl1,
6749
+ transferStroops: input.transferStroops,
6750
+ changeStroops,
6751
+ selfPrivateAddressStpl1ForChange: input.selfPrivateAddressStpl1ForChange,
6752
+ tokenAddress: input.tokenAddress,
6753
+ stateRoot,
6754
+ ...confidentialEscrowFields2(input),
6755
+ ...input.feeOutput ? { feeOutput: input.feeOutput } : {}
6756
+ };
6757
+ }
6010
6758
  async function buildTransferProofInputs(parameters) {
6011
6759
  const changeStroops = validateSingleCoinTransferAmounts(
6012
6760
  parameters.input.transferStroops,
6013
6761
  parameters.noteStroops,
6014
- parameters.input.selfPrivateAddressStpl1ForChange
6762
+ parameters.input.selfPrivateAddressStpl1ForChange,
6763
+ parameters.input.feeOutput?.requiredFee ?? 0n
6015
6764
  );
6016
6765
  const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
6017
6766
  parameters.input.senderPrivKeyScalarHex
@@ -6030,31 +6779,30 @@ async function buildTransferProofInputs(parameters) {
6030
6779
  withdrawObject,
6031
6780
  sweepWithdrawStamp(confidentialEscrowFields2(parameters.input))
6032
6781
  );
6033
- const { recipientSlot, deposits, changeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput({
6034
- senderPrivKeyScalarHex: parameters.input.senderPrivKeyScalarHex,
6035
- recipientPrivateAddressStpl1: parameters.input.recipientPrivateAddressStpl1,
6036
- transferStroops: parameters.input.transferStroops,
6037
- changeStroops,
6038
- selfPrivateAddressStpl1ForChange: parameters.input.selfPrivateAddressStpl1ForChange,
6039
- tokenAddress: parameters.input.tokenAddress,
6040
- stateRoot: witness.stateRoot,
6041
- ...confidentialEscrowFields2(parameters.input)
6042
- });
6782
+ const { recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput(
6783
+ transferDepositInputFromProof(parameters.input, changeStroops, witness.stateRoot)
6784
+ );
6043
6785
  return {
6044
6786
  witness,
6045
6787
  withdrawObject: stampedWithdraw,
6046
6788
  recipientSlot,
6047
6789
  deposits,
6048
6790
  changeCoin,
6791
+ feeCoin,
6049
6792
  publicInput
6050
6793
  };
6051
6794
  }
6052
6795
  async function prepareConfidentialTransferProof(parameters) {
6053
6796
  const poolService = getPrivacyPoolService();
6054
- const sdk = await poolService.getInitializedSdk();
6797
+ const sdk = await poolService.getInitializedSdk(
6798
+ poolService.feeBearingZkConfigNonce({
6799
+ kind: "transfer",
6800
+ ...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
6801
+ })
6802
+ );
6055
6803
  const applicationId = poolService.getApplicationId();
6056
6804
  const noteStroops = BigInt(parameters.coin.value);
6057
- const { withdrawObject, recipientSlot, deposits, changeCoin, publicInput } = await buildTransferProofInputs({
6805
+ const { withdrawObject, recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildTransferProofInputs({
6058
6806
  sdk,
6059
6807
  applicationId,
6060
6808
  input: parameters,
@@ -6077,7 +6825,8 @@ async function prepareConfidentialTransferProof(parameters) {
6077
6825
  applicationIdsPlaintext: buildSingleTransferApplicationIds(
6078
6826
  sdk,
6079
6827
  applicationId,
6080
- changeCoin
6828
+ changeCoin,
6829
+ feeCoin
6081
6830
  ),
6082
6831
  recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
6083
6832
  ...changeCoin ? { changeCoin } : {}
@@ -6100,10 +6849,15 @@ function proofEscrowFields(input) {
6100
6849
  }
6101
6850
  async function buildSingleTransferProofAtExecute(input) {
6102
6851
  const context = await buildSpendProofContextAtExecute(input);
6103
- const changeStroops = BigInt(context.coin.value) - input.prepared.intent.amount;
6852
+ const feeOutput = await quoteFeeOutputForPrepared({
6853
+ prepared: input.prepared,
6854
+ environment: input.environment,
6855
+ tokenAddress: context.tokenAddress
6856
+ });
6857
+ const changeStroops = BigInt(context.coin.value) - input.prepared.intent.amount - (feeOutput?.requiredFee ?? 0n);
6104
6858
  const proof = await prepareConfidentialTransferProof({
6105
6859
  coin: context.coin,
6106
- state: { commitments: context.commitments },
6860
+ state: context.merkleState,
6107
6861
  senderPrivKeyScalarHex: context.senderPrivKeyScalarHex,
6108
6862
  depositorEphemeralKey: serializeEphemeralKeyString({
6109
6863
  xHex: context.ephemeral.xHex,
@@ -6113,7 +6867,8 @@ async function buildSingleTransferProofAtExecute(input) {
6113
6867
  recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
6114
6868
  selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
6115
6869
  tokenAddress: context.tokenAddress,
6116
- ...proofEscrowFields(input)
6870
+ ...proofEscrowFields(input),
6871
+ ...feeOutput ? { feeOutput } : {}
6117
6872
  });
6118
6873
  return { context, proof };
6119
6874
  }
@@ -6130,11 +6885,16 @@ async function buildDualTransferProofAtExecute(input) {
6130
6885
  commitments: context.commitments
6131
6886
  });
6132
6887
  const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
6133
- const changeStroops = totalNotes - input.prepared.intent.amount;
6888
+ const feeOutput = await quoteFeeOutputForPrepared({
6889
+ prepared: input.prepared,
6890
+ environment: input.environment,
6891
+ tokenAddress: context.tokenAddress
6892
+ });
6893
+ const changeStroops = totalNotes - input.prepared.intent.amount - (feeOutput?.requiredFee ?? 0n);
6134
6894
  const proof = await prepareConfidentialTransferProofDual({
6135
6895
  coinA: context.coin,
6136
6896
  coinB: secondary.coin,
6137
- state: { commitments: context.commitments },
6897
+ state: context.merkleState,
6138
6898
  senderPrivKeyScalarHex: context.senderPrivKeyScalarHex,
6139
6899
  ephemeralAKey: serializeEphemeralKeyString({
6140
6900
  xHex: context.ephemeral.xHex,
@@ -6148,7 +6908,8 @@ async function buildDualTransferProofAtExecute(input) {
6148
6908
  recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
6149
6909
  selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
6150
6910
  tokenAddress: context.tokenAddress,
6151
- ...proofEscrowFields(input)
6911
+ ...proofEscrowFields(input),
6912
+ ...feeOutput ? { feeOutput } : {}
6152
6913
  });
6153
6914
  return { context, proof };
6154
6915
  }
@@ -6280,11 +7041,17 @@ async function runTransferFinalizeSteps(input) {
6280
7041
  walletPublicKey: input.walletPublicKey,
6281
7042
  ...recipient.escrowSend ? { escrowSendPrivateAddress: recipient.recipientPrivateAddressStpl1 } : {}
6282
7043
  });
6283
- return buildTransferFinalizeArtifacts({
6284
- proof,
6285
- context,
6286
- ...sweepFinalizeStamp(input.prepared, recipient)
6287
- });
7044
+ return {
7045
+ ...buildTransferFinalizeArtifacts({
7046
+ proof,
7047
+ context,
7048
+ ...sweepFinalizeStamp(input.prepared, recipient)
7049
+ }),
7050
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
7051
+ ...feeOutputKindFromPrepared(input.prepared),
7052
+ ...configuredNonceSpread(input.environment.zkConfigNonce)
7053
+ })
7054
+ };
6288
7055
  }
6289
7056
  function sweepArtifactStamp(artifacts) {
6290
7057
  return {
@@ -6312,6 +7079,24 @@ async function finalizeTransferAtExecute(prepared, environment) {
6312
7079
  return runTransferFinalizeSteps({ prepared, environment, walletPublicKey });
6313
7080
  }
6314
7081
 
7082
+ // src/transact/engine/withdraw-fee.ts
7083
+ async function withdrawFeeProofFields(input) {
7084
+ const feeOutput = await quoteFeeOutputForPrepared({
7085
+ prepared: input.prepared,
7086
+ environment: input.environment,
7087
+ tokenAddress: input.tokenAddress
7088
+ });
7089
+ const changeStroops = remainingAfterRequiredFee({
7090
+ available: input.available,
7091
+ instructed: input.prepared.intent.amount,
7092
+ requiredFee: feeOutput?.requiredFee ?? 0n
7093
+ });
7094
+ return {
7095
+ ...feeOutput ? { feeOutput } : {},
7096
+ ...changeStroops > 0n ? { changePrivateAddressStpl1: input.withdrawFrom } : {}
7097
+ };
7098
+ }
7099
+
6315
7100
  // src/transact/engine/execute.ts
6316
7101
  function enrichWithdrawOutputRecords(prepared, proof) {
6317
7102
  if (!proof.changeCoin || prepared.outputRecords.length === 0) {
@@ -6327,7 +7112,7 @@ function enrichWithdrawOutputRecords(prepared, proof) {
6327
7112
  precommitementHex: changeCoin.precommitementHex
6328
7113
  }));
6329
7114
  }
6330
- function buildWithdrawFinalizeArtifacts(proof, context) {
7115
+ function buildWithdrawFinalizeArtifacts(proof, context, environment) {
6331
7116
  return {
6332
7117
  proofHex: proof.proof_hex,
6333
7118
  publicHex: proof.public_hex,
@@ -6335,7 +7120,11 @@ function buildWithdrawFinalizeArtifacts(proof, context) {
6335
7120
  applicationIdsPlaintext: proof.applicationIdsPlaintext,
6336
7121
  tokenAddress: context.tokenAddress,
6337
7122
  walletPublicKey: context.walletPublicKey,
6338
- executeFinalizeRequired: false
7123
+ executeFinalizeRequired: false,
7124
+ zkConfigNonce: zkConfigNonceForFeeBearingKind({
7125
+ kind: "withdraw",
7126
+ ...configuredNonceSpread(environment.zkConfigNonce)
7127
+ })
6339
7128
  };
6340
7129
  }
6341
7130
  async function finalizeSingleWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
@@ -6344,10 +7133,16 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
6344
7133
  environment,
6345
7134
  recipientPrivateAddressStpl1: withdrawFrom
6346
7135
  });
6347
- const changeStroops = BigInt(context.coin.value) - prepared.intent.amount;
7136
+ const feeFields = await withdrawFeeProofFields({
7137
+ prepared,
7138
+ environment,
7139
+ tokenAddress: context.tokenAddress,
7140
+ available: BigInt(context.coin.value),
7141
+ withdrawFrom
7142
+ });
6348
7143
  const proof = await poolService.prepareWithdrawTransactProof({
6349
7144
  coin: context.coin,
6350
- state: { commitments: context.commitments },
7145
+ state: context.merkleState,
6351
7146
  destinationStellarAddress: prepared.intent.to,
6352
7147
  privKeyScalarHex: context.senderPrivKeyScalarHex,
6353
7148
  depositorEphemeralKey: serializeEphemeralKeyString({
@@ -6355,11 +7150,24 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
6355
7150
  yHex: context.ephemeral.yHex
6356
7151
  }),
6357
7152
  withdrawAmountStroops: prepared.intent.amount,
6358
- changePrivateAddressStpl1: changeStroops > 0n ? withdrawFrom : void 0,
6359
- tokenAddress: context.tokenAddress
7153
+ changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
7154
+ tokenAddress: context.tokenAddress,
7155
+ ...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
6360
7156
  });
6361
7157
  enrichWithdrawOutputRecords(prepared, proof);
6362
- return buildWithdrawFinalizeArtifacts(proof, context);
7158
+ return buildWithdrawFinalizeArtifacts(proof, context, environment);
7159
+ }
7160
+ function dualWithdrawEphemeralKeys(input) {
7161
+ return {
7162
+ depositorEphemeralAKey: serializeEphemeralKeyString({
7163
+ xHex: input.context.ephemeral.xHex,
7164
+ yHex: input.context.ephemeral.yHex
7165
+ }),
7166
+ depositorEphemeralBKey: serializeEphemeralKeyString({
7167
+ xHex: input.secondary.ephemeral.xHex,
7168
+ yHex: input.secondary.ephemeral.yHex
7169
+ })
7170
+ };
6363
7171
  }
6364
7172
  async function finalizeDualWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
6365
7173
  const [, secondaryRecord] = prepared.consumedRecords;
@@ -6377,28 +7185,27 @@ async function finalizeDualWithdrawAtExecute(prepared, environment, poolService,
6377
7185
  walletPublicKey: context.walletPublicKey,
6378
7186
  commitments: context.commitments
6379
7187
  });
6380
- const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
6381
- const changeStroops = totalNotes - prepared.intent.amount;
7188
+ const feeFields = await withdrawFeeProofFields({
7189
+ prepared,
7190
+ environment,
7191
+ tokenAddress: context.tokenAddress,
7192
+ available: BigInt(context.coin.value) + BigInt(secondary.coin.value),
7193
+ withdrawFrom
7194
+ });
6382
7195
  const proof = await poolService.prepareWithdrawTransactProofDual({
6383
7196
  coinA: context.coin,
6384
7197
  coinB: secondary.coin,
6385
- state: { commitments: context.commitments },
7198
+ state: context.merkleState,
6386
7199
  destinationStellarAddress: prepared.intent.to,
6387
7200
  privKeyScalarHex: context.senderPrivKeyScalarHex,
6388
- depositorEphemeralAKey: serializeEphemeralKeyString({
6389
- xHex: context.ephemeral.xHex,
6390
- yHex: context.ephemeral.yHex
6391
- }),
6392
- depositorEphemeralBKey: serializeEphemeralKeyString({
6393
- xHex: secondary.ephemeral.xHex,
6394
- yHex: secondary.ephemeral.yHex
6395
- }),
7201
+ ...dualWithdrawEphemeralKeys({ context, secondary }),
6396
7202
  withdrawAmountStroops: prepared.intent.amount,
6397
- changePrivateAddressStpl1: changeStroops > 0n ? withdrawFrom : void 0,
6398
- tokenAddress: context.tokenAddress
7203
+ changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
7204
+ tokenAddress: context.tokenAddress,
7205
+ ...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
6399
7206
  });
6400
7207
  enrichWithdrawOutputRecords(prepared, proof);
6401
- return buildWithdrawFinalizeArtifacts(proof, context);
7208
+ return buildWithdrawFinalizeArtifacts(proof, context, environment);
6402
7209
  }
6403
7210
  async function finalizeWithdrawAtExecute(prepared, environment, poolService) {
6404
7211
  if (prepared.kind !== "withdraw") {
@@ -6660,7 +7467,7 @@ async function requestKytPassageForPoolInteraction(input) {
6660
7467
  const applicationId = resolvePoolApplicationId(
6661
7468
  input.transactEnvironment.network.applicationId
6662
7469
  );
6663
- const zkConfigNonce = resolveZkConfigNonce(input.transactEnvironment);
7470
+ const zkConfigNonce = input.zkConfigNonce ?? resolveZkConfigNonce(input.transactEnvironment);
6664
7471
  const applicationIdsPlaintext = input.applicationIdsPlaintext ?? buildApplicationIdHints2(
6665
7472
  applicationId,
6666
7473
  layoutForKnownNonce(zkConfigNonce).nAuditSlots
@@ -6832,6 +7639,10 @@ async function submitSorobanConfidentialTransfer(input) {
6832
7639
  networkPassphrase: input.networkPassphrase,
6833
7640
  sorobanRpcUrl: input.sorobanRpcUrl
6834
7641
  });
7642
+ const nonce = transactNonceFromArtifacts(
7643
+ input.zkConfigNonce === void 0 ? void 0 : { zkConfigNonce: input.zkConfigNonce },
7644
+ input.transactEnvironment
7645
+ );
6835
7646
  const approval = await requestKytPassageForPoolInteraction({
6836
7647
  owner: input.walletPublicKey,
6837
7648
  poolContract: input.contractId,
@@ -6840,6 +7651,7 @@ async function submitSorobanConfidentialTransfer(input) {
6840
7651
  ...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
6841
7652
  ...input.outputNoteEphemeralScalars ? { outputNoteEphemeralScalars: input.outputNoteEphemeralScalars } : {},
6842
7653
  ...input.applicationIdsPlaintext ? { applicationIdsPlaintext: input.applicationIdsPlaintext } : {},
7654
+ ...input.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.zkConfigNonce },
6843
7655
  networkPassphrase: input.networkPassphrase,
6844
7656
  sorobanRpcUrl: input.sorobanRpcUrl,
6845
7657
  transactEnvironment: input.transactEnvironment
@@ -6848,7 +7660,7 @@ async function submitSorobanConfidentialTransfer(input) {
6848
7660
  contractClient: poolClient,
6849
7661
  contractId: input.contractId,
6850
7662
  from: input.walletPublicKey,
6851
- nonce: resolveZkConfigNonce(input.transactEnvironment),
7663
+ nonce,
6852
7664
  proofHex: input.proofHex,
6853
7665
  publicHex: input.publicHex,
6854
7666
  ...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
@@ -6879,12 +7691,14 @@ async function submitPreparedDeposit(prepared, environment) {
6879
7691
  networkPassphrase: environment.network.networkPassphrase,
6880
7692
  sorobanRpcUrl: environment.network.rpcUrl
6881
7693
  });
7694
+ const nonce = transactNonceFromArtifacts(artifacts, environment);
6882
7695
  const approval = await requestKytPassageForPoolInteraction({
6883
7696
  owner: artifacts.walletPublicKey,
6884
7697
  poolContract: environment.network.poolContract,
6885
7698
  proofHex: artifacts.proofHex,
6886
7699
  publicHex: artifacts.publicHex,
6887
7700
  ...optionalInspectCiphertext(artifacts),
7701
+ ...artifacts.zkConfigNonce === void 0 ? {} : { zkConfigNonce: artifacts.zkConfigNonce },
6888
7702
  networkPassphrase: environment.network.networkPassphrase,
6889
7703
  sorobanRpcUrl: environment.network.rpcUrl,
6890
7704
  transactEnvironment: environment
@@ -6893,7 +7707,7 @@ async function submitPreparedDeposit(prepared, environment) {
6893
7707
  contractClient: poolClient,
6894
7708
  contractId: environment.network.poolContract,
6895
7709
  from: artifacts.walletPublicKey,
6896
- nonce: resolveZkConfigNonce(environment),
7710
+ nonce,
6897
7711
  proofHex: artifacts.proofHex,
6898
7712
  publicHex: artifacts.publicHex,
6899
7713
  ...artifacts.ciphertextHex ? { ciphertextHex: artifacts.ciphertextHex } : {},
@@ -6914,6 +7728,7 @@ async function submitPreparedPoolTransact(prepared, environment) {
6914
7728
  proofHex: artifacts.proofHex,
6915
7729
  publicHex: artifacts.publicHex,
6916
7730
  ...optionalInspectCiphertext(artifacts),
7731
+ ...artifacts.zkConfigNonce === void 0 ? {} : { zkConfigNonce: artifacts.zkConfigNonce },
6917
7732
  networkPassphrase: environment.network.networkPassphrase,
6918
7733
  sorobanRpcUrl: environment.network.rpcUrl,
6919
7734
  transactEnvironment: environment
@@ -6969,7 +7784,7 @@ async function privacyPoolInitOptions(assets, transactEnvironment) {
6969
7784
  };
6970
7785
  }
6971
7786
  async function createDefaultTransactEngine(assets, transactEnvironment) {
6972
- await PrivacyPoolSDK3.init(await privacyPoolInitOptions(assets, transactEnvironment));
7787
+ await PrivacyPoolSDK4.init(await privacyPoolInitOptions(assets, transactEnvironment));
6973
7788
  if (transactEnvironment) {
6974
7789
  configurePrivacyPoolService(
6975
7790
  createPrivacyPoolService({
@@ -7465,6 +8280,7 @@ async function inspectRelayerSweepKyt(input) {
7465
8280
  ...input.prepared.transactArtifacts?.applicationIdsPlaintext ? {
7466
8281
  applicationIdsPlaintext: input.prepared.transactArtifacts.applicationIdsPlaintext
7467
8282
  } : {},
8283
+ ...input.prepared.transactArtifacts?.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.prepared.transactArtifacts.zkConfigNonce },
7468
8284
  networkPassphrase: environment.network.networkPassphrase,
7469
8285
  sorobanRpcUrl: environment.network.rpcUrl,
7470
8286
  transactEnvironment: environment
@@ -7477,7 +8293,10 @@ async function simulateSweepAuthEntries(input) {
7477
8293
  const operation = new Contract(environment.network.poolContract).call(
7478
8294
  "transact",
7479
8295
  new Address5(input.relayerPublicKey).toScVal(),
7480
- nativeToScVal2(resolveZkConfigNonce(environment), { type: "u64" }),
8296
+ nativeToScVal2(
8297
+ transactNonceFromArtifacts(input.prepared.transactArtifacts, environment),
8298
+ { type: "u64" }
8299
+ ),
7481
8300
  nativeToScVal2(hexToBytes(input.artifacts.proofHex), { type: "bytes" }),
7482
8301
  nativeToScVal2(hexToBytes(input.artifacts.publicHex), { type: "bytes" }),
7483
8302
  nativeToScVal2(hexToBytes(input.artifacts.ciphertextHex ?? ""), { type: "bytes" }),
@@ -7514,6 +8333,7 @@ async function unsignedEscrowAuthorizationForSweep(input) {
7514
8333
  });
7515
8334
  const auth = await simulateSweepAuthEntries({
7516
8335
  artifacts,
8336
+ prepared: input.prepared,
7517
8337
  transactEnvironment: input.transactEnvironment,
7518
8338
  relayerPublicKey,
7519
8339
  approval
@@ -7545,15 +8365,15 @@ async function loadDefaultStellarBrowserAssets(options) {
7545
8365
  }
7546
8366
 
7547
8367
  // src/transact/private-address/temporary-recipient.ts
7548
- import { PrivacyPoolSDK as PrivacyPoolSDK4 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
8368
+ import { PrivacyPoolSDK as PrivacyPoolSDK5 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
7549
8369
  var COORDINATE_HEX_LENGTH3 = 64;
7550
8370
  async function generateRandomDepositScalarHex() {
7551
8371
  await getPrivacyPoolService().getInitializedSdk();
7552
- return PrivacyPoolSDK4.generateRandomScalarHex32();
8372
+ return PrivacyPoolSDK5.generateRandomScalarHex32();
7553
8373
  }
7554
8374
  async function generateTemporaryRecipientPrivateAddress() {
7555
8375
  await getPrivacyPoolService().getInitializedSdk();
7556
- const temporaryScalarHex = PrivacyPoolSDK4.generateRandomScalarHex32();
8376
+ const temporaryScalarHex = PrivacyPoolSDK5.generateRandomScalarHex32();
7557
8377
  const sdk = await getPrivacyPoolService().getInitializedSdk();
7558
8378
  const point = sdk.ecdhEphemeralPublicKeyFromScalarHex(temporaryScalarHex);
7559
8379
  const publicKeyXHex = point.x.padStart(COORDINATE_HEX_LENGTH3, "0");
@@ -7809,10 +8629,11 @@ function requirePreparedArtifacts(prepared) {
7809
8629
  };
7810
8630
  }
7811
8631
  function prepareRelayTransactPackageFromPrepared(input) {
8632
+ const zkConfigNonce = input.zkConfigNonce ?? input.prepared.transactArtifacts?.zkConfigNonce;
7812
8633
  return prepareRelayTransactPackage({
7813
8634
  poolSelector: input.poolSelector,
7814
8635
  ...requirePreparedArtifacts(input.prepared),
7815
- ...input.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.zkConfigNonce },
8636
+ ...zkConfigNonce === void 0 ? {} : { zkConfigNonce },
7816
8637
  ...input.keyVersionHints ? { keyVersionHints: input.keyVersionHints } : {}
7817
8638
  });
7818
8639
  }
@@ -7922,6 +8743,7 @@ function requiredSubmissionMethod(prepared, zkConfigNonce = DEFAULT_ZK_CONFIG_NO
7922
8743
  return result.method;
7923
8744
  }
7924
8745
  export {
8746
+ BINDING_ZK_NONCE,
7925
8747
  BundledZkCircuit2 as BundledZkCircuit,
7926
8748
  COMMITMENT_V2_ZK_NONCE,
7927
8749
  DEFAULT_STEALTH_SIGN_NONCE as DEFAULT_PRIVATE_ADDRESS_SIGN_NONCE,
@@ -7950,6 +8772,7 @@ export {
7950
8772
  RELAY_TRANSACT_PACKAGE_VERSION_V1,
7951
8773
  RELAY_TRANSACT_SIGNAL_PREFIX_BYTES,
7952
8774
  RELAY_TRANSACT_SUPPORTED_SIGNAL_COUNT,
8775
+ SIX_BY_SIX_BINDING_ZK_NONCE,
7953
8776
  SIX_BY_SIX_ZK_NONCE,
7954
8777
  STANDARD_ZK_CONFIG_NONCE,
7955
8778
  SUBMISSION_METHOD,