@buildonspark/spark-sdk 0.3.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/uniffi/uniffi/spark_frost/spark_frost.kt +1361 -1367
  4. package/android/src/main/jniLibs/arm64-v8a/libuniffi_spark_frost.so +0 -0
  5. package/android/src/main/jniLibs/armeabi-v7a/libuniffi_spark_frost.so +0 -0
  6. package/android/src/main/jniLibs/x86/libuniffi_spark_frost.so +0 -0
  7. package/android/src/main/jniLibs/x86_64/libuniffi_spark_frost.so +0 -0
  8. package/dist/bare/index.cjs +1342 -1346
  9. package/dist/bare/index.d.cts +114 -68
  10. package/dist/bare/index.d.ts +114 -68
  11. package/dist/bare/index.js +1266 -1279
  12. package/dist/{chunk-FXIESWE6.js → chunk-27ILUWDJ.js} +1 -1
  13. package/dist/{chunk-5ASXVNTM.js → chunk-4YFT7DAE.js} +1 -1
  14. package/dist/{chunk-FP2CRVQH.js → chunk-G3LHXHF3.js} +1045 -1308
  15. package/dist/{chunk-XI6FCNYG.js → chunk-JLF6WJ7K.js} +1 -1
  16. package/dist/{chunk-3LPEQGVJ.js → chunk-LOXWCMZL.js} +1 -1
  17. package/dist/{chunk-5HU5W56H.js → chunk-WICAF6BS.js} +2 -2
  18. package/dist/{chunk-VFN34EOX.js → chunk-YEBEN7XD.js} +258 -0
  19. package/dist/{client-pNpGP15j.d.cts → client-BIqiUNy4.d.cts} +1 -1
  20. package/dist/{client-By-N7oJS.d.ts → client-BaQf-5gD.d.ts} +1 -1
  21. package/dist/debug.cjs +1330 -1336
  22. package/dist/debug.d.cts +20 -16
  23. package/dist/debug.d.ts +20 -16
  24. package/dist/debug.js +5 -5
  25. package/dist/graphql/objects/index.d.cts +3 -3
  26. package/dist/graphql/objects/index.d.ts +3 -3
  27. package/dist/index.cjs +1363 -1365
  28. package/dist/index.d.cts +7 -7
  29. package/dist/index.d.ts +7 -7
  30. package/dist/index.js +32 -24
  31. package/dist/index.node.cjs +1363 -1365
  32. package/dist/index.node.d.cts +7 -7
  33. package/dist/index.node.d.ts +7 -7
  34. package/dist/index.node.js +31 -23
  35. package/dist/{logging-DMFVY384.d.ts → logging-BNGm6dBp.d.ts} +42 -37
  36. package/dist/{logging-DxLp34Xm.d.cts → logging-D3IfXfHG.d.cts} +42 -37
  37. package/dist/native/index.react-native.cjs +1505 -1366
  38. package/dist/native/index.react-native.d.cts +112 -58
  39. package/dist/native/index.react-native.d.ts +112 -58
  40. package/dist/native/index.react-native.js +1415 -1289
  41. package/dist/proto/spark.cjs +258 -0
  42. package/dist/proto/spark.d.cts +1 -1
  43. package/dist/proto/spark.d.ts +1 -1
  44. package/dist/proto/spark.js +5 -1
  45. package/dist/proto/spark_token.d.cts +1 -1
  46. package/dist/proto/spark_token.d.ts +1 -1
  47. package/dist/proto/spark_token.js +2 -2
  48. package/dist/{spark-By6yHsrk.d.cts → spark-DOpheE8_.d.cts} +39 -2
  49. package/dist/{spark-By6yHsrk.d.ts → spark-DOpheE8_.d.ts} +39 -2
  50. package/dist/{spark-wallet.browser-C1dQknVj.d.ts → spark-wallet.browser-B2rGwjuM.d.ts} +2 -2
  51. package/dist/{spark-wallet.browser-CNMo3IvO.d.cts → spark-wallet.browser-Ck9No4Ks.d.cts} +2 -2
  52. package/dist/{spark-wallet.node-Og6__NMh.d.ts → spark-wallet.node-BqmKsGPs.d.ts} +2 -2
  53. package/dist/{spark-wallet.node-BZJhJZKq.d.cts → spark-wallet.node-C2TIkyt4.d.cts} +2 -2
  54. package/dist/tests/test-utils.cjs +1304 -1236
  55. package/dist/tests/test-utils.d.cts +18 -4
  56. package/dist/tests/test-utils.d.ts +18 -4
  57. package/dist/tests/test-utils.js +7 -7
  58. package/dist/{token-transactions-CLR3rnYi.d.cts → token-transactions-Db8mkjnU.d.cts} +2 -2
  59. package/dist/{token-transactions-C7yefB2S.d.ts → token-transactions-DoMcrxXQ.d.ts} +2 -2
  60. package/dist/types/index.cjs +256 -0
  61. package/dist/types/index.d.cts +2 -2
  62. package/dist/types/index.d.ts +2 -2
  63. package/dist/types/index.js +2 -2
  64. package/dist/{wallet-config-BoyMVa6n.d.ts → wallet-config-Bg3kWltL.d.ts} +42 -35
  65. package/dist/{wallet-config-xom-9UFF.d.cts → wallet-config-CuZKNo9S.d.cts} +42 -35
  66. package/ios/spark_frostFFI.xcframework/ios-arm64/SparkFrost +0 -0
  67. package/ios/spark_frostFFI.xcframework/ios-arm64/spark_frostFFI.framework/spark_frostFFI +0 -0
  68. package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/SparkFrost +0 -0
  69. package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/spark_frostFFI.framework/spark_frostFFI +0 -0
  70. package/ios/spark_frostFFI.xcframework/macos-arm64_x86_64/spark_frostFFI.framework/spark_frostFFI +0 -0
  71. package/package.json +1 -1
  72. package/src/index.react-native.ts +8 -2
  73. package/src/proto/spark.ts +348 -4
  74. package/src/services/config.ts +5 -0
  75. package/src/services/coop-exit.ts +26 -107
  76. package/src/services/deposit.ts +12 -48
  77. package/src/services/signing.ts +62 -49
  78. package/src/services/transfer.ts +437 -722
  79. package/src/services/wallet-config.ts +10 -0
  80. package/src/services/xhr-transport.ts +13 -3
  81. package/src/signer/signer.react-native.ts +73 -1
  82. package/src/spark-wallet/proto-descriptors.ts +1 -1
  83. package/src/spark-wallet/spark-wallet.ts +162 -315
  84. package/src/spark-wallet/types.ts +2 -2
  85. package/src/spark_descriptors.pb +0 -0
  86. package/src/tests/integration/lightning.test.ts +1 -27
  87. package/src/tests/integration/static_deposit.test.ts +6 -9
  88. package/src/tests/integration/unilateral-exit.test.ts +117 -0
  89. package/src/tests/optimize.test.ts +31 -1
  90. package/src/tests/utils/signing.ts +33 -0
  91. package/src/tests/utils/test-faucet.ts +61 -0
  92. package/src/utils/optimize.ts +42 -0
  93. package/src/utils/transaction.ts +250 -249
  94. package/src/utils/unilateral-exit.ts +1 -40
@@ -4,11 +4,11 @@ import {
4
4
  } from "./chunk-NX5KPN5F.js";
5
5
  import {
6
6
  SparkTokenServiceDefinition
7
- } from "./chunk-XI6FCNYG.js";
7
+ } from "./chunk-JLF6WJ7K.js";
8
8
  import {
9
9
  mapTransferToWalletTransfer,
10
10
  mapTreeNodeToWalletLeaf
11
- } from "./chunk-5ASXVNTM.js";
11
+ } from "./chunk-4YFT7DAE.js";
12
12
  import {
13
13
  SatsPayment,
14
14
  SendLeafKeyTweaks,
@@ -18,7 +18,7 @@ import {
18
18
  TokensPayment,
19
19
  TreeNode,
20
20
  networkToJSON
21
- } from "./chunk-VFN34EOX.js";
21
+ } from "./chunk-YEBEN7XD.js";
22
22
  import {
23
23
  BitcoinNetwork_default,
24
24
  ClaimStaticDepositFromJson,
@@ -335,7 +335,11 @@ var BASE_CONFIG = {
335
335
  console: {
336
336
  otel: false
337
337
  },
338
- events: {}
338
+ events: {},
339
+ optimizationOptions: {
340
+ auto: true,
341
+ multiplicity: 0
342
+ }
339
343
  };
340
344
  var LOCAL_WALLET_CONFIG = {
341
345
  ...BASE_CONFIG,
@@ -546,6 +550,9 @@ var WalletConfigService = class {
546
550
  getEvents() {
547
551
  return this.config.events;
548
552
  }
553
+ getOptimizationOptions() {
554
+ return this.config.optimizationOptions;
555
+ }
549
556
  };
550
557
 
551
558
  // src/utils/bitcoin.ts
@@ -700,6 +707,7 @@ function getTxEstimatedVbytesSizeByNumberOfInputsOutputs(numInputs, numOutputs)
700
707
  }
701
708
 
702
709
  // src/utils/transaction.ts
710
+ import { hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
703
711
  import { Transaction as Transaction2 } from "@scure/btc-signer";
704
712
  var INITIAL_TIMELOCK = 2e3;
705
713
  var TEST_UNILATERAL_TIMELOCK = 100;
@@ -708,9 +716,9 @@ var DIRECT_TIMELOCK_OFFSET = 50;
708
716
  var HTLC_TIMELOCK_OFFSET = 70;
709
717
  var DIRECT_HTLC_TIMELOCK_OFFSET = 85;
710
718
  var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
711
- var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
712
719
  var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
713
720
  var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
721
+ var INITIAL_ROOT_NODE_SEQUENCE = 1 << 30 | 0;
714
722
  var ESTIMATED_TX_SIZE = 191;
715
723
  var DEFAULT_SATS_PER_VBYTE = 5;
716
724
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -720,63 +728,8 @@ function maybeApplyFee(amount) {
720
728
  }
721
729
  return amount;
722
730
  }
723
- function createRootTx(depositOutPoint, depositTxOut) {
724
- const cpfpRootTx = new Transaction2({
725
- version: 3,
726
- allowUnknownOutputs: true
727
- });
728
- cpfpRootTx.addInput(depositOutPoint);
729
- cpfpRootTx.addOutput(depositTxOut);
730
- cpfpRootTx.addOutput(getEphemeralAnchorOutput());
731
- const directRootTx = new Transaction2({
732
- version: 3,
733
- allowUnknownOutputs: true
734
- });
735
- directRootTx.addInput(depositOutPoint);
736
- directRootTx.addOutput({
737
- script: depositTxOut.script,
738
- amount: maybeApplyFee(depositTxOut.amount ?? 0n)
739
- });
740
- return [cpfpRootTx, directRootTx];
741
- }
742
- function createSplitTx(parentOutPoint, childTxOuts) {
743
- const cpfpSplitTx = new Transaction2({
744
- version: 3,
745
- allowUnknownOutputs: true
746
- });
747
- cpfpSplitTx.addInput(parentOutPoint);
748
- for (const txOut of childTxOuts) {
749
- cpfpSplitTx.addOutput(txOut);
750
- }
751
- cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
752
- const directSplitTx = new Transaction2({
753
- version: 3,
754
- allowUnknownOutputs: true
755
- });
756
- directSplitTx.addInput(parentOutPoint);
757
- let totalOutputAmount = 0n;
758
- for (const txOut of childTxOuts) {
759
- totalOutputAmount += txOut.amount ?? 0n;
760
- }
761
- if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
762
- const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
763
- for (const txOut of childTxOuts) {
764
- const adjustedAmount = BigInt(
765
- Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
766
- );
767
- directSplitTx.addOutput({
768
- script: txOut.script,
769
- amount: adjustedAmount
770
- });
771
- }
772
- } else {
773
- for (const txOut of childTxOuts) {
774
- directSplitTx.addOutput(txOut);
775
- }
776
- }
777
- return [cpfpSplitTx, directSplitTx];
778
- }
779
731
  function createNodeTx({
732
+ sequence,
780
733
  txOut,
781
734
  parentOutPoint,
782
735
  applyFee,
@@ -786,7 +739,10 @@ function createNodeTx({
786
739
  version: 3,
787
740
  allowUnknownOutputs: true
788
741
  });
789
- nodeTx.addInput(parentOutPoint);
742
+ nodeTx.addInput({
743
+ ...parentOutPoint,
744
+ sequence
745
+ });
790
746
  if (applyFee) {
791
747
  nodeTx.addOutput({
792
748
  script: txOut.script,
@@ -800,52 +756,92 @@ function createNodeTx({
800
756
  }
801
757
  return nodeTx;
802
758
  }
803
- function createNodeTxs(txOut, txIn, directTxIn) {
804
- const cpfpNodeTx = createNodeTx({
805
- txOut,
806
- parentOutPoint: txIn,
807
- includeAnchor: true
808
- });
809
- let directNodeTx;
810
- if (directTxIn) {
811
- directNodeTx = createNodeTx({
812
- txOut,
813
- parentOutPoint: directTxIn,
814
- includeAnchor: false,
815
- applyFee: true
759
+ function createNodeTxs({
760
+ parentTx,
761
+ sequence,
762
+ directSequence,
763
+ vout
764
+ }) {
765
+ const parentOutput = parentTx.getOutput(vout);
766
+ if (!parentOutput.amount || !parentOutput.script) {
767
+ throw new ValidationError("Parent output amount or script not found", {
768
+ field: "parentOutput",
769
+ value: parentOutput
816
770
  });
817
771
  }
818
- return { cpfpNodeTx, directNodeTx };
772
+ const output = {
773
+ script: parentOutput.script,
774
+ amount: parentOutput.amount
775
+ };
776
+ const input = {
777
+ txid: hexToBytes2(getTxId(parentTx)),
778
+ index: vout
779
+ };
780
+ const nodeTx = createNodeTx({
781
+ sequence,
782
+ txOut: output,
783
+ parentOutPoint: input,
784
+ includeAnchor: true
785
+ });
786
+ const directNodeTx = createNodeTx({
787
+ sequence: directSequence ?? sequence + DIRECT_TIMELOCK_OFFSET,
788
+ txOut: output,
789
+ parentOutPoint: input,
790
+ includeAnchor: false,
791
+ applyFee: true
792
+ });
793
+ return { nodeTx, directNodeTx };
819
794
  }
820
- function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
821
- const cpfpLeafTx = new Transaction2({
822
- version: 3,
823
- allowUnknownOutputs: true
795
+ function createRootNodeTx(parentTx, vout) {
796
+ return createNodeTxs({
797
+ parentTx,
798
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
799
+ vout
824
800
  });
825
- cpfpLeafTx.addInput({
826
- ...parentOutPoint,
827
- sequence
801
+ }
802
+ function createZeroTimelockNodeTx(parentTx) {
803
+ return createNodeTxs({
804
+ parentTx,
805
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
806
+ directSequence: DIRECT_TIMELOCK_OFFSET,
807
+ vout: 0
828
808
  });
829
- cpfpLeafTx.addOutput(txOut);
830
- cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
831
- const directLeafTx = new Transaction2({
832
- version: 3,
833
- allowUnknownOutputs: true
809
+ }
810
+ function createInitialTimelockNodeTx(parentTx) {
811
+ return createNodeTxs({
812
+ parentTx,
813
+ sequence: INITIAL_SEQUENCE,
814
+ vout: 0
834
815
  });
835
- directLeafTx.addInput({
836
- ...parentOutPoint,
837
- sequence: directSequence
816
+ }
817
+ function createDecrementedTimelockNodeTx(parentTx, currentTx) {
818
+ const currentSequence = currentTx.getInput(0).sequence;
819
+ if (!currentSequence) {
820
+ throw new ValidationError("Current sequence not found", {
821
+ field: "currentSequence",
822
+ value: currentSequence
823
+ });
824
+ }
825
+ return createNodeTxs({
826
+ parentTx,
827
+ sequence: getNextTransactionSequence(currentSequence).nextSequence,
828
+ vout: 0
838
829
  });
839
- const amountSats = txOut.amount ?? 0n;
840
- let outputAmount = amountSats;
841
- if (shouldCalculateFee) {
842
- outputAmount = maybeApplyFee(amountSats);
830
+ }
831
+ function createTestUnilateralTimelockNodeTx(parentTx, nodeTx) {
832
+ const sequence = nodeTx.getInput(0).sequence;
833
+ if (!sequence) {
834
+ throw new ValidationError("Sequence not found", {
835
+ field: "sequence",
836
+ value: sequence
837
+ });
843
838
  }
844
- directLeafTx.addOutput({
845
- script: txOut.script,
846
- amount: outputAmount
839
+ const isBit30Defined = (sequence || 0) & 1 << 30;
840
+ return createNodeTxs({
841
+ parentTx,
842
+ sequence: isBit30Defined | TEST_UNILATERAL_TIMELOCK,
843
+ vout: 0
847
844
  });
848
- return [cpfpLeafTx, directLeafTx];
849
845
  }
850
846
  function createRefundTx({
851
847
  sequence,
@@ -901,110 +897,109 @@ function getNextHTLCTransactionSequence(currSequence, isNodeTx) {
901
897
  };
902
898
  }
903
899
  function createRefundTxs({
904
- sequence,
905
- directSequence,
906
- input,
907
- directInput,
908
- amountSats,
900
+ nodeTx,
901
+ directNodeTx,
909
902
  receivingPubkey,
910
- network
903
+ network,
904
+ sequence
911
905
  }) {
912
- const cpfpRefundTx = createRefundTx({
913
- sequence,
914
- input,
915
- amountSats,
916
- receivingPubkey,
917
- network,
918
- shouldCalculateFee: false,
919
- includeAnchor: true
920
- });
906
+ const refundInput = {
907
+ txid: hexToBytes2(getTxId(nodeTx)),
908
+ index: 0
909
+ };
910
+ const nodeAmountSats = nodeTx.getOutput(0).amount;
911
+ if (nodeAmountSats === void 0) {
912
+ throw new ValidationError("Node amount not found", {
913
+ field: "nodeAmountSats",
914
+ value: nodeAmountSats
915
+ });
916
+ }
921
917
  let directRefundTx;
922
- let directFromCpfpRefundTx;
923
- if (directSequence && directInput) {
918
+ if (directNodeTx) {
919
+ const directRefundInput = {
920
+ txid: hexToBytes2(getTxId(directNodeTx)),
921
+ index: 0
922
+ };
923
+ const directAmountSats = directNodeTx.getOutput(0).amount;
924
+ if (directAmountSats === void 0) {
925
+ throw new ValidationError("Direct amount not found", {
926
+ field: "directAmountSats",
927
+ value: directAmountSats
928
+ });
929
+ }
924
930
  directRefundTx = createRefundTx({
925
- sequence: directSequence,
926
- input: directInput,
927
- amountSats,
928
- receivingPubkey,
929
- network,
930
- shouldCalculateFee: true,
931
- includeAnchor: false
932
- });
933
- directFromCpfpRefundTx = createRefundTx({
934
- sequence: directSequence,
935
- input,
936
- amountSats,
931
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
932
+ input: directRefundInput,
933
+ amountSats: directAmountSats,
937
934
  receivingPubkey,
938
935
  network,
939
936
  shouldCalculateFee: true,
940
937
  includeAnchor: false
941
938
  });
942
- } else if (directInput && !directSequence) {
943
- throw new ValidationError(
944
- "directSequence must be provided if directInput is",
945
- {
946
- field: "directSequence",
947
- value: directSequence
948
- }
949
- );
950
939
  }
951
- return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
952
- }
953
- function createConnectorRefundTransactions(sequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey, network, shouldCalculateFee) {
954
- const cpfpRefundTx = new Transaction2({
955
- version: 3,
956
- allowUnknownOutputs: true
940
+ const cpfpRefundTx = createRefundTx({
941
+ sequence,
942
+ input: refundInput,
943
+ amountSats: nodeAmountSats,
944
+ receivingPubkey,
945
+ network,
946
+ shouldCalculateFee: false,
947
+ includeAnchor: true
957
948
  });
958
- cpfpRefundTx.addInput({
959
- ...cpfpNodeOutPoint,
960
- sequence
949
+ const directFromCpfpRefundTx = createRefundTx({
950
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
951
+ input: refundInput,
952
+ amountSats: nodeAmountSats,
953
+ receivingPubkey,
954
+ network,
955
+ shouldCalculateFee: true,
956
+ includeAnchor: false
961
957
  });
962
- cpfpRefundTx.addInput(connectorOutput);
963
- const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
964
- cpfpRefundTx.addOutput({
965
- script: receiverScript,
966
- amount: amountSats
958
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
959
+ }
960
+ function createInitialTimelockRefundTxs(params) {
961
+ return createRefundTxs({
962
+ ...params,
963
+ sequence: INITIAL_SEQUENCE
967
964
  });
968
- const directRefundTx = new Transaction2({
969
- version: 3,
970
- allowUnknownOutputs: true
965
+ }
966
+ function createDecrementedTimelockRefundTxs(params) {
967
+ const nextSequence = getNextTransactionSequence(params.sequence).nextSequence;
968
+ return createRefundTxs({
969
+ ...params,
970
+ sequence: nextSequence
971
971
  });
972
- directRefundTx.addInput({
973
- ...directNodeOutPoint,
974
- sequence
972
+ }
973
+ function createCurrentTimelockRefundTxs(params) {
974
+ return createRefundTxs(params);
975
+ }
976
+ function createTestUnilateralRefundTxs(params) {
977
+ return createRefundTxs({
978
+ ...params,
979
+ sequence: TEST_UNILATERAL_SEQUENCE
975
980
  });
976
- directRefundTx.addInput(connectorOutput);
977
- let outputAmount = amountSats;
978
- if (shouldCalculateFee) {
979
- outputAmount = maybeApplyFee(amountSats);
981
+ }
982
+ function createConnectorRefundTxs(params) {
983
+ const { connectorOutput, ...baseParams } = params;
984
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs(baseParams);
985
+ cpfpRefundTx.addInput(connectorOutput);
986
+ if (directRefundTx) {
987
+ directRefundTx.addInput(connectorOutput);
980
988
  }
981
- directRefundTx.addOutput({
982
- script: receiverScript,
983
- amount: outputAmount
984
- });
985
- const directFromCpfpTx = new Transaction2({
986
- version: 3,
987
- allowUnknownOutputs: true
988
- });
989
- directFromCpfpTx.addInput({
990
- ...cpfpNodeOutPoint,
991
- sequence
992
- });
993
- directFromCpfpTx.addInput(connectorOutput);
994
- directFromCpfpTx.addOutput({
995
- script: receiverScript,
996
- amount: outputAmount
997
- });
998
- return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
989
+ if (directFromCpfpRefundTx) {
990
+ directFromCpfpRefundTx.addInput(connectorOutput);
991
+ }
992
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
999
993
  }
1000
994
  function getCurrentTimelock(currSequence) {
1001
995
  return (currSequence || 0) & 65535;
1002
996
  }
1003
997
  function getTransactionSequence(currSequence) {
1004
998
  const timelock = getCurrentTimelock(currSequence);
999
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
1005
1000
  return {
1006
- nextSequence: 1 << 30 | timelock,
1007
- nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
1001
+ nextSequence: isBit30Defined | timelock,
1002
+ nextDirectSequence: isBit30Defined | timelock + DIRECT_TIMELOCK_OFFSET
1008
1003
  };
1009
1004
  }
1010
1005
  function checkIfValidSequence(currSequence) {
@@ -1023,6 +1018,13 @@ function checkIfValidSequence(currSequence) {
1023
1018
  });
1024
1019
  }
1025
1020
  }
1021
+ function isZeroTimelock(currSequence) {
1022
+ return getCurrentTimelock(currSequence) === 0;
1023
+ }
1024
+ function doesTxnNeedRenewed(currSequence) {
1025
+ const currentTimelock = getCurrentTimelock(currSequence);
1026
+ return currentTimelock <= 100;
1027
+ }
1026
1028
  function doesLeafNeedRefresh(currSequence, isNodeTx) {
1027
1029
  const currentTimelock = getCurrentTimelock(currSequence);
1028
1030
  if (isNodeTx) {
@@ -1033,6 +1035,7 @@ function doesLeafNeedRefresh(currSequence, isNodeTx) {
1033
1035
  function getNextTransactionSequence(currSequence, isNodeTx) {
1034
1036
  const currentTimelock = getCurrentTimelock(currSequence);
1035
1037
  const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
1038
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
1036
1039
  if (isNodeTx && nextTimelock < 0) {
1037
1040
  throw new ValidationError("timelock interval is less than 0", {
1038
1041
  field: "nextTimelock",
@@ -1047,8 +1050,8 @@ function getNextTransactionSequence(currSequence, isNodeTx) {
1047
1050
  });
1048
1051
  }
1049
1052
  return {
1050
- nextSequence: 1 << 30 | nextTimelock,
1051
- nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
1053
+ nextSequence: isBit30Defined | nextTimelock,
1054
+ nextDirectSequence: isBit30Defined | nextTimelock + DIRECT_TIMELOCK_OFFSET
1052
1055
  };
1053
1056
  }
1054
1057
  function getEphemeralAnchorOutput() {
@@ -1070,7 +1073,7 @@ var KeyDerivationType = /* @__PURE__ */ ((KeyDerivationType2) => {
1070
1073
  })(KeyDerivationType || {});
1071
1074
 
1072
1075
  // src/utils/transfer_package.ts
1073
- import { hexToBytes as hexToBytes2 } from "@noble/curves/utils";
1076
+ import { hexToBytes as hexToBytes3 } from "@noble/curves/utils";
1074
1077
  import { sha256 as sha2562 } from "@noble/hashes/sha2";
1075
1078
  function getTransferPackageSigningPayload(transferID, transferPackage) {
1076
1079
  const encryptedPayload = transferPackage.keyTweakPackage;
@@ -1079,7 +1082,7 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
1079
1082
  ).map(([key, value]) => ({ key, value }));
1080
1083
  pairs.sort((a, b) => a.key.localeCompare(b.key));
1081
1084
  const encoder = new TextEncoder();
1082
- let message = hexToBytes2(transferID.replaceAll("-", ""));
1085
+ let message = hexToBytes3(transferID.replaceAll("-", ""));
1083
1086
  for (const pair of pairs) {
1084
1087
  const keyPart = encoder.encode(pair.key + ":");
1085
1088
  const separator = encoder.encode(";");
@@ -1207,7 +1210,7 @@ function proofOfPossessionMessageHashForDepositAddress(userPubkey, operatorPubke
1207
1210
  // src/services/deposit.ts
1208
1211
  import { schnorr as schnorr2, secp256k1 as secp256k13 } from "@noble/curves/secp256k1";
1209
1212
  import { sha256 as sha2564 } from "@noble/hashes/sha2";
1210
- import { hexToBytes as hexToBytes3 } from "@noble/hashes/utils";
1213
+ import { hexToBytes as hexToBytes4 } from "@noble/hashes/utils";
1211
1214
  import { p2tr as p2tr2 } from "@scure/btc-signer";
1212
1215
  import { equalBytes } from "@scure/btc-signer/utils";
1213
1216
  var DepositService = class {
@@ -1261,7 +1264,7 @@ var DepositService = class {
1261
1264
  if (operator.identifier === this.config.getCoordinatorIdentifier() && !verifyCoordinatorProof) {
1262
1265
  continue;
1263
1266
  }
1264
- const operatorPubkey2 = hexToBytes3(operator.identityPublicKey);
1267
+ const operatorPubkey2 = hexToBytes4(operator.identityPublicKey);
1265
1268
  const operatorSig = address.depositAddressProof.addressSignatures[operator.identifier];
1266
1269
  if (!operatorSig) {
1267
1270
  throw new ValidationError("Operator signature not found", {
@@ -1380,38 +1383,18 @@ var DepositService = class {
1380
1383
  expected: "Valid output index"
1381
1384
  });
1382
1385
  }
1383
- const script = output.script;
1384
- const amount = output.amount;
1385
- if (!script || !amount) {
1386
- throw new ValidationError("No script or amount found in deposit tx", {
1387
- field: "output",
1388
- value: output,
1389
- expected: "Output with script and amount"
1390
- });
1391
- }
1392
- const depositOutPoint = {
1393
- txid: hexToBytes3(getTxId(depositTx)),
1394
- index: vout
1395
- };
1396
- const depositTxOut = {
1397
- script,
1398
- amount
1399
- };
1400
- const [cpfpRootTx, directRootTx] = createRootTx(
1401
- depositOutPoint,
1402
- depositTxOut
1386
+ const { nodeTx: cpfpRootTx, directNodeTx: directRootTx } = createRootNodeTx(
1387
+ depositTx,
1388
+ vout
1403
1389
  );
1404
1390
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
1405
1391
  const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
1406
1392
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
1407
1393
  const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
1408
1394
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
1409
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
1410
- sequence: INITIAL_SEQUENCE,
1411
- directSequence: INITIAL_DIRECT_SEQUENCE,
1412
- input: { txid: hexToBytes3(getTxId(cpfpRootTx)), index: 0 },
1413
- directInput: { txid: hexToBytes3(getTxId(directRootTx)), index: 0 },
1414
- amountSats: amount,
1395
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
1396
+ nodeTx: cpfpRootTx,
1397
+ directNodeTx: directRootTx,
1415
1398
  receivingPubkey: signingPubKey,
1416
1399
  network: this.config.getNetwork()
1417
1400
  });
@@ -1703,22 +1686,12 @@ var DepositService = class {
1703
1686
  expected: "Output with script and amount"
1704
1687
  });
1705
1688
  }
1706
- const depositOutPoint = {
1707
- txid: hexToBytes3(getTxId(depositTx)),
1708
- index: vout
1709
- };
1710
- const depositTxOut = {
1711
- script,
1712
- amount
1713
- };
1714
- const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
1689
+ const { nodeTx: cpfpRootTx } = createRootNodeTx(depositTx, vout);
1715
1690
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
1716
1691
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
1717
1692
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
1718
- const { cpfpRefundTx } = createRefundTxs({
1719
- sequence: INITIAL_SEQUENCE,
1720
- input: { txid: hexToBytes3(getTxId(cpfpRootTx)), index: 0 },
1721
- amountSats: amount,
1693
+ const { cpfpRefundTx } = createInitialTimelockRefundTxs({
1694
+ nodeTx: cpfpRootTx,
1722
1695
  receivingPubkey: signingPubKey,
1723
1696
  network: this.config.getNetwork()
1724
1697
  });
@@ -1879,7 +1852,7 @@ var setCrypto = (cryptoImplParam) => {
1879
1852
  import { BinaryWriter } from "@bufbuild/protobuf/wire";
1880
1853
  import { schnorr as schnorr3, secp256k1 as secp256k14 } from "@noble/curves/secp256k1";
1881
1854
  import { bytesToNumberBE as bytesToNumberBE2 } from "@noble/curves/utils";
1882
- import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes4 } from "@noble/hashes/utils";
1855
+ import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes5 } from "@noble/hashes/utils";
1883
1856
  import { bech32m as bech32m2 } from "@scure/base";
1884
1857
  import { UUID } from "uuidv7";
1885
1858
 
@@ -2071,7 +2044,7 @@ function encodeSparkAddress(payload) {
2071
2044
  function encodeSparkAddressWithSignature(payload, signature) {
2072
2045
  try {
2073
2046
  isValidPublicKey(payload.identityPublicKey);
2074
- const identityPublicKey = hexToBytes4(payload.identityPublicKey);
2047
+ const identityPublicKey = hexToBytes5(payload.identityPublicKey);
2075
2048
  let sparkInvoiceFields;
2076
2049
  if (payload.sparkInvoiceFields) {
2077
2050
  validateSparkInvoiceFields(payload.sparkInvoiceFields);
@@ -2575,7 +2548,7 @@ import {
2575
2548
  bytesToNumberBE as bytesToNumberBE4,
2576
2549
  numberToBytesBE as numberToBytesBE2
2577
2550
  } from "@noble/curves/utils";
2578
- import { hexToBytes as hexToBytes5 } from "@noble/hashes/utils";
2551
+ import { hexToBytes as hexToBytes6 } from "@noble/hashes/utils";
2579
2552
 
2580
2553
  // src/utils/token-hashing.ts
2581
2554
  import { sha256 as sha2566 } from "@noble/hashes/sha2";
@@ -3895,14 +3868,14 @@ var TokenTransactionService = class {
3895
3868
  }
3896
3869
  if (receiverAddress.sparkInvoiceFields) {
3897
3870
  return {
3898
- receiverPublicKey: hexToBytes5(receiverAddress.identityPublicKey),
3871
+ receiverPublicKey: hexToBytes6(receiverAddress.identityPublicKey),
3899
3872
  rawTokenIdentifier,
3900
3873
  tokenAmount: transfer.tokenAmount,
3901
3874
  sparkInvoice: transfer.receiverSparkAddress
3902
3875
  };
3903
3876
  }
3904
3877
  return {
3905
- receiverPublicKey: hexToBytes5(receiverAddress.identityPublicKey),
3878
+ receiverPublicKey: hexToBytes6(receiverAddress.identityPublicKey),
3906
3879
  rawTokenIdentifier,
3907
3880
  tokenAmount: transfer.tokenAmount
3908
3881
  };
@@ -3968,7 +3941,7 @@ var TokenTransactionService = class {
3968
3941
  for (const [_, operator] of Object.entries(
3969
3942
  this.config.getSigningOperators()
3970
3943
  )) {
3971
- operatorKeys.push(hexToBytes5(operator.identityPublicKey));
3944
+ operatorKeys.push(hexToBytes6(operator.identityPublicKey));
3972
3945
  }
3973
3946
  return operatorKeys;
3974
3947
  }
@@ -4231,8 +4204,8 @@ var TokenTransactionService = class {
4231
4204
  this.config.getCoordinatorAddress()
4232
4205
  );
4233
4206
  let queryParams = {
4234
- issuerPublicKeys: issuerPublicKeys?.map(hexToBytes5),
4235
- ownerPublicKeys: ownerPublicKeys?.map(hexToBytes5),
4207
+ issuerPublicKeys: issuerPublicKeys?.map(hexToBytes6),
4208
+ ownerPublicKeys: ownerPublicKeys?.map(hexToBytes6),
4236
4209
  tokenIdentifiers: tokenIdentifiers?.map((identifier) => {
4237
4210
  const { tokenIdentifier } = decodeBech32mTokenIdentifier(
4238
4211
  identifier,
@@ -4240,7 +4213,7 @@ var TokenTransactionService = class {
4240
4213
  );
4241
4214
  return tokenIdentifier;
4242
4215
  }),
4243
- tokenTransactionHashes: tokenTransactionHashes?.map(hexToBytes5),
4216
+ tokenTransactionHashes: tokenTransactionHashes?.map(hexToBytes6),
4244
4217
  outputIds: outputIds || [],
4245
4218
  limit: pageSize,
4246
4219
  offset
@@ -4345,7 +4318,7 @@ var TokenTransactionService = class {
4345
4318
  }
4346
4319
  const payload = {
4347
4320
  finalTokenTransactionHash,
4348
- operatorIdentityPublicKey: hexToBytes5(operator.identityPublicKey)
4321
+ operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
4349
4322
  };
4350
4323
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
4351
4324
  const ownerSignature = await this.signMessageWithKey(
@@ -4367,7 +4340,7 @@ var TokenTransactionService = class {
4367
4340
  }
4368
4341
  const payload = {
4369
4342
  finalTokenTransactionHash,
4370
- operatorIdentityPublicKey: hexToBytes5(operator.identityPublicKey)
4343
+ operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
4371
4344
  };
4372
4345
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
4373
4346
  const ownerSignature = await this.signMessageWithKey(
@@ -4383,7 +4356,7 @@ var TokenTransactionService = class {
4383
4356
  for (let i = 0; i < transferInput.outputsToSpend.length; i++) {
4384
4357
  const payload = {
4385
4358
  finalTokenTransactionHash,
4386
- operatorIdentityPublicKey: hexToBytes5(operator.identityPublicKey)
4359
+ operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
4387
4360
  };
4388
4361
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
4389
4362
  let ownerSignature;
@@ -4400,7 +4373,7 @@ var TokenTransactionService = class {
4400
4373
  }
4401
4374
  inputTtxoSignaturesPerOperator.push({
4402
4375
  ttxoSignatures,
4403
- operatorIdentityPublicKey: hexToBytes5(operator.identityPublicKey)
4376
+ operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
4404
4377
  });
4405
4378
  }
4406
4379
  return inputTtxoSignaturesPerOperator;
@@ -4830,7 +4803,7 @@ import {
4830
4803
  bytesToHex as bytesToHex6,
4831
4804
  bytesToNumberBE as bytesToNumberBE6,
4832
4805
  equalBytes as equalBytes4,
4833
- hexToBytes as hexToBytes6
4806
+ hexToBytes as hexToBytes7
4834
4807
  } from "@noble/curves/utils";
4835
4808
  import { sha256 as sha2567 } from "@noble/hashes/sha2";
4836
4809
  import { HDKey } from "@scure/bip32";
@@ -4848,7 +4821,7 @@ var isWebExtension = (
4848
4821
  "chrome" in globalThis && globalThis.chrome.runtime?.id
4849
4822
  );
4850
4823
  var userAgent = "navigator" in globalThis ? globalThis.navigator.userAgent || "unknown-user-agent" : void 0;
4851
- var packageVersion = true ? "0.3.8" : "unknown";
4824
+ var packageVersion = true ? "0.4.0" : "unknown";
4852
4825
  var baseEnvStr = "unknown";
4853
4826
  if (isBun) {
4854
4827
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -5228,7 +5201,7 @@ var DefaultSparkSigner = class {
5228
5201
  }
5229
5202
  async createSparkWalletFromSeed(seed, accountNumber) {
5230
5203
  if (typeof seed === "string") {
5231
- seed = hexToBytes6(seed);
5204
+ seed = hexToBytes7(seed);
5232
5205
  }
5233
5206
  const {
5234
5207
  identityKey,
@@ -5367,7 +5340,7 @@ var TaprootSparkSigner = class extends DefaultSparkSigner {
5367
5340
 
5368
5341
  // src/tests/utils/test-faucet.ts
5369
5342
  import { schnorr as schnorr6, secp256k1 as secp256k19 } from "@noble/curves/secp256k1";
5370
- import { bytesToHex as bytesToHex7, hexToBytes as hexToBytes7 } from "@noble/curves/utils";
5343
+ import { bytesToHex as bytesToHex7, hexToBytes as hexToBytes8 } from "@noble/curves/utils";
5371
5344
  import * as btc3 from "@scure/btc-signer";
5372
5345
  import { Address as Address2, OutScript as OutScript2, SigHash as SigHash2, Transaction as Transaction4 } from "@scure/btc-signer";
5373
5346
  import { taprootTweakPrivKey as taprootTweakPrivKey2 } from "@scure/btc-signer/utils";
@@ -5394,10 +5367,10 @@ var getFetch = () => {
5394
5367
  };
5395
5368
 
5396
5369
  // src/tests/utils/test-faucet.ts
5397
- var STATIC_FAUCET_KEY = hexToBytes7(
5370
+ var STATIC_FAUCET_KEY = hexToBytes8(
5398
5371
  "deadbeef1337cafe4242424242424242deadbeef1337cafe4242424242424242"
5399
5372
  );
5400
- var STATIC_MINING_KEY = hexToBytes7(
5373
+ var STATIC_MINING_KEY = hexToBytes8(
5401
5374
  "1337cafe4242deadbeef4242424242421337cafe4242deadbeef424242424242"
5402
5375
  );
5403
5376
  var SATS_PER_BTC = 1e8;
@@ -5480,7 +5453,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
5480
5453
  );
5481
5454
  await this.generateToAddress(1, address);
5482
5455
  const fundingTxRaw = await this.getRawTransaction(fundingTxid);
5483
- const fundingTx = Transaction4.fromRaw(hexToBytes7(fundingTxRaw.hex));
5456
+ const fundingTx = Transaction4.fromRaw(hexToBytes8(fundingTxRaw.hex));
5484
5457
  for (let i = 0; i < fundingTx.outputsLength; i++) {
5485
5458
  const output = fundingTx.getOutput(i);
5486
5459
  if (!output.script || !output.amount) continue;
@@ -5546,7 +5519,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
5546
5519
  this.coins.push({
5547
5520
  key: STATIC_FAUCET_KEY,
5548
5521
  outpoint: {
5549
- txid: hexToBytes7(splitTxId),
5522
+ txid: hexToBytes8(splitTxId),
5550
5523
  index: i
5551
5524
  },
5552
5525
  txout: signedSplitTx.getOutput(i)
@@ -5609,6 +5582,14 @@ var BitcoinFaucet = class _BitcoinFaucet {
5609
5582
  async mineBlocks(numBlocks) {
5610
5583
  return await this.generateToAddress(numBlocks, this.miningAddress);
5611
5584
  }
5585
+ async mineBlocksAndWaitForMiningToComplete(numBlocks) {
5586
+ const startBlock = await this.getBlockCount();
5587
+ await this.mineBlocks(numBlocks);
5588
+ await this.waitForBlocksMined({
5589
+ startBlock,
5590
+ expectedIncrease: numBlocks
5591
+ });
5592
+ }
5612
5593
  async call(method, params) {
5613
5594
  try {
5614
5595
  const { fetch, Headers: Headers2 } = getFetch();
@@ -5661,15 +5642,50 @@ var BitcoinFaucet = class _BitcoinFaucet {
5661
5642
  async getBlock(blockHash) {
5662
5643
  return await this.call("getblock", [blockHash, 2]);
5663
5644
  }
5645
+ async getBlockCount() {
5646
+ return await this.call("getblockcount", []);
5647
+ }
5648
+ async waitForBlocksMined({
5649
+ startBlock,
5650
+ expectedIncrease,
5651
+ timeoutMs = 3e4,
5652
+ intervalMs = 5e3
5653
+ }) {
5654
+ const deadline = Date.now() + timeoutMs;
5655
+ await new Promise((r) => setTimeout(r, intervalMs));
5656
+ const start = startBlock;
5657
+ const target = start + expectedIncrease;
5658
+ while (Date.now() < deadline) {
5659
+ const currentBlock = await this.getBlockCount();
5660
+ if (currentBlock >= target) return currentBlock;
5661
+ await new Promise((r) => setTimeout(r, intervalMs));
5662
+ }
5663
+ throw new Error(
5664
+ `Timed out waiting for ${expectedIncrease} blocks (target height ${target})`
5665
+ );
5666
+ }
5664
5667
  async broadcastTx(txHex) {
5665
5668
  let response = await this.call("sendrawtransaction", [txHex, 0]);
5666
5669
  return response;
5667
5670
  }
5671
+ async submitPackage(txHexs) {
5672
+ let response = await this.call("submitpackage", [txHexs]);
5673
+ return response;
5674
+ }
5668
5675
  async getNewAddress() {
5669
5676
  const key = secp256k19.utils.randomPrivateKey();
5670
5677
  const pubKey = secp256k19.getPublicKey(key);
5671
5678
  return getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */);
5672
5679
  }
5680
+ async getNewExternalWallet() {
5681
+ const key = secp256k19.utils.randomPrivateKey();
5682
+ const pubKey = secp256k19.getPublicKey(key);
5683
+ return {
5684
+ address: getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */),
5685
+ key,
5686
+ pubKey
5687
+ };
5688
+ }
5673
5689
  async sendToAddress(address, amount, blocksToGenerate = 1) {
5674
5690
  const coin = await this.fund();
5675
5691
  if (!coin) {
@@ -6635,7 +6651,7 @@ function collectResponses(responses) {
6635
6651
  }
6636
6652
 
6637
6653
  // src/utils/unilateral-exit.ts
6638
- import { bytesToHex as bytesToHex8, hexToBytes as hexToBytes8 } from "@noble/curves/utils";
6654
+ import { bytesToHex as bytesToHex8, hexToBytes as hexToBytes9 } from "@noble/curves/utils";
6639
6655
  import { ripemd160 } from "@noble/hashes/legacy";
6640
6656
  import { sha256 as sha2569 } from "@noble/hashes/sha2";
6641
6657
  import * as btc4 from "@scure/btc-signer";
@@ -6650,7 +6666,7 @@ function isEphemeralAnchorOutput(script, amount) {
6650
6666
  }
6651
6667
  async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network) {
6652
6668
  const result = [];
6653
- const nodes = nodeHexStrings.map((hex) => TreeNode.decode(hexToBytes8(hex)));
6669
+ const nodes = nodeHexStrings.map((hex) => TreeNode.decode(hexToBytes9(hex)));
6654
6670
  const nodeMap = /* @__PURE__ */ new Map();
6655
6671
  for (const node of nodes) {
6656
6672
  nodeMap.set(node.id, node);
@@ -6739,7 +6755,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
6739
6755
  `Node hex string at index ${i} appears to be a raw transaction hex, not a TreeNode protobuf. Use 'leafidtohex' command to convert node IDs to proper hex strings.`
6740
6756
  );
6741
6757
  }
6742
- const nodeBytes = hexToBytes8(hex);
6758
+ const nodeBytes = hexToBytes9(hex);
6743
6759
  const node = TreeNode.decode(nodeBytes);
6744
6760
  if (!node.id) {
6745
6761
  throw new Error(
@@ -6853,7 +6869,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
6853
6869
  usedUtxos,
6854
6870
  correctedParentTx
6855
6871
  } = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
6856
- const feeBumpTx = btc4.Transaction.fromPSBT(hexToBytes8(nodeFeeBumpPsbt));
6872
+ const feeBumpTx = btc4.Transaction.fromPSBT(hexToBytes9(nodeFeeBumpPsbt));
6857
6873
  var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
6858
6874
  var feeBumpOutPubKey = null;
6859
6875
  for (const usedUtxo of usedUtxos) {
@@ -6908,7 +6924,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
6908
6924
  void 0
6909
6925
  );
6910
6926
  const feeBumpTx2 = btc4.Transaction.fromPSBT(
6911
- hexToBytes8(refundFeeBump.feeBumpPsbt)
6927
+ hexToBytes9(refundFeeBump.feeBumpPsbt)
6912
6928
  );
6913
6929
  var feeBumpOut = feeBumpTx2.outputsLength === 1 ? feeBumpTx2.getOutput(0) : null;
6914
6930
  var feeBumpOutPubKey = null;
@@ -7023,9 +7039,9 @@ function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
7023
7039
  if (!fundingUtxo) {
7024
7040
  throw new Error(`UTXO at index ${i} is undefined`);
7025
7041
  }
7026
- const pubKeyHash = hash160(hexToBytes8(fundingUtxo.publicKey));
7042
+ const pubKeyHash = hash160(hexToBytes9(fundingUtxo.publicKey));
7027
7043
  const scriptToUse = new Uint8Array([0, 20, ...pubKeyHash]);
7028
- const providedScript = hexToBytes8(fundingUtxo.script);
7044
+ const providedScript = hexToBytes9(fundingUtxo.script);
7029
7045
  if (bytesToHex8(scriptToUse) !== bytesToHex8(providedScript)) {
7030
7046
  throw new Error(
7031
7047
  `\u274C Derived script doesn't match provided script for UTXO ${i + 1}.`
@@ -7139,7 +7155,6 @@ __export(utils_exports, {
7139
7155
  DIRECT_HTLC_TIMELOCK_OFFSET: () => DIRECT_HTLC_TIMELOCK_OFFSET,
7140
7156
  DIRECT_TIMELOCK_OFFSET: () => DIRECT_TIMELOCK_OFFSET,
7141
7157
  HTLC_TIMELOCK_OFFSET: () => HTLC_TIMELOCK_OFFSET,
7142
- INITIAL_DIRECT_SEQUENCE: () => INITIAL_DIRECT_SEQUENCE,
7143
7158
  INITIAL_SEQUENCE: () => INITIAL_SEQUENCE,
7144
7159
  LOGGER_NAMES: () => LOGGER_NAMES,
7145
7160
  Network: () => Network2,
@@ -7162,21 +7177,24 @@ __export(utils_exports, {
7162
7177
  constructFeeBumpTx: () => constructFeeBumpTx,
7163
7178
  constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
7164
7179
  constructUnilateralExitTxs: () => constructUnilateralExitTxs,
7165
- createConnectorRefundTransactions: () => createConnectorRefundTransactions,
7166
- createLeafNodeTx: () => createLeafNodeTx,
7167
- createNodeTx: () => createNodeTx,
7168
- createNodeTxs: () => createNodeTxs,
7169
- createRefundTx: () => createRefundTx,
7170
- createRefundTxs: () => createRefundTxs,
7171
- createRootTx: () => createRootTx,
7180
+ createConnectorRefundTxs: () => createConnectorRefundTxs,
7181
+ createCurrentTimelockRefundTxs: () => createCurrentTimelockRefundTxs,
7182
+ createDecrementedTimelockNodeTx: () => createDecrementedTimelockNodeTx,
7183
+ createDecrementedTimelockRefundTxs: () => createDecrementedTimelockRefundTxs,
7184
+ createInitialTimelockNodeTx: () => createInitialTimelockNodeTx,
7185
+ createInitialTimelockRefundTxs: () => createInitialTimelockRefundTxs,
7186
+ createRootNodeTx: () => createRootNodeTx,
7172
7187
  createSigningCommitment: () => createSigningCommitment,
7173
7188
  createSigningNonce: () => createSigningNonce,
7174
- createSplitTx: () => createSplitTx,
7189
+ createTestUnilateralRefundTxs: () => createTestUnilateralRefundTxs,
7190
+ createTestUnilateralTimelockNodeTx: () => createTestUnilateralTimelockNodeTx,
7191
+ createZeroTimelockNodeTx: () => createZeroTimelockNodeTx,
7175
7192
  decodeBech32mTokenIdentifier: () => decodeBech32mTokenIdentifier,
7176
7193
  decodeBytesToSigningCommitment: () => decodeBytesToSigningCommitment,
7177
7194
  decodeBytesToSigningNonce: () => decodeBytesToSigningNonce,
7178
7195
  decodeSparkAddress: () => decodeSparkAddress,
7179
7196
  doesLeafNeedRefresh: () => doesLeafNeedRefresh,
7197
+ doesTxnNeedRenewed: () => doesTxnNeedRenewed,
7180
7198
  encodeBech32mTokenIdentifier: () => encodeBech32mTokenIdentifier,
7181
7199
  encodeSigningCommitmentToBytes: () => encodeSigningCommitmentToBytes,
7182
7200
  encodeSigningNonceToBytes: () => encodeSigningNonceToBytes,
@@ -7214,12 +7232,14 @@ __export(utils_exports, {
7214
7232
  getTxFromRawTxHex: () => getTxFromRawTxHex,
7215
7233
  getTxId: () => getTxId,
7216
7234
  getTxIdNoReverse: () => getTxIdNoReverse,
7235
+ hash160: () => hash160,
7217
7236
  isEphemeralAnchorOutput: () => isEphemeralAnchorOutput,
7218
7237
  isLegacySparkAddress: () => isLegacySparkAddress,
7219
7238
  isSafeForNumber: () => isSafeForNumber,
7220
7239
  isTxBroadcast: () => isTxBroadcast,
7221
7240
  isValidPublicKey: () => isValidPublicKey,
7222
7241
  isValidSparkAddress: () => isValidSparkAddress,
7242
+ isZeroTimelock: () => isZeroTimelock,
7223
7243
  lastKeyWithTarget: () => lastKeyWithTarget,
7224
7244
  maybeApplyFee: () => maybeApplyFee,
7225
7245
  modInverse: () => modInverse,
@@ -7246,11 +7266,11 @@ import {
7246
7266
  bytesToHex as bytesToHex11,
7247
7267
  bytesToNumberBE as bytesToNumberBE8,
7248
7268
  equalBytes as equalBytes6,
7249
- hexToBytes as hexToBytes13
7269
+ hexToBytes as hexToBytes12
7250
7270
  } from "@noble/curves/utils";
7251
7271
  import { validateMnemonic } from "@scure/bip39";
7252
7272
  import { wordlist as wordlist2 } from "@scure/bip39/wordlists/english";
7253
- import { Address as Address4, OutScript as OutScript3, Transaction as Transaction9 } from "@scure/btc-signer";
7273
+ import { Address as Address4, OutScript as OutScript3, Transaction as Transaction7 } from "@scure/btc-signer";
7254
7274
  import { Mutex } from "async-mutex";
7255
7275
  import { uuidv7 as uuidv74 } from "uuidv7";
7256
7276
 
@@ -8372,28 +8392,14 @@ var SparkAuthProvider = class {
8372
8392
  };
8373
8393
 
8374
8394
  // src/services/coop-exit.ts
8375
- import { Transaction as Transaction7 } from "@scure/btc-signer";
8376
8395
  import { uuidv7 as uuidv72 } from "uuidv7";
8377
8396
 
8378
8397
  // src/services/transfer.ts
8379
8398
  import { secp256k1 as secp256k110 } from "@noble/curves/secp256k1";
8380
- import {
8381
- bytesToHex as bytesToHex10,
8382
- equalBytes as equalBytes5,
8383
- hexToBytes as hexToBytes9,
8384
- numberToBytesBE as numberToBytesBE4
8385
- } from "@noble/curves/utils";
8399
+ import { bytesToHex as bytesToHex10, equalBytes as equalBytes5, numberToBytesBE as numberToBytesBE4 } from "@noble/curves/utils";
8386
8400
  import { sha256 as sha25611 } from "@noble/hashes/sha2";
8387
- import { Transaction as Transaction6 } from "@scure/btc-signer";
8388
8401
  import * as ecies2 from "eciesjs";
8389
8402
  import { uuidv7 } from "uuidv7";
8390
- function getSigningJobProto(signingJob) {
8391
- return {
8392
- signingPublicKey: signingJob.signingPublicKey,
8393
- rawTx: signingJob.rawTx,
8394
- signingNonceCommitment: signingJob.signingNonceCommitment.commitment
8395
- };
8396
- }
8397
8403
  var BaseTransferService = class {
8398
8404
  config;
8399
8405
  connectionManager;
@@ -9075,42 +9081,27 @@ var TransferService = class extends BaseTransferService {
9075
9081
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
9076
9082
  }
9077
9083
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
9078
- const cpfpNodeOutPoint = {
9079
- txid: hexToBytes9(getTxId(nodeTx)),
9080
- index: 0
9081
- };
9082
9084
  let directNodeTx;
9083
- let directNodeOutPoint;
9084
9085
  if (leaf.leaf.directTx.length > 0) {
9085
9086
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
9086
- directNodeOutPoint = {
9087
- txid: hexToBytes9(getTxId(directNodeTx)),
9088
- index: 0
9089
- };
9090
9087
  }
9091
9088
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
9092
- const sequence = currRefundTx.getInput(0).sequence;
9093
- if (!sequence) {
9089
+ const currentSequence = currRefundTx.getInput(0).sequence;
9090
+ if (!currentSequence) {
9094
9091
  throw new ValidationError("Invalid refund transaction", {
9095
9092
  field: "sequence",
9096
9093
  value: currRefundTx.getInput(0),
9097
9094
  expected: "Non-null sequence"
9098
9095
  });
9099
9096
  }
9100
- const { nextSequence, nextDirectSequence } = isForClaim ? getTransactionSequence(sequence) : getNextTransactionSequence(sequence);
9101
- const amountSats = currRefundTx.getOutput(0).amount;
9102
- if (amountSats === void 0) {
9103
- throw new Error("Amount not found in signRefunds");
9104
- }
9105
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
9106
- sequence: nextSequence,
9107
- directSequence: nextDirectSequence,
9108
- input: cpfpNodeOutPoint,
9109
- directInput: directNodeOutPoint,
9110
- amountSats,
9097
+ const refundTxsParams = {
9098
+ nodeTx,
9099
+ directNodeTx,
9100
+ sequence: currentSequence,
9111
9101
  receivingPubkey: refundSigningData.receivingPubkey,
9112
9102
  network: this.config.getNetwork()
9113
- });
9103
+ };
9104
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = isForClaim ? createCurrentTimelockRefundTxs(refundTxsParams) : createDecrementedTimelockRefundTxs(refundTxsParams);
9114
9105
  refundSigningData.refundTx = cpfpRefundTx;
9115
9106
  refundSigningData.directRefundTx = directRefundTx;
9116
9107
  refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
@@ -9323,71 +9314,127 @@ var TransferService = class extends BaseTransferService {
9323
9314
  throw new Error(`Error querying pending transfers by sender: ${error}`);
9324
9315
  }
9325
9316
  }
9326
- async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
9317
+ async renewRefundTxn(node, parentNode) {
9318
+ const sparkClient = await this.connectionManager.createSparkClient(
9319
+ this.config.getCoordinatorAddress()
9320
+ );
9321
+ const signingJobs = await this.createRenewRefundSigningJobs(
9322
+ node,
9323
+ parentNode
9324
+ );
9325
+ const statechainCommitments = await sparkClient.get_signing_commitments({
9326
+ nodeIds: [node.id],
9327
+ count: signingJobs.length
9328
+ });
9329
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
9330
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
9331
+ if (!signingNonceCommitments) {
9332
+ throw new Error("Signing nonce commitments not found");
9333
+ }
9334
+ return {
9335
+ ...signingJob,
9336
+ signingNonceCommitments
9337
+ };
9338
+ });
9339
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
9340
+ const renewRefundTimelockSigningJob = {
9341
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
9342
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
9343
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
9344
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
9345
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
9346
+ };
9347
+ const response = await sparkClient.renew_leaf({
9348
+ leafId: node.id,
9349
+ signingJobs: {
9350
+ $case: "renewRefundTimelockSigningJob",
9351
+ renewRefundTimelockSigningJob
9352
+ }
9353
+ });
9354
+ if (response.renewResult?.$case !== "renewRefundTimelockResult" || !response.renewResult?.renewRefundTimelockResult.node) {
9355
+ throw new ValidationError("Unexpected renew result", {
9356
+ field: "renewResult",
9357
+ value: response.renewResult
9358
+ });
9359
+ }
9360
+ return response.renewResult?.renewRefundTimelockResult.node;
9361
+ }
9362
+ async renewNodeTxn(node, parentNode) {
9363
+ const sparkClient = await this.connectionManager.createSparkClient(
9364
+ this.config.getCoordinatorAddress()
9365
+ );
9366
+ const signingJobs = await this.createRenewNodeSigningJobs(node, parentNode);
9367
+ const statechainCommitments = await sparkClient.get_signing_commitments({
9368
+ nodeIds: [node.id],
9369
+ count: signingJobs.length
9370
+ });
9371
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
9372
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
9373
+ if (!signingNonceCommitments) {
9374
+ throw new Error("Signing nonce commitments not found");
9375
+ }
9376
+ return {
9377
+ ...signingJob,
9378
+ signingNonceCommitments
9379
+ };
9380
+ });
9381
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
9382
+ const response = await sparkClient.renew_leaf({
9383
+ leafId: node.id,
9384
+ signingJobs: {
9385
+ $case: "renewNodeTimelockSigningJob",
9386
+ renewNodeTimelockSigningJob: {
9387
+ splitNodeTxSigningJob: userSignedTxSigningJobs.get("split"),
9388
+ splitNodeDirectTxSigningJob: userSignedTxSigningJobs.get("directSplit"),
9389
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
9390
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
9391
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
9392
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
9393
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
9394
+ }
9395
+ }
9396
+ });
9397
+ if (response.renewResult?.$case !== "renewNodeTimelockResult" || !response.renewResult?.renewNodeTimelockResult.node) {
9398
+ throw new ValidationError("Unexpected renew result", {
9399
+ field: "renewResult",
9400
+ value: response.renewResult
9401
+ });
9402
+ }
9403
+ return response.renewResult.renewNodeTimelockResult.node;
9404
+ }
9405
+ async createRenewRefundSigningJobs(node, parentNode) {
9327
9406
  const signingJobs = [];
9328
- const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
9329
- const parentNodeOutput = parentNodeTx.getOutput(0);
9407
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
9408
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
9409
+ 0
9410
+ );
9330
9411
  if (!parentNodeOutput) {
9331
- throw Error("Could not get parent node output");
9412
+ throw new Error("Parent node output not found");
9332
9413
  }
9333
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
9334
- const nodeInput = nodeTx.getInput(0);
9335
- const nodeOutput = nodeTx.getOutput(0);
9336
- if (!nodeOutput) {
9337
- throw Error("Could not get node output");
9338
- }
9339
- let directNodeTx;
9340
- let directNodeInput;
9341
- if (node.directTx.length > 0) {
9342
- directNodeTx = getTxFromRawTxBytes(node.directTx);
9343
- directNodeInput = directNodeTx.getInput(0);
9344
- }
9345
- const currSequence = nodeInput.sequence;
9346
- if (!currSequence) {
9347
- throw new ValidationError("Invalid node transaction", {
9348
- field: "sequence",
9349
- value: nodeInput,
9350
- expected: "Non-null sequence"
9351
- });
9352
- }
9353
- let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
9354
- currSequence,
9355
- true
9356
- );
9357
- const output = {
9414
+ const unsignedParentNodeOutput = {
9358
9415
  script: parentNodeOutput.script,
9359
9416
  amount: parentNodeOutput.amount
9360
9417
  };
9361
- const newNodeInput = {
9362
- txid: nodeInput.txid,
9363
- index: nodeInput.index,
9364
- sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
9365
- };
9366
- const newDirectInput = directNodeTx && directNodeInput ? {
9367
- txid: directNodeInput.txid,
9368
- index: directNodeInput.index,
9369
- sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
9370
- } : void 0;
9371
- const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
9372
- output,
9373
- newNodeInput,
9374
- newDirectInput
9375
- );
9376
- const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
9377
- if (!newCpfpNodeOutput) {
9378
- throw Error("Could not get new cpfp node output");
9379
- }
9380
- const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
9381
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
9418
+ const keyDerivation = {
9382
9419
  type: "leaf" /* LEAF */,
9383
9420
  path: node.id
9384
- });
9421
+ };
9422
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
9423
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
9424
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
9425
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createDecrementedTimelockNodeTx(parentTx, nodeTx);
9385
9426
  signingJobs.push({
9386
9427
  signingPublicKey,
9387
- rawTx: cpfpNodeTx.toBytes(),
9428
+ rawTx: newNodeTx.toBytes(),
9388
9429
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9389
9430
  type: "node",
9390
- parentTxOut: parentNodeOutput
9431
+ parentTxOut: unsignedParentNodeOutput,
9432
+ leafId: node.id,
9433
+ keyDerivation: {
9434
+ type: "leaf" /* LEAF */,
9435
+ path: node.id
9436
+ },
9437
+ verifyingKey: node.verifyingPublicKey
9391
9438
  });
9392
9439
  if (newDirectNodeTx) {
9393
9440
  signingJobs.push({
@@ -9395,537 +9442,299 @@ var TransferService = class extends BaseTransferService {
9395
9442
  rawTx: newDirectNodeTx.toBytes(),
9396
9443
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9397
9444
  type: "directNode",
9398
- parentTxOut: parentNodeOutput
9445
+ parentTxOut: unsignedParentNodeOutput,
9446
+ leafId: node.id,
9447
+ keyDerivation: {
9448
+ type: "leaf" /* LEAF */,
9449
+ path: node.id
9450
+ },
9451
+ verifyingKey: node.verifyingPublicKey
9399
9452
  });
9400
9453
  }
9401
- const newCpfpRefundOutPoint = {
9402
- txid: hexToBytes9(getTxId(cpfpNodeTx)),
9403
- index: 0
9404
- };
9405
- let newDirectRefundOutPoint;
9406
- if (newDirectNodeTx) {
9407
- newDirectRefundOutPoint = {
9408
- txid: hexToBytes9(getTxId(newDirectNodeTx)),
9409
- index: 0
9410
- };
9454
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
9455
+ if (!newCpfpNodeOutput) {
9456
+ throw Error("Could not get new cpfp node output");
9411
9457
  }
9412
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
9413
- sequence: INITIAL_SEQUENCE,
9414
- directSequence: INITIAL_DIRECT_SEQUENCE,
9415
- input: newCpfpRefundOutPoint,
9416
- directInput: newDirectRefundOutPoint,
9417
- amountSats: nodeOutput.amount,
9418
- receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
9419
- type: "leaf" /* LEAF */,
9420
- path: node.id
9421
- }),
9458
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
9459
+ const amountSats = refundTx.getOutput(0).amount;
9460
+ if (amountSats === void 0) {
9461
+ throw new Error("Amount not found in extendTimelock");
9462
+ }
9463
+ const directAmountSats = newDirectNodeOutput?.amount;
9464
+ if (directAmountSats === void 0) {
9465
+ throw new Error("Amount not found in extendTimelock");
9466
+ }
9467
+ const {
9468
+ cpfpRefundTx: newRefundTx,
9469
+ directRefundTx: newDirectRefundTx,
9470
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
9471
+ } = createInitialTimelockRefundTxs({
9472
+ nodeTx: newNodeTx,
9473
+ directNodeTx: newDirectNodeTx,
9474
+ receivingPubkey: signingPublicKey,
9422
9475
  network: this.config.getNetwork()
9423
9476
  });
9424
9477
  signingJobs.push({
9425
9478
  signingPublicKey,
9426
- rawTx: cpfpRefundTx.toBytes(),
9479
+ rawTx: newRefundTx.toBytes(),
9427
9480
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9428
9481
  type: "cpfp",
9429
- parentTxOut: newCpfpNodeOutput
9482
+ parentTxOut: newCpfpNodeOutput,
9483
+ leafId: node.id,
9484
+ keyDerivation,
9485
+ verifyingKey: node.verifyingPublicKey
9430
9486
  });
9431
- if (directRefundTx && newDirectNodeOutput) {
9487
+ if (newDirectRefundTx && newDirectNodeOutput) {
9432
9488
  signingJobs.push({
9433
9489
  signingPublicKey,
9434
- rawTx: directRefundTx.toBytes(),
9490
+ rawTx: newDirectRefundTx.toBytes(),
9435
9491
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9436
9492
  type: "direct",
9437
- parentTxOut: newDirectNodeOutput
9493
+ parentTxOut: newDirectNodeOutput,
9494
+ leafId: node.id,
9495
+ keyDerivation,
9496
+ verifyingKey: node.verifyingPublicKey
9438
9497
  });
9439
9498
  }
9440
- if (directFromCpfpRefundTx && newCpfpNodeOutput) {
9499
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
9441
9500
  signingJobs.push({
9442
9501
  signingPublicKey,
9443
- rawTx: directFromCpfpRefundTx.toBytes(),
9502
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
9444
9503
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9445
9504
  type: "directFromCpfp",
9446
- parentTxOut: newCpfpNodeOutput
9505
+ parentTxOut: newCpfpNodeOutput,
9506
+ leafId: node.id,
9507
+ keyDerivation,
9508
+ verifyingKey: node.verifyingPublicKey
9447
9509
  });
9448
9510
  }
9449
- const sparkClient = await this.connectionManager.createSparkClient(
9450
- this.config.getCoordinatorAddress()
9451
- );
9452
- const response = await sparkClient.refresh_timelock_v2({
9453
- leafId: node.id,
9454
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
9455
- signingJobs: signingJobs.map(getSigningJobProto)
9456
- });
9457
- if (signingJobs.length !== response.signingResults.length) {
9458
- throw Error(
9459
- `number of signing jobs and signing results do not match: ${signingJobs.length} !== ${response.signingResults.length}`
9460
- );
9461
- }
9462
- let nodeSignatures = [];
9463
- let leafCpfpSignature;
9464
- let leafDirectSignature;
9465
- let cpfpRefundSignature;
9466
- let directRefundSignature;
9467
- let directFromCpfpRefundSignature;
9468
- for (const [i, signingResult] of response.signingResults.entries()) {
9469
- const signingJob = signingJobs[i];
9470
- if (!signingJob || !signingResult) {
9471
- throw Error("Signing job does not exist");
9472
- }
9473
- const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
9474
- const txOut = signingJob.parentTxOut;
9475
- if (!txOut) {
9476
- throw Error("Could not get tx out");
9477
- }
9478
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
9479
- const userSignature = await this.config.signer.signFrost({
9480
- message: rawTxSighash,
9481
- keyDerivation: {
9482
- type: "leaf" /* LEAF */,
9483
- path: node.id
9484
- },
9485
- publicKey: signingJob.signingPublicKey,
9486
- verifyingKey: signingResult.verifyingKey,
9487
- selfCommitment: signingJob.signingNonceCommitment,
9488
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
9489
- adaptorPubKey: new Uint8Array()
9490
- });
9491
- const signature = await this.config.signer.aggregateFrost({
9492
- message: rawTxSighash,
9493
- statechainSignatures: signingResult.signingResult?.signatureShares,
9494
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
9495
- verifyingKey: signingResult.verifyingKey,
9496
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
9497
- selfCommitment: signingJob.signingNonceCommitment,
9498
- publicKey: signingJob.signingPublicKey,
9499
- selfSignature: userSignature,
9500
- adaptorPubKey: new Uint8Array()
9501
- });
9502
- if (signingJob.type === "node") {
9503
- leafCpfpSignature = signature;
9504
- } else if (signingJob.type === "directNode") {
9505
- leafDirectSignature = signature;
9506
- } else if (signingJob.type === "cpfp") {
9507
- cpfpRefundSignature = signature;
9508
- } else if (signingJob.type === "direct") {
9509
- directRefundSignature = signature;
9510
- } else if (signingJob.type === "directFromCpfp") {
9511
- directFromCpfpRefundSignature = signature;
9512
- }
9513
- }
9514
- nodeSignatures.push({
9515
- nodeId: node.id,
9516
- nodeTxSignature: leafCpfpSignature || new Uint8Array(),
9517
- directNodeTxSignature: leafDirectSignature || new Uint8Array(),
9518
- refundTxSignature: cpfpRefundSignature || new Uint8Array(),
9519
- directRefundTxSignature: directRefundSignature || new Uint8Array(),
9520
- directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
9521
- });
9522
- const result = await sparkClient.finalize_node_signatures_v2({
9523
- intent: 3 /* REFRESH */,
9524
- nodeSignatures
9525
- });
9526
- return result;
9527
- }
9528
- async refreshTimelockNodes(node, parentNode) {
9529
- return await this.refreshTimelockNodesInternal(node, parentNode);
9511
+ return signingJobs;
9530
9512
  }
9531
- async extendTimelock(node) {
9532
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
9533
- const refundTx = getTxFromRawTxBytes(node.refundTx);
9534
- const refundSequence = refundTx.getInput(0).sequence || 0;
9535
- const newNodeOutPoint = {
9536
- txid: hexToBytes9(getTxId(nodeTx)),
9537
- index: 0
9538
- };
9539
- const {
9540
- nextSequence: newNodeSequence,
9541
- nextDirectSequence: newDirectNodeSequence
9542
- } = getNextTransactionSequence(refundSequence);
9543
- const newNodeTx = new Transaction6({
9544
- version: 3,
9545
- allowUnknownOutputs: true
9546
- });
9547
- newNodeTx.addInput({ ...newNodeOutPoint, sequence: newNodeSequence });
9548
- const originalOutput = nodeTx.getOutput(0);
9549
- if (!originalOutput) {
9550
- throw Error("Could not get original node output");
9551
- }
9552
- newNodeTx.addOutput({
9553
- script: originalOutput.script,
9554
- amount: originalOutput.amount
9555
- });
9556
- newNodeTx.addOutput(getEphemeralAnchorOutput());
9557
- let newDirectNodeTx;
9558
- if (node.directTx.length > 0) {
9559
- newDirectNodeTx = new Transaction6({
9560
- version: 3,
9561
- allowUnknownOutputs: true
9562
- });
9563
- newDirectNodeTx.addInput({
9564
- ...newNodeOutPoint,
9565
- sequence: newDirectNodeSequence
9566
- });
9567
- newDirectNodeTx.addOutput({
9568
- script: originalOutput.script,
9569
- amount: maybeApplyFee(originalOutput.amount)
9570
- });
9571
- }
9572
- const newCpfpRefundOutPoint = {
9573
- txid: hexToBytes9(getTxId(newNodeTx)),
9574
- index: 0
9575
- };
9576
- let newDirectRefundOutPoint;
9577
- if (newDirectNodeTx) {
9578
- newDirectRefundOutPoint = {
9579
- txid: hexToBytes9(getTxId(newDirectNodeTx)),
9580
- index: 0
9581
- };
9582
- }
9583
- const amountSats = refundTx.getOutput(0).amount;
9584
- if (amountSats === void 0) {
9585
- throw new Error("Amount not found in extendTimelock");
9586
- }
9587
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
9588
- type: "leaf" /* LEAF */,
9589
- path: node.id
9590
- });
9591
- const {
9592
- cpfpRefundTx: newCpfpRefundTx,
9593
- directRefundTx: newDirectRefundTx,
9594
- directFromCpfpRefundTx: newDirectFromCpfpRefundTx
9595
- } = createRefundTxs({
9596
- sequence: INITIAL_SEQUENCE,
9597
- directSequence: INITIAL_DIRECT_SEQUENCE,
9598
- input: newCpfpRefundOutPoint,
9599
- directInput: newDirectRefundOutPoint,
9600
- amountSats,
9601
- receivingPubkey: signingPublicKey,
9602
- network: this.config.getNetwork()
9603
- });
9604
- if (!newCpfpRefundTx) {
9605
- throw new ValidationError(
9606
- "Failed to create refund transactions in extendTimelock"
9607
- );
9608
- }
9609
- const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
9610
- const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
9611
- const cpfpRefundSighash = getSigHashFromTx(
9612
- newCpfpRefundTx,
9613
- 0,
9614
- newNodeTx.getOutput(0)
9513
+ async createRenewNodeSigningJobs(node, parentNode) {
9514
+ const signingJobs = [];
9515
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
9516
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
9517
+ 0
9615
9518
  );
9616
- const directRefundSighash = newDirectNodeTx && newDirectRefundTx ? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0)) : void 0;
9617
- const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx ? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0)) : void 0;
9618
- const newNodeSigningJob = {
9619
- signingPublicKey,
9620
- rawTx: newNodeTx.toBytes(),
9621
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9622
- };
9623
- const newDirectNodeSigningJob = newDirectNodeTx ? {
9624
- signingPublicKey,
9625
- rawTx: newDirectNodeTx.toBytes(),
9626
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9627
- } : void 0;
9628
- const newCpfpRefundSigningJob = {
9629
- signingPublicKey,
9630
- rawTx: newCpfpRefundTx.toBytes(),
9631
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9519
+ const unsignedParentNodeOutput = {
9520
+ script: parentNodeOutput.script,
9521
+ amount: parentNodeOutput.amount
9632
9522
  };
9633
- const newDirectRefundSigningJob = newDirectRefundTx ? {
9634
- signingPublicKey,
9635
- rawTx: newDirectRefundTx.toBytes(),
9636
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9637
- } : void 0;
9638
- const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
9639
- signingPublicKey,
9640
- rawTx: newDirectFromCpfpRefundTx.toBytes(),
9641
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9642
- } : void 0;
9643
- const sparkClient = await this.connectionManager.createSparkClient(
9644
- this.config.getCoordinatorAddress()
9645
- );
9646
- const response = await sparkClient.extend_leaf_v2({
9647
- leafId: node.id,
9648
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
9649
- nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
9650
- directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
9651
- refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
9652
- directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
9653
- directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
9654
- });
9655
- if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
9656
- throw new Error("Signing result does not exist");
9657
- }
9658
9523
  const keyDerivation = {
9659
9524
  type: "leaf" /* LEAF */,
9660
9525
  path: node.id
9661
9526
  };
9662
- const nodeUserSig = await this.config.signer.signFrost({
9663
- message: nodeSighash,
9527
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
9528
+ const { nodeTx: splitNodeTx, directNodeTx: splitNodeDirectTx } = createZeroTimelockNodeTx(parentTx);
9529
+ signingJobs.push({
9530
+ signingPublicKey,
9531
+ rawTx: splitNodeTx.toBytes(),
9532
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9533
+ type: "split",
9534
+ parentTxOut: unsignedParentNodeOutput,
9535
+ leafId: node.id,
9664
9536
  keyDerivation,
9665
- publicKey: signingPublicKey,
9666
- verifyingKey: response.nodeTxSigningResult.verifyingKey,
9667
- selfCommitment: newNodeSigningJob.signingNonceCommitment,
9668
- statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
9669
- adaptorPubKey: new Uint8Array()
9537
+ verifyingKey: node.verifyingPublicKey
9670
9538
  });
9671
- const nodeSig = await this.config.signer.aggregateFrost({
9672
- message: nodeSighash,
9673
- statechainSignatures: response.nodeTxSigningResult.signingResult?.signatureShares,
9674
- statechainPublicKeys: response.nodeTxSigningResult.signingResult?.publicKeys,
9675
- verifyingKey: response.nodeTxSigningResult.verifyingKey,
9676
- statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
9677
- selfCommitment: newNodeSigningJob.signingNonceCommitment,
9678
- publicKey: signingPublicKey,
9679
- selfSignature: nodeUserSig,
9680
- adaptorPubKey: new Uint8Array()
9681
- });
9682
- let directNodeSig;
9683
- if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
9684
- const directNodeUserSig = await this.config.signer.signFrost({
9685
- message: directNodeSighash,
9539
+ if (splitNodeDirectTx) {
9540
+ signingJobs.push({
9541
+ signingPublicKey,
9542
+ rawTx: splitNodeDirectTx.toBytes(),
9543
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9544
+ type: "directSplit",
9545
+ parentTxOut: unsignedParentNodeOutput,
9546
+ leafId: node.id,
9686
9547
  keyDerivation,
9687
- publicKey: signingPublicKey,
9688
- verifyingKey: response.directNodeTxSigningResult.verifyingKey,
9689
- selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
9690
- statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
9691
- adaptorPubKey: new Uint8Array()
9692
- });
9693
- directNodeSig = await this.config.signer.aggregateFrost({
9694
- message: directNodeSighash,
9695
- statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
9696
- statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
9697
- verifyingKey: response.directNodeTxSigningResult.verifyingKey,
9698
- statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
9699
- selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
9700
- publicKey: signingPublicKey,
9701
- selfSignature: directNodeUserSig,
9702
- adaptorPubKey: new Uint8Array()
9548
+ verifyingKey: node.verifyingPublicKey
9703
9549
  });
9704
9550
  }
9705
- const cpfpRefundUserSig = await this.config.signer.signFrost({
9706
- message: cpfpRefundSighash,
9551
+ const splitNodeOutput = splitNodeTx.getOutput(0);
9552
+ const splitNodeDirectOutput = splitNodeDirectTx.getOutput(0);
9553
+ if (!splitNodeDirectOutput.amount || !splitNodeDirectOutput.script) {
9554
+ throw new Error("Could not get split node output");
9555
+ }
9556
+ const unsignedSplitNodeOutput = {
9557
+ script: splitNodeDirectOutput.script,
9558
+ amount: splitNodeDirectOutput.amount
9559
+ };
9560
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createInitialTimelockNodeTx(splitNodeTx);
9561
+ signingJobs.push({
9562
+ signingPublicKey,
9563
+ rawTx: newNodeTx.toBytes(),
9564
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9565
+ type: "node",
9566
+ parentTxOut: splitNodeOutput,
9567
+ leafId: node.id,
9707
9568
  keyDerivation,
9708
- publicKey: signingPublicKey,
9709
- verifyingKey: response.refundTxSigningResult.verifyingKey,
9710
- selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
9711
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
9712
- adaptorPubKey: new Uint8Array()
9569
+ verifyingKey: node.verifyingPublicKey
9713
9570
  });
9714
- const cpfpRefundSig = await this.config.signer.aggregateFrost({
9715
- message: cpfpRefundSighash,
9716
- statechainSignatures: response.refundTxSigningResult.signingResult?.signatureShares,
9717
- statechainPublicKeys: response.refundTxSigningResult.signingResult?.publicKeys,
9718
- verifyingKey: response.refundTxSigningResult.verifyingKey,
9719
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
9720
- selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
9721
- publicKey: signingPublicKey,
9722
- selfSignature: cpfpRefundUserSig,
9723
- adaptorPubKey: new Uint8Array()
9724
- });
9725
- let directRefundSig;
9726
- if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
9727
- const directRefundUserSig = await this.config.signer.signFrost({
9728
- message: directRefundSighash,
9729
- keyDerivation,
9730
- publicKey: signingPublicKey,
9731
- verifyingKey: response.directRefundTxSigningResult.verifyingKey,
9732
- selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
9733
- statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
9734
- adaptorPubKey: new Uint8Array()
9735
- });
9736
- directRefundSig = await this.config.signer.aggregateFrost({
9737
- message: directRefundSighash,
9738
- statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
9739
- statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
9740
- verifyingKey: response.directRefundTxSigningResult.verifyingKey,
9741
- statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
9742
- selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
9743
- publicKey: signingPublicKey,
9744
- selfSignature: directRefundUserSig,
9745
- adaptorPubKey: new Uint8Array()
9746
- });
9747
- }
9748
- let directFromCpfpRefundSig;
9749
- if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
9750
- const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
9751
- message: directFromCpfpRefundSighash,
9571
+ if (newDirectNodeTx) {
9572
+ signingJobs.push({
9573
+ signingPublicKey,
9574
+ rawTx: newDirectNodeTx.toBytes(),
9575
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9576
+ type: "directNode",
9577
+ parentTxOut: splitNodeOutput,
9578
+ leafId: node.id,
9752
9579
  keyDerivation,
9753
- publicKey: signingPublicKey,
9754
- verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
9755
- selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
9756
- statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
9757
- adaptorPubKey: new Uint8Array()
9758
- });
9759
- directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
9760
- message: directFromCpfpRefundSighash,
9761
- statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
9762
- statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
9763
- verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
9764
- statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
9765
- selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
9766
- publicKey: signingPublicKey,
9767
- selfSignature: directFromCpfpRefundUserSig,
9768
- adaptorPubKey: new Uint8Array()
9580
+ verifyingKey: node.verifyingPublicKey
9769
9581
  });
9770
9582
  }
9771
- return await sparkClient.finalize_node_signatures_v2({
9772
- intent: 4 /* EXTEND */,
9773
- nodeSignatures: [
9774
- {
9775
- nodeId: response.leafId,
9776
- nodeTxSignature: nodeSig,
9777
- directNodeTxSignature: directNodeSig,
9778
- refundTxSignature: cpfpRefundSig,
9779
- directRefundTxSignature: directRefundSig,
9780
- directFromCpfpRefundTxSignature: directFromCpfpRefundSig
9781
- }
9782
- ]
9783
- });
9784
- }
9785
- async testonly_expireTimeLockNodeTx(node, parentNode) {
9786
- return await this.refreshTimelockNodesInternal(node, parentNode, true);
9787
- }
9788
- async testonly_expireTimeLockRefundtx(node) {
9789
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
9790
- const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
9791
- const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
9792
- const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
9793
- const currTimelock = getCurrentTimelock(currSequence);
9794
- if (currTimelock <= 100) {
9795
- throw new ValidationError("Cannot expire timelock below 100", {
9796
- field: "currTimelock",
9797
- value: currTimelock,
9798
- expected: "Timelock greater than 100"
9799
- });
9800
- }
9801
- const nextSequence = TEST_UNILATERAL_SEQUENCE;
9802
- const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
9803
- const nodeOutput = nodeTx.getOutput(0);
9804
- if (!nodeOutput) {
9805
- throw Error("Could not get node output");
9806
- }
9807
- const keyDerivation = {
9808
- type: "leaf" /* LEAF */,
9809
- path: node.id
9810
- };
9811
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
9812
- const cpfpRefundOutPoint = {
9813
- txid: hexToBytes9(getTxId(nodeTx)),
9814
- index: 0
9815
- };
9816
- let directRefundOutPoint;
9817
- if (directNodeTx) {
9818
- directRefundOutPoint = {
9819
- txid: hexToBytes9(getTxId(directNodeTx)),
9820
- index: 0
9821
- };
9583
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
9584
+ if (!newCpfpNodeOutput) {
9585
+ throw Error("Could not get new cpfp node output");
9822
9586
  }
9587
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
9823
9588
  const {
9824
- cpfpRefundTx: newCpfpRefundTx,
9589
+ cpfpRefundTx: newRefundTx,
9825
9590
  directRefundTx: newDirectRefundTx,
9826
9591
  directFromCpfpRefundTx: newDirectFromCpfpRefundTx
9827
- } = createRefundTxs({
9828
- sequence: nextSequence,
9829
- directSequence: nextDirectSequence,
9830
- input: cpfpRefundOutPoint,
9831
- directInput: directRefundOutPoint,
9832
- amountSats: nodeOutput.amount,
9592
+ } = createInitialTimelockRefundTxs({
9593
+ nodeTx: newNodeTx,
9594
+ directNodeTx: newDirectNodeTx,
9833
9595
  receivingPubkey: signingPublicKey,
9834
9596
  network: this.config.getNetwork()
9835
9597
  });
9836
- const signingJobs = [];
9837
9598
  signingJobs.push({
9838
9599
  signingPublicKey,
9839
- rawTx: newCpfpRefundTx.toBytes(),
9600
+ rawTx: newRefundTx.toBytes(),
9840
9601
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9841
9602
  type: "cpfp",
9842
- parentTxOut: nodeOutput
9603
+ parentTxOut: newCpfpNodeOutput,
9604
+ leafId: node.id,
9605
+ keyDerivation,
9606
+ verifyingKey: node.verifyingPublicKey
9843
9607
  });
9844
- const directNodeTxOut = directNodeTx?.getOutput(0);
9845
- if (newDirectRefundTx && directNodeTxOut) {
9608
+ if (newDirectRefundTx && newDirectNodeOutput) {
9846
9609
  signingJobs.push({
9847
9610
  signingPublicKey,
9848
9611
  rawTx: newDirectRefundTx.toBytes(),
9849
9612
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9850
9613
  type: "direct",
9851
- parentTxOut: directNodeTxOut
9614
+ parentTxOut: newDirectNodeOutput,
9615
+ leafId: node.id,
9616
+ keyDerivation,
9617
+ verifyingKey: node.verifyingPublicKey
9852
9618
  });
9853
9619
  }
9854
- if (newDirectFromCpfpRefundTx) {
9620
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
9855
9621
  signingJobs.push({
9856
9622
  signingPublicKey,
9857
9623
  rawTx: newDirectFromCpfpRefundTx.toBytes(),
9858
9624
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9859
9625
  type: "directFromCpfp",
9860
- parentTxOut: nodeOutput
9626
+ parentTxOut: newCpfpNodeOutput,
9627
+ leafId: node.id,
9628
+ keyDerivation,
9629
+ verifyingKey: node.verifyingPublicKey
9861
9630
  });
9862
9631
  }
9632
+ return signingJobs;
9633
+ }
9634
+ async renewZeroTimelockNodeTxn(node) {
9863
9635
  const sparkClient = await this.connectionManager.createSparkClient(
9864
9636
  this.config.getCoordinatorAddress()
9865
9637
  );
9866
- const response = await sparkClient.refresh_timelock_v2({
9867
- leafId: node.id,
9868
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
9869
- signingJobs: signingJobs.map(getSigningJobProto)
9638
+ const signingJobs = await this.createRenewZeroTimelockNodeSigningJobs(node);
9639
+ const statechainCommitments = await sparkClient.get_signing_commitments({
9640
+ nodeIds: [node.id],
9641
+ count: signingJobs.length
9642
+ });
9643
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
9644
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
9645
+ if (!signingNonceCommitments) {
9646
+ throw new ValidationError("Signing nonce commitments not found", {
9647
+ field: "signingNonceCommitments",
9648
+ value: signingNonceCommitments
9649
+ });
9650
+ }
9651
+ return {
9652
+ ...signingJob,
9653
+ signingNonceCommitments
9654
+ };
9870
9655
  });
9871
- if (response.signingResults.length !== signingJobs.length) {
9872
- throw Error(
9873
- `Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`
9874
- );
9875
- }
9876
- let cpfpRefundSignature;
9877
- let directRefundSignature;
9878
- let directFromCpfpRefundSignature;
9879
- for (const [i, signingJob] of signingJobs.entries()) {
9880
- const signingResult = response.signingResults[i];
9881
- if (!signingResult) {
9882
- throw Error("Signing result does not exist");
9656
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
9657
+ const renewZeroTimelockNodeSigningJob = {
9658
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
9659
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
9660
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
9661
+ directRefundTxSigningJob: void 0,
9662
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
9663
+ };
9664
+ const response = await sparkClient.renew_leaf({
9665
+ leafId: node.id,
9666
+ signingJobs: {
9667
+ $case: "renewNodeZeroTimelockSigningJob",
9668
+ renewNodeZeroTimelockSigningJob: renewZeroTimelockNodeSigningJob
9883
9669
  }
9884
- const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
9885
- const txOut = signingJob.parentTxOut;
9886
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
9887
- const userSignature = await this.config.signer.signFrost({
9888
- message: rawTxSighash,
9889
- keyDerivation,
9890
- publicKey: signingPublicKey,
9891
- verifyingKey: signingResult.verifyingKey,
9892
- selfCommitment: signingJob.signingNonceCommitment,
9893
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
9894
- adaptorPubKey: new Uint8Array()
9895
- });
9896
- const signature = await this.config.signer.aggregateFrost({
9897
- message: rawTxSighash,
9898
- statechainSignatures: signingResult.signingResult?.signatureShares,
9899
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
9900
- verifyingKey: signingResult.verifyingKey,
9901
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
9902
- selfCommitment: signingJob.signingNonceCommitment,
9903
- publicKey: signingPublicKey,
9904
- selfSignature: userSignature,
9905
- adaptorPubKey: new Uint8Array()
9670
+ });
9671
+ if (response.renewResult?.$case !== "renewNodeZeroTimelockResult" || !response.renewResult?.renewNodeZeroTimelockResult.node) {
9672
+ throw new ValidationError("Unexpected renew result", {
9673
+ field: "renewResult",
9674
+ value: response.renewResult
9906
9675
  });
9907
- if (signingJob.type === "cpfp") {
9908
- cpfpRefundSignature = signature;
9909
- } else if (signingJob.type === "direct") {
9910
- directRefundSignature = signature;
9911
- } else if (signingJob.type === "directFromCpfp") {
9912
- directFromCpfpRefundSignature = signature;
9913
- }
9914
9676
  }
9915
- const result = await sparkClient.finalize_node_signatures_v2({
9916
- intent: 3 /* REFRESH */,
9917
- nodeSignatures: [
9918
- {
9919
- nodeId: node.id,
9920
- nodeTxSignature: new Uint8Array(),
9921
- directNodeTxSignature: new Uint8Array(),
9922
- refundTxSignature: cpfpRefundSignature,
9923
- directRefundTxSignature: directRefundSignature,
9924
- directFromCpfpRefundTxSignature: directFromCpfpRefundSignature
9925
- }
9926
- ]
9677
+ return response.renewResult.renewNodeZeroTimelockResult.node;
9678
+ }
9679
+ async createRenewZeroTimelockNodeSigningJobs(node) {
9680
+ const signingJobs = [];
9681
+ const keyDerivation = {
9682
+ type: "leaf" /* LEAF */,
9683
+ path: node.id
9684
+ };
9685
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
9686
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
9687
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createZeroTimelockNodeTx(nodeTx);
9688
+ signingJobs.push({
9689
+ signingPublicKey,
9690
+ rawTx: newNodeTx.toBytes(),
9691
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9692
+ type: "node",
9693
+ parentTxOut: nodeTx.getOutput(0),
9694
+ leafId: node.id,
9695
+ keyDerivation,
9696
+ verifyingKey: node.verifyingPublicKey
9927
9697
  });
9928
- return result;
9698
+ signingJobs.push({
9699
+ signingPublicKey,
9700
+ rawTx: newDirectNodeTx.toBytes(),
9701
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9702
+ type: "directNode",
9703
+ parentTxOut: nodeTx.getOutput(0),
9704
+ leafId: node.id,
9705
+ keyDerivation,
9706
+ verifyingKey: node.verifyingPublicKey
9707
+ });
9708
+ const { cpfpRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
9709
+ nodeTx: newNodeTx,
9710
+ directNodeTx: newDirectNodeTx,
9711
+ receivingPubkey: signingPublicKey,
9712
+ network: this.config.getNetwork()
9713
+ });
9714
+ signingJobs.push({
9715
+ signingPublicKey,
9716
+ rawTx: cpfpRefundTx.toBytes(),
9717
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9718
+ type: "cpfp",
9719
+ parentTxOut: newNodeTx.getOutput(0),
9720
+ leafId: node.id,
9721
+ keyDerivation,
9722
+ verifyingKey: node.verifyingPublicKey
9723
+ });
9724
+ if (!directFromCpfpRefundTx) {
9725
+ throw new Error("Could not create direct refund transactions");
9726
+ }
9727
+ signingJobs.push({
9728
+ signingPublicKey,
9729
+ rawTx: directFromCpfpRefundTx.toBytes(),
9730
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9731
+ type: "directFromCpfp",
9732
+ parentTxOut: newNodeTx.getOutput(0),
9733
+ leafId: node.id,
9734
+ keyDerivation,
9735
+ verifyingKey: node.verifyingPublicKey
9736
+ });
9737
+ return signingJobs;
9929
9738
  }
9930
9739
  };
9931
9740
 
@@ -9967,71 +9776,6 @@ var CoopExitService = class extends BaseTransferService {
9967
9776
  directFromCpfpSignaturesMap
9968
9777
  };
9969
9778
  }
9970
- createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
9971
- const cpfpRefundTx = new Transaction7();
9972
- if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
9973
- throw new ValidationError("Invalid CPFP node outpoint", {
9974
- field: "cpfpNodeOutPoint",
9975
- value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
9976
- expected: "Both txid and index must be defined"
9977
- });
9978
- }
9979
- cpfpRefundTx.addInput({
9980
- txid: cpfpNodeOutPoint.txid,
9981
- index: cpfpNodeOutPoint.index,
9982
- sequence
9983
- });
9984
- cpfpRefundTx.addInput(connectorOutput);
9985
- const receiverScript = getP2TRScriptFromPublicKey(
9986
- receiverPubKey,
9987
- this.config.getNetwork()
9988
- );
9989
- cpfpRefundTx.addOutput({
9990
- script: receiverScript,
9991
- amount: amountSats
9992
- });
9993
- let directRefundTx;
9994
- let directFromCpfpRefundTx;
9995
- if (directNodeOutPoint) {
9996
- if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
9997
- throw new ValidationError("Invalid direct node outpoint", {
9998
- field: "directNodeOutPoint",
9999
- value: {
10000
- txid: directNodeOutPoint.txid,
10001
- index: directNodeOutPoint.index
10002
- },
10003
- expected: "Both txid and index must be defined"
10004
- });
10005
- }
10006
- directRefundTx = new Transaction7();
10007
- directRefundTx.addInput({
10008
- txid: directNodeOutPoint.txid,
10009
- index: directNodeOutPoint.index,
10010
- sequence: directSequence
10011
- });
10012
- directRefundTx.addInput(connectorOutput);
10013
- directRefundTx.addOutput({
10014
- script: receiverScript,
10015
- amount: maybeApplyFee(amountSats)
10016
- });
10017
- directFromCpfpRefundTx = new Transaction7();
10018
- directFromCpfpRefundTx.addInput({
10019
- txid: cpfpNodeOutPoint.txid,
10020
- index: cpfpNodeOutPoint.index,
10021
- sequence: directSequence
10022
- });
10023
- directFromCpfpRefundTx.addInput(connectorOutput);
10024
- directFromCpfpRefundTx.addOutput({
10025
- script: receiverScript,
10026
- amount: maybeApplyFee(amountSats)
10027
- });
10028
- }
10029
- return {
10030
- cpfpRefundTx,
10031
- directRefundTx,
10032
- directFromCpfpRefundTx
10033
- };
10034
- }
10035
9779
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey, transferId) {
10036
9780
  if (leaves.length !== connectorOutputs.length) {
10037
9781
  throw new ValidationError(
@@ -10065,29 +9809,39 @@ var CoopExitService = class extends BaseTransferService {
10065
9809
  expected: "Valid connector output"
10066
9810
  });
10067
9811
  }
9812
+ const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
9813
+ let directNodeTx;
9814
+ if (leaf.leaf.directTx.length > 0) {
9815
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
9816
+ }
10068
9817
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
10069
- const sequence = currentRefundTx.getInput(0).sequence;
10070
- if (!sequence) {
9818
+ if (!currentRefundTx) {
9819
+ throw new ValidationError("Invalid refund transaction", {
9820
+ field: "currentRefundTx",
9821
+ value: currentRefundTx,
9822
+ expected: "Non-null refund transaction"
9823
+ });
9824
+ }
9825
+ const currentSequence = currentRefundTx.getInput(0).sequence;
9826
+ if (!currentSequence) {
10071
9827
  throw new ValidationError("Invalid refund transaction", {
10072
9828
  field: "sequence",
10073
9829
  value: currentRefundTx.getInput(0),
10074
9830
  expected: "Non-null sequence"
10075
9831
  });
10076
9832
  }
10077
- const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
10078
9833
  let currentDirectRefundTx;
10079
9834
  if (leaf.leaf.directRefundTx.length > 0) {
10080
9835
  currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
10081
9836
  }
10082
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
10083
- nextSequence,
10084
- nextDirectSequence,
10085
- currentRefundTx.getInput(0),
10086
- currentDirectRefundTx?.getInput(0),
9837
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createConnectorRefundTxs({
9838
+ nodeTx,
9839
+ directNodeTx,
9840
+ sequence: currentSequence,
10087
9841
  connectorOutput,
10088
- BigInt(leaf.leaf.value),
10089
- receiverPubKey
10090
- );
9842
+ receivingPubkey: receiverPubKey,
9843
+ network: this.config.getNetwork()
9844
+ });
10091
9845
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
10092
9846
  const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
10093
9847
  const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -10568,12 +10322,9 @@ import {
10568
10322
  import { EventEmitter } from "eventemitter3";
10569
10323
  import { ClientError, Status } from "nice-grpc-common";
10570
10324
 
10571
- // src/services/signing.ts
10572
- import { hexToBytes as hexToBytes12 } from "@noble/curves/utils";
10573
-
10574
10325
  // src/utils/htlc-transactions.ts
10575
10326
  import {
10576
- Transaction as Transaction8,
10327
+ Transaction as Transaction6,
10577
10328
  Script,
10578
10329
  taprootListToTree,
10579
10330
  p2tr as p2tr3,
@@ -10661,7 +10412,7 @@ function createLightningHTLCTransaction({
10661
10412
  txid: hexToBytes11(getTxId(nodeTx)),
10662
10413
  index: 0
10663
10414
  };
10664
- const htlcTransaction = new Transaction8({
10415
+ const htlcTransaction = new Transaction6({
10665
10416
  version: 3,
10666
10417
  allowUnknownOutputs: true
10667
10418
  });
@@ -10780,20 +10531,7 @@ var SigningService = class {
10780
10531
  });
10781
10532
  }
10782
10533
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
10783
- const cpfpNodeOutPoint = {
10784
- txid: hexToBytes12(getTxId(nodeTx)),
10785
- index: 0
10786
- };
10787
10534
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
10788
- const sequence = currRefundTx.getInput(0).sequence;
10789
- if (!sequence) {
10790
- throw new ValidationError("Invalid refund transaction", {
10791
- field: "sequence",
10792
- value: currRefundTx.getInput(0),
10793
- expected: "Non-null sequence"
10794
- });
10795
- }
10796
- const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
10797
10535
  const amountSats = currRefundTx.getOutput(0).amount;
10798
10536
  if (amountSats === void 0) {
10799
10537
  throw new ValidationError("Invalid refund transaction", {
@@ -10803,20 +10541,21 @@ var SigningService = class {
10803
10541
  });
10804
10542
  }
10805
10543
  let directNodeTx;
10806
- let directNodeOutPoint;
10807
10544
  if (leaf.leaf.directTx.length > 0) {
10808
10545
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
10809
- directNodeOutPoint = {
10810
- txid: hexToBytes12(getTxId(directNodeTx)),
10811
- index: 0
10812
- };
10813
10546
  }
10814
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
10815
- sequence: nextSequence,
10816
- directSequence: nextDirectSequence,
10817
- input: cpfpNodeOutPoint,
10818
- directInput: directNodeOutPoint,
10819
- amountSats,
10547
+ const currentSequence = currRefundTx.getInput(0).sequence;
10548
+ if (!currentSequence) {
10549
+ throw new ValidationError("Invalid refund transaction", {
10550
+ field: "sequence",
10551
+ value: currRefundTx.getInput(0),
10552
+ expected: "Non-null sequence"
10553
+ });
10554
+ }
10555
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs({
10556
+ nodeTx,
10557
+ directNodeTx,
10558
+ sequence: currentSequence,
10820
10559
  receivingPubkey: receiverIdentityPubkey,
10821
10560
  network: this.config.getNetwork()
10822
10561
  });
@@ -10832,7 +10571,8 @@ var SigningService = class {
10832
10571
  cpfpSigningCommitments[i]?.signingNonceCommitments
10833
10572
  );
10834
10573
  cpfpLeafSigningJobs.push(...signingJobs);
10835
- if (directRefundTx) {
10574
+ const isZeroNode = getCurrentTimelock(nodeTx.getInput(0).sequence);
10575
+ if (directRefundTx && !isZeroNode) {
10836
10576
  if (!directNodeTx) {
10837
10577
  throw new ValidationError(
10838
10578
  "Direct node transaction undefined while direct refund transaction is defined",
@@ -10857,16 +10597,6 @@ var SigningService = class {
10857
10597
  directLeafSigningJobs.push(...signingJobs2);
10858
10598
  }
10859
10599
  if (directFromCpfpRefundTx) {
10860
- if (!directNodeTx) {
10861
- throw new ValidationError(
10862
- "Direct node transaction undefined while direct from CPFP refund transaction is defined",
10863
- {
10864
- field: "directNodeTx",
10865
- value: directNodeTx,
10866
- expected: "Non-null direct node transaction"
10867
- }
10868
- );
10869
- }
10870
10600
  const refundSighash2 = getSigHashFromTx(
10871
10601
  directFromCpfpRefundTx,
10872
10602
  0,
@@ -10987,21 +10717,116 @@ var SigningService = class {
10987
10717
  directFromCpfpLeafSigningJobs.push(...signingJobs2);
10988
10718
  }
10989
10719
  }
10990
- return {
10991
- cpfpLeafSigningJobs,
10992
- directLeafSigningJobs,
10993
- directFromCpfpLeafSigningJobs
10994
- };
10995
- }
10996
- };
10997
-
10998
- // src/utils/chunkArray.ts
10999
- function chunkArray(arr, size) {
11000
- const chunks = [];
11001
- for (let i = 0; i < arr.length; i += size) {
11002
- chunks.push(arr.slice(i, i + size));
10720
+ return {
10721
+ cpfpLeafSigningJobs,
10722
+ directLeafSigningJobs,
10723
+ directFromCpfpLeafSigningJobs
10724
+ };
10725
+ }
10726
+ async signSigningJobs(signingJobs) {
10727
+ const userSignedTxSigningJobs = /* @__PURE__ */ new Map();
10728
+ for (const signingJob of signingJobs) {
10729
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
10730
+ const txOut = signingJob.parentTxOut;
10731
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
10732
+ const userSignature = await this.config.signer.signFrost({
10733
+ message: rawTxSighash,
10734
+ keyDerivation: signingJob.keyDerivation,
10735
+ publicKey: signingJob.signingPublicKey,
10736
+ verifyingKey: signingJob.verifyingKey,
10737
+ selfCommitment: signingJob.signingNonceCommitment,
10738
+ statechainCommitments: signingJob.signingNonceCommitments,
10739
+ adaptorPubKey: new Uint8Array()
10740
+ });
10741
+ const userSignedTxSigningJob = {
10742
+ leafId: signingJob.leafId,
10743
+ signingPublicKey: signingJob.signingPublicKey,
10744
+ rawTx: rawTx.toBytes(),
10745
+ signingNonceCommitment: signingJob.signingNonceCommitment.commitment,
10746
+ signingCommitments: {
10747
+ signingCommitments: signingJob.signingNonceCommitments
10748
+ },
10749
+ userSignature
10750
+ };
10751
+ userSignedTxSigningJobs.set(signingJob.type, userSignedTxSigningJob);
10752
+ }
10753
+ return userSignedTxSigningJobs;
10754
+ }
10755
+ };
10756
+
10757
+ // src/utils/chunkArray.ts
10758
+ function chunkArray(arr, size) {
10759
+ const chunks = [];
10760
+ for (let i = 0; i < arr.length; i += size) {
10761
+ chunks.push(arr.slice(i, i + size));
10762
+ }
10763
+ return chunks;
10764
+ }
10765
+
10766
+ // src/utils/retry.ts
10767
+ var DEFAULT_RETRY_CONFIG = {
10768
+ maxAttempts: 5,
10769
+ baseDelayMs: 1e3,
10770
+ maxDelayMs: 1e4,
10771
+ backoffFactor: 2
10772
+ };
10773
+ function calculateBackoffDelay(attempt, config) {
10774
+ const delay = config.baseDelayMs * Math.pow(config.backoffFactor, attempt - 1);
10775
+ return Math.min(delay, config.maxDelayMs);
10776
+ }
10777
+ async function withRetry(operation, options = {}) {
10778
+ const config = options.config ?? DEFAULT_RETRY_CONFIG;
10779
+ const callbacks = options.callbacks ?? {};
10780
+ const { fetchData, onRetry, onError, onMaxAttemptsReached, onStart } = callbacks;
10781
+ if (onStart) {
10782
+ await onStart();
10783
+ }
10784
+ let currentData = void 0;
10785
+ for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
10786
+ try {
10787
+ if (attempt > 1 && fetchData) {
10788
+ const context = {
10789
+ attempt,
10790
+ maxAttempts: config.maxAttempts,
10791
+ error: new Error("Placeholder"),
10792
+ delayMs: calculateBackoffDelay(attempt, config),
10793
+ data: currentData
10794
+ };
10795
+ currentData = await fetchData(context);
10796
+ }
10797
+ return await operation(currentData);
10798
+ } catch (error) {
10799
+ const lastError = error instanceof Error ? error : new Error(String(error));
10800
+ const delayMs = calculateBackoffDelay(attempt, config);
10801
+ const context = {
10802
+ attempt,
10803
+ maxAttempts: config.maxAttempts,
10804
+ error: lastError,
10805
+ delayMs,
10806
+ data: currentData
10807
+ };
10808
+ if (onError) {
10809
+ const result = await onError(context);
10810
+ if (result) {
10811
+ return result;
10812
+ }
10813
+ }
10814
+ if (attempt === config.maxAttempts) {
10815
+ if (onMaxAttemptsReached) {
10816
+ const result = await onMaxAttemptsReached(context);
10817
+ if (result) {
10818
+ return result;
10819
+ }
10820
+ }
10821
+ throw lastError;
10822
+ }
10823
+ if (onRetry) {
10824
+ await onRetry(context);
10825
+ }
10826
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
10827
+ }
11003
10828
  }
11004
- return chunks;
10829
+ throw new Error("Unexpected retry loop exit");
11005
10830
  }
11006
10831
 
11007
10832
  // src/utils/optimize.ts
@@ -11020,6 +10845,34 @@ function sorted(arr) {
11020
10845
  function equals(a, b) {
11021
10846
  return a.length === b.length && a.every((val, index) => val === b[index]);
11022
10847
  }
10848
+ function countOccurrences(arr) {
10849
+ const map = /* @__PURE__ */ new Map();
10850
+ for (const x of arr) {
10851
+ map.set(x, (map.get(x) ?? 0) + 1);
10852
+ }
10853
+ return map;
10854
+ }
10855
+ function subtractCounters(a, b) {
10856
+ const result = /* @__PURE__ */ new Map();
10857
+ for (const [key, value] of a.entries()) {
10858
+ const diff = value - (b.get(key) ?? 0);
10859
+ if (diff > 0) {
10860
+ result.set(key, diff);
10861
+ }
10862
+ }
10863
+ return result;
10864
+ }
10865
+ function counterToFlatArray(counter) {
10866
+ const arr = [];
10867
+ for (const [k, v] of Array.from(counter.entries()).sort(
10868
+ (a, b) => a[0] - b[0]
10869
+ )) {
10870
+ for (let i = 0; i < v; i++) {
10871
+ arr.push(k);
10872
+ }
10873
+ }
10874
+ return arr;
10875
+ }
11023
10876
  function greedyLeaves(amount) {
11024
10877
  const leaves = [];
11025
10878
  let remaining = amount;
@@ -11035,6 +10888,24 @@ function greedyLeaves(amount) {
11035
10888
  assert(sum(leaves) === amount, "greedy_leaves: sum mismatch");
11036
10889
  return sorted(leaves);
11037
10890
  }
10891
+ function swapMinimizingLeaves(amount, multiplicity = 1) {
10892
+ const leaves = [];
10893
+ let remaining = amount;
10894
+ assert(multiplicity > 0, "multiplicity must be > 0");
10895
+ for (const leaf of DENOMINATIONS) {
10896
+ if (typeof leaf === "number" && leaf > 0) {
10897
+ for (let i = 0; i < multiplicity; i++) {
10898
+ if (remaining >= leaf) {
10899
+ remaining -= leaf;
10900
+ leaves.push(leaf);
10901
+ }
10902
+ }
10903
+ }
10904
+ }
10905
+ leaves.push(...greedyLeaves(remaining));
10906
+ assert(sum(leaves) === amount, "swap_minimizing_leaves: sum mismatch");
10907
+ return sorted(leaves);
10908
+ }
11038
10909
  var Swap = class {
11039
10910
  inLeaves;
11040
10911
  outLeaves;
@@ -11072,71 +10943,83 @@ function maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap = 64) {
11072
10943
  }
11073
10944
  return swaps;
11074
10945
  }
11075
-
11076
- // src/utils/retry.ts
11077
- var DEFAULT_RETRY_CONFIG = {
11078
- maxAttempts: 5,
11079
- baseDelayMs: 1e3,
11080
- maxDelayMs: 1e4,
11081
- backoffFactor: 2
11082
- };
11083
- function calculateBackoffDelay(attempt, config) {
11084
- const delay = config.baseDelayMs * Math.pow(config.backoffFactor, attempt - 1);
11085
- return Math.min(delay, config.maxDelayMs);
11086
- }
11087
- async function withRetry(operation, options = {}) {
11088
- const config = options.config ?? DEFAULT_RETRY_CONFIG;
11089
- const callbacks = options.callbacks ?? {};
11090
- const { fetchData, onRetry, onError, onMaxAttemptsReached, onStart } = callbacks;
11091
- if (onStart) {
11092
- await onStart();
11093
- }
11094
- let currentData = void 0;
11095
- for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
11096
- try {
11097
- if (attempt > 1 && fetchData) {
11098
- const context = {
11099
- attempt,
11100
- maxAttempts: config.maxAttempts,
11101
- error: new Error("Placeholder"),
11102
- delayMs: calculateBackoffDelay(attempt, config),
11103
- data: currentData
11104
- };
11105
- currentData = await fetchData(context);
11106
- }
11107
- return await operation(currentData);
11108
- } catch (error) {
11109
- const lastError = error instanceof Error ? error : new Error(String(error));
11110
- const delayMs = calculateBackoffDelay(attempt, config);
11111
- const context = {
11112
- attempt,
11113
- maxAttempts: config.maxAttempts,
11114
- error: lastError,
11115
- delayMs,
11116
- data: currentData
11117
- };
11118
- if (onError) {
11119
- const result = await onError(context);
11120
- if (result) {
11121
- return result;
11122
- }
11123
- }
11124
- if (attempt === config.maxAttempts) {
11125
- if (onMaxAttemptsReached) {
11126
- const result = await onMaxAttemptsReached(context);
11127
- if (result) {
11128
- return result;
10946
+ function minimizeTransferSwap(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
10947
+ const balance = sum(inputLeaves);
10948
+ const optimalLeaves = swapMinimizingLeaves(balance, multiplicity);
10949
+ const walletCounter = countOccurrences(inputLeaves);
10950
+ const optimalCounter = countOccurrences(optimalLeaves);
10951
+ const leavesToGive = subtractCounters(walletCounter, optimalCounter);
10952
+ const leavesToReceive = subtractCounters(optimalCounter, walletCounter);
10953
+ const leavesToGiveFlat = counterToFlatArray(leavesToGive);
10954
+ const leavesToReceiveFlat = counterToFlatArray(leavesToReceive);
10955
+ const swaps = [];
10956
+ let toGiveBatch = [];
10957
+ let toReceiveBatch = [];
10958
+ let give = [...leavesToGiveFlat];
10959
+ let receive = [...leavesToReceiveFlat];
10960
+ while (give.length > 0 || receive.length > 0) {
10961
+ if (sum(toGiveBatch) > sum(toReceiveBatch)) {
10962
+ if (receive.length === 0) break;
10963
+ toReceiveBatch.push(receive.shift());
10964
+ } else {
10965
+ if (give.length === 0) break;
10966
+ toGiveBatch.push(give.shift());
10967
+ }
10968
+ if (toGiveBatch.length > 0 && toReceiveBatch.length > 0 && sum(toGiveBatch) === sum(toReceiveBatch)) {
10969
+ if (toGiveBatch.length > maxLeavesPerSwap) {
10970
+ for (let i = 0; i < toGiveBatch.length; i += maxLeavesPerSwap) {
10971
+ const subset = toGiveBatch.slice(i, i + maxLeavesPerSwap);
10972
+ swaps.push(new Swap(subset, greedyLeaves(sum(subset))));
10973
+ }
10974
+ } else if (toReceiveBatch.length > maxLeavesPerSwap) {
10975
+ for (let cutoff = maxLeavesPerSwap; cutoff > 0; cutoff--) {
10976
+ const sumCut = sum(toReceiveBatch.slice(0, cutoff));
10977
+ const remainder = sum(toGiveBatch) - sumCut;
10978
+ const alternateBatch = [
10979
+ ...toReceiveBatch.slice(0, cutoff),
10980
+ ...greedyLeaves(remainder)
10981
+ ];
10982
+ if (alternateBatch.length <= maxLeavesPerSwap) {
10983
+ swaps.push(new Swap([...toGiveBatch], alternateBatch));
10984
+ break;
11129
10985
  }
11130
10986
  }
11131
- throw lastError;
11132
- }
11133
- if (onRetry) {
11134
- await onRetry(context);
10987
+ } else {
10988
+ swaps.push(new Swap([...toGiveBatch], [...toReceiveBatch]));
11135
10989
  }
11136
- await new Promise((resolve) => setTimeout(resolve, delayMs));
10990
+ toGiveBatch = [];
10991
+ toReceiveBatch = [];
11137
10992
  }
11138
10993
  }
11139
- throw new Error("Unexpected retry loop exit");
10994
+ return swaps;
10995
+ }
10996
+ function shouldOptimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
10997
+ if (multiplicity == 0) {
10998
+ const swaps = maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
10999
+ const numInputs = sum(swaps.map((swap) => swap.inLeaves.length));
11000
+ const numOutputs = sum(swaps.map((swap) => swap.outLeaves.length));
11001
+ return numOutputs * 5 < numInputs;
11002
+ } else {
11003
+ const swaps = minimizeTransferSwap(
11004
+ inputLeaves,
11005
+ multiplicity,
11006
+ maxLeavesPerSwap
11007
+ );
11008
+ const inputCounter = countOccurrences(
11009
+ swaps.flatMap((swap) => swap.inLeaves)
11010
+ );
11011
+ const outputCounter = countOccurrences(
11012
+ swaps.flatMap((swap) => swap.outLeaves)
11013
+ );
11014
+ return Math.abs(inputCounter.size - outputCounter.size) > 1;
11015
+ }
11016
+ }
11017
+ function optimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
11018
+ if (multiplicity == 0) {
11019
+ return maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
11020
+ } else {
11021
+ return minimizeTransferSwap(inputLeaves, multiplicity, maxLeavesPerSwap);
11022
+ }
11140
11023
  }
11141
11024
 
11142
11025
  // src/spark-wallet/spark-wallet.ts
@@ -11243,16 +11126,13 @@ var SparkWallet = class extends EventEmitter {
11243
11126
  if (event.transfer.transfer && !equalBytes6(senderIdentityPublicKey, receiverIdentityPublicKey)) {
11244
11127
  await this.claimTransfer({
11245
11128
  transfer: event.transfer.transfer,
11246
- emit: true,
11247
- optimize: true
11129
+ emit: true
11248
11130
  });
11249
11131
  }
11250
11132
  } else if (isDepositStreamEvent(event)) {
11251
11133
  const deposit = event.deposit.deposit;
11252
- const newLeaf = await this.transferService.extendTimelock(deposit);
11253
- await this.transferLeavesToSelf(newLeaf.nodes, {
11254
- type: "leaf" /* LEAF */,
11255
- path: deposit.id
11134
+ await this.withLeaves(async () => {
11135
+ this.leaves.push(deposit);
11256
11136
  });
11257
11137
  this.emit(
11258
11138
  SparkWalletEvent.DepositConfirmed,
@@ -11428,19 +11308,6 @@ var SparkWallet = class extends EventEmitter {
11428
11308
  }
11429
11309
  return availableLeaves.filter(([_, node]) => !leavesToIgnore.has(node.id)).map(([_, node]) => node);
11430
11310
  }
11431
- async checkExtendLeaves(leaves) {
11432
- await this.withLeaves(async () => {
11433
- for (const leaf of leaves) {
11434
- if (!leaf.parentNodeId && leaf.status === "AVAILABLE") {
11435
- const res = await this.transferService.extendTimelock(leaf);
11436
- await this.transferLeavesToSelf(res.nodes, {
11437
- type: "leaf" /* LEAF */,
11438
- path: leaf.id
11439
- });
11440
- }
11441
- }
11442
- });
11443
- }
11444
11311
  verifyKey(pubkey1, pubkey2, verifyingKey) {
11445
11312
  return equalBytes6(addPublicKeys(pubkey1, pubkey2), verifyingKey);
11446
11313
  }
@@ -11550,75 +11417,84 @@ var SparkWallet = class extends EventEmitter {
11550
11417
  }
11551
11418
  return nodes;
11552
11419
  }
11553
- areLeavesInefficient() {
11554
- const totalAmount = this.getInternalBalance();
11555
- if (this.leaves.length <= 1) {
11556
- return false;
11557
- }
11558
- const nextLowerPowerOfTwo = 31 - Math.clz32(totalAmount);
11559
- let remainingAmount = totalAmount;
11560
- let optimalLeavesLength = 0;
11561
- for (let i = nextLowerPowerOfTwo; i >= 0; i--) {
11562
- const denomination = 2 ** i;
11563
- while (remainingAmount >= denomination) {
11564
- remainingAmount -= denomination;
11565
- optimalLeavesLength++;
11566
- }
11420
+ async *optimizeLeaves(multiplicity = void 0) {
11421
+ const multiplicityValue = multiplicity ?? this.config.getOptimizationOptions().multiplicity ?? 0;
11422
+ if (multiplicityValue < 0) {
11423
+ throw new ValidationError("Multiplicity cannot be negative");
11424
+ } else if (multiplicityValue > 5) {
11425
+ throw new ValidationError("Multiplicity cannot be greater than 5");
11567
11426
  }
11568
- return this.leaves.length > optimalLeavesLength * 5;
11569
- }
11570
- async optimizeLeaves() {
11571
- if (this.optimizationInProgress || !this.areLeavesInefficient()) {
11427
+ if (this.optimizationInProgress || !shouldOptimize(
11428
+ this.leaves.map((leaf) => leaf.value),
11429
+ multiplicityValue
11430
+ )) {
11572
11431
  return;
11573
11432
  }
11574
- await this.withLeaves(async () => {
11433
+ const controller = new AbortController();
11434
+ const release = await this.leavesMutex.acquire();
11435
+ try {
11575
11436
  this.optimizationInProgress = true;
11576
- try {
11577
- this.leaves = await this.getLeaves();
11578
- const swaps = maximizeUnilateralExit(
11579
- this.leaves.map((leaf) => leaf.value)
11580
- );
11581
- const valueToNodes = /* @__PURE__ */ new Map();
11582
- this.leaves.forEach((leaf) => {
11583
- if (!valueToNodes.has(leaf.value)) {
11584
- valueToNodes.set(leaf.value, []);
11585
- }
11586
- valueToNodes.get(leaf.value).push(leaf);
11587
- });
11588
- for (const swap of swaps) {
11589
- const leavesToSend = [];
11590
- for (const leafValue of swap.inLeaves) {
11591
- const nodes = valueToNodes.get(leafValue);
11592
- if (nodes && nodes.length > 0) {
11593
- const node = nodes.shift();
11594
- leavesToSend.push(node);
11595
- } else {
11596
- throw new InternalValidationError(
11597
- `No unused leaf with value ${leafValue} found in leaves`
11598
- );
11599
- }
11437
+ this.leaves = await this.getLeaves();
11438
+ const swaps = optimize(
11439
+ this.leaves.map((leaf) => leaf.value),
11440
+ multiplicityValue
11441
+ );
11442
+ if (swaps.length === 0) {
11443
+ return;
11444
+ }
11445
+ yield {
11446
+ step: 0,
11447
+ total: swaps.length,
11448
+ controller
11449
+ };
11450
+ const valueToNodes = /* @__PURE__ */ new Map();
11451
+ this.leaves.forEach((leaf) => {
11452
+ if (!valueToNodes.has(leaf.value)) {
11453
+ valueToNodes.set(leaf.value, []);
11454
+ }
11455
+ valueToNodes.get(leaf.value).push(leaf);
11456
+ });
11457
+ for (const swap of swaps) {
11458
+ if (controller.signal.aborted) {
11459
+ break;
11460
+ }
11461
+ const leavesToSend = [];
11462
+ for (const leafValue of swap.inLeaves) {
11463
+ const nodes = valueToNodes.get(leafValue);
11464
+ if (nodes && nodes.length > 0) {
11465
+ const node = nodes.shift();
11466
+ leavesToSend.push(node);
11467
+ } else {
11468
+ throw new InternalValidationError(
11469
+ `No unused leaf with value ${leafValue} found in leaves`
11470
+ );
11600
11471
  }
11601
- await this.requestLeavesSwap({
11602
- leaves: leavesToSend,
11603
- targetAmounts: swap.outLeaves
11604
- });
11605
11472
  }
11606
- this.leaves = await this.getLeaves();
11607
- } finally {
11608
- this.optimizationInProgress = false;
11473
+ await this.requestLeavesSwap({
11474
+ leaves: leavesToSend,
11475
+ targetAmounts: swap.outLeaves
11476
+ });
11477
+ yield {
11478
+ step: swaps.indexOf(swap) + 1,
11479
+ total: swaps.length,
11480
+ controller
11481
+ };
11609
11482
  }
11610
- });
11483
+ this.leaves = await this.getLeaves();
11484
+ } finally {
11485
+ this.optimizationInProgress = false;
11486
+ release();
11487
+ }
11611
11488
  }
11612
11489
  async syncWallet() {
11613
11490
  await this.syncTokenOutputs();
11614
11491
  let leaves = await this.getLeaves();
11615
- leaves = await this.checkRefreshTimelockNodes(leaves);
11616
- leaves = await this.checkExtendTimeLockNodes(leaves);
11492
+ leaves = await this.checkRenewLeaves(leaves);
11617
11493
  this.leaves = leaves;
11618
- this.checkExtendLeaves(leaves);
11619
- this.optimizeLeaves().catch((e) => {
11620
- console.error("Failed to optimize leaves", e);
11621
- });
11494
+ if (this.config.getOptimizationOptions().auto) {
11495
+ for await (const _ of this.optimizeLeaves()) {
11496
+ }
11497
+ }
11622
11498
  }
11623
11499
  async withLeaves(operation) {
11624
11500
  const release = await this.leavesMutex.acquire();
@@ -11735,7 +11611,7 @@ var SparkWallet = class extends EventEmitter {
11735
11611
  mnemonic = mnemonicOrSeed;
11736
11612
  seed = await this.config.signer.mnemonicToSeed(mnemonicOrSeed);
11737
11613
  } else {
11738
- seed = hexToBytes13(mnemonicOrSeed);
11614
+ seed = hexToBytes12(mnemonicOrSeed);
11739
11615
  }
11740
11616
  }
11741
11617
  await this.initWalletFromSeed(seed, accountNumber);
@@ -11878,7 +11754,7 @@ var SparkWallet = class extends EventEmitter {
11878
11754
  directFromCpfpSignatureMap
11879
11755
  } = await this.transferService.startSwapSignRefund(
11880
11756
  leafKeyTweaks,
11881
- hexToBytes13(this.config.getSspIdentityPublicKey()),
11757
+ hexToBytes12(this.config.getSspIdentityPublicKey()),
11882
11758
  new Date(Date.now() + 2 * 60 * 1e3)
11883
11759
  );
11884
11760
  try {
@@ -12093,7 +11969,7 @@ var SparkWallet = class extends EventEmitter {
12093
11969
  throw new Error(`Leaf not found for node ${nodeId}`);
12094
11970
  }
12095
11971
  const cpfpNodeTx = getTxFromRawTxBytes(node.nodeTx);
12096
- const cpfpRefundTxBytes = hexToBytes13(leaf.rawUnsignedRefundTransaction);
11972
+ const cpfpRefundTxBytes = hexToBytes12(leaf.rawUnsignedRefundTransaction);
12097
11973
  const cpfpRefundTx = getTxFromRawTxBytes(cpfpRefundTxBytes);
12098
11974
  const cpfpSighash = getSigHashFromTx(
12099
11975
  cpfpRefundTx,
@@ -12102,7 +11978,7 @@ var SparkWallet = class extends EventEmitter {
12102
11978
  );
12103
11979
  const nodePublicKey = node.verifyingPublicKey;
12104
11980
  const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
12105
- const cpfpAdaptorSignatureBytes = hexToBytes13(
11981
+ const cpfpAdaptorSignatureBytes = hexToBytes12(
12106
11982
  leaf.adaptorSignedSignature
12107
11983
  );
12108
11984
  applyAdaptorToSignature(
@@ -12113,7 +11989,7 @@ var SparkWallet = class extends EventEmitter {
12113
11989
  );
12114
11990
  if (leaf.directRawUnsignedRefundTransaction) {
12115
11991
  const directNodeTx = getTxFromRawTxBytes(node.directTx);
12116
- const directRefundTxBytes = hexToBytes13(
11992
+ const directRefundTxBytes = hexToBytes12(
12117
11993
  leaf.directRawUnsignedRefundTransaction
12118
11994
  );
12119
11995
  const directRefundTx = getTxFromRawTxBytes(directRefundTxBytes);
@@ -12127,7 +12003,7 @@ var SparkWallet = class extends EventEmitter {
12127
12003
  `Direct adaptor signed signature missing for node ${nodeId}`
12128
12004
  );
12129
12005
  }
12130
- const directAdaptorSignatureBytes = hexToBytes13(
12006
+ const directAdaptorSignatureBytes = hexToBytes12(
12131
12007
  leaf.directAdaptorSignedSignature
12132
12008
  );
12133
12009
  applyAdaptorToSignature(
@@ -12138,7 +12014,7 @@ var SparkWallet = class extends EventEmitter {
12138
12014
  );
12139
12015
  }
12140
12016
  if (leaf.directFromCpfpRawUnsignedRefundTransaction) {
12141
- const directFromCpfpRefundTxBytes = hexToBytes13(
12017
+ const directFromCpfpRefundTxBytes = hexToBytes12(
12142
12018
  leaf.directFromCpfpRawUnsignedRefundTransaction
12143
12019
  );
12144
12020
  const directFromCpfpRefundTx = getTxFromRawTxBytes(
@@ -12154,7 +12030,7 @@ var SparkWallet = class extends EventEmitter {
12154
12030
  `Direct adaptor signed signature missing for node ${nodeId}`
12155
12031
  );
12156
12032
  }
12157
- const directFromCpfpAdaptorSignatureBytes = hexToBytes13(
12033
+ const directFromCpfpAdaptorSignatureBytes = hexToBytes12(
12158
12034
  leaf.directFromCpfpAdaptorSignedSignature
12159
12035
  );
12160
12036
  applyAdaptorToSignature(
@@ -12201,8 +12077,7 @@ var SparkWallet = class extends EventEmitter {
12201
12077
  }
12202
12078
  return await this.claimTransfer({
12203
12079
  transfer: incomingTransfer,
12204
- emit: false,
12205
- optimize: false
12080
+ emit: false
12206
12081
  });
12207
12082
  } catch (e) {
12208
12083
  console.error("[processSwapBatch] Error details:", {
@@ -12596,7 +12471,7 @@ var SparkWallet = class extends EventEmitter {
12596
12471
  }
12597
12472
  );
12598
12473
  }
12599
- const tx = new Transaction9();
12474
+ const tx = new Transaction7();
12600
12475
  tx.addInput({
12601
12476
  txid: depositTransactionId,
12602
12477
  index: outputIndex,
@@ -12636,7 +12511,7 @@ var SparkWallet = class extends EventEmitter {
12636
12511
  );
12637
12512
  const swapResponse = await sparkClient.initiate_static_deposit_utxo_refund({
12638
12513
  onChainUtxo: {
12639
- txid: hexToBytes13(depositTransactionId),
12514
+ txid: hexToBytes12(depositTransactionId),
12640
12515
  vout: outputIndex,
12641
12516
  network: networkType
12642
12517
  },
@@ -12763,7 +12638,7 @@ var SparkWallet = class extends EventEmitter {
12763
12638
  creditAmountView.setUint32(0, lowerHalf, true);
12764
12639
  creditAmountView.setUint32(4, upperHalf, true);
12765
12640
  parts.push(new Uint8Array(creditAmountBuffer));
12766
- parts.push(hexToBytes13(sspSignature));
12641
+ parts.push(hexToBytes12(sspSignature));
12767
12642
  const totalLength = parts.reduce((sum2, part) => sum2 + part.length, 0);
12768
12643
  const payload = new Uint8Array(totalLength);
12769
12644
  let offset = 0;
@@ -12867,26 +12742,7 @@ var SparkWallet = class extends EventEmitter {
12867
12742
  depositTx,
12868
12743
  vout
12869
12744
  });
12870
- const resultingNodes = [];
12871
- for (const node of res.nodes) {
12872
- if (node.status === "AVAILABLE") {
12873
- const { nodes } = await this.transferService.extendTimelock(node);
12874
- for (const n of nodes) {
12875
- if (n.status === "AVAILABLE") {
12876
- const transfer = await this.transferLeavesToSelf([n], {
12877
- type: "leaf" /* LEAF */,
12878
- path: node.id
12879
- });
12880
- resultingNodes.push(...transfer);
12881
- } else {
12882
- resultingNodes.push(n);
12883
- }
12884
- }
12885
- } else {
12886
- resultingNodes.push(node);
12887
- }
12888
- }
12889
- return resultingNodes;
12745
+ return res.nodes;
12890
12746
  }
12891
12747
  /**
12892
12748
  * Gets all unused deposit addresses for the wallet.
@@ -13028,6 +12884,9 @@ var SparkWallet = class extends EventEmitter {
13028
12884
  depositTx,
13029
12885
  vout
13030
12886
  });
12887
+ await this.withLeaves(async () => {
12888
+ this.leaves.push(...nodes2);
12889
+ });
13031
12890
  return nodes2;
13032
12891
  });
13033
12892
  this.mutexes.delete(txid);
@@ -13157,7 +13016,7 @@ var SparkWallet = class extends EventEmitter {
13157
13016
  const [outcome] = await this.transferWithInvoice([
13158
13017
  {
13159
13018
  amountSats,
13160
- receiverIdentityPubkey: hexToBytes13(receiverAddress.identityPublicKey)
13019
+ receiverIdentityPubkey: hexToBytes12(receiverAddress.identityPublicKey)
13161
13020
  }
13162
13021
  ]);
13163
13022
  if (!outcome) throw new Error("no transfer created");
@@ -13204,8 +13063,7 @@ var SparkWallet = class extends EventEmitter {
13204
13063
  `TreeNode group at index ${groupIndex} not found for amount ${amount} after selection`
13205
13064
  );
13206
13065
  }
13207
- let available = await this.checkRefreshTimelockNodes(group);
13208
- available = await this.checkExtendTimeLockNodes(available);
13066
+ const available = await this.checkRenewLeaves(group);
13209
13067
  if (available.length < group.length) {
13210
13068
  throw new Error(
13211
13069
  `Not enough available nodes after refresh/extend. Expected ${group.length}, got ${available.length}`
@@ -13248,7 +13106,7 @@ var SparkWallet = class extends EventEmitter {
13248
13106
  transfer.id
13249
13107
  );
13250
13108
  if (pending) {
13251
- await this.claimTransfer({ transfer: pending, optimize: true });
13109
+ await this.claimTransfer({ transfer: pending });
13252
13110
  }
13253
13111
  }
13254
13112
  return {
@@ -13298,75 +13156,45 @@ var SparkWallet = class extends EventEmitter {
13298
13156
  newKeyDerivation: { type: "random" /* RANDOM */ }
13299
13157
  };
13300
13158
  }
13301
- async checkExtendTimeLockNodes(nodes) {
13302
- const nodesToExtend = [];
13159
+ async checkRenewLeaves(nodes) {
13160
+ const nodesToRenewNode = [];
13161
+ const nodesToRenewRefund = [];
13162
+ const nodesToRenewZeroTimelock = [];
13303
13163
  const nodeIds = [];
13304
13164
  const validNodes = [];
13305
13165
  for (const node of nodes) {
13306
13166
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
13307
- const sequence = nodeTx.getInput(0).sequence;
13308
- if (!sequence) {
13167
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
13168
+ const nodeSequence = nodeTx.getInput(0).sequence;
13169
+ const refundSequence = refundTx.getInput(0).sequence;
13170
+ if (nodeSequence === void 0) {
13309
13171
  throw new ValidationError("Invalid node transaction", {
13310
13172
  field: "sequence",
13311
13173
  value: nodeTx.getInput(0),
13312
13174
  expected: "Non-null sequence"
13313
13175
  });
13314
13176
  }
13315
- const needsRefresh = doesLeafNeedRefresh(sequence, true);
13316
- if (needsRefresh) {
13317
- nodesToExtend.push(node);
13318
- nodeIds.push(node.id);
13319
- } else {
13320
- validNodes.push(node);
13321
- }
13322
- }
13323
- if (nodesToExtend.length === 0) {
13324
- return validNodes;
13325
- }
13326
- const nodesToAdd = [];
13327
- for (const node of nodesToExtend) {
13328
- const { nodes: nodes2 } = await this.transferService.extendTimelock(node);
13329
- this.leaves = this.leaves.filter((leaf) => leaf.id !== node.id);
13330
- const newNodes = await this.transferLeavesToSelf(nodes2, {
13331
- type: "leaf" /* LEAF */,
13332
- path: node.id
13333
- });
13334
- nodesToAdd.push(...newNodes);
13335
- }
13336
- this.updateLeaves(nodeIds, nodesToAdd);
13337
- validNodes.push(...nodesToAdd);
13338
- return validNodes;
13339
- }
13340
- /**
13341
- * Internal method to refresh timelock nodes.
13342
- *
13343
- * @param {string} nodeId - The optional ID of the node to refresh. If not provided, all nodes will be checked.
13344
- * @returns {Promise<void>}
13345
- * @private
13346
- */
13347
- async checkRefreshTimelockNodes(nodes) {
13348
- const nodesToRefresh = [];
13349
- const nodeIds = [];
13350
- const validNodes = [];
13351
- for (const node of nodes) {
13352
- const refundTx = getTxFromRawTxBytes(node.refundTx);
13353
- const sequence = refundTx.getInput(0).sequence;
13354
- if (!sequence) {
13177
+ if (!refundSequence) {
13355
13178
  throw new ValidationError("Invalid refund transaction", {
13356
13179
  field: "sequence",
13357
13180
  value: refundTx.getInput(0),
13358
13181
  expected: "Non-null sequence"
13359
13182
  });
13360
13183
  }
13361
- const needsRefresh = doesLeafNeedRefresh(sequence);
13362
- if (needsRefresh) {
13363
- nodesToRefresh.push(node);
13184
+ if (doesTxnNeedRenewed(refundSequence)) {
13185
+ if (isZeroTimelock(nodeSequence)) {
13186
+ nodesToRenewZeroTimelock.push(node);
13187
+ } else if (doesTxnNeedRenewed(nodeSequence)) {
13188
+ nodesToRenewNode.push(node);
13189
+ } else {
13190
+ nodesToRenewRefund.push(node);
13191
+ }
13364
13192
  nodeIds.push(node.id);
13365
13193
  } else {
13366
13194
  validNodes.push(node);
13367
13195
  }
13368
13196
  }
13369
- if (nodesToRefresh.length === 0) {
13197
+ if (nodesToRenewNode.length === 0 && nodesToRenewRefund.length === 0 && nodesToRenewZeroTimelock.length === 0) {
13370
13198
  return validNodes;
13371
13199
  }
13372
13200
  const nodesResp = await this.queryNodes({
@@ -13384,7 +13212,7 @@ var SparkWallet = class extends EventEmitter {
13384
13212
  nodesMap.set(node.id, node);
13385
13213
  }
13386
13214
  const nodesToAdd = [];
13387
- for (const node of nodesToRefresh) {
13215
+ for (const node of nodesToRenewNode) {
13388
13216
  if (!node.parentNodeId) {
13389
13217
  throw new Error(`node ${node.id} has no parent`);
13390
13218
  }
@@ -13392,17 +13220,25 @@ var SparkWallet = class extends EventEmitter {
13392
13220
  if (!parentNode) {
13393
13221
  throw new Error(`parent node ${node.parentNodeId} not found`);
13394
13222
  }
13395
- const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
13223
+ const newNode = await this.transferService.renewNodeTxn(node, parentNode);
13224
+ nodesToAdd.push(newNode);
13225
+ }
13226
+ for (const node of nodesToRenewRefund) {
13227
+ if (!node.parentNodeId) {
13228
+ throw new Error(`node ${node.id} has no parent`);
13229
+ }
13230
+ const parentNode = nodesMap.get(node.parentNodeId);
13231
+ if (!parentNode) {
13232
+ throw new Error(`parent node ${node.parentNodeId} not found`);
13233
+ }
13234
+ const newNode = await this.transferService.renewRefundTxn(
13396
13235
  node,
13397
13236
  parentNode
13398
13237
  );
13399
- if (nodes2.length !== 1) {
13400
- throw new Error(`expected 1 node, got ${nodes2.length}`);
13401
- }
13402
- const newNode = nodes2[0];
13403
- if (!newNode) {
13404
- throw new Error("Failed to refresh timelock node");
13405
- }
13238
+ nodesToAdd.push(newNode);
13239
+ }
13240
+ for (const node of nodesToRenewZeroTimelock) {
13241
+ const newNode = await this.transferService.renewZeroTimelockNodeTxn(node);
13406
13242
  nodesToAdd.push(newNode);
13407
13243
  }
13408
13244
  this.updateLeaves(nodeIds, nodesToAdd);
@@ -13443,14 +13279,14 @@ var SparkWallet = class extends EventEmitter {
13443
13279
  return response.nodes;
13444
13280
  });
13445
13281
  }
13446
- async processClaimedTransferResults(result, transfer, emit, optimize) {
13447
- result = await this.checkRefreshTimelockNodes(result);
13448
- result = await this.checkExtendTimeLockNodes(result);
13282
+ async processClaimedTransferResults(result, transfer, emit) {
13283
+ result = await this.checkRenewLeaves(result);
13449
13284
  const existingIds = new Set(this.leaves.map((leaf) => leaf.id));
13450
13285
  const uniqueResults = result.filter((node) => !existingIds.has(node.id));
13451
13286
  this.leaves.push(...uniqueResults);
13452
- if (optimize && transfer.type !== 40 /* COUNTER_SWAP */) {
13453
- await this.optimizeLeaves();
13287
+ if (this.config.getOptimizationOptions().auto && transfer.type !== 40 /* COUNTER_SWAP */) {
13288
+ for await (const _ of this.optimizeLeaves()) {
13289
+ }
13454
13290
  }
13455
13291
  if (emit) {
13456
13292
  this.emit(
@@ -13469,8 +13305,7 @@ var SparkWallet = class extends EventEmitter {
13469
13305
  */
13470
13306
  async claimTransfer({
13471
13307
  transfer,
13472
- emit,
13473
- optimize
13308
+ emit
13474
13309
  }) {
13475
13310
  const onError = async (context) => {
13476
13311
  const error = context.error;
@@ -13515,12 +13350,7 @@ var SparkWallet = class extends EventEmitter {
13515
13350
  if (result.length === 0) {
13516
13351
  return [];
13517
13352
  }
13518
- return await this.processClaimedTransferResults(
13519
- result,
13520
- transfer,
13521
- emit,
13522
- optimize
13523
- );
13353
+ return await this.processClaimedTransferResults(result, transfer, emit);
13524
13354
  } catch (error) {
13525
13355
  console.warn(
13526
13356
  `Failed to claim transfer after all retries. Please try reinitializing your wallet in a few minutes. Transfer ID: ${transfer.id}`,
@@ -13553,7 +13383,7 @@ var SparkWallet = class extends EventEmitter {
13553
13383
  continue;
13554
13384
  }
13555
13385
  promises.push(
13556
- this.claimTransfer({ transfer, emit, optimize: true }).then(() => transfer.id).catch((error) => {
13386
+ this.claimTransfer({ transfer, emit }).then(() => transfer.id).catch((error) => {
13557
13387
  console.warn(`Failed to claim transfer ${transfer.id}:`, error);
13558
13388
  return null;
13559
13389
  })
@@ -13779,7 +13609,7 @@ var SparkWallet = class extends EventEmitter {
13779
13609
  const sparkFallbackAddress = decodedInvoice.fallbackAddress;
13780
13610
  const paymentHash = decodedInvoice.paymentHash;
13781
13611
  if (preferSpark) {
13782
- if (sparkFallbackAddress === void 0 || isValidSparkFallback(hexToBytes13(sparkFallbackAddress)) === false) {
13612
+ if (sparkFallbackAddress === void 0 || isValidSparkFallback(hexToBytes12(sparkFallbackAddress)) === false) {
13783
13613
  console.warn(
13784
13614
  "No valid spark address found in invoice. Defaulting to lightning."
13785
13615
  );
@@ -13824,8 +13654,7 @@ var SparkWallet = class extends EventEmitter {
13824
13654
  selectedLeaves,
13825
13655
  `no leaves for ${totalAmount}`
13826
13656
  );
13827
- leaves = await this.checkRefreshTimelockNodes(leaves);
13828
- leaves = await this.checkExtendTimeLockNodes(leaves);
13657
+ leaves = await this.checkRenewLeaves(leaves);
13829
13658
  const leavesToSend = await Promise.all(
13830
13659
  leaves.map(async (leaf) => ({
13831
13660
  leaf,
@@ -13841,17 +13670,17 @@ var SparkWallet = class extends EventEmitter {
13841
13670
  const transferID = uuidv74();
13842
13671
  const startTransferRequest = await this.transferService.prepareTransferForLightning(
13843
13672
  leavesToSend,
13844
- hexToBytes13(this.config.getSspIdentityPublicKey()),
13845
- hexToBytes13(paymentHash),
13673
+ hexToBytes12(this.config.getSspIdentityPublicKey()),
13674
+ hexToBytes12(paymentHash),
13846
13675
  expiryTime,
13847
13676
  transferID
13848
13677
  );
13849
13678
  const swapResponse = await this.lightningService.swapNodesForPreimage({
13850
13679
  leaves: leavesToSend,
13851
- receiverIdentityPubkey: hexToBytes13(
13680
+ receiverIdentityPubkey: hexToBytes12(
13852
13681
  this.config.getSspIdentityPublicKey()
13853
13682
  ),
13854
- paymentHash: hexToBytes13(paymentHash),
13683
+ paymentHash: hexToBytes12(paymentHash),
13855
13684
  isInboundPayment: false,
13856
13685
  invoiceString: invoice,
13857
13686
  feeSats: feeEstimate,
@@ -13954,7 +13783,7 @@ var SparkWallet = class extends EventEmitter {
13954
13783
  }
13955
13784
  satsInvoices.push({
13956
13785
  amountSats: encodedAmount ?? Number(amount),
13957
- receiverIdentityPubkey: hexToBytes13(addressData.identityPublicKey),
13786
+ receiverIdentityPubkey: hexToBytes12(addressData.identityPublicKey),
13958
13787
  sparkInvoice: invoice
13959
13788
  });
13960
13789
  } else if (fields.paymentType?.type === "tokens") {
@@ -14156,10 +13985,8 @@ var SparkWallet = class extends EventEmitter {
14156
13985
  leavesToSendToSsp = leavesForTargetAmount;
14157
13986
  leavesToSendToSE = leavesForFee;
14158
13987
  }
14159
- leavesToSendToSsp = await this.checkRefreshTimelockNodes(leavesToSendToSsp);
14160
- leavesToSendToSsp = await this.checkExtendTimeLockNodes(leavesToSendToSsp);
14161
- leavesToSendToSE = await this.checkRefreshTimelockNodes(leavesToSendToSE);
14162
- leavesToSendToSE = await this.checkExtendTimeLockNodes(leavesToSendToSE);
13988
+ leavesToSendToSsp = await this.checkRenewLeaves(leavesToSendToSsp);
13989
+ leavesToSendToSE = await this.checkRenewLeaves(leavesToSendToSE);
14163
13990
  const leafKeyTweaks = await Promise.all(
14164
13991
  [...leavesToSendToSE, ...leavesToSendToSsp].map(async (leaf) => ({
14165
13992
  leaf,
@@ -14204,11 +14031,11 @@ var SparkWallet = class extends EventEmitter {
14204
14031
  const connectorOutputs = [];
14205
14032
  for (let i = 0; i < connectorTx.outputsLength - 1; i++) {
14206
14033
  connectorOutputs.push({
14207
- txid: hexToBytes13(connectorTxId),
14034
+ txid: hexToBytes12(connectorTxId),
14208
14035
  index: i
14209
14036
  });
14210
14037
  }
14211
- const sspPubIdentityKey = hexToBytes13(this.config.getSspIdentityPublicKey());
14038
+ const sspPubIdentityKey = hexToBytes12(this.config.getSspIdentityPublicKey());
14212
14039
  const transfer = await this.coopExitService.getConnectorRefundSignatures({
14213
14040
  leaves: leafKeyTweaks,
14214
14041
  exitTxId: coopExitTxId,
@@ -14245,8 +14072,7 @@ var SparkWallet = class extends EventEmitter {
14245
14072
  (await this.selectLeaves([amountSats])).get(amountSats),
14246
14073
  `no leaves for ${amountSats}`
14247
14074
  );
14248
- leaves = await this.checkRefreshTimelockNodes(leaves);
14249
- leaves = await this.checkExtendTimeLockNodes(leaves);
14075
+ leaves = await this.checkRenewLeaves(leaves);
14250
14076
  const feeEstimate = await sspClient.getCoopExitFeeQuote({
14251
14077
  leafExternalIds: leaves.map((leaf) => leaf.id),
14252
14078
  withdrawalAddress
@@ -14521,7 +14347,7 @@ var SparkWallet = class extends EventEmitter {
14521
14347
  async validateMessageWithIdentityKey(message, signature) {
14522
14348
  const hash = sha25613(message);
14523
14349
  if (typeof signature === "string") {
14524
- signature = hexToBytes13(signature);
14350
+ signature = hexToBytes12(signature);
14525
14351
  }
14526
14352
  return this.config.signer.validateMessageWithIdentityKey(hash, signature);
14527
14353
  }
@@ -14534,7 +14360,7 @@ var SparkWallet = class extends EventEmitter {
14534
14360
  */
14535
14361
  async signTransaction(txHex, keyType = "auto-detect") {
14536
14362
  try {
14537
- const tx = Transaction9.fromRaw(hexToBytes13(txHex));
14363
+ const tx = Transaction7.fromRaw(hexToBytes12(txHex));
14538
14364
  let publicKey;
14539
14365
  switch (keyType.toLowerCase()) {
14540
14366
  case "identity":
@@ -14741,15 +14567,6 @@ var SparkWallet = class extends EventEmitter {
14741
14567
  }
14742
14568
  );
14743
14569
  }
14744
- if (!nodeInput.sequence) {
14745
- throw new ValidationError(
14746
- `Node transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
14747
- {
14748
- field: "sequence",
14749
- value: nodeInput.sequence
14750
- }
14751
- );
14752
- }
14753
14570
  const refundInput = refundTx.getInput(0);
14754
14571
  if (!refundInput) {
14755
14572
  throw new ValidationError(
@@ -14785,89 +14602,6 @@ var SparkWallet = class extends EventEmitter {
14785
14602
  );
14786
14603
  }
14787
14604
  }
14788
- /**
14789
- * Refresh the timelock of a specific node.
14790
- *
14791
- * @param {string} nodeId - The ID of the node to refresh
14792
- * @returns {Promise<void>} Promise that resolves when the timelock is refreshed
14793
- */
14794
- async testOnly_expireTimelock(nodeId) {
14795
- const sparkClient = await this.connectionManager.createSparkClient(
14796
- this.config.getCoordinatorAddress()
14797
- );
14798
- try {
14799
- const response = await sparkClient.query_nodes({
14800
- source: {
14801
- $case: "nodeIds",
14802
- nodeIds: {
14803
- nodeIds: [nodeId]
14804
- }
14805
- },
14806
- includeParents: true
14807
- });
14808
- let leaf = response.nodes[nodeId];
14809
- if (!leaf) {
14810
- throw new ValidationError("Node not found", {
14811
- field: "nodeId",
14812
- value: nodeId
14813
- });
14814
- }
14815
- let parentNode;
14816
- let hasParentNode = false;
14817
- if (!leaf.parentNodeId) {
14818
- } else {
14819
- hasParentNode = true;
14820
- parentNode = response.nodes[leaf.parentNodeId];
14821
- if (!parentNode) {
14822
- throw new ValidationError("Parent node not found", {
14823
- field: "parentNodeId",
14824
- value: leaf.parentNodeId
14825
- });
14826
- }
14827
- }
14828
- const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
14829
- const refundTx = getTxFromRawTxBytes(leaf.refundTx);
14830
- if (hasParentNode) {
14831
- const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
14832
- if (nodeTimelock > 100) {
14833
- const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
14834
- leaf,
14835
- parentNode
14836
- );
14837
- if (!expiredNodeTxLeaf.nodes[0]) {
14838
- throw new ValidationError("No expired node tx leaf", {
14839
- field: "expiredNodeTxLeaf",
14840
- value: expiredNodeTxLeaf
14841
- });
14842
- }
14843
- leaf = expiredNodeTxLeaf.nodes[0];
14844
- }
14845
- }
14846
- const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
14847
- if (refundTimelock > 100) {
14848
- const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
14849
- if (!expiredTxLeaf.nodes[0]) {
14850
- throw new ValidationError("No expired tx leaf", {
14851
- field: "expiredTxLeaf",
14852
- value: expiredTxLeaf
14853
- });
14854
- }
14855
- leaf = expiredTxLeaf.nodes[0];
14856
- }
14857
- const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
14858
- if (leafIndex !== -1) {
14859
- this.leaves[leafIndex] = leaf;
14860
- }
14861
- } catch (error) {
14862
- throw new NetworkError(
14863
- "Failed to refresh timelock",
14864
- {
14865
- method: "refresh_timelock"
14866
- },
14867
- error
14868
- );
14869
- }
14870
- }
14871
14605
  cleanup() {
14872
14606
  if (this.claimTransfersInterval) {
14873
14607
  clearInterval(this.claimTransfersInterval);
@@ -15022,8 +14756,7 @@ var SparkWallet = class extends EventEmitter {
15022
14756
  "getLightningReceiveRequest",
15023
14757
  "getLightningSendRequest",
15024
14758
  "getCoopExitRequest",
15025
- "checkTimelock",
15026
- "testOnly_expireTimelock"
14759
+ "checkTimelock"
15027
14760
  ];
15028
14761
  methods.forEach((m) => this.wrapPublicSparkWalletMethodWithOtelSpan(m));
15029
14762
  this.initWallet = this.wrapWithOtelSpan(
@@ -15074,23 +14807,26 @@ export {
15074
14807
  HTLC_TIMELOCK_OFFSET,
15075
14808
  DIRECT_HTLC_TIMELOCK_OFFSET,
15076
14809
  INITIAL_SEQUENCE,
15077
- INITIAL_DIRECT_SEQUENCE,
15078
14810
  TEST_UNILATERAL_SEQUENCE,
15079
14811
  TEST_UNILATERAL_DIRECT_SEQUENCE,
15080
14812
  DEFAULT_FEE_SATS,
15081
14813
  maybeApplyFee,
15082
- createRootTx,
15083
- createSplitTx,
15084
- createNodeTx,
15085
- createNodeTxs,
15086
- createLeafNodeTx,
15087
- createRefundTx,
14814
+ createRootNodeTx,
14815
+ createZeroTimelockNodeTx,
14816
+ createInitialTimelockNodeTx,
14817
+ createDecrementedTimelockNodeTx,
14818
+ createTestUnilateralTimelockNodeTx,
15088
14819
  getNextHTLCTransactionSequence,
15089
- createRefundTxs,
15090
- createConnectorRefundTransactions,
14820
+ createInitialTimelockRefundTxs,
14821
+ createDecrementedTimelockRefundTxs,
14822
+ createCurrentTimelockRefundTxs,
14823
+ createTestUnilateralRefundTxs,
14824
+ createConnectorRefundTxs,
15091
14825
  getCurrentTimelock,
15092
14826
  getTransactionSequence,
15093
14827
  checkIfValidSequence,
14828
+ isZeroTimelock,
14829
+ doesTxnNeedRenewed,
15094
14830
  doesLeafNeedRefresh,
15095
14831
  getNextTransactionSequence,
15096
14832
  getEphemeralAnchorOutput,
@@ -15164,6 +14900,7 @@ export {
15164
14900
  isEphemeralAnchorOutput,
15165
14901
  constructUnilateralExitTxs,
15166
14902
  constructUnilateralExitFeeBumpPackages,
14903
+ hash160,
15167
14904
  constructFeeBumpTx,
15168
14905
  getSparkAddressFromTaproot,
15169
14906
  utils_exports