@arcanetech/privacy-sdk-stellar 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -0
- package/dist/index.js +215 -18
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +6 -0
- package/dist/node.js +209 -17
- package/dist/node.js.map +1 -1
- package/dist/transact/index.d.ts +6 -0
- package/dist/transact/index.js +131 -2
- package/dist/transact/index.js.map +1 -1
- package/generated/contract/README.md +2 -2
- package/generated/contract/dist/index.d.ts +106 -8
- package/generated/contract/dist/index.js +36 -8
- package/generated/contract/package.json +1 -1
- package/generated/contract/src/index.ts +98 -11
- package/generated/registry/package.json +1 -1
- package/package.json +8 -8
package/dist/node.d.ts
CHANGED
|
@@ -229,6 +229,12 @@ interface StellarKytEnvironment {
|
|
|
229
229
|
}
|
|
230
230
|
interface StellarTransactEnvironment {
|
|
231
231
|
network: StellarNetworkConfig;
|
|
232
|
+
/**
|
|
233
|
+
* Nonce (persistent storage key) of the pool's `ZkConfig` entry this
|
|
234
|
+
* environment's proofs/circuit shape target. Defaults to `0n` (the
|
|
235
|
+
* standard circuit) when omitted.
|
|
236
|
+
*/
|
|
237
|
+
zkConfigNonce?: bigint;
|
|
232
238
|
auditPublicKey?: AuditPublicKey;
|
|
233
239
|
kyt: StellarKytEnvironment;
|
|
234
240
|
signTransaction?: StellarSignTransaction;
|
package/dist/node.js
CHANGED
|
@@ -504,7 +504,7 @@ function createInternalPoolContractClient(input) {
|
|
|
504
504
|
networkPassphrase: input.networkPassphrase,
|
|
505
505
|
walletPublicKey: input.walletPublicKey
|
|
506
506
|
});
|
|
507
|
-
|
|
507
|
+
const client = new PrivacyPoolContractClient({
|
|
508
508
|
contractId: input.contractId,
|
|
509
509
|
networkPassphrase: input.networkPassphrase,
|
|
510
510
|
rpcUrl: input.sorobanRpcUrl,
|
|
@@ -512,6 +512,7 @@ function createInternalPoolContractClient(input) {
|
|
|
512
512
|
publicKey: input.walletPublicKey,
|
|
513
513
|
signTransaction: signBinding
|
|
514
514
|
});
|
|
515
|
+
return client;
|
|
515
516
|
}
|
|
516
517
|
|
|
517
518
|
// src/contracts/registry/create-registry-client.ts
|
|
@@ -1696,7 +1697,7 @@ function pickRecordsForSpendAmount(input) {
|
|
|
1696
1697
|
if (selectedTotal >= input.amount) {
|
|
1697
1698
|
return selected;
|
|
1698
1699
|
}
|
|
1699
|
-
if (input.kind === "transfer" && selected.length >= 2) {
|
|
1700
|
+
if ((input.kind === "transfer" || input.kind === "withdraw") && selected.length >= 2) {
|
|
1700
1701
|
throw insufficientStateError3(
|
|
1701
1702
|
"missing_private_records",
|
|
1702
1703
|
"Storage does not contain enough private records for this operation.",
|
|
@@ -6272,12 +6273,17 @@ function enrichWithdrawOutputRecords(prepared, proof) {
|
|
|
6272
6273
|
precommitementHex: changeCoin.precommitementHex
|
|
6273
6274
|
}));
|
|
6274
6275
|
}
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6276
|
+
function buildWithdrawFinalizeArtifacts(proof, context) {
|
|
6277
|
+
return {
|
|
6278
|
+
proofHex: proof.proof_hex,
|
|
6279
|
+
publicHex: proof.public_hex,
|
|
6280
|
+
applicationIdsPlaintext: proof.applicationIdsPlaintext,
|
|
6281
|
+
tokenAddress: context.tokenAddress,
|
|
6282
|
+
walletPublicKey: context.walletPublicKey,
|
|
6283
|
+
executeFinalizeRequired: false
|
|
6284
|
+
};
|
|
6285
|
+
}
|
|
6286
|
+
async function finalizeSingleWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
|
|
6281
6287
|
const context = await buildSpendProofContextAtExecute({
|
|
6282
6288
|
prepared,
|
|
6283
6289
|
environment,
|
|
@@ -6298,14 +6304,71 @@ async function finalizeWithdrawAtExecute(prepared, environment, poolService) {
|
|
|
6298
6304
|
tokenAddress: context.tokenAddress
|
|
6299
6305
|
});
|
|
6300
6306
|
enrichWithdrawOutputRecords(prepared, proof);
|
|
6301
|
-
return
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6307
|
+
return buildWithdrawFinalizeArtifacts(proof, context);
|
|
6308
|
+
}
|
|
6309
|
+
async function finalizeDualWithdrawAtExecute(prepared, environment, poolService, withdrawFrom) {
|
|
6310
|
+
const [, secondaryRecord] = prepared.consumedRecords;
|
|
6311
|
+
if (!secondaryRecord) {
|
|
6312
|
+
throw new Error("Dual withdraw execution requires two private input records.");
|
|
6313
|
+
}
|
|
6314
|
+
const context = await buildSpendProofContextAtExecute({
|
|
6315
|
+
prepared,
|
|
6316
|
+
environment,
|
|
6317
|
+
recipientPrivateAddressStpl1: withdrawFrom
|
|
6318
|
+
});
|
|
6319
|
+
const secondary = await readCoinEphemeralForRecord({
|
|
6320
|
+
record: secondaryRecord,
|
|
6321
|
+
environment,
|
|
6306
6322
|
walletPublicKey: context.walletPublicKey,
|
|
6307
|
-
|
|
6308
|
-
};
|
|
6323
|
+
commitments: context.commitments
|
|
6324
|
+
});
|
|
6325
|
+
const totalNotes = BigInt(context.coin.value) + BigInt(secondary.coin.value);
|
|
6326
|
+
const changeStroops = totalNotes - prepared.intent.amount;
|
|
6327
|
+
const proof = await poolService.prepareWithdrawTransactProofDual({
|
|
6328
|
+
coinA: context.coin,
|
|
6329
|
+
coinB: secondary.coin,
|
|
6330
|
+
state: { commitments: context.commitments },
|
|
6331
|
+
destinationStellarAddress: prepared.intent.to,
|
|
6332
|
+
privKeyScalarHex: context.senderPrivKeyScalarHex,
|
|
6333
|
+
depositorEphemeralAKey: serializeEphemeralKeyString({
|
|
6334
|
+
xHex: context.ephemeral.xHex,
|
|
6335
|
+
yHex: context.ephemeral.yHex
|
|
6336
|
+
}),
|
|
6337
|
+
depositorEphemeralBKey: serializeEphemeralKeyString({
|
|
6338
|
+
xHex: secondary.ephemeral.xHex,
|
|
6339
|
+
yHex: secondary.ephemeral.yHex
|
|
6340
|
+
}),
|
|
6341
|
+
withdrawAmountStroops: prepared.intent.amount,
|
|
6342
|
+
changePrivateAddressStpl1: changeStroops > 0n ? withdrawFrom : void 0,
|
|
6343
|
+
tokenAddress: context.tokenAddress
|
|
6344
|
+
});
|
|
6345
|
+
enrichWithdrawOutputRecords(prepared, proof);
|
|
6346
|
+
return buildWithdrawFinalizeArtifacts(proof, context);
|
|
6347
|
+
}
|
|
6348
|
+
async function finalizeWithdrawAtExecute(prepared, environment, poolService) {
|
|
6349
|
+
if (prepared.kind !== "withdraw") {
|
|
6350
|
+
throw new Error("Withdraw finalize requires a withdraw operation.");
|
|
6351
|
+
}
|
|
6352
|
+
const withdrawIntent = prepared.intent;
|
|
6353
|
+
const withdrawFrom = withdrawIntent.from;
|
|
6354
|
+
const recordCount = prepared.consumedRecords.length;
|
|
6355
|
+
if (recordCount === 1) {
|
|
6356
|
+
return finalizeSingleWithdrawAtExecute(
|
|
6357
|
+
prepared,
|
|
6358
|
+
environment,
|
|
6359
|
+
poolService,
|
|
6360
|
+
withdrawFrom
|
|
6361
|
+
);
|
|
6362
|
+
}
|
|
6363
|
+
if (recordCount === 2) {
|
|
6364
|
+
return finalizeDualWithdrawAtExecute(
|
|
6365
|
+
prepared,
|
|
6366
|
+
environment,
|
|
6367
|
+
poolService,
|
|
6368
|
+
withdrawFrom
|
|
6369
|
+
);
|
|
6370
|
+
}
|
|
6371
|
+
throw new Error("Confidential withdraw execution supports at most two input notes.");
|
|
6309
6372
|
}
|
|
6310
6373
|
async function finalizeSpendOperationAtExecute(prepared, environment, poolService) {
|
|
6311
6374
|
if (prepared.kind !== "transfer" && prepared.kind !== "withdraw") {
|
|
@@ -6463,6 +6526,12 @@ async function registerKytPassage(parameters) {
|
|
|
6463
6526
|
await waitForKytRegistration({ server, hash: result.hash });
|
|
6464
6527
|
}
|
|
6465
6528
|
|
|
6529
|
+
// src/transact/environment/zk-config-nonce.ts
|
|
6530
|
+
var DEFAULT_ZK_CONFIG_NONCE = 0n;
|
|
6531
|
+
function resolveZkConfigNonce(transactEnvironment) {
|
|
6532
|
+
return transactEnvironment.zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
|
|
6533
|
+
}
|
|
6534
|
+
|
|
6466
6535
|
// src/transact/kyt/passage/inspect.ts
|
|
6467
6536
|
function buildApplicationIdHints(applicationId) {
|
|
6468
6537
|
return [applicationId, applicationId, applicationId, applicationId];
|
|
@@ -6516,6 +6585,7 @@ async function requestKytPassageForPoolInteraction(input) {
|
|
|
6516
6585
|
decryptedAuditSlots: input.decryptedAuditSlots,
|
|
6517
6586
|
onboarding: input.onboarding,
|
|
6518
6587
|
nonce: buildNonce(),
|
|
6588
|
+
zkConfigNonce: resolveZkConfigNonce(input.transactEnvironment),
|
|
6519
6589
|
currentLedger
|
|
6520
6590
|
},
|
|
6521
6591
|
kytJsonReplacer
|
|
@@ -6538,11 +6608,123 @@ async function requestKytPassageForPoolInteraction(input) {
|
|
|
6538
6608
|
return body;
|
|
6539
6609
|
}
|
|
6540
6610
|
|
|
6611
|
+
// src/transact/submit/zk-config-ttl.ts
|
|
6612
|
+
import {
|
|
6613
|
+
Address as Address2,
|
|
6614
|
+
Operation as Operation2,
|
|
6615
|
+
SorobanDataBuilder,
|
|
6616
|
+
TransactionBuilder as TransactionBuilder2,
|
|
6617
|
+
rpc as rpc4,
|
|
6618
|
+
xdr as xdr3
|
|
6619
|
+
} from "@stellar/stellar-sdk";
|
|
6620
|
+
var ZK_CONFIG_TTL_THRESHOLD_LEDGERS = 1e5;
|
|
6621
|
+
var ZK_CONFIG_TTL_EXTEND_TO_LEDGERS = 5e5;
|
|
6622
|
+
var EXTEND_TTL_FEE_STROOPS = "1000000";
|
|
6623
|
+
var EXTEND_TTL_TIMEOUT_SECONDS = 300;
|
|
6624
|
+
var EXTEND_TTL_MAX_POLLS = 60;
|
|
6625
|
+
var EXTEND_TTL_POLL_INTERVAL_MS = 1e3;
|
|
6626
|
+
async function waitForExtendFootprintTtlConfirmation(parameters) {
|
|
6627
|
+
for (let attempt = 0; attempt < EXTEND_TTL_MAX_POLLS; attempt += 1) {
|
|
6628
|
+
const response = await parameters.server.getTransaction(parameters.hash);
|
|
6629
|
+
if (response.status === "SUCCESS") {
|
|
6630
|
+
return;
|
|
6631
|
+
}
|
|
6632
|
+
if (response.status === "FAILED") {
|
|
6633
|
+
throw new Error("ZK config TTL extension failed on-chain");
|
|
6634
|
+
}
|
|
6635
|
+
await new Promise((resolve) => {
|
|
6636
|
+
setTimeout(resolve, EXTEND_TTL_POLL_INTERVAL_MS);
|
|
6637
|
+
});
|
|
6638
|
+
}
|
|
6639
|
+
throw new Error("ZK config TTL extension was not confirmed in time");
|
|
6640
|
+
}
|
|
6641
|
+
function zkConfigLedgerKey(contractId, nonce) {
|
|
6642
|
+
const configKeyScValue = xdr3.ScVal.scvVec([
|
|
6643
|
+
xdr3.ScVal.scvSymbol("Config"),
|
|
6644
|
+
xdr3.ScVal.scvU64(xdr3.Uint64.fromString(nonce.toString()))
|
|
6645
|
+
]);
|
|
6646
|
+
return xdr3.LedgerKey.contractData(
|
|
6647
|
+
new xdr3.LedgerKeyContractData({
|
|
6648
|
+
contract: new Address2(contractId).toScAddress(),
|
|
6649
|
+
key: configKeyScValue,
|
|
6650
|
+
durability: xdr3.ContractDataDurability.persistent()
|
|
6651
|
+
})
|
|
6652
|
+
);
|
|
6653
|
+
}
|
|
6654
|
+
async function needsTtlExtension(parameters) {
|
|
6655
|
+
const [entriesResponse, latestLedger] = await Promise.all([
|
|
6656
|
+
parameters.server.getLedgerEntries(parameters.ledgerKey),
|
|
6657
|
+
parameters.server.getLatestLedger()
|
|
6658
|
+
]);
|
|
6659
|
+
const entry = entriesResponse.entries.at(0);
|
|
6660
|
+
if (!entry || entry.liveUntilLedgerSeq === void 0) {
|
|
6661
|
+
return false;
|
|
6662
|
+
}
|
|
6663
|
+
const remainingLedgers = entry.liveUntilLedgerSeq - latestLedger.sequence;
|
|
6664
|
+
return remainingLedgers < ZK_CONFIG_TTL_THRESHOLD_LEDGERS;
|
|
6665
|
+
}
|
|
6666
|
+
async function submitExtendFootprintTtlTransaction(parameters) {
|
|
6667
|
+
const sourceAccount = await parameters.server.getAccount(parameters.sourcePublicKey);
|
|
6668
|
+
const transaction = new TransactionBuilder2(sourceAccount, {
|
|
6669
|
+
fee: EXTEND_TTL_FEE_STROOPS,
|
|
6670
|
+
networkPassphrase: parameters.networkPassphrase
|
|
6671
|
+
}).addOperation(
|
|
6672
|
+
Operation2.extendFootprintTtl({ extendTo: ZK_CONFIG_TTL_EXTEND_TO_LEDGERS })
|
|
6673
|
+
).setSorobanData(
|
|
6674
|
+
new SorobanDataBuilder().setReadOnly([parameters.ledgerKey]).build()
|
|
6675
|
+
).setTimeout(EXTEND_TTL_TIMEOUT_SECONDS).build();
|
|
6676
|
+
const preparedTransaction = await parameters.server.prepareTransaction(transaction);
|
|
6677
|
+
const { signedTxXdr } = await parameters.signTransaction({
|
|
6678
|
+
xdr: preparedTransaction.toXDR(),
|
|
6679
|
+
networkPassphrase: parameters.networkPassphrase,
|
|
6680
|
+
address: parameters.sourcePublicKey
|
|
6681
|
+
});
|
|
6682
|
+
const signedTransaction = TransactionBuilder2.fromXDR(
|
|
6683
|
+
signedTxXdr,
|
|
6684
|
+
parameters.networkPassphrase
|
|
6685
|
+
);
|
|
6686
|
+
const result = await parameters.server.sendTransaction(signedTransaction);
|
|
6687
|
+
const status = String(result.status);
|
|
6688
|
+
if (status !== "PENDING" && status !== "SUCCESS") {
|
|
6689
|
+
throw new Error(`ZK config TTL extension submit failed: ${status}`);
|
|
6690
|
+
}
|
|
6691
|
+
await waitForExtendFootprintTtlConfirmation({
|
|
6692
|
+
server: parameters.server,
|
|
6693
|
+
hash: result.hash
|
|
6694
|
+
});
|
|
6695
|
+
}
|
|
6696
|
+
async function extendZkConfigTtlIfNeeded(parameters) {
|
|
6697
|
+
if (!parameters.signTransaction) {
|
|
6698
|
+
return;
|
|
6699
|
+
}
|
|
6700
|
+
const server = new rpc4.Server(parameters.sorobanRpcUrl, { allowHttp: true });
|
|
6701
|
+
const ledgerKey = zkConfigLedgerKey(parameters.contractId, parameters.nonce);
|
|
6702
|
+
if (!await needsTtlExtension({ server, ledgerKey })) {
|
|
6703
|
+
return;
|
|
6704
|
+
}
|
|
6705
|
+
await submitExtendFootprintTtlTransaction({
|
|
6706
|
+
server,
|
|
6707
|
+
ledgerKey,
|
|
6708
|
+
networkPassphrase: parameters.networkPassphrase,
|
|
6709
|
+
sourcePublicKey: parameters.sourcePublicKey,
|
|
6710
|
+
signTransaction: parameters.signTransaction
|
|
6711
|
+
});
|
|
6712
|
+
}
|
|
6713
|
+
|
|
6541
6714
|
// src/transact/submit/pool-transact.ts
|
|
6542
6715
|
import { Buffer as Buffer17 } from "buffer";
|
|
6543
6716
|
async function submitPoolTransact(parameters) {
|
|
6717
|
+
await extendZkConfigTtlIfNeeded({
|
|
6718
|
+
sorobanRpcUrl: parameters.sorobanRpcUrl,
|
|
6719
|
+
networkPassphrase: parameters.networkPassphrase,
|
|
6720
|
+
contractId: parameters.contractId,
|
|
6721
|
+
nonce: parameters.nonce,
|
|
6722
|
+
sourcePublicKey: parameters.from,
|
|
6723
|
+
signTransaction: parameters.transactEnvironment.signTransaction
|
|
6724
|
+
});
|
|
6544
6725
|
const assembledTransaction = await parameters.contractClient.transact({
|
|
6545
6726
|
from: parameters.from,
|
|
6727
|
+
nonce: parameters.nonce,
|
|
6546
6728
|
proof_bytes: Buffer17.from(parameters.proofHex.replace(/^0x/iu, ""), "hex"),
|
|
6547
6729
|
pub_signals_bytes: Buffer17.from(parameters.publicHex.replace(/^0x/iu, ""), "hex"),
|
|
6548
6730
|
onboarding: parameters.onboarding,
|
|
@@ -6581,11 +6763,16 @@ async function submitSorobanConfidentialTransfer(input) {
|
|
|
6581
6763
|
});
|
|
6582
6764
|
return submitPoolTransact({
|
|
6583
6765
|
contractClient: poolClient,
|
|
6766
|
+
contractId: input.contractId,
|
|
6584
6767
|
from: input.walletPublicKey,
|
|
6768
|
+
nonce: resolveZkConfigNonce(input.transactEnvironment),
|
|
6585
6769
|
proofHex: input.proofHex,
|
|
6586
6770
|
publicHex: input.publicHex,
|
|
6587
6771
|
...input.onboarding ? { onboarding: input.onboarding } : {},
|
|
6588
|
-
approval
|
|
6772
|
+
approval,
|
|
6773
|
+
networkPassphrase: input.networkPassphrase,
|
|
6774
|
+
sorobanRpcUrl: input.sorobanRpcUrl,
|
|
6775
|
+
transactEnvironment: input.transactEnvironment
|
|
6589
6776
|
});
|
|
6590
6777
|
}
|
|
6591
6778
|
|
|
@@ -6614,10 +6801,15 @@ async function submitPreparedDeposit(prepared, environment) {
|
|
|
6614
6801
|
});
|
|
6615
6802
|
return submitPoolTransact({
|
|
6616
6803
|
contractClient: poolClient,
|
|
6804
|
+
contractId: environment.network.poolContract,
|
|
6617
6805
|
from: artifacts.walletPublicKey,
|
|
6806
|
+
nonce: resolveZkConfigNonce(environment),
|
|
6618
6807
|
proofHex: artifacts.proofHex,
|
|
6619
6808
|
publicHex: artifacts.publicHex,
|
|
6620
|
-
approval
|
|
6809
|
+
approval,
|
|
6810
|
+
networkPassphrase: environment.network.networkPassphrase,
|
|
6811
|
+
sorobanRpcUrl: environment.network.rpcUrl,
|
|
6812
|
+
transactEnvironment: environment
|
|
6621
6813
|
});
|
|
6622
6814
|
}
|
|
6623
6815
|
async function submitPreparedPoolTransact(prepared, environment) {
|