@buildonspark/spark-sdk 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{chunk-PTRXJS7Q.js → chunk-TVUMSHWA.js} +1 -1
  3. package/dist/{chunk-PLLJIZC3.js → chunk-W4ZRBSWM.js} +2298 -778
  4. package/dist/{chunk-CDLETEDT.js → chunk-WAQKYSDI.js} +13 -1
  5. package/dist/{client-CGTRS23n.d.ts → client-BF4cn8F4.d.ts} +15 -3
  6. package/dist/{client-CcYzmpmj.d.cts → client-KhNkrXz4.d.cts} +15 -3
  7. package/dist/debug.cjs +2282 -762
  8. package/dist/debug.d.cts +17 -4
  9. package/dist/debug.d.ts +17 -4
  10. package/dist/debug.js +2 -2
  11. package/dist/graphql/objects/index.cjs +13 -1
  12. package/dist/graphql/objects/index.d.cts +2 -2
  13. package/dist/graphql/objects/index.d.ts +2 -2
  14. package/dist/graphql/objects/index.js +1 -1
  15. package/dist/index.cjs +2283 -752
  16. package/dist/index.d.cts +189 -8
  17. package/dist/index.d.ts +189 -8
  18. package/dist/index.js +29 -3
  19. package/dist/index.node.cjs +2387 -753
  20. package/dist/index.node.d.cts +9 -189
  21. package/dist/index.node.d.ts +9 -189
  22. package/dist/index.node.js +131 -3
  23. package/dist/native/index.cjs +2283 -752
  24. package/dist/native/index.d.cts +95 -30
  25. package/dist/native/index.d.ts +95 -30
  26. package/dist/native/index.js +2284 -767
  27. package/dist/{spark-wallet-CxcGPXRB.d.ts → spark-wallet-C1Tr_VKI.d.ts} +31 -25
  28. package/dist/{spark-wallet-DJJm19BP.d.cts → spark-wallet-DG3x2obf.d.cts} +31 -25
  29. package/dist/spark-wallet.node-CGxoeCpH.d.ts +13 -0
  30. package/dist/spark-wallet.node-CN9LoB_O.d.cts +13 -0
  31. package/dist/tests/test-utils.cjs +570 -73
  32. package/dist/tests/test-utils.d.cts +11 -11
  33. package/dist/tests/test-utils.d.ts +11 -11
  34. package/dist/tests/test-utils.js +53 -16
  35. package/dist/types/index.cjs +13 -1
  36. package/dist/types/index.d.cts +1 -1
  37. package/dist/types/index.d.ts +1 -1
  38. package/dist/types/index.js +1 -1
  39. package/dist/{xchain-address-Bh9w1SeC.d.ts → xchain-address-BHu6CpZC.d.ts} +54 -7
  40. package/dist/{xchain-address-SZ7dkVUE.d.cts → xchain-address-HBr6isnc.d.cts} +54 -7
  41. package/package.json +1 -1
  42. package/src/graphql/client.ts +8 -0
  43. package/src/graphql/mutations/CompleteLeavesSwap.ts +9 -1
  44. package/src/graphql/mutations/RequestSwapLeaves.ts +4 -0
  45. package/src/graphql/objects/CompleteLeavesSwapInput.ts +34 -34
  46. package/src/graphql/objects/LeavesSwapRequest.ts +4 -0
  47. package/src/graphql/objects/RequestLeavesSwapInput.ts +48 -47
  48. package/src/graphql/objects/SwapLeaf.ts +40 -32
  49. package/src/graphql/objects/UserLeafInput.ts +24 -0
  50. package/src/graphql/objects/UserRequest.ts +4 -0
  51. package/src/index.node.ts +1 -1
  52. package/src/native/index.ts +4 -5
  53. package/src/services/coop-exit.ts +171 -36
  54. package/src/services/deposit.ts +471 -74
  55. package/src/services/lightning.ts +18 -5
  56. package/src/services/signing.ts +162 -50
  57. package/src/services/transfer.ts +950 -384
  58. package/src/services/tree-creation.ts +342 -121
  59. package/src/spark-wallet/spark-wallet.node.ts +71 -66
  60. package/src/spark-wallet/spark-wallet.ts +405 -153
  61. package/src/tests/integration/coop-exit.test.ts +3 -8
  62. package/src/tests/integration/deposit.test.ts +3 -3
  63. package/src/tests/integration/lightning.test.ts +521 -466
  64. package/src/tests/integration/swap.test.ts +559 -307
  65. package/src/tests/integration/transfer.test.ts +625 -623
  66. package/src/tests/integration/wallet.test.ts +2 -2
  67. package/src/tests/integration/watchtower.test.ts +211 -0
  68. package/src/tests/test-utils.ts +63 -14
  69. package/src/tests/utils/test-faucet.ts +4 -2
  70. package/src/utils/adaptor-signature.ts +15 -5
  71. package/src/utils/fetch.ts +75 -0
  72. package/src/utils/mempool.ts +9 -4
  73. package/src/utils/transaction.ts +388 -26
@@ -1290,7 +1290,10 @@ __export(index_node_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,
@@ -1302,7 +1305,9 @@ __export(index_node_exports, {
1302
1305
  NotImplementedError: () => NotImplementedError,
1303
1306
  RPCError: () => RPCError,
1304
1307
  SparkSDKError: () => SparkSDKError,
1305
- SparkWallet: () => SparkWallet,
1308
+ SparkWallet: () => SparkWallet2,
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_node_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,
@@ -16204,13 +16217,24 @@ function applyAdaptorToSignature(pubkey, hash, signature, adaptorPrivateKeyBytes
16204
16217
  const adaptorPrivateKey = (0, import_utils4.bytesToNumberBE)(adaptorPrivateKeyBytes);
16205
16218
  const newS = (0, import_modular.mod)(sBigInt + adaptorPrivateKey, import_secp256k12.secp256k1.CURVE.n);
16206
16219
  const newSig = new Uint8Array([...r, ...(0, import_utils4.numberToBytesBE)(newS, 32)]);
16207
- if (import_secp256k12.schnorr.verify(newSig, hash, pubkey)) {
16208
- 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);
16209
16226
  }
16210
16227
  const altS = (0, import_modular.mod)(sBigInt - adaptorPrivateKey, import_secp256k12.secp256k1.CURVE.n);
16211
16228
  const altSig = new Uint8Array([...r, ...(0, import_utils4.numberToBytesBE)(altS, 32)]);
16212
- if (import_secp256k12.schnorr.verify(altSig, hash, pubkey)) {
16213
- 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
+ );
16214
16238
  }
16215
16239
  throw new Error("Cannot apply adaptor to signature");
16216
16240
  }
@@ -17104,7 +17128,11 @@ var SwapLeafFromJson = (obj) => {
17104
17128
  return {
17105
17129
  leafId: obj["swap_leaf_leaf_id"],
17106
17130
  rawUnsignedRefundTransaction: obj["swap_leaf_raw_unsigned_refund_transaction"],
17107
- adaptorSignedSignature: obj["swap_leaf_adaptor_signed_signature"]
17131
+ adaptorSignedSignature: obj["swap_leaf_adaptor_signed_signature"],
17132
+ directRawUnsignedRefundTransaction: obj["swap_leaf_direct_raw_unsigned_refund_transaction"],
17133
+ directAdaptorSignedSignature: obj["swap_leaf_direct_adaptor_signed_signature"],
17134
+ directFromCpfpRawUnsignedRefundTransaction: obj["swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction"],
17135
+ directFromCpfpAdaptorSignedSignature: obj["swap_leaf_direct_from_cpfp_adaptor_signed_signature"]
17108
17136
  };
17109
17137
  };
17110
17138
 
@@ -17359,6 +17387,10 @@ fragment LeavesSwapRequestFragment on LeavesSwapRequest {
17359
17387
  swap_leaf_leaf_id: leaf_id
17360
17388
  swap_leaf_raw_unsigned_refund_transaction: raw_unsigned_refund_transaction
17361
17389
  swap_leaf_adaptor_signed_signature: adaptor_signed_signature
17390
+ swap_leaf_direct_raw_unsigned_refund_transaction: direct_raw_unsigned_refund_transaction
17391
+ swap_leaf_direct_adaptor_signed_signature: direct_adaptor_signed_signature
17392
+ swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction: direct_from_cpfp_raw_unsigned_refund_transaction
17393
+ swap_leaf_direct_from_cpfp_adaptor_signed_signature: direct_from_cpfp_adaptor_signed_signature
17362
17394
  }
17363
17395
  }`;
17364
17396
 
@@ -17705,6 +17737,10 @@ fragment UserRequestFragment on UserRequest {
17705
17737
  swap_leaf_leaf_id: leaf_id
17706
17738
  swap_leaf_raw_unsigned_refund_transaction: raw_unsigned_refund_transaction
17707
17739
  swap_leaf_adaptor_signed_signature: adaptor_signed_signature
17740
+ swap_leaf_direct_raw_unsigned_refund_transaction: direct_raw_unsigned_refund_transaction
17741
+ swap_leaf_direct_adaptor_signed_signature: direct_adaptor_signed_signature
17742
+ swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction: direct_from_cpfp_raw_unsigned_refund_transaction
17743
+ swap_leaf_direct_from_cpfp_adaptor_signed_signature: direct_from_cpfp_adaptor_signed_signature
17708
17744
  }
17709
17745
  }
17710
17746
  ... on LightningReceiveRequest {
@@ -17830,18 +17866,40 @@ function mapTransferToWalletTransfer(proto, identityPublicKey) {
17830
17866
  };
17831
17867
  }
17832
17868
 
17869
+ // src/utils/fetch.ts
17870
+ init_buffer();
17871
+ var fetchImpl = typeof window !== "undefined" && window.fetch ? window.fetch.bind(window) : globalThis.fetch ? globalThis.fetch : null;
17872
+ var Headers = globalThis.Headers ?? null;
17873
+ var getFetch = () => {
17874
+ if (!fetchImpl) {
17875
+ throw new Error(
17876
+ "Fetch implementation is not set. Please set it using setFetch()."
17877
+ );
17878
+ }
17879
+ if (!Headers) {
17880
+ throw new Error(
17881
+ "Headers implementation is not set. Please set it using setFetch()."
17882
+ );
17883
+ }
17884
+ return {
17885
+ fetch: fetchImpl,
17886
+ Headers
17887
+ };
17888
+ };
17889
+
17833
17890
  // src/utils/mempool.ts
17834
17891
  async function getLatestDepositTxId(address2) {
17892
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
17835
17893
  const network = getNetworkFromAddress(address2);
17836
17894
  const baseUrl = network === BitcoinNetwork_default.REGTEST ? getElectrsUrl("REGTEST") : getElectrsUrl("MAINNET");
17837
- const headers = {};
17895
+ const headers = new Headers2();
17838
17896
  if (network === BitcoinNetwork_default.REGTEST) {
17839
17897
  const auth = btoa(
17840
17898
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
17841
17899
  );
17842
- headers["Authorization"] = `Basic ${auth}`;
17900
+ headers.set("Authorization", `Basic ${auth}`);
17843
17901
  }
17844
- const response = await fetch(`${baseUrl}/address/${address2}/txs`, {
17902
+ const response = await fetch2(`${baseUrl}/address/${address2}/txs`, {
17845
17903
  headers
17846
17904
  });
17847
17905
  const addressTxs = await response.json();
@@ -17858,14 +17916,15 @@ async function getLatestDepositTxId(address2) {
17858
17916
  return null;
17859
17917
  }
17860
17918
  async function isTxBroadcast(txid, baseUrl, network) {
17861
- const headers = {};
17919
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
17920
+ const headers = new Headers2();
17862
17921
  if (network === 3 /* REGTEST */) {
17863
17922
  const auth = btoa(
17864
17923
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
17865
17924
  );
17866
- headers["Authorization"] = `Basic ${auth}`;
17925
+ headers.set("Authorization", `Basic ${auth}`);
17867
17926
  }
17868
- const response = await fetch(`${baseUrl}/tx/${txid}`, {
17927
+ const response = await fetch2(`${baseUrl}/tx/${txid}`, {
17869
17928
  headers
17870
17929
  });
17871
17930
  const tx = await response.json();
@@ -18336,7 +18395,14 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
18336
18395
  // src/utils/transaction.ts
18337
18396
  init_buffer();
18338
18397
  var import_btc_signer = require("@scure/btc-signer");
18398
+ var INITIAL_TIMELOCK = 2e3;
18399
+ var TEST_UNILATERAL_TIMELOCK = 100;
18339
18400
  var TIME_LOCK_INTERVAL = 100;
18401
+ var DIRECT_TIMELOCK_OFFSET = 50;
18402
+ var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
18403
+ var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
18404
+ var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
18405
+ var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
18340
18406
  var ESTIMATED_TX_SIZE = 191;
18341
18407
  var DEFAULT_SATS_PER_VBYTE = 5;
18342
18408
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -18346,29 +18412,270 @@ function maybeApplyFee(amount) {
18346
18412
  }
18347
18413
  return amount;
18348
18414
  }
18349
- function createRefundTx(sequence, nodeOutPoint, amountSats, receivingPubkey, network) {
18350
- const newRefundTx = new import_btc_signer.Transaction({
18415
+ function createRootTx(depositOutPoint, depositTxOut) {
18416
+ const cpfpRootTx = new import_btc_signer.Transaction({
18417
+ version: 3,
18418
+ allowUnknownOutputs: true
18419
+ });
18420
+ cpfpRootTx.addInput(depositOutPoint);
18421
+ cpfpRootTx.addOutput(depositTxOut);
18422
+ cpfpRootTx.addOutput(getEphemeralAnchorOutput());
18423
+ const directRootTx = new import_btc_signer.Transaction({
18424
+ version: 3,
18425
+ allowUnknownOutputs: true
18426
+ });
18427
+ directRootTx.addInput(depositOutPoint);
18428
+ directRootTx.addOutput({
18429
+ script: depositTxOut.script,
18430
+ amount: maybeApplyFee(depositTxOut.amount ?? 0n)
18431
+ });
18432
+ return [cpfpRootTx, directRootTx];
18433
+ }
18434
+ function createSplitTx(parentOutPoint, childTxOuts) {
18435
+ const cpfpSplitTx = new import_btc_signer.Transaction({
18436
+ version: 3,
18437
+ allowUnknownOutputs: true
18438
+ });
18439
+ cpfpSplitTx.addInput(parentOutPoint);
18440
+ for (const txOut of childTxOuts) {
18441
+ cpfpSplitTx.addOutput(txOut);
18442
+ }
18443
+ cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
18444
+ const directSplitTx = new import_btc_signer.Transaction({
18445
+ version: 3,
18446
+ allowUnknownOutputs: true
18447
+ });
18448
+ directSplitTx.addInput(parentOutPoint);
18449
+ let totalOutputAmount = 0n;
18450
+ for (const txOut of childTxOuts) {
18451
+ totalOutputAmount += txOut.amount ?? 0n;
18452
+ }
18453
+ if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
18454
+ const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
18455
+ for (const txOut of childTxOuts) {
18456
+ const adjustedAmount = BigInt(
18457
+ Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
18458
+ );
18459
+ directSplitTx.addOutput({
18460
+ script: txOut.script,
18461
+ amount: adjustedAmount
18462
+ });
18463
+ }
18464
+ } else {
18465
+ for (const txOut of childTxOuts) {
18466
+ directSplitTx.addOutput(txOut);
18467
+ }
18468
+ }
18469
+ return [cpfpSplitTx, directSplitTx];
18470
+ }
18471
+ function createNodeTx({
18472
+ txOut,
18473
+ parentOutPoint,
18474
+ applyFee,
18475
+ includeAnchor
18476
+ }) {
18477
+ const nodeTx = new import_btc_signer.Transaction({
18478
+ version: 3,
18479
+ allowUnknownOutputs: true
18480
+ });
18481
+ nodeTx.addInput(parentOutPoint);
18482
+ if (applyFee) {
18483
+ nodeTx.addOutput({
18484
+ script: txOut.script,
18485
+ amount: maybeApplyFee(txOut.amount ?? 0n)
18486
+ });
18487
+ } else {
18488
+ nodeTx.addOutput(txOut);
18489
+ }
18490
+ if (includeAnchor) {
18491
+ nodeTx.addOutput(getEphemeralAnchorOutput());
18492
+ }
18493
+ return nodeTx;
18494
+ }
18495
+ function createNodeTxs(txOut, txIn, directTxIn) {
18496
+ const cpfpNodeTx = createNodeTx({
18497
+ txOut,
18498
+ parentOutPoint: txIn,
18499
+ includeAnchor: true
18500
+ });
18501
+ let directNodeTx;
18502
+ if (directTxIn) {
18503
+ directNodeTx = createNodeTx({
18504
+ txOut,
18505
+ parentOutPoint: directTxIn,
18506
+ includeAnchor: false,
18507
+ applyFee: true
18508
+ });
18509
+ }
18510
+ return { cpfpNodeTx, directNodeTx };
18511
+ }
18512
+ function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
18513
+ const cpfpLeafTx = new import_btc_signer.Transaction({
18514
+ version: 3,
18515
+ allowUnknownOutputs: true
18516
+ });
18517
+ cpfpLeafTx.addInput({
18518
+ ...parentOutPoint,
18519
+ sequence
18520
+ });
18521
+ cpfpLeafTx.addOutput(txOut);
18522
+ cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
18523
+ const directLeafTx = new import_btc_signer.Transaction({
18524
+ version: 3,
18525
+ allowUnknownOutputs: true
18526
+ });
18527
+ directLeafTx.addInput({
18528
+ ...parentOutPoint,
18529
+ sequence: directSequence
18530
+ });
18531
+ const amountSats = txOut.amount ?? 0n;
18532
+ let outputAmount = amountSats;
18533
+ if (shouldCalculateFee) {
18534
+ outputAmount = maybeApplyFee(amountSats);
18535
+ }
18536
+ directLeafTx.addOutput({
18537
+ script: txOut.script,
18538
+ amount: outputAmount
18539
+ });
18540
+ return [cpfpLeafTx, directLeafTx];
18541
+ }
18542
+ function createRefundTx({
18543
+ sequence,
18544
+ input,
18545
+ amountSats,
18546
+ receivingPubkey,
18547
+ network,
18548
+ shouldCalculateFee,
18549
+ includeAnchor
18550
+ }) {
18551
+ const refundTx = new import_btc_signer.Transaction({
18351
18552
  version: 3,
18352
18553
  allowUnknownOutputs: true
18353
18554
  });
18354
- newRefundTx.addInput({
18355
- ...nodeOutPoint,
18555
+ refundTx.addInput({
18556
+ ...input,
18356
18557
  sequence
18357
18558
  });
18358
18559
  const refundPkScript = getP2TRScriptFromPublicKey(receivingPubkey, network);
18359
- newRefundTx.addOutput({
18560
+ let outputAmount = amountSats;
18561
+ if (shouldCalculateFee) {
18562
+ outputAmount = maybeApplyFee(amountSats);
18563
+ }
18564
+ refundTx.addOutput({
18360
18565
  script: refundPkScript,
18566
+ amount: outputAmount
18567
+ });
18568
+ if (includeAnchor) {
18569
+ refundTx.addOutput(getEphemeralAnchorOutput());
18570
+ }
18571
+ return refundTx;
18572
+ }
18573
+ function createRefundTxs({
18574
+ sequence,
18575
+ directSequence,
18576
+ input,
18577
+ directInput,
18578
+ amountSats,
18579
+ receivingPubkey,
18580
+ network
18581
+ }) {
18582
+ const cpfpRefundTx = createRefundTx({
18583
+ sequence,
18584
+ input,
18585
+ amountSats,
18586
+ receivingPubkey,
18587
+ network,
18588
+ shouldCalculateFee: false,
18589
+ includeAnchor: true
18590
+ });
18591
+ let directRefundTx;
18592
+ let directFromCpfpRefundTx;
18593
+ if (directSequence && directInput) {
18594
+ directRefundTx = createRefundTx({
18595
+ sequence: directSequence,
18596
+ input: directInput,
18597
+ amountSats,
18598
+ receivingPubkey,
18599
+ network,
18600
+ shouldCalculateFee: true,
18601
+ includeAnchor: false
18602
+ });
18603
+ directFromCpfpRefundTx = createRefundTx({
18604
+ sequence: directSequence,
18605
+ input,
18606
+ amountSats,
18607
+ receivingPubkey,
18608
+ network,
18609
+ shouldCalculateFee: true,
18610
+ includeAnchor: false
18611
+ });
18612
+ } else if (directInput && !directSequence) {
18613
+ throw new ValidationError(
18614
+ "directSequence must be provided if directInput is",
18615
+ {
18616
+ field: "directSequence",
18617
+ value: directSequence
18618
+ }
18619
+ );
18620
+ }
18621
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
18622
+ }
18623
+ function createConnectorRefundTransactions(sequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey, network, shouldCalculateFee) {
18624
+ const cpfpRefundTx = new import_btc_signer.Transaction({
18625
+ version: 3,
18626
+ allowUnknownOutputs: true
18627
+ });
18628
+ cpfpRefundTx.addInput({
18629
+ ...cpfpNodeOutPoint,
18630
+ sequence
18631
+ });
18632
+ cpfpRefundTx.addInput(connectorOutput);
18633
+ const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
18634
+ cpfpRefundTx.addOutput({
18635
+ script: receiverScript,
18361
18636
  amount: amountSats
18362
18637
  });
18363
- newRefundTx.addOutput(getEphemeralAnchorOutput());
18364
- return newRefundTx;
18638
+ const directRefundTx = new import_btc_signer.Transaction({
18639
+ version: 3,
18640
+ allowUnknownOutputs: true
18641
+ });
18642
+ directRefundTx.addInput({
18643
+ ...directNodeOutPoint,
18644
+ sequence
18645
+ });
18646
+ directRefundTx.addInput(connectorOutput);
18647
+ let outputAmount = amountSats;
18648
+ if (shouldCalculateFee) {
18649
+ outputAmount = maybeApplyFee(amountSats);
18650
+ }
18651
+ directRefundTx.addOutput({
18652
+ script: receiverScript,
18653
+ amount: outputAmount
18654
+ });
18655
+ const directFromCpfpTx = new import_btc_signer.Transaction({
18656
+ version: 3,
18657
+ allowUnknownOutputs: true
18658
+ });
18659
+ directFromCpfpTx.addInput({
18660
+ ...cpfpNodeOutPoint,
18661
+ sequence
18662
+ });
18663
+ directFromCpfpTx.addInput(connectorOutput);
18664
+ directFromCpfpTx.addOutput({
18665
+ script: receiverScript,
18666
+ amount: outputAmount
18667
+ });
18668
+ return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
18365
18669
  }
18366
18670
  function getCurrentTimelock(currSequence) {
18367
18671
  return (currSequence || 0) & 65535;
18368
18672
  }
18369
18673
  function getTransactionSequence(currSequence) {
18370
18674
  const timelock = getCurrentTimelock(currSequence);
18371
- return 1 << 30 | timelock;
18675
+ return {
18676
+ nextSequence: 1 << 30 | timelock,
18677
+ nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
18678
+ };
18372
18679
  }
18373
18680
  function checkIfValidSequence(currSequence) {
18374
18681
  const TIME_LOCK_ACTIVE = (currSequence || 0) & 2147483648;
@@ -18386,24 +18693,32 @@ function checkIfValidSequence(currSequence) {
18386
18693
  });
18387
18694
  }
18388
18695
  }
18389
- function getNextTransactionSequence(currSequence, forRefresh) {
18696
+ function doesLeafNeedRefresh(currSequence, isNodeTx) {
18390
18697
  const currentTimelock = getCurrentTimelock(currSequence);
18391
- const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
18392
- if (forRefresh && nextTimelock <= 100 && currentTimelock > 0) {
18393
- return {
18394
- nextSequence: 1 << 30 | nextTimelock,
18395
- needRefresh: true
18396
- };
18698
+ if (isNodeTx) {
18699
+ return currentTimelock === 0;
18397
18700
  }
18398
- if (nextTimelock < 0) {
18701
+ return currentTimelock <= 100;
18702
+ }
18703
+ function getNextTransactionSequence(currSequence, isNodeTx) {
18704
+ const currentTimelock = getCurrentTimelock(currSequence);
18705
+ const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
18706
+ if (isNodeTx && nextTimelock < 0) {
18399
18707
  throw new ValidationError("timelock interval is less than 0", {
18400
18708
  field: "nextTimelock",
18401
- value: nextTimelock
18709
+ value: nextTimelock,
18710
+ expected: "Non-negative timelock interval"
18711
+ });
18712
+ } else if (!isNodeTx && nextTimelock <= 0) {
18713
+ throw new ValidationError("timelock interval is less than or equal to 0", {
18714
+ field: "nextTimelock",
18715
+ value: nextTimelock,
18716
+ expected: "Timelock greater than 0"
18402
18717
  });
18403
18718
  }
18404
18719
  return {
18405
18720
  nextSequence: 1 << 30 | nextTimelock,
18406
- needRefresh: nextTimelock <= 100
18721
+ nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
18407
18722
  };
18408
18723
  }
18409
18724
  function getEphemeralAnchorOutput() {
@@ -18932,7 +19247,7 @@ init_buffer();
18932
19247
  var import_core8 = require("@lightsparkdev/core");
18933
19248
  var isReactNative = typeof navigator !== "undefined" && navigator.product === "ReactNative";
18934
19249
  var isBun = globalThis.Bun !== void 0;
18935
- var packageVersion = true ? "0.2.3" : "unknown";
19250
+ var packageVersion = true ? "0.2.4" : "unknown";
18936
19251
  var baseEnvStr = "unknown";
18937
19252
  if (isBun) {
18938
19253
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -19481,6 +19796,9 @@ var TaprootSparkSigner = class extends DefaultSparkSigner {
19481
19796
  }
19482
19797
  };
19483
19798
 
19799
+ // src/spark-wallet/spark-wallet.node.ts
19800
+ init_buffer();
19801
+
19484
19802
  // src/spark-wallet/spark-wallet.ts
19485
19803
  init_buffer();
19486
19804
  var import_core12 = require("@lightsparkdev/core");
@@ -19643,10 +19961,18 @@ init_buffer();
19643
19961
  var CompleteLeavesSwap = `
19644
19962
  mutation CompleteLeavesSwap(
19645
19963
  $adaptor_secret_key: String!
19964
+ $direct_adaptor_secret_key: String!
19965
+ $direct_from_cpfp_adaptor_secret_key: String!
19646
19966
  $user_outbound_transfer_external_id: UUID!
19647
19967
  $leaves_swap_request_id: ID!
19648
19968
  ) {
19649
- 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 }) {
19969
+ complete_leaves_swap(input: {
19970
+ adaptor_secret_key: $adaptor_secret_key,
19971
+ direct_adaptor_secret_key: $direct_adaptor_secret_key,
19972
+ direct_from_cpfp_adaptor_secret_key: $direct_from_cpfp_adaptor_secret_key,
19973
+ user_outbound_transfer_external_id: $user_outbound_transfer_external_id,
19974
+ leaves_swap_request_id: $leaves_swap_request_id
19975
+ }) {
19650
19976
  request {
19651
19977
  ...LeavesSwapRequestFragment
19652
19978
  }
@@ -19777,6 +20103,8 @@ init_buffer();
19777
20103
  var RequestSwapLeaves = `
19778
20104
  mutation RequestSwapLeaves(
19779
20105
  $adaptor_pubkey: PublicKey!
20106
+ $direct_adaptor_pubkey: PublicKey
20107
+ $direct_from_cpfp_adaptor_pubkey: PublicKey
19780
20108
  $total_amount_sats: Long!
19781
20109
  $target_amount_sats: Long!
19782
20110
  $fee_sats: Long!
@@ -19786,6 +20114,8 @@ var RequestSwapLeaves = `
19786
20114
  ) {
19787
20115
  request_leaves_swap(input: {
19788
20116
  adaptor_pubkey: $adaptor_pubkey
20117
+ direct_adaptor_pubkey: $direct_adaptor_pubkey
20118
+ direct_from_cpfp_adaptor_pubkey: $direct_from_cpfp_adaptor_pubkey
19789
20119
  total_amount_sats: $total_amount_sats
19790
20120
  target_amount_sats: $target_amount_sats
19791
20121
  fee_sats: $fee_sats
@@ -20294,6 +20624,8 @@ var SspClient = class {
20294
20624
  }
20295
20625
  async requestLeaveSwap({
20296
20626
  adaptorPubkey,
20627
+ directAdaptorPubkey,
20628
+ directFromCpfpAdaptorPubkey,
20297
20629
  totalAmountSats,
20298
20630
  targetAmountSats,
20299
20631
  feeSats,
@@ -20305,6 +20637,8 @@ var SspClient = class {
20305
20637
  queryPayload: RequestSwapLeaves,
20306
20638
  variables: {
20307
20639
  adaptor_pubkey: adaptorPubkey,
20640
+ direct_adaptor_pubkey: directAdaptorPubkey,
20641
+ direct_from_cpfp_adaptor_pubkey: directFromCpfpAdaptorPubkey,
20308
20642
  total_amount_sats: totalAmountSats,
20309
20643
  target_amount_sats: targetAmountSats,
20310
20644
  fee_sats: feeSats,
@@ -20323,6 +20657,8 @@ var SspClient = class {
20323
20657
  }
20324
20658
  async completeLeaveSwap({
20325
20659
  adaptorSecretKey,
20660
+ directAdaptorSecretKey,
20661
+ directFromCpfpAdaptorSecretKey,
20326
20662
  userOutboundTransferExternalId,
20327
20663
  leavesSwapRequestId
20328
20664
  }) {
@@ -20330,6 +20666,8 @@ var SspClient = class {
20330
20666
  queryPayload: CompleteLeavesSwap,
20331
20667
  variables: {
20332
20668
  adaptor_secret_key: adaptorSecretKey,
20669
+ direct_adaptor_secret_key: directAdaptorSecretKey,
20670
+ direct_from_cpfp_adaptor_secret_key: directFromCpfpAdaptorSecretKey,
20333
20671
  user_outbound_transfer_external_id: userOutboundTransferExternalId,
20334
20672
  leaves_swap_request_id: leavesSwapRequestId
20335
20673
  },
@@ -24494,11 +24832,7 @@ var import_sha28 = require("@noble/hashes/sha2");
24494
24832
  var import_btc_signer2 = require("@scure/btc-signer");
24495
24833
  var ecies2 = __toESM(require("eciesjs"), 1);
24496
24834
  var import_uuidv72 = require("uuidv7");
24497
- var INITIAL_TIME_LOCK = 2e3;
24498
24835
  var DEFAULT_EXPIRY_TIME = 10 * 60 * 1e3;
24499
- function initialSequence() {
24500
- return 1 << 30 | INITIAL_TIME_LOCK;
24501
- }
24502
24836
  function getSigningJobProto(signingJob) {
24503
24837
  return {
24504
24838
  signingPublicKey: signingJob.signingPublicKey,
@@ -24515,12 +24849,14 @@ var BaseTransferService = class {
24515
24849
  this.connectionManager = connectionManager;
24516
24850
  this.signingService = signingService;
24517
24851
  }
24518
- async sendTransferTweakKey(transfer, leaves, refundSignatureMap) {
24852
+ async sendTransferTweakKey(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
24519
24853
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
24520
24854
  transfer.id,
24521
24855
  transfer.receiverIdentityPublicKey,
24522
24856
  leaves,
24523
- refundSignatureMap
24857
+ cpfpRefundSignatureMap,
24858
+ directRefundSignatureMap,
24859
+ directFromCpfpRefundSignatureMap
24524
24860
  );
24525
24861
  let updatedTransfer;
24526
24862
  const coordinatorOperator = this.config.getSigningOperators()[this.config.getCoordinatorIdentifier()];
@@ -24558,13 +24894,26 @@ var BaseTransferService = class {
24558
24894
  }
24559
24895
  return updatedTransfer;
24560
24896
  }
24561
- async deliverTransferPackage(transfer, leaves, refundSignatureMap) {
24897
+ async deliverTransferPackage(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
24562
24898
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
24563
24899
  transfer.id,
24564
24900
  transfer.receiverIdentityPublicKey,
24565
24901
  leaves,
24566
- refundSignatureMap
24902
+ cpfpRefundSignatureMap,
24903
+ directRefundSignatureMap,
24904
+ directFromCpfpRefundSignatureMap
24567
24905
  );
24906
+ for (const [key, operator] of Object.entries(
24907
+ this.config.getSigningOperators()
24908
+ )) {
24909
+ const tweaks = keyTweakInputMap.get(key);
24910
+ if (!tweaks) {
24911
+ throw new ValidationError("No tweaks for operator", {
24912
+ field: "operator",
24913
+ value: key
24914
+ });
24915
+ }
24916
+ }
24568
24917
  const transferPackage = await this.prepareTransferPackage(
24569
24918
  transfer.id,
24570
24919
  keyTweakInputMap,
@@ -24590,6 +24939,8 @@ var BaseTransferService = class {
24590
24939
  transferID,
24591
24940
  receiverIdentityPubkey,
24592
24941
  leaves,
24942
+ /* @__PURE__ */ new Map(),
24943
+ /* @__PURE__ */ new Map(),
24593
24944
  /* @__PURE__ */ new Map()
24594
24945
  );
24595
24946
  const transferPackage = await this.prepareTransferPackage(
@@ -24603,7 +24954,7 @@ var BaseTransferService = class {
24603
24954
  );
24604
24955
  let response;
24605
24956
  try {
24606
- response = await sparkClient.start_transfer({
24957
+ response = await sparkClient.start_transfer_v2({
24607
24958
  transferId: transferID,
24608
24959
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
24609
24960
  receiverIdentityPublicKey: receiverIdentityPubkey,
@@ -24632,12 +24983,22 @@ var BaseTransferService = class {
24632
24983
  nodes.push(leaf.leaf.id);
24633
24984
  }
24634
24985
  const signingCommitments = await sparkClient.get_signing_commitments({
24635
- nodeIds: nodes
24986
+ nodeIds: nodes,
24987
+ count: 3
24636
24988
  });
24637
- const leafSigningJobs = await this.signingService.signRefunds(
24989
+ const {
24990
+ cpfpLeafSigningJobs,
24991
+ directLeafSigningJobs,
24992
+ directFromCpfpLeafSigningJobs
24993
+ } = await this.signingService.signRefunds(
24638
24994
  leaves,
24639
- signingCommitments.signingCommitments,
24640
- receiverIdentityPubkey
24995
+ receiverIdentityPubkey,
24996
+ signingCommitments.signingCommitments.slice(0, leaves.length),
24997
+ signingCommitments.signingCommitments.slice(
24998
+ leaves.length,
24999
+ 2 * leaves.length
25000
+ ),
25001
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
24641
25002
  );
24642
25003
  const encryptedKeyTweaks = {};
24643
25004
  for (const [key, value] of keyTweakInputMap) {
@@ -24657,12 +25018,11 @@ var BaseTransferService = class {
24657
25018
  encryptedKeyTweaks[key] = Uint8Array.from(encryptedProto);
24658
25019
  }
24659
25020
  const transferPackage = {
24660
- leavesToSend: leafSigningJobs,
25021
+ leavesToSend: cpfpLeafSigningJobs,
24661
25022
  keyTweakPackage: encryptedKeyTweaks,
24662
25023
  userSignature: new Uint8Array(),
24663
- // TODO: Add direct refund signature
24664
- directLeavesToSend: [],
24665
- directFromCpfpLeavesToSend: []
25024
+ directLeavesToSend: directLeafSigningJobs,
25025
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs
24666
25026
  };
24667
25027
  const transferPackageSigningPayload = getTransferPackageSigningPayload(
24668
25028
  transferID,
@@ -24722,7 +25082,7 @@ var BaseTransferService = class {
24722
25082
  }
24723
25083
  return updatedTransfer;
24724
25084
  }
24725
- async signRefunds(leafDataMap, operatorSigningResults, adaptorPubKey) {
25085
+ async signRefunds(leafDataMap, operatorSigningResults, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
24726
25086
  const nodeSignatures = [];
24727
25087
  for (const operatorSigningResult of operatorSigningResults) {
24728
25088
  const leafData = leafDataMap.get(operatorSigningResult.leafId);
@@ -24737,54 +25097,120 @@ var BaseTransferService = class {
24737
25097
  `Output not found for leaf ${operatorSigningResult.leafId}`
24738
25098
  );
24739
25099
  }
24740
- const refundTxSighash = getSigHashFromTx(leafData.refundTx, 0, txOutput);
25100
+ const cpfpRefundTxSighash = getSigHashFromTx(
25101
+ leafData.refundTx,
25102
+ 0,
25103
+ txOutput
25104
+ );
24741
25105
  const publicKey = await this.config.signer.getPublicKeyFromDerivation(
24742
25106
  leafData.keyDerivation
24743
25107
  );
24744
- const userSignature = await this.config.signer.signFrost({
24745
- message: refundTxSighash,
25108
+ const cpfpUserSignature = await this.config.signer.signFrost({
25109
+ message: cpfpRefundTxSighash,
24746
25110
  publicKey,
24747
25111
  keyDerivation: leafData.keyDerivation,
24748
25112
  selfCommitment: leafData.signingNonceCommitment,
24749
25113
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
24750
- adaptorPubKey,
25114
+ adaptorPubKey: cpfpAdaptorPubKey,
24751
25115
  verifyingKey: operatorSigningResult.verifyingKey
24752
25116
  });
24753
- const refundAggregate = await this.config.signer.aggregateFrost({
24754
- message: refundTxSighash,
25117
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
25118
+ message: cpfpRefundTxSighash,
24755
25119
  statechainSignatures: operatorSigningResult.refundTxSigningResult?.signatureShares,
24756
25120
  statechainPublicKeys: operatorSigningResult.refundTxSigningResult?.publicKeys,
24757
25121
  verifyingKey: operatorSigningResult.verifyingKey,
24758
25122
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
24759
25123
  selfCommitment: leafData.signingNonceCommitment,
24760
25124
  publicKey,
24761
- selfSignature: userSignature,
24762
- adaptorPubKey
25125
+ selfSignature: cpfpUserSignature,
25126
+ adaptorPubKey: cpfpAdaptorPubKey
24763
25127
  });
25128
+ let directRefundAggregate;
25129
+ let directFromCpfpRefundAggregate;
25130
+ if (leafData.directTx) {
25131
+ const directTxOutput = leafData.directTx.getOutput(0);
25132
+ if (leafData.directRefundTx) {
25133
+ const directRefundTxSighash = getSigHashFromTx(
25134
+ leafData.directRefundTx,
25135
+ 0,
25136
+ directTxOutput
25137
+ );
25138
+ const directUserSignature = await this.config.signer.signFrost({
25139
+ message: directRefundTxSighash,
25140
+ publicKey,
25141
+ keyDerivation: leafData.keyDerivation,
25142
+ selfCommitment: leafData.directSigningNonceCommitment,
25143
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
25144
+ adaptorPubKey: directAdaptorPubKey,
25145
+ verifyingKey: operatorSigningResult.verifyingKey
25146
+ });
25147
+ directRefundAggregate = await this.config.signer.aggregateFrost({
25148
+ message: directRefundTxSighash,
25149
+ statechainSignatures: operatorSigningResult.directRefundTxSigningResult?.signatureShares,
25150
+ statechainPublicKeys: operatorSigningResult.directRefundTxSigningResult?.publicKeys,
25151
+ verifyingKey: operatorSigningResult.verifyingKey,
25152
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
25153
+ selfCommitment: leafData.directSigningNonceCommitment,
25154
+ publicKey,
25155
+ selfSignature: directUserSignature,
25156
+ adaptorPubKey: directAdaptorPubKey
25157
+ });
25158
+ }
25159
+ if (leafData.directFromCpfpRefundTx) {
25160
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
25161
+ leafData.directFromCpfpRefundTx,
25162
+ 0,
25163
+ txOutput
25164
+ );
25165
+ const directFromCpfpUserSignature = await this.config.signer.signFrost({
25166
+ message: directFromCpfpRefundTxSighash,
25167
+ publicKey,
25168
+ keyDerivation: leafData.keyDerivation,
25169
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
25170
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
25171
+ adaptorPubKey: directFromCpfpAdaptorPubKey,
25172
+ verifyingKey: operatorSigningResult.verifyingKey
25173
+ });
25174
+ directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
25175
+ message: directFromCpfpRefundTxSighash,
25176
+ statechainSignatures: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signatureShares,
25177
+ statechainPublicKeys: operatorSigningResult.directFromCpfpRefundTxSigningResult?.publicKeys,
25178
+ verifyingKey: operatorSigningResult.verifyingKey,
25179
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
25180
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
25181
+ publicKey,
25182
+ selfSignature: directFromCpfpUserSignature,
25183
+ adaptorPubKey: directFromCpfpAdaptorPubKey
25184
+ });
25185
+ }
25186
+ }
24764
25187
  nodeSignatures.push({
24765
25188
  nodeId: operatorSigningResult.leafId,
24766
- refundTxSignature: refundAggregate,
24767
25189
  nodeTxSignature: new Uint8Array(),
24768
- // TODO: Add direct refund signature
24769
25190
  directNodeTxSignature: new Uint8Array(),
24770
- directRefundTxSignature: new Uint8Array(),
24771
- directFromCpfpRefundTxSignature: new Uint8Array()
25191
+ refundTxSignature: cpfpRefundAggregate,
25192
+ directRefundTxSignature: directRefundAggregate ?? new Uint8Array(),
25193
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate ?? new Uint8Array()
24772
25194
  });
24773
25195
  }
24774
25196
  return nodeSignatures;
24775
25197
  }
24776
- async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, refundSignatureMap) {
25198
+ async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
24777
25199
  const receiverEciesPubKey = ecies2.PublicKey.fromHex(
24778
25200
  (0, import_utils14.bytesToHex)(receiverIdentityPubkey)
24779
25201
  );
24780
25202
  const leavesTweaksMap = /* @__PURE__ */ new Map();
24781
25203
  for (const leaf of leaves) {
24782
- const refundSignature = refundSignatureMap.get(leaf.leaf.id);
25204
+ const cpfpRefundSignature = cpfpRefundSignatureMap.get(leaf.leaf.id);
25205
+ const directRefundSignature = directRefundSignatureMap.get(leaf.leaf.id);
25206
+ const directFromCpfpRefundSignature = directFromCpfpRefundSignatureMap.get(leaf.leaf.id);
24783
25207
  const leafTweaksMap = await this.prepareSingleSendTransferKeyTweak(
24784
25208
  transferID,
24785
25209
  leaf,
24786
25210
  receiverEciesPubKey,
24787
- refundSignature
25211
+ cpfpRefundSignature,
25212
+ directRefundSignature,
25213
+ directFromCpfpRefundSignature
24788
25214
  );
24789
25215
  for (const [identifier, leafTweak] of leafTweaksMap) {
24790
25216
  leavesTweaksMap.set(identifier, [
@@ -24795,7 +25221,7 @@ var BaseTransferService = class {
24795
25221
  }
24796
25222
  return leavesTweaksMap;
24797
25223
  }
24798
- async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, refundSignature) {
25224
+ async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, cpfpRefundSignature, directRefundSignature, directFromCpfpRefundSignature) {
24799
25225
  const signingOperators = this.config.getSigningOperators();
24800
25226
  const { shares, secretCipher } = await this.config.signer.subtractSplitAndEncrypt({
24801
25227
  first: leaf.keyDerivation,
@@ -24843,10 +25269,9 @@ var BaseTransferService = class {
24843
25269
  pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak),
24844
25270
  secretCipher,
24845
25271
  signature,
24846
- refundSignature: refundSignature ?? new Uint8Array(),
24847
- // TODO: Add direct refund signature
24848
- directRefundSignature: new Uint8Array(),
24849
- directFromCpfpRefundSignature: new Uint8Array()
25272
+ refundSignature: cpfpRefundSignature ?? new Uint8Array(),
25273
+ directRefundSignature: directRefundSignature ?? new Uint8Array(),
25274
+ directFromCpfpRefundSignature: directFromCpfpRefundSignature ?? new Uint8Array()
24850
25275
  });
24851
25276
  }
24852
25277
  return leafTweaksMap;
@@ -24876,7 +25301,12 @@ var TransferService = class extends BaseTransferService {
24876
25301
  * Deprecated in v0.1.32
24877
25302
  */
24878
25303
  async sendTransfer(leaves, receiverIdentityPubkey) {
24879
- const { transfer, signatureMap } = await this.sendTransferSignRefund(
25304
+ const {
25305
+ transfer,
25306
+ signatureMap,
25307
+ directSignatureMap,
25308
+ directFromCpfpSignatureMap
25309
+ } = await this.sendTransferSignRefund(
24880
25310
  leaves,
24881
25311
  receiverIdentityPubkey,
24882
25312
  new Date(Date.now() + DEFAULT_EXPIRY_TIME)
@@ -24884,7 +25314,9 @@ var TransferService = class extends BaseTransferService {
24884
25314
  const transferWithTweakedKeys = await this.sendTransferTweakKey(
24885
25315
  transfer,
24886
25316
  leaves,
24887
- signatureMap
25317
+ signatureMap,
25318
+ directSignatureMap,
25319
+ directFromCpfpSignatureMap
24888
25320
  );
24889
25321
  return transferWithTweakedKeys;
24890
25322
  }
@@ -24988,7 +25420,13 @@ var TransferService = class extends BaseTransferService {
24988
25420
  return transferResp.transfers[0];
24989
25421
  }
24990
25422
  async sendTransferSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
24991
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
25423
+ const {
25424
+ transfer,
25425
+ signatureMap,
25426
+ directSignatureMap,
25427
+ directFromCpfpSignatureMap,
25428
+ leafDataMap
25429
+ } = await this.sendTransferSignRefundInternal(
24992
25430
  leaves,
24993
25431
  receiverIdentityPubkey,
24994
25432
  expiryTime,
@@ -24997,11 +25435,19 @@ var TransferService = class extends BaseTransferService {
24997
25435
  return {
24998
25436
  transfer,
24999
25437
  signatureMap,
25438
+ directSignatureMap,
25439
+ directFromCpfpSignatureMap,
25000
25440
  leafDataMap
25001
25441
  };
25002
25442
  }
25003
25443
  async startSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
25004
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
25444
+ const {
25445
+ transfer,
25446
+ signatureMap,
25447
+ directSignatureMap,
25448
+ directFromCpfpSignatureMap,
25449
+ leafDataMap
25450
+ } = await this.sendTransferSignRefundInternal(
25005
25451
  leaves,
25006
25452
  receiverIdentityPubkey,
25007
25453
  expiryTime,
@@ -25010,31 +25456,45 @@ var TransferService = class extends BaseTransferService {
25010
25456
  return {
25011
25457
  transfer,
25012
25458
  signatureMap,
25459
+ directSignatureMap,
25460
+ directFromCpfpSignatureMap,
25013
25461
  leafDataMap
25014
25462
  };
25015
25463
  }
25016
- async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, adaptorPubKey) {
25464
+ async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
25017
25465
  return this.sendTransferSignRefundInternal(
25018
25466
  leaves,
25019
25467
  receiverIdentityPubkey,
25020
25468
  expiryTime,
25021
25469
  true,
25022
- adaptorPubKey
25470
+ cpfpAdaptorPubKey,
25471
+ directAdaptorPubKey,
25472
+ directFromCpfpAdaptorPubKey
25023
25473
  );
25024
25474
  }
25025
- async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, adaptorPubKey) {
25475
+ async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
25026
25476
  const transferId = (0, import_uuidv72.uuidv7)();
25027
25477
  const leafDataMap = /* @__PURE__ */ new Map();
25028
25478
  for (const leaf of leaves) {
25029
25479
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25480
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25481
+ const directFromCpfpRefundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25030
25482
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
25031
25483
  const refundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
25484
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
25485
+ const directRefundTx = leaf.leaf.directRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directRefundTx) : void 0;
25486
+ const directFromCpfpRefundTx = leaf.leaf.directFromCpfpRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directFromCpfpRefundTx) : void 0;
25032
25487
  leafDataMap.set(leaf.leaf.id, {
25033
25488
  keyDerivation: leaf.keyDerivation,
25034
25489
  receivingPubkey: receiverIdentityPubkey,
25035
25490
  signingNonceCommitment,
25491
+ directSigningNonceCommitment,
25036
25492
  tx,
25493
+ directTx,
25037
25494
  refundTx,
25495
+ directRefundTx,
25496
+ directFromCpfpRefundTx,
25497
+ directFromCpfpRefundSigningNonceCommitment,
25038
25498
  vout: leaf.leaf.vout
25039
25499
  });
25040
25500
  }
@@ -25047,8 +25507,8 @@ var TransferService = class extends BaseTransferService {
25047
25507
  );
25048
25508
  let response;
25049
25509
  try {
25050
- if (adaptorPubKey !== void 0) {
25051
- response = await sparkClient.counter_leaf_swap({
25510
+ if (cpfpAdaptorPubKey !== void 0 || directAdaptorPubKey !== void 0 || directFromCpfpAdaptorPubKey !== void 0) {
25511
+ response = await sparkClient.counter_leaf_swap_v2({
25052
25512
  transfer: {
25053
25513
  transferId,
25054
25514
  leavesToSend: signingJobs,
@@ -25057,10 +25517,12 @@ var TransferService = class extends BaseTransferService {
25057
25517
  expiryTime
25058
25518
  },
25059
25519
  swapId: (0, import_uuidv72.uuidv7)(),
25060
- adaptorPublicKey: adaptorPubKey || new Uint8Array()
25520
+ adaptorPublicKey: cpfpAdaptorPubKey,
25521
+ directAdaptorPublicKey: directAdaptorPubKey,
25522
+ directFromCpfpAdaptorPublicKey: directFromCpfpAdaptorPubKey
25061
25523
  });
25062
25524
  } else if (forSwap) {
25063
- response = await sparkClient.start_leaf_swap({
25525
+ response = await sparkClient.start_leaf_swap_v2({
25064
25526
  transferId,
25065
25527
  leavesToSend: signingJobs,
25066
25528
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -25068,7 +25530,7 @@ var TransferService = class extends BaseTransferService {
25068
25530
  expiryTime
25069
25531
  });
25070
25532
  } else {
25071
- response = await sparkClient.start_transfer({
25533
+ response = await sparkClient.start_transfer_v2({
25072
25534
  transferId,
25073
25535
  leavesToSend: signingJobs,
25074
25536
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -25085,15 +25547,29 @@ var TransferService = class extends BaseTransferService {
25085
25547
  const signatures = await this.signRefunds(
25086
25548
  leafDataMap,
25087
25549
  response.signingResults,
25088
- adaptorPubKey
25550
+ cpfpAdaptorPubKey,
25551
+ directAdaptorPubKey,
25552
+ directFromCpfpAdaptorPubKey
25089
25553
  );
25090
- const signatureMap = /* @__PURE__ */ new Map();
25554
+ const cpfpSignatureMap = /* @__PURE__ */ new Map();
25555
+ const directSignatureMap = /* @__PURE__ */ new Map();
25556
+ const directFromCpfpSignatureMap = /* @__PURE__ */ new Map();
25091
25557
  for (const signature of signatures) {
25092
- signatureMap.set(signature.nodeId, signature.refundTxSignature);
25558
+ cpfpSignatureMap.set(signature.nodeId, signature.refundTxSignature);
25559
+ directSignatureMap.set(
25560
+ signature.nodeId,
25561
+ signature.directRefundTxSignature
25562
+ );
25563
+ directFromCpfpSignatureMap.set(
25564
+ signature.nodeId,
25565
+ signature.directFromCpfpRefundTxSignature
25566
+ );
25093
25567
  }
25094
25568
  return {
25095
25569
  transfer: response.transfer,
25096
- signatureMap,
25570
+ signatureMap: cpfpSignatureMap,
25571
+ directSignatureMap,
25572
+ directFromCpfpSignatureMap,
25097
25573
  leafDataMap,
25098
25574
  signingResults: response.signingResults
25099
25575
  };
@@ -25106,38 +25582,68 @@ var TransferService = class extends BaseTransferService {
25106
25582
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
25107
25583
  }
25108
25584
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
25109
- const nodeOutPoint = {
25585
+ const cpfpNodeOutPoint = {
25110
25586
  txid: (0, import_utils14.hexToBytes)(getTxId(nodeTx)),
25111
25587
  index: 0
25112
25588
  };
25589
+ let directNodeTx;
25590
+ let directNodeOutPoint;
25591
+ if (leaf.leaf.directTx.length > 0) {
25592
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
25593
+ directNodeOutPoint = {
25594
+ txid: (0, import_utils14.hexToBytes)(getTxId(directNodeTx)),
25595
+ index: 0
25596
+ };
25597
+ }
25113
25598
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
25114
- const nextSequence = isForClaim ? getTransactionSequence(currRefundTx.getInput(0).sequence) : getNextTransactionSequence(currRefundTx.getInput(0).sequence).nextSequence;
25599
+ const sequence = currRefundTx.getInput(0).sequence;
25600
+ if (!sequence) {
25601
+ throw new ValidationError("Invalid refund transaction", {
25602
+ field: "sequence",
25603
+ value: currRefundTx.getInput(0),
25604
+ expected: "Non-null sequence"
25605
+ });
25606
+ }
25607
+ const { nextSequence, nextDirectSequence } = isForClaim ? getTransactionSequence(sequence) : getNextTransactionSequence(sequence);
25115
25608
  const amountSats = currRefundTx.getOutput(0).amount;
25116
25609
  if (amountSats === void 0) {
25117
25610
  throw new Error("Amount not found in signRefunds");
25118
25611
  }
25119
- const refundTx = createRefundTx(
25120
- nextSequence,
25121
- nodeOutPoint,
25612
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
25613
+ sequence: nextSequence,
25614
+ directSequence: nextDirectSequence,
25615
+ input: cpfpNodeOutPoint,
25616
+ directInput: directNodeOutPoint,
25122
25617
  amountSats,
25123
- refundSigningData.receivingPubkey,
25124
- this.config.getNetwork()
25618
+ receivingPubkey: refundSigningData.receivingPubkey,
25619
+ network: this.config.getNetwork()
25620
+ });
25621
+ refundSigningData.refundTx = cpfpRefundTx;
25622
+ refundSigningData.directRefundTx = directRefundTx;
25623
+ refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
25624
+ const cpfpRefundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
25625
+ const directRefundNonceCommitmentProto = refundSigningData.directSigningNonceCommitment;
25626
+ const directFromCpfpRefundNonceCommitmentProto = refundSigningData.directFromCpfpRefundSigningNonceCommitment;
25627
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(
25628
+ refundSigningData.keyDerivation
25125
25629
  );
25126
- refundSigningData.refundTx = refundTx;
25127
- const refundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
25128
25630
  signingJobs.push({
25129
25631
  leafId: leaf.leaf.id,
25130
25632
  refundTxSigningJob: {
25131
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
25132
- refundSigningData.keyDerivation
25133
- ),
25134
- rawTx: refundTx.toBytes(),
25135
- signingNonceCommitment: refundNonceCommitmentProto.commitment
25633
+ signingPublicKey,
25634
+ rawTx: cpfpRefundTx.toBytes(),
25635
+ signingNonceCommitment: cpfpRefundNonceCommitmentProto.commitment
25136
25636
  },
25137
- // TODO: Add direct refund signature
25138
- directRefundTxSigningJob: void 0,
25139
- // TODO: Add direct refund signature
25140
- directFromCpfpRefundTxSigningJob: void 0
25637
+ directRefundTxSigningJob: directRefundTx ? {
25638
+ signingPublicKey,
25639
+ rawTx: directRefundTx.toBytes(),
25640
+ signingNonceCommitment: directRefundNonceCommitmentProto.commitment
25641
+ } : void 0,
25642
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
25643
+ signingPublicKey,
25644
+ rawTx: directFromCpfpRefundTx.toBytes(),
25645
+ signingNonceCommitment: directFromCpfpRefundNonceCommitmentProto.commitment
25646
+ } : void 0
25141
25647
  });
25142
25648
  }
25143
25649
  return signingJobs;
@@ -25258,13 +25764,17 @@ var TransferService = class extends BaseTransferService {
25258
25764
  const leafDataMap = /* @__PURE__ */ new Map();
25259
25765
  for (const leafKey of leafKeys) {
25260
25766
  const tx = getTxFromRawTxBytes(leafKey.leaf.nodeTx);
25767
+ const directTx = leafKey.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leafKey.leaf.directTx) : void 0;
25261
25768
  leafDataMap.set(leafKey.leaf.id, {
25262
25769
  keyDerivation: leafKey.newKeyDerivation,
25263
25770
  receivingPubkey: await this.config.signer.getPublicKeyFromDerivation(
25264
25771
  leafKey.newKeyDerivation
25265
25772
  ),
25266
25773
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25774
+ directSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25775
+ directFromCpfpRefundSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25267
25776
  tx,
25777
+ directTx,
25268
25778
  vout: leafKey.leaf.vout
25269
25779
  });
25270
25780
  }
@@ -25286,7 +25796,7 @@ var TransferService = class extends BaseTransferService {
25286
25796
  }
25287
25797
  }
25288
25798
  try {
25289
- resp = await sparkClient.claim_transfer_sign_refunds({
25799
+ resp = await sparkClient.claim_transfer_sign_refunds_v2({
25290
25800
  transferId: transfer.id,
25291
25801
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25292
25802
  signingJobs
@@ -25301,7 +25811,7 @@ var TransferService = class extends BaseTransferService {
25301
25811
  this.config.getCoordinatorAddress()
25302
25812
  );
25303
25813
  try {
25304
- return await sparkClient.finalize_node_signatures({
25814
+ return await sparkClient.finalize_node_signatures_v2({
25305
25815
  intent: 1 /* TRANSFER */,
25306
25816
  nodeSignatures
25307
25817
  });
@@ -25340,109 +25850,134 @@ var TransferService = class extends BaseTransferService {
25340
25850
  throw new Error(`Error querying pending transfers by sender: ${error}`);
25341
25851
  }
25342
25852
  }
25343
- async refreshTimelockNodes(nodes, parentNode) {
25344
- if (nodes.length === 0) {
25345
- throw Error("no nodes to refresh");
25346
- }
25853
+ async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
25347
25854
  const signingJobs = [];
25348
- const newNodeTxs = [];
25349
- for (let i = 0; i < nodes.length; i++) {
25350
- const node = nodes[i];
25351
- if (!node) {
25352
- throw Error("could not get node");
25353
- }
25354
- const nodeTx = getTxFromRawTxBytes(node?.nodeTx);
25355
- const input = nodeTx.getInput(0);
25356
- if (!input) {
25357
- throw Error("Could not fetch tx input");
25358
- }
25359
- const newTx = new import_btc_signer2.Transaction({ version: 3, allowUnknownOutputs: true });
25360
- const originalOutput = nodeTx.getOutput(0);
25361
- if (!originalOutput) {
25362
- throw Error("Could not get original output");
25363
- }
25364
- newTx.addOutput({
25365
- script: originalOutput.script,
25366
- amount: originalOutput.amount
25367
- });
25368
- for (let j = 1; j < nodeTx.outputsLength; j++) {
25369
- const additionalOutput = nodeTx.getOutput(j);
25370
- if (additionalOutput) {
25371
- newTx.addOutput(additionalOutput);
25372
- }
25373
- }
25374
- if (i === 0) {
25375
- const currSequence = input.sequence;
25376
- newTx.addInput({
25377
- ...input,
25378
- sequence: getNextTransactionSequence(currSequence).nextSequence
25379
- });
25380
- } else {
25381
- newTx.addInput({
25382
- ...input,
25383
- sequence: initialSequence(),
25384
- txid: newNodeTxs[i - 1]?.id
25385
- });
25386
- }
25387
- signingJobs.push({
25388
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
25389
- type: "leaf" /* LEAF */,
25390
- path: node.id
25391
- }),
25392
- rawTx: newTx.toBytes(),
25393
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25855
+ const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
25856
+ const parentNodeOutput = parentNodeTx.getOutput(0);
25857
+ if (!parentNodeOutput) {
25858
+ throw Error("Could not get parent node output");
25859
+ }
25860
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
25861
+ const nodeInput = nodeTx.getInput(0);
25862
+ const nodeOutput = nodeTx.getOutput(0);
25863
+ if (!nodeOutput) {
25864
+ throw Error("Could not get node output");
25865
+ }
25866
+ let directNodeTx;
25867
+ let directNodeInput;
25868
+ if (node.directTx.length > 0) {
25869
+ directNodeTx = getTxFromRawTxBytes(node.directTx);
25870
+ directNodeInput = directNodeTx.getInput(0);
25871
+ }
25872
+ const currSequence = nodeInput.sequence;
25873
+ if (!currSequence) {
25874
+ throw new ValidationError("Invalid node transaction", {
25875
+ field: "sequence",
25876
+ value: nodeInput,
25877
+ expected: "Non-null sequence"
25394
25878
  });
25395
- newNodeTxs[i] = newTx;
25396
25879
  }
25397
- const leaf = nodes[nodes.length - 1];
25398
- if (!leaf?.refundTx) {
25399
- throw Error("leaf does not have refund tx");
25880
+ let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
25881
+ currSequence,
25882
+ true
25883
+ );
25884
+ const output = {
25885
+ script: parentNodeOutput.script,
25886
+ amount: parentNodeOutput.amount
25887
+ };
25888
+ const newNodeInput = {
25889
+ txid: nodeInput.txid,
25890
+ index: nodeInput.index,
25891
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
25892
+ };
25893
+ const newDirectInput = directNodeTx && directNodeInput ? {
25894
+ txid: directNodeInput.txid,
25895
+ index: directNodeInput.index,
25896
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
25897
+ } : void 0;
25898
+ const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
25899
+ output,
25900
+ newNodeInput,
25901
+ newDirectInput
25902
+ );
25903
+ const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
25904
+ if (!newCpfpNodeOutput) {
25905
+ throw Error("Could not get new cpfp node output");
25400
25906
  }
25401
- const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
25402
- const newRefundTx = new import_btc_signer2.Transaction({
25403
- version: 3,
25404
- allowUnknownOutputs: true
25907
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
25908
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
25909
+ type: "leaf" /* LEAF */,
25910
+ path: node.id
25405
25911
  });
25406
- const originalRefundOutput = refundTx.getOutput(0);
25407
- if (!originalRefundOutput) {
25408
- throw Error("Could not get original refund output");
25409
- }
25410
- newRefundTx.addOutput({
25411
- script: originalRefundOutput.script,
25412
- amount: originalRefundOutput.amount
25912
+ signingJobs.push({
25913
+ signingPublicKey,
25914
+ rawTx: cpfpNodeTx.toBytes(),
25915
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25916
+ type: "node",
25917
+ parentTxOut: parentNodeOutput
25413
25918
  });
25414
- for (let j = 1; j < refundTx.outputsLength; j++) {
25415
- const additionalOutput = refundTx.getOutput(j);
25416
- if (additionalOutput) {
25417
- newRefundTx.addOutput(additionalOutput);
25418
- }
25419
- }
25420
- const refundTxInput = refundTx.getInput(0);
25421
- if (!refundTxInput) {
25422
- throw Error("refund tx doesn't have input");
25919
+ if (newDirectNodeTx) {
25920
+ signingJobs.push({
25921
+ signingPublicKey,
25922
+ rawTx: newDirectNodeTx.toBytes(),
25923
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25924
+ type: "directNode",
25925
+ parentTxOut: parentNodeOutput
25926
+ });
25423
25927
  }
25424
- if (!newNodeTxs[newNodeTxs.length - 1]) {
25425
- throw Error("Could not get last node tx");
25928
+ const newCpfpRefundOutPoint = {
25929
+ txid: (0, import_utils14.hexToBytes)(getTxId(cpfpNodeTx)),
25930
+ index: 0
25931
+ };
25932
+ let newDirectRefundOutPoint;
25933
+ if (newDirectNodeTx) {
25934
+ newDirectRefundOutPoint = {
25935
+ txid: (0, import_utils14.hexToBytes)(getTxId(newDirectNodeTx)),
25936
+ index: 0
25937
+ };
25426
25938
  }
25427
- newRefundTx.addInput({
25428
- ...refundTxInput,
25429
- sequence: initialSequence(),
25430
- txid: getTxId(newNodeTxs[newNodeTxs.length - 1])
25431
- });
25432
- const refundSigningJob = {
25433
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
25939
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
25940
+ sequence: INITIAL_SEQUENCE,
25941
+ directSequence: INITIAL_DIRECT_SEQUENCE,
25942
+ input: newCpfpRefundOutPoint,
25943
+ directInput: newDirectRefundOutPoint,
25944
+ amountSats: nodeOutput.amount,
25945
+ receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
25434
25946
  type: "leaf" /* LEAF */,
25435
- path: leaf.id
25947
+ path: node.id
25436
25948
  }),
25437
- rawTx: newRefundTx.toBytes(),
25438
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25439
- };
25440
- signingJobs.push(refundSigningJob);
25949
+ network: this.config.getNetwork()
25950
+ });
25951
+ signingJobs.push({
25952
+ signingPublicKey,
25953
+ rawTx: cpfpRefundTx.toBytes(),
25954
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25955
+ type: "cpfp",
25956
+ parentTxOut: newCpfpNodeOutput
25957
+ });
25958
+ if (directRefundTx && newDirectNodeOutput) {
25959
+ signingJobs.push({
25960
+ signingPublicKey,
25961
+ rawTx: directRefundTx.toBytes(),
25962
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25963
+ type: "direct",
25964
+ parentTxOut: newDirectNodeOutput
25965
+ });
25966
+ }
25967
+ if (directFromCpfpRefundTx && newCpfpNodeOutput) {
25968
+ signingJobs.push({
25969
+ signingPublicKey,
25970
+ rawTx: directFromCpfpRefundTx.toBytes(),
25971
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25972
+ type: "directFromCpfp",
25973
+ parentTxOut: newCpfpNodeOutput
25974
+ });
25975
+ }
25441
25976
  const sparkClient = await this.connectionManager.createSparkClient(
25442
25977
  this.config.getCoordinatorAddress()
25443
25978
  );
25444
- const response = await sparkClient.refresh_timelock({
25445
- leafId: leaf.id,
25979
+ const response = await sparkClient.refresh_timelock_v2({
25980
+ leafId: node.id,
25446
25981
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25447
25982
  signingJobs: signingJobs.map(getSigningJobProto)
25448
25983
  });
@@ -25452,45 +25987,27 @@ var TransferService = class extends BaseTransferService {
25452
25987
  );
25453
25988
  }
25454
25989
  let nodeSignatures = [];
25455
- let leafSignature;
25456
- let refundSignature;
25457
- let leafNodeId;
25458
- for (let i = 0; i < response.signingResults.length; i++) {
25459
- const signingResult = response.signingResults[i];
25990
+ let leafCpfpSignature;
25991
+ let leafDirectSignature;
25992
+ let cpfpRefundSignature;
25993
+ let directRefundSignature;
25994
+ let directFromCpfpRefundSignature;
25995
+ for (const [i, signingResult] of response.signingResults.entries()) {
25460
25996
  const signingJob = signingJobs[i];
25461
25997
  if (!signingJob || !signingResult) {
25462
25998
  throw Error("Signing job does not exist");
25463
25999
  }
25464
- if (!signingJob.signingNonceCommitment) {
25465
- throw Error("nonce commitment does not exist");
25466
- }
25467
26000
  const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
25468
- let parentTx;
25469
- let nodeId;
25470
- let vout;
25471
- if (i === nodes.length) {
25472
- nodeId = nodes[i - 1]?.id;
25473
- parentTx = newNodeTxs[i - 1];
25474
- vout = 0;
25475
- } else if (i === 0) {
25476
- nodeId = nodes[i]?.id;
25477
- parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
25478
- vout = nodes[i]?.vout;
25479
- } else {
25480
- nodeId = nodes[i]?.id;
25481
- parentTx = newNodeTxs[i - 1];
25482
- vout = nodes[i]?.vout;
26001
+ const txOut = signingJob.parentTxOut;
26002
+ if (!txOut) {
26003
+ throw Error("Could not get tx out");
25483
26004
  }
25484
- if (!parentTx || !nodeId || vout === void 0) {
25485
- throw Error("Could not parse signing results");
25486
- }
25487
- const txOut = parentTx.getOutput(vout);
25488
26005
  const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
25489
26006
  const userSignature = await this.config.signer.signFrost({
25490
26007
  message: rawTxSighash,
25491
26008
  keyDerivation: {
25492
26009
  type: "leaf" /* LEAF */,
25493
- path: nodeId
26010
+ path: node.id
25494
26011
  },
25495
26012
  publicKey: signingJob.signingPublicKey,
25496
26013
  verifyingKey: signingResult.verifyingKey,
@@ -25509,41 +26026,35 @@ var TransferService = class extends BaseTransferService {
25509
26026
  selfSignature: userSignature,
25510
26027
  adaptorPubKey: new Uint8Array()
25511
26028
  });
25512
- if (i !== nodes.length && i !== nodes.length - 1) {
25513
- nodeSignatures.push({
25514
- nodeId,
25515
- nodeTxSignature: signature,
25516
- refundTxSignature: new Uint8Array(),
25517
- // TODO: Add direct refund signature
25518
- directNodeTxSignature: new Uint8Array(),
25519
- directRefundTxSignature: new Uint8Array(),
25520
- directFromCpfpRefundTxSignature: new Uint8Array()
25521
- });
25522
- } else if (i === nodes.length) {
25523
- refundSignature = signature;
25524
- } else if (i === nodes.length - 1) {
25525
- leafNodeId = nodeId;
25526
- leafSignature = signature;
26029
+ if (signingJob.type === "node") {
26030
+ leafCpfpSignature = signature;
26031
+ } else if (signingJob.type === "directNode") {
26032
+ leafDirectSignature = signature;
26033
+ } else if (signingJob.type === "cpfp") {
26034
+ cpfpRefundSignature = signature;
26035
+ } else if (signingJob.type === "direct") {
26036
+ directRefundSignature = signature;
26037
+ } else if (signingJob.type === "directFromCpfp") {
26038
+ directFromCpfpRefundSignature = signature;
25527
26039
  }
25528
26040
  }
25529
- if (!leafSignature || !refundSignature || !leafNodeId) {
25530
- throw Error("leaf or refund signature does not exist");
25531
- }
25532
26041
  nodeSignatures.push({
25533
- nodeId: leafNodeId,
25534
- nodeTxSignature: leafSignature,
25535
- refundTxSignature: refundSignature,
25536
- // TODO: Add direct refund signature
25537
- directNodeTxSignature: new Uint8Array(),
25538
- directRefundTxSignature: new Uint8Array(),
25539
- directFromCpfpRefundTxSignature: new Uint8Array()
26042
+ nodeId: node.id,
26043
+ nodeTxSignature: leafCpfpSignature || new Uint8Array(),
26044
+ directNodeTxSignature: leafDirectSignature || new Uint8Array(),
26045
+ refundTxSignature: cpfpRefundSignature || new Uint8Array(),
26046
+ directRefundTxSignature: directRefundSignature || new Uint8Array(),
26047
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
25540
26048
  });
25541
- const result = await sparkClient.finalize_node_signatures({
26049
+ const result = await sparkClient.finalize_node_signatures_v2({
25542
26050
  intent: 3 /* REFRESH */,
25543
26051
  nodeSignatures
25544
26052
  });
25545
26053
  return result;
25546
26054
  }
26055
+ async refreshTimelockNodes(node, parentNode) {
26056
+ return await this.refreshTimelockNodesInternal(node, parentNode);
26057
+ }
25547
26058
  async extendTimelock(node) {
25548
26059
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
25549
26060
  const refundTx = getTxFromRawTxBytes(node.refundTx);
@@ -25552,7 +26063,10 @@ var TransferService = class extends BaseTransferService {
25552
26063
  txid: (0, import_utils14.hexToBytes)(getTxId(nodeTx)),
25553
26064
  index: 0
25554
26065
  };
25555
- const { nextSequence: newNodeSequence } = getNextTransactionSequence(refundSequence);
26066
+ const {
26067
+ nextSequence: newNodeSequence,
26068
+ nextDirectSequence: newDirectNodeSequence
26069
+ } = getNextTransactionSequence(refundSequence);
25556
26070
  const newNodeTx = new import_btc_signer2.Transaction({
25557
26071
  version: 3,
25558
26072
  allowUnknownOutputs: true
@@ -25565,81 +26079,122 @@ var TransferService = class extends BaseTransferService {
25565
26079
  newNodeTx.addOutput({
25566
26080
  script: originalOutput.script,
25567
26081
  amount: originalOutput.amount
25568
- // feeReducedAmount,
25569
26082
  });
25570
26083
  newNodeTx.addOutput(getEphemeralAnchorOutput());
25571
- const newRefundOutPoint = {
26084
+ let newDirectNodeTx;
26085
+ if (node.directTx.length > 0) {
26086
+ newDirectNodeTx = new import_btc_signer2.Transaction({
26087
+ version: 3,
26088
+ allowUnknownOutputs: true
26089
+ });
26090
+ newDirectNodeTx.addInput({
26091
+ ...newNodeOutPoint,
26092
+ sequence: newDirectNodeSequence
26093
+ });
26094
+ newDirectNodeTx.addOutput({
26095
+ script: originalOutput.script,
26096
+ amount: maybeApplyFee(originalOutput.amount)
26097
+ });
26098
+ }
26099
+ const newCpfpRefundOutPoint = {
25572
26100
  txid: (0, import_utils14.hexToBytes)(getTxId(newNodeTx)),
25573
26101
  index: 0
25574
26102
  };
26103
+ let newDirectRefundOutPoint;
26104
+ if (newDirectNodeTx) {
26105
+ newDirectRefundOutPoint = {
26106
+ txid: (0, import_utils14.hexToBytes)(getTxId(newDirectNodeTx)),
26107
+ index: 0
26108
+ };
26109
+ }
25575
26110
  const amountSats = refundTx.getOutput(0).amount;
25576
26111
  if (amountSats === void 0) {
25577
26112
  throw new Error("Amount not found in extendTimelock");
25578
26113
  }
25579
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
26114
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
25580
26115
  type: "leaf" /* LEAF */,
25581
26116
  path: node.id
25582
26117
  });
25583
- const newRefundTx = createRefundTx(
25584
- initialSequence(),
25585
- newRefundOutPoint,
26118
+ const {
26119
+ cpfpRefundTx: newCpfpRefundTx,
26120
+ directRefundTx: newDirectRefundTx,
26121
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
26122
+ } = createRefundTxs({
26123
+ sequence: INITIAL_SEQUENCE,
26124
+ directSequence: INITIAL_DIRECT_SEQUENCE,
26125
+ input: newCpfpRefundOutPoint,
26126
+ directInput: newDirectRefundOutPoint,
25586
26127
  amountSats,
25587
- // feeReducedRefundAmount,
25588
- signingPubKey,
25589
- this.config.getNetwork()
25590
- );
26128
+ receivingPubkey: signingPublicKey,
26129
+ network: this.config.getNetwork()
26130
+ });
26131
+ if (!newCpfpRefundTx) {
26132
+ throw new ValidationError(
26133
+ "Failed to create refund transactions in extendTimelock"
26134
+ );
26135
+ }
25591
26136
  const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
25592
- const refundSighash = getSigHashFromTx(
25593
- newRefundTx,
26137
+ const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
26138
+ const cpfpRefundSighash = getSigHashFromTx(
26139
+ newCpfpRefundTx,
25594
26140
  0,
25595
26141
  newNodeTx.getOutput(0)
25596
26142
  );
26143
+ const directRefundSighash = newDirectNodeTx && newDirectRefundTx ? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0)) : void 0;
26144
+ const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx ? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0)) : void 0;
25597
26145
  const newNodeSigningJob = {
25598
- signingPublicKey: signingPubKey,
26146
+ signingPublicKey,
25599
26147
  rawTx: newNodeTx.toBytes(),
25600
26148
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25601
26149
  };
25602
- const newRefundSigningJob = {
25603
- signingPublicKey: signingPubKey,
25604
- rawTx: newRefundTx.toBytes(),
26150
+ const newDirectNodeSigningJob = newDirectNodeTx ? {
26151
+ signingPublicKey,
26152
+ rawTx: newDirectNodeTx.toBytes(),
26153
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
26154
+ } : void 0;
26155
+ const newCpfpRefundSigningJob = {
26156
+ signingPublicKey,
26157
+ rawTx: newCpfpRefundTx.toBytes(),
25605
26158
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25606
26159
  };
26160
+ const newDirectRefundSigningJob = newDirectRefundTx ? {
26161
+ signingPublicKey,
26162
+ rawTx: newDirectRefundTx.toBytes(),
26163
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
26164
+ } : void 0;
26165
+ const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
26166
+ signingPublicKey,
26167
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
26168
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
26169
+ } : void 0;
25607
26170
  const sparkClient = await this.connectionManager.createSparkClient(
25608
26171
  this.config.getCoordinatorAddress()
25609
26172
  );
25610
- const response = await sparkClient.extend_leaf({
26173
+ const response = await sparkClient.extend_leaf_v2({
25611
26174
  leafId: node.id,
25612
26175
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25613
26176
  nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
25614
- refundTxSigningJob: getSigningJobProto(newRefundSigningJob)
26177
+ directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
26178
+ refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
26179
+ directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
26180
+ directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
25615
26181
  });
25616
26182
  if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
25617
26183
  throw new Error("Signing result does not exist");
25618
26184
  }
26185
+ const keyDerivation = {
26186
+ type: "leaf" /* LEAF */,
26187
+ path: node.id
26188
+ };
25619
26189
  const nodeUserSig = await this.config.signer.signFrost({
25620
26190
  message: nodeSighash,
25621
- keyDerivation: {
25622
- type: "leaf" /* LEAF */,
25623
- path: node.id
25624
- },
25625
- publicKey: signingPubKey,
26191
+ keyDerivation,
26192
+ publicKey: signingPublicKey,
25626
26193
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
25627
26194
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
25628
26195
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
25629
26196
  adaptorPubKey: new Uint8Array()
25630
26197
  });
25631
- const refundUserSig = await this.config.signer.signFrost({
25632
- message: refundSighash,
25633
- keyDerivation: {
25634
- type: "leaf" /* LEAF */,
25635
- path: node.id
25636
- },
25637
- publicKey: signingPubKey,
25638
- verifyingKey: response.refundTxSigningResult.verifyingKey,
25639
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
25640
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
25641
- adaptorPubKey: new Uint8Array()
25642
- });
25643
26198
  const nodeSig = await this.config.signer.aggregateFrost({
25644
26199
  message: nodeSighash,
25645
26200
  statechainSignatures: response.nodeTxSigningResult.signingResult?.signatureShares,
@@ -25647,122 +26202,253 @@ var TransferService = class extends BaseTransferService {
25647
26202
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
25648
26203
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
25649
26204
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
25650
- publicKey: signingPubKey,
26205
+ publicKey: signingPublicKey,
25651
26206
  selfSignature: nodeUserSig,
25652
26207
  adaptorPubKey: new Uint8Array()
25653
26208
  });
25654
- const refundSig = await this.config.signer.aggregateFrost({
25655
- message: refundSighash,
26209
+ let directNodeSig;
26210
+ if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
26211
+ const directNodeUserSig = await this.config.signer.signFrost({
26212
+ message: directNodeSighash,
26213
+ keyDerivation,
26214
+ publicKey: signingPublicKey,
26215
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
26216
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
26217
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
26218
+ adaptorPubKey: new Uint8Array()
26219
+ });
26220
+ directNodeSig = await this.config.signer.aggregateFrost({
26221
+ message: directNodeSighash,
26222
+ statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
26223
+ statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
26224
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
26225
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
26226
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
26227
+ publicKey: signingPublicKey,
26228
+ selfSignature: directNodeUserSig,
26229
+ adaptorPubKey: new Uint8Array()
26230
+ });
26231
+ }
26232
+ const cpfpRefundUserSig = await this.config.signer.signFrost({
26233
+ message: cpfpRefundSighash,
26234
+ keyDerivation,
26235
+ publicKey: signingPublicKey,
26236
+ verifyingKey: response.refundTxSigningResult.verifyingKey,
26237
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
26238
+ statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
26239
+ adaptorPubKey: new Uint8Array()
26240
+ });
26241
+ const cpfpRefundSig = await this.config.signer.aggregateFrost({
26242
+ message: cpfpRefundSighash,
25656
26243
  statechainSignatures: response.refundTxSigningResult.signingResult?.signatureShares,
25657
26244
  statechainPublicKeys: response.refundTxSigningResult.signingResult?.publicKeys,
25658
26245
  verifyingKey: response.refundTxSigningResult.verifyingKey,
25659
26246
  statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
25660
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
25661
- publicKey: signingPubKey,
25662
- selfSignature: refundUserSig,
26247
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
26248
+ publicKey: signingPublicKey,
26249
+ selfSignature: cpfpRefundUserSig,
25663
26250
  adaptorPubKey: new Uint8Array()
25664
26251
  });
25665
- return await sparkClient.finalize_node_signatures({
26252
+ let directRefundSig;
26253
+ if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
26254
+ const directRefundUserSig = await this.config.signer.signFrost({
26255
+ message: directRefundSighash,
26256
+ keyDerivation,
26257
+ publicKey: signingPublicKey,
26258
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
26259
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
26260
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
26261
+ adaptorPubKey: new Uint8Array()
26262
+ });
26263
+ directRefundSig = await this.config.signer.aggregateFrost({
26264
+ message: directRefundSighash,
26265
+ statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
26266
+ statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
26267
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
26268
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
26269
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
26270
+ publicKey: signingPublicKey,
26271
+ selfSignature: directRefundUserSig,
26272
+ adaptorPubKey: new Uint8Array()
26273
+ });
26274
+ }
26275
+ let directFromCpfpRefundSig;
26276
+ if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
26277
+ const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
26278
+ message: directFromCpfpRefundSighash,
26279
+ keyDerivation,
26280
+ publicKey: signingPublicKey,
26281
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
26282
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
26283
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
26284
+ adaptorPubKey: new Uint8Array()
26285
+ });
26286
+ directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
26287
+ message: directFromCpfpRefundSighash,
26288
+ statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
26289
+ statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
26290
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
26291
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
26292
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
26293
+ publicKey: signingPublicKey,
26294
+ selfSignature: directFromCpfpRefundUserSig,
26295
+ adaptorPubKey: new Uint8Array()
26296
+ });
26297
+ }
26298
+ return await sparkClient.finalize_node_signatures_v2({
25666
26299
  intent: 4 /* EXTEND */,
25667
26300
  nodeSignatures: [
25668
26301
  {
25669
26302
  nodeId: response.leafId,
25670
26303
  nodeTxSignature: nodeSig,
25671
- refundTxSignature: refundSig
26304
+ directNodeTxSignature: directNodeSig,
26305
+ refundTxSignature: cpfpRefundSig,
26306
+ directRefundTxSignature: directRefundSig,
26307
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSig
25672
26308
  }
25673
26309
  ]
25674
26310
  });
25675
26311
  }
25676
- async refreshTimelockRefundTx(node) {
26312
+ async testonly_expireTimeLockNodeTx(node, parentNode) {
26313
+ return await this.refreshTimelockNodesInternal(node, parentNode, true);
26314
+ }
26315
+ async testonly_expireTimeLockRefundtx(node) {
25677
26316
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
25678
- const refundTx = getTxFromRawTxBytes(node.refundTx);
25679
- const currSequence = refundTx.getInput(0).sequence || 0;
25680
- const { nextSequence } = getNextTransactionSequence(currSequence);
25681
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
26317
+ const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
26318
+ const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
26319
+ const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
26320
+ const currTimelock = getCurrentTimelock(currSequence);
26321
+ if (currTimelock <= 100) {
26322
+ throw new ValidationError("Cannot expire timelock below 100", {
26323
+ field: "currTimelock",
26324
+ value: currTimelock,
26325
+ expected: "Timelock greater than 100"
26326
+ });
26327
+ }
26328
+ const nextSequence = TEST_UNILATERAL_SEQUENCE;
26329
+ const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
26330
+ const nodeOutput = nodeTx.getOutput(0);
26331
+ if (!nodeOutput) {
26332
+ throw Error("Could not get node output");
26333
+ }
26334
+ const keyDerivation = {
25682
26335
  type: "leaf" /* LEAF */,
25683
26336
  path: node.id
25684
- });
25685
- const newRefundTx = new import_btc_signer2.Transaction({
25686
- version: 3,
25687
- allowUnknownOutputs: true
25688
- });
25689
- const originalRefundOutput = refundTx.getOutput(0);
25690
- if (!originalRefundOutput) {
25691
- throw Error("Could not get original refund output");
26337
+ };
26338
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
26339
+ const cpfpRefundOutPoint = {
26340
+ txid: (0, import_utils14.hexToBytes)(getTxId(nodeTx)),
26341
+ index: 0
26342
+ };
26343
+ let directRefundOutPoint;
26344
+ if (directNodeTx) {
26345
+ directRefundOutPoint = {
26346
+ txid: (0, import_utils14.hexToBytes)(getTxId(directNodeTx)),
26347
+ index: 0
26348
+ };
25692
26349
  }
25693
- newRefundTx.addOutput({
25694
- script: originalRefundOutput.script,
25695
- amount: originalRefundOutput.amount
26350
+ const {
26351
+ cpfpRefundTx: newCpfpRefundTx,
26352
+ directRefundTx: newDirectRefundTx,
26353
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
26354
+ } = createRefundTxs({
26355
+ sequence: nextSequence,
26356
+ directSequence: nextDirectSequence,
26357
+ input: cpfpRefundOutPoint,
26358
+ directInput: directRefundOutPoint,
26359
+ amountSats: nodeOutput.amount,
26360
+ receivingPubkey: signingPublicKey,
26361
+ network: this.config.getNetwork()
25696
26362
  });
25697
- for (let j = 1; j < refundTx.outputsLength; j++) {
25698
- const additionalOutput = refundTx.getOutput(j);
25699
- if (additionalOutput) {
25700
- newRefundTx.addOutput(additionalOutput);
25701
- }
26363
+ const signingJobs = [];
26364
+ signingJobs.push({
26365
+ signingPublicKey,
26366
+ rawTx: newCpfpRefundTx.toBytes(),
26367
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26368
+ type: "cpfp",
26369
+ parentTxOut: nodeOutput
26370
+ });
26371
+ const directNodeTxOut = directNodeTx?.getOutput(0);
26372
+ if (newDirectRefundTx && directNodeTxOut) {
26373
+ signingJobs.push({
26374
+ signingPublicKey,
26375
+ rawTx: newDirectRefundTx.toBytes(),
26376
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26377
+ type: "direct",
26378
+ parentTxOut: directNodeTxOut
26379
+ });
25702
26380
  }
25703
- const refundTxInput = refundTx.getInput(0);
25704
- if (!refundTxInput) {
25705
- throw Error("refund tx doesn't have input");
26381
+ if (newDirectFromCpfpRefundTx) {
26382
+ signingJobs.push({
26383
+ signingPublicKey,
26384
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
26385
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26386
+ type: "directFromCpfp",
26387
+ parentTxOut: nodeOutput
26388
+ });
25706
26389
  }
25707
- newRefundTx.addInput({
25708
- ...refundTxInput,
25709
- sequence: nextSequence
25710
- });
25711
- const refundSigningJob = {
25712
- signingPublicKey: signingPubKey,
25713
- rawTx: newRefundTx.toBytes(),
25714
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25715
- };
25716
26390
  const sparkClient = await this.connectionManager.createSparkClient(
25717
26391
  this.config.getCoordinatorAddress()
25718
26392
  );
25719
- const response = await sparkClient.refresh_timelock({
26393
+ const response = await sparkClient.refresh_timelock_v2({
25720
26394
  leafId: node.id,
25721
26395
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25722
- signingJobs: [getSigningJobProto(refundSigningJob)]
26396
+ signingJobs: signingJobs.map(getSigningJobProto)
25723
26397
  });
25724
- if (response.signingResults.length !== 1) {
26398
+ if (response.signingResults.length !== signingJobs.length) {
25725
26399
  throw Error(
25726
- `Expected 1 signing result, got ${response.signingResults.length}`
26400
+ `Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`
25727
26401
  );
25728
26402
  }
25729
- const signingResult = response.signingResults[0];
25730
- if (!signingResult || !refundSigningJob.signingNonceCommitment) {
25731
- throw Error("Signing result or nonce commitment does not exist");
26403
+ let cpfpRefundSignature;
26404
+ let directRefundSignature;
26405
+ let directFromCpfpRefundSignature;
26406
+ for (const [i, signingJob] of signingJobs.entries()) {
26407
+ const signingResult = response.signingResults[i];
26408
+ if (!signingResult) {
26409
+ throw Error("Signing result does not exist");
26410
+ }
26411
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
26412
+ const txOut = signingJob.parentTxOut;
26413
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
26414
+ const userSignature = await this.config.signer.signFrost({
26415
+ message: rawTxSighash,
26416
+ keyDerivation,
26417
+ publicKey: signingPublicKey,
26418
+ verifyingKey: signingResult.verifyingKey,
26419
+ selfCommitment: signingJob.signingNonceCommitment,
26420
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
26421
+ adaptorPubKey: new Uint8Array()
26422
+ });
26423
+ const signature = await this.config.signer.aggregateFrost({
26424
+ message: rawTxSighash,
26425
+ statechainSignatures: signingResult.signingResult?.signatureShares,
26426
+ statechainPublicKeys: signingResult.signingResult?.publicKeys,
26427
+ verifyingKey: signingResult.verifyingKey,
26428
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
26429
+ selfCommitment: signingJob.signingNonceCommitment,
26430
+ publicKey: signingPublicKey,
26431
+ selfSignature: userSignature,
26432
+ adaptorPubKey: new Uint8Array()
26433
+ });
26434
+ if (signingJob.type === "cpfp") {
26435
+ cpfpRefundSignature = signature;
26436
+ } else if (signingJob.type === "direct") {
26437
+ directRefundSignature = signature;
26438
+ } else if (signingJob.type === "directFromCpfp") {
26439
+ directFromCpfpRefundSignature = signature;
26440
+ }
25732
26441
  }
25733
- const rawTx = getTxFromRawTxBytes(refundSigningJob.rawTx);
25734
- const txOut = nodeTx.getOutput(0);
25735
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
25736
- const userSignature = await this.config.signer.signFrost({
25737
- message: rawTxSighash,
25738
- keyDerivation: {
25739
- type: "leaf" /* LEAF */,
25740
- path: node.id
25741
- },
25742
- publicKey: signingPubKey,
25743
- verifyingKey: signingResult.verifyingKey,
25744
- selfCommitment: refundSigningJob.signingNonceCommitment,
25745
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
25746
- adaptorPubKey: new Uint8Array()
25747
- });
25748
- const signature = await this.config.signer.aggregateFrost({
25749
- message: rawTxSighash,
25750
- statechainSignatures: signingResult.signingResult?.signatureShares,
25751
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
25752
- verifyingKey: signingResult.verifyingKey,
25753
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
25754
- selfCommitment: refundSigningJob.signingNonceCommitment,
25755
- publicKey: signingPubKey,
25756
- selfSignature: userSignature,
25757
- adaptorPubKey: new Uint8Array()
25758
- });
25759
- const result = await sparkClient.finalize_node_signatures({
26442
+ const result = await sparkClient.finalize_node_signatures_v2({
25760
26443
  intent: 3 /* REFRESH */,
25761
26444
  nodeSignatures: [
25762
26445
  {
25763
26446
  nodeId: node.id,
25764
26447
  nodeTxSignature: new Uint8Array(),
25765
- refundTxSignature: signature
26448
+ directNodeTxSignature: new Uint8Array(),
26449
+ refundTxSignature: cpfpRefundSignature,
26450
+ directRefundTxSignature: directRefundSignature,
26451
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature
25766
26452
  }
25767
26453
  ]
25768
26454
  });
@@ -25781,7 +26467,12 @@ var CoopExitService = class extends BaseTransferService {
25781
26467
  connectorOutputs,
25782
26468
  receiverPubKey
25783
26469
  }) {
25784
- const { transfer, signaturesMap } = await this.signCoopExitRefunds(
26470
+ const {
26471
+ transfer,
26472
+ signaturesMap,
26473
+ directSignaturesMap,
26474
+ directFromCpfpSignaturesMap
26475
+ } = await this.signCoopExitRefunds(
25785
26476
  leaves,
25786
26477
  exitTxId,
25787
26478
  connectorOutputs,
@@ -25790,34 +26481,81 @@ var CoopExitService = class extends BaseTransferService {
25790
26481
  const transferTweak = await this.deliverTransferPackage(
25791
26482
  transfer,
25792
26483
  leaves,
25793
- signaturesMap
26484
+ signaturesMap,
26485
+ directSignaturesMap,
26486
+ directFromCpfpSignaturesMap
25794
26487
  );
25795
- return { transfer: transferTweak, signaturesMap };
25796
- }
25797
- createConnectorRefundTransaction(sequence, nodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
25798
- const refundTx = new import_btc_signer3.Transaction();
25799
- if (!nodeOutPoint.txid || nodeOutPoint.index === void 0) {
25800
- throw new ValidationError("Invalid node outpoint", {
25801
- field: "nodeOutPoint",
25802
- value: { txid: nodeOutPoint.txid, index: nodeOutPoint.index },
26488
+ return {
26489
+ transfer: transferTweak,
26490
+ signaturesMap,
26491
+ directSignaturesMap,
26492
+ directFromCpfpSignaturesMap
26493
+ };
26494
+ }
26495
+ createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
26496
+ const cpfpRefundTx = new import_btc_signer3.Transaction();
26497
+ if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
26498
+ throw new ValidationError("Invalid CPFP node outpoint", {
26499
+ field: "cpfpNodeOutPoint",
26500
+ value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
25803
26501
  expected: "Both txid and index must be defined"
25804
26502
  });
25805
26503
  }
25806
- refundTx.addInput({
25807
- txid: nodeOutPoint.txid,
25808
- index: nodeOutPoint.index,
26504
+ cpfpRefundTx.addInput({
26505
+ txid: cpfpNodeOutPoint.txid,
26506
+ index: cpfpNodeOutPoint.index,
25809
26507
  sequence
25810
26508
  });
25811
- refundTx.addInput(connectorOutput);
26509
+ cpfpRefundTx.addInput(connectorOutput);
25812
26510
  const receiverScript = getP2TRScriptFromPublicKey(
25813
26511
  receiverPubKey,
25814
26512
  this.config.getNetwork()
25815
26513
  );
25816
- refundTx.addOutput({
26514
+ cpfpRefundTx.addOutput({
25817
26515
  script: receiverScript,
25818
26516
  amount: amountSats
25819
26517
  });
25820
- return refundTx;
26518
+ let directRefundTx;
26519
+ let directFromCpfpRefundTx;
26520
+ if (directNodeOutPoint) {
26521
+ if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
26522
+ throw new ValidationError("Invalid direct node outpoint", {
26523
+ field: "directNodeOutPoint",
26524
+ value: {
26525
+ txid: directNodeOutPoint.txid,
26526
+ index: directNodeOutPoint.index
26527
+ },
26528
+ expected: "Both txid and index must be defined"
26529
+ });
26530
+ }
26531
+ directRefundTx = new import_btc_signer3.Transaction();
26532
+ directRefundTx.addInput({
26533
+ txid: directNodeOutPoint.txid,
26534
+ index: directNodeOutPoint.index,
26535
+ sequence: directSequence
26536
+ });
26537
+ directRefundTx.addInput(connectorOutput);
26538
+ directRefundTx.addOutput({
26539
+ script: receiverScript,
26540
+ amount: maybeApplyFee(amountSats)
26541
+ });
26542
+ directFromCpfpRefundTx = new import_btc_signer3.Transaction();
26543
+ directFromCpfpRefundTx.addInput({
26544
+ txid: cpfpNodeOutPoint.txid,
26545
+ index: cpfpNodeOutPoint.index,
26546
+ sequence: directSequence
26547
+ });
26548
+ directFromCpfpRefundTx.addInput(connectorOutput);
26549
+ directFromCpfpRefundTx.addOutput({
26550
+ script: receiverScript,
26551
+ amount: maybeApplyFee(amountSats)
26552
+ });
26553
+ }
26554
+ return {
26555
+ cpfpRefundTx,
26556
+ directRefundTx,
26557
+ directFromCpfpRefundTx
26558
+ };
25821
26559
  }
25822
26560
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey) {
25823
26561
  if (leaves.length !== connectorOutputs.length) {
@@ -25853,39 +26591,67 @@ var CoopExitService = class extends BaseTransferService {
25853
26591
  });
25854
26592
  }
25855
26593
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
25856
- const { nextSequence } = getNextTransactionSequence(
25857
- currentRefundTx.getInput(0).sequence
25858
- );
25859
- const refundTx = this.createConnectorRefundTransaction(
26594
+ const sequence = currentRefundTx.getInput(0).sequence;
26595
+ if (!sequence) {
26596
+ throw new ValidationError("Invalid refund transaction", {
26597
+ field: "sequence",
26598
+ value: currentRefundTx.getInput(0),
26599
+ expected: "Non-null sequence"
26600
+ });
26601
+ }
26602
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
26603
+ let currentDirectRefundTx;
26604
+ if (leaf.leaf.directRefundTx.length > 0) {
26605
+ currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
26606
+ }
26607
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
25860
26608
  nextSequence,
26609
+ nextDirectSequence,
25861
26610
  currentRefundTx.getInput(0),
26611
+ currentDirectRefundTx?.getInput(0),
25862
26612
  connectorOutput,
25863
26613
  BigInt(leaf.leaf.value),
25864
26614
  receiverPubKey
25865
26615
  );
25866
26616
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26617
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26618
+ const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26619
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(leaf.keyDerivation);
25867
26620
  const signingJob = {
25868
26621
  leafId: leaf.leaf.id,
25869
26622
  refundTxSigningJob: {
25870
26623
  signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
25871
26624
  leaf.keyDerivation
25872
26625
  ),
25873
- rawTx: refundTx.toBytes(),
26626
+ rawTx: cpfpRefundTx.toBytes(),
25874
26627
  signingNonceCommitment: signingNonceCommitment.commitment
25875
26628
  },
25876
- // TODO: Add direct refund signature
25877
- directRefundTxSigningJob: void 0,
25878
- directFromCpfpRefundTxSigningJob: void 0
26629
+ directRefundTxSigningJob: directRefundTx ? {
26630
+ signingPublicKey,
26631
+ rawTx: directRefundTx.toBytes(),
26632
+ signingNonceCommitment: directSigningNonceCommitment.commitment
26633
+ } : void 0,
26634
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
26635
+ signingPublicKey,
26636
+ rawTx: directFromCpfpRefundTx.toBytes(),
26637
+ signingNonceCommitment: directFromCpfpSigningNonceCommitment.commitment
26638
+ } : void 0
25879
26639
  };
25880
26640
  signingJobs.push(signingJob);
25881
26641
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
26642
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
25882
26643
  leafDataMap.set(leaf.leaf.id, {
25883
26644
  keyDerivation: leaf.keyDerivation,
25884
- refundTx,
26645
+ receivingPubkey: receiverPubKey,
25885
26646
  signingNonceCommitment,
26647
+ directSigningNonceCommitment,
25886
26648
  tx,
25887
- vout: leaf.leaf.vout,
25888
- receivingPubkey: receiverPubKey
26649
+ directTx,
26650
+ refundTx: cpfpRefundTx,
26651
+ directRefundTx,
26652
+ directFromCpfpRefundTx,
26653
+ directFromCpfpRefundSigningNonceCommitment: directFromCpfpSigningNonceCommitment,
26654
+ vout: leaf.leaf.vout
25889
26655
  });
25890
26656
  }
25891
26657
  const sparkClient = await this.connectionManager.createSparkClient(
@@ -25893,7 +26659,7 @@ var CoopExitService = class extends BaseTransferService {
25893
26659
  );
25894
26660
  let response;
25895
26661
  try {
25896
- response = await sparkClient.cooperative_exit({
26662
+ response = await sparkClient.cooperative_exit_v2({
25897
26663
  transfer: {
25898
26664
  transferId: (0, import_uuidv73.uuidv7)(),
25899
26665
  leavesToSend: signingJobs,
@@ -25927,10 +26693,25 @@ var CoopExitService = class extends BaseTransferService {
25927
26693
  response.signingResults
25928
26694
  );
25929
26695
  const signaturesMap = /* @__PURE__ */ new Map();
26696
+ const directSignaturesMap = /* @__PURE__ */ new Map();
26697
+ const directFromCpfpSignaturesMap = /* @__PURE__ */ new Map();
25930
26698
  for (const signature of signatures) {
25931
26699
  signaturesMap.set(signature.nodeId, signature.refundTxSignature);
26700
+ directSignaturesMap.set(
26701
+ signature.nodeId,
26702
+ signature.directRefundTxSignature
26703
+ );
26704
+ directFromCpfpSignaturesMap.set(
26705
+ signature.nodeId,
26706
+ signature.directFromCpfpRefundTxSignature
26707
+ );
25932
26708
  }
25933
- return { transfer: response.transfer, signaturesMap };
26709
+ return {
26710
+ transfer: response.transfer,
26711
+ signaturesMap,
26712
+ directSignaturesMap,
26713
+ directFromCpfpSignaturesMap
26714
+ };
25934
26715
  }
25935
26716
  };
25936
26717
 
@@ -25939,10 +26720,8 @@ init_buffer();
25939
26720
  var import_secp256k110 = require("@noble/curves/secp256k1");
25940
26721
  var import_sha29 = require("@noble/hashes/sha2");
25941
26722
  var import_utils15 = require("@noble/hashes/utils");
25942
- var btc5 = __toESM(require("@scure/btc-signer"), 1);
25943
26723
  var import_btc_signer4 = require("@scure/btc-signer");
25944
26724
  var import_utils16 = require("@scure/btc-signer/utils");
25945
- var INITIAL_TIME_LOCK2 = 2e3;
25946
26725
  var DepositService = class {
25947
26726
  config;
25948
26727
  connectionManager;
@@ -26060,7 +26839,6 @@ var DepositService = class {
26060
26839
  depositTx,
26061
26840
  vout
26062
26841
  }) {
26063
- const rootTx = new import_btc_signer4.Transaction({ version: 3 });
26064
26842
  const output = depositTx.getOutput(vout);
26065
26843
  if (!output) {
26066
26844
  throw new ValidationError("Invalid deposit transaction output", {
@@ -26078,39 +26856,345 @@ var DepositService = class {
26078
26856
  expected: "Output with script and amount"
26079
26857
  });
26080
26858
  }
26081
- let outputAmount = amount;
26082
- rootTx.addInput({
26083
- txid: getTxId(depositTx),
26859
+ const depositOutPoint = {
26860
+ txid: (0, import_utils15.hexToBytes)(getTxId(depositTx)),
26084
26861
  index: vout
26085
- });
26086
- rootTx.addOutput({
26862
+ };
26863
+ const depositTxOut = {
26087
26864
  script,
26088
- amount: outputAmount
26865
+ amount
26866
+ };
26867
+ const [cpfpRootTx, directRootTx] = createRootTx(
26868
+ depositOutPoint,
26869
+ depositTxOut
26870
+ );
26871
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26872
+ const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26873
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
26874
+ const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
26875
+ const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
26876
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
26877
+ sequence: INITIAL_SEQUENCE,
26878
+ directSequence: INITIAL_DIRECT_SEQUENCE,
26879
+ input: { txid: (0, import_utils15.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
26880
+ directInput: { txid: (0, import_utils15.hexToBytes)(getTxId(directRootTx)), index: 0 },
26881
+ amountSats: amount,
26882
+ receivingPubkey: signingPubKey,
26883
+ network: this.config.getNetwork()
26089
26884
  });
26090
- rootTx.addOutput(getEphemeralAnchorOutput());
26091
- const rootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26092
- const rootTxSighash = getSigHashFromTx(rootTx, 0, output);
26093
- const refundTx = new import_btc_signer4.Transaction({ version: 3 });
26094
- const sequence = 1 << 30 | INITIAL_TIME_LOCK2;
26095
- refundTx.addInput({
26096
- txid: getTxId(rootTx),
26097
- index: 0,
26098
- sequence
26885
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26886
+ const directRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26887
+ const directFromCpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26888
+ const cpfpRefundTxSighash = getSigHashFromTx(
26889
+ cpfpRefundTx,
26890
+ 0,
26891
+ cpfpRootTx.getOutput(0)
26892
+ );
26893
+ if (!directRefundTx || !directFromCpfpRefundTx) {
26894
+ throw new ValidationError(
26895
+ "Expected direct refund transactions for tree creation",
26896
+ {
26897
+ field: "directRefundTx",
26898
+ value: directRefundTx
26899
+ }
26900
+ );
26901
+ }
26902
+ const directRefundTxSighash = getSigHashFromTx(
26903
+ directRefundTx,
26904
+ 0,
26905
+ directRootTx.getOutput(0)
26906
+ );
26907
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
26908
+ directFromCpfpRefundTx,
26909
+ 0,
26910
+ cpfpRootTx.getOutput(0)
26911
+ );
26912
+ const sparkClient = await this.connectionManager.createSparkClient(
26913
+ this.config.getCoordinatorAddress()
26914
+ );
26915
+ let treeResp;
26916
+ try {
26917
+ treeResp = await sparkClient.start_deposit_tree_creation({
26918
+ identityPublicKey: await this.config.signer.getIdentityPublicKey(),
26919
+ onChainUtxo: {
26920
+ vout,
26921
+ rawTx: depositTx.toBytes(true),
26922
+ network: this.config.getNetworkProto()
26923
+ },
26924
+ rootTxSigningJob: {
26925
+ rawTx: cpfpRootTx.toBytes(),
26926
+ signingPublicKey: signingPubKey,
26927
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
26928
+ },
26929
+ refundTxSigningJob: {
26930
+ rawTx: cpfpRefundTx.toBytes(),
26931
+ signingPublicKey: signingPubKey,
26932
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
26933
+ },
26934
+ directRootTxSigningJob: {
26935
+ rawTx: directRootTx.toBytes(),
26936
+ signingPublicKey: signingPubKey,
26937
+ signingNonceCommitment: directRootNonceCommitment.commitment
26938
+ },
26939
+ directRefundTxSigningJob: {
26940
+ rawTx: directRefundTx.toBytes(),
26941
+ signingPublicKey: signingPubKey,
26942
+ signingNonceCommitment: directRefundNonceCommitment.commitment
26943
+ },
26944
+ directFromCpfpRefundTxSigningJob: {
26945
+ rawTx: directFromCpfpRefundTx.toBytes(),
26946
+ signingPublicKey: signingPubKey,
26947
+ signingNonceCommitment: directFromCpfpRefundNonceCommitment.commitment
26948
+ }
26949
+ });
26950
+ } catch (error) {
26951
+ throw new NetworkError(
26952
+ "Failed to start deposit tree creation",
26953
+ {
26954
+ operation: "start_deposit_tree_creation",
26955
+ errorCount: 1,
26956
+ errors: error instanceof Error ? error.message : String(error)
26957
+ },
26958
+ error
26959
+ );
26960
+ }
26961
+ if (!treeResp.rootNodeSignatureShares?.verifyingKey) {
26962
+ throw new ValidationError("No verifying key found in tree response", {
26963
+ field: "verifyingKey",
26964
+ value: treeResp.rootNodeSignatureShares,
26965
+ expected: "Non-null verifying key"
26966
+ });
26967
+ }
26968
+ if (!treeResp.rootNodeSignatureShares.nodeTxSigningResult?.signingNonceCommitments) {
26969
+ throw new ValidationError(
26970
+ "No signing nonce commitments found in tree response",
26971
+ {
26972
+ field: "nodeTxSigningResult.signingNonceCommitments",
26973
+ value: treeResp.rootNodeSignatureShares.nodeTxSigningResult,
26974
+ expected: "Non-null signing nonce commitments"
26975
+ }
26976
+ );
26977
+ }
26978
+ if (!treeResp.rootNodeSignatureShares.refundTxSigningResult?.signingNonceCommitments) {
26979
+ throw new ValidationError(
26980
+ "No signing nonce commitments found in tree response",
26981
+ {
26982
+ field: "refundTxSigningResult.signingNonceCommitments"
26983
+ }
26984
+ );
26985
+ }
26986
+ if (!treeResp.rootNodeSignatureShares.directNodeTxSigningResult?.signingNonceCommitments) {
26987
+ throw new ValidationError(
26988
+ "No direct node signing nonce commitments found in tree response",
26989
+ {
26990
+ field: "directNodeTxSigningResult.signingNonceCommitments"
26991
+ }
26992
+ );
26993
+ }
26994
+ if (!treeResp.rootNodeSignatureShares.directRefundTxSigningResult?.signingNonceCommitments) {
26995
+ throw new ValidationError(
26996
+ "No direct refund signing nonce commitments found in tree response",
26997
+ {
26998
+ field: "directRefundTxSigningResult.signingNonceCommitments"
26999
+ }
27000
+ );
27001
+ }
27002
+ if (!treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult?.signingNonceCommitments) {
27003
+ throw new ValidationError(
27004
+ "No direct from CPFP refund signing nonce commitments found in tree response",
27005
+ {
27006
+ field: "directFromCpfpRefundTxSigningResult.signingNonceCommitments"
27007
+ }
27008
+ );
27009
+ }
27010
+ if (!(0, import_utils16.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
27011
+ throw new ValidationError("Verifying key mismatch", {
27012
+ field: "verifyingKey",
27013
+ value: treeResp.rootNodeSignatureShares.verifyingKey,
27014
+ expected: verifyingKey
27015
+ });
27016
+ }
27017
+ const cpfpRootSignature = await this.config.signer.signFrost({
27018
+ message: cpfpRootTxSighash,
27019
+ publicKey: signingPubKey,
27020
+ keyDerivation,
27021
+ verifyingKey,
27022
+ selfCommitment: cpfpRootNonceCommitment,
27023
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
27024
+ adaptorPubKey: new Uint8Array()
26099
27025
  });
27026
+ const directRootSignature = await this.config.signer.signFrost({
27027
+ message: directRootTxSighash,
27028
+ publicKey: signingPubKey,
27029
+ keyDerivation,
27030
+ verifyingKey,
27031
+ selfCommitment: directRootNonceCommitment,
27032
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
27033
+ adaptorPubKey: new Uint8Array()
27034
+ });
27035
+ const cpfpRefundSignature = await this.config.signer.signFrost({
27036
+ message: cpfpRefundTxSighash,
27037
+ publicKey: signingPubKey,
27038
+ keyDerivation,
27039
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27040
+ selfCommitment: cpfpRefundNonceCommitment,
27041
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
27042
+ adaptorPubKey: new Uint8Array()
27043
+ });
27044
+ const directRefundSignature = await this.config.signer.signFrost({
27045
+ message: directRefundTxSighash,
27046
+ publicKey: signingPubKey,
27047
+ keyDerivation,
27048
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27049
+ selfCommitment: directRefundNonceCommitment,
27050
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
27051
+ adaptorPubKey: new Uint8Array()
27052
+ });
27053
+ const directFromCpfpRefundSignature = await this.config.signer.signFrost({
27054
+ message: directFromCpfpRefundTxSighash,
27055
+ publicKey: signingPubKey,
27056
+ keyDerivation,
27057
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27058
+ selfCommitment: directFromCpfpRefundNonceCommitment,
27059
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
27060
+ adaptorPubKey: new Uint8Array()
27061
+ });
27062
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
27063
+ message: cpfpRootTxSighash,
27064
+ statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
27065
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
27066
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27067
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
27068
+ selfCommitment: cpfpRootNonceCommitment,
27069
+ publicKey: signingPubKey,
27070
+ selfSignature: cpfpRootSignature,
27071
+ adaptorPubKey: new Uint8Array()
27072
+ });
27073
+ const directRootAggregate = await this.config.signer.aggregateFrost({
27074
+ message: directRootTxSighash,
27075
+ statechainSignatures: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signatureShares,
27076
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.publicKeys,
27077
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27078
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
27079
+ selfCommitment: directRootNonceCommitment,
27080
+ publicKey: signingPubKey,
27081
+ selfSignature: directRootSignature,
27082
+ adaptorPubKey: new Uint8Array()
27083
+ });
27084
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
27085
+ message: cpfpRefundTxSighash,
27086
+ statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
27087
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
27088
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27089
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
27090
+ selfCommitment: cpfpRefundNonceCommitment,
27091
+ publicKey: signingPubKey,
27092
+ selfSignature: cpfpRefundSignature,
27093
+ adaptorPubKey: new Uint8Array()
27094
+ });
27095
+ const directRefundAggregate = await this.config.signer.aggregateFrost({
27096
+ message: directRefundTxSighash,
27097
+ statechainSignatures: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signatureShares,
27098
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.publicKeys,
27099
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27100
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
27101
+ selfCommitment: directRefundNonceCommitment,
27102
+ publicKey: signingPubKey,
27103
+ selfSignature: directRefundSignature,
27104
+ adaptorPubKey: new Uint8Array()
27105
+ });
27106
+ const directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
27107
+ message: directFromCpfpRefundTxSighash,
27108
+ statechainSignatures: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signatureShares,
27109
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.publicKeys,
27110
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27111
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
27112
+ selfCommitment: directFromCpfpRefundNonceCommitment,
27113
+ publicKey: signingPubKey,
27114
+ selfSignature: directFromCpfpRefundSignature,
27115
+ adaptorPubKey: new Uint8Array()
27116
+ });
27117
+ let finalizeResp;
27118
+ try {
27119
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
27120
+ intent: 0 /* CREATION */,
27121
+ nodeSignatures: [
27122
+ {
27123
+ nodeId: treeResp.rootNodeSignatureShares.nodeId,
27124
+ nodeTxSignature: cpfpRootAggregate,
27125
+ refundTxSignature: cpfpRefundAggregate,
27126
+ directNodeTxSignature: directRootAggregate,
27127
+ directRefundTxSignature: directRefundAggregate,
27128
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate
27129
+ }
27130
+ ]
27131
+ });
27132
+ } catch (error) {
27133
+ throw new NetworkError(
27134
+ "Failed to finalize node signatures",
27135
+ {
27136
+ operation: "finalize_node_signatures",
27137
+ errorCount: 1,
27138
+ errors: error instanceof Error ? error.message : String(error)
27139
+ },
27140
+ error
27141
+ );
27142
+ }
27143
+ return finalizeResp;
27144
+ }
27145
+ /**
27146
+ * @deprecated
27147
+ * Use createTreeRoot instead.
27148
+ * This is currently only used to test backwards compatibility.
27149
+ */
27150
+ async createTreeWithoutDirectTx({
27151
+ keyDerivation,
27152
+ verifyingKey,
27153
+ depositTx,
27154
+ vout
27155
+ }) {
27156
+ const output = depositTx.getOutput(vout);
27157
+ if (!output) {
27158
+ throw new ValidationError("Invalid deposit transaction output", {
27159
+ field: "vout",
27160
+ value: vout,
27161
+ expected: "Valid output index"
27162
+ });
27163
+ }
27164
+ const script = output.script;
27165
+ const amount = output.amount;
27166
+ if (!script || !amount) {
27167
+ throw new ValidationError("No script or amount found in deposit tx", {
27168
+ field: "output",
27169
+ value: output,
27170
+ expected: "Output with script and amount"
27171
+ });
27172
+ }
27173
+ const depositOutPoint = {
27174
+ txid: (0, import_utils15.hexToBytes)(getTxId(depositTx)),
27175
+ index: vout
27176
+ };
27177
+ const depositTxOut = {
27178
+ script,
27179
+ amount
27180
+ };
27181
+ const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
27182
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
27183
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
26100
27184
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
26101
- const refundP2trAddress = getP2TRAddressFromPublicKey(
26102
- signingPubKey,
26103
- this.config.getNetwork()
26104
- );
26105
- const refundAddress = btc5.Address(getNetwork(this.config.getNetwork())).decode(refundP2trAddress);
26106
- const refundPkScript = btc5.OutScript.encode(refundAddress);
26107
- refundTx.addOutput({
26108
- script: refundPkScript,
26109
- amount: outputAmount
27185
+ const { cpfpRefundTx } = createRefundTxs({
27186
+ sequence: INITIAL_SEQUENCE,
27187
+ input: { txid: (0, import_utils15.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
27188
+ amountSats: amount,
27189
+ receivingPubkey: signingPubKey,
27190
+ network: this.config.getNetwork()
26110
27191
  });
26111
- refundTx.addOutput(getEphemeralAnchorOutput());
26112
- const refundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26113
- const refundTxSighash = getSigHashFromTx(refundTx, 0, rootTx.getOutput(0));
27192
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
27193
+ const cpfpRefundTxSighash = getSigHashFromTx(
27194
+ cpfpRefundTx,
27195
+ 0,
27196
+ cpfpRootTx.getOutput(0)
27197
+ );
26114
27198
  const sparkClient = await this.connectionManager.createSparkClient(
26115
27199
  this.config.getCoordinatorAddress()
26116
27200
  );
@@ -26124,14 +27208,14 @@ var DepositService = class {
26124
27208
  network: this.config.getNetworkProto()
26125
27209
  },
26126
27210
  rootTxSigningJob: {
26127
- rawTx: rootTx.toBytes(),
27211
+ rawTx: cpfpRootTx.toBytes(),
26128
27212
  signingPublicKey: signingPubKey,
26129
- signingNonceCommitment: rootNonceCommitment.commitment
27213
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
26130
27214
  },
26131
27215
  refundTxSigningJob: {
26132
- rawTx: refundTx.toBytes(),
27216
+ rawTx: cpfpRefundTx.toBytes(),
26133
27217
  signingPublicKey: signingPubKey,
26134
- signingNonceCommitment: refundNonceCommitment.commitment
27218
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
26135
27219
  }
26136
27220
  });
26137
27221
  } catch (error) {
@@ -26177,55 +27261,55 @@ var DepositService = class {
26177
27261
  expected: verifyingKey
26178
27262
  });
26179
27263
  }
26180
- const rootSignature = await this.config.signer.signFrost({
26181
- message: rootTxSighash,
27264
+ const cpfpRootSignature = await this.config.signer.signFrost({
27265
+ message: cpfpRootTxSighash,
26182
27266
  publicKey: signingPubKey,
26183
27267
  keyDerivation,
26184
27268
  verifyingKey,
26185
- selfCommitment: rootNonceCommitment,
27269
+ selfCommitment: cpfpRootNonceCommitment,
26186
27270
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
26187
27271
  adaptorPubKey: new Uint8Array()
26188
27272
  });
26189
- const refundSignature = await this.config.signer.signFrost({
26190
- message: refundTxSighash,
27273
+ const cpfpRefundSignature = await this.config.signer.signFrost({
27274
+ message: cpfpRefundTxSighash,
26191
27275
  publicKey: signingPubKey,
26192
27276
  keyDerivation,
26193
- verifyingKey,
26194
- selfCommitment: refundNonceCommitment,
27277
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27278
+ selfCommitment: cpfpRefundNonceCommitment,
26195
27279
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
26196
27280
  adaptorPubKey: new Uint8Array()
26197
27281
  });
26198
- const rootAggregate = await this.config.signer.aggregateFrost({
26199
- message: rootTxSighash,
27282
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
27283
+ message: cpfpRootTxSighash,
26200
27284
  statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
26201
27285
  statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
26202
27286
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
26203
27287
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
26204
- selfCommitment: rootNonceCommitment,
27288
+ selfCommitment: cpfpRootNonceCommitment,
26205
27289
  publicKey: signingPubKey,
26206
- selfSignature: rootSignature,
27290
+ selfSignature: cpfpRootSignature,
26207
27291
  adaptorPubKey: new Uint8Array()
26208
27292
  });
26209
- const refundAggregate = await this.config.signer.aggregateFrost({
26210
- message: refundTxSighash,
27293
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
27294
+ message: cpfpRefundTxSighash,
26211
27295
  statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
26212
27296
  statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
26213
27297
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
26214
27298
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
26215
- selfCommitment: refundNonceCommitment,
27299
+ selfCommitment: cpfpRefundNonceCommitment,
26216
27300
  publicKey: signingPubKey,
26217
- selfSignature: refundSignature,
27301
+ selfSignature: cpfpRefundSignature,
26218
27302
  adaptorPubKey: new Uint8Array()
26219
27303
  });
26220
27304
  let finalizeResp;
26221
27305
  try {
26222
- finalizeResp = await sparkClient.finalize_node_signatures({
27306
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
26223
27307
  intent: 0 /* CREATION */,
26224
27308
  nodeSignatures: [
26225
27309
  {
26226
27310
  nodeId: treeResp.rootNodeSignatureShares.nodeId,
26227
- nodeTxSignature: rootAggregate,
26228
- refundTxSignature: refundAggregate
27311
+ nodeTxSignature: cpfpRootAggregate,
27312
+ refundTxSignature: cpfpRefundAggregate
26229
27313
  }
26230
27314
  ]
26231
27315
  });
@@ -26435,7 +27519,8 @@ var LightningService = class {
26435
27519
  let signingCommitments;
26436
27520
  try {
26437
27521
  signingCommitments = await sparkClient.get_signing_commitments({
26438
- nodeIds: leaves.map((leaf) => leaf.leaf.id)
27522
+ nodeIds: leaves.map((leaf) => leaf.leaf.id),
27523
+ count: 3
26439
27524
  });
26440
27525
  } catch (error) {
26441
27526
  throw new NetworkError(
@@ -26448,10 +27533,19 @@ var LightningService = class {
26448
27533
  error
26449
27534
  );
26450
27535
  }
26451
- const leafSigningJobs = await this.signingService.signRefunds(
27536
+ const {
27537
+ cpfpLeafSigningJobs,
27538
+ directLeafSigningJobs,
27539
+ directFromCpfpLeafSigningJobs
27540
+ } = await this.signingService.signRefunds(
26452
27541
  leaves,
26453
- signingCommitments.signingCommitments,
26454
- receiverIdentityPubkey
27542
+ receiverIdentityPubkey,
27543
+ signingCommitments.signingCommitments.slice(0, leaves.length),
27544
+ signingCommitments.signingCommitments.slice(
27545
+ leaves.length,
27546
+ 2 * leaves.length
27547
+ ),
27548
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
26455
27549
  );
26456
27550
  const transferId = (0, import_uuidv74.uuidv7)();
26457
27551
  let bolt11String = "";
@@ -26488,7 +27582,7 @@ var LightningService = class {
26488
27582
  const reason = isInboundPayment ? 1 /* REASON_RECEIVE */ : 0 /* REASON_SEND */;
26489
27583
  let response;
26490
27584
  try {
26491
- response = await sparkClient.initiate_preimage_swap({
27585
+ response = await sparkClient.initiate_preimage_swap_v2({
26492
27586
  paymentHash,
26493
27587
  invoiceAmount: {
26494
27588
  invoiceAmountProof: {
@@ -26500,7 +27594,9 @@ var LightningService = class {
26500
27594
  transfer: {
26501
27595
  transferId,
26502
27596
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
26503
- leavesToSend: leafSigningJobs,
27597
+ leavesToSend: cpfpLeafSigningJobs,
27598
+ directLeavesToSend: directLeafSigningJobs,
27599
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs,
26504
27600
  receiverIdentityPublicKey: receiverIdentityPubkey,
26505
27601
  expiryTime: new Date(Date.now() + 2 * 60 * 1e3)
26506
27602
  },
@@ -28977,13 +30073,6 @@ function isTokenTransaction(tokenTransaction) {
28977
30073
  init_buffer();
28978
30074
  var import_utils20 = require("@noble/curves/abstract/utils");
28979
30075
  var import_btc_signer5 = require("@scure/btc-signer");
28980
- var INITIAL_TIME_LOCK3 = 2e3;
28981
- function maybeApplyFee2(amount) {
28982
- if (amount > BigInt(DEFAULT_FEE_SATS)) {
28983
- return amount - BigInt(DEFAULT_FEE_SATS);
28984
- }
28985
- return amount;
28986
- }
28987
30076
  var TreeCreationService = class {
28988
30077
  config;
28989
30078
  connectionManager;
@@ -29098,7 +30187,7 @@ var TreeCreationService = class {
29098
30187
  );
29099
30188
  let response;
29100
30189
  try {
29101
- response = await sparkClient.create_tree(request);
30190
+ response = await sparkClient.create_tree_v2(request);
29102
30191
  } catch (error) {
29103
30192
  throw new Error(`Error creating tree: ${error}`);
29104
30193
  }
@@ -29115,7 +30204,7 @@ var TreeCreationService = class {
29115
30204
  );
29116
30205
  let finalizeResp;
29117
30206
  try {
29118
- finalizeResp = await sparkClient.finalize_node_signatures({
30207
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
29119
30208
  nodeSignatures
29120
30209
  });
29121
30210
  } catch (error) {
@@ -29180,91 +30269,111 @@ var TreeCreationService = class {
29180
30269
  async buildChildCreationNode(node, parentTx, vout, network) {
29181
30270
  const internalCreationNode = {
29182
30271
  nodeTxSigningJob: void 0,
29183
- refundTxSigningJob: void 0,
29184
- children: [],
29185
30272
  directNodeTxSigningJob: void 0,
30273
+ refundTxSigningJob: void 0,
29186
30274
  directRefundTxSigningJob: void 0,
29187
- directFromCpfpRefundTxSigningJob: void 0
30275
+ directFromCpfpRefundTxSigningJob: void 0,
30276
+ children: []
29188
30277
  };
29189
- const tx = new import_btc_signer5.Transaction({ version: 3 });
29190
- tx.addInput({
29191
- txid: getTxId(parentTx),
29192
- index: vout
29193
- });
29194
30278
  const parentTxOut = parentTx.getOutput(vout);
29195
30279
  if (!parentTxOut?.script || !parentTxOut?.amount) {
29196
30280
  throw new Error("parentTxOut is undefined");
29197
30281
  }
29198
- tx.addOutput({
30282
+ const parentOutPoint = {
30283
+ txid: (0, import_utils20.hexToBytes)(getTxId(parentTx)),
30284
+ index: vout
30285
+ };
30286
+ const parentTxOutObj = {
29199
30287
  script: parentTxOut.script,
29200
30288
  amount: parentTxOut.amount
29201
- // maybeApplyFee(parentTxOut.amount),
29202
- });
29203
- tx.addOutput(getEphemeralAnchorOutput());
29204
- const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
29205
- const signingJob = {
30289
+ };
30290
+ const { cpfpNodeTx, directNodeTx } = createNodeTxs(
30291
+ parentTxOutObj,
30292
+ parentOutPoint
30293
+ );
30294
+ const cpfpNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30295
+ const directNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30296
+ const cpfpNodeSigningJob = {
29206
30297
  signingPublicKey: node.signingPublicKey,
29207
- rawTx: tx.toBytes(),
29208
- signingNonceCommitment: signingNonceCommitment.commitment
30298
+ rawTx: cpfpNodeTx.toBytes(),
30299
+ signingNonceCommitment: cpfpNodeSigningCommitment.commitment
29209
30300
  };
29210
- internalCreationNode.nodeTxSigningCommitment = signingNonceCommitment;
29211
- internalCreationNode.nodeTxSigningJob = signingJob;
29212
- const sequence = 1 << 30 | INITIAL_TIME_LOCK3;
30301
+ const directNodeSigningJob = directNodeTx ? {
30302
+ signingPublicKey: node.signingPublicKey,
30303
+ rawTx: directNodeTx.toBytes(),
30304
+ signingNonceCommitment: directNodeSigningCommitment.commitment
30305
+ } : void 0;
30306
+ internalCreationNode.nodeTxSigningCommitment = cpfpNodeSigningCommitment;
30307
+ internalCreationNode.directNodeTxSigningCommitment = directNodeSigningCommitment;
30308
+ internalCreationNode.nodeTxSigningJob = cpfpNodeSigningJob;
30309
+ internalCreationNode.directNodeTxSigningJob = directNodeSigningJob;
30310
+ const sequence = INITIAL_SEQUENCE;
30311
+ const directSequence = INITIAL_DIRECT_SEQUENCE;
29213
30312
  const childCreationNode = {
29214
30313
  nodeTxSigningJob: void 0,
29215
- refundTxSigningJob: void 0,
29216
- children: [],
29217
30314
  directNodeTxSigningJob: void 0,
30315
+ refundTxSigningJob: void 0,
29218
30316
  directRefundTxSigningJob: void 0,
29219
- directFromCpfpRefundTxSigningJob: void 0
30317
+ directFromCpfpRefundTxSigningJob: void 0,
30318
+ children: []
29220
30319
  };
29221
- const childTx = new import_btc_signer5.Transaction({ version: 3 });
29222
- childTx.addInput({
29223
- txid: getTxId(tx),
29224
- index: 0,
29225
- sequence
29226
- });
29227
- childTx.addOutput({
29228
- script: parentTxOut.script,
29229
- amount: parentTxOut.amount
29230
- // maybeApplyFee(parentTxOut.amount),
29231
- });
29232
- childTx.addOutput(getEphemeralAnchorOutput());
29233
- const childSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
29234
- const childSigningJob = {
30320
+ const [cpfpLeafTx, directLeafTx] = createLeafNodeTx(
30321
+ sequence,
30322
+ directSequence,
30323
+ { txid: (0, import_utils20.hexToBytes)(getTxId(cpfpNodeTx)), index: 0 },
30324
+ parentTxOutObj,
30325
+ true
30326
+ // shouldCalculateFee
30327
+ );
30328
+ const cpfpLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30329
+ const directLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30330
+ const cpfpLeafSigningJob = {
29235
30331
  signingPublicKey: node.signingPublicKey,
29236
- rawTx: childTx.toBytes(),
29237
- signingNonceCommitment: childSigningNonceCommitment.commitment
29238
- };
29239
- childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
29240
- childCreationNode.nodeTxSigningJob = childSigningJob;
29241
- const refundTx = new import_btc_signer5.Transaction({ version: 3 });
29242
- refundTx.addInput({
29243
- txid: getTxId(childTx),
29244
- index: 0,
29245
- sequence
29246
- });
29247
- const refundP2trAddress = getP2TRAddressFromPublicKey(
29248
- node.signingPublicKey,
30332
+ rawTx: cpfpLeafTx.toBytes(),
30333
+ signingNonceCommitment: cpfpLeafSigningCommitment.commitment
30334
+ };
30335
+ const directLeafSigningJob = {
30336
+ signingPublicKey: node.signingPublicKey,
30337
+ rawTx: directLeafTx.toBytes(),
30338
+ signingNonceCommitment: directLeafSigningCommitment.commitment
30339
+ };
30340
+ childCreationNode.nodeTxSigningCommitment = cpfpLeafSigningCommitment;
30341
+ childCreationNode.directNodeTxSigningCommitment = directLeafSigningCommitment;
30342
+ childCreationNode.nodeTxSigningJob = cpfpLeafSigningJob;
30343
+ childCreationNode.directNodeTxSigningJob = directLeafSigningJob;
30344
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
30345
+ sequence,
30346
+ directSequence,
30347
+ input: { txid: (0, import_utils20.hexToBytes)(getTxId(cpfpLeafTx)), index: 0 },
30348
+ directInput: { txid: (0, import_utils20.hexToBytes)(getTxId(directLeafTx)), index: 0 },
30349
+ amountSats: parentTxOut.amount,
30350
+ receivingPubkey: node.signingPublicKey,
29249
30351
  network
29250
- );
29251
- const refundAddress = (0, import_btc_signer5.Address)(getNetwork(network)).decode(
29252
- refundP2trAddress
29253
- );
29254
- const refundPkScript = import_btc_signer5.OutScript.encode(refundAddress);
29255
- refundTx.addOutput({
29256
- script: refundPkScript,
29257
- amount: maybeApplyFee2(parentTxOut.amount)
29258
30352
  });
29259
- refundTx.addOutput(getEphemeralAnchorOutput());
29260
- const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
29261
- const refundSigningJob = {
30353
+ const cpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30354
+ const directRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30355
+ const directFromCpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30356
+ const cpfpRefundSigningJob = {
29262
30357
  signingPublicKey: node.signingPublicKey,
29263
- rawTx: refundTx.toBytes(),
29264
- signingNonceCommitment: refundSigningNonceCommitment.commitment
30358
+ rawTx: cpfpRefundTx.toBytes(),
30359
+ signingNonceCommitment: cpfpRefundSigningCommitment.commitment
29265
30360
  };
29266
- childCreationNode.refundTxSigningCommitment = refundSigningNonceCommitment;
29267
- childCreationNode.refundTxSigningJob = refundSigningJob;
30361
+ const directRefundSigningJob = directRefundTx ? {
30362
+ signingPublicKey: node.signingPublicKey,
30363
+ rawTx: directRefundTx.toBytes(),
30364
+ signingNonceCommitment: directRefundSigningCommitment.commitment
30365
+ } : void 0;
30366
+ const directFromCpfpRefundSigningJob = directFromCpfpRefundTx ? {
30367
+ signingPublicKey: node.signingPublicKey,
30368
+ rawTx: directFromCpfpRefundTx.toBytes(),
30369
+ signingNonceCommitment: directFromCpfpRefundSigningCommitment.commitment
30370
+ } : void 0;
30371
+ childCreationNode.refundTxSigningCommitment = cpfpRefundSigningCommitment;
30372
+ childCreationNode.directRefundTxSigningCommitment = directRefundSigningCommitment;
30373
+ childCreationNode.directFromCpfpRefundTxSigningCommitment = directFromCpfpRefundSigningCommitment;
30374
+ childCreationNode.refundTxSigningJob = cpfpRefundSigningJob;
30375
+ childCreationNode.directRefundTxSigningJob = directRefundSigningJob;
30376
+ childCreationNode.directFromCpfpRefundTxSigningJob = directFromCpfpRefundSigningJob;
29268
30377
  internalCreationNode.children.push(childCreationNode);
29269
30378
  return internalCreationNode;
29270
30379
  }
@@ -29273,11 +30382,7 @@ var TreeCreationService = class {
29273
30382
  if (!parentTxOutput?.script || !parentTxOutput?.amount) {
29274
30383
  throw new Error("parentTxOutput is undefined");
29275
30384
  }
29276
- const rootNodeTx = new import_btc_signer5.Transaction({ version: 3 });
29277
- rootNodeTx.addInput({
29278
- txid: getTxId(parentTx),
29279
- index: vout
29280
- });
30385
+ const childTxOuts = [];
29281
30386
  for (let i = 0; i < root.children.length; i++) {
29282
30387
  const child = root.children[i];
29283
30388
  if (!child || !child.address) {
@@ -29285,28 +30390,41 @@ var TreeCreationService = class {
29285
30390
  }
29286
30391
  const childAddress = (0, import_btc_signer5.Address)(getNetwork(network)).decode(child.address);
29287
30392
  const childPkScript = import_btc_signer5.OutScript.encode(childAddress);
29288
- rootNodeTx.addOutput({
30393
+ childTxOuts.push({
29289
30394
  script: childPkScript,
29290
30395
  amount: parentTxOutput.amount / 2n
29291
- // feeAdjustedAmount / 2n,
29292
30396
  });
29293
30397
  }
29294
- rootNodeTx.addOutput(getEphemeralAnchorOutput());
29295
- const rootNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
29296
- const rootNodeSigningJob = {
30398
+ const parentOutPoint = {
30399
+ txid: (0, import_utils20.hexToBytes)(getTxId(parentTx)),
30400
+ index: vout
30401
+ };
30402
+ const [cpfpSplitTx, directSplitTx] = createSplitTx(
30403
+ parentOutPoint,
30404
+ childTxOuts
30405
+ );
30406
+ const cpfpSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30407
+ const directSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30408
+ const cpfpSplitSigningJob = {
30409
+ signingPublicKey: root.signingPublicKey,
30410
+ rawTx: cpfpSplitTx.toBytes(),
30411
+ signingNonceCommitment: cpfpSplitSigningCommitment.commitment
30412
+ };
30413
+ const directSplitSigningJob = {
29297
30414
  signingPublicKey: root.signingPublicKey,
29298
- rawTx: rootNodeTx.toBytes(),
29299
- signingNonceCommitment: rootNodeSigningCommitment.commitment
30415
+ rawTx: directSplitTx.toBytes(),
30416
+ signingNonceCommitment: directSplitSigningCommitment.commitment
29300
30417
  };
29301
30418
  const rootCreationNode = {
29302
- nodeTxSigningJob: rootNodeSigningJob,
30419
+ nodeTxSigningJob: cpfpSplitSigningJob,
30420
+ directNodeTxSigningJob: directSplitSigningJob,
29303
30421
  refundTxSigningJob: void 0,
29304
- children: [],
29305
- directNodeTxSigningJob: void 0,
29306
30422
  directRefundTxSigningJob: void 0,
29307
- directFromCpfpRefundTxSigningJob: void 0
30423
+ directFromCpfpRefundTxSigningJob: void 0,
30424
+ children: []
29308
30425
  };
29309
- rootCreationNode.nodeTxSigningCommitment = rootNodeSigningCommitment;
30426
+ rootCreationNode.nodeTxSigningCommitment = cpfpSplitSigningCommitment;
30427
+ rootCreationNode.directNodeTxSigningCommitment = directSplitSigningCommitment;
29310
30428
  const leftChild = root.children[0];
29311
30429
  const rightChild = root.children[1];
29312
30430
  if (!leftChild || !rightChild) {
@@ -29314,13 +30432,15 @@ var TreeCreationService = class {
29314
30432
  }
29315
30433
  const leftChildCreationNode = await this.buildChildCreationNode(
29316
30434
  leftChild,
29317
- rootNodeTx,
30435
+ cpfpSplitTx,
30436
+ // Use CPFP version for children
29318
30437
  0,
29319
30438
  network
29320
30439
  );
29321
30440
  const rightChildCreationNode = await this.buildChildCreationNode(
29322
30441
  rightChild,
29323
- rootNodeTx,
30442
+ cpfpSplitTx,
30443
+ // Use CPFP version for children
29324
30444
  1,
29325
30445
  network
29326
30446
  );
@@ -29329,19 +30449,19 @@ var TreeCreationService = class {
29329
30449
  return rootCreationNode;
29330
30450
  }
29331
30451
  async signNodeCreation(parentTx, vout, internalNode, creationNode, creationResponseNode) {
29332
- if (!creationNode.nodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
30452
+ if (!creationNode.nodeTxSigningJob?.signingPublicKey || !creationNode.directNodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
29333
30453
  throw new Error("signingPublicKey or verificationKey is undefined");
29334
30454
  }
29335
30455
  const parentTxOutput = parentTx.getOutput(vout);
29336
30456
  if (!parentTxOutput) {
29337
30457
  throw new Error("parentTxOutput is undefined");
29338
30458
  }
29339
- const tx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
29340
- const txSighash = getSigHashFromTx(tx, 0, parentTxOutput);
29341
- let nodeTxSignature = new Uint8Array();
30459
+ const cpfpNodeTx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
30460
+ const cpfpNodeTxSighash = getSigHashFromTx(cpfpNodeTx, 0, parentTxOutput);
30461
+ let cpfpNodeTxSignature = new Uint8Array();
29342
30462
  if (creationNode.nodeTxSigningCommitment) {
29343
- const userSignature = await this.config.signer.signFrost({
29344
- message: txSighash,
30463
+ const cpfpUserSignature = await this.config.signer.signFrost({
30464
+ message: cpfpNodeTxSighash,
29345
30465
  publicKey: creationNode.nodeTxSigningJob.signingPublicKey,
29346
30466
  keyDerivation: {
29347
30467
  type: "leaf" /* LEAF */,
@@ -29351,30 +30471,84 @@ var TreeCreationService = class {
29351
30471
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
29352
30472
  verifyingKey: internalNode.verificationKey
29353
30473
  });
29354
- nodeTxSignature = await this.config.signer.aggregateFrost({
29355
- message: txSighash,
30474
+ cpfpNodeTxSignature = await this.config.signer.aggregateFrost({
30475
+ message: cpfpNodeTxSighash,
29356
30476
  statechainSignatures: creationResponseNode.nodeTxSigningResult?.signatureShares,
29357
30477
  statechainPublicKeys: creationResponseNode.nodeTxSigningResult?.publicKeys,
29358
30478
  verifyingKey: internalNode.verificationKey,
29359
30479
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
29360
30480
  selfCommitment: creationNode.nodeTxSigningCommitment,
29361
- selfSignature: userSignature,
30481
+ selfSignature: cpfpUserSignature,
29362
30482
  publicKey: internalNode.signingPublicKey
29363
30483
  });
29364
30484
  }
29365
- let refundTxSignature = new Uint8Array();
29366
- if (creationNode.refundTxSigningCommitment) {
29367
- const rawTx = creationNode.refundTxSigningJob?.rawTx;
29368
- if (!rawTx) {
29369
- throw new Error("rawTx is undefined");
29370
- }
29371
- if (!creationNode.refundTxSigningJob?.signingPublicKey) {
29372
- throw new Error("signingPublicKey is undefined");
29373
- }
29374
- const refundTx = getTxFromRawTxBytes(rawTx);
29375
- const refundTxSighash = getSigHashFromTx(refundTx, 0, parentTxOutput);
29376
- const refundSigningResponse = await this.config.signer.signFrost({
29377
- message: refundTxSighash,
30485
+ const directNodeTx = getTxFromRawTxBytes(
30486
+ creationNode.directNodeTxSigningJob.rawTx
30487
+ );
30488
+ const directNodeTxSighash = getSigHashFromTx(
30489
+ directNodeTx,
30490
+ 0,
30491
+ parentTxOutput
30492
+ );
30493
+ let directNodeTxSignature = new Uint8Array();
30494
+ if (creationNode.directNodeTxSigningCommitment) {
30495
+ const directUserSignature = await this.config.signer.signFrost({
30496
+ message: directNodeTxSighash,
30497
+ publicKey: creationNode.directNodeTxSigningJob.signingPublicKey,
30498
+ keyDerivation: {
30499
+ type: "leaf" /* LEAF */,
30500
+ path: creationResponseNode.nodeId
30501
+ },
30502
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
30503
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
30504
+ verifyingKey: internalNode.verificationKey
30505
+ });
30506
+ directNodeTxSignature = await this.config.signer.aggregateFrost({
30507
+ message: directNodeTxSighash,
30508
+ statechainSignatures: creationResponseNode.directNodeTxSigningResult?.signatureShares,
30509
+ statechainPublicKeys: creationResponseNode.directNodeTxSigningResult?.publicKeys,
30510
+ verifyingKey: internalNode.verificationKey,
30511
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
30512
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
30513
+ selfSignature: directUserSignature,
30514
+ publicKey: internalNode.signingPublicKey
30515
+ });
30516
+ }
30517
+ let cpfpRefundTxSignature = new Uint8Array();
30518
+ let directRefundTxSignature = new Uint8Array();
30519
+ let directFromCpfpRefundTxSignature = new Uint8Array();
30520
+ if (creationNode.refundTxSigningCommitment && creationNode.directRefundTxSigningCommitment && creationNode.directFromCpfpRefundTxSigningCommitment) {
30521
+ const rawCpfpRefundTx = creationNode.refundTxSigningJob?.rawTx;
30522
+ const rawDirectRefundTx = creationNode.directRefundTxSigningJob?.rawTx;
30523
+ const rawDirectFromCpfpRefundTx = creationNode.directFromCpfpRefundTxSigningJob?.rawTx;
30524
+ if (!rawCpfpRefundTx || !rawDirectRefundTx || !rawDirectFromCpfpRefundTx) {
30525
+ throw new Error("refund transaction rawTx is undefined");
30526
+ }
30527
+ if (!creationNode.refundTxSigningJob?.signingPublicKey || !creationNode.directRefundTxSigningJob?.signingPublicKey || !creationNode.directFromCpfpRefundTxSigningJob?.signingPublicKey) {
30528
+ throw new Error("refund transaction signingPublicKey is undefined");
30529
+ }
30530
+ const cpfpRefundTx = getTxFromRawTxBytes(rawCpfpRefundTx);
30531
+ const directRefundTx = getTxFromRawTxBytes(rawDirectRefundTx);
30532
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
30533
+ rawDirectFromCpfpRefundTx
30534
+ );
30535
+ const cpfpRefundTxSighash = getSigHashFromTx(
30536
+ cpfpRefundTx,
30537
+ 0,
30538
+ cpfpNodeTx.getOutput(0)
30539
+ );
30540
+ const directRefundTxSighash = getSigHashFromTx(
30541
+ directRefundTx,
30542
+ 0,
30543
+ directNodeTx.getOutput(0)
30544
+ );
30545
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
30546
+ directFromCpfpRefundTx,
30547
+ 0,
30548
+ cpfpNodeTx.getOutput(0)
30549
+ );
30550
+ const cpfpRefundUserSignature = await this.config.signer.signFrost({
30551
+ message: cpfpRefundTxSighash,
29378
30552
  publicKey: creationNode.refundTxSigningJob.signingPublicKey,
29379
30553
  keyDerivation: {
29380
30554
  type: "leaf" /* LEAF */,
@@ -29384,27 +30558,69 @@ var TreeCreationService = class {
29384
30558
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
29385
30559
  verifyingKey: internalNode.verificationKey
29386
30560
  });
29387
- refundTxSignature = await this.config.signer.aggregateFrost({
29388
- message: refundTxSighash,
30561
+ cpfpRefundTxSignature = await this.config.signer.aggregateFrost({
30562
+ message: cpfpRefundTxSighash,
29389
30563
  statechainSignatures: creationResponseNode.refundTxSigningResult?.signatureShares,
29390
30564
  statechainPublicKeys: creationResponseNode.refundTxSigningResult?.publicKeys,
29391
30565
  verifyingKey: internalNode.verificationKey,
29392
30566
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
29393
30567
  selfCommitment: creationNode.refundTxSigningCommitment,
29394
- selfSignature: refundSigningResponse,
30568
+ selfSignature: cpfpRefundUserSignature,
30569
+ publicKey: internalNode.signingPublicKey
30570
+ });
30571
+ const keyDerivation = {
30572
+ type: "leaf" /* LEAF */,
30573
+ path: creationResponseNode.nodeId
30574
+ };
30575
+ const directRefundUserSignature = await this.config.signer.signFrost({
30576
+ message: directRefundTxSighash,
30577
+ publicKey: creationNode.directRefundTxSigningJob.signingPublicKey,
30578
+ keyDerivation,
30579
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
30580
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
30581
+ verifyingKey: internalNode.verificationKey
30582
+ });
30583
+ directRefundTxSignature = await this.config.signer.aggregateFrost({
30584
+ message: directRefundTxSighash,
30585
+ statechainSignatures: creationResponseNode.directRefundTxSigningResult?.signatureShares,
30586
+ statechainPublicKeys: creationResponseNode.directRefundTxSigningResult?.publicKeys,
30587
+ verifyingKey: internalNode.verificationKey,
30588
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
30589
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
30590
+ selfSignature: directRefundUserSignature,
29395
30591
  publicKey: internalNode.signingPublicKey
29396
30592
  });
30593
+ const directFromCpfpRefundUserSignature = await this.config.signer.signFrost({
30594
+ message: directFromCpfpRefundTxSighash,
30595
+ publicKey: creationNode.directFromCpfpRefundTxSigningJob.signingPublicKey,
30596
+ keyDerivation,
30597
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
30598
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
30599
+ verifyingKey: internalNode.verificationKey
30600
+ });
30601
+ directFromCpfpRefundTxSignature = await this.config.signer.aggregateFrost(
30602
+ {
30603
+ message: directFromCpfpRefundTxSighash,
30604
+ statechainSignatures: creationResponseNode.directFromCpfpRefundTxSigningResult?.signatureShares,
30605
+ statechainPublicKeys: creationResponseNode.directFromCpfpRefundTxSigningResult?.publicKeys,
30606
+ verifyingKey: internalNode.verificationKey,
30607
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
30608
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
30609
+ selfSignature: directFromCpfpRefundUserSignature,
30610
+ publicKey: internalNode.signingPublicKey
30611
+ }
30612
+ );
29397
30613
  }
29398
30614
  return {
29399
- tx,
30615
+ tx: cpfpNodeTx,
30616
+ // Return CPFP version for children
29400
30617
  signature: {
29401
30618
  nodeId: creationResponseNode.nodeId,
29402
- nodeTxSignature,
29403
- refundTxSignature,
29404
- // TODO: Add direct refund signature
29405
- directNodeTxSignature: new Uint8Array(),
29406
- directRefundTxSignature: new Uint8Array(),
29407
- directFromCpfpRefundTxSignature: new Uint8Array()
30619
+ nodeTxSignature: cpfpNodeTxSignature,
30620
+ directNodeTxSignature,
30621
+ refundTxSignature: cpfpRefundTxSignature,
30622
+ directRefundTxSignature,
30623
+ directFromCpfpRefundTxSignature
29408
30624
  }
29409
30625
  };
29410
30626
  }
@@ -29467,8 +30683,45 @@ var SigningService = class {
29467
30683
  constructor(config) {
29468
30684
  this.config = config;
29469
30685
  }
29470
- async signRefunds(leaves, signingCommitments, receiverIdentityPubkey) {
30686
+ async signRefundsInternal(refundTx, sighash, leaf, signingCommitments) {
29471
30687
  const leafSigningJobs = [];
30688
+ const signingCommitment = await this.config.signer.getRandomSigningCommitment();
30689
+ if (!signingCommitments) {
30690
+ throw new ValidationError("Invalid signing commitments", {
30691
+ field: "signingNonceCommitments",
30692
+ value: signingCommitments,
30693
+ expected: "Non-null signing commitments"
30694
+ });
30695
+ }
30696
+ const signingResult = await this.config.signer.signFrost({
30697
+ message: sighash,
30698
+ keyDerivation: leaf.keyDerivation,
30699
+ publicKey: await this.config.signer.getPublicKeyFromDerivation(
30700
+ leaf.keyDerivation
30701
+ ),
30702
+ selfCommitment: signingCommitment,
30703
+ statechainCommitments: signingCommitments,
30704
+ adaptorPubKey: new Uint8Array(),
30705
+ verifyingKey: leaf.leaf.verifyingPublicKey
30706
+ });
30707
+ leafSigningJobs.push({
30708
+ leafId: leaf.leaf.id,
30709
+ signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
30710
+ leaf.keyDerivation
30711
+ ),
30712
+ rawTx: refundTx.toBytes(),
30713
+ signingNonceCommitment: signingCommitment.commitment,
30714
+ userSignature: signingResult,
30715
+ signingCommitments: {
30716
+ signingCommitments
30717
+ }
30718
+ });
30719
+ return leafSigningJobs;
30720
+ }
30721
+ async signRefunds(leaves, receiverIdentityPubkey, cpfpSigningCommitments, directSigningCommitments, directFromCpfpSigningCommitments) {
30722
+ const cpfpLeafSigningJobs = [];
30723
+ const directLeafSigningJobs = [];
30724
+ const directFromCpfpLeafSigningJobs = [];
29472
30725
  for (let i = 0; i < leaves.length; i++) {
29473
30726
  const leaf = leaves[i];
29474
30727
  if (!leaf?.leaf) {
@@ -29479,14 +30732,20 @@ var SigningService = class {
29479
30732
  });
29480
30733
  }
29481
30734
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
29482
- const nodeOutPoint = {
30735
+ const cpfpNodeOutPoint = {
29483
30736
  txid: (0, import_utils21.hexToBytes)(getTxId(nodeTx)),
29484
30737
  index: 0
29485
30738
  };
29486
30739
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
29487
- const { nextSequence } = getNextTransactionSequence(
29488
- currRefundTx.getInput(0).sequence
29489
- );
30740
+ const sequence = currRefundTx.getInput(0).sequence;
30741
+ if (!sequence) {
30742
+ throw new ValidationError("Invalid refund transaction", {
30743
+ field: "sequence",
30744
+ value: currRefundTx.getInput(0),
30745
+ expected: "Non-null sequence"
30746
+ });
30747
+ }
30748
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
29490
30749
  const amountSats = currRefundTx.getOutput(0).amount;
29491
30750
  if (amountSats === void 0) {
29492
30751
  throw new ValidationError("Invalid refund transaction", {
@@ -29495,48 +30754,90 @@ var SigningService = class {
29495
30754
  expected: "Non-null amount"
29496
30755
  });
29497
30756
  }
29498
- const refundTx = createRefundTx(
29499
- nextSequence,
29500
- nodeOutPoint,
30757
+ let directNodeTx;
30758
+ let directNodeOutPoint;
30759
+ if (leaf.leaf.directTx.length > 0) {
30760
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
30761
+ directNodeOutPoint = {
30762
+ txid: (0, import_utils21.hexToBytes)(getTxId(directNodeTx)),
30763
+ index: 0
30764
+ };
30765
+ }
30766
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
30767
+ sequence: nextSequence,
30768
+ directSequence: nextDirectSequence,
30769
+ input: cpfpNodeOutPoint,
30770
+ directInput: directNodeOutPoint,
29501
30771
  amountSats,
29502
- receiverIdentityPubkey,
29503
- this.config.getNetwork()
30772
+ receivingPubkey: receiverIdentityPubkey,
30773
+ network: this.config.getNetwork()
30774
+ });
30775
+ const refundSighash = getSigHashFromTx(
30776
+ cpfpRefundTx,
30777
+ 0,
30778
+ nodeTx.getOutput(0)
29504
30779
  );
29505
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
29506
- const signingCommitment = await this.config.signer.getRandomSigningCommitment();
29507
- const signingNonceCommitments = signingCommitments[i]?.signingNonceCommitments;
29508
- if (!signingNonceCommitments) {
29509
- throw new ValidationError("Invalid signing commitments", {
29510
- field: "signingNonceCommitments",
29511
- value: signingCommitments[i],
29512
- expected: "Non-null signing nonce commitments"
29513
- });
30780
+ const signingJobs = await this.signRefundsInternal(
30781
+ cpfpRefundTx,
30782
+ refundSighash,
30783
+ leaf,
30784
+ cpfpSigningCommitments[i]?.signingNonceCommitments
30785
+ );
30786
+ cpfpLeafSigningJobs.push(...signingJobs);
30787
+ if (directRefundTx) {
30788
+ if (!directNodeTx) {
30789
+ throw new ValidationError(
30790
+ "Direct node transaction undefined while direct refund transaction is defined",
30791
+ {
30792
+ field: "directNodeTx",
30793
+ value: directNodeTx,
30794
+ expected: "Non-null direct node transaction"
30795
+ }
30796
+ );
30797
+ }
30798
+ const refundSighash2 = getSigHashFromTx(
30799
+ directRefundTx,
30800
+ 0,
30801
+ directNodeTx.getOutput(0)
30802
+ );
30803
+ const signingJobs2 = await this.signRefundsInternal(
30804
+ directRefundTx,
30805
+ refundSighash2,
30806
+ leaf,
30807
+ directSigningCommitments[i]?.signingNonceCommitments
30808
+ );
30809
+ directLeafSigningJobs.push(...signingJobs2);
29514
30810
  }
29515
- const signingResult = await this.config.signer.signFrost({
29516
- message: sighash,
29517
- keyDerivation: leaf.keyDerivation,
29518
- publicKey: await this.config.signer.getPublicKeyFromDerivation(
29519
- leaf.keyDerivation
29520
- ),
29521
- selfCommitment: signingCommitment,
29522
- statechainCommitments: signingNonceCommitments,
29523
- adaptorPubKey: new Uint8Array(),
29524
- verifyingKey: leaf.leaf.verifyingPublicKey
29525
- });
29526
- leafSigningJobs.push({
29527
- leafId: leaf.leaf.id,
29528
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
29529
- leaf.keyDerivation
29530
- ),
29531
- rawTx: refundTx.toBytes(),
29532
- signingNonceCommitment: signingCommitment.commitment,
29533
- userSignature: signingResult,
29534
- signingCommitments: {
29535
- signingCommitments: signingNonceCommitments
30811
+ if (directFromCpfpRefundTx) {
30812
+ if (!directNodeTx) {
30813
+ throw new ValidationError(
30814
+ "Direct node transaction undefined while direct from CPFP refund transaction is defined",
30815
+ {
30816
+ field: "directNodeTx",
30817
+ value: directNodeTx,
30818
+ expected: "Non-null direct node transaction"
30819
+ }
30820
+ );
29536
30821
  }
29537
- });
30822
+ const refundSighash2 = getSigHashFromTx(
30823
+ directFromCpfpRefundTx,
30824
+ 0,
30825
+ nodeTx.getOutput(0)
30826
+ );
30827
+ const signingJobs2 = await this.signRefundsInternal(
30828
+ directFromCpfpRefundTx,
30829
+ refundSighash2,
30830
+ leaf,
30831
+ directFromCpfpSigningCommitments[i]?.signingNonceCommitments
30832
+ );
30833
+ directFromCpfpLeafSigningJobs.push(...signingJobs2);
30834
+ }
29538
30835
  }
29539
- return leafSigningJobs;
30836
+ return {
30837
+ cpfpLeafSigningJobs,
30838
+ directLeafSigningJobs,
30839
+ directFromCpfpLeafSigningJobs
30840
+ };
29540
30841
  }
29541
30842
  };
29542
30843
 
@@ -29544,7 +30845,7 @@ var SigningService = class {
29544
30845
  init_buffer();
29545
30846
  var import_utils22 = require("@noble/curves/abstract/utils");
29546
30847
  var import_secp256k114 = require("@noble/curves/secp256k1");
29547
- var btc6 = __toESM(require("@scure/btc-signer"), 1);
30848
+ var btc5 = __toESM(require("@scure/btc-signer"), 1);
29548
30849
  var import_btc_signer6 = require("@scure/btc-signer");
29549
30850
  var import_utils23 = require("@scure/btc-signer/utils");
29550
30851
  var STATIC_FAUCET_KEY = (0, import_utils22.hexToBytes)(
@@ -29690,7 +30991,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
29690
30991
  }
29691
30992
  async sendFaucetCoinToP2WPKHAddress(pubKey) {
29692
30993
  const sendToPubKeyTx = new import_btc_signer6.Transaction();
29693
- const p2wpkhAddress = btc6.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
30994
+ const p2wpkhAddress = btc5.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
29694
30995
  if (!p2wpkhAddress) {
29695
30996
  throw new Error("Invalid P2WPKH address");
29696
30997
  }
@@ -29746,12 +31047,13 @@ var BitcoinFaucet = class _BitcoinFaucet {
29746
31047
  }
29747
31048
  async call(method, params) {
29748
31049
  try {
29749
- const response = await fetch(this.url, {
31050
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
31051
+ const response = await fetch2(this.url, {
29750
31052
  method: "POST",
29751
- headers: {
31053
+ headers: new Headers2({
29752
31054
  "Content-Type": "application/json",
29753
31055
  Authorization: "Basic " + btoa(`${this.username}:${this.password}`)
29754
- },
31056
+ }),
29755
31057
  body: JSON.stringify({
29756
31058
  jsonrpc: "1.0",
29757
31059
  id: "spark-js",
@@ -30081,31 +31383,37 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30081
31383
  }
30082
31384
  }
30083
31385
  async getLeaves(isBalanceCheck = false) {
30084
- const leaves = await this.queryNodes({
30085
- source: {
30086
- $case: "ownerIdentityPubkey",
30087
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
30088
- },
30089
- includeParents: false,
30090
- network: NetworkToProto[this.config.getNetwork()]
30091
- });
31386
+ const operatorToLeaves = /* @__PURE__ */ new Map();
31387
+ const ownerIdentityPubkey = await this.config.signer.getIdentityPublicKey();
31388
+ let signingOperators = Object.entries(this.config.getSigningOperators());
31389
+ if (isBalanceCheck) {
31390
+ signingOperators = signingOperators.filter(
31391
+ ([id, _]) => id === this.config.getCoordinatorIdentifier()
31392
+ );
31393
+ }
31394
+ await Promise.all(
31395
+ signingOperators.map(async ([id, operator]) => {
31396
+ const leaves2 = await this.queryNodes(
31397
+ {
31398
+ source: {
31399
+ $case: "ownerIdentityPubkey",
31400
+ ownerIdentityPubkey
31401
+ },
31402
+ includeParents: false,
31403
+ network: NetworkToProto[this.config.getNetwork()]
31404
+ },
31405
+ operator.address
31406
+ );
31407
+ operatorToLeaves.set(id, leaves2);
31408
+ })
31409
+ );
31410
+ const leaves = operatorToLeaves.get(
31411
+ this.config.getCoordinatorIdentifier()
31412
+ );
30092
31413
  const leavesToIgnore = /* @__PURE__ */ new Set();
30093
31414
  if (!isBalanceCheck) {
30094
- for (const [id, operator] of Object.entries(
30095
- this.config.getSigningOperators()
30096
- )) {
31415
+ for (const [id, operatorLeaves] of operatorToLeaves) {
30097
31416
  if (id !== this.config.getCoordinatorIdentifier()) {
30098
- const operatorLeaves = await this.queryNodes(
30099
- {
30100
- source: {
30101
- $case: "ownerIdentityPubkey",
30102
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
30103
- },
30104
- includeParents: false,
30105
- network: NetworkToProto[this.config.getNetwork()]
30106
- },
30107
- operator.address
30108
- );
30109
31417
  for (const [nodeId, leaf] of Object.entries(leaves.nodes)) {
30110
31418
  const operatorLeaf = operatorLeaves.nodes[nodeId];
30111
31419
  if (!operatorLeaf) {
@@ -30546,21 +31854,68 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30546
31854
  }
30547
31855
  }))
30548
31856
  );
30549
- const { transfer, signatureMap } = await this.transferService.startSwapSignRefund(
31857
+ const {
31858
+ transfer,
31859
+ signatureMap,
31860
+ directSignatureMap,
31861
+ directFromCpfpSignatureMap
31862
+ } = await this.transferService.startSwapSignRefund(
30550
31863
  leafKeyTweaks,
30551
31864
  (0, import_utils24.hexToBytes)(this.config.getSspIdentityPublicKey()),
30552
31865
  new Date(Date.now() + 2 * 60 * 1e3)
30553
31866
  );
30554
31867
  try {
30555
31868
  if (!transfer.leaves[0]?.leaf) {
31869
+ console.error("[processSwapBatch] First leaf is missing");
30556
31870
  throw new Error("Failed to get leaf");
30557
31871
  }
30558
- const refundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
30559
- if (!refundSignature) {
30560
- throw new Error("Failed to get refund signature");
31872
+ const cpfpRefundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
31873
+ if (!cpfpRefundSignature) {
31874
+ console.error(
31875
+ "[processSwapBatch] Missing CPFP refund signature for first leaf"
31876
+ );
31877
+ throw new Error("Failed to get CPFP refund signature");
31878
+ }
31879
+ const directRefundSignature = directSignatureMap.get(
31880
+ transfer.leaves[0].leaf.id
31881
+ );
31882
+ if (!directRefundSignature) {
31883
+ console.error(
31884
+ "[processSwapBatch] Missing direct refund signature for first leaf"
31885
+ );
31886
+ throw new Error("Failed to get direct refund signature");
31887
+ }
31888
+ const directFromCpfpRefundSignature = directFromCpfpSignatureMap.get(
31889
+ transfer.leaves[0].leaf.id
31890
+ );
31891
+ if (!directFromCpfpRefundSignature) {
31892
+ console.error(
31893
+ "[processSwapBatch] Missing direct from CPFP refund signature for first leaf"
31894
+ );
31895
+ throw new Error("Failed to get direct from CPFP refund signature");
31896
+ }
31897
+ const {
31898
+ adaptorPrivateKey: cpfpAdaptorPrivateKey,
31899
+ adaptorSignature: cpfpAdaptorSignature
31900
+ } = generateAdaptorFromSignature(cpfpRefundSignature);
31901
+ let directAdaptorPrivateKey = new Uint8Array();
31902
+ let directAdaptorSignature = new Uint8Array();
31903
+ let directFromCpfpAdaptorPrivateKey = new Uint8Array();
31904
+ let directFromCpfpAdaptorSignature = new Uint8Array();
31905
+ if (directRefundSignature.length > 0) {
31906
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directRefundSignature);
31907
+ directAdaptorPrivateKey = adaptorPrivateKey;
31908
+ directAdaptorSignature = adaptorSignature;
31909
+ }
31910
+ if (directFromCpfpRefundSignature.length > 0) {
31911
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directFromCpfpRefundSignature);
31912
+ directFromCpfpAdaptorPrivateKey = adaptorPrivateKey;
31913
+ directFromCpfpAdaptorSignature = adaptorSignature;
30561
31914
  }
30562
- const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(refundSignature);
30563
31915
  if (!transfer.leaves[0].leaf) {
31916
+ console.error(
31917
+ "[processSwapBatch] First leaf missing when preparing user leaves"
31918
+ );
30564
31919
  throw new Error("Failed to get leaf");
30565
31920
  }
30566
31921
  const userLeaves = [];
@@ -30569,37 +31924,113 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30569
31924
  raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
30570
31925
  transfer.leaves[0].intermediateRefundTx
30571
31926
  ),
30572
- adaptor_added_signature: (0, import_utils24.bytesToHex)(adaptorSignature)
31927
+ direct_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
31928
+ transfer.leaves[0].intermediateDirectRefundTx
31929
+ ),
31930
+ direct_from_cpfp_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
31931
+ transfer.leaves[0].intermediateDirectFromCpfpRefundTx
31932
+ ),
31933
+ adaptor_added_signature: (0, import_utils24.bytesToHex)(cpfpAdaptorSignature),
31934
+ direct_adaptor_added_signature: (0, import_utils24.bytesToHex)(directAdaptorSignature),
31935
+ direct_from_cpfp_adaptor_added_signature: (0, import_utils24.bytesToHex)(
31936
+ directFromCpfpAdaptorSignature
31937
+ )
30573
31938
  });
30574
31939
  for (let i = 1; i < transfer.leaves.length; i++) {
30575
31940
  const leaf = transfer.leaves[i];
30576
31941
  if (!leaf?.leaf) {
31942
+ console.error(`[processSwapBatch] Leaf ${i + 1} is missing`);
30577
31943
  throw new Error("Failed to get leaf");
30578
31944
  }
30579
- const refundSignature2 = signatureMap.get(leaf.leaf.id);
30580
- if (!refundSignature2) {
30581
- throw new Error("Failed to get refund signature");
31945
+ const cpfpRefundSignature2 = signatureMap.get(leaf.leaf.id);
31946
+ if (!cpfpRefundSignature2) {
31947
+ console.error(
31948
+ `[processSwapBatch] Missing CPFP refund signature for leaf ${i + 1}`
31949
+ );
31950
+ throw new Error("Failed to get CPFP refund signature");
31951
+ }
31952
+ const directRefundSignature2 = directSignatureMap.get(leaf.leaf.id);
31953
+ if (!directRefundSignature2) {
31954
+ console.error(
31955
+ `[processSwapBatch] Missing direct refund signature for leaf ${i + 1}`
31956
+ );
31957
+ throw new Error("Failed to get direct refund signature");
31958
+ }
31959
+ const directFromCpfpRefundSignature2 = directFromCpfpSignatureMap.get(
31960
+ leaf.leaf.id
31961
+ );
31962
+ if (!directFromCpfpRefundSignature2) {
31963
+ console.error(
31964
+ `[processSwapBatch] Missing direct from CPFP refund signature for leaf ${i + 1}`
31965
+ );
31966
+ throw new Error("Failed to get direct from CPFP refund signature");
30582
31967
  }
30583
- const signature = generateSignatureFromExistingAdaptor(
30584
- refundSignature2,
30585
- adaptorPrivateKey
31968
+ const cpfpSignature = generateSignatureFromExistingAdaptor(
31969
+ cpfpRefundSignature2,
31970
+ cpfpAdaptorPrivateKey
30586
31971
  );
31972
+ let directSignature = new Uint8Array();
31973
+ if (directRefundSignature2.length > 0) {
31974
+ directSignature = generateSignatureFromExistingAdaptor(
31975
+ directRefundSignature2,
31976
+ directAdaptorPrivateKey
31977
+ );
31978
+ }
31979
+ let directFromCpfpSignature = new Uint8Array();
31980
+ if (directFromCpfpRefundSignature2.length > 0) {
31981
+ directFromCpfpSignature = generateSignatureFromExistingAdaptor(
31982
+ directFromCpfpRefundSignature2,
31983
+ directFromCpfpAdaptorPrivateKey
31984
+ );
31985
+ }
30587
31986
  userLeaves.push({
30588
31987
  leaf_id: leaf.leaf.id,
30589
31988
  raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
30590
31989
  leaf.intermediateRefundTx
30591
31990
  ),
30592
- adaptor_added_signature: (0, import_utils24.bytesToHex)(signature)
31991
+ direct_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
31992
+ leaf.intermediateDirectRefundTx
31993
+ ),
31994
+ direct_from_cpfp_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
31995
+ leaf.intermediateDirectFromCpfpRefundTx
31996
+ ),
31997
+ adaptor_added_signature: (0, import_utils24.bytesToHex)(cpfpSignature),
31998
+ direct_adaptor_added_signature: (0, import_utils24.bytesToHex)(directSignature),
31999
+ direct_from_cpfp_adaptor_added_signature: (0, import_utils24.bytesToHex)(
32000
+ directFromCpfpSignature
32001
+ )
30593
32002
  });
30594
32003
  }
30595
32004
  const sspClient = this.getSspClient();
30596
- const adaptorPubkey = (0, import_utils24.bytesToHex)(
30597
- import_secp256k115.secp256k1.getPublicKey(adaptorPrivateKey)
32005
+ const cpfpAdaptorPubkey = (0, import_utils24.bytesToHex)(
32006
+ import_secp256k115.secp256k1.getPublicKey(cpfpAdaptorPrivateKey)
30598
32007
  );
32008
+ if (!cpfpAdaptorPubkey) {
32009
+ throw new Error("Failed to generate CPFP adaptor pubkey");
32010
+ }
32011
+ let directAdaptorPubkey;
32012
+ if (directAdaptorPrivateKey.length > 0) {
32013
+ directAdaptorPubkey = (0, import_utils24.bytesToHex)(
32014
+ import_secp256k115.secp256k1.getPublicKey(directAdaptorPrivateKey)
32015
+ );
32016
+ }
32017
+ let directFromCpfpAdaptorPubkey;
32018
+ if (directFromCpfpAdaptorPrivateKey.length > 0) {
32019
+ directFromCpfpAdaptorPubkey = (0, import_utils24.bytesToHex)(
32020
+ import_secp256k115.secp256k1.getPublicKey(directFromCpfpAdaptorPrivateKey)
32021
+ );
32022
+ }
30599
32023
  let request = null;
32024
+ const targetAmountSats = targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0);
32025
+ const totalAmountSats = leavesBatch.reduce(
32026
+ (acc, leaf) => acc + leaf.value,
32027
+ 0
32028
+ );
30600
32029
  request = await sspClient.requestLeaveSwap({
30601
32030
  userLeaves,
30602
- adaptorPubkey,
32031
+ adaptorPubkey: cpfpAdaptorPubkey,
32032
+ directAdaptorPubkey,
32033
+ directFromCpfpAdaptorPubkey,
30603
32034
  targetAmountSats: targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
30604
32035
  totalAmountSats: leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
30605
32036
  targetAmountSatsList: targetAmounts,
@@ -30608,6 +32039,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30608
32039
  idempotencyKey: (0, import_uuidv75.uuidv7)()
30609
32040
  });
30610
32041
  if (!request) {
32042
+ console.error("[processSwapBatch] Leave swap request returned null");
30611
32043
  throw new Error("Failed to request leaves swap. No response returned.");
30612
32044
  }
30613
32045
  const nodes = await this.queryNodes({
@@ -30621,50 +32053,140 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30621
32053
  network: NetworkToProto[this.config.getNetwork()]
30622
32054
  });
30623
32055
  if (Object.values(nodes.nodes).length !== request.swapLeaves.length) {
32056
+ console.error("[processSwapBatch] Node count mismatch:", {
32057
+ actual: Object.values(nodes.nodes).length,
32058
+ expected: request.swapLeaves.length
32059
+ });
30624
32060
  throw new Error("Expected same number of nodes as swapLeaves");
30625
32061
  }
30626
32062
  for (const [nodeId, node] of Object.entries(nodes.nodes)) {
30627
32063
  if (!node.nodeTx) {
32064
+ console.error(`[processSwapBatch] Node tx missing for ${nodeId}`);
30628
32065
  throw new Error(`Node tx not found for leaf ${nodeId}`);
30629
32066
  }
30630
32067
  if (!node.verifyingPublicKey) {
32068
+ console.error(
32069
+ `[processSwapBatch] Verifying public key missing for ${nodeId}`
32070
+ );
30631
32071
  throw new Error(`Node public key not found for leaf ${nodeId}`);
30632
32072
  }
30633
32073
  const leaf = request.swapLeaves.find((leaf2) => leaf2.leafId === nodeId);
30634
32074
  if (!leaf) {
32075
+ console.error(`[processSwapBatch] Leaf not found for node ${nodeId}`);
30635
32076
  throw new Error(`Leaf not found for node ${nodeId}`);
30636
32077
  }
30637
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
30638
- const refundTxBytes = (0, import_utils24.hexToBytes)(leaf.rawUnsignedRefundTransaction);
30639
- const refundTx = getTxFromRawTxBytes(refundTxBytes);
30640
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
32078
+ const cpfpNodeTx = getTxFromRawTxBytes(node.nodeTx);
32079
+ const cpfpRefundTxBytes = (0, import_utils24.hexToBytes)(leaf.rawUnsignedRefundTransaction);
32080
+ const cpfpRefundTx = getTxFromRawTxBytes(cpfpRefundTxBytes);
32081
+ const cpfpSighash = getSigHashFromTx(
32082
+ cpfpRefundTx,
32083
+ 0,
32084
+ cpfpNodeTx.getOutput(0)
32085
+ );
30641
32086
  const nodePublicKey = node.verifyingPublicKey;
30642
32087
  const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
30643
- const adaptorSignatureBytes = (0, import_utils24.hexToBytes)(leaf.adaptorSignedSignature);
32088
+ const cpfpAdaptorSignatureBytes = (0, import_utils24.hexToBytes)(
32089
+ leaf.adaptorSignedSignature
32090
+ );
32091
+ applyAdaptorToSignature(
32092
+ taprootKey.slice(1),
32093
+ cpfpSighash,
32094
+ cpfpAdaptorSignatureBytes,
32095
+ cpfpAdaptorPrivateKey
32096
+ );
32097
+ if (!leaf.directRawUnsignedRefundTransaction) {
32098
+ throw new Error(
32099
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
32100
+ );
32101
+ }
32102
+ if (!leaf.directAdaptorSignedSignature) {
32103
+ throw new Error(
32104
+ `Direct adaptor signed signature missing for node ${nodeId}`
32105
+ );
32106
+ }
32107
+ const directNodeTx = getTxFromRawTxBytes(node.directTx);
32108
+ const directRefundTxBytes = (0, import_utils24.hexToBytes)(
32109
+ leaf.directRawUnsignedRefundTransaction
32110
+ );
32111
+ const directRefundTx = getTxFromRawTxBytes(directRefundTxBytes);
32112
+ const directSighash = getSigHashFromTx(
32113
+ directRefundTx,
32114
+ 0,
32115
+ directNodeTx.getOutput(0)
32116
+ );
32117
+ if (!leaf.directFromCpfpAdaptorSignedSignature) {
32118
+ throw new Error(
32119
+ `Direct adaptor signed signature missing for node ${nodeId}`
32120
+ );
32121
+ }
32122
+ const directAdaptorSignatureBytes = (0, import_utils24.hexToBytes)(
32123
+ leaf.directAdaptorSignedSignature
32124
+ );
32125
+ applyAdaptorToSignature(
32126
+ taprootKey.slice(1),
32127
+ directSighash,
32128
+ directAdaptorSignatureBytes,
32129
+ directAdaptorPrivateKey
32130
+ );
32131
+ if (!leaf.directRawUnsignedRefundTransaction) {
32132
+ throw new Error(
32133
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
32134
+ );
32135
+ }
32136
+ if (!leaf.directFromCpfpRawUnsignedRefundTransaction) {
32137
+ throw new Error(
32138
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
32139
+ );
32140
+ }
32141
+ const directFromCpfpRefundTxBytes = (0, import_utils24.hexToBytes)(
32142
+ leaf.directFromCpfpRawUnsignedRefundTransaction
32143
+ );
32144
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
32145
+ directFromCpfpRefundTxBytes
32146
+ );
32147
+ const directFromCpfpSighash = getSigHashFromTx(
32148
+ directFromCpfpRefundTx,
32149
+ 0,
32150
+ cpfpNodeTx.getOutput(0)
32151
+ );
32152
+ const directFromCpfpAdaptorSignatureBytes = (0, import_utils24.hexToBytes)(
32153
+ leaf.directFromCpfpAdaptorSignedSignature
32154
+ );
30644
32155
  applyAdaptorToSignature(
30645
32156
  taprootKey.slice(1),
30646
- sighash,
30647
- adaptorSignatureBytes,
30648
- adaptorPrivateKey
32157
+ directFromCpfpSighash,
32158
+ directFromCpfpAdaptorSignatureBytes,
32159
+ directFromCpfpAdaptorPrivateKey
30649
32160
  );
30650
32161
  }
30651
32162
  await this.transferService.deliverTransferPackage(
30652
32163
  transfer,
30653
32164
  leafKeyTweaks,
30654
- signatureMap
32165
+ signatureMap,
32166
+ directSignatureMap,
32167
+ directFromCpfpSignatureMap
30655
32168
  );
30656
32169
  const completeResponse = await sspClient.completeLeaveSwap({
30657
- adaptorSecretKey: (0, import_utils24.bytesToHex)(adaptorPrivateKey),
32170
+ adaptorSecretKey: (0, import_utils24.bytesToHex)(cpfpAdaptorPrivateKey),
32171
+ directAdaptorSecretKey: (0, import_utils24.bytesToHex)(directAdaptorPrivateKey),
32172
+ directFromCpfpAdaptorSecretKey: (0, import_utils24.bytesToHex)(
32173
+ directFromCpfpAdaptorPrivateKey
32174
+ ),
30658
32175
  userOutboundTransferExternalId: transfer.id,
30659
32176
  leavesSwapRequestId: request.id
30660
32177
  });
30661
32178
  if (!completeResponse || !completeResponse.inboundTransfer?.sparkId) {
32179
+ console.error(
32180
+ "[processSwapBatch] Invalid complete response:",
32181
+ completeResponse
32182
+ );
30662
32183
  throw new Error("Failed to complete leaves swap");
30663
32184
  }
30664
32185
  const incomingTransfer = await this.transferService.queryTransfer(
30665
32186
  completeResponse.inboundTransfer.sparkId
30666
32187
  );
30667
32188
  if (!incomingTransfer) {
32189
+ console.error("[processSwapBatch] No incoming transfer found");
30668
32190
  throw new Error("Failed to get incoming transfer");
30669
32191
  }
30670
32192
  return await this.claimTransfer({
@@ -30674,6 +32196,11 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30674
32196
  optimize: false
30675
32197
  });
30676
32198
  } catch (e) {
32199
+ console.error("[processSwapBatch] Error details:", {
32200
+ error: e,
32201
+ message: e.message,
32202
+ stack: e.stack
32203
+ });
30677
32204
  await this.cancelAllSenderInitiatedTransfers();
30678
32205
  throw new Error(`Failed to request leaves swap: ${e}`);
30679
32206
  }
@@ -31144,8 +32671,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31144
32671
  field: "txid"
31145
32672
  });
31146
32673
  }
32674
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
31147
32675
  const baseUrl = this.config.getElectrsUrl();
31148
- const headers = {};
32676
+ const headers = new Headers2();
31149
32677
  let txHex;
31150
32678
  if (this.config.getNetwork() === 4 /* LOCAL */) {
31151
32679
  const localFaucet = BitcoinFaucet.getInstance();
@@ -31156,9 +32684,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31156
32684
  const auth = btoa(
31157
32685
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
31158
32686
  );
31159
- headers["Authorization"] = `Basic ${auth}`;
32687
+ headers.set("Authorization", `Basic ${auth}`);
31160
32688
  }
31161
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
32689
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
31162
32690
  headers
31163
32691
  });
31164
32692
  txHex = await response.text();
@@ -31286,8 +32814,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31286
32814
  this.mutexes.set(txid, mutex);
31287
32815
  }
31288
32816
  const nodes = await mutex.runExclusive(async () => {
32817
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
31289
32818
  const baseUrl = this.config.getElectrsUrl();
31290
- const headers = {};
32819
+ const headers = new Headers2();
31291
32820
  let txHex;
31292
32821
  if (this.config.getNetwork() === 4 /* LOCAL */) {
31293
32822
  const localFaucet = BitcoinFaucet.getInstance();
@@ -31298,9 +32827,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31298
32827
  const auth = btoa(
31299
32828
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
31300
32829
  );
31301
- headers["Authorization"] = `Basic ${auth}`;
32830
+ headers.set("Authorization", `Basic ${auth}`);
31302
32831
  }
31303
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
32832
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
31304
32833
  headers
31305
32834
  });
31306
32835
  txHex = await response.text();
@@ -31542,10 +33071,16 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31542
33071
  const validNodes = [];
31543
33072
  for (const node of nodes) {
31544
33073
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
31545
- const { needRefresh } = getNextTransactionSequence(
31546
- nodeTx.getInput(0).sequence
31547
- );
31548
- if (needRefresh) {
33074
+ const sequence = nodeTx.getInput(0).sequence;
33075
+ if (!sequence) {
33076
+ throw new ValidationError("Invalid node transaction", {
33077
+ field: "sequence",
33078
+ value: nodeTx.getInput(0),
33079
+ expected: "Non-null sequence"
33080
+ });
33081
+ }
33082
+ const needsRefresh = doesLeafNeedRefresh(sequence, true);
33083
+ if (needsRefresh) {
31549
33084
  nodesToExtend.push(node);
31550
33085
  nodeIds.push(node.id);
31551
33086
  } else {
@@ -31582,11 +33117,16 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31582
33117
  const validNodes = [];
31583
33118
  for (const node of nodes) {
31584
33119
  const refundTx = getTxFromRawTxBytes(node.refundTx);
31585
- const { needRefresh } = getNextTransactionSequence(
31586
- refundTx.getInput(0).sequence,
31587
- true
31588
- );
31589
- if (needRefresh) {
33120
+ const sequence = refundTx.getInput(0).sequence;
33121
+ if (!sequence) {
33122
+ throw new ValidationError("Invalid refund transaction", {
33123
+ field: "sequence",
33124
+ value: refundTx.getInput(0),
33125
+ expected: "Non-null sequence"
33126
+ });
33127
+ }
33128
+ const needsRefresh = doesLeafNeedRefresh(sequence);
33129
+ if (needsRefresh) {
31590
33130
  nodesToRefresh.push(node);
31591
33131
  nodeIds.push(node.id);
31592
33132
  } else {
@@ -31620,7 +33160,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31620
33160
  throw new Error(`parent node ${node.parentNodeId} not found`);
31621
33161
  }
31622
33162
  const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
31623
- [node],
33163
+ node,
31624
33164
  parentNode
31625
33165
  );
31626
33166
  if (nodes2.length !== 1) {
@@ -31669,7 +33209,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31669
33209
  leavesToClaim.push({
31670
33210
  leaf: {
31671
33211
  ...leaf.leaf,
31672
- refundTx: leaf.intermediateRefundTx
33212
+ refundTx: leaf.intermediateRefundTx,
33213
+ directRefundTx: leaf.intermediateDirectRefundTx,
33214
+ directFromCpfpRefundTx: leaf.intermediateDirectFromCpfpRefundTx
31673
33215
  },
31674
33216
  keyDerivation: {
31675
33217
  type: "ecies" /* ECIES */,
@@ -31745,7 +33287,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31745
33287
  if (type && transfer.type !== type) {
31746
33288
  continue;
31747
33289
  }
31748
- 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 */) {
33290
+ 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 */) {
31749
33291
  continue;
31750
33292
  }
31751
33293
  promises.push(
@@ -32033,6 +33575,8 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
32033
33575
  await this.transferService.deliverTransferPackage(
32034
33576
  swapResponse.transfer,
32035
33577
  leavesToSend,
33578
+ /* @__PURE__ */ new Map(),
33579
+ /* @__PURE__ */ new Map(),
32036
33580
  /* @__PURE__ */ new Map()
32037
33581
  );
32038
33582
  const sspResponse = await sspClient.requestLightningSend({
@@ -32795,87 +34339,64 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
32795
34339
  },
32796
34340
  includeParents: true
32797
34341
  });
32798
- const node = response.nodes[nodeId];
32799
- if (!node) {
34342
+ let leaf = response.nodes[nodeId];
34343
+ if (!leaf) {
32800
34344
  throw new ValidationError("Node not found", {
32801
34345
  field: "nodeId",
32802
34346
  value: nodeId
32803
34347
  });
32804
34348
  }
32805
- if (!node.parentNodeId) {
32806
- throw new ValidationError("Node has no parent", {
32807
- field: "parentNodeId",
32808
- value: node.parentNodeId
32809
- });
32810
- }
32811
- const parentNode = response.nodes[node.parentNodeId];
32812
- if (!parentNode) {
32813
- throw new ValidationError("Parent node not found", {
32814
- field: "parentNodeId",
32815
- value: node.parentNodeId
32816
- });
32817
- }
32818
- const result = await this.transferService.refreshTimelockNodes(
32819
- [node],
32820
- parentNode
32821
- );
32822
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
32823
- if (leafIndex !== -1 && result.nodes.length > 0) {
32824
- const newNode = result.nodes[0];
32825
- if (newNode) {
32826
- this.leaves[leafIndex] = newNode;
34349
+ let parentNode;
34350
+ let hasParentNode = false;
34351
+ if (!leaf.parentNodeId) {
34352
+ } else {
34353
+ hasParentNode = true;
34354
+ parentNode = response.nodes[leaf.parentNodeId];
34355
+ if (!parentNode) {
34356
+ throw new ValidationError("Parent node not found", {
34357
+ field: "parentNodeId",
34358
+ value: leaf.parentNodeId
34359
+ });
32827
34360
  }
32828
34361
  }
32829
- } catch (error) {
32830
- throw new NetworkError(
32831
- "Failed to refresh timelock",
32832
- {
32833
- method: "refresh_timelock"
32834
- },
32835
- error
32836
- );
32837
- }
32838
- }
32839
- /**
32840
- * Refresh the timelock of a specific node's refund transaction only.
32841
- *
32842
- * @param {string} nodeId - The ID of the node whose refund transaction to refresh
32843
- * @returns {Promise<void>} Promise that resolves when the refund timelock is refreshed
32844
- */
32845
- async testOnly_expireTimelockRefundTx(nodeId) {
32846
- const sparkClient = await this.connectionManager.createSparkClient(
32847
- this.config.getCoordinatorAddress()
32848
- );
32849
- try {
32850
- const response = await sparkClient.query_nodes({
32851
- source: {
32852
- $case: "nodeIds",
32853
- nodeIds: {
32854
- nodeIds: [nodeId]
34362
+ const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
34363
+ const refundTx = getTxFromRawTxBytes(leaf.refundTx);
34364
+ if (hasParentNode) {
34365
+ const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
34366
+ if (nodeTimelock > 100) {
34367
+ const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
34368
+ leaf,
34369
+ parentNode
34370
+ );
34371
+ if (!expiredNodeTxLeaf.nodes[0]) {
34372
+ throw new ValidationError("No expired node tx leaf", {
34373
+ field: "expiredNodeTxLeaf",
34374
+ value: expiredNodeTxLeaf
34375
+ });
32855
34376
  }
32856
- },
32857
- includeParents: false
32858
- });
32859
- const node = response.nodes[nodeId];
32860
- if (!node) {
32861
- throw new ValidationError("Node not found", {
32862
- field: "nodeId",
32863
- value: nodeId
32864
- });
34377
+ leaf = expiredNodeTxLeaf.nodes[0];
34378
+ }
32865
34379
  }
32866
- const result = await this.transferService.refreshTimelockRefundTx(node);
32867
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
32868
- if (leafIndex !== -1 && result.nodes.length > 0) {
32869
- const newNode = result.nodes[0];
32870
- if (newNode) {
32871
- this.leaves[leafIndex] = newNode;
34380
+ const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
34381
+ if (refundTimelock > 100) {
34382
+ const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
34383
+ if (!expiredTxLeaf.nodes[0]) {
34384
+ throw new ValidationError("No expired tx leaf", {
34385
+ field: "expiredTxLeaf",
34386
+ value: expiredTxLeaf
34387
+ });
32872
34388
  }
34389
+ leaf = expiredTxLeaf.nodes[0];
34390
+ }
34391
+ const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
34392
+ if (leafIndex !== -1) {
34393
+ this.leaves[leafIndex] = leaf;
32873
34394
  }
32874
34395
  } catch (error) {
32875
34396
  throw new NetworkError(
32876
- "Failed to refresh refund timelock",
34397
+ "Failed to refresh timelock",
32877
34398
  {
32878
- method: "refresh_timelock_refund_tx"
34399
+ method: "refresh_timelock"
32879
34400
  },
32880
34401
  error
32881
34402
  );
@@ -32935,6 +34456,106 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
32935
34456
  }
32936
34457
  };
32937
34458
 
34459
+ // src/spark-wallet/spark-wallet.node.ts
34460
+ var import_core13 = require("@lightsparkdev/core");
34461
+ var SparkWallet2 = class extends SparkWallet {
34462
+ tracer = null;
34463
+ wrapWithOtelSpan(name, fn) {
34464
+ return async (...args) => {
34465
+ if (!this.tracer) {
34466
+ throw new Error("Tracer not initialized");
34467
+ }
34468
+ return await this.tracer.startActiveSpan(name, async (span) => {
34469
+ const traceId = span.spanContext().traceId;
34470
+ try {
34471
+ const result = await fn(...args);
34472
+ return result;
34473
+ } catch (error) {
34474
+ if (error instanceof Error) {
34475
+ error.message += ` [traceId: ${traceId}]`;
34476
+ } else if ((0, import_core13.isObject)(error)) {
34477
+ error["traceId"] = traceId;
34478
+ }
34479
+ throw error;
34480
+ } finally {
34481
+ span.end();
34482
+ }
34483
+ });
34484
+ };
34485
+ }
34486
+ async initializeTracer(tracerName) {
34487
+ const { trace, propagation, context } = await import("@opentelemetry/api");
34488
+ const { W3CTraceContextPropagator } = await import("@opentelemetry/core");
34489
+ const { AsyncLocalStorageContextManager } = await import("@opentelemetry/context-async-hooks");
34490
+ const { BasicTracerProvider } = await import("@opentelemetry/sdk-trace-base");
34491
+ trace.setGlobalTracerProvider(new BasicTracerProvider());
34492
+ propagation.setGlobalPropagator(new W3CTraceContextPropagator());
34493
+ context.setGlobalContextManager(new AsyncLocalStorageContextManager());
34494
+ this.tracer = trace.getTracer(tracerName);
34495
+ }
34496
+ getTraceName(methodName) {
34497
+ return `SparkWallet.${methodName}`;
34498
+ }
34499
+ wrapPublicMethodsWithOtelSpan(methodName) {
34500
+ const original = this[methodName];
34501
+ if (typeof original !== "function") {
34502
+ throw new Error(`Method ${methodName} is not a function on SparkWallet.`);
34503
+ }
34504
+ const wrapped = this.wrapWithOtelSpan(
34505
+ this.getTraceName(methodName),
34506
+ original.bind(this)
34507
+ );
34508
+ this[methodName] = wrapped;
34509
+ }
34510
+ wrapSparkWalletWithTracing() {
34511
+ const methods = [
34512
+ "getLeaves",
34513
+ "getIdentityPublicKey",
34514
+ "getSparkAddress",
34515
+ "createSparkPaymentIntent",
34516
+ "getSwapFeeEstimate",
34517
+ "getTransfers",
34518
+ "getBalance",
34519
+ "getSingleUseDepositAddress",
34520
+ "getStaticDepositAddress",
34521
+ "queryStaticDepositAddresses",
34522
+ "getClaimStaticDepositQuote",
34523
+ "claimStaticDeposit",
34524
+ "refundStaticDeposit",
34525
+ "getUnusedDepositAddresses",
34526
+ "claimDeposit",
34527
+ "advancedDeposit",
34528
+ "transfer",
34529
+ "createLightningInvoice",
34530
+ "payLightningInvoice",
34531
+ "getLightningSendFeeEstimate",
34532
+ "withdraw",
34533
+ "getWithdrawalFeeQuote",
34534
+ "getTransferFromSsp",
34535
+ "getTransfer",
34536
+ "transferTokens",
34537
+ "batchTransferTokens",
34538
+ "queryTokenTransactions",
34539
+ "getLightningReceiveRequest",
34540
+ "getLightningSendRequest",
34541
+ "getCoopExitRequest",
34542
+ "checkTimelock",
34543
+ "testOnly_expireTimelock"
34544
+ ];
34545
+ methods.forEach((m) => this.wrapPublicMethodsWithOtelSpan(m));
34546
+ this.initWallet = this.wrapWithOtelSpan(
34547
+ this.getTraceName("initWallet"),
34548
+ this.initWallet.bind(this)
34549
+ );
34550
+ }
34551
+ async initWallet(mnemonicOrSeed, accountNumber) {
34552
+ const res = super.initWallet(mnemonicOrSeed, accountNumber);
34553
+ await this.initializeTracer(this.tracerId);
34554
+ this.wrapSparkWalletWithTracing();
34555
+ return res;
34556
+ }
34557
+ };
34558
+
32938
34559
  // src/spark-wallet/types.ts
32939
34560
  init_buffer();
32940
34561
 
@@ -32946,7 +34567,10 @@ setCrypto(cryptoImpl2);
32946
34567
  AuthenticationError,
32947
34568
  ConfigurationError,
32948
34569
  DEFAULT_FEE_SATS,
34570
+ DIRECT_TIMELOCK_OFFSET,
32949
34571
  DefaultSparkSigner,
34572
+ INITIAL_DIRECT_SEQUENCE,
34573
+ INITIAL_SEQUENCE,
32950
34574
  InternalValidationError,
32951
34575
  KeyDerivationType,
32952
34576
  LRC_WALLET_NETWORK,
@@ -32959,6 +34583,8 @@ setCrypto(cryptoImpl2);
32959
34583
  RPCError,
32960
34584
  SparkSDKError,
32961
34585
  SparkWallet,
34586
+ TEST_UNILATERAL_DIRECT_SEQUENCE,
34587
+ TEST_UNILATERAL_SEQUENCE,
32962
34588
  TaprootOutputKeysGenerator,
32963
34589
  TaprootSparkSigner,
32964
34590
  TokenTransactionService,
@@ -32978,13 +34604,21 @@ setCrypto(cryptoImpl2);
32978
34604
  constructFeeBumpTx,
32979
34605
  constructUnilateralExitFeeBumpPackages,
32980
34606
  constructUnilateralExitTxs,
34607
+ createConnectorRefundTransactions,
34608
+ createLeafNodeTx,
34609
+ createNodeTx,
34610
+ createNodeTxs,
32981
34611
  createRefundTx,
34612
+ createRefundTxs,
34613
+ createRootTx,
32982
34614
  createSigningCommitment,
32983
34615
  createSigningNonce,
34616
+ createSplitTx,
32984
34617
  decodeBech32mTokenIdentifier,
32985
34618
  decodeBytesToSigningCommitment,
32986
34619
  decodeBytesToSigningNonce,
32987
34620
  decodeSparkAddress,
34621
+ doesLeafNeedRefresh,
32988
34622
  encodeBech32mTokenIdentifier,
32989
34623
  encodeSigningCommitmentToBytes,
32990
34624
  encodeSigningNonceToBytes,