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