@buildonspark/spark-sdk 0.2.2 → 0.2.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.
Files changed (94) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/{chunk-TM6CHQXC.js → chunk-3SEOTO43.js} +1 -1
  3. package/dist/{chunk-2ENZX6LT.js → chunk-AAZWSPUK.js} +84 -8
  4. package/dist/{chunk-4JD4HIAN.js → chunk-G4MSZ6DE.js} +299 -1
  5. package/dist/{chunk-S2AL73MZ.js → chunk-TVUMSHWA.js} +1 -1
  6. package/dist/{chunk-2TUM3R6C.js → chunk-W4ZRBSWM.js} +2351 -797
  7. package/dist/{chunk-CDLETEDT.js → chunk-WAQKYSDI.js} +13 -1
  8. package/dist/{client-CGTRS23n.d.ts → client-BF4cn8F4.d.ts} +15 -3
  9. package/dist/{client-CcYzmpmj.d.cts → client-KhNkrXz4.d.cts} +15 -3
  10. package/dist/debug.cjs +2948 -1023
  11. package/dist/debug.d.cts +19 -6
  12. package/dist/debug.d.ts +19 -6
  13. package/dist/debug.js +5 -5
  14. package/dist/graphql/objects/index.cjs +13 -1
  15. package/dist/graphql/objects/index.d.cts +2 -2
  16. package/dist/graphql/objects/index.d.ts +2 -2
  17. package/dist/graphql/objects/index.js +1 -1
  18. package/dist/index.cjs +2794 -858
  19. package/dist/index.d.cts +190 -9
  20. package/dist/index.d.ts +190 -9
  21. package/dist/index.js +32 -6
  22. package/dist/index.node.cjs +2931 -892
  23. package/dist/index.node.d.cts +10 -188
  24. package/dist/index.node.d.ts +10 -188
  25. package/dist/index.node.js +134 -6
  26. package/dist/native/index.cjs +2794 -858
  27. package/dist/native/index.d.cts +148 -40
  28. package/dist/native/index.d.ts +148 -40
  29. package/dist/native/index.js +2799 -877
  30. package/dist/proto/lrc20.d.cts +1 -1
  31. package/dist/proto/lrc20.d.ts +1 -1
  32. package/dist/proto/lrc20.js +1 -1
  33. package/dist/proto/spark.cjs +84 -8
  34. package/dist/proto/spark.d.cts +1 -1
  35. package/dist/proto/spark.d.ts +1 -1
  36. package/dist/proto/spark.js +1 -1
  37. package/dist/proto/spark_token.cjs +301 -0
  38. package/dist/proto/spark_token.d.cts +35 -2
  39. package/dist/proto/spark_token.d.ts +35 -2
  40. package/dist/proto/spark_token.js +8 -2
  41. package/dist/{sdk-types-DJ2ve9YY.d.cts → sdk-types-CB9HrW5O.d.cts} +1 -1
  42. package/dist/{sdk-types-DCIVdKUT.d.ts → sdk-types-CkRNraXT.d.ts} +1 -1
  43. package/dist/{spark-BUOx3U7Q.d.cts → spark-B_7nZx6T.d.cts} +112 -10
  44. package/dist/{spark-BUOx3U7Q.d.ts → spark-B_7nZx6T.d.ts} +112 -10
  45. package/dist/{spark-wallet-B_96y9BS.d.ts → spark-wallet-C1Tr_VKI.d.ts} +38 -28
  46. package/dist/{spark-wallet-CHwKQYJu.d.cts → spark-wallet-DG3x2obf.d.cts} +38 -28
  47. package/dist/spark-wallet.node-CGxoeCpH.d.ts +13 -0
  48. package/dist/spark-wallet.node-CN9LoB_O.d.cts +13 -0
  49. package/dist/tests/test-utils.cjs +1086 -218
  50. package/dist/tests/test-utils.d.cts +13 -13
  51. package/dist/tests/test-utils.d.ts +13 -13
  52. package/dist/tests/test-utils.js +56 -19
  53. package/dist/types/index.cjs +97 -9
  54. package/dist/types/index.d.cts +3 -3
  55. package/dist/types/index.d.ts +3 -3
  56. package/dist/types/index.js +3 -3
  57. package/dist/{xchain-address-D5MIHCDL.d.cts → xchain-address-BHu6CpZC.d.ts} +55 -8
  58. package/dist/{xchain-address-DLbW1iDh.d.ts → xchain-address-HBr6isnc.d.cts} +55 -8
  59. package/package.json +1 -1
  60. package/src/graphql/client.ts +8 -0
  61. package/src/graphql/mutations/CompleteLeavesSwap.ts +9 -1
  62. package/src/graphql/mutations/RequestSwapLeaves.ts +4 -0
  63. package/src/graphql/objects/CompleteLeavesSwapInput.ts +34 -34
  64. package/src/graphql/objects/LeavesSwapRequest.ts +4 -0
  65. package/src/graphql/objects/RequestLeavesSwapInput.ts +48 -47
  66. package/src/graphql/objects/SwapLeaf.ts +40 -32
  67. package/src/graphql/objects/UserLeafInput.ts +24 -0
  68. package/src/graphql/objects/UserRequest.ts +4 -0
  69. package/src/index.node.ts +1 -1
  70. package/src/native/index.ts +4 -5
  71. package/src/proto/spark.ts +172 -16
  72. package/src/proto/spark_token.ts +369 -0
  73. package/src/services/coop-exit.ts +171 -36
  74. package/src/services/deposit.ts +471 -74
  75. package/src/services/lightning.ts +18 -5
  76. package/src/services/signing.ts +162 -50
  77. package/src/services/token-transactions.ts +6 -2
  78. package/src/services/transfer.ts +950 -384
  79. package/src/services/tree-creation.ts +342 -121
  80. package/src/spark-wallet/spark-wallet.node.ts +71 -66
  81. package/src/spark-wallet/spark-wallet.ts +459 -166
  82. package/src/tests/integration/coop-exit.test.ts +3 -8
  83. package/src/tests/integration/deposit.test.ts +3 -3
  84. package/src/tests/integration/lightning.test.ts +521 -466
  85. package/src/tests/integration/swap.test.ts +559 -307
  86. package/src/tests/integration/transfer.test.ts +625 -623
  87. package/src/tests/integration/wallet.test.ts +2 -2
  88. package/src/tests/integration/watchtower.test.ts +211 -0
  89. package/src/tests/test-utils.ts +63 -14
  90. package/src/tests/utils/test-faucet.ts +4 -2
  91. package/src/utils/adaptor-signature.ts +15 -5
  92. package/src/utils/fetch.ts +75 -0
  93. package/src/utils/mempool.ts +9 -4
  94. package/src/utils/transaction.ts +388 -26
@@ -14336,14 +14336,6 @@ var SparkServiceDefinition = {
14336
14336
  responseStream: false,
14337
14337
  options: {}
14338
14338
  },
14339
- create_tree_v2: {
14340
- name: "create_tree_v2",
14341
- requestType: CreateTreeRequest,
14342
- requestStream: false,
14343
- responseType: CreateTreeResponse,
14344
- responseStream: false,
14345
- options: {}
14346
- },
14347
14339
  get_signing_operator_list: {
14348
14340
  name: "get_signing_operator_list",
14349
14341
  requestType: Empty,
@@ -14510,6 +14502,90 @@ var SparkServiceDefinition = {
14510
14502
  responseType: ExitSingleNodeTreesResponse,
14511
14503
  responseStream: false,
14512
14504
  options: {}
14505
+ },
14506
+ /**
14507
+ * The following endpoints enforce inclusion of Direct Transactions used
14508
+ * for unilateral exits
14509
+ */
14510
+ create_tree_v2: {
14511
+ name: "create_tree_v2",
14512
+ requestType: CreateTreeRequest,
14513
+ requestStream: false,
14514
+ responseType: CreateTreeResponse,
14515
+ responseStream: false,
14516
+ options: {}
14517
+ },
14518
+ cooperative_exit_v2: {
14519
+ name: "cooperative_exit_v2",
14520
+ requestType: CooperativeExitRequest,
14521
+ requestStream: false,
14522
+ responseType: CooperativeExitResponse,
14523
+ responseStream: false,
14524
+ options: {}
14525
+ },
14526
+ extend_leaf_v2: {
14527
+ name: "extend_leaf_v2",
14528
+ requestType: ExtendLeafRequest,
14529
+ requestStream: false,
14530
+ responseType: ExtendLeafResponse,
14531
+ responseStream: false,
14532
+ options: {}
14533
+ },
14534
+ claim_transfer_sign_refunds_v2: {
14535
+ name: "claim_transfer_sign_refunds_v2",
14536
+ requestType: ClaimTransferSignRefundsRequest,
14537
+ requestStream: false,
14538
+ responseType: ClaimTransferSignRefundsResponse,
14539
+ responseStream: false,
14540
+ options: {}
14541
+ },
14542
+ finalize_node_signatures_v2: {
14543
+ name: "finalize_node_signatures_v2",
14544
+ requestType: FinalizeNodeSignaturesRequest,
14545
+ requestStream: false,
14546
+ responseType: FinalizeNodeSignaturesResponse,
14547
+ responseStream: false,
14548
+ options: {}
14549
+ },
14550
+ initiate_preimage_swap_v2: {
14551
+ name: "initiate_preimage_swap_v2",
14552
+ requestType: InitiatePreimageSwapRequest,
14553
+ requestStream: false,
14554
+ responseType: InitiatePreimageSwapResponse,
14555
+ responseStream: false,
14556
+ options: {}
14557
+ },
14558
+ start_leaf_swap_v2: {
14559
+ name: "start_leaf_swap_v2",
14560
+ requestType: StartTransferRequest,
14561
+ requestStream: false,
14562
+ responseType: StartTransferResponse,
14563
+ responseStream: false,
14564
+ options: {}
14565
+ },
14566
+ counter_leaf_swap_v2: {
14567
+ name: "counter_leaf_swap_v2",
14568
+ requestType: CounterLeafSwapRequest,
14569
+ requestStream: false,
14570
+ responseType: CounterLeafSwapResponse,
14571
+ responseStream: false,
14572
+ options: {}
14573
+ },
14574
+ start_transfer_v2: {
14575
+ name: "start_transfer_v2",
14576
+ requestType: StartTransferRequest,
14577
+ requestStream: false,
14578
+ responseType: StartTransferResponse,
14579
+ responseStream: false,
14580
+ options: {}
14581
+ },
14582
+ refresh_timelock_v2: {
14583
+ name: "refresh_timelock_v2",
14584
+ requestType: RefreshTimelockRequest,
14585
+ requestStream: false,
14586
+ responseType: RefreshTimelockResponse,
14587
+ responseStream: false,
14588
+ options: {}
14513
14589
  }
14514
14590
  }
14515
14591
  };
@@ -14732,13 +14808,24 @@ function applyAdaptorToSignature(pubkey, hash, signature, adaptorPrivateKeyBytes
14732
14808
  const adaptorPrivateKey = bytesToNumberBE2(adaptorPrivateKeyBytes);
14733
14809
  const newS = mod(sBigInt + adaptorPrivateKey, secp256k12.CURVE.n);
14734
14810
  const newSig = new Uint8Array([...r, ...numberToBytesBE(newS, 32)]);
14735
- if (schnorr.verify(newSig, hash, pubkey)) {
14736
- return newSig;
14811
+ try {
14812
+ if (schnorr.verify(newSig, hash, pubkey)) {
14813
+ return newSig;
14814
+ }
14815
+ } catch (e) {
14816
+ console.error("[applyAdaptorToSignature] Addition verification failed:", e);
14737
14817
  }
14738
14818
  const altS = mod(sBigInt - adaptorPrivateKey, secp256k12.CURVE.n);
14739
14819
  const altSig = new Uint8Array([...r, ...numberToBytesBE(altS, 32)]);
14740
- if (schnorr.verify(altSig, hash, pubkey)) {
14741
- return altSig;
14820
+ try {
14821
+ if (schnorr.verify(altSig, hash, pubkey)) {
14822
+ return altSig;
14823
+ }
14824
+ } catch (e) {
14825
+ console.error(
14826
+ "[applyAdaptorToSignature] Subtraction verification failed:",
14827
+ e
14828
+ );
14742
14829
  }
14743
14830
  throw new Error("Cannot apply adaptor to signature");
14744
14831
  }
@@ -15605,7 +15692,11 @@ var SwapLeafFromJson = (obj) => {
15605
15692
  return {
15606
15693
  leafId: obj["swap_leaf_leaf_id"],
15607
15694
  rawUnsignedRefundTransaction: obj["swap_leaf_raw_unsigned_refund_transaction"],
15608
- adaptorSignedSignature: obj["swap_leaf_adaptor_signed_signature"]
15695
+ adaptorSignedSignature: obj["swap_leaf_adaptor_signed_signature"],
15696
+ directRawUnsignedRefundTransaction: obj["swap_leaf_direct_raw_unsigned_refund_transaction"],
15697
+ directAdaptorSignedSignature: obj["swap_leaf_direct_adaptor_signed_signature"],
15698
+ directFromCpfpRawUnsignedRefundTransaction: obj["swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction"],
15699
+ directFromCpfpAdaptorSignedSignature: obj["swap_leaf_direct_from_cpfp_adaptor_signed_signature"]
15609
15700
  };
15610
15701
  };
15611
15702
 
@@ -15851,6 +15942,10 @@ fragment LeavesSwapRequestFragment on LeavesSwapRequest {
15851
15942
  swap_leaf_leaf_id: leaf_id
15852
15943
  swap_leaf_raw_unsigned_refund_transaction: raw_unsigned_refund_transaction
15853
15944
  swap_leaf_adaptor_signed_signature: adaptor_signed_signature
15945
+ swap_leaf_direct_raw_unsigned_refund_transaction: direct_raw_unsigned_refund_transaction
15946
+ swap_leaf_direct_adaptor_signed_signature: direct_adaptor_signed_signature
15947
+ swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction: direct_from_cpfp_raw_unsigned_refund_transaction
15948
+ swap_leaf_direct_from_cpfp_adaptor_signed_signature: direct_from_cpfp_adaptor_signed_signature
15854
15949
  }
15855
15950
  }`;
15856
15951
 
@@ -16180,6 +16275,10 @@ fragment UserRequestFragment on UserRequest {
16180
16275
  swap_leaf_leaf_id: leaf_id
16181
16276
  swap_leaf_raw_unsigned_refund_transaction: raw_unsigned_refund_transaction
16182
16277
  swap_leaf_adaptor_signed_signature: adaptor_signed_signature
16278
+ swap_leaf_direct_raw_unsigned_refund_transaction: direct_raw_unsigned_refund_transaction
16279
+ swap_leaf_direct_adaptor_signed_signature: direct_adaptor_signed_signature
16280
+ swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction: direct_from_cpfp_raw_unsigned_refund_transaction
16281
+ swap_leaf_direct_from_cpfp_adaptor_signed_signature: direct_from_cpfp_adaptor_signed_signature
16183
16282
  }
16184
16283
  }
16185
16284
  ... on LightningReceiveRequest {
@@ -16304,18 +16403,39 @@ function mapTransferToWalletTransfer(proto, identityPublicKey) {
16304
16403
  };
16305
16404
  }
16306
16405
 
16406
+ // src/utils/fetch.ts
16407
+ var fetchImpl = typeof window !== "undefined" && window.fetch ? window.fetch.bind(window) : globalThis.fetch ? globalThis.fetch : null;
16408
+ var Headers = globalThis.Headers ?? null;
16409
+ var getFetch = () => {
16410
+ if (!fetchImpl) {
16411
+ throw new Error(
16412
+ "Fetch implementation is not set. Please set it using setFetch()."
16413
+ );
16414
+ }
16415
+ if (!Headers) {
16416
+ throw new Error(
16417
+ "Headers implementation is not set. Please set it using setFetch()."
16418
+ );
16419
+ }
16420
+ return {
16421
+ fetch: fetchImpl,
16422
+ Headers
16423
+ };
16424
+ };
16425
+
16307
16426
  // src/utils/mempool.ts
16308
16427
  async function getLatestDepositTxId(address2) {
16428
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
16309
16429
  const network = getNetworkFromAddress(address2);
16310
16430
  const baseUrl = network === BitcoinNetwork_default.REGTEST ? getElectrsUrl("REGTEST") : getElectrsUrl("MAINNET");
16311
- const headers = {};
16431
+ const headers = new Headers2();
16312
16432
  if (network === BitcoinNetwork_default.REGTEST) {
16313
16433
  const auth = btoa(
16314
16434
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
16315
16435
  );
16316
- headers["Authorization"] = `Basic ${auth}`;
16436
+ headers.set("Authorization", `Basic ${auth}`);
16317
16437
  }
16318
- const response = await fetch(`${baseUrl}/address/${address2}/txs`, {
16438
+ const response = await fetch2(`${baseUrl}/address/${address2}/txs`, {
16319
16439
  headers
16320
16440
  });
16321
16441
  const addressTxs = await response.json();
@@ -16332,14 +16452,15 @@ async function getLatestDepositTxId(address2) {
16332
16452
  return null;
16333
16453
  }
16334
16454
  async function isTxBroadcast(txid, baseUrl, network) {
16335
- const headers = {};
16455
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
16456
+ const headers = new Headers2();
16336
16457
  if (network === 3 /* REGTEST */) {
16337
16458
  const auth = btoa(
16338
16459
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
16339
16460
  );
16340
- headers["Authorization"] = `Basic ${auth}`;
16461
+ headers.set("Authorization", `Basic ${auth}`);
16341
16462
  }
16342
- const response = await fetch(`${baseUrl}/tx/${txid}`, {
16463
+ const response = await fetch2(`${baseUrl}/tx/${txid}`, {
16343
16464
  headers
16344
16465
  });
16345
16466
  const tx = await response.json();
@@ -16802,7 +16923,14 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
16802
16923
 
16803
16924
  // src/utils/transaction.ts
16804
16925
  import { Transaction as Transaction2 } from "@scure/btc-signer";
16926
+ var INITIAL_TIMELOCK = 2e3;
16927
+ var TEST_UNILATERAL_TIMELOCK = 100;
16805
16928
  var TIME_LOCK_INTERVAL = 100;
16929
+ var DIRECT_TIMELOCK_OFFSET = 50;
16930
+ var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
16931
+ var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
16932
+ var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
16933
+ var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
16806
16934
  var ESTIMATED_TX_SIZE = 191;
16807
16935
  var DEFAULT_SATS_PER_VBYTE = 5;
16808
16936
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -16812,29 +16940,270 @@ function maybeApplyFee(amount) {
16812
16940
  }
16813
16941
  return amount;
16814
16942
  }
16815
- function createRefundTx(sequence, nodeOutPoint, amountSats, receivingPubkey, network) {
16816
- const newRefundTx = new Transaction2({
16943
+ function createRootTx(depositOutPoint, depositTxOut) {
16944
+ const cpfpRootTx = new Transaction2({
16945
+ version: 3,
16946
+ allowUnknownOutputs: true
16947
+ });
16948
+ cpfpRootTx.addInput(depositOutPoint);
16949
+ cpfpRootTx.addOutput(depositTxOut);
16950
+ cpfpRootTx.addOutput(getEphemeralAnchorOutput());
16951
+ const directRootTx = new Transaction2({
16952
+ version: 3,
16953
+ allowUnknownOutputs: true
16954
+ });
16955
+ directRootTx.addInput(depositOutPoint);
16956
+ directRootTx.addOutput({
16957
+ script: depositTxOut.script,
16958
+ amount: maybeApplyFee(depositTxOut.amount ?? 0n)
16959
+ });
16960
+ return [cpfpRootTx, directRootTx];
16961
+ }
16962
+ function createSplitTx(parentOutPoint, childTxOuts) {
16963
+ const cpfpSplitTx = new Transaction2({
16964
+ version: 3,
16965
+ allowUnknownOutputs: true
16966
+ });
16967
+ cpfpSplitTx.addInput(parentOutPoint);
16968
+ for (const txOut of childTxOuts) {
16969
+ cpfpSplitTx.addOutput(txOut);
16970
+ }
16971
+ cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
16972
+ const directSplitTx = new Transaction2({
16973
+ version: 3,
16974
+ allowUnknownOutputs: true
16975
+ });
16976
+ directSplitTx.addInput(parentOutPoint);
16977
+ let totalOutputAmount = 0n;
16978
+ for (const txOut of childTxOuts) {
16979
+ totalOutputAmount += txOut.amount ?? 0n;
16980
+ }
16981
+ if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
16982
+ const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
16983
+ for (const txOut of childTxOuts) {
16984
+ const adjustedAmount = BigInt(
16985
+ Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
16986
+ );
16987
+ directSplitTx.addOutput({
16988
+ script: txOut.script,
16989
+ amount: adjustedAmount
16990
+ });
16991
+ }
16992
+ } else {
16993
+ for (const txOut of childTxOuts) {
16994
+ directSplitTx.addOutput(txOut);
16995
+ }
16996
+ }
16997
+ return [cpfpSplitTx, directSplitTx];
16998
+ }
16999
+ function createNodeTx({
17000
+ txOut,
17001
+ parentOutPoint,
17002
+ applyFee,
17003
+ includeAnchor
17004
+ }) {
17005
+ const nodeTx = new Transaction2({
17006
+ version: 3,
17007
+ allowUnknownOutputs: true
17008
+ });
17009
+ nodeTx.addInput(parentOutPoint);
17010
+ if (applyFee) {
17011
+ nodeTx.addOutput({
17012
+ script: txOut.script,
17013
+ amount: maybeApplyFee(txOut.amount ?? 0n)
17014
+ });
17015
+ } else {
17016
+ nodeTx.addOutput(txOut);
17017
+ }
17018
+ if (includeAnchor) {
17019
+ nodeTx.addOutput(getEphemeralAnchorOutput());
17020
+ }
17021
+ return nodeTx;
17022
+ }
17023
+ function createNodeTxs(txOut, txIn, directTxIn) {
17024
+ const cpfpNodeTx = createNodeTx({
17025
+ txOut,
17026
+ parentOutPoint: txIn,
17027
+ includeAnchor: true
17028
+ });
17029
+ let directNodeTx;
17030
+ if (directTxIn) {
17031
+ directNodeTx = createNodeTx({
17032
+ txOut,
17033
+ parentOutPoint: directTxIn,
17034
+ includeAnchor: false,
17035
+ applyFee: true
17036
+ });
17037
+ }
17038
+ return { cpfpNodeTx, directNodeTx };
17039
+ }
17040
+ function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
17041
+ const cpfpLeafTx = new Transaction2({
17042
+ version: 3,
17043
+ allowUnknownOutputs: true
17044
+ });
17045
+ cpfpLeafTx.addInput({
17046
+ ...parentOutPoint,
17047
+ sequence
17048
+ });
17049
+ cpfpLeafTx.addOutput(txOut);
17050
+ cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
17051
+ const directLeafTx = new Transaction2({
17052
+ version: 3,
17053
+ allowUnknownOutputs: true
17054
+ });
17055
+ directLeafTx.addInput({
17056
+ ...parentOutPoint,
17057
+ sequence: directSequence
17058
+ });
17059
+ const amountSats = txOut.amount ?? 0n;
17060
+ let outputAmount = amountSats;
17061
+ if (shouldCalculateFee) {
17062
+ outputAmount = maybeApplyFee(amountSats);
17063
+ }
17064
+ directLeafTx.addOutput({
17065
+ script: txOut.script,
17066
+ amount: outputAmount
17067
+ });
17068
+ return [cpfpLeafTx, directLeafTx];
17069
+ }
17070
+ function createRefundTx({
17071
+ sequence,
17072
+ input,
17073
+ amountSats,
17074
+ receivingPubkey,
17075
+ network,
17076
+ shouldCalculateFee,
17077
+ includeAnchor
17078
+ }) {
17079
+ const refundTx = new Transaction2({
16817
17080
  version: 3,
16818
17081
  allowUnknownOutputs: true
16819
17082
  });
16820
- newRefundTx.addInput({
16821
- ...nodeOutPoint,
17083
+ refundTx.addInput({
17084
+ ...input,
16822
17085
  sequence
16823
17086
  });
16824
17087
  const refundPkScript = getP2TRScriptFromPublicKey(receivingPubkey, network);
16825
- newRefundTx.addOutput({
17088
+ let outputAmount = amountSats;
17089
+ if (shouldCalculateFee) {
17090
+ outputAmount = maybeApplyFee(amountSats);
17091
+ }
17092
+ refundTx.addOutput({
16826
17093
  script: refundPkScript,
17094
+ amount: outputAmount
17095
+ });
17096
+ if (includeAnchor) {
17097
+ refundTx.addOutput(getEphemeralAnchorOutput());
17098
+ }
17099
+ return refundTx;
17100
+ }
17101
+ function createRefundTxs({
17102
+ sequence,
17103
+ directSequence,
17104
+ input,
17105
+ directInput,
17106
+ amountSats,
17107
+ receivingPubkey,
17108
+ network
17109
+ }) {
17110
+ const cpfpRefundTx = createRefundTx({
17111
+ sequence,
17112
+ input,
17113
+ amountSats,
17114
+ receivingPubkey,
17115
+ network,
17116
+ shouldCalculateFee: false,
17117
+ includeAnchor: true
17118
+ });
17119
+ let directRefundTx;
17120
+ let directFromCpfpRefundTx;
17121
+ if (directSequence && directInput) {
17122
+ directRefundTx = createRefundTx({
17123
+ sequence: directSequence,
17124
+ input: directInput,
17125
+ amountSats,
17126
+ receivingPubkey,
17127
+ network,
17128
+ shouldCalculateFee: true,
17129
+ includeAnchor: false
17130
+ });
17131
+ directFromCpfpRefundTx = createRefundTx({
17132
+ sequence: directSequence,
17133
+ input,
17134
+ amountSats,
17135
+ receivingPubkey,
17136
+ network,
17137
+ shouldCalculateFee: true,
17138
+ includeAnchor: false
17139
+ });
17140
+ } else if (directInput && !directSequence) {
17141
+ throw new ValidationError(
17142
+ "directSequence must be provided if directInput is",
17143
+ {
17144
+ field: "directSequence",
17145
+ value: directSequence
17146
+ }
17147
+ );
17148
+ }
17149
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
17150
+ }
17151
+ function createConnectorRefundTransactions(sequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey, network, shouldCalculateFee) {
17152
+ const cpfpRefundTx = new Transaction2({
17153
+ version: 3,
17154
+ allowUnknownOutputs: true
17155
+ });
17156
+ cpfpRefundTx.addInput({
17157
+ ...cpfpNodeOutPoint,
17158
+ sequence
17159
+ });
17160
+ cpfpRefundTx.addInput(connectorOutput);
17161
+ const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
17162
+ cpfpRefundTx.addOutput({
17163
+ script: receiverScript,
16827
17164
  amount: amountSats
16828
17165
  });
16829
- newRefundTx.addOutput(getEphemeralAnchorOutput());
16830
- return newRefundTx;
17166
+ const directRefundTx = new Transaction2({
17167
+ version: 3,
17168
+ allowUnknownOutputs: true
17169
+ });
17170
+ directRefundTx.addInput({
17171
+ ...directNodeOutPoint,
17172
+ sequence
17173
+ });
17174
+ directRefundTx.addInput(connectorOutput);
17175
+ let outputAmount = amountSats;
17176
+ if (shouldCalculateFee) {
17177
+ outputAmount = maybeApplyFee(amountSats);
17178
+ }
17179
+ directRefundTx.addOutput({
17180
+ script: receiverScript,
17181
+ amount: outputAmount
17182
+ });
17183
+ const directFromCpfpTx = new Transaction2({
17184
+ version: 3,
17185
+ allowUnknownOutputs: true
17186
+ });
17187
+ directFromCpfpTx.addInput({
17188
+ ...cpfpNodeOutPoint,
17189
+ sequence
17190
+ });
17191
+ directFromCpfpTx.addInput(connectorOutput);
17192
+ directFromCpfpTx.addOutput({
17193
+ script: receiverScript,
17194
+ amount: outputAmount
17195
+ });
17196
+ return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
16831
17197
  }
16832
17198
  function getCurrentTimelock(currSequence) {
16833
17199
  return (currSequence || 0) & 65535;
16834
17200
  }
16835
17201
  function getTransactionSequence(currSequence) {
16836
17202
  const timelock = getCurrentTimelock(currSequence);
16837
- return 1 << 30 | timelock;
17203
+ return {
17204
+ nextSequence: 1 << 30 | timelock,
17205
+ nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
17206
+ };
16838
17207
  }
16839
17208
  function checkIfValidSequence(currSequence) {
16840
17209
  const TIME_LOCK_ACTIVE = (currSequence || 0) & 2147483648;
@@ -16852,24 +17221,32 @@ function checkIfValidSequence(currSequence) {
16852
17221
  });
16853
17222
  }
16854
17223
  }
16855
- function getNextTransactionSequence(currSequence, forRefresh) {
17224
+ function doesLeafNeedRefresh(currSequence, isNodeTx) {
16856
17225
  const currentTimelock = getCurrentTimelock(currSequence);
16857
- const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
16858
- if (forRefresh && nextTimelock <= 100 && currentTimelock > 0) {
16859
- return {
16860
- nextSequence: 1 << 30 | nextTimelock,
16861
- needRefresh: true
16862
- };
17226
+ if (isNodeTx) {
17227
+ return currentTimelock === 0;
16863
17228
  }
16864
- if (nextTimelock < 0) {
17229
+ return currentTimelock <= 100;
17230
+ }
17231
+ function getNextTransactionSequence(currSequence, isNodeTx) {
17232
+ const currentTimelock = getCurrentTimelock(currSequence);
17233
+ const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
17234
+ if (isNodeTx && nextTimelock < 0) {
16865
17235
  throw new ValidationError("timelock interval is less than 0", {
16866
17236
  field: "nextTimelock",
16867
- value: nextTimelock
17237
+ value: nextTimelock,
17238
+ expected: "Non-negative timelock interval"
17239
+ });
17240
+ } else if (!isNodeTx && nextTimelock <= 0) {
17241
+ throw new ValidationError("timelock interval is less than or equal to 0", {
17242
+ field: "nextTimelock",
17243
+ value: nextTimelock,
17244
+ expected: "Timelock greater than 0"
16868
17245
  });
16869
17246
  }
16870
17247
  return {
16871
17248
  nextSequence: 1 << 30 | nextTimelock,
16872
- needRefresh: nextTimelock <= 100
17249
+ nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
16873
17250
  };
16874
17251
  }
16875
17252
  function getEphemeralAnchorOutput() {
@@ -17528,7 +17905,7 @@ import * as ecies from "eciesjs";
17528
17905
  import { isNode } from "@lightsparkdev/core";
17529
17906
  var isReactNative = typeof navigator !== "undefined" && navigator.product === "ReactNative";
17530
17907
  var isBun = globalThis.Bun !== void 0;
17531
- var packageVersion = true ? "0.2.2" : "unknown";
17908
+ var packageVersion = true ? "0.2.4" : "unknown";
17532
17909
  var baseEnvStr = "unknown";
17533
17910
  if (isBun) {
17534
17911
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -18021,7 +18398,7 @@ import {
18021
18398
  import { secp256k1 as secp256k114 } from "@noble/curves/secp256k1";
18022
18399
  import { validateMnemonic } from "@scure/bip39";
18023
18400
  import { wordlist as wordlist2 } from "@scure/bip39/wordlists/english";
18024
- import { Address as Address7, OutScript as OutScript5, Transaction as Transaction9 } from "@scure/btc-signer";
18401
+ import { Address as Address6, OutScript as OutScript4, Transaction as Transaction9 } from "@scure/btc-signer";
18025
18402
  import { Mutex } from "async-mutex";
18026
18403
  import { uuidv7 as uuidv75, uuidv7obj } from "uuidv7";
18027
18404
 
@@ -18172,10 +18549,18 @@ var CompleteCoopExit = `
18172
18549
  var CompleteLeavesSwap = `
18173
18550
  mutation CompleteLeavesSwap(
18174
18551
  $adaptor_secret_key: String!
18552
+ $direct_adaptor_secret_key: String!
18553
+ $direct_from_cpfp_adaptor_secret_key: String!
18175
18554
  $user_outbound_transfer_external_id: UUID!
18176
18555
  $leaves_swap_request_id: ID!
18177
18556
  ) {
18178
- complete_leaves_swap(input: { adaptor_secret_key: $adaptor_secret_key, user_outbound_transfer_external_id: $user_outbound_transfer_external_id, leaves_swap_request_id: $leaves_swap_request_id }) {
18557
+ complete_leaves_swap(input: {
18558
+ adaptor_secret_key: $adaptor_secret_key,
18559
+ direct_adaptor_secret_key: $direct_adaptor_secret_key,
18560
+ direct_from_cpfp_adaptor_secret_key: $direct_from_cpfp_adaptor_secret_key,
18561
+ user_outbound_transfer_external_id: $user_outbound_transfer_external_id,
18562
+ leaves_swap_request_id: $leaves_swap_request_id
18563
+ }) {
18179
18564
  request {
18180
18565
  ...LeavesSwapRequestFragment
18181
18566
  }
@@ -18298,6 +18683,8 @@ var RequestLightningSend = `
18298
18683
  var RequestSwapLeaves = `
18299
18684
  mutation RequestSwapLeaves(
18300
18685
  $adaptor_pubkey: PublicKey!
18686
+ $direct_adaptor_pubkey: PublicKey
18687
+ $direct_from_cpfp_adaptor_pubkey: PublicKey
18301
18688
  $total_amount_sats: Long!
18302
18689
  $target_amount_sats: Long!
18303
18690
  $fee_sats: Long!
@@ -18307,6 +18694,8 @@ var RequestSwapLeaves = `
18307
18694
  ) {
18308
18695
  request_leaves_swap(input: {
18309
18696
  adaptor_pubkey: $adaptor_pubkey
18697
+ direct_adaptor_pubkey: $direct_adaptor_pubkey
18698
+ direct_from_cpfp_adaptor_pubkey: $direct_from_cpfp_adaptor_pubkey
18310
18699
  total_amount_sats: $total_amount_sats
18311
18700
  target_amount_sats: $target_amount_sats
18312
18701
  fee_sats: $fee_sats
@@ -18797,6 +19186,8 @@ var SspClient = class {
18797
19186
  }
18798
19187
  async requestLeaveSwap({
18799
19188
  adaptorPubkey,
19189
+ directAdaptorPubkey,
19190
+ directFromCpfpAdaptorPubkey,
18800
19191
  totalAmountSats,
18801
19192
  targetAmountSats,
18802
19193
  feeSats,
@@ -18808,6 +19199,8 @@ var SspClient = class {
18808
19199
  queryPayload: RequestSwapLeaves,
18809
19200
  variables: {
18810
19201
  adaptor_pubkey: adaptorPubkey,
19202
+ direct_adaptor_pubkey: directAdaptorPubkey,
19203
+ direct_from_cpfp_adaptor_pubkey: directFromCpfpAdaptorPubkey,
18811
19204
  total_amount_sats: totalAmountSats,
18812
19205
  target_amount_sats: targetAmountSats,
18813
19206
  fee_sats: feeSats,
@@ -18826,6 +19219,8 @@ var SspClient = class {
18826
19219
  }
18827
19220
  async completeLeaveSwap({
18828
19221
  adaptorSecretKey,
19222
+ directAdaptorSecretKey,
19223
+ directFromCpfpAdaptorSecretKey,
18829
19224
  userOutboundTransferExternalId,
18830
19225
  leavesSwapRequestId
18831
19226
  }) {
@@ -18833,6 +19228,8 @@ var SspClient = class {
18833
19228
  queryPayload: CompleteLeavesSwap,
18834
19229
  variables: {
18835
19230
  adaptor_secret_key: adaptorSecretKey,
19231
+ direct_adaptor_secret_key: directAdaptorSecretKey,
19232
+ direct_from_cpfp_adaptor_secret_key: directFromCpfpAdaptorSecretKey,
18836
19233
  user_outbound_transfer_external_id: userOutboundTransferExternalId,
18837
19234
  leaves_swap_request_id: leavesSwapRequestId
18838
19235
  },
@@ -22193,72 +22590,367 @@ var TokenTransactionWithStatus2 = {
22193
22590
  return message;
22194
22591
  }
22195
22592
  };
22196
- var SparkTokenServiceDefinition = {
22197
- name: "SparkTokenService",
22198
- fullName: "spark_token.SparkTokenService",
22199
- methods: {
22200
- /**
22201
- * Start process to create final token transaction with all inputs required
22202
- * from user and SOs (including revocation secret commitment)
22203
- */
22204
- start_transaction: {
22205
- name: "start_transaction",
22206
- requestType: StartTransactionRequest,
22207
- requestStream: false,
22208
- responseType: StartTransactionResponse,
22209
- responseStream: false,
22210
- options: {}
22211
- },
22212
- /**
22213
- * Complete the transaction and commit it with all SOs. This will be
22214
- * coordinated by one SO.
22215
- */
22216
- commit_transaction: {
22217
- name: "commit_transaction",
22218
- requestType: CommitTransactionRequest,
22219
- requestStream: false,
22220
- responseType: CommitTransactionResponse,
22221
- responseStream: false,
22222
- options: {}
22223
- },
22224
- query_token_metadata: {
22225
- name: "query_token_metadata",
22226
- requestType: QueryTokenMetadataRequest,
22227
- requestStream: false,
22228
- responseType: QueryTokenMetadataResponse,
22229
- responseStream: false,
22230
- options: {}
22231
- },
22232
- query_token_transactions: {
22233
- name: "query_token_transactions",
22234
- requestType: QueryTokenTransactionsRequest2,
22235
- requestStream: false,
22236
- responseType: QueryTokenTransactionsResponse2,
22237
- responseStream: false,
22238
- options: {}
22239
- },
22240
- query_token_outputs: {
22241
- name: "query_token_outputs",
22242
- requestType: QueryTokenOutputsRequest2,
22243
- requestStream: false,
22244
- responseType: QueryTokenOutputsResponse2,
22245
- responseStream: false,
22246
- options: {}
22593
+ function createBaseFreezeTokensPayload2() {
22594
+ return {
22595
+ version: 0,
22596
+ ownerPublicKey: new Uint8Array(0),
22597
+ tokenPublicKey: void 0,
22598
+ tokenIdentifier: void 0,
22599
+ issuerProvidedTimestamp: 0,
22600
+ operatorIdentityPublicKey: new Uint8Array(0),
22601
+ shouldUnfreeze: false
22602
+ };
22603
+ }
22604
+ var FreezeTokensPayload2 = {
22605
+ encode(message, writer = new BinaryWriter7()) {
22606
+ if (message.version !== 0) {
22607
+ writer.uint32(8).uint32(message.version);
22247
22608
  }
22248
- }
22249
- };
22250
- function bytesFromBase645(b64) {
22251
- if (globalThis.Buffer) {
22252
- return Uint8Array.from(globalThis.Buffer.from(b64, "base64"));
22253
- } else {
22254
- const bin = globalThis.atob(b64);
22255
- const arr = new Uint8Array(bin.length);
22256
- for (let i = 0; i < bin.length; ++i) {
22257
- arr[i] = bin.charCodeAt(i);
22609
+ if (message.ownerPublicKey.length !== 0) {
22610
+ writer.uint32(18).bytes(message.ownerPublicKey);
22258
22611
  }
22259
- return arr;
22260
- }
22261
- }
22612
+ if (message.tokenPublicKey !== void 0) {
22613
+ writer.uint32(26).bytes(message.tokenPublicKey);
22614
+ }
22615
+ if (message.tokenIdentifier !== void 0) {
22616
+ writer.uint32(34).bytes(message.tokenIdentifier);
22617
+ }
22618
+ if (message.issuerProvidedTimestamp !== 0) {
22619
+ writer.uint32(40).uint64(message.issuerProvidedTimestamp);
22620
+ }
22621
+ if (message.operatorIdentityPublicKey.length !== 0) {
22622
+ writer.uint32(50).bytes(message.operatorIdentityPublicKey);
22623
+ }
22624
+ if (message.shouldUnfreeze !== false) {
22625
+ writer.uint32(56).bool(message.shouldUnfreeze);
22626
+ }
22627
+ return writer;
22628
+ },
22629
+ decode(input, length) {
22630
+ const reader = input instanceof BinaryReader7 ? input : new BinaryReader7(input);
22631
+ const end = length === void 0 ? reader.len : reader.pos + length;
22632
+ const message = createBaseFreezeTokensPayload2();
22633
+ while (reader.pos < end) {
22634
+ const tag = reader.uint32();
22635
+ switch (tag >>> 3) {
22636
+ case 1: {
22637
+ if (tag !== 8) {
22638
+ break;
22639
+ }
22640
+ message.version = reader.uint32();
22641
+ continue;
22642
+ }
22643
+ case 2: {
22644
+ if (tag !== 18) {
22645
+ break;
22646
+ }
22647
+ message.ownerPublicKey = reader.bytes();
22648
+ continue;
22649
+ }
22650
+ case 3: {
22651
+ if (tag !== 26) {
22652
+ break;
22653
+ }
22654
+ message.tokenPublicKey = reader.bytes();
22655
+ continue;
22656
+ }
22657
+ case 4: {
22658
+ if (tag !== 34) {
22659
+ break;
22660
+ }
22661
+ message.tokenIdentifier = reader.bytes();
22662
+ continue;
22663
+ }
22664
+ case 5: {
22665
+ if (tag !== 40) {
22666
+ break;
22667
+ }
22668
+ message.issuerProvidedTimestamp = longToNumber4(reader.uint64());
22669
+ continue;
22670
+ }
22671
+ case 6: {
22672
+ if (tag !== 50) {
22673
+ break;
22674
+ }
22675
+ message.operatorIdentityPublicKey = reader.bytes();
22676
+ continue;
22677
+ }
22678
+ case 7: {
22679
+ if (tag !== 56) {
22680
+ break;
22681
+ }
22682
+ message.shouldUnfreeze = reader.bool();
22683
+ continue;
22684
+ }
22685
+ }
22686
+ if ((tag & 7) === 4 || tag === 0) {
22687
+ break;
22688
+ }
22689
+ reader.skip(tag & 7);
22690
+ }
22691
+ return message;
22692
+ },
22693
+ fromJSON(object) {
22694
+ return {
22695
+ version: isSet6(object.version) ? globalThis.Number(object.version) : 0,
22696
+ ownerPublicKey: isSet6(object.ownerPublicKey) ? bytesFromBase645(object.ownerPublicKey) : new Uint8Array(0),
22697
+ tokenPublicKey: isSet6(object.tokenPublicKey) ? bytesFromBase645(object.tokenPublicKey) : void 0,
22698
+ tokenIdentifier: isSet6(object.tokenIdentifier) ? bytesFromBase645(object.tokenIdentifier) : void 0,
22699
+ issuerProvidedTimestamp: isSet6(object.issuerProvidedTimestamp) ? globalThis.Number(object.issuerProvidedTimestamp) : 0,
22700
+ operatorIdentityPublicKey: isSet6(object.operatorIdentityPublicKey) ? bytesFromBase645(object.operatorIdentityPublicKey) : new Uint8Array(0),
22701
+ shouldUnfreeze: isSet6(object.shouldUnfreeze) ? globalThis.Boolean(object.shouldUnfreeze) : false
22702
+ };
22703
+ },
22704
+ toJSON(message) {
22705
+ const obj = {};
22706
+ if (message.version !== 0) {
22707
+ obj.version = Math.round(message.version);
22708
+ }
22709
+ if (message.ownerPublicKey.length !== 0) {
22710
+ obj.ownerPublicKey = base64FromBytes5(message.ownerPublicKey);
22711
+ }
22712
+ if (message.tokenPublicKey !== void 0) {
22713
+ obj.tokenPublicKey = base64FromBytes5(message.tokenPublicKey);
22714
+ }
22715
+ if (message.tokenIdentifier !== void 0) {
22716
+ obj.tokenIdentifier = base64FromBytes5(message.tokenIdentifier);
22717
+ }
22718
+ if (message.issuerProvidedTimestamp !== 0) {
22719
+ obj.issuerProvidedTimestamp = Math.round(message.issuerProvidedTimestamp);
22720
+ }
22721
+ if (message.operatorIdentityPublicKey.length !== 0) {
22722
+ obj.operatorIdentityPublicKey = base64FromBytes5(message.operatorIdentityPublicKey);
22723
+ }
22724
+ if (message.shouldUnfreeze !== false) {
22725
+ obj.shouldUnfreeze = message.shouldUnfreeze;
22726
+ }
22727
+ return obj;
22728
+ },
22729
+ create(base) {
22730
+ return FreezeTokensPayload2.fromPartial(base ?? {});
22731
+ },
22732
+ fromPartial(object) {
22733
+ const message = createBaseFreezeTokensPayload2();
22734
+ message.version = object.version ?? 0;
22735
+ message.ownerPublicKey = object.ownerPublicKey ?? new Uint8Array(0);
22736
+ message.tokenPublicKey = object.tokenPublicKey ?? void 0;
22737
+ message.tokenIdentifier = object.tokenIdentifier ?? void 0;
22738
+ message.issuerProvidedTimestamp = object.issuerProvidedTimestamp ?? 0;
22739
+ message.operatorIdentityPublicKey = object.operatorIdentityPublicKey ?? new Uint8Array(0);
22740
+ message.shouldUnfreeze = object.shouldUnfreeze ?? false;
22741
+ return message;
22742
+ }
22743
+ };
22744
+ function createBaseFreezeTokensRequest2() {
22745
+ return { freezeTokensPayload: void 0, issuerSignature: new Uint8Array(0) };
22746
+ }
22747
+ var FreezeTokensRequest2 = {
22748
+ encode(message, writer = new BinaryWriter7()) {
22749
+ if (message.freezeTokensPayload !== void 0) {
22750
+ FreezeTokensPayload2.encode(message.freezeTokensPayload, writer.uint32(10).fork()).join();
22751
+ }
22752
+ if (message.issuerSignature.length !== 0) {
22753
+ writer.uint32(18).bytes(message.issuerSignature);
22754
+ }
22755
+ return writer;
22756
+ },
22757
+ decode(input, length) {
22758
+ const reader = input instanceof BinaryReader7 ? input : new BinaryReader7(input);
22759
+ const end = length === void 0 ? reader.len : reader.pos + length;
22760
+ const message = createBaseFreezeTokensRequest2();
22761
+ while (reader.pos < end) {
22762
+ const tag = reader.uint32();
22763
+ switch (tag >>> 3) {
22764
+ case 1: {
22765
+ if (tag !== 10) {
22766
+ break;
22767
+ }
22768
+ message.freezeTokensPayload = FreezeTokensPayload2.decode(reader, reader.uint32());
22769
+ continue;
22770
+ }
22771
+ case 2: {
22772
+ if (tag !== 18) {
22773
+ break;
22774
+ }
22775
+ message.issuerSignature = reader.bytes();
22776
+ continue;
22777
+ }
22778
+ }
22779
+ if ((tag & 7) === 4 || tag === 0) {
22780
+ break;
22781
+ }
22782
+ reader.skip(tag & 7);
22783
+ }
22784
+ return message;
22785
+ },
22786
+ fromJSON(object) {
22787
+ return {
22788
+ freezeTokensPayload: isSet6(object.freezeTokensPayload) ? FreezeTokensPayload2.fromJSON(object.freezeTokensPayload) : void 0,
22789
+ issuerSignature: isSet6(object.issuerSignature) ? bytesFromBase645(object.issuerSignature) : new Uint8Array(0)
22790
+ };
22791
+ },
22792
+ toJSON(message) {
22793
+ const obj = {};
22794
+ if (message.freezeTokensPayload !== void 0) {
22795
+ obj.freezeTokensPayload = FreezeTokensPayload2.toJSON(message.freezeTokensPayload);
22796
+ }
22797
+ if (message.issuerSignature.length !== 0) {
22798
+ obj.issuerSignature = base64FromBytes5(message.issuerSignature);
22799
+ }
22800
+ return obj;
22801
+ },
22802
+ create(base) {
22803
+ return FreezeTokensRequest2.fromPartial(base ?? {});
22804
+ },
22805
+ fromPartial(object) {
22806
+ const message = createBaseFreezeTokensRequest2();
22807
+ message.freezeTokensPayload = object.freezeTokensPayload !== void 0 && object.freezeTokensPayload !== null ? FreezeTokensPayload2.fromPartial(object.freezeTokensPayload) : void 0;
22808
+ message.issuerSignature = object.issuerSignature ?? new Uint8Array(0);
22809
+ return message;
22810
+ }
22811
+ };
22812
+ function createBaseFreezeTokensResponse2() {
22813
+ return { impactedOutputIds: [], impactedTokenAmount: new Uint8Array(0) };
22814
+ }
22815
+ var FreezeTokensResponse2 = {
22816
+ encode(message, writer = new BinaryWriter7()) {
22817
+ for (const v of message.impactedOutputIds) {
22818
+ writer.uint32(10).string(v);
22819
+ }
22820
+ if (message.impactedTokenAmount.length !== 0) {
22821
+ writer.uint32(18).bytes(message.impactedTokenAmount);
22822
+ }
22823
+ return writer;
22824
+ },
22825
+ decode(input, length) {
22826
+ const reader = input instanceof BinaryReader7 ? input : new BinaryReader7(input);
22827
+ const end = length === void 0 ? reader.len : reader.pos + length;
22828
+ const message = createBaseFreezeTokensResponse2();
22829
+ while (reader.pos < end) {
22830
+ const tag = reader.uint32();
22831
+ switch (tag >>> 3) {
22832
+ case 1: {
22833
+ if (tag !== 10) {
22834
+ break;
22835
+ }
22836
+ message.impactedOutputIds.push(reader.string());
22837
+ continue;
22838
+ }
22839
+ case 2: {
22840
+ if (tag !== 18) {
22841
+ break;
22842
+ }
22843
+ message.impactedTokenAmount = reader.bytes();
22844
+ continue;
22845
+ }
22846
+ }
22847
+ if ((tag & 7) === 4 || tag === 0) {
22848
+ break;
22849
+ }
22850
+ reader.skip(tag & 7);
22851
+ }
22852
+ return message;
22853
+ },
22854
+ fromJSON(object) {
22855
+ return {
22856
+ impactedOutputIds: globalThis.Array.isArray(object?.impactedOutputIds) ? object.impactedOutputIds.map((e) => globalThis.String(e)) : [],
22857
+ impactedTokenAmount: isSet6(object.impactedTokenAmount) ? bytesFromBase645(object.impactedTokenAmount) : new Uint8Array(0)
22858
+ };
22859
+ },
22860
+ toJSON(message) {
22861
+ const obj = {};
22862
+ if (message.impactedOutputIds?.length) {
22863
+ obj.impactedOutputIds = message.impactedOutputIds;
22864
+ }
22865
+ if (message.impactedTokenAmount.length !== 0) {
22866
+ obj.impactedTokenAmount = base64FromBytes5(message.impactedTokenAmount);
22867
+ }
22868
+ return obj;
22869
+ },
22870
+ create(base) {
22871
+ return FreezeTokensResponse2.fromPartial(base ?? {});
22872
+ },
22873
+ fromPartial(object) {
22874
+ const message = createBaseFreezeTokensResponse2();
22875
+ message.impactedOutputIds = object.impactedOutputIds?.map((e) => e) || [];
22876
+ message.impactedTokenAmount = object.impactedTokenAmount ?? new Uint8Array(0);
22877
+ return message;
22878
+ }
22879
+ };
22880
+ var SparkTokenServiceDefinition = {
22881
+ name: "SparkTokenService",
22882
+ fullName: "spark_token.SparkTokenService",
22883
+ methods: {
22884
+ /**
22885
+ * Start process to create final token transaction with all inputs required
22886
+ * from user and SOs (including revocation secret commitment)
22887
+ */
22888
+ start_transaction: {
22889
+ name: "start_transaction",
22890
+ requestType: StartTransactionRequest,
22891
+ requestStream: false,
22892
+ responseType: StartTransactionResponse,
22893
+ responseStream: false,
22894
+ options: {}
22895
+ },
22896
+ /**
22897
+ * Complete the transaction and commit it with all SOs. This will be
22898
+ * coordinated by one SO.
22899
+ */
22900
+ commit_transaction: {
22901
+ name: "commit_transaction",
22902
+ requestType: CommitTransactionRequest,
22903
+ requestStream: false,
22904
+ responseType: CommitTransactionResponse,
22905
+ responseStream: false,
22906
+ options: {}
22907
+ },
22908
+ query_token_metadata: {
22909
+ name: "query_token_metadata",
22910
+ requestType: QueryTokenMetadataRequest,
22911
+ requestStream: false,
22912
+ responseType: QueryTokenMetadataResponse,
22913
+ responseStream: false,
22914
+ options: {}
22915
+ },
22916
+ query_token_transactions: {
22917
+ name: "query_token_transactions",
22918
+ requestType: QueryTokenTransactionsRequest2,
22919
+ requestStream: false,
22920
+ responseType: QueryTokenTransactionsResponse2,
22921
+ responseStream: false,
22922
+ options: {}
22923
+ },
22924
+ query_token_outputs: {
22925
+ name: "query_token_outputs",
22926
+ requestType: QueryTokenOutputsRequest2,
22927
+ requestStream: false,
22928
+ responseType: QueryTokenOutputsResponse2,
22929
+ responseStream: false,
22930
+ options: {}
22931
+ },
22932
+ freeze_tokens: {
22933
+ name: "freeze_tokens",
22934
+ requestType: FreezeTokensRequest2,
22935
+ requestStream: false,
22936
+ responseType: FreezeTokensResponse2,
22937
+ responseStream: false,
22938
+ options: {}
22939
+ }
22940
+ }
22941
+ };
22942
+ function bytesFromBase645(b64) {
22943
+ if (globalThis.Buffer) {
22944
+ return Uint8Array.from(globalThis.Buffer.from(b64, "base64"));
22945
+ } else {
22946
+ const bin = globalThis.atob(b64);
22947
+ const arr = new Uint8Array(bin.length);
22948
+ for (let i = 0; i < bin.length; ++i) {
22949
+ arr[i] = bin.charCodeAt(i);
22950
+ }
22951
+ return arr;
22952
+ }
22953
+ }
22262
22954
  function base64FromBytes5(arr) {
22263
22955
  if (globalThis.Buffer) {
22264
22956
  return globalThis.Buffer.from(arr).toString("base64");
@@ -22700,11 +23392,7 @@ import { sha256 as sha2568 } from "@noble/hashes/sha2";
22700
23392
  import { Transaction as Transaction4 } from "@scure/btc-signer";
22701
23393
  import * as ecies2 from "eciesjs";
22702
23394
  import { uuidv7 as uuidv72 } from "uuidv7";
22703
- var INITIAL_TIME_LOCK = 2e3;
22704
23395
  var DEFAULT_EXPIRY_TIME = 10 * 60 * 1e3;
22705
- function initialSequence() {
22706
- return 1 << 30 | INITIAL_TIME_LOCK;
22707
- }
22708
23396
  function getSigningJobProto(signingJob) {
22709
23397
  return {
22710
23398
  signingPublicKey: signingJob.signingPublicKey,
@@ -22721,12 +23409,14 @@ var BaseTransferService = class {
22721
23409
  this.connectionManager = connectionManager;
22722
23410
  this.signingService = signingService;
22723
23411
  }
22724
- async sendTransferTweakKey(transfer, leaves, refundSignatureMap) {
23412
+ async sendTransferTweakKey(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
22725
23413
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
22726
23414
  transfer.id,
22727
23415
  transfer.receiverIdentityPublicKey,
22728
23416
  leaves,
22729
- refundSignatureMap
23417
+ cpfpRefundSignatureMap,
23418
+ directRefundSignatureMap,
23419
+ directFromCpfpRefundSignatureMap
22730
23420
  );
22731
23421
  let updatedTransfer;
22732
23422
  const coordinatorOperator = this.config.getSigningOperators()[this.config.getCoordinatorIdentifier()];
@@ -22764,13 +23454,26 @@ var BaseTransferService = class {
22764
23454
  }
22765
23455
  return updatedTransfer;
22766
23456
  }
22767
- async deliverTransferPackage(transfer, leaves, refundSignatureMap) {
23457
+ async deliverTransferPackage(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
22768
23458
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
22769
23459
  transfer.id,
22770
23460
  transfer.receiverIdentityPublicKey,
22771
23461
  leaves,
22772
- refundSignatureMap
23462
+ cpfpRefundSignatureMap,
23463
+ directRefundSignatureMap,
23464
+ directFromCpfpRefundSignatureMap
22773
23465
  );
23466
+ for (const [key, operator] of Object.entries(
23467
+ this.config.getSigningOperators()
23468
+ )) {
23469
+ const tweaks = keyTweakInputMap.get(key);
23470
+ if (!tweaks) {
23471
+ throw new ValidationError("No tweaks for operator", {
23472
+ field: "operator",
23473
+ value: key
23474
+ });
23475
+ }
23476
+ }
22774
23477
  const transferPackage = await this.prepareTransferPackage(
22775
23478
  transfer.id,
22776
23479
  keyTweakInputMap,
@@ -22796,6 +23499,8 @@ var BaseTransferService = class {
22796
23499
  transferID,
22797
23500
  receiverIdentityPubkey,
22798
23501
  leaves,
23502
+ /* @__PURE__ */ new Map(),
23503
+ /* @__PURE__ */ new Map(),
22799
23504
  /* @__PURE__ */ new Map()
22800
23505
  );
22801
23506
  const transferPackage = await this.prepareTransferPackage(
@@ -22809,7 +23514,7 @@ var BaseTransferService = class {
22809
23514
  );
22810
23515
  let response;
22811
23516
  try {
22812
- response = await sparkClient.start_transfer({
23517
+ response = await sparkClient.start_transfer_v2({
22813
23518
  transferId: transferID,
22814
23519
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
22815
23520
  receiverIdentityPublicKey: receiverIdentityPubkey,
@@ -22838,12 +23543,22 @@ var BaseTransferService = class {
22838
23543
  nodes.push(leaf.leaf.id);
22839
23544
  }
22840
23545
  const signingCommitments = await sparkClient.get_signing_commitments({
22841
- nodeIds: nodes
23546
+ nodeIds: nodes,
23547
+ count: 3
22842
23548
  });
22843
- const leafSigningJobs = await this.signingService.signRefunds(
23549
+ const {
23550
+ cpfpLeafSigningJobs,
23551
+ directLeafSigningJobs,
23552
+ directFromCpfpLeafSigningJobs
23553
+ } = await this.signingService.signRefunds(
22844
23554
  leaves,
22845
- signingCommitments.signingCommitments,
22846
- receiverIdentityPubkey
23555
+ receiverIdentityPubkey,
23556
+ signingCommitments.signingCommitments.slice(0, leaves.length),
23557
+ signingCommitments.signingCommitments.slice(
23558
+ leaves.length,
23559
+ 2 * leaves.length
23560
+ ),
23561
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
22847
23562
  );
22848
23563
  const encryptedKeyTweaks = {};
22849
23564
  for (const [key, value] of keyTweakInputMap) {
@@ -22863,12 +23578,11 @@ var BaseTransferService = class {
22863
23578
  encryptedKeyTweaks[key] = Uint8Array.from(encryptedProto);
22864
23579
  }
22865
23580
  const transferPackage = {
22866
- leavesToSend: leafSigningJobs,
23581
+ leavesToSend: cpfpLeafSigningJobs,
22867
23582
  keyTweakPackage: encryptedKeyTweaks,
22868
23583
  userSignature: new Uint8Array(),
22869
- // TODO: Add direct refund signature
22870
- directLeavesToSend: [],
22871
- directFromCpfpLeavesToSend: []
23584
+ directLeavesToSend: directLeafSigningJobs,
23585
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs
22872
23586
  };
22873
23587
  const transferPackageSigningPayload = getTransferPackageSigningPayload(
22874
23588
  transferID,
@@ -22928,7 +23642,7 @@ var BaseTransferService = class {
22928
23642
  }
22929
23643
  return updatedTransfer;
22930
23644
  }
22931
- async signRefunds(leafDataMap, operatorSigningResults, adaptorPubKey) {
23645
+ async signRefunds(leafDataMap, operatorSigningResults, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
22932
23646
  const nodeSignatures = [];
22933
23647
  for (const operatorSigningResult of operatorSigningResults) {
22934
23648
  const leafData = leafDataMap.get(operatorSigningResult.leafId);
@@ -22943,54 +23657,120 @@ var BaseTransferService = class {
22943
23657
  `Output not found for leaf ${operatorSigningResult.leafId}`
22944
23658
  );
22945
23659
  }
22946
- const refundTxSighash = getSigHashFromTx(leafData.refundTx, 0, txOutput);
23660
+ const cpfpRefundTxSighash = getSigHashFromTx(
23661
+ leafData.refundTx,
23662
+ 0,
23663
+ txOutput
23664
+ );
22947
23665
  const publicKey = await this.config.signer.getPublicKeyFromDerivation(
22948
23666
  leafData.keyDerivation
22949
23667
  );
22950
- const userSignature = await this.config.signer.signFrost({
22951
- message: refundTxSighash,
23668
+ const cpfpUserSignature = await this.config.signer.signFrost({
23669
+ message: cpfpRefundTxSighash,
22952
23670
  publicKey,
22953
23671
  keyDerivation: leafData.keyDerivation,
22954
23672
  selfCommitment: leafData.signingNonceCommitment,
22955
23673
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
22956
- adaptorPubKey,
23674
+ adaptorPubKey: cpfpAdaptorPubKey,
22957
23675
  verifyingKey: operatorSigningResult.verifyingKey
22958
23676
  });
22959
- const refundAggregate = await this.config.signer.aggregateFrost({
22960
- message: refundTxSighash,
23677
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
23678
+ message: cpfpRefundTxSighash,
22961
23679
  statechainSignatures: operatorSigningResult.refundTxSigningResult?.signatureShares,
22962
23680
  statechainPublicKeys: operatorSigningResult.refundTxSigningResult?.publicKeys,
22963
23681
  verifyingKey: operatorSigningResult.verifyingKey,
22964
23682
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
22965
23683
  selfCommitment: leafData.signingNonceCommitment,
22966
23684
  publicKey,
22967
- selfSignature: userSignature,
22968
- adaptorPubKey
23685
+ selfSignature: cpfpUserSignature,
23686
+ adaptorPubKey: cpfpAdaptorPubKey
22969
23687
  });
23688
+ let directRefundAggregate;
23689
+ let directFromCpfpRefundAggregate;
23690
+ if (leafData.directTx) {
23691
+ const directTxOutput = leafData.directTx.getOutput(0);
23692
+ if (leafData.directRefundTx) {
23693
+ const directRefundTxSighash = getSigHashFromTx(
23694
+ leafData.directRefundTx,
23695
+ 0,
23696
+ directTxOutput
23697
+ );
23698
+ const directUserSignature = await this.config.signer.signFrost({
23699
+ message: directRefundTxSighash,
23700
+ publicKey,
23701
+ keyDerivation: leafData.keyDerivation,
23702
+ selfCommitment: leafData.directSigningNonceCommitment,
23703
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
23704
+ adaptorPubKey: directAdaptorPubKey,
23705
+ verifyingKey: operatorSigningResult.verifyingKey
23706
+ });
23707
+ directRefundAggregate = await this.config.signer.aggregateFrost({
23708
+ message: directRefundTxSighash,
23709
+ statechainSignatures: operatorSigningResult.directRefundTxSigningResult?.signatureShares,
23710
+ statechainPublicKeys: operatorSigningResult.directRefundTxSigningResult?.publicKeys,
23711
+ verifyingKey: operatorSigningResult.verifyingKey,
23712
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
23713
+ selfCommitment: leafData.directSigningNonceCommitment,
23714
+ publicKey,
23715
+ selfSignature: directUserSignature,
23716
+ adaptorPubKey: directAdaptorPubKey
23717
+ });
23718
+ }
23719
+ if (leafData.directFromCpfpRefundTx) {
23720
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
23721
+ leafData.directFromCpfpRefundTx,
23722
+ 0,
23723
+ txOutput
23724
+ );
23725
+ const directFromCpfpUserSignature = await this.config.signer.signFrost({
23726
+ message: directFromCpfpRefundTxSighash,
23727
+ publicKey,
23728
+ keyDerivation: leafData.keyDerivation,
23729
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
23730
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
23731
+ adaptorPubKey: directFromCpfpAdaptorPubKey,
23732
+ verifyingKey: operatorSigningResult.verifyingKey
23733
+ });
23734
+ directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
23735
+ message: directFromCpfpRefundTxSighash,
23736
+ statechainSignatures: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signatureShares,
23737
+ statechainPublicKeys: operatorSigningResult.directFromCpfpRefundTxSigningResult?.publicKeys,
23738
+ verifyingKey: operatorSigningResult.verifyingKey,
23739
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
23740
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
23741
+ publicKey,
23742
+ selfSignature: directFromCpfpUserSignature,
23743
+ adaptorPubKey: directFromCpfpAdaptorPubKey
23744
+ });
23745
+ }
23746
+ }
22970
23747
  nodeSignatures.push({
22971
23748
  nodeId: operatorSigningResult.leafId,
22972
- refundTxSignature: refundAggregate,
22973
23749
  nodeTxSignature: new Uint8Array(),
22974
- // TODO: Add direct refund signature
22975
23750
  directNodeTxSignature: new Uint8Array(),
22976
- directRefundTxSignature: new Uint8Array(),
22977
- directFromCpfpRefundTxSignature: new Uint8Array()
23751
+ refundTxSignature: cpfpRefundAggregate,
23752
+ directRefundTxSignature: directRefundAggregate ?? new Uint8Array(),
23753
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate ?? new Uint8Array()
22978
23754
  });
22979
23755
  }
22980
23756
  return nodeSignatures;
22981
23757
  }
22982
- async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, refundSignatureMap) {
23758
+ async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
22983
23759
  const receiverEciesPubKey = ecies2.PublicKey.fromHex(
22984
23760
  bytesToHex10(receiverIdentityPubkey)
22985
23761
  );
22986
23762
  const leavesTweaksMap = /* @__PURE__ */ new Map();
22987
23763
  for (const leaf of leaves) {
22988
- const refundSignature = refundSignatureMap.get(leaf.leaf.id);
23764
+ const cpfpRefundSignature = cpfpRefundSignatureMap.get(leaf.leaf.id);
23765
+ const directRefundSignature = directRefundSignatureMap.get(leaf.leaf.id);
23766
+ const directFromCpfpRefundSignature = directFromCpfpRefundSignatureMap.get(leaf.leaf.id);
22989
23767
  const leafTweaksMap = await this.prepareSingleSendTransferKeyTweak(
22990
23768
  transferID,
22991
23769
  leaf,
22992
23770
  receiverEciesPubKey,
22993
- refundSignature
23771
+ cpfpRefundSignature,
23772
+ directRefundSignature,
23773
+ directFromCpfpRefundSignature
22994
23774
  );
22995
23775
  for (const [identifier, leafTweak] of leafTweaksMap) {
22996
23776
  leavesTweaksMap.set(identifier, [
@@ -23001,7 +23781,7 @@ var BaseTransferService = class {
23001
23781
  }
23002
23782
  return leavesTweaksMap;
23003
23783
  }
23004
- async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, refundSignature) {
23784
+ async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, cpfpRefundSignature, directRefundSignature, directFromCpfpRefundSignature) {
23005
23785
  const signingOperators = this.config.getSigningOperators();
23006
23786
  const { shares, secretCipher } = await this.config.signer.subtractSplitAndEncrypt({
23007
23787
  first: leaf.keyDerivation,
@@ -23049,10 +23829,9 @@ var BaseTransferService = class {
23049
23829
  pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak),
23050
23830
  secretCipher,
23051
23831
  signature,
23052
- refundSignature: refundSignature ?? new Uint8Array(),
23053
- // TODO: Add direct refund signature
23054
- directRefundSignature: new Uint8Array(),
23055
- directFromCpfpRefundSignature: new Uint8Array()
23832
+ refundSignature: cpfpRefundSignature ?? new Uint8Array(),
23833
+ directRefundSignature: directRefundSignature ?? new Uint8Array(),
23834
+ directFromCpfpRefundSignature: directFromCpfpRefundSignature ?? new Uint8Array()
23056
23835
  });
23057
23836
  }
23058
23837
  return leafTweaksMap;
@@ -23082,7 +23861,12 @@ var TransferService = class extends BaseTransferService {
23082
23861
  * Deprecated in v0.1.32
23083
23862
  */
23084
23863
  async sendTransfer(leaves, receiverIdentityPubkey) {
23085
- const { transfer, signatureMap } = await this.sendTransferSignRefund(
23864
+ const {
23865
+ transfer,
23866
+ signatureMap,
23867
+ directSignatureMap,
23868
+ directFromCpfpSignatureMap
23869
+ } = await this.sendTransferSignRefund(
23086
23870
  leaves,
23087
23871
  receiverIdentityPubkey,
23088
23872
  new Date(Date.now() + DEFAULT_EXPIRY_TIME)
@@ -23090,7 +23874,9 @@ var TransferService = class extends BaseTransferService {
23090
23874
  const transferWithTweakedKeys = await this.sendTransferTweakKey(
23091
23875
  transfer,
23092
23876
  leaves,
23093
- signatureMap
23877
+ signatureMap,
23878
+ directSignatureMap,
23879
+ directFromCpfpSignatureMap
23094
23880
  );
23095
23881
  return transferWithTweakedKeys;
23096
23882
  }
@@ -23194,7 +23980,13 @@ var TransferService = class extends BaseTransferService {
23194
23980
  return transferResp.transfers[0];
23195
23981
  }
23196
23982
  async sendTransferSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
23197
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
23983
+ const {
23984
+ transfer,
23985
+ signatureMap,
23986
+ directSignatureMap,
23987
+ directFromCpfpSignatureMap,
23988
+ leafDataMap
23989
+ } = await this.sendTransferSignRefundInternal(
23198
23990
  leaves,
23199
23991
  receiverIdentityPubkey,
23200
23992
  expiryTime,
@@ -23203,11 +23995,19 @@ var TransferService = class extends BaseTransferService {
23203
23995
  return {
23204
23996
  transfer,
23205
23997
  signatureMap,
23998
+ directSignatureMap,
23999
+ directFromCpfpSignatureMap,
23206
24000
  leafDataMap
23207
24001
  };
23208
24002
  }
23209
24003
  async startSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
23210
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
24004
+ const {
24005
+ transfer,
24006
+ signatureMap,
24007
+ directSignatureMap,
24008
+ directFromCpfpSignatureMap,
24009
+ leafDataMap
24010
+ } = await this.sendTransferSignRefundInternal(
23211
24011
  leaves,
23212
24012
  receiverIdentityPubkey,
23213
24013
  expiryTime,
@@ -23216,31 +24016,45 @@ var TransferService = class extends BaseTransferService {
23216
24016
  return {
23217
24017
  transfer,
23218
24018
  signatureMap,
24019
+ directSignatureMap,
24020
+ directFromCpfpSignatureMap,
23219
24021
  leafDataMap
23220
24022
  };
23221
24023
  }
23222
- async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, adaptorPubKey) {
24024
+ async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
23223
24025
  return this.sendTransferSignRefundInternal(
23224
24026
  leaves,
23225
24027
  receiverIdentityPubkey,
23226
24028
  expiryTime,
23227
24029
  true,
23228
- adaptorPubKey
24030
+ cpfpAdaptorPubKey,
24031
+ directAdaptorPubKey,
24032
+ directFromCpfpAdaptorPubKey
23229
24033
  );
23230
24034
  }
23231
- async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, adaptorPubKey) {
24035
+ async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
23232
24036
  const transferId = uuidv72();
23233
24037
  const leafDataMap = /* @__PURE__ */ new Map();
23234
24038
  for (const leaf of leaves) {
23235
24039
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
24040
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
24041
+ const directFromCpfpRefundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
23236
24042
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
23237
24043
  const refundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
24044
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
24045
+ const directRefundTx = leaf.leaf.directRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directRefundTx) : void 0;
24046
+ const directFromCpfpRefundTx = leaf.leaf.directFromCpfpRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directFromCpfpRefundTx) : void 0;
23238
24047
  leafDataMap.set(leaf.leaf.id, {
23239
24048
  keyDerivation: leaf.keyDerivation,
23240
24049
  receivingPubkey: receiverIdentityPubkey,
23241
24050
  signingNonceCommitment,
24051
+ directSigningNonceCommitment,
23242
24052
  tx,
24053
+ directTx,
23243
24054
  refundTx,
24055
+ directRefundTx,
24056
+ directFromCpfpRefundTx,
24057
+ directFromCpfpRefundSigningNonceCommitment,
23244
24058
  vout: leaf.leaf.vout
23245
24059
  });
23246
24060
  }
@@ -23253,8 +24067,8 @@ var TransferService = class extends BaseTransferService {
23253
24067
  );
23254
24068
  let response;
23255
24069
  try {
23256
- if (adaptorPubKey !== void 0) {
23257
- response = await sparkClient.counter_leaf_swap({
24070
+ if (cpfpAdaptorPubKey !== void 0 || directAdaptorPubKey !== void 0 || directFromCpfpAdaptorPubKey !== void 0) {
24071
+ response = await sparkClient.counter_leaf_swap_v2({
23258
24072
  transfer: {
23259
24073
  transferId,
23260
24074
  leavesToSend: signingJobs,
@@ -23263,10 +24077,12 @@ var TransferService = class extends BaseTransferService {
23263
24077
  expiryTime
23264
24078
  },
23265
24079
  swapId: uuidv72(),
23266
- adaptorPublicKey: adaptorPubKey || new Uint8Array()
24080
+ adaptorPublicKey: cpfpAdaptorPubKey,
24081
+ directAdaptorPublicKey: directAdaptorPubKey,
24082
+ directFromCpfpAdaptorPublicKey: directFromCpfpAdaptorPubKey
23267
24083
  });
23268
24084
  } else if (forSwap) {
23269
- response = await sparkClient.start_leaf_swap({
24085
+ response = await sparkClient.start_leaf_swap_v2({
23270
24086
  transferId,
23271
24087
  leavesToSend: signingJobs,
23272
24088
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -23274,7 +24090,7 @@ var TransferService = class extends BaseTransferService {
23274
24090
  expiryTime
23275
24091
  });
23276
24092
  } else {
23277
- response = await sparkClient.start_transfer({
24093
+ response = await sparkClient.start_transfer_v2({
23278
24094
  transferId,
23279
24095
  leavesToSend: signingJobs,
23280
24096
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -23291,15 +24107,29 @@ var TransferService = class extends BaseTransferService {
23291
24107
  const signatures = await this.signRefunds(
23292
24108
  leafDataMap,
23293
24109
  response.signingResults,
23294
- adaptorPubKey
24110
+ cpfpAdaptorPubKey,
24111
+ directAdaptorPubKey,
24112
+ directFromCpfpAdaptorPubKey
23295
24113
  );
23296
- const signatureMap = /* @__PURE__ */ new Map();
24114
+ const cpfpSignatureMap = /* @__PURE__ */ new Map();
24115
+ const directSignatureMap = /* @__PURE__ */ new Map();
24116
+ const directFromCpfpSignatureMap = /* @__PURE__ */ new Map();
23297
24117
  for (const signature of signatures) {
23298
- signatureMap.set(signature.nodeId, signature.refundTxSignature);
24118
+ cpfpSignatureMap.set(signature.nodeId, signature.refundTxSignature);
24119
+ directSignatureMap.set(
24120
+ signature.nodeId,
24121
+ signature.directRefundTxSignature
24122
+ );
24123
+ directFromCpfpSignatureMap.set(
24124
+ signature.nodeId,
24125
+ signature.directFromCpfpRefundTxSignature
24126
+ );
23299
24127
  }
23300
24128
  return {
23301
24129
  transfer: response.transfer,
23302
- signatureMap,
24130
+ signatureMap: cpfpSignatureMap,
24131
+ directSignatureMap,
24132
+ directFromCpfpSignatureMap,
23303
24133
  leafDataMap,
23304
24134
  signingResults: response.signingResults
23305
24135
  };
@@ -23312,38 +24142,68 @@ var TransferService = class extends BaseTransferService {
23312
24142
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
23313
24143
  }
23314
24144
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
23315
- const nodeOutPoint = {
24145
+ const cpfpNodeOutPoint = {
23316
24146
  txid: hexToBytes6(getTxId(nodeTx)),
23317
24147
  index: 0
23318
24148
  };
24149
+ let directNodeTx;
24150
+ let directNodeOutPoint;
24151
+ if (leaf.leaf.directTx.length > 0) {
24152
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
24153
+ directNodeOutPoint = {
24154
+ txid: hexToBytes6(getTxId(directNodeTx)),
24155
+ index: 0
24156
+ };
24157
+ }
23319
24158
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
23320
- const nextSequence = isForClaim ? getTransactionSequence(currRefundTx.getInput(0).sequence) : getNextTransactionSequence(currRefundTx.getInput(0).sequence).nextSequence;
24159
+ const sequence = currRefundTx.getInput(0).sequence;
24160
+ if (!sequence) {
24161
+ throw new ValidationError("Invalid refund transaction", {
24162
+ field: "sequence",
24163
+ value: currRefundTx.getInput(0),
24164
+ expected: "Non-null sequence"
24165
+ });
24166
+ }
24167
+ const { nextSequence, nextDirectSequence } = isForClaim ? getTransactionSequence(sequence) : getNextTransactionSequence(sequence);
23321
24168
  const amountSats = currRefundTx.getOutput(0).amount;
23322
24169
  if (amountSats === void 0) {
23323
24170
  throw new Error("Amount not found in signRefunds");
23324
24171
  }
23325
- const refundTx = createRefundTx(
23326
- nextSequence,
23327
- nodeOutPoint,
24172
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
24173
+ sequence: nextSequence,
24174
+ directSequence: nextDirectSequence,
24175
+ input: cpfpNodeOutPoint,
24176
+ directInput: directNodeOutPoint,
23328
24177
  amountSats,
23329
- refundSigningData.receivingPubkey,
23330
- this.config.getNetwork()
24178
+ receivingPubkey: refundSigningData.receivingPubkey,
24179
+ network: this.config.getNetwork()
24180
+ });
24181
+ refundSigningData.refundTx = cpfpRefundTx;
24182
+ refundSigningData.directRefundTx = directRefundTx;
24183
+ refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
24184
+ const cpfpRefundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
24185
+ const directRefundNonceCommitmentProto = refundSigningData.directSigningNonceCommitment;
24186
+ const directFromCpfpRefundNonceCommitmentProto = refundSigningData.directFromCpfpRefundSigningNonceCommitment;
24187
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(
24188
+ refundSigningData.keyDerivation
23331
24189
  );
23332
- refundSigningData.refundTx = refundTx;
23333
- const refundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
23334
24190
  signingJobs.push({
23335
24191
  leafId: leaf.leaf.id,
23336
24192
  refundTxSigningJob: {
23337
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
23338
- refundSigningData.keyDerivation
23339
- ),
23340
- rawTx: refundTx.toBytes(),
23341
- signingNonceCommitment: refundNonceCommitmentProto.commitment
24193
+ signingPublicKey,
24194
+ rawTx: cpfpRefundTx.toBytes(),
24195
+ signingNonceCommitment: cpfpRefundNonceCommitmentProto.commitment
23342
24196
  },
23343
- // TODO: Add direct refund signature
23344
- directRefundTxSigningJob: void 0,
23345
- // TODO: Add direct refund signature
23346
- directFromCpfpRefundTxSigningJob: void 0
24197
+ directRefundTxSigningJob: directRefundTx ? {
24198
+ signingPublicKey,
24199
+ rawTx: directRefundTx.toBytes(),
24200
+ signingNonceCommitment: directRefundNonceCommitmentProto.commitment
24201
+ } : void 0,
24202
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
24203
+ signingPublicKey,
24204
+ rawTx: directFromCpfpRefundTx.toBytes(),
24205
+ signingNonceCommitment: directFromCpfpRefundNonceCommitmentProto.commitment
24206
+ } : void 0
23347
24207
  });
23348
24208
  }
23349
24209
  return signingJobs;
@@ -23464,13 +24324,17 @@ var TransferService = class extends BaseTransferService {
23464
24324
  const leafDataMap = /* @__PURE__ */ new Map();
23465
24325
  for (const leafKey of leafKeys) {
23466
24326
  const tx = getTxFromRawTxBytes(leafKey.leaf.nodeTx);
24327
+ const directTx = leafKey.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leafKey.leaf.directTx) : void 0;
23467
24328
  leafDataMap.set(leafKey.leaf.id, {
23468
24329
  keyDerivation: leafKey.newKeyDerivation,
23469
24330
  receivingPubkey: await this.config.signer.getPublicKeyFromDerivation(
23470
24331
  leafKey.newKeyDerivation
23471
24332
  ),
23472
24333
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24334
+ directSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24335
+ directFromCpfpRefundSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
23473
24336
  tx,
24337
+ directTx,
23474
24338
  vout: leafKey.leaf.vout
23475
24339
  });
23476
24340
  }
@@ -23492,7 +24356,7 @@ var TransferService = class extends BaseTransferService {
23492
24356
  }
23493
24357
  }
23494
24358
  try {
23495
- resp = await sparkClient.claim_transfer_sign_refunds({
24359
+ resp = await sparkClient.claim_transfer_sign_refunds_v2({
23496
24360
  transferId: transfer.id,
23497
24361
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
23498
24362
  signingJobs
@@ -23507,7 +24371,7 @@ var TransferService = class extends BaseTransferService {
23507
24371
  this.config.getCoordinatorAddress()
23508
24372
  );
23509
24373
  try {
23510
- return await sparkClient.finalize_node_signatures({
24374
+ return await sparkClient.finalize_node_signatures_v2({
23511
24375
  intent: 1 /* TRANSFER */,
23512
24376
  nodeSignatures
23513
24377
  });
@@ -23546,109 +24410,134 @@ var TransferService = class extends BaseTransferService {
23546
24410
  throw new Error(`Error querying pending transfers by sender: ${error}`);
23547
24411
  }
23548
24412
  }
23549
- async refreshTimelockNodes(nodes, parentNode) {
23550
- if (nodes.length === 0) {
23551
- throw Error("no nodes to refresh");
23552
- }
24413
+ async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
23553
24414
  const signingJobs = [];
23554
- const newNodeTxs = [];
23555
- for (let i = 0; i < nodes.length; i++) {
23556
- const node = nodes[i];
23557
- if (!node) {
23558
- throw Error("could not get node");
23559
- }
23560
- const nodeTx = getTxFromRawTxBytes(node?.nodeTx);
23561
- const input = nodeTx.getInput(0);
23562
- if (!input) {
23563
- throw Error("Could not fetch tx input");
23564
- }
23565
- const newTx = new Transaction4({ version: 3, allowUnknownOutputs: true });
23566
- const originalOutput = nodeTx.getOutput(0);
23567
- if (!originalOutput) {
23568
- throw Error("Could not get original output");
23569
- }
23570
- newTx.addOutput({
23571
- script: originalOutput.script,
23572
- amount: originalOutput.amount
24415
+ const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
24416
+ const parentNodeOutput = parentNodeTx.getOutput(0);
24417
+ if (!parentNodeOutput) {
24418
+ throw Error("Could not get parent node output");
24419
+ }
24420
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
24421
+ const nodeInput = nodeTx.getInput(0);
24422
+ const nodeOutput = nodeTx.getOutput(0);
24423
+ if (!nodeOutput) {
24424
+ throw Error("Could not get node output");
24425
+ }
24426
+ let directNodeTx;
24427
+ let directNodeInput;
24428
+ if (node.directTx.length > 0) {
24429
+ directNodeTx = getTxFromRawTxBytes(node.directTx);
24430
+ directNodeInput = directNodeTx.getInput(0);
24431
+ }
24432
+ const currSequence = nodeInput.sequence;
24433
+ if (!currSequence) {
24434
+ throw new ValidationError("Invalid node transaction", {
24435
+ field: "sequence",
24436
+ value: nodeInput,
24437
+ expected: "Non-null sequence"
23573
24438
  });
23574
- for (let j = 1; j < nodeTx.outputsLength; j++) {
23575
- const additionalOutput = nodeTx.getOutput(j);
23576
- if (additionalOutput) {
23577
- newTx.addOutput(additionalOutput);
23578
- }
23579
- }
23580
- if (i === 0) {
23581
- const currSequence = input.sequence;
23582
- newTx.addInput({
23583
- ...input,
23584
- sequence: getNextTransactionSequence(currSequence).nextSequence
23585
- });
23586
- } else {
23587
- newTx.addInput({
23588
- ...input,
23589
- sequence: initialSequence(),
23590
- txid: newNodeTxs[i - 1]?.id
23591
- });
23592
- }
24439
+ }
24440
+ let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
24441
+ currSequence,
24442
+ true
24443
+ );
24444
+ const output = {
24445
+ script: parentNodeOutput.script,
24446
+ amount: parentNodeOutput.amount
24447
+ };
24448
+ const newNodeInput = {
24449
+ txid: nodeInput.txid,
24450
+ index: nodeInput.index,
24451
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
24452
+ };
24453
+ const newDirectInput = directNodeTx && directNodeInput ? {
24454
+ txid: directNodeInput.txid,
24455
+ index: directNodeInput.index,
24456
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
24457
+ } : void 0;
24458
+ const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
24459
+ output,
24460
+ newNodeInput,
24461
+ newDirectInput
24462
+ );
24463
+ const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
24464
+ if (!newCpfpNodeOutput) {
24465
+ throw Error("Could not get new cpfp node output");
24466
+ }
24467
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
24468
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
24469
+ type: "leaf" /* LEAF */,
24470
+ path: node.id
24471
+ });
24472
+ signingJobs.push({
24473
+ signingPublicKey,
24474
+ rawTx: cpfpNodeTx.toBytes(),
24475
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24476
+ type: "node",
24477
+ parentTxOut: parentNodeOutput
24478
+ });
24479
+ if (newDirectNodeTx) {
23593
24480
  signingJobs.push({
23594
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
23595
- type: "leaf" /* LEAF */,
23596
- path: node.id
23597
- }),
23598
- rawTx: newTx.toBytes(),
23599
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
24481
+ signingPublicKey,
24482
+ rawTx: newDirectNodeTx.toBytes(),
24483
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24484
+ type: "directNode",
24485
+ parentTxOut: parentNodeOutput
23600
24486
  });
23601
- newNodeTxs[i] = newTx;
23602
24487
  }
23603
- const leaf = nodes[nodes.length - 1];
23604
- if (!leaf?.refundTx) {
23605
- throw Error("leaf does not have refund tx");
24488
+ const newCpfpRefundOutPoint = {
24489
+ txid: hexToBytes6(getTxId(cpfpNodeTx)),
24490
+ index: 0
24491
+ };
24492
+ let newDirectRefundOutPoint;
24493
+ if (newDirectNodeTx) {
24494
+ newDirectRefundOutPoint = {
24495
+ txid: hexToBytes6(getTxId(newDirectNodeTx)),
24496
+ index: 0
24497
+ };
23606
24498
  }
23607
- const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
23608
- const newRefundTx = new Transaction4({
23609
- version: 3,
23610
- allowUnknownOutputs: true
24499
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
24500
+ sequence: INITIAL_SEQUENCE,
24501
+ directSequence: INITIAL_DIRECT_SEQUENCE,
24502
+ input: newCpfpRefundOutPoint,
24503
+ directInput: newDirectRefundOutPoint,
24504
+ amountSats: nodeOutput.amount,
24505
+ receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
24506
+ type: "leaf" /* LEAF */,
24507
+ path: node.id
24508
+ }),
24509
+ network: this.config.getNetwork()
23611
24510
  });
23612
- const originalRefundOutput = refundTx.getOutput(0);
23613
- if (!originalRefundOutput) {
23614
- throw Error("Could not get original refund output");
23615
- }
23616
- newRefundTx.addOutput({
23617
- script: originalRefundOutput.script,
23618
- amount: originalRefundOutput.amount
24511
+ signingJobs.push({
24512
+ signingPublicKey,
24513
+ rawTx: cpfpRefundTx.toBytes(),
24514
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24515
+ type: "cpfp",
24516
+ parentTxOut: newCpfpNodeOutput
23619
24517
  });
23620
- for (let j = 1; j < refundTx.outputsLength; j++) {
23621
- const additionalOutput = refundTx.getOutput(j);
23622
- if (additionalOutput) {
23623
- newRefundTx.addOutput(additionalOutput);
23624
- }
23625
- }
23626
- const refundTxInput = refundTx.getInput(0);
23627
- if (!refundTxInput) {
23628
- throw Error("refund tx doesn't have input");
24518
+ if (directRefundTx && newDirectNodeOutput) {
24519
+ signingJobs.push({
24520
+ signingPublicKey,
24521
+ rawTx: directRefundTx.toBytes(),
24522
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24523
+ type: "direct",
24524
+ parentTxOut: newDirectNodeOutput
24525
+ });
23629
24526
  }
23630
- if (!newNodeTxs[newNodeTxs.length - 1]) {
23631
- throw Error("Could not get last node tx");
24527
+ if (directFromCpfpRefundTx && newCpfpNodeOutput) {
24528
+ signingJobs.push({
24529
+ signingPublicKey,
24530
+ rawTx: directFromCpfpRefundTx.toBytes(),
24531
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24532
+ type: "directFromCpfp",
24533
+ parentTxOut: newCpfpNodeOutput
24534
+ });
23632
24535
  }
23633
- newRefundTx.addInput({
23634
- ...refundTxInput,
23635
- sequence: initialSequence(),
23636
- txid: getTxId(newNodeTxs[newNodeTxs.length - 1])
23637
- });
23638
- const refundSigningJob = {
23639
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
23640
- type: "leaf" /* LEAF */,
23641
- path: leaf.id
23642
- }),
23643
- rawTx: newRefundTx.toBytes(),
23644
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
23645
- };
23646
- signingJobs.push(refundSigningJob);
23647
24536
  const sparkClient = await this.connectionManager.createSparkClient(
23648
24537
  this.config.getCoordinatorAddress()
23649
24538
  );
23650
- const response = await sparkClient.refresh_timelock({
23651
- leafId: leaf.id,
24539
+ const response = await sparkClient.refresh_timelock_v2({
24540
+ leafId: node.id,
23652
24541
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
23653
24542
  signingJobs: signingJobs.map(getSigningJobProto)
23654
24543
  });
@@ -23658,45 +24547,27 @@ var TransferService = class extends BaseTransferService {
23658
24547
  );
23659
24548
  }
23660
24549
  let nodeSignatures = [];
23661
- let leafSignature;
23662
- let refundSignature;
23663
- let leafNodeId;
23664
- for (let i = 0; i < response.signingResults.length; i++) {
23665
- const signingResult = response.signingResults[i];
24550
+ let leafCpfpSignature;
24551
+ let leafDirectSignature;
24552
+ let cpfpRefundSignature;
24553
+ let directRefundSignature;
24554
+ let directFromCpfpRefundSignature;
24555
+ for (const [i, signingResult] of response.signingResults.entries()) {
23666
24556
  const signingJob = signingJobs[i];
23667
24557
  if (!signingJob || !signingResult) {
23668
24558
  throw Error("Signing job does not exist");
23669
24559
  }
23670
- if (!signingJob.signingNonceCommitment) {
23671
- throw Error("nonce commitment does not exist");
23672
- }
23673
24560
  const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
23674
- let parentTx;
23675
- let nodeId;
23676
- let vout;
23677
- if (i === nodes.length) {
23678
- nodeId = nodes[i - 1]?.id;
23679
- parentTx = newNodeTxs[i - 1];
23680
- vout = 0;
23681
- } else if (i === 0) {
23682
- nodeId = nodes[i]?.id;
23683
- parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
23684
- vout = nodes[i]?.vout;
23685
- } else {
23686
- nodeId = nodes[i]?.id;
23687
- parentTx = newNodeTxs[i - 1];
23688
- vout = nodes[i]?.vout;
24561
+ const txOut = signingJob.parentTxOut;
24562
+ if (!txOut) {
24563
+ throw Error("Could not get tx out");
23689
24564
  }
23690
- if (!parentTx || !nodeId || vout === void 0) {
23691
- throw Error("Could not parse signing results");
23692
- }
23693
- const txOut = parentTx.getOutput(vout);
23694
24565
  const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
23695
24566
  const userSignature = await this.config.signer.signFrost({
23696
24567
  message: rawTxSighash,
23697
24568
  keyDerivation: {
23698
24569
  type: "leaf" /* LEAF */,
23699
- path: nodeId
24570
+ path: node.id
23700
24571
  },
23701
24572
  publicKey: signingJob.signingPublicKey,
23702
24573
  verifyingKey: signingResult.verifyingKey,
@@ -23715,41 +24586,35 @@ var TransferService = class extends BaseTransferService {
23715
24586
  selfSignature: userSignature,
23716
24587
  adaptorPubKey: new Uint8Array()
23717
24588
  });
23718
- if (i !== nodes.length && i !== nodes.length - 1) {
23719
- nodeSignatures.push({
23720
- nodeId,
23721
- nodeTxSignature: signature,
23722
- refundTxSignature: new Uint8Array(),
23723
- // TODO: Add direct refund signature
23724
- directNodeTxSignature: new Uint8Array(),
23725
- directRefundTxSignature: new Uint8Array(),
23726
- directFromCpfpRefundTxSignature: new Uint8Array()
23727
- });
23728
- } else if (i === nodes.length) {
23729
- refundSignature = signature;
23730
- } else if (i === nodes.length - 1) {
23731
- leafNodeId = nodeId;
23732
- leafSignature = signature;
24589
+ if (signingJob.type === "node") {
24590
+ leafCpfpSignature = signature;
24591
+ } else if (signingJob.type === "directNode") {
24592
+ leafDirectSignature = signature;
24593
+ } else if (signingJob.type === "cpfp") {
24594
+ cpfpRefundSignature = signature;
24595
+ } else if (signingJob.type === "direct") {
24596
+ directRefundSignature = signature;
24597
+ } else if (signingJob.type === "directFromCpfp") {
24598
+ directFromCpfpRefundSignature = signature;
23733
24599
  }
23734
24600
  }
23735
- if (!leafSignature || !refundSignature || !leafNodeId) {
23736
- throw Error("leaf or refund signature does not exist");
23737
- }
23738
24601
  nodeSignatures.push({
23739
- nodeId: leafNodeId,
23740
- nodeTxSignature: leafSignature,
23741
- refundTxSignature: refundSignature,
23742
- // TODO: Add direct refund signature
23743
- directNodeTxSignature: new Uint8Array(),
23744
- directRefundTxSignature: new Uint8Array(),
23745
- directFromCpfpRefundTxSignature: new Uint8Array()
24602
+ nodeId: node.id,
24603
+ nodeTxSignature: leafCpfpSignature || new Uint8Array(),
24604
+ directNodeTxSignature: leafDirectSignature || new Uint8Array(),
24605
+ refundTxSignature: cpfpRefundSignature || new Uint8Array(),
24606
+ directRefundTxSignature: directRefundSignature || new Uint8Array(),
24607
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
23746
24608
  });
23747
- const result = await sparkClient.finalize_node_signatures({
24609
+ const result = await sparkClient.finalize_node_signatures_v2({
23748
24610
  intent: 3 /* REFRESH */,
23749
24611
  nodeSignatures
23750
24612
  });
23751
24613
  return result;
23752
24614
  }
24615
+ async refreshTimelockNodes(node, parentNode) {
24616
+ return await this.refreshTimelockNodesInternal(node, parentNode);
24617
+ }
23753
24618
  async extendTimelock(node) {
23754
24619
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
23755
24620
  const refundTx = getTxFromRawTxBytes(node.refundTx);
@@ -23758,7 +24623,10 @@ var TransferService = class extends BaseTransferService {
23758
24623
  txid: hexToBytes6(getTxId(nodeTx)),
23759
24624
  index: 0
23760
24625
  };
23761
- const { nextSequence: newNodeSequence } = getNextTransactionSequence(refundSequence);
24626
+ const {
24627
+ nextSequence: newNodeSequence,
24628
+ nextDirectSequence: newDirectNodeSequence
24629
+ } = getNextTransactionSequence(refundSequence);
23762
24630
  const newNodeTx = new Transaction4({
23763
24631
  version: 3,
23764
24632
  allowUnknownOutputs: true
@@ -23771,81 +24639,122 @@ var TransferService = class extends BaseTransferService {
23771
24639
  newNodeTx.addOutput({
23772
24640
  script: originalOutput.script,
23773
24641
  amount: originalOutput.amount
23774
- // feeReducedAmount,
23775
24642
  });
23776
24643
  newNodeTx.addOutput(getEphemeralAnchorOutput());
23777
- const newRefundOutPoint = {
24644
+ let newDirectNodeTx;
24645
+ if (node.directTx.length > 0) {
24646
+ newDirectNodeTx = new Transaction4({
24647
+ version: 3,
24648
+ allowUnknownOutputs: true
24649
+ });
24650
+ newDirectNodeTx.addInput({
24651
+ ...newNodeOutPoint,
24652
+ sequence: newDirectNodeSequence
24653
+ });
24654
+ newDirectNodeTx.addOutput({
24655
+ script: originalOutput.script,
24656
+ amount: maybeApplyFee(originalOutput.amount)
24657
+ });
24658
+ }
24659
+ const newCpfpRefundOutPoint = {
23778
24660
  txid: hexToBytes6(getTxId(newNodeTx)),
23779
24661
  index: 0
23780
24662
  };
24663
+ let newDirectRefundOutPoint;
24664
+ if (newDirectNodeTx) {
24665
+ newDirectRefundOutPoint = {
24666
+ txid: hexToBytes6(getTxId(newDirectNodeTx)),
24667
+ index: 0
24668
+ };
24669
+ }
23781
24670
  const amountSats = refundTx.getOutput(0).amount;
23782
24671
  if (amountSats === void 0) {
23783
24672
  throw new Error("Amount not found in extendTimelock");
23784
24673
  }
23785
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
24674
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
23786
24675
  type: "leaf" /* LEAF */,
23787
24676
  path: node.id
23788
24677
  });
23789
- const newRefundTx = createRefundTx(
23790
- initialSequence(),
23791
- newRefundOutPoint,
24678
+ const {
24679
+ cpfpRefundTx: newCpfpRefundTx,
24680
+ directRefundTx: newDirectRefundTx,
24681
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
24682
+ } = createRefundTxs({
24683
+ sequence: INITIAL_SEQUENCE,
24684
+ directSequence: INITIAL_DIRECT_SEQUENCE,
24685
+ input: newCpfpRefundOutPoint,
24686
+ directInput: newDirectRefundOutPoint,
23792
24687
  amountSats,
23793
- // feeReducedRefundAmount,
23794
- signingPubKey,
23795
- this.config.getNetwork()
23796
- );
24688
+ receivingPubkey: signingPublicKey,
24689
+ network: this.config.getNetwork()
24690
+ });
24691
+ if (!newCpfpRefundTx) {
24692
+ throw new ValidationError(
24693
+ "Failed to create refund transactions in extendTimelock"
24694
+ );
24695
+ }
23797
24696
  const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
23798
- const refundSighash = getSigHashFromTx(
23799
- newRefundTx,
24697
+ const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
24698
+ const cpfpRefundSighash = getSigHashFromTx(
24699
+ newCpfpRefundTx,
23800
24700
  0,
23801
24701
  newNodeTx.getOutput(0)
23802
24702
  );
24703
+ const directRefundSighash = newDirectNodeTx && newDirectRefundTx ? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0)) : void 0;
24704
+ const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx ? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0)) : void 0;
23803
24705
  const newNodeSigningJob = {
23804
- signingPublicKey: signingPubKey,
24706
+ signingPublicKey,
23805
24707
  rawTx: newNodeTx.toBytes(),
23806
24708
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
23807
24709
  };
23808
- const newRefundSigningJob = {
23809
- signingPublicKey: signingPubKey,
23810
- rawTx: newRefundTx.toBytes(),
24710
+ const newDirectNodeSigningJob = newDirectNodeTx ? {
24711
+ signingPublicKey,
24712
+ rawTx: newDirectNodeTx.toBytes(),
24713
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
24714
+ } : void 0;
24715
+ const newCpfpRefundSigningJob = {
24716
+ signingPublicKey,
24717
+ rawTx: newCpfpRefundTx.toBytes(),
23811
24718
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
23812
24719
  };
24720
+ const newDirectRefundSigningJob = newDirectRefundTx ? {
24721
+ signingPublicKey,
24722
+ rawTx: newDirectRefundTx.toBytes(),
24723
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
24724
+ } : void 0;
24725
+ const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
24726
+ signingPublicKey,
24727
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
24728
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
24729
+ } : void 0;
23813
24730
  const sparkClient = await this.connectionManager.createSparkClient(
23814
24731
  this.config.getCoordinatorAddress()
23815
24732
  );
23816
- const response = await sparkClient.extend_leaf({
24733
+ const response = await sparkClient.extend_leaf_v2({
23817
24734
  leafId: node.id,
23818
24735
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
23819
24736
  nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
23820
- refundTxSigningJob: getSigningJobProto(newRefundSigningJob)
24737
+ directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
24738
+ refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
24739
+ directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
24740
+ directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
23821
24741
  });
23822
24742
  if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
23823
24743
  throw new Error("Signing result does not exist");
23824
24744
  }
24745
+ const keyDerivation = {
24746
+ type: "leaf" /* LEAF */,
24747
+ path: node.id
24748
+ };
23825
24749
  const nodeUserSig = await this.config.signer.signFrost({
23826
24750
  message: nodeSighash,
23827
- keyDerivation: {
23828
- type: "leaf" /* LEAF */,
23829
- path: node.id
23830
- },
23831
- publicKey: signingPubKey,
24751
+ keyDerivation,
24752
+ publicKey: signingPublicKey,
23832
24753
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
23833
24754
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
23834
24755
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
23835
24756
  adaptorPubKey: new Uint8Array()
23836
24757
  });
23837
- const refundUserSig = await this.config.signer.signFrost({
23838
- message: refundSighash,
23839
- keyDerivation: {
23840
- type: "leaf" /* LEAF */,
23841
- path: node.id
23842
- },
23843
- publicKey: signingPubKey,
23844
- verifyingKey: response.refundTxSigningResult.verifyingKey,
23845
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
23846
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
23847
- adaptorPubKey: new Uint8Array()
23848
- });
23849
24758
  const nodeSig = await this.config.signer.aggregateFrost({
23850
24759
  message: nodeSighash,
23851
24760
  statechainSignatures: response.nodeTxSigningResult.signingResult?.signatureShares,
@@ -23853,122 +24762,253 @@ var TransferService = class extends BaseTransferService {
23853
24762
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
23854
24763
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
23855
24764
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
23856
- publicKey: signingPubKey,
24765
+ publicKey: signingPublicKey,
23857
24766
  selfSignature: nodeUserSig,
23858
24767
  adaptorPubKey: new Uint8Array()
23859
24768
  });
23860
- const refundSig = await this.config.signer.aggregateFrost({
23861
- message: refundSighash,
24769
+ let directNodeSig;
24770
+ if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
24771
+ const directNodeUserSig = await this.config.signer.signFrost({
24772
+ message: directNodeSighash,
24773
+ keyDerivation,
24774
+ publicKey: signingPublicKey,
24775
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
24776
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
24777
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
24778
+ adaptorPubKey: new Uint8Array()
24779
+ });
24780
+ directNodeSig = await this.config.signer.aggregateFrost({
24781
+ message: directNodeSighash,
24782
+ statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
24783
+ statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
24784
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
24785
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
24786
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
24787
+ publicKey: signingPublicKey,
24788
+ selfSignature: directNodeUserSig,
24789
+ adaptorPubKey: new Uint8Array()
24790
+ });
24791
+ }
24792
+ const cpfpRefundUserSig = await this.config.signer.signFrost({
24793
+ message: cpfpRefundSighash,
24794
+ keyDerivation,
24795
+ publicKey: signingPublicKey,
24796
+ verifyingKey: response.refundTxSigningResult.verifyingKey,
24797
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
24798
+ statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
24799
+ adaptorPubKey: new Uint8Array()
24800
+ });
24801
+ const cpfpRefundSig = await this.config.signer.aggregateFrost({
24802
+ message: cpfpRefundSighash,
23862
24803
  statechainSignatures: response.refundTxSigningResult.signingResult?.signatureShares,
23863
24804
  statechainPublicKeys: response.refundTxSigningResult.signingResult?.publicKeys,
23864
24805
  verifyingKey: response.refundTxSigningResult.verifyingKey,
23865
24806
  statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
23866
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
23867
- publicKey: signingPubKey,
23868
- selfSignature: refundUserSig,
24807
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
24808
+ publicKey: signingPublicKey,
24809
+ selfSignature: cpfpRefundUserSig,
23869
24810
  adaptorPubKey: new Uint8Array()
23870
24811
  });
23871
- return await sparkClient.finalize_node_signatures({
24812
+ let directRefundSig;
24813
+ if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
24814
+ const directRefundUserSig = await this.config.signer.signFrost({
24815
+ message: directRefundSighash,
24816
+ keyDerivation,
24817
+ publicKey: signingPublicKey,
24818
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
24819
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
24820
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
24821
+ adaptorPubKey: new Uint8Array()
24822
+ });
24823
+ directRefundSig = await this.config.signer.aggregateFrost({
24824
+ message: directRefundSighash,
24825
+ statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
24826
+ statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
24827
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
24828
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
24829
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
24830
+ publicKey: signingPublicKey,
24831
+ selfSignature: directRefundUserSig,
24832
+ adaptorPubKey: new Uint8Array()
24833
+ });
24834
+ }
24835
+ let directFromCpfpRefundSig;
24836
+ if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
24837
+ const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
24838
+ message: directFromCpfpRefundSighash,
24839
+ keyDerivation,
24840
+ publicKey: signingPublicKey,
24841
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
24842
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
24843
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
24844
+ adaptorPubKey: new Uint8Array()
24845
+ });
24846
+ directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
24847
+ message: directFromCpfpRefundSighash,
24848
+ statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
24849
+ statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
24850
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
24851
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
24852
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
24853
+ publicKey: signingPublicKey,
24854
+ selfSignature: directFromCpfpRefundUserSig,
24855
+ adaptorPubKey: new Uint8Array()
24856
+ });
24857
+ }
24858
+ return await sparkClient.finalize_node_signatures_v2({
23872
24859
  intent: 4 /* EXTEND */,
23873
24860
  nodeSignatures: [
23874
24861
  {
23875
24862
  nodeId: response.leafId,
23876
24863
  nodeTxSignature: nodeSig,
23877
- refundTxSignature: refundSig
24864
+ directNodeTxSignature: directNodeSig,
24865
+ refundTxSignature: cpfpRefundSig,
24866
+ directRefundTxSignature: directRefundSig,
24867
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSig
23878
24868
  }
23879
24869
  ]
23880
24870
  });
23881
24871
  }
23882
- async refreshTimelockRefundTx(node) {
24872
+ async testonly_expireTimeLockNodeTx(node, parentNode) {
24873
+ return await this.refreshTimelockNodesInternal(node, parentNode, true);
24874
+ }
24875
+ async testonly_expireTimeLockRefundtx(node) {
23883
24876
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
23884
- const refundTx = getTxFromRawTxBytes(node.refundTx);
23885
- const currSequence = refundTx.getInput(0).sequence || 0;
23886
- const { nextSequence } = getNextTransactionSequence(currSequence);
23887
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
24877
+ const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
24878
+ const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
24879
+ const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
24880
+ const currTimelock = getCurrentTimelock(currSequence);
24881
+ if (currTimelock <= 100) {
24882
+ throw new ValidationError("Cannot expire timelock below 100", {
24883
+ field: "currTimelock",
24884
+ value: currTimelock,
24885
+ expected: "Timelock greater than 100"
24886
+ });
24887
+ }
24888
+ const nextSequence = TEST_UNILATERAL_SEQUENCE;
24889
+ const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
24890
+ const nodeOutput = nodeTx.getOutput(0);
24891
+ if (!nodeOutput) {
24892
+ throw Error("Could not get node output");
24893
+ }
24894
+ const keyDerivation = {
23888
24895
  type: "leaf" /* LEAF */,
23889
24896
  path: node.id
23890
- });
23891
- const newRefundTx = new Transaction4({
23892
- version: 3,
23893
- allowUnknownOutputs: true
23894
- });
23895
- const originalRefundOutput = refundTx.getOutput(0);
23896
- if (!originalRefundOutput) {
23897
- throw Error("Could not get original refund output");
24897
+ };
24898
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
24899
+ const cpfpRefundOutPoint = {
24900
+ txid: hexToBytes6(getTxId(nodeTx)),
24901
+ index: 0
24902
+ };
24903
+ let directRefundOutPoint;
24904
+ if (directNodeTx) {
24905
+ directRefundOutPoint = {
24906
+ txid: hexToBytes6(getTxId(directNodeTx)),
24907
+ index: 0
24908
+ };
23898
24909
  }
23899
- newRefundTx.addOutput({
23900
- script: originalRefundOutput.script,
23901
- amount: originalRefundOutput.amount
24910
+ const {
24911
+ cpfpRefundTx: newCpfpRefundTx,
24912
+ directRefundTx: newDirectRefundTx,
24913
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
24914
+ } = createRefundTxs({
24915
+ sequence: nextSequence,
24916
+ directSequence: nextDirectSequence,
24917
+ input: cpfpRefundOutPoint,
24918
+ directInput: directRefundOutPoint,
24919
+ amountSats: nodeOutput.amount,
24920
+ receivingPubkey: signingPublicKey,
24921
+ network: this.config.getNetwork()
23902
24922
  });
23903
- for (let j = 1; j < refundTx.outputsLength; j++) {
23904
- const additionalOutput = refundTx.getOutput(j);
23905
- if (additionalOutput) {
23906
- newRefundTx.addOutput(additionalOutput);
23907
- }
24923
+ const signingJobs = [];
24924
+ signingJobs.push({
24925
+ signingPublicKey,
24926
+ rawTx: newCpfpRefundTx.toBytes(),
24927
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24928
+ type: "cpfp",
24929
+ parentTxOut: nodeOutput
24930
+ });
24931
+ const directNodeTxOut = directNodeTx?.getOutput(0);
24932
+ if (newDirectRefundTx && directNodeTxOut) {
24933
+ signingJobs.push({
24934
+ signingPublicKey,
24935
+ rawTx: newDirectRefundTx.toBytes(),
24936
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24937
+ type: "direct",
24938
+ parentTxOut: directNodeTxOut
24939
+ });
23908
24940
  }
23909
- const refundTxInput = refundTx.getInput(0);
23910
- if (!refundTxInput) {
23911
- throw Error("refund tx doesn't have input");
24941
+ if (newDirectFromCpfpRefundTx) {
24942
+ signingJobs.push({
24943
+ signingPublicKey,
24944
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
24945
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24946
+ type: "directFromCpfp",
24947
+ parentTxOut: nodeOutput
24948
+ });
23912
24949
  }
23913
- newRefundTx.addInput({
23914
- ...refundTxInput,
23915
- sequence: nextSequence
23916
- });
23917
- const refundSigningJob = {
23918
- signingPublicKey: signingPubKey,
23919
- rawTx: newRefundTx.toBytes(),
23920
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
23921
- };
23922
24950
  const sparkClient = await this.connectionManager.createSparkClient(
23923
24951
  this.config.getCoordinatorAddress()
23924
24952
  );
23925
- const response = await sparkClient.refresh_timelock({
24953
+ const response = await sparkClient.refresh_timelock_v2({
23926
24954
  leafId: node.id,
23927
24955
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
23928
- signingJobs: [getSigningJobProto(refundSigningJob)]
24956
+ signingJobs: signingJobs.map(getSigningJobProto)
23929
24957
  });
23930
- if (response.signingResults.length !== 1) {
24958
+ if (response.signingResults.length !== signingJobs.length) {
23931
24959
  throw Error(
23932
- `Expected 1 signing result, got ${response.signingResults.length}`
24960
+ `Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`
23933
24961
  );
23934
24962
  }
23935
- const signingResult = response.signingResults[0];
23936
- if (!signingResult || !refundSigningJob.signingNonceCommitment) {
23937
- throw Error("Signing result or nonce commitment does not exist");
24963
+ let cpfpRefundSignature;
24964
+ let directRefundSignature;
24965
+ let directFromCpfpRefundSignature;
24966
+ for (const [i, signingJob] of signingJobs.entries()) {
24967
+ const signingResult = response.signingResults[i];
24968
+ if (!signingResult) {
24969
+ throw Error("Signing result does not exist");
24970
+ }
24971
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
24972
+ const txOut = signingJob.parentTxOut;
24973
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
24974
+ const userSignature = await this.config.signer.signFrost({
24975
+ message: rawTxSighash,
24976
+ keyDerivation,
24977
+ publicKey: signingPublicKey,
24978
+ verifyingKey: signingResult.verifyingKey,
24979
+ selfCommitment: signingJob.signingNonceCommitment,
24980
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
24981
+ adaptorPubKey: new Uint8Array()
24982
+ });
24983
+ const signature = await this.config.signer.aggregateFrost({
24984
+ message: rawTxSighash,
24985
+ statechainSignatures: signingResult.signingResult?.signatureShares,
24986
+ statechainPublicKeys: signingResult.signingResult?.publicKeys,
24987
+ verifyingKey: signingResult.verifyingKey,
24988
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
24989
+ selfCommitment: signingJob.signingNonceCommitment,
24990
+ publicKey: signingPublicKey,
24991
+ selfSignature: userSignature,
24992
+ adaptorPubKey: new Uint8Array()
24993
+ });
24994
+ if (signingJob.type === "cpfp") {
24995
+ cpfpRefundSignature = signature;
24996
+ } else if (signingJob.type === "direct") {
24997
+ directRefundSignature = signature;
24998
+ } else if (signingJob.type === "directFromCpfp") {
24999
+ directFromCpfpRefundSignature = signature;
25000
+ }
23938
25001
  }
23939
- const rawTx = getTxFromRawTxBytes(refundSigningJob.rawTx);
23940
- const txOut = nodeTx.getOutput(0);
23941
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
23942
- const userSignature = await this.config.signer.signFrost({
23943
- message: rawTxSighash,
23944
- keyDerivation: {
23945
- type: "leaf" /* LEAF */,
23946
- path: node.id
23947
- },
23948
- publicKey: signingPubKey,
23949
- verifyingKey: signingResult.verifyingKey,
23950
- selfCommitment: refundSigningJob.signingNonceCommitment,
23951
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
23952
- adaptorPubKey: new Uint8Array()
23953
- });
23954
- const signature = await this.config.signer.aggregateFrost({
23955
- message: rawTxSighash,
23956
- statechainSignatures: signingResult.signingResult?.signatureShares,
23957
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
23958
- verifyingKey: signingResult.verifyingKey,
23959
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
23960
- selfCommitment: refundSigningJob.signingNonceCommitment,
23961
- publicKey: signingPubKey,
23962
- selfSignature: userSignature,
23963
- adaptorPubKey: new Uint8Array()
23964
- });
23965
- const result = await sparkClient.finalize_node_signatures({
25002
+ const result = await sparkClient.finalize_node_signatures_v2({
23966
25003
  intent: 3 /* REFRESH */,
23967
25004
  nodeSignatures: [
23968
25005
  {
23969
25006
  nodeId: node.id,
23970
25007
  nodeTxSignature: new Uint8Array(),
23971
- refundTxSignature: signature
25008
+ directNodeTxSignature: new Uint8Array(),
25009
+ refundTxSignature: cpfpRefundSignature,
25010
+ directRefundTxSignature: directRefundSignature,
25011
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature
23972
25012
  }
23973
25013
  ]
23974
25014
  });
@@ -23987,7 +25027,12 @@ var CoopExitService = class extends BaseTransferService {
23987
25027
  connectorOutputs,
23988
25028
  receiverPubKey
23989
25029
  }) {
23990
- const { transfer, signaturesMap } = await this.signCoopExitRefunds(
25030
+ const {
25031
+ transfer,
25032
+ signaturesMap,
25033
+ directSignaturesMap,
25034
+ directFromCpfpSignaturesMap
25035
+ } = await this.signCoopExitRefunds(
23991
25036
  leaves,
23992
25037
  exitTxId,
23993
25038
  connectorOutputs,
@@ -23996,34 +25041,81 @@ var CoopExitService = class extends BaseTransferService {
23996
25041
  const transferTweak = await this.deliverTransferPackage(
23997
25042
  transfer,
23998
25043
  leaves,
23999
- signaturesMap
25044
+ signaturesMap,
25045
+ directSignaturesMap,
25046
+ directFromCpfpSignaturesMap
24000
25047
  );
24001
- return { transfer: transferTweak, signaturesMap };
24002
- }
24003
- createConnectorRefundTransaction(sequence, nodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
24004
- const refundTx = new Transaction5();
24005
- if (!nodeOutPoint.txid || nodeOutPoint.index === void 0) {
24006
- throw new ValidationError("Invalid node outpoint", {
24007
- field: "nodeOutPoint",
24008
- value: { txid: nodeOutPoint.txid, index: nodeOutPoint.index },
25048
+ return {
25049
+ transfer: transferTweak,
25050
+ signaturesMap,
25051
+ directSignaturesMap,
25052
+ directFromCpfpSignaturesMap
25053
+ };
25054
+ }
25055
+ createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
25056
+ const cpfpRefundTx = new Transaction5();
25057
+ if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
25058
+ throw new ValidationError("Invalid CPFP node outpoint", {
25059
+ field: "cpfpNodeOutPoint",
25060
+ value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
24009
25061
  expected: "Both txid and index must be defined"
24010
25062
  });
24011
25063
  }
24012
- refundTx.addInput({
24013
- txid: nodeOutPoint.txid,
24014
- index: nodeOutPoint.index,
25064
+ cpfpRefundTx.addInput({
25065
+ txid: cpfpNodeOutPoint.txid,
25066
+ index: cpfpNodeOutPoint.index,
24015
25067
  sequence
24016
25068
  });
24017
- refundTx.addInput(connectorOutput);
25069
+ cpfpRefundTx.addInput(connectorOutput);
24018
25070
  const receiverScript = getP2TRScriptFromPublicKey(
24019
25071
  receiverPubKey,
24020
25072
  this.config.getNetwork()
24021
25073
  );
24022
- refundTx.addOutput({
25074
+ cpfpRefundTx.addOutput({
24023
25075
  script: receiverScript,
24024
25076
  amount: amountSats
24025
25077
  });
24026
- return refundTx;
25078
+ let directRefundTx;
25079
+ let directFromCpfpRefundTx;
25080
+ if (directNodeOutPoint) {
25081
+ if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
25082
+ throw new ValidationError("Invalid direct node outpoint", {
25083
+ field: "directNodeOutPoint",
25084
+ value: {
25085
+ txid: directNodeOutPoint.txid,
25086
+ index: directNodeOutPoint.index
25087
+ },
25088
+ expected: "Both txid and index must be defined"
25089
+ });
25090
+ }
25091
+ directRefundTx = new Transaction5();
25092
+ directRefundTx.addInput({
25093
+ txid: directNodeOutPoint.txid,
25094
+ index: directNodeOutPoint.index,
25095
+ sequence: directSequence
25096
+ });
25097
+ directRefundTx.addInput(connectorOutput);
25098
+ directRefundTx.addOutput({
25099
+ script: receiverScript,
25100
+ amount: maybeApplyFee(amountSats)
25101
+ });
25102
+ directFromCpfpRefundTx = new Transaction5();
25103
+ directFromCpfpRefundTx.addInput({
25104
+ txid: cpfpNodeOutPoint.txid,
25105
+ index: cpfpNodeOutPoint.index,
25106
+ sequence: directSequence
25107
+ });
25108
+ directFromCpfpRefundTx.addInput(connectorOutput);
25109
+ directFromCpfpRefundTx.addOutput({
25110
+ script: receiverScript,
25111
+ amount: maybeApplyFee(amountSats)
25112
+ });
25113
+ }
25114
+ return {
25115
+ cpfpRefundTx,
25116
+ directRefundTx,
25117
+ directFromCpfpRefundTx
25118
+ };
24027
25119
  }
24028
25120
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey) {
24029
25121
  if (leaves.length !== connectorOutputs.length) {
@@ -24059,39 +25151,67 @@ var CoopExitService = class extends BaseTransferService {
24059
25151
  });
24060
25152
  }
24061
25153
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
24062
- const { nextSequence } = getNextTransactionSequence(
24063
- currentRefundTx.getInput(0).sequence
24064
- );
24065
- const refundTx = this.createConnectorRefundTransaction(
25154
+ const sequence = currentRefundTx.getInput(0).sequence;
25155
+ if (!sequence) {
25156
+ throw new ValidationError("Invalid refund transaction", {
25157
+ field: "sequence",
25158
+ value: currentRefundTx.getInput(0),
25159
+ expected: "Non-null sequence"
25160
+ });
25161
+ }
25162
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
25163
+ let currentDirectRefundTx;
25164
+ if (leaf.leaf.directRefundTx.length > 0) {
25165
+ currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
25166
+ }
25167
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
24066
25168
  nextSequence,
25169
+ nextDirectSequence,
24067
25170
  currentRefundTx.getInput(0),
25171
+ currentDirectRefundTx?.getInput(0),
24068
25172
  connectorOutput,
24069
25173
  BigInt(leaf.leaf.value),
24070
25174
  receiverPubKey
24071
25175
  );
24072
25176
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25177
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25178
+ const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25179
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(leaf.keyDerivation);
24073
25180
  const signingJob = {
24074
25181
  leafId: leaf.leaf.id,
24075
25182
  refundTxSigningJob: {
24076
25183
  signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
24077
25184
  leaf.keyDerivation
24078
25185
  ),
24079
- rawTx: refundTx.toBytes(),
25186
+ rawTx: cpfpRefundTx.toBytes(),
24080
25187
  signingNonceCommitment: signingNonceCommitment.commitment
24081
25188
  },
24082
- // TODO: Add direct refund signature
24083
- directRefundTxSigningJob: void 0,
24084
- directFromCpfpRefundTxSigningJob: void 0
25189
+ directRefundTxSigningJob: directRefundTx ? {
25190
+ signingPublicKey,
25191
+ rawTx: directRefundTx.toBytes(),
25192
+ signingNonceCommitment: directSigningNonceCommitment.commitment
25193
+ } : void 0,
25194
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
25195
+ signingPublicKey,
25196
+ rawTx: directFromCpfpRefundTx.toBytes(),
25197
+ signingNonceCommitment: directFromCpfpSigningNonceCommitment.commitment
25198
+ } : void 0
24085
25199
  };
24086
25200
  signingJobs.push(signingJob);
24087
25201
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
25202
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
24088
25203
  leafDataMap.set(leaf.leaf.id, {
24089
25204
  keyDerivation: leaf.keyDerivation,
24090
- refundTx,
25205
+ receivingPubkey: receiverPubKey,
24091
25206
  signingNonceCommitment,
25207
+ directSigningNonceCommitment,
24092
25208
  tx,
24093
- vout: leaf.leaf.vout,
24094
- receivingPubkey: receiverPubKey
25209
+ directTx,
25210
+ refundTx: cpfpRefundTx,
25211
+ directRefundTx,
25212
+ directFromCpfpRefundTx,
25213
+ directFromCpfpRefundSigningNonceCommitment: directFromCpfpSigningNonceCommitment,
25214
+ vout: leaf.leaf.vout
24095
25215
  });
24096
25216
  }
24097
25217
  const sparkClient = await this.connectionManager.createSparkClient(
@@ -24099,7 +25219,7 @@ var CoopExitService = class extends BaseTransferService {
24099
25219
  );
24100
25220
  let response;
24101
25221
  try {
24102
- response = await sparkClient.cooperative_exit({
25222
+ response = await sparkClient.cooperative_exit_v2({
24103
25223
  transfer: {
24104
25224
  transferId: uuidv73(),
24105
25225
  leavesToSend: signingJobs,
@@ -24133,10 +25253,25 @@ var CoopExitService = class extends BaseTransferService {
24133
25253
  response.signingResults
24134
25254
  );
24135
25255
  const signaturesMap = /* @__PURE__ */ new Map();
25256
+ const directSignaturesMap = /* @__PURE__ */ new Map();
25257
+ const directFromCpfpSignaturesMap = /* @__PURE__ */ new Map();
24136
25258
  for (const signature of signatures) {
24137
25259
  signaturesMap.set(signature.nodeId, signature.refundTxSignature);
25260
+ directSignaturesMap.set(
25261
+ signature.nodeId,
25262
+ signature.directRefundTxSignature
25263
+ );
25264
+ directFromCpfpSignaturesMap.set(
25265
+ signature.nodeId,
25266
+ signature.directFromCpfpRefundTxSignature
25267
+ );
24138
25268
  }
24139
- return { transfer: response.transfer, signaturesMap };
25269
+ return {
25270
+ transfer: response.transfer,
25271
+ signaturesMap,
25272
+ directSignaturesMap,
25273
+ directFromCpfpSignaturesMap
25274
+ };
24140
25275
  }
24141
25276
  };
24142
25277
 
@@ -24144,10 +25279,8 @@ var CoopExitService = class extends BaseTransferService {
24144
25279
  import { schnorr as schnorr4, secp256k1 as secp256k19 } from "@noble/curves/secp256k1";
24145
25280
  import { sha256 as sha2569 } from "@noble/hashes/sha2";
24146
25281
  import { hexToBytes as hexToBytes7 } from "@noble/hashes/utils";
24147
- import * as btc5 from "@scure/btc-signer";
24148
- import { p2tr as p2tr2, Transaction as Transaction6 } from "@scure/btc-signer";
25282
+ import { p2tr as p2tr2 } from "@scure/btc-signer";
24149
25283
  import { equalBytes as equalBytes4 } from "@scure/btc-signer/utils";
24150
- var INITIAL_TIME_LOCK2 = 2e3;
24151
25284
  var DepositService = class {
24152
25285
  config;
24153
25286
  connectionManager;
@@ -24215,57 +25348,370 @@ var DepositService = class {
24215
25348
  });
24216
25349
  }
24217
25350
  }
24218
- }
24219
- async generateDepositAddress({
24220
- signingPubkey,
24221
- leafId,
24222
- isStatic = false
24223
- }) {
25351
+ }
25352
+ async generateDepositAddress({
25353
+ signingPubkey,
25354
+ leafId,
25355
+ isStatic = false
25356
+ }) {
25357
+ const sparkClient = await this.connectionManager.createSparkClient(
25358
+ this.config.getCoordinatorAddress()
25359
+ );
25360
+ let depositResp;
25361
+ try {
25362
+ depositResp = await sparkClient.generate_deposit_address({
25363
+ signingPublicKey: signingPubkey,
25364
+ identityPublicKey: await this.config.signer.getIdentityPublicKey(),
25365
+ network: this.config.getNetworkProto(),
25366
+ leafId,
25367
+ isStatic
25368
+ });
25369
+ } catch (error) {
25370
+ throw new NetworkError(
25371
+ "Failed to generate deposit address",
25372
+ {
25373
+ operation: "generate_deposit_address",
25374
+ errorCount: 1,
25375
+ errors: error instanceof Error ? error.message : String(error)
25376
+ },
25377
+ error
25378
+ );
25379
+ }
25380
+ if (!depositResp.depositAddress) {
25381
+ throw new ValidationError(
25382
+ "No deposit address response from coordinator",
25383
+ {
25384
+ field: "depositAddress",
25385
+ value: depositResp
25386
+ }
25387
+ );
25388
+ }
25389
+ await this.validateDepositAddress({
25390
+ address: depositResp.depositAddress,
25391
+ userPubkey: signingPubkey
25392
+ });
25393
+ return depositResp;
25394
+ }
25395
+ async createTreeRoot({
25396
+ keyDerivation,
25397
+ verifyingKey,
25398
+ depositTx,
25399
+ vout
25400
+ }) {
25401
+ const output = depositTx.getOutput(vout);
25402
+ if (!output) {
25403
+ throw new ValidationError("Invalid deposit transaction output", {
25404
+ field: "vout",
25405
+ value: vout,
25406
+ expected: "Valid output index"
25407
+ });
25408
+ }
25409
+ const script = output.script;
25410
+ const amount = output.amount;
25411
+ if (!script || !amount) {
25412
+ throw new ValidationError("No script or amount found in deposit tx", {
25413
+ field: "output",
25414
+ value: output,
25415
+ expected: "Output with script and amount"
25416
+ });
25417
+ }
25418
+ const depositOutPoint = {
25419
+ txid: hexToBytes7(getTxId(depositTx)),
25420
+ index: vout
25421
+ };
25422
+ const depositTxOut = {
25423
+ script,
25424
+ amount
25425
+ };
25426
+ const [cpfpRootTx, directRootTx] = createRootTx(
25427
+ depositOutPoint,
25428
+ depositTxOut
25429
+ );
25430
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25431
+ const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25432
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
25433
+ const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
25434
+ const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
25435
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
25436
+ sequence: INITIAL_SEQUENCE,
25437
+ directSequence: INITIAL_DIRECT_SEQUENCE,
25438
+ input: { txid: hexToBytes7(getTxId(cpfpRootTx)), index: 0 },
25439
+ directInput: { txid: hexToBytes7(getTxId(directRootTx)), index: 0 },
25440
+ amountSats: amount,
25441
+ receivingPubkey: signingPubKey,
25442
+ network: this.config.getNetwork()
25443
+ });
25444
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25445
+ const directRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25446
+ const directFromCpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25447
+ const cpfpRefundTxSighash = getSigHashFromTx(
25448
+ cpfpRefundTx,
25449
+ 0,
25450
+ cpfpRootTx.getOutput(0)
25451
+ );
25452
+ if (!directRefundTx || !directFromCpfpRefundTx) {
25453
+ throw new ValidationError(
25454
+ "Expected direct refund transactions for tree creation",
25455
+ {
25456
+ field: "directRefundTx",
25457
+ value: directRefundTx
25458
+ }
25459
+ );
25460
+ }
25461
+ const directRefundTxSighash = getSigHashFromTx(
25462
+ directRefundTx,
25463
+ 0,
25464
+ directRootTx.getOutput(0)
25465
+ );
25466
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
25467
+ directFromCpfpRefundTx,
25468
+ 0,
25469
+ cpfpRootTx.getOutput(0)
25470
+ );
24224
25471
  const sparkClient = await this.connectionManager.createSparkClient(
24225
25472
  this.config.getCoordinatorAddress()
24226
25473
  );
24227
- let depositResp;
25474
+ let treeResp;
24228
25475
  try {
24229
- depositResp = await sparkClient.generate_deposit_address({
24230
- signingPublicKey: signingPubkey,
25476
+ treeResp = await sparkClient.start_deposit_tree_creation({
24231
25477
  identityPublicKey: await this.config.signer.getIdentityPublicKey(),
24232
- network: this.config.getNetworkProto(),
24233
- leafId,
24234
- isStatic
25478
+ onChainUtxo: {
25479
+ vout,
25480
+ rawTx: depositTx.toBytes(true),
25481
+ network: this.config.getNetworkProto()
25482
+ },
25483
+ rootTxSigningJob: {
25484
+ rawTx: cpfpRootTx.toBytes(),
25485
+ signingPublicKey: signingPubKey,
25486
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
25487
+ },
25488
+ refundTxSigningJob: {
25489
+ rawTx: cpfpRefundTx.toBytes(),
25490
+ signingPublicKey: signingPubKey,
25491
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
25492
+ },
25493
+ directRootTxSigningJob: {
25494
+ rawTx: directRootTx.toBytes(),
25495
+ signingPublicKey: signingPubKey,
25496
+ signingNonceCommitment: directRootNonceCommitment.commitment
25497
+ },
25498
+ directRefundTxSigningJob: {
25499
+ rawTx: directRefundTx.toBytes(),
25500
+ signingPublicKey: signingPubKey,
25501
+ signingNonceCommitment: directRefundNonceCommitment.commitment
25502
+ },
25503
+ directFromCpfpRefundTxSigningJob: {
25504
+ rawTx: directFromCpfpRefundTx.toBytes(),
25505
+ signingPublicKey: signingPubKey,
25506
+ signingNonceCommitment: directFromCpfpRefundNonceCommitment.commitment
25507
+ }
24235
25508
  });
24236
25509
  } catch (error) {
24237
25510
  throw new NetworkError(
24238
- "Failed to generate deposit address",
25511
+ "Failed to start deposit tree creation",
24239
25512
  {
24240
- operation: "generate_deposit_address",
25513
+ operation: "start_deposit_tree_creation",
24241
25514
  errorCount: 1,
24242
25515
  errors: error instanceof Error ? error.message : String(error)
24243
25516
  },
24244
25517
  error
24245
25518
  );
24246
25519
  }
24247
- if (!depositResp.depositAddress) {
25520
+ if (!treeResp.rootNodeSignatureShares?.verifyingKey) {
25521
+ throw new ValidationError("No verifying key found in tree response", {
25522
+ field: "verifyingKey",
25523
+ value: treeResp.rootNodeSignatureShares,
25524
+ expected: "Non-null verifying key"
25525
+ });
25526
+ }
25527
+ if (!treeResp.rootNodeSignatureShares.nodeTxSigningResult?.signingNonceCommitments) {
24248
25528
  throw new ValidationError(
24249
- "No deposit address response from coordinator",
25529
+ "No signing nonce commitments found in tree response",
24250
25530
  {
24251
- field: "depositAddress",
24252
- value: depositResp
25531
+ field: "nodeTxSigningResult.signingNonceCommitments",
25532
+ value: treeResp.rootNodeSignatureShares.nodeTxSigningResult,
25533
+ expected: "Non-null signing nonce commitments"
24253
25534
  }
24254
25535
  );
24255
25536
  }
24256
- await this.validateDepositAddress({
24257
- address: depositResp.depositAddress,
24258
- userPubkey: signingPubkey
25537
+ if (!treeResp.rootNodeSignatureShares.refundTxSigningResult?.signingNonceCommitments) {
25538
+ throw new ValidationError(
25539
+ "No signing nonce commitments found in tree response",
25540
+ {
25541
+ field: "refundTxSigningResult.signingNonceCommitments"
25542
+ }
25543
+ );
25544
+ }
25545
+ if (!treeResp.rootNodeSignatureShares.directNodeTxSigningResult?.signingNonceCommitments) {
25546
+ throw new ValidationError(
25547
+ "No direct node signing nonce commitments found in tree response",
25548
+ {
25549
+ field: "directNodeTxSigningResult.signingNonceCommitments"
25550
+ }
25551
+ );
25552
+ }
25553
+ if (!treeResp.rootNodeSignatureShares.directRefundTxSigningResult?.signingNonceCommitments) {
25554
+ throw new ValidationError(
25555
+ "No direct refund signing nonce commitments found in tree response",
25556
+ {
25557
+ field: "directRefundTxSigningResult.signingNonceCommitments"
25558
+ }
25559
+ );
25560
+ }
25561
+ if (!treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult?.signingNonceCommitments) {
25562
+ throw new ValidationError(
25563
+ "No direct from CPFP refund signing nonce commitments found in tree response",
25564
+ {
25565
+ field: "directFromCpfpRefundTxSigningResult.signingNonceCommitments"
25566
+ }
25567
+ );
25568
+ }
25569
+ if (!equalBytes4(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
25570
+ throw new ValidationError("Verifying key mismatch", {
25571
+ field: "verifyingKey",
25572
+ value: treeResp.rootNodeSignatureShares.verifyingKey,
25573
+ expected: verifyingKey
25574
+ });
25575
+ }
25576
+ const cpfpRootSignature = await this.config.signer.signFrost({
25577
+ message: cpfpRootTxSighash,
25578
+ publicKey: signingPubKey,
25579
+ keyDerivation,
25580
+ verifyingKey,
25581
+ selfCommitment: cpfpRootNonceCommitment,
25582
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
25583
+ adaptorPubKey: new Uint8Array()
24259
25584
  });
24260
- return depositResp;
25585
+ const directRootSignature = await this.config.signer.signFrost({
25586
+ message: directRootTxSighash,
25587
+ publicKey: signingPubKey,
25588
+ keyDerivation,
25589
+ verifyingKey,
25590
+ selfCommitment: directRootNonceCommitment,
25591
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
25592
+ adaptorPubKey: new Uint8Array()
25593
+ });
25594
+ const cpfpRefundSignature = await this.config.signer.signFrost({
25595
+ message: cpfpRefundTxSighash,
25596
+ publicKey: signingPubKey,
25597
+ keyDerivation,
25598
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
25599
+ selfCommitment: cpfpRefundNonceCommitment,
25600
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
25601
+ adaptorPubKey: new Uint8Array()
25602
+ });
25603
+ const directRefundSignature = await this.config.signer.signFrost({
25604
+ message: directRefundTxSighash,
25605
+ publicKey: signingPubKey,
25606
+ keyDerivation,
25607
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
25608
+ selfCommitment: directRefundNonceCommitment,
25609
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
25610
+ adaptorPubKey: new Uint8Array()
25611
+ });
25612
+ const directFromCpfpRefundSignature = await this.config.signer.signFrost({
25613
+ message: directFromCpfpRefundTxSighash,
25614
+ publicKey: signingPubKey,
25615
+ keyDerivation,
25616
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
25617
+ selfCommitment: directFromCpfpRefundNonceCommitment,
25618
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
25619
+ adaptorPubKey: new Uint8Array()
25620
+ });
25621
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
25622
+ message: cpfpRootTxSighash,
25623
+ statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
25624
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
25625
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
25626
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
25627
+ selfCommitment: cpfpRootNonceCommitment,
25628
+ publicKey: signingPubKey,
25629
+ selfSignature: cpfpRootSignature,
25630
+ adaptorPubKey: new Uint8Array()
25631
+ });
25632
+ const directRootAggregate = await this.config.signer.aggregateFrost({
25633
+ message: directRootTxSighash,
25634
+ statechainSignatures: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signatureShares,
25635
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.publicKeys,
25636
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
25637
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
25638
+ selfCommitment: directRootNonceCommitment,
25639
+ publicKey: signingPubKey,
25640
+ selfSignature: directRootSignature,
25641
+ adaptorPubKey: new Uint8Array()
25642
+ });
25643
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
25644
+ message: cpfpRefundTxSighash,
25645
+ statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
25646
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
25647
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
25648
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
25649
+ selfCommitment: cpfpRefundNonceCommitment,
25650
+ publicKey: signingPubKey,
25651
+ selfSignature: cpfpRefundSignature,
25652
+ adaptorPubKey: new Uint8Array()
25653
+ });
25654
+ const directRefundAggregate = await this.config.signer.aggregateFrost({
25655
+ message: directRefundTxSighash,
25656
+ statechainSignatures: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signatureShares,
25657
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.publicKeys,
25658
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
25659
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
25660
+ selfCommitment: directRefundNonceCommitment,
25661
+ publicKey: signingPubKey,
25662
+ selfSignature: directRefundSignature,
25663
+ adaptorPubKey: new Uint8Array()
25664
+ });
25665
+ const directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
25666
+ message: directFromCpfpRefundTxSighash,
25667
+ statechainSignatures: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signatureShares,
25668
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.publicKeys,
25669
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
25670
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
25671
+ selfCommitment: directFromCpfpRefundNonceCommitment,
25672
+ publicKey: signingPubKey,
25673
+ selfSignature: directFromCpfpRefundSignature,
25674
+ adaptorPubKey: new Uint8Array()
25675
+ });
25676
+ let finalizeResp;
25677
+ try {
25678
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
25679
+ intent: 0 /* CREATION */,
25680
+ nodeSignatures: [
25681
+ {
25682
+ nodeId: treeResp.rootNodeSignatureShares.nodeId,
25683
+ nodeTxSignature: cpfpRootAggregate,
25684
+ refundTxSignature: cpfpRefundAggregate,
25685
+ directNodeTxSignature: directRootAggregate,
25686
+ directRefundTxSignature: directRefundAggregate,
25687
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate
25688
+ }
25689
+ ]
25690
+ });
25691
+ } catch (error) {
25692
+ throw new NetworkError(
25693
+ "Failed to finalize node signatures",
25694
+ {
25695
+ operation: "finalize_node_signatures",
25696
+ errorCount: 1,
25697
+ errors: error instanceof Error ? error.message : String(error)
25698
+ },
25699
+ error
25700
+ );
25701
+ }
25702
+ return finalizeResp;
24261
25703
  }
24262
- async createTreeRoot({
25704
+ /**
25705
+ * @deprecated
25706
+ * Use createTreeRoot instead.
25707
+ * This is currently only used to test backwards compatibility.
25708
+ */
25709
+ async createTreeWithoutDirectTx({
24263
25710
  keyDerivation,
24264
25711
  verifyingKey,
24265
25712
  depositTx,
24266
25713
  vout
24267
25714
  }) {
24268
- const rootTx = new Transaction6({ version: 3 });
24269
25715
  const output = depositTx.getOutput(vout);
24270
25716
  if (!output) {
24271
25717
  throw new ValidationError("Invalid deposit transaction output", {
@@ -24283,39 +25729,31 @@ var DepositService = class {
24283
25729
  expected: "Output with script and amount"
24284
25730
  });
24285
25731
  }
24286
- let outputAmount = amount;
24287
- rootTx.addInput({
24288
- txid: getTxId(depositTx),
25732
+ const depositOutPoint = {
25733
+ txid: hexToBytes7(getTxId(depositTx)),
24289
25734
  index: vout
24290
- });
24291
- rootTx.addOutput({
25735
+ };
25736
+ const depositTxOut = {
24292
25737
  script,
24293
- amount: outputAmount
24294
- });
24295
- rootTx.addOutput(getEphemeralAnchorOutput());
24296
- const rootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
24297
- const rootTxSighash = getSigHashFromTx(rootTx, 0, output);
24298
- const refundTx = new Transaction6({ version: 3 });
24299
- const sequence = 1 << 30 | INITIAL_TIME_LOCK2;
24300
- refundTx.addInput({
24301
- txid: getTxId(rootTx),
24302
- index: 0,
24303
- sequence
24304
- });
25738
+ amount
25739
+ };
25740
+ const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
25741
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25742
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
24305
25743
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
24306
- const refundP2trAddress = getP2TRAddressFromPublicKey(
24307
- signingPubKey,
24308
- this.config.getNetwork()
24309
- );
24310
- const refundAddress = btc5.Address(getNetwork(this.config.getNetwork())).decode(refundP2trAddress);
24311
- const refundPkScript = btc5.OutScript.encode(refundAddress);
24312
- refundTx.addOutput({
24313
- script: refundPkScript,
24314
- amount: outputAmount
25744
+ const { cpfpRefundTx } = createRefundTxs({
25745
+ sequence: INITIAL_SEQUENCE,
25746
+ input: { txid: hexToBytes7(getTxId(cpfpRootTx)), index: 0 },
25747
+ amountSats: amount,
25748
+ receivingPubkey: signingPubKey,
25749
+ network: this.config.getNetwork()
24315
25750
  });
24316
- refundTx.addOutput(getEphemeralAnchorOutput());
24317
- const refundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
24318
- const refundTxSighash = getSigHashFromTx(refundTx, 0, rootTx.getOutput(0));
25751
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25752
+ const cpfpRefundTxSighash = getSigHashFromTx(
25753
+ cpfpRefundTx,
25754
+ 0,
25755
+ cpfpRootTx.getOutput(0)
25756
+ );
24319
25757
  const sparkClient = await this.connectionManager.createSparkClient(
24320
25758
  this.config.getCoordinatorAddress()
24321
25759
  );
@@ -24329,14 +25767,14 @@ var DepositService = class {
24329
25767
  network: this.config.getNetworkProto()
24330
25768
  },
24331
25769
  rootTxSigningJob: {
24332
- rawTx: rootTx.toBytes(),
25770
+ rawTx: cpfpRootTx.toBytes(),
24333
25771
  signingPublicKey: signingPubKey,
24334
- signingNonceCommitment: rootNonceCommitment.commitment
25772
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
24335
25773
  },
24336
25774
  refundTxSigningJob: {
24337
- rawTx: refundTx.toBytes(),
25775
+ rawTx: cpfpRefundTx.toBytes(),
24338
25776
  signingPublicKey: signingPubKey,
24339
- signingNonceCommitment: refundNonceCommitment.commitment
25777
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
24340
25778
  }
24341
25779
  });
24342
25780
  } catch (error) {
@@ -24382,55 +25820,55 @@ var DepositService = class {
24382
25820
  expected: verifyingKey
24383
25821
  });
24384
25822
  }
24385
- const rootSignature = await this.config.signer.signFrost({
24386
- message: rootTxSighash,
25823
+ const cpfpRootSignature = await this.config.signer.signFrost({
25824
+ message: cpfpRootTxSighash,
24387
25825
  publicKey: signingPubKey,
24388
25826
  keyDerivation,
24389
25827
  verifyingKey,
24390
- selfCommitment: rootNonceCommitment,
25828
+ selfCommitment: cpfpRootNonceCommitment,
24391
25829
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
24392
25830
  adaptorPubKey: new Uint8Array()
24393
25831
  });
24394
- const refundSignature = await this.config.signer.signFrost({
24395
- message: refundTxSighash,
25832
+ const cpfpRefundSignature = await this.config.signer.signFrost({
25833
+ message: cpfpRefundTxSighash,
24396
25834
  publicKey: signingPubKey,
24397
25835
  keyDerivation,
24398
- verifyingKey,
24399
- selfCommitment: refundNonceCommitment,
25836
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
25837
+ selfCommitment: cpfpRefundNonceCommitment,
24400
25838
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
24401
25839
  adaptorPubKey: new Uint8Array()
24402
25840
  });
24403
- const rootAggregate = await this.config.signer.aggregateFrost({
24404
- message: rootTxSighash,
25841
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
25842
+ message: cpfpRootTxSighash,
24405
25843
  statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
24406
25844
  statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
24407
25845
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
24408
25846
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
24409
- selfCommitment: rootNonceCommitment,
25847
+ selfCommitment: cpfpRootNonceCommitment,
24410
25848
  publicKey: signingPubKey,
24411
- selfSignature: rootSignature,
25849
+ selfSignature: cpfpRootSignature,
24412
25850
  adaptorPubKey: new Uint8Array()
24413
25851
  });
24414
- const refundAggregate = await this.config.signer.aggregateFrost({
24415
- message: refundTxSighash,
25852
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
25853
+ message: cpfpRefundTxSighash,
24416
25854
  statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
24417
25855
  statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
24418
25856
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
24419
25857
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
24420
- selfCommitment: refundNonceCommitment,
25858
+ selfCommitment: cpfpRefundNonceCommitment,
24421
25859
  publicKey: signingPubKey,
24422
- selfSignature: refundSignature,
25860
+ selfSignature: cpfpRefundSignature,
24423
25861
  adaptorPubKey: new Uint8Array()
24424
25862
  });
24425
25863
  let finalizeResp;
24426
25864
  try {
24427
- finalizeResp = await sparkClient.finalize_node_signatures({
25865
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
24428
25866
  intent: 0 /* CREATION */,
24429
25867
  nodeSignatures: [
24430
25868
  {
24431
25869
  nodeId: treeResp.rootNodeSignatureShares.nodeId,
24432
- nodeTxSignature: rootAggregate,
24433
- refundTxSignature: refundAggregate
25870
+ nodeTxSignature: cpfpRootAggregate,
25871
+ refundTxSignature: cpfpRefundAggregate
24434
25872
  }
24435
25873
  ]
24436
25874
  });
@@ -24642,7 +26080,8 @@ var LightningService = class {
24642
26080
  let signingCommitments;
24643
26081
  try {
24644
26082
  signingCommitments = await sparkClient.get_signing_commitments({
24645
- nodeIds: leaves.map((leaf) => leaf.leaf.id)
26083
+ nodeIds: leaves.map((leaf) => leaf.leaf.id),
26084
+ count: 3
24646
26085
  });
24647
26086
  } catch (error) {
24648
26087
  throw new NetworkError(
@@ -24655,10 +26094,19 @@ var LightningService = class {
24655
26094
  error
24656
26095
  );
24657
26096
  }
24658
- const leafSigningJobs = await this.signingService.signRefunds(
26097
+ const {
26098
+ cpfpLeafSigningJobs,
26099
+ directLeafSigningJobs,
26100
+ directFromCpfpLeafSigningJobs
26101
+ } = await this.signingService.signRefunds(
24659
26102
  leaves,
24660
- signingCommitments.signingCommitments,
24661
- receiverIdentityPubkey
26103
+ receiverIdentityPubkey,
26104
+ signingCommitments.signingCommitments.slice(0, leaves.length),
26105
+ signingCommitments.signingCommitments.slice(
26106
+ leaves.length,
26107
+ 2 * leaves.length
26108
+ ),
26109
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
24662
26110
  );
24663
26111
  const transferId = uuidv74();
24664
26112
  let bolt11String = "";
@@ -24695,7 +26143,7 @@ var LightningService = class {
24695
26143
  const reason = isInboundPayment ? 1 /* REASON_RECEIVE */ : 0 /* REASON_SEND */;
24696
26144
  let response;
24697
26145
  try {
24698
- response = await sparkClient.initiate_preimage_swap({
26146
+ response = await sparkClient.initiate_preimage_swap_v2({
24699
26147
  paymentHash,
24700
26148
  invoiceAmount: {
24701
26149
  invoiceAmountProof: {
@@ -24707,7 +26155,9 @@ var LightningService = class {
24707
26155
  transfer: {
24708
26156
  transferId,
24709
26157
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
24710
- leavesToSend: leafSigningJobs,
26158
+ leavesToSend: cpfpLeafSigningJobs,
26159
+ directLeavesToSend: directLeafSigningJobs,
26160
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs,
24711
26161
  receiverIdentityPublicKey: receiverIdentityPubkey,
24712
26162
  expiryTime: new Date(Date.now() + 2 * 60 * 1e3)
24713
26163
  },
@@ -26961,7 +28411,9 @@ var TokenTransactionService = class {
26961
28411
  issuerPublicKeys,
26962
28412
  tokenTransactionHashes,
26963
28413
  tokenIdentifiers,
26964
- outputIds
28414
+ outputIds,
28415
+ pageSize,
28416
+ offset
26965
28417
  } = params;
26966
28418
  const tokenClient = await this.connectionManager.createSparkTokenClient(
26967
28419
  this.config.getCoordinatorAddress()
@@ -26972,8 +28424,8 @@ var TokenTransactionService = class {
26972
28424
  tokenIdentifiers: tokenIdentifiers?.map(hexToBytes9),
26973
28425
  tokenTransactionHashes: tokenTransactionHashes?.map(hexToBytes9),
26974
28426
  outputIds: outputIds || [],
26975
- limit: 100,
26976
- offset: 0
28427
+ limit: pageSize,
28428
+ offset
26977
28429
  };
26978
28430
  try {
26979
28431
  const response = await tokenClient.query_token_transactions(queryParams);
@@ -27180,14 +28632,7 @@ function isTokenTransaction(tokenTransaction) {
27180
28632
 
27181
28633
  // src/services/tree-creation.ts
27182
28634
  import { hexToBytes as hexToBytes10 } from "@noble/curves/abstract/utils";
27183
- import { Address as Address5, OutScript as OutScript3, Transaction as Transaction7 } from "@scure/btc-signer";
27184
- var INITIAL_TIME_LOCK3 = 2e3;
27185
- function maybeApplyFee2(amount) {
27186
- if (amount > BigInt(DEFAULT_FEE_SATS)) {
27187
- return amount - BigInt(DEFAULT_FEE_SATS);
27188
- }
27189
- return amount;
27190
- }
28635
+ import { Address as Address4, OutScript as OutScript2 } from "@scure/btc-signer";
27191
28636
  var TreeCreationService = class {
27192
28637
  config;
27193
28638
  connectionManager;
@@ -27302,7 +28747,7 @@ var TreeCreationService = class {
27302
28747
  );
27303
28748
  let response;
27304
28749
  try {
27305
- response = await sparkClient.create_tree(request);
28750
+ response = await sparkClient.create_tree_v2(request);
27306
28751
  } catch (error) {
27307
28752
  throw new Error(`Error creating tree: ${error}`);
27308
28753
  }
@@ -27319,7 +28764,7 @@ var TreeCreationService = class {
27319
28764
  );
27320
28765
  let finalizeResp;
27321
28766
  try {
27322
- finalizeResp = await sparkClient.finalize_node_signatures({
28767
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
27323
28768
  nodeSignatures
27324
28769
  });
27325
28770
  } catch (error) {
@@ -27384,91 +28829,111 @@ var TreeCreationService = class {
27384
28829
  async buildChildCreationNode(node, parentTx, vout, network) {
27385
28830
  const internalCreationNode = {
27386
28831
  nodeTxSigningJob: void 0,
27387
- refundTxSigningJob: void 0,
27388
- children: [],
27389
28832
  directNodeTxSigningJob: void 0,
28833
+ refundTxSigningJob: void 0,
27390
28834
  directRefundTxSigningJob: void 0,
27391
- directFromCpfpRefundTxSigningJob: void 0
28835
+ directFromCpfpRefundTxSigningJob: void 0,
28836
+ children: []
27392
28837
  };
27393
- const tx = new Transaction7({ version: 3 });
27394
- tx.addInput({
27395
- txid: getTxId(parentTx),
27396
- index: vout
27397
- });
27398
28838
  const parentTxOut = parentTx.getOutput(vout);
27399
28839
  if (!parentTxOut?.script || !parentTxOut?.amount) {
27400
28840
  throw new Error("parentTxOut is undefined");
27401
28841
  }
27402
- tx.addOutput({
28842
+ const parentOutPoint = {
28843
+ txid: hexToBytes10(getTxId(parentTx)),
28844
+ index: vout
28845
+ };
28846
+ const parentTxOutObj = {
27403
28847
  script: parentTxOut.script,
27404
28848
  amount: parentTxOut.amount
27405
- // maybeApplyFee(parentTxOut.amount),
27406
- });
27407
- tx.addOutput(getEphemeralAnchorOutput());
27408
- const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
27409
- const signingJob = {
28849
+ };
28850
+ const { cpfpNodeTx, directNodeTx } = createNodeTxs(
28851
+ parentTxOutObj,
28852
+ parentOutPoint
28853
+ );
28854
+ const cpfpNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
28855
+ const directNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
28856
+ const cpfpNodeSigningJob = {
27410
28857
  signingPublicKey: node.signingPublicKey,
27411
- rawTx: tx.toBytes(),
27412
- signingNonceCommitment: signingNonceCommitment.commitment
28858
+ rawTx: cpfpNodeTx.toBytes(),
28859
+ signingNonceCommitment: cpfpNodeSigningCommitment.commitment
27413
28860
  };
27414
- internalCreationNode.nodeTxSigningCommitment = signingNonceCommitment;
27415
- internalCreationNode.nodeTxSigningJob = signingJob;
27416
- const sequence = 1 << 30 | INITIAL_TIME_LOCK3;
28861
+ const directNodeSigningJob = directNodeTx ? {
28862
+ signingPublicKey: node.signingPublicKey,
28863
+ rawTx: directNodeTx.toBytes(),
28864
+ signingNonceCommitment: directNodeSigningCommitment.commitment
28865
+ } : void 0;
28866
+ internalCreationNode.nodeTxSigningCommitment = cpfpNodeSigningCommitment;
28867
+ internalCreationNode.directNodeTxSigningCommitment = directNodeSigningCommitment;
28868
+ internalCreationNode.nodeTxSigningJob = cpfpNodeSigningJob;
28869
+ internalCreationNode.directNodeTxSigningJob = directNodeSigningJob;
28870
+ const sequence = INITIAL_SEQUENCE;
28871
+ const directSequence = INITIAL_DIRECT_SEQUENCE;
27417
28872
  const childCreationNode = {
27418
28873
  nodeTxSigningJob: void 0,
27419
- refundTxSigningJob: void 0,
27420
- children: [],
27421
28874
  directNodeTxSigningJob: void 0,
28875
+ refundTxSigningJob: void 0,
27422
28876
  directRefundTxSigningJob: void 0,
27423
- directFromCpfpRefundTxSigningJob: void 0
28877
+ directFromCpfpRefundTxSigningJob: void 0,
28878
+ children: []
27424
28879
  };
27425
- const childTx = new Transaction7({ version: 3 });
27426
- childTx.addInput({
27427
- txid: getTxId(tx),
27428
- index: 0,
27429
- sequence
27430
- });
27431
- childTx.addOutput({
27432
- script: parentTxOut.script,
27433
- amount: parentTxOut.amount
27434
- // maybeApplyFee(parentTxOut.amount),
27435
- });
27436
- childTx.addOutput(getEphemeralAnchorOutput());
27437
- const childSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
27438
- const childSigningJob = {
28880
+ const [cpfpLeafTx, directLeafTx] = createLeafNodeTx(
28881
+ sequence,
28882
+ directSequence,
28883
+ { txid: hexToBytes10(getTxId(cpfpNodeTx)), index: 0 },
28884
+ parentTxOutObj,
28885
+ true
28886
+ // shouldCalculateFee
28887
+ );
28888
+ const cpfpLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
28889
+ const directLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
28890
+ const cpfpLeafSigningJob = {
27439
28891
  signingPublicKey: node.signingPublicKey,
27440
- rawTx: childTx.toBytes(),
27441
- signingNonceCommitment: childSigningNonceCommitment.commitment
28892
+ rawTx: cpfpLeafTx.toBytes(),
28893
+ signingNonceCommitment: cpfpLeafSigningCommitment.commitment
27442
28894
  };
27443
- childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
27444
- childCreationNode.nodeTxSigningJob = childSigningJob;
27445
- const refundTx = new Transaction7({ version: 3 });
27446
- refundTx.addInput({
27447
- txid: getTxId(childTx),
27448
- index: 0,
27449
- sequence
27450
- });
27451
- const refundP2trAddress = getP2TRAddressFromPublicKey(
27452
- node.signingPublicKey,
28895
+ const directLeafSigningJob = {
28896
+ signingPublicKey: node.signingPublicKey,
28897
+ rawTx: directLeafTx.toBytes(),
28898
+ signingNonceCommitment: directLeafSigningCommitment.commitment
28899
+ };
28900
+ childCreationNode.nodeTxSigningCommitment = cpfpLeafSigningCommitment;
28901
+ childCreationNode.directNodeTxSigningCommitment = directLeafSigningCommitment;
28902
+ childCreationNode.nodeTxSigningJob = cpfpLeafSigningJob;
28903
+ childCreationNode.directNodeTxSigningJob = directLeafSigningJob;
28904
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
28905
+ sequence,
28906
+ directSequence,
28907
+ input: { txid: hexToBytes10(getTxId(cpfpLeafTx)), index: 0 },
28908
+ directInput: { txid: hexToBytes10(getTxId(directLeafTx)), index: 0 },
28909
+ amountSats: parentTxOut.amount,
28910
+ receivingPubkey: node.signingPublicKey,
27453
28911
  network
27454
- );
27455
- const refundAddress = Address5(getNetwork(network)).decode(
27456
- refundP2trAddress
27457
- );
27458
- const refundPkScript = OutScript3.encode(refundAddress);
27459
- refundTx.addOutput({
27460
- script: refundPkScript,
27461
- amount: maybeApplyFee2(parentTxOut.amount)
27462
28912
  });
27463
- refundTx.addOutput(getEphemeralAnchorOutput());
27464
- const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
27465
- const refundSigningJob = {
28913
+ const cpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
28914
+ const directRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
28915
+ const directFromCpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
28916
+ const cpfpRefundSigningJob = {
27466
28917
  signingPublicKey: node.signingPublicKey,
27467
- rawTx: refundTx.toBytes(),
27468
- signingNonceCommitment: refundSigningNonceCommitment.commitment
28918
+ rawTx: cpfpRefundTx.toBytes(),
28919
+ signingNonceCommitment: cpfpRefundSigningCommitment.commitment
27469
28920
  };
27470
- childCreationNode.refundTxSigningCommitment = refundSigningNonceCommitment;
27471
- childCreationNode.refundTxSigningJob = refundSigningJob;
28921
+ const directRefundSigningJob = directRefundTx ? {
28922
+ signingPublicKey: node.signingPublicKey,
28923
+ rawTx: directRefundTx.toBytes(),
28924
+ signingNonceCommitment: directRefundSigningCommitment.commitment
28925
+ } : void 0;
28926
+ const directFromCpfpRefundSigningJob = directFromCpfpRefundTx ? {
28927
+ signingPublicKey: node.signingPublicKey,
28928
+ rawTx: directFromCpfpRefundTx.toBytes(),
28929
+ signingNonceCommitment: directFromCpfpRefundSigningCommitment.commitment
28930
+ } : void 0;
28931
+ childCreationNode.refundTxSigningCommitment = cpfpRefundSigningCommitment;
28932
+ childCreationNode.directRefundTxSigningCommitment = directRefundSigningCommitment;
28933
+ childCreationNode.directFromCpfpRefundTxSigningCommitment = directFromCpfpRefundSigningCommitment;
28934
+ childCreationNode.refundTxSigningJob = cpfpRefundSigningJob;
28935
+ childCreationNode.directRefundTxSigningJob = directRefundSigningJob;
28936
+ childCreationNode.directFromCpfpRefundTxSigningJob = directFromCpfpRefundSigningJob;
27472
28937
  internalCreationNode.children.push(childCreationNode);
27473
28938
  return internalCreationNode;
27474
28939
  }
@@ -27477,40 +28942,49 @@ var TreeCreationService = class {
27477
28942
  if (!parentTxOutput?.script || !parentTxOutput?.amount) {
27478
28943
  throw new Error("parentTxOutput is undefined");
27479
28944
  }
27480
- const rootNodeTx = new Transaction7({ version: 3 });
27481
- rootNodeTx.addInput({
27482
- txid: getTxId(parentTx),
27483
- index: vout
27484
- });
28945
+ const childTxOuts = [];
27485
28946
  for (let i = 0; i < root.children.length; i++) {
27486
28947
  const child = root.children[i];
27487
28948
  if (!child || !child.address) {
27488
28949
  throw new Error("child address is undefined");
27489
28950
  }
27490
- const childAddress = Address5(getNetwork(network)).decode(child.address);
27491
- const childPkScript = OutScript3.encode(childAddress);
27492
- rootNodeTx.addOutput({
28951
+ const childAddress = Address4(getNetwork(network)).decode(child.address);
28952
+ const childPkScript = OutScript2.encode(childAddress);
28953
+ childTxOuts.push({
27493
28954
  script: childPkScript,
27494
28955
  amount: parentTxOutput.amount / 2n
27495
- // feeAdjustedAmount / 2n,
27496
28956
  });
27497
28957
  }
27498
- rootNodeTx.addOutput(getEphemeralAnchorOutput());
27499
- const rootNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
27500
- const rootNodeSigningJob = {
28958
+ const parentOutPoint = {
28959
+ txid: hexToBytes10(getTxId(parentTx)),
28960
+ index: vout
28961
+ };
28962
+ const [cpfpSplitTx, directSplitTx] = createSplitTx(
28963
+ parentOutPoint,
28964
+ childTxOuts
28965
+ );
28966
+ const cpfpSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
28967
+ const directSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
28968
+ const cpfpSplitSigningJob = {
28969
+ signingPublicKey: root.signingPublicKey,
28970
+ rawTx: cpfpSplitTx.toBytes(),
28971
+ signingNonceCommitment: cpfpSplitSigningCommitment.commitment
28972
+ };
28973
+ const directSplitSigningJob = {
27501
28974
  signingPublicKey: root.signingPublicKey,
27502
- rawTx: rootNodeTx.toBytes(),
27503
- signingNonceCommitment: rootNodeSigningCommitment.commitment
28975
+ rawTx: directSplitTx.toBytes(),
28976
+ signingNonceCommitment: directSplitSigningCommitment.commitment
27504
28977
  };
27505
28978
  const rootCreationNode = {
27506
- nodeTxSigningJob: rootNodeSigningJob,
28979
+ nodeTxSigningJob: cpfpSplitSigningJob,
28980
+ directNodeTxSigningJob: directSplitSigningJob,
27507
28981
  refundTxSigningJob: void 0,
27508
- children: [],
27509
- directNodeTxSigningJob: void 0,
27510
28982
  directRefundTxSigningJob: void 0,
27511
- directFromCpfpRefundTxSigningJob: void 0
28983
+ directFromCpfpRefundTxSigningJob: void 0,
28984
+ children: []
27512
28985
  };
27513
- rootCreationNode.nodeTxSigningCommitment = rootNodeSigningCommitment;
28986
+ rootCreationNode.nodeTxSigningCommitment = cpfpSplitSigningCommitment;
28987
+ rootCreationNode.directNodeTxSigningCommitment = directSplitSigningCommitment;
27514
28988
  const leftChild = root.children[0];
27515
28989
  const rightChild = root.children[1];
27516
28990
  if (!leftChild || !rightChild) {
@@ -27518,13 +28992,15 @@ var TreeCreationService = class {
27518
28992
  }
27519
28993
  const leftChildCreationNode = await this.buildChildCreationNode(
27520
28994
  leftChild,
27521
- rootNodeTx,
28995
+ cpfpSplitTx,
28996
+ // Use CPFP version for children
27522
28997
  0,
27523
28998
  network
27524
28999
  );
27525
29000
  const rightChildCreationNode = await this.buildChildCreationNode(
27526
29001
  rightChild,
27527
- rootNodeTx,
29002
+ cpfpSplitTx,
29003
+ // Use CPFP version for children
27528
29004
  1,
27529
29005
  network
27530
29006
  );
@@ -27533,19 +29009,19 @@ var TreeCreationService = class {
27533
29009
  return rootCreationNode;
27534
29010
  }
27535
29011
  async signNodeCreation(parentTx, vout, internalNode, creationNode, creationResponseNode) {
27536
- if (!creationNode.nodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
29012
+ if (!creationNode.nodeTxSigningJob?.signingPublicKey || !creationNode.directNodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
27537
29013
  throw new Error("signingPublicKey or verificationKey is undefined");
27538
29014
  }
27539
29015
  const parentTxOutput = parentTx.getOutput(vout);
27540
29016
  if (!parentTxOutput) {
27541
29017
  throw new Error("parentTxOutput is undefined");
27542
29018
  }
27543
- const tx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
27544
- const txSighash = getSigHashFromTx(tx, 0, parentTxOutput);
27545
- let nodeTxSignature = new Uint8Array();
29019
+ const cpfpNodeTx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
29020
+ const cpfpNodeTxSighash = getSigHashFromTx(cpfpNodeTx, 0, parentTxOutput);
29021
+ let cpfpNodeTxSignature = new Uint8Array();
27546
29022
  if (creationNode.nodeTxSigningCommitment) {
27547
- const userSignature = await this.config.signer.signFrost({
27548
- message: txSighash,
29023
+ const cpfpUserSignature = await this.config.signer.signFrost({
29024
+ message: cpfpNodeTxSighash,
27549
29025
  publicKey: creationNode.nodeTxSigningJob.signingPublicKey,
27550
29026
  keyDerivation: {
27551
29027
  type: "leaf" /* LEAF */,
@@ -27555,30 +29031,84 @@ var TreeCreationService = class {
27555
29031
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
27556
29032
  verifyingKey: internalNode.verificationKey
27557
29033
  });
27558
- nodeTxSignature = await this.config.signer.aggregateFrost({
27559
- message: txSighash,
29034
+ cpfpNodeTxSignature = await this.config.signer.aggregateFrost({
29035
+ message: cpfpNodeTxSighash,
27560
29036
  statechainSignatures: creationResponseNode.nodeTxSigningResult?.signatureShares,
27561
29037
  statechainPublicKeys: creationResponseNode.nodeTxSigningResult?.publicKeys,
27562
29038
  verifyingKey: internalNode.verificationKey,
27563
29039
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
27564
29040
  selfCommitment: creationNode.nodeTxSigningCommitment,
27565
- selfSignature: userSignature,
29041
+ selfSignature: cpfpUserSignature,
29042
+ publicKey: internalNode.signingPublicKey
29043
+ });
29044
+ }
29045
+ const directNodeTx = getTxFromRawTxBytes(
29046
+ creationNode.directNodeTxSigningJob.rawTx
29047
+ );
29048
+ const directNodeTxSighash = getSigHashFromTx(
29049
+ directNodeTx,
29050
+ 0,
29051
+ parentTxOutput
29052
+ );
29053
+ let directNodeTxSignature = new Uint8Array();
29054
+ if (creationNode.directNodeTxSigningCommitment) {
29055
+ const directUserSignature = await this.config.signer.signFrost({
29056
+ message: directNodeTxSighash,
29057
+ publicKey: creationNode.directNodeTxSigningJob.signingPublicKey,
29058
+ keyDerivation: {
29059
+ type: "leaf" /* LEAF */,
29060
+ path: creationResponseNode.nodeId
29061
+ },
29062
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
29063
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
29064
+ verifyingKey: internalNode.verificationKey
29065
+ });
29066
+ directNodeTxSignature = await this.config.signer.aggregateFrost({
29067
+ message: directNodeTxSighash,
29068
+ statechainSignatures: creationResponseNode.directNodeTxSigningResult?.signatureShares,
29069
+ statechainPublicKeys: creationResponseNode.directNodeTxSigningResult?.publicKeys,
29070
+ verifyingKey: internalNode.verificationKey,
29071
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
29072
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
29073
+ selfSignature: directUserSignature,
27566
29074
  publicKey: internalNode.signingPublicKey
27567
29075
  });
27568
29076
  }
27569
- let refundTxSignature = new Uint8Array();
27570
- if (creationNode.refundTxSigningCommitment) {
27571
- const rawTx = creationNode.refundTxSigningJob?.rawTx;
27572
- if (!rawTx) {
27573
- throw new Error("rawTx is undefined");
27574
- }
27575
- if (!creationNode.refundTxSigningJob?.signingPublicKey) {
27576
- throw new Error("signingPublicKey is undefined");
27577
- }
27578
- const refundTx = getTxFromRawTxBytes(rawTx);
27579
- const refundTxSighash = getSigHashFromTx(refundTx, 0, parentTxOutput);
27580
- const refundSigningResponse = await this.config.signer.signFrost({
27581
- message: refundTxSighash,
29077
+ let cpfpRefundTxSignature = new Uint8Array();
29078
+ let directRefundTxSignature = new Uint8Array();
29079
+ let directFromCpfpRefundTxSignature = new Uint8Array();
29080
+ if (creationNode.refundTxSigningCommitment && creationNode.directRefundTxSigningCommitment && creationNode.directFromCpfpRefundTxSigningCommitment) {
29081
+ const rawCpfpRefundTx = creationNode.refundTxSigningJob?.rawTx;
29082
+ const rawDirectRefundTx = creationNode.directRefundTxSigningJob?.rawTx;
29083
+ const rawDirectFromCpfpRefundTx = creationNode.directFromCpfpRefundTxSigningJob?.rawTx;
29084
+ if (!rawCpfpRefundTx || !rawDirectRefundTx || !rawDirectFromCpfpRefundTx) {
29085
+ throw new Error("refund transaction rawTx is undefined");
29086
+ }
29087
+ if (!creationNode.refundTxSigningJob?.signingPublicKey || !creationNode.directRefundTxSigningJob?.signingPublicKey || !creationNode.directFromCpfpRefundTxSigningJob?.signingPublicKey) {
29088
+ throw new Error("refund transaction signingPublicKey is undefined");
29089
+ }
29090
+ const cpfpRefundTx = getTxFromRawTxBytes(rawCpfpRefundTx);
29091
+ const directRefundTx = getTxFromRawTxBytes(rawDirectRefundTx);
29092
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
29093
+ rawDirectFromCpfpRefundTx
29094
+ );
29095
+ const cpfpRefundTxSighash = getSigHashFromTx(
29096
+ cpfpRefundTx,
29097
+ 0,
29098
+ cpfpNodeTx.getOutput(0)
29099
+ );
29100
+ const directRefundTxSighash = getSigHashFromTx(
29101
+ directRefundTx,
29102
+ 0,
29103
+ directNodeTx.getOutput(0)
29104
+ );
29105
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
29106
+ directFromCpfpRefundTx,
29107
+ 0,
29108
+ cpfpNodeTx.getOutput(0)
29109
+ );
29110
+ const cpfpRefundUserSignature = await this.config.signer.signFrost({
29111
+ message: cpfpRefundTxSighash,
27582
29112
  publicKey: creationNode.refundTxSigningJob.signingPublicKey,
27583
29113
  keyDerivation: {
27584
29114
  type: "leaf" /* LEAF */,
@@ -27588,27 +29118,69 @@ var TreeCreationService = class {
27588
29118
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
27589
29119
  verifyingKey: internalNode.verificationKey
27590
29120
  });
27591
- refundTxSignature = await this.config.signer.aggregateFrost({
27592
- message: refundTxSighash,
29121
+ cpfpRefundTxSignature = await this.config.signer.aggregateFrost({
29122
+ message: cpfpRefundTxSighash,
27593
29123
  statechainSignatures: creationResponseNode.refundTxSigningResult?.signatureShares,
27594
29124
  statechainPublicKeys: creationResponseNode.refundTxSigningResult?.publicKeys,
27595
29125
  verifyingKey: internalNode.verificationKey,
27596
29126
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
27597
29127
  selfCommitment: creationNode.refundTxSigningCommitment,
27598
- selfSignature: refundSigningResponse,
29128
+ selfSignature: cpfpRefundUserSignature,
29129
+ publicKey: internalNode.signingPublicKey
29130
+ });
29131
+ const keyDerivation = {
29132
+ type: "leaf" /* LEAF */,
29133
+ path: creationResponseNode.nodeId
29134
+ };
29135
+ const directRefundUserSignature = await this.config.signer.signFrost({
29136
+ message: directRefundTxSighash,
29137
+ publicKey: creationNode.directRefundTxSigningJob.signingPublicKey,
29138
+ keyDerivation,
29139
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
29140
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
29141
+ verifyingKey: internalNode.verificationKey
29142
+ });
29143
+ directRefundTxSignature = await this.config.signer.aggregateFrost({
29144
+ message: directRefundTxSighash,
29145
+ statechainSignatures: creationResponseNode.directRefundTxSigningResult?.signatureShares,
29146
+ statechainPublicKeys: creationResponseNode.directRefundTxSigningResult?.publicKeys,
29147
+ verifyingKey: internalNode.verificationKey,
29148
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
29149
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
29150
+ selfSignature: directRefundUserSignature,
27599
29151
  publicKey: internalNode.signingPublicKey
27600
29152
  });
29153
+ const directFromCpfpRefundUserSignature = await this.config.signer.signFrost({
29154
+ message: directFromCpfpRefundTxSighash,
29155
+ publicKey: creationNode.directFromCpfpRefundTxSigningJob.signingPublicKey,
29156
+ keyDerivation,
29157
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
29158
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
29159
+ verifyingKey: internalNode.verificationKey
29160
+ });
29161
+ directFromCpfpRefundTxSignature = await this.config.signer.aggregateFrost(
29162
+ {
29163
+ message: directFromCpfpRefundTxSighash,
29164
+ statechainSignatures: creationResponseNode.directFromCpfpRefundTxSigningResult?.signatureShares,
29165
+ statechainPublicKeys: creationResponseNode.directFromCpfpRefundTxSigningResult?.publicKeys,
29166
+ verifyingKey: internalNode.verificationKey,
29167
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
29168
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
29169
+ selfSignature: directFromCpfpRefundUserSignature,
29170
+ publicKey: internalNode.signingPublicKey
29171
+ }
29172
+ );
27601
29173
  }
27602
29174
  return {
27603
- tx,
29175
+ tx: cpfpNodeTx,
29176
+ // Return CPFP version for children
27604
29177
  signature: {
27605
29178
  nodeId: creationResponseNode.nodeId,
27606
- nodeTxSignature,
27607
- refundTxSignature,
27608
- // TODO: Add direct refund signature
27609
- directNodeTxSignature: new Uint8Array(),
27610
- directRefundTxSignature: new Uint8Array(),
27611
- directFromCpfpRefundTxSignature: new Uint8Array()
29179
+ nodeTxSignature: cpfpNodeTxSignature,
29180
+ directNodeTxSignature,
29181
+ refundTxSignature: cpfpRefundTxSignature,
29182
+ directRefundTxSignature,
29183
+ directFromCpfpRefundTxSignature
27612
29184
  }
27613
29185
  };
27614
29186
  }
@@ -27670,8 +29242,45 @@ var SigningService = class {
27670
29242
  constructor(config) {
27671
29243
  this.config = config;
27672
29244
  }
27673
- async signRefunds(leaves, signingCommitments, receiverIdentityPubkey) {
29245
+ async signRefundsInternal(refundTx, sighash, leaf, signingCommitments) {
27674
29246
  const leafSigningJobs = [];
29247
+ const signingCommitment = await this.config.signer.getRandomSigningCommitment();
29248
+ if (!signingCommitments) {
29249
+ throw new ValidationError("Invalid signing commitments", {
29250
+ field: "signingNonceCommitments",
29251
+ value: signingCommitments,
29252
+ expected: "Non-null signing commitments"
29253
+ });
29254
+ }
29255
+ const signingResult = await this.config.signer.signFrost({
29256
+ message: sighash,
29257
+ keyDerivation: leaf.keyDerivation,
29258
+ publicKey: await this.config.signer.getPublicKeyFromDerivation(
29259
+ leaf.keyDerivation
29260
+ ),
29261
+ selfCommitment: signingCommitment,
29262
+ statechainCommitments: signingCommitments,
29263
+ adaptorPubKey: new Uint8Array(),
29264
+ verifyingKey: leaf.leaf.verifyingPublicKey
29265
+ });
29266
+ leafSigningJobs.push({
29267
+ leafId: leaf.leaf.id,
29268
+ signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
29269
+ leaf.keyDerivation
29270
+ ),
29271
+ rawTx: refundTx.toBytes(),
29272
+ signingNonceCommitment: signingCommitment.commitment,
29273
+ userSignature: signingResult,
29274
+ signingCommitments: {
29275
+ signingCommitments
29276
+ }
29277
+ });
29278
+ return leafSigningJobs;
29279
+ }
29280
+ async signRefunds(leaves, receiverIdentityPubkey, cpfpSigningCommitments, directSigningCommitments, directFromCpfpSigningCommitments) {
29281
+ const cpfpLeafSigningJobs = [];
29282
+ const directLeafSigningJobs = [];
29283
+ const directFromCpfpLeafSigningJobs = [];
27675
29284
  for (let i = 0; i < leaves.length; i++) {
27676
29285
  const leaf = leaves[i];
27677
29286
  if (!leaf?.leaf) {
@@ -27682,14 +29291,20 @@ var SigningService = class {
27682
29291
  });
27683
29292
  }
27684
29293
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
27685
- const nodeOutPoint = {
29294
+ const cpfpNodeOutPoint = {
27686
29295
  txid: hexToBytes11(getTxId(nodeTx)),
27687
29296
  index: 0
27688
29297
  };
27689
29298
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
27690
- const { nextSequence } = getNextTransactionSequence(
27691
- currRefundTx.getInput(0).sequence
27692
- );
29299
+ const sequence = currRefundTx.getInput(0).sequence;
29300
+ if (!sequence) {
29301
+ throw new ValidationError("Invalid refund transaction", {
29302
+ field: "sequence",
29303
+ value: currRefundTx.getInput(0),
29304
+ expected: "Non-null sequence"
29305
+ });
29306
+ }
29307
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
27693
29308
  const amountSats = currRefundTx.getOutput(0).amount;
27694
29309
  if (amountSats === void 0) {
27695
29310
  throw new ValidationError("Invalid refund transaction", {
@@ -27698,56 +29313,98 @@ var SigningService = class {
27698
29313
  expected: "Non-null amount"
27699
29314
  });
27700
29315
  }
27701
- const refundTx = createRefundTx(
27702
- nextSequence,
27703
- nodeOutPoint,
29316
+ let directNodeTx;
29317
+ let directNodeOutPoint;
29318
+ if (leaf.leaf.directTx.length > 0) {
29319
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
29320
+ directNodeOutPoint = {
29321
+ txid: hexToBytes11(getTxId(directNodeTx)),
29322
+ index: 0
29323
+ };
29324
+ }
29325
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
29326
+ sequence: nextSequence,
29327
+ directSequence: nextDirectSequence,
29328
+ input: cpfpNodeOutPoint,
29329
+ directInput: directNodeOutPoint,
27704
29330
  amountSats,
27705
- receiverIdentityPubkey,
27706
- this.config.getNetwork()
29331
+ receivingPubkey: receiverIdentityPubkey,
29332
+ network: this.config.getNetwork()
29333
+ });
29334
+ const refundSighash = getSigHashFromTx(
29335
+ cpfpRefundTx,
29336
+ 0,
29337
+ nodeTx.getOutput(0)
27707
29338
  );
27708
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
27709
- const signingCommitment = await this.config.signer.getRandomSigningCommitment();
27710
- const signingNonceCommitments = signingCommitments[i]?.signingNonceCommitments;
27711
- if (!signingNonceCommitments) {
27712
- throw new ValidationError("Invalid signing commitments", {
27713
- field: "signingNonceCommitments",
27714
- value: signingCommitments[i],
27715
- expected: "Non-null signing nonce commitments"
27716
- });
29339
+ const signingJobs = await this.signRefundsInternal(
29340
+ cpfpRefundTx,
29341
+ refundSighash,
29342
+ leaf,
29343
+ cpfpSigningCommitments[i]?.signingNonceCommitments
29344
+ );
29345
+ cpfpLeafSigningJobs.push(...signingJobs);
29346
+ if (directRefundTx) {
29347
+ if (!directNodeTx) {
29348
+ throw new ValidationError(
29349
+ "Direct node transaction undefined while direct refund transaction is defined",
29350
+ {
29351
+ field: "directNodeTx",
29352
+ value: directNodeTx,
29353
+ expected: "Non-null direct node transaction"
29354
+ }
29355
+ );
29356
+ }
29357
+ const refundSighash2 = getSigHashFromTx(
29358
+ directRefundTx,
29359
+ 0,
29360
+ directNodeTx.getOutput(0)
29361
+ );
29362
+ const signingJobs2 = await this.signRefundsInternal(
29363
+ directRefundTx,
29364
+ refundSighash2,
29365
+ leaf,
29366
+ directSigningCommitments[i]?.signingNonceCommitments
29367
+ );
29368
+ directLeafSigningJobs.push(...signingJobs2);
27717
29369
  }
27718
- const signingResult = await this.config.signer.signFrost({
27719
- message: sighash,
27720
- keyDerivation: leaf.keyDerivation,
27721
- publicKey: await this.config.signer.getPublicKeyFromDerivation(
27722
- leaf.keyDerivation
27723
- ),
27724
- selfCommitment: signingCommitment,
27725
- statechainCommitments: signingNonceCommitments,
27726
- adaptorPubKey: new Uint8Array(),
27727
- verifyingKey: leaf.leaf.verifyingPublicKey
27728
- });
27729
- leafSigningJobs.push({
27730
- leafId: leaf.leaf.id,
27731
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
27732
- leaf.keyDerivation
27733
- ),
27734
- rawTx: refundTx.toBytes(),
27735
- signingNonceCommitment: signingCommitment.commitment,
27736
- userSignature: signingResult,
27737
- signingCommitments: {
27738
- signingCommitments: signingNonceCommitments
29370
+ if (directFromCpfpRefundTx) {
29371
+ if (!directNodeTx) {
29372
+ throw new ValidationError(
29373
+ "Direct node transaction undefined while direct from CPFP refund transaction is defined",
29374
+ {
29375
+ field: "directNodeTx",
29376
+ value: directNodeTx,
29377
+ expected: "Non-null direct node transaction"
29378
+ }
29379
+ );
27739
29380
  }
27740
- });
29381
+ const refundSighash2 = getSigHashFromTx(
29382
+ directFromCpfpRefundTx,
29383
+ 0,
29384
+ nodeTx.getOutput(0)
29385
+ );
29386
+ const signingJobs2 = await this.signRefundsInternal(
29387
+ directFromCpfpRefundTx,
29388
+ refundSighash2,
29389
+ leaf,
29390
+ directFromCpfpSigningCommitments[i]?.signingNonceCommitments
29391
+ );
29392
+ directFromCpfpLeafSigningJobs.push(...signingJobs2);
29393
+ }
27741
29394
  }
27742
- return leafSigningJobs;
29395
+ return {
29396
+ cpfpLeafSigningJobs,
29397
+ directLeafSigningJobs,
29398
+ directFromCpfpLeafSigningJobs
29399
+ };
27743
29400
  }
27744
29401
  };
27745
29402
 
27746
29403
  // src/tests/utils/test-faucet.ts
27747
29404
  import { bytesToHex as bytesToHex12, hexToBytes as hexToBytes12 } from "@noble/curves/abstract/utils";
27748
29405
  import { schnorr as schnorr5, secp256k1 as secp256k113 } from "@noble/curves/secp256k1";
27749
- import * as btc6 from "@scure/btc-signer";
27750
- import { Address as Address6, OutScript as OutScript4, SigHash as SigHash2, Transaction as Transaction8 } from "@scure/btc-signer";
29406
+ import * as btc5 from "@scure/btc-signer";
29407
+ import { Address as Address5, OutScript as OutScript3, SigHash as SigHash2, Transaction as Transaction8 } from "@scure/btc-signer";
27751
29408
  import { taprootTweakPrivKey as taprootTweakPrivKey2 } from "@scure/btc-signer/utils";
27752
29409
  var STATIC_FAUCET_KEY = hexToBytes12(
27753
29410
  "deadbeef1337cafe4242424242424242deadbeef1337cafe4242424242424242"
@@ -27892,7 +29549,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
27892
29549
  }
27893
29550
  async sendFaucetCoinToP2WPKHAddress(pubKey) {
27894
29551
  const sendToPubKeyTx = new Transaction8();
27895
- const p2wpkhAddress = btc6.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
29552
+ const p2wpkhAddress = btc5.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
27896
29553
  if (!p2wpkhAddress) {
27897
29554
  throw new Error("Invalid P2WPKH address");
27898
29555
  }
@@ -27948,12 +29605,13 @@ var BitcoinFaucet = class _BitcoinFaucet {
27948
29605
  }
27949
29606
  async call(method, params) {
27950
29607
  try {
27951
- const response = await fetch(this.url, {
29608
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
29609
+ const response = await fetch2(this.url, {
27952
29610
  method: "POST",
27953
- headers: {
29611
+ headers: new Headers2({
27954
29612
  "Content-Type": "application/json",
27955
29613
  Authorization: "Basic " + btoa(`${this.username}:${this.password}`)
27956
- },
29614
+ }),
27957
29615
  body: JSON.stringify({
27958
29616
  jsonrpc: "1.0",
27959
29617
  id: "spark-js",
@@ -28008,10 +29666,10 @@ var BitcoinFaucet = class _BitcoinFaucet {
28008
29666
  const tx = new Transaction8();
28009
29667
  tx.addInput(coin.outpoint);
28010
29668
  const availableAmount = COIN_AMOUNT - FEE_AMOUNT;
28011
- const destinationAddress = Address6(getNetwork(4 /* LOCAL */)).decode(
29669
+ const destinationAddress = Address5(getNetwork(4 /* LOCAL */)).decode(
28012
29670
  address2
28013
29671
  );
28014
- const destinationScript = OutScript4.encode(destinationAddress);
29672
+ const destinationScript = OutScript3.encode(destinationAddress);
28015
29673
  tx.addOutput({
28016
29674
  script: destinationScript,
28017
29675
  amount
@@ -28282,31 +29940,37 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
28282
29940
  }
28283
29941
  }
28284
29942
  async getLeaves(isBalanceCheck = false) {
28285
- const leaves = await this.queryNodes({
28286
- source: {
28287
- $case: "ownerIdentityPubkey",
28288
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
28289
- },
28290
- includeParents: false,
28291
- network: NetworkToProto[this.config.getNetwork()]
28292
- });
29943
+ const operatorToLeaves = /* @__PURE__ */ new Map();
29944
+ const ownerIdentityPubkey = await this.config.signer.getIdentityPublicKey();
29945
+ let signingOperators = Object.entries(this.config.getSigningOperators());
29946
+ if (isBalanceCheck) {
29947
+ signingOperators = signingOperators.filter(
29948
+ ([id, _]) => id === this.config.getCoordinatorIdentifier()
29949
+ );
29950
+ }
29951
+ await Promise.all(
29952
+ signingOperators.map(async ([id, operator]) => {
29953
+ const leaves2 = await this.queryNodes(
29954
+ {
29955
+ source: {
29956
+ $case: "ownerIdentityPubkey",
29957
+ ownerIdentityPubkey
29958
+ },
29959
+ includeParents: false,
29960
+ network: NetworkToProto[this.config.getNetwork()]
29961
+ },
29962
+ operator.address
29963
+ );
29964
+ operatorToLeaves.set(id, leaves2);
29965
+ })
29966
+ );
29967
+ const leaves = operatorToLeaves.get(
29968
+ this.config.getCoordinatorIdentifier()
29969
+ );
28293
29970
  const leavesToIgnore = /* @__PURE__ */ new Set();
28294
29971
  if (!isBalanceCheck) {
28295
- for (const [id, operator] of Object.entries(
28296
- this.config.getSigningOperators()
28297
- )) {
29972
+ for (const [id, operatorLeaves] of operatorToLeaves) {
28298
29973
  if (id !== this.config.getCoordinatorIdentifier()) {
28299
- const operatorLeaves = await this.queryNodes(
28300
- {
28301
- source: {
28302
- $case: "ownerIdentityPubkey",
28303
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
28304
- },
28305
- includeParents: false,
28306
- network: NetworkToProto[this.config.getNetwork()]
28307
- },
28308
- operator.address
28309
- );
28310
29974
  for (const [nodeId, leaf] of Object.entries(leaves.nodes)) {
28311
29975
  const operatorLeaf = operatorLeaves.nodes[nodeId];
28312
29976
  if (!operatorLeaf) {
@@ -28323,11 +29987,24 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
28323
29987
  }
28324
29988
  }
28325
29989
  }
28326
- const verifyKey = (pubkey1, pubkey2, verifyingKey) => {
28327
- return equalBytes5(addPublicKeys(pubkey1, pubkey2), verifyingKey);
28328
- };
28329
- for (const [id, leaf] of Object.entries(leaves.nodes)) {
28330
- if (!verifyKey(
29990
+ const availableLeaves = Object.entries(leaves.nodes).filter(
29991
+ ([_, node]) => node.status === "AVAILABLE"
29992
+ );
29993
+ for (const [id, leaf] of availableLeaves) {
29994
+ if (leaf.parentNodeId && leaf.status === "AVAILABLE" && this.verifyKey(
29995
+ await this.config.signer.getPublicKeyFromDerivation({
29996
+ type: "leaf" /* LEAF */,
29997
+ path: leaf.parentNodeId
29998
+ }),
29999
+ leaf.signingKeyshare?.publicKey ?? new Uint8Array(),
30000
+ leaf.verifyingPublicKey
30001
+ )) {
30002
+ this.transferLeavesToSelf([leaf], {
30003
+ type: "leaf" /* LEAF */,
30004
+ path: leaf.parentNodeId
30005
+ });
30006
+ leavesToIgnore.add(id);
30007
+ } else if (!this.verifyKey(
28331
30008
  await this.config.signer.getPublicKeyFromDerivation({
28332
30009
  type: "leaf" /* LEAF */,
28333
30010
  path: leaf.id
@@ -28338,9 +30015,23 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
28338
30015
  leavesToIgnore.add(id);
28339
30016
  }
28340
30017
  }
28341
- return Object.entries(leaves.nodes).filter(
28342
- ([_, node]) => node.status === "AVAILABLE" && !leavesToIgnore.has(node.id)
28343
- ).map(([_, node]) => node);
30018
+ return availableLeaves.filter(([_, node]) => !leavesToIgnore.has(node.id)).map(([_, node]) => node);
30019
+ }
30020
+ async checkExtendLeaves(leaves) {
30021
+ await this.withLeaves(async () => {
30022
+ for (const leaf of leaves) {
30023
+ if (!leaf.parentNodeId && leaf.status === "AVAILABLE") {
30024
+ const res = await this.transferService.extendTimelock(leaf);
30025
+ await this.transferLeavesToSelf(res.nodes, {
30026
+ type: "leaf" /* LEAF */,
30027
+ path: leaf.id
30028
+ });
30029
+ }
30030
+ }
30031
+ });
30032
+ }
30033
+ verifyKey(pubkey1, pubkey2, verifyingKey) {
30034
+ return equalBytes5(addPublicKeys(pubkey1, pubkey2), verifyingKey);
28344
30035
  }
28345
30036
  async selectLeaves(targetAmounts) {
28346
30037
  if (targetAmounts.length === 0) {
@@ -28479,6 +30170,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
28479
30170
  leaves = await this.checkRefreshTimelockNodes(leaves);
28480
30171
  leaves = await this.checkExtendTimeLockNodes(leaves);
28481
30172
  this.leaves = leaves;
30173
+ this.checkExtendLeaves(leaves);
28482
30174
  this.optimizeLeaves().catch((e) => {
28483
30175
  console.error("Failed to optimize leaves", e);
28484
30176
  });
@@ -28719,21 +30411,68 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
28719
30411
  }
28720
30412
  }))
28721
30413
  );
28722
- const { transfer, signatureMap } = await this.transferService.startSwapSignRefund(
30414
+ const {
30415
+ transfer,
30416
+ signatureMap,
30417
+ directSignatureMap,
30418
+ directFromCpfpSignatureMap
30419
+ } = await this.transferService.startSwapSignRefund(
28723
30420
  leafKeyTweaks,
28724
30421
  hexToBytes13(this.config.getSspIdentityPublicKey()),
28725
30422
  new Date(Date.now() + 2 * 60 * 1e3)
28726
30423
  );
28727
30424
  try {
28728
30425
  if (!transfer.leaves[0]?.leaf) {
30426
+ console.error("[processSwapBatch] First leaf is missing");
28729
30427
  throw new Error("Failed to get leaf");
28730
30428
  }
28731
- const refundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
28732
- if (!refundSignature) {
28733
- throw new Error("Failed to get refund signature");
30429
+ const cpfpRefundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
30430
+ if (!cpfpRefundSignature) {
30431
+ console.error(
30432
+ "[processSwapBatch] Missing CPFP refund signature for first leaf"
30433
+ );
30434
+ throw new Error("Failed to get CPFP refund signature");
30435
+ }
30436
+ const directRefundSignature = directSignatureMap.get(
30437
+ transfer.leaves[0].leaf.id
30438
+ );
30439
+ if (!directRefundSignature) {
30440
+ console.error(
30441
+ "[processSwapBatch] Missing direct refund signature for first leaf"
30442
+ );
30443
+ throw new Error("Failed to get direct refund signature");
30444
+ }
30445
+ const directFromCpfpRefundSignature = directFromCpfpSignatureMap.get(
30446
+ transfer.leaves[0].leaf.id
30447
+ );
30448
+ if (!directFromCpfpRefundSignature) {
30449
+ console.error(
30450
+ "[processSwapBatch] Missing direct from CPFP refund signature for first leaf"
30451
+ );
30452
+ throw new Error("Failed to get direct from CPFP refund signature");
30453
+ }
30454
+ const {
30455
+ adaptorPrivateKey: cpfpAdaptorPrivateKey,
30456
+ adaptorSignature: cpfpAdaptorSignature
30457
+ } = generateAdaptorFromSignature(cpfpRefundSignature);
30458
+ let directAdaptorPrivateKey = new Uint8Array();
30459
+ let directAdaptorSignature = new Uint8Array();
30460
+ let directFromCpfpAdaptorPrivateKey = new Uint8Array();
30461
+ let directFromCpfpAdaptorSignature = new Uint8Array();
30462
+ if (directRefundSignature.length > 0) {
30463
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directRefundSignature);
30464
+ directAdaptorPrivateKey = adaptorPrivateKey;
30465
+ directAdaptorSignature = adaptorSignature;
30466
+ }
30467
+ if (directFromCpfpRefundSignature.length > 0) {
30468
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directFromCpfpRefundSignature);
30469
+ directFromCpfpAdaptorPrivateKey = adaptorPrivateKey;
30470
+ directFromCpfpAdaptorSignature = adaptorSignature;
28734
30471
  }
28735
- const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(refundSignature);
28736
30472
  if (!transfer.leaves[0].leaf) {
30473
+ console.error(
30474
+ "[processSwapBatch] First leaf missing when preparing user leaves"
30475
+ );
28737
30476
  throw new Error("Failed to get leaf");
28738
30477
  }
28739
30478
  const userLeaves = [];
@@ -28742,37 +30481,113 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
28742
30481
  raw_unsigned_refund_transaction: bytesToHex13(
28743
30482
  transfer.leaves[0].intermediateRefundTx
28744
30483
  ),
28745
- adaptor_added_signature: bytesToHex13(adaptorSignature)
30484
+ direct_raw_unsigned_refund_transaction: bytesToHex13(
30485
+ transfer.leaves[0].intermediateDirectRefundTx
30486
+ ),
30487
+ direct_from_cpfp_raw_unsigned_refund_transaction: bytesToHex13(
30488
+ transfer.leaves[0].intermediateDirectFromCpfpRefundTx
30489
+ ),
30490
+ adaptor_added_signature: bytesToHex13(cpfpAdaptorSignature),
30491
+ direct_adaptor_added_signature: bytesToHex13(directAdaptorSignature),
30492
+ direct_from_cpfp_adaptor_added_signature: bytesToHex13(
30493
+ directFromCpfpAdaptorSignature
30494
+ )
28746
30495
  });
28747
30496
  for (let i = 1; i < transfer.leaves.length; i++) {
28748
30497
  const leaf = transfer.leaves[i];
28749
30498
  if (!leaf?.leaf) {
30499
+ console.error(`[processSwapBatch] Leaf ${i + 1} is missing`);
28750
30500
  throw new Error("Failed to get leaf");
28751
30501
  }
28752
- const refundSignature2 = signatureMap.get(leaf.leaf.id);
28753
- if (!refundSignature2) {
28754
- throw new Error("Failed to get refund signature");
30502
+ const cpfpRefundSignature2 = signatureMap.get(leaf.leaf.id);
30503
+ if (!cpfpRefundSignature2) {
30504
+ console.error(
30505
+ `[processSwapBatch] Missing CPFP refund signature for leaf ${i + 1}`
30506
+ );
30507
+ throw new Error("Failed to get CPFP refund signature");
30508
+ }
30509
+ const directRefundSignature2 = directSignatureMap.get(leaf.leaf.id);
30510
+ if (!directRefundSignature2) {
30511
+ console.error(
30512
+ `[processSwapBatch] Missing direct refund signature for leaf ${i + 1}`
30513
+ );
30514
+ throw new Error("Failed to get direct refund signature");
30515
+ }
30516
+ const directFromCpfpRefundSignature2 = directFromCpfpSignatureMap.get(
30517
+ leaf.leaf.id
30518
+ );
30519
+ if (!directFromCpfpRefundSignature2) {
30520
+ console.error(
30521
+ `[processSwapBatch] Missing direct from CPFP refund signature for leaf ${i + 1}`
30522
+ );
30523
+ throw new Error("Failed to get direct from CPFP refund signature");
28755
30524
  }
28756
- const signature = generateSignatureFromExistingAdaptor(
28757
- refundSignature2,
28758
- adaptorPrivateKey
30525
+ const cpfpSignature = generateSignatureFromExistingAdaptor(
30526
+ cpfpRefundSignature2,
30527
+ cpfpAdaptorPrivateKey
28759
30528
  );
30529
+ let directSignature = new Uint8Array();
30530
+ if (directRefundSignature2.length > 0) {
30531
+ directSignature = generateSignatureFromExistingAdaptor(
30532
+ directRefundSignature2,
30533
+ directAdaptorPrivateKey
30534
+ );
30535
+ }
30536
+ let directFromCpfpSignature = new Uint8Array();
30537
+ if (directFromCpfpRefundSignature2.length > 0) {
30538
+ directFromCpfpSignature = generateSignatureFromExistingAdaptor(
30539
+ directFromCpfpRefundSignature2,
30540
+ directFromCpfpAdaptorPrivateKey
30541
+ );
30542
+ }
28760
30543
  userLeaves.push({
28761
30544
  leaf_id: leaf.leaf.id,
28762
30545
  raw_unsigned_refund_transaction: bytesToHex13(
28763
30546
  leaf.intermediateRefundTx
28764
30547
  ),
28765
- adaptor_added_signature: bytesToHex13(signature)
30548
+ direct_raw_unsigned_refund_transaction: bytesToHex13(
30549
+ leaf.intermediateDirectRefundTx
30550
+ ),
30551
+ direct_from_cpfp_raw_unsigned_refund_transaction: bytesToHex13(
30552
+ leaf.intermediateDirectFromCpfpRefundTx
30553
+ ),
30554
+ adaptor_added_signature: bytesToHex13(cpfpSignature),
30555
+ direct_adaptor_added_signature: bytesToHex13(directSignature),
30556
+ direct_from_cpfp_adaptor_added_signature: bytesToHex13(
30557
+ directFromCpfpSignature
30558
+ )
28766
30559
  });
28767
30560
  }
28768
30561
  const sspClient = this.getSspClient();
28769
- const adaptorPubkey = bytesToHex13(
28770
- secp256k114.getPublicKey(adaptorPrivateKey)
30562
+ const cpfpAdaptorPubkey = bytesToHex13(
30563
+ secp256k114.getPublicKey(cpfpAdaptorPrivateKey)
28771
30564
  );
30565
+ if (!cpfpAdaptorPubkey) {
30566
+ throw new Error("Failed to generate CPFP adaptor pubkey");
30567
+ }
30568
+ let directAdaptorPubkey;
30569
+ if (directAdaptorPrivateKey.length > 0) {
30570
+ directAdaptorPubkey = bytesToHex13(
30571
+ secp256k114.getPublicKey(directAdaptorPrivateKey)
30572
+ );
30573
+ }
30574
+ let directFromCpfpAdaptorPubkey;
30575
+ if (directFromCpfpAdaptorPrivateKey.length > 0) {
30576
+ directFromCpfpAdaptorPubkey = bytesToHex13(
30577
+ secp256k114.getPublicKey(directFromCpfpAdaptorPrivateKey)
30578
+ );
30579
+ }
28772
30580
  let request = null;
30581
+ const targetAmountSats = targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0);
30582
+ const totalAmountSats = leavesBatch.reduce(
30583
+ (acc, leaf) => acc + leaf.value,
30584
+ 0
30585
+ );
28773
30586
  request = await sspClient.requestLeaveSwap({
28774
30587
  userLeaves,
28775
- adaptorPubkey,
30588
+ adaptorPubkey: cpfpAdaptorPubkey,
30589
+ directAdaptorPubkey,
30590
+ directFromCpfpAdaptorPubkey,
28776
30591
  targetAmountSats: targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
28777
30592
  totalAmountSats: leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
28778
30593
  targetAmountSatsList: targetAmounts,
@@ -28781,6 +30596,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
28781
30596
  idempotencyKey: uuidv75()
28782
30597
  });
28783
30598
  if (!request) {
30599
+ console.error("[processSwapBatch] Leave swap request returned null");
28784
30600
  throw new Error("Failed to request leaves swap. No response returned.");
28785
30601
  }
28786
30602
  const nodes = await this.queryNodes({
@@ -28794,50 +30610,140 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
28794
30610
  network: NetworkToProto[this.config.getNetwork()]
28795
30611
  });
28796
30612
  if (Object.values(nodes.nodes).length !== request.swapLeaves.length) {
30613
+ console.error("[processSwapBatch] Node count mismatch:", {
30614
+ actual: Object.values(nodes.nodes).length,
30615
+ expected: request.swapLeaves.length
30616
+ });
28797
30617
  throw new Error("Expected same number of nodes as swapLeaves");
28798
30618
  }
28799
30619
  for (const [nodeId, node] of Object.entries(nodes.nodes)) {
28800
30620
  if (!node.nodeTx) {
30621
+ console.error(`[processSwapBatch] Node tx missing for ${nodeId}`);
28801
30622
  throw new Error(`Node tx not found for leaf ${nodeId}`);
28802
30623
  }
28803
30624
  if (!node.verifyingPublicKey) {
30625
+ console.error(
30626
+ `[processSwapBatch] Verifying public key missing for ${nodeId}`
30627
+ );
28804
30628
  throw new Error(`Node public key not found for leaf ${nodeId}`);
28805
30629
  }
28806
30630
  const leaf = request.swapLeaves.find((leaf2) => leaf2.leafId === nodeId);
28807
30631
  if (!leaf) {
30632
+ console.error(`[processSwapBatch] Leaf not found for node ${nodeId}`);
28808
30633
  throw new Error(`Leaf not found for node ${nodeId}`);
28809
30634
  }
28810
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
28811
- const refundTxBytes = hexToBytes13(leaf.rawUnsignedRefundTransaction);
28812
- const refundTx = getTxFromRawTxBytes(refundTxBytes);
28813
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
30635
+ const cpfpNodeTx = getTxFromRawTxBytes(node.nodeTx);
30636
+ const cpfpRefundTxBytes = hexToBytes13(leaf.rawUnsignedRefundTransaction);
30637
+ const cpfpRefundTx = getTxFromRawTxBytes(cpfpRefundTxBytes);
30638
+ const cpfpSighash = getSigHashFromTx(
30639
+ cpfpRefundTx,
30640
+ 0,
30641
+ cpfpNodeTx.getOutput(0)
30642
+ );
28814
30643
  const nodePublicKey = node.verifyingPublicKey;
28815
30644
  const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
28816
- const adaptorSignatureBytes = hexToBytes13(leaf.adaptorSignedSignature);
30645
+ const cpfpAdaptorSignatureBytes = hexToBytes13(
30646
+ leaf.adaptorSignedSignature
30647
+ );
30648
+ applyAdaptorToSignature(
30649
+ taprootKey.slice(1),
30650
+ cpfpSighash,
30651
+ cpfpAdaptorSignatureBytes,
30652
+ cpfpAdaptorPrivateKey
30653
+ );
30654
+ if (!leaf.directRawUnsignedRefundTransaction) {
30655
+ throw new Error(
30656
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
30657
+ );
30658
+ }
30659
+ if (!leaf.directAdaptorSignedSignature) {
30660
+ throw new Error(
30661
+ `Direct adaptor signed signature missing for node ${nodeId}`
30662
+ );
30663
+ }
30664
+ const directNodeTx = getTxFromRawTxBytes(node.directTx);
30665
+ const directRefundTxBytes = hexToBytes13(
30666
+ leaf.directRawUnsignedRefundTransaction
30667
+ );
30668
+ const directRefundTx = getTxFromRawTxBytes(directRefundTxBytes);
30669
+ const directSighash = getSigHashFromTx(
30670
+ directRefundTx,
30671
+ 0,
30672
+ directNodeTx.getOutput(0)
30673
+ );
30674
+ if (!leaf.directFromCpfpAdaptorSignedSignature) {
30675
+ throw new Error(
30676
+ `Direct adaptor signed signature missing for node ${nodeId}`
30677
+ );
30678
+ }
30679
+ const directAdaptorSignatureBytes = hexToBytes13(
30680
+ leaf.directAdaptorSignedSignature
30681
+ );
30682
+ applyAdaptorToSignature(
30683
+ taprootKey.slice(1),
30684
+ directSighash,
30685
+ directAdaptorSignatureBytes,
30686
+ directAdaptorPrivateKey
30687
+ );
30688
+ if (!leaf.directRawUnsignedRefundTransaction) {
30689
+ throw new Error(
30690
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
30691
+ );
30692
+ }
30693
+ if (!leaf.directFromCpfpRawUnsignedRefundTransaction) {
30694
+ throw new Error(
30695
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
30696
+ );
30697
+ }
30698
+ const directFromCpfpRefundTxBytes = hexToBytes13(
30699
+ leaf.directFromCpfpRawUnsignedRefundTransaction
30700
+ );
30701
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
30702
+ directFromCpfpRefundTxBytes
30703
+ );
30704
+ const directFromCpfpSighash = getSigHashFromTx(
30705
+ directFromCpfpRefundTx,
30706
+ 0,
30707
+ cpfpNodeTx.getOutput(0)
30708
+ );
30709
+ const directFromCpfpAdaptorSignatureBytes = hexToBytes13(
30710
+ leaf.directFromCpfpAdaptorSignedSignature
30711
+ );
28817
30712
  applyAdaptorToSignature(
28818
30713
  taprootKey.slice(1),
28819
- sighash,
28820
- adaptorSignatureBytes,
28821
- adaptorPrivateKey
30714
+ directFromCpfpSighash,
30715
+ directFromCpfpAdaptorSignatureBytes,
30716
+ directFromCpfpAdaptorPrivateKey
28822
30717
  );
28823
30718
  }
28824
30719
  await this.transferService.deliverTransferPackage(
28825
30720
  transfer,
28826
30721
  leafKeyTweaks,
28827
- signatureMap
30722
+ signatureMap,
30723
+ directSignatureMap,
30724
+ directFromCpfpSignatureMap
28828
30725
  );
28829
30726
  const completeResponse = await sspClient.completeLeaveSwap({
28830
- adaptorSecretKey: bytesToHex13(adaptorPrivateKey),
30727
+ adaptorSecretKey: bytesToHex13(cpfpAdaptorPrivateKey),
30728
+ directAdaptorSecretKey: bytesToHex13(directAdaptorPrivateKey),
30729
+ directFromCpfpAdaptorSecretKey: bytesToHex13(
30730
+ directFromCpfpAdaptorPrivateKey
30731
+ ),
28831
30732
  userOutboundTransferExternalId: transfer.id,
28832
30733
  leavesSwapRequestId: request.id
28833
30734
  });
28834
30735
  if (!completeResponse || !completeResponse.inboundTransfer?.sparkId) {
30736
+ console.error(
30737
+ "[processSwapBatch] Invalid complete response:",
30738
+ completeResponse
30739
+ );
28835
30740
  throw new Error("Failed to complete leaves swap");
28836
30741
  }
28837
30742
  const incomingTransfer = await this.transferService.queryTransfer(
28838
30743
  completeResponse.inboundTransfer.sparkId
28839
30744
  );
28840
30745
  if (!incomingTransfer) {
30746
+ console.error("[processSwapBatch] No incoming transfer found");
28841
30747
  throw new Error("Failed to get incoming transfer");
28842
30748
  }
28843
30749
  return await this.claimTransfer({
@@ -28847,6 +30753,11 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
28847
30753
  optimize: false
28848
30754
  });
28849
30755
  } catch (e) {
30756
+ console.error("[processSwapBatch] Error details:", {
30757
+ error: e,
30758
+ message: e.message,
30759
+ stack: e.stack
30760
+ });
28850
30761
  await this.cancelAllSenderInitiatedTransfers();
28851
30762
  throw new Error(`Failed to request leaves swap: ${e}`);
28852
30763
  }
@@ -29161,10 +31072,10 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29161
31072
  index: outputIndex,
29162
31073
  witnessScript: new Uint8Array()
29163
31074
  });
29164
- const addressDecoded = Address7(getNetwork(network)).decode(
31075
+ const addressDecoded = Address6(getNetwork(network)).decode(
29165
31076
  destinationAddress
29166
31077
  );
29167
- const outputScript = OutScript5.encode(addressDecoded);
31078
+ const outputScript = OutScript4.encode(addressDecoded);
29168
31079
  tx.addOutput({
29169
31080
  script: outputScript,
29170
31081
  amount: BigInt(creditAmountSats)
@@ -29297,8 +31208,8 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29297
31208
  if (!output) {
29298
31209
  continue;
29299
31210
  }
29300
- const parsedScript = OutScript5.decode(output.script);
29301
- const address2 = Address7(getNetwork(this.config.getNetwork())).encode(
31211
+ const parsedScript = OutScript4.decode(output.script);
31212
+ const address2 = Address6(getNetwork(this.config.getNetwork())).encode(
29302
31213
  parsedScript
29303
31214
  );
29304
31215
  if (staticDepositAddresses.has(address2)) {
@@ -29317,8 +31228,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29317
31228
  field: "txid"
29318
31229
  });
29319
31230
  }
31231
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
29320
31232
  const baseUrl = this.config.getElectrsUrl();
29321
- const headers = {};
31233
+ const headers = new Headers2();
29322
31234
  let txHex;
29323
31235
  if (this.config.getNetwork() === 4 /* LOCAL */) {
29324
31236
  const localFaucet = BitcoinFaucet.getInstance();
@@ -29329,9 +31241,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29329
31241
  const auth = btoa(
29330
31242
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
29331
31243
  );
29332
- headers["Authorization"] = `Basic ${auth}`;
31244
+ headers.set("Authorization", `Basic ${auth}`);
29333
31245
  }
29334
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
31246
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
29335
31247
  headers
29336
31248
  });
29337
31249
  txHex = await response.text();
@@ -29459,8 +31371,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29459
31371
  this.mutexes.set(txid, mutex);
29460
31372
  }
29461
31373
  const nodes = await mutex.runExclusive(async () => {
31374
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
29462
31375
  const baseUrl = this.config.getElectrsUrl();
29463
- const headers = {};
31376
+ const headers = new Headers2();
29464
31377
  let txHex;
29465
31378
  if (this.config.getNetwork() === 4 /* LOCAL */) {
29466
31379
  const localFaucet = BitcoinFaucet.getInstance();
@@ -29471,9 +31384,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29471
31384
  const auth = btoa(
29472
31385
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
29473
31386
  );
29474
- headers["Authorization"] = `Basic ${auth}`;
31387
+ headers.set("Authorization", `Basic ${auth}`);
29475
31388
  }
29476
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
31389
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
29477
31390
  headers
29478
31391
  });
29479
31392
  txHex = await response.text();
@@ -29501,8 +31414,8 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29501
31414
  if (!output) {
29502
31415
  continue;
29503
31416
  }
29504
- const parsedScript = OutScript5.decode(output.script);
29505
- const address2 = Address7(getNetwork(this.config.getNetwork())).encode(
31417
+ const parsedScript = OutScript4.decode(output.script);
31418
+ const address2 = Address6(getNetwork(this.config.getNetwork())).encode(
29506
31419
  parsedScript
29507
31420
  );
29508
31421
  if (unusedDepositAddresses.has(address2)) {
@@ -29562,8 +31475,8 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29562
31475
  if (!output) {
29563
31476
  continue;
29564
31477
  }
29565
- const parsedScript = OutScript5.decode(output.script);
29566
- const address2 = Address7(getNetwork(this.config.getNetwork())).encode(
31478
+ const parsedScript = OutScript4.decode(output.script);
31479
+ const address2 = Address6(getNetwork(this.config.getNetwork())).encode(
29567
31480
  parsedScript
29568
31481
  );
29569
31482
  const unusedDepositAddress = unusedDepositAddresses.get(address2);
@@ -29715,10 +31628,16 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29715
31628
  const validNodes = [];
29716
31629
  for (const node of nodes) {
29717
31630
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
29718
- const { needRefresh } = getNextTransactionSequence(
29719
- nodeTx.getInput(0).sequence
29720
- );
29721
- if (needRefresh) {
31631
+ const sequence = nodeTx.getInput(0).sequence;
31632
+ if (!sequence) {
31633
+ throw new ValidationError("Invalid node transaction", {
31634
+ field: "sequence",
31635
+ value: nodeTx.getInput(0),
31636
+ expected: "Non-null sequence"
31637
+ });
31638
+ }
31639
+ const needsRefresh = doesLeafNeedRefresh(sequence, true);
31640
+ if (needsRefresh) {
29722
31641
  nodesToExtend.push(node);
29723
31642
  nodeIds.push(node.id);
29724
31643
  } else {
@@ -29755,11 +31674,16 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29755
31674
  const validNodes = [];
29756
31675
  for (const node of nodes) {
29757
31676
  const refundTx = getTxFromRawTxBytes(node.refundTx);
29758
- const { needRefresh } = getNextTransactionSequence(
29759
- refundTx.getInput(0).sequence,
29760
- true
29761
- );
29762
- if (needRefresh) {
31677
+ const sequence = refundTx.getInput(0).sequence;
31678
+ if (!sequence) {
31679
+ throw new ValidationError("Invalid refund transaction", {
31680
+ field: "sequence",
31681
+ value: refundTx.getInput(0),
31682
+ expected: "Non-null sequence"
31683
+ });
31684
+ }
31685
+ const needsRefresh = doesLeafNeedRefresh(sequence);
31686
+ if (needsRefresh) {
29763
31687
  nodesToRefresh.push(node);
29764
31688
  nodeIds.push(node.id);
29765
31689
  } else {
@@ -29793,7 +31717,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29793
31717
  throw new Error(`parent node ${node.parentNodeId} not found`);
29794
31718
  }
29795
31719
  const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
29796
- [node],
31720
+ node,
29797
31721
  parentNode
29798
31722
  );
29799
31723
  if (nodes2.length !== 1) {
@@ -29842,7 +31766,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29842
31766
  leavesToClaim.push({
29843
31767
  leaf: {
29844
31768
  ...leaf.leaf,
29845
- refundTx: leaf.intermediateRefundTx
31769
+ refundTx: leaf.intermediateRefundTx,
31770
+ directRefundTx: leaf.intermediateDirectRefundTx,
31771
+ directFromCpfpRefundTx: leaf.intermediateDirectFromCpfpRefundTx
29846
31772
  },
29847
31773
  keyDerivation: {
29848
31774
  type: "ecies" /* ECIES */,
@@ -29918,7 +31844,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
29918
31844
  if (type && transfer.type !== type) {
29919
31845
  continue;
29920
31846
  }
29921
- if (transfer.status !== 2 /* TRANSFER_STATUS_SENDER_KEY_TWEAKED */ && transfer.status !== 3 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAKED */ && transfer.status !== 4 /* TRANSFER_STATUS_RECEIVER_REFUND_SIGNED */ && transfer.status !== 10 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_APPLIED */) {
31847
+ if (transfer.status !== 2 /* TRANSFER_STATUS_SENDER_KEY_TWEAKED */ && transfer.status !== 3 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAKED */ && transfer.status !== 4 /* TRANSFER_STATUS_RECEIVER_REFUND_SIGNED */ && transfer.status !== 10 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_APPLIED */ && transfer.status !== 9 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_LOCKED */) {
29922
31848
  continue;
29923
31849
  }
29924
31850
  promises.push(
@@ -30206,6 +32132,8 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
30206
32132
  await this.transferService.deliverTransferPackage(
30207
32133
  swapResponse.transfer,
30208
32134
  leavesToSend,
32135
+ /* @__PURE__ */ new Map(),
32136
+ /* @__PURE__ */ new Map(),
30209
32137
  /* @__PURE__ */ new Map()
30210
32138
  );
30211
32139
  const sspResponse = await sspClient.requestLightningSend({
@@ -30639,14 +32567,18 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
30639
32567
  issuerPublicKeys,
30640
32568
  tokenTransactionHashes,
30641
32569
  tokenIdentifiers,
30642
- outputIds
32570
+ outputIds,
32571
+ pageSize = 100,
32572
+ offset = 0
30643
32573
  }) {
30644
32574
  return this.tokenTransactionService.queryTokenTransactions({
30645
32575
  ownerPublicKeys,
30646
32576
  issuerPublicKeys,
30647
32577
  tokenTransactionHashes,
30648
32578
  tokenIdentifiers,
30649
- outputIds
32579
+ outputIds,
32580
+ pageSize,
32581
+ offset
30650
32582
  });
30651
32583
  }
30652
32584
  async getTokenL1Address() {
@@ -30964,87 +32896,64 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
30964
32896
  },
30965
32897
  includeParents: true
30966
32898
  });
30967
- const node = response.nodes[nodeId];
30968
- if (!node) {
32899
+ let leaf = response.nodes[nodeId];
32900
+ if (!leaf) {
30969
32901
  throw new ValidationError("Node not found", {
30970
32902
  field: "nodeId",
30971
32903
  value: nodeId
30972
32904
  });
30973
32905
  }
30974
- if (!node.parentNodeId) {
30975
- throw new ValidationError("Node has no parent", {
30976
- field: "parentNodeId",
30977
- value: node.parentNodeId
30978
- });
30979
- }
30980
- const parentNode = response.nodes[node.parentNodeId];
30981
- if (!parentNode) {
30982
- throw new ValidationError("Parent node not found", {
30983
- field: "parentNodeId",
30984
- value: node.parentNodeId
30985
- });
30986
- }
30987
- const result = await this.transferService.refreshTimelockNodes(
30988
- [node],
30989
- parentNode
30990
- );
30991
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
30992
- if (leafIndex !== -1 && result.nodes.length > 0) {
30993
- const newNode = result.nodes[0];
30994
- if (newNode) {
30995
- this.leaves[leafIndex] = newNode;
32906
+ let parentNode;
32907
+ let hasParentNode = false;
32908
+ if (!leaf.parentNodeId) {
32909
+ } else {
32910
+ hasParentNode = true;
32911
+ parentNode = response.nodes[leaf.parentNodeId];
32912
+ if (!parentNode) {
32913
+ throw new ValidationError("Parent node not found", {
32914
+ field: "parentNodeId",
32915
+ value: leaf.parentNodeId
32916
+ });
30996
32917
  }
30997
32918
  }
30998
- } catch (error) {
30999
- throw new NetworkError(
31000
- "Failed to refresh timelock",
31001
- {
31002
- method: "refresh_timelock"
31003
- },
31004
- error
31005
- );
31006
- }
31007
- }
31008
- /**
31009
- * Refresh the timelock of a specific node's refund transaction only.
31010
- *
31011
- * @param {string} nodeId - The ID of the node whose refund transaction to refresh
31012
- * @returns {Promise<void>} Promise that resolves when the refund timelock is refreshed
31013
- */
31014
- async testOnly_expireTimelockRefundTx(nodeId) {
31015
- const sparkClient = await this.connectionManager.createSparkClient(
31016
- this.config.getCoordinatorAddress()
31017
- );
31018
- try {
31019
- const response = await sparkClient.query_nodes({
31020
- source: {
31021
- $case: "nodeIds",
31022
- nodeIds: {
31023
- nodeIds: [nodeId]
32919
+ const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
32920
+ const refundTx = getTxFromRawTxBytes(leaf.refundTx);
32921
+ if (hasParentNode) {
32922
+ const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
32923
+ if (nodeTimelock > 100) {
32924
+ const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
32925
+ leaf,
32926
+ parentNode
32927
+ );
32928
+ if (!expiredNodeTxLeaf.nodes[0]) {
32929
+ throw new ValidationError("No expired node tx leaf", {
32930
+ field: "expiredNodeTxLeaf",
32931
+ value: expiredNodeTxLeaf
32932
+ });
31024
32933
  }
31025
- },
31026
- includeParents: false
31027
- });
31028
- const node = response.nodes[nodeId];
31029
- if (!node) {
31030
- throw new ValidationError("Node not found", {
31031
- field: "nodeId",
31032
- value: nodeId
31033
- });
32934
+ leaf = expiredNodeTxLeaf.nodes[0];
32935
+ }
31034
32936
  }
31035
- const result = await this.transferService.refreshTimelockRefundTx(node);
31036
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
31037
- if (leafIndex !== -1 && result.nodes.length > 0) {
31038
- const newNode = result.nodes[0];
31039
- if (newNode) {
31040
- this.leaves[leafIndex] = newNode;
32937
+ const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
32938
+ if (refundTimelock > 100) {
32939
+ const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
32940
+ if (!expiredTxLeaf.nodes[0]) {
32941
+ throw new ValidationError("No expired tx leaf", {
32942
+ field: "expiredTxLeaf",
32943
+ value: expiredTxLeaf
32944
+ });
31041
32945
  }
32946
+ leaf = expiredTxLeaf.nodes[0];
32947
+ }
32948
+ const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
32949
+ if (leafIndex !== -1) {
32950
+ this.leaves[leafIndex] = leaf;
31042
32951
  }
31043
32952
  } catch (error) {
31044
32953
  throw new NetworkError(
31045
- "Failed to refresh refund timelock",
32954
+ "Failed to refresh timelock",
31046
32955
  {
31047
- method: "refresh_timelock_refund_tx"
32956
+ method: "refresh_timelock"
31048
32957
  },
31049
32958
  error
31050
32959
  );
@@ -31110,7 +33019,10 @@ export {
31110
33019
  AuthenticationError,
31111
33020
  ConfigurationError,
31112
33021
  DEFAULT_FEE_SATS,
33022
+ DIRECT_TIMELOCK_OFFSET,
31113
33023
  ReactNativeSparkSigner as DefaultSparkSigner,
33024
+ INITIAL_DIRECT_SEQUENCE,
33025
+ INITIAL_SEQUENCE,
31114
33026
  InternalValidationError,
31115
33027
  LRC_WALLET_NETWORK,
31116
33028
  LRC_WALLET_NETWORK_TYPE,
@@ -31122,6 +33034,8 @@ export {
31122
33034
  ReactNativeSparkSigner,
31123
33035
  SparkSDKError,
31124
33036
  SparkWallet,
33037
+ TEST_UNILATERAL_DIRECT_SEQUENCE,
33038
+ TEST_UNILATERAL_SEQUENCE,
31125
33039
  TokenTransactionService,
31126
33040
  ValidationError,
31127
33041
  WalletConfig,
@@ -31138,13 +33052,21 @@ export {
31138
33052
  constructFeeBumpTx,
31139
33053
  constructUnilateralExitFeeBumpPackages,
31140
33054
  constructUnilateralExitTxs,
33055
+ createConnectorRefundTransactions,
33056
+ createLeafNodeTx,
33057
+ createNodeTx,
33058
+ createNodeTxs,
31141
33059
  createRefundTx,
33060
+ createRefundTxs,
33061
+ createRootTx,
31142
33062
  createSigningCommitment,
31143
33063
  createSigningNonce,
33064
+ createSplitTx,
31144
33065
  decodeBech32mTokenIdentifier,
31145
33066
  decodeBytesToSigningCommitment,
31146
33067
  decodeBytesToSigningNonce,
31147
33068
  decodeSparkAddress,
33069
+ doesLeafNeedRefresh,
31148
33070
  encodeBech32mTokenIdentifier,
31149
33071
  encodeSigningCommitmentToBytes,
31150
33072
  encodeSigningNonceToBytes,