@buildonspark/spark-sdk 0.2.3 → 0.2.5

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 (91) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/{chunk-3SEOTO43.js → chunk-3SPMJMUX.js} +3 -2
  3. package/dist/{chunk-CDLETEDT.js → chunk-CQY5ML2A.js} +16 -3
  4. package/dist/{chunk-PTRXJS7Q.js → chunk-LQZL2D3Y.js} +1 -1
  5. package/dist/{chunk-PLLJIZC3.js → chunk-U7LRIWTF.js} +2471 -822
  6. package/dist/{client-CcYzmpmj.d.cts → client-C88GCTPB.d.cts} +211 -104
  7. package/dist/{client-CGTRS23n.d.ts → client-Dg6vS_2I.d.ts} +211 -104
  8. package/dist/debug.cjs +2511 -831
  9. package/dist/debug.d.cts +19 -6
  10. package/dist/debug.d.ts +19 -6
  11. package/dist/debug.js +3 -3
  12. package/dist/graphql/objects/index.cjs +13 -1
  13. package/dist/graphql/objects/index.d.cts +6 -51
  14. package/dist/graphql/objects/index.d.ts +6 -51
  15. package/dist/graphql/objects/index.js +1 -1
  16. package/dist/index.cjs +2491 -797
  17. package/dist/index.d.cts +189 -9
  18. package/dist/index.d.ts +189 -9
  19. package/dist/index.js +32 -4
  20. package/dist/index.node.cjs +2596 -799
  21. package/dist/index.node.d.cts +9 -190
  22. package/dist/index.node.d.ts +9 -190
  23. package/dist/index.node.js +134 -4
  24. package/dist/native/index.cjs +2491 -797
  25. package/dist/native/index.d.cts +309 -174
  26. package/dist/native/index.d.ts +309 -174
  27. package/dist/native/index.js +2495 -814
  28. package/dist/proto/lrc20.d.cts +1 -1
  29. package/dist/proto/lrc20.d.ts +1 -1
  30. package/dist/proto/spark.d.cts +1 -1
  31. package/dist/proto/spark.d.ts +1 -1
  32. package/dist/proto/spark_token.d.cts +1 -1
  33. package/dist/proto/spark_token.d.ts +1 -1
  34. package/dist/{spark-B_7nZx6T.d.cts → spark-ESAfZARg.d.cts} +1 -1
  35. package/dist/{spark-B_7nZx6T.d.ts → spark-ESAfZARg.d.ts} +1 -1
  36. package/dist/{spark-wallet-CxcGPXRB.d.ts → spark-wallet-B2WwKN8W.d.ts} +57 -35
  37. package/dist/{spark-wallet-DJJm19BP.d.cts → spark-wallet-Di65w0Us.d.cts} +57 -35
  38. package/dist/spark-wallet.node-7R0Rxyj9.d.cts +13 -0
  39. package/dist/spark-wallet.node-CSPWOWRu.d.ts +13 -0
  40. package/dist/tests/test-utils.cjs +578 -77
  41. package/dist/tests/test-utils.d.cts +12 -13
  42. package/dist/tests/test-utils.d.ts +12 -13
  43. package/dist/tests/test-utils.js +54 -17
  44. package/dist/types/index.cjs +16 -3
  45. package/dist/types/index.d.cts +3 -4
  46. package/dist/types/index.d.ts +3 -4
  47. package/dist/types/index.js +2 -2
  48. package/dist/{xchain-address-Bh9w1SeC.d.ts → xchain-address-BsveIy5l.d.ts} +56 -8
  49. package/dist/{xchain-address-SZ7dkVUE.d.cts → xchain-address-CqRu3F21.d.cts} +56 -8
  50. package/package.json +1 -1
  51. package/src/graphql/client.ts +57 -8
  52. package/src/graphql/mutations/CompleteLeavesSwap.ts +9 -1
  53. package/src/graphql/mutations/RequestSwapLeaves.ts +4 -0
  54. package/src/graphql/objects/CompleteLeavesSwapInput.ts +34 -34
  55. package/src/graphql/objects/LeavesSwapRequest.ts +4 -0
  56. package/src/graphql/objects/RequestLeavesSwapInput.ts +48 -47
  57. package/src/graphql/objects/SparkWalletUser.ts +1 -1
  58. package/src/graphql/objects/SwapLeaf.ts +40 -32
  59. package/src/graphql/objects/UserLeafInput.ts +24 -0
  60. package/src/graphql/objects/UserRequest.ts +4 -0
  61. package/src/graphql/queries/Transfers.ts +15 -0
  62. package/src/index.node.ts +1 -1
  63. package/src/native/index.ts +4 -5
  64. package/src/services/coop-exit.ts +171 -36
  65. package/src/services/deposit.ts +471 -74
  66. package/src/services/lightning.ts +18 -5
  67. package/src/services/signing.ts +162 -50
  68. package/src/services/transfer.ts +950 -384
  69. package/src/services/tree-creation.ts +342 -121
  70. package/src/spark-wallet/spark-wallet.node.ts +71 -66
  71. package/src/spark-wallet/spark-wallet.ts +561 -192
  72. package/src/tests/integration/coop-exit.test.ts +3 -8
  73. package/src/tests/integration/deposit.test.ts +3 -3
  74. package/src/tests/integration/lightning.test.ts +521 -466
  75. package/src/tests/integration/ssp/static_deposit.test.ts +83 -1
  76. package/src/tests/integration/ssp/transfers.test.ts +97 -0
  77. package/src/tests/integration/swap.test.ts +559 -307
  78. package/src/tests/integration/transfer.test.ts +625 -623
  79. package/src/tests/integration/wallet.test.ts +2 -2
  80. package/src/tests/integration/watchtower.test.ts +211 -0
  81. package/src/tests/test-utils.ts +63 -14
  82. package/src/tests/utils/test-faucet.ts +4 -2
  83. package/src/types/sdk-types.ts +15 -0
  84. package/src/utils/adaptor-signature.ts +15 -5
  85. package/src/utils/bitcoin.ts +13 -0
  86. package/src/utils/fetch.ts +75 -0
  87. package/src/utils/mempool.ts +9 -4
  88. package/src/utils/transaction.ts +388 -26
  89. package/dist/sdk-types-CB9HrW5O.d.cts +0 -44
  90. package/dist/sdk-types-CkRNraXT.d.ts +0 -44
  91. package/src/graphql/queries/Transfer.ts +0 -10
package/dist/index.cjs CHANGED
@@ -1290,7 +1290,10 @@ __export(index_exports, {
1290
1290
  AuthenticationError: () => AuthenticationError,
1291
1291
  ConfigurationError: () => ConfigurationError,
1292
1292
  DEFAULT_FEE_SATS: () => DEFAULT_FEE_SATS,
1293
+ DIRECT_TIMELOCK_OFFSET: () => DIRECT_TIMELOCK_OFFSET,
1293
1294
  DefaultSparkSigner: () => DefaultSparkSigner,
1295
+ INITIAL_DIRECT_SEQUENCE: () => INITIAL_DIRECT_SEQUENCE,
1296
+ INITIAL_SEQUENCE: () => INITIAL_SEQUENCE,
1294
1297
  InternalValidationError: () => InternalValidationError,
1295
1298
  KeyDerivationType: () => KeyDerivationType,
1296
1299
  LRC_WALLET_NETWORK: () => LRC_WALLET_NETWORK,
@@ -1303,6 +1306,8 @@ __export(index_exports, {
1303
1306
  RPCError: () => RPCError,
1304
1307
  SparkSDKError: () => SparkSDKError,
1305
1308
  SparkWallet: () => SparkWallet,
1309
+ TEST_UNILATERAL_DIRECT_SEQUENCE: () => TEST_UNILATERAL_DIRECT_SEQUENCE,
1310
+ TEST_UNILATERAL_SEQUENCE: () => TEST_UNILATERAL_SEQUENCE,
1306
1311
  TaprootOutputKeysGenerator: () => TaprootOutputKeysGenerator,
1307
1312
  TaprootSparkSigner: () => TaprootSparkSigner,
1308
1313
  TokenTransactionService: () => TokenTransactionService,
@@ -1322,13 +1327,21 @@ __export(index_exports, {
1322
1327
  constructFeeBumpTx: () => constructFeeBumpTx,
1323
1328
  constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
1324
1329
  constructUnilateralExitTxs: () => constructUnilateralExitTxs,
1330
+ createConnectorRefundTransactions: () => createConnectorRefundTransactions,
1331
+ createLeafNodeTx: () => createLeafNodeTx,
1332
+ createNodeTx: () => createNodeTx,
1333
+ createNodeTxs: () => createNodeTxs,
1325
1334
  createRefundTx: () => createRefundTx,
1335
+ createRefundTxs: () => createRefundTxs,
1336
+ createRootTx: () => createRootTx,
1326
1337
  createSigningCommitment: () => createSigningCommitment,
1327
1338
  createSigningNonce: () => createSigningNonce,
1339
+ createSplitTx: () => createSplitTx,
1328
1340
  decodeBech32mTokenIdentifier: () => decodeBech32mTokenIdentifier,
1329
1341
  decodeBytesToSigningCommitment: () => decodeBytesToSigningCommitment,
1330
1342
  decodeBytesToSigningNonce: () => decodeBytesToSigningNonce,
1331
1343
  decodeSparkAddress: () => decodeSparkAddress,
1344
+ doesLeafNeedRefresh: () => doesLeafNeedRefresh,
1332
1345
  encodeBech32mTokenIdentifier: () => encodeBech32mTokenIdentifier,
1333
1346
  encodeSigningCommitmentToBytes: () => encodeSigningCommitmentToBytes,
1334
1347
  encodeSigningNonceToBytes: () => encodeSigningNonceToBytes,
@@ -1357,6 +1370,7 @@ __export(index_exports, {
1357
1370
  getSparkAddressFromTaproot: () => getSparkAddressFromTaproot,
1358
1371
  getTransactionSequence: () => getTransactionSequence,
1359
1372
  getTransferPackageSigningPayload: () => getTransferPackageSigningPayload,
1373
+ getTxEstimatedVbytesSizeByNumberOfInputsOutputs: () => getTxEstimatedVbytesSizeByNumberOfInputsOutputs,
1360
1374
  getTxFromRawTxBytes: () => getTxFromRawTxBytes,
1361
1375
  getTxFromRawTxHex: () => getTxFromRawTxHex,
1362
1376
  getTxId: () => getTxId,
@@ -16203,13 +16217,24 @@ function applyAdaptorToSignature(pubkey, hash, signature, adaptorPrivateKeyBytes
16203
16217
  const adaptorPrivateKey = (0, import_utils4.bytesToNumberBE)(adaptorPrivateKeyBytes);
16204
16218
  const newS = (0, import_modular.mod)(sBigInt + adaptorPrivateKey, import_secp256k12.secp256k1.CURVE.n);
16205
16219
  const newSig = new Uint8Array([...r, ...(0, import_utils4.numberToBytesBE)(newS, 32)]);
16206
- if (import_secp256k12.schnorr.verify(newSig, hash, pubkey)) {
16207
- return newSig;
16220
+ try {
16221
+ if (import_secp256k12.schnorr.verify(newSig, hash, pubkey)) {
16222
+ return newSig;
16223
+ }
16224
+ } catch (e) {
16225
+ console.error("[applyAdaptorToSignature] Addition verification failed:", e);
16208
16226
  }
16209
16227
  const altS = (0, import_modular.mod)(sBigInt - adaptorPrivateKey, import_secp256k12.secp256k1.CURVE.n);
16210
16228
  const altSig = new Uint8Array([...r, ...(0, import_utils4.numberToBytesBE)(altS, 32)]);
16211
- if (import_secp256k12.schnorr.verify(altSig, hash, pubkey)) {
16212
- return altSig;
16229
+ try {
16230
+ if (import_secp256k12.schnorr.verify(altSig, hash, pubkey)) {
16231
+ return altSig;
16232
+ }
16233
+ } catch (e) {
16234
+ console.error(
16235
+ "[applyAdaptorToSignature] Subtraction verification failed:",
16236
+ e
16237
+ );
16213
16238
  }
16214
16239
  throw new Error("Cannot apply adaptor to signature");
16215
16240
  }
@@ -16532,6 +16557,12 @@ function getTxId(tx) {
16532
16557
  function getTxIdNoReverse(tx) {
16533
16558
  return (0, import_utils5.bytesToHex)((0, import_sha2.sha256)((0, import_sha2.sha256)(tx.toBytes(true))));
16534
16559
  }
16560
+ function getTxEstimatedVbytesSizeByNumberOfInputsOutputs(numInputs, numOutputs) {
16561
+ const TX_OVERHEAD = 10;
16562
+ const IN_VBYTES = 150;
16563
+ const OUT_VBYTES = 34;
16564
+ return TX_OVERHEAD + numInputs * IN_VBYTES + numOutputs * OUT_VBYTES;
16565
+ }
16535
16566
 
16536
16567
  // src/utils/keys.ts
16537
16568
  init_buffer();
@@ -16914,6 +16945,20 @@ var import_core = require("@lightsparkdev/core");
16914
16945
 
16915
16946
  // src/graphql/objects/ClaimStaticDepositStatus.ts
16916
16947
  init_buffer();
16948
+ var ClaimStaticDepositStatus = /* @__PURE__ */ ((ClaimStaticDepositStatus2) => {
16949
+ ClaimStaticDepositStatus2["FUTURE_VALUE"] = "FUTURE_VALUE";
16950
+ ClaimStaticDepositStatus2["CREATED"] = "CREATED";
16951
+ ClaimStaticDepositStatus2["TRANSFER_CREATED"] = "TRANSFER_CREATED";
16952
+ ClaimStaticDepositStatus2["TRANSFER_CREATION_FAILED"] = "TRANSFER_CREATION_FAILED";
16953
+ ClaimStaticDepositStatus2["REFUND_SIGNING_COMMITMENTS_QUERYING_FAILED"] = "REFUND_SIGNING_COMMITMENTS_QUERYING_FAILED";
16954
+ ClaimStaticDepositStatus2["REFUND_SIGNING_FAILED"] = "REFUND_SIGNING_FAILED";
16955
+ ClaimStaticDepositStatus2["UTXO_SWAPPING_FAILED"] = "UTXO_SWAPPING_FAILED";
16956
+ ClaimStaticDepositStatus2["TRANSFER_COMPLETED"] = "TRANSFER_COMPLETED";
16957
+ ClaimStaticDepositStatus2["SPEND_TX_CREATED"] = "SPEND_TX_CREATED";
16958
+ ClaimStaticDepositStatus2["SPEND_TX_BROADCAST"] = "SPEND_TX_BROADCAST";
16959
+ return ClaimStaticDepositStatus2;
16960
+ })(ClaimStaticDepositStatus || {});
16961
+ var ClaimStaticDepositStatus_default = ClaimStaticDepositStatus;
16917
16962
 
16918
16963
  // src/graphql/objects/CurrencyAmount.ts
16919
16964
  init_buffer();
@@ -16956,6 +17001,26 @@ var CurrencyAmountToJson = (obj) => {
16956
17001
  };
16957
17002
  };
16958
17003
 
17004
+ // src/graphql/objects/ClaimStaticDeposit.ts
17005
+ var ClaimStaticDepositFromJson = (obj) => {
17006
+ return {
17007
+ id: obj["claim_static_deposit_id"],
17008
+ createdAt: obj["claim_static_deposit_created_at"],
17009
+ updatedAt: obj["claim_static_deposit_updated_at"],
17010
+ network: BitcoinNetwork_default[obj["claim_static_deposit_network"]] ?? BitcoinNetwork_default.FUTURE_VALUE,
17011
+ creditAmount: CurrencyAmountFromJson(
17012
+ obj["claim_static_deposit_credit_amount"]
17013
+ ),
17014
+ maxFee: CurrencyAmountFromJson(obj["claim_static_deposit_max_fee"]),
17015
+ status: ClaimStaticDepositStatus_default[obj["claim_static_deposit_status"]] ?? ClaimStaticDepositStatus_default.FUTURE_VALUE,
17016
+ transactionId: obj["claim_static_deposit_transaction_id"],
17017
+ outputIndex: obj["claim_static_deposit_output_index"],
17018
+ bitcoinNetwork: BitcoinNetwork_default[obj["claim_static_deposit_bitcoin_network"]] ?? BitcoinNetwork_default.FUTURE_VALUE,
17019
+ typename: "ClaimStaticDeposit",
17020
+ transferSparkId: obj["claim_static_deposit_transfer_spark_id"]
17021
+ };
17022
+ };
17023
+
16959
17024
  // src/graphql/objects/ClaimStaticDepositRequestType.ts
16960
17025
  init_buffer();
16961
17026
  var ClaimStaticDepositRequestType = /* @__PURE__ */ ((ClaimStaticDepositRequestType2) => {
@@ -17103,7 +17168,11 @@ var SwapLeafFromJson = (obj) => {
17103
17168
  return {
17104
17169
  leafId: obj["swap_leaf_leaf_id"],
17105
17170
  rawUnsignedRefundTransaction: obj["swap_leaf_raw_unsigned_refund_transaction"],
17106
- adaptorSignedSignature: obj["swap_leaf_adaptor_signed_signature"]
17171
+ adaptorSignedSignature: obj["swap_leaf_adaptor_signed_signature"],
17172
+ directRawUnsignedRefundTransaction: obj["swap_leaf_direct_raw_unsigned_refund_transaction"],
17173
+ directAdaptorSignedSignature: obj["swap_leaf_direct_adaptor_signed_signature"],
17174
+ directFromCpfpRawUnsignedRefundTransaction: obj["swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction"],
17175
+ directFromCpfpAdaptorSignedSignature: obj["swap_leaf_direct_from_cpfp_adaptor_signed_signature"]
17107
17176
  };
17108
17177
  };
17109
17178
 
@@ -17358,6 +17427,10 @@ fragment LeavesSwapRequestFragment on LeavesSwapRequest {
17358
17427
  swap_leaf_leaf_id: leaf_id
17359
17428
  swap_leaf_raw_unsigned_refund_transaction: raw_unsigned_refund_transaction
17360
17429
  swap_leaf_adaptor_signed_signature: adaptor_signed_signature
17430
+ swap_leaf_direct_raw_unsigned_refund_transaction: direct_raw_unsigned_refund_transaction
17431
+ swap_leaf_direct_adaptor_signed_signature: direct_adaptor_signed_signature
17432
+ swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction: direct_from_cpfp_raw_unsigned_refund_transaction
17433
+ swap_leaf_direct_from_cpfp_adaptor_signed_signature: direct_from_cpfp_adaptor_signed_signature
17361
17434
  }
17362
17435
  }`;
17363
17436
 
@@ -17704,6 +17777,10 @@ fragment UserRequestFragment on UserRequest {
17704
17777
  swap_leaf_leaf_id: leaf_id
17705
17778
  swap_leaf_raw_unsigned_refund_transaction: raw_unsigned_refund_transaction
17706
17779
  swap_leaf_adaptor_signed_signature: adaptor_signed_signature
17780
+ swap_leaf_direct_raw_unsigned_refund_transaction: direct_raw_unsigned_refund_transaction
17781
+ swap_leaf_direct_adaptor_signed_signature: direct_adaptor_signed_signature
17782
+ swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction: direct_from_cpfp_raw_unsigned_refund_transaction
17783
+ swap_leaf_direct_from_cpfp_adaptor_signed_signature: direct_from_cpfp_adaptor_signed_signature
17707
17784
  }
17708
17785
  }
17709
17786
  ... on LightningReceiveRequest {
@@ -17811,7 +17888,7 @@ function mapTransferLeafToWalletTransferLeaf(proto) {
17811
17888
  intermediateRefundTx: (0, import_utils7.bytesToHex)(proto.intermediateRefundTx)
17812
17889
  };
17813
17890
  }
17814
- function mapTransferToWalletTransfer(proto, identityPublicKey) {
17891
+ function mapTransferToWalletTransfer(proto, identityPublicKey, userRequest) {
17815
17892
  const receiverIdentityPublicKey = (0, import_utils7.bytesToHex)(proto.receiverIdentityPublicKey);
17816
17893
  const senderIdentityPublicKey = (0, import_utils7.bytesToHex)(proto.senderIdentityPublicKey);
17817
17894
  return {
@@ -17825,22 +17902,45 @@ function mapTransferToWalletTransfer(proto, identityPublicKey) {
17825
17902
  createdTime: proto.createdTime ? new Date(proto.createdTime) : void 0,
17826
17903
  updatedTime: proto.updatedTime ? new Date(proto.updatedTime) : void 0,
17827
17904
  type: TransferType[proto.type],
17828
- transferDirection: receiverIdentityPublicKey === identityPublicKey ? "INCOMING" /* INCOMING */ : "OUTGOING" /* OUTGOING */
17905
+ transferDirection: receiverIdentityPublicKey === identityPublicKey ? "INCOMING" /* INCOMING */ : "OUTGOING" /* OUTGOING */,
17906
+ userRequest
17829
17907
  };
17830
17908
  }
17831
17909
 
17910
+ // src/utils/fetch.ts
17911
+ init_buffer();
17912
+ var fetchImpl = typeof window !== "undefined" && window.fetch ? window.fetch.bind(window) : globalThis.fetch ? globalThis.fetch : null;
17913
+ var Headers = globalThis.Headers ?? null;
17914
+ var getFetch = () => {
17915
+ if (!fetchImpl) {
17916
+ throw new Error(
17917
+ "Fetch implementation is not set. Please set it using setFetch()."
17918
+ );
17919
+ }
17920
+ if (!Headers) {
17921
+ throw new Error(
17922
+ "Headers implementation is not set. Please set it using setFetch()."
17923
+ );
17924
+ }
17925
+ return {
17926
+ fetch: fetchImpl,
17927
+ Headers
17928
+ };
17929
+ };
17930
+
17832
17931
  // src/utils/mempool.ts
17833
17932
  async function getLatestDepositTxId(address2) {
17933
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
17834
17934
  const network = getNetworkFromAddress(address2);
17835
17935
  const baseUrl = network === BitcoinNetwork_default.REGTEST ? getElectrsUrl("REGTEST") : getElectrsUrl("MAINNET");
17836
- const headers = {};
17936
+ const headers = new Headers2();
17837
17937
  if (network === BitcoinNetwork_default.REGTEST) {
17838
17938
  const auth = btoa(
17839
17939
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
17840
17940
  );
17841
- headers["Authorization"] = `Basic ${auth}`;
17941
+ headers.set("Authorization", `Basic ${auth}`);
17842
17942
  }
17843
- const response = await fetch(`${baseUrl}/address/${address2}/txs`, {
17943
+ const response = await fetch2(`${baseUrl}/address/${address2}/txs`, {
17844
17944
  headers
17845
17945
  });
17846
17946
  const addressTxs = await response.json();
@@ -17857,14 +17957,15 @@ async function getLatestDepositTxId(address2) {
17857
17957
  return null;
17858
17958
  }
17859
17959
  async function isTxBroadcast(txid, baseUrl, network) {
17860
- const headers = {};
17960
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
17961
+ const headers = new Headers2();
17861
17962
  if (network === 3 /* REGTEST */) {
17862
17963
  const auth = btoa(
17863
17964
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
17864
17965
  );
17865
- headers["Authorization"] = `Basic ${auth}`;
17966
+ headers.set("Authorization", `Basic ${auth}`);
17866
17967
  }
17867
- const response = await fetch(`${baseUrl}/tx/${txid}`, {
17968
+ const response = await fetch2(`${baseUrl}/tx/${txid}`, {
17868
17969
  headers
17869
17970
  });
17870
17971
  const tx = await response.json();
@@ -18335,7 +18436,14 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
18335
18436
  // src/utils/transaction.ts
18336
18437
  init_buffer();
18337
18438
  var import_btc_signer = require("@scure/btc-signer");
18439
+ var INITIAL_TIMELOCK = 2e3;
18440
+ var TEST_UNILATERAL_TIMELOCK = 100;
18338
18441
  var TIME_LOCK_INTERVAL = 100;
18442
+ var DIRECT_TIMELOCK_OFFSET = 50;
18443
+ var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
18444
+ var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
18445
+ var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
18446
+ var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
18339
18447
  var ESTIMATED_TX_SIZE = 191;
18340
18448
  var DEFAULT_SATS_PER_VBYTE = 5;
18341
18449
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -18345,29 +18453,270 @@ function maybeApplyFee(amount) {
18345
18453
  }
18346
18454
  return amount;
18347
18455
  }
18348
- function createRefundTx(sequence, nodeOutPoint, amountSats, receivingPubkey, network) {
18349
- const newRefundTx = new import_btc_signer.Transaction({
18456
+ function createRootTx(depositOutPoint, depositTxOut) {
18457
+ const cpfpRootTx = new import_btc_signer.Transaction({
18458
+ version: 3,
18459
+ allowUnknownOutputs: true
18460
+ });
18461
+ cpfpRootTx.addInput(depositOutPoint);
18462
+ cpfpRootTx.addOutput(depositTxOut);
18463
+ cpfpRootTx.addOutput(getEphemeralAnchorOutput());
18464
+ const directRootTx = new import_btc_signer.Transaction({
18465
+ version: 3,
18466
+ allowUnknownOutputs: true
18467
+ });
18468
+ directRootTx.addInput(depositOutPoint);
18469
+ directRootTx.addOutput({
18470
+ script: depositTxOut.script,
18471
+ amount: maybeApplyFee(depositTxOut.amount ?? 0n)
18472
+ });
18473
+ return [cpfpRootTx, directRootTx];
18474
+ }
18475
+ function createSplitTx(parentOutPoint, childTxOuts) {
18476
+ const cpfpSplitTx = new import_btc_signer.Transaction({
18477
+ version: 3,
18478
+ allowUnknownOutputs: true
18479
+ });
18480
+ cpfpSplitTx.addInput(parentOutPoint);
18481
+ for (const txOut of childTxOuts) {
18482
+ cpfpSplitTx.addOutput(txOut);
18483
+ }
18484
+ cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
18485
+ const directSplitTx = new import_btc_signer.Transaction({
18486
+ version: 3,
18487
+ allowUnknownOutputs: true
18488
+ });
18489
+ directSplitTx.addInput(parentOutPoint);
18490
+ let totalOutputAmount = 0n;
18491
+ for (const txOut of childTxOuts) {
18492
+ totalOutputAmount += txOut.amount ?? 0n;
18493
+ }
18494
+ if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
18495
+ const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
18496
+ for (const txOut of childTxOuts) {
18497
+ const adjustedAmount = BigInt(
18498
+ Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
18499
+ );
18500
+ directSplitTx.addOutput({
18501
+ script: txOut.script,
18502
+ amount: adjustedAmount
18503
+ });
18504
+ }
18505
+ } else {
18506
+ for (const txOut of childTxOuts) {
18507
+ directSplitTx.addOutput(txOut);
18508
+ }
18509
+ }
18510
+ return [cpfpSplitTx, directSplitTx];
18511
+ }
18512
+ function createNodeTx({
18513
+ txOut,
18514
+ parentOutPoint,
18515
+ applyFee,
18516
+ includeAnchor
18517
+ }) {
18518
+ const nodeTx = new import_btc_signer.Transaction({
18519
+ version: 3,
18520
+ allowUnknownOutputs: true
18521
+ });
18522
+ nodeTx.addInput(parentOutPoint);
18523
+ if (applyFee) {
18524
+ nodeTx.addOutput({
18525
+ script: txOut.script,
18526
+ amount: maybeApplyFee(txOut.amount ?? 0n)
18527
+ });
18528
+ } else {
18529
+ nodeTx.addOutput(txOut);
18530
+ }
18531
+ if (includeAnchor) {
18532
+ nodeTx.addOutput(getEphemeralAnchorOutput());
18533
+ }
18534
+ return nodeTx;
18535
+ }
18536
+ function createNodeTxs(txOut, txIn, directTxIn) {
18537
+ const cpfpNodeTx = createNodeTx({
18538
+ txOut,
18539
+ parentOutPoint: txIn,
18540
+ includeAnchor: true
18541
+ });
18542
+ let directNodeTx;
18543
+ if (directTxIn) {
18544
+ directNodeTx = createNodeTx({
18545
+ txOut,
18546
+ parentOutPoint: directTxIn,
18547
+ includeAnchor: false,
18548
+ applyFee: true
18549
+ });
18550
+ }
18551
+ return { cpfpNodeTx, directNodeTx };
18552
+ }
18553
+ function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
18554
+ const cpfpLeafTx = new import_btc_signer.Transaction({
18555
+ version: 3,
18556
+ allowUnknownOutputs: true
18557
+ });
18558
+ cpfpLeafTx.addInput({
18559
+ ...parentOutPoint,
18560
+ sequence
18561
+ });
18562
+ cpfpLeafTx.addOutput(txOut);
18563
+ cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
18564
+ const directLeafTx = new import_btc_signer.Transaction({
18565
+ version: 3,
18566
+ allowUnknownOutputs: true
18567
+ });
18568
+ directLeafTx.addInput({
18569
+ ...parentOutPoint,
18570
+ sequence: directSequence
18571
+ });
18572
+ const amountSats = txOut.amount ?? 0n;
18573
+ let outputAmount = amountSats;
18574
+ if (shouldCalculateFee) {
18575
+ outputAmount = maybeApplyFee(amountSats);
18576
+ }
18577
+ directLeafTx.addOutput({
18578
+ script: txOut.script,
18579
+ amount: outputAmount
18580
+ });
18581
+ return [cpfpLeafTx, directLeafTx];
18582
+ }
18583
+ function createRefundTx({
18584
+ sequence,
18585
+ input,
18586
+ amountSats,
18587
+ receivingPubkey,
18588
+ network,
18589
+ shouldCalculateFee,
18590
+ includeAnchor
18591
+ }) {
18592
+ const refundTx = new import_btc_signer.Transaction({
18350
18593
  version: 3,
18351
18594
  allowUnknownOutputs: true
18352
18595
  });
18353
- newRefundTx.addInput({
18354
- ...nodeOutPoint,
18596
+ refundTx.addInput({
18597
+ ...input,
18355
18598
  sequence
18356
18599
  });
18357
18600
  const refundPkScript = getP2TRScriptFromPublicKey(receivingPubkey, network);
18358
- newRefundTx.addOutput({
18601
+ let outputAmount = amountSats;
18602
+ if (shouldCalculateFee) {
18603
+ outputAmount = maybeApplyFee(amountSats);
18604
+ }
18605
+ refundTx.addOutput({
18359
18606
  script: refundPkScript,
18607
+ amount: outputAmount
18608
+ });
18609
+ if (includeAnchor) {
18610
+ refundTx.addOutput(getEphemeralAnchorOutput());
18611
+ }
18612
+ return refundTx;
18613
+ }
18614
+ function createRefundTxs({
18615
+ sequence,
18616
+ directSequence,
18617
+ input,
18618
+ directInput,
18619
+ amountSats,
18620
+ receivingPubkey,
18621
+ network
18622
+ }) {
18623
+ const cpfpRefundTx = createRefundTx({
18624
+ sequence,
18625
+ input,
18626
+ amountSats,
18627
+ receivingPubkey,
18628
+ network,
18629
+ shouldCalculateFee: false,
18630
+ includeAnchor: true
18631
+ });
18632
+ let directRefundTx;
18633
+ let directFromCpfpRefundTx;
18634
+ if (directSequence && directInput) {
18635
+ directRefundTx = createRefundTx({
18636
+ sequence: directSequence,
18637
+ input: directInput,
18638
+ amountSats,
18639
+ receivingPubkey,
18640
+ network,
18641
+ shouldCalculateFee: true,
18642
+ includeAnchor: false
18643
+ });
18644
+ directFromCpfpRefundTx = createRefundTx({
18645
+ sequence: directSequence,
18646
+ input,
18647
+ amountSats,
18648
+ receivingPubkey,
18649
+ network,
18650
+ shouldCalculateFee: true,
18651
+ includeAnchor: false
18652
+ });
18653
+ } else if (directInput && !directSequence) {
18654
+ throw new ValidationError(
18655
+ "directSequence must be provided if directInput is",
18656
+ {
18657
+ field: "directSequence",
18658
+ value: directSequence
18659
+ }
18660
+ );
18661
+ }
18662
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
18663
+ }
18664
+ function createConnectorRefundTransactions(sequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey, network, shouldCalculateFee) {
18665
+ const cpfpRefundTx = new import_btc_signer.Transaction({
18666
+ version: 3,
18667
+ allowUnknownOutputs: true
18668
+ });
18669
+ cpfpRefundTx.addInput({
18670
+ ...cpfpNodeOutPoint,
18671
+ sequence
18672
+ });
18673
+ cpfpRefundTx.addInput(connectorOutput);
18674
+ const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
18675
+ cpfpRefundTx.addOutput({
18676
+ script: receiverScript,
18360
18677
  amount: amountSats
18361
18678
  });
18362
- newRefundTx.addOutput(getEphemeralAnchorOutput());
18363
- return newRefundTx;
18679
+ const directRefundTx = new import_btc_signer.Transaction({
18680
+ version: 3,
18681
+ allowUnknownOutputs: true
18682
+ });
18683
+ directRefundTx.addInput({
18684
+ ...directNodeOutPoint,
18685
+ sequence
18686
+ });
18687
+ directRefundTx.addInput(connectorOutput);
18688
+ let outputAmount = amountSats;
18689
+ if (shouldCalculateFee) {
18690
+ outputAmount = maybeApplyFee(amountSats);
18691
+ }
18692
+ directRefundTx.addOutput({
18693
+ script: receiverScript,
18694
+ amount: outputAmount
18695
+ });
18696
+ const directFromCpfpTx = new import_btc_signer.Transaction({
18697
+ version: 3,
18698
+ allowUnknownOutputs: true
18699
+ });
18700
+ directFromCpfpTx.addInput({
18701
+ ...cpfpNodeOutPoint,
18702
+ sequence
18703
+ });
18704
+ directFromCpfpTx.addInput(connectorOutput);
18705
+ directFromCpfpTx.addOutput({
18706
+ script: receiverScript,
18707
+ amount: outputAmount
18708
+ });
18709
+ return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
18364
18710
  }
18365
18711
  function getCurrentTimelock(currSequence) {
18366
18712
  return (currSequence || 0) & 65535;
18367
18713
  }
18368
18714
  function getTransactionSequence(currSequence) {
18369
18715
  const timelock = getCurrentTimelock(currSequence);
18370
- return 1 << 30 | timelock;
18716
+ return {
18717
+ nextSequence: 1 << 30 | timelock,
18718
+ nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
18719
+ };
18371
18720
  }
18372
18721
  function checkIfValidSequence(currSequence) {
18373
18722
  const TIME_LOCK_ACTIVE = (currSequence || 0) & 2147483648;
@@ -18385,24 +18734,32 @@ function checkIfValidSequence(currSequence) {
18385
18734
  });
18386
18735
  }
18387
18736
  }
18388
- function getNextTransactionSequence(currSequence, forRefresh) {
18737
+ function doesLeafNeedRefresh(currSequence, isNodeTx) {
18389
18738
  const currentTimelock = getCurrentTimelock(currSequence);
18390
- const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
18391
- if (forRefresh && nextTimelock <= 100 && currentTimelock > 0) {
18392
- return {
18393
- nextSequence: 1 << 30 | nextTimelock,
18394
- needRefresh: true
18395
- };
18739
+ if (isNodeTx) {
18740
+ return currentTimelock === 0;
18396
18741
  }
18397
- if (nextTimelock < 0) {
18742
+ return currentTimelock <= 100;
18743
+ }
18744
+ function getNextTransactionSequence(currSequence, isNodeTx) {
18745
+ const currentTimelock = getCurrentTimelock(currSequence);
18746
+ const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
18747
+ if (isNodeTx && nextTimelock < 0) {
18398
18748
  throw new ValidationError("timelock interval is less than 0", {
18399
18749
  field: "nextTimelock",
18400
- value: nextTimelock
18750
+ value: nextTimelock,
18751
+ expected: "Non-negative timelock interval"
18752
+ });
18753
+ } else if (!isNodeTx && nextTimelock <= 0) {
18754
+ throw new ValidationError("timelock interval is less than or equal to 0", {
18755
+ field: "nextTimelock",
18756
+ value: nextTimelock,
18757
+ expected: "Timelock greater than 0"
18401
18758
  });
18402
18759
  }
18403
18760
  return {
18404
18761
  nextSequence: 1 << 30 | nextTimelock,
18405
- needRefresh: nextTimelock <= 100
18762
+ nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
18406
18763
  };
18407
18764
  }
18408
18765
  function getEphemeralAnchorOutput() {
@@ -18931,7 +19288,7 @@ init_buffer();
18931
19288
  var import_core8 = require("@lightsparkdev/core");
18932
19289
  var isReactNative = typeof navigator !== "undefined" && navigator.product === "ReactNative";
18933
19290
  var isBun = globalThis.Bun !== void 0;
18934
- var packageVersion = true ? "0.2.3" : "unknown";
19291
+ var packageVersion = true ? "0.2.5" : "unknown";
18935
19292
  var baseEnvStr = "unknown";
18936
19293
  if (isBun) {
18937
19294
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -19642,10 +19999,18 @@ init_buffer();
19642
19999
  var CompleteLeavesSwap = `
19643
20000
  mutation CompleteLeavesSwap(
19644
20001
  $adaptor_secret_key: String!
20002
+ $direct_adaptor_secret_key: String!
20003
+ $direct_from_cpfp_adaptor_secret_key: String!
19645
20004
  $user_outbound_transfer_external_id: UUID!
19646
20005
  $leaves_swap_request_id: ID!
19647
20006
  ) {
19648
- 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 }) {
20007
+ complete_leaves_swap(input: {
20008
+ adaptor_secret_key: $adaptor_secret_key,
20009
+ direct_adaptor_secret_key: $direct_adaptor_secret_key,
20010
+ direct_from_cpfp_adaptor_secret_key: $direct_from_cpfp_adaptor_secret_key,
20011
+ user_outbound_transfer_external_id: $user_outbound_transfer_external_id,
20012
+ leaves_swap_request_id: $leaves_swap_request_id
20013
+ }) {
19649
20014
  request {
19650
20015
  ...LeavesSwapRequestFragment
19651
20016
  }
@@ -19776,6 +20141,8 @@ init_buffer();
19776
20141
  var RequestSwapLeaves = `
19777
20142
  mutation RequestSwapLeaves(
19778
20143
  $adaptor_pubkey: PublicKey!
20144
+ $direct_adaptor_pubkey: PublicKey
20145
+ $direct_from_cpfp_adaptor_pubkey: PublicKey
19779
20146
  $total_amount_sats: Long!
19780
20147
  $target_amount_sats: Long!
19781
20148
  $fee_sats: Long!
@@ -19785,6 +20152,8 @@ var RequestSwapLeaves = `
19785
20152
  ) {
19786
20153
  request_leaves_swap(input: {
19787
20154
  adaptor_pubkey: $adaptor_pubkey
20155
+ direct_adaptor_pubkey: $direct_adaptor_pubkey
20156
+ direct_from_cpfp_adaptor_pubkey: $direct_from_cpfp_adaptor_pubkey
19788
20157
  total_amount_sats: $total_amount_sats
19789
20158
  target_amount_sats: $target_amount_sats
19790
20159
  fee_sats: $fee_sats
@@ -20079,15 +20448,19 @@ var LightningSendFeeEstimate = `
20079
20448
  ${FRAGMENT13}
20080
20449
  `;
20081
20450
 
20082
- // src/graphql/queries/Transfer.ts
20451
+ // src/graphql/queries/Transfers.ts
20083
20452
  init_buffer();
20084
- var GetTransfer = `
20085
- query Transfer($transfer_spark_id: UUID!) {
20086
- transfer(transfer_spark_id: $transfer_spark_id) {
20453
+ var GetTransfers = `
20454
+ query Transfers($transfer_spark_ids: [UUID!]!) {
20455
+ transfers(transfer_spark_ids: $transfer_spark_ids) {
20087
20456
  ...TransferFragment
20457
+ transfer_user_request: user_request {
20458
+ ...UserRequestFragment
20459
+ }
20088
20460
  }
20089
20461
  }
20090
20462
  ${FRAGMENT2}
20463
+ ${FRAGMENT6}
20091
20464
  `;
20092
20465
 
20093
20466
  // src/graphql/queries/UserRequest.ts
@@ -20293,6 +20666,8 @@ var SspClient = class {
20293
20666
  }
20294
20667
  async requestLeaveSwap({
20295
20668
  adaptorPubkey,
20669
+ directAdaptorPubkey,
20670
+ directFromCpfpAdaptorPubkey,
20296
20671
  totalAmountSats,
20297
20672
  targetAmountSats,
20298
20673
  feeSats,
@@ -20304,6 +20679,8 @@ var SspClient = class {
20304
20679
  queryPayload: RequestSwapLeaves,
20305
20680
  variables: {
20306
20681
  adaptor_pubkey: adaptorPubkey,
20682
+ direct_adaptor_pubkey: directAdaptorPubkey,
20683
+ direct_from_cpfp_adaptor_pubkey: directFromCpfpAdaptorPubkey,
20307
20684
  total_amount_sats: totalAmountSats,
20308
20685
  target_amount_sats: targetAmountSats,
20309
20686
  fee_sats: feeSats,
@@ -20322,6 +20699,8 @@ var SspClient = class {
20322
20699
  }
20323
20700
  async completeLeaveSwap({
20324
20701
  adaptorSecretKey,
20702
+ directAdaptorSecretKey,
20703
+ directFromCpfpAdaptorSecretKey,
20325
20704
  userOutboundTransferExternalId,
20326
20705
  leavesSwapRequestId
20327
20706
  }) {
@@ -20329,6 +20708,8 @@ var SspClient = class {
20329
20708
  queryPayload: CompleteLeavesSwap,
20330
20709
  variables: {
20331
20710
  adaptor_secret_key: adaptorSecretKey,
20711
+ direct_adaptor_secret_key: directAdaptorSecretKey,
20712
+ direct_from_cpfp_adaptor_secret_key: directFromCpfpAdaptorSecretKey,
20332
20713
  user_outbound_transfer_external_id: userOutboundTransferExternalId,
20333
20714
  leaves_swap_request_id: leavesSwapRequestId
20334
20715
  },
@@ -20436,14 +20817,47 @@ var SspClient = class {
20436
20817
  }
20437
20818
  });
20438
20819
  }
20439
- async getTransfer(id) {
20820
+ async getTransfers(ids) {
20440
20821
  return await this.executeRawQuery({
20441
- queryPayload: GetTransfer,
20822
+ queryPayload: GetTransfers,
20442
20823
  variables: {
20443
- transfer_spark_id: id
20824
+ transfer_spark_ids: ids
20444
20825
  },
20445
20826
  constructObject: (response) => {
20446
- return TransferFromJson(response.transfer);
20827
+ return response.transfers.map((transfer) => {
20828
+ const transferObj = TransferFromJson(
20829
+ transfer
20830
+ );
20831
+ switch (transfer.transfer_user_request.__typename) {
20832
+ case "ClaimStaticDeposit":
20833
+ transferObj.userRequest = ClaimStaticDepositFromJson(
20834
+ transfer.transfer_user_request
20835
+ );
20836
+ break;
20837
+ case "CoopExitRequest":
20838
+ transferObj.userRequest = CoopExitRequestFromJson(
20839
+ transfer.transfer_user_request
20840
+ );
20841
+ break;
20842
+ case "LeavesSwapRequest":
20843
+ transferObj.userRequest = LeavesSwapRequestFromJson(
20844
+ transfer.transfer_user_request
20845
+ );
20846
+ break;
20847
+ case "LightningReceiveRequest":
20848
+ transferObj.userRequest = LightningReceiveRequestFromJson(
20849
+ transfer.transfer_user_request
20850
+ );
20851
+ break;
20852
+ case "LightningSendRequest":
20853
+ transferObj.userRequest = LightningSendRequestFromJson(
20854
+ transfer.transfer_user_request
20855
+ );
20856
+ break;
20857
+ }
20858
+ const { userRequestId, ...rest } = transferObj;
20859
+ return rest;
20860
+ });
20447
20861
  }
20448
20862
  });
20449
20863
  }
@@ -24493,11 +24907,7 @@ var import_sha28 = require("@noble/hashes/sha2");
24493
24907
  var import_btc_signer2 = require("@scure/btc-signer");
24494
24908
  var ecies2 = __toESM(require("eciesjs"), 1);
24495
24909
  var import_uuidv72 = require("uuidv7");
24496
- var INITIAL_TIME_LOCK = 2e3;
24497
24910
  var DEFAULT_EXPIRY_TIME = 10 * 60 * 1e3;
24498
- function initialSequence() {
24499
- return 1 << 30 | INITIAL_TIME_LOCK;
24500
- }
24501
24911
  function getSigningJobProto(signingJob) {
24502
24912
  return {
24503
24913
  signingPublicKey: signingJob.signingPublicKey,
@@ -24514,12 +24924,14 @@ var BaseTransferService = class {
24514
24924
  this.connectionManager = connectionManager;
24515
24925
  this.signingService = signingService;
24516
24926
  }
24517
- async sendTransferTweakKey(transfer, leaves, refundSignatureMap) {
24927
+ async sendTransferTweakKey(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
24518
24928
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
24519
24929
  transfer.id,
24520
24930
  transfer.receiverIdentityPublicKey,
24521
24931
  leaves,
24522
- refundSignatureMap
24932
+ cpfpRefundSignatureMap,
24933
+ directRefundSignatureMap,
24934
+ directFromCpfpRefundSignatureMap
24523
24935
  );
24524
24936
  let updatedTransfer;
24525
24937
  const coordinatorOperator = this.config.getSigningOperators()[this.config.getCoordinatorIdentifier()];
@@ -24557,13 +24969,26 @@ var BaseTransferService = class {
24557
24969
  }
24558
24970
  return updatedTransfer;
24559
24971
  }
24560
- async deliverTransferPackage(transfer, leaves, refundSignatureMap) {
24972
+ async deliverTransferPackage(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
24561
24973
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
24562
24974
  transfer.id,
24563
24975
  transfer.receiverIdentityPublicKey,
24564
24976
  leaves,
24565
- refundSignatureMap
24977
+ cpfpRefundSignatureMap,
24978
+ directRefundSignatureMap,
24979
+ directFromCpfpRefundSignatureMap
24566
24980
  );
24981
+ for (const [key, operator] of Object.entries(
24982
+ this.config.getSigningOperators()
24983
+ )) {
24984
+ const tweaks = keyTweakInputMap.get(key);
24985
+ if (!tweaks) {
24986
+ throw new ValidationError("No tweaks for operator", {
24987
+ field: "operator",
24988
+ value: key
24989
+ });
24990
+ }
24991
+ }
24567
24992
  const transferPackage = await this.prepareTransferPackage(
24568
24993
  transfer.id,
24569
24994
  keyTweakInputMap,
@@ -24589,6 +25014,8 @@ var BaseTransferService = class {
24589
25014
  transferID,
24590
25015
  receiverIdentityPubkey,
24591
25016
  leaves,
25017
+ /* @__PURE__ */ new Map(),
25018
+ /* @__PURE__ */ new Map(),
24592
25019
  /* @__PURE__ */ new Map()
24593
25020
  );
24594
25021
  const transferPackage = await this.prepareTransferPackage(
@@ -24602,7 +25029,7 @@ var BaseTransferService = class {
24602
25029
  );
24603
25030
  let response;
24604
25031
  try {
24605
- response = await sparkClient.start_transfer({
25032
+ response = await sparkClient.start_transfer_v2({
24606
25033
  transferId: transferID,
24607
25034
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
24608
25035
  receiverIdentityPublicKey: receiverIdentityPubkey,
@@ -24631,12 +25058,22 @@ var BaseTransferService = class {
24631
25058
  nodes.push(leaf.leaf.id);
24632
25059
  }
24633
25060
  const signingCommitments = await sparkClient.get_signing_commitments({
24634
- nodeIds: nodes
25061
+ nodeIds: nodes,
25062
+ count: 3
24635
25063
  });
24636
- const leafSigningJobs = await this.signingService.signRefunds(
25064
+ const {
25065
+ cpfpLeafSigningJobs,
25066
+ directLeafSigningJobs,
25067
+ directFromCpfpLeafSigningJobs
25068
+ } = await this.signingService.signRefunds(
24637
25069
  leaves,
24638
- signingCommitments.signingCommitments,
24639
- receiverIdentityPubkey
25070
+ receiverIdentityPubkey,
25071
+ signingCommitments.signingCommitments.slice(0, leaves.length),
25072
+ signingCommitments.signingCommitments.slice(
25073
+ leaves.length,
25074
+ 2 * leaves.length
25075
+ ),
25076
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
24640
25077
  );
24641
25078
  const encryptedKeyTweaks = {};
24642
25079
  for (const [key, value] of keyTweakInputMap) {
@@ -24656,12 +25093,11 @@ var BaseTransferService = class {
24656
25093
  encryptedKeyTweaks[key] = Uint8Array.from(encryptedProto);
24657
25094
  }
24658
25095
  const transferPackage = {
24659
- leavesToSend: leafSigningJobs,
25096
+ leavesToSend: cpfpLeafSigningJobs,
24660
25097
  keyTweakPackage: encryptedKeyTweaks,
24661
25098
  userSignature: new Uint8Array(),
24662
- // TODO: Add direct refund signature
24663
- directLeavesToSend: [],
24664
- directFromCpfpLeavesToSend: []
25099
+ directLeavesToSend: directLeafSigningJobs,
25100
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs
24665
25101
  };
24666
25102
  const transferPackageSigningPayload = getTransferPackageSigningPayload(
24667
25103
  transferID,
@@ -24721,7 +25157,7 @@ var BaseTransferService = class {
24721
25157
  }
24722
25158
  return updatedTransfer;
24723
25159
  }
24724
- async signRefunds(leafDataMap, operatorSigningResults, adaptorPubKey) {
25160
+ async signRefunds(leafDataMap, operatorSigningResults, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
24725
25161
  const nodeSignatures = [];
24726
25162
  for (const operatorSigningResult of operatorSigningResults) {
24727
25163
  const leafData = leafDataMap.get(operatorSigningResult.leafId);
@@ -24736,54 +25172,120 @@ var BaseTransferService = class {
24736
25172
  `Output not found for leaf ${operatorSigningResult.leafId}`
24737
25173
  );
24738
25174
  }
24739
- const refundTxSighash = getSigHashFromTx(leafData.refundTx, 0, txOutput);
25175
+ const cpfpRefundTxSighash = getSigHashFromTx(
25176
+ leafData.refundTx,
25177
+ 0,
25178
+ txOutput
25179
+ );
24740
25180
  const publicKey = await this.config.signer.getPublicKeyFromDerivation(
24741
25181
  leafData.keyDerivation
24742
25182
  );
24743
- const userSignature = await this.config.signer.signFrost({
24744
- message: refundTxSighash,
25183
+ const cpfpUserSignature = await this.config.signer.signFrost({
25184
+ message: cpfpRefundTxSighash,
24745
25185
  publicKey,
24746
25186
  keyDerivation: leafData.keyDerivation,
24747
25187
  selfCommitment: leafData.signingNonceCommitment,
24748
25188
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
24749
- adaptorPubKey,
25189
+ adaptorPubKey: cpfpAdaptorPubKey,
24750
25190
  verifyingKey: operatorSigningResult.verifyingKey
24751
25191
  });
24752
- const refundAggregate = await this.config.signer.aggregateFrost({
24753
- message: refundTxSighash,
25192
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
25193
+ message: cpfpRefundTxSighash,
24754
25194
  statechainSignatures: operatorSigningResult.refundTxSigningResult?.signatureShares,
24755
25195
  statechainPublicKeys: operatorSigningResult.refundTxSigningResult?.publicKeys,
24756
25196
  verifyingKey: operatorSigningResult.verifyingKey,
24757
25197
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
24758
25198
  selfCommitment: leafData.signingNonceCommitment,
24759
25199
  publicKey,
24760
- selfSignature: userSignature,
24761
- adaptorPubKey
25200
+ selfSignature: cpfpUserSignature,
25201
+ adaptorPubKey: cpfpAdaptorPubKey
24762
25202
  });
25203
+ let directRefundAggregate;
25204
+ let directFromCpfpRefundAggregate;
25205
+ if (leafData.directTx) {
25206
+ const directTxOutput = leafData.directTx.getOutput(0);
25207
+ if (leafData.directRefundTx) {
25208
+ const directRefundTxSighash = getSigHashFromTx(
25209
+ leafData.directRefundTx,
25210
+ 0,
25211
+ directTxOutput
25212
+ );
25213
+ const directUserSignature = await this.config.signer.signFrost({
25214
+ message: directRefundTxSighash,
25215
+ publicKey,
25216
+ keyDerivation: leafData.keyDerivation,
25217
+ selfCommitment: leafData.directSigningNonceCommitment,
25218
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
25219
+ adaptorPubKey: directAdaptorPubKey,
25220
+ verifyingKey: operatorSigningResult.verifyingKey
25221
+ });
25222
+ directRefundAggregate = await this.config.signer.aggregateFrost({
25223
+ message: directRefundTxSighash,
25224
+ statechainSignatures: operatorSigningResult.directRefundTxSigningResult?.signatureShares,
25225
+ statechainPublicKeys: operatorSigningResult.directRefundTxSigningResult?.publicKeys,
25226
+ verifyingKey: operatorSigningResult.verifyingKey,
25227
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
25228
+ selfCommitment: leafData.directSigningNonceCommitment,
25229
+ publicKey,
25230
+ selfSignature: directUserSignature,
25231
+ adaptorPubKey: directAdaptorPubKey
25232
+ });
25233
+ }
25234
+ if (leafData.directFromCpfpRefundTx) {
25235
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
25236
+ leafData.directFromCpfpRefundTx,
25237
+ 0,
25238
+ txOutput
25239
+ );
25240
+ const directFromCpfpUserSignature = await this.config.signer.signFrost({
25241
+ message: directFromCpfpRefundTxSighash,
25242
+ publicKey,
25243
+ keyDerivation: leafData.keyDerivation,
25244
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
25245
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
25246
+ adaptorPubKey: directFromCpfpAdaptorPubKey,
25247
+ verifyingKey: operatorSigningResult.verifyingKey
25248
+ });
25249
+ directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
25250
+ message: directFromCpfpRefundTxSighash,
25251
+ statechainSignatures: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signatureShares,
25252
+ statechainPublicKeys: operatorSigningResult.directFromCpfpRefundTxSigningResult?.publicKeys,
25253
+ verifyingKey: operatorSigningResult.verifyingKey,
25254
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
25255
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
25256
+ publicKey,
25257
+ selfSignature: directFromCpfpUserSignature,
25258
+ adaptorPubKey: directFromCpfpAdaptorPubKey
25259
+ });
25260
+ }
25261
+ }
24763
25262
  nodeSignatures.push({
24764
25263
  nodeId: operatorSigningResult.leafId,
24765
- refundTxSignature: refundAggregate,
24766
25264
  nodeTxSignature: new Uint8Array(),
24767
- // TODO: Add direct refund signature
24768
25265
  directNodeTxSignature: new Uint8Array(),
24769
- directRefundTxSignature: new Uint8Array(),
24770
- directFromCpfpRefundTxSignature: new Uint8Array()
25266
+ refundTxSignature: cpfpRefundAggregate,
25267
+ directRefundTxSignature: directRefundAggregate ?? new Uint8Array(),
25268
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate ?? new Uint8Array()
24771
25269
  });
24772
25270
  }
24773
25271
  return nodeSignatures;
24774
25272
  }
24775
- async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, refundSignatureMap) {
25273
+ async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
24776
25274
  const receiverEciesPubKey = ecies2.PublicKey.fromHex(
24777
25275
  (0, import_utils14.bytesToHex)(receiverIdentityPubkey)
24778
25276
  );
24779
25277
  const leavesTweaksMap = /* @__PURE__ */ new Map();
24780
25278
  for (const leaf of leaves) {
24781
- const refundSignature = refundSignatureMap.get(leaf.leaf.id);
25279
+ const cpfpRefundSignature = cpfpRefundSignatureMap.get(leaf.leaf.id);
25280
+ const directRefundSignature = directRefundSignatureMap.get(leaf.leaf.id);
25281
+ const directFromCpfpRefundSignature = directFromCpfpRefundSignatureMap.get(leaf.leaf.id);
24782
25282
  const leafTweaksMap = await this.prepareSingleSendTransferKeyTweak(
24783
25283
  transferID,
24784
25284
  leaf,
24785
25285
  receiverEciesPubKey,
24786
- refundSignature
25286
+ cpfpRefundSignature,
25287
+ directRefundSignature,
25288
+ directFromCpfpRefundSignature
24787
25289
  );
24788
25290
  for (const [identifier, leafTweak] of leafTweaksMap) {
24789
25291
  leavesTweaksMap.set(identifier, [
@@ -24794,7 +25296,7 @@ var BaseTransferService = class {
24794
25296
  }
24795
25297
  return leavesTweaksMap;
24796
25298
  }
24797
- async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, refundSignature) {
25299
+ async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, cpfpRefundSignature, directRefundSignature, directFromCpfpRefundSignature) {
24798
25300
  const signingOperators = this.config.getSigningOperators();
24799
25301
  const { shares, secretCipher } = await this.config.signer.subtractSplitAndEncrypt({
24800
25302
  first: leaf.keyDerivation,
@@ -24842,10 +25344,9 @@ var BaseTransferService = class {
24842
25344
  pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak),
24843
25345
  secretCipher,
24844
25346
  signature,
24845
- refundSignature: refundSignature ?? new Uint8Array(),
24846
- // TODO: Add direct refund signature
24847
- directRefundSignature: new Uint8Array(),
24848
- directFromCpfpRefundSignature: new Uint8Array()
25347
+ refundSignature: cpfpRefundSignature ?? new Uint8Array(),
25348
+ directRefundSignature: directRefundSignature ?? new Uint8Array(),
25349
+ directFromCpfpRefundSignature: directFromCpfpRefundSignature ?? new Uint8Array()
24849
25350
  });
24850
25351
  }
24851
25352
  return leafTweaksMap;
@@ -24875,7 +25376,12 @@ var TransferService = class extends BaseTransferService {
24875
25376
  * Deprecated in v0.1.32
24876
25377
  */
24877
25378
  async sendTransfer(leaves, receiverIdentityPubkey) {
24878
- const { transfer, signatureMap } = await this.sendTransferSignRefund(
25379
+ const {
25380
+ transfer,
25381
+ signatureMap,
25382
+ directSignatureMap,
25383
+ directFromCpfpSignatureMap
25384
+ } = await this.sendTransferSignRefund(
24879
25385
  leaves,
24880
25386
  receiverIdentityPubkey,
24881
25387
  new Date(Date.now() + DEFAULT_EXPIRY_TIME)
@@ -24883,7 +25389,9 @@ var TransferService = class extends BaseTransferService {
24883
25389
  const transferWithTweakedKeys = await this.sendTransferTweakKey(
24884
25390
  transfer,
24885
25391
  leaves,
24886
- signatureMap
25392
+ signatureMap,
25393
+ directSignatureMap,
25394
+ directFromCpfpSignatureMap
24887
25395
  );
24888
25396
  return transferWithTweakedKeys;
24889
25397
  }
@@ -24987,7 +25495,13 @@ var TransferService = class extends BaseTransferService {
24987
25495
  return transferResp.transfers[0];
24988
25496
  }
24989
25497
  async sendTransferSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
24990
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
25498
+ const {
25499
+ transfer,
25500
+ signatureMap,
25501
+ directSignatureMap,
25502
+ directFromCpfpSignatureMap,
25503
+ leafDataMap
25504
+ } = await this.sendTransferSignRefundInternal(
24991
25505
  leaves,
24992
25506
  receiverIdentityPubkey,
24993
25507
  expiryTime,
@@ -24996,11 +25510,19 @@ var TransferService = class extends BaseTransferService {
24996
25510
  return {
24997
25511
  transfer,
24998
25512
  signatureMap,
25513
+ directSignatureMap,
25514
+ directFromCpfpSignatureMap,
24999
25515
  leafDataMap
25000
25516
  };
25001
25517
  }
25002
25518
  async startSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
25003
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
25519
+ const {
25520
+ transfer,
25521
+ signatureMap,
25522
+ directSignatureMap,
25523
+ directFromCpfpSignatureMap,
25524
+ leafDataMap
25525
+ } = await this.sendTransferSignRefundInternal(
25004
25526
  leaves,
25005
25527
  receiverIdentityPubkey,
25006
25528
  expiryTime,
@@ -25009,31 +25531,45 @@ var TransferService = class extends BaseTransferService {
25009
25531
  return {
25010
25532
  transfer,
25011
25533
  signatureMap,
25534
+ directSignatureMap,
25535
+ directFromCpfpSignatureMap,
25012
25536
  leafDataMap
25013
25537
  };
25014
25538
  }
25015
- async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, adaptorPubKey) {
25539
+ async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
25016
25540
  return this.sendTransferSignRefundInternal(
25017
25541
  leaves,
25018
25542
  receiverIdentityPubkey,
25019
25543
  expiryTime,
25020
25544
  true,
25021
- adaptorPubKey
25545
+ cpfpAdaptorPubKey,
25546
+ directAdaptorPubKey,
25547
+ directFromCpfpAdaptorPubKey
25022
25548
  );
25023
25549
  }
25024
- async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, adaptorPubKey) {
25550
+ async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
25025
25551
  const transferId = (0, import_uuidv72.uuidv7)();
25026
25552
  const leafDataMap = /* @__PURE__ */ new Map();
25027
25553
  for (const leaf of leaves) {
25028
25554
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25555
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25556
+ const directFromCpfpRefundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25029
25557
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
25030
25558
  const refundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
25559
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
25560
+ const directRefundTx = leaf.leaf.directRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directRefundTx) : void 0;
25561
+ const directFromCpfpRefundTx = leaf.leaf.directFromCpfpRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directFromCpfpRefundTx) : void 0;
25031
25562
  leafDataMap.set(leaf.leaf.id, {
25032
25563
  keyDerivation: leaf.keyDerivation,
25033
25564
  receivingPubkey: receiverIdentityPubkey,
25034
25565
  signingNonceCommitment,
25566
+ directSigningNonceCommitment,
25035
25567
  tx,
25568
+ directTx,
25036
25569
  refundTx,
25570
+ directRefundTx,
25571
+ directFromCpfpRefundTx,
25572
+ directFromCpfpRefundSigningNonceCommitment,
25037
25573
  vout: leaf.leaf.vout
25038
25574
  });
25039
25575
  }
@@ -25046,8 +25582,8 @@ var TransferService = class extends BaseTransferService {
25046
25582
  );
25047
25583
  let response;
25048
25584
  try {
25049
- if (adaptorPubKey !== void 0) {
25050
- response = await sparkClient.counter_leaf_swap({
25585
+ if (cpfpAdaptorPubKey !== void 0 || directAdaptorPubKey !== void 0 || directFromCpfpAdaptorPubKey !== void 0) {
25586
+ response = await sparkClient.counter_leaf_swap_v2({
25051
25587
  transfer: {
25052
25588
  transferId,
25053
25589
  leavesToSend: signingJobs,
@@ -25056,10 +25592,12 @@ var TransferService = class extends BaseTransferService {
25056
25592
  expiryTime
25057
25593
  },
25058
25594
  swapId: (0, import_uuidv72.uuidv7)(),
25059
- adaptorPublicKey: adaptorPubKey || new Uint8Array()
25595
+ adaptorPublicKey: cpfpAdaptorPubKey,
25596
+ directAdaptorPublicKey: directAdaptorPubKey,
25597
+ directFromCpfpAdaptorPublicKey: directFromCpfpAdaptorPubKey
25060
25598
  });
25061
25599
  } else if (forSwap) {
25062
- response = await sparkClient.start_leaf_swap({
25600
+ response = await sparkClient.start_leaf_swap_v2({
25063
25601
  transferId,
25064
25602
  leavesToSend: signingJobs,
25065
25603
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -25067,7 +25605,7 @@ var TransferService = class extends BaseTransferService {
25067
25605
  expiryTime
25068
25606
  });
25069
25607
  } else {
25070
- response = await sparkClient.start_transfer({
25608
+ response = await sparkClient.start_transfer_v2({
25071
25609
  transferId,
25072
25610
  leavesToSend: signingJobs,
25073
25611
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -25084,15 +25622,29 @@ var TransferService = class extends BaseTransferService {
25084
25622
  const signatures = await this.signRefunds(
25085
25623
  leafDataMap,
25086
25624
  response.signingResults,
25087
- adaptorPubKey
25625
+ cpfpAdaptorPubKey,
25626
+ directAdaptorPubKey,
25627
+ directFromCpfpAdaptorPubKey
25088
25628
  );
25089
- const signatureMap = /* @__PURE__ */ new Map();
25629
+ const cpfpSignatureMap = /* @__PURE__ */ new Map();
25630
+ const directSignatureMap = /* @__PURE__ */ new Map();
25631
+ const directFromCpfpSignatureMap = /* @__PURE__ */ new Map();
25090
25632
  for (const signature of signatures) {
25091
- signatureMap.set(signature.nodeId, signature.refundTxSignature);
25633
+ cpfpSignatureMap.set(signature.nodeId, signature.refundTxSignature);
25634
+ directSignatureMap.set(
25635
+ signature.nodeId,
25636
+ signature.directRefundTxSignature
25637
+ );
25638
+ directFromCpfpSignatureMap.set(
25639
+ signature.nodeId,
25640
+ signature.directFromCpfpRefundTxSignature
25641
+ );
25092
25642
  }
25093
25643
  return {
25094
25644
  transfer: response.transfer,
25095
- signatureMap,
25645
+ signatureMap: cpfpSignatureMap,
25646
+ directSignatureMap,
25647
+ directFromCpfpSignatureMap,
25096
25648
  leafDataMap,
25097
25649
  signingResults: response.signingResults
25098
25650
  };
@@ -25105,38 +25657,68 @@ var TransferService = class extends BaseTransferService {
25105
25657
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
25106
25658
  }
25107
25659
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
25108
- const nodeOutPoint = {
25660
+ const cpfpNodeOutPoint = {
25109
25661
  txid: (0, import_utils14.hexToBytes)(getTxId(nodeTx)),
25110
25662
  index: 0
25111
25663
  };
25664
+ let directNodeTx;
25665
+ let directNodeOutPoint;
25666
+ if (leaf.leaf.directTx.length > 0) {
25667
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
25668
+ directNodeOutPoint = {
25669
+ txid: (0, import_utils14.hexToBytes)(getTxId(directNodeTx)),
25670
+ index: 0
25671
+ };
25672
+ }
25112
25673
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
25113
- const nextSequence = isForClaim ? getTransactionSequence(currRefundTx.getInput(0).sequence) : getNextTransactionSequence(currRefundTx.getInput(0).sequence).nextSequence;
25674
+ const sequence = currRefundTx.getInput(0).sequence;
25675
+ if (!sequence) {
25676
+ throw new ValidationError("Invalid refund transaction", {
25677
+ field: "sequence",
25678
+ value: currRefundTx.getInput(0),
25679
+ expected: "Non-null sequence"
25680
+ });
25681
+ }
25682
+ const { nextSequence, nextDirectSequence } = isForClaim ? getTransactionSequence(sequence) : getNextTransactionSequence(sequence);
25114
25683
  const amountSats = currRefundTx.getOutput(0).amount;
25115
25684
  if (amountSats === void 0) {
25116
25685
  throw new Error("Amount not found in signRefunds");
25117
25686
  }
25118
- const refundTx = createRefundTx(
25119
- nextSequence,
25120
- nodeOutPoint,
25687
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
25688
+ sequence: nextSequence,
25689
+ directSequence: nextDirectSequence,
25690
+ input: cpfpNodeOutPoint,
25691
+ directInput: directNodeOutPoint,
25121
25692
  amountSats,
25122
- refundSigningData.receivingPubkey,
25123
- this.config.getNetwork()
25693
+ receivingPubkey: refundSigningData.receivingPubkey,
25694
+ network: this.config.getNetwork()
25695
+ });
25696
+ refundSigningData.refundTx = cpfpRefundTx;
25697
+ refundSigningData.directRefundTx = directRefundTx;
25698
+ refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
25699
+ const cpfpRefundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
25700
+ const directRefundNonceCommitmentProto = refundSigningData.directSigningNonceCommitment;
25701
+ const directFromCpfpRefundNonceCommitmentProto = refundSigningData.directFromCpfpRefundSigningNonceCommitment;
25702
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(
25703
+ refundSigningData.keyDerivation
25124
25704
  );
25125
- refundSigningData.refundTx = refundTx;
25126
- const refundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
25127
25705
  signingJobs.push({
25128
25706
  leafId: leaf.leaf.id,
25129
25707
  refundTxSigningJob: {
25130
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
25131
- refundSigningData.keyDerivation
25132
- ),
25133
- rawTx: refundTx.toBytes(),
25134
- signingNonceCommitment: refundNonceCommitmentProto.commitment
25708
+ signingPublicKey,
25709
+ rawTx: cpfpRefundTx.toBytes(),
25710
+ signingNonceCommitment: cpfpRefundNonceCommitmentProto.commitment
25135
25711
  },
25136
- // TODO: Add direct refund signature
25137
- directRefundTxSigningJob: void 0,
25138
- // TODO: Add direct refund signature
25139
- directFromCpfpRefundTxSigningJob: void 0
25712
+ directRefundTxSigningJob: directRefundTx ? {
25713
+ signingPublicKey,
25714
+ rawTx: directRefundTx.toBytes(),
25715
+ signingNonceCommitment: directRefundNonceCommitmentProto.commitment
25716
+ } : void 0,
25717
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
25718
+ signingPublicKey,
25719
+ rawTx: directFromCpfpRefundTx.toBytes(),
25720
+ signingNonceCommitment: directFromCpfpRefundNonceCommitmentProto.commitment
25721
+ } : void 0
25140
25722
  });
25141
25723
  }
25142
25724
  return signingJobs;
@@ -25257,13 +25839,17 @@ var TransferService = class extends BaseTransferService {
25257
25839
  const leafDataMap = /* @__PURE__ */ new Map();
25258
25840
  for (const leafKey of leafKeys) {
25259
25841
  const tx = getTxFromRawTxBytes(leafKey.leaf.nodeTx);
25842
+ const directTx = leafKey.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leafKey.leaf.directTx) : void 0;
25260
25843
  leafDataMap.set(leafKey.leaf.id, {
25261
25844
  keyDerivation: leafKey.newKeyDerivation,
25262
25845
  receivingPubkey: await this.config.signer.getPublicKeyFromDerivation(
25263
25846
  leafKey.newKeyDerivation
25264
25847
  ),
25265
25848
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25849
+ directSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25850
+ directFromCpfpRefundSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25266
25851
  tx,
25852
+ directTx,
25267
25853
  vout: leafKey.leaf.vout
25268
25854
  });
25269
25855
  }
@@ -25285,7 +25871,7 @@ var TransferService = class extends BaseTransferService {
25285
25871
  }
25286
25872
  }
25287
25873
  try {
25288
- resp = await sparkClient.claim_transfer_sign_refunds({
25874
+ resp = await sparkClient.claim_transfer_sign_refunds_v2({
25289
25875
  transferId: transfer.id,
25290
25876
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25291
25877
  signingJobs
@@ -25300,7 +25886,7 @@ var TransferService = class extends BaseTransferService {
25300
25886
  this.config.getCoordinatorAddress()
25301
25887
  );
25302
25888
  try {
25303
- return await sparkClient.finalize_node_signatures({
25889
+ return await sparkClient.finalize_node_signatures_v2({
25304
25890
  intent: 1 /* TRANSFER */,
25305
25891
  nodeSignatures
25306
25892
  });
@@ -25339,109 +25925,134 @@ var TransferService = class extends BaseTransferService {
25339
25925
  throw new Error(`Error querying pending transfers by sender: ${error}`);
25340
25926
  }
25341
25927
  }
25342
- async refreshTimelockNodes(nodes, parentNode) {
25343
- if (nodes.length === 0) {
25344
- throw Error("no nodes to refresh");
25345
- }
25928
+ async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
25346
25929
  const signingJobs = [];
25347
- const newNodeTxs = [];
25348
- for (let i = 0; i < nodes.length; i++) {
25349
- const node = nodes[i];
25350
- if (!node) {
25351
- throw Error("could not get node");
25352
- }
25353
- const nodeTx = getTxFromRawTxBytes(node?.nodeTx);
25354
- const input = nodeTx.getInput(0);
25355
- if (!input) {
25356
- throw Error("Could not fetch tx input");
25357
- }
25358
- const newTx = new import_btc_signer2.Transaction({ version: 3, allowUnknownOutputs: true });
25359
- const originalOutput = nodeTx.getOutput(0);
25360
- if (!originalOutput) {
25361
- throw Error("Could not get original output");
25362
- }
25363
- newTx.addOutput({
25364
- script: originalOutput.script,
25365
- amount: originalOutput.amount
25366
- });
25367
- for (let j = 1; j < nodeTx.outputsLength; j++) {
25368
- const additionalOutput = nodeTx.getOutput(j);
25369
- if (additionalOutput) {
25370
- newTx.addOutput(additionalOutput);
25371
- }
25372
- }
25373
- if (i === 0) {
25374
- const currSequence = input.sequence;
25375
- newTx.addInput({
25376
- ...input,
25377
- sequence: getNextTransactionSequence(currSequence).nextSequence
25378
- });
25379
- } else {
25380
- newTx.addInput({
25381
- ...input,
25382
- sequence: initialSequence(),
25383
- txid: newNodeTxs[i - 1]?.id
25384
- });
25385
- }
25386
- signingJobs.push({
25387
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
25388
- type: "leaf" /* LEAF */,
25389
- path: node.id
25390
- }),
25391
- rawTx: newTx.toBytes(),
25392
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25930
+ const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
25931
+ const parentNodeOutput = parentNodeTx.getOutput(0);
25932
+ if (!parentNodeOutput) {
25933
+ throw Error("Could not get parent node output");
25934
+ }
25935
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
25936
+ const nodeInput = nodeTx.getInput(0);
25937
+ const nodeOutput = nodeTx.getOutput(0);
25938
+ if (!nodeOutput) {
25939
+ throw Error("Could not get node output");
25940
+ }
25941
+ let directNodeTx;
25942
+ let directNodeInput;
25943
+ if (node.directTx.length > 0) {
25944
+ directNodeTx = getTxFromRawTxBytes(node.directTx);
25945
+ directNodeInput = directNodeTx.getInput(0);
25946
+ }
25947
+ const currSequence = nodeInput.sequence;
25948
+ if (!currSequence) {
25949
+ throw new ValidationError("Invalid node transaction", {
25950
+ field: "sequence",
25951
+ value: nodeInput,
25952
+ expected: "Non-null sequence"
25393
25953
  });
25394
- newNodeTxs[i] = newTx;
25395
25954
  }
25396
- const leaf = nodes[nodes.length - 1];
25397
- if (!leaf?.refundTx) {
25398
- throw Error("leaf does not have refund tx");
25955
+ let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
25956
+ currSequence,
25957
+ true
25958
+ );
25959
+ const output = {
25960
+ script: parentNodeOutput.script,
25961
+ amount: parentNodeOutput.amount
25962
+ };
25963
+ const newNodeInput = {
25964
+ txid: nodeInput.txid,
25965
+ index: nodeInput.index,
25966
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
25967
+ };
25968
+ const newDirectInput = directNodeTx && directNodeInput ? {
25969
+ txid: directNodeInput.txid,
25970
+ index: directNodeInput.index,
25971
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
25972
+ } : void 0;
25973
+ const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
25974
+ output,
25975
+ newNodeInput,
25976
+ newDirectInput
25977
+ );
25978
+ const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
25979
+ if (!newCpfpNodeOutput) {
25980
+ throw Error("Could not get new cpfp node output");
25399
25981
  }
25400
- const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
25401
- const newRefundTx = new import_btc_signer2.Transaction({
25402
- version: 3,
25403
- allowUnknownOutputs: true
25982
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
25983
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
25984
+ type: "leaf" /* LEAF */,
25985
+ path: node.id
25404
25986
  });
25405
- const originalRefundOutput = refundTx.getOutput(0);
25406
- if (!originalRefundOutput) {
25407
- throw Error("Could not get original refund output");
25408
- }
25409
- newRefundTx.addOutput({
25410
- script: originalRefundOutput.script,
25411
- amount: originalRefundOutput.amount
25987
+ signingJobs.push({
25988
+ signingPublicKey,
25989
+ rawTx: cpfpNodeTx.toBytes(),
25990
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25991
+ type: "node",
25992
+ parentTxOut: parentNodeOutput
25412
25993
  });
25413
- for (let j = 1; j < refundTx.outputsLength; j++) {
25414
- const additionalOutput = refundTx.getOutput(j);
25415
- if (additionalOutput) {
25416
- newRefundTx.addOutput(additionalOutput);
25417
- }
25418
- }
25419
- const refundTxInput = refundTx.getInput(0);
25420
- if (!refundTxInput) {
25421
- throw Error("refund tx doesn't have input");
25994
+ if (newDirectNodeTx) {
25995
+ signingJobs.push({
25996
+ signingPublicKey,
25997
+ rawTx: newDirectNodeTx.toBytes(),
25998
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25999
+ type: "directNode",
26000
+ parentTxOut: parentNodeOutput
26001
+ });
25422
26002
  }
25423
- if (!newNodeTxs[newNodeTxs.length - 1]) {
25424
- throw Error("Could not get last node tx");
26003
+ const newCpfpRefundOutPoint = {
26004
+ txid: (0, import_utils14.hexToBytes)(getTxId(cpfpNodeTx)),
26005
+ index: 0
26006
+ };
26007
+ let newDirectRefundOutPoint;
26008
+ if (newDirectNodeTx) {
26009
+ newDirectRefundOutPoint = {
26010
+ txid: (0, import_utils14.hexToBytes)(getTxId(newDirectNodeTx)),
26011
+ index: 0
26012
+ };
25425
26013
  }
25426
- newRefundTx.addInput({
25427
- ...refundTxInput,
25428
- sequence: initialSequence(),
25429
- txid: getTxId(newNodeTxs[newNodeTxs.length - 1])
25430
- });
25431
- const refundSigningJob = {
25432
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
26014
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
26015
+ sequence: INITIAL_SEQUENCE,
26016
+ directSequence: INITIAL_DIRECT_SEQUENCE,
26017
+ input: newCpfpRefundOutPoint,
26018
+ directInput: newDirectRefundOutPoint,
26019
+ amountSats: nodeOutput.amount,
26020
+ receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
25433
26021
  type: "leaf" /* LEAF */,
25434
- path: leaf.id
26022
+ path: node.id
25435
26023
  }),
25436
- rawTx: newRefundTx.toBytes(),
25437
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25438
- };
25439
- signingJobs.push(refundSigningJob);
26024
+ network: this.config.getNetwork()
26025
+ });
26026
+ signingJobs.push({
26027
+ signingPublicKey,
26028
+ rawTx: cpfpRefundTx.toBytes(),
26029
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26030
+ type: "cpfp",
26031
+ parentTxOut: newCpfpNodeOutput
26032
+ });
26033
+ if (directRefundTx && newDirectNodeOutput) {
26034
+ signingJobs.push({
26035
+ signingPublicKey,
26036
+ rawTx: directRefundTx.toBytes(),
26037
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26038
+ type: "direct",
26039
+ parentTxOut: newDirectNodeOutput
26040
+ });
26041
+ }
26042
+ if (directFromCpfpRefundTx && newCpfpNodeOutput) {
26043
+ signingJobs.push({
26044
+ signingPublicKey,
26045
+ rawTx: directFromCpfpRefundTx.toBytes(),
26046
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26047
+ type: "directFromCpfp",
26048
+ parentTxOut: newCpfpNodeOutput
26049
+ });
26050
+ }
25440
26051
  const sparkClient = await this.connectionManager.createSparkClient(
25441
26052
  this.config.getCoordinatorAddress()
25442
26053
  );
25443
- const response = await sparkClient.refresh_timelock({
25444
- leafId: leaf.id,
26054
+ const response = await sparkClient.refresh_timelock_v2({
26055
+ leafId: node.id,
25445
26056
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25446
26057
  signingJobs: signingJobs.map(getSigningJobProto)
25447
26058
  });
@@ -25451,45 +26062,27 @@ var TransferService = class extends BaseTransferService {
25451
26062
  );
25452
26063
  }
25453
26064
  let nodeSignatures = [];
25454
- let leafSignature;
25455
- let refundSignature;
25456
- let leafNodeId;
25457
- for (let i = 0; i < response.signingResults.length; i++) {
25458
- const signingResult = response.signingResults[i];
26065
+ let leafCpfpSignature;
26066
+ let leafDirectSignature;
26067
+ let cpfpRefundSignature;
26068
+ let directRefundSignature;
26069
+ let directFromCpfpRefundSignature;
26070
+ for (const [i, signingResult] of response.signingResults.entries()) {
25459
26071
  const signingJob = signingJobs[i];
25460
26072
  if (!signingJob || !signingResult) {
25461
26073
  throw Error("Signing job does not exist");
25462
26074
  }
25463
- if (!signingJob.signingNonceCommitment) {
25464
- throw Error("nonce commitment does not exist");
25465
- }
25466
26075
  const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
25467
- let parentTx;
25468
- let nodeId;
25469
- let vout;
25470
- if (i === nodes.length) {
25471
- nodeId = nodes[i - 1]?.id;
25472
- parentTx = newNodeTxs[i - 1];
25473
- vout = 0;
25474
- } else if (i === 0) {
25475
- nodeId = nodes[i]?.id;
25476
- parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
25477
- vout = nodes[i]?.vout;
25478
- } else {
25479
- nodeId = nodes[i]?.id;
25480
- parentTx = newNodeTxs[i - 1];
25481
- vout = nodes[i]?.vout;
25482
- }
25483
- if (!parentTx || !nodeId || vout === void 0) {
25484
- throw Error("Could not parse signing results");
26076
+ const txOut = signingJob.parentTxOut;
26077
+ if (!txOut) {
26078
+ throw Error("Could not get tx out");
25485
26079
  }
25486
- const txOut = parentTx.getOutput(vout);
25487
26080
  const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
25488
26081
  const userSignature = await this.config.signer.signFrost({
25489
26082
  message: rawTxSighash,
25490
26083
  keyDerivation: {
25491
26084
  type: "leaf" /* LEAF */,
25492
- path: nodeId
26085
+ path: node.id
25493
26086
  },
25494
26087
  publicKey: signingJob.signingPublicKey,
25495
26088
  verifyingKey: signingResult.verifyingKey,
@@ -25508,41 +26101,35 @@ var TransferService = class extends BaseTransferService {
25508
26101
  selfSignature: userSignature,
25509
26102
  adaptorPubKey: new Uint8Array()
25510
26103
  });
25511
- if (i !== nodes.length && i !== nodes.length - 1) {
25512
- nodeSignatures.push({
25513
- nodeId,
25514
- nodeTxSignature: signature,
25515
- refundTxSignature: new Uint8Array(),
25516
- // TODO: Add direct refund signature
25517
- directNodeTxSignature: new Uint8Array(),
25518
- directRefundTxSignature: new Uint8Array(),
25519
- directFromCpfpRefundTxSignature: new Uint8Array()
25520
- });
25521
- } else if (i === nodes.length) {
25522
- refundSignature = signature;
25523
- } else if (i === nodes.length - 1) {
25524
- leafNodeId = nodeId;
25525
- leafSignature = signature;
26104
+ if (signingJob.type === "node") {
26105
+ leafCpfpSignature = signature;
26106
+ } else if (signingJob.type === "directNode") {
26107
+ leafDirectSignature = signature;
26108
+ } else if (signingJob.type === "cpfp") {
26109
+ cpfpRefundSignature = signature;
26110
+ } else if (signingJob.type === "direct") {
26111
+ directRefundSignature = signature;
26112
+ } else if (signingJob.type === "directFromCpfp") {
26113
+ directFromCpfpRefundSignature = signature;
25526
26114
  }
25527
26115
  }
25528
- if (!leafSignature || !refundSignature || !leafNodeId) {
25529
- throw Error("leaf or refund signature does not exist");
25530
- }
25531
26116
  nodeSignatures.push({
25532
- nodeId: leafNodeId,
25533
- nodeTxSignature: leafSignature,
25534
- refundTxSignature: refundSignature,
25535
- // TODO: Add direct refund signature
25536
- directNodeTxSignature: new Uint8Array(),
25537
- directRefundTxSignature: new Uint8Array(),
25538
- directFromCpfpRefundTxSignature: new Uint8Array()
26117
+ nodeId: node.id,
26118
+ nodeTxSignature: leafCpfpSignature || new Uint8Array(),
26119
+ directNodeTxSignature: leafDirectSignature || new Uint8Array(),
26120
+ refundTxSignature: cpfpRefundSignature || new Uint8Array(),
26121
+ directRefundTxSignature: directRefundSignature || new Uint8Array(),
26122
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
25539
26123
  });
25540
- const result = await sparkClient.finalize_node_signatures({
26124
+ const result = await sparkClient.finalize_node_signatures_v2({
25541
26125
  intent: 3 /* REFRESH */,
25542
26126
  nodeSignatures
25543
26127
  });
25544
26128
  return result;
25545
26129
  }
26130
+ async refreshTimelockNodes(node, parentNode) {
26131
+ return await this.refreshTimelockNodesInternal(node, parentNode);
26132
+ }
25546
26133
  async extendTimelock(node) {
25547
26134
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
25548
26135
  const refundTx = getTxFromRawTxBytes(node.refundTx);
@@ -25551,7 +26138,10 @@ var TransferService = class extends BaseTransferService {
25551
26138
  txid: (0, import_utils14.hexToBytes)(getTxId(nodeTx)),
25552
26139
  index: 0
25553
26140
  };
25554
- const { nextSequence: newNodeSequence } = getNextTransactionSequence(refundSequence);
26141
+ const {
26142
+ nextSequence: newNodeSequence,
26143
+ nextDirectSequence: newDirectNodeSequence
26144
+ } = getNextTransactionSequence(refundSequence);
25555
26145
  const newNodeTx = new import_btc_signer2.Transaction({
25556
26146
  version: 3,
25557
26147
  allowUnknownOutputs: true
@@ -25564,81 +26154,122 @@ var TransferService = class extends BaseTransferService {
25564
26154
  newNodeTx.addOutput({
25565
26155
  script: originalOutput.script,
25566
26156
  amount: originalOutput.amount
25567
- // feeReducedAmount,
25568
26157
  });
25569
26158
  newNodeTx.addOutput(getEphemeralAnchorOutput());
25570
- const newRefundOutPoint = {
26159
+ let newDirectNodeTx;
26160
+ if (node.directTx.length > 0) {
26161
+ newDirectNodeTx = new import_btc_signer2.Transaction({
26162
+ version: 3,
26163
+ allowUnknownOutputs: true
26164
+ });
26165
+ newDirectNodeTx.addInput({
26166
+ ...newNodeOutPoint,
26167
+ sequence: newDirectNodeSequence
26168
+ });
26169
+ newDirectNodeTx.addOutput({
26170
+ script: originalOutput.script,
26171
+ amount: maybeApplyFee(originalOutput.amount)
26172
+ });
26173
+ }
26174
+ const newCpfpRefundOutPoint = {
25571
26175
  txid: (0, import_utils14.hexToBytes)(getTxId(newNodeTx)),
25572
26176
  index: 0
25573
26177
  };
26178
+ let newDirectRefundOutPoint;
26179
+ if (newDirectNodeTx) {
26180
+ newDirectRefundOutPoint = {
26181
+ txid: (0, import_utils14.hexToBytes)(getTxId(newDirectNodeTx)),
26182
+ index: 0
26183
+ };
26184
+ }
25574
26185
  const amountSats = refundTx.getOutput(0).amount;
25575
26186
  if (amountSats === void 0) {
25576
26187
  throw new Error("Amount not found in extendTimelock");
25577
26188
  }
25578
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
26189
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
25579
26190
  type: "leaf" /* LEAF */,
25580
26191
  path: node.id
25581
26192
  });
25582
- const newRefundTx = createRefundTx(
25583
- initialSequence(),
25584
- newRefundOutPoint,
26193
+ const {
26194
+ cpfpRefundTx: newCpfpRefundTx,
26195
+ directRefundTx: newDirectRefundTx,
26196
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
26197
+ } = createRefundTxs({
26198
+ sequence: INITIAL_SEQUENCE,
26199
+ directSequence: INITIAL_DIRECT_SEQUENCE,
26200
+ input: newCpfpRefundOutPoint,
26201
+ directInput: newDirectRefundOutPoint,
25585
26202
  amountSats,
25586
- // feeReducedRefundAmount,
25587
- signingPubKey,
25588
- this.config.getNetwork()
25589
- );
26203
+ receivingPubkey: signingPublicKey,
26204
+ network: this.config.getNetwork()
26205
+ });
26206
+ if (!newCpfpRefundTx) {
26207
+ throw new ValidationError(
26208
+ "Failed to create refund transactions in extendTimelock"
26209
+ );
26210
+ }
25590
26211
  const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
25591
- const refundSighash = getSigHashFromTx(
25592
- newRefundTx,
26212
+ const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
26213
+ const cpfpRefundSighash = getSigHashFromTx(
26214
+ newCpfpRefundTx,
25593
26215
  0,
25594
26216
  newNodeTx.getOutput(0)
25595
26217
  );
26218
+ const directRefundSighash = newDirectNodeTx && newDirectRefundTx ? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0)) : void 0;
26219
+ const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx ? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0)) : void 0;
25596
26220
  const newNodeSigningJob = {
25597
- signingPublicKey: signingPubKey,
26221
+ signingPublicKey,
25598
26222
  rawTx: newNodeTx.toBytes(),
25599
26223
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25600
26224
  };
25601
- const newRefundSigningJob = {
25602
- signingPublicKey: signingPubKey,
25603
- rawTx: newRefundTx.toBytes(),
26225
+ const newDirectNodeSigningJob = newDirectNodeTx ? {
26226
+ signingPublicKey,
26227
+ rawTx: newDirectNodeTx.toBytes(),
26228
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
26229
+ } : void 0;
26230
+ const newCpfpRefundSigningJob = {
26231
+ signingPublicKey,
26232
+ rawTx: newCpfpRefundTx.toBytes(),
25604
26233
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25605
26234
  };
26235
+ const newDirectRefundSigningJob = newDirectRefundTx ? {
26236
+ signingPublicKey,
26237
+ rawTx: newDirectRefundTx.toBytes(),
26238
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
26239
+ } : void 0;
26240
+ const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
26241
+ signingPublicKey,
26242
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
26243
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
26244
+ } : void 0;
25606
26245
  const sparkClient = await this.connectionManager.createSparkClient(
25607
26246
  this.config.getCoordinatorAddress()
25608
26247
  );
25609
- const response = await sparkClient.extend_leaf({
26248
+ const response = await sparkClient.extend_leaf_v2({
25610
26249
  leafId: node.id,
25611
26250
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25612
26251
  nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
25613
- refundTxSigningJob: getSigningJobProto(newRefundSigningJob)
26252
+ directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
26253
+ refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
26254
+ directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
26255
+ directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
25614
26256
  });
25615
26257
  if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
25616
26258
  throw new Error("Signing result does not exist");
25617
26259
  }
26260
+ const keyDerivation = {
26261
+ type: "leaf" /* LEAF */,
26262
+ path: node.id
26263
+ };
25618
26264
  const nodeUserSig = await this.config.signer.signFrost({
25619
26265
  message: nodeSighash,
25620
- keyDerivation: {
25621
- type: "leaf" /* LEAF */,
25622
- path: node.id
25623
- },
25624
- publicKey: signingPubKey,
26266
+ keyDerivation,
26267
+ publicKey: signingPublicKey,
25625
26268
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
25626
26269
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
25627
26270
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
25628
26271
  adaptorPubKey: new Uint8Array()
25629
26272
  });
25630
- const refundUserSig = await this.config.signer.signFrost({
25631
- message: refundSighash,
25632
- keyDerivation: {
25633
- type: "leaf" /* LEAF */,
25634
- path: node.id
25635
- },
25636
- publicKey: signingPubKey,
25637
- verifyingKey: response.refundTxSigningResult.verifyingKey,
25638
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
25639
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
25640
- adaptorPubKey: new Uint8Array()
25641
- });
25642
26273
  const nodeSig = await this.config.signer.aggregateFrost({
25643
26274
  message: nodeSighash,
25644
26275
  statechainSignatures: response.nodeTxSigningResult.signingResult?.signatureShares,
@@ -25646,122 +26277,253 @@ var TransferService = class extends BaseTransferService {
25646
26277
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
25647
26278
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
25648
26279
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
25649
- publicKey: signingPubKey,
26280
+ publicKey: signingPublicKey,
25650
26281
  selfSignature: nodeUserSig,
25651
26282
  adaptorPubKey: new Uint8Array()
25652
26283
  });
25653
- const refundSig = await this.config.signer.aggregateFrost({
25654
- message: refundSighash,
26284
+ let directNodeSig;
26285
+ if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
26286
+ const directNodeUserSig = await this.config.signer.signFrost({
26287
+ message: directNodeSighash,
26288
+ keyDerivation,
26289
+ publicKey: signingPublicKey,
26290
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
26291
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
26292
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
26293
+ adaptorPubKey: new Uint8Array()
26294
+ });
26295
+ directNodeSig = await this.config.signer.aggregateFrost({
26296
+ message: directNodeSighash,
26297
+ statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
26298
+ statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
26299
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
26300
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
26301
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
26302
+ publicKey: signingPublicKey,
26303
+ selfSignature: directNodeUserSig,
26304
+ adaptorPubKey: new Uint8Array()
26305
+ });
26306
+ }
26307
+ const cpfpRefundUserSig = await this.config.signer.signFrost({
26308
+ message: cpfpRefundSighash,
26309
+ keyDerivation,
26310
+ publicKey: signingPublicKey,
26311
+ verifyingKey: response.refundTxSigningResult.verifyingKey,
26312
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
26313
+ statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
26314
+ adaptorPubKey: new Uint8Array()
26315
+ });
26316
+ const cpfpRefundSig = await this.config.signer.aggregateFrost({
26317
+ message: cpfpRefundSighash,
25655
26318
  statechainSignatures: response.refundTxSigningResult.signingResult?.signatureShares,
25656
26319
  statechainPublicKeys: response.refundTxSigningResult.signingResult?.publicKeys,
25657
26320
  verifyingKey: response.refundTxSigningResult.verifyingKey,
25658
26321
  statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
25659
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
25660
- publicKey: signingPubKey,
25661
- selfSignature: refundUserSig,
26322
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
26323
+ publicKey: signingPublicKey,
26324
+ selfSignature: cpfpRefundUserSig,
25662
26325
  adaptorPubKey: new Uint8Array()
25663
26326
  });
25664
- return await sparkClient.finalize_node_signatures({
26327
+ let directRefundSig;
26328
+ if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
26329
+ const directRefundUserSig = await this.config.signer.signFrost({
26330
+ message: directRefundSighash,
26331
+ keyDerivation,
26332
+ publicKey: signingPublicKey,
26333
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
26334
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
26335
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
26336
+ adaptorPubKey: new Uint8Array()
26337
+ });
26338
+ directRefundSig = await this.config.signer.aggregateFrost({
26339
+ message: directRefundSighash,
26340
+ statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
26341
+ statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
26342
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
26343
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
26344
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
26345
+ publicKey: signingPublicKey,
26346
+ selfSignature: directRefundUserSig,
26347
+ adaptorPubKey: new Uint8Array()
26348
+ });
26349
+ }
26350
+ let directFromCpfpRefundSig;
26351
+ if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
26352
+ const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
26353
+ message: directFromCpfpRefundSighash,
26354
+ keyDerivation,
26355
+ publicKey: signingPublicKey,
26356
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
26357
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
26358
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
26359
+ adaptorPubKey: new Uint8Array()
26360
+ });
26361
+ directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
26362
+ message: directFromCpfpRefundSighash,
26363
+ statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
26364
+ statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
26365
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
26366
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
26367
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
26368
+ publicKey: signingPublicKey,
26369
+ selfSignature: directFromCpfpRefundUserSig,
26370
+ adaptorPubKey: new Uint8Array()
26371
+ });
26372
+ }
26373
+ return await sparkClient.finalize_node_signatures_v2({
25665
26374
  intent: 4 /* EXTEND */,
25666
26375
  nodeSignatures: [
25667
26376
  {
25668
26377
  nodeId: response.leafId,
25669
26378
  nodeTxSignature: nodeSig,
25670
- refundTxSignature: refundSig
26379
+ directNodeTxSignature: directNodeSig,
26380
+ refundTxSignature: cpfpRefundSig,
26381
+ directRefundTxSignature: directRefundSig,
26382
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSig
25671
26383
  }
25672
26384
  ]
25673
26385
  });
25674
26386
  }
25675
- async refreshTimelockRefundTx(node) {
26387
+ async testonly_expireTimeLockNodeTx(node, parentNode) {
26388
+ return await this.refreshTimelockNodesInternal(node, parentNode, true);
26389
+ }
26390
+ async testonly_expireTimeLockRefundtx(node) {
25676
26391
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
25677
- const refundTx = getTxFromRawTxBytes(node.refundTx);
25678
- const currSequence = refundTx.getInput(0).sequence || 0;
25679
- const { nextSequence } = getNextTransactionSequence(currSequence);
25680
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
26392
+ const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
26393
+ const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
26394
+ const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
26395
+ const currTimelock = getCurrentTimelock(currSequence);
26396
+ if (currTimelock <= 100) {
26397
+ throw new ValidationError("Cannot expire timelock below 100", {
26398
+ field: "currTimelock",
26399
+ value: currTimelock,
26400
+ expected: "Timelock greater than 100"
26401
+ });
26402
+ }
26403
+ const nextSequence = TEST_UNILATERAL_SEQUENCE;
26404
+ const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
26405
+ const nodeOutput = nodeTx.getOutput(0);
26406
+ if (!nodeOutput) {
26407
+ throw Error("Could not get node output");
26408
+ }
26409
+ const keyDerivation = {
25681
26410
  type: "leaf" /* LEAF */,
25682
26411
  path: node.id
25683
- });
25684
- const newRefundTx = new import_btc_signer2.Transaction({
25685
- version: 3,
25686
- allowUnknownOutputs: true
25687
- });
25688
- const originalRefundOutput = refundTx.getOutput(0);
25689
- if (!originalRefundOutput) {
25690
- throw Error("Could not get original refund output");
26412
+ };
26413
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
26414
+ const cpfpRefundOutPoint = {
26415
+ txid: (0, import_utils14.hexToBytes)(getTxId(nodeTx)),
26416
+ index: 0
26417
+ };
26418
+ let directRefundOutPoint;
26419
+ if (directNodeTx) {
26420
+ directRefundOutPoint = {
26421
+ txid: (0, import_utils14.hexToBytes)(getTxId(directNodeTx)),
26422
+ index: 0
26423
+ };
25691
26424
  }
25692
- newRefundTx.addOutput({
25693
- script: originalRefundOutput.script,
25694
- amount: originalRefundOutput.amount
26425
+ const {
26426
+ cpfpRefundTx: newCpfpRefundTx,
26427
+ directRefundTx: newDirectRefundTx,
26428
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
26429
+ } = createRefundTxs({
26430
+ sequence: nextSequence,
26431
+ directSequence: nextDirectSequence,
26432
+ input: cpfpRefundOutPoint,
26433
+ directInput: directRefundOutPoint,
26434
+ amountSats: nodeOutput.amount,
26435
+ receivingPubkey: signingPublicKey,
26436
+ network: this.config.getNetwork()
25695
26437
  });
25696
- for (let j = 1; j < refundTx.outputsLength; j++) {
25697
- const additionalOutput = refundTx.getOutput(j);
25698
- if (additionalOutput) {
25699
- newRefundTx.addOutput(additionalOutput);
25700
- }
26438
+ const signingJobs = [];
26439
+ signingJobs.push({
26440
+ signingPublicKey,
26441
+ rawTx: newCpfpRefundTx.toBytes(),
26442
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26443
+ type: "cpfp",
26444
+ parentTxOut: nodeOutput
26445
+ });
26446
+ const directNodeTxOut = directNodeTx?.getOutput(0);
26447
+ if (newDirectRefundTx && directNodeTxOut) {
26448
+ signingJobs.push({
26449
+ signingPublicKey,
26450
+ rawTx: newDirectRefundTx.toBytes(),
26451
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26452
+ type: "direct",
26453
+ parentTxOut: directNodeTxOut
26454
+ });
25701
26455
  }
25702
- const refundTxInput = refundTx.getInput(0);
25703
- if (!refundTxInput) {
25704
- throw Error("refund tx doesn't have input");
26456
+ if (newDirectFromCpfpRefundTx) {
26457
+ signingJobs.push({
26458
+ signingPublicKey,
26459
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
26460
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26461
+ type: "directFromCpfp",
26462
+ parentTxOut: nodeOutput
26463
+ });
25705
26464
  }
25706
- newRefundTx.addInput({
25707
- ...refundTxInput,
25708
- sequence: nextSequence
25709
- });
25710
- const refundSigningJob = {
25711
- signingPublicKey: signingPubKey,
25712
- rawTx: newRefundTx.toBytes(),
25713
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25714
- };
25715
26465
  const sparkClient = await this.connectionManager.createSparkClient(
25716
26466
  this.config.getCoordinatorAddress()
25717
26467
  );
25718
- const response = await sparkClient.refresh_timelock({
26468
+ const response = await sparkClient.refresh_timelock_v2({
25719
26469
  leafId: node.id,
25720
26470
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25721
- signingJobs: [getSigningJobProto(refundSigningJob)]
26471
+ signingJobs: signingJobs.map(getSigningJobProto)
25722
26472
  });
25723
- if (response.signingResults.length !== 1) {
26473
+ if (response.signingResults.length !== signingJobs.length) {
25724
26474
  throw Error(
25725
- `Expected 1 signing result, got ${response.signingResults.length}`
26475
+ `Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`
25726
26476
  );
25727
26477
  }
25728
- const signingResult = response.signingResults[0];
25729
- if (!signingResult || !refundSigningJob.signingNonceCommitment) {
25730
- throw Error("Signing result or nonce commitment does not exist");
26478
+ let cpfpRefundSignature;
26479
+ let directRefundSignature;
26480
+ let directFromCpfpRefundSignature;
26481
+ for (const [i, signingJob] of signingJobs.entries()) {
26482
+ const signingResult = response.signingResults[i];
26483
+ if (!signingResult) {
26484
+ throw Error("Signing result does not exist");
26485
+ }
26486
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
26487
+ const txOut = signingJob.parentTxOut;
26488
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
26489
+ const userSignature = await this.config.signer.signFrost({
26490
+ message: rawTxSighash,
26491
+ keyDerivation,
26492
+ publicKey: signingPublicKey,
26493
+ verifyingKey: signingResult.verifyingKey,
26494
+ selfCommitment: signingJob.signingNonceCommitment,
26495
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
26496
+ adaptorPubKey: new Uint8Array()
26497
+ });
26498
+ const signature = await this.config.signer.aggregateFrost({
26499
+ message: rawTxSighash,
26500
+ statechainSignatures: signingResult.signingResult?.signatureShares,
26501
+ statechainPublicKeys: signingResult.signingResult?.publicKeys,
26502
+ verifyingKey: signingResult.verifyingKey,
26503
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
26504
+ selfCommitment: signingJob.signingNonceCommitment,
26505
+ publicKey: signingPublicKey,
26506
+ selfSignature: userSignature,
26507
+ adaptorPubKey: new Uint8Array()
26508
+ });
26509
+ if (signingJob.type === "cpfp") {
26510
+ cpfpRefundSignature = signature;
26511
+ } else if (signingJob.type === "direct") {
26512
+ directRefundSignature = signature;
26513
+ } else if (signingJob.type === "directFromCpfp") {
26514
+ directFromCpfpRefundSignature = signature;
26515
+ }
25731
26516
  }
25732
- const rawTx = getTxFromRawTxBytes(refundSigningJob.rawTx);
25733
- const txOut = nodeTx.getOutput(0);
25734
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
25735
- const userSignature = await this.config.signer.signFrost({
25736
- message: rawTxSighash,
25737
- keyDerivation: {
25738
- type: "leaf" /* LEAF */,
25739
- path: node.id
25740
- },
25741
- publicKey: signingPubKey,
25742
- verifyingKey: signingResult.verifyingKey,
25743
- selfCommitment: refundSigningJob.signingNonceCommitment,
25744
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
25745
- adaptorPubKey: new Uint8Array()
25746
- });
25747
- const signature = await this.config.signer.aggregateFrost({
25748
- message: rawTxSighash,
25749
- statechainSignatures: signingResult.signingResult?.signatureShares,
25750
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
25751
- verifyingKey: signingResult.verifyingKey,
25752
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
25753
- selfCommitment: refundSigningJob.signingNonceCommitment,
25754
- publicKey: signingPubKey,
25755
- selfSignature: userSignature,
25756
- adaptorPubKey: new Uint8Array()
25757
- });
25758
- const result = await sparkClient.finalize_node_signatures({
26517
+ const result = await sparkClient.finalize_node_signatures_v2({
25759
26518
  intent: 3 /* REFRESH */,
25760
26519
  nodeSignatures: [
25761
26520
  {
25762
26521
  nodeId: node.id,
25763
26522
  nodeTxSignature: new Uint8Array(),
25764
- refundTxSignature: signature
26523
+ directNodeTxSignature: new Uint8Array(),
26524
+ refundTxSignature: cpfpRefundSignature,
26525
+ directRefundTxSignature: directRefundSignature,
26526
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature
25765
26527
  }
25766
26528
  ]
25767
26529
  });
@@ -25780,7 +26542,12 @@ var CoopExitService = class extends BaseTransferService {
25780
26542
  connectorOutputs,
25781
26543
  receiverPubKey
25782
26544
  }) {
25783
- const { transfer, signaturesMap } = await this.signCoopExitRefunds(
26545
+ const {
26546
+ transfer,
26547
+ signaturesMap,
26548
+ directSignaturesMap,
26549
+ directFromCpfpSignaturesMap
26550
+ } = await this.signCoopExitRefunds(
25784
26551
  leaves,
25785
26552
  exitTxId,
25786
26553
  connectorOutputs,
@@ -25789,34 +26556,81 @@ var CoopExitService = class extends BaseTransferService {
25789
26556
  const transferTweak = await this.deliverTransferPackage(
25790
26557
  transfer,
25791
26558
  leaves,
25792
- signaturesMap
26559
+ signaturesMap,
26560
+ directSignaturesMap,
26561
+ directFromCpfpSignaturesMap
25793
26562
  );
25794
- return { transfer: transferTweak, signaturesMap };
25795
- }
25796
- createConnectorRefundTransaction(sequence, nodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
25797
- const refundTx = new import_btc_signer3.Transaction();
25798
- if (!nodeOutPoint.txid || nodeOutPoint.index === void 0) {
25799
- throw new ValidationError("Invalid node outpoint", {
25800
- field: "nodeOutPoint",
25801
- value: { txid: nodeOutPoint.txid, index: nodeOutPoint.index },
26563
+ return {
26564
+ transfer: transferTweak,
26565
+ signaturesMap,
26566
+ directSignaturesMap,
26567
+ directFromCpfpSignaturesMap
26568
+ };
26569
+ }
26570
+ createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
26571
+ const cpfpRefundTx = new import_btc_signer3.Transaction();
26572
+ if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
26573
+ throw new ValidationError("Invalid CPFP node outpoint", {
26574
+ field: "cpfpNodeOutPoint",
26575
+ value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
25802
26576
  expected: "Both txid and index must be defined"
25803
26577
  });
25804
26578
  }
25805
- refundTx.addInput({
25806
- txid: nodeOutPoint.txid,
25807
- index: nodeOutPoint.index,
26579
+ cpfpRefundTx.addInput({
26580
+ txid: cpfpNodeOutPoint.txid,
26581
+ index: cpfpNodeOutPoint.index,
25808
26582
  sequence
25809
26583
  });
25810
- refundTx.addInput(connectorOutput);
26584
+ cpfpRefundTx.addInput(connectorOutput);
25811
26585
  const receiverScript = getP2TRScriptFromPublicKey(
25812
26586
  receiverPubKey,
25813
26587
  this.config.getNetwork()
25814
26588
  );
25815
- refundTx.addOutput({
26589
+ cpfpRefundTx.addOutput({
25816
26590
  script: receiverScript,
25817
26591
  amount: amountSats
25818
26592
  });
25819
- return refundTx;
26593
+ let directRefundTx;
26594
+ let directFromCpfpRefundTx;
26595
+ if (directNodeOutPoint) {
26596
+ if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
26597
+ throw new ValidationError("Invalid direct node outpoint", {
26598
+ field: "directNodeOutPoint",
26599
+ value: {
26600
+ txid: directNodeOutPoint.txid,
26601
+ index: directNodeOutPoint.index
26602
+ },
26603
+ expected: "Both txid and index must be defined"
26604
+ });
26605
+ }
26606
+ directRefundTx = new import_btc_signer3.Transaction();
26607
+ directRefundTx.addInput({
26608
+ txid: directNodeOutPoint.txid,
26609
+ index: directNodeOutPoint.index,
26610
+ sequence: directSequence
26611
+ });
26612
+ directRefundTx.addInput(connectorOutput);
26613
+ directRefundTx.addOutput({
26614
+ script: receiverScript,
26615
+ amount: maybeApplyFee(amountSats)
26616
+ });
26617
+ directFromCpfpRefundTx = new import_btc_signer3.Transaction();
26618
+ directFromCpfpRefundTx.addInput({
26619
+ txid: cpfpNodeOutPoint.txid,
26620
+ index: cpfpNodeOutPoint.index,
26621
+ sequence: directSequence
26622
+ });
26623
+ directFromCpfpRefundTx.addInput(connectorOutput);
26624
+ directFromCpfpRefundTx.addOutput({
26625
+ script: receiverScript,
26626
+ amount: maybeApplyFee(amountSats)
26627
+ });
26628
+ }
26629
+ return {
26630
+ cpfpRefundTx,
26631
+ directRefundTx,
26632
+ directFromCpfpRefundTx
26633
+ };
25820
26634
  }
25821
26635
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey) {
25822
26636
  if (leaves.length !== connectorOutputs.length) {
@@ -25852,39 +26666,67 @@ var CoopExitService = class extends BaseTransferService {
25852
26666
  });
25853
26667
  }
25854
26668
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
25855
- const { nextSequence } = getNextTransactionSequence(
25856
- currentRefundTx.getInput(0).sequence
25857
- );
25858
- const refundTx = this.createConnectorRefundTransaction(
26669
+ const sequence = currentRefundTx.getInput(0).sequence;
26670
+ if (!sequence) {
26671
+ throw new ValidationError("Invalid refund transaction", {
26672
+ field: "sequence",
26673
+ value: currentRefundTx.getInput(0),
26674
+ expected: "Non-null sequence"
26675
+ });
26676
+ }
26677
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
26678
+ let currentDirectRefundTx;
26679
+ if (leaf.leaf.directRefundTx.length > 0) {
26680
+ currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
26681
+ }
26682
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
25859
26683
  nextSequence,
26684
+ nextDirectSequence,
25860
26685
  currentRefundTx.getInput(0),
26686
+ currentDirectRefundTx?.getInput(0),
25861
26687
  connectorOutput,
25862
26688
  BigInt(leaf.leaf.value),
25863
26689
  receiverPubKey
25864
26690
  );
25865
26691
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26692
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26693
+ const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26694
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(leaf.keyDerivation);
25866
26695
  const signingJob = {
25867
26696
  leafId: leaf.leaf.id,
25868
26697
  refundTxSigningJob: {
25869
26698
  signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
25870
26699
  leaf.keyDerivation
25871
26700
  ),
25872
- rawTx: refundTx.toBytes(),
26701
+ rawTx: cpfpRefundTx.toBytes(),
25873
26702
  signingNonceCommitment: signingNonceCommitment.commitment
25874
26703
  },
25875
- // TODO: Add direct refund signature
25876
- directRefundTxSigningJob: void 0,
25877
- directFromCpfpRefundTxSigningJob: void 0
26704
+ directRefundTxSigningJob: directRefundTx ? {
26705
+ signingPublicKey,
26706
+ rawTx: directRefundTx.toBytes(),
26707
+ signingNonceCommitment: directSigningNonceCommitment.commitment
26708
+ } : void 0,
26709
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
26710
+ signingPublicKey,
26711
+ rawTx: directFromCpfpRefundTx.toBytes(),
26712
+ signingNonceCommitment: directFromCpfpSigningNonceCommitment.commitment
26713
+ } : void 0
25878
26714
  };
25879
26715
  signingJobs.push(signingJob);
25880
26716
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
26717
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
25881
26718
  leafDataMap.set(leaf.leaf.id, {
25882
26719
  keyDerivation: leaf.keyDerivation,
25883
- refundTx,
26720
+ receivingPubkey: receiverPubKey,
25884
26721
  signingNonceCommitment,
26722
+ directSigningNonceCommitment,
25885
26723
  tx,
25886
- vout: leaf.leaf.vout,
25887
- receivingPubkey: receiverPubKey
26724
+ directTx,
26725
+ refundTx: cpfpRefundTx,
26726
+ directRefundTx,
26727
+ directFromCpfpRefundTx,
26728
+ directFromCpfpRefundSigningNonceCommitment: directFromCpfpSigningNonceCommitment,
26729
+ vout: leaf.leaf.vout
25888
26730
  });
25889
26731
  }
25890
26732
  const sparkClient = await this.connectionManager.createSparkClient(
@@ -25892,7 +26734,7 @@ var CoopExitService = class extends BaseTransferService {
25892
26734
  );
25893
26735
  let response;
25894
26736
  try {
25895
- response = await sparkClient.cooperative_exit({
26737
+ response = await sparkClient.cooperative_exit_v2({
25896
26738
  transfer: {
25897
26739
  transferId: (0, import_uuidv73.uuidv7)(),
25898
26740
  leavesToSend: signingJobs,
@@ -25926,10 +26768,25 @@ var CoopExitService = class extends BaseTransferService {
25926
26768
  response.signingResults
25927
26769
  );
25928
26770
  const signaturesMap = /* @__PURE__ */ new Map();
26771
+ const directSignaturesMap = /* @__PURE__ */ new Map();
26772
+ const directFromCpfpSignaturesMap = /* @__PURE__ */ new Map();
25929
26773
  for (const signature of signatures) {
25930
26774
  signaturesMap.set(signature.nodeId, signature.refundTxSignature);
26775
+ directSignaturesMap.set(
26776
+ signature.nodeId,
26777
+ signature.directRefundTxSignature
26778
+ );
26779
+ directFromCpfpSignaturesMap.set(
26780
+ signature.nodeId,
26781
+ signature.directFromCpfpRefundTxSignature
26782
+ );
25931
26783
  }
25932
- return { transfer: response.transfer, signaturesMap };
26784
+ return {
26785
+ transfer: response.transfer,
26786
+ signaturesMap,
26787
+ directSignaturesMap,
26788
+ directFromCpfpSignaturesMap
26789
+ };
25933
26790
  }
25934
26791
  };
25935
26792
 
@@ -25938,10 +26795,8 @@ init_buffer();
25938
26795
  var import_secp256k110 = require("@noble/curves/secp256k1");
25939
26796
  var import_sha29 = require("@noble/hashes/sha2");
25940
26797
  var import_utils15 = require("@noble/hashes/utils");
25941
- var btc5 = __toESM(require("@scure/btc-signer"), 1);
25942
26798
  var import_btc_signer4 = require("@scure/btc-signer");
25943
26799
  var import_utils16 = require("@scure/btc-signer/utils");
25944
- var INITIAL_TIME_LOCK2 = 2e3;
25945
26800
  var DepositService = class {
25946
26801
  config;
25947
26802
  connectionManager;
@@ -26059,7 +26914,6 @@ var DepositService = class {
26059
26914
  depositTx,
26060
26915
  vout
26061
26916
  }) {
26062
- const rootTx = new import_btc_signer4.Transaction({ version: 3 });
26063
26917
  const output = depositTx.getOutput(vout);
26064
26918
  if (!output) {
26065
26919
  throw new ValidationError("Invalid deposit transaction output", {
@@ -26077,39 +26931,345 @@ var DepositService = class {
26077
26931
  expected: "Output with script and amount"
26078
26932
  });
26079
26933
  }
26080
- let outputAmount = amount;
26081
- rootTx.addInput({
26082
- txid: getTxId(depositTx),
26934
+ const depositOutPoint = {
26935
+ txid: (0, import_utils15.hexToBytes)(getTxId(depositTx)),
26083
26936
  index: vout
26084
- });
26085
- rootTx.addOutput({
26937
+ };
26938
+ const depositTxOut = {
26086
26939
  script,
26087
- amount: outputAmount
26940
+ amount
26941
+ };
26942
+ const [cpfpRootTx, directRootTx] = createRootTx(
26943
+ depositOutPoint,
26944
+ depositTxOut
26945
+ );
26946
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26947
+ const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26948
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
26949
+ const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
26950
+ const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
26951
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
26952
+ sequence: INITIAL_SEQUENCE,
26953
+ directSequence: INITIAL_DIRECT_SEQUENCE,
26954
+ input: { txid: (0, import_utils15.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
26955
+ directInput: { txid: (0, import_utils15.hexToBytes)(getTxId(directRootTx)), index: 0 },
26956
+ amountSats: amount,
26957
+ receivingPubkey: signingPubKey,
26958
+ network: this.config.getNetwork()
26088
26959
  });
26089
- rootTx.addOutput(getEphemeralAnchorOutput());
26090
- const rootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26091
- const rootTxSighash = getSigHashFromTx(rootTx, 0, output);
26092
- const refundTx = new import_btc_signer4.Transaction({ version: 3 });
26093
- const sequence = 1 << 30 | INITIAL_TIME_LOCK2;
26094
- refundTx.addInput({
26095
- txid: getTxId(rootTx),
26096
- index: 0,
26097
- sequence
26960
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26961
+ const directRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26962
+ const directFromCpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26963
+ const cpfpRefundTxSighash = getSigHashFromTx(
26964
+ cpfpRefundTx,
26965
+ 0,
26966
+ cpfpRootTx.getOutput(0)
26967
+ );
26968
+ if (!directRefundTx || !directFromCpfpRefundTx) {
26969
+ throw new ValidationError(
26970
+ "Expected direct refund transactions for tree creation",
26971
+ {
26972
+ field: "directRefundTx",
26973
+ value: directRefundTx
26974
+ }
26975
+ );
26976
+ }
26977
+ const directRefundTxSighash = getSigHashFromTx(
26978
+ directRefundTx,
26979
+ 0,
26980
+ directRootTx.getOutput(0)
26981
+ );
26982
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
26983
+ directFromCpfpRefundTx,
26984
+ 0,
26985
+ cpfpRootTx.getOutput(0)
26986
+ );
26987
+ const sparkClient = await this.connectionManager.createSparkClient(
26988
+ this.config.getCoordinatorAddress()
26989
+ );
26990
+ let treeResp;
26991
+ try {
26992
+ treeResp = await sparkClient.start_deposit_tree_creation({
26993
+ identityPublicKey: await this.config.signer.getIdentityPublicKey(),
26994
+ onChainUtxo: {
26995
+ vout,
26996
+ rawTx: depositTx.toBytes(true),
26997
+ network: this.config.getNetworkProto()
26998
+ },
26999
+ rootTxSigningJob: {
27000
+ rawTx: cpfpRootTx.toBytes(),
27001
+ signingPublicKey: signingPubKey,
27002
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
27003
+ },
27004
+ refundTxSigningJob: {
27005
+ rawTx: cpfpRefundTx.toBytes(),
27006
+ signingPublicKey: signingPubKey,
27007
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
27008
+ },
27009
+ directRootTxSigningJob: {
27010
+ rawTx: directRootTx.toBytes(),
27011
+ signingPublicKey: signingPubKey,
27012
+ signingNonceCommitment: directRootNonceCommitment.commitment
27013
+ },
27014
+ directRefundTxSigningJob: {
27015
+ rawTx: directRefundTx.toBytes(),
27016
+ signingPublicKey: signingPubKey,
27017
+ signingNonceCommitment: directRefundNonceCommitment.commitment
27018
+ },
27019
+ directFromCpfpRefundTxSigningJob: {
27020
+ rawTx: directFromCpfpRefundTx.toBytes(),
27021
+ signingPublicKey: signingPubKey,
27022
+ signingNonceCommitment: directFromCpfpRefundNonceCommitment.commitment
27023
+ }
27024
+ });
27025
+ } catch (error) {
27026
+ throw new NetworkError(
27027
+ "Failed to start deposit tree creation",
27028
+ {
27029
+ operation: "start_deposit_tree_creation",
27030
+ errorCount: 1,
27031
+ errors: error instanceof Error ? error.message : String(error)
27032
+ },
27033
+ error
27034
+ );
27035
+ }
27036
+ if (!treeResp.rootNodeSignatureShares?.verifyingKey) {
27037
+ throw new ValidationError("No verifying key found in tree response", {
27038
+ field: "verifyingKey",
27039
+ value: treeResp.rootNodeSignatureShares,
27040
+ expected: "Non-null verifying key"
27041
+ });
27042
+ }
27043
+ if (!treeResp.rootNodeSignatureShares.nodeTxSigningResult?.signingNonceCommitments) {
27044
+ throw new ValidationError(
27045
+ "No signing nonce commitments found in tree response",
27046
+ {
27047
+ field: "nodeTxSigningResult.signingNonceCommitments",
27048
+ value: treeResp.rootNodeSignatureShares.nodeTxSigningResult,
27049
+ expected: "Non-null signing nonce commitments"
27050
+ }
27051
+ );
27052
+ }
27053
+ if (!treeResp.rootNodeSignatureShares.refundTxSigningResult?.signingNonceCommitments) {
27054
+ throw new ValidationError(
27055
+ "No signing nonce commitments found in tree response",
27056
+ {
27057
+ field: "refundTxSigningResult.signingNonceCommitments"
27058
+ }
27059
+ );
27060
+ }
27061
+ if (!treeResp.rootNodeSignatureShares.directNodeTxSigningResult?.signingNonceCommitments) {
27062
+ throw new ValidationError(
27063
+ "No direct node signing nonce commitments found in tree response",
27064
+ {
27065
+ field: "directNodeTxSigningResult.signingNonceCommitments"
27066
+ }
27067
+ );
27068
+ }
27069
+ if (!treeResp.rootNodeSignatureShares.directRefundTxSigningResult?.signingNonceCommitments) {
27070
+ throw new ValidationError(
27071
+ "No direct refund signing nonce commitments found in tree response",
27072
+ {
27073
+ field: "directRefundTxSigningResult.signingNonceCommitments"
27074
+ }
27075
+ );
27076
+ }
27077
+ if (!treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult?.signingNonceCommitments) {
27078
+ throw new ValidationError(
27079
+ "No direct from CPFP refund signing nonce commitments found in tree response",
27080
+ {
27081
+ field: "directFromCpfpRefundTxSigningResult.signingNonceCommitments"
27082
+ }
27083
+ );
27084
+ }
27085
+ if (!(0, import_utils16.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
27086
+ throw new ValidationError("Verifying key mismatch", {
27087
+ field: "verifyingKey",
27088
+ value: treeResp.rootNodeSignatureShares.verifyingKey,
27089
+ expected: verifyingKey
27090
+ });
27091
+ }
27092
+ const cpfpRootSignature = await this.config.signer.signFrost({
27093
+ message: cpfpRootTxSighash,
27094
+ publicKey: signingPubKey,
27095
+ keyDerivation,
27096
+ verifyingKey,
27097
+ selfCommitment: cpfpRootNonceCommitment,
27098
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
27099
+ adaptorPubKey: new Uint8Array()
27100
+ });
27101
+ const directRootSignature = await this.config.signer.signFrost({
27102
+ message: directRootTxSighash,
27103
+ publicKey: signingPubKey,
27104
+ keyDerivation,
27105
+ verifyingKey,
27106
+ selfCommitment: directRootNonceCommitment,
27107
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
27108
+ adaptorPubKey: new Uint8Array()
27109
+ });
27110
+ const cpfpRefundSignature = await this.config.signer.signFrost({
27111
+ message: cpfpRefundTxSighash,
27112
+ publicKey: signingPubKey,
27113
+ keyDerivation,
27114
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27115
+ selfCommitment: cpfpRefundNonceCommitment,
27116
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
27117
+ adaptorPubKey: new Uint8Array()
27118
+ });
27119
+ const directRefundSignature = await this.config.signer.signFrost({
27120
+ message: directRefundTxSighash,
27121
+ publicKey: signingPubKey,
27122
+ keyDerivation,
27123
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27124
+ selfCommitment: directRefundNonceCommitment,
27125
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
27126
+ adaptorPubKey: new Uint8Array()
27127
+ });
27128
+ const directFromCpfpRefundSignature = await this.config.signer.signFrost({
27129
+ message: directFromCpfpRefundTxSighash,
27130
+ publicKey: signingPubKey,
27131
+ keyDerivation,
27132
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27133
+ selfCommitment: directFromCpfpRefundNonceCommitment,
27134
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
27135
+ adaptorPubKey: new Uint8Array()
27136
+ });
27137
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
27138
+ message: cpfpRootTxSighash,
27139
+ statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
27140
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
27141
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27142
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
27143
+ selfCommitment: cpfpRootNonceCommitment,
27144
+ publicKey: signingPubKey,
27145
+ selfSignature: cpfpRootSignature,
27146
+ adaptorPubKey: new Uint8Array()
27147
+ });
27148
+ const directRootAggregate = await this.config.signer.aggregateFrost({
27149
+ message: directRootTxSighash,
27150
+ statechainSignatures: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signatureShares,
27151
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.publicKeys,
27152
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27153
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
27154
+ selfCommitment: directRootNonceCommitment,
27155
+ publicKey: signingPubKey,
27156
+ selfSignature: directRootSignature,
27157
+ adaptorPubKey: new Uint8Array()
27158
+ });
27159
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
27160
+ message: cpfpRefundTxSighash,
27161
+ statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
27162
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
27163
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27164
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
27165
+ selfCommitment: cpfpRefundNonceCommitment,
27166
+ publicKey: signingPubKey,
27167
+ selfSignature: cpfpRefundSignature,
27168
+ adaptorPubKey: new Uint8Array()
27169
+ });
27170
+ const directRefundAggregate = await this.config.signer.aggregateFrost({
27171
+ message: directRefundTxSighash,
27172
+ statechainSignatures: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signatureShares,
27173
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.publicKeys,
27174
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27175
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
27176
+ selfCommitment: directRefundNonceCommitment,
27177
+ publicKey: signingPubKey,
27178
+ selfSignature: directRefundSignature,
27179
+ adaptorPubKey: new Uint8Array()
26098
27180
  });
27181
+ const directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
27182
+ message: directFromCpfpRefundTxSighash,
27183
+ statechainSignatures: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signatureShares,
27184
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.publicKeys,
27185
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27186
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
27187
+ selfCommitment: directFromCpfpRefundNonceCommitment,
27188
+ publicKey: signingPubKey,
27189
+ selfSignature: directFromCpfpRefundSignature,
27190
+ adaptorPubKey: new Uint8Array()
27191
+ });
27192
+ let finalizeResp;
27193
+ try {
27194
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
27195
+ intent: 0 /* CREATION */,
27196
+ nodeSignatures: [
27197
+ {
27198
+ nodeId: treeResp.rootNodeSignatureShares.nodeId,
27199
+ nodeTxSignature: cpfpRootAggregate,
27200
+ refundTxSignature: cpfpRefundAggregate,
27201
+ directNodeTxSignature: directRootAggregate,
27202
+ directRefundTxSignature: directRefundAggregate,
27203
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate
27204
+ }
27205
+ ]
27206
+ });
27207
+ } catch (error) {
27208
+ throw new NetworkError(
27209
+ "Failed to finalize node signatures",
27210
+ {
27211
+ operation: "finalize_node_signatures",
27212
+ errorCount: 1,
27213
+ errors: error instanceof Error ? error.message : String(error)
27214
+ },
27215
+ error
27216
+ );
27217
+ }
27218
+ return finalizeResp;
27219
+ }
27220
+ /**
27221
+ * @deprecated
27222
+ * Use createTreeRoot instead.
27223
+ * This is currently only used to test backwards compatibility.
27224
+ */
27225
+ async createTreeWithoutDirectTx({
27226
+ keyDerivation,
27227
+ verifyingKey,
27228
+ depositTx,
27229
+ vout
27230
+ }) {
27231
+ const output = depositTx.getOutput(vout);
27232
+ if (!output) {
27233
+ throw new ValidationError("Invalid deposit transaction output", {
27234
+ field: "vout",
27235
+ value: vout,
27236
+ expected: "Valid output index"
27237
+ });
27238
+ }
27239
+ const script = output.script;
27240
+ const amount = output.amount;
27241
+ if (!script || !amount) {
27242
+ throw new ValidationError("No script or amount found in deposit tx", {
27243
+ field: "output",
27244
+ value: output,
27245
+ expected: "Output with script and amount"
27246
+ });
27247
+ }
27248
+ const depositOutPoint = {
27249
+ txid: (0, import_utils15.hexToBytes)(getTxId(depositTx)),
27250
+ index: vout
27251
+ };
27252
+ const depositTxOut = {
27253
+ script,
27254
+ amount
27255
+ };
27256
+ const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
27257
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
27258
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
26099
27259
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
26100
- const refundP2trAddress = getP2TRAddressFromPublicKey(
26101
- signingPubKey,
26102
- this.config.getNetwork()
26103
- );
26104
- const refundAddress = btc5.Address(getNetwork(this.config.getNetwork())).decode(refundP2trAddress);
26105
- const refundPkScript = btc5.OutScript.encode(refundAddress);
26106
- refundTx.addOutput({
26107
- script: refundPkScript,
26108
- amount: outputAmount
27260
+ const { cpfpRefundTx } = createRefundTxs({
27261
+ sequence: INITIAL_SEQUENCE,
27262
+ input: { txid: (0, import_utils15.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
27263
+ amountSats: amount,
27264
+ receivingPubkey: signingPubKey,
27265
+ network: this.config.getNetwork()
26109
27266
  });
26110
- refundTx.addOutput(getEphemeralAnchorOutput());
26111
- const refundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26112
- const refundTxSighash = getSigHashFromTx(refundTx, 0, rootTx.getOutput(0));
27267
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
27268
+ const cpfpRefundTxSighash = getSigHashFromTx(
27269
+ cpfpRefundTx,
27270
+ 0,
27271
+ cpfpRootTx.getOutput(0)
27272
+ );
26113
27273
  const sparkClient = await this.connectionManager.createSparkClient(
26114
27274
  this.config.getCoordinatorAddress()
26115
27275
  );
@@ -26123,14 +27283,14 @@ var DepositService = class {
26123
27283
  network: this.config.getNetworkProto()
26124
27284
  },
26125
27285
  rootTxSigningJob: {
26126
- rawTx: rootTx.toBytes(),
27286
+ rawTx: cpfpRootTx.toBytes(),
26127
27287
  signingPublicKey: signingPubKey,
26128
- signingNonceCommitment: rootNonceCommitment.commitment
27288
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
26129
27289
  },
26130
27290
  refundTxSigningJob: {
26131
- rawTx: refundTx.toBytes(),
27291
+ rawTx: cpfpRefundTx.toBytes(),
26132
27292
  signingPublicKey: signingPubKey,
26133
- signingNonceCommitment: refundNonceCommitment.commitment
27293
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
26134
27294
  }
26135
27295
  });
26136
27296
  } catch (error) {
@@ -26176,55 +27336,55 @@ var DepositService = class {
26176
27336
  expected: verifyingKey
26177
27337
  });
26178
27338
  }
26179
- const rootSignature = await this.config.signer.signFrost({
26180
- message: rootTxSighash,
27339
+ const cpfpRootSignature = await this.config.signer.signFrost({
27340
+ message: cpfpRootTxSighash,
26181
27341
  publicKey: signingPubKey,
26182
27342
  keyDerivation,
26183
27343
  verifyingKey,
26184
- selfCommitment: rootNonceCommitment,
27344
+ selfCommitment: cpfpRootNonceCommitment,
26185
27345
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
26186
27346
  adaptorPubKey: new Uint8Array()
26187
27347
  });
26188
- const refundSignature = await this.config.signer.signFrost({
26189
- message: refundTxSighash,
27348
+ const cpfpRefundSignature = await this.config.signer.signFrost({
27349
+ message: cpfpRefundTxSighash,
26190
27350
  publicKey: signingPubKey,
26191
27351
  keyDerivation,
26192
- verifyingKey,
26193
- selfCommitment: refundNonceCommitment,
27352
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27353
+ selfCommitment: cpfpRefundNonceCommitment,
26194
27354
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
26195
27355
  adaptorPubKey: new Uint8Array()
26196
27356
  });
26197
- const rootAggregate = await this.config.signer.aggregateFrost({
26198
- message: rootTxSighash,
27357
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
27358
+ message: cpfpRootTxSighash,
26199
27359
  statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
26200
27360
  statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
26201
27361
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
26202
27362
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
26203
- selfCommitment: rootNonceCommitment,
27363
+ selfCommitment: cpfpRootNonceCommitment,
26204
27364
  publicKey: signingPubKey,
26205
- selfSignature: rootSignature,
27365
+ selfSignature: cpfpRootSignature,
26206
27366
  adaptorPubKey: new Uint8Array()
26207
27367
  });
26208
- const refundAggregate = await this.config.signer.aggregateFrost({
26209
- message: refundTxSighash,
27368
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
27369
+ message: cpfpRefundTxSighash,
26210
27370
  statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
26211
27371
  statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
26212
27372
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
26213
27373
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
26214
- selfCommitment: refundNonceCommitment,
27374
+ selfCommitment: cpfpRefundNonceCommitment,
26215
27375
  publicKey: signingPubKey,
26216
- selfSignature: refundSignature,
27376
+ selfSignature: cpfpRefundSignature,
26217
27377
  adaptorPubKey: new Uint8Array()
26218
27378
  });
26219
27379
  let finalizeResp;
26220
27380
  try {
26221
- finalizeResp = await sparkClient.finalize_node_signatures({
27381
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
26222
27382
  intent: 0 /* CREATION */,
26223
27383
  nodeSignatures: [
26224
27384
  {
26225
27385
  nodeId: treeResp.rootNodeSignatureShares.nodeId,
26226
- nodeTxSignature: rootAggregate,
26227
- refundTxSignature: refundAggregate
27386
+ nodeTxSignature: cpfpRootAggregate,
27387
+ refundTxSignature: cpfpRefundAggregate
26228
27388
  }
26229
27389
  ]
26230
27390
  });
@@ -26434,7 +27594,8 @@ var LightningService = class {
26434
27594
  let signingCommitments;
26435
27595
  try {
26436
27596
  signingCommitments = await sparkClient.get_signing_commitments({
26437
- nodeIds: leaves.map((leaf) => leaf.leaf.id)
27597
+ nodeIds: leaves.map((leaf) => leaf.leaf.id),
27598
+ count: 3
26438
27599
  });
26439
27600
  } catch (error) {
26440
27601
  throw new NetworkError(
@@ -26447,10 +27608,19 @@ var LightningService = class {
26447
27608
  error
26448
27609
  );
26449
27610
  }
26450
- const leafSigningJobs = await this.signingService.signRefunds(
27611
+ const {
27612
+ cpfpLeafSigningJobs,
27613
+ directLeafSigningJobs,
27614
+ directFromCpfpLeafSigningJobs
27615
+ } = await this.signingService.signRefunds(
26451
27616
  leaves,
26452
- signingCommitments.signingCommitments,
26453
- receiverIdentityPubkey
27617
+ receiverIdentityPubkey,
27618
+ signingCommitments.signingCommitments.slice(0, leaves.length),
27619
+ signingCommitments.signingCommitments.slice(
27620
+ leaves.length,
27621
+ 2 * leaves.length
27622
+ ),
27623
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
26454
27624
  );
26455
27625
  const transferId = (0, import_uuidv74.uuidv7)();
26456
27626
  let bolt11String = "";
@@ -26487,7 +27657,7 @@ var LightningService = class {
26487
27657
  const reason = isInboundPayment ? 1 /* REASON_RECEIVE */ : 0 /* REASON_SEND */;
26488
27658
  let response;
26489
27659
  try {
26490
- response = await sparkClient.initiate_preimage_swap({
27660
+ response = await sparkClient.initiate_preimage_swap_v2({
26491
27661
  paymentHash,
26492
27662
  invoiceAmount: {
26493
27663
  invoiceAmountProof: {
@@ -26499,7 +27669,9 @@ var LightningService = class {
26499
27669
  transfer: {
26500
27670
  transferId,
26501
27671
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
26502
- leavesToSend: leafSigningJobs,
27672
+ leavesToSend: cpfpLeafSigningJobs,
27673
+ directLeavesToSend: directLeafSigningJobs,
27674
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs,
26503
27675
  receiverIdentityPublicKey: receiverIdentityPubkey,
26504
27676
  expiryTime: new Date(Date.now() + 2 * 60 * 1e3)
26505
27677
  },
@@ -28976,13 +30148,6 @@ function isTokenTransaction(tokenTransaction) {
28976
30148
  init_buffer();
28977
30149
  var import_utils20 = require("@noble/curves/abstract/utils");
28978
30150
  var import_btc_signer5 = require("@scure/btc-signer");
28979
- var INITIAL_TIME_LOCK3 = 2e3;
28980
- function maybeApplyFee2(amount) {
28981
- if (amount > BigInt(DEFAULT_FEE_SATS)) {
28982
- return amount - BigInt(DEFAULT_FEE_SATS);
28983
- }
28984
- return amount;
28985
- }
28986
30151
  var TreeCreationService = class {
28987
30152
  config;
28988
30153
  connectionManager;
@@ -29097,7 +30262,7 @@ var TreeCreationService = class {
29097
30262
  );
29098
30263
  let response;
29099
30264
  try {
29100
- response = await sparkClient.create_tree(request);
30265
+ response = await sparkClient.create_tree_v2(request);
29101
30266
  } catch (error) {
29102
30267
  throw new Error(`Error creating tree: ${error}`);
29103
30268
  }
@@ -29114,7 +30279,7 @@ var TreeCreationService = class {
29114
30279
  );
29115
30280
  let finalizeResp;
29116
30281
  try {
29117
- finalizeResp = await sparkClient.finalize_node_signatures({
30282
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
29118
30283
  nodeSignatures
29119
30284
  });
29120
30285
  } catch (error) {
@@ -29179,91 +30344,111 @@ var TreeCreationService = class {
29179
30344
  async buildChildCreationNode(node, parentTx, vout, network) {
29180
30345
  const internalCreationNode = {
29181
30346
  nodeTxSigningJob: void 0,
29182
- refundTxSigningJob: void 0,
29183
- children: [],
29184
30347
  directNodeTxSigningJob: void 0,
30348
+ refundTxSigningJob: void 0,
29185
30349
  directRefundTxSigningJob: void 0,
29186
- directFromCpfpRefundTxSigningJob: void 0
30350
+ directFromCpfpRefundTxSigningJob: void 0,
30351
+ children: []
29187
30352
  };
29188
- const tx = new import_btc_signer5.Transaction({ version: 3 });
29189
- tx.addInput({
29190
- txid: getTxId(parentTx),
29191
- index: vout
29192
- });
29193
30353
  const parentTxOut = parentTx.getOutput(vout);
29194
30354
  if (!parentTxOut?.script || !parentTxOut?.amount) {
29195
30355
  throw new Error("parentTxOut is undefined");
29196
30356
  }
29197
- tx.addOutput({
30357
+ const parentOutPoint = {
30358
+ txid: (0, import_utils20.hexToBytes)(getTxId(parentTx)),
30359
+ index: vout
30360
+ };
30361
+ const parentTxOutObj = {
29198
30362
  script: parentTxOut.script,
29199
30363
  amount: parentTxOut.amount
29200
- // maybeApplyFee(parentTxOut.amount),
29201
- });
29202
- tx.addOutput(getEphemeralAnchorOutput());
29203
- const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
29204
- const signingJob = {
30364
+ };
30365
+ const { cpfpNodeTx, directNodeTx } = createNodeTxs(
30366
+ parentTxOutObj,
30367
+ parentOutPoint
30368
+ );
30369
+ const cpfpNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30370
+ const directNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30371
+ const cpfpNodeSigningJob = {
29205
30372
  signingPublicKey: node.signingPublicKey,
29206
- rawTx: tx.toBytes(),
29207
- signingNonceCommitment: signingNonceCommitment.commitment
30373
+ rawTx: cpfpNodeTx.toBytes(),
30374
+ signingNonceCommitment: cpfpNodeSigningCommitment.commitment
29208
30375
  };
29209
- internalCreationNode.nodeTxSigningCommitment = signingNonceCommitment;
29210
- internalCreationNode.nodeTxSigningJob = signingJob;
29211
- const sequence = 1 << 30 | INITIAL_TIME_LOCK3;
30376
+ const directNodeSigningJob = directNodeTx ? {
30377
+ signingPublicKey: node.signingPublicKey,
30378
+ rawTx: directNodeTx.toBytes(),
30379
+ signingNonceCommitment: directNodeSigningCommitment.commitment
30380
+ } : void 0;
30381
+ internalCreationNode.nodeTxSigningCommitment = cpfpNodeSigningCommitment;
30382
+ internalCreationNode.directNodeTxSigningCommitment = directNodeSigningCommitment;
30383
+ internalCreationNode.nodeTxSigningJob = cpfpNodeSigningJob;
30384
+ internalCreationNode.directNodeTxSigningJob = directNodeSigningJob;
30385
+ const sequence = INITIAL_SEQUENCE;
30386
+ const directSequence = INITIAL_DIRECT_SEQUENCE;
29212
30387
  const childCreationNode = {
29213
30388
  nodeTxSigningJob: void 0,
29214
- refundTxSigningJob: void 0,
29215
- children: [],
29216
30389
  directNodeTxSigningJob: void 0,
30390
+ refundTxSigningJob: void 0,
29217
30391
  directRefundTxSigningJob: void 0,
29218
- directFromCpfpRefundTxSigningJob: void 0
30392
+ directFromCpfpRefundTxSigningJob: void 0,
30393
+ children: []
29219
30394
  };
29220
- const childTx = new import_btc_signer5.Transaction({ version: 3 });
29221
- childTx.addInput({
29222
- txid: getTxId(tx),
29223
- index: 0,
29224
- sequence
29225
- });
29226
- childTx.addOutput({
29227
- script: parentTxOut.script,
29228
- amount: parentTxOut.amount
29229
- // maybeApplyFee(parentTxOut.amount),
29230
- });
29231
- childTx.addOutput(getEphemeralAnchorOutput());
29232
- const childSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
29233
- const childSigningJob = {
30395
+ const [cpfpLeafTx, directLeafTx] = createLeafNodeTx(
30396
+ sequence,
30397
+ directSequence,
30398
+ { txid: (0, import_utils20.hexToBytes)(getTxId(cpfpNodeTx)), index: 0 },
30399
+ parentTxOutObj,
30400
+ true
30401
+ // shouldCalculateFee
30402
+ );
30403
+ const cpfpLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30404
+ const directLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30405
+ const cpfpLeafSigningJob = {
29234
30406
  signingPublicKey: node.signingPublicKey,
29235
- rawTx: childTx.toBytes(),
29236
- signingNonceCommitment: childSigningNonceCommitment.commitment
29237
- };
29238
- childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
29239
- childCreationNode.nodeTxSigningJob = childSigningJob;
29240
- const refundTx = new import_btc_signer5.Transaction({ version: 3 });
29241
- refundTx.addInput({
29242
- txid: getTxId(childTx),
29243
- index: 0,
29244
- sequence
29245
- });
29246
- const refundP2trAddress = getP2TRAddressFromPublicKey(
29247
- node.signingPublicKey,
30407
+ rawTx: cpfpLeafTx.toBytes(),
30408
+ signingNonceCommitment: cpfpLeafSigningCommitment.commitment
30409
+ };
30410
+ const directLeafSigningJob = {
30411
+ signingPublicKey: node.signingPublicKey,
30412
+ rawTx: directLeafTx.toBytes(),
30413
+ signingNonceCommitment: directLeafSigningCommitment.commitment
30414
+ };
30415
+ childCreationNode.nodeTxSigningCommitment = cpfpLeafSigningCommitment;
30416
+ childCreationNode.directNodeTxSigningCommitment = directLeafSigningCommitment;
30417
+ childCreationNode.nodeTxSigningJob = cpfpLeafSigningJob;
30418
+ childCreationNode.directNodeTxSigningJob = directLeafSigningJob;
30419
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
30420
+ sequence,
30421
+ directSequence,
30422
+ input: { txid: (0, import_utils20.hexToBytes)(getTxId(cpfpLeafTx)), index: 0 },
30423
+ directInput: { txid: (0, import_utils20.hexToBytes)(getTxId(directLeafTx)), index: 0 },
30424
+ amountSats: parentTxOut.amount,
30425
+ receivingPubkey: node.signingPublicKey,
29248
30426
  network
29249
- );
29250
- const refundAddress = (0, import_btc_signer5.Address)(getNetwork(network)).decode(
29251
- refundP2trAddress
29252
- );
29253
- const refundPkScript = import_btc_signer5.OutScript.encode(refundAddress);
29254
- refundTx.addOutput({
29255
- script: refundPkScript,
29256
- amount: maybeApplyFee2(parentTxOut.amount)
29257
30427
  });
29258
- refundTx.addOutput(getEphemeralAnchorOutput());
29259
- const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
29260
- const refundSigningJob = {
30428
+ const cpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30429
+ const directRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30430
+ const directFromCpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30431
+ const cpfpRefundSigningJob = {
29261
30432
  signingPublicKey: node.signingPublicKey,
29262
- rawTx: refundTx.toBytes(),
29263
- signingNonceCommitment: refundSigningNonceCommitment.commitment
30433
+ rawTx: cpfpRefundTx.toBytes(),
30434
+ signingNonceCommitment: cpfpRefundSigningCommitment.commitment
29264
30435
  };
29265
- childCreationNode.refundTxSigningCommitment = refundSigningNonceCommitment;
29266
- childCreationNode.refundTxSigningJob = refundSigningJob;
30436
+ const directRefundSigningJob = directRefundTx ? {
30437
+ signingPublicKey: node.signingPublicKey,
30438
+ rawTx: directRefundTx.toBytes(),
30439
+ signingNonceCommitment: directRefundSigningCommitment.commitment
30440
+ } : void 0;
30441
+ const directFromCpfpRefundSigningJob = directFromCpfpRefundTx ? {
30442
+ signingPublicKey: node.signingPublicKey,
30443
+ rawTx: directFromCpfpRefundTx.toBytes(),
30444
+ signingNonceCommitment: directFromCpfpRefundSigningCommitment.commitment
30445
+ } : void 0;
30446
+ childCreationNode.refundTxSigningCommitment = cpfpRefundSigningCommitment;
30447
+ childCreationNode.directRefundTxSigningCommitment = directRefundSigningCommitment;
30448
+ childCreationNode.directFromCpfpRefundTxSigningCommitment = directFromCpfpRefundSigningCommitment;
30449
+ childCreationNode.refundTxSigningJob = cpfpRefundSigningJob;
30450
+ childCreationNode.directRefundTxSigningJob = directRefundSigningJob;
30451
+ childCreationNode.directFromCpfpRefundTxSigningJob = directFromCpfpRefundSigningJob;
29267
30452
  internalCreationNode.children.push(childCreationNode);
29268
30453
  return internalCreationNode;
29269
30454
  }
@@ -29272,11 +30457,7 @@ var TreeCreationService = class {
29272
30457
  if (!parentTxOutput?.script || !parentTxOutput?.amount) {
29273
30458
  throw new Error("parentTxOutput is undefined");
29274
30459
  }
29275
- const rootNodeTx = new import_btc_signer5.Transaction({ version: 3 });
29276
- rootNodeTx.addInput({
29277
- txid: getTxId(parentTx),
29278
- index: vout
29279
- });
30460
+ const childTxOuts = [];
29280
30461
  for (let i = 0; i < root.children.length; i++) {
29281
30462
  const child = root.children[i];
29282
30463
  if (!child || !child.address) {
@@ -29284,28 +30465,41 @@ var TreeCreationService = class {
29284
30465
  }
29285
30466
  const childAddress = (0, import_btc_signer5.Address)(getNetwork(network)).decode(child.address);
29286
30467
  const childPkScript = import_btc_signer5.OutScript.encode(childAddress);
29287
- rootNodeTx.addOutput({
30468
+ childTxOuts.push({
29288
30469
  script: childPkScript,
29289
30470
  amount: parentTxOutput.amount / 2n
29290
- // feeAdjustedAmount / 2n,
29291
30471
  });
29292
30472
  }
29293
- rootNodeTx.addOutput(getEphemeralAnchorOutput());
29294
- const rootNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
29295
- const rootNodeSigningJob = {
30473
+ const parentOutPoint = {
30474
+ txid: (0, import_utils20.hexToBytes)(getTxId(parentTx)),
30475
+ index: vout
30476
+ };
30477
+ const [cpfpSplitTx, directSplitTx] = createSplitTx(
30478
+ parentOutPoint,
30479
+ childTxOuts
30480
+ );
30481
+ const cpfpSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30482
+ const directSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30483
+ const cpfpSplitSigningJob = {
29296
30484
  signingPublicKey: root.signingPublicKey,
29297
- rawTx: rootNodeTx.toBytes(),
29298
- signingNonceCommitment: rootNodeSigningCommitment.commitment
30485
+ rawTx: cpfpSplitTx.toBytes(),
30486
+ signingNonceCommitment: cpfpSplitSigningCommitment.commitment
30487
+ };
30488
+ const directSplitSigningJob = {
30489
+ signingPublicKey: root.signingPublicKey,
30490
+ rawTx: directSplitTx.toBytes(),
30491
+ signingNonceCommitment: directSplitSigningCommitment.commitment
29299
30492
  };
29300
30493
  const rootCreationNode = {
29301
- nodeTxSigningJob: rootNodeSigningJob,
30494
+ nodeTxSigningJob: cpfpSplitSigningJob,
30495
+ directNodeTxSigningJob: directSplitSigningJob,
29302
30496
  refundTxSigningJob: void 0,
29303
- children: [],
29304
- directNodeTxSigningJob: void 0,
29305
30497
  directRefundTxSigningJob: void 0,
29306
- directFromCpfpRefundTxSigningJob: void 0
30498
+ directFromCpfpRefundTxSigningJob: void 0,
30499
+ children: []
29307
30500
  };
29308
- rootCreationNode.nodeTxSigningCommitment = rootNodeSigningCommitment;
30501
+ rootCreationNode.nodeTxSigningCommitment = cpfpSplitSigningCommitment;
30502
+ rootCreationNode.directNodeTxSigningCommitment = directSplitSigningCommitment;
29309
30503
  const leftChild = root.children[0];
29310
30504
  const rightChild = root.children[1];
29311
30505
  if (!leftChild || !rightChild) {
@@ -29313,13 +30507,15 @@ var TreeCreationService = class {
29313
30507
  }
29314
30508
  const leftChildCreationNode = await this.buildChildCreationNode(
29315
30509
  leftChild,
29316
- rootNodeTx,
30510
+ cpfpSplitTx,
30511
+ // Use CPFP version for children
29317
30512
  0,
29318
30513
  network
29319
30514
  );
29320
30515
  const rightChildCreationNode = await this.buildChildCreationNode(
29321
30516
  rightChild,
29322
- rootNodeTx,
30517
+ cpfpSplitTx,
30518
+ // Use CPFP version for children
29323
30519
  1,
29324
30520
  network
29325
30521
  );
@@ -29328,19 +30524,19 @@ var TreeCreationService = class {
29328
30524
  return rootCreationNode;
29329
30525
  }
29330
30526
  async signNodeCreation(parentTx, vout, internalNode, creationNode, creationResponseNode) {
29331
- if (!creationNode.nodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
30527
+ if (!creationNode.nodeTxSigningJob?.signingPublicKey || !creationNode.directNodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
29332
30528
  throw new Error("signingPublicKey or verificationKey is undefined");
29333
30529
  }
29334
30530
  const parentTxOutput = parentTx.getOutput(vout);
29335
30531
  if (!parentTxOutput) {
29336
30532
  throw new Error("parentTxOutput is undefined");
29337
30533
  }
29338
- const tx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
29339
- const txSighash = getSigHashFromTx(tx, 0, parentTxOutput);
29340
- let nodeTxSignature = new Uint8Array();
30534
+ const cpfpNodeTx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
30535
+ const cpfpNodeTxSighash = getSigHashFromTx(cpfpNodeTx, 0, parentTxOutput);
30536
+ let cpfpNodeTxSignature = new Uint8Array();
29341
30537
  if (creationNode.nodeTxSigningCommitment) {
29342
- const userSignature = await this.config.signer.signFrost({
29343
- message: txSighash,
30538
+ const cpfpUserSignature = await this.config.signer.signFrost({
30539
+ message: cpfpNodeTxSighash,
29344
30540
  publicKey: creationNode.nodeTxSigningJob.signingPublicKey,
29345
30541
  keyDerivation: {
29346
30542
  type: "leaf" /* LEAF */,
@@ -29350,30 +30546,84 @@ var TreeCreationService = class {
29350
30546
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
29351
30547
  verifyingKey: internalNode.verificationKey
29352
30548
  });
29353
- nodeTxSignature = await this.config.signer.aggregateFrost({
29354
- message: txSighash,
30549
+ cpfpNodeTxSignature = await this.config.signer.aggregateFrost({
30550
+ message: cpfpNodeTxSighash,
29355
30551
  statechainSignatures: creationResponseNode.nodeTxSigningResult?.signatureShares,
29356
30552
  statechainPublicKeys: creationResponseNode.nodeTxSigningResult?.publicKeys,
29357
30553
  verifyingKey: internalNode.verificationKey,
29358
30554
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
29359
30555
  selfCommitment: creationNode.nodeTxSigningCommitment,
29360
- selfSignature: userSignature,
30556
+ selfSignature: cpfpUserSignature,
29361
30557
  publicKey: internalNode.signingPublicKey
29362
30558
  });
29363
30559
  }
29364
- let refundTxSignature = new Uint8Array();
29365
- if (creationNode.refundTxSigningCommitment) {
29366
- const rawTx = creationNode.refundTxSigningJob?.rawTx;
29367
- if (!rawTx) {
29368
- throw new Error("rawTx is undefined");
29369
- }
29370
- if (!creationNode.refundTxSigningJob?.signingPublicKey) {
29371
- throw new Error("signingPublicKey is undefined");
29372
- }
29373
- const refundTx = getTxFromRawTxBytes(rawTx);
29374
- const refundTxSighash = getSigHashFromTx(refundTx, 0, parentTxOutput);
29375
- const refundSigningResponse = await this.config.signer.signFrost({
29376
- message: refundTxSighash,
30560
+ const directNodeTx = getTxFromRawTxBytes(
30561
+ creationNode.directNodeTxSigningJob.rawTx
30562
+ );
30563
+ const directNodeTxSighash = getSigHashFromTx(
30564
+ directNodeTx,
30565
+ 0,
30566
+ parentTxOutput
30567
+ );
30568
+ let directNodeTxSignature = new Uint8Array();
30569
+ if (creationNode.directNodeTxSigningCommitment) {
30570
+ const directUserSignature = await this.config.signer.signFrost({
30571
+ message: directNodeTxSighash,
30572
+ publicKey: creationNode.directNodeTxSigningJob.signingPublicKey,
30573
+ keyDerivation: {
30574
+ type: "leaf" /* LEAF */,
30575
+ path: creationResponseNode.nodeId
30576
+ },
30577
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
30578
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
30579
+ verifyingKey: internalNode.verificationKey
30580
+ });
30581
+ directNodeTxSignature = await this.config.signer.aggregateFrost({
30582
+ message: directNodeTxSighash,
30583
+ statechainSignatures: creationResponseNode.directNodeTxSigningResult?.signatureShares,
30584
+ statechainPublicKeys: creationResponseNode.directNodeTxSigningResult?.publicKeys,
30585
+ verifyingKey: internalNode.verificationKey,
30586
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
30587
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
30588
+ selfSignature: directUserSignature,
30589
+ publicKey: internalNode.signingPublicKey
30590
+ });
30591
+ }
30592
+ let cpfpRefundTxSignature = new Uint8Array();
30593
+ let directRefundTxSignature = new Uint8Array();
30594
+ let directFromCpfpRefundTxSignature = new Uint8Array();
30595
+ if (creationNode.refundTxSigningCommitment && creationNode.directRefundTxSigningCommitment && creationNode.directFromCpfpRefundTxSigningCommitment) {
30596
+ const rawCpfpRefundTx = creationNode.refundTxSigningJob?.rawTx;
30597
+ const rawDirectRefundTx = creationNode.directRefundTxSigningJob?.rawTx;
30598
+ const rawDirectFromCpfpRefundTx = creationNode.directFromCpfpRefundTxSigningJob?.rawTx;
30599
+ if (!rawCpfpRefundTx || !rawDirectRefundTx || !rawDirectFromCpfpRefundTx) {
30600
+ throw new Error("refund transaction rawTx is undefined");
30601
+ }
30602
+ if (!creationNode.refundTxSigningJob?.signingPublicKey || !creationNode.directRefundTxSigningJob?.signingPublicKey || !creationNode.directFromCpfpRefundTxSigningJob?.signingPublicKey) {
30603
+ throw new Error("refund transaction signingPublicKey is undefined");
30604
+ }
30605
+ const cpfpRefundTx = getTxFromRawTxBytes(rawCpfpRefundTx);
30606
+ const directRefundTx = getTxFromRawTxBytes(rawDirectRefundTx);
30607
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
30608
+ rawDirectFromCpfpRefundTx
30609
+ );
30610
+ const cpfpRefundTxSighash = getSigHashFromTx(
30611
+ cpfpRefundTx,
30612
+ 0,
30613
+ cpfpNodeTx.getOutput(0)
30614
+ );
30615
+ const directRefundTxSighash = getSigHashFromTx(
30616
+ directRefundTx,
30617
+ 0,
30618
+ directNodeTx.getOutput(0)
30619
+ );
30620
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
30621
+ directFromCpfpRefundTx,
30622
+ 0,
30623
+ cpfpNodeTx.getOutput(0)
30624
+ );
30625
+ const cpfpRefundUserSignature = await this.config.signer.signFrost({
30626
+ message: cpfpRefundTxSighash,
29377
30627
  publicKey: creationNode.refundTxSigningJob.signingPublicKey,
29378
30628
  keyDerivation: {
29379
30629
  type: "leaf" /* LEAF */,
@@ -29383,27 +30633,69 @@ var TreeCreationService = class {
29383
30633
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
29384
30634
  verifyingKey: internalNode.verificationKey
29385
30635
  });
29386
- refundTxSignature = await this.config.signer.aggregateFrost({
29387
- message: refundTxSighash,
30636
+ cpfpRefundTxSignature = await this.config.signer.aggregateFrost({
30637
+ message: cpfpRefundTxSighash,
29388
30638
  statechainSignatures: creationResponseNode.refundTxSigningResult?.signatureShares,
29389
30639
  statechainPublicKeys: creationResponseNode.refundTxSigningResult?.publicKeys,
29390
30640
  verifyingKey: internalNode.verificationKey,
29391
30641
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
29392
30642
  selfCommitment: creationNode.refundTxSigningCommitment,
29393
- selfSignature: refundSigningResponse,
30643
+ selfSignature: cpfpRefundUserSignature,
30644
+ publicKey: internalNode.signingPublicKey
30645
+ });
30646
+ const keyDerivation = {
30647
+ type: "leaf" /* LEAF */,
30648
+ path: creationResponseNode.nodeId
30649
+ };
30650
+ const directRefundUserSignature = await this.config.signer.signFrost({
30651
+ message: directRefundTxSighash,
30652
+ publicKey: creationNode.directRefundTxSigningJob.signingPublicKey,
30653
+ keyDerivation,
30654
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
30655
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
30656
+ verifyingKey: internalNode.verificationKey
30657
+ });
30658
+ directRefundTxSignature = await this.config.signer.aggregateFrost({
30659
+ message: directRefundTxSighash,
30660
+ statechainSignatures: creationResponseNode.directRefundTxSigningResult?.signatureShares,
30661
+ statechainPublicKeys: creationResponseNode.directRefundTxSigningResult?.publicKeys,
30662
+ verifyingKey: internalNode.verificationKey,
30663
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
30664
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
30665
+ selfSignature: directRefundUserSignature,
29394
30666
  publicKey: internalNode.signingPublicKey
29395
30667
  });
30668
+ const directFromCpfpRefundUserSignature = await this.config.signer.signFrost({
30669
+ message: directFromCpfpRefundTxSighash,
30670
+ publicKey: creationNode.directFromCpfpRefundTxSigningJob.signingPublicKey,
30671
+ keyDerivation,
30672
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
30673
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
30674
+ verifyingKey: internalNode.verificationKey
30675
+ });
30676
+ directFromCpfpRefundTxSignature = await this.config.signer.aggregateFrost(
30677
+ {
30678
+ message: directFromCpfpRefundTxSighash,
30679
+ statechainSignatures: creationResponseNode.directFromCpfpRefundTxSigningResult?.signatureShares,
30680
+ statechainPublicKeys: creationResponseNode.directFromCpfpRefundTxSigningResult?.publicKeys,
30681
+ verifyingKey: internalNode.verificationKey,
30682
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
30683
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
30684
+ selfSignature: directFromCpfpRefundUserSignature,
30685
+ publicKey: internalNode.signingPublicKey
30686
+ }
30687
+ );
29396
30688
  }
29397
30689
  return {
29398
- tx,
30690
+ tx: cpfpNodeTx,
30691
+ // Return CPFP version for children
29399
30692
  signature: {
29400
30693
  nodeId: creationResponseNode.nodeId,
29401
- nodeTxSignature,
29402
- refundTxSignature,
29403
- // TODO: Add direct refund signature
29404
- directNodeTxSignature: new Uint8Array(),
29405
- directRefundTxSignature: new Uint8Array(),
29406
- directFromCpfpRefundTxSignature: new Uint8Array()
30694
+ nodeTxSignature: cpfpNodeTxSignature,
30695
+ directNodeTxSignature,
30696
+ refundTxSignature: cpfpRefundTxSignature,
30697
+ directRefundTxSignature,
30698
+ directFromCpfpRefundTxSignature
29407
30699
  }
29408
30700
  };
29409
30701
  }
@@ -29466,8 +30758,45 @@ var SigningService = class {
29466
30758
  constructor(config) {
29467
30759
  this.config = config;
29468
30760
  }
29469
- async signRefunds(leaves, signingCommitments, receiverIdentityPubkey) {
29470
- const leafSigningJobs = [];
30761
+ async signRefundsInternal(refundTx, sighash, leaf, signingCommitments) {
30762
+ const leafSigningJobs = [];
30763
+ const signingCommitment = await this.config.signer.getRandomSigningCommitment();
30764
+ if (!signingCommitments) {
30765
+ throw new ValidationError("Invalid signing commitments", {
30766
+ field: "signingNonceCommitments",
30767
+ value: signingCommitments,
30768
+ expected: "Non-null signing commitments"
30769
+ });
30770
+ }
30771
+ const signingResult = await this.config.signer.signFrost({
30772
+ message: sighash,
30773
+ keyDerivation: leaf.keyDerivation,
30774
+ publicKey: await this.config.signer.getPublicKeyFromDerivation(
30775
+ leaf.keyDerivation
30776
+ ),
30777
+ selfCommitment: signingCommitment,
30778
+ statechainCommitments: signingCommitments,
30779
+ adaptorPubKey: new Uint8Array(),
30780
+ verifyingKey: leaf.leaf.verifyingPublicKey
30781
+ });
30782
+ leafSigningJobs.push({
30783
+ leafId: leaf.leaf.id,
30784
+ signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
30785
+ leaf.keyDerivation
30786
+ ),
30787
+ rawTx: refundTx.toBytes(),
30788
+ signingNonceCommitment: signingCommitment.commitment,
30789
+ userSignature: signingResult,
30790
+ signingCommitments: {
30791
+ signingCommitments
30792
+ }
30793
+ });
30794
+ return leafSigningJobs;
30795
+ }
30796
+ async signRefunds(leaves, receiverIdentityPubkey, cpfpSigningCommitments, directSigningCommitments, directFromCpfpSigningCommitments) {
30797
+ const cpfpLeafSigningJobs = [];
30798
+ const directLeafSigningJobs = [];
30799
+ const directFromCpfpLeafSigningJobs = [];
29471
30800
  for (let i = 0; i < leaves.length; i++) {
29472
30801
  const leaf = leaves[i];
29473
30802
  if (!leaf?.leaf) {
@@ -29478,14 +30807,20 @@ var SigningService = class {
29478
30807
  });
29479
30808
  }
29480
30809
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
29481
- const nodeOutPoint = {
30810
+ const cpfpNodeOutPoint = {
29482
30811
  txid: (0, import_utils21.hexToBytes)(getTxId(nodeTx)),
29483
30812
  index: 0
29484
30813
  };
29485
30814
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
29486
- const { nextSequence } = getNextTransactionSequence(
29487
- currRefundTx.getInput(0).sequence
29488
- );
30815
+ const sequence = currRefundTx.getInput(0).sequence;
30816
+ if (!sequence) {
30817
+ throw new ValidationError("Invalid refund transaction", {
30818
+ field: "sequence",
30819
+ value: currRefundTx.getInput(0),
30820
+ expected: "Non-null sequence"
30821
+ });
30822
+ }
30823
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
29489
30824
  const amountSats = currRefundTx.getOutput(0).amount;
29490
30825
  if (amountSats === void 0) {
29491
30826
  throw new ValidationError("Invalid refund transaction", {
@@ -29494,48 +30829,90 @@ var SigningService = class {
29494
30829
  expected: "Non-null amount"
29495
30830
  });
29496
30831
  }
29497
- const refundTx = createRefundTx(
29498
- nextSequence,
29499
- nodeOutPoint,
30832
+ let directNodeTx;
30833
+ let directNodeOutPoint;
30834
+ if (leaf.leaf.directTx.length > 0) {
30835
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
30836
+ directNodeOutPoint = {
30837
+ txid: (0, import_utils21.hexToBytes)(getTxId(directNodeTx)),
30838
+ index: 0
30839
+ };
30840
+ }
30841
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
30842
+ sequence: nextSequence,
30843
+ directSequence: nextDirectSequence,
30844
+ input: cpfpNodeOutPoint,
30845
+ directInput: directNodeOutPoint,
29500
30846
  amountSats,
29501
- receiverIdentityPubkey,
29502
- this.config.getNetwork()
30847
+ receivingPubkey: receiverIdentityPubkey,
30848
+ network: this.config.getNetwork()
30849
+ });
30850
+ const refundSighash = getSigHashFromTx(
30851
+ cpfpRefundTx,
30852
+ 0,
30853
+ nodeTx.getOutput(0)
29503
30854
  );
29504
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
29505
- const signingCommitment = await this.config.signer.getRandomSigningCommitment();
29506
- const signingNonceCommitments = signingCommitments[i]?.signingNonceCommitments;
29507
- if (!signingNonceCommitments) {
29508
- throw new ValidationError("Invalid signing commitments", {
29509
- field: "signingNonceCommitments",
29510
- value: signingCommitments[i],
29511
- expected: "Non-null signing nonce commitments"
29512
- });
30855
+ const signingJobs = await this.signRefundsInternal(
30856
+ cpfpRefundTx,
30857
+ refundSighash,
30858
+ leaf,
30859
+ cpfpSigningCommitments[i]?.signingNonceCommitments
30860
+ );
30861
+ cpfpLeafSigningJobs.push(...signingJobs);
30862
+ if (directRefundTx) {
30863
+ if (!directNodeTx) {
30864
+ throw new ValidationError(
30865
+ "Direct node transaction undefined while direct refund transaction is defined",
30866
+ {
30867
+ field: "directNodeTx",
30868
+ value: directNodeTx,
30869
+ expected: "Non-null direct node transaction"
30870
+ }
30871
+ );
30872
+ }
30873
+ const refundSighash2 = getSigHashFromTx(
30874
+ directRefundTx,
30875
+ 0,
30876
+ directNodeTx.getOutput(0)
30877
+ );
30878
+ const signingJobs2 = await this.signRefundsInternal(
30879
+ directRefundTx,
30880
+ refundSighash2,
30881
+ leaf,
30882
+ directSigningCommitments[i]?.signingNonceCommitments
30883
+ );
30884
+ directLeafSigningJobs.push(...signingJobs2);
29513
30885
  }
29514
- const signingResult = await this.config.signer.signFrost({
29515
- message: sighash,
29516
- keyDerivation: leaf.keyDerivation,
29517
- publicKey: await this.config.signer.getPublicKeyFromDerivation(
29518
- leaf.keyDerivation
29519
- ),
29520
- selfCommitment: signingCommitment,
29521
- statechainCommitments: signingNonceCommitments,
29522
- adaptorPubKey: new Uint8Array(),
29523
- verifyingKey: leaf.leaf.verifyingPublicKey
29524
- });
29525
- leafSigningJobs.push({
29526
- leafId: leaf.leaf.id,
29527
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
29528
- leaf.keyDerivation
29529
- ),
29530
- rawTx: refundTx.toBytes(),
29531
- signingNonceCommitment: signingCommitment.commitment,
29532
- userSignature: signingResult,
29533
- signingCommitments: {
29534
- signingCommitments: signingNonceCommitments
30886
+ if (directFromCpfpRefundTx) {
30887
+ if (!directNodeTx) {
30888
+ throw new ValidationError(
30889
+ "Direct node transaction undefined while direct from CPFP refund transaction is defined",
30890
+ {
30891
+ field: "directNodeTx",
30892
+ value: directNodeTx,
30893
+ expected: "Non-null direct node transaction"
30894
+ }
30895
+ );
29535
30896
  }
29536
- });
30897
+ const refundSighash2 = getSigHashFromTx(
30898
+ directFromCpfpRefundTx,
30899
+ 0,
30900
+ nodeTx.getOutput(0)
30901
+ );
30902
+ const signingJobs2 = await this.signRefundsInternal(
30903
+ directFromCpfpRefundTx,
30904
+ refundSighash2,
30905
+ leaf,
30906
+ directFromCpfpSigningCommitments[i]?.signingNonceCommitments
30907
+ );
30908
+ directFromCpfpLeafSigningJobs.push(...signingJobs2);
30909
+ }
29537
30910
  }
29538
- return leafSigningJobs;
30911
+ return {
30912
+ cpfpLeafSigningJobs,
30913
+ directLeafSigningJobs,
30914
+ directFromCpfpLeafSigningJobs
30915
+ };
29539
30916
  }
29540
30917
  };
29541
30918
 
@@ -29543,7 +30920,7 @@ var SigningService = class {
29543
30920
  init_buffer();
29544
30921
  var import_utils22 = require("@noble/curves/abstract/utils");
29545
30922
  var import_secp256k114 = require("@noble/curves/secp256k1");
29546
- var btc6 = __toESM(require("@scure/btc-signer"), 1);
30923
+ var btc5 = __toESM(require("@scure/btc-signer"), 1);
29547
30924
  var import_btc_signer6 = require("@scure/btc-signer");
29548
30925
  var import_utils23 = require("@scure/btc-signer/utils");
29549
30926
  var STATIC_FAUCET_KEY = (0, import_utils22.hexToBytes)(
@@ -29689,7 +31066,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
29689
31066
  }
29690
31067
  async sendFaucetCoinToP2WPKHAddress(pubKey) {
29691
31068
  const sendToPubKeyTx = new import_btc_signer6.Transaction();
29692
- const p2wpkhAddress = btc6.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
31069
+ const p2wpkhAddress = btc5.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
29693
31070
  if (!p2wpkhAddress) {
29694
31071
  throw new Error("Invalid P2WPKH address");
29695
31072
  }
@@ -29745,12 +31122,13 @@ var BitcoinFaucet = class _BitcoinFaucet {
29745
31122
  }
29746
31123
  async call(method, params) {
29747
31124
  try {
29748
- const response = await fetch(this.url, {
31125
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
31126
+ const response = await fetch2(this.url, {
29749
31127
  method: "POST",
29750
- headers: {
31128
+ headers: new Headers2({
29751
31129
  "Content-Type": "application/json",
29752
31130
  Authorization: "Basic " + btoa(`${this.username}:${this.password}`)
29753
- },
31131
+ }),
29754
31132
  body: JSON.stringify({
29755
31133
  jsonrpc: "1.0",
29756
31134
  id: "spark-js",
@@ -30080,31 +31458,37 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30080
31458
  }
30081
31459
  }
30082
31460
  async getLeaves(isBalanceCheck = false) {
30083
- const leaves = await this.queryNodes({
30084
- source: {
30085
- $case: "ownerIdentityPubkey",
30086
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
30087
- },
30088
- includeParents: false,
30089
- network: NetworkToProto[this.config.getNetwork()]
30090
- });
31461
+ const operatorToLeaves = /* @__PURE__ */ new Map();
31462
+ const ownerIdentityPubkey = await this.config.signer.getIdentityPublicKey();
31463
+ let signingOperators = Object.entries(this.config.getSigningOperators());
31464
+ if (isBalanceCheck) {
31465
+ signingOperators = signingOperators.filter(
31466
+ ([id, _]) => id === this.config.getCoordinatorIdentifier()
31467
+ );
31468
+ }
31469
+ await Promise.all(
31470
+ signingOperators.map(async ([id, operator]) => {
31471
+ const leaves2 = await this.queryNodes(
31472
+ {
31473
+ source: {
31474
+ $case: "ownerIdentityPubkey",
31475
+ ownerIdentityPubkey
31476
+ },
31477
+ includeParents: false,
31478
+ network: NetworkToProto[this.config.getNetwork()]
31479
+ },
31480
+ operator.address
31481
+ );
31482
+ operatorToLeaves.set(id, leaves2);
31483
+ })
31484
+ );
31485
+ const leaves = operatorToLeaves.get(
31486
+ this.config.getCoordinatorIdentifier()
31487
+ );
30091
31488
  const leavesToIgnore = /* @__PURE__ */ new Set();
30092
31489
  if (!isBalanceCheck) {
30093
- for (const [id, operator] of Object.entries(
30094
- this.config.getSigningOperators()
30095
- )) {
31490
+ for (const [id, operatorLeaves] of operatorToLeaves) {
30096
31491
  if (id !== this.config.getCoordinatorIdentifier()) {
30097
- const operatorLeaves = await this.queryNodes(
30098
- {
30099
- source: {
30100
- $case: "ownerIdentityPubkey",
30101
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
30102
- },
30103
- includeParents: false,
30104
- network: NetworkToProto[this.config.getNetwork()]
30105
- },
30106
- operator.address
30107
- );
30108
31492
  for (const [nodeId, leaf] of Object.entries(leaves.nodes)) {
30109
31493
  const operatorLeaf = operatorLeaves.nodes[nodeId];
30110
31494
  if (!operatorLeaf) {
@@ -30545,21 +31929,68 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30545
31929
  }
30546
31930
  }))
30547
31931
  );
30548
- const { transfer, signatureMap } = await this.transferService.startSwapSignRefund(
31932
+ const {
31933
+ transfer,
31934
+ signatureMap,
31935
+ directSignatureMap,
31936
+ directFromCpfpSignatureMap
31937
+ } = await this.transferService.startSwapSignRefund(
30549
31938
  leafKeyTweaks,
30550
31939
  (0, import_utils24.hexToBytes)(this.config.getSspIdentityPublicKey()),
30551
31940
  new Date(Date.now() + 2 * 60 * 1e3)
30552
31941
  );
30553
31942
  try {
30554
31943
  if (!transfer.leaves[0]?.leaf) {
31944
+ console.error("[processSwapBatch] First leaf is missing");
30555
31945
  throw new Error("Failed to get leaf");
30556
31946
  }
30557
- const refundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
30558
- if (!refundSignature) {
30559
- throw new Error("Failed to get refund signature");
31947
+ const cpfpRefundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
31948
+ if (!cpfpRefundSignature) {
31949
+ console.error(
31950
+ "[processSwapBatch] Missing CPFP refund signature for first leaf"
31951
+ );
31952
+ throw new Error("Failed to get CPFP refund signature");
31953
+ }
31954
+ const directRefundSignature = directSignatureMap.get(
31955
+ transfer.leaves[0].leaf.id
31956
+ );
31957
+ if (!directRefundSignature) {
31958
+ console.error(
31959
+ "[processSwapBatch] Missing direct refund signature for first leaf"
31960
+ );
31961
+ throw new Error("Failed to get direct refund signature");
31962
+ }
31963
+ const directFromCpfpRefundSignature = directFromCpfpSignatureMap.get(
31964
+ transfer.leaves[0].leaf.id
31965
+ );
31966
+ if (!directFromCpfpRefundSignature) {
31967
+ console.error(
31968
+ "[processSwapBatch] Missing direct from CPFP refund signature for first leaf"
31969
+ );
31970
+ throw new Error("Failed to get direct from CPFP refund signature");
31971
+ }
31972
+ const {
31973
+ adaptorPrivateKey: cpfpAdaptorPrivateKey,
31974
+ adaptorSignature: cpfpAdaptorSignature
31975
+ } = generateAdaptorFromSignature(cpfpRefundSignature);
31976
+ let directAdaptorPrivateKey = new Uint8Array();
31977
+ let directAdaptorSignature = new Uint8Array();
31978
+ let directFromCpfpAdaptorPrivateKey = new Uint8Array();
31979
+ let directFromCpfpAdaptorSignature = new Uint8Array();
31980
+ if (directRefundSignature.length > 0) {
31981
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directRefundSignature);
31982
+ directAdaptorPrivateKey = adaptorPrivateKey;
31983
+ directAdaptorSignature = adaptorSignature;
31984
+ }
31985
+ if (directFromCpfpRefundSignature.length > 0) {
31986
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directFromCpfpRefundSignature);
31987
+ directFromCpfpAdaptorPrivateKey = adaptorPrivateKey;
31988
+ directFromCpfpAdaptorSignature = adaptorSignature;
30560
31989
  }
30561
- const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(refundSignature);
30562
31990
  if (!transfer.leaves[0].leaf) {
31991
+ console.error(
31992
+ "[processSwapBatch] First leaf missing when preparing user leaves"
31993
+ );
30563
31994
  throw new Error("Failed to get leaf");
30564
31995
  }
30565
31996
  const userLeaves = [];
@@ -30568,37 +31999,113 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30568
31999
  raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
30569
32000
  transfer.leaves[0].intermediateRefundTx
30570
32001
  ),
30571
- adaptor_added_signature: (0, import_utils24.bytesToHex)(adaptorSignature)
32002
+ direct_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
32003
+ transfer.leaves[0].intermediateDirectRefundTx
32004
+ ),
32005
+ direct_from_cpfp_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
32006
+ transfer.leaves[0].intermediateDirectFromCpfpRefundTx
32007
+ ),
32008
+ adaptor_added_signature: (0, import_utils24.bytesToHex)(cpfpAdaptorSignature),
32009
+ direct_adaptor_added_signature: (0, import_utils24.bytesToHex)(directAdaptorSignature),
32010
+ direct_from_cpfp_adaptor_added_signature: (0, import_utils24.bytesToHex)(
32011
+ directFromCpfpAdaptorSignature
32012
+ )
30572
32013
  });
30573
32014
  for (let i = 1; i < transfer.leaves.length; i++) {
30574
32015
  const leaf = transfer.leaves[i];
30575
32016
  if (!leaf?.leaf) {
32017
+ console.error(`[processSwapBatch] Leaf ${i + 1} is missing`);
30576
32018
  throw new Error("Failed to get leaf");
30577
32019
  }
30578
- const refundSignature2 = signatureMap.get(leaf.leaf.id);
30579
- if (!refundSignature2) {
30580
- throw new Error("Failed to get refund signature");
32020
+ const cpfpRefundSignature2 = signatureMap.get(leaf.leaf.id);
32021
+ if (!cpfpRefundSignature2) {
32022
+ console.error(
32023
+ `[processSwapBatch] Missing CPFP refund signature for leaf ${i + 1}`
32024
+ );
32025
+ throw new Error("Failed to get CPFP refund signature");
32026
+ }
32027
+ const directRefundSignature2 = directSignatureMap.get(leaf.leaf.id);
32028
+ if (!directRefundSignature2) {
32029
+ console.error(
32030
+ `[processSwapBatch] Missing direct refund signature for leaf ${i + 1}`
32031
+ );
32032
+ throw new Error("Failed to get direct refund signature");
32033
+ }
32034
+ const directFromCpfpRefundSignature2 = directFromCpfpSignatureMap.get(
32035
+ leaf.leaf.id
32036
+ );
32037
+ if (!directFromCpfpRefundSignature2) {
32038
+ console.error(
32039
+ `[processSwapBatch] Missing direct from CPFP refund signature for leaf ${i + 1}`
32040
+ );
32041
+ throw new Error("Failed to get direct from CPFP refund signature");
30581
32042
  }
30582
- const signature = generateSignatureFromExistingAdaptor(
30583
- refundSignature2,
30584
- adaptorPrivateKey
32043
+ const cpfpSignature = generateSignatureFromExistingAdaptor(
32044
+ cpfpRefundSignature2,
32045
+ cpfpAdaptorPrivateKey
30585
32046
  );
32047
+ let directSignature = new Uint8Array();
32048
+ if (directRefundSignature2.length > 0) {
32049
+ directSignature = generateSignatureFromExistingAdaptor(
32050
+ directRefundSignature2,
32051
+ directAdaptorPrivateKey
32052
+ );
32053
+ }
32054
+ let directFromCpfpSignature = new Uint8Array();
32055
+ if (directFromCpfpRefundSignature2.length > 0) {
32056
+ directFromCpfpSignature = generateSignatureFromExistingAdaptor(
32057
+ directFromCpfpRefundSignature2,
32058
+ directFromCpfpAdaptorPrivateKey
32059
+ );
32060
+ }
30586
32061
  userLeaves.push({
30587
32062
  leaf_id: leaf.leaf.id,
30588
32063
  raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
30589
32064
  leaf.intermediateRefundTx
30590
32065
  ),
30591
- adaptor_added_signature: (0, import_utils24.bytesToHex)(signature)
32066
+ direct_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
32067
+ leaf.intermediateDirectRefundTx
32068
+ ),
32069
+ direct_from_cpfp_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
32070
+ leaf.intermediateDirectFromCpfpRefundTx
32071
+ ),
32072
+ adaptor_added_signature: (0, import_utils24.bytesToHex)(cpfpSignature),
32073
+ direct_adaptor_added_signature: (0, import_utils24.bytesToHex)(directSignature),
32074
+ direct_from_cpfp_adaptor_added_signature: (0, import_utils24.bytesToHex)(
32075
+ directFromCpfpSignature
32076
+ )
30592
32077
  });
30593
32078
  }
30594
32079
  const sspClient = this.getSspClient();
30595
- const adaptorPubkey = (0, import_utils24.bytesToHex)(
30596
- import_secp256k115.secp256k1.getPublicKey(adaptorPrivateKey)
32080
+ const cpfpAdaptorPubkey = (0, import_utils24.bytesToHex)(
32081
+ import_secp256k115.secp256k1.getPublicKey(cpfpAdaptorPrivateKey)
30597
32082
  );
32083
+ if (!cpfpAdaptorPubkey) {
32084
+ throw new Error("Failed to generate CPFP adaptor pubkey");
32085
+ }
32086
+ let directAdaptorPubkey;
32087
+ if (directAdaptorPrivateKey.length > 0) {
32088
+ directAdaptorPubkey = (0, import_utils24.bytesToHex)(
32089
+ import_secp256k115.secp256k1.getPublicKey(directAdaptorPrivateKey)
32090
+ );
32091
+ }
32092
+ let directFromCpfpAdaptorPubkey;
32093
+ if (directFromCpfpAdaptorPrivateKey.length > 0) {
32094
+ directFromCpfpAdaptorPubkey = (0, import_utils24.bytesToHex)(
32095
+ import_secp256k115.secp256k1.getPublicKey(directFromCpfpAdaptorPrivateKey)
32096
+ );
32097
+ }
30598
32098
  let request = null;
32099
+ const targetAmountSats = targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0);
32100
+ const totalAmountSats = leavesBatch.reduce(
32101
+ (acc, leaf) => acc + leaf.value,
32102
+ 0
32103
+ );
30599
32104
  request = await sspClient.requestLeaveSwap({
30600
32105
  userLeaves,
30601
- adaptorPubkey,
32106
+ adaptorPubkey: cpfpAdaptorPubkey,
32107
+ directAdaptorPubkey,
32108
+ directFromCpfpAdaptorPubkey,
30602
32109
  targetAmountSats: targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
30603
32110
  totalAmountSats: leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
30604
32111
  targetAmountSatsList: targetAmounts,
@@ -30607,6 +32114,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30607
32114
  idempotencyKey: (0, import_uuidv75.uuidv7)()
30608
32115
  });
30609
32116
  if (!request) {
32117
+ console.error("[processSwapBatch] Leave swap request returned null");
30610
32118
  throw new Error("Failed to request leaves swap. No response returned.");
30611
32119
  }
30612
32120
  const nodes = await this.queryNodes({
@@ -30620,50 +32128,140 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30620
32128
  network: NetworkToProto[this.config.getNetwork()]
30621
32129
  });
30622
32130
  if (Object.values(nodes.nodes).length !== request.swapLeaves.length) {
32131
+ console.error("[processSwapBatch] Node count mismatch:", {
32132
+ actual: Object.values(nodes.nodes).length,
32133
+ expected: request.swapLeaves.length
32134
+ });
30623
32135
  throw new Error("Expected same number of nodes as swapLeaves");
30624
32136
  }
30625
32137
  for (const [nodeId, node] of Object.entries(nodes.nodes)) {
30626
32138
  if (!node.nodeTx) {
32139
+ console.error(`[processSwapBatch] Node tx missing for ${nodeId}`);
30627
32140
  throw new Error(`Node tx not found for leaf ${nodeId}`);
30628
32141
  }
30629
32142
  if (!node.verifyingPublicKey) {
32143
+ console.error(
32144
+ `[processSwapBatch] Verifying public key missing for ${nodeId}`
32145
+ );
30630
32146
  throw new Error(`Node public key not found for leaf ${nodeId}`);
30631
32147
  }
30632
32148
  const leaf = request.swapLeaves.find((leaf2) => leaf2.leafId === nodeId);
30633
32149
  if (!leaf) {
32150
+ console.error(`[processSwapBatch] Leaf not found for node ${nodeId}`);
30634
32151
  throw new Error(`Leaf not found for node ${nodeId}`);
30635
32152
  }
30636
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
30637
- const refundTxBytes = (0, import_utils24.hexToBytes)(leaf.rawUnsignedRefundTransaction);
30638
- const refundTx = getTxFromRawTxBytes(refundTxBytes);
30639
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
32153
+ const cpfpNodeTx = getTxFromRawTxBytes(node.nodeTx);
32154
+ const cpfpRefundTxBytes = (0, import_utils24.hexToBytes)(leaf.rawUnsignedRefundTransaction);
32155
+ const cpfpRefundTx = getTxFromRawTxBytes(cpfpRefundTxBytes);
32156
+ const cpfpSighash = getSigHashFromTx(
32157
+ cpfpRefundTx,
32158
+ 0,
32159
+ cpfpNodeTx.getOutput(0)
32160
+ );
30640
32161
  const nodePublicKey = node.verifyingPublicKey;
30641
32162
  const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
30642
- const adaptorSignatureBytes = (0, import_utils24.hexToBytes)(leaf.adaptorSignedSignature);
32163
+ const cpfpAdaptorSignatureBytes = (0, import_utils24.hexToBytes)(
32164
+ leaf.adaptorSignedSignature
32165
+ );
32166
+ applyAdaptorToSignature(
32167
+ taprootKey.slice(1),
32168
+ cpfpSighash,
32169
+ cpfpAdaptorSignatureBytes,
32170
+ cpfpAdaptorPrivateKey
32171
+ );
32172
+ if (!leaf.directRawUnsignedRefundTransaction) {
32173
+ throw new Error(
32174
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
32175
+ );
32176
+ }
32177
+ if (!leaf.directAdaptorSignedSignature) {
32178
+ throw new Error(
32179
+ `Direct adaptor signed signature missing for node ${nodeId}`
32180
+ );
32181
+ }
32182
+ const directNodeTx = getTxFromRawTxBytes(node.directTx);
32183
+ const directRefundTxBytes = (0, import_utils24.hexToBytes)(
32184
+ leaf.directRawUnsignedRefundTransaction
32185
+ );
32186
+ const directRefundTx = getTxFromRawTxBytes(directRefundTxBytes);
32187
+ const directSighash = getSigHashFromTx(
32188
+ directRefundTx,
32189
+ 0,
32190
+ directNodeTx.getOutput(0)
32191
+ );
32192
+ if (!leaf.directFromCpfpAdaptorSignedSignature) {
32193
+ throw new Error(
32194
+ `Direct adaptor signed signature missing for node ${nodeId}`
32195
+ );
32196
+ }
32197
+ const directAdaptorSignatureBytes = (0, import_utils24.hexToBytes)(
32198
+ leaf.directAdaptorSignedSignature
32199
+ );
32200
+ applyAdaptorToSignature(
32201
+ taprootKey.slice(1),
32202
+ directSighash,
32203
+ directAdaptorSignatureBytes,
32204
+ directAdaptorPrivateKey
32205
+ );
32206
+ if (!leaf.directRawUnsignedRefundTransaction) {
32207
+ throw new Error(
32208
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
32209
+ );
32210
+ }
32211
+ if (!leaf.directFromCpfpRawUnsignedRefundTransaction) {
32212
+ throw new Error(
32213
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
32214
+ );
32215
+ }
32216
+ const directFromCpfpRefundTxBytes = (0, import_utils24.hexToBytes)(
32217
+ leaf.directFromCpfpRawUnsignedRefundTransaction
32218
+ );
32219
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
32220
+ directFromCpfpRefundTxBytes
32221
+ );
32222
+ const directFromCpfpSighash = getSigHashFromTx(
32223
+ directFromCpfpRefundTx,
32224
+ 0,
32225
+ cpfpNodeTx.getOutput(0)
32226
+ );
32227
+ const directFromCpfpAdaptorSignatureBytes = (0, import_utils24.hexToBytes)(
32228
+ leaf.directFromCpfpAdaptorSignedSignature
32229
+ );
30643
32230
  applyAdaptorToSignature(
30644
32231
  taprootKey.slice(1),
30645
- sighash,
30646
- adaptorSignatureBytes,
30647
- adaptorPrivateKey
32232
+ directFromCpfpSighash,
32233
+ directFromCpfpAdaptorSignatureBytes,
32234
+ directFromCpfpAdaptorPrivateKey
30648
32235
  );
30649
32236
  }
30650
32237
  await this.transferService.deliverTransferPackage(
30651
32238
  transfer,
30652
32239
  leafKeyTweaks,
30653
- signatureMap
32240
+ signatureMap,
32241
+ directSignatureMap,
32242
+ directFromCpfpSignatureMap
30654
32243
  );
30655
32244
  const completeResponse = await sspClient.completeLeaveSwap({
30656
- adaptorSecretKey: (0, import_utils24.bytesToHex)(adaptorPrivateKey),
32245
+ adaptorSecretKey: (0, import_utils24.bytesToHex)(cpfpAdaptorPrivateKey),
32246
+ directAdaptorSecretKey: (0, import_utils24.bytesToHex)(directAdaptorPrivateKey),
32247
+ directFromCpfpAdaptorSecretKey: (0, import_utils24.bytesToHex)(
32248
+ directFromCpfpAdaptorPrivateKey
32249
+ ),
30657
32250
  userOutboundTransferExternalId: transfer.id,
30658
32251
  leavesSwapRequestId: request.id
30659
32252
  });
30660
32253
  if (!completeResponse || !completeResponse.inboundTransfer?.sparkId) {
32254
+ console.error(
32255
+ "[processSwapBatch] Invalid complete response:",
32256
+ completeResponse
32257
+ );
30661
32258
  throw new Error("Failed to complete leaves swap");
30662
32259
  }
30663
32260
  const incomingTransfer = await this.transferService.queryTransfer(
30664
32261
  completeResponse.inboundTransfer.sparkId
30665
32262
  );
30666
32263
  if (!incomingTransfer) {
32264
+ console.error("[processSwapBatch] No incoming transfer found");
30667
32265
  throw new Error("Failed to get incoming transfer");
30668
32266
  }
30669
32267
  return await this.claimTransfer({
@@ -30673,6 +32271,11 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30673
32271
  optimize: false
30674
32272
  });
30675
32273
  } catch (e) {
32274
+ console.error("[processSwapBatch] Error details:", {
32275
+ error: e,
32276
+ message: e.message,
32277
+ stack: e.stack
32278
+ });
30676
32279
  await this.cancelAllSenderInitiatedTransfers();
30677
32280
  throw new Error(`Failed to request leaves swap: ${e}`);
30678
32281
  }
@@ -30692,9 +32295,22 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30692
32295
  const identityPublicKey = (0, import_utils24.bytesToHex)(
30693
32296
  await this.config.signer.getIdentityPublicKey()
30694
32297
  );
32298
+ const userRequests = await this.sspClient?.getTransfers(
32299
+ transfers.transfers.map((transfer) => transfer.id)
32300
+ );
32301
+ const userRequestsMap = /* @__PURE__ */ new Map();
32302
+ for (const userRequest of userRequests || []) {
32303
+ if (userRequest && userRequest.sparkId && userRequest.userRequest) {
32304
+ userRequestsMap.set(userRequest.sparkId, userRequest.userRequest);
32305
+ }
32306
+ }
30695
32307
  return {
30696
32308
  transfers: transfers.transfers.map(
30697
- (transfer) => mapTransferToWalletTransfer(transfer, identityPublicKey)
32309
+ (transfer) => mapTransferToWalletTransfer(
32310
+ transfer,
32311
+ identityPublicKey,
32312
+ userRequestsMap.get(transfer.id)
32313
+ )
30698
32314
  ),
30699
32315
  offset: transfers.offset
30700
32316
  };
@@ -30877,7 +32493,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30877
32493
  network = BitcoinNetwork_default.REGTEST;
30878
32494
  }
30879
32495
  if (outputIndex === void 0) {
30880
- outputIndex = await this.getDepositTransactionVout(transactionId);
32496
+ outputIndex = await this.getDepositTransactionVout({
32497
+ txid: transactionId
32498
+ });
30881
32499
  }
30882
32500
  const quote = await sspClient.getClaimDepositQuote({
30883
32501
  transactionId,
@@ -30908,7 +32526,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30908
32526
  throw new Error("SSP client not initialized");
30909
32527
  }
30910
32528
  if (outputIndex === void 0) {
30911
- outputIndex = await this.getDepositTransactionVout(transactionId);
32529
+ outputIndex = await this.getDepositTransactionVout({
32530
+ txid: transactionId
32531
+ });
30912
32532
  }
30913
32533
  let network = this.config.getSspNetwork();
30914
32534
  if (network === BitcoinNetwork_default.FUTURE_VALUE) {
@@ -30942,25 +32562,94 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30942
32562
  }
30943
32563
  return response;
30944
32564
  }
32565
+ /**
32566
+ * Get a quote on how much credit you can claim for a deposit from the SSP. If the quote charges less fees than the max fee, claim the deposit.
32567
+ *
32568
+ * @param {Object} params - The parameters object
32569
+ * @param {string} params.transactionId - The ID of the transaction
32570
+ * @param {number} params.maxFee - The maximum fee to claim the deposit for
32571
+ * @param {number} [params.outputIndex] - The index of the output
32572
+ * @returns {Promise<StaticDepositQuoteOutput>} Quote for claiming a deposit to a static deposit address
32573
+ */
32574
+ async claimStaticDepositWithMaxFee({
32575
+ transactionId,
32576
+ maxFee,
32577
+ outputIndex
32578
+ }) {
32579
+ const sspClient = this.getSspClient();
32580
+ let network = this.config.getSspNetwork();
32581
+ if (network === BitcoinNetwork_default.FUTURE_VALUE) {
32582
+ network = BitcoinNetwork_default.REGTEST;
32583
+ }
32584
+ const depositTx = await this.getDepositTransaction(transactionId);
32585
+ if (outputIndex === void 0) {
32586
+ outputIndex = await this.getDepositTransactionVout({
32587
+ txid: transactionId,
32588
+ depositTx
32589
+ });
32590
+ }
32591
+ const depositAmount = Number(depositTx.getOutput(outputIndex).amount);
32592
+ const quote = await sspClient.getClaimDepositQuote({
32593
+ transactionId,
32594
+ outputIndex,
32595
+ network
32596
+ });
32597
+ if (!quote) {
32598
+ throw new Error("Failed to get claim deposit quote");
32599
+ }
32600
+ const { creditAmountSats, signature: sspSignature } = quote;
32601
+ const feeCharged = depositAmount - creditAmountSats;
32602
+ if (feeCharged > maxFee) {
32603
+ throw new ValidationError("Fee larger than max fee", {
32604
+ field: "feeCharged",
32605
+ value: feeCharged
32606
+ });
32607
+ }
32608
+ const response = await this.claimStaticDeposit({
32609
+ transactionId,
32610
+ creditAmountSats,
32611
+ sspSignature,
32612
+ outputIndex
32613
+ });
32614
+ if (!response) {
32615
+ throw new Error("Failed to claim static deposit");
32616
+ }
32617
+ return response;
32618
+ }
30945
32619
  /**
30946
32620
  * Refunds a static deposit to a destination address.
30947
32621
  *
30948
- * @param {string} depositTransactionId - The ID of the transaction
30949
- * @param {number} [outputIndex] - The index of the output
30950
- * @param {string} destinationAddress - The destination address
30951
- * @param {number} fee - The fee to refund
32622
+ * @param {Object} params - The refund parameters
32623
+ * @param {string} params.depositTransactionId - The ID of the transaction
32624
+ * @param {number} [params.outputIndex] - The index of the output
32625
+ * @param {string} params.destinationAddress - The destination address
32626
+ * @param {number} [params.fee] - **@deprecated** The fee to refund
32627
+ * @param {number} [params.satsPerVbyteFee] - The fee per vbyte to refund
30952
32628
  * @returns {Promise<string>} The hex of the refund transaction
30953
32629
  */
30954
32630
  async refundStaticDeposit({
30955
32631
  depositTransactionId,
30956
32632
  outputIndex,
30957
32633
  destinationAddress,
30958
- fee
32634
+ fee,
32635
+ satsPerVbyteFee
30959
32636
  }) {
30960
- if (fee <= 300) {
32637
+ if (fee !== void 0) {
32638
+ console.warn(
32639
+ `refundStaticDeposit(): \`fee\` parameter is deprecated and will be removed; please switch to \`satsPerVbyteFee\`.`
32640
+ );
32641
+ }
32642
+ if (fee === void 0 && satsPerVbyteFee === void 0) {
32643
+ throw new ValidationError("Fee or satsPerVbyteFee must be provided");
32644
+ }
32645
+ if (satsPerVbyteFee && satsPerVbyteFee > 150) {
32646
+ throw new ValidationError("satsPerVbyteFee must be less than 150");
32647
+ }
32648
+ const finalFee = satsPerVbyteFee ? satsPerVbyteFee * getTxEstimatedVbytesSizeByNumberOfInputsOutputs(1, 1) : fee;
32649
+ if (finalFee <= 200) {
30961
32650
  throw new ValidationError("Fee must be greater than 300", {
30962
32651
  field: "fee",
30963
- value: fee
32652
+ value: finalFee
30964
32653
  });
30965
32654
  }
30966
32655
  let network = this.config.getNetwork();
@@ -30968,10 +32657,13 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30968
32657
  const networkJSON = networkToJSON(networkType);
30969
32658
  const depositTx = await this.getDepositTransaction(depositTransactionId);
30970
32659
  if (outputIndex === void 0) {
30971
- outputIndex = await this.getDepositTransactionVout(depositTransactionId);
32660
+ outputIndex = await this.getDepositTransactionVout({
32661
+ txid: depositTransactionId,
32662
+ depositTx
32663
+ });
30972
32664
  }
30973
32665
  const totalAmount = depositTx.getOutput(outputIndex).amount;
30974
- const creditAmountSats = Number(totalAmount) - fee;
32666
+ const creditAmountSats = Number(totalAmount) - finalFee;
30975
32667
  if (creditAmountSats <= 0) {
30976
32668
  throw new ValidationError(
30977
32669
  "Fee too large. Credit amount must be greater than 0",
@@ -31019,26 +32711,14 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31019
32711
  const sparkClient = await this.connectionManager.createSparkClient(
31020
32712
  this.config.getCoordinatorAddress()
31021
32713
  );
31022
- const transferId = (0, import_uuidv75.uuidv7)();
31023
- const swapResponse = await sparkClient.initiate_utxo_swap({
32714
+ const swapResponse = await sparkClient.initiate_static_deposit_utxo_refund({
31024
32715
  onChainUtxo: {
31025
32716
  txid: (0, import_utils24.hexToBytes)(depositTransactionId),
31026
32717
  vout: outputIndex,
31027
32718
  network: networkType
31028
32719
  },
31029
- requestType: 2 /* Refund */,
31030
- amount: {
31031
- creditAmountSats: 0,
31032
- $case: "creditAmountSats"
31033
- },
31034
32720
  userSignature: swapResponseUserSignature,
31035
- sspSignature: new Uint8Array(),
31036
- transfer: {
31037
- transferId,
31038
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
31039
- receiverIdentityPublicKey: await this.config.signer.getIdentityPublicKey()
31040
- },
31041
- spendTxSigningJob: signingJob
32721
+ refundTxSigningJob: signingJob
31042
32722
  });
31043
32723
  if (!swapResponse) {
31044
32724
  throw new Error("Failed to initiate utxo swap");
@@ -31051,15 +32731,15 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31051
32731
  path: 0
31052
32732
  },
31053
32733
  selfCommitment: signingNonceCommitment,
31054
- statechainCommitments: swapResponse.spendTxSigningResult.signingNonceCommitments,
32734
+ statechainCommitments: swapResponse.refundTxSigningResult.signingNonceCommitments,
31055
32735
  verifyingKey: swapResponse.depositAddress.verifyingPublicKey
31056
32736
  });
31057
32737
  const signatureResult = await this.config.signer.aggregateFrost({
31058
32738
  message: spendTxSighash,
31059
- statechainSignatures: swapResponse.spendTxSigningResult.signatureShares,
31060
- statechainPublicKeys: swapResponse.spendTxSigningResult.publicKeys,
32739
+ statechainSignatures: swapResponse.refundTxSigningResult.signatureShares,
32740
+ statechainPublicKeys: swapResponse.refundTxSigningResult.publicKeys,
31061
32741
  verifyingKey: swapResponse.depositAddress.verifyingPublicKey,
31062
- statechainCommitments: swapResponse.spendTxSigningResult.signingNonceCommitments,
32742
+ statechainCommitments: swapResponse.refundTxSigningResult.signingNonceCommitments,
31063
32743
  selfCommitment: signingNonceCommitment,
31064
32744
  publicKey: await this.config.signer.getStaticDepositSigningKey(0),
31065
32745
  selfSignature: userSignature
@@ -31112,8 +32792,13 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31112
32792
  }
31113
32793
  return payload;
31114
32794
  }
31115
- async getDepositTransactionVout(txid) {
31116
- const depositTx = await this.getDepositTransaction(txid);
32795
+ async getDepositTransactionVout({
32796
+ txid,
32797
+ depositTx
32798
+ }) {
32799
+ if (!depositTx) {
32800
+ depositTx = await this.getDepositTransaction(txid);
32801
+ }
31117
32802
  const staticDepositAddresses = new Set(
31118
32803
  await this.queryStaticDepositAddresses()
31119
32804
  );
@@ -31143,8 +32828,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31143
32828
  field: "txid"
31144
32829
  });
31145
32830
  }
32831
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
31146
32832
  const baseUrl = this.config.getElectrsUrl();
31147
- const headers = {};
32833
+ const headers = new Headers2();
31148
32834
  let txHex;
31149
32835
  if (this.config.getNetwork() === 4 /* LOCAL */) {
31150
32836
  const localFaucet = BitcoinFaucet.getInstance();
@@ -31155,9 +32841,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31155
32841
  const auth = btoa(
31156
32842
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
31157
32843
  );
31158
- headers["Authorization"] = `Basic ${auth}`;
32844
+ headers.set("Authorization", `Basic ${auth}`);
31159
32845
  }
31160
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
32846
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
31161
32847
  headers
31162
32848
  });
31163
32849
  txHex = await response.text();
@@ -31285,8 +32971,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31285
32971
  this.mutexes.set(txid, mutex);
31286
32972
  }
31287
32973
  const nodes = await mutex.runExclusive(async () => {
32974
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
31288
32975
  const baseUrl = this.config.getElectrsUrl();
31289
- const headers = {};
32976
+ const headers = new Headers2();
31290
32977
  let txHex;
31291
32978
  if (this.config.getNetwork() === 4 /* LOCAL */) {
31292
32979
  const localFaucet = BitcoinFaucet.getInstance();
@@ -31297,9 +32984,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31297
32984
  const auth = btoa(
31298
32985
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
31299
32986
  );
31300
- headers["Authorization"] = `Basic ${auth}`;
32987
+ headers.set("Authorization", `Basic ${auth}`);
31301
32988
  }
31302
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
32989
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
31303
32990
  headers
31304
32991
  });
31305
32992
  txHex = await response.text();
@@ -31541,10 +33228,16 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31541
33228
  const validNodes = [];
31542
33229
  for (const node of nodes) {
31543
33230
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
31544
- const { needRefresh } = getNextTransactionSequence(
31545
- nodeTx.getInput(0).sequence
31546
- );
31547
- if (needRefresh) {
33231
+ const sequence = nodeTx.getInput(0).sequence;
33232
+ if (!sequence) {
33233
+ throw new ValidationError("Invalid node transaction", {
33234
+ field: "sequence",
33235
+ value: nodeTx.getInput(0),
33236
+ expected: "Non-null sequence"
33237
+ });
33238
+ }
33239
+ const needsRefresh = doesLeafNeedRefresh(sequence, true);
33240
+ if (needsRefresh) {
31548
33241
  nodesToExtend.push(node);
31549
33242
  nodeIds.push(node.id);
31550
33243
  } else {
@@ -31581,11 +33274,16 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31581
33274
  const validNodes = [];
31582
33275
  for (const node of nodes) {
31583
33276
  const refundTx = getTxFromRawTxBytes(node.refundTx);
31584
- const { needRefresh } = getNextTransactionSequence(
31585
- refundTx.getInput(0).sequence,
31586
- true
31587
- );
31588
- if (needRefresh) {
33277
+ const sequence = refundTx.getInput(0).sequence;
33278
+ if (!sequence) {
33279
+ throw new ValidationError("Invalid refund transaction", {
33280
+ field: "sequence",
33281
+ value: refundTx.getInput(0),
33282
+ expected: "Non-null sequence"
33283
+ });
33284
+ }
33285
+ const needsRefresh = doesLeafNeedRefresh(sequence);
33286
+ if (needsRefresh) {
31589
33287
  nodesToRefresh.push(node);
31590
33288
  nodeIds.push(node.id);
31591
33289
  } else {
@@ -31619,7 +33317,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31619
33317
  throw new Error(`parent node ${node.parentNodeId} not found`);
31620
33318
  }
31621
33319
  const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
31622
- [node],
33320
+ node,
31623
33321
  parentNode
31624
33322
  );
31625
33323
  if (nodes2.length !== 1) {
@@ -31668,7 +33366,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31668
33366
  leavesToClaim.push({
31669
33367
  leaf: {
31670
33368
  ...leaf.leaf,
31671
- refundTx: leaf.intermediateRefundTx
33369
+ refundTx: leaf.intermediateRefundTx,
33370
+ directRefundTx: leaf.intermediateDirectRefundTx,
33371
+ directFromCpfpRefundTx: leaf.intermediateDirectFromCpfpRefundTx
31672
33372
  },
31673
33373
  keyDerivation: {
31674
33374
  type: "ecies" /* ECIES */,
@@ -31744,7 +33444,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31744
33444
  if (type && transfer.type !== type) {
31745
33445
  continue;
31746
33446
  }
31747
- 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 */) {
33447
+ 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 */) {
31748
33448
  continue;
31749
33449
  }
31750
33450
  promises.push(
@@ -32032,6 +33732,8 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
32032
33732
  await this.transferService.deliverTransferPackage(
32033
33733
  swapResponse.transfer,
32034
33734
  leavesToSend,
33735
+ /* @__PURE__ */ new Map(),
33736
+ /* @__PURE__ */ new Map(),
32035
33737
  /* @__PURE__ */ new Map()
32036
33738
  );
32037
33739
  const sspResponse = await sspClient.requestLightningSend({
@@ -32311,11 +34013,12 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
32311
34013
  * Gets a transfer that has been sent by the SSP to the wallet.
32312
34014
  *
32313
34015
  * @param {string} id - The ID of the transfer
32314
- * @returns {Promise<GraphQLTransferObj | null>} The transfer
34016
+ * @returns {Promise<TransferWithUserRequest | undefined>} The transfer
32315
34017
  */
32316
34018
  async getTransferFromSsp(id) {
32317
34019
  const sspClient = this.getSspClient();
32318
- return await sspClient.getTransfer(id);
34020
+ const transfers = await sspClient.getTransfers([id]);
34021
+ return transfers?.[0];
32319
34022
  }
32320
34023
  /**
32321
34024
  * Gets a transfer, that the wallet is a participant of, in the Spark network.
@@ -32794,87 +34497,64 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
32794
34497
  },
32795
34498
  includeParents: true
32796
34499
  });
32797
- const node = response.nodes[nodeId];
32798
- if (!node) {
34500
+ let leaf = response.nodes[nodeId];
34501
+ if (!leaf) {
32799
34502
  throw new ValidationError("Node not found", {
32800
34503
  field: "nodeId",
32801
34504
  value: nodeId
32802
34505
  });
32803
34506
  }
32804
- if (!node.parentNodeId) {
32805
- throw new ValidationError("Node has no parent", {
32806
- field: "parentNodeId",
32807
- value: node.parentNodeId
32808
- });
32809
- }
32810
- const parentNode = response.nodes[node.parentNodeId];
32811
- if (!parentNode) {
32812
- throw new ValidationError("Parent node not found", {
32813
- field: "parentNodeId",
32814
- value: node.parentNodeId
32815
- });
32816
- }
32817
- const result = await this.transferService.refreshTimelockNodes(
32818
- [node],
32819
- parentNode
32820
- );
32821
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
32822
- if (leafIndex !== -1 && result.nodes.length > 0) {
32823
- const newNode = result.nodes[0];
32824
- if (newNode) {
32825
- this.leaves[leafIndex] = newNode;
34507
+ let parentNode;
34508
+ let hasParentNode = false;
34509
+ if (!leaf.parentNodeId) {
34510
+ } else {
34511
+ hasParentNode = true;
34512
+ parentNode = response.nodes[leaf.parentNodeId];
34513
+ if (!parentNode) {
34514
+ throw new ValidationError("Parent node not found", {
34515
+ field: "parentNodeId",
34516
+ value: leaf.parentNodeId
34517
+ });
32826
34518
  }
32827
34519
  }
32828
- } catch (error) {
32829
- throw new NetworkError(
32830
- "Failed to refresh timelock",
32831
- {
32832
- method: "refresh_timelock"
32833
- },
32834
- error
32835
- );
32836
- }
32837
- }
32838
- /**
32839
- * Refresh the timelock of a specific node's refund transaction only.
32840
- *
32841
- * @param {string} nodeId - The ID of the node whose refund transaction to refresh
32842
- * @returns {Promise<void>} Promise that resolves when the refund timelock is refreshed
32843
- */
32844
- async testOnly_expireTimelockRefundTx(nodeId) {
32845
- const sparkClient = await this.connectionManager.createSparkClient(
32846
- this.config.getCoordinatorAddress()
32847
- );
32848
- try {
32849
- const response = await sparkClient.query_nodes({
32850
- source: {
32851
- $case: "nodeIds",
32852
- nodeIds: {
32853
- nodeIds: [nodeId]
34520
+ const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
34521
+ const refundTx = getTxFromRawTxBytes(leaf.refundTx);
34522
+ if (hasParentNode) {
34523
+ const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
34524
+ if (nodeTimelock > 100) {
34525
+ const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
34526
+ leaf,
34527
+ parentNode
34528
+ );
34529
+ if (!expiredNodeTxLeaf.nodes[0]) {
34530
+ throw new ValidationError("No expired node tx leaf", {
34531
+ field: "expiredNodeTxLeaf",
34532
+ value: expiredNodeTxLeaf
34533
+ });
32854
34534
  }
32855
- },
32856
- includeParents: false
32857
- });
32858
- const node = response.nodes[nodeId];
32859
- if (!node) {
32860
- throw new ValidationError("Node not found", {
32861
- field: "nodeId",
32862
- value: nodeId
32863
- });
34535
+ leaf = expiredNodeTxLeaf.nodes[0];
34536
+ }
32864
34537
  }
32865
- const result = await this.transferService.refreshTimelockRefundTx(node);
32866
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
32867
- if (leafIndex !== -1 && result.nodes.length > 0) {
32868
- const newNode = result.nodes[0];
32869
- if (newNode) {
32870
- this.leaves[leafIndex] = newNode;
34538
+ const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
34539
+ if (refundTimelock > 100) {
34540
+ const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
34541
+ if (!expiredTxLeaf.nodes[0]) {
34542
+ throw new ValidationError("No expired tx leaf", {
34543
+ field: "expiredTxLeaf",
34544
+ value: expiredTxLeaf
34545
+ });
32871
34546
  }
34547
+ leaf = expiredTxLeaf.nodes[0];
34548
+ }
34549
+ const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
34550
+ if (leafIndex !== -1) {
34551
+ this.leaves[leafIndex] = leaf;
32872
34552
  }
32873
34553
  } catch (error) {
32874
34554
  throw new NetworkError(
32875
- "Failed to refresh refund timelock",
34555
+ "Failed to refresh timelock",
32876
34556
  {
32877
- method: "refresh_timelock_refund_tx"
34557
+ method: "refresh_timelock"
32878
34558
  },
32879
34559
  error
32880
34560
  );
@@ -32945,7 +34625,10 @@ setCrypto(cryptoImpl2);
32945
34625
  AuthenticationError,
32946
34626
  ConfigurationError,
32947
34627
  DEFAULT_FEE_SATS,
34628
+ DIRECT_TIMELOCK_OFFSET,
32948
34629
  DefaultSparkSigner,
34630
+ INITIAL_DIRECT_SEQUENCE,
34631
+ INITIAL_SEQUENCE,
32949
34632
  InternalValidationError,
32950
34633
  KeyDerivationType,
32951
34634
  LRC_WALLET_NETWORK,
@@ -32958,6 +34641,8 @@ setCrypto(cryptoImpl2);
32958
34641
  RPCError,
32959
34642
  SparkSDKError,
32960
34643
  SparkWallet,
34644
+ TEST_UNILATERAL_DIRECT_SEQUENCE,
34645
+ TEST_UNILATERAL_SEQUENCE,
32961
34646
  TaprootOutputKeysGenerator,
32962
34647
  TaprootSparkSigner,
32963
34648
  TokenTransactionService,
@@ -32977,13 +34662,21 @@ setCrypto(cryptoImpl2);
32977
34662
  constructFeeBumpTx,
32978
34663
  constructUnilateralExitFeeBumpPackages,
32979
34664
  constructUnilateralExitTxs,
34665
+ createConnectorRefundTransactions,
34666
+ createLeafNodeTx,
34667
+ createNodeTx,
34668
+ createNodeTxs,
32980
34669
  createRefundTx,
34670
+ createRefundTxs,
34671
+ createRootTx,
32981
34672
  createSigningCommitment,
32982
34673
  createSigningNonce,
34674
+ createSplitTx,
32983
34675
  decodeBech32mTokenIdentifier,
32984
34676
  decodeBytesToSigningCommitment,
32985
34677
  decodeBytesToSigningNonce,
32986
34678
  decodeSparkAddress,
34679
+ doesLeafNeedRefresh,
32987
34680
  encodeBech32mTokenIdentifier,
32988
34681
  encodeSigningCommitmentToBytes,
32989
34682
  encodeSigningNonceToBytes,
@@ -33012,6 +34705,7 @@ setCrypto(cryptoImpl2);
33012
34705
  getSparkAddressFromTaproot,
33013
34706
  getTransactionSequence,
33014
34707
  getTransferPackageSigningPayload,
34708
+ getTxEstimatedVbytesSizeByNumberOfInputsOutputs,
33015
34709
  getTxFromRawTxBytes,
33016
34710
  getTxFromRawTxHex,
33017
34711
  getTxId,