@arcanetech/privacy-sdk-stellar 0.5.2 → 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/README.md +1 -0
- package/dist/index.d.ts +28 -5
- package/dist/index.js +708 -226
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +1 -0
- package/dist/node.js +693 -219
- package/dist/node.js.map +1 -1
- package/dist/testing/index.d.ts +1 -0
- package/dist/transact/index.d.ts +28 -5
- package/dist/transact/index.js +677 -217
- package/dist/transact/index.js.map +1 -1
- package/package.json +6 -6
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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${
|
|
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
|
-
|
|
746
|
-
|
|
747
|
-
|
|
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
|
+
};
|
|
748
829
|
}
|
|
749
|
-
if (
|
|
750
|
-
|
|
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;
|
|
751
853
|
}
|
|
752
|
-
return
|
|
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");
|
|
863
|
+
}
|
|
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
|
|
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 =
|
|
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);
|
|
@@ -827,13 +939,13 @@ import { ed25519PubkeyPayloadHexToWithdrawFrDecimals } from "@arcanetech/stellar
|
|
|
827
939
|
|
|
828
940
|
// src/transact/stellar/account.ts
|
|
829
941
|
import { StrKey as StrKey3 } from "@stellar/stellar-sdk";
|
|
830
|
-
import { Buffer as
|
|
942
|
+
import { Buffer as Buffer7 } from "buffer";
|
|
831
943
|
var MUXED_ACCOUNT_ID_BYTE_LENGTH = 8;
|
|
832
944
|
var ED25519_PUBLIC_KEY_BYTE_LENGTH = 32;
|
|
833
945
|
function ed25519HexFromGAddress(trimmed) {
|
|
834
946
|
try {
|
|
835
947
|
const raw = StrKey3.decodeEd25519PublicKey(trimmed);
|
|
836
|
-
return
|
|
948
|
+
return Buffer7.from(raw).toString("hex");
|
|
837
949
|
} catch {
|
|
838
950
|
return void 0;
|
|
839
951
|
}
|
|
@@ -890,6 +1002,32 @@ function privKeyScalarDecimalFromRecipientScalarHex(hexInput) {
|
|
|
890
1002
|
return canonicalBabyJubScalarFromInteger(value).toString(DECIMAL_RADIX2);
|
|
891
1003
|
}
|
|
892
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
|
+
|
|
893
1031
|
// src/transact/proofs/withdraw/helpers.ts
|
|
894
1032
|
function destinationWithdrawFrAndScalar(parameters) {
|
|
895
1033
|
const pkHex = ed25519PublicKeyHexFromStellarAccount(
|
|
@@ -944,14 +1082,15 @@ async function resolveWithdrawChangeDeposits(parameters) {
|
|
|
944
1082
|
}
|
|
945
1083
|
};
|
|
946
1084
|
}
|
|
947
|
-
function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1) {
|
|
1085
|
+
function validateDualWithdrawAmounts(withdrawAmountStroops, totalNotes, changePrivateAddressStpl1, requiredFee) {
|
|
948
1086
|
if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
|
|
949
1087
|
throw new Error("Withdraw amount must be positive");
|
|
950
1088
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1089
|
+
const changeStroops = remainingAfterRequiredFee({
|
|
1090
|
+
available: totalNotes,
|
|
1091
|
+
instructed: withdrawAmountStroops,
|
|
1092
|
+
requiredFee
|
|
1093
|
+
});
|
|
955
1094
|
requireChangeRecipientWhenPartial(changeStroops, changePrivateAddressStpl1);
|
|
956
1095
|
return changeStroops;
|
|
957
1096
|
}
|
|
@@ -964,11 +1103,31 @@ function buildDualWithdrawPublicInput(parameters) {
|
|
|
964
1103
|
publicWithdrawalAmount: parameters.publicWithdrawalAmount
|
|
965
1104
|
});
|
|
966
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
|
+
}
|
|
967
1125
|
async function prepareDualWithdrawProofInputs(parameters) {
|
|
968
1126
|
const changeStroops = validateDualWithdrawAmounts(
|
|
969
1127
|
parameters.withdrawAmountStroops,
|
|
970
1128
|
BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value),
|
|
971
|
-
parameters.changePrivateAddressStpl1
|
|
1129
|
+
parameters.changePrivateAddressStpl1,
|
|
1130
|
+
parameters.feeOutput?.requiredFee ?? 0n
|
|
972
1131
|
);
|
|
973
1132
|
const { legA, legB } = dualWithdrawLegsWithSharedRoot({
|
|
974
1133
|
sdk: parameters.sdk,
|
|
@@ -983,11 +1142,12 @@ async function prepareDualWithdrawProofInputs(parameters) {
|
|
|
983
1142
|
),
|
|
984
1143
|
applicationId: parameters.applicationId
|
|
985
1144
|
});
|
|
986
|
-
const { deposits, changeCoin } = await
|
|
1145
|
+
const { deposits, changeCoin, feeCoin } = await withdrawOutputsWithOptionalFee({
|
|
987
1146
|
changeStroops,
|
|
988
1147
|
changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
|
|
989
1148
|
buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
|
|
990
|
-
tokenAddress: parameters.tokenAddress
|
|
1149
|
+
tokenAddress: parameters.tokenAddress,
|
|
1150
|
+
...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
|
|
991
1151
|
});
|
|
992
1152
|
const publicInput = buildDualWithdrawPublicInput({
|
|
993
1153
|
destinationStellarAddress: parameters.destinationStellarAddress,
|
|
@@ -1006,19 +1166,55 @@ async function prepareDualWithdrawProofInputs(parameters) {
|
|
|
1006
1166
|
audit,
|
|
1007
1167
|
deposits,
|
|
1008
1168
|
withdrawLegs: [legA.withdrawObject, legB.withdrawObject],
|
|
1009
|
-
...changeCoin ? { changeCoin } : {}
|
|
1169
|
+
...changeCoin ? { changeCoin } : {},
|
|
1170
|
+
...feeCoin ? { feeCoin } : {}
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
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 } : {}
|
|
1010
1192
|
};
|
|
1011
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
|
+
}
|
|
1012
1207
|
|
|
1013
1208
|
// src/transact/proofs/withdraw/transact-proof.ts
|
|
1014
|
-
function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops) {
|
|
1209
|
+
function validateSingleWithdrawAmounts(withdrawAmountStroops, noteStroops, requiredFee) {
|
|
1015
1210
|
if (withdrawAmountStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
|
|
1016
1211
|
throw new Error("Withdraw amount must be positive");
|
|
1017
1212
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1213
|
+
return remainingAfterRequiredFee({
|
|
1214
|
+
available: noteStroops,
|
|
1215
|
+
instructed: withdrawAmountStroops,
|
|
1216
|
+
requiredFee
|
|
1217
|
+
});
|
|
1022
1218
|
}
|
|
1023
1219
|
async function proveSingleWithdrawTransaction(parameters) {
|
|
1024
1220
|
return parameters.sdk.proveTransaction(
|
|
@@ -1057,7 +1253,8 @@ function buildPrimaryWithdrawForCoin(parameters) {
|
|
|
1057
1253
|
async function prepareSingleWithdrawProofInputs(parameters) {
|
|
1058
1254
|
const changeStroops = validateSingleWithdrawAmounts(
|
|
1059
1255
|
parameters.withdrawAmountStroops,
|
|
1060
|
-
BigInt(parameters.coin.value)
|
|
1256
|
+
BigInt(parameters.coin.value),
|
|
1257
|
+
parameters.feeOutput?.requiredFee ?? 0n
|
|
1061
1258
|
);
|
|
1062
1259
|
const { witness, primaryWithdraw } = buildPrimaryWithdrawForCoin({
|
|
1063
1260
|
sdk: parameters.sdk,
|
|
@@ -1066,11 +1263,12 @@ async function prepareSingleWithdrawProofInputs(parameters) {
|
|
|
1066
1263
|
state: parameters.state,
|
|
1067
1264
|
privKeyScalarHex: parameters.privKeyScalarHex
|
|
1068
1265
|
});
|
|
1069
|
-
const { deposits, changeCoin } = await
|
|
1266
|
+
const { deposits, changeCoin, feeCoin } = await withdrawDepositsWithFee({
|
|
1070
1267
|
changeStroops,
|
|
1071
1268
|
changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
|
|
1072
1269
|
buildAlignedDepositSlot: parameters.buildAlignedDepositSlot,
|
|
1073
|
-
tokenAddress: parameters.tokenAddress
|
|
1270
|
+
tokenAddress: parameters.tokenAddress,
|
|
1271
|
+
...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
|
|
1074
1272
|
});
|
|
1075
1273
|
const publicInput = buildWithdrawPublicInput({
|
|
1076
1274
|
stateRoot: witness.stateRoot,
|
|
@@ -1083,6 +1281,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
|
|
|
1083
1281
|
primaryWithdraw,
|
|
1084
1282
|
deposits,
|
|
1085
1283
|
changeCoin,
|
|
1284
|
+
feeCoin,
|
|
1086
1285
|
publicInput,
|
|
1087
1286
|
audit: buildPoolTransactionAuditParameters({
|
|
1088
1287
|
applicationId: parameters.applicationId,
|
|
@@ -1092,7 +1291,7 @@ async function prepareSingleWithdrawProofInputs(parameters) {
|
|
|
1092
1291
|
};
|
|
1093
1292
|
}
|
|
1094
1293
|
async function proveWithdrawTransact(parameters) {
|
|
1095
|
-
const { primaryWithdraw, deposits, changeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
|
|
1294
|
+
const { primaryWithdraw, deposits, changeCoin, feeCoin, publicInput, audit } = await prepareSingleWithdrawProofInputs(parameters);
|
|
1096
1295
|
const proof = await proveSingleWithdrawTransaction({
|
|
1097
1296
|
sdk: parameters.sdk,
|
|
1098
1297
|
publicInput,
|
|
@@ -1107,13 +1306,17 @@ async function proveWithdrawTransact(parameters) {
|
|
|
1107
1306
|
applicationIdsPlaintext: buildApplicationIdHints({
|
|
1108
1307
|
sdk: parameters.sdk,
|
|
1109
1308
|
inputIds: [parameters.applicationId],
|
|
1110
|
-
outputIds:
|
|
1309
|
+
outputIds: withdrawOutputApplicationIds({
|
|
1310
|
+
applicationId: parameters.applicationId,
|
|
1311
|
+
hasChange: Boolean(changeCoin),
|
|
1312
|
+
hasFeeOutput: Boolean(feeCoin)
|
|
1313
|
+
})
|
|
1111
1314
|
}),
|
|
1112
1315
|
...changeCoin ? { changeCoin } : {}
|
|
1113
1316
|
};
|
|
1114
1317
|
}
|
|
1115
1318
|
async function proveWithdrawTransactDual(parameters) {
|
|
1116
|
-
const { publicInput, audit, withdrawLegs, deposits, changeCoin } = await prepareDualWithdrawProofInputs(parameters);
|
|
1319
|
+
const { publicInput, audit, withdrawLegs, deposits, changeCoin, feeCoin } = await prepareDualWithdrawProofInputs(parameters);
|
|
1117
1320
|
const proof = await parameters.sdk.proveTransaction(
|
|
1118
1321
|
publicInput,
|
|
1119
1322
|
padPublicLegsToLayout(
|
|
@@ -1132,7 +1335,11 @@ async function proveWithdrawTransactDual(parameters) {
|
|
|
1132
1335
|
applicationIdsPlaintext: buildApplicationIdHints({
|
|
1133
1336
|
sdk: parameters.sdk,
|
|
1134
1337
|
inputIds: [audit.applicationId, audit.applicationId],
|
|
1135
|
-
outputIds:
|
|
1338
|
+
outputIds: withdrawOutputApplicationIds({
|
|
1339
|
+
applicationId: audit.applicationId,
|
|
1340
|
+
hasChange: Boolean(changeCoin),
|
|
1341
|
+
hasFeeOutput: Boolean(feeCoin)
|
|
1342
|
+
})
|
|
1136
1343
|
}),
|
|
1137
1344
|
...changeCoin ? { changeCoin } : {}
|
|
1138
1345
|
};
|
|
@@ -1153,6 +1360,9 @@ var DEFAULT_ZK_CONFIG_NONCE = BINDING_ZK_NONCE;
|
|
|
1153
1360
|
function resolveZkConfigNonce(transactEnvironment) {
|
|
1154
1361
|
return transactEnvironment.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
|
|
1155
1362
|
}
|
|
1363
|
+
function transactNonceFromArtifacts(artifacts, transactEnvironment) {
|
|
1364
|
+
return artifacts?.zkConfigNonce ?? resolveZkConfigNonce(transactEnvironment);
|
|
1365
|
+
}
|
|
1156
1366
|
|
|
1157
1367
|
// src/transact/zk/browser-artifact-cache.ts
|
|
1158
1368
|
import { ZK_ARTIFACT_VERSION } from "@arcanetech/stellar-privacy-pool-zk-sdk";
|
|
@@ -1405,6 +1615,122 @@ async function buildAlignedDepositSlotForSdk(sdk, applicationId, parameters) {
|
|
|
1405
1615
|
};
|
|
1406
1616
|
}
|
|
1407
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
|
+
|
|
1408
1734
|
// src/transact/pool/service.ts
|
|
1409
1735
|
var PrivacyPoolService = class {
|
|
1410
1736
|
constructor(assets, applicationId, auditPublicKey, zkCircuits, zkConfigNonce, zkArtifactBaseUrl) {
|
|
@@ -1421,42 +1747,50 @@ var PrivacyPoolService = class {
|
|
|
1421
1747
|
zkCircuits;
|
|
1422
1748
|
zkConfigNonce;
|
|
1423
1749
|
zkArtifactBaseUrl;
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
async
|
|
1427
|
-
|
|
1428
|
-
|
|
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;
|
|
1429
1758
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1759
|
+
const pending = this.initPromises.get(key);
|
|
1760
|
+
if (pending) {
|
|
1761
|
+
return pending;
|
|
1762
|
+
}
|
|
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);
|
|
1432
1771
|
}
|
|
1433
|
-
this.initPromise = (async () => {
|
|
1434
|
-
if (!this.assets) {
|
|
1435
|
-
throw new Error(
|
|
1436
|
-
"PrivacyPoolService requires browser ZK assets before initialization."
|
|
1437
|
-
);
|
|
1438
|
-
}
|
|
1439
|
-
const zkConfigNonce = this.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
|
|
1440
|
-
const zkCircuits = await materializeSelectedZkCircuit(
|
|
1441
|
-
this.zkCircuits,
|
|
1442
|
-
zkConfigNonce,
|
|
1443
|
-
this.zkArtifactBaseUrl
|
|
1444
|
-
);
|
|
1445
|
-
this.sdk = await PrivacyPoolSDK2.init({
|
|
1446
|
-
wasmBinary: this.assets.sdkWasm,
|
|
1447
|
-
zkCircuits,
|
|
1448
|
-
zkConfigNonce,
|
|
1449
|
-
...optionalZkArtifactBaseUrl(this.zkArtifactBaseUrl)
|
|
1450
|
-
});
|
|
1451
|
-
})();
|
|
1452
|
-
return this.initPromise;
|
|
1453
1772
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1773
|
+
feeBearingZkConfigNonce(input) {
|
|
1774
|
+
return zkConfigNonceForFeeBearingKind({
|
|
1775
|
+
...input,
|
|
1776
|
+
...configuredNonceSpread(this.zkConfigNonce)
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
async initializeSdk(nonce) {
|
|
1780
|
+
if (!this.assets) {
|
|
1781
|
+
throw new Error(
|
|
1782
|
+
"PrivacyPoolService requires browser ZK assets before initialization."
|
|
1783
|
+
);
|
|
1458
1784
|
}
|
|
1459
|
-
return
|
|
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
|
+
);
|
|
1460
1794
|
}
|
|
1461
1795
|
getAuditPublicKey() {
|
|
1462
1796
|
return this.auditPublicKey;
|
|
@@ -1489,65 +1823,51 @@ var PrivacyPoolService = class {
|
|
|
1489
1823
|
return { commitment_hex, coin, depositScalarHex, precommitementHex };
|
|
1490
1824
|
}
|
|
1491
1825
|
async prepareDepositTransactProof(parameters) {
|
|
1492
|
-
const sdk = await this.getInitializedSdk(
|
|
1493
|
-
|
|
1494
|
-
const recipientPublicKeys = recipientPublicKeysDecimalFromPrivateAddress(
|
|
1495
|
-
parameters.privateAddressStpl1
|
|
1496
|
-
);
|
|
1497
|
-
const applicationId = this.getApplicationId();
|
|
1498
|
-
const audit = buildPoolTransactionAuditParameters({
|
|
1499
|
-
applicationId,
|
|
1500
|
-
nAuditSlots: sdk.getLayout().nAuditSlots,
|
|
1501
|
-
...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {}
|
|
1502
|
-
});
|
|
1503
|
-
const deposit = {
|
|
1504
|
-
value: parameters.coin.value,
|
|
1505
|
-
nullifier: parameters.coin.nullifier,
|
|
1506
|
-
ephemeralKeyScalar: scalarHexToFrDecimal2(parameters.depositScalarHex),
|
|
1507
|
-
asset: [parameters.coin.asset_hi, parameters.coin.asset_lo],
|
|
1508
|
-
applicationId,
|
|
1509
|
-
recipientPublicKeys
|
|
1510
|
-
};
|
|
1511
|
-
const publicInput = withTokenAddressPublicInputs(
|
|
1512
|
-
{
|
|
1513
|
-
stateRoot,
|
|
1514
|
-
privKeyScalar: randomFrDecimal253()
|
|
1515
|
-
},
|
|
1516
|
-
parameters.tokenAddress
|
|
1517
|
-
);
|
|
1518
|
-
const proof = await sdk.proveTransaction(
|
|
1519
|
-
publicInput,
|
|
1520
|
-
padPublicLegsToLayout(
|
|
1521
|
-
sdk,
|
|
1522
|
-
buildPublicDepositLegs(parameters.tokenAddress, parameters.coin.value)
|
|
1523
|
-
),
|
|
1524
|
-
padWithdrawSlotsToLayout(sdk, []),
|
|
1525
|
-
padDepositSlotsToLayout(sdk, [deposit]),
|
|
1526
|
-
audit
|
|
1826
|
+
const sdk = await this.getInitializedSdk(
|
|
1827
|
+
this.feeBearingZkConfigNonce({ kind: "deposit" })
|
|
1527
1828
|
);
|
|
1528
|
-
|
|
1829
|
+
return proveDepositTransact({
|
|
1529
1830
|
sdk,
|
|
1530
|
-
|
|
1531
|
-
|
|
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 } : {}
|
|
1532
1841
|
});
|
|
1533
|
-
return { ...proof, applicationIdsPlaintext };
|
|
1534
1842
|
}
|
|
1535
1843
|
async calculateNullifierHash(nullifier, privKeyScalar) {
|
|
1536
1844
|
const sdk = await this.getInitializedSdk();
|
|
1537
1845
|
return sdk.calculateNullifierHash(nullifier, privKeyScalar);
|
|
1538
1846
|
}
|
|
1539
1847
|
async prepareWithdrawTransactProof(parameters) {
|
|
1540
|
-
const sdk = await this.getInitializedSdk(
|
|
1848
|
+
const sdk = await this.getInitializedSdk(
|
|
1849
|
+
this.feeBearingZkConfigNonce({ kind: "withdraw" })
|
|
1850
|
+
);
|
|
1541
1851
|
return proveWithdrawTransact({
|
|
1542
1852
|
sdk,
|
|
1543
1853
|
applicationId: this.getApplicationId(),
|
|
1544
1854
|
buildAlignedDepositSlot: (input) => this.buildAlignedDepositSlot(input),
|
|
1545
1855
|
...this.auditPublicKey ? { auditPublicKey: this.auditPublicKey } : {},
|
|
1546
|
-
|
|
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 } : {}
|
|
1547
1865
|
});
|
|
1548
1866
|
}
|
|
1549
1867
|
async prepareWithdrawTransactProofDual(parameters) {
|
|
1550
|
-
const sdk = await this.getInitializedSdk(
|
|
1868
|
+
const sdk = await this.getInitializedSdk(
|
|
1869
|
+
this.feeBearingZkConfigNonce({ kind: "withdraw" })
|
|
1870
|
+
);
|
|
1551
1871
|
return proveWithdrawTransactDual({
|
|
1552
1872
|
sdk,
|
|
1553
1873
|
applicationId: this.getApplicationId(),
|
|
@@ -1562,7 +1882,8 @@ var PrivacyPoolService = class {
|
|
|
1562
1882
|
ephemeralBKey: parameters.depositorEphemeralBKey,
|
|
1563
1883
|
withdrawAmountStroops: parameters.withdrawAmountStroops,
|
|
1564
1884
|
changePrivateAddressStpl1: parameters.changePrivateAddressStpl1,
|
|
1565
|
-
tokenAddress: parameters.tokenAddress
|
|
1885
|
+
tokenAddress: parameters.tokenAddress,
|
|
1886
|
+
...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
|
|
1566
1887
|
});
|
|
1567
1888
|
}
|
|
1568
1889
|
};
|
|
@@ -2150,6 +2471,13 @@ async function filterUnspentPrivateRecords(input) {
|
|
|
2150
2471
|
}
|
|
2151
2472
|
return unspent;
|
|
2152
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
|
+
}
|
|
2153
2481
|
function pickRecordsForSpendAmount(input) {
|
|
2154
2482
|
let selectedTotal = 0n;
|
|
2155
2483
|
const selected = [];
|
|
@@ -2203,7 +2531,7 @@ async function selectPrivateRecords(input) {
|
|
|
2203
2531
|
return pickRecordsForSpendAmount({
|
|
2204
2532
|
available,
|
|
2205
2533
|
kind: input.kind,
|
|
2206
|
-
amount: input.intent.amount
|
|
2534
|
+
amount: spendAmountCoveringProtocolFee(input.intent.amount)
|
|
2207
2535
|
});
|
|
2208
2536
|
}
|
|
2209
2537
|
function sumRecordAmounts(records) {
|
|
@@ -5312,7 +5640,7 @@ function createStellarPrivacyClientFromResolvedConfig(config) {
|
|
|
5312
5640
|
}
|
|
5313
5641
|
|
|
5314
5642
|
// src/transact/engine/default-factory.ts
|
|
5315
|
-
import { PrivacyPoolSDK as
|
|
5643
|
+
import { PrivacyPoolSDK as PrivacyPoolSDK4 } from "@arcanetech/stellar-privacy-pool-zk-sdk";
|
|
5316
5644
|
|
|
5317
5645
|
// src/transact/pool/proof-types.ts
|
|
5318
5646
|
function ciphertextArtifactsFromProof(proof) {
|
|
@@ -5327,9 +5655,15 @@ function ciphertextArtifactsFromProof(proof) {
|
|
|
5327
5655
|
// src/transact/engine/prepare/deposit.ts
|
|
5328
5656
|
var STROOPS_PER_UNIT = 10000000n;
|
|
5329
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
|
+
}
|
|
5330
5664
|
const aligned = await parameters.poolService.createAlignedShieldCoinData({
|
|
5331
5665
|
privateAddressStpl1: parameters.prepared.intent.to,
|
|
5332
|
-
amountStroops:
|
|
5666
|
+
amountStroops: userAmount,
|
|
5333
5667
|
tokenAddress: parameters.tokenAddress
|
|
5334
5668
|
});
|
|
5335
5669
|
const poolClient = readPoolClientFactory(parameters.environment)({
|
|
@@ -5344,7 +5678,9 @@ async function createAlignedDepositWithProof(parameters) {
|
|
|
5344
5678
|
coin: aligned.coin,
|
|
5345
5679
|
depositScalarHex: aligned.depositScalarHex,
|
|
5346
5680
|
merkleRootBytes: merkleTx.result,
|
|
5347
|
-
tokenAddress: parameters.tokenAddress
|
|
5681
|
+
tokenAddress: parameters.tokenAddress,
|
|
5682
|
+
publicDepositStroops: instructed,
|
|
5683
|
+
...feeOutput ? { feeOutput } : {}
|
|
5348
5684
|
});
|
|
5349
5685
|
return { ...aligned, proof };
|
|
5350
5686
|
}
|
|
@@ -5363,8 +5699,8 @@ async function prepareDepositOperation(prepared, environment, poolService) {
|
|
|
5363
5699
|
privateAddress: prepared.intent.to,
|
|
5364
5700
|
assetId: prepared.intent.asset,
|
|
5365
5701
|
poolContract: environment.network.poolContract,
|
|
5366
|
-
amount:
|
|
5367
|
-
amountDisplay: Number(
|
|
5702
|
+
amount: BigInt(depositProof.coin.value),
|
|
5703
|
+
amountDisplay: Number(depositProof.coin.value) / Number(STROOPS_PER_UNIT),
|
|
5368
5704
|
commitmentHex: depositProof.commitment_hex,
|
|
5369
5705
|
coin: depositProof.coin,
|
|
5370
5706
|
depositScalarHex: depositProof.depositScalarHex,
|
|
@@ -5380,7 +5716,11 @@ async function prepareDepositOperation(prepared, environment, poolService) {
|
|
|
5380
5716
|
depositScalarHex: depositProof.depositScalarHex,
|
|
5381
5717
|
precommitementHex: depositProof.precommitementHex,
|
|
5382
5718
|
commitmentHex: depositProof.commitment_hex,
|
|
5383
|
-
walletPublicKey
|
|
5719
|
+
walletPublicKey,
|
|
5720
|
+
zkConfigNonce: zkConfigNonceForFeeBearingKind({
|
|
5721
|
+
kind: "deposit",
|
|
5722
|
+
...configuredNonceSpread(environment.zkConfigNonce)
|
|
5723
|
+
})
|
|
5384
5724
|
});
|
|
5385
5725
|
}
|
|
5386
5726
|
|
|
@@ -5554,9 +5894,6 @@ function escrowOutputFields(input) {
|
|
|
5554
5894
|
recipientLo: input.escrowSend.recipientLo
|
|
5555
5895
|
};
|
|
5556
5896
|
}
|
|
5557
|
-
function paddingDepositPair(recipientDeposit) {
|
|
5558
|
-
return [recipientDeposit, "dummy"];
|
|
5559
|
-
}
|
|
5560
5897
|
async function buildChangeDepositPair(parameters) {
|
|
5561
5898
|
const changeSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
|
|
5562
5899
|
privateAddressStpl1: parameters.selfPrivateAddressStpl1ForChange.trim(),
|
|
@@ -5573,34 +5910,54 @@ async function buildChangeDepositPair(parameters) {
|
|
|
5573
5910
|
}
|
|
5574
5911
|
};
|
|
5575
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
|
+
}
|
|
5576
5932
|
async function buildRecipientAndOptionalChangeDeposits(parameters) {
|
|
5577
5933
|
const escrow = {
|
|
5578
5934
|
...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
|
|
5579
5935
|
...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
|
|
5580
5936
|
};
|
|
5581
|
-
const recipientPrivateAddress = parameters.recipientPrivateAddressStpl1.trim();
|
|
5582
5937
|
const recipientSlot = await getPrivacyPoolService().buildAlignedDepositSlot({
|
|
5583
|
-
privateAddressStpl1:
|
|
5938
|
+
privateAddressStpl1: parameters.recipientPrivateAddressStpl1.trim(),
|
|
5584
5939
|
amountStroops: parameters.transferStroops,
|
|
5585
5940
|
tokenAddress: parameters.tokenAddress,
|
|
5586
5941
|
...escrowOutputFields(escrow)
|
|
5587
5942
|
});
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
recipientSlot,
|
|
5591
|
-
deposits: paddingDepositPair(recipientSlot.deposit)
|
|
5592
|
-
};
|
|
5593
|
-
}
|
|
5594
|
-
const changeResult = await buildChangeDepositPair({
|
|
5595
|
-
selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
|
|
5943
|
+
const withoutFee = await depositsWithoutFee({
|
|
5944
|
+
recipientDeposit: recipientSlot.deposit,
|
|
5596
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,
|
|
5597
5952
|
tokenAddress: parameters.tokenAddress,
|
|
5598
|
-
|
|
5953
|
+
buildAlignedDepositSlot: (aligned) => getPrivacyPoolService().buildAlignedDepositSlot(aligned),
|
|
5954
|
+
...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
|
|
5599
5955
|
});
|
|
5600
5956
|
return {
|
|
5601
5957
|
recipientSlot,
|
|
5602
|
-
deposits:
|
|
5603
|
-
changeCoin:
|
|
5958
|
+
deposits: withFee.deposits,
|
|
5959
|
+
..."changeCoin" in withoutFee ? { changeCoin: withoutFee.changeCoin } : {},
|
|
5960
|
+
...withFee.feeCoin ? { feeCoin: withFee.feeCoin } : {}
|
|
5604
5961
|
};
|
|
5605
5962
|
}
|
|
5606
5963
|
|
|
@@ -5653,14 +6010,15 @@ function sweepOutputOwnerFields(parameters) {
|
|
|
5653
6010
|
return { sweepOutputOwnerPubX, sweepOutputOwnerPubY };
|
|
5654
6011
|
}
|
|
5655
6012
|
async function buildTransferDepositsAndPublicInput(parameters) {
|
|
5656
|
-
const { recipientSlot, deposits, changeCoin } = await buildRecipientAndOptionalChangeDeposits({
|
|
6013
|
+
const { recipientSlot, deposits, changeCoin, feeCoin } = await buildRecipientAndOptionalChangeDeposits({
|
|
5657
6014
|
recipientPrivateAddressStpl1: parameters.recipientPrivateAddressStpl1,
|
|
5658
6015
|
transferStroops: parameters.transferStroops,
|
|
5659
6016
|
changeStroops: parameters.changeStroops,
|
|
5660
6017
|
selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
|
|
5661
6018
|
tokenAddress: parameters.tokenAddress,
|
|
5662
6019
|
...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
|
|
5663
|
-
...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
|
|
6020
|
+
...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
|
|
6021
|
+
...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
|
|
5664
6022
|
});
|
|
5665
6023
|
const publicInput = withTokenAddressPublicInputs(
|
|
5666
6024
|
{
|
|
@@ -5675,6 +6033,7 @@ async function buildTransferDepositsAndPublicInput(parameters) {
|
|
|
5675
6033
|
recipientSlot,
|
|
5676
6034
|
deposits,
|
|
5677
6035
|
...changeCoin ? { changeCoin } : {},
|
|
6036
|
+
...feeCoin ? { feeCoin } : {},
|
|
5678
6037
|
publicInput
|
|
5679
6038
|
};
|
|
5680
6039
|
}
|
|
@@ -5690,7 +6049,8 @@ async function buildSenderTransferDepositsAndPublicInput(parameters) {
|
|
|
5690
6049
|
parameters.senderPrivKeyScalarHex
|
|
5691
6050
|
),
|
|
5692
6051
|
...parameters.escrowSend ? { escrowSend: parameters.escrowSend } : {},
|
|
5693
|
-
...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {}
|
|
6052
|
+
...parameters.escrowClaimantLimbs ? { escrowClaimantLimbs: parameters.escrowClaimantLimbs } : {},
|
|
6053
|
+
...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
|
|
5694
6054
|
});
|
|
5695
6055
|
}
|
|
5696
6056
|
|
|
@@ -5714,11 +6074,20 @@ function stampDualWithdrawLegs(legs, parameters) {
|
|
|
5714
6074
|
}
|
|
5715
6075
|
};
|
|
5716
6076
|
}
|
|
5717
|
-
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
|
+
}
|
|
5718
6087
|
return buildApplicationIdHints({
|
|
5719
6088
|
sdk,
|
|
5720
6089
|
inputIds: [applicationId, applicationId],
|
|
5721
|
-
outputIds
|
|
6090
|
+
outputIds
|
|
5722
6091
|
});
|
|
5723
6092
|
}
|
|
5724
6093
|
async function proveDualConfidentialTransfer(parameters) {
|
|
@@ -5738,16 +6107,24 @@ async function proveDualConfidentialTransfer(parameters) {
|
|
|
5738
6107
|
audit
|
|
5739
6108
|
);
|
|
5740
6109
|
}
|
|
5741
|
-
|
|
6110
|
+
function dualTransferChangeStroops(parameters) {
|
|
5742
6111
|
const totalNotes = BigInt(parameters.coinA.value) + BigInt(parameters.coinB.value);
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
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
|
+
});
|
|
5747
6120
|
requireChangeRecipientWhenPartial(
|
|
5748
6121
|
changeStroops,
|
|
5749
6122
|
parameters.selfPrivateAddressStpl1ForChange
|
|
5750
6123
|
);
|
|
6124
|
+
return changeStroops;
|
|
6125
|
+
}
|
|
6126
|
+
async function buildDualTransferProofContext(parameters, applicationId, sdk) {
|
|
6127
|
+
const changeStroops = dualTransferChangeStroops(parameters);
|
|
5751
6128
|
const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
|
|
5752
6129
|
parameters.senderPrivKeyScalarHex
|
|
5753
6130
|
);
|
|
@@ -5774,15 +6151,21 @@ async function buildDualTransferProofContext(parameters, applicationId, sdk) {
|
|
|
5774
6151
|
selfPrivateAddressStpl1ForChange: parameters.selfPrivateAddressStpl1ForChange,
|
|
5775
6152
|
tokenAddress: parameters.tokenAddress,
|
|
5776
6153
|
stateRoot: legA.witness.stateRoot,
|
|
5777
|
-
...confidentialEscrowFields(parameters)
|
|
6154
|
+
...confidentialEscrowFields(parameters),
|
|
6155
|
+
...parameters.feeOutput ? { feeOutput: parameters.feeOutput } : {}
|
|
5778
6156
|
});
|
|
5779
6157
|
return { legA, legB, applicationId, ...transferInputs };
|
|
5780
6158
|
}
|
|
5781
6159
|
async function prepareConfidentialTransferProofDual(parameters) {
|
|
5782
6160
|
const poolService = getPrivacyPoolService();
|
|
5783
|
-
const sdk = await poolService.getInitializedSdk(
|
|
6161
|
+
const sdk = await poolService.getInitializedSdk(
|
|
6162
|
+
poolService.feeBearingZkConfigNonce({
|
|
6163
|
+
kind: "transfer",
|
|
6164
|
+
...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
|
|
6165
|
+
})
|
|
6166
|
+
);
|
|
5784
6167
|
const applicationId = poolService.getApplicationId();
|
|
5785
|
-
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);
|
|
5786
6169
|
const auditPublicKey = poolService.getAuditPublicKey();
|
|
5787
6170
|
const proof = await (auditPublicKey ? proveDualConfidentialTransfer({
|
|
5788
6171
|
sdk,
|
|
@@ -5805,7 +6188,8 @@ async function prepareConfidentialTransferProofDual(parameters) {
|
|
|
5805
6188
|
applicationIdsPlaintext: buildDualTransferApplicationIds(
|
|
5806
6189
|
sdk,
|
|
5807
6190
|
applicationId,
|
|
5808
|
-
changeCoin
|
|
6191
|
+
changeCoin,
|
|
6192
|
+
feeCoin
|
|
5809
6193
|
),
|
|
5810
6194
|
recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
|
|
5811
6195
|
...changeCoin ? { changeCoin } : {}
|
|
@@ -5813,22 +6197,32 @@ async function prepareConfidentialTransferProofDual(parameters) {
|
|
|
5813
6197
|
}
|
|
5814
6198
|
|
|
5815
6199
|
// src/transact/proofs/confidential/single.ts
|
|
5816
|
-
function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange) {
|
|
6200
|
+
function validateSingleCoinTransferAmounts(transferStroops, noteStroops, selfPrivateAddressStpl1ForChange, requiredFee) {
|
|
5817
6201
|
if (transferStroops < MIN_CONFIDENTIAL_TRANSFER_STROOPS) {
|
|
5818
6202
|
throw new Error("Transfer amount must be positive");
|
|
5819
6203
|
}
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
6204
|
+
const changeStroops = remainingAfterRequiredFee({
|
|
6205
|
+
available: noteStroops,
|
|
6206
|
+
instructed: transferStroops,
|
|
6207
|
+
requiredFee
|
|
6208
|
+
});
|
|
5824
6209
|
requireChangeRecipientWhenPartial(changeStroops, selfPrivateAddressStpl1ForChange);
|
|
5825
6210
|
return changeStroops;
|
|
5826
6211
|
}
|
|
5827
|
-
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
|
+
}
|
|
5828
6222
|
return buildApplicationIdHints({
|
|
5829
6223
|
sdk,
|
|
5830
6224
|
inputIds: [applicationId],
|
|
5831
|
-
outputIds
|
|
6225
|
+
outputIds
|
|
5832
6226
|
});
|
|
5833
6227
|
}
|
|
5834
6228
|
function buildTransferAuditParameters(parameters) {
|
|
@@ -5854,11 +6248,25 @@ function confidentialEscrowFields2(input) {
|
|
|
5854
6248
|
...input.escrowClaimantLimbs ? { escrowClaimantLimbs: input.escrowClaimantLimbs } : {}
|
|
5855
6249
|
};
|
|
5856
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
|
+
}
|
|
5857
6264
|
async function buildTransferProofInputs(parameters) {
|
|
5858
6265
|
const changeStroops = validateSingleCoinTransferAmounts(
|
|
5859
6266
|
parameters.input.transferStroops,
|
|
5860
6267
|
parameters.noteStroops,
|
|
5861
|
-
parameters.input.selfPrivateAddressStpl1ForChange
|
|
6268
|
+
parameters.input.selfPrivateAddressStpl1ForChange,
|
|
6269
|
+
parameters.input.feeOutput?.requiredFee ?? 0n
|
|
5862
6270
|
);
|
|
5863
6271
|
const privKeyScalar = privKeyScalarDecimalFromRecipientScalarHex(
|
|
5864
6272
|
parameters.input.senderPrivKeyScalarHex
|
|
@@ -5877,31 +6285,30 @@ async function buildTransferProofInputs(parameters) {
|
|
|
5877
6285
|
withdrawObject,
|
|
5878
6286
|
sweepWithdrawStamp(confidentialEscrowFields2(parameters.input))
|
|
5879
6287
|
);
|
|
5880
|
-
const { recipientSlot, deposits, changeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput(
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
transferStroops: parameters.input.transferStroops,
|
|
5884
|
-
changeStroops,
|
|
5885
|
-
selfPrivateAddressStpl1ForChange: parameters.input.selfPrivateAddressStpl1ForChange,
|
|
5886
|
-
tokenAddress: parameters.input.tokenAddress,
|
|
5887
|
-
stateRoot: witness.stateRoot,
|
|
5888
|
-
...confidentialEscrowFields2(parameters.input)
|
|
5889
|
-
});
|
|
6288
|
+
const { recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildSenderTransferDepositsAndPublicInput(
|
|
6289
|
+
transferDepositInputFromProof(parameters.input, changeStroops, witness.stateRoot)
|
|
6290
|
+
);
|
|
5890
6291
|
return {
|
|
5891
6292
|
witness,
|
|
5892
6293
|
withdrawObject: stampedWithdraw,
|
|
5893
6294
|
recipientSlot,
|
|
5894
6295
|
deposits,
|
|
5895
6296
|
changeCoin,
|
|
6297
|
+
feeCoin,
|
|
5896
6298
|
publicInput
|
|
5897
6299
|
};
|
|
5898
6300
|
}
|
|
5899
6301
|
async function prepareConfidentialTransferProof(parameters) {
|
|
5900
6302
|
const poolService = getPrivacyPoolService();
|
|
5901
|
-
const sdk = await poolService.getInitializedSdk(
|
|
6303
|
+
const sdk = await poolService.getInitializedSdk(
|
|
6304
|
+
poolService.feeBearingZkConfigNonce({
|
|
6305
|
+
kind: "transfer",
|
|
6306
|
+
...parameters.escrowClaimantLimbs ? { spendSource: "escrow" } : {}
|
|
6307
|
+
})
|
|
6308
|
+
);
|
|
5902
6309
|
const applicationId = poolService.getApplicationId();
|
|
5903
6310
|
const noteStroops = BigInt(parameters.coin.value);
|
|
5904
|
-
const { withdrawObject, recipientSlot, deposits, changeCoin, publicInput } = await buildTransferProofInputs({
|
|
6311
|
+
const { withdrawObject, recipientSlot, deposits, changeCoin, feeCoin, publicInput } = await buildTransferProofInputs({
|
|
5905
6312
|
sdk,
|
|
5906
6313
|
applicationId,
|
|
5907
6314
|
input: parameters,
|
|
@@ -5924,7 +6331,8 @@ async function prepareConfidentialTransferProof(parameters) {
|
|
|
5924
6331
|
applicationIdsPlaintext: buildSingleTransferApplicationIds(
|
|
5925
6332
|
sdk,
|
|
5926
6333
|
applicationId,
|
|
5927
|
-
changeCoin
|
|
6334
|
+
changeCoin,
|
|
6335
|
+
feeCoin
|
|
5928
6336
|
),
|
|
5929
6337
|
recipientCoin: generatedOutputCoinFromSlot(recipientSlot),
|
|
5930
6338
|
...changeCoin ? { changeCoin } : {}
|
|
@@ -5947,7 +6355,12 @@ function proofEscrowFields(input) {
|
|
|
5947
6355
|
}
|
|
5948
6356
|
async function buildSingleTransferProofAtExecute(input) {
|
|
5949
6357
|
const context = await buildSpendProofContextAtExecute(input);
|
|
5950
|
-
const
|
|
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);
|
|
5951
6364
|
const proof = await prepareConfidentialTransferProof({
|
|
5952
6365
|
coin: context.coin,
|
|
5953
6366
|
state: { commitments: context.commitments },
|
|
@@ -5960,7 +6373,8 @@ async function buildSingleTransferProofAtExecute(input) {
|
|
|
5960
6373
|
recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
|
|
5961
6374
|
selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
|
|
5962
6375
|
tokenAddress: context.tokenAddress,
|
|
5963
|
-
...proofEscrowFields(input)
|
|
6376
|
+
...proofEscrowFields(input),
|
|
6377
|
+
...feeOutput ? { feeOutput } : {}
|
|
5964
6378
|
});
|
|
5965
6379
|
return { context, proof };
|
|
5966
6380
|
}
|
|
@@ -5977,7 +6391,12 @@ async function buildDualTransferProofAtExecute(input) {
|
|
|
5977
6391
|
commitments: context.commitments
|
|
5978
6392
|
});
|
|
5979
6393
|
const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
|
|
5980
|
-
const
|
|
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);
|
|
5981
6400
|
const proof = await prepareConfidentialTransferProofDual({
|
|
5982
6401
|
coinA: context.coin,
|
|
5983
6402
|
coinB: secondary.coin,
|
|
@@ -5995,7 +6414,8 @@ async function buildDualTransferProofAtExecute(input) {
|
|
|
5995
6414
|
recipientPrivateAddressStpl1: context.recipientPrivateAddressStpl1,
|
|
5996
6415
|
selfPrivateAddressStpl1ForChange: readChangeRecipient(input, changeStroops),
|
|
5997
6416
|
tokenAddress: context.tokenAddress,
|
|
5998
|
-
...proofEscrowFields(input)
|
|
6417
|
+
...proofEscrowFields(input),
|
|
6418
|
+
...feeOutput ? { feeOutput } : {}
|
|
5999
6419
|
});
|
|
6000
6420
|
return { context, proof };
|
|
6001
6421
|
}
|
|
@@ -6127,11 +6547,17 @@ async function runTransferFinalizeSteps(input) {
|
|
|
6127
6547
|
walletPublicKey: input.walletPublicKey,
|
|
6128
6548
|
...recipient.escrowSend ? { escrowSendPrivateAddress: recipient.recipientPrivateAddressStpl1 } : {}
|
|
6129
6549
|
});
|
|
6130
|
-
return
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
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
|
+
};
|
|
6135
6561
|
}
|
|
6136
6562
|
function sweepArtifactStamp(artifacts) {
|
|
6137
6563
|
return {
|
|
@@ -6159,6 +6585,24 @@ async function finalizeTransferAtExecute(prepared, environment) {
|
|
|
6159
6585
|
return runTransferFinalizeSteps({ prepared, environment, walletPublicKey });
|
|
6160
6586
|
}
|
|
6161
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
|
+
|
|
6162
6606
|
// src/transact/engine/execute.ts
|
|
6163
6607
|
function enrichWithdrawOutputRecords(prepared, proof) {
|
|
6164
6608
|
if (!proof.changeCoin || prepared.outputRecords.length === 0) {
|
|
@@ -6174,7 +6618,7 @@ function enrichWithdrawOutputRecords(prepared, proof) {
|
|
|
6174
6618
|
precommitementHex: changeCoin.precommitementHex
|
|
6175
6619
|
}));
|
|
6176
6620
|
}
|
|
6177
|
-
function buildWithdrawFinalizeArtifacts(proof, context) {
|
|
6621
|
+
function buildWithdrawFinalizeArtifacts(proof, context, environment) {
|
|
6178
6622
|
return {
|
|
6179
6623
|
proofHex: proof.proof_hex,
|
|
6180
6624
|
publicHex: proof.public_hex,
|
|
@@ -6182,7 +6626,11 @@ function buildWithdrawFinalizeArtifacts(proof, context) {
|
|
|
6182
6626
|
applicationIdsPlaintext: proof.applicationIdsPlaintext,
|
|
6183
6627
|
tokenAddress: context.tokenAddress,
|
|
6184
6628
|
walletPublicKey: context.walletPublicKey,
|
|
6185
|
-
executeFinalizeRequired: false
|
|
6629
|
+
executeFinalizeRequired: false,
|
|
6630
|
+
zkConfigNonce: zkConfigNonceForFeeBearingKind({
|
|
6631
|
+
kind: "withdraw",
|
|
6632
|
+
...configuredNonceSpread(environment.zkConfigNonce)
|
|
6633
|
+
})
|
|
6186
6634
|
};
|
|
6187
6635
|
}
|
|
6188
6636
|
async function finalizeSingleWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
|
|
@@ -6191,7 +6639,13 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
|
|
|
6191
6639
|
environment,
|
|
6192
6640
|
recipientPrivateAddressStpl1: withdrawFrom
|
|
6193
6641
|
});
|
|
6194
|
-
const
|
|
6642
|
+
const feeFields = await withdrawFeeProofFields({
|
|
6643
|
+
prepared,
|
|
6644
|
+
environment,
|
|
6645
|
+
tokenAddress: context.tokenAddress,
|
|
6646
|
+
available: BigInt(context.coin.value),
|
|
6647
|
+
withdrawFrom
|
|
6648
|
+
});
|
|
6195
6649
|
const proof = await poolService.prepareWithdrawTransactProof({
|
|
6196
6650
|
coin: context.coin,
|
|
6197
6651
|
state: { commitments: context.commitments },
|
|
@@ -6202,11 +6656,24 @@ async function finalizeSingleWithdrawAtExecute(prepared, environment, poolServic
|
|
|
6202
6656
|
yHex: context.ephemeral.yHex
|
|
6203
6657
|
}),
|
|
6204
6658
|
withdrawAmountStroops: prepared.intent.amount,
|
|
6205
|
-
changePrivateAddressStpl1:
|
|
6206
|
-
tokenAddress: context.tokenAddress
|
|
6659
|
+
changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
|
|
6660
|
+
tokenAddress: context.tokenAddress,
|
|
6661
|
+
...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
|
|
6207
6662
|
});
|
|
6208
6663
|
enrichWithdrawOutputRecords(prepared, proof);
|
|
6209
|
-
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
|
+
};
|
|
6210
6677
|
}
|
|
6211
6678
|
async function finalizeDualWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
|
|
6212
6679
|
const [, secondaryRecord] = prepared.consumedRecords;
|
|
@@ -6224,28 +6691,27 @@ async function finalizeDualWithdrawAtExecute(prepared, environment, poolService,
|
|
|
6224
6691
|
walletPublicKey: context.walletPublicKey,
|
|
6225
6692
|
commitments: context.commitments
|
|
6226
6693
|
});
|
|
6227
|
-
const
|
|
6228
|
-
|
|
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
|
+
});
|
|
6229
6701
|
const proof = await poolService.prepareWithdrawTransactProofDual({
|
|
6230
6702
|
coinA: context.coin,
|
|
6231
6703
|
coinB: secondary.coin,
|
|
6232
6704
|
state: { commitments: context.commitments },
|
|
6233
6705
|
destinationStellarAddress: prepared.intent.to,
|
|
6234
6706
|
privKeyScalarHex: context.senderPrivKeyScalarHex,
|
|
6235
|
-
|
|
6236
|
-
xHex: context.ephemeral.xHex,
|
|
6237
|
-
yHex: context.ephemeral.yHex
|
|
6238
|
-
}),
|
|
6239
|
-
depositorEphemeralBKey: serializeEphemeralKeyString({
|
|
6240
|
-
xHex: secondary.ephemeral.xHex,
|
|
6241
|
-
yHex: secondary.ephemeral.yHex
|
|
6242
|
-
}),
|
|
6707
|
+
...dualWithdrawEphemeralKeys({ context, secondary }),
|
|
6243
6708
|
withdrawAmountStroops: prepared.intent.amount,
|
|
6244
|
-
changePrivateAddressStpl1:
|
|
6245
|
-
tokenAddress: context.tokenAddress
|
|
6709
|
+
changePrivateAddressStpl1: feeFields.changePrivateAddressStpl1,
|
|
6710
|
+
tokenAddress: context.tokenAddress,
|
|
6711
|
+
...feeFields.feeOutput ? { feeOutput: feeFields.feeOutput } : {}
|
|
6246
6712
|
});
|
|
6247
6713
|
enrichWithdrawOutputRecords(prepared, proof);
|
|
6248
|
-
return buildWithdrawFinalizeArtifacts(proof, context);
|
|
6714
|
+
return buildWithdrawFinalizeArtifacts(proof, context, environment);
|
|
6249
6715
|
}
|
|
6250
6716
|
async function finalizeWithdrawAtExecute(prepared, environment, poolService) {
|
|
6251
6717
|
if (prepared.kind !== "withdraw") {
|
|
@@ -6507,7 +6973,7 @@ async function requestKytPassageForPoolInteraction(input) {
|
|
|
6507
6973
|
const applicationId = resolvePoolApplicationId(
|
|
6508
6974
|
input.transactEnvironment.network.applicationId
|
|
6509
6975
|
);
|
|
6510
|
-
const zkConfigNonce = resolveZkConfigNonce(input.transactEnvironment);
|
|
6976
|
+
const zkConfigNonce = input.zkConfigNonce ?? resolveZkConfigNonce(input.transactEnvironment);
|
|
6511
6977
|
const applicationIdsPlaintext = input.applicationIdsPlaintext ?? buildApplicationIdHints2(
|
|
6512
6978
|
applicationId,
|
|
6513
6979
|
layoutForKnownNonce(zkConfigNonce).nAuditSlots
|
|
@@ -6679,6 +7145,10 @@ async function submitSorobanConfidentialTransfer(input) {
|
|
|
6679
7145
|
networkPassphrase: input.networkPassphrase,
|
|
6680
7146
|
sorobanRpcUrl: input.sorobanRpcUrl
|
|
6681
7147
|
});
|
|
7148
|
+
const nonce = transactNonceFromArtifacts(
|
|
7149
|
+
input.zkConfigNonce === void 0 ? void 0 : { zkConfigNonce: input.zkConfigNonce },
|
|
7150
|
+
input.transactEnvironment
|
|
7151
|
+
);
|
|
6682
7152
|
const approval = await requestKytPassageForPoolInteraction({
|
|
6683
7153
|
owner: input.walletPublicKey,
|
|
6684
7154
|
poolContract: input.contractId,
|
|
@@ -6687,6 +7157,7 @@ async function submitSorobanConfidentialTransfer(input) {
|
|
|
6687
7157
|
...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
|
|
6688
7158
|
...input.outputNoteEphemeralScalars ? { outputNoteEphemeralScalars: input.outputNoteEphemeralScalars } : {},
|
|
6689
7159
|
...input.applicationIdsPlaintext ? { applicationIdsPlaintext: input.applicationIdsPlaintext } : {},
|
|
7160
|
+
...input.zkConfigNonce === void 0 ? {} : { zkConfigNonce: input.zkConfigNonce },
|
|
6690
7161
|
networkPassphrase: input.networkPassphrase,
|
|
6691
7162
|
sorobanRpcUrl: input.sorobanRpcUrl,
|
|
6692
7163
|
transactEnvironment: input.transactEnvironment
|
|
@@ -6695,7 +7166,7 @@ async function submitSorobanConfidentialTransfer(input) {
|
|
|
6695
7166
|
contractClient: poolClient,
|
|
6696
7167
|
contractId: input.contractId,
|
|
6697
7168
|
from: input.walletPublicKey,
|
|
6698
|
-
nonce
|
|
7169
|
+
nonce,
|
|
6699
7170
|
proofHex: input.proofHex,
|
|
6700
7171
|
publicHex: input.publicHex,
|
|
6701
7172
|
...input.ciphertextHex ? { ciphertextHex: input.ciphertextHex } : {},
|
|
@@ -6726,12 +7197,14 @@ async function submitPreparedDeposit(prepared, environment) {
|
|
|
6726
7197
|
networkPassphrase: environment.network.networkPassphrase,
|
|
6727
7198
|
sorobanRpcUrl: environment.network.rpcUrl
|
|
6728
7199
|
});
|
|
7200
|
+
const nonce = transactNonceFromArtifacts(artifacts, environment);
|
|
6729
7201
|
const approval = await requestKytPassageForPoolInteraction({
|
|
6730
7202
|
owner: artifacts.walletPublicKey,
|
|
6731
7203
|
poolContract: environment.network.poolContract,
|
|
6732
7204
|
proofHex: artifacts.proofHex,
|
|
6733
7205
|
publicHex: artifacts.publicHex,
|
|
6734
7206
|
...optionalInspectCiphertext(artifacts),
|
|
7207
|
+
...artifacts.zkConfigNonce === void 0 ? {} : { zkConfigNonce: artifacts.zkConfigNonce },
|
|
6735
7208
|
networkPassphrase: environment.network.networkPassphrase,
|
|
6736
7209
|
sorobanRpcUrl: environment.network.rpcUrl,
|
|
6737
7210
|
transactEnvironment: environment
|
|
@@ -6740,7 +7213,7 @@ async function submitPreparedDeposit(prepared, environment) {
|
|
|
6740
7213
|
contractClient: poolClient,
|
|
6741
7214
|
contractId: environment.network.poolContract,
|
|
6742
7215
|
from: artifacts.walletPublicKey,
|
|
6743
|
-
nonce
|
|
7216
|
+
nonce,
|
|
6744
7217
|
proofHex: artifacts.proofHex,
|
|
6745
7218
|
publicHex: artifacts.publicHex,
|
|
6746
7219
|
...artifacts.ciphertextHex ? { ciphertextHex: artifacts.ciphertextHex } : {},
|
|
@@ -6761,6 +7234,7 @@ async function submitPreparedPoolTransact(prepared, environment) {
|
|
|
6761
7234
|
proofHex: artifacts.proofHex,
|
|
6762
7235
|
publicHex: artifacts.publicHex,
|
|
6763
7236
|
...optionalInspectCiphertext(artifacts),
|
|
7237
|
+
...artifacts.zkConfigNonce === void 0 ? {} : { zkConfigNonce: artifacts.zkConfigNonce },
|
|
6764
7238
|
networkPassphrase: environment.network.networkPassphrase,
|
|
6765
7239
|
sorobanRpcUrl: environment.network.rpcUrl,
|
|
6766
7240
|
transactEnvironment: environment
|
|
@@ -6816,7 +7290,7 @@ async function privacyPoolInitOptions(assets, transactEnvironment) {
|
|
|
6816
7290
|
};
|
|
6817
7291
|
}
|
|
6818
7292
|
async function createDefaultTransactEngine(assets, transactEnvironment) {
|
|
6819
|
-
await
|
|
7293
|
+
await PrivacyPoolSDK4.init(await privacyPoolInitOptions(assets, transactEnvironment));
|
|
6820
7294
|
if (transactEnvironment) {
|
|
6821
7295
|
configurePrivacyPoolService(
|
|
6822
7296
|
createPrivacyPoolService({
|